cleanup brightness hooks

This commit is contained in:
Jorrin
2024-02-14 17:43:35 +01:00
parent a7392e92c9
commit ea6698b6e4
3 changed files with 81 additions and 60 deletions

View File

@@ -5,13 +5,11 @@ import {
Dimensions, Dimensions,
Platform, Platform,
StyleSheet, StyleSheet,
Text,
View, View,
} from "react-native"; } from "react-native";
import { Gesture, GestureDetector } from "react-native-gesture-handler"; import { Gesture, GestureDetector } from "react-native-gesture-handler";
import { runOnJS, useSharedValue } from "react-native-reanimated"; import { runOnJS, useSharedValue } from "react-native-reanimated";
import { ResizeMode, Video } from "expo-av"; import { ResizeMode, Video } from "expo-av";
import * as Brightness from "expo-brightness";
import * as NavigationBar from "expo-navigation-bar"; import * as NavigationBar from "expo-navigation-bar";
import { useLocalSearchParams, useRouter } from "expo-router"; import { useLocalSearchParams, useRouter } from "expo-router";
import * as StatusBar from "expo-status-bar"; import * as StatusBar from "expo-status-bar";
@@ -22,6 +20,8 @@ import { findHighestQuality } from "@movie-web/provider-utils";
import type { ItemData } from "~/components/item/item"; import type { ItemData } from "~/components/item/item";
import type { HeaderData } from "~/components/player/Header"; import type { HeaderData } from "~/components/player/Header";
import { ControlsOverlay } from "~/components/player/ControlsOverlay"; import { ControlsOverlay } from "~/components/player/ControlsOverlay";
import { Text } from "~/components/ui/Text";
import { useBrightness } from "~/hooks/player/useBrightness";
import { usePlayerStore } from "~/stores/player/store"; import { usePlayerStore } from "~/stores/player/store";
export default function VideoPlayerWrapper() { export default function VideoPlayerWrapper() {
@@ -43,16 +43,23 @@ interface VideoPlayerProps {
} }
const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => { const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
const {
brightness,
debouncedBrightness,
showBrightnessOverlay,
setShowBrightnessOverlay,
handleBrightnessChange,
} = useBrightness();
const [videoSrc, setVideoSrc] = useState<AVPlaybackSource>(); const [videoSrc, setVideoSrc] = useState<AVPlaybackSource>();
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [headerData, setHeaderData] = useState<HeaderData>(); const [headerData, setHeaderData] = useState<HeaderData>();
const [resizeMode, setResizeMode] = useState(ResizeMode.CONTAIN); const [resizeMode, setResizeMode] = useState(ResizeMode.CONTAIN);
const [shouldPlay, setShouldPlay] = useState(true); const [shouldPlay, setShouldPlay] = useState(true);
const [showVolumeOverlay, setShowVolumeOverlay] = useState(false); const [showVolumeOverlay, setShowVolumeOverlay] = useState(false);
const [showBrightnessOverlay, setShowBrightnessOverlay] = useState(false);
const [currentVolume, setCurrentVolume] = useState(0.5); const [currentVolume, setCurrentVolume] = useState(0.5);
const router = useRouter(); const router = useRouter();
const scale = useSharedValue(1); const scale = useSharedValue(1);
const setVideoRef = usePlayerStore((state) => state.setVideoRef); const setVideoRef = usePlayerStore((state) => state.setVideoRef);
const setStatus = usePlayerStore((state) => state.setStatus); const setStatus = usePlayerStore((state) => state.setStatus);
const isIdle = usePlayerStore((state) => state.interface.isIdle); const isIdle = usePlayerStore((state) => state.interface.isIdle);
@@ -87,35 +94,15 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
runOnJS(togglePlayback)(); runOnJS(togglePlayback)();
}); });
const brightness = useSharedValue(0.5);
const handleVolumeChange = (newValue: number) => { const handleVolumeChange = (newValue: number) => {
setCurrentVolume(newValue); setCurrentVolume(newValue);
setShowVolumeOverlay(true); setShowVolumeOverlay(true);
setTimeout(() => setShowVolumeOverlay(false), 2000); setTimeout(() => setShowVolumeOverlay(false), 2000);
}; };
const handleBrightnessChange = async (newValue: number) => {
try {
await Brightness.setBrightnessAsync(newValue);
setShowBrightnessOverlay(true);
setTimeout(() => setShowBrightnessOverlay(false), 2000);
} catch (error) {
console.error("Failed to set brightness:", error);
}
};
const screenHalfWidth = Dimensions.get("window").width / 2; const screenHalfWidth = Dimensions.get("window").width / 2;
const panGesture = Gesture.Pan() const panGesture = Gesture.Pan()
.onStart((event) => {
const isRightHalf = event.x > screenHalfWidth;
if (isRightHalf) {
runOnJS(setCurrentVolume)(0.5);
} else {
brightness.value = 0.5;
}
})
.onUpdate((event) => { .onUpdate((event) => {
const divisor = 5000; const divisor = 5000;
if (event.x > screenHalfWidth) { if (event.x > screenHalfWidth) {
@@ -131,6 +118,9 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
brightness.value = newBrightness; brightness.value = newBrightness;
runOnJS(handleBrightnessChange)(newBrightness); runOnJS(handleBrightnessChange)(newBrightness);
} }
})
.onEnd(() => {
runOnJS(setShowBrightnessOverlay)(false);
}); });
const composedGesture = Gesture.Exclusive( const composedGesture = Gesture.Exclusive(
@@ -152,18 +142,6 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
await NavigationBar.setVisibilityAsync("hidden"); await NavigationBar.setVisibilityAsync("hidden");
} }
const { status } = await Brightness.requestPermissionsAsync();
if (status !== Brightness.PermissionStatus.GRANTED) {
console.warn("Brightness permissions not granted");
}
try {
const currentBrightness = await Brightness.getBrightnessAsync();
brightness.value = currentBrightness;
} catch (error) {
console.error("Failed to get initial brightness:", error);
}
setIsLoading(true); setIsLoading(true);
const { item, stream, media } = data; const { item, stream, media } = data;
@@ -215,13 +193,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
void NavigationBar.setVisibilityAsync("visible"); void NavigationBar.setVisibilityAsync("visible");
} }
}; };
}, [ }, [data, dismissFullscreenPlayer, presentFullscreenPlayer, router]);
brightness,
data,
dismissFullscreenPlayer,
presentFullscreenPlayer,
router,
]);
const onVideoLoadStart = () => { const onVideoLoadStart = () => {
setIsLoading(true); setIsLoading(true);
@@ -251,17 +223,15 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
<ControlsOverlay headerData={headerData} /> <ControlsOverlay headerData={headerData} />
)} )}
{showVolumeOverlay && ( {showVolumeOverlay && (
<View style={styles.overlay}> <View className="absolute bottom-12 self-center rounded-xl bg-black p-3 opacity-50">
<Text style={styles.overlayText}> <Text className="text-bold">
Volume: {Math.round(currentVolume * 100)}% Volume: {Math.round(currentVolume * 100)}%
</Text> </Text>
</View> </View>
)} )}
{showBrightnessOverlay && ( {showBrightnessOverlay && (
<View style={styles.overlay}> <View className="bottom-12 self-center rounded-xl bg-black p-3 opacity-50">
<Text style={styles.overlayText}> <Text className="font-bold">Brightness: {debouncedBrightness}</Text>
Brightness: {Math.round(brightness.value * 100)}%
</Text>
</View> </View>
)} )}
</View> </View>
@@ -307,16 +277,4 @@ const styles = StyleSheet.create({
left: 0, left: 0,
right: 0, right: 0,
}, },
overlay: {
position: "absolute",
bottom: 50,
alignSelf: "center",
backgroundColor: "rgba(0,0,0,0.5)",
padding: 10,
borderRadius: 5,
},
overlayText: {
color: "#fff",
fontSize: 16,
},
}); });

View File

@@ -0,0 +1,46 @@
import { useCallback, useEffect, useState } from "react";
import { useSharedValue } from "react-native-reanimated";
import * as Brightness from "expo-brightness";
import { useDebounce } from "../useDebounce";
export const useBrightness = () => {
const [showBrightnessOverlay, setShowBrightnessOverlay] = useState(false);
const debouncedShowBrightnessOverlay = useDebounce(showBrightnessOverlay, 20);
const brightness = useSharedValue(0.5);
const debouncedBrightness = useDebounce(brightness.value, 20);
useEffect(() => {
async function init() {
try {
const { status } = await Brightness.requestPermissionsAsync();
if (status === Brightness.PermissionStatus.GRANTED) {
const currentBrightness = await Brightness.getBrightnessAsync();
brightness.value = currentBrightness;
}
} catch (error) {
console.error("Failed to get brightness permissions:", error);
}
}
void init();
}, []);
const handleBrightnessChange = useCallback(async (newValue: number) => {
try {
setShowBrightnessOverlay(true);
brightness.value = newValue;
await Brightness.setBrightnessAsync(newValue);
} catch (error) {
console.error("Failed to set brightness:", error);
}
}, []);
return {
showBrightnessOverlay: debouncedShowBrightnessOverlay,
brightness,
debouncedBrightness: `${Math.round(debouncedBrightness * 100)}%`,
setShowBrightnessOverlay,
handleBrightnessChange,
} as const;
};

View File

@@ -0,0 +1,17 @@
import { useEffect, useState } from "react";
export const useDebounce = <T>(value: T, delay?: number): T => {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay ?? 500);
return () => {
clearTimeout(timer);
};
}, [value, delay]);
return debouncedValue;
};