歌曲添加、移动弹窗新增创建新列表功能

This commit is contained in:
lyswhut 2021-05-26 16:00:35 +08:00
parent dc26be3651
commit 1899859db2
11 changed files with 242 additions and 37 deletions

View File

@ -1,10 +1,4 @@
### 修复 ### 新增
- 尝试修复软件启动时恢复上一次播放的歌曲可能导致软件崩溃的问题 - 新增我的列表中已收藏的在线列表的更新功能。注意这将会覆盖本地的目标列表歌曲将被替换成最新的在线列表与PC端的同步一样
- 尝试修复播放详情页歌词导致UI冻结的问题 - 歌曲添加、移动弹窗新增创建新列表功能
- 修复企鹅音乐搜索歌曲没有结果的问题
### 其他
- 整合日志记录
- 更新 exoPlayer 到 2.14.0

View File

@ -1,11 +1,12 @@
import React, { useCallback, useMemo, memo } from 'react' import React, { useCallback, useMemo, memo, useState, useRef, useEffect } from 'react'
import { View, StyleSheet, Text, ScrollView } from 'react-native' import { View, StyleSheet, Text, ScrollView, TouchableOpacity } from 'react-native'
import Dialog from '@/components/common/Dialog' import Dialog from '@/components/common/Dialog'
import Button from '@/components/common/Button' import Button from '@/components/common/Button'
import { useGetter, useDispatch } from '@/store' import { useGetter, useDispatch } from '@/store'
import { useTranslation } from '@/plugins/i18n' import { useTranslation } from '@/plugins/i18n'
import { useDimensions } from '@/utils/hooks' import { useDimensions } from '@/utils/hooks'
import { BorderWidths } from '@/theme'
import Input from '@/components/common/Input'
const ListItem = ({ list, onPress, musicInfo, width }) => { const ListItem = ({ list, onPress, musicInfo, width }) => {
const theme = useGetter('common', 'theme') const theme = useGetter('common', 'theme')
@ -17,7 +18,7 @@ const ListItem = ({ list, onPress, musicInfo, width }) => {
<View style={{ ...styles.listItem, width: width }}> <View style={{ ...styles.listItem, width: width }}>
<Button <Button
disabled={isDisabled} disabled={isDisabled}
style={{ ...styles.button, backgroundColor: theme.secondary45, opacity: isDisabled ? 0.6 : 1 }} style={{ ...styles.button, backgroundColor: theme.secondary45, borderColor: theme.secondary45, opacity: isDisabled ? 0.6 : 1 }}
onPress={() => { onPress(list) }} onPress={() => { onPress(list) }}
> >
<Text numberOfLines={1} style={{ fontSize: 12, color: isDisabled ? theme.secondary10 : theme.secondary }}>{list.name}</Text> <Text numberOfLines={1} style={{ fontSize: 12, color: isDisabled ? theme.secondary10 : theme.secondary }}>{list.name}</Text>
@ -36,11 +37,54 @@ const Title = ({ musicInfo, isMove }) => {
) )
} }
const CreateUserList = ({ isEdit, hideEdit }) => {
const [text, setText] = useState('')
const inputRef = useRef()
const { t } = useTranslation()
const theme = useGetter('common', 'theme')
const createUserList = useDispatch('list', 'createUserList')
useEffect(() => {
if (isEdit) {
setText('')
global.requestAnimationFrame(() => {
inputRef.current.focus()
})
}
}, [isEdit])
const handleSubmitEditing = () => {
hideEdit()
const name = text.trim()
if (!name.length) return
createUserList({ name })
}
return isEdit
? (
<View style={styles.imputContainer}>
<Input
placeholder={t('list_create_input_placeholder')}
value={text}
onChangeText={setText}
ref={inputRef}
onBlur={handleSubmitEditing}
onSubmitEditing={handleSubmitEditing}
style={{ ...styles.input, backgroundColor: theme.secondary45 }}
/>
</View>
)
: null
}
export default memo(({ visible, hideModal, musicInfo, listId, isMove = false }) => { export default memo(({ visible, hideModal, musicInfo, listId, isMove = false }) => {
const allList = useGetter('list', 'allList') const allList = useGetter('list', 'allList')
const addMusicToList = useDispatch('list', 'listAdd') const addMusicToList = useDispatch('list', 'listAdd')
const moveMusicToList = useDispatch('list', 'listMove') const moveMusicToList = useDispatch('list', 'listMove')
const { window } = useDimensions() const { window } = useDimensions()
const theme = useGetter('common', 'theme')
const [isEdit, setIsEdit] = useState(false)
const { t } = useTranslation()
const itemWidth = useMemo(() => { const itemWidth = useMemo(() => {
let w = window.width * 0.9 - 20 let w = window.width * 0.9 - 20
@ -67,13 +111,42 @@ export default memo(({ visible, hideModal, musicInfo, listId, isMove = false })
hideModal() hideModal()
}, [addMusicToList, hideModal, isMove, listId, moveMusicToList, musicInfo]) }, [addMusicToList, hideModal, isMove, listId, moveMusicToList, musicInfo])
const hideEdit = useCallback(() => {
setIsEdit(false)
}, [])
useEffect(() => {
if (!visible) {
hideEdit()
}
}, [hideEdit, visible])
return ( return (
<Dialog visible={visible} hideDialog={hideModal}> <Dialog visible={visible} hideDialog={hideModal}>
<Title musicInfo={musicInfo} isMove={isMove} /> <Title musicInfo={musicInfo} isMove={isMove} />
<View style={{ flexShrink: 1 }} onStartShouldSetResponder={() => true}> <View style={{ flexShrink: 1 }} onStartShouldSetResponder={() => true}>
<ScrollView style={{ flexGrow: 0 }} keyboardShouldPersistTaps={'always'}> <ScrollView style={{ flexGrow: 0 }}>
<View style={{ ...styles.list }}> <View style={{ ...styles.list }}>
{ allList.map(list => <ListItem key={list.id} list={list} musicInfo={musicInfo} onPress={handleSelect} width={itemWidth} />) } { allList.map(list => <ListItem key={list.id} list={list} musicInfo={musicInfo} onPress={handleSelect} width={itemWidth} />) }
<View style={{ ...styles.listItem, width: itemWidth }}>
<TouchableOpacity
style={{ ...styles.button, borderColor: theme.secondary20, borderStyle: 'dashed' }}
onPress={() => setIsEdit(true)}>
<Text numberOfLines={1} style={{ fontSize: 12, color: theme.secondary }}>{t('list_create')}</Text>
</TouchableOpacity>
{
isEdit
? (
<View style={styles.imputContainer}>
<CreateUserList
isEdit={isEdit}
hideEdit={hideEdit}
/>
</View>
)
: null
}
</View>
</View> </View>
</ScrollView> </ScrollView>
</View> </View>
@ -99,8 +172,8 @@ const styles = StyleSheet.create({
paddingRight: 10, paddingRight: 10,
}, },
button: { button: {
paddingTop: 9, paddingTop: 8,
paddingBottom: 9, paddingBottom: 8,
paddingLeft: 10, paddingLeft: 10,
paddingRight: 10, paddingRight: 10,
marginRight: 10, marginRight: 10,
@ -108,5 +181,28 @@ const styles = StyleSheet.create({
borderRadius: 4, borderRadius: 4,
width: '100%', width: '100%',
alignItems: 'center', alignItems: 'center',
borderWidth: BorderWidths.normal2,
},
imputContainer: {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
// paddingRight: 10,
// paddingBottom: 10,
// backgroundColor: 'rgba(0,0,0,0.2)',
},
input: {
fontSize: 12,
paddingTop: 8,
paddingBottom: 8,
paddingLeft: 10,
paddingRight: 10,
marginRight: 10,
marginBottom: 10,
borderRadius: 4,
width: '100%',
textAlign: 'center',
}, },
}) })

View File

@ -1,10 +1,12 @@
import React, { useCallback, useMemo, memo } from 'react' import React, { useCallback, useMemo, memo, useState, useRef, useEffect } from 'react'
import { View, StyleSheet, Text, ScrollView } from 'react-native' import { View, StyleSheet, Text, ScrollView, TouchableOpacity } from 'react-native'
import Dialog from '@/components/common/Dialog' import Dialog from '@/components/common/Dialog'
import Button from '@/components/common/Button' import Button from '@/components/common/Button'
import { useGetter, useDispatch } from '@/store' import { useGetter, useDispatch } from '@/store'
import { useTranslation } from '@/plugins/i18n' import { useTranslation } from '@/plugins/i18n'
import { useDimensions } from '@/utils/hooks' import { useDimensions } from '@/utils/hooks'
import { BorderWidths } from '@/theme'
import Input from '@/components/common/Input'
const ListItem = ({ list, onPress, width }) => { const ListItem = ({ list, onPress, width }) => {
@ -13,7 +15,7 @@ const ListItem = ({ list, onPress, width }) => {
return ( return (
<View style={{ ...styles.listItem, width: width }}> <View style={{ ...styles.listItem, width: width }}>
<Button <Button
style={{ ...styles.button, backgroundColor: theme.secondary45 }} style={{ ...styles.button, backgroundColor: theme.secondary45, borderColor: theme.secondary45 }}
onPress={() => { onPress(list) }} onPress={() => { onPress(list) }}
> >
<Text numberOfLines={1} style={{ fontSize: 12, color: theme.secondary }}>{list.name}</Text> <Text numberOfLines={1} style={{ fontSize: 12, color: theme.secondary }}>{list.name}</Text>
@ -32,11 +34,54 @@ const Title = ({ list, isMove }) => {
) )
} }
const CreateUserList = ({ isEdit, hideEdit }) => {
const [text, setText] = useState('')
const inputRef = useRef()
const { t } = useTranslation()
const theme = useGetter('common', 'theme')
const createUserList = useDispatch('list', 'createUserList')
useEffect(() => {
if (isEdit) {
setText('')
global.requestAnimationFrame(() => {
inputRef.current.focus()
})
}
}, [isEdit])
const handleSubmitEditing = () => {
hideEdit()
const name = text.trim()
if (!name.length) return
createUserList({ name })
}
return isEdit
? (
<View style={styles.imputContainer}>
<Input
placeholder={t('list_create_input_placeholder')}
value={text}
onChangeText={setText}
ref={inputRef}
onBlur={handleSubmitEditing}
onSubmitEditing={handleSubmitEditing}
style={{ ...styles.input, backgroundColor: theme.secondary45 }}
/>
</View>
)
: null
}
export default memo(({ visible, hideModal, list, onAdd, excludeList = [], listId, isMove = false }) => { export default memo(({ visible, hideModal, list, onAdd, excludeList = [], listId, isMove = false }) => {
const allList = useGetter('list', 'allList') const allList = useGetter('list', 'allList')
const addMultiMusicToList = useDispatch('list', 'listAddMultiple') const addMultiMusicToList = useDispatch('list', 'listAddMultiple')
const moveMultiMusicToList = useDispatch('list', 'listMoveMultiple') const moveMultiMusicToList = useDispatch('list', 'listMoveMultiple')
const { window } = useDimensions() const { window } = useDimensions()
const theme = useGetter('common', 'theme')
const [isEdit, setIsEdit] = useState(false)
const { t } = useTranslation()
const itemWidth = useMemo(() => { const itemWidth = useMemo(() => {
let w = window.width * 0.9 - 20 let w = window.width * 0.9 - 20
@ -68,13 +113,42 @@ export default memo(({ visible, hideModal, list, onAdd, excludeList = [], listId
return allList.filter(({ id }) => !excludeList.includes(id)) return allList.filter(({ id }) => !excludeList.includes(id))
}, [allList, excludeList]) }, [allList, excludeList])
const hideEdit = useCallback(() => {
setIsEdit(false)
}, [])
useEffect(() => {
if (!visible) {
hideEdit()
}
}, [hideEdit, visible])
return ( return (
<Dialog visible={visible} hideDialog={hideModal}> <Dialog visible={visible} hideDialog={hideModal}>
<Title list={list} isMove={isMove} /> <Title list={list} isMove={isMove} />
<View style={{ flexShrink: 1 }} onStartShouldSetResponder={() => true}> <View style={{ flexShrink: 1 }} onStartShouldSetResponder={() => true}>
<ScrollView style={{ flexGrow: 0 }} keyboardShouldPersistTaps={'always'}> <ScrollView style={{ flexGrow: 0 }}>
<View style={{ ...styles.list }}> <View style={{ ...styles.list }}>
{ filteredList.map(list => <ListItem key={list.id} list={list} onPress={handleSelect} width={itemWidth} />) } { filteredList.map(list => <ListItem key={list.id} list={list} onPress={handleSelect} width={itemWidth} />) }
<View style={{ ...styles.listItem, width: itemWidth }}>
<TouchableOpacity
style={{ ...styles.button, borderColor: theme.secondary20, borderStyle: 'dashed' }}
onPress={() => setIsEdit(true)}>
<Text numberOfLines={1} style={{ fontSize: 12, color: theme.secondary }}>{t('list_create')}</Text>
</TouchableOpacity>
{
isEdit
? (
<View style={styles.imputContainer}>
<CreateUserList
isEdit={isEdit}
hideEdit={hideEdit}
/>
</View>
)
: null
}
</View>
</View> </View>
</ScrollView> </ScrollView>
</View> </View>
@ -100,8 +174,8 @@ const styles = StyleSheet.create({
paddingRight: 10, paddingRight: 10,
}, },
button: { button: {
paddingTop: 9, paddingTop: 8,
paddingBottom: 9, paddingBottom: 8,
paddingLeft: 10, paddingLeft: 10,
paddingRight: 10, paddingRight: 10,
marginRight: 10, marginRight: 10,
@ -109,5 +183,28 @@ const styles = StyleSheet.create({
borderRadius: 4, borderRadius: 4,
width: '100%', width: '100%',
alignItems: 'center', alignItems: 'center',
borderWidth: BorderWidths.normal2,
},
imputContainer: {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
// paddingRight: 10,
// paddingBottom: 10,
// backgroundColor: 'rgba(0,0,0,0.2)',
},
input: {
fontSize: 12,
paddingTop: 8,
paddingBottom: 8,
paddingLeft: 10,
paddingRight: 10,
marginRight: 10,
marginBottom: 10,
borderRadius: 4,
width: '100%',
textAlign: 'center',
}, },
}) })

View File

@ -175,7 +175,7 @@ export default memo(({
// case 'copyName': // case 'copyName':
// break // break
case 'add': case 'add':
console.log(selectedListRef.current.length) // console.log(selectedListRef.current.length)
selectedListRef.current.length selectedListRef.current.length
? setVisibleMusicMultiAddModal(true) ? setVisibleMusicMultiAddModal(true)
: setVisibleMusicAddModal(true) : setVisibleMusicAddModal(true)

View File

@ -81,13 +81,13 @@ const Menu = ({
}, [screenSize, buttonPosition, menus, width, height]) }, [screenSize, buttonPosition, menus, width, height])
const menuPress = useCallback((menu, index) => { const menuPress = useCallback((menu, index) => {
if (menu.disabled) return // if (menu.disabled) return
onPress(menu, index) onPress(menu, index)
hideMenu() hideMenu()
}, [onPress, hideMenu]) }, [onPress, hideMenu])
const menuLongPress = useCallback((menu, index) => { const menuLongPress = useCallback((menu, index) => {
if (menu.disabled) return // if (menu.disabled) return
longPress(menu, index) longPress(menu, index)
// hideMenu() // hideMenu()
}, [longPress]) }, [longPress])
@ -98,6 +98,17 @@ const Menu = ({
<Animated.ScrollView keyboardShouldPersistTaps={'always'}> <Animated.ScrollView keyboardShouldPersistTaps={'always'}>
{ {
menus.map((menu, index) => ( menus.map((menu, index) => (
menu.disabled
? (
<View
key={menu.action}
style={{ ...styles.menuItem, width: width, height: height, opacity: 0.4 }}
underlayColor={theme.secondary40}
>
<Text style={{ ...styles.menuText, textAlign: center ? 'center' : 'left', color: theme.normal }} numberOfLines={1}>{menu.label}</Text>
</View>
)
: (
<TouchableHighlight <TouchableHighlight
key={menu.action} key={menu.action}
style={{ ...styles.menuItem, width: width, height: height }} style={{ ...styles.menuItem, width: width, height: height }}
@ -107,6 +118,8 @@ const Menu = ({
> >
<Text style={{ ...styles.menuText, textAlign: center ? 'center' : 'left', color: theme.normal }} numberOfLines={1}>{menu.label}</Text> <Text style={{ ...styles.menuText, textAlign: center ? 'center' : 'left', color: theme.normal }} numberOfLines={1}>{menu.label}</Text>
</TouchableHighlight> </TouchableHighlight>
)
)) ))
} }
</Animated.ScrollView> </Animated.ScrollView>

View File

@ -27,6 +27,8 @@
"change_position_music_title": "Adjust the position of {{name}} to", "change_position_music_title": "Adjust the position of {{name}} to",
"change_position_music_multi_title": "Adjust the position of the selected {{num}} song to", "change_position_music_multi_title": "Adjust the position of the selected {{num}} song to",
"list_remove": "Remove", "list_remove": "Remove",
"list_create": "Create a new list",
"list_create_input_placeholder": "What name do you think of...",
"play_detail_todo_tip": "What do you want? No, this function has not been implemented yet 😛, But you can try to locate the currently playing song by long pressing (only valid for playing songs in \"My List\")", "play_detail_todo_tip": "What do you want? No, this function has not been implemented yet 😛, But you can try to locate the currently playing song by long pressing (only valid for playing songs in \"My List\")",

View File

@ -27,6 +27,8 @@
"change_position_music_title": "将 {{name}} 的位置调整到", "change_position_music_title": "将 {{name}} 的位置调整到",
"change_position_music_multi_title": "将已选的 {{num}} 首歌曲的位置调整到", "change_position_music_multi_title": "将已选的 {{num}} 首歌曲的位置调整到",
"list_remove": "移除", "list_remove": "移除",
"list_create": "新建列表",
"list_create_input_placeholder": "你想起啥名...",
"play_detail_todo_tip": "你想干嘛?不可以的,这个功能还没有实现哦😛,不过你可以试着长按来定位当前播放的歌曲(仅对播放“我的列表”里的歌曲有效哦)", "play_detail_todo_tip": "你想干嘛?不可以的,这个功能还没有实现哦😛,不过你可以试着长按来定位当前播放的歌曲(仅对播放“我的列表”里的歌曲有效哦)",

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -126,8 +126,9 @@ const List = memo(({ setVisiblePanel, currentList, activeListIdRef, handleCancel
}, [handleRemoveList, handleSyncSourceList]) }, [handleRemoveList, handleSyncSourceList])
const menus = useMemo(() => { const menus = useMemo(() => {
if (selectedListIndex == -1) return [] const list = userList[selectedListIndex]
const source = userList[selectedListIndex].source if (!list) return []
const source = list.source
return [ return [
{ action: 'rename', label: t('list_rename') }, { action: 'rename', label: t('list_rename') },