mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-29 16:07:40 +01:00
fix all eslint issues
Co-authored-by: William Oldham <wegg7250@gmail.com>
This commit is contained in:
parent
069c5271df
commit
0287bdad57
22 changed files with 384 additions and 323 deletions
14
.eslintrc.js
14
.eslintrc.js
|
@ -1,3 +1,6 @@
|
||||||
|
const a11yOff = Object.keys(require('eslint-plugin-jsx-a11y').rules)
|
||||||
|
.reduce((acc, rule) => { acc[`jsx-a11y/${rule}`] = 'off'; return acc }, {})
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
extends: [
|
extends: [
|
||||||
"airbnb",
|
"airbnb",
|
||||||
|
@ -24,6 +27,16 @@ module.exports = {
|
||||||
"react/react-in-jsx-scope": "off",
|
"react/react-in-jsx-scope": "off",
|
||||||
"react/require-default-props": "off",
|
"react/require-default-props": "off",
|
||||||
"react/destructuring-assignment": "off",
|
"react/destructuring-assignment": "off",
|
||||||
|
"no-underscore-dangle": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"no-console": "off",
|
||||||
|
"@typescript-eslint/no-this-alias": "off",
|
||||||
|
"import/prefer-default-export": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
"no-shadow": "off",
|
||||||
|
"@typescript-eslint/no-shadow": ["error"],
|
||||||
|
"no-restricted-syntax": "off",
|
||||||
|
"react/jsx-props-no-spreading": "off",
|
||||||
"react/jsx-filename-extension": [
|
"react/jsx-filename-extension": [
|
||||||
"error",
|
"error",
|
||||||
{ extensions: [".js", ".tsx", ".jsx"] },
|
{ extensions: [".js", ".tsx", ".jsx"] },
|
||||||
|
@ -36,5 +49,6 @@ module.exports = {
|
||||||
tsx: "never",
|
tsx: "never",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
...a11yOff
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
11
package.json
11
package.json
|
@ -7,6 +7,7 @@
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
"@testing-library/user-event": "^12.1.10",
|
"@testing-library/user-event": "^12.1.10",
|
||||||
|
"@types/react-router": "^5.1.18",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.0.0",
|
||||||
"fuse.js": "^6.4.6",
|
"fuse.js": "^6.4.6",
|
||||||
"hls.js": "^1.0.7",
|
"hls.js": "^1.0.7",
|
||||||
|
@ -23,13 +24,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"lint": "eslint src/**"
|
"lint": "eslint --ext .tsx,.ts src"
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"extends": [
|
|
||||||
"react-app",
|
|
||||||
"react-app/jest"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
|
@ -57,7 +52,7 @@
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-import-resolver-typescript": "^2.5.0",
|
"eslint-import-resolver-typescript": "^2.5.0",
|
||||||
"eslint-plugin-import": "^2.25.4",
|
"eslint-plugin-import": "^2.25.4",
|
||||||
"eslint-plugin-jsx-a11y": "6.5.1",
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
"eslint-plugin-react": "7.28.0",
|
"eslint-plugin-react": "7.28.0",
|
||||||
"eslint-plugin-react-hooks": "4.3.0",
|
"eslint-plugin-react-hooks": "4.3.0",
|
||||||
"postcss": "^8.4.6",
|
"postcss": "^8.4.6",
|
||||||
|
|
|
@ -4,6 +4,14 @@ export interface ButtonControlProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ButtonControl({ onClick, children, className }: ButtonControlProps) {
|
export function ButtonControl({
|
||||||
return <button onClick={onClick} className={className}>{children}</button>
|
onClick,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: ButtonControlProps) {
|
||||||
|
return (
|
||||||
|
<button onClick={onClick} className={className} type="button">
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,13 @@ import React, {
|
||||||
import { Backdrop, useBackdrop } from "components/layout/Backdrop";
|
import { Backdrop, useBackdrop } from "components/layout/Backdrop";
|
||||||
import { ButtonControlProps, ButtonControl } from "./ButtonControl";
|
import { ButtonControlProps, ButtonControl } from "./ButtonControl";
|
||||||
|
|
||||||
export interface DropdownButtonProps extends ButtonControlProps {
|
export interface OptionItem {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
icon: Icons;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DropdownButtonProps extends ButtonControlProps {
|
||||||
icon: Icons;
|
icon: Icons;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setOpen: (open: boolean) => void;
|
setOpen: (open: boolean) => void;
|
||||||
|
@ -24,12 +30,6 @@ export interface OptionProps {
|
||||||
tabIndex?: number;
|
tabIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OptionItem {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
icon: Icons;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Option({ option, onClick, tabIndex }: OptionProps) {
|
function Option({ option, onClick, tabIndex }: OptionProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -49,7 +49,7 @@ function Option({ option, onClick, tabIndex }: OptionProps) {
|
||||||
export const DropdownButton = React.forwardRef<
|
export const DropdownButton = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
DropdownButtonProps
|
DropdownButtonProps
|
||||||
>((props, ref) => {
|
>((props: DropdownButtonProps, ref) => {
|
||||||
const [setBackdrop, backdropProps, highlightedProps] = useBackdrop();
|
const [setBackdrop, backdropProps, highlightedProps] = useBackdrop();
|
||||||
const [delayedSelectedId, setDelayedSelectedId] = useState(
|
const [delayedSelectedId, setDelayedSelectedId] = useState(
|
||||||
props.selectedItem
|
props.selectedItem
|
||||||
|
|
|
@ -42,7 +42,7 @@ const iconList: Record<Icons, string> = {
|
||||||
export function Icon(props: IconProps) {
|
export function Icon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
dangerouslySetInnerHTML={{ __html: iconList[props.icon] }}
|
dangerouslySetInnerHTML={{ __html: iconList[props.icon] }} // eslint-disable-line react/no-danger
|
||||||
className={props.className}
|
className={props.className}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { DropdownButton } from "./buttons/DropdownButton";
|
||||||
import { Icons } from "./Icon";
|
import { Icons } from "./Icon";
|
||||||
import { TextInputControl } from "./text-inputs/TextInputControl";
|
import { TextInputControl } from "./text-inputs/TextInputControl";
|
||||||
|
|
||||||
|
|
||||||
export interface SearchBarProps {
|
export interface SearchBarProps {
|
||||||
buttonText?: string;
|
buttonText?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
@ -30,7 +29,7 @@ export function SearchBarInput(props: SearchBarProps) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-denim-300 hover:bg-denim-400 focus-within:bg-denim-400 flex flex-col items-center gap-4 rounded-[28px] px-4 py-4 transition-colors sm:flex-row sm:py-2 sm:pl-8 sm:pr-2">
|
<div className="bg-denim-300 hover:bg-denim-400 focus-within:bg-denim-400 flex flex-col items-center gap-4 rounded-[28px] px-4 py-4 transition-colors sm:flex-row sm:py-2 sm:pl-8 sm:pr-2">
|
||||||
<TextInputControl
|
<TextInputControl
|
||||||
onChange={setSearch}
|
onChange={(val) => setSearch(val)}
|
||||||
value={props.value.searchQuery}
|
value={props.value.searchQuery}
|
||||||
className="placeholder-denim-700 w-full flex-1 bg-transparent text-white focus:outline-none"
|
className="placeholder-denim-700 w-full flex-1 bg-transparent text-white focus:outline-none"
|
||||||
placeholder={props.placeholder}
|
placeholder={props.placeholder}
|
||||||
|
@ -39,9 +38,9 @@ export function SearchBarInput(props: SearchBarProps) {
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
icon={Icons.SEARCH}
|
icon={Icons.SEARCH}
|
||||||
open={dropdownOpen}
|
open={dropdownOpen}
|
||||||
setOpen={setDropdownOpen}
|
setOpen={(val) => setDropdownOpen(val)}
|
||||||
selectedItem={props.value.type}
|
selectedItem={props.value.type}
|
||||||
setSelectedItem={setType}
|
setSelectedItem={(val) => setType(val)}
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
id: MWMediaType.MOVIE,
|
id: MWMediaType.MOVIE,
|
||||||
|
|
|
@ -40,7 +40,7 @@ export function useBackdrop(): [
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Backdrop(props: BackdropProps) {
|
export function Backdrop(props: BackdropProps) {
|
||||||
const clickEvent = props.onClick || ((e: MouseEvent) => {});
|
const clickEvent = props.onClick || (() => {});
|
||||||
const animationEvent = props.onBackdropHide || (() => {});
|
const animationEvent = props.onBackdropHide || (() => {});
|
||||||
const [isVisible, setVisible, fadeProps] = useFade();
|
const [isVisible, setVisible, fadeProps] = useFade();
|
||||||
|
|
||||||
|
|
|
@ -14,10 +14,16 @@ interface ErrorBoundaryState {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ErrorBoundary extends Component<{}, ErrorBoundaryState> {
|
export class ErrorBoundary extends Component<
|
||||||
state: ErrorBoundaryState = {
|
Record<string, unknown>,
|
||||||
|
ErrorBoundaryState
|
||||||
|
> {
|
||||||
|
constructor() {
|
||||||
|
super({});
|
||||||
|
this.state = {
|
||||||
hasError: false,
|
hasError: false,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static getDerivedStateFromError() {
|
static getDerivedStateFromError() {
|
||||||
return {
|
return {
|
||||||
|
@ -50,8 +56,16 @@ export class ErrorBoundary extends Component<{}, ErrorBoundaryState> {
|
||||||
<IconPatch icon={Icons.WARNING} className="mb-6 text-red-400" />
|
<IconPatch icon={Icons.WARNING} className="mb-6 text-red-400" />
|
||||||
<Title>Whoops, it broke</Title>
|
<Title>Whoops, it broke</Title>
|
||||||
<p className="my-6 max-w-lg">
|
<p className="my-6 max-w-lg">
|
||||||
The app encountered an error and wasn't able to recover, please
|
The app encountered an error and wasn't able to recover, please
|
||||||
report it to the <Link url={DISCORD_LINK} newTab>Discord server</Link> or on <Link url={GITHUB_LINK} newTab>GitHub</Link>.
|
report it to the{" "}
|
||||||
|
<Link url={DISCORD_LINK} newTab>
|
||||||
|
Discord server
|
||||||
|
</Link>{" "}
|
||||||
|
or on{" "}
|
||||||
|
<Link url={GITHUB_LINK} newTab>
|
||||||
|
GitHub
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{this.state.error ? (
|
{this.state.error ? (
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { IconPatch } from "components/buttons/IconPatch";
|
||||||
import { Icons } from "components/Icon";
|
import { Icons } from "components/Icon";
|
||||||
import { Loading } from "components/layout/Loading";
|
import { Loading } from "components/layout/Loading";
|
||||||
import { MWMediaStream } from "providers";
|
import { MWMediaStream } from "providers";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { ReactElement, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
export interface VideoPlayerProps {
|
export interface VideoPlayerProps {
|
||||||
source: MWMediaStream;
|
source: MWMediaStream;
|
||||||
|
@ -16,7 +16,7 @@ export function SkeletonVideoPlayer(props: { error?: boolean }) {
|
||||||
{props.error ? (
|
{props.error ? (
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<IconPatch icon={Icons.WARNING} className="text-red-400" />
|
<IconPatch icon={Icons.WARNING} className="text-red-400" />
|
||||||
<p className="mt-5 text-white">Couldn't get your stream</p>
|
<p className="mt-5 text-white">Couldn't get your stream</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
|
@ -41,13 +41,16 @@ export function VideoPlayer(props: VideoPlayerProps) {
|
||||||
setErrored(false);
|
setErrored(false);
|
||||||
}, [props.source.url]);
|
}, [props.source.url]);
|
||||||
|
|
||||||
|
let skeletonUi: null | ReactElement = null;
|
||||||
|
if (hasErrored) {
|
||||||
|
skeletonUi = <SkeletonVideoPlayer error />;
|
||||||
|
} else if (isLoading) {
|
||||||
|
skeletonUi = <SkeletonVideoPlayer />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{hasErrored ? (
|
{skeletonUi}
|
||||||
<SkeletonVideoPlayer error />
|
|
||||||
) : isLoading ? (
|
|
||||||
<SkeletonVideoPlayer />
|
|
||||||
) : null}
|
|
||||||
<video
|
<video
|
||||||
className={`bg-denim-500 w-full rounded-xl ${
|
className={`bg-denim-500 w-full rounded-xl ${
|
||||||
!showVideo ? "hidden" : ""
|
!showVideo ? "hidden" : ""
|
||||||
|
|
|
@ -30,6 +30,7 @@ export function useLoading<T extends (...args: any) => Promise<any>>(
|
||||||
if (!isMounted.current) return resolve(undefined);
|
if (!isMounted.current) return resolve(undefined);
|
||||||
setSuccess(true);
|
setSuccess(true);
|
||||||
resolve(v);
|
resolve(v);
|
||||||
|
return null;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (isMounted) {
|
if (isMounted) {
|
||||||
|
|
|
@ -2,6 +2,15 @@ import { MWPortableMedia } from "providers";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useParams } from "react-router";
|
import { useParams } from "react-router";
|
||||||
|
|
||||||
|
export function deserializePortableMedia(media: string): MWPortableMedia {
|
||||||
|
return JSON.parse(atob(decodeURIComponent(media)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serializePortableMedia(media: MWPortableMedia): string {
|
||||||
|
const data = encodeURIComponent(btoa(JSON.stringify(media)));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
export function usePortableMedia(): MWPortableMedia | undefined {
|
export function usePortableMedia(): MWPortableMedia | undefined {
|
||||||
const { media } = useParams<{ media: string }>();
|
const { media } = useParams<{ media: string }>();
|
||||||
const [mediaObject, setMediaObject] = useState<MWPortableMedia | undefined>(
|
const [mediaObject, setMediaObject] = useState<MWPortableMedia | undefined>(
|
||||||
|
@ -19,12 +28,3 @@ export function usePortableMedia(): MWPortableMedia | undefined {
|
||||||
|
|
||||||
return mediaObject;
|
return mediaObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deserializePortableMedia(media: string): MWPortableMedia {
|
|
||||||
return JSON.parse(atob(decodeURIComponent(media)));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serializePortableMedia(media: MWPortableMedia): string {
|
|
||||||
const data = encodeURIComponent(btoa(JSON.stringify(media)));
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
import { getProviderFromId } from "./methods/helpers";
|
import { getProviderFromId } from "./methods/helpers";
|
||||||
import {
|
import { MWMedia, MWPortableMedia, MWMediaStream } from "./types";
|
||||||
MWMedia,
|
|
||||||
MWPortableMedia,
|
|
||||||
MWMediaStream,
|
|
||||||
} from "./types";
|
|
||||||
import contentCache from "./methods/contentCache";
|
import contentCache from "./methods/contentCache";
|
||||||
|
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
|
@ -35,7 +31,7 @@ export async function convertPortableToMedia(
|
||||||
if (output) return output;
|
if (output) return output;
|
||||||
|
|
||||||
const provider = getProviderFromId(portable.providerId);
|
const provider = getProviderFromId(portable.providerId);
|
||||||
return await provider?.getMediaFromPortable(portable);
|
return provider?.getMediaFromPortable(portable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -47,5 +43,5 @@ export async function getStream(
|
||||||
const provider = getProviderFromId(media.providerId);
|
const provider = getProviderFromId(media.providerId);
|
||||||
if (!provider) return undefined;
|
if (!provider) return undefined;
|
||||||
|
|
||||||
return await provider.getStream(media);
|
return provider.getStream(media);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@ import { MWMediaType, MWPortableMedia } from "providers/types";
|
||||||
const getTheFlixUrl = (media: MWPortableMedia, params?: URLSearchParams) => {
|
const getTheFlixUrl = (media: MWPortableMedia, params?: URLSearchParams) => {
|
||||||
if (media.mediaType === MWMediaType.MOVIE) {
|
if (media.mediaType === MWMediaType.MOVIE) {
|
||||||
return `https://theflix.to/movie/${media.mediaId}?${params}`;
|
return `https://theflix.to/movie/${media.mediaId}?${params}`;
|
||||||
} if (media.mediaType === MWMediaType.SERIES) {
|
}
|
||||||
|
if (media.mediaType === MWMediaType.SERIES) {
|
||||||
return `https://theflix.to/tv-show/${media.mediaId}/season-${media.season}/episode-${media.episode}`;
|
return `https://theflix.to/tv-show/${media.mediaId}/season-${media.season}/episode-${media.episode}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ export async function getDataFromPortableSearch(
|
||||||
|
|
||||||
if (media.mediaType === MWMediaType.MOVIE) {
|
if (media.mediaType === MWMediaType.MOVIE) {
|
||||||
return JSON.parse(node.innerHTML).props.pageProps.movie;
|
return JSON.parse(node.innerHTML).props.pageProps.movie;
|
||||||
} if (media.mediaType === MWMediaType.SERIES) {
|
|
||||||
return JSON.parse(node.innerHTML).props.pageProps.selectedTv;
|
|
||||||
}
|
}
|
||||||
|
// must be series here
|
||||||
|
return JSON.parse(node.innerHTML).props.pageProps.selectedTv;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@ import { MWMediaType, MWProviderMediaResult, MWQuery } from "providers";
|
||||||
const getTheFlixUrl = (type: "tv-shows" | "movies", params: URLSearchParams) =>
|
const getTheFlixUrl = (type: "tv-shows" | "movies", params: URLSearchParams) =>
|
||||||
`https://theflix.to/${type}/trending?${params}`;
|
`https://theflix.to/${type}/trending?${params}`;
|
||||||
|
|
||||||
export async function searchTheFlix(query: MWQuery): Promise<string> {
|
export function searchTheFlix(query: MWQuery): Promise<string> {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.append("search", query.searchQuery);
|
params.append("search", query.searchQuery);
|
||||||
return await fetch(
|
return fetch(
|
||||||
CORS_PROXY_URL +
|
CORS_PROXY_URL +
|
||||||
getTheFlixUrl(
|
getTheFlixUrl(
|
||||||
query.type === MWMediaType.MOVIE ? "movies" : "tv-shows",
|
query.type === MWMediaType.MOVIE ? "movies" : "tv-shows",
|
||||||
|
@ -30,10 +30,7 @@ export function getDataFromSearch(page: string, limit = 10): any[] {
|
||||||
|
|
||||||
export function turnDataIntoMedia(data: any): MWProviderMediaResult {
|
export function turnDataIntoMedia(data: any): MWProviderMediaResult {
|
||||||
return {
|
return {
|
||||||
mediaId:
|
mediaId: `${data.id}-${data.name
|
||||||
`${data.id
|
|
||||||
}-${
|
|
||||||
data.name
|
|
||||||
.replace(/[^a-z0-9]+|\s+/gim, " ")
|
.replace(/[^a-z0-9]+|\s+/gim, " ")
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/\s+/g, "-")
|
.replace(/\s+/g, "-")
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import { MWMassProviderOutput, MWMedia, MWQuery, convertMediaToPortable } from "providers";
|
import {
|
||||||
|
MWMassProviderOutput,
|
||||||
|
MWMedia,
|
||||||
|
MWQuery,
|
||||||
|
convertMediaToPortable,
|
||||||
|
} from "providers";
|
||||||
import { SimpleCache } from "utils/cache";
|
import { SimpleCache } from "utils/cache";
|
||||||
import { GetProvidersForType } from "./helpers";
|
import { GetProvidersForType } from "./helpers";
|
||||||
import contentCache from "./contentCache";
|
import contentCache from "./contentCache";
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
const resultCache = new SimpleCache<MWQuery, MWMassProviderOutput>();
|
const resultCache = new SimpleCache<MWQuery, MWMassProviderOutput>();
|
||||||
resultCache.setCompare((a,b) => a.searchQuery === b.searchQuery && a.type === b.type);
|
resultCache.setCompare(
|
||||||
|
(a, b) => a.searchQuery === b.searchQuery && a.type === b.type
|
||||||
|
);
|
||||||
resultCache.initialize();
|
resultCache.initialize();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** actually call all providers with the search query
|
** actually call all providers with the search query
|
||||||
*/
|
*/
|
||||||
async function callProviders(
|
async function callProviders(query: MWQuery): Promise<MWMassProviderOutput> {
|
||||||
query: MWQuery
|
|
||||||
): Promise<MWMassProviderOutput> {
|
|
||||||
const allQueries = GetProvidersForType(query.type).map<
|
const allQueries = GetProvidersForType(query.type).map<
|
||||||
Promise<{ media: MWMedia[]; success: boolean; id: string }>
|
Promise<{ media: MWMedia[]; success: boolean; id: string }>
|
||||||
>(async (provider) => {
|
>(async (provider) => {
|
||||||
|
@ -55,33 +60,37 @@ async function callProviders(
|
||||||
|
|
||||||
output.results.forEach((result: MWMedia) => {
|
output.results.forEach((result: MWMedia) => {
|
||||||
contentCache.set(convertMediaToPortable(result), result, 60 * 60);
|
contentCache.set(convertMediaToPortable(result), result, 60 * 60);
|
||||||
})
|
});
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** sort results based on query
|
** sort results based on query
|
||||||
*/
|
*/
|
||||||
function sortResults(query: MWQuery, providerResults: MWMassProviderOutput): MWMassProviderOutput {
|
function sortResults(
|
||||||
const fuse = new Fuse(providerResults.results, { threshold: 0.3, keys: ["title"] });
|
query: MWQuery,
|
||||||
providerResults.results = fuse.search(query.searchQuery).map((v) => v.item);
|
providerResults: MWMassProviderOutput
|
||||||
return providerResults;
|
): MWMassProviderOutput {
|
||||||
|
const results: MWMassProviderOutput = { ...providerResults };
|
||||||
|
const fuse = new Fuse(results.results, { threshold: 0.3, keys: ["title"] });
|
||||||
|
results.results = fuse.search(query.searchQuery).map((v) => v.item);
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Call search on all providers that matches query type
|
** Call search on all providers that matches query type
|
||||||
*/
|
*/
|
||||||
export async function SearchProviders(
|
export async function SearchProviders(
|
||||||
query: MWQuery
|
inputQuery: MWQuery
|
||||||
): Promise<MWMassProviderOutput> {
|
): Promise<MWMassProviderOutput> {
|
||||||
// input normalisation
|
// input normalisation
|
||||||
|
const query = { ...inputQuery };
|
||||||
query.searchQuery = query.searchQuery.toLowerCase().trim();
|
query.searchQuery = query.searchQuery.toLowerCase().trim();
|
||||||
|
|
||||||
// consult cache first
|
// consult cache first
|
||||||
let output = resultCache.get(query);
|
let output = resultCache.get(query);
|
||||||
if (!output)
|
if (!output) output = await callProviders(query);
|
||||||
output = await callProviders(query);
|
|
||||||
|
|
||||||
// sort results
|
// sort results
|
||||||
output = sortResults(query, output);
|
output = sortResults(query, output);
|
||||||
|
|
|
@ -23,7 +23,7 @@ export interface MWMediaMeta extends MWPortableMedia {
|
||||||
year: string;
|
year: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MWMedia = MWMediaMeta
|
export type MWMedia = MWMediaMeta;
|
||||||
|
|
||||||
export type MWProviderMediaResult = Omit<MWMedia, "mediaType" | "providerId">;
|
export type MWProviderMediaResult = Omit<MWMedia, "mediaType" | "providerId">;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import { getProviderMetadata, MWMediaMeta } from "providers";
|
import { getProviderMetadata, MWMediaMeta } from "providers";
|
||||||
import { createContext, ReactNode, useContext, useState } from "react";
|
import {
|
||||||
|
createContext,
|
||||||
|
ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { BookmarkStore } from "./store";
|
import { BookmarkStore } from "./store";
|
||||||
|
|
||||||
interface BookmarkStoreData {
|
interface BookmarkStoreData {
|
||||||
|
@ -20,13 +27,28 @@ const BookmarkedContext = createContext<BookmarkStoreDataWrapper>({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getBookmarkIndexFromMedia(
|
||||||
|
bookmarks: MWMediaMeta[],
|
||||||
|
media: MWMediaMeta
|
||||||
|
): number {
|
||||||
|
const a = bookmarks.findIndex(
|
||||||
|
(v) =>
|
||||||
|
v.mediaId === media.mediaId &&
|
||||||
|
v.providerId === media.providerId &&
|
||||||
|
v.episode === media.episode &&
|
||||||
|
v.season === media.season
|
||||||
|
);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
export function BookmarkContextProvider(props: { children: ReactNode }) {
|
export function BookmarkContextProvider(props: { children: ReactNode }) {
|
||||||
const bookmarkLocalstorage = BookmarkStore.get();
|
const bookmarkLocalstorage = BookmarkStore.get();
|
||||||
const [bookmarkStorage, setBookmarkStore] = useState<BookmarkStoreData>(
|
const [bookmarkStorage, setBookmarkStore] = useState<BookmarkStoreData>(
|
||||||
bookmarkLocalstorage as BookmarkStoreData
|
bookmarkLocalstorage as BookmarkStoreData
|
||||||
);
|
);
|
||||||
|
|
||||||
function setBookmarked(data: any) {
|
const setBookmarked = useCallback(
|
||||||
|
(data: any) => {
|
||||||
setBookmarkStore((old) => {
|
setBookmarkStore((old) => {
|
||||||
const old2 = JSON.parse(JSON.stringify(old));
|
const old2 = JSON.parse(JSON.stringify(old));
|
||||||
let newData = data;
|
let newData = data;
|
||||||
|
@ -36,9 +58,12 @@ export function BookmarkContextProvider(props: { children: ReactNode }) {
|
||||||
bookmarkLocalstorage.save(newData);
|
bookmarkLocalstorage.save(newData);
|
||||||
return newData;
|
return newData;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
[bookmarkLocalstorage, setBookmarkStore]
|
||||||
|
);
|
||||||
|
|
||||||
const contextValue = {
|
const contextValue = useMemo(
|
||||||
|
() => ({
|
||||||
setItemBookmark(media: MWMediaMeta, bookmarked: boolean) {
|
setItemBookmark(media: MWMediaMeta, bookmarked: boolean) {
|
||||||
setBookmarked((data: BookmarkStoreData) => {
|
setBookmarked((data: BookmarkStoreData) => {
|
||||||
if (bookmarked) {
|
if (bookmarked) {
|
||||||
|
@ -65,10 +90,14 @@ export function BookmarkContextProvider(props: { children: ReactNode }) {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getFilteredBookmarks() {
|
getFilteredBookmarks() {
|
||||||
return bookmarkStorage.bookmarks.filter((bookmark) => getProviderMetadata(bookmark.providerId)?.enabled);
|
return bookmarkStorage.bookmarks.filter(
|
||||||
|
(bookmark) => getProviderMetadata(bookmark.providerId)?.enabled
|
||||||
|
);
|
||||||
},
|
},
|
||||||
bookmarkStore: bookmarkStorage,
|
bookmarkStore: bookmarkStorage,
|
||||||
};
|
}),
|
||||||
|
[bookmarkStorage, setBookmarked]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BookmarkedContext.Provider value={contextValue}>
|
<BookmarkedContext.Provider value={contextValue}>
|
||||||
|
@ -81,19 +110,6 @@ export function useBookmarkContext() {
|
||||||
return useContext(BookmarkedContext);
|
return useContext(BookmarkedContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBookmarkIndexFromMedia(
|
|
||||||
bookmarks: MWMediaMeta[],
|
|
||||||
media: MWMediaMeta
|
|
||||||
): number {
|
|
||||||
const a = bookmarks.findIndex((v) => (
|
|
||||||
v.mediaId === media.mediaId &&
|
|
||||||
v.providerId === media.providerId &&
|
|
||||||
v.episode === media.episode &&
|
|
||||||
v.season === media.season
|
|
||||||
));
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getIfBookmarkedFromPortable(
|
export function getIfBookmarkedFromPortable(
|
||||||
bookmarks: MWMediaMeta[],
|
bookmarks: MWMediaMeta[],
|
||||||
media: MWMediaMeta
|
media: MWMediaMeta
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import { MWMediaMeta, getProviderMetadata } from "providers";
|
import { MWMediaMeta, getProviderMetadata } from "providers";
|
||||||
import React, { createContext, ReactNode, useContext, useState } from "react";
|
import React, {
|
||||||
|
createContext,
|
||||||
|
ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { VideoProgressStore } from "./store";
|
import { VideoProgressStore } from "./store";
|
||||||
|
|
||||||
interface WatchedStoreItem extends MWMediaMeta {
|
interface WatchedStoreItem extends MWMediaMeta {
|
||||||
|
@ -17,6 +24,19 @@ interface WatchedStoreDataWrapper {
|
||||||
watched: WatchedStoreData;
|
watched: WatchedStoreData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getWatchedFromPortable(
|
||||||
|
items: WatchedStoreItem[],
|
||||||
|
media: MWMediaMeta
|
||||||
|
): WatchedStoreItem | undefined {
|
||||||
|
return items.find(
|
||||||
|
(v) =>
|
||||||
|
v.mediaId === media.mediaId &&
|
||||||
|
v.providerId === media.providerId &&
|
||||||
|
v.episode === media.episode &&
|
||||||
|
v.season === media.season
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const WatchedContext = createContext<WatchedStoreDataWrapper>({
|
const WatchedContext = createContext<WatchedStoreDataWrapper>({
|
||||||
updateProgress: () => {},
|
updateProgress: () => {},
|
||||||
getFilteredWatched: () => [],
|
getFilteredWatched: () => [],
|
||||||
|
@ -32,7 +52,8 @@ export function WatchedContextProvider(props: { children: ReactNode }) {
|
||||||
watchedLocalstorage as WatchedStoreData
|
watchedLocalstorage as WatchedStoreData
|
||||||
);
|
);
|
||||||
|
|
||||||
function setWatched(data: any) {
|
const setWatched = useCallback(
|
||||||
|
(data: any) => {
|
||||||
setWatchedReal((old) => {
|
setWatchedReal((old) => {
|
||||||
let newData = data;
|
let newData = data;
|
||||||
if (data.constructor === Function) {
|
if (data.constructor === Function) {
|
||||||
|
@ -41,10 +62,17 @@ export function WatchedContextProvider(props: { children: ReactNode }) {
|
||||||
watchedLocalstorage.save(newData);
|
watchedLocalstorage.save(newData);
|
||||||
return newData;
|
return newData;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
[setWatchedReal, watchedLocalstorage]
|
||||||
|
);
|
||||||
|
|
||||||
const contextValue = {
|
const contextValue = useMemo(
|
||||||
updateProgress(media: MWMediaMeta, progress: number, total: number): void {
|
() => ({
|
||||||
|
updateProgress(
|
||||||
|
media: MWMediaMeta,
|
||||||
|
progress: number,
|
||||||
|
total: number
|
||||||
|
): void {
|
||||||
setWatched((data: WatchedStoreData) => {
|
setWatched((data: WatchedStoreData) => {
|
||||||
let item = getWatchedFromPortable(data.items, media);
|
let item = getWatchedFromPortable(data.items, media);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
|
@ -70,10 +98,14 @@ export function WatchedContextProvider(props: { children: ReactNode }) {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getFilteredWatched() {
|
getFilteredWatched() {
|
||||||
return watched.items.filter((item) => getProviderMetadata(item.providerId)?.enabled);
|
return watched.items.filter(
|
||||||
|
(item) => getProviderMetadata(item.providerId)?.enabled
|
||||||
|
);
|
||||||
},
|
},
|
||||||
watched,
|
watched,
|
||||||
};
|
}),
|
||||||
|
[watched, setWatched]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WatchedContext.Provider value={contextValue}>
|
<WatchedContext.Provider value={contextValue}>
|
||||||
|
@ -85,15 +117,3 @@ export function WatchedContextProvider(props: { children: ReactNode }) {
|
||||||
export function useWatchedContext() {
|
export function useWatchedContext() {
|
||||||
return useContext(WatchedContext);
|
return useContext(WatchedContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWatchedFromPortable(
|
|
||||||
items: WatchedStoreItem[],
|
|
||||||
media: MWMediaMeta
|
|
||||||
): WatchedStoreItem | undefined {
|
|
||||||
return items.find((v) => (
|
|
||||||
v.mediaId === media.mediaId &&
|
|
||||||
v.providerId === media.providerId &&
|
|
||||||
v.episode === media.episode &&
|
|
||||||
v.season === media.season
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,7 +11,8 @@ function buildStoreObject(d: any) {
|
||||||
id: d.storageString,
|
id: d.storageString,
|
||||||
};
|
};
|
||||||
|
|
||||||
function update(this: any, obj: any) {
|
function update(this: any, obj2: any) {
|
||||||
|
let obj = obj2;
|
||||||
if (!obj) throw new Error("object to update is not an object");
|
if (!obj) throw new Error("object to update is not an object");
|
||||||
|
|
||||||
// repeat until object fully updated
|
// repeat until object fully updated
|
||||||
|
@ -53,54 +54,54 @@ function buildStoreObject(d: any) {
|
||||||
function get(this: any) {
|
function get(this: any) {
|
||||||
// get from storage api
|
// get from storage api
|
||||||
const store = this;
|
const store = this;
|
||||||
let data: any = localStorage.getItem(this.id);
|
let gottenData: any = localStorage.getItem(this.id);
|
||||||
|
|
||||||
// parse json if item exists
|
// parse json if item exists
|
||||||
if (data) {
|
if (gottenData) {
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(data);
|
gottenData = JSON.parse(gottenData);
|
||||||
if (!data.constructor) {
|
if (!gottenData.constructor) {
|
||||||
console.error(
|
console.error(
|
||||||
`Storage item for store ${this.id} has not constructor`
|
`Storage item for store ${this.id} has not constructor`
|
||||||
);
|
);
|
||||||
throw new Error("storage item has no constructor");
|
throw new Error("storage item has no constructor");
|
||||||
}
|
}
|
||||||
if (data.constructor !== Object) {
|
if (gottenData.constructor !== Object) {
|
||||||
console.error(`Storage item for store ${this.id} is not an object`);
|
console.error(`Storage item for store ${this.id} is not an object`);
|
||||||
throw new Error("storage item is not an object");
|
throw new Error("storage item is not an object");
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// if errored, set to null so it generates new one, see below
|
// if errored, set to null so it generates new one, see below
|
||||||
console.error(`Failed to parse storage item for store ${this.id}`);
|
console.error(`Failed to parse storage item for store ${this.id}`);
|
||||||
data = null;
|
gottenData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if item doesnt exist, generate from version init
|
// if item doesnt exist, generate from version init
|
||||||
if (!data) {
|
if (!gottenData) {
|
||||||
data = this.versions[this.currentVersion.toString()].init();
|
gottenData = this.versions[this.currentVersion.toString()].init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the data if needed
|
// update the data if needed
|
||||||
data = this.update(data);
|
gottenData = this.update(gottenData);
|
||||||
|
|
||||||
// add a save object to return value
|
// add a save object to return value
|
||||||
data.save = function save(newData: any) {
|
gottenData.save = function save(newData: any) {
|
||||||
const dataToStore = newData || data;
|
const dataToStore = newData || gottenData;
|
||||||
localStorage.setItem(store.id, JSON.stringify(dataToStore));
|
localStorage.setItem(store.id, JSON.stringify(dataToStore));
|
||||||
};
|
};
|
||||||
|
|
||||||
// add instance helpers
|
// add instance helpers
|
||||||
Object.entries(d.instanceHelpers).forEach(([name, helper]: any) => {
|
Object.entries(d.instanceHelpers).forEach(([name, helper]: any) => {
|
||||||
if (data[name] !== undefined)
|
if (gottenData[name] !== undefined)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`helper name: ${name} on instance of store ${this.id} is reserved`
|
`helper name: ${name} on instance of store ${this.id} is reserved`
|
||||||
);
|
);
|
||||||
data[name] = helper.bind(data);
|
gottenData[name] = helper.bind(gottenData);
|
||||||
});
|
});
|
||||||
|
|
||||||
// return data
|
// return data
|
||||||
return data;
|
return gottenData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add functions to store
|
// add functions to store
|
||||||
|
@ -158,7 +159,7 @@ export function versionedStoreBuilder(): any {
|
||||||
? (data: any) => {
|
? (data: any) => {
|
||||||
// update function, and increment version
|
// update function, and increment version
|
||||||
migrate(data);
|
migrate(data);
|
||||||
data["--version"] = version;
|
data["--version"] = version; // eslint-disable-line no-param-reassign
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
@ -176,7 +177,8 @@ export function versionedStoreBuilder(): any {
|
||||||
|
|
||||||
registerHelper({ name, helper, type }: any) {
|
registerHelper({ name, helper, type }: any) {
|
||||||
// type
|
// type
|
||||||
if (!type) type = "instance";
|
let helperType: string = type;
|
||||||
|
if (!helperType) helperType = "instance";
|
||||||
|
|
||||||
// input checking
|
// input checking
|
||||||
if (!name || name.constructor !== String) {
|
if (!name || name.constructor !== String) {
|
||||||
|
@ -185,14 +187,14 @@ export function versionedStoreBuilder(): any {
|
||||||
if (!helper || helper.constructor !== Function) {
|
if (!helper || helper.constructor !== Function) {
|
||||||
throw new Error("helper function is not a function");
|
throw new Error("helper function is not a function");
|
||||||
}
|
}
|
||||||
if (!["instance", "static"].includes(type)) {
|
if (!["instance", "static"].includes(helperType)) {
|
||||||
throw new Error("helper type must be either 'instance' or 'static'");
|
throw new Error("helper type must be either 'instance' or 'static'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// register helper
|
// register helper
|
||||||
if (type === "instance")
|
if (helperType === "instance")
|
||||||
this._data.instanceHelpers[name as string] = helper;
|
this._data.instanceHelpers[name as string] = helper;
|
||||||
else if (type === "static")
|
else if (helperType === "static")
|
||||||
this._data.staticHelpers[name as string] = helper;
|
this._data.staticHelpers[name as string] = helper;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
getProviderFromId,
|
getProviderFromId,
|
||||||
MWMediaProvider,
|
MWMediaProvider,
|
||||||
} from "providers";
|
} from "providers";
|
||||||
import { ReactNode, useEffect, useState } from "react";
|
import { ReactElement, useEffect, useState } from "react";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
getIfBookmarkedFromPortable,
|
getIfBookmarkedFromPortable,
|
||||||
|
@ -57,7 +57,7 @@ function StyledMediaView(props: StyledMediaViewProps) {
|
||||||
<>
|
<>
|
||||||
<VideoPlayer
|
<VideoPlayer
|
||||||
source={props.stream}
|
source={props.stream}
|
||||||
onProgress={updateProgress}
|
onProgress={(e) => updateProgress(e)}
|
||||||
startAt={startAtTime}
|
startAt={startAtTime}
|
||||||
/>
|
/>
|
||||||
<Paper className="mt-5">
|
<Paper className="mt-5">
|
||||||
|
@ -110,11 +110,13 @@ function MediaViewContent(props: { portable: MWPortableMedia }) {
|
||||||
const mediaPortable = props.portable;
|
const mediaPortable = props.portable;
|
||||||
const [streamUrl, setStreamUrl] = useState<MWMediaStream | undefined>();
|
const [streamUrl, setStreamUrl] = useState<MWMediaStream | undefined>();
|
||||||
const [media, setMedia] = useState<MWMedia | undefined>();
|
const [media, setMedia] = useState<MWMedia | undefined>();
|
||||||
const [fetchAllData, loading, error] = useLoading((mediaPortable) => {
|
const [fetchAllData, loading, error] = useLoading(
|
||||||
const streamPromise = getStream(mediaPortable);
|
(portable: MWPortableMedia) => {
|
||||||
const mediaPromise = convertPortableToMedia(mediaPortable);
|
const streamPromise = getStream(portable);
|
||||||
|
const mediaPromise = convertPortableToMedia(portable);
|
||||||
return Promise.all([streamPromise, mediaPromise]);
|
return Promise.all([streamPromise, mediaPromise]);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -127,7 +129,7 @@ function MediaViewContent(props: { portable: MWPortableMedia }) {
|
||||||
})();
|
})();
|
||||||
}, [mediaPortable, setStreamUrl, fetchAllData]);
|
}, [mediaPortable, setStreamUrl, fetchAllData]);
|
||||||
|
|
||||||
let content: ReactNode;
|
let content: ReactElement | null = null;
|
||||||
if (loading) content = <LoadingMediaView />;
|
if (loading) content = <LoadingMediaView />;
|
||||||
else if (error) content = <LoadingMediaView error />;
|
else if (error) content = <LoadingMediaView error />;
|
||||||
else if (mediaPortable && media && streamUrl)
|
else if (mediaPortable && media && streamUrl)
|
||||||
|
@ -141,7 +143,7 @@ function MediaViewContent(props: { portable: MWPortableMedia }) {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
return <>{content}</>;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MediaView() {
|
export function MediaView() {
|
||||||
|
@ -152,11 +154,11 @@ export function MediaView() {
|
||||||
<div className="flex min-h-screen w-full">
|
<div className="flex min-h-screen w-full">
|
||||||
<Navigation>
|
<Navigation>
|
||||||
<ArrowLink
|
<ArrowLink
|
||||||
onClick={() => {
|
onClick={() =>
|
||||||
reactHistory.action !== "POP"
|
reactHistory.action !== "POP"
|
||||||
? reactHistory.goBack()
|
? reactHistory.goBack()
|
||||||
: reactHistory.push("/");
|
: reactHistory.push("/")
|
||||||
}}
|
}
|
||||||
direction="left"
|
direction="left"
|
||||||
linkText="Go back"
|
linkText="Go back"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { WatchedMediaCard } from "components/media/WatchedMediaCard";
|
import { WatchedMediaCard } from "components/media/WatchedMediaCard";
|
||||||
import { SearchBarInput } from "components/SearchBar";
|
import { SearchBarInput } from "components/SearchBar";
|
||||||
import { MWMassProviderOutput, MWQuery, SearchProviders } from "providers";
|
import { MWMassProviderOutput, MWQuery, SearchProviders } from "providers";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { ThinContainer } from "components/layout/ThinContainer";
|
import { ThinContainer } from "components/layout/ThinContainer";
|
||||||
import { SectionHeading } from "components/layout/SectionHeading";
|
import { SectionHeading } from "components/layout/SectionHeading";
|
||||||
import { Icons } from "components/Icon";
|
import { Icons } from "components/Icon";
|
||||||
|
@ -78,9 +78,9 @@ function SearchResultsView({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function runSearch(query: MWQuery) {
|
async function runSearch(query: MWQuery) {
|
||||||
const results = await runSearchQuery(query);
|
const searchResults = await runSearchQuery(query);
|
||||||
if (!results) return;
|
if (!searchResults) return;
|
||||||
setResults(results);
|
setResults(searchResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchQuery.searchQuery !== "") runSearch(searchQuery);
|
if (searchQuery.searchQuery !== "") runSearch(searchQuery);
|
||||||
|
@ -123,53 +123,6 @@ function SearchResultsView({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SearchView() {
|
|
||||||
const [searching, setSearching] = useState<boolean>(false);
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const [search, setSearch] = useSearchQuery();
|
|
||||||
|
|
||||||
const debouncedSearch = useDebounce<MWQuery>(search, 2000);
|
|
||||||
useEffect(() => {
|
|
||||||
setSearching(search.searchQuery !== "");
|
|
||||||
setLoading(search.searchQuery !== "");
|
|
||||||
}, [search]);
|
|
||||||
useEffect(() => {
|
|
||||||
setLoading(false);
|
|
||||||
}, [debouncedSearch]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Navigation />
|
|
||||||
<ThinContainer>
|
|
||||||
{/* input section */}
|
|
||||||
<div className="mt-44 space-y-16 text-center">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<Tagline>Because watching legally is boring</Tagline>
|
|
||||||
<Title>What movie do you want to watch?</Title>
|
|
||||||
</div>
|
|
||||||
<SearchBarInput
|
|
||||||
onChange={setSearch}
|
|
||||||
value={search}
|
|
||||||
placeholder="What movie do you want to watch?"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* results view */}
|
|
||||||
{loading ? (
|
|
||||||
<SearchLoading />
|
|
||||||
) : searching ? (
|
|
||||||
<SearchResultsView
|
|
||||||
searchQuery={debouncedSearch}
|
|
||||||
clear={() => setSearch({ searchQuery: "" })}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ExtraItems />
|
|
||||||
)}
|
|
||||||
</ThinContainer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ExtraItems() {
|
function ExtraItems() {
|
||||||
const { getFilteredBookmarks } = useBookmarkContext();
|
const { getFilteredBookmarks } = useBookmarkContext();
|
||||||
const { getFilteredWatched } = useWatchedContext();
|
const { getFilteredWatched } = useWatchedContext();
|
||||||
|
@ -207,3 +160,53 @@ function ExtraItems() {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function SearchView() {
|
||||||
|
const [searching, setSearching] = useState<boolean>(false);
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const [search, setSearch] = useSearchQuery();
|
||||||
|
|
||||||
|
const debouncedSearch = useDebounce<MWQuery>(search, 2000);
|
||||||
|
useEffect(() => {
|
||||||
|
setSearching(search.searchQuery !== "");
|
||||||
|
setLoading(search.searchQuery !== "");
|
||||||
|
}, [search]);
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(false);
|
||||||
|
}, [debouncedSearch]);
|
||||||
|
|
||||||
|
const resultView = useMemo(() => {
|
||||||
|
if (loading) return <SearchLoading />;
|
||||||
|
if (searching)
|
||||||
|
return (
|
||||||
|
<SearchResultsView
|
||||||
|
searchQuery={debouncedSearch}
|
||||||
|
clear={() => setSearch({ searchQuery: "" })}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
return <ExtraItems />;
|
||||||
|
}, [loading, searching, debouncedSearch, setSearch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Navigation />
|
||||||
|
<ThinContainer>
|
||||||
|
{/* input section */}
|
||||||
|
<div className="mt-44 space-y-16 text-center">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<Tagline>Because watching legally is boring</Tagline>
|
||||||
|
<Title>What movie do you want to watch?</Title>
|
||||||
|
</div>
|
||||||
|
<SearchBarInput
|
||||||
|
onChange={setSearch}
|
||||||
|
value={search}
|
||||||
|
placeholder="What movie do you want to watch?"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* results view */}
|
||||||
|
{resultView}
|
||||||
|
</ThinContainer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
79
yarn.lock
79
yarn.lock
|
@ -1256,7 +1256,7 @@
|
||||||
|
|
||||||
"@eslint/eslintrc@^1.2.0":
|
"@eslint/eslintrc@^1.2.0":
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.0.tgz#7ce1547a5c46dfe56e1e45c3c9ed18038c721c6a"
|
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.0.tgz"
|
||||||
integrity sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==
|
integrity sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv "^6.12.4"
|
ajv "^6.12.4"
|
||||||
|
@ -1999,7 +1999,7 @@
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
"@types/react-router" "*"
|
"@types/react-router" "*"
|
||||||
|
|
||||||
"@types/react-router@*":
|
"@types/react-router@*", "@types/react-router@^5.1.18":
|
||||||
version "5.1.18"
|
version "5.1.18"
|
||||||
resolved "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.18.tgz"
|
resolved "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.18.tgz"
|
||||||
integrity sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g==
|
integrity sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g==
|
||||||
|
@ -2100,7 +2100,7 @@
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@^5.13.0":
|
"@typescript-eslint/eslint-plugin@^5.13.0":
|
||||||
version "5.13.0"
|
version "5.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.13.0.tgz#2809052b85911ced9c54a60dac10e515e9114497"
|
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.13.0.tgz"
|
||||||
integrity sha512-vLktb2Uec81fxm/cfz2Hd6QaWOs8qdmVAZXLdOBX6JFJDhf6oDZpMzZ4/LZ6SFM/5DgDcxIMIvy3F+O9yZBuiQ==
|
integrity sha512-vLktb2Uec81fxm/cfz2Hd6QaWOs8qdmVAZXLdOBX6JFJDhf6oDZpMzZ4/LZ6SFM/5DgDcxIMIvy3F+O9yZBuiQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager" "5.13.0"
|
"@typescript-eslint/scope-manager" "5.13.0"
|
||||||
|
@ -2137,7 +2137,7 @@
|
||||||
|
|
||||||
"@typescript-eslint/parser@^5.13.0":
|
"@typescript-eslint/parser@^5.13.0":
|
||||||
version "5.13.0"
|
version "5.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.13.0.tgz#0394ed8f2f849273c0bf4b811994d177112ced5c"
|
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.13.0.tgz"
|
||||||
integrity sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg==
|
integrity sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager" "5.13.0"
|
"@typescript-eslint/scope-manager" "5.13.0"
|
||||||
|
@ -2165,7 +2165,7 @@
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@5.13.0":
|
"@typescript-eslint/scope-manager@5.13.0":
|
||||||
version "5.13.0"
|
version "5.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz#cf6aff61ca497cb19f0397eea8444a58f46156b6"
|
resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz"
|
||||||
integrity sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA==
|
integrity sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "5.13.0"
|
"@typescript-eslint/types" "5.13.0"
|
||||||
|
@ -2182,7 +2182,7 @@
|
||||||
|
|
||||||
"@typescript-eslint/type-utils@5.13.0":
|
"@typescript-eslint/type-utils@5.13.0":
|
||||||
version "5.13.0"
|
version "5.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.13.0.tgz#b0efd45c85b7bab1125c97b752cab3a86c7b615d"
|
resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.13.0.tgz"
|
||||||
integrity sha512-/nz7qFizaBM1SuqAKb7GLkcNn2buRdDgZraXlkhz+vUGiN1NZ9LzkA595tHHeduAiS2MsHqMNhE2zNzGdw43Yg==
|
integrity sha512-/nz7qFizaBM1SuqAKb7GLkcNn2buRdDgZraXlkhz+vUGiN1NZ9LzkA595tHHeduAiS2MsHqMNhE2zNzGdw43Yg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/utils" "5.13.0"
|
"@typescript-eslint/utils" "5.13.0"
|
||||||
|
@ -2196,7 +2196,7 @@
|
||||||
|
|
||||||
"@typescript-eslint/types@5.13.0":
|
"@typescript-eslint/types@5.13.0":
|
||||||
version "5.13.0"
|
version "5.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.13.0.tgz#da1de4ae905b1b9ff682cab0bed6b2e3be9c04e5"
|
resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.13.0.tgz"
|
||||||
integrity sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg==
|
integrity sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg==
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@5.11.0":
|
"@typescript-eslint/typescript-estree@5.11.0":
|
||||||
|
@ -2214,7 +2214,7 @@
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@5.13.0":
|
"@typescript-eslint/typescript-estree@5.13.0":
|
||||||
version "5.13.0"
|
version "5.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz#b37c07b748ff030a3e93d87c842714e020b78141"
|
resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz"
|
||||||
integrity sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA==
|
integrity sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "5.13.0"
|
"@typescript-eslint/types" "5.13.0"
|
||||||
|
@ -2239,7 +2239,7 @@
|
||||||
|
|
||||||
"@typescript-eslint/utils@5.13.0":
|
"@typescript-eslint/utils@5.13.0":
|
||||||
version "5.13.0"
|
version "5.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.13.0.tgz#2328feca700eb02837298339a2e49c46b41bd0af"
|
resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.13.0.tgz"
|
||||||
integrity sha512-+9oHlPWYNl6AwwoEt5TQryEHwiKRVjz7Vk6kaBeD3/kwHE5YqTGHtm/JZY8Bo9ITOeKutFaXnBlMgSATMJALUQ==
|
integrity sha512-+9oHlPWYNl6AwwoEt5TQryEHwiKRVjz7Vk6kaBeD3/kwHE5YqTGHtm/JZY8Bo9ITOeKutFaXnBlMgSATMJALUQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/json-schema" "^7.0.9"
|
"@types/json-schema" "^7.0.9"
|
||||||
|
@ -2259,7 +2259,7 @@
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@5.13.0":
|
"@typescript-eslint/visitor-keys@5.13.0":
|
||||||
version "5.13.0"
|
version "5.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz#f45ff55bcce16403b221ac9240fbeeae4764f0fd"
|
resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz"
|
||||||
integrity sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g==
|
integrity sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "5.13.0"
|
"@typescript-eslint/types" "5.13.0"
|
||||||
|
@ -4051,7 +4051,7 @@ escodegen@^2.0.0:
|
||||||
|
|
||||||
eslint-config-airbnb-base@^15.0.0:
|
eslint-config-airbnb-base@^15.0.0:
|
||||||
version "15.0.0"
|
version "15.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz#6b09add90ac79c2f8d723a2580e07f3925afd236"
|
resolved "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz"
|
||||||
integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==
|
integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==
|
||||||
dependencies:
|
dependencies:
|
||||||
confusing-browser-globals "^1.0.10"
|
confusing-browser-globals "^1.0.10"
|
||||||
|
@ -4061,7 +4061,7 @@ eslint-config-airbnb-base@^15.0.0:
|
||||||
|
|
||||||
eslint-config-airbnb@19.0.4:
|
eslint-config-airbnb@19.0.4:
|
||||||
version "19.0.4"
|
version "19.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz#84d4c3490ad70a0ffa571138ebcdea6ab085fdc3"
|
resolved "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz"
|
||||||
integrity sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==
|
integrity sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint-config-airbnb-base "^15.0.0"
|
eslint-config-airbnb-base "^15.0.0"
|
||||||
|
@ -4070,7 +4070,7 @@ eslint-config-airbnb@19.0.4:
|
||||||
|
|
||||||
eslint-config-prettier@^8.5.0:
|
eslint-config-prettier@^8.5.0:
|
||||||
version "8.5.0"
|
version "8.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1"
|
resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz"
|
||||||
integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==
|
integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==
|
||||||
|
|
||||||
eslint-config-react-app@^7.0.0:
|
eslint-config-react-app@^7.0.0:
|
||||||
|
@ -4103,7 +4103,7 @@ eslint-import-resolver-node@^0.3.6:
|
||||||
|
|
||||||
eslint-import-resolver-typescript@^2.5.0:
|
eslint-import-resolver-typescript@^2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a"
|
resolved "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz"
|
||||||
integrity sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==
|
integrity sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
debug "^4.3.1"
|
debug "^4.3.1"
|
||||||
|
@ -4128,7 +4128,7 @@ eslint-plugin-flowtype@^8.0.3:
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
string-natural-compare "^3.0.1"
|
string-natural-compare "^3.0.1"
|
||||||
|
|
||||||
eslint-plugin-import@^2.25.3:
|
eslint-plugin-import@^2.25.3, eslint-plugin-import@^2.25.4:
|
||||||
version "2.25.4"
|
version "2.25.4"
|
||||||
resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz"
|
resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz"
|
||||||
integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==
|
integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==
|
||||||
|
@ -4147,25 +4147,6 @@ eslint-plugin-import@^2.25.3:
|
||||||
resolve "^1.20.0"
|
resolve "^1.20.0"
|
||||||
tsconfig-paths "^3.12.0"
|
tsconfig-paths "^3.12.0"
|
||||||
|
|
||||||
eslint-plugin-import@^2.25.4:
|
|
||||||
version "2.25.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1"
|
|
||||||
integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==
|
|
||||||
dependencies:
|
|
||||||
array-includes "^3.1.4"
|
|
||||||
array.prototype.flat "^1.2.5"
|
|
||||||
debug "^2.6.9"
|
|
||||||
doctrine "^2.1.0"
|
|
||||||
eslint-import-resolver-node "^0.3.6"
|
|
||||||
eslint-module-utils "^2.7.2"
|
|
||||||
has "^1.0.3"
|
|
||||||
is-core-module "^2.8.0"
|
|
||||||
is-glob "^4.0.3"
|
|
||||||
minimatch "^3.0.4"
|
|
||||||
object.values "^1.1.5"
|
|
||||||
resolve "^1.20.0"
|
|
||||||
tsconfig-paths "^3.12.0"
|
|
||||||
|
|
||||||
eslint-plugin-jest@^25.3.0:
|
eslint-plugin-jest@^25.3.0:
|
||||||
version "25.7.0"
|
version "25.7.0"
|
||||||
resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz"
|
resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz"
|
||||||
|
@ -4173,9 +4154,9 @@ eslint-plugin-jest@^25.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/experimental-utils" "^5.0.0"
|
"@typescript-eslint/experimental-utils" "^5.0.0"
|
||||||
|
|
||||||
eslint-plugin-jsx-a11y@6.5.1, eslint-plugin-jsx-a11y@^6.5.1:
|
eslint-plugin-jsx-a11y@^6.5.1:
|
||||||
version "6.5.1"
|
version "6.5.1"
|
||||||
resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz#cdbf2df901040ca140b6ec14715c988889c2a6d8"
|
||||||
integrity sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==
|
integrity sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.16.3"
|
"@babel/runtime" "^7.16.3"
|
||||||
|
@ -4241,7 +4222,7 @@ eslint-scope@^7.1.0:
|
||||||
|
|
||||||
eslint-scope@^7.1.1:
|
eslint-scope@^7.1.1:
|
||||||
version "7.1.1"
|
version "7.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
|
resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz"
|
||||||
integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
|
integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
|
||||||
dependencies:
|
dependencies:
|
||||||
esrecurse "^4.3.0"
|
esrecurse "^4.3.0"
|
||||||
|
@ -4266,7 +4247,7 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.2
|
||||||
|
|
||||||
eslint-visitor-keys@^3.3.0:
|
eslint-visitor-keys@^3.3.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
|
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz"
|
||||||
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
||||||
|
|
||||||
eslint-webpack-plugin@^3.1.1:
|
eslint-webpack-plugin@^3.1.1:
|
||||||
|
@ -4282,7 +4263,7 @@ eslint-webpack-plugin@^3.1.1:
|
||||||
|
|
||||||
eslint@^8.10.0:
|
eslint@^8.10.0:
|
||||||
version "8.10.0"
|
version "8.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.10.0.tgz#931be395eb60f900c01658b278e05b6dae47199d"
|
resolved "https://registry.npmjs.org/eslint/-/eslint-8.10.0.tgz"
|
||||||
integrity sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==
|
integrity sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint/eslintrc" "^1.2.0"
|
"@eslint/eslintrc" "^1.2.0"
|
||||||
|
@ -4373,7 +4354,7 @@ espree@^9.2.0, espree@^9.3.0:
|
||||||
|
|
||||||
espree@^9.3.1:
|
espree@^9.3.1:
|
||||||
version "9.3.1"
|
version "9.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd"
|
resolved "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz"
|
||||||
integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==
|
integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn "^8.7.0"
|
acorn "^8.7.0"
|
||||||
|
@ -4823,7 +4804,7 @@ glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
||||||
|
|
||||||
glob@^7.1.7:
|
glob@^7.1.7:
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz"
|
||||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
fs.realpath "^1.0.0"
|
fs.realpath "^1.0.0"
|
||||||
|
@ -7402,12 +7383,12 @@ prelude-ls@~1.1.2:
|
||||||
|
|
||||||
prettier-plugin-tailwindcss@^0.1.7:
|
prettier-plugin-tailwindcss@^0.1.7:
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.1.7.tgz#f51de7b7bbabaa0724d3aff7a62957e5aa873482"
|
resolved "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.1.7.tgz"
|
||||||
integrity sha512-tmBr45hCLuit2Cz9Pwow0/Jl1bGivYGsfcF29O+3sKcE++ybjz9dfie565S3ZsvAeV8uYer9SRMBWDsHPly2Lg==
|
integrity sha512-tmBr45hCLuit2Cz9Pwow0/Jl1bGivYGsfcF29O+3sKcE++ybjz9dfie565S3ZsvAeV8uYer9SRMBWDsHPly2Lg==
|
||||||
|
|
||||||
prettier@^2.5.1:
|
prettier@^2.5.1:
|
||||||
version "2.5.1"
|
version "2.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a"
|
resolved "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz"
|
||||||
integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==
|
integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==
|
||||||
|
|
||||||
pretty-bytes@^5.3.0, pretty-bytes@^5.4.1:
|
pretty-bytes@^5.3.0, pretty-bytes@^5.4.1:
|
||||||
|
@ -7481,7 +7462,7 @@ proxy-addr@~2.0.5:
|
||||||
|
|
||||||
proxy-compare@2.0.2:
|
proxy-compare@2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/proxy-compare/-/proxy-compare-2.0.2.tgz#343e624d0ec399dfbe575f1d365d4fa042c9fc69"
|
resolved "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.0.2.tgz"
|
||||||
integrity sha512-3qUXJBariEj3eO90M3Rgqq3+/P5Efl0t/dl9g/1uVzIQmO3M+ql4hvNH3mYdu8H+1zcKv07YvL55tsY74jmH1A==
|
integrity sha512-3qUXJBariEj3eO90M3Rgqq3+/P5Efl0t/dl9g/1uVzIQmO3M+ql4hvNH3mYdu8H+1zcKv07YvL55tsY74jmH1A==
|
||||||
|
|
||||||
psl@^1.1.33:
|
psl@^1.1.33:
|
||||||
|
@ -7720,7 +7701,7 @@ react-side-effect@^2.1.0:
|
||||||
|
|
||||||
react-tracked@^1.7.6:
|
react-tracked@^1.7.6:
|
||||||
version "1.7.6"
|
version "1.7.6"
|
||||||
resolved "https://registry.yarnpkg.com/react-tracked/-/react-tracked-1.7.6.tgz#11bccec80acccdf5029db20171a887b8b16b7ae1"
|
resolved "https://registry.npmjs.org/react-tracked/-/react-tracked-1.7.6.tgz"
|
||||||
integrity sha512-yqfkqj4UZpsadBLIHnPLrc8a0SLgjKoSQrdyipfWeXvLnPl+/AV8MrqRVbNogUJsqHOo+ojlWy2PMxuZPVcPnQ==
|
integrity sha512-yqfkqj4UZpsadBLIHnPLrc8a0SLgjKoSQrdyipfWeXvLnPl+/AV8MrqRVbNogUJsqHOo+ojlWy2PMxuZPVcPnQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
proxy-compare "2.0.2"
|
proxy-compare "2.0.2"
|
||||||
|
@ -8057,7 +8038,7 @@ saxes@^5.0.1:
|
||||||
|
|
||||||
scheduler@^0.20.2:
|
scheduler@^0.20.2:
|
||||||
version "0.20.2"
|
version "0.20.2"
|
||||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
|
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz"
|
||||||
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
|
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
|
@ -8792,7 +8773,7 @@ tsconfig-paths@^3.12.0:
|
||||||
|
|
||||||
tsconfig-paths@^3.9.0:
|
tsconfig-paths@^3.9.0:
|
||||||
version "3.13.0"
|
version "3.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz#f3e9b8f6876698581d94470c03c95b3a48c0e3d7"
|
resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz"
|
||||||
integrity sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==
|
integrity sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/json5" "^0.0.29"
|
"@types/json5" "^0.0.29"
|
||||||
|
@ -8868,7 +8849,7 @@ typedarray-to-buffer@^3.1.5:
|
||||||
|
|
||||||
typescript@^4.6.2:
|
typescript@^4.6.2:
|
||||||
version "4.6.2"
|
version "4.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4"
|
resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz"
|
||||||
integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==
|
integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==
|
||||||
|
|
||||||
unbox-primitive@^1.0.1:
|
unbox-primitive@^1.0.1:
|
||||||
|
@ -8968,7 +8949,7 @@ uri-js@^4.2.2:
|
||||||
|
|
||||||
use-context-selector@1.3.9:
|
use-context-selector@1.3.9:
|
||||||
version "1.3.9"
|
version "1.3.9"
|
||||||
resolved "https://registry.yarnpkg.com/use-context-selector/-/use-context-selector-1.3.9.tgz#d1527393839f0d790ccdd52e28e8f353b8be6c2e"
|
resolved "https://registry.npmjs.org/use-context-selector/-/use-context-selector-1.3.9.tgz"
|
||||||
integrity sha512-YgzRyeFjoJXwFn2qLVAuIbV6EQ8DOuzu3SS/eiCxyAyvBhcn02jYSz8c5v22QQU3LW6Ez/Iyo62kKvS7Kdqt3A==
|
integrity sha512-YgzRyeFjoJXwFn2qLVAuIbV6EQ8DOuzu3SS/eiCxyAyvBhcn02jYSz8c5v22QQU3LW6Ez/Iyo62kKvS7Kdqt3A==
|
||||||
|
|
||||||
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||||
|
|
Loading…
Reference in a new issue