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",
tsconfigRootDir: "./",
},
settings: {
"import/resolver": {
typescript: {},
},
},
plugins: ["@typescript-eslint", "import"],
rules: {
"react/jsx-uses-react": "off",

View File

@ -26,7 +26,7 @@
"build": "vite build",
"preview": "vite preview",
"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"
},
"browserslist": {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,16 @@
import { ReactNode } from "react";
export interface PaperProps {
children?: ReactNode,
className?: string,
children?: ReactNode;
className?: string;
}
export function Paper(props: PaperProps) {
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}
</div>
)
);
}

View File

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

View File

@ -9,12 +9,12 @@ export function Episode(props: EpisodeProps) {
return (
<div
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 ${
props.active ? "shadow-bink-500 shadow-[inset_0_0_0_2px]" : ""
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-[inset_0_0_0_2px] shadow-bink-500" : ""
}`}
>
<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={{
width: `${props.progress || 0}%`,
}}

View File

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

View File

@ -16,22 +16,27 @@ interface ILinkPropsInternal extends ILinkPropsBase {
to: string;
}
type LinkProps =
| ILinkPropsExternal
| ILinkPropsInternal
| ILinkPropsBase;
type LinkProps = ILinkPropsExternal | ILinkPropsInternal | ILinkPropsBase;
export function Link(props: LinkProps) {
const isExternal = !!(props as ILinkPropsExternal).url;
const isInternal = !!(props as ILinkPropsInternal).to;
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}
</span>
);
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)
return (
<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
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(
() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
},
[value, delay]
);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}

View File

@ -1,7 +1,9 @@
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 [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
const fadeProps = {
style,
onAnimationEnd
onAnimationEnd,
};
return [isVisible, setShow, fadeProps];

View File

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

View File

@ -1,8 +1,8 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from "i18next-http-backend";
import LanguageDetector from "i18next-browser-languagedetector";
i18n
// 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
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
fallbackLng: 'en-GB',
fallbackLng: "en-GB",
interpolation: {
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 { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon";
import { Navigation } from "@/components/layout/Navigation";
@ -29,7 +30,6 @@ import {
useBookmarkContext,
} from "@/state/bookmark";
import { getWatchedFromPortable, useWatchedContext } from "@/state/watched";
import { useTranslation } from "react-i18next";
import { NotFoundChecks } from "./notfound/NotFoundChecks";
interface StyledMediaViewProps {

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from "react";
import { useDebounce } from "@/hooks/useDebounce";
import { MWQuery } from "@/providers";
import { useEffect, useMemo, useState } from "react";
import { HomeView } from "./HomeView";
import { SearchLoadingView } from "./SearchLoadingView";
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 { Icons } from "@/components/Icon";
import { SectionHeading } from "@/components/layout/SectionHeading";
@ -5,8 +7,6 @@ import { MediaGrid } from "@/components/media/MediaGrid";
import { WatchedMediaCard } from "@/components/media/WatchedMediaCard";
import { useLoading } from "@/hooks/useLoading";
import { MWMassProviderOutput, MWQuery, SearchProviders } from "@/providers";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { SearchLoadingView } from "./SearchLoadingView";
function SearchSuffix(props: {

View File

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