mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
wip: design refactor
This commit is contained in:
@@ -3,7 +3,7 @@ import { runtimeTicksToMinutes } from "@/utils/time";
|
||||
import { useActionSheet } from "@expo/react-native-action-sheet";
|
||||
import { Feather, Ionicons } from "@expo/vector-icons";
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useMemo } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
import CastContext, {
|
||||
PlayServicesState,
|
||||
@@ -13,6 +13,14 @@ import { Button } from "./Button";
|
||||
import { Text } from "./common/Text";
|
||||
import { useAtom } from "jotai";
|
||||
import { itemThemeColorAtom } from "@/utils/atoms/primaryColor";
|
||||
import Animated, {
|
||||
useSharedValue,
|
||||
useAnimatedStyle,
|
||||
withTiming,
|
||||
interpolateColor,
|
||||
runOnJS,
|
||||
useAnimatedReaction,
|
||||
} from "react-native-reanimated";
|
||||
|
||||
interface Props extends React.ComponentProps<typeof Button> {
|
||||
item?: BaseItemDto | null;
|
||||
@@ -26,6 +34,47 @@ export const PlayButton: React.FC<Props> = ({ item, url, ...props }) => {
|
||||
|
||||
const [color] = useAtom(itemThemeColorAtom);
|
||||
|
||||
// Create a shared value for animation progress
|
||||
const progress = useSharedValue(0);
|
||||
|
||||
// Create shared values for start and end colors
|
||||
const startColor = useSharedValue(color);
|
||||
const endColor = useSharedValue(color);
|
||||
|
||||
useEffect(() => {
|
||||
// When color changes, update end color and animate progress
|
||||
endColor.value = color;
|
||||
progress.value = 0; // Reset progress
|
||||
progress.value = withTiming(1, { duration: 300 }); // Animate to 1 over 500ms
|
||||
}, [color]);
|
||||
|
||||
// Animated style for primary color
|
||||
const animatedPrimaryStyle = useAnimatedStyle(() => ({
|
||||
backgroundColor: interpolateColor(
|
||||
progress.value,
|
||||
[0, 1],
|
||||
[startColor.value.average, endColor.value.average]
|
||||
),
|
||||
}));
|
||||
|
||||
// Animated style for text color
|
||||
const animatedTextStyle = useAnimatedStyle(() => ({
|
||||
color: interpolateColor(
|
||||
progress.value,
|
||||
[0, 1],
|
||||
[startColor.value.text, endColor.value.text]
|
||||
),
|
||||
}));
|
||||
|
||||
// Update start color after animation completes
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
startColor.value = color;
|
||||
}, 500); // Should match the duration in withTiming
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [color]);
|
||||
|
||||
const onPress = async () => {
|
||||
if (!url || !item) return;
|
||||
|
||||
@@ -85,37 +134,43 @@ export const PlayButton: React.FC<Props> = ({ item, url, ...props }) => {
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={onPress} className="relative" {...props}>
|
||||
<Animated.View
|
||||
style={[
|
||||
animatedPrimaryStyle,
|
||||
{
|
||||
width:
|
||||
playbackPercent === 0
|
||||
? "100%"
|
||||
: `${Math.max(playbackPercent, 15)}%`,
|
||||
height: "100%",
|
||||
},
|
||||
]}
|
||||
className="absolute w-full h-full top-0 left-0 rounded-xl z-10"
|
||||
/>
|
||||
<Animated.View
|
||||
style={[animatedPrimaryStyle]}
|
||||
className="absolute w-full h-full top-0 left-0 rounded-xl "
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
width:
|
||||
playbackPercent === 0
|
||||
? "100%"
|
||||
: `${Math.max(playbackPercent, 15)}%`,
|
||||
height: "100%",
|
||||
backgroundColor: color.primary,
|
||||
borderWidth: 1,
|
||||
borderColor: color.primary,
|
||||
borderStyle: "solid",
|
||||
}}
|
||||
className="absolute w-full h-full top-0 left-0 rounded-xl z-10"
|
||||
></View>
|
||||
<View
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: color.primary,
|
||||
}}
|
||||
className="absolute w-full h-full top-0 left-0 rounded-xl opacity-40"
|
||||
></View>
|
||||
<View className="flex flex-row items-center justify-center bg-transparent rounded-xl z-20 h-12 w-full ">
|
||||
className="flex flex-row items-center justify-center bg-transparent rounded-xl z-20 h-12 w-full "
|
||||
>
|
||||
<View className="flex flex-row items-center space-x-2">
|
||||
<Text
|
||||
className="font-bold"
|
||||
style={{
|
||||
color: color.text,
|
||||
}}
|
||||
>
|
||||
<Animated.Text style={[animatedTextStyle, { fontWeight: "bold" }]}>
|
||||
{runtimeTicksToMinutes(item?.RunTimeTicks)}
|
||||
</Text>
|
||||
<Ionicons name="play-circle" size={24} color={color.text} />
|
||||
{client && <Feather name="cast" size={22} color={color.text} />}
|
||||
</Animated.Text>
|
||||
<Animated.Text style={animatedTextStyle}>
|
||||
<Ionicons name="play-circle" size={24} />
|
||||
</Animated.Text>
|
||||
{client && (
|
||||
<Animated.Text style={animatedTextStyle}>
|
||||
<Feather name="cast" size={22} />
|
||||
</Animated.Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
Reference in New Issue
Block a user