Skip to content

Commit

Permalink
新增重复歌曲列表,可以方便移除我的列表中的重复歌曲,此列表会列出目标列表里歌曲名相同的歌曲,可在“我的列表”里的列表名菜单中使用
Browse files Browse the repository at this point in the history
  • Loading branch information
lyswhut committed Jul 12, 2024
1 parent a081b64 commit 163daa1
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 2 deletions.
4 changes: 2 additions & 2 deletions publish/changeLog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
我们发布了关于 LX Music 项目发展调整与新项目计划的说明,
详情看: https://github.com/lyswhut/lx-music-desktop/issues/1912

### 修复
### 新增

- 修复数据存储管理在移除数据时可能出现移除失败的问题
- 新增重复歌曲列表,可以方便移除我的列表中的重复歌曲,此列表会列出目标列表里歌曲名相同的歌曲,可在“我的列表”里的列表名菜单中使用
1 change: 1 addition & 0 deletions src/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
"list_update_error": "{name} failed to update",
"list_update_success": "{name} updated successfully",
"list_updating": "updating",
"lists__duplicate": "repeat song",
"lists_dislike_music_add_tip": "Added",
"lists_dislike_music_tip": "Do you really dislike {name}?",
"load_failed": "Ah, loading failed 😥",
Expand Down
1 change: 1 addition & 0 deletions src/lang/zh_cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
"list_update_error": "{name} 更新失败",
"list_update_success": "{name} 更新成功",
"list_updating": "更新中",
"lists__duplicate": "重复歌曲",
"lists_dislike_music_add_tip": "已添加",
"lists_dislike_music_tip": "你真的不喜欢 {name} 吗?",
"load_failed": "啊 加载失败了 😥",
Expand Down
257 changes: 257 additions & 0 deletions src/screens/Home/Views/Mylist/MyList/DuplicateMusic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
import { useRef, useImperativeHandle, forwardRef, useState, useCallback, memo, useEffect } from 'react'
import Text from '@/components/common/Text'
import { createStyle } from '@/utils/tools'
import Dialog, { type DialogType } from '@/components/common/Dialog'
import { FlatList, View, type FlatListProps as _FlatListProps } from 'react-native'
import { scaleSizeH } from '@/utils/pixelRatio'
import { useTheme } from '@/store/theme/hook'
import { type DuplicateMusicItem, filterDuplicateMusic } from './utils'
import { getListMusics, removeListMusics } from '@/core/list'
import Button from '@/components/common/Button'
import { Icon } from '@/components/common/Icon'
import { useUnmounted } from '@/utils/hooks'
import { playList } from '@/core/player/player'
import { useI18n } from '@/lang'

type FlatListProps = _FlatListProps<DuplicateMusicItem>
const ITEM_HEIGHT = scaleSizeH(56)

const Title = ({ title }: {
title: string
}) => {
return (
<Text style={styles.title} size={16}>
{title}
</Text>
)
}

const Empty = () => {
const theme = useTheme()
const t = useI18n()

return (
<View style={styles.noitem}>
<Text color={theme['c-font-label']}>{t('no_item')}</Text>
</View>
)
}

const ListItem = memo(({ info, index, onRemove, onPlay }: {
info: DuplicateMusicItem
index: number
onPlay: (info: DuplicateMusicItem) => void
onRemove: (idx: number) => void
}) => {
const theme = useTheme()

return (
<View style={{ ...styles.listItem, height: ITEM_HEIGHT }} onStartShouldSetResponder={() => true}>
<View style={styles.listItemLabel}>
<Text style={styles.sn} size={13} color={theme['c-300']}>{info.index + 1}</Text>
</View>
<View style={styles.listItemInfo}>
<Text color={theme['c-font']} size={14} numberOfLines={1}>{info.musicInfo.name}</Text>
<View style={styles.listItemAlbum}>
<Text color={theme['c-font']} size={12} numberOfLines={1}>
{info.musicInfo.singer}
{
info.musicInfo.meta.albumName ? (
<Text color={theme['c-font-label']} size={12} numberOfLines={1}> ({info.musicInfo.meta.albumName})</Text>
) : null
}
</Text>
</View>
</View>
<View style={styles.listItemLabel}>
<Text style={styles.sn} size={13} color={theme['c-300']}>{ info.musicInfo.source }</Text>
</View>
<View style={styles.listItemLabel}>
<Text style={styles.sn} size={13} color={theme['c-300']}>{info.musicInfo.interval}</Text>
</View>
<View style={styles.listItemBtns}>
<Button style={styles.listItemBtn} onPress={() => { onPlay(info) }}>
<Icon name="play-outline" style={{ color: theme['c-button-font'] }} size={18} />
</Button>
<Button style={styles.listItemBtn} onPress={() => { onRemove(index) }}>
<Icon name="remove" style={{ color: theme['c-button-font'] }} size={18} />
</Button>
</View>
</View>
)
})

const List = ({ listId }: { listId: string }) => {
const [list, setList] = useState<DuplicateMusicItem[]>([])
const isUnmountedRef = useUnmounted()

const handleFilterList = useCallback(() => {
if (isUnmountedRef.current) return
void getListMusics(listId).then((list) => {
if (isUnmountedRef.current) return
void filterDuplicateMusic(list).then((l) => {
if (isUnmountedRef.current) return
setList(l)
})
})
}, [isUnmountedRef, listId])
const handlePlay = useCallback((info: DuplicateMusicItem) => {
const { index: musicInfoIndex } = info
void playList(listId, musicInfoIndex)
}, [listId])
const handleRemove = useCallback((index: number) => {
setList(list => {
const { musicInfo: targetMusicInfo } = list.splice(index, 1)[0]
void removeListMusics(listId, [targetMusicInfo.id]).then(() => {
handleFilterList()
})
return [...list]
})
}, [handleFilterList, listId])

useEffect(handleFilterList, [handleFilterList])

const renderItem = useCallback(({ item, index }: { item: DuplicateMusicItem, index: number }) => {
return <ListItem info={item} index={index} onPlay={handlePlay} onRemove={handleRemove} />
}, [handlePlay, handleRemove])
const getkey = useCallback<NonNullable<FlatListProps['keyExtractor']>>(item => item.id, [])
const getItemLayout = useCallback<NonNullable<FlatListProps['getItemLayout']>>((data, index) => {
return { length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index }
}, [])

return (
list.length ? (
<FlatList
style={styles.list}
removeClippedSubviews={true}
keyboardShouldPersistTaps={'always'}
data={list}
renderItem={renderItem}
keyExtractor={getkey}
getItemLayout={getItemLayout}
/>
) : (
<Empty />
)
)
}

export interface ModalType {
show: (info: LX.List.MyListInfo) => void
}
const initInfo = {}

const Modal = forwardRef<ModalType, {}>((props, ref) => {
const [info, setInfo] = useState<LX.List.MyListInfo>(initInfo as LX.List.MyListInfo)
const dialogRef = useRef<DialogType>(null)
useImperativeHandle(ref, () => ({
show(info) {
setInfo(info)

requestAnimationFrame(() => {
dialogRef.current?.setVisible(true)
})
},
}))
const handleHide = () => {
requestAnimationFrame(() => {
const ninfo = { ...info, name: '' }
setInfo(ninfo as LX.List.MyListInfo)
})
}

return (
<Dialog ref={dialogRef} onHide={handleHide}>
{
info.name
? (<>
<Title title={info.name} />
<List listId={info.id} />
</>)
: null
}
</Dialog>
)
})

export interface DuplicateMusicType {
show: (info: LX.List.MyListInfo) => void
}

export default forwardRef<DuplicateMusicType, {}>((props, ref) => {
const musicAddModalRef = useRef<ModalType>(null)
const [visible, setVisible] = useState(false)

useImperativeHandle(ref, () => ({
show(listInfo) {
if (visible) musicAddModalRef.current?.show(listInfo)
else {
setVisible(true)
requestAnimationFrame(() => {
musicAddModalRef.current?.show(listInfo)
})
}
},
}))

return (
visible
? <Modal ref={musicAddModalRef} />
: null
)
})


const styles = createStyle({
container: {
// flexGrow: 1,
},
title: {
textAlign: 'center',
paddingVertical: 15,
// backgroundColor: 'rgba(0,0,0,0.2)',
},
list: {
flexGrow: 0,
},
listItem: {
flexDirection: 'row',
flexWrap: 'nowrap',
alignItems: 'center',
},
sn: {
width: 38,
// fontSize: 12,
textAlign: 'center',
// backgroundColor: 'rgba(0,0,0,0.2)',
paddingLeft: 3,
paddingRight: 3,
},
listItemInfo: {
flexGrow: 1,
flexShrink: 1,
// backgroundColor: 'rgba(0,0,0,0.2)',
},
listItemAlbum: {
flexDirection: 'row',
marginTop: 3,
},
listItemLabel: {
flex: 0,
},
listItemBtns: {
flex: 0,
flexDirection: 'row',
gap: 5,
paddingHorizontal: 8,
},
listItemBtn: {
padding: 5,
},
noitem: {
paddingVertical: 35,
alignItems: 'center',
},
})


6 changes: 6 additions & 0 deletions src/screens/Home/Views/Mylist/MyList/ListMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface ListMenuProps {
onNew: (position: number) => void
onRename: (listInfo: LX.List.UserListInfo) => void
onSort: (listInfo: LX.List.MyListInfo) => void
onDuplicateMusic: (listInfo: LX.List.MyListInfo) => void
onImport: (listInfo: LX.List.MyListInfo, index: number) => void
onExport: (listInfo: LX.List.MyListInfo, index: number) => void
onSync: (listInfo: LX.List.UserListInfo) => void
Expand All @@ -40,6 +41,7 @@ export default forwardRef<ListMenuType, ListMenuProps>(({
onNew,
onRename,
onSort,
onDuplicateMusic,
onImport,
onExport,
onSync,
Expand Down Expand Up @@ -88,6 +90,7 @@ export default forwardRef<ListMenuType, ListMenuProps>(({
{ action: 'new', label: t('list_create') },
{ action: 'rename', disabled: !rename, label: t('list_rename') },
{ action: 'sort', label: t('list_sort') },
{ action: 'duplicateMusic', label: t('lists__duplicate') },
{ action: 'local_file', disabled: !local_file, label: t('list_select_local_file') },
{ action: 'sync', disabled: !sync || !local_file, label: t('list_sync') },
{ action: 'import', label: t('list_import') },
Expand All @@ -109,6 +112,9 @@ export default forwardRef<ListMenuType, ListMenuProps>(({
case 'sort':
onSort(selectInfo.listInfo)
break
case 'duplicateMusic':
onDuplicateMusic(selectInfo.listInfo)
break
case 'import':
onImport(selectInfo.listInfo, selectInfo.index)
break
Expand Down
4 changes: 4 additions & 0 deletions src/screens/Home/Views/Mylist/MyList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import List from './List'
import ListImportExport, { type ListImportExportType } from './ListImportExport'
import { handleRemove, handleSync } from './listAction'
import ListMusicSort, { type ListMusicSortType } from './ListMusicSort'
import DuplicateMusic, { type DuplicateMusicType } from './DuplicateMusic'


export default () => {
const [visible, setVisible] = useState(false)
const listMenuRef = useRef<ListMenuType>(null)
const listNameEditRef = useRef<ListNameEditType>(null)
const listMusicSortRef = useRef<ListMusicSortType>(null)
const duplicateMusicRef = useRef<DuplicateMusicType>(null)
const listImportExportRef = useRef<ListImportExportType>(null)

useEffect(() => {
Expand All @@ -38,12 +40,14 @@ export default () => {
<List onShowMenu={(info, position) => listMenuRef.current?.show(info, position)} />
<ListNameEdit ref={listNameEditRef} />
<ListMusicSort ref={listMusicSortRef} />
<DuplicateMusic ref={duplicateMusicRef} />
<ListImportExport ref={listImportExportRef} />
<ListMenu
ref={listMenuRef}
onNew={index => listNameEditRef.current?.showCreate(index)}
onRename={info => listNameEditRef.current?.show(info)}
onSort={info => listMusicSortRef.current?.show(info)}
onDuplicateMusic={info => duplicateMusicRef.current?.show(info)}
onImport={(info, position) => listImportExportRef.current?.import(info, position)}
onExport={(info, position) => listImportExportRef.current?.export(info, position)}
onRemove={info => { handleRemove(info) }}
Expand Down
Loading

0 comments on commit 163daa1

Please sign in to comment.