add back button and header layout to player

This commit is contained in:
Jorrin
2024-02-11 22:58:39 +01:00
parent 2dd7eb49bb
commit 5773f00cd3
12 changed files with 171 additions and 55 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

View File

@@ -42,7 +42,7 @@ export default function Searchbar({
ref={inputRef}
placeholder="What are you looking for?"
placeholderTextColor={Colors.secondary[200]}
className="w-full rounded-3xl py-3 pr-5 text-white focus-visible:outline-none"
className="w-full rounded-3xl py-3 pr-5 text-white"
/>
</View>
);

View File

@@ -17,14 +17,19 @@ import {
import { fetchMediaDetails } from "@movie-web/tmdb";
import type { ItemData } from "~/components/item/item";
import { usePlayer } from "../hooks/usePlayer";
import { Header } from "~/components/player/Header";
import { PlayerProvider, usePlayer } from "~/context/player.context";
export default function VideoPlayerWrapper() {
const params = useLocalSearchParams();
const data = params.data
? (JSON.parse(params.data as string) as ItemData)
: null;
return <VideoPlayer data={data} />;
return (
<PlayerProvider>
<VideoPlayer data={data} />
</PlayerProvider>
);
}
interface VideoPlayerProps {
@@ -37,7 +42,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
const [isLoading, setIsLoading] = useState(true);
const router = useRouter();
const {
videoRef,
setVideoRef,
unlockOrientation,
presentFullscreenPlayer,
dismissFullscreenPlayer,
@@ -99,6 +104,8 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
: [],
);
console.log("stream", url);
setVideoSrc({
uri: url,
headers: {
@@ -143,17 +150,19 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ data }) => {
return (
<View className="flex-1 items-center justify-center bg-black">
<Video
ref={videoRef}
ref={setVideoRef}
source={videoSrc}
// textTracks={textTracks} // breaks playback
className="absolute inset-0"
fullscreen={true}
fullscreen
paused={false}
controls={true}
controls
useSecureView
onLoadStart={onVideoLoadStart}
onReadyForDisplay={onReadyForDisplay}
/>
{isLoading && <ActivityIndicator size="large" color="#0000ff" />}
{!isLoading && <Header title="S8 E11 Rocky 8" />}
</View>
);
};

View File

@@ -0,0 +1,29 @@
import { useRouter } from "expo-router";
import { Ionicons } from "@expo/vector-icons";
import { usePlayer } from "~/context/player.context";
export const BackButton = ({
className,
}: Partial<React.ComponentProps<typeof Ionicons>>) => {
const { unlockOrientation } = usePlayer();
const router = useRouter();
return (
<Ionicons
name="arrow-back"
onPress={() => {
unlockOrientation()
.then(() => {
return router.back();
})
.catch(() => {
return router.back();
});
}}
size={36}
color="white"
className={className}
/>
);
};

View File

@@ -0,0 +1,22 @@
import { Image, View } from "react-native";
import Icon from "../../../assets/images/icon-transparent.png";
import { Text } from "../ui/Text";
import { BackButton } from "./BackButton";
interface HeaderProps {
title: string;
}
export const Header = ({ title }: HeaderProps) => {
return (
<View className="absolute top-0 flex w-full flex-row items-center justify-between px-6 pt-6">
<BackButton className="w-36" />
<Text className="font-bold">{title}</Text>
<View className="flex w-36 flex-row items-center justify-center gap-2 space-x-2 rounded-full bg-secondary-300 px-4 py-2 opacity-50">
<Image source={Icon} className="h-6 w-6" />
<Text className="font-bold">movie-web</Text>
</View>
</View>
);
};

View File

@@ -0,0 +1,77 @@
import type { VideoRef } from "react-native-video";
import React, { createContext, useCallback, useContext, useState } from "react";
import * as ScreenOrientation from "expo-screen-orientation";
interface PlayerContextProps {
videoRef: VideoRef | null;
setVideoRef: (ref: VideoRef | null) => void;
lockOrientation: () => Promise<void>;
unlockOrientation: () => Promise<void>;
presentFullscreenPlayer: () => Promise<void>;
dismissFullscreenPlayer: () => Promise<void>;
}
interface PlayerProviderProps {
children: React.ReactNode;
}
const PlayerContext = createContext<PlayerContextProps | undefined>(undefined);
export const PlayerProvider = ({ children }: PlayerProviderProps) => {
const [internalVideoRef, setInternalVideoRef] = useState<VideoRef | null>(
null,
);
const setVideoRef = useCallback((ref: VideoRef | null) => {
setInternalVideoRef(ref);
}, []);
const lockOrientation = useCallback(async () => {
await ScreenOrientation.lockAsync(
ScreenOrientation.OrientationLock.LANDSCAPE,
);
}, []);
const unlockOrientation = useCallback(async () => {
await ScreenOrientation.lockAsync(
ScreenOrientation.OrientationLock.PORTRAIT_UP,
);
}, []);
const presentFullscreenPlayer = useCallback(async () => {
if (internalVideoRef) {
internalVideoRef.presentFullscreenPlayer();
await lockOrientation();
}
}, [internalVideoRef, lockOrientation]);
const dismissFullscreenPlayer = useCallback(async () => {
if (internalVideoRef) {
internalVideoRef.dismissFullscreenPlayer();
await unlockOrientation();
}
}, [internalVideoRef, unlockOrientation]);
const contextValue: PlayerContextProps = {
videoRef: internalVideoRef,
setVideoRef,
lockOrientation,
unlockOrientation,
presentFullscreenPlayer,
dismissFullscreenPlayer,
};
return (
<PlayerContext.Provider value={contextValue}>
{children}
</PlayerContext.Provider>
);
};
export const usePlayer = (): PlayerContextProps => {
const context = useContext(PlayerContext);
if (!context) {
throw new Error("usePlayer must be used within a PlayerProvider");
}
return context;
};

View File

@@ -1,41 +0,0 @@
import type { VideoRef } from "react-native-video";
import { useCallback, useRef } from "react";
import * as ScreenOrientation from "expo-screen-orientation";
export const usePlayer = () => {
const ref = useRef<VideoRef>(null);
const lockOrientation = useCallback(async () => {
await ScreenOrientation.lockAsync(
ScreenOrientation.OrientationLock.LANDSCAPE,
);
}, []);
const unlockOrientation = useCallback(async () => {
await ScreenOrientation.lockAsync(
ScreenOrientation.OrientationLock.PORTRAIT_UP,
);
}, []);
const presentFullscreenPlayer = useCallback(async () => {
if (ref.current) {
ref.current.presentFullscreenPlayer();
await lockOrientation();
}
}, [lockOrientation]);
const dismissFullscreenPlayer = useCallback(async () => {
if (ref.current) {
ref.current.dismissFullscreenPlayer();
await unlockOrientation();
}
}, [unlockOrientation]);
return {
videoRef: ref,
lockOrientation,
unlockOrientation,
presentFullscreenPlayer,
dismissFullscreenPlayer,
} as const;
};

11
apps/expo/src/types/globals.d.ts vendored Normal file
View File

@@ -0,0 +1,11 @@
declare module "*.svg" {
import type { ImageSourcePropType } from "react-native";
const content: ImageSourcePropType;
export default content;
}
declare module "*.png" {
import type { ImageSourcePropType } from "react-native";
const content: ImageSourcePropType;
export default content;
}

View File

@@ -9,8 +9,6 @@
"build": "turbo build",
"clean": "git clean -xdf node_modules",
"clean:workspaces": "turbo clean",
"db:push": "pnpm -F db push",
"db:studio": "pnpm -F db studio",
"dev": "turbo dev --parallel",
"format": "turbo format --continue -- --cache --cache-location node_modules/.cache/.prettiercache",
"format:fix": "turbo format --continue -- --write --cache --cache-location node_modules/.cache/.prettiercache",

View File

@@ -29,7 +29,7 @@
},
"prettier": "@movie-web/prettier-config",
"dependencies": {
"@movie-web/providers": "^2.1.1",
"@movie-web/providers": "^2.2.0",
"tmdb-ts": "^1.6.1"
}
}

15
pnpm-lock.yaml generated
View File

@@ -168,8 +168,8 @@ importers:
packages/provider-utils:
dependencies:
'@movie-web/providers':
specifier: ^2.1.1
version: 2.1.1
specifier: ^2.2.0
version: 2.2.0
tmdb-ts:
specifier: ^1.6.1
version: 1.6.1
@@ -2348,15 +2348,17 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
/@movie-web/providers@2.1.1:
resolution: {integrity: sha512-g2CA/w3YlGw3b3v6yDSgUIUdym4rFs4CzZOo/OlyL4HtsFH9mk182ukt7HYSxgddCEJRjl81LZZc3/pLRIGcMA==}
/@movie-web/providers@2.2.0:
resolution: {integrity: sha512-7rKUpLPklwOtS5P2CAeh0P3sPIuYvtkKIgm0kVMp+OsSpKd9IcuYm79bbDrA0MDi3IMGik1W6la9Mzy91+8uYQ==}
dependencies:
cheerio: 1.0.0-rc.12
cookie: 0.6.0
crypto-js: 4.2.0
form-data: 4.0.0
iso-639-1: 3.1.0
nanoid: 3.3.7
node-fetch: 2.7.0
set-cookie-parser: 2.6.0
unpacker: 1.0.1
transitivePeerDependencies:
- encoding
@@ -4543,6 +4545,11 @@ packages:
engines: {node: '>= 0.6'}
dev: false
/cookie@0.6.0:
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
engines: {node: '>= 0.6'}
dev: false
/core-js-compat@3.35.1:
resolution: {integrity: sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw==}
dependencies:

View File

@@ -10,5 +10,9 @@ export default {
300: "#32324F",
700: "#131322",
},
iconBackground: "#14141c",
icon: {
background: "#14141c",
},
background: "#0a0a12",
};