diff --git a/package-lock.json b/package-lock.json
index 8905ad6..cd6959a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,7 @@
"@sentry/react-native": "5.19.1",
"@shopify/flash-list": "1.6.3",
"@total-typescript/ts-reset": "^0.5.1",
+ "clsx": "^2.1.0",
"expo": "~50.0.8",
"expo-application": "~5.8.3",
"expo-asset": "~9.0.2",
@@ -45,6 +46,7 @@
"react-native-webview": "13.6.4",
"spark-md5": "^3.0.2",
"swr": "^2.2.4",
+ "throttle-debounce": "^5.0.0",
"zod": "^3.21.4"
},
"devDependencies": {
@@ -54,6 +56,7 @@
"@types/he": "^1.2.3",
"@types/react": "~18.2.14",
"@types/spark-md5": "^3.0.4",
+ "@types/throttle-debounce": "^5.0.2",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"babel-plugin-transform-remove-console": "^6.9.4",
@@ -7415,6 +7418,12 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
},
+ "node_modules/@types/throttle-debounce": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
+ "integrity": "sha512-pDzSNulqooSKvSNcksnV72nk8p7gRqN8As71Sp28nov1IgmPKWbOEIwAWvBME5pPTtaXJAvG3O4oc76HlQ4kqQ==",
+ "dev": true
+ },
"node_modules/@types/which": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.3.tgz",
@@ -9030,6 +9039,14 @@
"node": ">=6"
}
},
+ "node_modules/clsx": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
+ "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/color": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
@@ -18825,6 +18842,14 @@
"resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz",
"integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA=="
},
+ "node_modules/throttle-debounce": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.0.tgz",
+ "integrity": "sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==",
+ "engines": {
+ "node": ">=12.22"
+ }
+ },
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
diff --git a/package.json b/package.json
index 66fae1c..651294c 100644
--- a/package.json
+++ b/package.json
@@ -41,8 +41,10 @@
"@sentry/react-native": "5.19.1",
"@shopify/flash-list": "1.6.3",
"@total-typescript/ts-reset": "^0.5.1",
+ "clsx": "^2.1.0",
"expo": "~50.0.8",
"expo-application": "~5.8.3",
+ "expo-asset": "~9.0.2",
"expo-background-fetch": "~11.8.1",
"expo-clipboard": "~5.0.1",
"expo-constants": "~15.4.5",
@@ -67,8 +69,8 @@
"react-native-webview": "13.6.4",
"spark-md5": "^3.0.2",
"swr": "^2.2.4",
- "zod": "^3.21.4",
- "expo-asset": "~9.0.2"
+ "throttle-debounce": "^5.0.0",
+ "zod": "^3.21.4"
},
"devDependencies": {
"@babel/core": "^7.20.0",
@@ -77,6 +79,7 @@
"@types/he": "^1.2.3",
"@types/react": "~18.2.14",
"@types/spark-md5": "^3.0.4",
+ "@types/throttle-debounce": "^5.0.2",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"babel-plugin-transform-remove-console": "^6.9.4",
diff --git a/src/components/Comment.tsx b/src/components/Comment.tsx
index 76f8199..2a2e4e8 100644
--- a/src/components/Comment.tsx
+++ b/src/components/Comment.tsx
@@ -1,6 +1,8 @@
import { useNavigation, useRoute } from '@react-navigation/native'
import { NativeStackScreenProps } from '@react-navigation/native-stack'
import { Button, Text } from '@rneui/themed'
+import clsx from 'clsx'
+import * as Clipboard from 'expo-clipboard'
import React from 'react'
import { Image, Linking, View } from 'react-native'
@@ -9,7 +11,7 @@ import { colors } from '@/constants/colors.tw'
import type { CommentItemType, CommentMessageContent } from '../api/comments'
import { useStore } from '../store'
import type { NavigationProps, RootStackParamList } from '../types'
-import { parseImgUrl } from '../utils'
+import { parseImgUrl, showToast } from '../utils'
function CommentText(props: {
nodes: CommentMessageContent
@@ -39,7 +41,10 @@ function CommentText(props: {
return (
{
navigation.push('Dynamic', {
user: {
@@ -59,10 +64,15 @@ function CommentText(props: {
{
+ Clipboard.setStringAsync(node.url).then(() => {
+ showToast('已复制链接')
+ })
+ }}
onPress={() => {
Linking.openURL(node.url)
}}>
- {node.url}
+ {'🔗 链接 '}
)
}
@@ -108,7 +118,7 @@ function CommentText(props: {
)
}
return (
-
+
{node.text}
)
@@ -117,8 +127,9 @@ function CommentText(props: {
)
}
-function CommentItem(props: {
+export function CommentItem(props: {
comment: CommentItemType | CommentItemType['replies'][0]
+ smallFont?: boolean
}) {
const { comment } = props
const { setImagesList, setCurrentImageIndex } = useStore()
@@ -135,15 +146,15 @@ function CommentItem(props: {
: route.params.name
: ''
const navigation = useNavigation()
- const fontSize = 'rcount' in comment ? 'text-base' : 'text-sm'
+ const fontSize = props.smallFont ? 'text-sm' : 'text-base'
return (
{
navigation.push('Dynamic', {
user: {
@@ -160,7 +171,9 @@ function CommentItem(props: {
{comment.sex === '男' ? '♂:' : comment.sex === '女' ? '♀:' : ':'}
{'top' in comment && comment.top ? (
- 🔝
+
+ {' 置顶 '}
+
) : null}
{comment.message?.length ? (
{comment.replies?.length ? (
{comment.replies.map(reply => {
- return
+ return
})}
{comment.moreText && comment.rcount > comment.replies.length ? (
) : null
}
- contentContainerStyle={tw('py-5')}
+ contentContainerStyle={tw('pb-5')}
onEndReached={() => {
update()
}}
diff --git a/src/components/VideoItem.tsx b/src/components/VideoItem.tsx
index 31d3f51..ebc8793 100644
--- a/src/components/VideoItem.tsx
+++ b/src/components/VideoItem.tsx
@@ -122,7 +122,9 @@ function VideoListItem({
+ className={
+ isFollowed ? colors.secondary.text : colors.primary.text
+ }>
UP:
{video.name}
diff --git a/src/routes/Dynamic/CommonItem.tsx b/src/routes/Dynamic/CommonItem.tsx
index eff32b1..01c7497 100644
--- a/src/routes/Dynamic/CommonItem.tsx
+++ b/src/routes/Dynamic/CommonItem.tsx
@@ -1,5 +1,6 @@
import { useNavigation } from '@react-navigation/native'
import { Text } from '@rneui/themed'
+import clsx from 'clsx'
import { Image } from 'expo-image'
import React from 'react'
import { Linking, Pressable, TouchableOpacity, View } from 'react-native'
@@ -90,12 +91,11 @@ export function CommonContent(props: {
{title ? (
{title}
diff --git a/src/routes/Dynamic/Header.tsx b/src/routes/Dynamic/Header.tsx
index b28a5ed..90449d3 100644
--- a/src/routes/Dynamic/Header.tsx
+++ b/src/routes/Dynamic/Header.tsx
@@ -1,10 +1,11 @@
import { useNavigation, useRoute } from '@react-navigation/native'
import type { NativeStackScreenProps } from '@react-navigation/native-stack'
-import { Avatar, Button, Icon, Text } from '@rneui/themed'
+import { Avatar, Icon, Text } from '@rneui/themed'
+import clsx from 'clsx'
import * as Clipboard from 'expo-clipboard'
import { Image } from 'expo-image'
import React from 'react'
-import { Linking, View } from 'react-native'
+import { Linking, Pressable, View } from 'react-native'
import { Menu, MenuItem } from 'react-native-material-menu'
import { colors } from '@/constants/colors.tw'
@@ -48,7 +49,7 @@ export function HeaderLeft(props: { scrollTop: () => void }) {
{dynamicUser?.face ? (
void }) {
{sex}
) : null}
+ {dynamicUser.mid && livingUrl ? (
+ {
+ if (dynamicUser.mid) {
+ setCheckLiveTimeStamp(Date.now())
+ navigation.navigate('WebPage', {
+ title: dynamicUser.name + '的直播间',
+ url: livingUrl,
+ })
+ }
+ }}
+ className="inset-0 absolute justify-center items-center w-10 h-10 rounded-full bg-neutral-950/60">
+
+ 直播中
+
+
+ ) : null}
) : null}
{
props.scrollTop()
}}>
{userName}
-
- {level}
- {' '}
-
+ {level + ' '}
{fans ? (
void }) {
) : null}
- {dynamicUser.mid && livingUrl ? (
-
- ) : null}
)
}
diff --git a/src/routes/Follow/FollowItem.tsx b/src/routes/Follow/FollowItem.tsx
index f5eb760..870e39a 100644
--- a/src/routes/Follow/FollowItem.tsx
+++ b/src/routes/Follow/FollowItem.tsx
@@ -192,30 +192,6 @@ function FollowItem(props: { item: UpInfo; index?: number }) {
/>
) : null}
- {/* {livingUps[mid] ? (
-
- ) : (
-
- {name}
-
- )} */}
- 暂无更多(只记录最近400条)
+ 暂无更多(最近约400条)
) : null
}
diff --git a/src/routes/Play/VideoHeader.tsx b/src/routes/Play/VideoHeader.tsx
index f690210..31101f2 100644
--- a/src/routes/Play/VideoHeader.tsx
+++ b/src/routes/Play/VideoHeader.tsx
@@ -4,6 +4,7 @@ import {
useRoute,
} from '@react-navigation/native'
import { Avatar, Icon, Text } from '@rneui/themed'
+import clsx from 'clsx'
import { Image } from 'expo-image'
import React from 'react'
import { Alert, Linking, Pressable, TouchableOpacity, View } from 'react-native'
@@ -161,7 +162,12 @@ function VideoHeader() {
}
/>
+ className={clsx(
+ 'text-sm',
+ isCollected
+ ? [colors.warning.text, 'font-bold']
+ : colors.gray8.text,
+ )}>
{parseNumber(videoInfo?.collectNum)}
diff --git a/src/routes/Play/VideoInfo.tsx b/src/routes/Play/VideoInfo.tsx
index c68bb53..0f874c6 100644
--- a/src/routes/Play/VideoInfo.tsx
+++ b/src/routes/Play/VideoInfo.tsx
@@ -81,7 +81,7 @@ function VideoInfo(props: {
) : null}
{!isLoading && videoInfo?.interactive ? (
-
+
【该视频为交互视频,暂不支持】
) : null}
diff --git a/src/routes/Play/inject-play.js b/src/routes/Play/inject-play.js
index e9947e9..0ed3a70 100644
--- a/src/routes/Play/inject-play.js
+++ b/src/routes/Play/inject-play.js
@@ -96,7 +96,7 @@ function __$hack() {
}
const style = document.createElement('style')
style.textContent = `
- body, #bilibiliPlayer, .mplayer {
+ html, body, #bilibiliPlayer, .mplayer {
background-color: black!important;
}
.b-danmaku {
@@ -128,32 +128,6 @@ function __$hack() {
})
})
waitForDom('.mplayer-right', right => {
- // if (!document.getElementById('download-button')) {
- // const downloadBtn = document.createElement('div')
- // downloadBtn.id = 'download-button'
- // // downloadBtn.innerHTML = '⇓'
- // downloadBtn.innerHTML = '⇩'
- // downloadBtn.style.cssText = `
- // width: 24px;
- // height: 24px;
- // color: white;
- // font-size: 20px;
- // line-height: 28px;
- // text-align: center;
- // background: rgba(0,0,0,.2);
- // border-radius: 50%;
- // font-weight: 500;
- // `
- // downloadBtn.addEventListener('click', () => {
- // window.ReactNativeWebView.postMessage(
- // JSON.stringify({
- // action: 'downloadVideo',
- // payload: null,
- // }),
- // )
- // })
- // right.appendChild(downloadBtn)
- // }
if (!document.getElementById('play-rate-button')) {
const rateBtn = document.createElement('div')
rateBtn.id = 'play-rate-button'
diff --git a/src/routes/Play/update-playurl.js b/src/routes/Play/update-playurl.js
index dee4c1a..6e13295 100644
--- a/src/routes/Play/update-playurl.js
+++ b/src/routes/Play/update-playurl.js
@@ -33,10 +33,10 @@ function __$hack() {
// }),
// )
video.load()
- setTimeout(() => {
- video.muted = false
- video.play()
- }, 10)
+ // setTimeout(() => {
+ video.muted = false
+ video.play()
+ // }, 10)
}
}
window.setNewVideoUrl = function (url) {
@@ -48,6 +48,7 @@ function __$hack() {
evt => {
const video = evt.target
if (video && video.tagName === 'VIDEO') {
+ video.pause()
replacePlayUrl()
}
},
diff --git a/src/routes/VideoList/VideoItem.tsx b/src/routes/VideoList/VideoItem.tsx
index 3c5792f..b3de58b 100644
--- a/src/routes/VideoList/VideoItem.tsx
+++ b/src/routes/VideoList/VideoItem.tsx
@@ -1,4 +1,5 @@
import { Icon, Text } from '@rneui/themed'
+import clsx from 'clsx'
import { Image } from 'expo-image'
import React from 'react'
import { View } from 'react-native'
@@ -54,14 +55,20 @@ function VideoItem({ video }: { video: VideoItemType }) {
+ className={clsx(
+ 'text-white text-xs',
+ isBlackTag && 'line-through opacity-60',
+ )}>
{video.tag}
) : null}
{video.title}
@@ -83,11 +90,12 @@ function VideoItem({ video }: { video: VideoItemType }) {
+ ? ['font-bold', colors.secondary.text]
+ : colors.primary.text,
+ )}>
{video.name}
diff --git a/src/utils/index.ts b/src/utils/index.ts
index c1ae818..405b832 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,6 +1,7 @@
import * as Updates from 'expo-updates'
import { Alert, Linking, Platform, Share, ToastAndroid } from 'react-native'
import Toast from 'react-native-root-toast'
+import { throttle } from 'throttle-debounce'
import { checkUpdate } from '@/api/check-update'
@@ -120,22 +121,27 @@ export function delay(ms: number) {
})
}
-const showedMessage: Record void> = {}
-
+const toastFuncMap: Record void> = {}
export function showToast(message: string, long = false) {
- if (!(message in showedMessage)) {
- showedMessage[message] = () => {
- Platform.OS === 'android'
- ? ToastAndroid.show(
- message,
- long ? ToastAndroid.LONG : ToastAndroid.SHORT,
- )
- : Toast.show(message, {
- duration: long ? Toast.durations.LONG : Toast.durations.SHORT,
- })
- }
+ if (!(message in toastFuncMap)) {
+ toastFuncMap[message] = throttle(
+ 5000,
+ () => {
+ Platform.OS === 'android'
+ ? ToastAndroid.show(
+ message,
+ long ? ToastAndroid.LONG : ToastAndroid.SHORT,
+ )
+ : Toast.show(message, {
+ duration: long ? Toast.durations.LONG : Toast.durations.SHORT,
+ })
+ },
+ {
+ noLeading: false,
+ },
+ )
}
- showedMessage[message]()
+ toastFuncMap[message]()
}
let showedFatalError = false