修复导入歌单时可能会导致歌单数据存储异常的问题(#500)

This commit is contained in:
lyswhut 2024-05-25 19:55:02 +08:00
parent 0d996e2bf3
commit 8210da380a
5 changed files with 65 additions and 31 deletions

View File

@ -5,6 +5,7 @@
### 修复
- 修复重复的数据初始化调用
- 修复导入歌单时可能会导致歌单数据存储异常的问题(#500
### 变更

View File

@ -2,7 +2,9 @@ import AsyncStorage from '@react-native-async-storage/async-storage'
import { log } from '@/utils/log'
const partKeyPrefix = '@___PART___'
const partKeyArrPrefix = '@___PART_A___'
const partKeyPrefixRxp = /^@___PART___/
const partKeyArrPrefixRxp = /^@___PART_A___/
const keySplit = ','
const limit = 500000
@ -15,16 +17,25 @@ const buildData = (key: string, value: any, datas: Array<[string, string]>) => {
const partKeys = []
for (let i = 0, len = Math.floor(valueStr.length / limit); i <= len; i++) {
let partKey = `${partKeyPrefix}${key}${i}`
let partKey = `${partKeyArrPrefix}${key}${i}`
partKeys.push(partKey)
datas.push([partKey, valueStr.substring(i * limit, (i + 1) * limit)])
}
datas.push([key, `${partKeyPrefix}${partKeys.join(keySplit)}`])
datas.push([key, partKeyArrPrefix + JSON.stringify(partKeys)])
}
const handleGetDataOld = async<T>(partKeys: string): Promise<T> => {
const keys = partKeys.replace(partKeyPrefixRxp, '').split(keySplit)
return AsyncStorage.multiGet(keys).then(datas => {
return JSON.parse(datas.map(data => data[1]).join(''))
})
}
const handleGetData = async<T>(partKeys: string): Promise<T> => {
const keys = partKeys.replace(partKeyPrefixRxp, '').split(keySplit)
if (partKeys.startsWith(partKeyPrefix)) return handleGetDataOld<T>(partKeys)
const keys = JSON.parse(partKeys.replace(partKeyArrPrefixRxp, '')) as string[]
return AsyncStorage.multiGet(keys).then(datas => {
return JSON.parse(datas.map(data => data[1]).join(''))
})
@ -35,6 +46,7 @@ export const saveData = async(key: string, value: any) => {
buildData(key, value, datas)
try {
await removeData(key)
await AsyncStorage.multiSet(datas)
} catch (e: any) {
// saving error
@ -52,7 +64,7 @@ export const getData = async<T = unknown>(key: string): Promise<T | null> => {
log.error('storage error[getData]:', key, e.message)
throw e
}
if (value && partKeyPrefixRxp.test(value)) {
if (value && (partKeyPrefixRxp.test(value) || partKeyArrPrefixRxp.test(value))) {
return handleGetData<T>(value)
} else if (value == null) return value
return JSON.parse(value)
@ -67,25 +79,39 @@ export const removeData = async(key: string) => {
log.error('storage error[removeData]:', key, e.message)
throw e
}
if (value && partKeyPrefixRxp.test(value)) {
let partKeys = value.replace(partKeyPrefixRxp, '').split(keySplit)
partKeys.push(key)
try {
await AsyncStorage.multiRemove(partKeys)
} catch (e: any) {
// remove error
log.error('storage error[removeData]:', key, e.message)
throw e
}
} else {
try {
await AsyncStorage.removeItem(key)
} catch (e: any) {
// remove error
log.error('storage error[removeData]:', key, e.message)
throw e
if (value) {
if (partKeyPrefixRxp.test(value)) {
let partKeys = value.replace(partKeyPrefixRxp, '').split(keySplit)
partKeys.push(key)
try {
await AsyncStorage.multiRemove(partKeys)
} catch (e: any) {
// remove error
log.error('storage error[removeData]:', key, e.message)
throw e
}
return
} else if (partKeyArrPrefixRxp.test(value)) {
let partKeys = JSON.parse(value.replace(partKeyArrPrefixRxp, '')) as string[]
partKeys.push(key)
try {
await AsyncStorage.multiRemove(partKeys)
} catch (e: any) {
// remove error
log.error('storage error[removeData]:', key, e.message)
throw e
}
return
}
}
try {
await AsyncStorage.removeItem(key)
} catch (e: any) {
// remove error
log.error('storage error[removeData]:', key, e.message)
throw e
}
}
export const getAllKeys = async() => {
@ -114,7 +140,7 @@ export const getDataMultiple = async<T extends readonly string[]>(keys: T) => {
}
const promises: Array<Promise<ReadonlyArray<[unknown | null]>>> = []
for (const [, value] of datas) {
if (value && partKeyPrefixRxp.test(value)) {
if (value && (partKeyPrefixRxp.test(value) || partKeyArrPrefixRxp.test(value))) {
promises.push(handleGetData(value))
} else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
@ -132,6 +158,7 @@ export const saveDataMultiple = async(datas: Array<[string, any]>) => {
buildData(key, value, allData)
}
try {
await removeDataMultiple(datas.map(k => k[0]))
await AsyncStorage.multiSet(allData)
} catch (e: any) {
// save error
@ -147,8 +174,12 @@ export const removeDataMultiple = async(keys: string[]) => {
let allKeys = []
for (const [key, value] of datas) {
allKeys.push(key)
if (value && partKeyPrefixRxp.test(value)) {
allKeys.push(...value.replace(partKeyPrefixRxp, '').split(keySplit))
if (value) {
if (partKeyPrefixRxp.test(value)) {
allKeys.push(...value.replace(partKeyPrefixRxp, '').split(keySplit))
} else if (partKeyArrPrefixRxp.test(value)) {
allKeys.push(...JSON.parse(value.replace(partKeyPrefixRxp, '')) as string[])
}
}
}
try {

View File

@ -48,7 +48,8 @@ export const handleImport = (path: string, position: number) => {
void readListData(path).then(async listData => {
if (listData == null) return
void handleImportListPart(listData, position)
}).catch(() => {
}).catch((err) => {
log.error(err)
toast(global.i18n.t('setting_backup_part_import_list_tip_error'))
})
}

View File

@ -103,7 +103,8 @@ export const handleImportListPart = async(listData: LX.ConfigFile.MyListInfoPart
position: Math.max(position, -1),
}).then(() => {
toast(global.i18n.t('setting_backup_part_import_list_tip_success'))
}).catch(() => {
}).catch((err) => {
log.error(err)
toast(global.i18n.t('setting_backup_part_import_list_tip_error'))
})
}
@ -160,7 +161,8 @@ export const handleImportList = (path: string) => {
void importPlayList(path).then((skipTip) => {
if (skipTip) return
toast(global.i18n.t('setting_backup_part_import_list_tip_success'))
}).catch(() => {
}).catch((err) => {
log.error(err)
toast(global.i18n.t('setting_backup_part_import_list_tip_error'))
})
}

View File

@ -37,7 +37,7 @@ export const clearLogs = async() => {
export const log = {
info(...msgs: any[]) {
// console.info(...msgs)
const msg = msgs.map(m => typeof m == 'string' ? m : JSON.stringify(m)).join(' ')
const msg = msgs.map(m => typeof m == 'string' ? m : m instanceof Error ? m.stack ?? m.message : JSON.stringify(m)).join(' ')
if (msg.startsWith('%c')) return
const time = new Date().toLocaleString()
if (logTools.tempLog) {
@ -46,15 +46,14 @@ export const log = {
},
warn(...msgs: any[]) {
// console.warn(...msgs)
const msg = msgs.map(m => typeof m == 'string' ? m : JSON.stringify(m)).join(' ')
const msg = msgs.map(m => typeof m == 'string' ? m : m instanceof Error ? m.stack ?? m.message : JSON.stringify(m)).join(' ')
const time = new Date().toLocaleString()
if (logTools.tempLog) {
logTools.tempLog.push({ type: 'WARN', time, text: msg })
} else logTools.writeLog(`${time} WARN ${msg}`)
},
error(...msgs: any[]) {
// console.error...(msgs)
const msg = msgs.map(m => typeof m == 'string' ? m : JSON.stringify(m)).join(' ')
const msg = msgs.map(m => typeof m == 'string' ? m : m instanceof Error ? m.stack ?? m.message : JSON.stringify(m)).join(' ')
const time = new Date().toLocaleString()
if (logTools.tempLog) {
logTools.tempLog.push({ type: 'ERROR', time, text: msg })