responsiveness and loading states

This commit is contained in:
mrjvs 2022-03-13 17:26:46 +01:00
parent 570ca14905
commit 7709ffd90f
6 changed files with 48 additions and 52 deletions

View File

@ -16,7 +16,7 @@ interface DropdownProps {
export const Dropdown = React.forwardRef<HTMLDivElement, DropdownProps>(
(props: DropdownProps) => (
<div className="relative my-4 w-72 ">
<div className="relative my-4 max-w-[18rem]">
<Listbox value={props.selectedItem} onChange={props.setSelectedItem}>
{({ open }) => (
<>
@ -37,7 +37,7 @@ export const Dropdown = React.forwardRef<HTMLDivElement, DropdownProps>(
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="bg-denim-500 scrollbar-thin scrollbar-track-denim-400 scrollbar-thumb-denim-200 absolute bottom-11 z-10 mt-1 max-h-60 w-72 overflow-auto rounded-md py-1 text-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:bottom-10 sm:text-sm">
<Listbox.Options className="bg-denim-500 scrollbar-thin scrollbar-track-denim-400 scrollbar-thumb-denim-200 absolute bottom-11 left-0 right-0 z-10 mt-1 max-h-60 overflow-auto rounded-md py-1 text-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:bottom-10 sm:text-sm">
{props.options.map((opt) => (
<Listbox.Option
className={({ active }) =>

View File

@ -1,4 +1,6 @@
import { IconPatch } from "components/buttons/IconPatch";
import { Dropdown, OptionItem } from "components/Dropdown";
import { Icons } from "components/Icon";
import { WatchedEpisode } from "components/media/WatchedEpisodeButton";
import { useLoading } from "hooks/useLoading";
import { serializePortableMedia } from "hooks/usePortableMedia";
@ -17,6 +19,28 @@ export interface SeasonsProps {
media: MWMedia;
}
export function LoadingSeasons(props: { error?: boolean }) {
return (
<div>
<div>
<div className="bg-denim-400 mb-3 mt-5 h-10 w-56 rounded opacity-50" />
</div>
{!props.error ? (
<>
<div className="bg-denim-400 mr-3 mb-3 inline-block h-10 w-10 rounded opacity-50" />
<div className="bg-denim-400 mr-3 mb-3 inline-block h-10 w-10 rounded opacity-50" />
<div className="bg-denim-400 mr-3 mb-3 inline-block h-10 w-10 rounded opacity-50" />
</>
) : (
<div className="flex items-center space-x-3">
<IconPatch icon={Icons.WARNING} className="text-red-400" />
<p>Failed to load seasons and episodes</p>
</div>
)}
</div>
);
}
export function Seasons(props: SeasonsProps) {
const [searchSeasons, loading, error, success] = useLoading(
(portableMedia: MWPortableMedia) => getSeasonDataFromMedia(portableMedia)
@ -58,8 +82,8 @@ export function Seasons(props: SeasonsProps) {
return (
<>
{loading ? <p>Loading...</p> : null}
{error ? <p>error!</p> : null}
{loading ? <LoadingSeasons /> : null}
{error ? <LoadingSeasons error /> : null}
{success && seasons.seasons.length ? (
<>
<Dropdown

View File

@ -1,6 +1,5 @@
import {
convertMediaToPortable,
getEpisodeFromMedia,
getProviderFromId,
MWMediaMeta,
MWMediaType,

View File

@ -1,43 +0,0 @@
import {
MWMediaProvider,
MWMediaSeasons,
MWMediaType,
MWPortableMedia,
MWQuery,
} from "providers/types";
import { MWMediaStream, MWProviderMediaResult } from "providers";
export const tempScraper: MWMediaProvider = {
id: "temp",
enabled: true,
type: [MWMediaType.MOVIE, MWMediaType.SERIES],
displayName: "temp",
async getMediaFromPortable(
media: MWPortableMedia
): Promise<MWProviderMediaResult> {
return {
...media,
year: "1234",
title: "temp",
};
},
async searchForMedia(query: MWQuery): Promise<MWProviderMediaResult[]> {
return [];
},
async getStream(media: MWPortableMedia): Promise<MWMediaStream> {
return {
url: "hi",
type: "mp4",
};
},
async getSeasonDataFromMedia(media): Promise<MWMediaSeasons> {
return {
seasons: [],
};
},
};

View File

@ -84,13 +84,22 @@ export const theFlixScraper: MWMediaProvider = {
.querySelectorAll(`script[id="__NEXT_DATA__"]`)
)[0];
const data = JSON.parse(node.innerHTML).props.pageProps.selectedTv.seasons;
let data = JSON.parse(node.innerHTML).props.pageProps.selectedTv.seasons;
data = data.filter((season: any) => season.releaseDate != null);
data = data.map((season: any) => {
const episodes = season.episodes.filter(
(episode: any) => episode.releaseDate != null
);
return { ...season, episodes };
});
return {
seasons: data.map((d: any) => ({
sort: d.seasonNumber === 0 ? 999 : d.seasonNumber,
id: d.seasonNumber.toString(),
type: d.seasonNumber === 0 ? "special" : "season",
title: d.seasonNumber === 0 ? "Specials" : undefined,
title: d.name,
episodes: d.episodes.map((e: any) => ({
title: e.name,
sort: e.episodeNumber,

View File

@ -2,7 +2,7 @@ import { IconPatch } from "components/buttons/IconPatch";
import { Icons } from "components/Icon";
import { Navigation } from "components/layout/Navigation";
import { Paper } from "components/layout/Paper";
import { Seasons } from "components/layout/Seasons";
import { LoadingSeasons, Seasons } from "components/layout/Seasons";
import { SkeletonVideoPlayer, VideoPlayer } from "components/media/VideoPlayer";
import { ArrowLink } from "components/text/ArrowLink";
import { DotList } from "components/text/DotList";
@ -110,7 +110,14 @@ function LoadingMediaFooter(props: { error?: boolean }) {
<span className="bg-denim-400 mr-4 inline-block h-2 w-12 rounded-full" />
<span className="bg-denim-400 mr-4 inline-block h-2 w-12 rounded-full" />
</div>
{props.error ? "error!" : null}
{props.error ? (
<div className="flex items-center space-x-3">
<IconPatch icon={Icons.WARNING} className="text-red-400" />
<p>Your url may be invalid</p>
</div>
) : (
<LoadingSeasons />
)}
</div>
</div>
</Paper>