cleanup settings modal + add sources list

This commit is contained in:
mrjvs 2023-10-17 21:25:54 +02:00
parent 65d46190e6
commit abec91a322
7 changed files with 340 additions and 241 deletions

View File

@ -0,0 +1,17 @@
import classNames from "classnames";
import "flag-icons/css/flag-icons.min.css";
export interface FlagIconProps {
countryCode?: string;
}
export function FlagIcon(props: FlagIconProps) {
return (
<span
className={classNames(
"!w-8 h-6 rounded overflow-hidden bg-video-context-flagBg bg-cover bg-center block fi",
props.countryCode ? `fi-${props.countryCode}` : undefined
)}
/>
);
}

View File

@ -1,256 +1,27 @@
import "flag-icons/css/flag-icons.min.css"; import { useEffect } from "react";
import classNames from "classnames"; import { Icons } from "@/components/Icon";
import { useCallback, useEffect, useState } from "react";
import { Toggle } from "@/components/buttons/Toggle";
import { Icon, Icons } from "@/components/Icon";
import { OverlayAnchor } from "@/components/overlays/OverlayAnchor"; import { OverlayAnchor } from "@/components/overlays/OverlayAnchor";
import { Overlay } from "@/components/overlays/OverlayDisplay"; import { Overlay } from "@/components/overlays/OverlayDisplay";
import { OverlayPage } from "@/components/overlays/OverlayPage"; import { OverlayPage } from "@/components/overlays/OverlayPage";
import { OverlayRouter } from "@/components/overlays/OverlayRouter"; import { OverlayRouter } from "@/components/overlays/OverlayRouter";
import { SettingsMenu } from "@/components/player/atoms/settings/SettingsMenu";
import { SourceSelectionView } from "@/components/player/atoms/settings/SourceSelectingView";
import { VideoPlayerButton } from "@/components/player/internals/Button"; import { VideoPlayerButton } from "@/components/player/internals/Button";
import { Context } from "@/components/player/internals/ContextUtils"; import { Context } from "@/components/player/internals/ContextUtils";
import { useOverlayRouter } from "@/hooks/useOverlayRouter"; import { useOverlayRouter } from "@/hooks/useOverlayRouter";
import { usePlayerStore } from "@/stores/player/store"; import { usePlayerStore } from "@/stores/player/store";
import {
SourceQuality,
allQualities,
qualityToString,
} from "@/stores/player/utils/qualities";
function QualityOption(props: { import { CaptionSettingsView } from "./settings/CaptionSettingsView";
children: React.ReactNode; import { CaptionsView } from "./settings/CaptionsView";
selected?: boolean; import { QualityView } from "./settings/QualityView";
disabled?: boolean;
onClick?: () => void;
}) {
let textClasses;
if (props.selected) textClasses = "text-white";
if (props.disabled)
textClasses = "text-video-context-type-main text-opacity-40";
return (
<Context.Link onClick={props.disabled ? undefined : props.onClick}>
<Context.LinkTitle textClass={textClasses}>
{props.children}
</Context.LinkTitle>
{props.selected ? (
<Icon
icon={Icons.CIRCLE_CHECK}
className="text-xl text-video-context-type-accent"
/>
) : null}
</Context.Link>
);
}
function QualityView({ id }: { id: string }) {
const router = useOverlayRouter(id);
const availableQualities = usePlayerStore((s) => s.qualities);
const currentQuality = usePlayerStore((s) => s.currentQuality);
const switchQuality = usePlayerStore((s) => s.switchQuality);
const change = useCallback(
(q: SourceQuality) => {
switchQuality(q);
router.close();
},
[router, switchQuality]
);
const allVisibleQualities = allQualities.filter((t) => t !== "unknown");
return (
<>
<Context.BackLink onClick={() => router.navigate("/")}>
Quality
</Context.BackLink>
<Context.Section>
{allVisibleQualities.map((v) => (
<QualityOption
key={v}
selected={v === currentQuality}
onClick={
availableQualities.includes(v) ? () => change(v) : undefined
}
disabled={!availableQualities.includes(v)}
>
{qualityToString(v)}
</QualityOption>
))}
<Context.Divider />
<Context.Link>
<Context.LinkTitle>Automatic quality</Context.LinkTitle>
<span>Toggle</span>
</Context.Link>
<Context.SmallText>
You can try{" "}
<Context.Anchor onClick={() => router.navigate("/source")}>
switching source
</Context.Anchor>{" "}
to get different quality options.
</Context.SmallText>
</Context.Section>
</>
);
}
function ColorOption(props: {
color: string;
active?: boolean;
onClick: () => void;
}) {
return (
<div
className={classNames(
"p-1.5 bg-video-context-buttonFocus rounded transition-colors duration-100",
props.active ? "bg-opacity-100" : "bg-opacity-0 cursor-pointer"
)}
onClick={props.onClick}
>
<div
className="w-6 h-6 rounded-full flex justify-center items-center"
style={{ backgroundColor: props.color }}
>
{props.active ? (
<Icon className="text-sm text-black" icon={Icons.CHECKMARK} />
) : null}
</div>
</div>
);
}
function CaptionSettingsView({ id }: { id: string }) {
const router = useOverlayRouter(id);
return (
<>
<Context.BackLink onClick={() => router.navigate("/captions")}>
Custom captions
</Context.BackLink>
<Context.Section>
<div className="flex justify-between items-center">
<Context.FieldTitle>Color</Context.FieldTitle>
<div className="flex justify-center items-center">
<ColorOption onClick={() => {}} color="#FFFFFF" active />
<ColorOption onClick={() => {}} color="#80B1FA" />
<ColorOption onClick={() => {}} color="#E2E535" />
</div>
</div>
</Context.Section>
</>
);
}
function CaptionOption(props: {
countryCode?: string;
children: React.ReactNode;
selected?: boolean;
}) {
return (
<div className="grid grid-cols-[auto,1fr,auto] items-center gap-3 rounded -ml-3 -mr-3 px-3 py-2 cursor-pointer hover:bg-video-context-border hover:bg-opacity-10">
<div>
<span
className={classNames(
"!w-8 h-6 rounded overflow-hidden bg-video-context-flagBg bg-cover bg-center block fi",
props.countryCode ? `fi-${props.countryCode}` : undefined
)}
/>
</div>
<span
className={classNames(props.selected && "text-white", "font-medium")}
>
{props.children}
</span>
{props.selected ? (
<Icon
icon={Icons.CIRCLE_CHECK}
className="text-xl text-video-context-type-accent"
/>
) : null}
</div>
);
}
function CaptionsView({ id }: { id: string }) {
const router = useOverlayRouter(id);
return (
<>
<Context.BackLink
onClick={() => router.navigate("/")}
rightSide={
<button
type="button"
onClick={() => router.navigate("/captions/settings")}
>
Customize
</button>
}
>
Captions
</Context.BackLink>
<Context.Section>
<CaptionOption>Off</CaptionOption>
<CaptionOption countryCode="nl" selected>
Nederlands
</CaptionOption>
<CaptionOption countryCode="gr">Idk Gibraltar of zo?</CaptionOption>
</Context.Section>
</>
);
}
function SettingsOverlay({ id }: { id: string }) { function SettingsOverlay({ id }: { id: string }) {
const router = useOverlayRouter(id);
const currentQuality = usePlayerStore((s) => s.currentQuality);
const [tmpBool, setTmpBool] = useState(false);
function toggleBool() {
setTmpBool(!tmpBool);
}
return ( return (
<Overlay id={id}> <Overlay id={id}>
<OverlayRouter id={id}> <OverlayRouter id={id}>
<OverlayPage id={id} path="/" width={343} height={431}> <OverlayPage id={id} path="/" width={343} height={431}>
<Context.Card> <SettingsMenu id={id} />
<Context.SectionTitle>Video settings</Context.SectionTitle>
<Context.Section>
<Context.Link onClick={() => router.navigate("/quality")}>
<Context.LinkTitle>Quality</Context.LinkTitle>
<Context.LinkChevron>
{currentQuality ? qualityToString(currentQuality) : ""}
</Context.LinkChevron>
</Context.Link>
<Context.Link onClick={() => router.navigate("/source")}>
<Context.LinkTitle>Video source</Context.LinkTitle>
<Context.LinkChevron>SuperStream</Context.LinkChevron>
</Context.Link>
<Context.Link>
<Context.LinkTitle>Download</Context.LinkTitle>
<Context.IconButton icon={Icons.DOWNLOAD} />
</Context.Link>
</Context.Section>
<Context.SectionTitle>Viewing Experience</Context.SectionTitle>
<Context.Section>
<Context.Link>
<Context.LinkTitle>Enable Captions</Context.LinkTitle>
<Toggle enabled={tmpBool} onClick={() => toggleBool()} />
</Context.Link>
<Context.Link onClick={() => router.navigate("/captions")}>
<Context.LinkTitle>Caption settings</Context.LinkTitle>
<Context.LinkChevron>English</Context.LinkChevron>
</Context.Link>
<Context.Link>
<Context.LinkTitle>Playback settings</Context.LinkTitle>
<Context.LinkChevron />
</Context.Link>
</Context.Section>
</Context.Card>
</OverlayPage> </OverlayPage>
<OverlayPage id={id} path="/quality" width={343} height={431}> <OverlayPage id={id} path="/quality" width={343} height={431}>
<Context.Card> <Context.Card>
@ -269,10 +40,7 @@ function SettingsOverlay({ id }: { id: string }) {
</OverlayPage> </OverlayPage>
<OverlayPage id={id} path="/source" width={343} height={431}> <OverlayPage id={id} path="/source" width={343} height={431}>
<Context.Card> <Context.Card>
<Context.BackLink onClick={() => router.navigate("/")}> <SourceSelectionView id={id} />
It&apos;s a minion!
</Context.BackLink>
<img src="https://media2.giphy.com/media/oa4Au5xDZ6HJYF6KGH/giphy.gif" />
</Context.Card> </Context.Card>
</OverlayPage> </OverlayPage>
</OverlayRouter> </OverlayRouter>

View File

@ -0,0 +1,52 @@
import classNames from "classnames";
import { Icon, Icons } from "@/components/Icon";
import { Context } from "@/components/player/internals/ContextUtils";
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
export function ColorOption(props: {
color: string;
active?: boolean;
onClick: () => void;
}) {
return (
<div
className={classNames(
"p-1.5 bg-video-context-buttonFocus rounded transition-colors duration-100",
props.active ? "bg-opacity-100" : "bg-opacity-0 cursor-pointer"
)}
onClick={props.onClick}
>
<div
className="w-6 h-6 rounded-full flex justify-center items-center"
style={{ backgroundColor: props.color }}
>
{props.active ? (
<Icon className="text-sm text-black" icon={Icons.CHECKMARK} />
) : null}
</div>
</div>
);
}
export function CaptionSettingsView({ id }: { id: string }) {
const router = useOverlayRouter(id);
return (
<>
<Context.BackLink onClick={() => router.navigate("/captions")}>
Custom captions
</Context.BackLink>
<Context.Section>
<div className="flex justify-between items-center">
<Context.FieldTitle>Color</Context.FieldTitle>
<div className="flex justify-center items-center">
<ColorOption onClick={() => {}} color="#FFFFFF" active />
<ColorOption onClick={() => {}} color="#80B1FA" />
<ColorOption onClick={() => {}} color="#E2E535" />
</div>
</div>
</Context.Section>
</>
);
}

View File

@ -0,0 +1,60 @@
import classNames from "classnames";
import { FlagIcon } from "@/components/FlagIcon";
import { Icon, Icons } from "@/components/Icon";
import { Context } from "@/components/player/internals/ContextUtils";
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
export function CaptionOption(props: {
countryCode?: string;
children: React.ReactNode;
selected?: boolean;
}) {
return (
<div className="grid grid-cols-[auto,1fr,auto] items-center gap-3 rounded -ml-3 -mr-3 px-3 py-2 cursor-pointer hover:bg-video-context-border hover:bg-opacity-10">
<div>
<FlagIcon countryCode={props.countryCode} />
</div>
<span
className={classNames(props.selected && "text-white", "font-medium")}
>
{props.children}
</span>
{props.selected ? (
<Icon
icon={Icons.CIRCLE_CHECK}
className="text-xl text-video-context-type-accent"
/>
) : null}
</div>
);
}
export function CaptionsView({ id }: { id: string }) {
const router = useOverlayRouter(id);
return (
<>
<Context.BackLink
onClick={() => router.navigate("/")}
rightSide={
<button
type="button"
onClick={() => router.navigate("/captions/settings")}
>
Customize
</button>
}
>
Captions
</Context.BackLink>
<Context.Section>
<CaptionOption>Off</CaptionOption>
<CaptionOption countryCode="nl" selected>
Nederlands
</CaptionOption>
<CaptionOption countryCode="gr">Idk Gibraltar of zo?</CaptionOption>
</Context.Section>
</>
);
}

View File

@ -0,0 +1,88 @@
import { useCallback } from "react";
import { Icon, Icons } from "@/components/Icon";
import { Context } from "@/components/player/internals/ContextUtils";
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
import { usePlayerStore } from "@/stores/player/store";
import {
SourceQuality,
allQualities,
qualityToString,
} from "@/stores/player/utils/qualities";
export function QualityOption(props: {
children: React.ReactNode;
selected?: boolean;
disabled?: boolean;
onClick?: () => void;
}) {
let textClasses;
if (props.selected) textClasses = "text-white";
if (props.disabled)
textClasses = "text-video-context-type-main text-opacity-40";
return (
<Context.Link onClick={props.disabled ? undefined : props.onClick}>
<Context.LinkTitle textClass={textClasses}>
{props.children}
</Context.LinkTitle>
{props.selected ? (
<Icon
icon={Icons.CIRCLE_CHECK}
className="text-xl text-video-context-type-accent"
/>
) : null}
</Context.Link>
);
}
export function QualityView({ id }: { id: string }) {
const router = useOverlayRouter(id);
const availableQualities = usePlayerStore((s) => s.qualities);
const currentQuality = usePlayerStore((s) => s.currentQuality);
const switchQuality = usePlayerStore((s) => s.switchQuality);
const change = useCallback(
(q: SourceQuality) => {
switchQuality(q);
router.close();
},
[router, switchQuality]
);
const allVisibleQualities = allQualities.filter((t) => t !== "unknown");
return (
<>
<Context.BackLink onClick={() => router.navigate("/")}>
Quality
</Context.BackLink>
<Context.Section>
{allVisibleQualities.map((v) => (
<QualityOption
key={v}
selected={v === currentQuality}
onClick={
availableQualities.includes(v) ? () => change(v) : undefined
}
disabled={!availableQualities.includes(v)}
>
{qualityToString(v)}
</QualityOption>
))}
<Context.Divider />
<Context.Link>
<Context.LinkTitle>Automatic quality</Context.LinkTitle>
<span>Toggle</span>
</Context.Link>
<Context.SmallText>
You can try{" "}
<Context.Anchor onClick={() => router.navigate("/source")}>
switching source
</Context.Anchor>{" "}
to get different quality options.
</Context.SmallText>
</Context.Section>
</>
);
}

View File

@ -0,0 +1,57 @@
import { useState } from "react";
import { Toggle } from "@/components/buttons/Toggle";
import { Icons } from "@/components/Icon";
import { Context } from "@/components/player/internals/ContextUtils";
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
import { usePlayerStore } from "@/stores/player/store";
import { qualityToString } from "@/stores/player/utils/qualities";
export function SettingsMenu({ id }: { id: string }) {
const router = useOverlayRouter(id);
const currentQuality = usePlayerStore((s) => s.currentQuality);
const [tmpBool, setTmpBool] = useState(false);
function toggleBool() {
setTmpBool(!tmpBool);
}
return (
<Context.Card>
<Context.SectionTitle>Video settings</Context.SectionTitle>
<Context.Section>
<Context.Link onClick={() => router.navigate("/quality")}>
<Context.LinkTitle>Quality</Context.LinkTitle>
<Context.LinkChevron>
{currentQuality ? qualityToString(currentQuality) : ""}
</Context.LinkChevron>
</Context.Link>
<Context.Link onClick={() => router.navigate("/source")}>
<Context.LinkTitle>Video source</Context.LinkTitle>
<Context.LinkChevron>SuperStream</Context.LinkChevron>
</Context.Link>
<Context.Link>
<Context.LinkTitle>Download</Context.LinkTitle>
<Context.IconButton icon={Icons.DOWNLOAD} />
</Context.Link>
</Context.Section>
<Context.SectionTitle>Viewing Experience</Context.SectionTitle>
<Context.Section>
<Context.Link>
<Context.LinkTitle>Enable Captions</Context.LinkTitle>
<Toggle enabled={tmpBool} onClick={() => toggleBool()} />
</Context.Link>
<Context.Link onClick={() => router.navigate("/captions")}>
<Context.LinkTitle>Caption settings</Context.LinkTitle>
<Context.LinkChevron>English</Context.LinkChevron>
</Context.Link>
<Context.Link>
<Context.LinkTitle>Playback settings</Context.LinkTitle>
<Context.LinkChevron />
</Context.Link>
</Context.Section>
</Context.Card>
);
}

View File

@ -0,0 +1,57 @@
import classNames from "classnames";
import { useMemo } from "react";
import { Icon, Icons } from "@/components/Icon";
import { Context } from "@/components/player/internals/ContextUtils";
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
import { usePlayerStore } from "@/stores/player/store";
import { providers } from "@/utils/providers";
export function SourceOption(props: {
children: React.ReactNode;
selected?: boolean;
onClick?: () => void;
}) {
return (
<div
onClick={props.onClick}
className="grid grid-cols-[auto,1fr,auto] items-center gap-3 rounded -ml-3 -mr-3 px-3 py-2 cursor-pointer hover:bg-video-context-border hover:bg-opacity-10"
>
<span
className={classNames(props.selected && "text-white", "font-medium")}
>
{props.children}
</span>
{props.selected ? (
<Icon
icon={Icons.CIRCLE_CHECK}
className="text-xl text-video-context-type-accent"
/>
) : null}
</div>
);
}
export function SourceSelectionView({ id }: { id: string }) {
const router = useOverlayRouter(id);
const metaType = usePlayerStore((s) => s.meta?.type);
const sources = useMemo(() => {
if (!metaType) return [];
return providers
.listSources()
.filter((v) => v.mediaTypes?.includes(metaType));
}, [metaType]);
return (
<>
<Context.BackLink onClick={() => router.navigate("/")}>
Sources
</Context.BackLink>
<Context.Section>
{sources.map((v) => (
<SourceOption key={v.id}>{v.name}</SourceOption>
))}
</Context.Section>
</>
);
}