diff --git a/.vscode/settings.json b/.vscode/settings.json index 279011fe..2f5fdb18 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,8 @@ "eslint.format.enable": true, "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "ms-vsliveshare.vsliveshare" } -} +} \ No newline at end of file diff --git a/src/components/player/atoms/Settings.tsx b/src/components/player/atoms/Settings.tsx index 44126ebe..e2ca4a9c 100644 --- a/src/components/player/atoms/Settings.tsx +++ b/src/components/player/atoms/Settings.tsx @@ -1,3 +1,4 @@ +import classNames from "classnames"; import { useCallback, useEffect, useState } from "react"; import { Toggle } from "@/components/buttons/Toggle"; @@ -93,6 +94,31 @@ function QualityView({ id }: { id: string }) { ); } +function ColorOption(props: { + color: string; + active?: boolean; + onClick: () => void; +}) { + return ( +
+
+ {props.active ? ( + + ) : null} +
+
+ ); +} + function CaptionSettingsView({ id }: { id: string }) { const router = useOverlayRouter(id); @@ -102,12 +128,32 @@ function CaptionSettingsView({ id }: { id: string }) { Custom captions - Hello! +
+ Color +
+ {}} color="#FFFFFF" active /> + {}} color="#80B1FA" /> + {}} color="#E2E535" /> +
+
); } +function CaptionsView({ id }: { id: string }) { + const router = useOverlayRouter(id); + + return ( + <> + router.navigate("/captions")}> + Captions + + Yee! + + ); +} + function SettingsOverlay({ id }: { id: string }) { const router = useOverlayRouter(id); const currentQuality = usePlayerStore((s) => s.currentQuality); @@ -165,15 +211,7 @@ function SettingsOverlay({ id }: { id: string }) { - router.navigate("/")}> - Captions - - + diff --git a/src/components/player/internals/ContextUtils.tsx b/src/components/player/internals/ContextUtils.tsx index 71a7683b..bd5790e7 100644 --- a/src/components/player/internals/ContextUtils.tsx +++ b/src/components/player/internals/ContextUtils.tsx @@ -154,18 +154,23 @@ function Anchor(props: { children: React.ReactNode; onClick: () => void }) { ); } +function FieldTitle(props: { children: React.ReactNode }) { + return

{props.children}

; +} + export const Context = { - Card, CardWithScrollable, - Title, SectionTitle, - BackLink, - Section, - Link, - LinkTitle, LinkChevron, IconButton, - Divider, + FieldTitle, SmallText, + BackLink, + LinkTitle, + Section, + Divider, Anchor, + Title, + Link, + Card, }; diff --git a/src/pages/parts/home/BookmarksPart.tsx b/src/pages/parts/home/BookmarksPart.tsx index 1a7f5671..c75b374d 100644 --- a/src/pages/parts/home/BookmarksPart.tsx +++ b/src/pages/parts/home/BookmarksPart.tsx @@ -8,26 +8,38 @@ import { SectionHeading } from "@/components/layout/SectionHeading"; import { MediaGrid } from "@/components/media/MediaGrid"; import { WatchedMediaCard } from "@/components/media/WatchedMediaCard"; import { useBookmarkStore } from "@/stores/bookmarks"; +import { useProgressStore } from "@/stores/progress"; import { MediaItem } from "@/utils/mediaTypes"; export function BookmarksPart() { const { t } = useTranslation(); + const progressItems = useProgressStore((s) => s.items); const bookmarks = useBookmarkStore((s) => s.bookmarks); const removeBookmark = useBookmarkStore((s) => s.removeBookmark); const [editing, setEditing] = useState(false); const [gridRef] = useAutoAnimate(); - // TODO sort on last watched const items = useMemo(() => { - const output: MediaItem[] = []; + let output: MediaItem[] = []; Object.entries(bookmarks).forEach((entry) => { output.push({ id: entry[0], ...entry[1], }); }); + output = output.sort((a, b) => { + const bookmarkA = bookmarks[a.id]; + const bookmarkB = bookmarks[b.id]; + const progressA = progressItems[a.id]; + const progressB = progressItems[b.id]; + + const dateA = Math.max(bookmarkA.updatedAt, progressA?.updatedAt ?? 0); + const dateB = Math.max(bookmarkB.updatedAt, progressB?.updatedAt ?? 0); + + return dateB - dateA; + }); return output; - }, [bookmarks]); + }, [bookmarks, progressItems]); if (items.length === 0) return null; diff --git a/src/pages/parts/home/WatchingPart.tsx b/src/pages/parts/home/WatchingPart.tsx index 99ccf6a4..62daa769 100644 --- a/src/pages/parts/home/WatchingPart.tsx +++ b/src/pages/parts/home/WatchingPart.tsx @@ -21,18 +21,19 @@ export function WatchingPart() { const sortedProgressItems = useMemo(() => { let output: MediaItem[] = []; - Object.entries(progressItems).forEach((entry) => { - output.push({ - id: entry[0], - ...entry[1], + Object.entries(progressItems) + .sort((a, b) => b[1].updatedAt - a[1].updatedAt) + .forEach((entry) => { + output.push({ + id: entry[0], + ...entry[1], + }); }); - }); output = output.filter((v) => { const isBookMarked = !!bookmarks[v.id]; return !isBookMarked; }); - // TODO sort on last modified date return output; }, [progressItems, bookmarks]); diff --git a/src/stores/bookmarks/index.ts b/src/stores/bookmarks/index.ts index bd3ac204..96b1931c 100644 --- a/src/stores/bookmarks/index.ts +++ b/src/stores/bookmarks/index.ts @@ -9,6 +9,7 @@ export interface BookmarkMediaItem { year: number; poster?: string; type: "show" | "movie"; + updatedAt: number; } export interface ProgressStore { @@ -34,6 +35,7 @@ export const useBookmarkStore = create( title: meta.title, year: meta.releaseYear, poster: meta.poster, + updatedAt: Date.now(), }; }); }, diff --git a/src/stores/progress/index.ts b/src/stores/progress/index.ts index 55b66df7..a52cd978 100644 --- a/src/stores/progress/index.ts +++ b/src/stores/progress/index.ts @@ -29,6 +29,7 @@ export interface ProgressMediaItem { poster?: string; type: "show" | "movie"; progress?: ProgressItem; + updatedAt: number; seasons: Record; episodes: Record; } @@ -61,11 +62,14 @@ export const useProgressStore = create( type: meta.type, episodes: {}, seasons: {}, + updatedAt: 0, title: meta.title, year: meta.releaseYear, poster: meta.poster, }; const item = s.items[meta.tmdbId]; + item.updatedAt = Date.now(); + if (meta.type === "movie") { if (!item.progress) item.progress = { diff --git a/tailwind.config.js b/tailwind.config.js index 68c60438..2dac6a03 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -26,23 +26,23 @@ module.exports = { "ash-400": "#3D394D", "ash-300": "#2C293A", "ash-200": "#2B2836", - "ash-100": "#1E1C26" + "ash-100": "#1E1C26", }, /* fonts */ fontFamily: { - "open-sans": "'Open Sans'" + "open-sans": "'Open Sans'", }, /* animations */ keyframes: { "loading-pin": { "0%, 40%, 100%": { height: "0.5em", "background-color": "#282336" }, - "20%": { height: "1em", "background-color": "white" } - } + "20%": { height: "1em", "background-color": "white" }, + }, }, - animation: { "loading-pin": "loading-pin 1.8s ease-in-out infinite" } - } + animation: { "loading-pin": "loading-pin 1.8s ease-in-out infinite" }, + }, }, plugins: [ require("tailwind-scrollbar"), @@ -52,31 +52,31 @@ module.exports = { colors: { // Branding pill: { - background: "#1C1C36" + background: "#1C1C36", }, // meta data for the theme itself global: { accentA: "#505DBD", - accentB: "#3440A1" + accentB: "#3440A1", }, // light bar lightBar: { - light: "#2A2A71" + light: "#2A2A71", }, // Buttons buttons: { toggle: "#8D44D6", - toggleDisabled: "#202836" + toggleDisabled: "#202836", }, // only used for body colors/textures background: { main: "#0A0A10", accentA: "#6E3B80", - accentB: "#1F1F50" + accentB: "#1F1F50", }, // typography @@ -85,7 +85,7 @@ module.exports = { text: "#73739D", dimmed: "#926CAD", divider: "#262632", - secondary: "#64647B" + secondary: "#64647B", }, // search bar @@ -94,7 +94,7 @@ module.exports = { focused: "#24243C", placeholder: "#4A4A71", icon: "#545476", - text: "#FFFFFF" + text: "#FFFFFF", }, // media cards @@ -106,7 +106,7 @@ module.exports = { barColor: "#4B4B63", barFillColor: "#BA7FD6", badge: "#151522", - badgeText: "#5F5F7A" + badgeText: "#5F5F7A", }, // video player @@ -118,34 +118,35 @@ module.exports = { error: "#E44F4F", success: "#40B44B", loading: "#B759D8", - noresult: "#64647B" + noresult: "#64647B", }, progress: { background: "#8787A8", preloaded: "#8787A8", - watched: "#A75FC9" + watched: "#A75FC9", }, audio: { - set: "#A75FC9" + set: "#A75FC9", }, context: { background: "#0C1216", light: "#4D79A8", border: "#4F5C66", + buttonFocus: "#202836", type: { main: "#617A8A", secondary: "#374A56", - accent: "#A570FA" - } - } - } - } - } - } - }) - ] + accent: "#A570FA", + }, + }, + }, + }, + }, + }, + }), + ], };