Add basics touch controls for the player

This commit is contained in:
Zoe Roux 2023-12-13 14:49:00 +01:00
parent d593eb3134
commit e13f2a7e42
7 changed files with 133 additions and 12 deletions

View File

@ -26,6 +26,7 @@ export default withRoute(
{
options: {
headerShown: false,
navigationBarHidden: true,
},
statusBar: { hidden: true },
fullscreen: true,

View File

@ -26,6 +26,7 @@ export default withRoute(
{
options: {
headerShown: false,
navigationBarHidden: true,
},
statusBar: { hidden: true },
fullscreen: true,

View File

@ -22,7 +22,7 @@ import "../polyfill";
import { HydrationBoundary, QueryClientProvider } from "@tanstack/react-query";
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 {
AccountProvider,
@ -96,6 +96,7 @@ const GlobalCssTheme = () => {
`}</style>
<WebTooltip theme={theme} />
<SkeletonCss />
<TouchOnlyCss />
<HiddenIfNoJs />
</>
);

View File

@ -23,3 +23,4 @@ export * from "./nojs";
export * from "./head";
export * from "./spacing";
export * from "./capitalize";
export * from "./touchonly";

View 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" },
};

View File

@ -31,6 +31,7 @@ import {
Skeleton,
Slider,
tooltip,
touchOnly,
ts,
} from "@kyoo/primitives";
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 { useRouter } from "solito/router";
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 { bufferedAtom, durationAtom, loadAtom, playAtom, progressAtom } from "../state";
@ -84,6 +85,7 @@ export const Hover = ({
{({ css }) => (
<>
<Back isLoading={isLoading} name={showName} href={href} {...css(opacity, props)} />
<TouchControls previousSlug={previousSlug} nextSlug={nextSlug} />
<Pressable
tabIndex={-1}
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 buffered = useAtomValue(bufferedAtom);
const duration = useAtomValue(durationAtom);
@ -163,7 +165,7 @@ export const ProgressBar = ({ chapters }: { chapters?: Chapter[] }) => {
);
};
export const Back = ({
const Back = ({
isLoading,
name,
href,

View File

@ -18,10 +18,20 @@
* 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 { 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 SkipNext from "@material-symbols/svg-400/rounded/skip_next-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 VolumeUp from "@material-symbols/svg-400/rounded/volume_up-fill.svg";
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 = ({
previousSlug,
@ -48,12 +59,76 @@ export const LeftButtons = ({
return (
<View {...css({ flexDirection: "row" })}>
<View {...css({ flexDirection: "row" }, noTouch)}>
{previousSlug && (
<IconButton
icon={SkipPrevious}
as={Link}
href={previousSlug}
replace
{...tooltip(t("player.previous"), true)}
{...spacing}
/>
)}
<IconButton
icon={isPlaying ? Pause : PlayArrow}
onPress={() => setPlay(!isPlaying)}
{...tooltip(isPlaying ? t("player.pause") : t("player.play"), true)}
{...spacing}
/>
{nextSlug && (
<IconButton
icon={SkipNext}
as={Link}
href={nextSlug}
replace
{...tooltip(t("player.next"), true)}
{...spacing}
/>
)}
{Platform.OS === "web" && <VolumeSlider />}
</View>
<ProgressText {...css({ marginLeft: ts(1) })} />
</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}
/>
@ -61,6 +136,7 @@ export const LeftButtons = ({
<IconButton
icon={isPlaying ? Pause : PlayArrow}
onPress={() => setPlay(!isPlaying)}
size={ts(8)}
{...tooltip(isPlaying ? t("player.pause") : t("player.play"), true)}
{...spacing}
/>
@ -70,16 +146,14 @@ export const LeftButtons = ({
as={Link}
href={nextSlug}
replace
size={ts(4)}
{...tooltip(t("player.next"), true)}
{...spacing}
/>
)}
<VolumeSlider />
<ProgressText />
</View>
);
};
const VolumeSlider = () => {
const [volume, setVolume] = useAtom(volumeAtom);
const [isMuted, setMuted] = useAtom(mutedAtom);
@ -119,13 +193,13 @@ const VolumeSlider = () => {
);
};
const ProgressText = () => {
const ProgressText = (props: Stylable) => {
const progress = useAtomValue(progressAtom);
const duration = useAtomValue(durationAtom);
const { css } = useYoshiki();
return (
<P {...css({ alignSelf: "center" })}>
<P {...css({ alignSelf: "center" }, props)}>
{toTimerString(progress, duration)} : {toTimerString(duration)}
</P>
);