mirror of
https://github.com/ikun0014/lx-music-mobile.git
synced 2025-05-23 22:37:41 +08:00
歌曲添加、移动弹窗新增创建新列表功能
This commit is contained in:
parent
dc26be3651
commit
1899859db2
Binary file not shown.
@ -1,10 +1,4 @@
|
||||
### 修复
|
||||
### 新增
|
||||
|
||||
- 尝试修复软件启动时恢复上一次播放的歌曲可能导致软件崩溃的问题
|
||||
- 尝试修复播放详情页歌词导致UI冻结的问题
|
||||
- 修复企鹅音乐搜索歌曲没有结果的问题
|
||||
|
||||
### 其他
|
||||
|
||||
- 整合日志记录
|
||||
- 更新 exoPlayer 到 2.14.0
|
||||
- 新增我的列表中已收藏的在线列表的更新功能。注意:这将会覆盖本地的目标列表,歌曲将被替换成最新的在线列表(与PC端的同步一样)
|
||||
- 歌曲添加、移动弹窗新增创建新列表功能
|
||||
|
@ -1,11 +1,12 @@
|
||||
import React, { useCallback, useMemo, memo } from 'react'
|
||||
import { View, StyleSheet, Text, ScrollView } from 'react-native'
|
||||
import React, { useCallback, useMemo, memo, useState, useRef, useEffect } from 'react'
|
||||
import { View, StyleSheet, Text, ScrollView, TouchableOpacity } from 'react-native'
|
||||
import Dialog from '@/components/common/Dialog'
|
||||
import Button from '@/components/common/Button'
|
||||
import { useGetter, useDispatch } from '@/store'
|
||||
import { useTranslation } from '@/plugins/i18n'
|
||||
import { useDimensions } from '@/utils/hooks'
|
||||
|
||||
import { BorderWidths } from '@/theme'
|
||||
import Input from '@/components/common/Input'
|
||||
|
||||
const ListItem = ({ list, onPress, musicInfo, width }) => {
|
||||
const theme = useGetter('common', 'theme')
|
||||
@ -17,7 +18,7 @@ const ListItem = ({ list, onPress, musicInfo, width }) => {
|
||||
<View style={{ ...styles.listItem, width: width }}>
|
||||
<Button
|
||||
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) }}
|
||||
>
|
||||
<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 }) => {
|
||||
const allList = useGetter('list', 'allList')
|
||||
const addMusicToList = useDispatch('list', 'listAdd')
|
||||
const moveMusicToList = useDispatch('list', 'listMove')
|
||||
const { window } = useDimensions()
|
||||
const theme = useGetter('common', 'theme')
|
||||
const [isEdit, setIsEdit] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const itemWidth = useMemo(() => {
|
||||
let w = window.width * 0.9 - 20
|
||||
@ -67,13 +111,42 @@ export default memo(({ visible, hideModal, musicInfo, listId, isMove = false })
|
||||
hideModal()
|
||||
}, [addMusicToList, hideModal, isMove, listId, moveMusicToList, musicInfo])
|
||||
|
||||
const hideEdit = useCallback(() => {
|
||||
setIsEdit(false)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!visible) {
|
||||
hideEdit()
|
||||
}
|
||||
}, [hideEdit, visible])
|
||||
|
||||
return (
|
||||
<Dialog visible={visible} hideDialog={hideModal}>
|
||||
<Title musicInfo={musicInfo} isMove={isMove} />
|
||||
<View style={{ flexShrink: 1 }} onStartShouldSetResponder={() => true}>
|
||||
<ScrollView style={{ flexGrow: 0 }} keyboardShouldPersistTaps={'always'}>
|
||||
<ScrollView style={{ flexGrow: 0 }}>
|
||||
<View style={{ ...styles.list }}>
|
||||
{ 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>
|
||||
</ScrollView>
|
||||
</View>
|
||||
@ -99,8 +172,8 @@ const styles = StyleSheet.create({
|
||||
paddingRight: 10,
|
||||
},
|
||||
button: {
|
||||
paddingTop: 9,
|
||||
paddingBottom: 9,
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
marginRight: 10,
|
||||
@ -108,5 +181,28 @@ const styles = StyleSheet.create({
|
||||
borderRadius: 4,
|
||||
width: '100%',
|
||||
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',
|
||||
},
|
||||
})
|
||||
|
@ -1,10 +1,12 @@
|
||||
import React, { useCallback, useMemo, memo } from 'react'
|
||||
import { View, StyleSheet, Text, ScrollView } from 'react-native'
|
||||
import React, { useCallback, useMemo, memo, useState, useRef, useEffect } from 'react'
|
||||
import { View, StyleSheet, Text, ScrollView, TouchableOpacity } from 'react-native'
|
||||
import Dialog from '@/components/common/Dialog'
|
||||
import Button from '@/components/common/Button'
|
||||
import { useGetter, useDispatch } from '@/store'
|
||||
import { useTranslation } from '@/plugins/i18n'
|
||||
import { useDimensions } from '@/utils/hooks'
|
||||
import { BorderWidths } from '@/theme'
|
||||
import Input from '@/components/common/Input'
|
||||
|
||||
|
||||
const ListItem = ({ list, onPress, width }) => {
|
||||
@ -13,7 +15,7 @@ const ListItem = ({ list, onPress, width }) => {
|
||||
return (
|
||||
<View style={{ ...styles.listItem, width: width }}>
|
||||
<Button
|
||||
style={{ ...styles.button, backgroundColor: theme.secondary45 }}
|
||||
style={{ ...styles.button, backgroundColor: theme.secondary45, borderColor: theme.secondary45 }}
|
||||
onPress={() => { onPress(list) }}
|
||||
>
|
||||
<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 }) => {
|
||||
const allList = useGetter('list', 'allList')
|
||||
const addMultiMusicToList = useDispatch('list', 'listAddMultiple')
|
||||
const moveMultiMusicToList = useDispatch('list', 'listMoveMultiple')
|
||||
const { window } = useDimensions()
|
||||
const theme = useGetter('common', 'theme')
|
||||
const [isEdit, setIsEdit] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const itemWidth = useMemo(() => {
|
||||
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))
|
||||
}, [allList, excludeList])
|
||||
|
||||
const hideEdit = useCallback(() => {
|
||||
setIsEdit(false)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!visible) {
|
||||
hideEdit()
|
||||
}
|
||||
}, [hideEdit, visible])
|
||||
|
||||
return (
|
||||
<Dialog visible={visible} hideDialog={hideModal}>
|
||||
<Title list={list} isMove={isMove} />
|
||||
<View style={{ flexShrink: 1 }} onStartShouldSetResponder={() => true}>
|
||||
<ScrollView style={{ flexGrow: 0 }} keyboardShouldPersistTaps={'always'}>
|
||||
<ScrollView style={{ flexGrow: 0 }}>
|
||||
<View style={{ ...styles.list }}>
|
||||
{ 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>
|
||||
</ScrollView>
|
||||
</View>
|
||||
@ -100,8 +174,8 @@ const styles = StyleSheet.create({
|
||||
paddingRight: 10,
|
||||
},
|
||||
button: {
|
||||
paddingTop: 9,
|
||||
paddingBottom: 9,
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
marginRight: 10,
|
||||
@ -109,5 +183,28 @@ const styles = StyleSheet.create({
|
||||
borderRadius: 4,
|
||||
width: '100%',
|
||||
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',
|
||||
},
|
||||
})
|
||||
|
@ -175,7 +175,7 @@ export default memo(({
|
||||
// case 'copyName':
|
||||
// break
|
||||
case 'add':
|
||||
console.log(selectedListRef.current.length)
|
||||
// console.log(selectedListRef.current.length)
|
||||
selectedListRef.current.length
|
||||
? setVisibleMusicMultiAddModal(true)
|
||||
: setVisibleMusicAddModal(true)
|
||||
|
@ -81,13 +81,13 @@ const Menu = ({
|
||||
}, [screenSize, buttonPosition, menus, width, height])
|
||||
|
||||
const menuPress = useCallback((menu, index) => {
|
||||
if (menu.disabled) return
|
||||
// if (menu.disabled) return
|
||||
onPress(menu, index)
|
||||
hideMenu()
|
||||
}, [onPress, hideMenu])
|
||||
|
||||
const menuLongPress = useCallback((menu, index) => {
|
||||
if (menu.disabled) return
|
||||
// if (menu.disabled) return
|
||||
longPress(menu, index)
|
||||
// hideMenu()
|
||||
}, [longPress])
|
||||
@ -98,15 +98,28 @@ const Menu = ({
|
||||
<Animated.ScrollView keyboardShouldPersistTaps={'always'}>
|
||||
{
|
||||
menus.map((menu, index) => (
|
||||
<TouchableHighlight
|
||||
key={menu.action}
|
||||
style={{ ...styles.menuItem, width: width, height: height }}
|
||||
underlayColor={theme.secondary40}
|
||||
onPress={() => { menuPress(menu, index) }}
|
||||
onLongPress={() => { menuLongPress(menu, index) }}
|
||||
>
|
||||
<Text style={{ ...styles.menuText, textAlign: center ? 'center' : 'left', color: theme.normal }} numberOfLines={1}>{menu.label}</Text>
|
||||
</TouchableHighlight>
|
||||
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
|
||||
key={menu.action}
|
||||
style={{ ...styles.menuItem, width: width, height: height }}
|
||||
underlayColor={theme.secondary40}
|
||||
onPress={() => { menuPress(menu, index) }}
|
||||
onLongPress={() => { menuLongPress(menu, index) }}
|
||||
>
|
||||
<Text style={{ ...styles.menuText, textAlign: center ? 'center' : 'left', color: theme.normal }} numberOfLines={1}>{menu.label}</Text>
|
||||
</TouchableHighlight>
|
||||
)
|
||||
|
||||
))
|
||||
}
|
||||
</Animated.ScrollView>
|
||||
|
@ -27,6 +27,8 @@
|
||||
"change_position_music_title": "Adjust the position of {{name}} to",
|
||||
"change_position_music_multi_title": "Adjust the position of the selected {{num}} song to",
|
||||
"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\")",
|
||||
|
||||
|
@ -27,6 +27,8 @@
|
||||
"change_position_music_title": "将 {{name}} 的位置调整到",
|
||||
"change_position_music_multi_title": "将已选的 {{num}} 首歌曲的位置调整到",
|
||||
"list_remove": "移除",
|
||||
"list_create": "新建列表",
|
||||
"list_create_input_placeholder": "你想起啥名...",
|
||||
|
||||
"play_detail_todo_tip": "你想干嘛?不可以的,这个功能还没有实现哦😛,不过你可以试着长按来定位当前播放的歌曲(仅对播放“我的列表”里的歌曲有效哦)",
|
||||
|
||||
|
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -126,8 +126,9 @@ const List = memo(({ setVisiblePanel, currentList, activeListIdRef, handleCancel
|
||||
}, [handleRemoveList, handleSyncSourceList])
|
||||
|
||||
const menus = useMemo(() => {
|
||||
if (selectedListIndex == -1) return []
|
||||
const source = userList[selectedListIndex].source
|
||||
const list = userList[selectedListIndex]
|
||||
if (!list) return []
|
||||
const source = list.source
|
||||
|
||||
return [
|
||||
{ action: 'rename', label: t('list_rename') },
|
||||
|
Loading…
x
Reference in New Issue
Block a user