This commit is contained in:
lyswhut 2023-03-05 13:08:57 +08:00
parent 4bbf3f51f9
commit feeef4f6d2
69 changed files with 65 additions and 1228 deletions

View File

@ -16,7 +16,7 @@
"pack:android:debug": "./gradlew assembleDebug", "pack:android:debug": "./gradlew assembleDebug",
"pack": "npm run pack:android", "pack": "npm run pack:android",
"pack:android": "cd android && gradlew.bat assembleRelease", "pack:android": "cd android && gradlew.bat assembleRelease",
"clear": "react-native clean-project", "clear": "cd android && gradlew.bat clean",
"build:theme": "node src/theme/themes/createThemes.js", "build:theme": "node src/theme/themes/createThemes.js",
"publish": "node publish" "publish": "node publish"
}, },

View File

@ -1,6 +1,6 @@
import React, { useState, useRef, useEffect } from 'react' import React, { useState, useRef, useEffect } from 'react'
import { View } from 'react-native' import { View } from 'react-native'
import Input, { InputType } from '@/components/common/Input' import Input, { type InputType } from '@/components/common/Input'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'
import { useI18n } from '@/lang' import { useI18n } from '@/lang'
import { createUserList } from '@/core/list' import { createUserList } from '@/core/list'

View File

@ -1,5 +1,5 @@
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react' import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react'
import Dialog, { DialogType } from '@/components/common/Dialog' import Dialog, { type DialogType } from '@/components/common/Dialog'
import { toast } from '@/utils/tools' import { toast } from '@/utils/tools'
import Title from './Title' import Title from './Title'
import List from './List' import List from './List'

View File

@ -1,5 +1,5 @@
import React, { useRef, useImperativeHandle, forwardRef, useState } from 'react' import React, { useRef, useImperativeHandle, forwardRef, useState } from 'react'
import Modal, { MusicMultiAddModalType as ModalType, SelectInfo } from './MusicMultiAddModal' import Modal, { type MusicMultiAddModalType as ModalType, type SelectInfo } from './MusicMultiAddModal'
export interface MusicMultiAddModalType { export interface MusicMultiAddModalType {
show: (info: SelectInfo) => void show: (info: SelectInfo) => void

View File

@ -113,10 +113,10 @@ export default forwardRef<MultipleModeBarType, MultipleModeBarProps>(({ onSelect
return ( return (
<Animated.View style={animaStyle}> <Animated.View style={animaStyle}>
<View style={styles.switchBtn}> <View style={styles.switchBtn}>
<Button onPress={() => onSwitchMode('single')} style={{ ...styles.btn, backgroundColor: selectMode == 'single' ? theme['c-button-background'] : 'rgba(0,0,0,0)' }}> <Button onPress={() => { onSwitchMode('single') }} style={{ ...styles.btn, backgroundColor: selectMode == 'single' ? theme['c-button-background'] : 'rgba(0,0,0,0)' }}>
<Text color={theme['c-button-font']}>{global.i18n.t('list_select_single')}</Text> <Text color={theme['c-button-font']}>{global.i18n.t('list_select_single')}</Text>
</Button> </Button>
<Button onPress={() => onSwitchMode('range')} style={{ ...styles.btn, backgroundColor: selectMode == 'range' ? theme['c-button-background'] : 'rgba(0,0,0,0)' }}> <Button onPress={() => { onSwitchMode('range') }} style={{ ...styles.btn, backgroundColor: selectMode == 'range' ? theme['c-button-background'] : 'rgba(0,0,0,0)' }}>
<Text color={theme['c-button-font']}>{global.i18n.t('list_select_range')}</Text> <Text color={theme['c-button-font']}>{global.i18n.t('list_select_range')}</Text>
</Button> </Button>
</View> </View>

View File

@ -1,5 +1,5 @@
import React, { useState, forwardRef, useImperativeHandle, Ref } from 'react' import React, { useState, forwardRef, useImperativeHandle, type Ref } from 'react'
import { FlatList, FlatListProps } from 'react-native' import { FlatList, type FlatListProps } from 'react-native'
// import InsetShadow from 'react-native-inset-shadow' // import InsetShadow from 'react-native-inset-shadow'

View File

@ -1,8 +1,8 @@
import React, { useRef, useImperativeHandle, forwardRef, useState, useEffect } from 'react' import React, { useRef, useImperativeHandle, forwardRef, useState, useEffect } from 'react'
import ConfirmAlert, { ConfirmAlertType } from '@/components/common/ConfirmAlert' import ConfirmAlert, { type ConfirmAlertType } from '@/components/common/ConfirmAlert'
import Text from '@/components/common/Text' import Text from '@/components/common/Text'
import { View } from 'react-native' import { View } from 'react-native'
import Input, { InputType } from '@/components/common/Input' import Input, { type InputType } from '@/components/common/Input'
import { createStyle, toast } from '@/utils/tools' import { createStyle, toast } from '@/utils/tools'
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'
import { cancelTimeoutExit, getTimeoutExitTime, onTimeUpdate, startTimeoutExit, stopTimeoutExit, useTimeoutExitTimeInfo } from '@/core/player/timeoutExit' import { cancelTimeoutExit, getTimeoutExitTime, onTimeUpdate, startTimeoutExit, stopTimeoutExit, useTimeoutExitTimeInfo } from '@/core/player/timeoutExit'

View File

@ -1,6 +1,6 @@
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'
import React, { useMemo, useRef, useImperativeHandle, forwardRef } from 'react' import React, { useMemo, useRef, useImperativeHandle, forwardRef } from 'react'
import { Pressable, PressableProps, StyleSheet, View, ViewProps } from 'react-native' import { Pressable, type PressableProps, StyleSheet, type View, type ViewProps } from 'react-native'
// import { AppColors } from '@/theme' // import { AppColors } from '@/theme'

View File

@ -1,10 +1,10 @@
import React, { forwardRef, memo, useImperativeHandle, useRef, useState } from 'react' import React, { forwardRef, memo, useImperativeHandle, useRef, useState } from 'react'
import { View, TouchableOpacity } from 'react-native' import { View, TouchableOpacity } from 'react-native'
import Input, { InputType } from '@/components/common/Input' import Input, { type InputType } from '@/components/common/Input'
import Text from '@/components/common/Text' import Text from '@/components/common/Text'
import { Icon } from '@/components/common/Icon' import { Icon } from '@/components/common/Icon'
import StatusBar from '@/components/common/StatusBar' import StatusBar from '@/components/common/StatusBar'
import ConfirmAlert, { ConfirmAlertType } from '@/components/common/ConfirmAlert' import ConfirmAlert, { type ConfirmAlertType } from '@/components/common/ConfirmAlert'
import { createStyle, toast } from '@/utils/tools' import { createStyle, toast } from '@/utils/tools'
import { mkdir } from '@/utils/fs' import { mkdir } from '@/utils/fs'
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'

View File

@ -4,7 +4,7 @@ import { createStyle } from '@/utils/tools'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { View, FlatList } from 'react-native' import { View, FlatList } from 'react-native'
import ListItem, { PathItem } from './ListItem' import ListItem, { type PathItem } from './ListItem'
export default ({ list, onSetPath, toParentDir }: { export default ({ list, onSetPath, toParentDir }: {

View File

@ -2,9 +2,9 @@ import React, { useState, useRef, forwardRef, useImperativeHandle } from 'react'
// import { StyleSheet, View, Text, StatusBar, ScrollView } from 'react-native' // import { StyleSheet, View, Text, StatusBar, ScrollView } from 'react-native'
// import { useGetter, useDispatch } from '@/store' // import { useGetter, useDispatch } from '@/store'
import List, { ListType } from './List' import List, { type ListType } from './List'
import ConfirmAlert, { ConfirmAlertType } from '@/components/common/ConfirmAlert' import ConfirmAlert, { type ConfirmAlertType } from '@/components/common/ConfirmAlert'
import { checkStoragePermissions, requestStoragePermission, toast } from '@/utils/tools' import { checkStoragePermissions, requestStoragePermission, toast } from '@/utils/tools'
import { useI18n } from '@/lang' import { useI18n } from '@/lang'

View File

@ -2,7 +2,7 @@ import React, { useMemo, useRef, useImperativeHandle, forwardRef, useState } fro
import { View, TouchableWithoutFeedback } from 'react-native' import { View, TouchableWithoutFeedback } from 'react-native'
import { useDimensions } from '@/utils/hooks' import { useDimensions } from '@/utils/hooks'
import Modal, { ModalType } from '@/components/common/Modal' import Modal, { type ModalType } from '@/components/common/Modal'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'
// import { useGetter } from '@/store' // import { useGetter } from '@/store'

View File

@ -1,8 +1,8 @@
import React, { useRef, forwardRef } from 'react' import React, { useRef, forwardRef } from 'react'
// import { View } from 'react-native' // import { View } from 'react-native'
import Panel, { PanelType } from './Panel' import Panel, { type PanelType } from './Panel'
import Button, { BtnType } from '@/components/common/Button' import Button, { type BtnType } from '@/components/common/Button'
export interface DorpDownPanelProps { export interface DorpDownPanelProps {

View File

@ -1,7 +1,7 @@
import { createIconSetFromIcoMoon } from 'react-native-vector-icons' import { createIconSetFromIcoMoon } from 'react-native-vector-icons'
import icoMoonConfig from '@/resources/fonts/selection.json' import icoMoonConfig from '@/resources/fonts/selection.json'
import { scaleSizeW } from '@/utils/pixelRatio' import { scaleSizeW } from '@/utils/pixelRatio'
import { ComponentProps } from 'react' import { type ComponentProps } from 'react'
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'
// import IconAntDesign from 'react-native-vector-icons/AntDesign' // import IconAntDesign from 'react-native-vector-icons/AntDesign'

View File

@ -1,5 +1,5 @@
import React, { useRef, useImperativeHandle, forwardRef, useCallback } from 'react' import React, { useRef, useImperativeHandle, forwardRef, useCallback } from 'react'
import { TextInput, View, TouchableOpacity, StyleSheet, TextInputProps } from 'react-native' import { TextInput, View, TouchableOpacity, StyleSheet, type TextInputProps } from 'react-native'
import { Icon } from '@/components/common/Icon' import { Icon } from '@/components/common/Icon'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'

View File

@ -1,6 +1,6 @@
import React, { memo } from 'react' import React, { memo } from 'react'
import Slider, { SliderProps as _SliderProps } from '@react-native-community/slider' import Slider, { type SliderProps as _SliderProps } from '@react-native-community/slider'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'

View File

@ -1,5 +1,5 @@
import React, { memo, useMemo } from 'react' import React, { memo, useMemo } from 'react'
import { View, Pressable, GestureResponderEvent } from 'react-native' import { View, Pressable, type GestureResponderEvent } from 'react-native'
import { useLayout } from '@/utils/hooks' import { useLayout } from '@/utils/hooks'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'

View File

@ -143,7 +143,7 @@ export const getListDetailAll = async(id: string, isRefresh = false): Promise<LX
if (pageCache) return pageCache.data if (pageCache) return pageCache.data
return getListLimit(source, bangId, page) return getListLimit(source, bangId, page)
} }
return loadData(1).then(result => { return loadData(1).then(async result => {
if (result.total <= result.limit) return result.list if (result.total <= result.limit) return result.list
let maxPage = Math.ceil(result.total / result.limit) let maxPage = Math.ceil(result.total / result.limit)

View File

@ -355,7 +355,7 @@ export const getOnlineOtherSourceLyricInfo = async({ musicInfos, onToggleSource,
reqPromise = Promise.reject(err) reqPromise = Promise.reject(err)
} }
retryedSource.includes(musicInfo.source) retryedSource.includes(musicInfo.source)
return reqPromise.then((lyricInfo: LX.Music.LyricInfo) => { return reqPromise.then(async(lyricInfo: LX.Music.LyricInfo) => {
return existTimeExp.test(lyricInfo.lyric) ? { return existTimeExp.test(lyricInfo.lyric) ? {
lyricInfo, lyricInfo,
musicInfo, musicInfo,
@ -389,7 +389,7 @@ export const handleGetOnlineLyricInfo = async({ musicInfo, onToggleSource, isRef
} catch (err) { } catch (err) {
reqPromise = Promise.reject(err) reqPromise = Promise.reject(err)
} }
return reqPromise.then((lyricInfo: LX.Music.LyricInfo) => { return reqPromise.then(async(lyricInfo: LX.Music.LyricInfo) => {
return existTimeExp.test(lyricInfo.lyric) ? { return existTimeExp.test(lyricInfo.lyric) ? {
musicInfo, musicInfo,
lyricInfo, lyricInfo,

View File

@ -80,7 +80,7 @@ const getMusicPlayUrl = async(musicInfo: LX.Music.MusicInfo | LX.Download.ListIt
if (global.lx.isPlayedStop || diffCurrentMusicInfo(musicInfo)) return null if (global.lx.isPlayedStop || diffCurrentMusicInfo(musicInfo)) return null
return url return url
}).catch(err => { }).catch(async err => {
// console.log('err', err.message) // console.log('err', err.message)
if (global.lx.isPlayedStop || if (global.lx.isPlayedStop ||
diffCurrentMusicInfo(musicInfo) || diffCurrentMusicInfo(musicInfo) ||

View File

@ -216,7 +216,7 @@ export const getListDetailAll = async(source: LX.OnlineSource, id: string, isRef
if (pageCache) return pageCache.data if (pageCache) return pageCache.data
return getListDetailLimit(source, id, page) return getListDetailLimit(source, id, page)
} }
return loadData(1).then(result => { return loadData(1).then(async result => {
if (result.total <= result.limit) return result.list if (result.total <= result.limit) return result.list
let maxPage = Math.ceil(result.total / result.limit) let maxPage = Math.ceil(result.total / result.limit)

View File

@ -124,7 +124,7 @@ export const useLrcSet = () => {
setLines(lines) setLines(lines)
} }
lrcTools.addSetLyricHook(callback) lrcTools.addSetLyricHook(callback)
return () => lrcTools.removeSetLyricHook(callback) return () => { lrcTools.removeSetLyricHook(callback) }
}, []) }, [])
return lines return lines

View File

@ -76,7 +76,7 @@ const registerPlaybackService = async() => {
global.app_event.setProgress(position) global.app_event.setProgress(position)
}) })
TrackPlayer.addEventListener(TPEvent.PlaybackState, info => { TrackPlayer.addEventListener(TPEvent.PlaybackState, async info => {
if (global.lx.gettingUrlId || isTempId()) return if (global.lx.gettingUrlId || isTempId()) return
// let currentIsPlaying = false // let currentIsPlaying = false

View File

@ -61,7 +61,7 @@ const codeAuth = async(urlInfo: LX.Sync.UrlInfo, serverId: string, authCode: str
const keyAuth = async(urlInfo: LX.Sync.UrlInfo, keyInfo: LX.Sync.KeyInfo) => { const keyAuth = async(urlInfo: LX.Sync.UrlInfo, keyInfo: LX.Sync.KeyInfo) => {
const msg = aesEncrypt(SYNC_CODE.authMsg + await getDeviceName(), keyInfo.key) const msg = aesEncrypt(SYNC_CODE.authMsg + await getDeviceName(), keyInfo.key)
return request(`${urlInfo.httpProtocol}//${urlInfo.hostPath}/ah`, { headers: { i: keyInfo.clientId, m: msg } }).then(({ text, code }) => { return request(`${urlInfo.httpProtocol}//${urlInfo.hostPath}/ah`, { headers: { i: keyInfo.clientId, m: msg } }).then(async({ text, code }) => {
if (code != 200) throw new Error(SYNC_CODE.authFailed) if (code != 200) throw new Error(SYNC_CODE.authFailed)
let msg let msg

View File

@ -1,7 +1,7 @@
import React, { useEffect, useRef } from 'react' import React, { useEffect, useRef } from 'react'
import { filterList, getHotComment } from './utils' import { filterList, getHotComment } from './utils'
import music from '@/utils/musicSdk' import music from '@/utils/musicSdk'
import List, { ListType } from './components/List' import List, { type ListType } from './components/List'
const limit = 15 const limit = 15
export default ({ musicInfo, onUpdateTotal }: { export default ({ musicInfo, onUpdateTotal }: {

View File

@ -52,6 +52,7 @@ const CommentFloor = memo(({ comment, isLast }: {
<Text style={styles.likedCount} size={12} color={ theme['c-450'] }>{comment.likedCount}</Text> <Text style={styles.likedCount} size={12} color={ theme['c-450'] }>{comment.likedCount}</Text>
</View> </View>
) )
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
return ( return (

View File

@ -1,6 +1,6 @@
import React, { useMemo, useRef, useImperativeHandle, forwardRef, useState } from 'react' import React, { useMemo, useRef, useImperativeHandle, forwardRef, useState } from 'react'
import { useI18n } from '@/lang' import { useI18n } from '@/lang'
import Menu, { MenuType, Position } from '@/components/common/Menu' import Menu, { type MenuType, type Position } from '@/components/common/Menu'
export interface SelectInfo { export interface SelectInfo {
listId: string listId: string

View File

@ -18,7 +18,7 @@ export default forwardRef<MusicListType, {}>((props, ref) => {
const listRef = useRef<OnlineListType>(null) const listRef = useRef<OnlineListType>(null)
const isUnmountedRef = useRef(false) const isUnmountedRef = useRef(false)
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
loadList(source, id) { async loadList(source, id) {
const listDetailInfo = boardState.listDetailInfo const listDetailInfo = boardState.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) {

View File

@ -37,7 +37,7 @@ export default forwardRef<ActiveListType, ActiveListProps>(({ onShowSearchBar },
useEffect(() => { useEffect(() => {
void getListPrevSelectId().then((id) => { void getListPrevSelectId().then((id) => {
void setActiveList(id) setActiveList(id)
}) })
}, []) }, [])

View File

@ -1,6 +1,6 @@
import React, { useMemo, useRef, useImperativeHandle, forwardRef, useState } from 'react' import React, { useMemo, useRef, useImperativeHandle, forwardRef, useState } from 'react'
import { useI18n } from '@/lang' import { useI18n } from '@/lang'
import Menu, { MenuType, Position } from '@/components/common/Menu' import Menu, { type MenuType, type Position } from '@/components/common/Menu'
export interface SelectInfo { export interface SelectInfo {
musicInfo: LX.Music.MusicInfo musicInfo: LX.Music.MusicInfo

View File

@ -18,7 +18,7 @@ export default forwardRef<MusicListType, {}>((props, ref) => {
const searchInfoRef = useRef<{ text: string, source: Source }>({ text: '', source: 'kw' }) const searchInfoRef = useRef<{ text: string, source: Source }>({ text: '', source: 'kw' })
const isUnmountedRef = useRef(false) const isUnmountedRef = useRef(false)
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
loadList(text, source) { async loadList(text, source) {
// const listDetailInfo = searchMusicState.listDetailInfo // const listDetailInfo = searchMusicState.listDetailInfo
listRef.current?.setList([], source == 'all') listRef.current?.setList([], source == 'all')
if (searchMusicState.searchText == text && searchMusicState.source == source && searchMusicState.listInfos[searchMusicState.source]!.list.length) { if (searchMusicState.searchText == text && searchMusicState.source == source && searchMusicState.listInfos[searchMusicState.source]!.list.length) {

View File

@ -19,7 +19,7 @@ export default forwardRef<MusicListType, {}>((props, ref) => {
const searchInfoRef = useRef<{ text: string, source: Source }>({ text: '', source: 'kw' }) const searchInfoRef = useRef<{ text: string, source: Source }>({ text: '', source: 'kw' })
const isUnmountedRef = useRef(false) const isUnmountedRef = useRef(false)
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
loadList(text, source) { async loadList(text, source) {
// const listDetailInfo = searchSonglistState.listDetailInfo // const listDetailInfo = searchSonglistState.listDetailInfo
listRef.current?.setList([], source == 'all') listRef.current?.setList([], source == 'all')
if (searchSonglistState.searchText == text && searchSonglistState.source == source && searchSonglistState.listInfos[searchSonglistState.source]!.list.length) { if (searchSonglistState.searchText == text && searchSonglistState.source == source && searchSonglistState.listInfos[searchSonglistState.source]!.list.length) {

View File

@ -31,7 +31,7 @@ const Item = ({ position, label }: {
}) => { }) => {
const isActive = useActive(position) const isActive = useActive(position)
// const [toggleCheckBox, setToggleCheckBox] = useState(false) // const [toggleCheckBox, setToggleCheckBox] = useState(false)
return <CheckBox marginRight={8} check={isActive} label={label} onChange={() => updateSetting({ 'common.drawerLayoutPosition': position })} need /> return <CheckBox marginRight={8} check={isActive} label={label} onChange={() => { updateSetting({ 'common.drawerLayoutPosition': position }) }} need />
} }
export default memo(() => { export default memo(() => {

View File

@ -21,7 +21,7 @@ const Item = ({ id, name }: {
}) => { }) => {
const isActive = useActive(id) const isActive = useActive(id)
// const [toggleCheckBox, setToggleCheckBox] = useState(false) // const [toggleCheckBox, setToggleCheckBox] = useState(false)
return <CheckBox marginRight={8} check={isActive} label={name} onChange={() => setLanguage(id)} need /> return <CheckBox marginRight={8} check={isActive} label={name} onChange={() => { setLanguage(id) }} need />
} }
export default memo(() => { export default memo(() => {

View File

@ -27,7 +27,7 @@ const Item = ({ id, name }: {
}) => { }) => {
const isActive = useActive(id) const isActive = useActive(id)
// const [toggleCheckBox, setToggleCheckBox] = useState(false) // const [toggleCheckBox, setToggleCheckBox] = useState(false)
return <CheckBox marginBottom={3} check={isActive} label={name} onChange={() => setShareType(id)} need /> return <CheckBox marginBottom={3} check={isActive} label={name} onChange={() => { setShareType(id) }} need />
} }
export default memo(() => { export default memo(() => {

View File

@ -29,7 +29,7 @@ const Item = ({ id, name, change }: {
}) => { }) => {
const isActive = useActive(id) const isActive = useActive(id)
// const [toggleCheckBox, setToggleCheckBox] = useState(false) // const [toggleCheckBox, setToggleCheckBox] = useState(false)
return <CheckBox marginBottom={5} check={isActive} label={name} onChange={() => change(id)} need /> return <CheckBox marginBottom={5} check={isActive} label={name} onChange={() => { change(id) }} need />
} }
export default memo(() => { export default memo(() => {

View File

@ -27,7 +27,7 @@ const Item = ({ id, name }: {
}) => { }) => {
const isActive = useActive(id) const isActive = useActive(id)
// const [toggleCheckBox, setToggleCheckBox] = useState(false) // const [toggleCheckBox, setToggleCheckBox] = useState(false)
return <CheckBox marginBottom={3} check={isActive} label={name} onChange={() => setSourceNameType(id)} need /> return <CheckBox marginBottom={3} check={isActive} label={name} onChange={() => { setSourceNameType(id) }} need />
} }
export default memo(() => { export default memo(() => {

View File

@ -1,12 +1,12 @@
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react' import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { View, ImageBackground, TouchableOpacity, InteractionManager, ImageSourcePropType } from 'react-native' import { View, ImageBackground, TouchableOpacity, InteractionManager, type ImageSourcePropType } from 'react-native'
import { setTheme } from '@/core/theme' import { setTheme } from '@/core/theme'
import { useI18n } from '@/lang' import { useI18n } from '@/lang'
import { useSettingValue } from '@/store/setting/hook' import { useSettingValue } from '@/store/setting/hook'
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'
import SubTitle from '../components/SubTitle' import SubTitle from '../components/SubTitle'
import { BG_IMAGES, getAllThemes, LocalTheme } from '@/theme/themes' import { BG_IMAGES, getAllThemes, type LocalTheme } from '@/theme/themes'
import Text from '@/components/common/Text' import Text from '@/components/common/Text'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'
import { scaleSizeH } from '@/utils/pixelRatio' import { scaleSizeH } from '@/utils/pixelRatio'
@ -28,7 +28,7 @@ const ThemeItem = ({ id, name, color, image, setTheme }: {
const isActive = useActive(id) const isActive = useActive(id)
return ( return (
<TouchableOpacity style={{ ...styles.item, width: scaleSizeH(ITEM_HEIGHT) }} activeOpacity={0.5} onPress={() => setTheme(id)}> <TouchableOpacity style={{ ...styles.item, width: scaleSizeH(ITEM_HEIGHT) }} activeOpacity={0.5} onPress={() => { setTheme(id) }}>
<View style={{ ...styles.colorContent, width: scaleSizeH(COLOR_ITEM_HEIGHT), borderColor: isActive ? color : 'transparent' }}> <View style={{ ...styles.colorContent, width: scaleSizeH(COLOR_ITEM_HEIGHT), borderColor: isActive ? color : 'transparent' }}>
{ {
image image

View File

@ -24,7 +24,7 @@ const Item = ({ id, name }: {
}) => { }) => {
const isActive = useActive(id) const isActive = useActive(id)
// const [toggleCheckBox, setToggleCheckBox] = useState(false) // const [toggleCheckBox, setToggleCheckBox] = useState(false)
return <CheckBox marginRight={8} check={isActive} label={name} onChange={() => setAddMusicLocationType(id)} need /> return <CheckBox marginRight={8} check={isActive} label={name} onChange={() => { setAddMusicLocationType(id) }} need />
} }

View File

@ -1,7 +1,7 @@
import React, { memo, useMemo } from 'react' import React, { memo, useMemo } from 'react'
import { View } from 'react-native' import { View } from 'react-native'
import InputItem, { InputItemProps } from '../components/InputItem' import InputItem, { type InputItemProps } from '../components/InputItem'
import { createStyle, toast } from '@/utils/tools' import { createStyle, toast } from '@/utils/tools'
import { useSettingValue } from '@/store/setting/hook' import { useSettingValue } from '@/store/setting/hook'
import { useI18n } from '@/lang' import { useI18n } from '@/lang'

View File

@ -80,6 +80,7 @@ export default memo(({ host, setHost }: {
return () => { return () => {
isUnmountedRef.current = true isUnmountedRef.current = true
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
useEffect(() => { useEffect(() => {

View File

@ -1,6 +1,6 @@
import React, { memo } from 'react' import React, { memo } from 'react'
import Button, { BtnProps } from '@/components/common/Button' import Button, { type BtnProps } from '@/components/common/Button'
import Text from '@/components/common/Text' import Text from '@/components/common/Text'
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'

View File

@ -55,6 +55,7 @@ export default memo(({ value, label, onChanged, ...props }: InputItemProps) => {
setText(newValue) setText(newValue)
textRef.current = newValue textRef.current = newValue
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value]) }, [value])
const handleSetSelectMode = (text: string) => { const handleSetSelectMode = (text: string) => {
setText(text) setText(text)

View File

@ -13,7 +13,7 @@ export default forwardRef<ListType, {}>((props, ref) => {
const listRef = useRef<SonglistType>(null) const listRef = useRef<SonglistType>(null)
const isUnmountedRef = useRef(false) const isUnmountedRef = useRef(false)
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
loadList(source, sortId, tagId) { async loadList(source, sortId, tagId) {
const listInfo = songlistState.listInfo const listInfo = songlistState.listInfo
listRef.current?.setList([]) listRef.current?.setList([])
if (listInfo.tagId == tagId && listInfo.sortId == sortId && listInfo.source == source && listInfo.list.length) { if (listInfo.tagId == tagId && listInfo.sortId == sortId && listInfo.source == source && listInfo.list.length) {

View File

@ -1,5 +1,5 @@
import React, { useRef } from 'react' import React, { useRef } from 'react'
import MusicAddModal, { MusicAddModalType } from '@/components/MusicAddModal' import MusicAddModal, { type MusicAddModalType } from '@/components/MusicAddModal'
import playerState from '@/store/player/state' import playerState from '@/store/player/state'
import Btn from './Btn' import Btn from './Btn'

View File

@ -1,5 +1,5 @@
import React, { memo, useMemo } from 'react' import React, { memo, useMemo } from 'react'
import { View, Pressable, GestureResponderEvent } from 'react-native' import { View, Pressable, type GestureResponderEvent } from 'react-native'
import { useLayout } from '@/utils/hooks' import { useLayout } from '@/utils/hooks'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'

View File

@ -1,5 +1,5 @@
import React, { useRef } from 'react' import React, { useRef } from 'react'
import MusicAddModal, { MusicAddModalType } from '@/components/MusicAddModal' import MusicAddModal, { type MusicAddModalType } from '@/components/MusicAddModal'
import playerState from '@/store/player/state' import playerState from '@/store/player/state'
import Btn from './Btn' import Btn from './Btn'

View File

@ -1,5 +1,5 @@
import React, { memo, useRef } from 'react' import React, { memo, useRef } from 'react'
import TimeoutExitEditModal, { TimeoutExitEditModalType, useTimeInfo } from '@/components/TimeoutExitEditModal' import TimeoutExitEditModal, { type TimeoutExitEditModalType, useTimeInfo } from '@/components/TimeoutExitEditModal'
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'
import Btn from './Btn' import Btn from './Btn'

View File

@ -1,5 +1,5 @@
import React, { memo, useMemo } from 'react' import React, { memo, useMemo } from 'react'
import { View, Pressable, GestureResponderEvent } from 'react-native' import { View, Pressable, type GestureResponderEvent } from 'react-native'
import { useLayout } from '@/utils/hooks' import { useLayout } from '@/utils/hooks'
import { createStyle } from '@/utils/tools' import { createStyle } from '@/utils/tools'
import { useTheme } from '@/store/theme/hook' import { useTheme } from '@/store/theme/hook'

View File

@ -18,7 +18,7 @@ export default forwardRef<MusicListType, MusicListProps>(({ componentId }, ref)
const headerRef = useRef<HeaderType>(null) const headerRef = useRef<HeaderType>(null)
const isUnmountedRef = useRef(false) const isUnmountedRef = useRef(false)
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
loadList(source, id) { async loadList(source, id) {
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) {
@ -99,6 +99,7 @@ export default forwardRef<MusicListType, MusicListProps>(({ componentId }, ref)
}) })
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
const header = useMemo(() => <Header ref={headerRef} componentId={componentId} />, []) const header = useMemo(() => <Header ref={headerRef} componentId={componentId} />, [])
return <OnlineList return <OnlineList

View File

@ -24,6 +24,7 @@ export default ({ componentId }: { componentId: string }) => {
return () => { return () => {
isUnmountedRef.current = true isUnmountedRef.current = true
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])

View File

@ -46,6 +46,7 @@ export const usePageVisible = (visibleNames: COMPONENT_IDS[], onChange: (visible
return () => { return () => {
global.state_event.off('componentIdsUpdated', handlecheck) global.state_event.off('componentIdsUpdated', handlecheck)
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
} }
@ -62,6 +63,7 @@ export const useAssertApiSupport = (source: LX.Source) => {
return () => { return () => {
global.state_event.off('apiSourceUpdated', handleUpdate) global.state_event.off('apiSourceUpdated', handleUpdate)
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
return value return value

View File

@ -84,6 +84,7 @@ export const useListFetching = (listId: string) => {
return () => { return () => {
global.state_event.off('fetchingListStatusUpdated', handleUpdate) global.state_event.off('fetchingListStatusUpdated', handleUpdate)
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
return fetching return fetching

View File

@ -1,35 +0,0 @@
// import { createSlice } from '@reduxjs/toolkit'
import { NAV_MENUS } from '@/config/constant'
// import type { PayloadAction } from '@reduxjs/toolkit'
// import type { RootState } from '@/store'
// type MenuIds = (typeof NAV_MENUS)[number]['id']
// // Define a type for the slice state
// interface InitState {
// activeId: MenuIds
// }
// // Define the initial state using that type
// const initialState: InitState = {
// activeId: 'search',
// }
// // export const slice = createSlice({
// // name: 'common',
// // // `createSlice` will infer the state type from the `initialState` argument
// // initialState,
// // reducers: {
// // setNavActive(state, action: PayloadAction<MenuIds>) {
// // if (action.payload === state.activeId) return
// // state.activeId = action.payload
// // },
// // },
// // })
// export const { setNavActive } = slice.actions
// // Other code such as selectors can use the imported `RootState` type
// // export const selectCount = (state: RootState) => state.counter.value
// export default slice.reducer

View File

@ -1,5 +0,0 @@
import common from './common'
export default {
common,
}

View File

@ -1,19 +0,0 @@
import common from './common'
// import * as search from './search'
// import * as player from './player'
// import * as list from './list'
// import * as songList from './songList'
// import * as top from './top'
// export {
// common,
// search,
// player,
// list,
// songList,
// top,
// }
export default {
...common,
}

View File

@ -1,89 +0,0 @@
import music from '@/utils/musicSdk'
import { deduplicationList } from '@/utils/tools'
export const TYPES = {
loading: null,
setText: null,
addHistory: null,
setList: null,
setLists: null,
clearList: null,
removeHistory: null,
clearHistory: null,
setTipList: null,
setVisibleTipList: null,
}
for (const key of Object.keys(TYPES)) {
TYPES[key] = `search__${key}`
}
const sources = []
for (const source of music.sources) {
const musicSearch = music[source.id].musicSearch
if (!musicSearch) continue
sources.push(source)
}
export const search = ({ page, limit }) => (dispatch, getState) => {
// dispatch({ type: TYPES.setText, payload: text })
const state = getState()
const text = state.search.text
if (!text.length) {
dispatch({ type: TYPES.clearList })
return Promise.resolve()
}
dispatch({ type: TYPES.addHistory, payload: text })
if (state.common.setting.search.searchSource == 'all') {
const task = []
for (const source of sources) {
if (source.id == 'all') continue
dispatch({ type: TYPES.loading, payload: true })
task.push(music[source.id].musicSearch.search(text, page).catch(error => {
console.log(error)
return {
allPage: 1,
limit: 30,
list: [],
source: source.id,
total: 0,
}
}))
}
return Promise.all(task).then(results => dispatch({ type: TYPES.setLists, payload: { results, page } }))
.finally(() => dispatch({ type: TYPES.loading, payload: false }))
} else {
dispatch({ type: TYPES.loading, payload: true })
return music[state.common.setting.search.searchSource].musicSearch.search(text, page, limit).catch(error => {
console.log(error)
return {
allPage: 1,
limit: 30,
list: [],
source: state.common.setting.search.searchSource,
total: 0,
}
}).then(data => dispatch({ type: TYPES.setList, payload: { page, ...data, list: deduplicationList(data.list) } }))
.finally(() => dispatch({ type: TYPES.loading, payload: false }))
}
}
export const setText = text => ({ type: TYPES.setText, payload: text })
export const setTipList = list => ({ type: TYPES.setTipList, payload: list })
export const setVisibleTipList = visible => ({ type: TYPES.setVisibleTipList, payload: visible })
export const clearList = () => ({
type: TYPES.clearList,
})
export const addHistory = text => ({
type: TYPES.addHistory,
payload: text,
})
export const removeHistory = index => ({
type: TYPES.addHistory,
payload: index,
})
export const clearHistory = () => ({
type: TYPES.addHistory,
})

View File

@ -1,33 +0,0 @@
import { createSelector } from 'reselect'
export const text = state => state.search.text
export const sourceList = state => state.search.sourceList
export const aggregationListInfo = state => state.search.aggregationListInfo
export const isEnd = state => state.search.isEnd
export const isLoading = state => state.search.isLoading
export const tipList = state => state.search.tipInfo.list
export const tipListVisible = state => state.search.tipInfo.visible
export const rawSources = state => state.search.sources
export const tempSearchSource = state => state.common.setting.search.tempSearchSource
export const searchSource = state => state.common.setting.search.searchSource
export const sources = createSelector([rawSources], sources => {
return sources.map(source => ({ label: source.name, id: source.id }))
})
export const currentSourceName = createSelector([rawSources, searchSource], (sources, searchSource) => {
const source = sources.find(s => s.id == searchSource)
return source ? source.name : 'unknown'
})
export const listInfo = createSelector([searchSource, sourceList, aggregationListInfo], (searchSource, sourceList, aggregationListInfo) => {
return searchSource == 'all'
? aggregationListInfo
: sourceList[searchSource]
})

View File

@ -1,5 +0,0 @@
import * as action from './action'
import * as getter from './getter'
export { action, getter }
export { default as reducer } from './reducer'

View File

@ -1,248 +0,0 @@
import { TYPES } from './action'
import music from '@/utils/musicSdk'
let historyList
if (historyList == null) {
historyList = []
// electronStore_data.set('searchHistoryList', historyList)
}
const sources = []
const sourceList = {}
for (const source of music.sources) {
const musicSearch = music[source.id].musicSearch
if (!musicSearch) continue
sources.push(source)
sourceList[source.id] = {
page: 1,
maxPage: 0,
limit: 30,
total: 0,
list: [],
}
}
// sources.push({
// id: 'all',
// name: '聚合搜索',
// })
const initialState = {
text: '',
historyList,
sources,
isEnd: false,
isLoading: false,
sourceList,
aggregationListInfo: {
list: [],
page: 1,
limit: 30,
maxPage: 1,
total: 0,
},
tipInfo: {
list: [],
visible: false,
},
}
// https://blog.csdn.net/xcxy2015/article/details/77164126#comments
const similar = (a, b) => {
if (!a || !b) return 0
if (a.length > b.length) { // 保证 a <= b
const t = b
b = a
a = t
}
const al = a.length
const bl = b.length
const mp = [] // 一个表
let i, j, ai, lt, tmp // ai字符串a的第i个字符。 lt左上角的值。 tmp暂存新的值。
for (i = 0; i <= bl; i++) mp[i] = i
for (i = 1; i <= al; i++) {
ai = a.charAt(i - 1)
lt = mp[0]
mp[0] = mp[0] + 1
for (j = 1; j <= bl; j++) {
tmp = Math.min(mp[j] + 1, mp[j - 1] + 1, lt + (ai == b.charAt(j - 1) ? 0 : 1))
lt = mp[j]
mp[j] = tmp
}
}
return 1 - (mp[bl] / bl)
}
const sortInsert = (arr, data) => {
const key = data.num
let left = 0
let right = arr.length - 1
while (left <= right) {
const middle = parseInt((left + right) / 2)
if (key == arr[middle]) {
left = middle
break
} else if (key < arr[middle].num) {
right = middle - 1
} else {
left = middle + 1
}
}
while (left > 0) {
if (arr[left - 1].num != key) break
left--
}
arr.splice(left, 0, data)
}
const handleSortList = (list, keyword) => {
const arr = []
for (const item of list) {
sortInsert(arr, {
num: similar(keyword, `${item.name} ${item.singer}`),
data: item,
})
}
return arr.map(item => item.data).reverse()
}
const filterList = list => {
const set = new Set()
for (let i = list.length - 1; i > -1; i--) {
const item = list[i]
if (set.has(item.songmid)) {
list.splice(i, 1)
} else {
set.add(item.songmid)
}
}
return list
}
const mutations = {
[TYPES.loading](state, isLoading) {
return {
...state,
isLoading,
}
},
[TYPES.setText](state, text) {
return {
...state,
text,
}
},
[TYPES.addHistory](state, text) {
let historyList = [...state.historyList]
const index = historyList.indexOf(text)
if (index > -1) historyList.splice(index, 1)
if (historyList.length >= 15) historyList = historyList.slice(0, 14)
historyList.unshift(text)
return {
...state,
historyList,
}
},
[TYPES.setList](state, datas) {
const source = { ...state.sourceList[datas.source] }
source.list = datas.page > 1 ? filterList([...source.list, ...datas.list]) : datas.list
source.total = datas.total
source.maxPage = datas.allPage
source.page = datas.page
state.isEnd = datas.page >= source.maxPage
// source.limit = datas.limit
return {
...state,
sourceList: {
...state.sourceList,
[datas.source]: source,
},
}
},
[TYPES.setLists](state, { results, page }) {
const pages = []
let total = 0
// let limit = 0
let maxPage = 1
const list = []
state = { ...state }
const aggregationListInfo = { ...state.aggregationListInfo }
for (const source of results) {
if (source.allPage < page) continue
list.push(...source.list)
pages.push(source.allPage)
total += source.total
// limit = Math.max(source.limit, limit)
maxPage = Math.max(source.allPage, maxPage)
}
aggregationListInfo.maxPage = Math.max(...pages)
aggregationListInfo.total = total
// aggregationListInfo.limit = limit
aggregationListInfo.page = page
aggregationListInfo.maxPage = maxPage
aggregationListInfo.list = page > 1 ? filterList([...aggregationListInfo.list, ...handleSortList(list, state.text)]) : handleSortList(list, state.text)
aggregationListInfo.isEnd = aggregationListInfo.maxPage >= page
return state
},
[TYPES.clearList](state) {
state = { ...state }
state.sourceList = { ...state.sourceList }
for (const source of Object.keys(state.sourceList)) {
state.sourceList[source] = { ...state.sourceList[source] }
state.sourceList[source].list = []
state.sourceList[source].page = 0
state.sourceList[source].maxPage = 1
state.sourceList[source].total = 0
}
const aggregationListInfo = { ...state.aggregationListInfo }
aggregationListInfo.list = []
aggregationListInfo.page = 0
aggregationListInfo.maxPage = 1
aggregationListInfo.total = 0
state.text = ''
state.isEnd = false
return state
},
[TYPES.removeHistory](state, index) {
const historyList = [...state.historyList]
historyList.splice(index, 1)
return {
...state,
historyList,
}
},
[TYPES.clearHistory](state) {
return {
...state,
historyList: [],
}
},
[TYPES.setTipList](state, list) {
return {
...state,
tipInfo: {
...state.tipInfo,
list,
},
}
},
[TYPES.setVisibleTipList](state, visible) {
return {
...state,
tipInfo: {
...state.tipInfo,
visible,
},
}
},
}
export default (state = initialState, action) =>
mutations[action.type]
? mutations[action.type](state, action.payload)
: state

View File

@ -1,235 +0,0 @@
import music from '@/utils/musicSdk'
import { deduplicationList } from '@/utils/tools'
const cache = new Map()
const LIST_LOAD_LIMIT = 30
export const TYPES = {
setTags: null,
setList: null,
clearList: null,
setListDetail: null,
setVisibleListDetail: null,
setSelectListInfo: null,
setListLoading: null,
setListDetailLoading: null,
setListEnd: null,
setListDetailEnd: null,
setGetListDetailFailed: null,
clearListDetail: null,
}
for (const key of Object.keys(TYPES)) {
TYPES[key] = `list__${key}`
}
export const getTags = () => (dispatch, getState) => {
const state = getState()
let source = state.common.setting.songList.source
if (state.songList.tags[source]) return Promise.resolve()
return music[source].songList.getTags().then(result => dispatch(setTags({ tags: result, source })))
}
export const getList = ({ page = 1, isRefresh = false }) => (dispatch, getState) => {
const allState = getState()
const rootState = allState.common
let source = rootState.setting.songList.source
let tabId = rootState.setting.songList.tagInfo.id
let sortId = rootState.setting.songList.sortId
let listKey = `slist__${source}__${sortId}__${tabId}`
let pageKey = `slist__${source}__${sortId}__${tabId}__${page}`
if (isRefresh && cache.has(listKey)) cache.delete(listKey)
if (!cache.has(listKey)) cache.set(listKey, new Map())
const listCache = cache.get(listKey)
if (listCache.has(pageKey)) return Promise.resolve(listCache.get(pageKey)).then(result => dispatch(setList({ result, pageKey, listKey, page })))
dispatch(setListEnd(false))
dispatch(setListLoading(true))
return music[source]?.songList.getList(sortId, tabId, page).then(result => {
dispatch(setList({ result, pageKey, listKey, page }))
listCache.set(pageKey, result)
}).finally(() => {
const state = getState().songList
if (state.list.pageKey != pageKey) return
dispatch(setListLoading(false))
})
}
const getListDetailLimit = ({ source, id, page }) => {
const listKey = `sdetail__${source}__${id}`
const prevPageKey = `sdetail__${source}__${id}__${page - 1}`
const tempListKey = `sdetail__${source}__${id}__temp`
const listCache = cache.get(listKey)
let sourcePage = 0
if (listCache.has(prevPageKey)) {
sourcePage = listCache.get(prevPageKey).sourcePage
}
return music[source]?.songList.getListDetail(id, sourcePage + 1).then(result => {
let p = page
if (listCache.has(tempListKey)) {
const list = listCache.get(tempListKey)
listCache.delete(tempListKey)
listCache.set(`sdetail__${source}__${id}__${p}`, {
data: {
...result,
list: [...list, ...result.list.splice(0, LIST_LOAD_LIMIT - list.length)],
page: p,
limit: LIST_LOAD_LIMIT,
},
sourcePage,
})
p++
}
sourcePage++
do {
if (result.list.length < LIST_LOAD_LIMIT && sourcePage < Math.ceil(result.total / result.limit)) {
listCache.set(tempListKey, result.list.splice(0, LIST_LOAD_LIMIT))
break
}
listCache.set(`sdetail__${source}__${id}__${p}`, {
data: {
...result,
list: result.list.splice(0, LIST_LOAD_LIMIT),
page: p,
limit: LIST_LOAD_LIMIT,
},
sourcePage,
})
p++
} while (result.list.length > 0)
return listCache.get(`sdetail__${source}__${id}__${page}`).data
})
}
export const getListDetail = ({ id, page, isRefresh = false }) => (dispatch, getState) => {
const allState = getState()
const rootState = allState.common
let source = rootState.setting.songList.source
let listKey = `sdetail__${source}__${id}`
let pageKey = `sdetail__${source}__${id}__${page}`
if (isRefresh && cache.has(listKey)) cache.delete(listKey)
if (!cache.has(listKey)) cache.set(listKey, new Map())
dispatch(setGetListDetailFailed(false))
const listCache = cache.get(listKey)
if (listCache.has(pageKey)) {
return Promise.resolve(listCache.get(pageKey).data).then(result => dispatch(setListDetail({ result, listKey, pageKey, source, id, page })))
}
dispatch(setListDetailEnd(false))
dispatch(setListDetailLoading(true))
return getListDetailLimit({ source, id, page }).then(result => {
dispatch(setListDetail({ result, listKey, pageKey, source, id, page }))
// listCache.set(pageKey, result)
}).catch(err => {
console.log(err)
if (page == 1) {
dispatch(setGetListDetailFailed(true))
}
return Promise.reject(err)
}).finally(() => {
const state = getState().songList
if (state.listDetail.pageKey != pageKey) return
dispatch(setListDetailLoading(false))
})
}
export const getListDetailAll = ({ source, id, isRefresh = false }) => (dispatch, getState) => {
let listKey = `sdetail__${source}__${id}`
if (isRefresh && cache.has(listKey)) cache.delete(listKey)
if (!cache.has(listKey)) cache.set(listKey, new Map())
const listCache = cache.get(listKey)
const loadData = (id, page) => {
let pageKey = `sdetail__${source}__${id}__${page}`
return listCache.has(pageKey)
? Promise.resolve(listCache.get(pageKey).data)
: getListDetailLimit({ source, id, page }).then(result => {
// listCache.set(pageKey, result)
return result
})
}
return loadData(id, 1).then(result => {
if (result.total <= result.limit) return result.list
let maxPage = Math.ceil(result.total / result.limit)
const loadDetail = (loadPage = 2) => {
return loadPage == maxPage
? loadData(id, loadPage).then(result => result.list)
: loadData(id, loadPage).then(result1 => loadDetail(++loadPage).then(result2 => [...result1.list, ...result2]))
}
return loadDetail().then(result2 => [...result.list, ...result2])
}).then(list => deduplicationList(list))
}
export const setVisibleListDetail = isShow => {
return {
type: TYPES.setVisibleListDetail,
payload: isShow,
}
}
export const setSelectListInfo = info => (dispatch, getState) => {
dispatch({
type: TYPES.setSelectListInfo,
payload: info,
})
dispatch({
type: TYPES.clearListDetail,
})
}
export const setGetListDetailFailed = isFailed => {
return {
type: TYPES.setGetListDetailFailed,
payload: isFailed,
}
}
export const setTags = ({ tags, source }) => {
return {
type: TYPES.setTags,
payload: { tags, source },
}
}
export const setList = ({ result, pageKey, listKey, page }) => {
return {
type: TYPES.setList,
payload: { result, pageKey, listKey, page },
}
}
export const clearList = () => {
return { type: TYPES.clearList }
}
export const setListLoading = isLoading => {
return {
type: TYPES.setListLoading,
payload: isLoading,
}
}
export const setListDetailLoading = isLoading => {
return {
type: TYPES.setListDetailLoading,
payload: isLoading,
}
}
export const setListEnd = isEnd => {
return {
type: TYPES.setListEnd,
payload: isEnd,
}
}
export const setListDetailEnd = isEnd => {
return {
type: TYPES.setListDetailEnd,
payload: isEnd,
}
}
export const setListDetail = ({ result, pageKey, listKey, source, id, page }) => {
return {
type: TYPES.setListDetail,
payload: { result, pageKey, listKey, source, id, page },
}
}

View File

@ -1,32 +0,0 @@
import { createSelector } from 'reselect'
// sourceInfo(state, getters, rootState, { sourceNames }) {
// return { sources: sources.map(item => ({ id: item.id, name: sourceNames[item.id] })), sortList }
// },
// tags: state => state.tags,
// isVisibleListDetail: state => state.isVisibleListDetail,
export const rawSources = state => state.songList.sources
export const sortList = state => state.songList.sortList
export const tags = state => state.songList.tags
export const isVisibleListDetail = state => state.songList.isVisibleListDetail
export const isGetListDetailFailed = state => state.songList.isGetListDetailFailed
export const selectListInfo = state => state.songList.selectListInfo
export const listData = state => state.songList.list
export const listDetailData = state => state.songList.listDetail
export const listInfo = state => state.songList.list
export const listDetailInfo = state => state.songList.listDetail
export const songListSource = state => state.common.setting.songList.source
export const songListSortId = state => state.common.setting.songList.sortId
export const songListTagInfo = state => state.common.setting.songList.tagInfo
export const sources = createSelector([rawSources], sources => {
return sources.map(source => ({ label: source.name, id: source.id }))
})

View File

@ -1,5 +0,0 @@
import * as action from './action'
import * as getter from './getter'
export { action, getter }
export { default as reducer } from './reducer'

View File

@ -1,191 +0,0 @@
import { TYPES } from './action'
import music from '@/utils/musicSdk'
import { deduplicationList } from '@/utils/tools'
const sortList = {}
const sources = []
for (const source of music.sources) {
const songList = music[source.id].songList
if (!songList) continue
sortList[source.id] = songList.sortList
sources.push(source)
}
// state
const initialState = {
sources,
sortList,
tags: {},
list: {
list: [],
total: 0,
page: 1,
limit: 30,
listKey: null,
pageKey: null,
isLoading: false,
isEnd: false,
},
listDetail: {
list: [],
desc: null,
total: 0,
page: 1,
limit: 30,
info: {},
listKey: null,
pageKey: null,
isLoading: false,
isEnd: false,
},
selectListInfo: {},
isVisibleListDetail: false,
isGetListDetailFailed: false,
}
sources.forEach(source => {
initialState.tags[source.id] = null
})
const mutations = {
[TYPES.setTags](state, { tags, source }) {
return {
...state,
tags: { ...state.tags, [source]: tags },
}
},
[TYPES.clearList](state) {
return {
...state,
list: {
...state.list,
list: [],
total: 0,
page: 1,
pageKey: null,
listKey: null,
isLoading: false,
isEnd: false,
},
}
},
[TYPES.setList](state, { result, pageKey, listKey, page }) {
if (pageKey == state.list.pageKey && state.list.list.length) return state
return {
...state,
list: {
...state.list,
list: listKey == state.list.listKey && page != 1 ? [...state.list.list, ...result.list] : result.list,
total: result.total,
limit: result.limit,
page,
pageKey,
listKey,
isEnd: page >= Math.ceil(result.total / result.limit),
},
}
},
[TYPES.setListDetail](state, { result, pageKey, listKey, source, id, page }) {
return {
...state,
listDetail: {
...state.listDetail,
list: deduplicationList(listKey == state.listDetail.listKey && page != 1 ? [...state.listDetail.list, ...result.list] : result.list),
id,
source,
total: result.total,
limit: result.limit,
page,
pageKey,
listKey,
isEnd: page >= Math.ceil(result.total / result.limit),
info: result.info || {
name: state.selectListInfo.name,
img: state.selectListInfo.img,
desc: state.selectListInfo.desc,
author: state.selectListInfo.author,
play_count: state.selectListInfo.play_count,
},
},
}
},
[TYPES.setVisibleListDetail](state, bool) {
const newState = {
...state,
isVisibleListDetail: bool,
}
if (!bool) newState.listDetail = { ...newState.listDetail, list: [] }
return newState
},
[TYPES.setSelectListInfo](state, info) {
return {
...state,
selectListInfo: info,
}
},
[TYPES.clearListDetail](state) {
return {
...state,
listDetail: {
...state.listDetail,
id: null,
source: null,
list: [],
desc: null,
total: 0,
page: 1,
limit: 30,
pageKey: null,
listKey: null,
isLoading: false,
isEnd: false,
info: {},
},
}
},
[TYPES.setGetListDetailFailed](state, isFailed) {
return {
...state,
isGetListDetailFailed: isFailed,
}
},
[TYPES.setListLoading](state, isLoading) {
return {
...state,
list: {
...state.list,
isLoading,
},
}
},
[TYPES.setListDetailLoading](state, isLoading) {
return {
...state,
listDetail: {
...state.listDetail,
isLoading,
},
}
},
[TYPES.setListEnd](state, isEnd) {
return {
...state,
list: {
...state.list,
isEnd,
},
}
},
[TYPES.setListDetailEnd](state, isEnd) {
return {
...state,
listDetail: {
...state.listDetail,
isEnd,
},
}
},
}
export default (state = initialState, action) => mutations[action.type]
? mutations[action.type](state, action.payload)
: state

View File

@ -1,161 +0,0 @@
import music from '@/utils/musicSdk'
import { deduplicationList } from '@/utils/tools'
const cache = new Map()
const LIST_LOAD_LIMIT = 30
export const TYPES = {
setBoardsList: null,
setList: null,
clearList: null,
setListLoading: null,
setListEnd: null,
}
for (const key of Object.keys(TYPES)) {
TYPES[key] = `top__${key}`
}
export const getBoardsList = () => (dispatch, getState) => {
const state = getState()
let source = state.common.setting.leaderboard.source
// let tabId = rootState.setting.leaderboard.tabId
// let key = `${source}${tabId}${page}`
// if (state.list.length && state.key == key) return true
// commit('clearList')
if (state.top.boards[source].length) return Promise.resolve()
return music[source].leaderboard.getBoards().then(result => dispatch(setBoardsList({ boards: result, source })))
}
const getListLimit = ({ source, tabId, bangId, page }) => {
const listKey = `${source}__${tabId}`
const prevPageKey = `${source}__${tabId}__${page - 1}`
const tempListKey = `${source}__${tabId}__temp`
const listCache = cache.get(listKey)
let sourcePage = 0
if (listCache.has(prevPageKey)) {
sourcePage = listCache.get(prevPageKey).sourcePage
}
return music[source].leaderboard.getList(bangId, sourcePage + 1).then(result => {
let p = page
if (listCache.has(tempListKey)) {
const list = listCache.get(tempListKey)
listCache.delete(tempListKey)
listCache.set(`${source}__${tabId}__${p}`, {
data: {
...result,
list: [...list, ...result.list.splice(0, LIST_LOAD_LIMIT - list.length)],
page: p,
limit: LIST_LOAD_LIMIT,
},
sourcePage,
})
p++
}
sourcePage++
do {
if (result.list.length < LIST_LOAD_LIMIT && sourcePage < Math.ceil(result.total / result.limit)) {
listCache.set(tempListKey, result.list.splice(0, LIST_LOAD_LIMIT))
break
}
listCache.set(`${source}__${tabId}__${p}`, {
data: {
...result,
list: result.list.splice(0, LIST_LOAD_LIMIT),
page: p,
limit: LIST_LOAD_LIMIT,
},
sourcePage,
})
p++
} while (result.list.length > 0)
return listCache.get(`${source}__${tabId}__${page}`).data
})
}
export const getList = ({ page, isRefresh = false }) => (dispatch, getState) => {
const state = getState()
let tabId = state.common.setting.leaderboard.tabId
if (tabId == null) return Promise.resolve()
// console.log(tabId)
const [source, bangId] = tabId.split('__')
const listKey = `${source}__${tabId}`
const pageKey = `${source}__${tabId}__${page}`
if (isRefresh && cache.has(listKey)) cache.delete(listKey)
if (!cache.has(listKey)) cache.set(listKey, new Map())
const listCache = cache.get(listKey)
if (listCache.has(pageKey)) {
return Promise.resolve(listCache.get(pageKey).data).then(result => dispatch(setList({ result, listKey, pageKey, page })))
}
dispatch(setListEnd(false))
dispatch(setListLoading(true))
return getListLimit({ source, tabId, bangId, page }).then(result => {
dispatch(setList({ result, listKey, pageKey, page }))
// listCache.set(pageKey, result)
}).finally(() => {
const state = getState().top
if (state.listInfo.pageKey != pageKey) return
dispatch(setListLoading(false))
})
}
export const getListAll = ({ id: tabId, isRefresh = false }) => (dispatch, getState) => {
// console.log(tabId)
const [source, bangId] = tabId.split('__')
const listKey = `${source}__${tabId}`
if (isRefresh && cache.has(listKey)) cache.delete(listKey)
if (!cache.has(listKey)) cache.set(listKey, new Map())
const listCache = cache.get(listKey)
const loadData = (bangId, page) => {
const pageKey = `${source}__${tabId}__${page}`
return listCache.has(pageKey)
? Promise.resolve(listCache.get(pageKey).data)
: getListLimit({ source, tabId, bangId, page }).then(result => {
// listCache.set(pageKey, result)
return result
})
}
return loadData(bangId, 1).then(result => {
if (result.total <= result.limit) return result.list
let maxPage = Math.ceil(result.total / result.limit)
const loadDetail = (loadPage = 2) => {
return loadPage == maxPage
? loadData(bangId, loadPage).then(result => result.list)
: loadData(bangId, loadPage).then(result1 => loadDetail(++loadPage).then(result2 => [...result1.list, ...result2]))
}
return loadDetail().then(result2 => [...result.list, ...result2])
}).then(list => deduplicationList(list))
}
export const setBoardsList = ({ boards, source }) => {
return {
type: TYPES.setBoardsList,
payload: { boards, source },
}
}
export const setList = ({ result, pageKey, listKey, page }) => {
return {
type: TYPES.setList,
payload: { result, pageKey, listKey, page },
}
}
export const clearList = () => {
return { type: TYPES.clearList }
}
export const setListLoading = isLoading => {
return {
type: TYPES.setListLoading,
payload: isLoading,
}
}
export const setListEnd = isEnd => {
return {
type: TYPES.setListEnd,
payload: isEnd,
}
}

View File

@ -1,24 +0,0 @@
import { createSelector } from 'reselect'
// sourceInfo(state, getters, rootState, { sourceNames }) {
// return { sources: sources.map(item => ({ id: item.id, name: sourceNames[item.id] })), sortList }
// },
// tags: state => state.tags,
// isVisibleListDetail: state => state.isVisibleListDetail,
export const rawSources = state => state.top.sources
export const boards = state => state.top.boards
export const listInfo = state => state.top.listInfo
export const isEnd = state => state.top.isEnd
export const isLoading = state => state.top.isLoading
export const sourceId = state => state.common.setting.leaderboard.source
export const tabId = state => state.common.setting.leaderboard.tabId
export const sources = createSelector([rawSources], sources => {
return sources.map(source => ({ label: source.name, id: source.id }))
})

View File

@ -1,5 +0,0 @@
import * as action from './action'
import * as getter from './getter'
export { action, getter }
export { default as reducer } from './reducer'

View File

@ -1,84 +0,0 @@
import { TYPES } from './action'
import music from '@/utils/musicSdk'
import { deduplicationList } from '@/utils/tools'
const sourceList = {}
const sources = []
for (const source of music.sources) {
const leaderboard = music[source.id].leaderboard
if (!leaderboard || !leaderboard.getBoards) continue
sourceList[source.id] = []
sources.push(source)
}
// state
const initialState = {
sources,
boards: sourceList,
listInfo: {
list: [],
total: 0,
page: 1,
limit: 30,
listKey: null,
pageKey: null,
},
isLoading: false,
isEnd: false,
}
const mutations = {
[TYPES.setBoardsList](state, { boards, source }) {
return {
...state,
boards: { ...state.boards, [source]: boards.list },
}
},
[TYPES.clearList](state) {
return {
...state,
listInfo: {
...state.listInfo,
list: [],
total: 0,
page: 1,
pageKey: null,
listKey: null,
},
isLoading: false,
isEnd: false,
}
},
[TYPES.setList](state, { result, pageKey, listKey, page }) {
return {
...state,
listInfo: {
...state.listInfo,
list: deduplicationList(listKey == state.listInfo.listKey && page != 1 ? [...state.listInfo.list, ...result.list] : result.list),
total: result.total,
limit: result.limit,
page,
pageKey,
listKey,
},
isEnd: page >= Math.ceil(result.total / result.limit),
}
},
[TYPES.setListLoading](state, isLoading) {
return {
...state,
isLoading,
}
},
[TYPES.setListEnd](state, isEnd) {
return {
...state,
isEnd,
}
},
}
export default (state = initialState, action) => mutations[action.type]
? mutations[action.type](state, action.payload)
: state

View File

@ -5,7 +5,7 @@ import settingState from '@/store/setting/state'
import themeState from '@/store/theme/state' import themeState from '@/store/theme/state'
import { isUrl } from '@/utils' import { isUrl } from '@/utils'
import { externalDirectoryPath } from '@/utils/fs' import { externalDirectoryPath } from '@/utils/fs'
import { ImageSourcePropType } from 'react-native' import { type ImageSourcePropType } from 'react-native'
export const BG_IMAGES = { export const BG_IMAGES = {
'china_ink.jpg': require('./images/china_ink.jpg') as ImageSourcePropType, 'china_ink.jpg': require('./images/china_ink.jpg') as ImageSourcePropType,