Merge branch 'feat-providers-video' of https://github.com/castdrian/mw-native into pr/9

This commit is contained in:
Jorrin
2024-02-20 21:53:28 +01:00
7 changed files with 154 additions and 105 deletions

View File

@@ -1,3 +1,4 @@
import { useRef } from "react";
import { Platform, View } from "react-native"; import { Platform, View } from "react-native";
import { Tabs } from "expo-router"; import { Tabs } from "expo-router";
@@ -6,87 +7,102 @@ import Colors from "@movie-web/tailwind-config/colors";
import { MovieWebSvg } from "~/components/Icon"; import { MovieWebSvg } from "~/components/Icon";
import SvgTabBarIcon from "~/components/SvgTabBarIcon"; import SvgTabBarIcon from "~/components/SvgTabBarIcon";
import TabBarIcon from "~/components/TabBarIcon"; import TabBarIcon from "~/components/TabBarIcon";
import SearchTabContext from "./search/SearchTabContext";
export default function TabLayout() { export default function TabLayout() {
// eslint-disable-next-line @typescript-eslint/no-empty-function
const focusSearchInputRef = useRef(() => {});
return ( return (
<Tabs <SearchTabContext.Provider value={{ focusSearchInputRef }}>
sceneContainerStyle={{ <Tabs
backgroundColor: Colors.background, sceneContainerStyle={{
}} backgroundColor: Colors.background,
screenOptions={{ }}
headerShown: false, screenListeners={({ route }) => ({
tabBarActiveTintColor: Colors.primary[100], tabPress: () => {
tabBarStyle: { switch (route.name) {
backgroundColor: Colors.secondary[700], case "search":
borderTopColor: "transparent", focusSearchInputRef.current();
borderTopRightRadius: 20, break;
borderTopLeftRadius: 20, }
paddingBottom: Platform.select({ ios: 100 }),
height: 80,
},
tabBarItemStyle: {
paddingVertical: 18,
height: 82,
},
tabBarLabelStyle: [
{
marginTop: 2,
}, },
], })}
}} screenOptions={{
> headerShown: false,
<Tabs.Screen tabBarActiveTintColor: Colors.primary[100],
name="index" tabBarStyle: {
options={{ backgroundColor: Colors.secondary[700],
title: "Home", borderTopColor: "transparent",
tabBarIcon: ({ focused }) => ( borderTopRightRadius: 20,
<TabBarIcon name="home" focused={focused} /> borderTopLeftRadius: 20,
), paddingBottom: Platform.select({ ios: 100 }),
height: 80,
},
tabBarItemStyle: {
paddingVertical: 18,
height: 82,
},
tabBarLabelStyle: [
{
marginTop: 2,
},
],
}} }}
/> >
<Tabs.Screen <Tabs.Screen
name="downloads" name="index"
options={{ options={{
title: "Downloads", title: "Home",
tabBarIcon: ({ focused }) => ( tabBarIcon: ({ focused }) => (
<TabBarIcon name="download" focused={focused} /> <TabBarIcon name="home" focused={focused} />
), ),
}} }}
/> />
<Tabs.Screen <Tabs.Screen
name="search" name="downloads"
options={{ options={{
title: "Search", title: "Downloads",
tabBarLabel: "", tabBarIcon: ({ focused }) => (
tabBarIcon: ({ focused }) => ( <TabBarIcon name="download" focused={focused} />
<View ),
className={`android:top-2 ios:top-2 flex h-14 w-14 items-center justify-center overflow-hidden rounded-full ${focused ? "bg-primary-300" : "bg-primary-400"} text-center align-middle text-2xl text-white`} }}
> />
<TabBarIcon name="search" color="#FFF" /> <Tabs.Screen
</View> name="search"
), options={{
}} title: "Search",
/> tabBarLabel: "",
<Tabs.Screen tabBarIcon: ({ focused }) => (
name="movie-web" <View
options={{ className={`android:top-2 ios:top-2 flex h-14 w-14 items-center justify-center overflow-hidden rounded-full ${focused ? "bg-primary-300" : "bg-primary-400"} text-center align-middle text-2xl text-white`}
title: "movie-web", >
tabBarIcon: ({ focused }) => ( <TabBarIcon name="search" color="#FFF" />
<SvgTabBarIcon focused={focused}> </View>
<MovieWebSvg /> ),
</SvgTabBarIcon> }}
), />
}} <Tabs.Screen
/> name="movie-web"
<Tabs.Screen options={{
name="settings" title: "movie-web",
options={{ tabBarIcon: ({ focused }) => (
title: "Settings", <SvgTabBarIcon focused={focused}>
tabBarIcon: ({ focused }) => ( <MovieWebSvg />
<TabBarIcon name="cog" focused={focused} /> </SvgTabBarIcon>
), ),
}} }}
/> />
</Tabs> <Tabs.Screen
name="settings"
options={{
title: "Settings",
tabBarIcon: ({ focused }) => (
<TabBarIcon name="cog" focused={focused} />
),
}}
/>
</Tabs>
</SearchTabContext.Provider>
); );
} }

View File

@@ -0,0 +1,8 @@
import React from "react";
const SearchTabContext = React.createContext({
// eslint-disable-next-line @typescript-eslint/no-empty-function
focusSearchInputRef: { current: () => {} },
});
export default SearchTabContext;

View File

@@ -1,10 +1,12 @@
import { useCallback, useRef, useState } from "react"; import { useCallback, useContext, useEffect, useRef, useState } from "react";
import { TextInput, View } from "react-native"; import { TextInput, View } from "react-native";
import { useFocusEffect } from "expo-router"; import { useFocusEffect } from "expo-router";
import { FontAwesome5 } from "@expo/vector-icons"; import { FontAwesome5 } from "@expo/vector-icons";
import Colors from "@movie-web/tailwind-config/colors"; import Colors from "@movie-web/tailwind-config/colors";
import SearchTabContext from "./SearchTabContext";
export default function Searchbar({ export default function Searchbar({
onSearchChange, onSearchChange,
}: { }: {
@@ -13,6 +15,14 @@ export default function Searchbar({
const [keyword, setKeyword] = useState(""); const [keyword, setKeyword] = useState("");
const inputRef = useRef<TextInput>(null); const inputRef = useRef<TextInput>(null);
const { focusSearchInputRef } = useContext(SearchTabContext);
useEffect(() => {
focusSearchInputRef.current = () => {
inputRef.current?.focus();
};
}, [focusSearchInputRef]);
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
// When the screen is focused // When the screen is focused

View File

@@ -80,7 +80,7 @@ export default function SearchScreen() {
<ScrollView <ScrollView
onScrollBeginDrag={handleScrollBegin} onScrollBeginDrag={handleScrollBegin}
onMomentumScrollEnd={handleScrollEnd} onMomentumScrollEnd={handleScrollEnd}
scrollEnabled={data && data.length > 0} scrollEnabled={data && data.length > 0 ? true : false}
keyboardDismissMode="on-drag" keyboardDismissMode="on-drag"
keyboardShouldPersistTaps="handled" keyboardShouldPersistTaps="handled"
> >

View File

@@ -47,6 +47,7 @@ export const VideoPlayer = () => {
const [hasStartedPlaying, setHasStartedPlaying] = useState(false); const [hasStartedPlaying, setHasStartedPlaying] = useState(false);
const router = useRouter(); const router = useRouter();
const scale = useSharedValue(1); const scale = useSharedValue(1);
const [lastVelocityY, setLastVelocityY] = useState(0);
const isIdle = usePlayerStore((state) => state.interface.isIdle); const isIdle = usePlayerStore((state) => state.interface.isIdle);
const stream = usePlayerStore((state) => state.interface.currentStream); const stream = usePlayerStore((state) => state.interface.currentStream);
@@ -87,26 +88,34 @@ export const VideoPlayer = () => {
const directionMultiplier = event.velocityY < 0 ? 1 : -1; const directionMultiplier = event.velocityY < 0 ? 1 : -1;
const change = directionMultiplier * Math.abs(event.velocityY / divisor);
const newVolume = Math.max(0, Math.min(1, currentVolume.value + change));
const newBrightness = Math.max(0, Math.min(1, brightness.value + change));
if (event.x > screenHalfWidth) { if (event.x > screenHalfWidth) {
const change =
directionMultiplier * Math.abs(event.velocityY / divisor);
const newVolume = Math.max(
0,
Math.min(1, currentVolume.value + change),
);
runOnJS(handleVolumeChange)(newVolume); runOnJS(handleVolumeChange)(newVolume);
} else { } else {
const change =
directionMultiplier * Math.abs(event.velocityY / divisor);
const newBrightness = Math.max(
0,
Math.min(1, brightness.value + change),
);
brightness.value = newBrightness; brightness.value = newBrightness;
runOnJS(handleBrightnessChange)(newBrightness); runOnJS(handleBrightnessChange)(newBrightness);
} }
if (
(event.velocityY < 0 && lastVelocityY >= 0) ||
(event.velocityY >= 0 && lastVelocityY < 0)
) {
runOnJS(setLastVelocityY)(event.velocityY);
}
if (event.x > screenHalfWidth) {
runOnJS(handleVolumeChange)(newVolume);
runOnJS(setShowVolumeOverlay)(true);
} else {
runOnJS(handleBrightnessChange)(newBrightness);
runOnJS(setShowBrightnessOverlay)(true);
}
}) })
.onEnd(() => { .onEnd(() => {
runOnJS(setLastVelocityY)(0);
runOnJS(setShowVolumeOverlay)(false); runOnJS(setShowVolumeOverlay)(false);
runOnJS(setShowBrightnessOverlay)(false); runOnJS(setShowBrightnessOverlay)(false);
}); });

View File

@@ -24,17 +24,20 @@ export const useBrightness = () => {
} }
void init(); void init();
}, []); }, [brightness]);
const handleBrightnessChange = useCallback(async (newValue: number) => { const handleBrightnessChange = useCallback(
try { async (newValue: number) => {
setShowBrightnessOverlay(true); try {
brightness.value = newValue; setShowBrightnessOverlay(true);
await Brightness.setBrightnessAsync(newValue); brightness.value = newValue;
} catch (error) { await Brightness.setBrightnessAsync(newValue);
console.error("Failed to set brightness:", error); } catch (error) {
} console.error("Failed to set brightness:", error);
}, []); }
},
[brightness],
);
return { return {
showBrightnessOverlay: debouncedShowBrightnessOverlay, showBrightnessOverlay: debouncedShowBrightnessOverlay,

View File

@@ -9,10 +9,13 @@ export const useVolume = () => {
const volume = useSharedValue(1); const volume = useSharedValue(1);
const debouncedVolume = useDebounce(volume.value, 20); const debouncedVolume = useDebounce(volume.value, 20);
const handleVolumeChange = useCallback((newValue: number) => { const handleVolumeChange = useCallback(
volume.value = newValue; (newValue: number) => {
setShowVolumeOverlay(true); volume.value = newValue;
}, []); setShowVolumeOverlay(true);
},
[volume],
);
return { return {
showVolumeOverlay: debouncedShowVolumeOverlay, showVolumeOverlay: debouncedShowVolumeOverlay,