mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-01 04:34:50 -04:00
Add basics touch controls for the player
This commit is contained in:
parent
d593eb3134
commit
e13f2a7e42
@ -26,6 +26,7 @@ export default withRoute(
|
|||||||
{
|
{
|
||||||
options: {
|
options: {
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
|
navigationBarHidden: true,
|
||||||
},
|
},
|
||||||
statusBar: { hidden: true },
|
statusBar: { hidden: true },
|
||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
|
@ -26,6 +26,7 @@ export default withRoute(
|
|||||||
{
|
{
|
||||||
options: {
|
options: {
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
|
navigationBarHidden: true,
|
||||||
},
|
},
|
||||||
statusBar: { hidden: true },
|
statusBar: { hidden: true },
|
||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
|
@ -22,7 +22,7 @@ import "../polyfill";
|
|||||||
|
|
||||||
import { HydrationBoundary, QueryClientProvider } from "@tanstack/react-query";
|
import { HydrationBoundary, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||||
import { HiddenIfNoJs, SkeletonCss, ThemeSelector } from "@kyoo/primitives";
|
import { HiddenIfNoJs, TouchOnlyCss, SkeletonCss, ThemeSelector } from "@kyoo/primitives";
|
||||||
import { WebTooltip } from "@kyoo/primitives/src/tooltip.web";
|
import { WebTooltip } from "@kyoo/primitives/src/tooltip.web";
|
||||||
import {
|
import {
|
||||||
AccountProvider,
|
AccountProvider,
|
||||||
@ -96,6 +96,7 @@ const GlobalCssTheme = () => {
|
|||||||
`}</style>
|
`}</style>
|
||||||
<WebTooltip theme={theme} />
|
<WebTooltip theme={theme} />
|
||||||
<SkeletonCss />
|
<SkeletonCss />
|
||||||
|
<TouchOnlyCss />
|
||||||
<HiddenIfNoJs />
|
<HiddenIfNoJs />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -23,3 +23,4 @@ export * from "./nojs";
|
|||||||
export * from "./head";
|
export * from "./head";
|
||||||
export * from "./spacing";
|
export * from "./spacing";
|
||||||
export * from "./capitalize";
|
export * from "./capitalize";
|
||||||
|
export * from "./touchonly";
|
||||||
|
41
front/packages/primitives/src/utils/touchonly.tsx
Normal file
41
front/packages/primitives/src/utils/touchonly.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Platform, ViewProps } from "react-native";
|
||||||
|
|
||||||
|
export const TouchOnlyCss = () => {
|
||||||
|
return (
|
||||||
|
<style jsx global>{`
|
||||||
|
:where(body.noHover) .noTouch {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
:where(body:not(.noHover)) .touchOnly {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const touchOnly: ViewProps = {
|
||||||
|
style: Platform.OS === "web" ? ({ $$css: true, touchOnly: "touchOnly" } as any) : {},
|
||||||
|
};
|
||||||
|
export const noTouch: ViewProps = {
|
||||||
|
style: Platform.OS === "web" ? ({ $$css: true, noTouch: "noTouch" } as any) : { display: "none" },
|
||||||
|
};
|
@ -31,6 +31,7 @@ import {
|
|||||||
Skeleton,
|
Skeleton,
|
||||||
Slider,
|
Slider,
|
||||||
tooltip,
|
tooltip,
|
||||||
|
touchOnly,
|
||||||
ts,
|
ts,
|
||||||
} from "@kyoo/primitives";
|
} from "@kyoo/primitives";
|
||||||
import { Chapter, KyooImage, Subtitle, Audio } from "@kyoo/models";
|
import { Chapter, KyooImage, Subtitle, Audio } from "@kyoo/models";
|
||||||
@ -40,7 +41,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { percent, rem, useYoshiki } from "yoshiki/native";
|
import { percent, rem, useYoshiki } from "yoshiki/native";
|
||||||
import { useRouter } from "solito/router";
|
import { useRouter } from "solito/router";
|
||||||
import ArrowBack from "@material-symbols/svg-400/rounded/arrow_back-fill.svg";
|
import ArrowBack from "@material-symbols/svg-400/rounded/arrow_back-fill.svg";
|
||||||
import { LeftButtons } from "./left-buttons";
|
import { LeftButtons, TouchControls } from "./left-buttons";
|
||||||
import { RightButtons } from "./right-buttons";
|
import { RightButtons } from "./right-buttons";
|
||||||
import { bufferedAtom, durationAtom, loadAtom, playAtom, progressAtom } from "../state";
|
import { bufferedAtom, durationAtom, loadAtom, playAtom, progressAtom } from "../state";
|
||||||
|
|
||||||
@ -84,6 +85,7 @@ export const Hover = ({
|
|||||||
{({ css }) => (
|
{({ css }) => (
|
||||||
<>
|
<>
|
||||||
<Back isLoading={isLoading} name={showName} href={href} {...css(opacity, props)} />
|
<Back isLoading={isLoading} name={showName} href={href} {...css(opacity, props)} />
|
||||||
|
<TouchControls previousSlug={previousSlug} nextSlug={nextSlug} />
|
||||||
<Pressable
|
<Pressable
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
onPointerDown={onPointerDown}
|
onPointerDown={onPointerDown}
|
||||||
@ -144,7 +146,7 @@ export const Hover = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ProgressBar = ({ chapters }: { chapters?: Chapter[] }) => {
|
const ProgressBar = ({ chapters }: { chapters?: Chapter[] }) => {
|
||||||
const [progress, setProgress] = useAtom(progressAtom);
|
const [progress, setProgress] = useAtom(progressAtom);
|
||||||
const buffered = useAtomValue(bufferedAtom);
|
const buffered = useAtomValue(bufferedAtom);
|
||||||
const duration = useAtomValue(durationAtom);
|
const duration = useAtomValue(durationAtom);
|
||||||
@ -163,7 +165,7 @@ export const ProgressBar = ({ chapters }: { chapters?: Chapter[] }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Back = ({
|
const Back = ({
|
||||||
isLoading,
|
isLoading,
|
||||||
name,
|
name,
|
||||||
href,
|
href,
|
||||||
|
@ -18,10 +18,20 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IconButton, Link, P, Slider, tooltip, ts } from "@kyoo/primitives";
|
import {
|
||||||
|
IconButton,
|
||||||
|
Link,
|
||||||
|
NoTouch,
|
||||||
|
P,
|
||||||
|
Slider,
|
||||||
|
noTouch,
|
||||||
|
tooltip,
|
||||||
|
touchOnly,
|
||||||
|
ts,
|
||||||
|
} from "@kyoo/primitives";
|
||||||
import { useAtom, useAtomValue } from "jotai";
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { View } from "react-native";
|
import { Platform, View } from "react-native";
|
||||||
import SkipPrevious from "@material-symbols/svg-400/rounded/skip_previous-fill.svg";
|
import SkipPrevious from "@material-symbols/svg-400/rounded/skip_previous-fill.svg";
|
||||||
import SkipNext from "@material-symbols/svg-400/rounded/skip_next-fill.svg";
|
import SkipNext from "@material-symbols/svg-400/rounded/skip_next-fill.svg";
|
||||||
import PlayArrow from "@material-symbols/svg-400/rounded/play_arrow-fill.svg";
|
import PlayArrow from "@material-symbols/svg-400/rounded/play_arrow-fill.svg";
|
||||||
@ -31,7 +41,8 @@ import VolumeMute from "@material-symbols/svg-400/rounded/volume_mute-fill.svg";
|
|||||||
import VolumeDown from "@material-symbols/svg-400/rounded/volume_down-fill.svg";
|
import VolumeDown from "@material-symbols/svg-400/rounded/volume_down-fill.svg";
|
||||||
import VolumeUp from "@material-symbols/svg-400/rounded/volume_up-fill.svg";
|
import VolumeUp from "@material-symbols/svg-400/rounded/volume_up-fill.svg";
|
||||||
import { durationAtom, mutedAtom, playAtom, progressAtom, volumeAtom } from "../state";
|
import { durationAtom, mutedAtom, playAtom, progressAtom, volumeAtom } from "../state";
|
||||||
import { px, useYoshiki } from "yoshiki/native";
|
import { Stylable, px, useYoshiki } from "yoshiki/native";
|
||||||
|
import { Component, ComponentProps } from "react";
|
||||||
|
|
||||||
export const LeftButtons = ({
|
export const LeftButtons = ({
|
||||||
previousSlug,
|
previousSlug,
|
||||||
@ -48,6 +59,7 @@ export const LeftButtons = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View {...css({ flexDirection: "row" })}>
|
<View {...css({ flexDirection: "row" })}>
|
||||||
|
<View {...css({ flexDirection: "row" }, noTouch)}>
|
||||||
{previousSlug && (
|
{previousSlug && (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={SkipPrevious}
|
icon={SkipPrevious}
|
||||||
@ -74,12 +86,74 @@ export const LeftButtons = ({
|
|||||||
{...spacing}
|
{...spacing}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<VolumeSlider />
|
{Platform.OS === "web" && <VolumeSlider />}
|
||||||
<ProgressText />
|
</View>
|
||||||
|
<ProgressText {...css({ marginLeft: ts(1) })} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const TouchControls = ({
|
||||||
|
previousSlug,
|
||||||
|
nextSlug,
|
||||||
|
}: {
|
||||||
|
previousSlug?: string | null;
|
||||||
|
nextSlug?: string | null;
|
||||||
|
}) => {
|
||||||
|
const { css } = useYoshiki();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [isPlaying, setPlay] = useAtom(playAtom);
|
||||||
|
|
||||||
|
const spacing = css({ backgroundColor: (theme) => theme.darkOverlay, marginHorizontal: ts(3) });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
{...css(
|
||||||
|
{
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
},
|
||||||
|
touchOnly,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{previousSlug && (
|
||||||
|
<IconButton
|
||||||
|
icon={SkipPrevious}
|
||||||
|
as={Link}
|
||||||
|
href={previousSlug}
|
||||||
|
replace
|
||||||
|
size={ts(4)}
|
||||||
|
{...tooltip(t("player.previous"), true)}
|
||||||
|
{...spacing}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
icon={isPlaying ? Pause : PlayArrow}
|
||||||
|
onPress={() => setPlay(!isPlaying)}
|
||||||
|
size={ts(8)}
|
||||||
|
{...tooltip(isPlaying ? t("player.pause") : t("player.play"), true)}
|
||||||
|
{...spacing}
|
||||||
|
/>
|
||||||
|
{nextSlug && (
|
||||||
|
<IconButton
|
||||||
|
icon={SkipNext}
|
||||||
|
as={Link}
|
||||||
|
href={nextSlug}
|
||||||
|
replace
|
||||||
|
size={ts(4)}
|
||||||
|
{...tooltip(t("player.next"), true)}
|
||||||
|
{...spacing}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
const VolumeSlider = () => {
|
const VolumeSlider = () => {
|
||||||
const [volume, setVolume] = useAtom(volumeAtom);
|
const [volume, setVolume] = useAtom(volumeAtom);
|
||||||
const [isMuted, setMuted] = useAtom(mutedAtom);
|
const [isMuted, setMuted] = useAtom(mutedAtom);
|
||||||
@ -119,13 +193,13 @@ const VolumeSlider = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProgressText = () => {
|
const ProgressText = (props: Stylable) => {
|
||||||
const progress = useAtomValue(progressAtom);
|
const progress = useAtomValue(progressAtom);
|
||||||
const duration = useAtomValue(durationAtom);
|
const duration = useAtomValue(durationAtom);
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<P {...css({ alignSelf: "center" })}>
|
<P {...css({ alignSelf: "center" }, props)}>
|
||||||
{toTimerString(progress, duration)} : {toTimerString(duration)}
|
{toTimerString(progress, duration)} : {toTimerString(duration)}
|
||||||
</P>
|
</P>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user