mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-27 22:01:51 +01:00
cleanup settings modal + add sources list
This commit is contained in:
parent
65d46190e6
commit
abec91a322
17
src/components/FlagIcon.tsx
Normal file
17
src/components/FlagIcon.tsx
Normal 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
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -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's a minion!
|
|
||||||
</Context.BackLink>
|
|
||||||
<img src="https://media2.giphy.com/media/oa4Au5xDZ6HJYF6KGH/giphy.gif" />
|
|
||||||
</Context.Card>
|
</Context.Card>
|
||||||
</OverlayPage>
|
</OverlayPage>
|
||||||
</OverlayRouter>
|
</OverlayRouter>
|
||||||
|
52
src/components/player/atoms/settings/CaptionSettingsView.tsx
Normal file
52
src/components/player/atoms/settings/CaptionSettingsView.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
60
src/components/player/atoms/settings/CaptionsView.tsx
Normal file
60
src/components/player/atoms/settings/CaptionsView.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
88
src/components/player/atoms/settings/QualityView.tsx
Normal file
88
src/components/player/atoms/settings/QualityView.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
57
src/components/player/atoms/settings/SettingsMenu.tsx
Normal file
57
src/components/player/atoms/settings/SettingsMenu.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
57
src/components/player/atoms/settings/SourceSelectingView.tsx
Normal file
57
src/components/player/atoms/settings/SourceSelectingView.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user