From 1a97295c7b4f88bb09e347ba3e4e9f313edd9adf Mon Sep 17 00:00:00 2001 From: lyswhut Date: Sat, 4 Nov 2023 12:26:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/assets/script/user-api-preload.js | 16 +- src/app.ts | 50 ++- src/components/common/CheckBox/index.tsx | 18 +- src/config/setting.ts | 19 +- .../init/{userApi.ts => userApi/index.ts} | 51 ++- src/core/init/userApi/request.js | 122 ++++++ src/core/userApi.ts | 2 - src/lang/en_us.json | 7 +- src/lang/zh_cn.json | 7 +- src/navigation/navigation.ts | 356 +++++++++--------- .../settings/Basic/UserApiEditModal/List.tsx | 44 ++- .../settings/Basic/UserApiEditModal/index.tsx | 17 +- .../settings/Other/DislikeEditModal.tsx | 9 +- src/utils/tools.ts | 59 +-- 14 files changed, 470 insertions(+), 307 deletions(-) rename src/core/init/{userApi.ts => userApi/index.ts} (84%) create mode 100644 src/core/init/userApi/request.js diff --git a/android/app/src/main/assets/script/user-api-preload.js b/android/app/src/main/assets/script/user-api-preload.js index 45a3c3e..5640a3b 100644 --- a/android/app/src/main/assets/script/user-api-preload.js +++ b/android/app/src/main/assets/script/user-api-preload.js @@ -104,14 +104,14 @@ globalThis.lx_setup = (key, id, name, description, rawScript) => { request: 'request', cancelRequest: 'cancelRequest', response: 'response', - 'utils.crypto.aesEncrypt': 'utils.crypto.aesEncrypt', - 'utils.crypto.rsaEncrypt': 'utils.crypto.rsaEncrypt', - 'utils.crypto.randomBytes': 'utils.crypto.randomBytes', - 'utils.crypto.md5': 'utils.crypto.md5', - 'utils.buffer.from': 'utils.buffer.from', - 'utils.buffer.bufToString': 'utils.buffer.bufToString', - 'utils.zlib.inflate': 'utils.zlib.inflate', - 'utils.zlib.deflate': 'utils.zlib.deflate', + // 'utils.crypto.aesEncrypt': 'utils.crypto.aesEncrypt', + // 'utils.crypto.rsaEncrypt': 'utils.crypto.rsaEncrypt', + // 'utils.crypto.randomBytes': 'utils.crypto.randomBytes', + // 'utils.crypto.md5': 'utils.crypto.md5', + // 'utils.buffer.from': 'utils.buffer.from', + // 'utils.buffer.bufToString': 'utils.buffer.bufToString', + // 'utils.zlib.inflate': 'utils.zlib.inflate', + // 'utils.zlib.deflate': 'utils.zlib.deflate', } const EVENT_NAMES = Object.freeze({ request: 'request', diff --git a/src/app.ts b/src/app.ts index 72b4a0d..5a35091 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,10 +3,10 @@ import { init as initLog } from '@/utils/log' import { bootLog, getBootLog } from '@/utils/bootLog' import '@/config/globalData' import { getFontSize } from '@/utils/data' -import { Alert } from 'react-native' import { exitApp } from './utils/nativeModules/utils' import { windowSizeTools } from './utils/windowSizeTools' import { listenLaunchEvent } from './navigation/regLaunchedEvent' +import { tipDialog } from './utils/tools' console.log('starting app...') listenLaunchEvent() @@ -33,15 +33,13 @@ void Promise.all([getFontSize(), windowSizeTools.init()]).then(async([fontSize]) try { handlePushedHomeScreen = await init() } catch (err: any) { - Alert.alert('初始化失败 (Init Failed)', `Boot Log:\n${tryGetBootLog()}\n\n${(err.stack ?? err.message) as string}`, [ - { - text: 'Exit', - onPress() { - exitApp() - }, - }, - ], { - cancelable: false, + void tipDialog({ + title: '初始化失败 (Init Failed)', + message: `Boot Log:\n${tryGetBootLog()}\n\n${(err.stack ?? err.message) as string}`, + btnText: 'Exit', + bgClose: false, + }).then(() => { + exitApp() }) return } @@ -57,27 +55,23 @@ void Promise.all([getFontSize(), windowSizeTools.init()]).then(async([fontSize]) await navigations.pushHomeScreen().then(() => { void handlePushedHomeScreen() }).catch((err: any) => { - Alert.alert('Error', err.message, [ - { - text: 'Exit', - onPress() { - exitApp() - }, - }, - ], { - cancelable: false, + void tipDialog({ + title: 'Error', + message: err.message, + btnText: 'Exit', + bgClose: false, + }).then(() => { + exitApp() }) }) }) }).catch((err) => { - Alert.alert('初始化失败 (Init Failed)', `Boot Log:\n\n${(err.stack ?? err.message) as string}`, [ - { - text: 'Exit', - onPress() { - exitApp() - }, - }, - ], { - cancelable: false, + void tipDialog({ + title: '初始化失败 (Init Failed)', + message: `Boot Log:\n\n${(err.stack ?? err.message) as string}`, + btnText: 'Exit', + bgClose: false, + }).then(() => { + exitApp() }) }) diff --git a/src/components/common/CheckBox/index.tsx b/src/components/common/CheckBox/index.tsx index d3b9093..968185f 100644 --- a/src/components/common/CheckBox/index.tsx +++ b/src/components/common/CheckBox/index.tsx @@ -1,8 +1,8 @@ import { useCallback, useEffect, useMemo, useState } from 'react' -import { View, TouchableOpacity, Alert } from 'react-native' +import { View, TouchableOpacity } from 'react-native' import CheckBox from './Checkbox' -import { createStyle } from '@/utils/tools' +import { createStyle, tipDialog } from '@/utils/tools' import { scaleSizeH, scaleSizeW } from '@/utils/pixelRatio' import { useTheme } from '@/store/theme/hook' import Text from '../Text' @@ -54,15 +54,11 @@ export default ({ check, label, children, onChange, helpTitle, helpDesc, disable const helpComponent = useMemo(() => { const handleShowHelp = () => { - Alert.alert(helpTitle ?? '', helpDesc, - // [{ - // text: '我知道了 (Close)', - // // onPress: () => { - // // void saveData(storageDataPrefix.cheatTip, true) - // // resolve() - // // }, - // }], - ) + void tipDialog({ + title: helpTitle ?? '', + message: helpDesc, + btnText: global.i18n.t('understand'), + }) } return (helpTitle ?? helpDesc) ? ( diff --git a/src/config/setting.ts b/src/config/setting.ts index 918f0ef..31e4efa 100644 --- a/src/config/setting.ts +++ b/src/config/setting.ts @@ -4,8 +4,7 @@ import { getData, removeData, saveData } from '@/plugins/storage' import migrateSetting from './migrateSetting' import settingState from '@/store/setting/state' import { migrateMetaData, migrateListData } from './migrate' -import { Alert } from 'react-native' -import { exitApp } from '@/utils/tools' +import { exitApp, tipDialog } from '@/utils/tools' // 业务相关工具方法 @@ -90,15 +89,13 @@ export const initSetting = async() => { await migrateListData() await migrateMetaData() } catch (err: any) { - Alert.alert('数据迁移失败 (Migrate data Failed)', `请加企鹅群(830125506)或到GitHub反馈,为了防止数据丢失,应用将停止运行,错误信息:\n${(err.stack ?? err.message) as string}`, [ - { - text: 'Exit', - onPress() { - exitApp() - }, - }, - ], { - cancelable: false, + void tipDialog({ + title: '数据迁移失败 (Migrate data Failed)', + message: `请加企鹅群(830125506)或到GitHub反馈,为了防止数据丢失,应用将停止运行,错误信息:\n${(err.stack ?? err.message) as string}`, + btnText: 'Exit', + bgClose: false, + }).then(() => { + exitApp() }) throw err } diff --git a/src/core/init/userApi.ts b/src/core/init/userApi/index.ts similarity index 84% rename from src/core/init/userApi.ts rename to src/core/init/userApi/index.ts index de6d35c..eeaaf9a 100644 --- a/src/core/init/userApi.ts +++ b/src/core/init/userApi/index.ts @@ -1,9 +1,10 @@ import { type InitParams, onScriptAction, sendAction, type ResponseParams, type UpdateInfoParams } from '@/utils/nativeModules/userApi' -import { log, setUserApiList, setUserApiStatus } from '../userApi' +import { log, setUserApiList, setUserApiStatus } from '@/core/userApi' import settingState from '@/store/setting/state' import BackgroundTimer from 'react-native-background-timer' -import { httpFetch } from '@/utils/request' +import { fetchData } from './request' import { getUserApiList } from '@/utils/data' +import { confirmDialog, openUrl, tipDialog } from '@/utils/tools' export default async(setting: LX.AppSetting) => { @@ -90,26 +91,26 @@ export default async(setting: LX.AppSetting) => { global.state_event.apiSourceUpdated(settingState.setting['common.apiSource']) } const showUpdateAlert = ({ name, log, updateUrl }: UpdateInfoParams) => { - // if (updateUrl) { - // void dialog({ - // message: `${t('user_api__update_alert', { name })}\n${log}`, - // selection: true, - // showCancel: true, - // confirmButtonText: t('user_api__update_alert_open_url'), - // cancelButtonText: t('close'), - // }).then(confirm => { - // if (!confirm) return - // window.setTimeout(() => { - // void openUrl(updateUrl) - // }, 300) - // }) - // } else { - // void dialog({ - // message: `${t('user_api__update_alert', { name })}\n${log}`, - // selection: true, - // confirmButtonText: t('ok'), - // }) - // } + if (updateUrl) { + void confirmDialog({ + message: `${global.i18n.t('user_api_update_alert', { name })}\n${log}`, + // selection: true, + // showCancel: true, + confirmButtonText: global.i18n.t('user_api_update_alert_open_url'), + cancelButtonText: global.i18n.t('close'), + }).then(confirm => { + if (!confirm) return + setTimeout(() => { + void openUrl(updateUrl) + }, 300) + }) + } else { + void tipDialog({ + message: `${global.i18n.t('user_api_update_alert', { name })}\n${log}`, + // selection: true, + btnText: global.i18n.t('ok'), + }) + } } onScriptAction((event) => { @@ -122,11 +123,7 @@ export default async(setting: LX.AppSetting) => { handleUserApiResponse(event.data) break case 'request': - httpFetch(event.data.url, { - ...event.data.options, - credentials: 'omit', - cache: 'default', - }).promise.then(response => { + fetchData(event.data.url, event.data.options).request.then(response => { // console.log(response) sendAction('response', { error: null, diff --git a/src/core/init/userApi/request.js b/src/core/init/userApi/request.js new file mode 100644 index 0000000..8fa5b3e --- /dev/null +++ b/src/core/init/userApi/request.js @@ -0,0 +1,122 @@ +// import needle from 'needle' +// import progress from 'request-progress' +import BackgroundTimer from 'react-native-background-timer' + +const defaultHeaders = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', +} +// var proxyUrl = "http://" + user + ":" + password + "@" + host + ":" + port; +// var proxiedRequest = request.defaults({'proxy': proxyUrl}); + +const handleRequestData = async({ + method = 'get', + headers = {}, + format = 'json', + credentials = 'omit', + cache = 'default', + ...options +}) => { + // console.log(url, options) + headers = Object.assign({ + Accept: 'application/json', + }, headers) + if (method.toLocaleLowerCase() === 'post' && !headers['Content-Type']) { + if (options.form) { + headers['Content-Type'] = 'application/x-www-form-urlencoded' + const formBody = [] + for (let [key, value] of Object.entries(options.form)) { + let encodedKey = encodeURIComponent(key) + let encodedValue = encodeURIComponent(value) + formBody.push(`${encodedKey}=${encodedValue}`) + } + options.body = formBody.join('&') + delete options.form + } else if (options.formData) { + headers['Content-Type'] = 'multipart/form-data' + const formBody = [] + for (let [key, value] of Object.entries(options.form)) { + let encodedKey = encodeURIComponent(key) + let encodedValue = encodeURIComponent(value) + formBody.push(`${encodedKey}=${encodedValue}`) + } + options.body = options.formData + delete options.formData + } else { + headers['Content-Type'] = 'application/json' + } + } + if (headers['Content-Type'] === 'application/json' && options.body) { + options.body = JSON.stringify(options.body) + } + + return { + ...options, + method, + credentials, + cache, + headers: Object.assign({}, defaultHeaders, headers), + } +} + +// https://stackoverflow.com/a/64945178 +const blobToBuffer = (blob) => { + return new Promise((resolve, reject) => { + const reader = new global.FileReader() + reader.onerror = reject + reader.onload = () => { + const data = reader.result.slice(reader.result.indexOf('base64,') + 7) + resolve(Buffer.from(data, 'base64')) + } + reader.readAsDataURL(blob) + }) +} + +export const fetchData = (url, { timeout = 15000, ...options }) => { + // console.log('---start---', url) + + const controller = new global.AbortController() + let id = BackgroundTimer.setTimeout(() => { + id = null + controller.abort() + }, timeout) + + return { + request: handleRequestData(options).then(options => { + return global.fetch(url, { + ...options, + signal: controller.signal, + }).then(resp => (options.binary ? resp.blob() : resp.text()).then(text => { + // console.log(options, headers, text) + return { + headers: resp.headers.map, + body: text, + statusCode: resp.status, + statusMessage: resp.statusText, + url: resp.url, + ok: resp.ok, + } + })).then(resp => { + if (options.binary) { + return blobToBuffer(resp.body).then(buffer => { + resp.body = buffer + return resp + }) + } else { + try { + resp.body = JSON.parse(resp.body) + } catch {} + return resp + } + }).catch(err => { + // console.log(err, err.code, err.message) + return Promise.reject(err) + }).finally(() => { + if (id == null) return + BackgroundTimer.clearTimeout(id) + }) + }), + abort() { + controller.abort() + }, + } +} diff --git a/src/core/userApi.ts b/src/core/userApi.ts index 56b208c..fddd7ee 100644 --- a/src/core/userApi.ts +++ b/src/core/userApi.ts @@ -33,9 +33,7 @@ export const importUserApi = async(script: string) => { } export const removeUserApi = async(ids: string[]) => { - console.log(ids) const list = await removeUserApiFromStore(ids) - console.log(list) action.setUserApiList(list) } diff --git a/src/lang/en_us.json b/src/lang/en_us.json index 88e5b07..df9ecdf 100644 --- a/src/lang/en_us.json +++ b/src/lang/en_us.json @@ -388,14 +388,19 @@ "timeout_exit_tip_on": "Stop playing after {time}", "toggle_source_failed": "Failed to change the source, please try to manually search for the song in other sources to play", "toggle_source_try": "Try switching to another source...", + "understand": "Already understood 👌", "user_api_allow_show_update_alert": "Allow update popups to be displayed", "user_api_btn_import": "Import", + "user_api_empty": "It’s actually empty here 😲", "user_api_import_desc": "Select custom source file", "user_api_import_failed_tip": "Import failed", + "user_api_max_tip": "A maximum of 20 sources can exist at the same time🤪\n\nIf you want to continue importing, please remove some old sources to make room.", "user_api_note": "Tip: Although we have isolated the running environment of the script as much as possible, importing scripts containing malicious behaviors may still affect your system, so please import with caution.", "user_api_readme": "Source writing instructions: ", "user_api_remove_tip": "Do you really want to remove {name}?", - "user_api_title": "Custom source management", + "user_api_title": "Custom source management (experimental)", + "user_api_update_alert": "Custom source [{name}] found new version", + "user_api_update_alert_open_url": "Open update address", "version_btn_close": "Close", "version_btn_downloading": "I am trying to download...{total}/{current} ({progress}%)", "version_btn_failed": "Retry", diff --git a/src/lang/zh_cn.json b/src/lang/zh_cn.json index 9b9be87..6995209 100644 --- a/src/lang/zh_cn.json +++ b/src/lang/zh_cn.json @@ -388,14 +388,19 @@ "timeout_exit_tip_on": "{time} 后停止播放", "toggle_source_failed": "换源失败,请尝试手动在其他源搜索该歌曲播放", "toggle_source_try": "尝试切换到其他源...", + "understand": "已了解 👌", "user_api_allow_show_update_alert": "允许显示更新弹窗", "user_api_btn_import": "导入", + "user_api_empty": "这里竟然是空的 😲", "user_api_import_desc": "选择自定义源文件", "user_api_import_failed_tip": "导入失败", + "user_api_max_tip": "最多只能同时存在20个源哦🤪\n想要继续导入的话,请先移除一些旧的源腾出位置吧", "user_api_note": "提示:虽然我们已经尽可能地隔离了脚本的运行环境,但导入包含恶意行为的脚本仍可能会影响你的系统,请谨慎导入。", "user_api_readme": "源编写说明:", "user_api_remove_tip": "你真的要移除 {name} 吗?", - "user_api_title": "自定义源管理", + "user_api_title": "自定义源管理(实验性)", + "user_api_update_alert": "自定义源 [{name}] 发现新版本", + "user_api_update_alert_open_url": "打开更新地址", "version_btn_close": "关闭", "version_btn_downloading": "正在努力下载中...{total}/{current} ({progress}%)", "version_btn_failed": "重试", diff --git a/src/navigation/navigation.ts b/src/navigation/navigation.ts index 592bfe3..53b8b4d 100644 --- a/src/navigation/navigation.ts +++ b/src/navigation/navigation.ts @@ -117,75 +117,76 @@ export function pushPlayDetailScreen(componentId: string) { }, }) */ - void InteractionManager.runAfterInteractions(() => { - const theme = themeState.theme + requestAnimationFrame(() => { + void InteractionManager.runAfterInteractions(() => { + const theme = themeState.theme - void Navigation.push(componentId, { - component: { - name: PLAY_DETAIL_SCREEN, - options: { - topBar: { - visible: false, - height: 0, - drawBehind: false, - }, - statusBar: { - drawBehind: true, - visible: true, - style: getStatusBarStyle(theme.isDark), - backgroundColor: 'transparent', - }, - // navigationBar: { - // // visible: false, - // backgroundColor: theme['c-content-background'], - // }, - layout: { - componentBackgroundColor: theme['c-content-background'], - }, - animations: { - push: { - sharedElementTransitions: [ - { - fromId: NAV_SHEAR_NATIVE_IDS.playDetail_pic, - toId: NAV_SHEAR_NATIVE_IDS.playDetail_pic, - interpolation: { type: 'spring' }, - }, - ], - elementTransitions: [ - { - id: NAV_SHEAR_NATIVE_IDS.playDetail_header, - alpha: { - from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1 - duration: 300, + void Navigation.push(componentId, { + component: { + name: PLAY_DETAIL_SCREEN, + options: { + topBar: { + visible: false, + height: 0, + drawBehind: false, + }, + statusBar: { + drawBehind: true, + visible: true, + style: getStatusBarStyle(theme.isDark), + backgroundColor: 'transparent', + }, + // navigationBar: { + // // visible: false, + // backgroundColor: theme['c-content-background'], + // }, + layout: { + componentBackgroundColor: theme['c-content-background'], + }, + animations: { + push: { + sharedElementTransitions: [ + { + fromId: NAV_SHEAR_NATIVE_IDS.playDetail_pic, + toId: NAV_SHEAR_NATIVE_IDS.playDetail_pic, + interpolation: { type: 'spring' }, }, - translationY: { - from: -32, // Animate translationY from 16dp to 0dp - duration: 300, + ], + elementTransitions: [ + { + id: NAV_SHEAR_NATIVE_IDS.playDetail_header, + alpha: { + from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1 + duration: 300, + }, + translationY: { + from: -32, // Animate translationY from 16dp to 0dp + duration: 300, + }, }, - }, - { - id: NAV_SHEAR_NATIVE_IDS.playDetail_pageIndicator, - alpha: { - from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1 - duration: 300, + { + id: NAV_SHEAR_NATIVE_IDS.playDetail_pageIndicator, + alpha: { + from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1 + duration: 300, + }, + translationX: { + from: -32, // Animate translationY from 16dp to 0dp + duration: 300, + }, }, - translationX: { - from: -32, // Animate translationY from 16dp to 0dp - duration: 300, + { + id: NAV_SHEAR_NATIVE_IDS.playDetail_player, + alpha: { + from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1 + duration: 300, + }, + translationY: { + from: 32, // Animate translationY from 16dp to 0dp + duration: 300, + }, }, - }, - { - id: NAV_SHEAR_NATIVE_IDS.playDetail_player, - alpha: { - from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1 - duration: 300, - }, - translationY: { - from: 32, // Animate translationY from 16dp to 0dp - duration: 300, - }, - }, - ], + ], // content: { // translationX: { // from: windowSizeTools.getSize().width, @@ -193,70 +194,72 @@ export function pushPlayDetailScreen(componentId: string) { // duration: 300, // }, // }, - }, - pop: { - content: { - translationX: { - from: 0, - to: windowSizeTools.getSize().width, - duration: 300, + }, + pop: { + content: { + translationX: { + from: 0, + to: windowSizeTools.getSize().width, + duration: 300, + }, }, }, }, }, }, - }, + }) }) }) } export function pushSonglistDetailScreen(componentId: string, id: string) { const theme = themeState.theme - void InteractionManager.runAfterInteractions(() => { - void Navigation.push(componentId, { - component: { - name: SONGLIST_DETAIL_SCREEN, - options: { - topBar: { - visible: false, - height: 0, - drawBehind: false, - }, - statusBar: { - drawBehind: true, - visible: true, - style: getStatusBarStyle(theme.isDark), - backgroundColor: 'transparent', - }, - // navigationBar: { - // // visible: false, - // backgroundColor: theme['c-content-background'], - // }, - layout: { - componentBackgroundColor: theme['c-content-background'], - }, - animations: { - push: { - sharedElementTransitions: [ - { - fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${id}`, - toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${id}`, - interpolation: { type: 'spring' }, - }, - ], - elementTransitions: [ - { - id: NAV_SHEAR_NATIVE_IDS.songlistDetail_title, - alpha: { - from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1 - duration: 300, + requestAnimationFrame(() => { + void InteractionManager.runAfterInteractions(() => { + void Navigation.push(componentId, { + component: { + name: SONGLIST_DETAIL_SCREEN, + options: { + topBar: { + visible: false, + height: 0, + drawBehind: false, + }, + statusBar: { + drawBehind: true, + visible: true, + style: getStatusBarStyle(theme.isDark), + backgroundColor: 'transparent', + }, + // navigationBar: { + // // visible: false, + // backgroundColor: theme['c-content-background'], + // }, + layout: { + componentBackgroundColor: theme['c-content-background'], + }, + animations: { + push: { + sharedElementTransitions: [ + { + fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${id}`, + toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${id}`, + interpolation: { type: 'spring' }, }, - translationX: { - from: 16, // Animate translationX from 16dp to 0dp - duration: 300, + ], + elementTransitions: [ + { + id: NAV_SHEAR_NATIVE_IDS.songlistDetail_title, + alpha: { + from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1 + duration: 300, + }, + translationX: { + from: 16, // Animate translationX from 16dp to 0dp + duration: 300, + }, }, - }, - ], + ], // content: { // scaleX: { // from: 1.2, @@ -274,28 +277,28 @@ export function pushSonglistDetailScreen(componentId: string, id: string) { // duration: 200, // }, // }, - }, - pop: { - sharedElementTransitions: [ - { - fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${id}`, - toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${id}`, - interpolation: { type: 'spring' }, - }, - ], - elementTransitions: [ - { - id: NAV_SHEAR_NATIVE_IDS.songlistDetail_title, - alpha: { - to: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1 - duration: 300, + }, + pop: { + sharedElementTransitions: [ + { + fromId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_to_${id}`, + toId: `${NAV_SHEAR_NATIVE_IDS.songlistDetail_pic}_from_${id}`, + interpolation: { type: 'spring' }, }, - translationX: { - to: 16, // Animate translationX from 16dp to 0dp - duration: 300, + ], + elementTransitions: [ + { + id: NAV_SHEAR_NATIVE_IDS.songlistDetail_title, + alpha: { + to: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1 + duration: 300, + }, + translationX: { + to: 16, // Animate translationX from 16dp to 0dp + duration: 300, + }, }, - }, - ], + ], // content: { // alpha: { // from: 1, @@ -303,10 +306,11 @@ export function pushSonglistDetailScreen(componentId: string, id: string) { // duration: 200, // }, // }, + }, }, }, }, - }, + }) }) }) } @@ -343,53 +347,55 @@ export function pushCommentScreen(componentId: string) { }, }) */ - void InteractionManager.runAfterInteractions(() => { - const theme = themeState.theme + requestAnimationFrame(() => { + void InteractionManager.runAfterInteractions(() => { + const theme = themeState.theme - void Navigation.push(componentId, { - component: { - name: COMMENT_SCREEN, - options: { - topBar: { - visible: false, - height: 0, - drawBehind: false, - }, - statusBar: { - drawBehind: true, - visible: true, - style: getStatusBarStyle(theme.isDark), - backgroundColor: 'transparent', - }, - // navigationBar: { - // // visible: false, - // backgroundColor: theme['c-content-background'], - // }, - layout: { - componentBackgroundColor: theme['c-content-background'], - }, - animations: { - push: { - content: { - translationX: { - from: windowSizeTools.getSize().width, - to: 0, - duration: 300, + void Navigation.push(componentId, { + component: { + name: COMMENT_SCREEN, + options: { + topBar: { + visible: false, + height: 0, + drawBehind: false, + }, + statusBar: { + drawBehind: true, + visible: true, + style: getStatusBarStyle(theme.isDark), + backgroundColor: 'transparent', + }, + // navigationBar: { + // // visible: false, + // backgroundColor: theme['c-content-background'], + // }, + layout: { + componentBackgroundColor: theme['c-content-background'], + }, + animations: { + push: { + content: { + translationX: { + from: windowSizeTools.getSize().width, + to: 0, + duration: 300, + }, }, }, - }, - pop: { - content: { - translationX: { - from: 0, - to: windowSizeTools.getSize().width, - duration: 300, + pop: { + content: { + translationX: { + from: 0, + to: windowSizeTools.getSize().width, + duration: 300, + }, }, }, }, }, }, - }, + }) }) }) } diff --git a/src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/List.tsx b/src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/List.tsx index 71e1a10..253ef3c 100644 --- a/src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/List.tsx +++ b/src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/List.tsx @@ -10,6 +10,9 @@ import { removeUserApi, setUserApiAllowShowUpdateAlert } from '@/core/userApi' import { BorderRadius } from '@/theme' import CheckBox from '@/components/common/CheckBox' import { Icon } from '@/components/common/Icon' +import settingState from '@/store/setting/state' +import apiSourceInfo from '@/utils/musicSdk/api-source-info' +import { setApiSource } from '@/core/apiSource' const ListItem = ({ item, activeId, onRemove, onChangeAllowShowUpdateAlert }: { @@ -29,12 +32,12 @@ const ListItem = ({ item, activeId, onRemove, onChangeAllowShowUpdateAlert }: { return ( - - {item.name} + + {item.name} {item.description} - + - + @@ -55,6 +58,8 @@ export interface UserApiEditModalType { export default () => { const userApiList = useUserApiList() const apiSource = useSettingValue('common.apiSource') + const theme = useTheme() + const t = useI18n() const handleRemove = useCallback(async(id: string, name: string) => { const confirm = await confirmDialog({ @@ -64,7 +69,12 @@ export default () => { bgClose: false, }) if (!confirm) return - void removeUserApi([id]) + void removeUserApi([id]).finally(() => { + if (settingState.setting['common.apiSource'] == id) { + let backApi = apiSourceInfo.find(api => !api.disabled) + setApiSource(backApi?.id ?? '') + } + }) }, []) const handleChangeAllowShowUpdateAlert = useCallback((id: string, enabled: boolean) => { void setUserApiAllowShowUpdateAlert(id, enabled) @@ -74,8 +84,9 @@ export default () => { true}> { - userApiList.map((item) => { - return ( + userApiList.length + ? userApiList.map((item) => { + return ( { onRemove={handleRemove} onChangeAllowShowUpdateAlert={handleChangeAllowShowUpdateAlert} /> - ) - }) + ) + }) + : {t('user_api_empty')} } @@ -107,6 +119,15 @@ const styles = createStyle({ alignItems: 'center', justifyContent: 'space-between', }, + listItemLeft: { + paddingRight: 10, + flex: 1, + gap: 2, + }, + listItemRight: { + flex: 0, + // backgroundColor: 'rgba(0, 0, 0, 0.2)', + }, // btns: { // padding: 5, // }, @@ -114,6 +135,11 @@ const styles = createStyle({ padding: 10, // backgroundColor: 'rgba(0, 0, 0, 0.2)', }, + tipText: { + textAlign: 'center', + marginTop: 25, + marginBottom: 15, + }, }) diff --git a/src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/index.tsx b/src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/index.tsx index 27e5765..ef86e64 100644 --- a/src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/index.tsx +++ b/src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/index.tsx @@ -1,13 +1,14 @@ import { useRef, useImperativeHandle, forwardRef, useState } from 'react' import Text from '@/components/common/Text' import { View, TouchableOpacity } from 'react-native' -import { createStyle, openUrl } from '@/utils/tools' +import { createStyle, openUrl, tipDialog } from '@/utils/tools' import { useTheme } from '@/store/theme/hook' import { useI18n } from '@/lang' import Dialog, { type DialogType } from '@/components/common/Dialog' import Button from '@/components/common/Button' import List from './List' import ScriptImportExport, { type ScriptImportExportType } from './ScriptImportExport' +import { state } from '@/store/userApi' // interface UrlInputType { // setText: (text: string) => void @@ -96,6 +97,13 @@ export default forwardRef((props, ref) => { dialogRef.current?.setVisible(false) } const handleImport = () => { + if (state.list.length > 20) { + void tipDialog({ + message: t('user_api_max_tip'), + btnText: t('ok'), + }) + return + } scriptImportExportRef.current?.import() } const openFAQPage = () => { @@ -108,7 +116,7 @@ export default forwardRef((props, ref) => { {/* */} - {t('user_api_title')} + {t('user_api_title')} @@ -169,10 +177,7 @@ const styles = createStyle({ }, btn: { flex: 1, - paddingTop: 8, - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 10, + padding: 10, alignItems: 'center', borderRadius: 4, marginRight: 15, diff --git a/src/screens/Home/Views/Setting/settings/Other/DislikeEditModal.tsx b/src/screens/Home/Views/Setting/settings/Other/DislikeEditModal.tsx index daff2a4..a8c1533 100644 --- a/src/screens/Home/Views/Setting/settings/Other/DislikeEditModal.tsx +++ b/src/screens/Home/Views/Setting/settings/Other/DislikeEditModal.tsx @@ -45,7 +45,7 @@ const RuleInput = forwardRef((props, ref) => { multiline textAlignVertical="top" placeholder={t('setting_dislike_list_input_tip')} - size={12} + size={13} style={{ ...styles.input, height, backgroundColor: theme['c-primary-input-background'] }} /> @@ -106,7 +106,7 @@ export default forwardRef(({ onSave - {t('setting_dislike_list_tips')} + {t('setting_dislike_list_tips')}