diff --git a/apps/expo/src/app/(tabs)/_layout.tsx b/apps/expo/src/app/(tabs)/_layout.tsx
index e927659..84c88b0 100644
--- a/apps/expo/src/app/(tabs)/_layout.tsx
+++ b/apps/expo/src/app/(tabs)/_layout.tsx
@@ -1,3 +1,4 @@
+import { useRef } from "react";
import { Platform, View } from "react-native";
import { Tabs } from "expo-router";
@@ -6,87 +7,102 @@ import Colors from "@movie-web/tailwind-config/colors";
import { MovieWebSvg } from "~/components/Icon";
import SvgTabBarIcon from "~/components/SvgTabBarIcon";
import TabBarIcon from "~/components/TabBarIcon";
+import SearchTabContext from "./search/SearchTabContext";
export default function TabLayout() {
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ const focusSearchInputRef = useRef(() => {});
+
return (
-
+ ({
+ tabPress: () => {
+ switch (route.name) {
+ case "search":
+ focusSearchInputRef.current();
+ break;
+ }
},
- ],
- }}
- >
- (
-
- ),
+ })}
+ screenOptions={{
+ headerShown: false,
+ tabBarActiveTintColor: Colors.primary[100],
+ tabBarStyle: {
+ backgroundColor: Colors.secondary[700],
+ borderTopColor: "transparent",
+ borderTopRightRadius: 20,
+ borderTopLeftRadius: 20,
+ paddingBottom: Platform.select({ ios: 100 }),
+ height: 80,
+ },
+ tabBarItemStyle: {
+ paddingVertical: 18,
+ height: 82,
+ },
+ tabBarLabelStyle: [
+ {
+ marginTop: 2,
+ },
+ ],
}}
- />
- (
-
- ),
- }}
- />
- (
-
-
-
- ),
- }}
- />
- (
-
-
-
- ),
- }}
- />
- (
-
- ),
- }}
- />
-
+ >
+ (
+
+ ),
+ }}
+ />
+ (
+
+ ),
+ }}
+ />
+ (
+
+
+
+ ),
+ }}
+ />
+ (
+
+
+
+ ),
+ }}
+ />
+ (
+
+ ),
+ }}
+ />
+
+
);
}
diff --git a/apps/expo/src/app/(tabs)/search/SearchTabContext.tsx b/apps/expo/src/app/(tabs)/search/SearchTabContext.tsx
new file mode 100644
index 0000000..ab67870
--- /dev/null
+++ b/apps/expo/src/app/(tabs)/search/SearchTabContext.tsx
@@ -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;
diff --git a/apps/expo/src/app/(tabs)/search/Searchbar.tsx b/apps/expo/src/app/(tabs)/search/Searchbar.tsx
index 0125e92..a662ec8 100644
--- a/apps/expo/src/app/(tabs)/search/Searchbar.tsx
+++ b/apps/expo/src/app/(tabs)/search/Searchbar.tsx
@@ -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 { useFocusEffect } from "expo-router";
import { FontAwesome5 } from "@expo/vector-icons";
import Colors from "@movie-web/tailwind-config/colors";
+import SearchTabContext from "./SearchTabContext";
+
export default function Searchbar({
onSearchChange,
}: {
@@ -13,6 +15,14 @@ export default function Searchbar({
const [keyword, setKeyword] = useState("");
const inputRef = useRef(null);
+ const { focusSearchInputRef } = useContext(SearchTabContext);
+
+ useEffect(() => {
+ focusSearchInputRef.current = () => {
+ inputRef.current?.focus();
+ };
+ }, [focusSearchInputRef]);
+
useFocusEffect(
useCallback(() => {
// When the screen is focused
diff --git a/apps/expo/src/app/(tabs)/search/_layout.tsx b/apps/expo/src/app/(tabs)/search/_layout.tsx
index a0d07ca..2e2465a 100644
--- a/apps/expo/src/app/(tabs)/search/_layout.tsx
+++ b/apps/expo/src/app/(tabs)/search/_layout.tsx
@@ -80,7 +80,7 @@ export default function SearchScreen() {
0}
+ scrollEnabled={data && data.length > 0 ? true : false}
keyboardDismissMode="on-drag"
keyboardShouldPersistTaps="handled"
>
diff --git a/apps/expo/src/components/player/VideoPlayer.tsx b/apps/expo/src/components/player/VideoPlayer.tsx
index c3f5277..2e71865 100644
--- a/apps/expo/src/components/player/VideoPlayer.tsx
+++ b/apps/expo/src/components/player/VideoPlayer.tsx
@@ -47,6 +47,7 @@ export const VideoPlayer = () => {
const [hasStartedPlaying, setHasStartedPlaying] = useState(false);
const router = useRouter();
const scale = useSharedValue(1);
+ const [lastVelocityY, setLastVelocityY] = useState(0);
const isIdle = usePlayerStore((state) => state.interface.isIdle);
const stream = usePlayerStore((state) => state.interface.currentStream);
@@ -87,26 +88,34 @@ export const VideoPlayer = () => {
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) {
- const change =
- directionMultiplier * Math.abs(event.velocityY / divisor);
- const newVolume = Math.max(
- 0,
- Math.min(1, currentVolume.value + change),
- );
runOnJS(handleVolumeChange)(newVolume);
} else {
- const change =
- directionMultiplier * Math.abs(event.velocityY / divisor);
- const newBrightness = Math.max(
- 0,
- Math.min(1, brightness.value + change),
- );
brightness.value = 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(() => {
+ runOnJS(setLastVelocityY)(0);
runOnJS(setShowVolumeOverlay)(false);
runOnJS(setShowBrightnessOverlay)(false);
});
diff --git a/apps/expo/src/hooks/player/useBrightness.ts b/apps/expo/src/hooks/player/useBrightness.ts
index 844c4b2..4e6bfe5 100644
--- a/apps/expo/src/hooks/player/useBrightness.ts
+++ b/apps/expo/src/hooks/player/useBrightness.ts
@@ -24,17 +24,20 @@ export const useBrightness = () => {
}
void init();
- }, []);
+ }, [brightness]);
- 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);
- }
- }, []);
+ 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);
+ }
+ },
+ [brightness],
+ );
return {
showBrightnessOverlay: debouncedShowBrightnessOverlay,
diff --git a/apps/expo/src/hooks/player/useVolume.ts b/apps/expo/src/hooks/player/useVolume.ts
index 42e5661..4722bf5 100644
--- a/apps/expo/src/hooks/player/useVolume.ts
+++ b/apps/expo/src/hooks/player/useVolume.ts
@@ -9,10 +9,13 @@ export const useVolume = () => {
const volume = useSharedValue(1);
const debouncedVolume = useDebounce(volume.value, 20);
- const handleVolumeChange = useCallback((newValue: number) => {
- volume.value = newValue;
- setShowVolumeOverlay(true);
- }, []);
+ const handleVolumeChange = useCallback(
+ (newValue: number) => {
+ volume.value = newValue;
+ setShowVolumeOverlay(true);
+ },
+ [volume],
+ );
return {
showVolumeOverlay: debouncedShowVolumeOverlay,