mirror of
https://github.com/movie-web/native-app.git
synced 2025-09-13 18:13:25 +00:00
add episode download section
This commit is contained in:
@@ -3,10 +3,12 @@ import type { ContextMenuOnPressNativeEvent } from "react-native-context-menu-vi
|
||||
import React from "react";
|
||||
import ContextMenu from "react-native-context-menu-view";
|
||||
import { TouchableOpacity } from "react-native-gesture-handler";
|
||||
import { useRouter } from "expo-router";
|
||||
import { Image, Text, View, XStack, YStack } from "tamagui";
|
||||
|
||||
import type { Download } from "~/hooks/useDownloadManager";
|
||||
import type { Download, DownloadContent } from "~/hooks/useDownloadManager";
|
||||
import { useDownloadManager } from "~/hooks/useDownloadManager";
|
||||
import { mapSeasonAndEpisodeNumberToText } from "./player/utils";
|
||||
import { MWProgress } from "./ui/Progress";
|
||||
import { FlashingText } from "./ui/Text";
|
||||
|
||||
@@ -101,6 +103,11 @@ export function DownloadItem(props: DownloadItemProps) {
|
||||
<YStack gap="$2">
|
||||
<XStack gap="$6" maxWidth="65%">
|
||||
<Text fontWeight="$bold" ellipse flexGrow={1}>
|
||||
{props.item.media.type === "show" &&
|
||||
mapSeasonAndEpisodeNumberToText(
|
||||
props.item.media.season.number,
|
||||
props.item.media.episode.number,
|
||||
) + " "}
|
||||
{props.item.media.title}
|
||||
</Text>
|
||||
{props.item.type !== "hls" && (
|
||||
@@ -136,3 +143,54 @@ export function DownloadItem(props: DownloadItemProps) {
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
|
||||
export function ShowDownloadItem({ download }: { download: DownloadContent }) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() =>
|
||||
router.push({
|
||||
pathname: "/(downloads)/[tmdbId]",
|
||||
params: { tmdbId: download.media.tmdbId },
|
||||
})
|
||||
}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<XStack gap="$4" alignItems="center">
|
||||
<View
|
||||
aspectRatio={9 / 14}
|
||||
width={70}
|
||||
maxHeight={180}
|
||||
overflow="hidden"
|
||||
borderRadius="$2"
|
||||
>
|
||||
<Image
|
||||
source={{
|
||||
uri: "https://image.tmdb.org/t/p/original//or06FN3Dka5tukK1e9sl16pB3iy.jpg",
|
||||
}}
|
||||
width="100%"
|
||||
height="100%"
|
||||
/>
|
||||
</View>
|
||||
<YStack gap="$2">
|
||||
<YStack gap="$1">
|
||||
<Text fontWeight="$bold" ellipse flexGrow={1} fontSize="$5">
|
||||
{download.media.title}
|
||||
</Text>
|
||||
<Text fontSize="$2">
|
||||
{download.downloads.length} Episode
|
||||
{download.downloads.length > 1 ? "s" : ""} |{" "}
|
||||
{formatBytes(
|
||||
download.downloads.reduce(
|
||||
(acc, curr) => acc + curr.fileSize,
|
||||
0,
|
||||
),
|
||||
)}
|
||||
</Text>
|
||||
</YStack>
|
||||
</YStack>
|
||||
</XStack>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
@@ -124,6 +124,7 @@ export default function Item({ data }: { data: ItemData }) {
|
||||
width="100%"
|
||||
overflow="hidden"
|
||||
borderRadius={24}
|
||||
height="$14"
|
||||
>
|
||||
<Image source={{ uri: posterUrl }} width="100%" height="100%" />
|
||||
</View>
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { Linking } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import * as Haptics from "expo-haptics";
|
||||
import { FontAwesome6, MaterialIcons } from "@expo/vector-icons";
|
||||
import { Circle, View } from "tamagui";
|
||||
@@ -8,20 +7,13 @@ import { DISCORD_LINK, GITHUB_LINK } from "~/constants/core";
|
||||
import { BrandPill } from "../BrandPill";
|
||||
|
||||
export function Header() {
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
return (
|
||||
<View
|
||||
paddingTop={insets.top}
|
||||
alignItems="center"
|
||||
gap="$3"
|
||||
flexDirection="row"
|
||||
>
|
||||
<View alignItems="center" gap="$3" flexDirection="row">
|
||||
<BrandPill />
|
||||
|
||||
<Circle
|
||||
backgroundColor="$pillBackground"
|
||||
size="$4.5"
|
||||
size="$3.5"
|
||||
pressStyle={{
|
||||
opacity: 1,
|
||||
scale: 1.05,
|
||||
@@ -33,11 +25,11 @@ export function Header() {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)
|
||||
}
|
||||
>
|
||||
<MaterialIcons name="discord" size={32} color="white" />
|
||||
<MaterialIcons name="discord" size={28} color="white" />
|
||||
</Circle>
|
||||
<Circle
|
||||
backgroundColor="$pillBackground"
|
||||
size="$4.5"
|
||||
size="$3.5"
|
||||
pressStyle={{
|
||||
opacity: 1,
|
||||
scale: 1.05,
|
||||
@@ -49,7 +41,7 @@ export function Header() {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)
|
||||
}
|
||||
>
|
||||
<FontAwesome6 name="github" size={32} color="white" />
|
||||
<FontAwesome6 name="github" size={28} color="white" />
|
||||
</Circle>
|
||||
</View>
|
||||
);
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { ScrollView } from "tamagui";
|
||||
import { LinearGradient } from "tamagui/linear-gradient";
|
||||
|
||||
@@ -7,6 +8,7 @@ interface Props {
|
||||
children?: React.ReactNode;
|
||||
onScrollBeginDrag?: () => void;
|
||||
onMomentumScrollEnd?: () => void;
|
||||
showHeader?: boolean;
|
||||
scrollEnabled?: boolean;
|
||||
keyboardDismissMode?: "none" | "on-drag" | "interactive";
|
||||
keyboardShouldPersistTaps?: "always" | "never" | "handled";
|
||||
@@ -17,11 +19,14 @@ export default function ScreenLayout({
|
||||
children,
|
||||
onScrollBeginDrag,
|
||||
onMomentumScrollEnd,
|
||||
showHeader = true,
|
||||
scrollEnabled,
|
||||
keyboardDismissMode,
|
||||
keyboardShouldPersistTaps,
|
||||
contentContainerStyle,
|
||||
}: Props) {
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
return (
|
||||
<LinearGradient
|
||||
flex={1}
|
||||
@@ -38,8 +43,9 @@ export default function ScreenLayout({
|
||||
start={[0, 0]}
|
||||
end={[1, 1]}
|
||||
flexGrow={1}
|
||||
paddingTop={showHeader ? insets.top : insets.top + 50}
|
||||
>
|
||||
<Header />
|
||||
{showHeader && <Header />}
|
||||
<ScrollView
|
||||
onScrollBeginDrag={onScrollBeginDrag}
|
||||
onMomentumScrollEnd={onMomentumScrollEnd}
|
||||
|
@@ -4,10 +4,7 @@ import { usePlayerStore } from "~/stores/player/store";
|
||||
import { BrandPill } from "../BrandPill";
|
||||
import { BackButton } from "./BackButton";
|
||||
import { Controls } from "./Controls";
|
||||
|
||||
const mapSeasonAndEpisodeNumberToText = (season: number, episode: number) => {
|
||||
return `S${season.toString().padStart(2, "0")}E${episode.toString().padStart(2, "0")}`;
|
||||
};
|
||||
import { mapSeasonAndEpisodeNumberToText } from "./utils";
|
||||
|
||||
export const Header = () => {
|
||||
const isIdle = usePlayerStore((state) => state.interface.isIdle);
|
||||
|
@@ -16,3 +16,10 @@ export const mapMillisecondsToTime = (milliseconds: number): string => {
|
||||
|
||||
return formattedTime;
|
||||
};
|
||||
|
||||
export const mapSeasonAndEpisodeNumberToText = (
|
||||
season: number,
|
||||
episode: number,
|
||||
) => {
|
||||
return `S${season.toString().padStart(2, "0")}E${episode.toString().padStart(2, "0")}`;
|
||||
};
|
||||
|
Reference in New Issue
Block a user