diff --git a/README.md b/README.md index e01cdc8f..fcb2baf7 100644 --- a/README.md +++ b/README.md @@ -57,13 +57,13 @@ Check out [this project's issues](https://github.com/JamesHawkinss/movie-web/iss - [x] video load error, video loading (from actual video player) - [ ] Series episodes+seasons - [ ] Get rid of react warnings -- [ ] Add 404 page for media (media not found, provider disabled, provider not found) & general (page not found) -- [ ] Handle disabled providers (continue watching, bookmarks & router) +- [x] Add 404 page for media (media not found, provider disabled, provider not found) & general (page not found) +- [x] Handle disabled providers (continue watching, bookmarks & router) - [ ] Subtitles - [ ] Implement all scrapers - [ ] implement sources that are not mp4 +- [x] Bug: go back doesn't work if used directly from link - [ ] Migrate old video progress -- [ ] Bug: go back doesn't work if used directly from link ## After all rewrite code has been written diff --git a/src/App.tsx b/src/App.tsx index bd785aa6..e67c09b8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,7 +2,7 @@ import { MWMediaType } from "providers"; import { Redirect, Route, Switch } from "react-router-dom"; import { BookmarkContextProvider } from "state/bookmark"; import { WatchedContextProvider } from "state/watched"; -import { NotFoundPage } from "views/NotFoundView"; +import { NotFoundPage } from "views/notfound/NotFoundView"; import "./index.css"; import { MediaView } from "./views/MediaView"; import { SearchView } from "./views/SearchView"; diff --git a/src/components/media/WatchedMediaCard.tsx b/src/components/media/WatchedMediaCard.tsx index 6a4d5559..2093541b 100644 --- a/src/components/media/WatchedMediaCard.tsx +++ b/src/components/media/WatchedMediaCard.tsx @@ -8,7 +8,7 @@ export interface WatchedMediaCardProps { export function WatchedMediaCard(props: WatchedMediaCardProps) { const { watched } = useWatchedContext(); - const foundWatched = getWatchedFromPortable(watched, props.media); + const foundWatched = getWatchedFromPortable(watched.items, props.media); const watchedPercentage = (foundWatched && foundWatched.percentage) || 0; return ( diff --git a/src/state/watched/context.tsx b/src/state/watched/context.tsx index 508adee8..f802ee69 100644 --- a/src/state/watched/context.tsx +++ b/src/state/watched/context.tsx @@ -1,4 +1,4 @@ -import { MWMediaMeta } from "providers"; +import { MWMediaMeta, getProviderMetadata } from "providers"; import React, { createContext, ReactNode, useContext, useState } from "react"; import { VideoProgressStore } from "./store"; @@ -13,11 +13,13 @@ interface WatchedStoreData { interface WatchedStoreDataWrapper { updateProgress(media: MWMediaMeta, progress: number, total: number): void; + getFilteredWatched(): WatchedStoreItem[]; watched: WatchedStoreData; } const WatchedContext = createContext({ updateProgress: () => {}, + getFilteredWatched: () => [], watched: { items: [], }, @@ -42,13 +44,9 @@ export function WatchedContextProvider(props: { children: ReactNode }) { } const contextValue = { - updateProgress( - media: MWMediaMeta, - progress: number, - total: number - ): void { + updateProgress(media: MWMediaMeta, progress: number, total: number): void { setWatched((data: WatchedStoreData) => { - let item = getWatchedFromPortable(data, media); + let item = getWatchedFromPortable(data.items, media); if (!item) { item = { mediaId: media.mediaId, @@ -71,6 +69,11 @@ export function WatchedContextProvider(props: { children: ReactNode }) { return data; }); }, + getFilteredWatched() { + return watched.items.filter((item) => { + return getProviderMetadata(item.providerId)?.enabled; + }); + }, watched, }; @@ -86,10 +89,10 @@ export function useWatchedContext() { } export function getWatchedFromPortable( - store: WatchedStoreData, + items: WatchedStoreItem[], media: MWMediaMeta ): WatchedStoreItem | undefined { - return store.items.find((v) => { + return items.find((v) => { return ( v.mediaId === media.mediaId && v.providerId === media.providerId && diff --git a/src/views/MediaView.tsx b/src/views/MediaView.tsx index 3a4853a7..d0957fcd 100644 --- a/src/views/MediaView.tsx +++ b/src/views/MediaView.tsx @@ -18,11 +18,13 @@ import { MWMediaProvider, } from "providers"; import { ReactNode, useEffect, useState } from "react"; +import { useHistory } from "react-router-dom"; import { getIfBookmarkedFromPortable, useBookmarkContext, } from "state/bookmark"; import { getWatchedFromPortable, useWatchedContext } from "state/watched"; +import { NotFoundChecks } from "./notfound/NotFoundChecks"; interface StyledMediaViewProps { media: MWMedia; @@ -31,9 +33,9 @@ interface StyledMediaViewProps { } function StyledMediaView(props: StyledMediaViewProps) { - const store = useWatchedContext(); + const watchedStore = useWatchedContext(); const startAtTime: number | undefined = getWatchedFromPortable( - store.watched, + watchedStore.watched.items, props.media )?.progress; const { setItemBookmark, getFilteredBookmarks } = useBookmarkContext(); @@ -48,7 +50,7 @@ function StyledMediaView(props: StyledMediaViewProps) { if (el.currentTime <= 30) { return; // Don't update stored progress if less than 30s into the video } - store.updateProgress(props.media, el.currentTime, el.duration); + watchedStore.updateProgress(props.media, el.currentTime, el.duration); } return ( @@ -104,8 +106,8 @@ function LoadingMediaView(props: { error?: boolean }) { ); } -export function MediaView() { - const mediaPortable: MWPortableMedia | undefined = usePortableMedia(); +function MediaViewContent(props: { portable: MWPortableMedia }) { + const mediaPortable = props.portable; const [streamUrl, setStreamUrl] = useState(); const [media, setMedia] = useState(); const [fetchAllData, loading, error] = useLoading((mediaPortable) => { @@ -139,18 +141,31 @@ export function MediaView() { /> ); + return <>{content}; +} + +export function MediaView() { + const mediaPortable: MWPortableMedia | undefined = usePortableMedia(); + const reactHistory = useHistory(); + return ( -
+
window.history.back()} + onClick={() => { + reactHistory.action !== "POP" + ? reactHistory.goBack() + : reactHistory.push("/"); + }} direction="left" linkText="Go back" /> -
- {content} -
+ +
+ +
+
); } diff --git a/src/views/SearchView.tsx b/src/views/SearchView.tsx index 74e192fb..72da0c41 100644 --- a/src/views/SearchView.tsx +++ b/src/views/SearchView.tsx @@ -172,11 +172,11 @@ export function SearchView() { function ExtraItems() { const { getFilteredBookmarks } = useBookmarkContext(); - const { watched } = useWatchedContext(); + const { getFilteredWatched } = useWatchedContext(); const bookmarks = getFilteredBookmarks(); - const watchedItems = watched.items.filter( + const watchedItems = getFilteredWatched().filter( (v) => !getIfBookmarkedFromPortable(bookmarks, v) ); diff --git a/src/views/notfound/NotFoundChecks.tsx b/src/views/notfound/NotFoundChecks.tsx new file mode 100644 index 00000000..8bc4cd04 --- /dev/null +++ b/src/views/notfound/NotFoundChecks.tsx @@ -0,0 +1,27 @@ +import { getProviderMetadata, MWPortableMedia } from "providers"; +import { ReactNode } from "react"; +import { NotFoundMedia, NotFoundProvider } from "./NotFoundView"; + +export interface NotFoundChecksProps { + portable: MWPortableMedia | undefined; + children?: ReactNode; +} + +/* + ** Component that only renders children if the passed-in portable is fully correct + */ +export function NotFoundChecks(props: NotFoundChecksProps) { + const providerMeta = props.portable + ? getProviderMetadata(props.portable.providerId) + : undefined; + + if (!providerMeta || !providerMeta.exists) { + return ; + } + + if (!providerMeta.enabled) { + return ; + } + + return <>{props.children}; +} diff --git a/src/views/NotFoundView.tsx b/src/views/notfound/NotFoundView.tsx similarity index 90% rename from src/views/NotFoundView.tsx rename to src/views/notfound/NotFoundView.tsx index 6a237232..fd6f1831 100644 --- a/src/views/NotFoundView.tsx +++ b/src/views/notfound/NotFoundView.tsx @@ -18,7 +18,7 @@ function NotFoundWrapper(props: { children?: ReactNode }) { export function NotFoundMedia() { return ( - +
- +
); } export function NotFoundProvider() { return ( - +
- +
); }