修复加载某些图片导致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 Text from './Text'
import { useLayout } from '@/utils/hooks'
export type { OnLoadEvent } from 'react-native-fast-image'
export interface ImageProps extends Omit<FastImageProps, 'source'> {
url?: string | number
export interface ImageProps extends ViewProps {
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',
}
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'
? _Image.resolveAssetSource(url).uri
: url?.startsWith('/')
? 'file://' + url
: url
const showDefault = useMemo(() => !uri || isError, [isError, uri])
return (
<FastImage
{...props}
source={{
uri,
headers: defaultHeaders,
priority: FastImage.priority.normal,
}}
resizeMode={resizeMode}
/>
showDefault ? <EmptyPic style={style} nativeID={nativeID} />
: (
<FastImage
style={style}
source={{
uri: uri!,
headers: defaultHeaders,
priority: FastImage.priority.normal,
}}
onError={handleError}
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) => {
_Image.getSize(uri, success, failure)
}
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,
style,
imageStyle,
importantForAccessibility,
url,
...props
}, ref) => {
@ -64,7 +63,7 @@ export default forwardRef<ImageBackgroundType, ImageBackgroundProps>(({
return (
<View
accessibilityIgnoresInvertColors={true}
importantForAccessibility={importantForAccessibility}
importantForAccessibility={'no'}
ref={ref}
style={style}>
{
@ -72,7 +71,6 @@ export default forwardRef<ImageBackgroundType, ImageBackgroundProps>(({
<Image
{...props}
url={url}
importantForAccessibility={importantForAccessibility}
style={[
StyleSheet.absoluteFill,
{

View File

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

View File

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

View File

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

View File

@ -48,8 +48,7 @@ const styles = createStyle({
// marginTop: -progressContentPadding,
// backgroundColor: 'rgba(0, 0, 0, .1)',
// borderTopWidth: BorderWidths.normal2,
paddingTop: 5,
paddingBottom: 5,
paddingVertical: 5,
paddingLeft: 5,
// backgroundColor: AppColors.primary,
// 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
duration: number
paddingTop?: number
}) => {
// const { progress } = usePlayTimeBuffer()
const theme = useTheme()
@ -87,7 +88,7 @@ const Progress = ({ progress, duration }: {
}, [])
return (
<View style={styles.progress}>
<View style={{ ...styles.progress, paddingTop }}>
<View style={{ flex: 1 }}>
<DefaultBar />
{/* <BufferedBar bufferedProgress={bufferedProgress} /> */}
@ -120,7 +121,7 @@ const styles = createStyle({
},
progressBar: {
height: '100%',
borderRadius: 3,
borderRadius: 2,
},
pressBar: {
position: 'absolute',

View File

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

View File

@ -151,7 +151,11 @@ const handleRestorePlay = async(restorePlayInfo: LX.Player.SavedPlayInfo) => {
const playMusicInfo = playerState.playMusicInfo
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 })
global.app_event.picUpdated()
})
@ -180,7 +184,10 @@ const debouncePlay = debounceBackgroundTimer((musicInfo: LX.Player.PlayMusic) =>
setMusicUrl(musicInfo)
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 })
global.app_event.picUpdated()
})

View File

@ -19,7 +19,6 @@ const LIST_LOAD_LIMIT = 30
*/
export const setSelectListInfo = (info: ListInfoItem) => {
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 { getStatusBarStyle } from './utils'
import { windowSizeTools } from '@/utils/windowSizeTools'
import { type ListInfoItem } from '@/store/songlist/state'
// const store = getStore()
// 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
requestAnimationFrame(() => {
@ -219,6 +220,9 @@ export function pushSonglistDetailScreen(componentId: string, id: string) {
void Navigation.push(componentId, {
component: {
name: SONGLIST_DETAIL_SCREEN,
passProps: {
info,
},
options: {
topBar: {
visible: false,
@ -242,8 +246,8 @@ export function pushSonglistDetailScreen(componentId: string, id: string) {
push: {
sharedElementTransitions: [
{
fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${id}`,
toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${id}`,
fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${info.id}`,
toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${info.id}`,
interpolation: { type: 'spring' },
},
],
@ -281,8 +285,8 @@ export function pushSonglistDetailScreen(componentId: string, id: string) {
pop: {
sharedElementTransitions: [
{
fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${id}`,
toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${id}`,
fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${info.id}`,
toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${info.id}`,
interpolation: { type: 'spring' },
},
],

View File

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

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 }: {
componentId: string
}) => {
const pagerViewRef = useRef<PagerView>(null)
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 theme = useTheme()
const [total, setTotal] = useState({ hot: 0, new: 0 })
@ -86,21 +94,21 @@ export default memo(({ componentId }: {
const tabs = useMemo(() => {
return [
{ id: 'hot', label: t('comment_tab_hot', { total: total.hot ? `(${total.hot})` : '' }) },
{ id: 'new', label: t('comment_tab_new', { total: total.new ? `(${total.new})` : '' }) },
{ id: TABS[0], label: t('comment_tab_hot', { total: total.hot ? `(${total.hot})` : '' }) },
{ id: TABS[1], label: t('comment_tab_new', { total: total.new ? `(${total.new})` : '' }) },
] as const
}, [total, t])
const toggleTab = (id: ActiveId) => {
const toggleTab = useCallback((id: ActiveId) => {
setActiveId(id)
pagerViewRef.current?.setPage(tabs.findIndex(tab => tab.id == id))
}
pagerViewRef.current?.setPage(TABS.findIndex(tab => tab == id))
}, [])
const onPageSelected = ({ nativeEvent }: PagerViewOnPageSelectedEvent) => {
setActiveId(tabs[nativeEvent.position].id)
}
const onPageSelected = useCallback(({ nativeEvent }: PagerViewOnPageSelectedEvent) => {
setActiveId(TABS[nativeEvent.position])
}, [])
const refreshComment = () => {
const refreshComment = useCallback(() => {
if (!playerState.playMusicInfo.musicInfo) return
let playerMusicInfo = playerState.playMusicInfo.musicInfo
if ('progress' in playerMusicInfo) playerMusicInfo = playerMusicInfo.metadata.musicInfo
@ -110,7 +118,7 @@ export default memo(({ componentId }: {
return
}
setMusicInfo(playerMusicInfo)
}
}, [musicInfo, t])
const setHotTotal = useCallback((total: number) => {
setTotal(totalInfo => ({ ...totalInfo, hot: total }))
@ -119,14 +127,35 @@ export default memo(({ componentId }: {
setTotal(totalInfo => ({ ...totalInfo, new: total }))
}, [])
useEffect(() => {
setTimeout(() => {
if (!playerState.playMusicInfo.musicInfo) return
let playerMusicInfo = playerState.playMusicInfo.musicInfo
if ('progress' in playerMusicInfo) playerMusicInfo = playerMusicInfo.metadata.musicInfo
setMusicInfo(playerMusicInfo)
}, 300)
}, [])
const commentComponent = useMemo(() => {
return (
<View style={styles.container}>
<View style={{ ...styles.tabHeader, borderBottomColor: theme['c-border-background'], height: BAR_HEIGHT }}>
<View style={styles.left}>
{tabs.map(({ id, label }) => <HeaderItem id={id} label={label} key={id} isActive={activeId == id} onPress={toggleTab} />)}
</View>
<View>
<TouchableOpacity onPress={refreshComment} style={{ ...styles.btn, width: BAR_HEIGHT }}>
<Icon name="available_updates" size={20} color={theme['c-600']} />
</TouchableOpacity>
</View>
</View>
<PagerView
ref={pagerViewRef}
onPageSelected={onPageSelected}
// onPageScrollStateChanged={onPageScrollStateChanged}
style={styles.pagerView}
>
<View collapsable={false} style={styles.pageStyle}>
<HotCommentPage activeId={activeId} musicInfo={musicInfo as LX.Music.MusicInfoOnline} onUpdateTotal={setHotTotal} />
</View>
<View collapsable={false} style={styles.pageStyle}>
<NewCommentPage activeId={activeId} musicInfo={musicInfo as LX.Music.MusicInfoOnline} onUpdateTotal={setNewTotal} />
</View>
</PagerView>
</View>
)
}, [activeId, musicInfo, onPageSelected, refreshComment, setHotTotal, setNewTotal, tabs, theme, toggleTab])
return (
<PageContent>
@ -142,33 +171,7 @@ export default memo(({ componentId }: {
<Text>{t('comment_not support')}</Text>
</View>
)
: (
<View style={styles.container}>
<View style={{ ...styles.tabHeader, borderBottomColor: theme['c-border-background'], height: BAR_HEIGHT }}>
<View style={styles.left}>
{tabs.map(({ id, label }) => <HeaderItem id={id} label={label} key={id} isActive={activeId == id} onPress={toggleTab} />)}
</View>
<View>
<TouchableOpacity onPress={refreshComment} style={{ ...styles.btn, width: BAR_HEIGHT }}>
<Icon name="available_updates" size={20} color={theme['c-600']} />
</TouchableOpacity>
</View>
</View>
<PagerView
ref={pagerViewRef}
onPageSelected={onPageSelected}
// onPageScrollStateChanged={onPageScrollStateChanged}
style={styles.pagerView}
>
<View collapsable={false} style={styles.pageStyle}>
<HotCommentPage activeId={activeId} musicInfo={musicInfo} onUpdateTotal={setHotTotal} />
</View>
<View collapsable={false} style={styles.pageStyle}>
<NewCommentPage activeId={activeId} musicInfo={musicInfo} onUpdateTotal={setNewTotal} />
</View>
</PagerView>
</View>
)
: commentComponent
}
</>
}

View File

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

View File

@ -1,9 +1,6 @@
import { memo, useState } from 'react'
import { View } from 'react-native'
// 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 { useWindowSize } from '@/utils/hooks'
import { useNavigationComponentDidAppear } from '@/navigation'
@ -15,16 +12,6 @@ import { BTN_WIDTH } from './MoreBtn/Btn'
import { marginLeft } from './constant'
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 }) => {
const musicInfo = usePlayerMusicInfo()
@ -44,18 +31,11 @@ export default memo(({ componentId }: { componentId: string }) => {
return (
<View style={{ ...styles.container, height: contentHeight }}>
<View style={{ ...styles.content, elevation: animated ? 3 : 0 }}>
{
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} />
}
<Image url={musicInfo.pic} nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_pic} style={{
width: imgWidth,
height: imgWidth,
borderRadius: 2,
}} />
</View>
</View>
)
@ -75,17 +55,4 @@ const styles = createStyle({
backgroundColor: 'rgba(0,0,0,0)',
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 { useLayout } from '@/utils/hooks'
import { createStyle } from '@/utils/tools'
import { usePlayerMusicInfo } from '@/store/player/hook'
import { useTheme } from '@/store/theme/hook'
import { BorderRadius } from '@/theme'
import { useWindowSize } from '@/utils/hooks'
import Text from '@/components/common/Text'
import { NAV_SHEAR_NATIVE_IDS } from '@/config/constant'
import { useNavigationComponentDidAppear } from '@/navigation'
import StatusBar from '@/components/common/StatusBar'
import { HEADER_HEIGHT } from './components/Header'
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 }) => {
const musicInfo = usePlayerMusicInfo()
@ -35,23 +22,19 @@ export default ({ componentId }: { componentId: string }) => {
})
// console.log('render pic')
const imgWidth = Math.min(winWidth * 0.8, (winHeight - StatusBar.currentHeight - HEADER_HEIGHT) * 0.5)
const style = useMemo(() => {
const imgWidth = Math.min(winWidth * 0.8, (winHeight - StatusBar.currentHeight - HEADER_HEIGHT) * 0.5)
return {
width: imgWidth,
height: imgWidth,
borderRadius: 2,
}
}, [winHeight, winWidth])
return (
<View style={styles.container}>
<View style={{ ...styles.content, elevation: animated ? 3 : 0 }}>
{
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} />
}
<Image url={musicInfo.pic} nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_pic} style={style} />
</View>
</View>
)
@ -70,17 +53,4 @@ const styles = createStyle({
backgroundColor: 'rgba(0,0,0,0)',
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 songlistState from '@/store/songlist/state'
import { useI18n } from '@/lang'
import { useListInfo } from './state'
// import { NAV_SHEAR_NATIVE_IDS } from '@/config/constant'
export default memo(() => {
const theme = useTheme()
const t = useI18n()
const info = useListInfo()
const back = () => {
void pop(commonState.componentIds.songlistDetail!)
@ -22,12 +24,12 @@ export default memo(() => {
const handlePlayAll = () => {
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 = () => {
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 (

View File

@ -9,8 +9,8 @@ import { useTheme } from '@/store/theme/hook'
import Text from '@/components/common/Text'
import { createStyle } from '@/utils/tools'
import StatusBar from '@/components/common/StatusBar'
import songlistState from '@/store/songlist/state'
import Image from '@/components/common/Image'
import { useListInfo } from './state'
const IMAGE_WIDTH = scaleSizeW(70)
@ -20,6 +20,7 @@ const Pic = ({ componentId, playCount, imgUrl }: {
imgUrl?: string
}) => {
const [animated, setAnimated] = useState(false)
const info = useListInfo()
useNavigationComponentDidAppear(componentId, () => {
setAnimated(true)
@ -27,7 +28,7 @@ const Pic = ({ componentId, playCount, imgUrl }: {
return (
<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
? <Text style={styles.playCount} numberOfLines={ 1 }>{playCount}</Text>
@ -53,7 +54,8 @@ export interface DetailInfo {
export default forwardRef<HeaderType, HeaderProps>(({ componentId }: { componentId: string }, ref) => {
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, () => ({
setInfo(info) {

View File

@ -4,6 +4,7 @@ import { clearListDetail, getListDetail, setListDetail, setListDetailInfo } from
import songlistState from '@/store/songlist/state'
import { handlePlay } from './listAction'
import Header, { type HeaderType } from './Header'
import { useListInfo } from './state'
export interface MusicListProps {
componentId: string
@ -17,42 +18,45 @@ export default forwardRef<MusicListType, MusicListProps>(({ componentId }, ref)
const listRef = useRef<OnlineListType>(null)
const headerRef = useRef<HeaderType>(null)
const isUnmountedRef = useRef(false)
const info = useListInfo()
useImperativeHandle(ref, () => ({
async loadList(source, id) {
clearListDetail()
const listDetailInfo = songlistState.listDetailInfo
listRef.current?.setList([])
if (listDetailInfo.id == id && listDetailInfo.source == source && listDetailInfo.list.length) {
requestAnimationFrame(() => {
listRef.current?.setList(listDetailInfo.list)
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
desc: songlistState.selectListInfo.desc || listDetailInfo.info.desc || '',
playCount: (songlistState.selectListInfo.play_count ?? listDetailInfo.info.play_count) ?? '',
imgUrl: songlistState.selectListInfo.img ?? listDetailInfo.info.img,
desc: info.desc || listDetailInfo.info.desc || '',
playCount: (info.play_count ?? listDetailInfo.info.play_count) ?? '',
imgUrl: info.img ?? listDetailInfo.info.img,
})
})
} else {
listRef.current?.setStatus('loading')
const page = 1
setListDetailInfo(songlistState.selectListInfo.source, songlistState.selectListInfo.id)
setListDetailInfo(info.source, info.id)
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
desc: songlistState.selectListInfo.desc || listDetailInfo.info.desc || '',
playCount: (songlistState.selectListInfo.play_count ?? listDetailInfo.info.play_count) ?? '',
imgUrl: songlistState.selectListInfo.img ?? listDetailInfo.info.img,
desc: info.desc || listDetailInfo.info.desc || '',
playCount: (info.play_count ?? listDetailInfo.info.play_count) ?? '',
imgUrl: info.img ?? listDetailInfo.info.img,
})
return getListDetail(id, source, page).then((listDetail) => {
const result = setListDetail(listDetail, id, page)
if (isUnmountedRef.current) return
requestAnimationFrame(() => {
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
desc: songlistState.selectListInfo.desc || listDetailInfo.info.desc || '',
playCount: (songlistState.selectListInfo.play_count ?? listDetailInfo.info.play_count) ?? '',
imgUrl: songlistState.selectListInfo.img ?? listDetailInfo.info.img,
desc: info.desc || listDetailInfo.info.desc || '',
playCount: (info.play_count ?? listDetailInfo.info.play_count) ?? '',
imgUrl: info.img ?? listDetailInfo.info.img,
})
listRef.current?.setList(result.list)
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 { setComponentId } from '@/core/common'
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 { ListInfoContext } from './state'
export default ({ componentId }: { componentId: string }) => {
export default ({ componentId, info }: { componentId: string, info: ListInfoItem }) => {
const musicListRef = useRef<MusicListType>(null)
const isUnmountedRef = useRef(false)
@ -18,7 +19,7 @@ export default ({ componentId }: { componentId: string }) => {
isUnmountedRef.current = false
musicListRef.current?.loadList(songlistState.selectListInfo.source, songlistState.selectListInfo.id)
musicListRef.current?.loadList(info.source, info.id)
return () => {
@ -31,7 +32,9 @@ export default ({ componentId }: { componentId: string }) => {
return (
<PageContent>
<StatusBar />
<MusicList ref={musicListRef} componentId={componentId} />
<ListInfoContext.Provider value={info}>
<MusicList ref={musicListRef} componentId={componentId} />
</ListInfoContext.Provider>
<PlayerBar />
</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 })
},
setLoadErrorPicUrl(url: string) {
state.loadErrorPicUrl = url
},
}

View File

@ -24,6 +24,8 @@ export interface InitState {
playedList: LX.Player.PlayMusicInfo[]
tempPlayList: LX.Player.PlayMusicInfo[]
loadErrorPicUrl: string
progress: {
nowPlayTime: number
@ -63,6 +65,7 @@ const state: InitState = {
volume: 1,
playRate: 1,
statusText: '',
loadErrorPicUrl: '',
playedList: [],
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'
export default {
@ -58,13 +58,4 @@ export default {
state.listDetailInfo.info = {}
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[]>>
tags: Tags
listInfo: ListInfo
selectListInfo: ListInfoItem
listDetailInfo: ListDetailInfo
}
@ -99,17 +98,6 @@ const state: InitState = {
tagId: '',
sortId: '',
},
selectListInfo: {
play_count: '',
id: '',
author: '',
name: '',
time: '',
img: '',
// grade: basic.favorcnt / 10,
desc: '',
source: 'kw',
},
listDetailInfo: {
list: [],
id: '',