mirror of
https://github.com/movie-web/native-app.git
synced 2025-09-13 14:53:24 +00:00
feat: gesture controls
This commit is contained in:
@@ -27,6 +27,7 @@ const defineConfig = (): ExpoConfig => ({
|
|||||||
foregroundImage: "./assets/images/adaptive-icon.png",
|
foregroundImage: "./assets/images/adaptive-icon.png",
|
||||||
backgroundColor: "#FFFFFF",
|
backgroundColor: "#FFFFFF",
|
||||||
},
|
},
|
||||||
|
permissions: ["WRITE_SETTINGS"],
|
||||||
},
|
},
|
||||||
web: {
|
web: {
|
||||||
favicon: "./assets/images/favicon.png",
|
favicon: "./assets/images/favicon.png",
|
||||||
|
@@ -27,6 +27,7 @@
|
|||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"expo": "~50.0.5",
|
"expo": "~50.0.5",
|
||||||
"expo-av": "~13.10.5",
|
"expo-av": "~13.10.5",
|
||||||
|
"expo-brightness": "~11.8.0",
|
||||||
"expo-build-properties": "~0.11.1",
|
"expo-build-properties": "~0.11.1",
|
||||||
"expo-constants": "~15.4.5",
|
"expo-constants": "~15.4.5",
|
||||||
"expo-linking": "~6.2.2",
|
"expo-linking": "~6.2.2",
|
||||||
|
@@ -1,9 +1,16 @@
|
|||||||
import type { AVPlaybackSource } from "expo-av";
|
import type { AVPlaybackSource } from "expo-av";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { ActivityIndicator, Platform, StyleSheet, View } from "react-native";
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Dimensions,
|
||||||
|
Platform,
|
||||||
|
StyleSheet,
|
||||||
|
View,
|
||||||
|
} 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";
|
||||||
@@ -40,6 +47,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
|
|||||||
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 [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);
|
||||||
@@ -76,7 +84,53 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
|
|||||||
runOnJS(togglePlayback)();
|
runOnJS(togglePlayback)();
|
||||||
});
|
});
|
||||||
|
|
||||||
const composedGesture = Gesture.Exclusive(pinchGesture, doubleTapGesture);
|
const brightness = useSharedValue(0.5);
|
||||||
|
|
||||||
|
const handleVolumeChange = (newValue: number) => {
|
||||||
|
setCurrentVolume(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBrightnessChange = async (newValue: number) => {
|
||||||
|
try {
|
||||||
|
await Brightness.setBrightnessAsync(newValue);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to set brightness:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const screenHalfWidth = Dimensions.get("window").width / 2;
|
||||||
|
|
||||||
|
const panGesture = Gesture.Pan()
|
||||||
|
.onStart((event) => {
|
||||||
|
const isRightHalf = event.x > screenHalfWidth;
|
||||||
|
if (isRightHalf) {
|
||||||
|
runOnJS(setCurrentVolume)(0.5);
|
||||||
|
} else {
|
||||||
|
brightness.value = 0.5;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.onUpdate((event) => {
|
||||||
|
const divisor = 5000;
|
||||||
|
if (event.x > screenHalfWidth) {
|
||||||
|
const change = -event.translationY / divisor;
|
||||||
|
const newVolume = Math.max(0, Math.min(1, currentVolume + change));
|
||||||
|
runOnJS(handleVolumeChange)(newVolume);
|
||||||
|
} else {
|
||||||
|
const change = -event.translationY / divisor;
|
||||||
|
const newBrightness = Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(1, brightness.value + change),
|
||||||
|
);
|
||||||
|
brightness.value = newBrightness;
|
||||||
|
runOnJS(handleBrightnessChange)(newBrightness);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const composedGesture = Gesture.Exclusive(
|
||||||
|
panGesture,
|
||||||
|
pinchGesture,
|
||||||
|
doubleTapGesture,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initializePlayer = async () => {
|
const initializePlayer = async () => {
|
||||||
@@ -90,6 +144,19 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
|
|||||||
if (Platform.OS === "android") {
|
if (Platform.OS === "android") {
|
||||||
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;
|
||||||
@@ -141,7 +208,13 @@ 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);
|
||||||
@@ -159,6 +232,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
|
|||||||
source={videoSrc}
|
source={videoSrc}
|
||||||
shouldPlay={shouldPlay}
|
shouldPlay={shouldPlay}
|
||||||
resizeMode={resizeMode}
|
resizeMode={resizeMode}
|
||||||
|
volume={currentVolume}
|
||||||
onLoadStart={onVideoLoadStart}
|
onLoadStart={onVideoLoadStart}
|
||||||
onReadyForDisplay={onReadyForDisplay}
|
onReadyForDisplay={onReadyForDisplay}
|
||||||
onPlaybackStatusUpdate={setStatus}
|
onPlaybackStatusUpdate={setStatus}
|
||||||
|
11
pnpm-lock.yaml
generated
11
pnpm-lock.yaml
generated
@@ -58,6 +58,9 @@ importers:
|
|||||||
expo-av:
|
expo-av:
|
||||||
specifier: ~13.10.5
|
specifier: ~13.10.5
|
||||||
version: 13.10.5(expo@50.0.5)
|
version: 13.10.5(expo@50.0.5)
|
||||||
|
expo-brightness:
|
||||||
|
specifier: ~11.8.0
|
||||||
|
version: 11.8.0(expo@50.0.5)
|
||||||
expo-build-properties:
|
expo-build-properties:
|
||||||
specifier: ~0.11.1
|
specifier: ~0.11.1
|
||||||
version: 0.11.1(expo@50.0.5)
|
version: 0.11.1(expo@50.0.5)
|
||||||
@@ -5517,6 +5520,14 @@ packages:
|
|||||||
expo: 50.0.5(@babel/core@7.23.9)(@react-native/babel-preset@0.73.20)
|
expo: 50.0.5(@babel/core@7.23.9)(@react-native/babel-preset@0.73.20)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/expo-brightness@11.8.0(expo@50.0.5):
|
||||||
|
resolution: {integrity: sha512-ipQA7s8PvJVhy+Ls6Dsql0veXXV5CdMcbXNPwQuXTbUofRE+8FHO0vasShMZlKYcD9KNgFygjx0U+THi80dtAw==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
dependencies:
|
||||||
|
expo: 50.0.5(@babel/core@7.23.9)(@react-native/babel-preset@0.73.20)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/expo-build-properties@0.11.1(expo@50.0.5):
|
/expo-build-properties@0.11.1(expo@50.0.5):
|
||||||
resolution: {integrity: sha512-m4j4aEjFaDuBE6KWYMxDhWgLzzSmpE7uHKAwtvXyNmRK+6JKF0gjiXi0sXgI5ngNppDQpsyPFMvqG7uQpRuCuw==}
|
resolution: {integrity: sha512-m4j4aEjFaDuBE6KWYMxDhWgLzzSmpE7uHKAwtvXyNmRK+6JKF0gjiXi0sXgI5ngNppDQpsyPFMvqG7uQpRuCuw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
Reference in New Issue
Block a user