1
0
Fork 0
mirror of https://github.com/sussy-code/smov.git synced 2024-12-20 14:37:43 +01:00

update linting

This commit is contained in:
Jelle van Snik 2023-01-08 15:42:35 +01:00
parent 196d6ae6e5
commit f93b9b5b0f
24 changed files with 165 additions and 155 deletions

View file

@ -23,6 +23,11 @@ module.exports = {
project: "./tsconfig.json", project: "./tsconfig.json",
tsconfigRootDir: "./", tsconfigRootDir: "./",
}, },
settings: {
"import/resolver": {
typescript: {},
},
},
plugins: ["@typescript-eslint", "import"], plugins: ["@typescript-eslint", "import"],
rules: { rules: {
"react/jsx-uses-react": "off", "react/jsx-uses-react": "off",

View file

@ -26,7 +26,7 @@
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"lint": "eslint --ext .tsx,.ts src", "lint": "eslint --ext .tsx,.ts src",
"lint:strict": "eslint --ext .tsx,.ts --max-warnings 0 src", "lint:fix": "eslint --fix --ext .tsx,.ts src",
"lint:report": "eslint --ext .tsx,.ts --output-file eslint_report.json --format json src" "lint:report": "eslint --ext .tsx,.ts --output-file eslint_report.json --format json src"
}, },
"browserslist": { "browserslist": {

View file

@ -57,5 +57,5 @@ export function Dropdown(props: DropdownProps) {
)} )}
</Listbox> </Listbox>
</div> </div>
) );
} }

View file

@ -1,6 +1,6 @@
import { useState } from "react"; import { useState } from "react";
import { MWMediaType, MWQuery } from "@/providers";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { MWMediaType, MWQuery } from "@/providers";
import { DropdownButton } from "./buttons/DropdownButton"; import { DropdownButton } from "./buttons/DropdownButton";
import { Icon, Icons } from "./Icon"; import { Icon, Icons } from "./Icon";
import { TextInputControl } from "./text-inputs/TextInputControl"; import { TextInputControl } from "./text-inputs/TextInputControl";

View file

@ -1,6 +1,6 @@
import React, { createRef, useEffect, useState } from "react"; import React, { createRef, useEffect, useState } from "react";
import { useFade } from "@/hooks/useFade";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { useFade } from "@/hooks/useFade";
interface BackdropProps { interface BackdropProps {
onClick?: (e: MouseEvent) => void; onClick?: (e: MouseEvent) => void;

View file

@ -1,5 +1,5 @@
import { Icon, Icons } from "@/components/Icon";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Icon, Icons } from "@/components/Icon";
export function BrandPill(props: { clickable?: boolean }) { export function BrandPill(props: { clickable?: boolean }) {
const { t } = useTranslation(); const { t } = useTranslation();

View file

@ -8,10 +8,10 @@ export function Loading(props: LoadingProps) {
<div className={props.className}> <div className={props.className}>
<div className="flex flex-col items-center justify-center"> <div className="flex flex-col items-center justify-center">
<div className="flex h-12 items-center justify-center"> <div className="flex h-12 items-center justify-center">
<div className="animate-loading-pin bg-denim-300 mx-1 h-2 w-2 rounded-full" /> <div className="mx-1 h-2 w-2 animate-loading-pin rounded-full bg-denim-300" />
<div className="animate-loading-pin bg-denim-300 mx-1 h-2 w-2 rounded-full [animation-delay:150ms]" /> <div className="mx-1 h-2 w-2 animate-loading-pin rounded-full bg-denim-300 [animation-delay:150ms]" />
<div className="animate-loading-pin bg-denim-300 mx-1 h-2 w-2 rounded-full [animation-delay:300ms]" /> <div className="mx-1 h-2 w-2 animate-loading-pin rounded-full bg-denim-300 [animation-delay:300ms]" />
<div className="animate-loading-pin bg-denim-300 mx-1 h-2 w-2 rounded-full [animation-delay:450ms]" /> <div className="mx-1 h-2 w-2 animate-loading-pin rounded-full bg-denim-300 [animation-delay:450ms]" />
</div> </div>
{props.text && props.text.length ? ( {props.text && props.text.length ? (
<p className="mt-3 max-w-xs text-sm opacity-75">{props.text}</p> <p className="mt-3 max-w-xs text-sm opacity-75">{props.text}</p>

View file

@ -1,14 +1,16 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
export interface PaperProps { export interface PaperProps {
children?: ReactNode, children?: ReactNode;
className?: string, className?: string;
} }
export function Paper(props: PaperProps) { export function Paper(props: PaperProps) {
return ( return (
<div className={`bg-denim-200 lg:rounded-xl px-4 sm:px-8 md:px-12 py-6 sm:py-8 md:py-12 ${props.className}`}> <div
className={`bg-denim-200 px-4 py-6 sm:px-8 sm:py-8 md:px-12 md:py-12 lg:rounded-xl ${props.className}`}
>
{props.children} {props.children}
</div> </div>
) );
} }

View file

@ -1,5 +1,6 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { IconPatch } from "@/components/buttons/IconPatch"; import { IconPatch } from "@/components/buttons/IconPatch";
import { Dropdown, OptionItem } from "@/components/Dropdown"; import { Dropdown, OptionItem } from "@/components/Dropdown";
import { Icons } from "@/components/Icon"; import { Icons } from "@/components/Icon";
@ -14,7 +15,6 @@ import {
MWPortableMedia, MWPortableMedia,
} from "@/providers"; } from "@/providers";
import { getSeasonDataFromMedia } from "@/providers/methods/seasons"; import { getSeasonDataFromMedia } from "@/providers/methods/seasons";
import { useTranslation } from "react-i18next";
export interface SeasonsProps { export interface SeasonsProps {
media: MWMedia; media: MWMedia;
@ -37,7 +37,7 @@ export function LoadingSeasons(props: { error?: boolean }) {
) : ( ) : (
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<IconPatch icon={Icons.WARNING} className="text-red-400" /> <IconPatch icon={Icons.WARNING} className="text-red-400" />
<p>{t('seasons.failed')}</p> <p>{t("seasons.failed")}</p>
</div> </div>
)} )}
</div> </div>
@ -75,7 +75,7 @@ export function Seasons(props: SeasonsProps) {
const mapSeason = (season: MWMediaSeason) => ({ const mapSeason = (season: MWMediaSeason) => ({
id: season.id, id: season.id,
name: season.title || `${t('seasons.season', { season: season.sort })}`, name: season.title || `${t("seasons.season", { season: season.sort })}`,
}); });
const options = seasons.seasons.map(mapSeason); const options = seasons.seasons.map(mapSeason);

View file

@ -9,12 +9,12 @@ export function Episode(props: EpisodeProps) {
return ( return (
<div <div
onClick={props.onClick} onClick={props.onClick}
className={`bg-denim-500 hover:bg-denim-400 transition-[background-color, transform, box-shadow] relative mr-3 mb-3 inline-flex h-10 w-10 cursor-pointer select-none items-center justify-center overflow-hidden rounded font-bold text-white active:scale-110 ${ className={`transition-[background-color, transform, box-shadow] relative mr-3 mb-3 inline-flex h-10 w-10 cursor-pointer select-none items-center justify-center overflow-hidden rounded bg-denim-500 font-bold text-white hover:bg-denim-400 active:scale-110 ${
props.active ? "shadow-bink-500 shadow-[inset_0_0_0_2px]" : "" props.active ? "shadow-[inset_0_0_0_2px] shadow-bink-500" : ""
}`} }`}
> >
<div <div
className="bg-bink-500 absolute bottom-0 top-0 left-0 bg-opacity-50" className="absolute bottom-0 top-0 left-0 bg-bink-500 bg-opacity-50"
style={{ style={{
width: `${props.progress || 0}%`, width: `${props.progress || 0}%`,
}} }}

View file

@ -5,7 +5,7 @@ export interface DotListProps {
export function DotList(props: DotListProps) { export function DotList(props: DotListProps) {
return ( return (
<p className={`text-denim-700 font-semibold ${props.className || ""}`}> <p className={`font-semibold text-denim-700 ${props.className || ""}`}>
{props.content.map((item, index) => ( {props.content.map((item, index) => (
<span key={item}> <span key={item}>
{index !== 0 ? ( {index !== 0 ? (

View file

@ -16,22 +16,27 @@ interface ILinkPropsInternal extends ILinkPropsBase {
to: string; to: string;
} }
type LinkProps = type LinkProps = ILinkPropsExternal | ILinkPropsInternal | ILinkPropsBase;
| ILinkPropsExternal
| ILinkPropsInternal
| ILinkPropsBase;
export function Link(props: LinkProps) { export function Link(props: LinkProps) {
const isExternal = !!(props as ILinkPropsExternal).url; const isExternal = !!(props as ILinkPropsExternal).url;
const isInternal = !!(props as ILinkPropsInternal).to; const isInternal = !!(props as ILinkPropsInternal).to;
const content = ( const content = (
<span className="text-bink-600 hover:text-bink-700 cursor-pointer font-bold"> <span className="cursor-pointer font-bold text-bink-600 hover:text-bink-700">
{props.children} {props.children}
</span> </span>
); );
if (isExternal) if (isExternal)
return <a target={(props as ILinkPropsExternal).newTab ? "_blank" : undefined} rel="noreferrer" href={(props as ILinkPropsExternal).url}>{content}</a>; return (
<a
target={(props as ILinkPropsExternal).newTab ? "_blank" : undefined}
rel="noreferrer"
href={(props as ILinkPropsExternal).url}
>
{content}
</a>
);
if (isInternal) if (isInternal)
return ( return (
<LinkRouter to={(props as ILinkPropsInternal).to}>{content}</LinkRouter> <LinkRouter to={(props as ILinkPropsInternal).to}>{content}</LinkRouter>

View file

@ -4,17 +4,14 @@ export function useDebounce<T>(value: T, delay: number): T {
// State and setters for debounced value // State and setters for debounced value
const [debouncedValue, setDebouncedValue] = useState<T>(value); const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect( useEffect(() => {
() => { const handler = setTimeout(() => {
const handler = setTimeout(() => { setDebouncedValue(value);
setDebouncedValue(value); }, delay);
}, delay); return () => {
return () => { clearTimeout(handler);
clearTimeout(handler); };
}; }, [value, delay]);
},
[value, delay]
);
return debouncedValue; return debouncedValue;
} }

View file

@ -1,7 +1,9 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import './useFade.css' import "./useFade.css";
export const useFade = (initial = false): [boolean, React.Dispatch<React.SetStateAction<boolean>>, any] => { export const useFade = (
initial = false
): [boolean, React.Dispatch<React.SetStateAction<boolean>>, any] => {
const [show, setShow] = useState<boolean>(initial); const [show, setShow] = useState<boolean>(initial);
const [isVisible, setVisible] = useState<boolean>(show); const [isVisible, setVisible] = useState<boolean>(show);
@ -20,7 +22,7 @@ export const useFade = (initial = false): [boolean, React.Dispatch<React.SetStat
// These props go on the fading DOM element // These props go on the fading DOM element
const fadeProps = { const fadeProps = {
style, style,
onAnimationEnd onAnimationEnd,
}; };
return [isVisible, setShow, fadeProps]; return [isVisible, setShow, fadeProps];

View file

@ -1,97 +1,97 @@
export enum MWMediaType { export enum MWMediaType {
MOVIE = "movie", MOVIE = "movie",
SERIES = "series", SERIES = "series",
ANIME = "anime", ANIME = "anime",
} }
export interface MWPortableMedia { export interface MWPortableMedia {
mediaId: string; mediaId: string;
mediaType: MWMediaType; mediaType: MWMediaType;
providerId: string; providerId: string;
seasonId?: string; seasonId?: string;
episodeId?: string; episodeId?: string;
} }
export type MWMediaStreamType = "m3u8" | "mp4"; export type MWMediaStreamType = "m3u8" | "mp4";
export interface MWMediaCaption { export interface MWMediaCaption {
id: string; id: string;
url: string; url: string;
label: string; label: string;
} }
export interface MWMediaStream { export interface MWMediaStream {
url: string; url: string;
type: MWMediaStreamType; type: MWMediaStreamType;
captions: MWMediaCaption[]; captions: MWMediaCaption[];
} }
export interface MWMediaMeta extends MWPortableMedia { export interface MWMediaMeta extends MWPortableMedia {
title: string; title: string;
year: string; year: string;
seasonCount?: number; seasonCount?: number;
} }
export interface MWMediaEpisode { export interface MWMediaEpisode {
sort: number; sort: number;
id: string; id: string;
title: string; title: string;
} }
export interface MWMediaSeason { export interface MWMediaSeason {
sort: number; sort: number;
id: string; id: string;
title?: string; title?: string;
type: "season" | "special"; type: "season" | "special";
episodes: MWMediaEpisode[]; episodes: MWMediaEpisode[];
} }
export interface MWMediaSeasons { export interface MWMediaSeasons {
seasons: MWMediaSeason[]; seasons: MWMediaSeason[];
} }
export interface MWMedia extends MWMediaMeta { export interface MWMedia extends MWMediaMeta {
seriesData?: MWMediaSeasons; seriesData?: MWMediaSeasons;
} }
export type MWProviderMediaResult = Omit<MWMedia, "mediaType" | "providerId">; export type MWProviderMediaResult = Omit<MWMedia, "mediaType" | "providerId">;
export interface MWQuery { export interface MWQuery {
searchQuery: string; searchQuery: string;
type: MWMediaType; type: MWMediaType;
} }
export interface MWMediaProviderBase { export interface MWMediaProviderBase {
id: string; // id of provider, must be unique id: string; // id of provider, must be unique
enabled: boolean; enabled: boolean;
type: MWMediaType[]; type: MWMediaType[];
displayName: string; displayName: string;
getMediaFromPortable(media: MWPortableMedia): Promise<MWProviderMediaResult>; getMediaFromPortable(media: MWPortableMedia): Promise<MWProviderMediaResult>;
searchForMedia(query: MWQuery): Promise<MWProviderMediaResult[]>; searchForMedia(query: MWQuery): Promise<MWProviderMediaResult[]>;
getStream(media: MWPortableMedia): Promise<MWMediaStream>; getStream(media: MWPortableMedia): Promise<MWMediaStream>;
getSeasonDataFromMedia?: (media: MWPortableMedia) => Promise<MWMediaSeasons>; getSeasonDataFromMedia?: (media: MWPortableMedia) => Promise<MWMediaSeasons>;
} }
export type MWMediaProviderSeries = MWMediaProviderBase & { export type MWMediaProviderSeries = MWMediaProviderBase & {
getSeasonDataFromMedia: (media: MWPortableMedia) => Promise<MWMediaSeasons>; getSeasonDataFromMedia: (media: MWPortableMedia) => Promise<MWMediaSeasons>;
}; };
export type MWMediaProvider = MWMediaProviderBase; export type MWMediaProvider = MWMediaProviderBase;
export interface MWMediaProviderMetadata { export interface MWMediaProviderMetadata {
exists: boolean; exists: boolean;
id?: string; id?: string;
enabled: boolean; enabled: boolean;
type: MWMediaType[]; type: MWMediaType[];
provider?: MWMediaProvider; provider?: MWMediaProvider;
} }
export interface MWMassProviderOutput { export interface MWMassProviderOutput {
providers: { providers: {
id: string; id: string;
success: boolean; success: boolean;
}[]; }[];
results: MWMedia[]; results: MWMedia[];
stats: { stats: {
total: number; total: number;
failed: number; failed: number;
succeeded: number; succeeded: number;
}; };
} }

View file

@ -1,8 +1,8 @@
import i18n from 'i18next'; import i18n from "i18next";
import { initReactI18next } from 'react-i18next'; import { initReactI18next } from "react-i18next";
import Backend from 'i18next-http-backend'; import Backend from "i18next-http-backend";
import LanguageDetector from 'i18next-browser-languagedetector'; import LanguageDetector from "i18next-browser-languagedetector";
i18n i18n
// load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales) // load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
@ -17,12 +17,11 @@ i18n
// init i18next // init i18next
// for all options read: https://www.i18next.com/overview/configuration-options // for all options read: https://www.i18next.com/overview/configuration-options
.init({ .init({
fallbackLng: 'en-GB', fallbackLng: "en-GB",
interpolation: { interpolation: {
escapeValue: false, // not needed for react as it escapes by default escapeValue: false, // not needed for react as it escapes by default
} },
}); });
export default i18n;
export default i18n;

View file

@ -1 +1 @@
export * from "./context"; export * from "./context";

View file

@ -1,5 +1,6 @@
import { ReactElement, useEffect, useState } from "react"; import { ReactElement, useEffect, useState } from "react";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { IconPatch } from "@/components/buttons/IconPatch"; import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon"; import { Icons } from "@/components/Icon";
import { Navigation } from "@/components/layout/Navigation"; import { Navigation } from "@/components/layout/Navigation";
@ -29,7 +30,6 @@ import {
useBookmarkContext, useBookmarkContext,
} from "@/state/bookmark"; } from "@/state/bookmark";
import { getWatchedFromPortable, useWatchedContext } from "@/state/watched"; import { getWatchedFromPortable, useWatchedContext } from "@/state/watched";
import { useTranslation } from "react-i18next";
import { NotFoundChecks } from "./notfound/NotFoundChecks"; import { NotFoundChecks } from "./notfound/NotFoundChecks";
interface StyledMediaViewProps { interface StyledMediaViewProps {

View file

@ -1,10 +1,10 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { IconPatch } from "@/components/buttons/IconPatch"; import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon"; import { Icons } from "@/components/Icon";
import { Navigation } from "@/components/layout/Navigation"; import { Navigation } from "@/components/layout/Navigation";
import { ArrowLink } from "@/components/text/ArrowLink"; import { ArrowLink } from "@/components/text/ArrowLink";
import { Title } from "@/components/text/Title"; import { Title } from "@/components/text/Title";
import { useTranslation } from "react-i18next";
function NotFoundWrapper(props: { children?: ReactNode }) { function NotFoundWrapper(props: { children?: ReactNode }) {
return ( return (

View file

@ -1,3 +1,4 @@
import { useTranslation } from "react-i18next";
import { Icons } from "@/components/Icon"; import { Icons } from "@/components/Icon";
import { SectionHeading } from "@/components/layout/SectionHeading"; import { SectionHeading } from "@/components/layout/SectionHeading";
import { MediaGrid } from "@/components/media/MediaGrid"; import { MediaGrid } from "@/components/media/MediaGrid";
@ -7,7 +8,6 @@ import {
useBookmarkContext, useBookmarkContext,
} from "@/state/bookmark"; } from "@/state/bookmark";
import { useWatchedContext } from "@/state/watched"; import { useWatchedContext } from "@/state/watched";
import { useTranslation } from "react-i18next";
function Bookmarks() { function Bookmarks() {
const { t } = useTranslation(); const { t } = useTranslation();

View file

@ -1,5 +1,5 @@
import { Loading } from "@/components/layout/Loading";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Loading } from "@/components/layout/Loading";
export function SearchLoadingView() { export function SearchLoadingView() {
const { t } = useTranslation(); const { t } = useTranslation();

View file

@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from "react";
import { useDebounce } from "@/hooks/useDebounce"; import { useDebounce } from "@/hooks/useDebounce";
import { MWQuery } from "@/providers"; import { MWQuery } from "@/providers";
import { useEffect, useMemo, useState } from "react";
import { HomeView } from "./HomeView"; import { HomeView } from "./HomeView";
import { SearchLoadingView } from "./SearchLoadingView"; import { SearchLoadingView } from "./SearchLoadingView";
import { SearchResultsView } from "./SearchResultsView"; import { SearchResultsView } from "./SearchResultsView";

View file

@ -1,3 +1,5 @@
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { IconPatch } from "@/components/buttons/IconPatch"; import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon"; import { Icons } from "@/components/Icon";
import { SectionHeading } from "@/components/layout/SectionHeading"; import { SectionHeading } from "@/components/layout/SectionHeading";
@ -5,8 +7,6 @@ import { MediaGrid } from "@/components/media/MediaGrid";
import { WatchedMediaCard } from "@/components/media/WatchedMediaCard"; import { WatchedMediaCard } from "@/components/media/WatchedMediaCard";
import { useLoading } from "@/hooks/useLoading"; import { useLoading } from "@/hooks/useLoading";
import { MWMassProviderOutput, MWQuery, SearchProviders } from "@/providers"; import { MWMassProviderOutput, MWQuery, SearchProviders } from "@/providers";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { SearchLoadingView } from "./SearchLoadingView"; import { SearchLoadingView } from "./SearchLoadingView";
function SearchSuffix(props: { function SearchSuffix(props: {

View file

@ -1,12 +1,12 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import Sticky from "react-stickynode";
import { useTranslation } from "react-i18next";
import { Navigation } from "@/components/layout/Navigation"; import { Navigation } from "@/components/layout/Navigation";
import { ThinContainer } from "@/components/layout/ThinContainer"; import { ThinContainer } from "@/components/layout/ThinContainer";
import { SearchBarInput } from "@/components/SearchBar"; import { SearchBarInput } from "@/components/SearchBar";
import Sticky from "react-stickynode";
import { Title } from "@/components/text/Title"; import { Title } from "@/components/text/Title";
import { useSearchQuery } from "@/hooks/useSearchQuery"; import { useSearchQuery } from "@/hooks/useSearchQuery";
import { WideContainer } from "@/components/layout/WideContainer"; import { WideContainer } from "@/components/layout/WideContainer";
import { useTranslation } from "react-i18next";
import { SearchResultsPartial } from "./SearchResultsPartial"; import { SearchResultsPartial } from "./SearchResultsPartial";
export function SearchView() { export function SearchView() {