diff --git a/src/api/hot-videos.ts b/src/api/hot-videos.ts index 712c937..8da922a 100644 --- a/src/api/hot-videos.ts +++ b/src/api/hot-videos.ts @@ -44,11 +44,11 @@ export type VideoItem = ReturnType // https://api.bilibili.com/x/web-interface/popular?ps=20&pn=1 -export function useHotVideos() { +export function useHotVideos(t: number) { const { data, mutate, size, setSize, isValidating, isLoading, error } = useSWRInfinite( index => { - return `/x/web-interface/popular?ps=30&pn=${index + 1}` + return `/x/web-interface/popular?ps=30&pn=${index + 1}&_t=${t}` }, // fetcher2, { diff --git a/src/api/play-url.ts b/src/api/play-url.ts index 92798b1..50ef080 100644 --- a/src/api/play-url.ts +++ b/src/api/play-url.ts @@ -1,15 +1,12 @@ import useSWR from 'swr' import { z } from 'zod' +import request from './fetcher' import { PlayUrlResponseSchema } from './play-url.schema' type Res = z.infer -export function usePlayUrl( - bvid: string, - cid?: string | number, - highQuality = true, -) { +export function usePlayUrl(bvid: string, cid?: number) { const search = new URLSearchParams() // https://socialsisteryi.github.io/bilibili-API-collect/docs/video/videostream_url.html if (bvid && cid) { @@ -17,11 +14,11 @@ export function usePlayUrl( bvid, cid, type: 'mp4', - qn: highQuality ? 64 : 16, + qn: 64, fnval: 1, try_look: 1, platform: 'pc', - high_quality: highQuality ? 1 : 0, + high_quality: 1, } Object.entries(query).forEach(([k, v]) => { search.append(k, v + '') @@ -35,9 +32,40 @@ export function usePlayUrl( }, ) return data?.durl + ? data.durl[0]?.backup_url?.[0] || data.durl[0]?.url || '' + : null +} + +export function getDownloadUrl(bvid: string, cid?: number) { + const search = new URLSearchParams() + if (!bvid || !cid) { + return + } + // https://socialsisteryi.github.io/bilibili-API-collect/docs/video/videostream_url.html + + const query = { + bvid, + cid, + type: 'mp4', + qn: 64, + fnval: 4048, + platform: 'html5', + high_quality: 1, + try_look: 1, + } + Object.entries(query).forEach(([k, v]) => { + search.append(k, v + '') + }) + return request(`/x/player/wbi/playurl?${search}`).then(res => { + return res.durl?.[0]?.url + }) } -export function useVideoDownloadUrl(bvid: string, cid?: string | number) { +export function useVideoDownloadUrl( + bvid: string, + cid: string | number, + onSuccess: (d?: Res['durl']) => void, +) { const search = new URLSearchParams() // https://socialsisteryi.github.io/bilibili-API-collect/docs/video/videostream_url.html if (bvid && cid) { @@ -55,11 +83,13 @@ export function useVideoDownloadUrl(bvid: string, cid?: string | number) { search.append(k, v + '') }) } - const { data } = useSWR( bvid && cid ? `/x/player/wbi/playurl?${search}` : null, { dedupingInterval: 2 * 60 * 1000 * 1000, + onSuccess: d => { + onSuccess?.(d?.durl) + }, }, ) return data?.durl diff --git a/src/api/video-info.schema.ts b/src/api/video-info.schema.ts index 61aeffe..9526770 100644 --- a/src/api/video-info.schema.ts +++ b/src/api/video-info.schema.ts @@ -22,6 +22,31 @@ export const VideoInfoResponseSchema = z.object({ mission_id: z.number(), no_cache: z.boolean(), owner: z.object({ mid: z.number(), name: z.string(), face: z.string() }), + rights: z.object({ + // bp: z.number(), + // elec: z.number(), + // download: z.number(), + // movie: z.number(), + // pay: z.number(), + // hd5: z.number(), + // no_reprint: z.number(), + // autoplay: z.number(), + // ugc_pay: z.number(), + is_cooperation: z.union([z.literal(0), z.literal(1)]), + // ugc_pay_preview: z.number(), + // no_background: z.number(), + // clean_mode: z.number(), + is_stein_gate: z.union([z.literal(0), z.literal(1)]), + is_360: z.union([z.literal(0), z.literal(1)]), + // no_share: z.number(), + // arc_pay: z.number(), + // free_watch: z.number(), + }), + argue_info: z.object({ + argue_msg: z.string(), + argue_type: z.number(), + argue_link: z.string(), + }), pages: z .object({ cid: z.number(), diff --git a/src/api/video-info.ts b/src/api/video-info.ts index 98f8cfa..f3aa88f 100644 --- a/src/api/video-info.ts +++ b/src/api/video-info.ts @@ -31,6 +31,10 @@ const getVideoInfo = (data: VideoInfoResponse) => { danmuNum: data.stat.danmaku, videos: data.videos, // 如果是分片视频或者互动视频,这个videos会是大于1的数字 tag: data.tname, + interactive: data.rights.is_stein_gate === 1, + cooperation: data.rights.is_cooperation === 1, + argument: data.argue_info.argue_msg, + argumentLink: data.argue_info.argue_link, // location: data.pub_location, pages: data.pages.map(v => { // 如果是分片视频,那么length会是分片数量,否则是1 diff --git a/src/components/ErrorFallback.tsx b/src/components/ErrorFallback.tsx index f611930..25a2267 100644 --- a/src/components/ErrorFallback.tsx +++ b/src/components/ErrorFallback.tsx @@ -6,13 +6,8 @@ import { Button, Image, Linking, Text, View } from 'react-native' import { colors } from '@/constants/colors.tw' import { site } from '../constants' -// import { showFatalError } from '../utils' export default function ErrorFallback(props: { message?: string }) { - // React.useEffect(() => { - // showFatalError(props) - // }, [props]) - if (__DEV__) { // eslint-disable-next-line no-console console.error(props.message) diff --git a/src/hooks/useRNETheme.ts b/src/hooks/useRNETheme.ts index 2e5b960..84f18cb 100644 --- a/src/hooks/useRNETheme.ts +++ b/src/hooks/useRNETheme.ts @@ -14,7 +14,6 @@ export default function useRNETheme() { useTWC(colors.white.text).color, useTWC(colors.black.text).color, ] - // console.log(white, black) const isDark = useIsDark() const getRNETheme = React.useCallback(() => { return createTheme({ diff --git a/src/hooks/useRouteTheme.ts b/src/hooks/useRouteTheme.ts index 6783b24..5404534 100644 --- a/src/hooks/useRouteTheme.ts +++ b/src/hooks/useRouteTheme.ts @@ -26,7 +26,6 @@ export default function useRouteTheme() { const [routeTheme, setRouteTheme] = React.useState(getRouteTheme) useAppStateChange(() => { - // console.log('change route color mode') setRouteTheme(getRouteTheme()) }) React.useEffect(() => { diff --git a/src/hooks/useUpdateNavigationOptions.tsx b/src/hooks/useUpdateNavigationOptions.tsx new file mode 100644 index 0000000..47076d9 --- /dev/null +++ b/src/hooks/useUpdateNavigationOptions.tsx @@ -0,0 +1,14 @@ +import { useNavigation } from '@react-navigation/native' +import { NativeStackNavigationOptions } from '@react-navigation/native-stack' +import React from 'react' + +import type { NavigationProps } from '@/types' + +export default function useUpdateNavigationOptions( + options: Partial, +) { + const navigation = useNavigation() + React.useEffect(() => { + navigation.setOptions(options) + }, [navigation, options]) +} diff --git a/src/routes/Collect/index.tsx b/src/routes/Collect/index.tsx index da09292..039d9dc 100644 --- a/src/routes/Collect/index.tsx +++ b/src/routes/Collect/index.tsx @@ -1,4 +1,3 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack' import { Text } from '@rneui/themed' import { FlashList } from '@shopify/flash-list' import React from 'react' @@ -6,18 +5,21 @@ import { Alert, Linking } from 'react-native' import VideoListItem from '@/components/VideoItem' import { colors } from '@/constants/colors.tw' +import useUpdateNavigationOptions from '@/hooks/useUpdateNavigationOptions' import { useStore } from '@/store' -import { CollectVideoInfo, RootStackParamList } from '@/types' +import { CollectVideoInfo } from '@/types' -type Props = NativeStackScreenProps - -function CollectList(props: Props) { +function CollectList() { const { $collectedVideos, set$collectedVideos } = useStore() - React.useEffect(() => { - props.navigation.setOptions({ - headerTitle: `我的收藏(${$collectedVideos.length})`, - }) - }, [props.navigation, $collectedVideos.length]) + const headerTitle = `我的收藏(${$collectedVideos.length})` + useUpdateNavigationOptions( + React.useMemo(() => { + return { + headerTitle, + } + }, [headerTitle]), + ) + const buttons = (video: CollectVideoInfo) => { return [ { diff --git a/src/routes/Dynamic/index.tsx b/src/routes/Dynamic/index.tsx index da692c6..930a5f6 100644 --- a/src/routes/Dynamic/index.tsx +++ b/src/routes/Dynamic/index.tsx @@ -6,6 +6,7 @@ import React from 'react' import { Image, View } from 'react-native' import { colors } from '@/constants/colors.tw' +import useUpdateNavigationOptions from '@/hooks/useUpdateNavigationOptions' import { type DynamicItemAllType, @@ -76,7 +77,7 @@ const Loading = React.memo(LoadingComp) export default React.memo(Dynamic) -function Dynamic({ navigation, route }: Props) { +function Dynamic({ route }: Props) { const upId = route.params?.user?.mid // || specialUser?.mid const dynamicListRef = React.useRef(null) const { data: userInfo } = useUserInfo(upId) @@ -117,13 +118,15 @@ function Dynamic({ navigation, route }: Props) { /> ) }, []) + useUpdateNavigationOptions( + React.useMemo(() => { + return { + headerTitle, + headerRight, + } + }, [headerTitle]), + ) - React.useEffect(() => { - navigation.setOptions({ - headerTitle, - headerRight, - }) - }, [navigation, headerTitle]) const renderItem = ({ item }: { item: DynamicItemAllType }) => { return } diff --git a/src/routes/Follow/index.tsx b/src/routes/Follow/index.tsx index 9398b3b..75aae5e 100644 --- a/src/routes/Follow/index.tsx +++ b/src/routes/Follow/index.tsx @@ -1,4 +1,3 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack' import { Text } from '@rneui/themed' import React from 'react' import { @@ -9,10 +8,12 @@ import { View, } from 'react-native' +import useUpdateNavigationOptions from '@/hooks/useUpdateNavigationOptions' + import useIsDark from '../../hooks/useIsDark' import useMounted from '../../hooks/useMounted' import { useStore } from '../../store' -import type { RootStackParamList, UpInfo } from '../../types' +import type { UpInfo } from '../../types' import FollowItem from './FollowItem' const tvL = require('../../../assets/tv-l.png') @@ -39,9 +40,7 @@ function TvImg() { export default React.memo(FollowList) -type Props = NativeStackScreenProps - -function FollowList(props: Props) { +function FollowList() { // eslint-disable-next-line no-console __DEV__ && console.log('Follow page') const { $followedUps, _updatedCount, $upUpdateMap, livingUps } = useStore() @@ -51,18 +50,20 @@ function FollowList(props: Props) { const { width } = useWindowDimensions() const columns = Math.floor(width / 90) const count = $followedUps.length - - React.useEffect(() => { - props.navigation.setOptions({ - headerTitle: - '关注的UP' + - (count - ? _updatedCount - ? ` (${_updatedCount}/${count})` - : ` (${count})` - : ''), - }) - }, [props.navigation, count, _updatedCount]) + const headerTitle = + '关注的UP' + + (count + ? _updatedCount + ? ` (${_updatedCount}/${count})` + : ` (${count})` + : '') + useUpdateNavigationOptions( + React.useMemo(() => { + return { + headerTitle, + } + }, [headerTitle]), + ) const renderItem = React.useCallback( ({ diff --git a/src/routes/History/index.tsx b/src/routes/History/index.tsx index cadf9db..19ddcfe 100644 --- a/src/routes/History/index.tsx +++ b/src/routes/History/index.tsx @@ -1,4 +1,3 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack' import { Text } from '@rneui/themed' import { FlashList } from '@shopify/flash-list' import React from 'react' @@ -6,19 +5,21 @@ import { Linking } from 'react-native' import VideoListItem from '@/components/VideoItem' import { colors } from '@/constants/colors.tw' +import useUpdateNavigationOptions from '@/hooks/useUpdateNavigationOptions' import { useStore } from '@/store' -import { CollectVideoInfo, HistoryVideoInfo, RootStackParamList } from '@/types' +import { CollectVideoInfo, HistoryVideoInfo } from '@/types' -type Props = NativeStackScreenProps - -function HistoryList(props: Props) { +function HistoryList() { const { $watchedVideos } = useStore() - React.useEffect(() => { - const count = Object.keys($watchedVideos).length - props.navigation.setOptions({ - headerTitle: `观看历史(${count})`, - }) - }, [props.navigation, $watchedVideos]) + const headerTitle = `观看历史(${Object.keys($watchedVideos).length})` + useUpdateNavigationOptions( + React.useMemo(() => { + return { + headerTitle, + } + }, [headerTitle]), + ) + const buttons = (video: CollectVideoInfo) => { return [ { diff --git a/src/routes/Play/Player.tsx b/src/routes/Play/Player.tsx index 1b760e6..05fbf42 100644 --- a/src/routes/Play/Player.tsx +++ b/src/routes/Play/Player.tsx @@ -19,7 +19,7 @@ import { } from 'react-native' import WebView, { type WebViewMessageEvent } from 'react-native-webview' -import { usePlayUrl, useVideoDownloadUrl } from '@/api/play-url' +import { getDownloadUrl, usePlayUrl } from '@/api/play-url' import { UA } from '@/constants' import { colors } from '@/constants/colors.tw' import type { NavigationProps, RootStackParamList } from '@/types' @@ -50,20 +50,12 @@ function Player(props: { currentPage: number; currentCid?: number }) { ...route.params, ...data, } - const durl = usePlayUrl( + const videoUrl = usePlayUrl( isWifi ? videoInfo.bvid : '', - isWifi ? props.currentCid || videoInfo.cid : '', - ) - const videoUrl = durl ? durl[0]?.backup_url?.[0] || durl[0]?.url || '' : null - // const [playState, setPlayState] = React.useState('init') - const [isEnded, setIsEnded] = React.useState(true) - - const downloadVideoUrl = useVideoDownloadUrl( - videoInfo.bvid, - props.currentCid || videoInfo.cid, + isWifi ? props.currentCid || videoInfo.cid : 0, ) - // const durl = usePlayUrl('BV1SZ421y7Ae', '1460675026') + const [isEnded, setIsEnded] = React.useState(true) React.useEffect(() => { if (!getIsWiFi()) { return @@ -123,7 +115,6 @@ function Player(props: { currentPage: number; currentCid?: number }) { } }) const navigation = useNavigation() - // const isFocused = useIsFocused(); useFocusEffect( React.useCallback(() => { @@ -143,7 +134,6 @@ function Player(props: { currentPage: number; currentCid?: number }) { setTimeout(() => { navigation.dispatch(e.data.action) }) - // }) return unsubscribe }, [navigation]) @@ -159,9 +149,6 @@ function Player(props: { currentPage: number; currentCid?: number }) { if (loadPlayer && videoWidth && videoHeight) { if (isEnded) { videoViewHeight = width * 0.6 - // videoViewHeight = isVerticalVideo - // ? (width * videoWidth) / videoHeight - // : (videoHeight / videoWidth) * width } else { if (isVerticalVideo) { videoViewHeight = verticalExpand ? height * 0.66 : height * 0.33 @@ -174,7 +161,6 @@ function Player(props: { currentPage: number; currentCid?: number }) { try { const eventData = JSON.parse(evt.nativeEvent.data) as any if (eventData.action === 'playState') { - // setPlayState(eventData.payload) setIsEnded(eventData.payload === 'ended') if (eventData.payload === 'play') { KeepAwake.activateKeepAwakeAsync('PLAY') @@ -195,12 +181,14 @@ function Player(props: { currentPage: number; currentCid?: number }) { } } if (eventData.action === 'downloadVideo') { - const url = downloadVideoUrl?.[0]?.url - if (url) { - Linking.openURL(url) - } else { - showToast('抱歉,暂不支持下载') - } + showToast('请稍后在浏览器中下载') + getDownloadUrl(videoInfo.bvid, props.currentCid || videoInfo.cid)?.then( + url => { + if (url) { + Linking.openURL(url) + } + }, + ) } if (eventData.action === 'reload') { // TODO: @@ -283,7 +271,6 @@ function Player(props: { currentPage: number; currentCid?: number }) { page: isWifi ? undefined : props.currentPage, autoplay: isWifi ? 0 : 1, hasMuteButton: true, - // portraitFullScreen: 1, }).forEach(([k, v]) => { if (v !== undefined) { search.append(k, v + '') diff --git a/src/routes/Play/VideoHeader.tsx b/src/routes/Play/VideoHeader.tsx index a842c4d..67e1afe 100644 --- a/src/routes/Play/VideoHeader.tsx +++ b/src/routes/Play/VideoHeader.tsx @@ -6,7 +6,7 @@ import { import { Avatar, Icon, Text } from '@rneui/themed' import { Image } from 'expo-image' import React from 'react' -import { Alert, Pressable, TouchableOpacity, View } from 'react-native' +import { Alert, Linking, Pressable, TouchableOpacity, View } from 'react-native' import { useWatchingCount } from '@/api/watching-count' import { colors } from '@/constants/colors.tw' @@ -73,6 +73,19 @@ function VideoHeader() { return ( + {videoInfo?.argument ? ( + + { + if (videoInfo.argumentLink) { + Linking.openURL(videoInfo.argumentLink) + } + }}> + ⚠️ {videoInfo.argument} + + + ) : null} { diff --git a/src/routes/Play/VideoInfo.tsx b/src/routes/Play/VideoInfo.tsx index 98a2978..c68bb53 100644 --- a/src/routes/Play/VideoInfo.tsx +++ b/src/routes/Play/VideoInfo.tsx @@ -32,7 +32,7 @@ function VideoInfo(props: { videoDesc = '' } return ( - + {title} {videoDesc ? ( @@ -79,11 +79,10 @@ function VideoInfo(props: { ) })} - ) : !isLoading && - videoInfo?.videos && - videoInfo?.videos !== videoInfo?.pages?.length ? ( + ) : null} + {!isLoading && videoInfo?.interactive ? ( - 该视频为交互视频,暂不支持 + 【该视频为交互视频,暂不支持】 ) : null} diff --git a/src/routes/Play/index.tsx b/src/routes/Play/index.tsx index f2946c2..18531ba 100644 --- a/src/routes/Play/index.tsx +++ b/src/routes/Play/index.tsx @@ -5,6 +5,8 @@ import * as Clipboard from 'expo-clipboard' import React from 'react' import { ScrollView, View } from 'react-native' +import useUpdateNavigationOptions from '@/hooks/useUpdateNavigationOptions' + import { useVideoInfo } from '../../api/video-info' import CommentList from '../../components/CommentList' import useMemoizedFn from '../../hooks/useMemoizedFn' @@ -25,7 +27,7 @@ type Props = NativeStackScreenProps export default React.memo(Play) -function Play({ route, navigation }: Props) { +function Play({ route }: Props) { const { bvid } = route.params const [currentPage, setCurrentPage] = React.useState(1) const { data } = useVideoInfo(bvid) @@ -34,12 +36,15 @@ function Play({ route, navigation }: Props) { ...data, } const [currentCid, setCurrentCid] = React.useState(videoInfo.cid) - React.useEffect(() => { - const headerTitle = () => - navigation.setOptions({ - headerTitle, - }) - }, [navigation]) + useUpdateNavigationOptions( + React.useMemo(() => { + const headerTitle = () => + return { + headerTitle, + } + }, []), + ) + useFocusEffect( useMemoizedFn(() => { setViewingVideoId(bvid) diff --git a/src/routes/SearchUps/index.tsx b/src/routes/SearchUps/index.tsx index 126d7cb..7aff583 100644 --- a/src/routes/SearchUps/index.tsx +++ b/src/routes/SearchUps/index.tsx @@ -1,49 +1,48 @@ -import { NativeStackScreenProps } from '@react-navigation/native-stack' import React from 'react' import { SearchBarCommands } from 'react-native-screens' import { colors } from '@/constants/colors.tw' import useMounted from '@/hooks/useMounted' -import { RootStackParamList } from '@/types' +import useUpdateNavigationOptions from '@/hooks/useUpdateNavigationOptions' import UpList from './UpList' -type Props = NativeStackScreenProps - -function SearchUps(props: Props) { +function SearchUps() { const searchBarRef = React.useRef(null) const blackColor = tw(colors.black.text).color const [searchKeyWord, setSearchKeyWord] = React.useState('') - React.useEffect(() => { - props.navigation.setOptions({ - headerSearchBarOptions: { - ref: searchBarRef, - placeholder: '搜索UP主', - headerIconColor: blackColor, - hintTextColor: blackColor, - textColor: blackColor, - tintColor: blackColor, - disableBackButtonOverride: false, - shouldShowHintSearchIcon: false, - onClose: () => { - setSearchKeyWord('') - }, - onSearchButtonPress: ({ nativeEvent: { text } }) => { - const keyword = text.trim() - if (!keyword) { - return - } - setSearchKeyWord(keyword) + useUpdateNavigationOptions( + React.useMemo(() => { + return { + headerSearchBarOptions: { + ref: searchBarRef, + placeholder: '搜索UP主', + headerIconColor: blackColor, + hintTextColor: blackColor, + textColor: blackColor, + tintColor: blackColor, + disableBackButtonOverride: false, + shouldShowHintSearchIcon: false, + onClose: () => { + setSearchKeyWord('') + }, + onSearchButtonPress: ({ nativeEvent: { text } }) => { + const keyword = text.trim() + if (!keyword) { + return + } + setSearchKeyWord(keyword) + }, }, - }, - }) - }, [props.navigation, blackColor, setSearchKeyWord]) + } + }, [blackColor]), + ) useMounted(() => { setTimeout(() => { searchBarRef.current?.focus() - }, 100) + }, 80) }) return diff --git a/src/routes/SearchVideos/VideoList.tsx b/src/routes/SearchVideos/VideoList.tsx index 06c8def..d3e1997 100644 --- a/src/routes/SearchVideos/VideoList.tsx +++ b/src/routes/SearchVideos/VideoList.tsx @@ -14,7 +14,7 @@ function EmptyContent(props: { loading: boolean }) { {Array.from({ length: 20 }).map((_, i) => { return ( - -function SearchVideos(props: Props) { +function SearchVideos() { const searchBarRef = React.useRef(null) const blackColor = tw(colors.black.text).color const [searchKeyWord, setSearchKeyWord] = React.useState('') - - React.useEffect(() => { - props.navigation.setOptions({ - headerSearchBarOptions: { - ref: searchBarRef, - placeholder: '搜索视频', - headerIconColor: blackColor, - hintTextColor: blackColor, - textColor: blackColor, - tintColor: blackColor, - disableBackButtonOverride: false, - shouldShowHintSearchIcon: false, - onClose: () => { - setSearchKeyWord('') - }, - onSearchButtonPress: ({ nativeEvent: { text } }) => { - const keyword = text.trim() - if (!keyword) { - return - } - setSearchKeyWord(keyword) + useUpdateNavigationOptions( + React.useMemo(() => { + return { + headerSearchBarOptions: { + ref: searchBarRef, + placeholder: '搜索视频', + headerIconColor: blackColor, + hintTextColor: blackColor, + textColor: blackColor, + tintColor: blackColor, + disableBackButtonOverride: false, + shouldShowHintSearchIcon: false, + onClose: () => { + setSearchKeyWord('') + }, + onSearchButtonPress: ({ nativeEvent: { text } }) => { + const keyword = text.trim() + if (!keyword) { + return + } + setSearchKeyWord(keyword) + }, }, - }, - }) - }, [props.navigation, blackColor, searchKeyWord]) + } + }, [blackColor]), + ) useMounted(() => { setTimeout(() => { searchBarRef.current?.focus() searchBarRef.current?.setText('') - }, 100) + }, 80) }) return diff --git a/src/routes/VideoList/HotList.tsx b/src/routes/VideoList/HotList.tsx index dd90613..20031cc 100644 --- a/src/routes/VideoList/HotList.tsx +++ b/src/routes/VideoList/HotList.tsx @@ -10,6 +10,7 @@ let refreshTime = Date.now() export default React.memo(Hot) function Hot() { + const [reload, setReload] = React.useState(0) const { list, page, @@ -19,7 +20,7 @@ function Hot() { mutate, isReachingEnd, error, - } = useHotVideos() + } = useHotVideos(reload) React.useEffect(() => { if (Date.now() - refreshTime > 5 * 60 * 1000) { mutate() @@ -48,8 +49,13 @@ function Hot() { onReachEnd={() => { setSize(page + 1) }} - onRefresh={() => { - mutate() + onRefresh={fab => { + // mutate() + if (!fab) { + setReload(reload + 1) + } else { + mutate() + } }} footer={getFooter} /> diff --git a/src/routes/VideoList/Loading.tsx b/src/routes/VideoList/Loading.tsx index e1bfc50..2f58cbd 100644 --- a/src/routes/VideoList/Loading.tsx +++ b/src/routes/VideoList/Loading.tsx @@ -7,12 +7,7 @@ const getWidth = () => Math.floor(Math.random() * (100 - 10 + 1)) + 10 function VideoLoading() { return ( - + { return ( - + diff --git a/src/routes/VideoList/VideoList.tsx b/src/routes/VideoList/VideoList.tsx index 69e37f5..e995113 100644 --- a/src/routes/VideoList/VideoList.tsx +++ b/src/routes/VideoList/VideoList.tsx @@ -21,7 +21,7 @@ function VideoList(props: { type: 'Hot' | 'Rank' | 'Search' footer?: Footer | ((l: any[]) => Footer) onReachEnd?: () => void - onRefresh?: () => void + onRefresh?: (fab?: boolean) => void isRefreshing?: boolean }) { const { @@ -98,7 +98,7 @@ function VideoList(props: { return ( gotoPlay(item)} onLongPress={() => { @@ -187,7 +187,7 @@ function VideoList(props: { ? props.footer(videoList) : props.footer } - contentContainerStyle={tw('px-1 pt-6')} + contentContainerStyle={tw('px-[4px] pt-6')} estimatedFirstItemOffset={100} {...refreshProps} {...reachEndProps} @@ -202,7 +202,7 @@ function VideoList(props: { size="small" onPress={() => { listRef.current?.scrollToOffset(0) - props.onRefresh?.() + props.onRefresh?.(true) }} /> )} diff --git a/src/routes/WebPage/index.tsx b/src/routes/WebPage/index.tsx index abe2d40..793ae40 100644 --- a/src/routes/WebPage/index.tsx +++ b/src/routes/WebPage/index.tsx @@ -10,6 +10,8 @@ import { } from 'react-native' import { WebView } from 'react-native-webview' +import useUpdateNavigationOptions from '@/hooks/useUpdateNavigationOptions' + import { UA } from '../../constants' import useIsDark from '../../hooks/useIsDark' import { useStore } from '../../store' @@ -34,7 +36,7 @@ type Props = NativeStackScreenProps export default React.memo(WebPage) -function WebPage({ route, navigation }: Props) { +function WebPage({ route }: Props) { const { url, title } = route.params const webviewRef = React.useRef(null) @@ -42,6 +44,7 @@ function WebPage({ route, navigation }: Props) { const isDark = useIsDark() const [height, setHeight] = React.useState(Dimensions.get('screen').height) const [isEnabled, setEnabled] = React.useState(true) + const [pageTitle, setPageTitle] = React.useState(title) const { isRefreshing, onRefresh } = useRefresh( React.useCallback(() => { return new Promise(r => { @@ -50,15 +53,17 @@ function WebPage({ route, navigation }: Props) { }) }, []), ) - - React.useEffect(() => { - const headerRight = () => { - return - } - navigation.setOptions({ - headerRight, - }) - }, [navigation, onRefresh]) + useUpdateNavigationOptions( + React.useMemo(() => { + const headerRight = () => { + return + } + return { + headerRight, + headerTitle: pageTitle, + } + }, [onRefresh, pageTitle]), + ) return ( setHeight(e.nativeEvent.layout.height)} @@ -92,9 +97,7 @@ function WebPage({ route, navigation }: Props) { onMessage={evt => { const data = JSON.parse(evt.nativeEvent.data) as any if (data.action === 'set-title' && !title) { - navigation.setOptions({ - headerTitle: data.payload, - }) + setPageTitle(data.payload) } }} onLoad={() => {