mirror of
https://github.com/ikun0014/lx-music-mobile.git
synced 2025-07-04 00:32:10 +08:00
新增单个列表导入/导出功能,新增删除列表前的确认弹窗,防止误删列表
This commit is contained in:
parent
d9179c745c
commit
83086356b3
@ -2,6 +2,8 @@
|
||||
|
||||
- 新增歌曲评论显示,可在播放详情页进入。(与PC端一样,目前仅支持显示部分评论)
|
||||
- 新增播放、收藏整个排行榜功能,可长按排行榜名字后在弹出的菜单中操作
|
||||
- 新增单个列表导入/导出功能,可以方便分享歌曲列表,可在点击“我的列表”里的列表名右侧的按钮后弹出的菜单中使用
|
||||
- 新增删除列表前的确认弹窗,防止误删列表
|
||||
|
||||
### 优化
|
||||
|
||||
|
@ -23,3 +23,5 @@ export const NAV_VIEW_NAMES = {
|
||||
list: 3,
|
||||
setting: 4,
|
||||
}
|
||||
|
||||
const LXM_FILE_EXT_RXP = /\.(json|lxmc)$/
|
||||
|
@ -25,6 +25,8 @@
|
||||
"date_format_minute": "{{num}} minutes ago",
|
||||
"date_format_second": "{{num}} seconds ago",
|
||||
"delete": "Delete",
|
||||
"dialog_cancel": "No",
|
||||
"dialog_confirm": "OK",
|
||||
"disagree": "Disagree",
|
||||
"disagree_tip": "Cancelled...",
|
||||
"input_error": "Don't input indiscriminately 😡",
|
||||
@ -39,11 +41,20 @@
|
||||
"list_edit_action_tip_remove_success": "Removed successfully",
|
||||
"list_end": "In The End",
|
||||
"list_error": "Loading failed 😥",
|
||||
"list_export": "Export",
|
||||
"list_export_part_desc": "Choose where to save the list file",
|
||||
"list_import": "Import",
|
||||
"list_import_part_button_cancel": "No",
|
||||
"list_import_part_button_confirm": "Overwrite",
|
||||
"list_import_part_confirm": "The imported list ({{importName}}) has the same ID as the local list ({{localName}}). Do you overwrite the local list?",
|
||||
"list_import_part_desc": "Select list file",
|
||||
"list_import_part_tip_failed": "This does not seem to be a single list file",
|
||||
"list_loading": "Loading...",
|
||||
"list_multi_add_title_first_add": "Add selected",
|
||||
"list_multi_add_title_first_move": "Move the selected one",
|
||||
"list_multi_add_title_last": "First song to...",
|
||||
"list_remove": "Remove",
|
||||
"list_remove_tip_button": "Yes, that's right",
|
||||
"list_rename": "Rename",
|
||||
"list_rename_title": "Rename List",
|
||||
"list_select_all": "Select All",
|
||||
|
@ -25,6 +25,8 @@
|
||||
"date_format_minute": "{{num}}分钟前",
|
||||
"date_format_second": "{{num}}秒前",
|
||||
"delete": "删除",
|
||||
"dialog_cancel": "我不",
|
||||
"dialog_confirm": "好的",
|
||||
"disagree": "我就不",
|
||||
"disagree_tip": "那算了... 🙄",
|
||||
"input_error": "不要乱输好吧😡",
|
||||
@ -39,11 +41,21 @@
|
||||
"list_edit_action_tip_remove_success": "移除成功",
|
||||
"list_end": "到底啦~",
|
||||
"list_error": "加载失败😥",
|
||||
"list_export": "导出",
|
||||
"list_export_part_desc": "选择列表文件保存位置",
|
||||
"list_import": "导入",
|
||||
"list_import_part_button_cancel": "不要啊",
|
||||
"list_import_part_button_confirm": "覆盖掉",
|
||||
"list_import_part_confirm": "导入的列表({{importName}})与本地列表({{localName}})的ID相同,是否覆盖本地列表?",
|
||||
"list_import_part_desc": "选择列表文件",
|
||||
"list_import_part_tip_failed": "这似乎不是单个的列表文件哦",
|
||||
"list_loading": "加载中...",
|
||||
"list_multi_add_title_first_add": "添加已选的",
|
||||
"list_multi_add_title_first_move": "移动已选的",
|
||||
"list_multi_add_title_last": "首歌曲到...",
|
||||
"list_remove": "移除",
|
||||
"list_remove_tip": "你真的想要移除 {{name}} 吗?",
|
||||
"list_remove_tip_button": "是的 没错",
|
||||
"list_rename": "重命名",
|
||||
"list_rename_title": "重命名列表",
|
||||
"list_select_all": "全选",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { memo, useMemo, useEffect, useCallback, useState, useRef } from 'react'
|
||||
import { StyleSheet, Text, View, TouchableOpacity, ScrollView } from 'react-native'
|
||||
import { StyleSheet, Text, View, TouchableOpacity, ScrollView, InteractionManager } from 'react-native'
|
||||
|
||||
import { useGetter, useDispatch } from '@/store'
|
||||
import { useTranslation } from '@/plugins/i18n'
|
||||
@ -10,9 +10,39 @@ import { BorderWidths } from '@/theme'
|
||||
import Menu from '@/components/common/Menu'
|
||||
import ConfirmAlert from '@/components/common/ConfirmAlert'
|
||||
import Input from '@/components/common/Input'
|
||||
import { getListScrollPosition, saveListScrollPosition, toast } from '@/utils/tools'
|
||||
import { LIST_SCROLL_POSITION_KEY } from '@/config/constant'
|
||||
import { filterFileName } from '@/utils'
|
||||
import { getListScrollPosition, saveListScrollPosition, toast, handleSaveFile, handleReadFile, confirmDialog } from '@/utils/tools'
|
||||
import { LIST_SCROLL_POSITION_KEY, LXM_FILE_EXT_RXP } from '@/config/constant'
|
||||
import musicSdk from '@/utils/music'
|
||||
import ChoosePath from '@/components/common/ChoosePath'
|
||||
import { log } from '@/utils/log'
|
||||
|
||||
const exportList = async(list, path) => {
|
||||
const data = JSON.parse(JSON.stringify({
|
||||
type: 'playListPart',
|
||||
data: list,
|
||||
}))
|
||||
for (const item of data.data.list) {
|
||||
if (item.otherSource) delete item.otherSource
|
||||
if (item.lrc) delete item.lrc
|
||||
}
|
||||
try {
|
||||
await handleSaveFile(path + `/lx_list_part_${filterFileName(list.name)}.lxmc`, data)
|
||||
} catch (error) {
|
||||
log.error(error.stack)
|
||||
}
|
||||
}
|
||||
const importList = async path => {
|
||||
let listData
|
||||
try {
|
||||
listData = await handleReadFile(path)
|
||||
} catch (error) {
|
||||
log.error(error.stack)
|
||||
return
|
||||
}
|
||||
console.log(listData.type)
|
||||
return listData
|
||||
}
|
||||
|
||||
const ListItem = ({ onPress, name, id, showMenu, activeId, loading, index }) => {
|
||||
const theme = useGetter('common', 'theme')
|
||||
@ -38,6 +68,89 @@ const ListItem = ({ onPress, name, id, showMenu, activeId, loading, index }) =>
|
||||
)
|
||||
}
|
||||
|
||||
const ImportExport = ({ actionType, visible, hide, selectedListRef }) => {
|
||||
const [title, setTitle] = useState('')
|
||||
const [dirOnly, setDirOnly] = useState(false)
|
||||
const setList = useDispatch('list', 'setList')
|
||||
const createUserList = useDispatch('list', 'createUserList')
|
||||
const { t } = useTranslation()
|
||||
useEffect(() => {
|
||||
switch (actionType) {
|
||||
case 'import':
|
||||
setTitle(t('list_import_part_desc'))
|
||||
setDirOnly(false)
|
||||
break
|
||||
case 'export':
|
||||
default:
|
||||
setTitle(t('list_export_part_desc'))
|
||||
setDirOnly(true)
|
||||
break
|
||||
}
|
||||
}, [actionType, t])
|
||||
|
||||
const onConfirmPath = useCallback(path => {
|
||||
hide()
|
||||
switch (actionType) {
|
||||
case 'import':
|
||||
toast(t('setting_backup_part_import_list_tip_unzip'))
|
||||
importList(path).then(async listData => {
|
||||
if (listData.type != 'playListPart') return toast(t('list_import_part_tip_failed'))
|
||||
const targetList = global.allList[listData.data.id]
|
||||
if (targetList) {
|
||||
const confirm = await confirmDialog({
|
||||
message: t('list_import_part_confirm', { importName: listData.data.name, localName: targetList.name }),
|
||||
cancelButtonText: t('list_import_part_button_cancel'),
|
||||
confirmButtonText: t('list_import_part_button_confirm'),
|
||||
bgClose: false,
|
||||
})
|
||||
if (confirm) {
|
||||
listData.data.name = targetList.name
|
||||
setList({
|
||||
name: listData.data.name,
|
||||
id: listData.data.id,
|
||||
list: listData.data.list,
|
||||
source: listData.data.source,
|
||||
sourceListId: listData.data.sourceListId,
|
||||
})
|
||||
return
|
||||
}
|
||||
listData.data.id += `__${Date.now()}`
|
||||
}
|
||||
createUserList({
|
||||
name: listData.data.name,
|
||||
id: listData.data.id,
|
||||
list: listData.data.list,
|
||||
source: listData.data.source,
|
||||
sourceListId: listData.data.sourceListId,
|
||||
position: Math.max(selectedListRef.current.index, -1),
|
||||
})
|
||||
})
|
||||
break
|
||||
case 'export':
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
toast(t('setting_backup_part_export_list_tip_zip'))
|
||||
exportList(selectedListRef.current.listInfo, path).then(() => {
|
||||
toast(t('setting_backup_part_export_list_tip_success'))
|
||||
}).catch(err => {
|
||||
log.error(err.message)
|
||||
toast(t('setting_backup_part_export_list_tip_failed') + ': ' + err.message)
|
||||
})
|
||||
})
|
||||
break
|
||||
}
|
||||
}, [actionType, createUserList, hide, setList, t])
|
||||
|
||||
return (
|
||||
<ChoosePath
|
||||
visible={visible}
|
||||
hide={hide}
|
||||
title={title}
|
||||
dirOnly={dirOnly}
|
||||
filter={LXM_FILE_EXT_RXP}
|
||||
onConfirm={onConfirmPath} />
|
||||
)
|
||||
}
|
||||
|
||||
const List = memo(({ setVisiblePanel, currentList, handleCancelMultiSelect }) => {
|
||||
const theme = useGetter('common', 'theme')
|
||||
const defaultList = useGetter('list', 'defaultList')
|
||||
@ -61,6 +174,8 @@ const List = memo(({ setVisiblePanel, currentList, handleCancelMultiSelect }) =>
|
||||
const getBoardListAll = useDispatch('top', 'getListAll')
|
||||
const getListDetailAll = useDispatch('songList', 'getListDetailAll')
|
||||
const [fetchingListStatus, setFetchingListStatus] = useState({})
|
||||
const [isShowChoosePath, setShowChoosePath] = useState(false)
|
||||
const [actionType, setActionType] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
userListRef.current = userList
|
||||
@ -75,6 +190,29 @@ const List = memo(({ setVisiblePanel, currentList, handleCancelMultiSelect }) =>
|
||||
removeUserList({ id })
|
||||
}, [removeUserList])
|
||||
|
||||
const getTargetListInfo = useCallback(index => {
|
||||
let list
|
||||
switch (index) {
|
||||
case -2:
|
||||
list = defaultList
|
||||
break
|
||||
case -1:
|
||||
list = loveList
|
||||
break
|
||||
default:
|
||||
list = userListRef.current[index]
|
||||
break
|
||||
}
|
||||
return list
|
||||
}, [defaultList, loveList])
|
||||
|
||||
const handleImportAndExportList = useCallback((type, index) => {
|
||||
const list = getTargetListInfo(index)
|
||||
if (!list) return
|
||||
selectedListRef.current.listInfo = list
|
||||
setActionType(type)
|
||||
setShowChoosePath(true)
|
||||
}, [getTargetListInfo])
|
||||
|
||||
const hideMenu = useCallback(() => {
|
||||
setVisibleMenu(false)
|
||||
@ -113,6 +251,12 @@ const List = memo(({ setVisiblePanel, currentList, handleCancelMultiSelect }) =>
|
||||
setListNameText(selectedListRef.current.name)
|
||||
setVisibleRename(true)
|
||||
break
|
||||
case 'import':
|
||||
handleImportAndExportList('import', selectedListRef.current.index)
|
||||
break
|
||||
case 'export':
|
||||
handleImportAndExportList('export', selectedListRef.current.index)
|
||||
break
|
||||
case 'sync':
|
||||
handleSyncSourceList(selectedListRef.current.index)
|
||||
break
|
||||
@ -120,26 +264,50 @@ const List = memo(({ setVisiblePanel, currentList, handleCancelMultiSelect }) =>
|
||||
|
||||
// break
|
||||
case 'remove':
|
||||
confirmDialog({
|
||||
message: t('list_remove_tip', { name: selectedListRef.current.name }),
|
||||
confirmButtonText: t('list_remove_tip_button'),
|
||||
}).then(isRemove => {
|
||||
if (!isRemove) return
|
||||
handleRemoveList(selectedListRef.current.id)
|
||||
})
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}, [handleRemoveList, handleSyncSourceList])
|
||||
}, [handleImportAndExportList, handleRemoveList, handleSyncSourceList, t])
|
||||
|
||||
const menus = useMemo(() => {
|
||||
const list = userList[selectedListIndex]
|
||||
let list
|
||||
let rename = false
|
||||
let sync = false
|
||||
let remove = false
|
||||
switch (selectedListIndex) {
|
||||
case -2:
|
||||
list = defaultList
|
||||
break
|
||||
case -1:
|
||||
list = loveList
|
||||
break
|
||||
default:
|
||||
list = userList[selectedListIndex]
|
||||
rename = true
|
||||
remove = true
|
||||
sync = list.source && !!musicSdk[list.source].songList
|
||||
break
|
||||
}
|
||||
if (!list) return []
|
||||
const source = list.source
|
||||
|
||||
return [
|
||||
{ action: 'rename', label: t('list_rename') },
|
||||
{ action: 'sync', label: t('list_sync'), disabled: !source || !musicSdk[source].songList },
|
||||
{ action: 'rename', disabled: !rename, label: t('list_rename') },
|
||||
{ action: 'sync', disabled: !sync, label: t('list_sync') },
|
||||
{ action: 'import', label: t('list_import') },
|
||||
{ action: 'export', label: t('list_export') },
|
||||
// { action: 'changePosition', label: t('change_position') },
|
||||
{ action: 'remove', label: t('list_remove') },
|
||||
{ action: 'remove', disabled: !remove, label: t('list_remove') },
|
||||
]
|
||||
}, [selectedListIndex, userList, t])
|
||||
}, [selectedListIndex, t, defaultList, loveList, userList])
|
||||
|
||||
const handleCancelRename = useCallback(() => {
|
||||
setVisibleRename(false)
|
||||
@ -150,13 +318,6 @@ const List = memo(({ setVisiblePanel, currentList, handleCancelMultiSelect }) =>
|
||||
setVisibleRename(false)
|
||||
}, [listNameText, setUserListName])
|
||||
|
||||
const handleToggleDefaultList = () => {
|
||||
handleToggleList(defaultList)
|
||||
}
|
||||
const handleToggleLoveList = () => {
|
||||
handleToggleList(loveList)
|
||||
}
|
||||
|
||||
const handleScroll = useCallback(({ nativeEvent }) => {
|
||||
saveListScrollPosition(LIST_SCROLL_POSITION_KEY, nativeEvent.contentOffset.y)
|
||||
}, [])
|
||||
@ -167,7 +328,7 @@ const List = memo(({ setVisiblePanel, currentList, handleCancelMultiSelect }) =>
|
||||
})
|
||||
const showMenu = useCallback((id, name, index, position) => {
|
||||
// console.log(position)
|
||||
if (id == 'default' || id == 'love') return
|
||||
// if (id == 'default' || id == 'love') return
|
||||
setButtonPosition({ ...position })
|
||||
selectedListRef.current.id = id
|
||||
selectedListRef.current.name = name
|
||||
@ -179,16 +340,8 @@ const List = memo(({ setVisiblePanel, currentList, handleCancelMultiSelect }) =>
|
||||
<View style={{ ...styles.container, borderBottomColor: theme.secondary10 }}>
|
||||
<ScrollView style={{ flexShrink: 1, flexGrow: 0 }} onScroll={handleScroll} ref={scrollViewRef} keyboardShouldPersistTaps={'always'}>
|
||||
<View style={{ ...styles.listContainer, backgroundColor: theme.primary }} onStartShouldSetResponder={() => true}>
|
||||
<View style={{ ...styles.listItem, borderBottomColor: theme.secondary45 }}>
|
||||
<TouchableOpacity style={styles.listName} onPress={handleToggleDefaultList}>
|
||||
<Text numberOfLines={1} style={{ color: theme.normal }}>{defaultList.name}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View style={{ ...styles.listItem, borderBottomColor: theme.secondary45 }}>
|
||||
<TouchableOpacity style={styles.listName} onPress={handleToggleLoveList}>
|
||||
<Text numberOfLines={1} style={{ color: theme.normal }}>{loveList.name}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<ListItem name={defaultList.name} id={defaultList.id} index={-2} loading={false} onPress={() => handleToggleList(defaultList)} activeId={currentList.id} showMenu={showMenu} />
|
||||
<ListItem name={loveList.name} id={loveList.id} index={-1} loading={false} onPress={() => handleToggleList(loveList)} activeId={currentList.id} showMenu={showMenu} />
|
||||
{userList.map(({ id, name }, index) => <ListItem key={id} name={name} id={id} index={index} loading={fetchingListStatus[id]} onPress={() => handleToggleList({ id, name })} activeId={currentList.id} showMenu={showMenu} />)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
@ -208,6 +361,7 @@ const List = memo(({ setVisiblePanel, currentList, handleCancelMultiSelect }) =>
|
||||
/>
|
||||
</View>
|
||||
</ConfirmAlert>
|
||||
<ImportExport actionType={actionType} visible={isShowChoosePath} hide={() => setShowChoosePath(false)} selectedListRef={selectedListRef} />
|
||||
</View>
|
||||
)
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { memo, useCallback, useState, useRef } from 'react'
|
||||
import { StyleSheet, View, Text, InteractionManager } from 'react-native'
|
||||
import { readFile, writeFile, temporaryDirectoryPath, unlink } from '@/utils/fs'
|
||||
import { StyleSheet, View, InteractionManager } from 'react-native'
|
||||
import { log } from '@/utils/log'
|
||||
import { LXM_FILE_EXT_RXP } from '@/config/constant'
|
||||
|
||||
import { useGetter, useDispatch } from '@/store'
|
||||
// import { gzip, ungzip } from 'pako'
|
||||
@ -10,33 +10,9 @@ import SubTitle from '../components/SubTitle'
|
||||
import Button from '../components/Button'
|
||||
import ChoosePath from '@/components/common/ChoosePath'
|
||||
import { useTranslation } from '@/plugins/i18n'
|
||||
import { toast } from '@/utils/tools'
|
||||
import { gzip, ungzip } from '@/utils/gzip'
|
||||
import { toast, handleSaveFile, handleReadFile } from '@/utils/tools'
|
||||
|
||||
const lxmFileExt = /\.(json|lxmc)$/
|
||||
|
||||
const handleSaveFile = async(path, data) => {
|
||||
// if (!path.endsWith('.json')) path += '.json'
|
||||
// const buffer = gzip(data)
|
||||
const tempFilePath = `${temporaryDirectoryPath}/tempFile.json`
|
||||
await writeFile(tempFilePath, data, 'utf8')
|
||||
await gzip(tempFilePath, path)
|
||||
await unlink(tempFilePath)
|
||||
}
|
||||
const handleReadFile = async(path) => {
|
||||
let isJSON = path.endsWith('.json')
|
||||
let data
|
||||
if (isJSON) {
|
||||
data = await readFile(path, 'utf8')
|
||||
} else {
|
||||
const tempFilePath = `${temporaryDirectoryPath}/tempFile.json`
|
||||
await ungzip(path, tempFilePath)
|
||||
data = await readFile(tempFilePath, 'utf8')
|
||||
await unlink(tempFilePath)
|
||||
}
|
||||
return data
|
||||
}
|
||||
const exportList = async(allList, path) => {
|
||||
const exportAllList = async(allList, path) => {
|
||||
const data = JSON.parse(JSON.stringify({
|
||||
type: 'playList',
|
||||
data: allList,
|
||||
@ -46,12 +22,16 @@ const exportList = async(allList, path) => {
|
||||
if (item.otherSource) delete item.otherSource
|
||||
}
|
||||
}
|
||||
await handleSaveFile(path + '/lx_list.lxmc', JSON.stringify(data))
|
||||
try {
|
||||
await handleSaveFile(path + '/lx_list.lxmc', data)
|
||||
} catch (error) {
|
||||
log.error(error.stack)
|
||||
}
|
||||
}
|
||||
const importList = async path => {
|
||||
const importAllList = async path => {
|
||||
let listData
|
||||
try {
|
||||
listData = JSON.parse(await handleReadFile(path))
|
||||
listData = await handleReadFile(path)
|
||||
} catch (error) {
|
||||
log.error(error.stack)
|
||||
return
|
||||
@ -116,7 +96,7 @@ export default memo(() => {
|
||||
case 'import_list':
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
toast(t('setting_backup_part_import_list_tip_unzip'))
|
||||
importList(path).then(listData => {
|
||||
importAllList(path).then(listData => {
|
||||
// 兼容0.6.2及以前版本的列表数据
|
||||
if (listData.type === 'defautlList') {
|
||||
handleSetList(setList, [
|
||||
@ -164,7 +144,7 @@ export default memo(() => {
|
||||
case 'export_list':
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
toast(t('setting_backup_part_export_list_tip_zip'))
|
||||
exportList(allList, path).then(() => {
|
||||
exportAllList(allList, path).then(() => {
|
||||
toast(t('setting_backup_part_export_list_tip_success'))
|
||||
}).catch(err => {
|
||||
log.error(err.message)
|
||||
@ -206,7 +186,7 @@ export default memo(() => {
|
||||
hide={() => setShowChoosePath(false)}
|
||||
title={title}
|
||||
dirOnly={dirOnly}
|
||||
filter={lxmFileExt}
|
||||
filter={LXM_FILE_EXT_RXP}
|
||||
onConfirm={onConfirmPath} />
|
||||
</>
|
||||
)
|
||||
|
@ -256,14 +256,14 @@ export const updateMusicInfo = ({ listId, id, data, isSync }) => (dispatch, getS
|
||||
saveList(global.allList[listId])
|
||||
}
|
||||
|
||||
export const createUserList = ({ name, id = `userlist_${Date.now()}`, list = [], source, sourceListId, isSync }) => async(dispatch, getState) => {
|
||||
export const createUserList = ({ name, id = `userlist_${Date.now()}`, list = [], source, sourceListId, position, isSync }) => async(dispatch, getState) => {
|
||||
if (!isSync) {
|
||||
listSync.sendListAction('create_user_list', { name, id, list, source, sourceListId })
|
||||
listSync.sendListAction('create_user_list', { name, id, list, source, sourceListId, position })
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: TYPES.createUserList,
|
||||
payload: { name, id, source, sourceListId },
|
||||
payload: { name, id, source, sourceListId, position },
|
||||
})
|
||||
dispatch(listAddMultiple({ id, list, isSync: true }))
|
||||
await saveList(global.allList[id])
|
||||
|
@ -241,7 +241,7 @@ const mutations = {
|
||||
return updateStateList({ ...state }, [listId])
|
||||
},
|
||||
|
||||
[TYPES.createUserList](state, { name, id, source, sourceListId }) {
|
||||
[TYPES.createUserList](state, { name, id, source, sourceListId, position }) {
|
||||
let newList = state.userList.find(item => item.id === id)
|
||||
if (newList) return state
|
||||
const newState = { ...state }
|
||||
@ -252,7 +252,13 @@ const mutations = {
|
||||
source,
|
||||
sourceListId,
|
||||
}
|
||||
newState.userList = [...state.userList, newList]
|
||||
const userList = [...state.userList]
|
||||
if (position == null) {
|
||||
userList.push(newList)
|
||||
} else {
|
||||
userList.splice(position + 1, 0, newList)
|
||||
}
|
||||
newState.userList = userList
|
||||
allListUpdate(newList)
|
||||
return newState
|
||||
},
|
||||
|
@ -156,3 +156,6 @@ export const debounce = (fn, delay = 100) => {
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
|
||||
const fileNameRxp = /[\\/:*?#"<>|]/g
|
||||
export const filterFileName = name => name.replace(fileNameRxp, '')
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { Platform, NativeModules, ToastAndroid, BackHandler, Linking, Dimensions } from 'react-native'
|
||||
import { Platform, NativeModules, ToastAndroid, BackHandler, Linking, Dimensions, Alert } from 'react-native'
|
||||
import ExtraDimensions from 'react-native-extra-dimensions-android'
|
||||
import { getData, setData, getAllKeys, removeData, removeDataMultiple, setDataMultiple, getDataMultiple } from '@/plugins/storage'
|
||||
import { storageDataPrefix } from '@/config'
|
||||
import { throttle } from './index'
|
||||
import { gzip, ungzip } from '@/utils/gzip'
|
||||
import { readFile, writeFile, temporaryDirectoryPath, unlink } from '@/utils/fs'
|
||||
|
||||
const playInfoStorageKey = storageDataPrefix.playInfo
|
||||
const listPositionPrefix = storageDataPrefix.listPosition
|
||||
@ -242,6 +244,57 @@ export const setSyncHost = async({ host, port }) => {
|
||||
|
||||
export const exitApp = BackHandler.exitApp
|
||||
|
||||
export const handleSaveFile = async(path, data) => {
|
||||
// if (!path.endsWith('.json')) path += '.json'
|
||||
// const buffer = gzip(data)
|
||||
const tempFilePath = `${temporaryDirectoryPath}/tempFile.json`
|
||||
await writeFile(tempFilePath, JSON.stringify(data), 'utf8')
|
||||
await gzip(tempFilePath, path)
|
||||
await unlink(tempFilePath)
|
||||
}
|
||||
export const handleReadFile = async(path) => {
|
||||
let isJSON = path.endsWith('.json')
|
||||
let data
|
||||
if (isJSON) {
|
||||
data = await readFile(path, 'utf8')
|
||||
} else {
|
||||
const tempFilePath = `${temporaryDirectoryPath}/tempFile.json`
|
||||
await ungzip(path, tempFilePath)
|
||||
data = await readFile(tempFilePath, 'utf8')
|
||||
await unlink(tempFilePath)
|
||||
}
|
||||
return JSON.parse(data)
|
||||
}
|
||||
|
||||
export const confirmDialog = ({
|
||||
message = '',
|
||||
cancelButtonText = global.i18n.t('dialog_cancel'),
|
||||
confirmButtonText = global.i18n.t('dialog_confirm'),
|
||||
bgClose = true,
|
||||
}) => {
|
||||
return new Promise(resolve => {
|
||||
Alert.alert(null, message, [
|
||||
{
|
||||
text: cancelButtonText,
|
||||
onPress() {
|
||||
resolve(false)
|
||||
},
|
||||
},
|
||||
{
|
||||
text: confirmButtonText,
|
||||
onPress() {
|
||||
resolve(true)
|
||||
},
|
||||
},
|
||||
], {
|
||||
cancelable: bgClose,
|
||||
onDismiss() {
|
||||
resolve(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
deviceLanguage,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user