修复加载某些图片导致APP卡死的问题

This commit is contained in:
lyswhut 2023-12-17 14:43:48 +08:00
parent 8352ca7803
commit 5cf9863f9f
25 changed files with 247 additions and 249 deletions

View File

@ -1,9 +1,18 @@
import { Image as _Image } from 'react-native' import { useTheme } from '@/store/theme/hook'
import { BorderRadius } from '@/theme'
import { createStyle } from '@/utils/tools'
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { View, type ViewProps, Image as _Image, StyleSheet } from 'react-native'
import FastImage, { type FastImageProps } from 'react-native-fast-image' import FastImage, { type FastImageProps } from 'react-native-fast-image'
import Text from './Text'
import { useLayout } from '@/utils/hooks'
export type { OnLoadEvent } from 'react-native-fast-image' export type { OnLoadEvent } from 'react-native-fast-image'
export interface ImageProps extends Omit<FastImageProps, 'source'> { export interface ImageProps extends ViewProps {
url?: string | number style: FastImageProps['style']
url?: string | number | null
resizeMode?: FastImageProps['resizeMode']
onError?: (url: string | number) => void
} }
@ -11,26 +20,69 @@ const defaultHeaders = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
} }
const Image = ({ url, resizeMode = FastImage.resizeMode.cover, ...props }: ImageProps) => { const EmptyPic = memo(({ style, nativeID }: { style: ImageProps['style'], nativeID: ImageProps['nativeID'] }) => {
const theme = useTheme()
const { onLayout, width } = useLayout()
const size = width * 0.36
return (
<View style={StyleSheet.compose({ ...styles.emptyPic, backgroundColor: theme['c-primary-light-900-alpha-200'], gap: size * 0.1 }, style)} onLayout={onLayout} nativeID={nativeID}>
<Text size={size} color={theme['c-primary-light-400-alpha-200']}>L</Text>
<Text size={size} color={theme['c-primary-light-400-alpha-200']} style={styles.text}>X</Text>
</View>
)
})
const Image = memo(({ url, resizeMode = FastImage.resizeMode.cover, style, onError, nativeID }: ImageProps) => {
const [isError, setError] = useState(false)
const handleError = useCallback(() => {
setError(true)
onError?.(url!)
}, [onError, url])
useEffect(() => {
setError(false)
}, [url])
let uri = typeof url == 'number' let uri = typeof url == 'number'
? _Image.resolveAssetSource(url).uri ? _Image.resolveAssetSource(url).uri
: url?.startsWith('/') : url?.startsWith('/')
? 'file://' + url ? 'file://' + url
: url : url
const showDefault = useMemo(() => !uri || isError, [isError, uri])
return ( return (
showDefault ? <EmptyPic style={style} nativeID={nativeID} />
: (
<FastImage <FastImage
{...props} style={style}
source={{ source={{
uri, uri: uri!,
headers: defaultHeaders, headers: defaultHeaders,
priority: FastImage.priority.normal, priority: FastImage.priority.normal,
}} }}
onError={handleError}
resizeMode={resizeMode} resizeMode={resizeMode}
nativeID={nativeID}
/> />
) )
} )
}, (prevProps, nextProps) => {
return prevProps.url == nextProps.url &&
prevProps.style == nextProps.style &&
prevProps.nativeID == nextProps.nativeID
})
export const getSize = (uri: string, success: (width: number, height: number) => void, failure?: (error: any) => void) => { export const getSize = (uri: string, success: (width: number, height: number) => void, failure?: (error: any) => void) => {
_Image.getSize(uri, success, failure) _Image.getSize(uri, success, failure)
} }
export default Image export default Image
const styles = createStyle({
emptyPic: {
borderRadius: BorderRadius.normal,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
text: {
paddingLeft: 2,
},
})

View File

@ -56,7 +56,6 @@ export default forwardRef<ImageBackgroundType, ImageBackgroundProps>(({
children, children,
style, style,
imageStyle, imageStyle,
importantForAccessibility,
url, url,
...props ...props
}, ref) => { }, ref) => {
@ -64,7 +63,7 @@ export default forwardRef<ImageBackgroundType, ImageBackgroundProps>(({
return ( return (
<View <View
accessibilityIgnoresInvertColors={true} accessibilityIgnoresInvertColors={true}
importantForAccessibility={importantForAccessibility} importantForAccessibility={'no'}
ref={ref} ref={ref}
style={style}> style={style}>
{ {
@ -72,7 +71,6 @@ export default forwardRef<ImageBackgroundType, ImageBackgroundProps>(({
<Image <Image
{...props} {...props}
url={url} url={url}
importantForAccessibility={importantForAccessibility}
style={[ style={[
StyleSheet.absoluteFill, StyleSheet.absoluteFill,
{ {

View File

@ -1,42 +1,22 @@
import { memo } from 'react' import { StyleSheet, TouchableOpacity } from 'react-native'
import { View, TouchableOpacity } from 'react-native'
import { navigations } from '@/navigation' import { navigations } from '@/navigation'
import { usePlayerMusicInfo } from '@/store/player/hook' import { usePlayerMusicInfo } from '@/store/player/hook'
import { useTheme } from '@/store/theme/hook'
import { scaleSizeH } from '@/utils/pixelRatio' import { scaleSizeH } from '@/utils/pixelRatio'
import { createStyle } from '@/utils/tools'
import { BorderRadius } from '@/theme'
import commonState from '@/store/common/state' import commonState from '@/store/common/state'
import playerState from '@/store/player/state' import playerState from '@/store/player/state'
import Text from '@/components/common/Text'
import { LIST_IDS, NAV_SHEAR_NATIVE_IDS } from '@/config/constant' import { LIST_IDS, NAV_SHEAR_NATIVE_IDS } from '@/config/constant'
import Image from '@/components/common/Image' import Image from '@/components/common/Image'
import { useCallback } from 'react'
import { setLoadErrorPicUrl, setMusicInfo } from '@/core/player/playInfo'
const PIC_HEIGHT = scaleSizeH(46) const PIC_HEIGHT = scaleSizeH(46)
const styles = createStyle({ const styles = StyleSheet.create({
// content: { image: {
// marginBottom: 3, width: PIC_HEIGHT,
// },/ height: PIC_HEIGHT,
emptyPic: { borderRadius: 2,
borderRadius: BorderRadius.normal,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
}, },
text: {
paddingLeft: 2,
},
})
const EmptyPic = memo(() => {
const theme = useTheme()
return (
<View nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_pic} style={{ ...styles.emptyPic, width: PIC_HEIGHT, height: PIC_HEIGHT, backgroundColor: theme['c-primary-light-900-alpha-200'] }}>
<Text size={20} color={theme['c-primary-light-400-alpha-200']}>L</Text>
<Text size={20} color={theme['c-primary-light-400-alpha-200']} style={styles.text}>X</Text>
</View>
)
}) })
export default ({ isHome }: { isHome: boolean }) => { export default ({ isHome }: { isHome: boolean }) => {
@ -57,23 +37,16 @@ export default ({ isHome }: { isHome: boolean }) => {
global.app_event.jumpListPosition() global.app_event.jumpListPosition()
} }
// console.log('render pic') const handleError = useCallback((url: string | number) => {
setLoadErrorPicUrl(url as string)
setMusicInfo({
pic: null,
})
}, [])
return ( return (
<TouchableOpacity onLongPress={handleLongPress} onPress={handlePress} activeOpacity={0.7} > <TouchableOpacity onLongPress={handleLongPress} onPress={handlePress} activeOpacity={0.7} >
{ <Image url={musicInfo.pic} nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_pic} style={styles.image} onError={handleError} />
musicInfo.pic
? (
<Image url={musicInfo.pic} nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_pic} style={{
// ...styles.playInfoImg,
// backgroundColor: theme.primary,
width: PIC_HEIGHT,
height: PIC_HEIGHT,
borderRadius: 2,
}} />
)
: <EmptyPic />
}
</TouchableOpacity> </TouchableOpacity>
) )
} }

View File

@ -9,8 +9,11 @@ import { createStyle } from '@/utils/tools'
import Text from '@/components/common/Text' import Text from '@/components/common/Text'
import { COMPONENT_IDS } from '@/config/constant' import { COMPONENT_IDS } from '@/config/constant'
import { usePageVisible } from '@/store/common/hook' import { usePageVisible } from '@/store/common/hook'
import { scaleSizeH } from '@/utils/pixelRatio'
const FONT_SIZE = 13 const FONT_SIZE = 13
const PADDING_TOP_RAW = 3
const PADDING_TOP = scaleSizeH(PADDING_TOP_RAW)
const PlayTimeCurrent = ({ timeStr }: { timeStr: string }) => { const PlayTimeCurrent = ({ timeStr }: { timeStr: string }) => {
const theme = useTheme() const theme = useTheme()
@ -42,7 +45,9 @@ export default ({ isHome }: { isHome: boolean }) => {
<Text size={FONT_SIZE} color={theme['c-500']}> / </Text> <Text size={FONT_SIZE} color={theme['c-500']}> / </Text>
<PlayTimeMax timeStr={maxPlayTimeStr} /> <PlayTimeMax timeStr={maxPlayTimeStr} />
</View> </View>
<View style={[StyleSheet.absoluteFill, styles.progress]}><Progress progress={progress} duration={maxPlayTime} /></View> <View style={[StyleSheet.absoluteFill, styles.progress]}>
<Progress progress={progress} duration={maxPlayTime} paddingTop={PADDING_TOP} />
</View>
</View> </View>
) )
} }
@ -64,7 +69,7 @@ const styles = createStyle({
// position: 'absolute', // position: 'absolute',
// width: '100%', // width: '100%',
// top: 0, // top: 0,
paddingVertical: 2, paddingTop: PADDING_TOP_RAW,
paddingHorizontal: 3, paddingHorizontal: 3,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
@ -78,5 +83,6 @@ const styles = createStyle({
flexGrow: 1, flexGrow: 1,
flexShrink: 1, flexShrink: 1,
paddingRight: 5, paddingRight: 5,
// backgroundColor: '#ccc',
}, },
}) })

View File

@ -69,7 +69,8 @@ const styles = createStyle({
container: { container: {
width: '100%', width: '100%',
paddingHorizontal: 2, paddingHorizontal: 2,
paddingBottom: 2, // paddingBottom: 4,
// height: '50%',
// backgroundColor: 'rgba(0, 0, 0, .1)', // backgroundColor: 'rgba(0, 0, 0, .1)',
}, },
}) })

View File

@ -48,8 +48,7 @@ const styles = createStyle({
// marginTop: -progressContentPadding, // marginTop: -progressContentPadding,
// backgroundColor: 'rgba(0, 0, 0, .1)', // backgroundColor: 'rgba(0, 0, 0, .1)',
// borderTopWidth: BorderWidths.normal2, // borderTopWidth: BorderWidths.normal2,
paddingTop: 5, paddingVertical: 5,
paddingBottom: 5,
paddingLeft: 5, paddingLeft: 5,
// backgroundColor: AppColors.primary, // backgroundColor: AppColors.primary,
// backgroundColor: 'red', // backgroundColor: 'red',

View File

@ -67,9 +67,10 @@ const PreassBar = memo(({ onDragState, setDragProgress, onSetProgress }: {
}) })
const Progress = ({ progress, duration }: { const Progress = ({ progress, duration, paddingTop }: {
progress: number progress: number
duration: number duration: number
paddingTop?: number
}) => { }) => {
// const { progress } = usePlayTimeBuffer() // const { progress } = usePlayTimeBuffer()
const theme = useTheme() const theme = useTheme()
@ -87,7 +88,7 @@ const Progress = ({ progress, duration }: {
}, []) }, [])
return ( return (
<View style={styles.progress}> <View style={{ ...styles.progress, paddingTop }}>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<DefaultBar /> <DefaultBar />
{/* <BufferedBar bufferedProgress={bufferedProgress} /> */} {/* <BufferedBar bufferedProgress={bufferedProgress} /> */}
@ -120,7 +121,7 @@ const styles = createStyle({
}, },
progressBar: { progressBar: {
height: '100%', height: '100%',
borderRadius: 3, borderRadius: 2,
}, },
pressBar: { pressBar: {
position: 'absolute', position: 'absolute',

View File

@ -10,6 +10,10 @@ export const setMusicInfo = (musicInfo: Partial<LX.Player.MusicInfo>) => {
playerActions.setMusicInfo(musicInfo) playerActions.setMusicInfo(musicInfo)
} }
export const setLoadErrorPicUrl = (url: string) => {
playerActions.setLoadErrorPicUrl(url)
}
export const setPlayListId = (listId: string | null) => { export const setPlayListId = (listId: string | null) => {
playerActions.setPlayListId(listId) playerActions.setPlayListId(listId)
} }

View File

@ -151,7 +151,11 @@ const handleRestorePlay = async(restorePlayInfo: LX.Player.SavedPlayInfo) => {
const playMusicInfo = playerState.playMusicInfo const playMusicInfo = playerState.playMusicInfo
void getPicPath({ musicInfo, listId: playMusicInfo.listId }).then((url: string) => { void getPicPath({ musicInfo, listId: playMusicInfo.listId }).then((url: string) => {
if (musicInfo.id != playMusicInfo.musicInfo?.id) return if (
musicInfo.id != playMusicInfo.musicInfo?.id ||
playerState.musicInfo.pic == url ||
playerState.loadErrorPicUrl == url
) return
setMusicInfo({ pic: url }) setMusicInfo({ pic: url })
global.app_event.picUpdated() global.app_event.picUpdated()
}) })
@ -180,7 +184,10 @@ const debouncePlay = debounceBackgroundTimer((musicInfo: LX.Player.PlayMusic) =>
setMusicUrl(musicInfo) setMusicUrl(musicInfo)
void getPicPath({ musicInfo, listId: playerState.playMusicInfo.listId }).then((url: string) => { void getPicPath({ musicInfo, listId: playerState.playMusicInfo.listId }).then((url: string) => {
if (musicInfo.id != playerState.playMusicInfo.musicInfo?.id) return if (
musicInfo.id != playerState.playMusicInfo.musicInfo?.id ||
playerState.musicInfo.pic == url ||
playerState.loadErrorPicUrl == url) return
setMusicInfo({ pic: url }) setMusicInfo({ pic: url })
global.app_event.picUpdated() global.app_event.picUpdated()
}) })

View File

@ -19,7 +19,6 @@ const LIST_LOAD_LIMIT = 30
*/ */
export const setSelectListInfo = (info: ListInfoItem) => { export const setSelectListInfo = (info: ListInfoItem) => {
songlistActions.clearListDetail() songlistActions.clearListDetail()
songlistActions.setSelectListInfo(info)
} }
/** /**

View File

@ -13,6 +13,7 @@ import themeState from '@/store/theme/state'
import { NAV_SHEAR_NATIVE_IDS } from '@/config/constant' import { NAV_SHEAR_NATIVE_IDS } from '@/config/constant'
import { getStatusBarStyle } from './utils' import { getStatusBarStyle } from './utils'
import { windowSizeTools } from '@/utils/windowSizeTools' import { windowSizeTools } from '@/utils/windowSizeTools'
import { type ListInfoItem } from '@/store/songlist/state'
// const store = getStore() // const store = getStore()
// const getTheme = () => getter('common', 'theme')(store.getState()) // const getTheme = () => getter('common', 'theme')(store.getState())
@ -211,7 +212,7 @@ export function pushPlayDetailScreen(componentId: string) {
}) })
}) })
} }
export function pushSonglistDetailScreen(componentId: string, id: string) { export function pushSonglistDetailScreen(componentId: string, info: ListInfoItem) {
const theme = themeState.theme const theme = themeState.theme
requestAnimationFrame(() => { requestAnimationFrame(() => {
@ -219,6 +220,9 @@ export function pushSonglistDetailScreen(componentId: string, id: string) {
void Navigation.push(componentId, { void Navigation.push(componentId, {
component: { component: {
name: SONGLIST_DETAIL_SCREEN, name: SONGLIST_DETAIL_SCREEN,
passProps: {
info,
},
options: { options: {
topBar: { topBar: {
visible: false, visible: false,
@ -242,8 +246,8 @@ export function pushSonglistDetailScreen(componentId: string, id: string) {
push: { push: {
sharedElementTransitions: [ sharedElementTransitions: [
{ {
fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${id}`, fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${info.id}`,
toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${id}`, toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${info.id}`,
interpolation: { type: 'spring' }, interpolation: { type: 'spring' },
}, },
], ],
@ -281,8 +285,8 @@ export function pushSonglistDetailScreen(componentId: string, id: string) {
pop: { pop: {
sharedElementTransitions: [ sharedElementTransitions: [
{ {
fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${id}`, fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${info.id}`,
toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${id}`, toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${info.id}`,
interpolation: { type: 'spring' }, interpolation: { type: 'spring' },
}, },
], ],

View File

@ -1,5 +1,5 @@
import { memo, useState, useMemo, useCallback } from 'react' import { memo, useState, useMemo, useCallback } from 'react'
import { View } from 'react-native' import { StyleSheet, View } from 'react-native'
import { BorderWidths } from '@/theme' import { BorderWidths } from '@/theme'
import { Icon } from '@/components/common/Icon' import { Icon } from '@/components/common/Icon'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'
@ -65,7 +65,7 @@ const CommentFloor = memo(({ comment, isLast }: {
<Image <Image
url={comment.avatar && !isAvatarError ? comment.avatar : defaultUser} url={comment.avatar && !isAvatarError ? comment.avatar : defaultUser}
onError={handleAvatarError} onError={handleAvatarError}
style={{ height: avatarWidth, width: avatarWidth, borderRadius: 4 }} /> style={stylesRaw.avatar} />
</View> </View>
<View style={styles.right}> <View style={styles.right}>
<View style={styles.info}> <View style={styles.info}>
@ -152,4 +152,12 @@ const styles = createStyle({
}, },
}) })
const stylesRaw = StyleSheet.create({
avatar: {
height: avatarWidth,
width: avatarWidth,
borderRadius: 4,
},
})
export default CommentFloor export default CommentFloor

View File

@ -69,12 +69,20 @@ const NewCommentPage = memo(({ activeId, musicInfo, onUpdateTotal }: {
} }
}) })
const TABS = [
'hot',
'new',
] as const
const getMusicInfo = (musicInfo: LX.Player.PlayMusic | null) => {
if (!musicInfo) return null
return 'progress' in musicInfo ? musicInfo.metadata.musicInfo : musicInfo
}
export default memo(({ componentId }: { export default memo(({ componentId }: {
componentId: string componentId: string
}) => { }) => {
const pagerViewRef = useRef<PagerView>(null) const pagerViewRef = useRef<PagerView>(null)
const [activeId, setActiveId] = useState<ActiveId>('hot') const [activeId, setActiveId] = useState<ActiveId>('hot')
const [musicInfo, setMusicInfo] = useState<LX.Music.MusicInfo | null>(null) const [musicInfo, setMusicInfo] = useState<LX.Music.MusicInfo | null>(getMusicInfo(playerState.playMusicInfo.musicInfo))
const t = useI18n() const t = useI18n()
const theme = useTheme() const theme = useTheme()
const [total, setTotal] = useState({ hot: 0, new: 0 }) const [total, setTotal] = useState({ hot: 0, new: 0 })
@ -86,21 +94,21 @@ export default memo(({ componentId }: {
const tabs = useMemo(() => { const tabs = useMemo(() => {
return [ return [
{ id: 'hot', label: t('comment_tab_hot', { total: total.hot ? `(${total.hot})` : '' }) }, { id: TABS[0], label: t('comment_tab_hot', { total: total.hot ? `(${total.hot})` : '' }) },
{ id: 'new', label: t('comment_tab_new', { total: total.new ? `(${total.new})` : '' }) }, { id: TABS[1], label: t('comment_tab_new', { total: total.new ? `(${total.new})` : '' }) },
] as const ] as const
}, [total, t]) }, [total, t])
const toggleTab = (id: ActiveId) => { const toggleTab = useCallback((id: ActiveId) => {
setActiveId(id) setActiveId(id)
pagerViewRef.current?.setPage(tabs.findIndex(tab => tab.id == id)) pagerViewRef.current?.setPage(TABS.findIndex(tab => tab == id))
} }, [])
const onPageSelected = ({ nativeEvent }: PagerViewOnPageSelectedEvent) => { const onPageSelected = useCallback(({ nativeEvent }: PagerViewOnPageSelectedEvent) => {
setActiveId(tabs[nativeEvent.position].id) setActiveId(TABS[nativeEvent.position])
} }, [])
const refreshComment = () => { const refreshComment = useCallback(() => {
if (!playerState.playMusicInfo.musicInfo) return if (!playerState.playMusicInfo.musicInfo) return
let playerMusicInfo = playerState.playMusicInfo.musicInfo let playerMusicInfo = playerState.playMusicInfo.musicInfo
if ('progress' in playerMusicInfo) playerMusicInfo = playerMusicInfo.metadata.musicInfo if ('progress' in playerMusicInfo) playerMusicInfo = playerMusicInfo.metadata.musicInfo
@ -110,7 +118,7 @@ export default memo(({ componentId }: {
return return
} }
setMusicInfo(playerMusicInfo) setMusicInfo(playerMusicInfo)
} }, [musicInfo, t])
const setHotTotal = useCallback((total: number) => { const setHotTotal = useCallback((total: number) => {
setTotal(totalInfo => ({ ...totalInfo, hot: total })) setTotal(totalInfo => ({ ...totalInfo, hot: total }))
@ -119,30 +127,8 @@ export default memo(({ componentId }: {
setTotal(totalInfo => ({ ...totalInfo, new: total })) setTotal(totalInfo => ({ ...totalInfo, new: total }))
}, []) }, [])
useEffect(() => { const commentComponent = useMemo(() => {
setTimeout(() => {
if (!playerState.playMusicInfo.musicInfo) return
let playerMusicInfo = playerState.playMusicInfo.musicInfo
if ('progress' in playerMusicInfo) playerMusicInfo = playerMusicInfo.metadata.musicInfo
setMusicInfo(playerMusicInfo)
}, 300)
}, [])
return ( return (
<PageContent>
{
musicInfo == null
? null
: <>
<Header musicInfo={musicInfo} />
{
musicInfo.source == 'local'
? (
<View style={{ ...styles.container, alignItems: 'center', justifyContent: 'center' }}>
<Text>{t('comment_not support')}</Text>
</View>
)
: (
<View style={styles.container}> <View style={styles.container}>
<View style={{ ...styles.tabHeader, borderBottomColor: theme['c-border-background'], height: BAR_HEIGHT }}> <View style={{ ...styles.tabHeader, borderBottomColor: theme['c-border-background'], height: BAR_HEIGHT }}>
<View style={styles.left}> <View style={styles.left}>
@ -161,14 +147,31 @@ export default memo(({ componentId }: {
style={styles.pagerView} style={styles.pagerView}
> >
<View collapsable={false} style={styles.pageStyle}> <View collapsable={false} style={styles.pageStyle}>
<HotCommentPage activeId={activeId} musicInfo={musicInfo} onUpdateTotal={setHotTotal} /> <HotCommentPage activeId={activeId} musicInfo={musicInfo as LX.Music.MusicInfoOnline} onUpdateTotal={setHotTotal} />
</View> </View>
<View collapsable={false} style={styles.pageStyle}> <View collapsable={false} style={styles.pageStyle}>
<NewCommentPage activeId={activeId} musicInfo={musicInfo} onUpdateTotal={setNewTotal} /> <NewCommentPage activeId={activeId} musicInfo={musicInfo as LX.Music.MusicInfoOnline} onUpdateTotal={setNewTotal} />
</View> </View>
</PagerView> </PagerView>
</View> </View>
) )
}, [activeId, musicInfo, onPageSelected, refreshComment, setHotTotal, setNewTotal, tabs, theme, toggleTab])
return (
<PageContent>
{
musicInfo == null
? null
: <>
<Header musicInfo={musicInfo} />
{
musicInfo.source == 'local'
? (
<View style={{ ...styles.container, alignItems: 'center', justifyContent: 'center' }}>
<Text>{t('comment_not support')}</Text>
</View>
)
: commentComponent
} }
</> </>
} }

View File

@ -2,7 +2,6 @@ import { useRef, forwardRef, useImperativeHandle } from 'react'
import { type ListInfoItem } from '@/store/songlist/state' import { type ListInfoItem } from '@/store/songlist/state'
// import LoadingMask, { LoadingMaskType } from '@/components/common/LoadingMask' // import LoadingMask, { LoadingMaskType } from '@/components/common/LoadingMask'
import List, { type ListProps, type ListType, type Status } from './List' import List, { type ListProps, type ListType, type Status } from './List'
import { setSelectListInfo } from '@/core/songlist'
import { navigations } from '@/navigation' import { navigations } from '@/navigation'
import commonState from '@/store/common/state' import commonState from '@/store/common/state'
@ -32,9 +31,7 @@ export default forwardRef<SonglistType, SonglistProps>(({
})) }))
const handleOpenDetail = (item: ListInfoItem, index: number) => { const handleOpenDetail = (item: ListInfoItem, index: number) => {
// console.log(item) navigations.pushSonglistDetailScreen(commonState.componentIds.home!, item)
setSelectListInfo(item)
navigations.pushSonglistDetailScreen(commonState.componentIds.home!, item.id)
} }
return ( return (

View File

@ -1,9 +1,6 @@
import { memo, useState } from 'react' import { memo, useState } from 'react'
import { View } from 'react-native' import { View } from 'react-native'
// import { useLayout } from '@/utils/hooks' // import { useLayout } from '@/utils/hooks'
import { useTheme } from '@/store/theme/hook'
import { BorderRadius } from '@/theme'
import Text from '@/components/common/Text'
import { usePlayerMusicInfo } from '@/store/player/hook' import { usePlayerMusicInfo } from '@/store/player/hook'
import { useWindowSize } from '@/utils/hooks' import { useWindowSize } from '@/utils/hooks'
import { useNavigationComponentDidAppear } from '@/navigation' import { useNavigationComponentDidAppear } from '@/navigation'
@ -15,16 +12,6 @@ import { BTN_WIDTH } from './MoreBtn/Btn'
import { marginLeft } from './constant' import { marginLeft } from './constant'
import Image from '@/components/common/Image' import Image from '@/components/common/Image'
const EmptyPic = memo(({ width }: { width: number }) => {
const theme = useTheme()
const size = width * 0.2
return (
<View style={{ ...styles.emptyPic, width, height: width, backgroundColor: theme['c-primary-light-900-alpha-200'] }}>
<Text size={size} color={theme['c-primary-light-400-alpha-200']}>L</Text>
<Text size={size} color={theme['c-primary-light-400-alpha-200']} style={styles.text}>X</Text>
</View>
)
})
export default memo(({ componentId }: { componentId: string }) => { export default memo(({ componentId }: { componentId: string }) => {
const musicInfo = usePlayerMusicInfo() const musicInfo = usePlayerMusicInfo()
@ -44,18 +31,11 @@ export default memo(({ componentId }: { componentId: string }) => {
return ( return (
<View style={{ ...styles.container, height: contentHeight }}> <View style={{ ...styles.container, height: contentHeight }}>
<View style={{ ...styles.content, elevation: animated ? 3 : 0 }}> <View style={{ ...styles.content, elevation: animated ? 3 : 0 }}>
{
musicInfo.pic
? (
<Image url={musicInfo.pic} nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_pic} style={{ <Image url={musicInfo.pic} nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_pic} style={{
...styles.img,
width: imgWidth, width: imgWidth,
height: imgWidth, height: imgWidth,
borderRadius: 2, borderRadius: 2,
}} /> }} />
)
: <EmptyPic width={imgWidth} />
}
</View> </View>
</View> </View>
) )
@ -75,17 +55,4 @@ const styles = createStyle({
backgroundColor: 'rgba(0,0,0,0)', backgroundColor: 'rgba(0,0,0,0)',
borderRadius: 4, borderRadius: 4,
}, },
img: {
borderRadius: 4,
// opacity: 0,
},
emptyPic: {
borderRadius: BorderRadius.normal,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
text: {
paddingLeft: 2,
},
}) })

View File

@ -1,28 +1,15 @@
import { memo, useState } from 'react' import { useMemo, useState } from 'react'
import { View } from 'react-native' import { View } from 'react-native'
// import { useLayout } from '@/utils/hooks' // import { useLayout } from '@/utils/hooks'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'
import { usePlayerMusicInfo } from '@/store/player/hook' import { usePlayerMusicInfo } from '@/store/player/hook'
import { useTheme } from '@/store/theme/hook'
import { BorderRadius } from '@/theme'
import { useWindowSize } from '@/utils/hooks' import { useWindowSize } from '@/utils/hooks'
import Text from '@/components/common/Text'
import { NAV_SHEAR_NATIVE_IDS } from '@/config/constant' import { NAV_SHEAR_NATIVE_IDS } from '@/config/constant'
import { useNavigationComponentDidAppear } from '@/navigation' import { useNavigationComponentDidAppear } from '@/navigation'
import StatusBar from '@/components/common/StatusBar' import StatusBar from '@/components/common/StatusBar'
import { HEADER_HEIGHT } from './components/Header' import { HEADER_HEIGHT } from './components/Header'
import Image from '@/components/common/Image' import Image from '@/components/common/Image'
const EmptyPic = memo(({ width }: { width: number }) => {
const theme = useTheme()
const size = width * 0.2
return (
<View style={{ ...styles.emptyPic, width, height: width, backgroundColor: theme['c-primary-light-900-alpha-200'] }}>
<Text size={size} color={theme['c-primary-light-400-alpha-200']}>L</Text>
<Text size={size} color={theme['c-primary-light-400-alpha-200']} style={styles.text}>X</Text>
</View>
)
})
export default ({ componentId }: { componentId: string }) => { export default ({ componentId }: { componentId: string }) => {
const musicInfo = usePlayerMusicInfo() const musicInfo = usePlayerMusicInfo()
@ -35,23 +22,19 @@ export default ({ componentId }: { componentId: string }) => {
}) })
// console.log('render pic') // console.log('render pic')
const style = useMemo(() => {
const imgWidth = Math.min(winWidth * 0.8, (winHeight - StatusBar.currentHeight - HEADER_HEIGHT) * 0.5) const imgWidth = Math.min(winWidth * 0.8, (winHeight - StatusBar.currentHeight - HEADER_HEIGHT) * 0.5)
return {
width: imgWidth,
height: imgWidth,
borderRadius: 2,
}
}, [winHeight, winWidth])
return ( return (
<View style={styles.container}> <View style={styles.container}>
<View style={{ ...styles.content, elevation: animated ? 3 : 0 }}> <View style={{ ...styles.content, elevation: animated ? 3 : 0 }}>
{ <Image url={musicInfo.pic} nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_pic} style={style} />
musicInfo.pic
? (
<Image url={musicInfo.pic} nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_pic} style={{
...styles.img,
width: imgWidth,
height: imgWidth,
borderRadius: 2,
}} />
)
: <EmptyPic width={imgWidth} />
}
</View> </View>
</View> </View>
) )
@ -70,17 +53,4 @@ const styles = createStyle({
backgroundColor: 'rgba(0,0,0,0)', backgroundColor: 'rgba(0,0,0,0)',
borderRadius: 4, borderRadius: 4,
}, },
img: {
borderRadius: 4,
// opacity: 0,
},
emptyPic: {
borderRadius: BorderRadius.normal,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
text: {
paddingLeft: 2,
},
}) })

View File

@ -10,11 +10,13 @@ import Text from '@/components/common/Text'
import { handleCollect, handlePlay } from './listAction' import { handleCollect, handlePlay } from './listAction'
import songlistState from '@/store/songlist/state' import songlistState from '@/store/songlist/state'
import { useI18n } from '@/lang' import { useI18n } from '@/lang'
import { useListInfo } from './state'
// import { NAV_SHEAR_NATIVE_IDS } from '@/config/constant' // import { NAV_SHEAR_NATIVE_IDS } from '@/config/constant'
export default memo(() => { export default memo(() => {
const theme = useTheme() const theme = useTheme()
const t = useI18n() const t = useI18n()
const info = useListInfo()
const back = () => { const back = () => {
void pop(commonState.componentIds.songlistDetail!) void pop(commonState.componentIds.songlistDetail!)
@ -22,12 +24,12 @@ export default memo(() => {
const handlePlayAll = () => { const handlePlayAll = () => {
if (!songlistState.listDetailInfo.info.name) return if (!songlistState.listDetailInfo.info.name) return
void handlePlay(songlistState.selectListInfo.id, songlistState.selectListInfo.source, songlistState.listDetailInfo.list) void handlePlay(info.id, info.source, songlistState.listDetailInfo.list)
} }
const handleCollection = () => { const handleCollection = () => {
if (!songlistState.listDetailInfo.info.name) return if (!songlistState.listDetailInfo.info.name) return
void handleCollect(songlistState.selectListInfo.id, songlistState.selectListInfo.source, songlistState.listDetailInfo.info.name || songlistState.selectListInfo.name) void handleCollect(info.id, info.source, songlistState.listDetailInfo.info.name || info.name)
} }
return ( return (

View File

@ -9,8 +9,8 @@ import { useTheme } from '@/store/theme/hook'
import Text from '@/components/common/Text' import Text from '@/components/common/Text'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'
import StatusBar from '@/components/common/StatusBar' import StatusBar from '@/components/common/StatusBar'
import songlistState from '@/store/songlist/state'
import Image from '@/components/common/Image' import Image from '@/components/common/Image'
import { useListInfo } from './state'
const IMAGE_WIDTH = scaleSizeW(70) const IMAGE_WIDTH = scaleSizeW(70)
@ -20,6 +20,7 @@ const Pic = ({ componentId, playCount, imgUrl }: {
imgUrl?: string imgUrl?: string
}) => { }) => {
const [animated, setAnimated] = useState(false) const [animated, setAnimated] = useState(false)
const info = useListInfo()
useNavigationComponentDidAppear(componentId, () => { useNavigationComponentDidAppear(componentId, () => {
setAnimated(true) setAnimated(true)
@ -27,7 +28,7 @@ const Pic = ({ componentId, playCount, imgUrl }: {
return ( return (
<View style={{ ...styles.listItemImg, width: IMAGE_WIDTH, height: IMAGE_WIDTH }}> <View style={{ ...styles.listItemImg, width: IMAGE_WIDTH, height: IMAGE_WIDTH }}>
<Image nativeID={`${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${songlistState.selectListInfo.id}`} url={imgUrl} style={{ flex: 1, justifyContent: 'flex-end', borderRadius: 4 }} /> <Image nativeID={`${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${info.id}`} url={imgUrl} style={{ flex: 1, justifyContent: 'flex-end', borderRadius: 4 }} />
{ {
playCount && animated playCount && animated
? <Text style={styles.playCount} numberOfLines={ 1 }>{playCount}</Text> ? <Text style={styles.playCount} numberOfLines={ 1 }>{playCount}</Text>
@ -53,7 +54,8 @@ export interface DetailInfo {
export default forwardRef<HeaderType, HeaderProps>(({ componentId }: { componentId: string }, ref) => { export default forwardRef<HeaderType, HeaderProps>(({ componentId }: { componentId: string }, ref) => {
const theme = useTheme() const theme = useTheme()
const [detailInfo, setDetailInfo] = useState<DetailInfo>({ name: '', desc: '', playCount: '' }) const info = useListInfo()
const [detailInfo, setDetailInfo] = useState<DetailInfo>({ name: '', desc: '', playCount: '', imgUrl: info.img })
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
setInfo(info) { setInfo(info) {

View File

@ -4,6 +4,7 @@ import { clearListDetail, getListDetail, setListDetail, setListDetailInfo } from
import songlistState from '@/store/songlist/state' import songlistState from '@/store/songlist/state'
import { handlePlay } from './listAction' import { handlePlay } from './listAction'
import Header, { type HeaderType } from './Header' import Header, { type HeaderType } from './Header'
import { useListInfo } from './state'
export interface MusicListProps { export interface MusicListProps {
componentId: string componentId: string
@ -17,42 +18,45 @@ export default forwardRef<MusicListType, MusicListProps>(({ componentId }, ref)
const listRef = useRef<OnlineListType>(null) const listRef = useRef<OnlineListType>(null)
const headerRef = useRef<HeaderType>(null) const headerRef = useRef<HeaderType>(null)
const isUnmountedRef = useRef(false) const isUnmountedRef = useRef(false)
const info = useListInfo()
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
async loadList(source, id) { async loadList(source, id) {
clearListDetail()
const listDetailInfo = songlistState.listDetailInfo const listDetailInfo = songlistState.listDetailInfo
listRef.current?.setList([]) listRef.current?.setList([])
if (listDetailInfo.id == id && listDetailInfo.source == source && listDetailInfo.list.length) { if (listDetailInfo.id == id && listDetailInfo.source == source && listDetailInfo.list.length) {
requestAnimationFrame(() => { requestAnimationFrame(() => {
listRef.current?.setList(listDetailInfo.list) listRef.current?.setList(listDetailInfo.list)
headerRef.current?.setInfo({ headerRef.current?.setInfo({
name: (songlistState.selectListInfo.name || listDetailInfo.info.name) ?? '', name: (info.name || listDetailInfo.info.name) ?? '',
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
desc: songlistState.selectListInfo.desc || listDetailInfo.info.desc || '', desc: info.desc || listDetailInfo.info.desc || '',
playCount: (songlistState.selectListInfo.play_count ?? listDetailInfo.info.play_count) ?? '', playCount: (info.play_count ?? listDetailInfo.info.play_count) ?? '',
imgUrl: songlistState.selectListInfo.img ?? listDetailInfo.info.img, imgUrl: info.img ?? listDetailInfo.info.img,
}) })
}) })
} else { } else {
listRef.current?.setStatus('loading') listRef.current?.setStatus('loading')
const page = 1 const page = 1
setListDetailInfo(songlistState.selectListInfo.source, songlistState.selectListInfo.id) setListDetailInfo(info.source, info.id)
headerRef.current?.setInfo({ headerRef.current?.setInfo({
name: (songlistState.selectListInfo.name || listDetailInfo.info.name) ?? '', name: (info.name || listDetailInfo.info.name) ?? '',
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
desc: songlistState.selectListInfo.desc || listDetailInfo.info.desc || '', desc: info.desc || listDetailInfo.info.desc || '',
playCount: (songlistState.selectListInfo.play_count ?? listDetailInfo.info.play_count) ?? '', playCount: (info.play_count ?? listDetailInfo.info.play_count) ?? '',
imgUrl: songlistState.selectListInfo.img ?? listDetailInfo.info.img, imgUrl: info.img ?? listDetailInfo.info.img,
}) })
return getListDetail(id, source, page).then((listDetail) => { return getListDetail(id, source, page).then((listDetail) => {
const result = setListDetail(listDetail, id, page) const result = setListDetail(listDetail, id, page)
if (isUnmountedRef.current) return if (isUnmountedRef.current) return
requestAnimationFrame(() => { requestAnimationFrame(() => {
headerRef.current?.setInfo({ headerRef.current?.setInfo({
name: (songlistState.selectListInfo.name || listDetailInfo.info.name) ?? '', name: (info.name || listDetailInfo.info.name) ?? '',
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
desc: songlistState.selectListInfo.desc || listDetailInfo.info.desc || '', desc: info.desc || listDetailInfo.info.desc || '',
playCount: (songlistState.selectListInfo.play_count ?? listDetailInfo.info.play_count) ?? '', playCount: (info.play_count ?? listDetailInfo.info.play_count) ?? '',
imgUrl: songlistState.selectListInfo.img ?? listDetailInfo.info.img, imgUrl: info.img ?? listDetailInfo.info.img,
}) })
listRef.current?.setList(result.list) listRef.current?.setList(result.list)
listRef.current?.setStatus(songlistState.listDetailInfo.maxPage <= page ? 'end' : 'idle') listRef.current?.setStatus(songlistState.listDetailInfo.maxPage <= page ? 'end' : 'idle')

View File

@ -5,11 +5,12 @@ import PageContent from '@/components/PageContent'
import StatusBar from '@/components/common/StatusBar' import StatusBar from '@/components/common/StatusBar'
import { setComponentId } from '@/core/common' import { setComponentId } from '@/core/common'
import { COMPONENT_IDS } from '@/config/constant' import { COMPONENT_IDS } from '@/config/constant'
import songlistState from '@/store/songlist/state' import { type ListInfoItem } from '@/store/songlist/state'
import PlayerBar from '@/components/player/PlayerBar' import PlayerBar from '@/components/player/PlayerBar'
import { ListInfoContext } from './state'
export default ({ componentId }: { componentId: string }) => { export default ({ componentId, info }: { componentId: string, info: ListInfoItem }) => {
const musicListRef = useRef<MusicListType>(null) const musicListRef = useRef<MusicListType>(null)
const isUnmountedRef = useRef(false) const isUnmountedRef = useRef(false)
@ -18,7 +19,7 @@ export default ({ componentId }: { componentId: string }) => {
isUnmountedRef.current = false isUnmountedRef.current = false
musicListRef.current?.loadList(songlistState.selectListInfo.source, songlistState.selectListInfo.id) musicListRef.current?.loadList(info.source, info.id)
return () => { return () => {
@ -31,7 +32,9 @@ export default ({ componentId }: { componentId: string }) => {
return ( return (
<PageContent> <PageContent>
<StatusBar /> <StatusBar />
<ListInfoContext.Provider value={info}>
<MusicList ref={musicListRef} componentId={componentId} /> <MusicList ref={musicListRef} componentId={componentId} />
</ListInfoContext.Provider>
<PlayerBar /> <PlayerBar />
</PageContent> </PageContent>
) )

View File

@ -0,0 +1,13 @@
import { type ListInfoItem } from '@/store/songlist/state'
import { createContext, useContext } from 'react'
export const ListInfoContext = createContext<ListInfoItem>({
id: '',
author: '',
name: '',
source: 'kw',
})
export const useListInfo = () => {
return useContext(ListInfoContext)
}

View File

@ -104,4 +104,7 @@ export default {
global.state_event.playTempPlayListChanged({ ...state.tempPlayList }) global.state_event.playTempPlayListChanged({ ...state.tempPlayList })
}, },
setLoadErrorPicUrl(url: string) {
state.loadErrorPicUrl = url
},
} }

View File

@ -24,6 +24,8 @@ export interface InitState {
playedList: LX.Player.PlayMusicInfo[] playedList: LX.Player.PlayMusicInfo[]
tempPlayList: LX.Player.PlayMusicInfo[] tempPlayList: LX.Player.PlayMusicInfo[]
loadErrorPicUrl: string
progress: { progress: {
nowPlayTime: number nowPlayTime: number
@ -63,6 +65,7 @@ const state: InitState = {
volume: 1, volume: 1,
playRate: 1, playRate: 1,
statusText: '', statusText: '',
loadErrorPicUrl: '',
playedList: [], playedList: [],
tempPlayList: [], tempPlayList: [],

View File

@ -1,4 +1,4 @@
import type { TagInfo, ListDetailInfo, ListInfo, ListInfoItem, Source } from './state' import type { TagInfo, ListDetailInfo, ListInfo, Source } from './state'
import state from './state' import state from './state'
export default { export default {
@ -58,13 +58,4 @@ export default {
state.listDetailInfo.info = {} state.listDetailInfo.info = {}
state.listDetailInfo.maxPage = 1 state.listDetailInfo.maxPage = 1
}, },
setSelectListInfo(info: ListInfoItem) {
state.selectListInfo.author = info.author
state.selectListInfo.desc = info.desc
state.selectListInfo.id = info.id
state.selectListInfo.img = info.img
state.selectListInfo.name = info.name
state.selectListInfo.play_count = info.play_count
state.selectListInfo.source = info.source
},
} }

View File

@ -79,7 +79,6 @@ export interface InitState {
sortList: Partial<Record<Source, SortInfo[]>> sortList: Partial<Record<Source, SortInfo[]>>
tags: Tags tags: Tags
listInfo: ListInfo listInfo: ListInfo
selectListInfo: ListInfoItem
listDetailInfo: ListDetailInfo listDetailInfo: ListDetailInfo
} }
@ -99,17 +98,6 @@ const state: InitState = {
tagId: '', tagId: '',
sortId: '', sortId: '',
}, },
selectListInfo: {
play_count: '',
id: '',
author: '',
name: '',
time: '',
img: '',
// grade: basic.favorcnt / 10,
desc: '',
source: 'kw',
},
listDetailInfo: { listDetailInfo: {
list: [], list: [],
id: '', id: '',