asList(new UtilsModule(reactContext));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/launch_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/drawable-hdpi/launch_screen.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-land-hdpi/launch_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/drawable-land-hdpi/launch_screen.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-land-mdpi/launch_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/drawable-land-mdpi/launch_screen.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-land-xhdpi/launch_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/drawable-land-xhdpi/launch_screen.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-land-xxhdpi/launch_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/drawable-land-xxhdpi/launch_screen.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-land-xxxhdpi/launch_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/drawable-land-xxxhdpi/launch_screen.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/launch_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/drawable-mdpi/launch_screen.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/launch_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/drawable-xhdpi/launch_screen.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/launch_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/drawable-xxhdpi/launch_screen.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxxhdpi/launch_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/drawable-xxxhdpi/launch_screen.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/rounded_corner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | IKUN Music
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/app/src/release/java/com/ikunshare/music/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.ikunshare.music.mobile;
8 |
9 | import android.content.Context;
10 | import com.facebook.react.ReactInstanceManager;
11 |
12 | /**
13 | * Class responsible of loading Flipper inside your React Native application. This is the release
14 | * flavor of it so it's empty as we don't want to load Flipper.
15 | */
16 | public class ReactNativeFlipper {
17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
18 | // Do nothing as we don't want to initialize Flipper on Release.
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = "35.0.0"
6 | minSdkVersion = 21
7 | compileSdkVersion = 35
8 | targetSdkVersion = 29
9 |
10 | ndkVersion = "26.1.10909125"
11 | kotlinVersion = "1.9.24" // Or any version above 1.3.x
12 | RNNKotlinVersion = kotlinVersion
13 |
14 | // https://github.com/DylanVann/react-native-fast-image/blob/9ab80fcd570b7f56da66ab20e52c9a35934067c9/docs/app-glide-module.md
15 | excludeAppGlideModule = true
16 | }
17 | repositories {
18 | google()
19 | mavenCentral()
20 | }
21 | dependencies {
22 | classpath('com.android.tools.build:gradle:8.6.1')
23 | classpath("com.facebook.react:react-native-gradle-plugin")
24 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
25 | }
26 | }
27 |
28 | apply plugin: "com.facebook.react.rootproject"
29 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'com.ikunshare.music.mobile'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':app'
4 | includeBuild('../node_modules/@react-native/gradle-plugin')
5 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "IKUN Music",
3 | "displayName": "洛雪音乐助手"
4 | }
5 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:@react-native/babel-preset'],
3 | plugins: [
4 | '@babel/plugin-proposal-export-namespace-from',
5 | [
6 | 'module-resolver',
7 | {
8 | root: ['.'],
9 | extensions: [
10 | '.android.ts',
11 | '.ios.ts',
12 | '.android.tsx',
13 | '.ios.tsx',
14 | '.tsx',
15 | '.ts',
16 | '.android.js',
17 | '.ios.js',
18 | '.android.jsx',
19 | '.ios.jsx',
20 | '.jsx',
21 | '.js',
22 | '.json',
23 | ],
24 | alias: {
25 | '@': './src',
26 | // '@config': './src/config',
27 | // '@store': './src/store',
28 | // '@components': './src/components',
29 | // '@navigation': './src/navigation',
30 | // '@screens': './src/screens',
31 | // '@theme': './src/theme',
32 | },
33 | },
34 | ],
35 | ],
36 | }
37 |
--------------------------------------------------------------------------------
/dependencies-patch.js:
--------------------------------------------------------------------------------
1 | // 修补依赖源码以使构建的依赖恢复正常工作
2 |
3 | const fs = require('node:fs')
4 | const path = require('node:path')
5 |
6 | const rootPath = path.join(__dirname, './')
7 |
8 | const patchs = []
9 |
10 | ;(async () => {
11 | for (const [filePath, fromStr, toStr] of patchs) {
12 | console.log(`Patching ${filePath.replace(rootPath, '')}`)
13 | try {
14 | const file = (await fs.promises.readFile(filePath)).toString()
15 | await fs.promises.writeFile(filePath, file.replace(fromStr, toStr))
16 | } catch (err) {
17 | console.error(`Patch ${filePath.replace(rootPath, '')} failed: ${err.message}`)
18 | }
19 | }
20 | console.log('\nDependencies patch finished.\n')
21 | })()
22 |
--------------------------------------------------------------------------------
/doc/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/doc/images/icon.png
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 | import './shim'
5 | import './src/app'
6 |
--------------------------------------------------------------------------------
/metro.config.js:
--------------------------------------------------------------------------------
1 | const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')
2 |
3 | /**
4 | * Metro configuration
5 | * https://facebook.github.io/metro/docs/configuration
6 | *
7 | * @type {import('metro-config').MetroConfig}
8 | */
9 | const config = {
10 | resolver: {
11 | extraNodeModules: {
12 | // crypto: require.resolve('react-native-quick-crypto'),
13 | // stream: require.resolve('stream-browserify'),
14 | buffer: require.resolve('@craftzdog/react-native-buffer'),
15 | },
16 | },
17 | }
18 |
19 | module.exports = mergeConfig(getDefaultConfig(__dirname), config)
20 |
--------------------------------------------------------------------------------
/publish/changeLog.md:
--------------------------------------------------------------------------------
1 | ### 修复
2 |
3 | - 修复 tx 歌单搜索名字、描述出现乱码的问题
4 | - 修复解析某些本地歌词文件时出现乱码的问题(#694)
5 |
6 | ### 优化
7 |
8 | - 优化软件文案编排(#701, #703, @3gf8jv4dv)
9 |
10 | ### 其他
11 |
12 | - 更新项目文档(@3gf8jv4dv)
13 |
--------------------------------------------------------------------------------
/shim.js:
--------------------------------------------------------------------------------
1 | global.Buffer = require('buffer').Buffer
2 |
--------------------------------------------------------------------------------
/src/components/MetadataEditModal/InputItem.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import { StyleSheet, View } from 'react-native'
4 | import type { InputProps } from '@/components/common/Input'
5 | import Input from '@/components/common/Input'
6 | import { useTheme } from '@/store/theme/hook'
7 | import Text from '@/components/common/Text'
8 |
9 | export interface InputItemProps extends InputProps {
10 | value: string
11 | label: string
12 | onChanged: (text: string) => void
13 | }
14 |
15 | export default memo(({ value, label, onChanged, ...props }: InputItemProps) => {
16 | const theme = useTheme()
17 | return (
18 |
19 |
20 | {label}
21 |
22 |
28 |
29 | )
30 | })
31 |
32 | const styles = StyleSheet.create({
33 | container: {
34 | // paddingLeft: 25,
35 | marginBottom: 15,
36 | },
37 | label: {
38 | marginBottom: 2,
39 | },
40 | input: {
41 | flexGrow: 1,
42 | flexShrink: 1,
43 | // borderRadius: 4,
44 | // paddingTop: 3,
45 | // paddingBottom: 3,
46 | // maxWidth: 300,
47 | },
48 | })
49 |
--------------------------------------------------------------------------------
/src/components/MusicAddModal/Title.tsx:
--------------------------------------------------------------------------------
1 | import Text from '@/components/common/Text'
2 | import { createStyle } from '@/utils/tools'
3 | import { useTheme } from '@/store/theme/hook'
4 | import { useI18n } from '@/lang'
5 |
6 | export default ({ musicInfo, isMove }: { musicInfo: LX.Music.MusicInfo; isMove: boolean }) => {
7 | const theme = useTheme()
8 | const t = useI18n()
9 | return (
10 |
11 | {t(isMove ? 'list_add_title_first_move' : 'list_add_title_first_add')}{' '}
12 | {musicInfo.name} {t('list_add_title_last')}
13 |
14 | )
15 | }
16 |
17 | const styles = createStyle({
18 | title: {
19 | textAlign: 'center',
20 | paddingTop: 15,
21 | paddingBottom: 15,
22 | },
23 | })
24 |
--------------------------------------------------------------------------------
/src/components/MusicAddModal/index.tsx:
--------------------------------------------------------------------------------
1 | import { useRef, useImperativeHandle, forwardRef, useState } from 'react'
2 | import Modal, {
3 | type MusicAddModalType as ModalType,
4 | type MusicAddModalProps as ModalProps,
5 | type SelectInfo,
6 | } from './MusicAddModal'
7 |
8 | export interface MusicAddModalProps {
9 | onAdded?: ModalProps['onAdded']
10 | }
11 | export interface MusicAddModalType {
12 | show: (info: SelectInfo) => void
13 | }
14 |
15 | export default forwardRef(({ onAdded }, ref) => {
16 | const musicAddModalRef = useRef(null)
17 | const [visible, setVisible] = useState(false)
18 |
19 | useImperativeHandle(ref, () => ({
20 | show(listInfo) {
21 | if (visible) musicAddModalRef.current?.show(listInfo)
22 | else {
23 | setVisible(true)
24 | requestAnimationFrame(() => {
25 | musicAddModalRef.current?.show(listInfo)
26 | })
27 | }
28 | },
29 | }))
30 |
31 | return visible ? : null
32 | })
33 |
--------------------------------------------------------------------------------
/src/components/MusicMultiAddModal/Title.tsx:
--------------------------------------------------------------------------------
1 | import Text from '@/components/common/Text'
2 | import { createStyle } from '@/utils/tools'
3 | import { useTheme } from '@/store/theme/hook'
4 | import { useI18n } from '@/lang'
5 |
6 | export default ({
7 | selectedList,
8 | isMove,
9 | }: {
10 | selectedList: LX.Music.MusicInfo[]
11 | isMove: boolean
12 | }) => {
13 | const theme = useTheme()
14 | const t = useI18n()
15 | return (
16 |
17 | {t(isMove ? 'list_multi_add_title_first_move' : 'list_multi_add_title_first_add')}{' '}
18 |
19 | {selectedList.length}
20 | {' '}
21 | {t('list_multi_add_title_last')}
22 |
23 | )
24 | }
25 |
26 | const styles = createStyle({
27 | title: {
28 | textAlign: 'center',
29 | paddingTop: 15,
30 | paddingBottom: 15,
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/src/components/MusicMultiAddModal/index.tsx:
--------------------------------------------------------------------------------
1 | import { useRef, useImperativeHandle, forwardRef, useState } from 'react'
2 | import Modal, {
3 | type MusicMultiAddModalType as ModalType,
4 | type MusicMultiAddModalProps as ModalProps,
5 | type SelectInfo,
6 | } from './MusicMultiAddModal'
7 |
8 | export interface MusicAddModalProps {
9 | onAdded?: ModalProps['onAdded']
10 | }
11 | export interface MusicMultiAddModalType {
12 | show: (info: SelectInfo) => void
13 | }
14 |
15 | export default forwardRef(({ onAdded }, ref) => {
16 | const musicMultiAddModalRef = useRef(null)
17 | const [visible, setVisible] = useState(false)
18 |
19 | useImperativeHandle(ref, () => ({
20 | show(listInfo) {
21 | if (visible) musicMultiAddModalRef.current?.show(listInfo)
22 | else {
23 | setVisible(true)
24 | requestAnimationFrame(() => {
25 | musicMultiAddModalRef.current?.show(listInfo)
26 | })
27 | }
28 | },
29 | }))
30 |
31 | return visible ? : null
32 | })
33 |
--------------------------------------------------------------------------------
/src/components/SearchTipList/List.tsx:
--------------------------------------------------------------------------------
1 | import { useState, forwardRef, useImperativeHandle, type Ref } from 'react'
2 | import { FlatList, type FlatListProps } from 'react-native'
3 |
4 | // import InsetShadow from 'react-native-inset-shadow'
5 |
6 | export type ItemT = FlatListProps['data']
7 |
8 | export type ListProps = Pick<
9 | FlatListProps,
10 | | 'renderItem'
11 | | 'maxToRenderPerBatch'
12 | | 'windowSize'
13 | | 'initialNumToRender'
14 | | 'keyExtractor'
15 | | 'getItemLayout'
16 | | 'keyboardShouldPersistTaps'
17 | >
18 |
19 | export interface ListType {
20 | setList: (list: T[]) => void
21 | }
22 |
23 | const List = >(props: ListProps, ref: Ref>) => {
24 | const [list, setList] = useState([])
25 | useImperativeHandle(ref, () => ({
26 | setList(list) {
27 | setList(list)
28 | },
29 | }))
30 |
31 | return (
32 |
38 | )
39 | }
40 |
41 | export default forwardRef(List) as (
42 | p: ListProps & { ref?: Ref> }
43 | ) => JSX.Element | null
44 |
--------------------------------------------------------------------------------
/src/components/common/ButtonPrimary.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import Button, { type BtnProps } from '@/components/common/Button'
4 | import Text from '@/components/common/Text'
5 | import { useTheme } from '@/store/theme/hook'
6 | import { createStyle } from '@/utils/tools'
7 |
8 | export interface ButtonProps extends BtnProps {
9 | size?: number
10 | }
11 |
12 | export default memo(({ disabled, size = 14, onPress, children }: ButtonProps) => {
13 | const theme = useTheme()
14 |
15 | return (
16 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | button: {
30 | paddingHorizontal: 10,
31 | paddingVertical: 5,
32 | borderRadius: 4,
33 | marginRight: 10,
34 | },
35 | })
36 |
--------------------------------------------------------------------------------
/src/components/common/FileSelect.tsx:
--------------------------------------------------------------------------------
1 | import ChoosePath, { type ReadOptions, type ChoosePathType } from '@/components/common/ChoosePath'
2 | import { forwardRef, useImperativeHandle, useRef, useState } from 'react'
3 |
4 | export interface FileSelectType {
5 | show: (options: ReadOptions, onSelect: typeof noop) => void
6 | }
7 | const noop = (path: string) => {}
8 | export default forwardRef((props, ref) => {
9 | const [visible, setVisible] = useState(false)
10 | const choosePathRef = useRef(null)
11 | const onSelectRef = useRef(noop)
12 | // console.log('render import export')
13 |
14 | useImperativeHandle(ref, () => ({
15 | show(options, onSelect) {
16 | onSelectRef.current = onSelect ?? noop
17 | if (visible) {
18 | choosePathRef.current?.show(options)
19 | } else {
20 | setVisible(true)
21 | requestAnimationFrame(() => {
22 | choosePathRef.current?.show(options)
23 | })
24 | }
25 | },
26 | }))
27 |
28 | return visible ? : null
29 | })
30 |
--------------------------------------------------------------------------------
/src/components/common/Loading.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import { createStyle } from '@/utils/tools'
4 | import { useTheme } from '@/store/theme/hook'
5 | import { ActivityIndicator, StyleSheet, type ActivityIndicatorProps } from 'react-native'
6 | import { setSpText } from '@/utils/pixelRatio'
7 | import Text from './Text'
8 |
9 | export interface LoadingProps extends Omit {
10 | size?: number
11 | label?: string
12 | }
13 |
14 | const LoadingLabel = ({ style, label, ...props }: LoadingProps) => {
15 | return (
16 | <>
17 |
18 |
19 | {label}
20 |
21 | >
22 | )
23 | }
24 |
25 | export default memo(({ size = 15, label, ...props }: LoadingProps) => {
26 | const theme = useTheme()
27 |
28 | return label ? (
29 |
30 | ) : (
31 |
32 | )
33 | })
34 |
35 | const styles = createStyle({
36 | loadingLabel: {
37 | marginRight: 6,
38 | },
39 | })
40 |
--------------------------------------------------------------------------------
/src/components/common/StatusBar.tsx:
--------------------------------------------------------------------------------
1 | import { useTheme } from '@/store/theme/hook'
2 | import { StatusBar as RNStatusBar } from 'react-native'
3 |
4 | const StatusBar = function () {
5 | const theme = useTheme()
6 | const statusBarStyle = theme.isDark ? 'light-content' : 'dark-content'
7 | return (
8 |
9 | )
10 | }
11 |
12 | StatusBar.currentHeight = RNStatusBar.currentHeight ?? 0
13 | StatusBar.setBarStyle = RNStatusBar.setBarStyle
14 |
15 | export default StatusBar
16 |
--------------------------------------------------------------------------------
/src/components/player/PlayerBar/components/Status.tsx:
--------------------------------------------------------------------------------
1 | import { useLrcPlay } from '@/plugins/lyric'
2 | import { useIsPlay, useStatusText } from '@/store/player/hook'
3 | // import { createStyle } from '@/utils/tools'
4 | import Text from '@/components/common/Text'
5 |
6 | export default ({ autoUpdate }: { autoUpdate: boolean }) => {
7 | const { text } = useLrcPlay(autoUpdate)
8 | const statusText = useStatusText()
9 | const isPlay = useIsPlay()
10 | // console.log('render status')
11 |
12 | const status = isPlay ? text : statusText
13 |
14 | return (
15 |
16 | {status}
17 |
18 | )
19 | }
20 |
21 | // const styles = createStyle({
22 | // text: {
23 | // // fontSize: 10,
24 | // // lineHeight: 18,
25 | // // height: 18,
26 | // // height: '100%',
27 | // // backgroundColor: 'rgba(0,0,0,0.2)',
28 | // },
29 | // })
30 |
--------------------------------------------------------------------------------
/src/config/index.js:
--------------------------------------------------------------------------------
1 | // import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'
2 | import defaultUrl from '@/resources/medias/Silence02s.mp3'
3 | import notificationIcon from '@/resources/images/notification.xhdpi.png'
4 | // const defaultUrl = resolveAssetSource(resourceDefaultUrl).uri
5 |
6 | export { defaultUrl, notificationIcon }
7 | // export const defaultUrl = require('@/resources/medias/Silence02s.mp3')
8 |
--------------------------------------------------------------------------------
/src/core/dislikeList.ts:
--------------------------------------------------------------------------------
1 | import { action } from '@/store/dislikeList'
2 |
3 | export const addDislikeInfo = async (infos: LX.Dislike.DislikeMusicInfo[]) => {
4 | await global.dislike_event.dislike_music_add(infos)
5 | }
6 |
7 | export const overwirteDislikeInfo = async (rules: string) => {
8 | await global.dislike_event.dislike_data_overwrite(rules)
9 | }
10 |
11 | export const clearDislikeInfo = async () => {
12 | await global.dislike_event.dislike_music_clear()
13 | }
14 |
15 | export const hasDislike = (info: LX.Music.MusicInfo | LX.Download.ListItem | null) => {
16 | if (!info) return false
17 | return action.hasDislike(info)
18 | }
19 |
20 | export const setDislikeInfo = (info: LX.Dislike.DislikeInfo) => {
21 | action.setDislikeInfo(info)
22 | }
23 |
24 | export { getDislikeInfo } from '@/utils/dislikeManage'
25 |
--------------------------------------------------------------------------------
/src/core/init/deeplink/playerAction.ts:
--------------------------------------------------------------------------------
1 | import {
2 | collectMusic,
3 | dislikeMusic,
4 | pause,
5 | play,
6 | playNext,
7 | playPrev,
8 | togglePlay,
9 | uncollectMusic,
10 | } from '@/core/player/player'
11 |
12 | export type PlayerAction =
13 | | 'play'
14 | | 'pause'
15 | | 'skipNext'
16 | | 'skipPrev'
17 | | 'togglePlay'
18 | | 'collect'
19 | | 'uncollect'
20 | | 'dislike'
21 |
22 | export const handlePlayerAction = async (action: PlayerAction) => {
23 | switch (action) {
24 | case 'play':
25 | play()
26 | break
27 | case 'pause':
28 | void pause()
29 | break
30 | case 'skipNext':
31 | void playNext()
32 | break
33 | case 'skipPrev':
34 | void playPrev()
35 | break
36 | case 'togglePlay':
37 | togglePlay()
38 | break
39 | case 'collect':
40 | collectMusic()
41 | break
42 | case 'uncollect':
43 | uncollectMusic()
44 | break
45 | case 'dislike':
46 | void dislikeMusic()
47 | break
48 | // default: throw new Error('Unknown action: ' + (action as any ?? ''))
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/core/init/i18n.ts:
--------------------------------------------------------------------------------
1 | import { createI18n } from '@/lang/i18n'
2 | import type { I18n } from '@/lang/i18n'
3 | import { getDeviceLanguage } from '@/utils/tools'
4 | import { setLanguage, updateSetting } from '@/core/common'
5 |
6 | export default async (setting: LX.AppSetting) => {
7 | let lang = setting['common.langId']
8 |
9 | global.i18n = createI18n()
10 |
11 | if (!lang || !global.i18n.availableLocales.includes(lang)) {
12 | const deviceLanguage = (await getDeviceLanguage()).toLowerCase()
13 | if (
14 | typeof deviceLanguage == 'string' &&
15 | global.i18n.availableLocales.includes(deviceLanguage as I18n['locale'])
16 | ) {
17 | lang = deviceLanguage as I18n['locale']
18 | } else {
19 | lang = 'en_us'
20 | }
21 | updateSetting({ 'common.langId': lang })
22 | }
23 | setLanguage(lang)
24 | }
25 |
--------------------------------------------------------------------------------
/src/core/init/player/index.ts:
--------------------------------------------------------------------------------
1 | import initPlayer from './player'
2 | import initPlayInfo from './playInfo'
3 | import initPlayStatus from './playStatus'
4 | import initPlayerEvent from './playerEvent'
5 | import initWatchList from './watchList'
6 | import initPlayProgress from './playProgress'
7 | import initPreloadNextMusic from './preloadNextMusic'
8 | import initLyric from './lyric'
9 |
10 | export default async (setting: LX.AppSetting) => {
11 | await initPlayer(setting)
12 | await initLyric(setting)
13 | await initPlayInfo(setting)
14 | initPlayStatus()
15 | initPlayerEvent()
16 | initWatchList()
17 | initPlayProgress()
18 | initPreloadNextMusic()
19 | }
20 |
--------------------------------------------------------------------------------
/src/core/init/sync.ts:
--------------------------------------------------------------------------------
1 | import { connectServer } from '@/plugins/sync'
2 | import { updateSetting } from '@/core/common'
3 | import { getSyncHost } from '@/plugins/sync/data'
4 |
5 | export default async (setting: LX.AppSetting) => {
6 | if (!setting['sync.enable']) return
7 |
8 | const host = await getSyncHost()
9 | // console.log(host)
10 | if (!host) {
11 | updateSetting({ 'sync.enable': false })
12 | return
13 | }
14 | void connectServer(host)
15 | }
16 |
--------------------------------------------------------------------------------
/src/core/player/playStatus.ts:
--------------------------------------------------------------------------------
1 | import playerActions from '@/store/player/action'
2 | import playerState from '@/store/player/state'
3 |
4 | export const setIsPlay = (val: boolean) => {
5 | if (playerState.isPlay == val) return
6 | playerActions.setIsPlay(val)
7 | }
8 |
9 | export const setStatusText = (val: string) => {
10 | if (playerState.statusText == val) return
11 | playerActions.setStatusText(val)
12 | }
13 |
--------------------------------------------------------------------------------
/src/core/player/playedList.ts:
--------------------------------------------------------------------------------
1 | import playerActions from '@/store/player/action'
2 |
3 | /**
4 | * 将歌曲添加到已播放列表
5 | * @param playMusicInfo playMusicInfo对象
6 | */
7 | export const addPlayedList = (playMusicInfo: LX.Player.PlayMusicInfo) => {
8 | playerActions.addPlayedList(playMusicInfo)
9 | }
10 | /**
11 | * 将歌曲从已播放列表移除
12 | * @param index 歌曲位置
13 | */
14 | export const removePlayedList = (index: number) => {
15 | playerActions.removePlayedList(index)
16 | }
17 | /**
18 | * 清空已播放列表
19 | */
20 | export const clearPlayedList = () => {
21 | playerActions.clearPlayedList()
22 | }
23 |
--------------------------------------------------------------------------------
/src/core/player/progress.ts:
--------------------------------------------------------------------------------
1 | import playerActions from '@/store/player/action'
2 |
3 | export const setNowPlayTime = (time: number) => {
4 | playerActions.setNowPlayTime(time)
5 | }
6 |
7 | export const setMaxplayTime = (time: number) => {
8 | playerActions.setMaxplayTime(time)
9 | }
10 |
11 | export const setProgress = (currentTime: number, totalTime: number) => {
12 | playerActions.setProgress(currentTime, totalTime)
13 | }
14 |
--------------------------------------------------------------------------------
/src/core/player/tempPlayList.ts:
--------------------------------------------------------------------------------
1 | import playerActions from '@/store/player/action'
2 | import playerState from '@/store/player/state'
3 | import { playNext } from './player'
4 |
5 | /**
6 | * 添加歌曲到稍后播放列表
7 | * @param list 歌曲列表
8 | */
9 | export const addTempPlayList = (list: LX.Player.TempPlayListItem[]) => {
10 | playerActions.addTempPlayList(list)
11 | if (!playerState.playMusicInfo.musicInfo) void playNext()
12 | }
13 | /**
14 | * 从稍后播放列表移除歌曲
15 | * @param index 歌曲位置
16 | */
17 | export const removeTempPlayList = (index: number) => {
18 | playerActions.removeTempPlayList(index)
19 | }
20 | /**
21 | * 清空稍后播放列表
22 | */
23 | export const clearTempPlayeList = () => {
24 | playerActions.clearTempPlayeList()
25 | }
26 |
--------------------------------------------------------------------------------
/src/core/theme.ts:
--------------------------------------------------------------------------------
1 | import themeActions from '@/store/theme/action'
2 | import { getTheme } from '@/theme/themes'
3 | import { updateSetting } from './common'
4 | import themeState from '@/store/theme/state'
5 |
6 | export const setShouldUseDarkColors = (shouldUseDarkColors: boolean) => {
7 | themeActions.setShouldUseDarkColors(shouldUseDarkColors)
8 | }
9 |
10 | export const applyTheme = (theme: LX.Theme) => {
11 | themeActions.setTheme(theme)
12 | }
13 |
14 | export const setTheme = (id: string) => {
15 | updateSetting({ 'theme.id': id })
16 | void getTheme().then((theme) => {
17 | if (theme.id == themeState.theme.id) return
18 | applyTheme(theme)
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/src/lang/Readme.md:
--------------------------------------------------------------------------------
1 | 新增语言时创建的语言文件夹需要与以下列表对应:
2 |
3 | - `ar_sa` - Arabic Saudi Arabia
4 | - `cs_cz` - Czech Czech Republic
5 | - `da_dk` - Danish Denmark
6 | - `de_de` - German Germany
7 | - `el_gr` - Modern Greek Greece
8 | - `en_au` - English Australia
9 | - `en_gb` - English United Kingdom
10 | - `en_ie` - English Ireland
11 | - `en_us` - English United States
12 | - `en_za` - English South Africa
13 | - `es_es` - Spanish Spain
14 | - `es_mx` - Spanish Mexico
15 | - `fi_fi` - Finnish Finland
16 | - `fr_ca` - French Canada
17 | - `fr_fr` - French France
18 | - `he_il` - Hebrew Israel
19 | - `hi_in` - Hindi India
20 | - `hu_hu` - Hungarian Hungary
21 | - `id_id` - Indonesian Indonesia
22 | - `it_it` - Italian Italy
23 | - `ja_jp` - Japanese Japan
24 | - `ko_kr` - Korean Republic of Korea
25 | - `nl_be` - Dutch Belgium
26 | - `nl_nl` - Dutch Netherlands
27 | - `no_no` - Norwegian Norway
28 | - `pl_pl` - Polish Poland
29 | - `pt_br` - Portuguese Brazil
30 | - `pt_pt` - Portuguese Portugal
31 | - `ro_ro` - Romanian Romania
32 | - `ru_ru` - Russian Russian Federation
33 | - `sk_sk` - Slovak Slovakia
34 | - `sv_se` - Swedish Sweden
35 | - `th_th` - Thai Thailand
36 | - `tr_tr` - Turkish Turkey
37 | - `zh_cn` - Chinese China
38 | - `zh_hk` - Chinese Hong Kong
39 | - `zh_tw` - Chinese Taiwan
40 |
--------------------------------------------------------------------------------
/src/lang/index.ts:
--------------------------------------------------------------------------------
1 | import zh_cn from './zh-cn.json'
2 | import zh_tw from './zh-tw.json'
3 | import en_us from './en-us.json'
4 |
5 | type Message =
6 | | Record
7 | | Record
8 | | Record
9 |
10 | const langs = [
11 | {
12 | name: '简体中文',
13 | locale: 'zh_cn',
14 | // alternate: 'zh-hans',
15 | country: 'cn',
16 | fallback: true,
17 | message: zh_cn,
18 | },
19 | {
20 | name: '繁體中文',
21 | locale: 'zh_tw',
22 | // alternate: 'zh-hant',
23 | country: 'cn',
24 | message: zh_tw,
25 | },
26 | {
27 | name: 'English',
28 | locale: 'en_us',
29 | country: 'us',
30 | message: en_us,
31 | },
32 | ] as const
33 |
34 | const langList: Array<{
35 | name: string
36 | locale: (typeof langs)[number]['locale']
37 | // alternate?: string
38 | }> = []
39 | type Messages = Record<(typeof langs)[number]['locale'], Message>
40 |
41 | // @ts-expect-error
42 | const messages: Messages = {}
43 |
44 | langs.forEach((item) => {
45 | langList.push({
46 | name: item.name,
47 | locale: item.locale,
48 | // alternate: item.alternate,
49 | })
50 | messages[item.locale] = item.message
51 | })
52 |
53 | export { langList, messages }
54 |
55 | export type { Messages, Message }
56 |
57 | export * from './i18n'
58 |
--------------------------------------------------------------------------------
/src/navigation/event.ts:
--------------------------------------------------------------------------------
1 | import { type EmitterSubscription } from 'react-native'
2 | import { Navigation } from 'react-native-navigation'
3 |
4 | export const onModalDismissed = (id: string, handler: () => void) => {
5 | let modalDismissedListener: EmitterSubscription | null =
6 | Navigation.events().registerModalDismissedListener(({ componentId, modalsDismissed }) => {
7 | if (componentId != id || !modalDismissedListener) return
8 | handler()
9 | modalDismissedListener.remove()
10 | modalDismissedListener = null
11 | })
12 | return () => {
13 | if (!modalDismissedListener) return
14 | modalDismissedListener.remove()
15 | modalDismissedListener = null
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/navigation/index.ts:
--------------------------------------------------------------------------------
1 | import { Navigation } from 'react-native-navigation'
2 | import * as screenNames from './screenNames'
3 | import * as navigations from './navigation'
4 |
5 | import registerScreens from './registerScreens'
6 | import { removeComponentId } from '@/core/common'
7 | import { onAppLaunched } from './regLaunchedEvent'
8 |
9 | let unRegisterEvent: ReturnType<
10 | ReturnType['registerScreenPoppedListener']
11 | >
12 |
13 | const init = (callback: () => void | Promise) => {
14 | // Register all screens on launch
15 | registerScreens()
16 |
17 | if (unRegisterEvent) unRegisterEvent.remove()
18 |
19 | Navigation.setDefaultOptions({
20 | // animations: {
21 | // setRoot: {
22 | // waitForRender: true,
23 | // },
24 | // },
25 | })
26 | unRegisterEvent = Navigation.events().registerScreenPoppedListener(({ componentId }) => {
27 | removeComponentId(componentId)
28 | })
29 | onAppLaunched(() => {
30 | console.log('Register app launched listener')
31 | void callback()
32 | })
33 | }
34 |
35 | export * from './utils'
36 | export * from './event'
37 | export * from './hooks'
38 |
39 | export { init, screenNames, navigations }
40 |
--------------------------------------------------------------------------------
/src/navigation/regLaunchedEvent.ts:
--------------------------------------------------------------------------------
1 | import { Navigation } from 'react-native-navigation'
2 |
3 | let launched = false
4 | const handlers: Array<() => void> = []
5 |
6 | export const listenLaunchEvent = () => {
7 | Navigation.events().registerAppLaunchedListener(() => {
8 | // console.log('Register app launched listener', launched)
9 | launched = true
10 | setImmediate(() => {
11 | for (const handler of handlers) handler()
12 | })
13 | })
14 | }
15 |
16 | export const onAppLaunched = (handler: () => void) => {
17 | handlers.push(handler)
18 | if (launched) {
19 | setImmediate(() => {
20 | handler()
21 | })
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/navigation/screenNames.ts:
--------------------------------------------------------------------------------
1 | export const HOME_SCREEN = 'lxm.HomeScreen'
2 | export const PLAY_DETAIL_SCREEN = 'lxm.PlayDetailScreen'
3 | export const SONGLIST_DETAIL_SCREEN = 'lxm.SonglistDetailScreen'
4 | export const COMMENT_SCREEN = 'lxm.CommentScreen'
5 | export const VERSION_MODAL = 'lxm.VersionModal'
6 | export const PACT_MODAL = 'lxm.PactModal'
7 | export const SYNC_MODE_MODAL = 'lxm.SyncModeModal'
8 | // export const SETTING_SCREEN = 'lxm.SettingScreen'
9 | // export const TOAST_SCREEN = 'lxm.ToastScreen'
10 |
--------------------------------------------------------------------------------
/src/plugins/sync/client/modules/dislike/index.ts:
--------------------------------------------------------------------------------
1 | export { default as handler } from './handler'
2 |
3 | export * from './localEvent'
4 |
--------------------------------------------------------------------------------
/src/plugins/sync/client/modules/dislike/localEvent.ts:
--------------------------------------------------------------------------------
1 | import { SYNC_CLOSE_CODE } from '@/plugins/sync/constants'
2 | import { registerDislikeActionEvent } from '../../../dislikeEvent'
3 |
4 | let unregisterLocalListAction: (() => void) | null
5 |
6 | export const registerEvent = (socket: LX.Sync.Socket) => {
7 | // socket = _socket
8 | // socket.onClose(() => {
9 | // unregisterLocalListAction?.()
10 | // unregisterLocalListAction = null
11 | // })
12 | unregisterEvent()
13 | unregisterLocalListAction = registerDislikeActionEvent((action) => {
14 | if (!socket.moduleReadys?.dislike) return
15 | void socket.remoteQueueDislike.onDislikeSyncAction(action).catch((err) => {
16 | // TODO send status
17 | socket.moduleReadys.dislike = false
18 | socket.close(SYNC_CLOSE_CODE.failed)
19 | console.log(err.message)
20 | })
21 | })
22 | }
23 |
24 | export const unregisterEvent = () => {
25 | unregisterLocalListAction?.()
26 | unregisterLocalListAction = null
27 | }
28 |
--------------------------------------------------------------------------------
/src/plugins/sync/client/modules/index.ts:
--------------------------------------------------------------------------------
1 | import * as list from './list'
2 | import * as dislike from './dislike'
3 | // export * as theme from './theme'
4 |
5 | export const callObj = Object.assign({}, list.handler, dislike.handler)
6 |
7 | export const modules = {
8 | list,
9 | dislike,
10 | }
11 |
12 | export const featureVersion = {
13 | list: 1,
14 | dislike: 1,
15 | } as const
16 |
--------------------------------------------------------------------------------
/src/plugins/sync/client/modules/list/index.ts:
--------------------------------------------------------------------------------
1 | export { default as handler } from './handler'
2 |
3 | export * from './localEvent'
4 |
--------------------------------------------------------------------------------
/src/plugins/sync/client/modules/list/localEvent.ts:
--------------------------------------------------------------------------------
1 | import { SYNC_CLOSE_CODE } from '@/plugins/sync/constants'
2 | import { registerListActionEvent } from '../../../listEvent'
3 |
4 | let unregisterLocalListAction: (() => void) | null
5 |
6 | export const registerEvent = (socket: LX.Sync.Socket) => {
7 | // socket = _socket
8 | // socket.onClose(() => {
9 | // unregisterLocalListAction?.()
10 | // unregisterLocalListAction = null
11 | // })
12 | unregisterEvent()
13 | unregisterLocalListAction = registerListActionEvent((action) => {
14 | if (!socket.moduleReadys?.list) return
15 | void socket.remoteQueueList.onListSyncAction(action).catch((err) => {
16 | // TODO send status
17 | socket.moduleReadys.list = false
18 | socket.close(SYNC_CLOSE_CODE.failed)
19 | console.log(err.message)
20 | })
21 | })
22 | }
23 |
24 | export const unregisterEvent = () => {
25 | unregisterLocalListAction?.()
26 | unregisterLocalListAction = null
27 | }
28 |
--------------------------------------------------------------------------------
/src/plugins/sync/client/sync/handler.ts:
--------------------------------------------------------------------------------
1 | // 这个文件导出的方法将暴露给服务端调用,第一个参数固定为当前 socket 对象
2 | // import { getUserSpace } from '@/user'
3 | // import { modules } from '../modules'
4 |
5 | import { featureVersion } from '../modules'
6 |
7 | const handler: Omit, 'finished'> = {
8 | async getEnabledFeatures(socket, serverType, supportedFeatures) {
9 | // const userSpace = getUserSpace(socket.userInfo.name)
10 | const features: LX.Sync.EnabledFeatures = {}
11 | switch (serverType) {
12 | case 'server':
13 | if (featureVersion.list == supportedFeatures.list) {
14 | features.list = { skipSnapshot: false }
15 | }
16 | if (featureVersion.dislike == supportedFeatures.dislike) {
17 | features.dislike = { skipSnapshot: false }
18 | }
19 | return features
20 | case 'desktop-app':
21 | default:
22 | if (featureVersion.list == supportedFeatures.list) {
23 | features.list = { skipSnapshot: false }
24 | }
25 | if (featureVersion.dislike == supportedFeatures.dislike) {
26 | features.dislike = { skipSnapshot: false }
27 | }
28 | return features
29 | }
30 | },
31 | }
32 |
33 | export default handler
34 |
--------------------------------------------------------------------------------
/src/plugins/sync/client/sync/index.ts:
--------------------------------------------------------------------------------
1 | import handler from './handler'
2 | import { callObj as _callObj } from '../modules'
3 | export { modules } from '../modules'
4 |
5 | export const callObj = {
6 | ...handler,
7 | ..._callObj,
8 | }
9 |
--------------------------------------------------------------------------------
/src/plugins/sync/data.ts:
--------------------------------------------------------------------------------
1 | export {
2 | getSyncAuthKey,
3 | setSyncAuthKey,
4 | getSyncHost,
5 | setSyncHost,
6 | getSyncHostHistory,
7 | addSyncHostHistory,
8 | removeSyncHostHistory,
9 | } from '@/utils/data'
10 |
--------------------------------------------------------------------------------
/src/plugins/sync/index.ts:
--------------------------------------------------------------------------------
1 | // import Event from './event/event'
2 |
3 | export { connectServer, disconnectServer, getStatus } from './client'
4 |
--------------------------------------------------------------------------------
/src/plugins/sync/log.ts:
--------------------------------------------------------------------------------
1 | import { log as writeLog } from '@/utils/log'
2 |
3 | export default {
4 | r_info(...params: any[]) {
5 | writeLog.info(...params)
6 | },
7 | r_warn(...params: any[]) {
8 | writeLog.warn(...params)
9 | },
10 | r_error(...params: any[]) {
11 | writeLog.error(...params)
12 | },
13 | info(...params: any[]) {
14 | if (global.lx.isEnableSyncLog) writeLog.info(...params)
15 | },
16 | warn(...params: any[]) {
17 | if (global.lx.isEnableSyncLog) writeLog.warn(...params)
18 | },
19 | error(...params: any[]) {
20 | if (global.lx.isEnableSyncLog) writeLog.error(...params)
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/src/resources/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/resources/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/src/resources/images/defaultUser.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/resources/images/defaultUser.jpg
--------------------------------------------------------------------------------
/src/resources/images/notification.hdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/resources/images/notification.hdpi.png
--------------------------------------------------------------------------------
/src/resources/images/notification.ldpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/resources/images/notification.ldpi.png
--------------------------------------------------------------------------------
/src/resources/images/notification.mdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/resources/images/notification.mdpi.png
--------------------------------------------------------------------------------
/src/resources/images/notification.xhdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/resources/images/notification.xhdpi.png
--------------------------------------------------------------------------------
/src/resources/images/notification2.hdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/resources/images/notification2.hdpi.png
--------------------------------------------------------------------------------
/src/resources/images/notification2.ldpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/resources/images/notification2.ldpi.png
--------------------------------------------------------------------------------
/src/resources/images/notification2.mdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/resources/images/notification2.mdpi.png
--------------------------------------------------------------------------------
/src/resources/images/notification2.xhdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/resources/images/notification2.xhdpi.png
--------------------------------------------------------------------------------
/src/resources/medias/Silence02s.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/resources/medias/Silence02s.mp3
--------------------------------------------------------------------------------
/src/screens/Home/Horizontal/Main.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useMemo, useState } from 'react'
2 | import Search from '../Views/Search'
3 | import SongList from '../Views/SongList'
4 | import Mylist from '../Views/Mylist'
5 | import Leaderboard from '../Views/Leaderboard'
6 | import Setting from '../Views/Setting'
7 | import commonState, { type InitState as CommonState } from '@/store/common/state'
8 |
9 | const Main = () => {
10 | const [id, setId] = useState(commonState.navActiveId)
11 |
12 | useEffect(() => {
13 | const handleUpdate = (id: CommonState['navActiveId']) => {
14 | requestAnimationFrame(() => {
15 | setId(id)
16 | })
17 | }
18 | global.state_event.on('navActiveIdUpdated', handleUpdate)
19 | return () => {
20 | global.state_event.off('navActiveIdUpdated', handleUpdate)
21 | }
22 | }, [])
23 |
24 | const component = useMemo(() => {
25 | switch (id) {
26 | case 'nav_songlist':
27 | return
28 | case 'nav_top':
29 | return
30 | case 'nav_love':
31 | return
32 | case 'nav_setting':
33 | return
34 | case 'nav_search':
35 | default:
36 | return
37 | }
38 | }, [id])
39 |
40 | return component
41 | }
42 |
43 | export default Main
44 |
--------------------------------------------------------------------------------
/src/screens/Home/Horizontal/index.tsx:
--------------------------------------------------------------------------------
1 | import { View } from 'react-native'
2 | import Aside from './Aside'
3 | import PlayerBar from '@/components/player/PlayerBar'
4 | import StatusBar from '@/components/common/StatusBar'
5 | import Header from './Header'
6 | import Main from './Main'
7 | import { createStyle } from '@/utils/tools'
8 |
9 | const styles = createStyle({
10 | container: {
11 | flex: 1,
12 | flexDirection: 'row',
13 | },
14 | content: {
15 | flex: 1,
16 | overflow: 'hidden',
17 | },
18 | })
19 |
20 | export default () => {
21 | return (
22 | <>
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | >
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/src/screens/Home/Vertical/index.tsx:
--------------------------------------------------------------------------------
1 | import Content from './Content'
2 | import PlayerBar from '@/components/player/PlayerBar'
3 |
4 | export default () => {
5 | return (
6 | <>
7 |
8 |
9 | >
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Download/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | // StyleSheet,
3 | View,
4 | // Button,
5 | Text,
6 | } from 'react-native'
7 |
8 | // import Menu from '@/components/Menu'
9 |
10 | export default () => {
11 | return (
12 |
13 | 下载
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Leaderboard/index.tsx:
--------------------------------------------------------------------------------
1 | import { useHorizontalMode } from '@/utils/hooks'
2 | import Vertical from './Vertical'
3 | import Horizontal from './Horizontal'
4 | // import { AppColors } from '@/theme'
5 |
6 | export default () => {
7 | const isHorizontalMode = useHorizontalMode()
8 |
9 | return isHorizontalMode ? :
10 | }
11 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/Vertical/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Main'
2 | // // import { View } from 'react-native'
3 | // import Main from './Main'
4 | // import { createStyle } from '@/utils/tools'
5 |
6 | // const Content = () => {
7 | // return (
8 | //
9 | //
10 | //
11 | // )
12 | // }
13 |
14 | // const styles = createStyle({
15 | // container: {
16 | // flex: 1,
17 | // flexDirection: 'column',
18 | // },
19 | // // main: {
20 | // // paddingLeft: 15,
21 | // // paddingRight: 15,
22 | // // paddingTop: 15,
23 | // // paddingBottom: 15,
24 | // // },
25 | // })
26 |
27 | // export default Content
28 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/components/Button.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import Button, { type BtnProps } from '@/components/common/Button'
4 | import Text from '@/components/common/Text'
5 | import { useTheme } from '@/store/theme/hook'
6 | import { createStyle } from '@/utils/tools'
7 |
8 | type ButtonProps = BtnProps
9 |
10 | export default memo(({ disabled, onPress, children }: ButtonProps) => {
11 | const theme = useTheme()
12 |
13 | return (
14 |
23 | )
24 | })
25 |
26 | const styles = createStyle({
27 | button: {
28 | paddingLeft: 10,
29 | paddingRight: 10,
30 | paddingTop: 5,
31 | paddingBottom: 5,
32 | borderRadius: 4,
33 | marginRight: 10,
34 | },
35 | })
36 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/components/CheckBoxItem.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import { View } from 'react-native'
4 |
5 | import CheckBox, { type CheckBoxProps } from '@/components/common/CheckBox'
6 | import { createStyle } from '@/utils/tools'
7 |
8 | export default memo((props: CheckBoxProps) => {
9 | return (
10 |
11 |
12 |
13 | )
14 | })
15 |
16 | const styles = createStyle({
17 | container: {
18 | paddingLeft: 25,
19 | // marginTop: -10,
20 | // marginBottom: 0,
21 | },
22 | })
23 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/components/Section.tsx:
--------------------------------------------------------------------------------
1 | import { View } from 'react-native'
2 |
3 | import { createStyle } from '@/utils/tools'
4 | import { useTheme } from '@/store/theme/hook'
5 | import Text from '@/components/common/Text'
6 |
7 | interface Props {
8 | title: string
9 | children: React.ReactNode | React.ReactNode[]
10 | }
11 |
12 | export default ({ title, children }: Props) => {
13 | const theme = useTheme()
14 |
15 | return (
16 |
17 |
18 | {title}
19 |
20 | {children}
21 |
22 | )
23 | }
24 |
25 | const styles = createStyle({
26 | container: {
27 | // paddingLeft: 10,
28 | // backgroundColor: 'rgba(0,0,0,0.2)',
29 | },
30 | title: {
31 | borderLeftWidth: 5,
32 | paddingLeft: 12,
33 | marginBottom: 10,
34 | // lineHeight: 16,
35 | },
36 | })
37 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/components/Slider.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import Slider, { type SliderProps } from '@react-native-community/slider'
4 | import { useTheme } from '@/store/theme/hook'
5 | import { createStyle } from '@/utils/tools'
6 |
7 | export type { SliderProps }
8 |
9 | export default memo(
10 | ({
11 | value,
12 | minimumValue,
13 | maximumValue,
14 | onSlidingStart,
15 | onSlidingComplete,
16 | onValueChange,
17 | step,
18 | }: SliderProps) => {
19 | const theme = useTheme()
20 |
21 | return (
22 |
35 | )
36 | }
37 | )
38 |
39 | const styles = createStyle({
40 | slider: {
41 | flexShrink: 0,
42 | flexGrow: 1,
43 | // width: '100%',
44 | maxWidth: 300,
45 | height: 40,
46 | marginTop: -6,
47 | },
48 | })
49 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/components/SubTitle.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import { View } from 'react-native'
4 | import { createStyle } from '@/utils/tools'
5 | import Text from '@/components/common/Text'
6 |
7 | export default memo(
8 | ({ title, children }: { title: string; children: React.ReactNode | React.ReactNode[] }) => {
9 | return (
10 |
11 | {title}
12 | {children}
13 |
14 | )
15 | }
16 | )
17 |
18 | const styles = createStyle({
19 | container: {
20 | paddingLeft: 25,
21 | marginBottom: 18,
22 | },
23 | title: {
24 | marginLeft: -10,
25 | marginBottom: 10,
26 | // lineHeight: 16,
27 | },
28 | })
29 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/index.tsx:
--------------------------------------------------------------------------------
1 | import { useHorizontalMode } from '@/utils/hooks'
2 | import Vertical from './Vertical'
3 | import Horizontal from './Horizontal'
4 | import { useBackHandler } from '@/utils/hooks/useBackHandler'
5 | import { useCallback } from 'react'
6 | // import { AppColors } from '@/theme'
7 | import commonState from '@/store/common/state'
8 | import { setNavActiveId } from '@/core/common'
9 |
10 | export type { SettingScreenIds } from './Main'
11 |
12 | export default () => {
13 | const isHorizontalMode = useHorizontalMode()
14 | useBackHandler(
15 | useCallback(() => {
16 | if (
17 | Object.keys(commonState.componentIds).length == 1 &&
18 | commonState.navActiveId == 'nav_setting'
19 | ) {
20 | setNavActiveId(commonState.lastNavActiveId)
21 | return true
22 | }
23 | return false
24 | }, [])
25 | )
26 |
27 | return isHorizontalMode ? :
28 | }
29 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Backup/All.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/screens/Home/Views/Setting/settings/Backup/All.js
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Backup/index.tsx:
--------------------------------------------------------------------------------
1 | import { useI18n } from '@/lang'
2 | import { memo } from 'react'
3 |
4 | import Section from '../../components/Section'
5 | import Part from './Part'
6 | // import MaxCache from './MaxCache'
7 |
8 | export default memo(() => {
9 | const t = useI18n()
10 |
11 | return (
12 |
16 | )
17 | })
18 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Basic/IsAlwaysKeepStatusbarHeight.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const val = useSettingValue('common.alwaysKeepStatusbarHeight')
13 | const update = (alwaysKeepStatusbarHeight: boolean) => {
14 | updateSetting({ 'common.alwaysKeepStatusbarHeight': alwaysKeepStatusbarHeight })
15 | }
16 |
17 | return (
18 |
19 |
25 |
26 | )
27 | })
28 |
29 | const styles = createStyle({
30 | content: {
31 | marginTop: 5,
32 | marginBottom: 15,
33 | },
34 | })
35 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Basic/IsAutoHidePlayBar.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const autoHidePlayBar = useSettingValue('common.autoHidePlayBar')
13 | const setAutoHidePlayBar = (autoHidePlayBar: boolean) => {
14 | updateSetting({ 'common.autoHidePlayBar': autoHidePlayBar })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | // marginBottom: 15,
32 | },
33 | })
34 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Basic/IsHomePageScroll.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const homePageScroll = useSettingValue('common.homePageScroll')
13 | const setHomePageScroll = (homePageScroll: boolean) => {
14 | updateSetting({ 'common.homePageScroll': homePageScroll })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Basic/IsShowBackBtn.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const showBackBtn = useSettingValue('common.showBackBtn')
13 | const setShowBackBtn = (showBackBtn: boolean) => {
14 | updateSetting({ 'common.showBackBtn': showBackBtn })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Basic/IsShowExitBtn.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const showExitBtn = useSettingValue('common.showExitBtn')
13 | const setShowExitBtn = (showExitBtn: boolean) => {
14 | updateSetting({ 'common.showExitBtn': showExitBtn })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Basic/IsStartupAutoPlay.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const startupAutoPlay = useSettingValue('player.startupAutoPlay')
13 | const setStartupAutoPlay = (startupAutoPlay: boolean) => {
14 | updateSetting({ 'player.startupAutoPlay': startupAutoPlay })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | // marginBottom: 15,
32 | },
33 | })
34 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Basic/IsStartupPushPlayDetailScreen.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const startupPushPlayDetailScreen = useSettingValue('player.startupPushPlayDetailScreen')
13 | const setStartupPushPlayDetailScreen = (startupPushPlayDetailScreen: boolean) => {
14 | updateSetting({ 'player.startupPushPlayDetailScreen': startupPushPlayDetailScreen })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | // marginBottom: 15,
32 | },
33 | })
34 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Basic/IsUseSystemFileSelector.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const val = useSettingValue('common.useSystemFileSelector')
13 | const update = (useSystemFileSelector: boolean) => {
14 | updateSetting({ 'common.useSystemFileSelector': useSystemFileSelector })
15 | }
16 |
17 | return (
18 |
19 |
25 |
26 | )
27 | })
28 |
29 | const styles = createStyle({
30 | content: {
31 | marginTop: 5,
32 | // marginBottom: 15,
33 | },
34 | })
35 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/action.ts:
--------------------------------------------------------------------------------
1 | import { importUserApi } from '@/core/userApi'
2 | import { readFile } from '@/utils/fs'
3 | import { log } from '@/utils/log'
4 | import { toast } from '@/utils/tools'
5 |
6 | export const handleImportScript = async (script: string) => {
7 | await importUserApi(script)
8 | .then(() => {
9 | toast(global.i18n.t('user_api_import_success_tip'))
10 | })
11 | .catch((error: any) => {
12 | log.error(error.stack)
13 | toast(global.i18n.t('user_api_import_failed_tip', { message: error.message }), 'long')
14 | })
15 | }
16 |
17 | export const handleImportLocalFile = (path: string) => {
18 | // toast(global.i18n.t('setting_backup_part_import_list_tip_unzip'))
19 | void readFile(path)
20 | .then(async (script) => {
21 | if (script == null) throw new Error('Read file failed')
22 | void handleImportScript(script)
23 | })
24 | .catch((error: any) => {
25 | toast(global.i18n.t('user_api_import_failed_tip', { message: error.message }), 'long')
26 | })
27 | }
28 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/List/IsClickPlayList.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isClickPlayList = useSettingValue('list.isClickPlayList')
13 | const setClickPlayList = (isClickPlayList: boolean) => {
14 | updateSetting({ 'list.isClickPlayList': isClickPlayList })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | // marginBottom: 15,
32 | },
33 | })
34 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/List/IsShowAlbumName.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isShowAlbumName = useSettingValue('list.isShowAlbumName')
13 | const setShowAlbumName = (isShowAlbumName: boolean) => {
14 | requestAnimationFrame(() => {
15 | updateSetting({ 'list.isShowAlbumName': isShowAlbumName })
16 | })
17 | }
18 |
19 | return (
20 |
21 |
26 |
27 | )
28 | })
29 |
30 | const styles = createStyle({
31 | content: {
32 | marginTop: 5,
33 | // marginBottom: 15,
34 | },
35 | })
36 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/List/IsShowInterval.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isShowInterval = useSettingValue('list.isShowInterval')
13 | const setShowInterval = (isShowInterval: boolean) => {
14 | requestAnimationFrame(() => {
15 | updateSetting({ 'list.isShowInterval': isShowInterval })
16 | })
17 | }
18 |
19 | return (
20 |
21 |
26 |
27 | )
28 | })
29 |
30 | const styles = createStyle({
31 | content: {
32 | marginTop: 5,
33 | marginBottom: 15,
34 | },
35 | })
36 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/List/index.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import Section from '../../components/Section'
4 | import AddMusicLocationType from './AddMusicLocationType'
5 | import IsClickPlayList from './IsClickPlayList'
6 | import IsShowAlbumName from './IsShowAlbumName'
7 | import IsShowInterval from './IsShowInterval'
8 |
9 | import { useI18n } from '@/lang'
10 |
11 | export default memo(() => {
12 | const t = useI18n()
13 |
14 | return (
15 |
21 | )
22 | })
23 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/LyricDesktop/IsLockLyric.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 | import { View } from 'react-native'
3 | import { useSettingValue } from '@/store/setting/hook'
4 | import { useI18n } from '@/lang'
5 | import { createStyle } from '@/utils/tools'
6 |
7 | import CheckBoxItem from '../../components/CheckBoxItem'
8 | import { toggleDesktopLyricLock } from '@/core/desktopLyric'
9 | import { updateSetting } from '@/core/common'
10 |
11 | export default memo(() => {
12 | const t = useI18n()
13 | const isLock = useSettingValue('desktopLyric.isLock')
14 | const setLock = (isLock: boolean) => {
15 | void toggleDesktopLyricLock(isLock).then(() => {
16 | updateSetting({ 'desktopLyric.isLock': isLock })
17 | })
18 | }
19 |
20 | return (
21 |
22 |
23 |
24 | )
25 | })
26 |
27 | const styles = createStyle({
28 | content: {
29 | marginTop: 5,
30 | },
31 | })
32 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/LyricDesktop/IsShowToggleAnima.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 | import { View } from 'react-native'
3 | import { useSettingValue } from '@/store/setting/hook'
4 | import { useI18n } from '@/lang'
5 | import { createStyle } from '@/utils/tools'
6 |
7 | import CheckBoxItem from '../../components/CheckBoxItem'
8 | import { setShowDesktopLyricToggleAnima } from '@/core/desktopLyric'
9 | import { updateSetting } from '@/core/common'
10 |
11 | export default memo(() => {
12 | const t = useI18n()
13 | const showToggleAnima = useSettingValue('desktopLyric.showToggleAnima')
14 | const update = (showToggleAnima: boolean) => {
15 | void setShowDesktopLyricToggleAnima(showToggleAnima).then(() => {
16 | updateSetting({ 'desktopLyric.showToggleAnima': showToggleAnima })
17 | })
18 | }
19 |
20 | return (
21 |
22 |
27 |
28 | )
29 | })
30 |
31 | const styles = createStyle({
32 | content: {
33 | marginTop: 5,
34 | },
35 | })
36 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/LyricDesktop/IsSingleLine.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 | import { View } from 'react-native'
3 | import { useSettingValue } from '@/store/setting/hook'
4 | import { useI18n } from '@/lang'
5 | import { createStyle } from '@/utils/tools'
6 |
7 | import CheckBoxItem from '../../components/CheckBoxItem'
8 | import { setDesktopLyricSingleLine } from '@/core/desktopLyric'
9 | import { updateSetting } from '@/core/common'
10 |
11 | export default memo(() => {
12 | const t = useI18n()
13 | const isSingleLine = useSettingValue('desktopLyric.isSingleLine')
14 | const update = (isSingleLine: boolean) => {
15 | void setDesktopLyricSingleLine(isSingleLine).then(() => {
16 | updateSetting({ 'desktopLyric.isSingleLine': isSingleLine })
17 | })
18 | }
19 |
20 | return (
21 |
22 |
27 |
28 | )
29 | })
30 |
31 | const styles = createStyle({
32 | content: {
33 | marginTop: 5,
34 | marginBottom: 15,
35 | },
36 | })
37 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/LyricDesktop/index.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import Section from '../../components/Section'
4 | import IsShowLyric from './IsShowLyric'
5 | import IsLockLyric from './IsLockLyric'
6 | import IsShowToggleAnima from './IsShowToggleAnima'
7 | import IsSingleLine from './IsSingleLine'
8 | import TextSize from './TextSize'
9 | import ViewWidth from './ViewWidth'
10 | import MaxLineNum from './MaxLineNum'
11 | import TextOpacity from './TextOpacity'
12 | import TextPositionX from './TextPositionX'
13 | import TextPositionY from './TextPositionY'
14 | import { useI18n } from '@/lang'
15 | import Theme from './Theme'
16 | // import { useTranslation } from '@/plugins/i18n'
17 |
18 | export default memo(() => {
19 | const t = useI18n()
20 |
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | })
37 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Other/index.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import Section from '../../components/Section'
4 | import ResourceCache from './ResourceCache'
5 | import MetaCache from './MetaCache'
6 | import DislikeList from './DislikeList'
7 | import Log from './Log'
8 | // import MaxCache from './MaxCache'
9 | import { useI18n } from '@/lang'
10 |
11 | export default memo(() => {
12 | const t = useI18n()
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | {/* */}
21 |
22 | )
23 | })
24 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Player/IsAutoCleanPlayedList.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isAutoCleanPlayedList = useSettingValue('player.isAutoCleanPlayedList')
13 | const setAutoCleanPlayedList = (isAutoCleanPlayedList: boolean) => {
14 | updateSetting({ 'player.isAutoCleanPlayedList': isAutoCleanPlayedList })
15 | }
16 |
17 | return (
18 |
19 |
25 |
26 | )
27 | })
28 |
29 | const styles = createStyle({
30 | content: {
31 | marginTop: 5,
32 | },
33 | })
34 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Player/IsEnableAudioOffload.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle, toast } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isEnableAudioOffload = useSettingValue('player.isEnableAudioOffload')
13 | const setHandleAudioFocus = (isEnableAudioOffload: boolean) => {
14 | updateSetting({ 'player.isEnableAudioOffload': isEnableAudioOffload })
15 | toast(t('setting_play_handle_audio_focus_tip'))
16 | }
17 |
18 | return (
19 |
20 |
26 |
27 | )
28 | })
29 |
30 | const styles = createStyle({
31 | content: {
32 | marginTop: 5,
33 | },
34 | })
35 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Player/IsHandleAudioFocus.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle, toast } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isHandleAudioFocus = useSettingValue('player.isHandleAudioFocus')
13 | const setHandleAudioFocus = (isHandleAudioFocus: boolean) => {
14 | updateSetting({ 'player.isHandleAudioFocus': isHandleAudioFocus })
15 | toast(t('setting_play_handle_audio_focus_tip'))
16 | }
17 |
18 | return (
19 |
20 |
25 |
26 | )
27 | })
28 |
29 | const styles = createStyle({
30 | content: {
31 | marginTop: 5,
32 | },
33 | })
34 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Player/IsS2T.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isS2t = useSettingValue('player.isS2t')
13 | const setS2T = (isS2t: boolean) => {
14 | updateSetting({ 'player.isS2t': isS2t })
15 | }
16 |
17 | return (
18 |
19 |
20 |
21 | )
22 | })
23 |
24 | const styles = createStyle({
25 | content: {
26 | marginTop: 5,
27 | },
28 | })
29 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Player/IsSavePlayTime.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isSavePlayTime = useSettingValue('player.isSavePlayTime')
13 | const setSavePlayTime = (isSavePlayTime: boolean) => {
14 | updateSetting({ 'player.isSavePlayTime': isSavePlayTime })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Player/IsShowLyricRoma.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 | import { toggleRoma } from '@/core/lyric'
10 |
11 | export default memo(() => {
12 | const t = useI18n()
13 | const isShowLyricRoma = useSettingValue('player.isShowLyricRoma')
14 | const setShowLyricRoma = (isShowLyricRoma: boolean) => {
15 | updateSetting({ 'player.isShowLyricRoma': isShowLyricRoma })
16 | void toggleRoma(isShowLyricRoma)
17 | }
18 |
19 | return (
20 |
21 |
26 |
27 | )
28 | })
29 |
30 | const styles = createStyle({
31 | content: {
32 | marginTop: 5,
33 | },
34 | })
35 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Player/IsShowLyricTranslation.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 | import { toggleTranslation } from '@/core/lyric'
10 |
11 | export default memo(() => {
12 | const t = useI18n()
13 | const isShowLyricTranslation = useSettingValue('player.isShowLyricTranslation')
14 | const setShowLyricTranslation = (isShowLyricTranslation: boolean) => {
15 | updateSetting({ 'player.isShowLyricTranslation': isShowLyricTranslation })
16 | void toggleTranslation(isShowLyricTranslation)
17 | }
18 |
19 | return (
20 |
21 |
26 |
27 | )
28 | })
29 |
30 | const styles = createStyle({
31 | content: {
32 | marginTop: 5,
33 | },
34 | })
35 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Player/IsShowNotificationImage.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isShowNotificationImage = useSettingValue('player.isShowNotificationImage')
13 | const setShowNotificationImage = (isShowNotificationImage: boolean) => {
14 | updateSetting({ 'player.isShowNotificationImage': isShowNotificationImage })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Player/index.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import Section from '../../components/Section'
4 | import IsSavePlayTime from './IsSavePlayTime'
5 | import PlayHighQuality from './PlayHighQuality'
6 | import IsHandleAudioFocus from './IsHandleAudioFocus'
7 | import IsEnableAudioOffload from './IsEnableAudioOffload'
8 | import IsAutoCleanPlayedList from './IsAutoCleanPlayedList'
9 | import IsShowBluetoothLyric from './IsShowBluetoothLyric'
10 | import IsShowNotificationImage from './IsShowNotificationImage'
11 | import IsShowLyricTranslation from './IsShowLyricTranslation'
12 | import IsShowLyricRoma from './IsShowLyricRoma'
13 | import IsS2T from './IsS2T'
14 | import MaxCache from './MaxCache'
15 | import { useI18n } from '@/lang'
16 |
17 | export default memo(() => {
18 | const t = useI18n()
19 |
20 | return (
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | )
35 | })
36 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Search/IsShowHistorySearch.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isShowHistorySearch = useSettingValue('search.isShowHistorySearch')
13 | const handleUpdate = (isShowHistorySearch: boolean) => {
14 | updateSetting({ 'search.isShowHistorySearch': isShowHistorySearch })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | marginBottom: 15,
32 | },
33 | })
34 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Search/IsShowHotSearch.tsx:
--------------------------------------------------------------------------------
1 | import { updateSetting } from '@/core/common'
2 | import { useI18n } from '@/lang'
3 | import { createStyle } from '@/utils/tools'
4 | import { memo } from 'react'
5 | import { View } from 'react-native'
6 | import { useSettingValue } from '@/store/setting/hook'
7 |
8 | import CheckBoxItem from '../../components/CheckBoxItem'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isShowHotSearch = useSettingValue('search.isShowHotSearch')
13 | const handleUpdate = (isShowHotSearch: boolean) => {
14 | updateSetting({ 'search.isShowHotSearch': isShowHotSearch })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Search/index.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | import Section from '../../components/Section'
4 | import IsShowHotSearch from './IsShowHotSearch'
5 | import IsShowHistorySearch from './IsShowHistorySearch'
6 |
7 | import { useI18n } from '@/lang'
8 |
9 | export default memo(() => {
10 | const t = useI18n()
11 |
12 | return (
13 |
17 | )
18 | })
19 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Sync/index.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useState } from 'react'
2 |
3 | import Section from '../../components/Section'
4 | import IsEnable from './IsEnable'
5 | import History from './History'
6 | import { useI18n } from '@/lang'
7 | // import SyncHost from './SyncHost'
8 |
9 | export default memo(() => {
10 | const t = useI18n()
11 |
12 | const [host, setHost] = useState('')
13 |
14 | return (
15 |
19 | )
20 | })
21 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Theme/IsDynamicBg.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 | import { View } from 'react-native'
3 |
4 | import CheckBoxItem from '../../components/CheckBoxItem'
5 | import { createStyle } from '@/utils/tools'
6 | import { useI18n } from '@/lang'
7 | import { updateSetting } from '@/core/common'
8 | import { useSettingValue } from '@/store/setting/hook'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isDynamicBg = useSettingValue('theme.dynamicBg')
13 | const setIsDynamicBg = (isDynamicBg: boolean) => {
14 | updateSetting({ 'theme.dynamicBg': isDynamicBg })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | // marginBottom: 15,
32 | },
33 | })
34 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Theme/IsFontShadow.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 | import { View } from 'react-native'
3 |
4 | import CheckBoxItem from '../../components/CheckBoxItem'
5 | import { createStyle } from '@/utils/tools'
6 | import { useI18n } from '@/lang'
7 | import { updateSetting } from '@/core/common'
8 | import { useSettingValue } from '@/store/setting/hook'
9 |
10 | export default memo(() => {
11 | const t = useI18n()
12 | const isFontShadow = useSettingValue('theme.fontShadow')
13 | const setIsFontShadow = (isFontShadow: boolean) => {
14 | updateSetting({ 'theme.fontShadow': isFontShadow })
15 | }
16 |
17 | return (
18 |
19 |
24 |
25 | )
26 | })
27 |
28 | const styles = createStyle({
29 | content: {
30 | marginTop: 5,
31 | marginBottom: 15,
32 | },
33 | })
34 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Theme/IsHideBgDark.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 | import { View } from 'react-native'
3 |
4 | import CheckBoxItem from '../../components/CheckBoxItem'
5 | import { createStyle } from '@/utils/tools'
6 | import { useI18n } from '@/lang'
7 | import { updateSetting } from '@/core/common'
8 | import { useSettingValue } from '@/store/setting/hook'
9 | import { getTheme } from '@/theme/themes'
10 | import { applyTheme } from '@/core/theme'
11 | import settingState from '@/store/setting/state'
12 |
13 | export default memo(() => {
14 | const t = useI18n()
15 | const isHideBgDark = useSettingValue('theme.hideBgDark')
16 | const setIsAutoTheme = (isHideBgDark: boolean) => {
17 | updateSetting({ 'theme.hideBgDark': isHideBgDark })
18 | void getTheme().then((theme) => {
19 | if (!theme.isDark && !settingState.setting['common.isAutoTheme']) return
20 | applyTheme(theme)
21 | })
22 | }
23 |
24 | return (
25 |
26 |
31 |
32 | )
33 | })
34 |
35 | const styles = createStyle({
36 | content: {
37 | marginTop: 5,
38 | // marginBottom: 15,
39 | },
40 | })
41 |
--------------------------------------------------------------------------------
/src/screens/Home/Views/Setting/settings/Theme/index.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 |
3 | // import Section from '../../components/Section'
4 | import Theme from './Theme'
5 | import IsAutoTheme from './IsAutoTheme'
6 | import IsHideBgDark from './IsHideBgDark'
7 | import IsDynamicBg from './IsDynamicBg'
8 | import IsFontShadow from './IsFontShadow'
9 | // import { useI18n } from '@/lang/i18n'
10 |
11 | export default memo(() => {
12 | return (
13 | <>
14 |
15 |
16 |
17 |
18 |
19 | >
20 | )
21 | })
22 |
--------------------------------------------------------------------------------
/src/screens/Home/index.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useHorizontalMode } from '@/utils/hooks'
3 | import PageContent from '@/components/PageContent'
4 | import { setComponentId } from '@/core/common'
5 | import { COMPONENT_IDS } from '@/config/constant'
6 | import Vertical from './Vertical'
7 | import Horizontal from './Horizontal'
8 | import { navigations } from '@/navigation'
9 | import settingState from '@/store/setting/state'
10 |
11 | interface Props {
12 | componentId: string
13 | }
14 |
15 | export default ({ componentId }: Props) => {
16 | const isHorizontalMode = useHorizontalMode()
17 | useEffect(() => {
18 | setComponentId(COMPONENT_IDS.home, componentId)
19 |
20 | if (settingState.setting['player.startupPushPlayDetailScreen']) {
21 | navigations.pushPlayDetailScreen(componentId, true)
22 | }
23 | }, [])
24 |
25 | return {isHorizontalMode ? : }
26 | }
27 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Horizontal/MoreBtn/Btn.tsx:
--------------------------------------------------------------------------------
1 | import { TouchableOpacity } from 'react-native'
2 | import { Icon } from '@/components/common/Icon'
3 | import { createStyle } from '@/utils/tools'
4 | import { useTheme } from '@/store/theme/hook'
5 | import { scaleSizeW } from '@/utils/pixelRatio'
6 |
7 | export const BTN_WIDTH = scaleSizeW(32)
8 | export const BTN_ICON_SIZE = 22
9 |
10 | export default ({
11 | icon,
12 | color,
13 | onPress,
14 | }: {
15 | icon: string
16 | color?: string
17 | onPress: () => void
18 | }) => {
19 | const theme = useTheme()
20 | return (
21 |
26 |
27 |
28 | )
29 | }
30 |
31 | const styles = createStyle({
32 | cotrolBtn: {
33 | marginBottom: 5,
34 | justifyContent: 'center',
35 | alignItems: 'center',
36 |
37 | // backgroundColor: '#ccc',
38 | shadowOpacity: 1,
39 | textShadowRadius: 1,
40 | },
41 | })
42 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Horizontal/MoreBtn/MusicAddBtn.tsx:
--------------------------------------------------------------------------------
1 | import { useRef } from 'react'
2 | import MusicAddModal, { type MusicAddModalType } from '@/components/MusicAddModal'
3 | import playerState from '@/store/player/state'
4 | import Btn from './Btn'
5 |
6 | export default () => {
7 | const musicAddModalRef = useRef(null)
8 |
9 | const handleShowMusicAddModal = () => {
10 | const musicInfo = playerState.playMusicInfo.musicInfo
11 | if (!musicInfo) return
12 | musicAddModalRef.current?.show({
13 | musicInfo: 'progress' in musicInfo ? musicInfo.metadata.musicInfo : musicInfo,
14 | isMove: false,
15 | listId: playerState.playMusicInfo.listId!,
16 | })
17 | }
18 |
19 | return (
20 | <>
21 |
22 |
23 | >
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Horizontal/MoreBtn/TimeoutExitBtn.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useRef } from 'react'
2 | import TimeoutExitEditModal, {
3 | type TimeoutExitEditModalType,
4 | useTimeInfo,
5 | } from '@/components/TimeoutExitEditModal'
6 | import { useTheme } from '@/store/theme/hook'
7 | import Btn from './Btn'
8 |
9 | export default memo(() => {
10 | const theme = useTheme()
11 | const modalRef = useRef(null)
12 |
13 | const timeInfo = useTimeInfo()
14 |
15 | const handleShow = () => {
16 | modalRef.current?.show()
17 | }
18 |
19 | return (
20 | <>
21 |
26 |
27 | >
28 | )
29 | })
30 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Horizontal/MoreBtn/index.tsx:
--------------------------------------------------------------------------------
1 | import { createStyle } from '@/utils/tools'
2 | import { View } from 'react-native'
3 | import PlayModeBtn from './PlayModeBtn'
4 | import MusicAddBtn from './MusicAddBtn'
5 | import TimeoutExitBtn from './TimeoutExitBtn'
6 |
7 | export default () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 | )
15 | }
16 |
17 | const styles = createStyle({
18 | container: {
19 | flexShrink: 0,
20 | flexGrow: 0,
21 | flexDirection: 'column',
22 | alignItems: 'center',
23 | // backgroundColor: 'rgba(0,0,0,0.1)',
24 | justifyContent: 'center',
25 | position: 'absolute',
26 | height: '100%',
27 | left: 0,
28 | gap: 16,
29 | zIndex: 1,
30 | },
31 | })
32 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Horizontal/Player/Status.tsx:
--------------------------------------------------------------------------------
1 | // import { useLrcPlay } from '@/plugins/lyric'
2 | import { useStatusText } from '@/store/player/hook'
3 | // import { createStyle } from '@/utils/tools'
4 | import Text from '@/components/common/Text'
5 |
6 | export default () => {
7 | // const { text } = useLrcPlay()
8 | const statusText = useStatusText()
9 | // console.log('render status')
10 |
11 | // const status = playerStatus.isPlay ? text : playerStatus.statusText
12 |
13 | return (
14 |
15 | {statusText}
16 |
17 | )
18 | }
19 |
20 | // const styles = createStyle({
21 | // text: {
22 | // fontSize: 10,
23 | // },
24 | // })
25 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Horizontal/Player/index.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 | import { View } from 'react-native'
3 |
4 | // import Title from './components/Title'
5 | import { createStyle } from '@/utils/tools'
6 | import { NAV_SHEAR_NATIVE_IDS } from '@/config/constant'
7 | import PlayInfo from './PlayInfo'
8 | import ControlBtn from './ControlBtn'
9 | import { marginLeftRaw } from '../constant'
10 |
11 | export default memo(() => {
12 | return (
13 |
14 |
15 |
16 |
17 | )
18 | })
19 |
20 | const styles = createStyle({
21 | container: {
22 | flexShrink: 0,
23 | flexGrow: 1,
24 | marginLeft: marginLeftRaw,
25 | // paddingRight: 15,
26 | // backgroundColor: 'rgba(0,0,0,0.1)',
27 | },
28 | })
29 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Horizontal/components/Btn.tsx:
--------------------------------------------------------------------------------
1 | import { TouchableOpacity } from 'react-native'
2 | import { Icon } from '@/components/common/Icon'
3 | import { createStyle } from '@/utils/tools'
4 | import { useTheme } from '@/store/theme/hook'
5 | import { scaleSizeW } from '@/utils/pixelRatio'
6 |
7 | import { HEADER_HEIGHT } from '@/config/constant'
8 | export const BTN_WIDTH = scaleSizeW(HEADER_HEIGHT)
9 | export const BTN_ICON_SIZE = 20
10 |
11 | export default ({
12 | icon,
13 | size,
14 | color,
15 | onPress,
16 | onLongPress,
17 | }: {
18 | icon: string
19 | size?: number
20 | color?: string
21 | onPress: () => void
22 | onLongPress?: () => void
23 | }) => {
24 | const theme = useTheme()
25 | return (
26 |
32 |
33 |
34 | )
35 | }
36 |
37 | const styles = createStyle({
38 | cotrolBtn: {
39 | // marginLeft: 5,
40 | justifyContent: 'center',
41 | alignItems: 'center',
42 |
43 | // backgroundColor: '#ccc',
44 | shadowOpacity: 1,
45 | textShadowRadius: 1,
46 | },
47 | })
48 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Horizontal/components/CommentBtn.tsx:
--------------------------------------------------------------------------------
1 | import Btn from './Btn'
2 | import { navigations } from '@/navigation'
3 | import commonState from '@/store/common/state'
4 |
5 | export default () => {
6 | const handleShowCommentScreen = () => {
7 | navigations.pushCommentScreen(commonState.componentIds.playDetail!)
8 | }
9 |
10 | return
11 | }
12 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Horizontal/components/DesktopLyricBtn.tsx:
--------------------------------------------------------------------------------
1 | import Btn from './Btn'
2 | import { useSettingValue } from '@/store/setting/hook'
3 | import DesktopLyricEnable, { type DesktopLyricEnableType } from '@/components/DesktopLyricEnable'
4 | import { memo, useRef } from 'react'
5 | import { toggleDesktopLyricLock } from '@/core/desktopLyric'
6 | import { updateSetting } from '@/core/common'
7 | import settingState from '@/store/setting/state'
8 |
9 | export default memo(() => {
10 | const enabledLyric = useSettingValue('desktopLyric.enable')
11 | const desktopLyricEnableRef = useRef(null)
12 | const update = () => {
13 | desktopLyricEnableRef.current?.setEnabled(!enabledLyric)
14 | }
15 | const updateLock = () => {
16 | const isLock = !settingState.setting['desktopLyric.isLock']
17 | void toggleDesktopLyricLock(isLock).then(() => {
18 | updateSetting({ 'desktopLyric.isLock': isLock })
19 | })
20 | }
21 |
22 | return (
23 | <>
24 |
29 |
30 | >
31 | )
32 | })
33 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Horizontal/constant.ts:
--------------------------------------------------------------------------------
1 | import { scaleSizeW } from '@/utils/pixelRatio'
2 |
3 | export const marginLeftRaw = 15
4 | export const marginLeft = scaleSizeW(marginLeftRaw)
5 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Vertical/Player/components/MoreBtn/Btn.tsx:
--------------------------------------------------------------------------------
1 | import { TouchableOpacity } from 'react-native'
2 | import { Icon } from '@/components/common/Icon'
3 | import { createStyle } from '@/utils/tools'
4 | import { useTheme } from '@/store/theme/hook'
5 | import { scaleSizeW } from '@/utils/pixelRatio'
6 |
7 | export const BTN_WIDTH = scaleSizeW(36)
8 | export const BTN_ICON_SIZE = 24
9 |
10 | export default ({
11 | icon,
12 | color,
13 | onPress,
14 | onLongPress,
15 | }: {
16 | icon: string
17 | color?: string
18 | onPress: () => void
19 | onLongPress?: () => void
20 | }) => {
21 | const theme = useTheme()
22 | return (
23 |
29 |
30 |
31 | )
32 | }
33 |
34 | const styles = createStyle({
35 | cotrolBtn: {
36 | marginLeft: 5,
37 | justifyContent: 'center',
38 | alignItems: 'center',
39 |
40 | // backgroundColor: '#ccc',
41 | shadowOpacity: 1,
42 | textShadowRadius: 1,
43 | },
44 | })
45 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Vertical/Player/components/MoreBtn/CommentBtn.tsx:
--------------------------------------------------------------------------------
1 | import Btn from './Btn'
2 | import { navigations } from '@/navigation'
3 | import commonState from '@/store/common/state'
4 |
5 | export default () => {
6 | const handleShowCommentScreen = () => {
7 | navigations.pushCommentScreen(commonState.componentIds.playDetail!)
8 | }
9 |
10 | return
11 | }
12 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Vertical/Player/components/MoreBtn/DesktopLyricBtn.tsx:
--------------------------------------------------------------------------------
1 | import Btn from './Btn'
2 | import { useSettingValue } from '@/store/setting/hook'
3 | import DesktopLyricEnable, { type DesktopLyricEnableType } from '@/components/DesktopLyricEnable'
4 | import { memo, useRef } from 'react'
5 | import { toggleDesktopLyricLock } from '@/core/desktopLyric'
6 | import { updateSetting } from '@/core/common'
7 | import settingState from '@/store/setting/state'
8 |
9 | export default memo(() => {
10 | const enabledLyric = useSettingValue('desktopLyric.enable')
11 | const desktopLyricEnableRef = useRef(null)
12 | const update = () => {
13 | desktopLyricEnableRef.current?.setEnabled(!enabledLyric)
14 | }
15 | const updateLock = () => {
16 | const isLock = !settingState.setting['desktopLyric.isLock']
17 | void toggleDesktopLyricLock(isLock).then(() => {
18 | updateSetting({ 'desktopLyric.isLock': isLock })
19 | })
20 | }
21 |
22 | return (
23 | <>
24 |
29 |
30 | >
31 | )
32 | })
33 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Vertical/Player/components/MoreBtn/MusicAddBtn.tsx:
--------------------------------------------------------------------------------
1 | import { useRef } from 'react'
2 | import MusicAddModal, { type MusicAddModalType } from '@/components/MusicAddModal'
3 | import playerState from '@/store/player/state'
4 | import Btn from './Btn'
5 |
6 | export default () => {
7 | const musicAddModalRef = useRef(null)
8 |
9 | const handleShowMusicAddModal = () => {
10 | const musicInfo = playerState.playMusicInfo.musicInfo
11 | if (!musicInfo) return
12 | musicAddModalRef.current?.show({
13 | musicInfo: 'progress' in musicInfo ? musicInfo.metadata.musicInfo : musicInfo,
14 | isMove: false,
15 | listId: playerState.playMusicInfo.listId!,
16 | })
17 | }
18 |
19 | return (
20 | <>
21 |
22 |
23 | >
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Vertical/Player/components/MoreBtn/TimeoutExitBtn.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useRef } from 'react'
2 | import TimeoutExitEditModal, {
3 | type TimeoutExitEditModalType,
4 | useTimeInfo,
5 | } from '@/components/TimeoutExitEditModal'
6 | import { useTheme } from '@/store/theme/hook'
7 | import Btn from './Btn'
8 |
9 | export default memo(() => {
10 | const theme = useTheme()
11 | const modalRef = useRef(null)
12 |
13 | const timeInfo = useTimeInfo()
14 |
15 | const handleShow = () => {
16 | modalRef.current?.show()
17 | }
18 |
19 | return (
20 | <>
21 |
26 |
27 | >
28 | )
29 | })
30 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Vertical/Player/components/MoreBtn/index.tsx:
--------------------------------------------------------------------------------
1 | import { createStyle } from '@/utils/tools'
2 | import { View } from 'react-native'
3 | import PlayModeBtn from './PlayModeBtn'
4 | import MusicAddBtn from './MusicAddBtn'
5 | import DesktopLyricBtn from './DesktopLyricBtn'
6 | import CommentBtn from './CommentBtn'
7 |
8 | export default () => {
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 | )
17 | }
18 |
19 | const styles = createStyle({
20 | container: {
21 | // flexShrink: 0,
22 | // flexGrow: 0,
23 | width: '100%',
24 | flexDirection: 'row',
25 | alignItems: 'center',
26 | justifyContent: 'space-around',
27 | // backgroundColor: 'rgba(0,0,0,0.1)',
28 | },
29 | })
30 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Vertical/Player/components/Status.tsx:
--------------------------------------------------------------------------------
1 | // import { useLrcPlay } from '@/plugins/lyric'
2 | import { useStatusText } from '@/store/player/hook'
3 | import { createStyle } from '@/utils/tools'
4 | import Text from '@/components/common/Text'
5 |
6 | export default () => {
7 | // const { text } = useLrcPlay()
8 | const statusText = useStatusText()
9 | // console.log('render status')
10 |
11 | // const status = playerStatus.isPlay ? text : playerStatus.statusText
12 |
13 | return (
14 |
15 | {statusText}
16 |
17 | )
18 | }
19 |
20 | const styles = createStyle({
21 | text: {
22 | textAlign: 'center',
23 | },
24 | })
25 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Vertical/Player/index.tsx:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 | import { View } from 'react-native'
3 |
4 | // import Title from './components/Title'
5 | import MoreBtn from './components/MoreBtn'
6 | import PlayInfo from './components/PlayInfo'
7 | import ControlBtn from './components/ControlBtn'
8 | import { createStyle } from '@/utils/tools'
9 | import { NAV_SHEAR_NATIVE_IDS } from '@/config/constant'
10 |
11 | export default memo(() => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | )
19 | })
20 |
21 | const styles = createStyle({
22 | container: {
23 | flex: 0,
24 | width: '100%',
25 | // paddingTop: progressContentPadding,
26 | // marginTop: -progressContentPadding,
27 | // backgroundColor: 'rgba(0, 0, 0, .1)',
28 | paddingHorizontal: 15,
29 | paddingBottom: 15,
30 | paddingTop: 5,
31 | // backgroundColor: AppColors.primary,
32 | // backgroundColor: 'red',
33 | flexDirection: 'column',
34 | },
35 | status: {
36 | marginTop: 10,
37 | flexDirection: 'column',
38 | flex: 0,
39 | paddingLeft: 5,
40 | justifyContent: 'space-evenly',
41 | // backgroundColor: 'rgba(0, 0, 0, .1)',
42 | },
43 | })
44 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Vertical/components/Btn.tsx:
--------------------------------------------------------------------------------
1 | import { TouchableOpacity } from 'react-native'
2 | import { Icon } from '@/components/common/Icon'
3 | import { createStyle } from '@/utils/tools'
4 | import { scaleSizeH } from '@/utils/pixelRatio'
5 | import { HEADER_HEIGHT as _HEADER_HEIGHT } from '@/config/constant'
6 |
7 | export const HEADER_HEIGHT = scaleSizeH(_HEADER_HEIGHT)
8 |
9 | export default ({
10 | icon,
11 | color,
12 | onPress,
13 | }: {
14 | icon: string
15 | color?: string
16 | onPress: () => void
17 | }) => {
18 | return (
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | const styles = createStyle({
26 | button: {
27 | justifyContent: 'center',
28 | alignItems: 'center',
29 | height: '100%',
30 | flex: 0,
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/Vertical/components/TimeoutExitBtn.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useRef } from 'react'
2 | import TimeoutExitEditModal, {
3 | type TimeoutExitEditModalType,
4 | useTimeInfo,
5 | } from '@/components/TimeoutExitEditModal'
6 | import { useTheme } from '@/store/theme/hook'
7 | import Btn from './Btn'
8 |
9 | export default memo(() => {
10 | const theme = useTheme()
11 | const modalRef = useRef(null)
12 |
13 | const timeInfo = useTimeInfo()
14 |
15 | const handleShow = () => {
16 | modalRef.current?.show()
17 | }
18 |
19 | return (
20 | <>
21 |
26 |
27 | >
28 | )
29 | })
30 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/components/SettingPopup/settings/SettingLyricProgress.tsx:
--------------------------------------------------------------------------------
1 | import { View } from 'react-native'
2 | // import Text from '@/components/common/Text'
3 | import { useSettingValue } from '@/store/setting/hook'
4 | import { updateSetting } from '@/core/common'
5 | import { useI18n } from '@/lang'
6 | import CheckBox from '@/components/common/CheckBox'
7 | import styles from './style'
8 |
9 | export default () => {
10 | const t = useI18n()
11 | const isShowLyricProgressSetting = useSettingValue('playDetail.isShowLyricProgressSetting')
12 | const setShowLyricProgressSetting = (showLyricProgressSetting: boolean) => {
13 | updateSetting({ 'playDetail.isShowLyricProgressSetting': showLyricProgressSetting })
14 | }
15 |
16 | return (
17 |
18 |
19 |
20 |
26 |
27 |
28 |
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/components/SettingPopup/settings/style.ts:
--------------------------------------------------------------------------------
1 | import { createStyle } from '@/utils/tools'
2 |
3 | export default createStyle({
4 | container: {
5 | paddingTop: 5,
6 | paddingLeft: 15,
7 | paddingRight: 15,
8 | paddingBottom: 15,
9 | alignItems: 'flex-start',
10 | },
11 | // title: {
12 |
13 | // },
14 | label: {
15 | width: 50,
16 | textAlign: 'center',
17 | },
18 | content: {
19 | flexGrow: 0,
20 | flexShrink: 1,
21 | flexDirection: 'row',
22 | flexWrap: 'nowrap',
23 | alignItems: 'center',
24 | },
25 | list: {
26 | flexGrow: 0,
27 | flexShrink: 1,
28 | flexDirection: 'row',
29 | flexWrap: 'wrap',
30 | paddingTop: 5,
31 | },
32 | })
33 |
--------------------------------------------------------------------------------
/src/screens/PlayDetail/index.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | // import { View, StyleSheet } from 'react-native'
3 | import { useHorizontalMode } from '@/utils/hooks'
4 |
5 | import Vertical from './Vertical'
6 | import Horizontal from './Horizontal'
7 | import PageContent from '@/components/PageContent'
8 | import StatusBar from '@/components/common/StatusBar'
9 | import { setComponentId } from '@/core/common'
10 | import { COMPONENT_IDS } from '@/config/constant'
11 |
12 | export default ({ componentId }: { componentId: string }) => {
13 | const isHorizontalMode = useHorizontalMode()
14 |
15 | useEffect(() => {
16 | setComponentId(COMPONENT_IDS.playDetail, componentId)
17 | }, [])
18 |
19 | return (
20 |
21 |
22 | {isHorizontalMode ? (
23 |
24 | ) : (
25 |
26 | )}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/screens/SonglistDetail/state.ts:
--------------------------------------------------------------------------------
1 | import { type ListInfoItem } from '@/store/songlist/state'
2 | import { createContext, useContext } from 'react'
3 |
4 | export const ListInfoContext = createContext({
5 | id: '',
6 | author: '',
7 | name: '',
8 | source: 'kw',
9 | })
10 |
11 | export const useListInfo = () => {
12 | return useContext(ListInfoContext)
13 | }
14 |
--------------------------------------------------------------------------------
/src/screens/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Home } from './Home'
2 | export { default as PlayDetail } from './PlayDetail'
3 | export { default as SonglistDetail } from './SonglistDetail'
4 | export { default as Comment } from './Comment'
5 | // export { default as Setting } from './Setting'
6 | // export { default as LoginScreen } from './LoginScreen/LoginScreen'
7 | // export { default as SingleAppScreen } from './SingleAppScreen/SingleAppScreen'
8 | // export { default as Tab1Screen } from './Tab1Screen/Tab1Screen'
9 | // export { default as Tab2Screen } from './Tab2Screen/Tab2Screen'
10 |
--------------------------------------------------------------------------------
/src/store/Provider/Provider.tsx:
--------------------------------------------------------------------------------
1 | // import { PureComponent } from 'react'
2 | // import PropTypes from 'prop-types'
3 | // import { Provider } from 'react-redux'
4 | // import { store } from '../store'
5 |
6 | // class AppStoreProvider extends PureComponent<{ children: any }> {
7 | // getChildContext() {
8 | // return {
9 | // store,
10 | // }
11 | // }
12 |
13 | // static childContextTypes = {
14 | // store: PropTypes.shape({}),
15 | // }
16 |
17 | // render() {
18 | // return (
19 | //
20 | // {this.props.children}
21 | //
22 | // )
23 | // }
24 | // }
25 |
26 | // export default AppStoreProvider
27 |
--------------------------------------------------------------------------------
/src/store/Provider/ThemeProvider.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useEffect, useState } from 'react'
2 |
3 | import themeState, { ThemeContext } from '../theme/state'
4 |
5 | export default memo(({ children }: { children: React.ReactNode }) => {
6 | const [theme, setTheme] = useState(themeState.theme)
7 |
8 | useEffect(() => {
9 | const handleUpdateTheme = (theme: LX.ActiveTheme) => {
10 | requestAnimationFrame(() => {
11 | setTheme(theme)
12 | })
13 | }
14 | global.state_event.on('themeUpdated', handleUpdateTheme)
15 | return () => {
16 | global.state_event.off('themeUpdated', handleUpdateTheme)
17 | }
18 | }, [])
19 |
20 | return {children}
21 | })
22 |
--------------------------------------------------------------------------------
/src/store/Provider/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Provider } from './ThemeProvider'
2 |
--------------------------------------------------------------------------------
/src/store/common/state.ts:
--------------------------------------------------------------------------------
1 | import { type NAV_ID_Type, type COMPONENT_IDS } from '@/config/constant'
2 |
3 | export interface InitState {
4 | fontSize: number
5 | statusbarHeight: number
6 | componentIds: Partial>
7 | navActiveId: NAV_ID_Type
8 | lastNavActiveId: NAV_ID_Type
9 | sourceNames: Record
10 | bgPic: string | null
11 | }
12 |
13 | const initData = {}
14 |
15 | const state: InitState = {
16 | fontSize: global.lx.fontSize,
17 | statusbarHeight: 0,
18 | componentIds: {},
19 | navActiveId: 'nav_search',
20 | lastNavActiveId: 'nav_search',
21 | sourceNames: initData as InitState['sourceNames'],
22 | bgPic: null,
23 | }
24 |
25 | export default state
26 |
--------------------------------------------------------------------------------
/src/store/dislikeList/action.ts:
--------------------------------------------------------------------------------
1 | import { state } from './state'
2 | import { SPLIT_CHAR } from '@/config/constant'
3 | import { event } from './event'
4 |
5 | export const hasDislike = (info: LX.Music.MusicInfo | LX.Download.ListItem) => {
6 | if ('progress' in info) info = info.metadata.musicInfo
7 | const name =
8 | info.name
9 | ?.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS)
10 | .toLocaleLowerCase()
11 | .trim() ?? ''
12 | const singer =
13 | info.singer
14 | ?.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS)
15 | .toLocaleLowerCase()
16 | .trim() ?? ''
17 |
18 | return (
19 | state.dislikeInfo.musicNames.has(name) ||
20 | state.dislikeInfo.singerNames.has(singer) ||
21 | state.dislikeInfo.names.has(`${name}${SPLIT_CHAR.DISLIKE_NAME}${singer}`)
22 | )
23 | }
24 |
25 | export const setDislikeInfo = (dislikeInfo: LX.Dislike.DislikeInfo) => {
26 | state.dislikeInfo.rules = dislikeInfo.rules
27 | state.dislikeInfo.names = dislikeInfo.names
28 | state.dislikeInfo.musicNames = dislikeInfo.musicNames
29 | state.dislikeInfo.singerNames = dislikeInfo.singerNames
30 | event.dislike_changed()
31 | }
32 |
--------------------------------------------------------------------------------
/src/store/dislikeList/event.ts:
--------------------------------------------------------------------------------
1 | import Event from '@/event/Event'
2 |
3 | class DislikeEvent extends Event {
4 | dislike_changed() {
5 | this.emit('dislike_changed')
6 | }
7 | }
8 |
9 | type EventMethods = Omit
10 |
11 | declare class EventType extends DislikeEvent {
12 | on(event: K, listener: EventMethods[K]): any
13 | off(event: K, listener: EventMethods[K]): any
14 | }
15 |
16 | type DislikeEventTypes = Omit>
17 |
18 | export const event: DislikeEventTypes = new DislikeEvent()
19 |
--------------------------------------------------------------------------------
/src/store/dislikeList/hook.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import { state } from './state'
3 | import { event } from './event'
4 |
5 | export const useRuleNum = () => {
6 | const [num, setNum] = useState(
7 | state.dislikeInfo.musicNames.size +
8 | state.dislikeInfo.singerNames.size +
9 | state.dislikeInfo.names.size
10 | )
11 |
12 | useEffect(() => {
13 | const handleUpdate = () => {
14 | setNum(
15 | state.dislikeInfo.musicNames.size +
16 | state.dislikeInfo.singerNames.size +
17 | state.dislikeInfo.names.size
18 | )
19 | }
20 | event.on('dislike_changed', handleUpdate)
21 | return () => {
22 | event.off('dislike_changed', handleUpdate)
23 | }
24 | }, [])
25 |
26 | return num
27 | }
28 |
--------------------------------------------------------------------------------
/src/store/dislikeList/index.ts:
--------------------------------------------------------------------------------
1 | export * as action from './action'
2 | export * from './state'
3 | export * from './hook'
4 |
--------------------------------------------------------------------------------
/src/store/dislikeList/state.ts:
--------------------------------------------------------------------------------
1 | interface InitState {
2 | dislikeInfo: LX.Dislike.DislikeInfo
3 | }
4 | const state: InitState = {
5 | dislikeInfo: {
6 | names: new Set(),
7 | musicNames: new Set(),
8 | singerNames: new Set(),
9 | rules: '',
10 | },
11 | }
12 |
13 | export { state }
14 |
--------------------------------------------------------------------------------
/src/store/hotSearch/action.ts:
--------------------------------------------------------------------------------
1 | import state, { type Source } from './state'
2 |
3 | export type Lists = Array<{ source: LX.OnlineSource; list: string[] }>
4 |
5 | const setList = (source: LX.OnlineSource, list: string[]): string[] => {
6 | const l = (state.sourceList[source] = list.slice(0, 20))
7 | return l
8 | }
9 |
10 | const setLists = (lists: Lists): string[] => {
11 | let wordsMap = new Map()
12 | for (const { source, list } of lists) {
13 | if (!state.sourceList[source]?.length) state.sourceList[source] = list.slice(0, 20)
14 | for (let item of list) {
15 | item = item.trim()
16 | wordsMap.set(item, (wordsMap.get(item) ?? 0) + 1)
17 | }
18 | }
19 | const wordsMapArr = Array.from(wordsMap)
20 | wordsMapArr.sort((a, b) => a[0].localeCompare(b[0]))
21 | wordsMapArr.sort((a, b) => b[1] - a[1])
22 | const words = wordsMapArr.map((item) => item[0])
23 | return (state.sourceList.all = words.slice(0, state.sources.length * 10))
24 | }
25 |
26 | export default {
27 | setList(source: Source, list: string[] | Lists) {
28 | if (source == 'all') {
29 | return setLists(list as Lists)
30 | }
31 | return setList(source, list as string[])
32 | },
33 | clearList(source: Source) {
34 | state.sourceList[source] = []
35 | },
36 | }
37 |
--------------------------------------------------------------------------------
/src/store/hotSearch/state.ts:
--------------------------------------------------------------------------------
1 | import musicSdk from '@/utils/musicSdk'
2 |
3 | // import { deduplicationList } from '@common/utils/renderer'
4 |
5 | export declare type Source = LX.OnlineSource | 'all'
6 |
7 | type SourceLists = Partial>
8 |
9 | export interface InitState {
10 | sources: Source[]
11 | sourceList: SourceLists
12 | }
13 |
14 | const state: InitState = {
15 | sources: [],
16 | sourceList: {
17 | all: [],
18 | },
19 | }
20 |
21 | for (const source of musicSdk.sources) {
22 | if (!musicSdk[source.id as LX.OnlineSource]?.hotSearch) continue
23 | state.sources.push(source.id as LX.OnlineSource)
24 | state.sourceList[source.id as LX.OnlineSource] = []
25 | }
26 | state.sources.push('all')
27 |
28 | export default state
29 |
--------------------------------------------------------------------------------
/src/store/index.ts:
--------------------------------------------------------------------------------
1 | export const useGetter = () => {}
2 |
--------------------------------------------------------------------------------
/src/store/leaderboard/state.ts:
--------------------------------------------------------------------------------
1 | import music from '@/utils/musicSdk'
2 |
3 | export declare type Source = LX.OnlineSource
4 |
5 | export declare interface BoardItem {
6 | id: string
7 | name: string
8 | bangid: string
9 | }
10 | export declare interface Board {
11 | list: BoardItem[]
12 | source: LX.OnlineSource
13 | }
14 | type Boards = Partial>
15 |
16 | export declare interface ListDetailInfo {
17 | list: LX.Music.MusicInfoOnline[]
18 | total: number
19 | maxPage: number
20 | page: number
21 | source: LX.OnlineSource | null
22 | limit: number
23 | key: string | null
24 | id: string
25 | }
26 |
27 | export interface InitState {
28 | sources: LX.OnlineSource[]
29 | boards: Boards
30 | listDetailInfo: ListDetailInfo
31 | }
32 |
33 | const state: InitState = {
34 | sources: [],
35 | boards: {},
36 | listDetailInfo: {
37 | list: [],
38 | total: 0,
39 | page: 1,
40 | maxPage: 1,
41 | limit: 30,
42 | key: null,
43 | source: null,
44 | id: '',
45 | },
46 | }
47 |
48 | for (const source of music.sources) {
49 | if (!music[source.id as LX.OnlineSource]?.leaderboard?.getBoards) continue
50 | state.sources.push(source.id as LX.OnlineSource)
51 | }
52 |
53 | export default state
54 |
--------------------------------------------------------------------------------
/src/store/list/state.ts:
--------------------------------------------------------------------------------
1 | import { LIST_IDS } from '@/config/constant'
2 |
3 | export interface InitState {
4 | allMusicList: Map
5 | defaultList: LX.List.MyDefaultListInfo
6 | loveList: LX.List.MyLoveListInfo
7 | tempList: LX.List.MyTempListInfo
8 | userList: LX.List.UserListInfo[]
9 | activeListId: string
10 |
11 | allList: Array
12 |
13 | tempListMeta: {
14 | id: string
15 | }
16 |
17 | fetchingListStatus: Record
18 | }
19 |
20 | const state: InitState = {
21 | allMusicList: new Map(),
22 | defaultList: {
23 | id: LIST_IDS.DEFAULT,
24 | name: '试听列表',
25 | },
26 | loveList: {
27 | id: LIST_IDS.LOVE,
28 | name: '我的收藏',
29 | },
30 | tempList: {
31 | id: LIST_IDS.TEMP,
32 | name: '临时列表',
33 | meta: {},
34 | },
35 | userList: [],
36 | activeListId: '',
37 | allList: [],
38 | tempListMeta: {
39 | id: '',
40 | },
41 | fetchingListStatus: {},
42 | }
43 |
44 | state.allList = [state.defaultList, state.loveList]
45 |
46 | export default state
47 |
--------------------------------------------------------------------------------
/src/store/search/state.ts:
--------------------------------------------------------------------------------
1 | export type SearchType = 'music' | 'songlist'
2 |
3 | export interface InitState {
4 | temp_source: 'kw'
5 | // temp_source: LX.OnlineSource
6 | searchType: SearchType
7 | searchText: string
8 | tipListInfo: {
9 | text: string
10 | source: 'kw'
11 | list: string[]
12 | }
13 | historyList: string[]
14 | }
15 |
16 | const state: InitState = {
17 | temp_source: 'kw',
18 | searchType: 'music',
19 | searchText: '',
20 | tipListInfo: {
21 | text: '',
22 | source: 'kw',
23 | list: [],
24 | },
25 | historyList: [],
26 | }
27 |
28 | export default state
29 |
--------------------------------------------------------------------------------
/src/store/setting/action.ts:
--------------------------------------------------------------------------------
1 | import { updateSetting as mergeSetting } from '@/config/setting'
2 | import state from './state'
3 |
4 | export default {
5 | // mergeSetting(newSetting: Partial) {
6 | // for (const [key, value] of Object.entries(newSetting)) {
7 | // // @ts-expect-error
8 | // state[key] = value
9 | // }
10 | // },
11 | initSetting(newSetting: LX.AppSetting) {
12 | state.setting = newSetting
13 | },
14 | updateSetting(newSetting: Partial) {
15 | const result = mergeSetting(newSetting)
16 | state.setting = result.setting
17 | global.state_event.configUpdated(result.updatedSettingKeys, result.updatedSetting)
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/src/store/setting/hook.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import state from './state'
3 |
4 | export const useSetting = () => {
5 | const [setting, updateSetting] = useState(state.setting)
6 |
7 | useEffect(() => {
8 | const handleUpdate = () => {
9 | updateSetting(state.setting)
10 | }
11 | global.state_event.on('configUpdated', handleUpdate)
12 | return () => {
13 | global.state_event.off('configUpdated', handleUpdate)
14 | }
15 | }, [])
16 |
17 | return setting
18 | }
19 |
20 | export const useSettingValue = (key: T): LX.AppSetting[T] => {
21 | const [value, update] = useState(state.setting[key])
22 |
23 | useEffect(() => {
24 | const handleUpdate = (keys: Array) => {
25 | if (!keys.includes(key)) return
26 | update(state.setting[key])
27 | }
28 | global.state_event.on('configUpdated', handleUpdate)
29 | return () => {
30 | global.state_event.off('configUpdated', handleUpdate)
31 | }
32 | }, [key])
33 |
34 | return value
35 | }
36 |
--------------------------------------------------------------------------------
/src/store/setting/state.ts:
--------------------------------------------------------------------------------
1 | import defaultSetting from '@/config/defaultSetting'
2 |
3 | interface InitState {
4 | setting: LX.AppSetting
5 | }
6 |
7 | const state: InitState = {
8 | setting: { ...defaultSetting },
9 | }
10 |
11 | export default state
12 |
--------------------------------------------------------------------------------
/src/store/sync/action.ts:
--------------------------------------------------------------------------------
1 | import state from './state'
2 |
3 | export default {
4 | setStatus(info: LX.Sync.Status) {
5 | state.status.status = info.status
6 | state.status.message = info.message
7 |
8 | global.state_event.syncStatusUpdated({ ...state.status })
9 | },
10 | setMessage(message: LX.Sync.Status['message']) {
11 | state.status.message = message
12 |
13 | global.state_event.syncStatusUpdated({ ...state.status })
14 | },
15 | setServerInfo(name: string, type: keyof LX.Sync.ModeTypes) {
16 | state.serverName = name
17 | state.type = type
18 | },
19 | setSyncModeComponentId(id: string) {
20 | state.syncModeComponentId = id
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/src/store/sync/hook.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import state from './state'
3 |
4 | export const useStatus = () => {
5 | const [value, update] = useState(state.status)
6 |
7 | useEffect(() => {
8 | global.state_event.on('syncStatusUpdated', update)
9 | return () => {
10 | global.state_event.off('syncStatusUpdated', update)
11 | }
12 | }, [])
13 |
14 | return value
15 | }
16 |
--------------------------------------------------------------------------------
/src/store/sync/state.ts:
--------------------------------------------------------------------------------
1 | interface InitState {
2 | status: LX.Sync.Status
3 | serverName: string
4 | type: keyof LX.Sync.ModeTypes
5 | syncModeComponentId: string
6 | }
7 | const state: InitState = {
8 | status: {
9 | status: false,
10 | message: '',
11 | },
12 | serverName: '',
13 | type: 'list',
14 | syncModeComponentId: '',
15 | }
16 |
17 | export default state
18 |
--------------------------------------------------------------------------------
/src/store/theme/action.ts:
--------------------------------------------------------------------------------
1 | import { buildActiveThemeColors } from '@/theme/themes'
2 | import state from './state'
3 |
4 | export default {
5 | setTheme(theme: LX.Theme) {
6 | state.theme = buildActiveThemeColors(theme)
7 | // ThemeContext.displayName
8 | global.state_event.themeUpdated(state.theme)
9 | },
10 | setShouldUseDarkColors(shouldUseDarkColors: boolean) {
11 | if (state.shouldUseDarkColors == shouldUseDarkColors) return
12 | state.shouldUseDarkColors = shouldUseDarkColors
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/src/store/userApi/action.ts:
--------------------------------------------------------------------------------
1 | import { state } from './state'
2 | import { event } from './event'
3 |
4 | export const setStatus = (
5 | status: LX.UserApi.UserApiStatus['status'],
6 | message: LX.UserApi.UserApiStatus['message']
7 | ) => {
8 | state.status.status = status
9 | state.status.message = message
10 |
11 | event.status_changed({ status, message })
12 | }
13 |
14 | export const setUserApiList = (list: LX.UserApi.UserApiInfo[]) => {
15 | state.list = list
16 |
17 | event.list_changed([...list])
18 | }
19 |
20 | export const addUserApi = (info: LX.UserApi.UserApiInfo) => {
21 | state.list.push(info)
22 |
23 | event.list_changed([...state.list])
24 | }
25 |
26 | export const setUserApiAllowShowUpdateAlert = (id: string, enable: boolean) => {
27 | const targetIndex = state.list.findIndex((api) => api.id == id)
28 | if (targetIndex < 0) return
29 | state.list[targetIndex].allowShowUpdateAlert = enable
30 | state.list.splice(targetIndex, 1, { ...state.list[targetIndex] })
31 |
32 | event.list_changed([...state.list])
33 | }
34 |
--------------------------------------------------------------------------------
/src/store/userApi/event.ts:
--------------------------------------------------------------------------------
1 | import Event from '@/event/Event'
2 |
3 | class UserApiEvent extends Event {
4 | status_changed(status: { status: boolean; message?: string }) {
5 | this.emit('status_changed', status)
6 | }
7 |
8 | list_changed(list: LX.UserApi.UserApiInfo[]) {
9 | this.emit('list_changed', list)
10 | }
11 | }
12 |
13 | type EventMethods = Omit
14 |
15 | declare class EventType extends UserApiEvent {
16 | on(event: K, listener: EventMethods[K]): any
17 | off(event: K, listener: EventMethods[K]): any
18 | }
19 |
20 | type UserApiEventTypes = Omit>
21 |
22 | export const event: UserApiEventTypes = new UserApiEvent()
23 |
--------------------------------------------------------------------------------
/src/store/userApi/hook.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import { state } from './state'
3 | import { event } from './event'
4 |
5 | export const useStatus = () => {
6 | const [value, update] = useState(state.status)
7 |
8 | useEffect(() => {
9 | event.on('status_changed', update)
10 | return () => {
11 | event.off('status_changed', update)
12 | }
13 | }, [])
14 |
15 | return value
16 | }
17 |
18 | export const useUserApiList = () => {
19 | const [value, update] = useState(state.list)
20 |
21 | useEffect(() => {
22 | event.on('list_changed', update)
23 | return () => {
24 | event.off('list_changed', update)
25 | }
26 | }, [])
27 |
28 | return value
29 | }
30 |
--------------------------------------------------------------------------------
/src/store/userApi/index.ts:
--------------------------------------------------------------------------------
1 | export * as action from './action'
2 | export * from './state'
3 | export * from './hook'
4 |
--------------------------------------------------------------------------------
/src/store/userApi/state.ts:
--------------------------------------------------------------------------------
1 | interface InitState {
2 | list: LX.UserApi.UserApiInfo[]
3 | status: {
4 | status: boolean
5 | message?: string
6 | }
7 | apis: Partial
8 | }
9 | const state: InitState = {
10 | list: [],
11 | status: {
12 | status: false,
13 | message: 'initing',
14 | },
15 | apis: {},
16 | }
17 |
18 | export { state }
19 |
--------------------------------------------------------------------------------
/src/store/version/action.ts:
--------------------------------------------------------------------------------
1 | import state, { type InitState } from './state'
2 |
3 | export default {
4 | setVersionInfo(info: Partial) {
5 | Object.assign(state.versionInfo, info)
6 | global.state_event.versionInfoUpdated({ ...state.versionInfo })
7 | },
8 | setIgnoreVersion(version: InitState['ignoreVersion']) {
9 | state.ignoreVersion = version
10 | global.state_event.versionInfoIgnoreVersionUpdated(version)
11 | },
12 | setProgress(info: InitState['progress']) {
13 | if (state.progress.total != info.total) {
14 | state.progress.total = info.total
15 | }
16 | state.progress.current = info.current
17 |
18 | global.state_event.versionDownloadProgressUpdated({ ...state.progress })
19 | },
20 | setVisibleModal(visible: boolean) {
21 | if (state.showModal == visible) return
22 | state.showModal = visible
23 | },
24 | }
25 |
--------------------------------------------------------------------------------
/src/store/version/state.ts:
--------------------------------------------------------------------------------
1 | import { version } from '../../../package.json'
2 |
3 | export interface ProgressInfo {
4 | total: number
5 | current: number
6 | }
7 |
8 | export interface VersionInfo {
9 | version: string
10 | desc: string
11 | history?: LX.VersionInfo[]
12 | }
13 |
14 | export interface InitState {
15 | showModal: boolean
16 | versionInfo: {
17 | version: string
18 | newVersion: VersionInfo | null
19 | showModal: boolean
20 | isUnknown: boolean
21 | isLatest: boolean
22 | reCheck: boolean
23 | status: LX.UpdateStatus
24 | }
25 | ignoreVersion: string | null
26 | progress: ProgressInfo
27 | }
28 |
29 | const state: InitState = {
30 | showModal: false,
31 | versionInfo: {
32 | version,
33 | newVersion: null,
34 | showModal: false,
35 | reCheck: false,
36 | isUnknown: false,
37 | isLatest: false,
38 | status: 'checking',
39 | },
40 | ignoreVersion: null,
41 | progress: {
42 | total: 0,
43 | current: 0,
44 | },
45 | }
46 |
47 | export default state
48 |
--------------------------------------------------------------------------------
/src/theme/Typography.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Typography:
3 | * This contains all the typography config for the application
4 | * #Note: color and font size are defaulted as they can be overridden
5 | * as required.
6 | */
7 |
8 | export const FontWeights = {
9 | Bold: {
10 | fontFamily: 'SFProDisplay-Bold',
11 | color: '#000',
12 | },
13 | Regular: {
14 | fontFamily: 'SFProDisplay-Regular',
15 | color: '#000',
16 | },
17 | Light: {
18 | fontFamily: 'SFProDisplay-Light',
19 | color: '#000',
20 | },
21 | }
22 |
23 | export const FontSizes = {
24 | Heading: {
25 | fontSize: 32,
26 | },
27 | SubHeading: {
28 | fontSize: 24,
29 | },
30 | Label: {
31 | fontSize: 20,
32 | },
33 | Body: {
34 | fontSize: 16,
35 | },
36 | Caption: {
37 | fontSize: 14,
38 | },
39 | }
40 |
41 | export const BorderWidths = {
42 | normal: 0.4,
43 | normal1: 0.6,
44 | normal2: 1,
45 | normal3: 1.4,
46 | normal4: 2,
47 | }
48 |
49 | export const BorderRadius = {
50 | normal: 4,
51 | }
52 |
--------------------------------------------------------------------------------
/src/theme/index.js:
--------------------------------------------------------------------------------
1 | import { AppColors, MaterialColors } from './Colors'
2 | import { FontWeights, FontSizes, BorderWidths, BorderRadius } from './Typography'
3 |
4 | export { default as Themes } from './themes'
5 | export { AppColors, MaterialColors, FontWeights, FontSizes, BorderWidths, BorderRadius }
6 |
--------------------------------------------------------------------------------
/src/theme/themes/images/china_ink.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/theme/themes/images/china_ink.jpg
--------------------------------------------------------------------------------
/src/theme/themes/images/jqbg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/theme/themes/images/jqbg.jpg
--------------------------------------------------------------------------------
/src/theme/themes/images/landingMoon2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/theme/themes/images/landingMoon2.png
--------------------------------------------------------------------------------
/src/theme/themes/images/myzcbg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/theme/themes/images/myzcbg.jpg
--------------------------------------------------------------------------------
/src/theme/themes/images/xnkl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikunshare/ikun-music-mobile/367c64a36b3154f4954d7a3f60a15bda38263bb8/src/theme/themes/images/xnkl.png
--------------------------------------------------------------------------------
/src/types/common.d.ts:
--------------------------------------------------------------------------------
1 | // import './app_setting'
2 |
3 | declare namespace LX {
4 | type OnlineSource = 'kw' | 'kg' | 'tx' | 'wy' | 'mg'
5 | type Source = OnlineSource | 'local'
6 | type Quality = '128k' | '320k' | 'flac' | 'hires' | 'atmos' | 'atmos_plus' | 'master'
7 | type QualityList = Partial>
8 |
9 | type ShareType = 'system' | 'clipboard'
10 |
11 | type UpdateStatus = 'downloaded' | 'downloading' | 'error' | 'checking' | 'idle'
12 | interface VersionInfo {
13 | version: string
14 | desc: string
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/types/config_files.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace LX {
2 | namespace ConfigFile {
3 | interface MyListInfoPart {
4 | type: 'playListPart_v2'
5 | data: LX.List.MyDefaultListInfoFull | LX.List.MyLoveListInfoFull | LX.List.UserListInfoFull
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/types/dislike_list.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace LX {
2 | namespace Dislike {
3 | // interface ListItemMusicText {
4 | // id?: string
5 | // // type: 'music'
6 | // name: string | null
7 | // singer: string | null
8 | // }
9 | // interface ListItemMusic {
10 | // id?: number
11 | // type: 'musicId'
12 | // musicId: string
13 | // meta: LX.Music.MusicInfo
14 | // }
15 | // type ListItem = ListItemMusicText
16 | // type ListItem = string
17 | // type ListItem = ListItemMusic | ListItemMusicText
18 |
19 | interface DislikeMusicInfo {
20 | name: string
21 | singer: string
22 | }
23 |
24 | type DislikeRules = string
25 |
26 | interface DislikeInfo {
27 | // musicIds: Set
28 | names: Set
29 | musicNames: Set
30 | singerNames: Set
31 | // list: LX.Dislike.ListItem[]
32 | rules: DislikeRules
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/types/dislike_list_sync.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace LX {
2 | namespace Sync {
3 | namespace Dislike {
4 | interface ListInfo {
5 | lastSyncDate?: number
6 | snapshotKey: string
7 | }
8 |
9 | interface SyncActionBase {
10 | action: A
11 | }
12 | interface SyncActionData extends SyncActionBase {
13 | data: D
14 | }
15 | type SyncAction = D extends undefined
16 | ? SyncActionBase
17 | : SyncActionData
18 | type ActionList =
19 | | SyncAction<'dislike_data_overwrite', LX.Dislike.DislikeRules>
20 | | SyncAction<'dislike_music_add', LX.Dislike.DislikeMusicInfo[]>
21 | | SyncAction<'dislike_music_clear'>
22 |
23 | type SyncMode =
24 | | 'merge_local_remote'
25 | | 'merge_remote_local'
26 | | 'overwrite_local_remote'
27 | | 'overwrite_remote_local'
28 | // | 'none'
29 | | 'cancel'
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/types/utils.d.ts:
--------------------------------------------------------------------------------
1 | type MakeOptional = Omit & Partial>
2 |
3 | type DeepPartial = {
4 | [P in keyof T]?: T[P] extends object ? DeepPartial : T[P]
5 | }
6 |
7 | type Modify = Omit & R
8 |
9 | type MakeArrayItemReadOnly = { [K in keyof T]: Readonly }
10 |
11 | type ForwardRefFn = (
12 | p: React.PropsWithChildren
& React.RefAttributes
13 | ) => React.ReactNode | null
14 |
15 | // type UndefinedOrNever = undefined
16 | type Actions = {
17 | [U in T as U['action']]: 'data' extends keyof U ? U['data'] : undefined
18 | }
19 |
20 | type WarpPromiseValue = T extends (...args: infer P) => Promise
21 | ? (...args: P) => Promise
22 | : T extends (...args: infer P2) => infer R2
23 | ? (...args: P2) => Promise
24 | : Promise
25 |
26 | type WarpPromiseRecord> = {
27 | [K in keyof T]: WarpPromiseValue
28 | }
29 |
--------------------------------------------------------------------------------
/src/utils/bootLog.ts:
--------------------------------------------------------------------------------
1 | const logs: string[] = []
2 |
3 | export const bootLog = (...msgs: any[]) => {
4 | logs.push(msgs.map((m) => (typeof m == 'string' ? m : JSON.stringify(m))).join(' '))
5 | }
6 |
7 | export const getBootLog = () => {
8 | return logs.join('\n')
9 | }
10 |
--------------------------------------------------------------------------------
/src/utils/errorHandle.ts:
--------------------------------------------------------------------------------
1 | import { Alert } from 'react-native'
2 | // import { exitApp } from '@/utils/common'
3 | import { setJSExceptionHandler, setNativeExceptionHandler } from 'react-native-exception-handler'
4 | import { log } from '@/utils/log'
5 |
6 | const errorHandler = (e: Error, isFatal: boolean) => {
7 | if (isFatal) {
8 | Alert.alert(
9 | '💥Unexpected error occurred💥',
10 | `
11 | 应用出 bug 了😭,以下是错误异常信息。请截图并在 GitHub 反馈(并附上刚才你进行了什么操作)。现在应用可能会出现异常,若出现异常请尝试强制结束应用后重新启动!
12 |
13 | Error:
14 | ${isFatal ? 'Fatal:' : ''} ${e.name} ${e.message}
15 | `,
16 | [
17 | {
18 | text: '关闭 (Close)',
19 | onPress: () => {
20 | // exitApp()
21 | },
22 | },
23 | ]
24 | )
25 | }
26 | log.error(e.stack)
27 | }
28 |
29 | if (process.env.NODE_ENV !== 'development') {
30 | setJSExceptionHandler(errorHandler)
31 |
32 | setNativeExceptionHandler((errorString) => {
33 | log.error(errorString)
34 | console.log('+++++', errorString, '+++++')
35 | }, false)
36 | }
37 |
--------------------------------------------------------------------------------
/src/utils/hooks/index.js:
--------------------------------------------------------------------------------
1 | export { default as useLayout } from './useLayout'
2 | export { default as useKeyboard } from './useKeyboard'
3 | export { default as useWindowSize } from './useWindowSize'
4 | export { default as useHorizontalMode } from './useHorizontalMode'
5 | export { default as useDeviceOrientation } from './useDeviceOrientation'
6 |
7 | // export { default as usePlayTime } from './usePlayTime'
8 | export { default as useAssertApiSupport } from './useAssertApiSupport'
9 | export { useDrag } from './useDrag'
10 | export { useUnmounted } from './useUnmounted'
11 |
--------------------------------------------------------------------------------
/src/utils/hooks/useAnimateColor.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useMemo, useRef, useState } from 'react'
2 | import { Animated } from 'react-native'
3 |
4 | const ANIMATION_DURATION = 800
5 |
6 | export const useAnimateColor = (color: string) => {
7 | const anim = useMemo(() => new Animated.Value(0), [color])
8 | const [finished, setFinished] = useState(true)
9 | const currentColor = useRef(color)
10 | const nextColor = useMemo(() => color, [color])
11 |
12 | const animColor = anim.interpolate({
13 | inputRange: [0, 1],
14 | outputRange: [currentColor.current, nextColor],
15 | })
16 |
17 | useEffect(() => {
18 | setFinished(false)
19 | Animated.timing(anim, {
20 | toValue: 1,
21 | duration: ANIMATION_DURATION,
22 | useNativeDriver: false,
23 | }).start((finished) => {
24 | if (!finished) return
25 | // currentColor.current = nextColor
26 | setFinished(true)
27 | })
28 | requestAnimationFrame(() => {
29 | currentColor.current = nextColor
30 | })
31 | }, [nextColor])
32 |
33 | return [animColor, finished] as const
34 | }
35 |
--------------------------------------------------------------------------------
/src/utils/hooks/useAssertApiSupport.js:
--------------------------------------------------------------------------------
1 | import { useGetter } from '@/store'
2 |
3 | export default (source) => {
4 | const supportQualitys = useGetter('common', 'supportQualitys')
5 |
6 | return Boolean(supportQualitys[source])
7 | }
8 |
--------------------------------------------------------------------------------
/src/utils/hooks/useBackHandler.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { BackHandler } from 'react-native'
3 |
4 | export function useBackHandler(handler: () => boolean) {
5 | useEffect(() => {
6 | BackHandler.addEventListener('hardwareBackPress', handler)
7 |
8 | return () => {
9 | BackHandler.removeEventListener('hardwareBackPress', handler)
10 | }
11 | }, [handler])
12 | }
13 |
--------------------------------------------------------------------------------
/src/utils/hooks/useDeviceOrientation.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState, useCallback } from 'react'
2 | import { windowSizeTools } from '../windowSizeTools'
3 |
4 | export default () => {
5 | const isOrientationPortrait = ({ width, height }) => height >= width
6 | const isOrientationLandscape = ({ width, height }) => width >= height
7 |
8 | const size = windowSizeTools.getSize()
9 | const [orientation, setOrientation] = useState({
10 | portrait: isOrientationPortrait(size),
11 | landscape: isOrientationLandscape(size),
12 | })
13 |
14 | const onChange = useCallback((size) => {
15 | setOrientation({
16 | portrait: isOrientationPortrait(size),
17 | landscape: isOrientationLandscape(size),
18 | })
19 | }, [])
20 |
21 | useEffect(() => {
22 | const changeEvent = windowSizeTools.onSizeChanged(onChange)
23 |
24 | return () => {
25 | changeEvent.remove()
26 | }
27 | }, [orientation.portrait, orientation.landscape, onChange])
28 |
29 | return orientation
30 | }
31 |
--------------------------------------------------------------------------------
/src/utils/hooks/useHorizontalMode.ts:
--------------------------------------------------------------------------------
1 | import { useWindowSize } from '@/utils/hooks'
2 | import { isHorizontalMode } from '../tools'
3 |
4 | export default () => {
5 | const windowSize = useWindowSize()
6 |
7 | return isHorizontalMode(windowSize.width, windowSize.height)
8 | }
9 |
--------------------------------------------------------------------------------
/src/utils/hooks/useKeyboard.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import { Keyboard } from 'react-native'
3 |
4 | export default () => {
5 | const [shown, setShown] = useState(false)
6 | const [keyboardHeight, setKeyboardHeight] = useState(0)
7 |
8 | const handleKeyboardDidShow = (e) => {
9 | // const isShow = e.endCoordinates.height > 115
10 | // setShown(isShow)
11 | // setKeyboardHeight(isShow ? e.endCoordinates.height : 0)
12 | setShown(true)
13 | setKeyboardHeight(e.endCoordinates.height)
14 | }
15 |
16 | const handleKeyboardDidHide = () => {
17 | setShown(false)
18 | setKeyboardHeight(0)
19 | }
20 |
21 | useEffect(() => {
22 | const keyboardDidShow = Keyboard.addListener('keyboardDidShow', handleKeyboardDidShow)
23 | const keyboardDidHide = Keyboard.addListener('keyboardDidHide', handleKeyboardDidHide)
24 |
25 | return () => {
26 | keyboardDidShow.remove()
27 | keyboardDidHide.remove()
28 | }
29 | }, [])
30 |
31 | return {
32 | keyboardShown: shown,
33 | keyboardHeight,
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/utils/hooks/useLayout.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useCallback } from 'react'
2 | import { type LayoutChangeEvent } from 'react-native'
3 |
4 | export default () => {
5 | const [layout, setLayout] = useState({
6 | x: 0,
7 | y: 0,
8 | width: 0,
9 | height: 0,
10 | })
11 | const onLayout = useCallback((e: LayoutChangeEvent) => {
12 | setLayout(e.nativeEvent.layout)
13 | }, [])
14 |
15 | return {
16 | onLayout,
17 | ...layout,
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/utils/hooks/usePlayTime.js:
--------------------------------------------------------------------------------
1 | // import { useMemo } from 'react'
2 | // import { useProgress } from '@/plugins/player/utils'
3 | // import { formatPlayTime2 } from '@/utils'
4 | // // import { useGetter } from '@/store'
5 | // // import { STATE_PLAYING, STATE_BUFFERING } from 'react-native-track-player'
6 |
7 | // export default () => {
8 | // const { position, buffered, duration } = useProgress(250)
9 | // // const isGettingUrl = useGetter('player', 'isGettingUrl')
10 |
11 | // return {
12 | // curTimeStr: formatPlayTime2(position),
13 | // maxTimeStr: formatPlayTime2(duration),
14 | // time: position,
15 | // duration,
16 | // bufferedProgress: duration ? buffered / duration * 100 : 100,
17 | // progress: duration ? position / duration * 100 : 0,
18 | // }
19 | // }
20 |
--------------------------------------------------------------------------------
/src/utils/hooks/useUnmounted.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react'
2 |
3 | export function useUnmounted() {
4 | const isUnmountedRef = useRef(false)
5 | useEffect(() => {
6 | isUnmountedRef.current = false
7 | return () => {
8 | isUnmountedRef.current = true
9 | }
10 | }, [])
11 |
12 | return isUnmountedRef
13 | }
14 |
--------------------------------------------------------------------------------
/src/utils/hooks/useWindowSize.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import { type SizeHandler, windowSizeTools } from '@/utils/windowSizeTools'
3 |
4 | export default () => {
5 | const [size, setSize] = useState(windowSizeTools.getSize())
6 |
7 | useEffect(() => {
8 | const onChange: SizeHandler = (size) => {
9 | setSize(size)
10 | }
11 |
12 | const remove = windowSizeTools.onSizeChanged(onChange)
13 | return () => {
14 | remove()
15 | }
16 | }, [])
17 |
18 | return size
19 | }
20 |
--------------------------------------------------------------------------------
/src/utils/message.ts:
--------------------------------------------------------------------------------
1 | export const requestMsg = {
2 | fail: '请求异常😮,可以多试几次,若还是不行就换一首吧。。。',
3 | unachievable: '哦No😱...接口无法访问了!',
4 | timeout: '请求超时',
5 | // unachievable: '哦No😱...接口无法访问了!已帮你切换到临时接口,重试下看能不能播放吧~',
6 | notConnectNetwork: '无法连接到服务器',
7 | cancelRequest: '取消http请求',
8 | tooManyRequests: '服务器繁忙',
9 | } as const
10 |
--------------------------------------------------------------------------------
/src/utils/music.ts:
--------------------------------------------------------------------------------
1 | import { existsFile } from './fs'
2 |
3 | export const getLocalFilePath = async (musicInfo: LX.Music.MusicInfoLocal): Promise => {
4 | if (await existsFile(musicInfo.meta.filePath)) return musicInfo.meta.filePath
5 | // 直接从应用外 intent 调用打开的文件,ogg等类型无法判断文件是否存在,但这类文件路径为纯数字
6 | return /\/\d+$/.test(musicInfo.meta.filePath) ? musicInfo.meta.filePath : ''
7 | }
8 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/api-source-info.ts:
--------------------------------------------------------------------------------
1 | const sources: Array<{
2 | id: string
3 | name: string
4 | disabled: boolean
5 | supportQualitys: Partial>
6 | }> = [
7 | {
8 | id: 'mobi',
9 | name: 'Mobi(酷我破解版用的接口)',
10 | disabled: false,
11 | supportQualitys: {
12 | kw: ['128k', '320k', 'flac', 'hires'],
13 | },
14 | },
15 | ]
16 |
17 | export default sources
18 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/api-source.js:
--------------------------------------------------------------------------------
1 | import apiSourceInfo from './api-source-info'
2 | import api_mobi_kw from './kw/api-mobi'
3 | import settingState from '@/store/setting/state'
4 |
5 | const apiList = {
6 | mobi_api_kw: api_mobi_kw,
7 | }
8 | const supportQuality = {}
9 |
10 | for (const api of apiSourceInfo) {
11 | supportQuality[api.id] = api.supportQualitys
12 | }
13 |
14 | const getAPI = (source) => apiList[`${settingState.setting['common.apiSource']}_api_${source}`]
15 |
16 | const apis = (source) => {
17 | if (/^user_api/.test(settingState.setting['common.apiSource'])) return global.lx.apis[source]
18 | const api = getAPI(source)
19 | if (api) return api
20 | throw new Error('Api is not found')
21 | }
22 |
23 | export { apis, supportQuality }
24 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/kg/index.js:
--------------------------------------------------------------------------------
1 | import leaderboard from './leaderboard'
2 | import { apis } from '../api-source'
3 | import songList from './songList'
4 | import musicSearch from './musicSearch'
5 | import pic from './pic'
6 | import lyric from './lyric'
7 | import hotSearch from './hotSearch'
8 | import comment from './comment'
9 | // import tipSearch from './tipSearch'
10 |
11 | const kg = {
12 | // tipSearch,
13 | leaderboard,
14 | songList,
15 | musicSearch,
16 | hotSearch,
17 | comment,
18 | getMusicUrl(songInfo, type) {
19 | return apis('kg').getMusicUrl(songInfo, type)
20 | },
21 | getLyric(songInfo) {
22 | return lyric.getLyric(songInfo)
23 | },
24 | // getLyric(songInfo) {
25 | // return apis('kg').getLyric(songInfo)
26 | // },
27 | getPic(songInfo) {
28 | return pic.getPic(songInfo)
29 | },
30 | getMusicDetailPageUrl(songInfo) {
31 | return `https://www.kugou.com/song/#hash=${songInfo.hash}&album_id=${songInfo.albumId}`
32 | },
33 | // getPic(songInfo) {
34 | // return apis('kg').getPic(songInfo)
35 | // },
36 | }
37 |
38 | export default kg
39 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/kg/tipSearch.js:
--------------------------------------------------------------------------------
1 | import { createHttpFetch } from './util'
2 |
3 | export default {
4 | requestObj: null,
5 | cancelTipSearch() {
6 | if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
7 | },
8 | tipSearchBySong(str) {
9 | this.cancelTipSearch()
10 | this.requestObj = createHttpFetch(
11 | `https://searchtip.kugou.com/getSearchTip?MusicTipCount=10&keyword=${encodeURIComponent(str)}`,
12 | {
13 | headers: {
14 | referer: 'https://www.kugou.com/',
15 | },
16 | }
17 | )
18 | return this.requestObj.then((body) => {
19 | return body[0].RecordDatas
20 | })
21 | },
22 | handleResult(rawData) {
23 | return rawData.map((info) => info.HintInfo)
24 | },
25 | async search(str) {
26 | return this.tipSearchBySong(str).then((result) => this.handleResult(result))
27 | },
28 | }
29 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/kw/hotSearch.js:
--------------------------------------------------------------------------------
1 | import { httpFetch } from '../../request'
2 |
3 | export default {
4 | _requestObj: null,
5 | async getList(retryNum = 0) {
6 | if (this._requestObj) this._requestObj.cancelHttp()
7 | if (retryNum > 2) return Promise.reject(new Error('try max num'))
8 |
9 | const _requestObj = httpFetch(
10 | 'http://hotword.kuwo.cn/hotword.s?prod=kwplayer_ar_9.3.0.1&corp=kuwo&newver=2&vipver=9.3.0.1&source=kwplayer_ar_9.3.0.1_40.apk&p2p=1¬race=0&uid=0&plat=kwplayer_ar&rformat=json&encoding=utf8&tabid=1',
11 | {
12 | headers: {
13 | 'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 9;)',
14 | },
15 | }
16 | )
17 | const { body, statusCode } = await _requestObj.promise
18 | if (statusCode != 200 || body.status !== 'ok') throw new Error('获取热搜词失败')
19 | // console.log(body, statusCode)
20 | return { source: 'kw', list: this.filterList(body.tagvalue) }
21 | },
22 | filterList(rawList) {
23 | return rawList.map((item) => item.key)
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/kw/pic.js:
--------------------------------------------------------------------------------
1 | import { httpFetch } from '../../request'
2 |
3 | export default {
4 | getPic({ songmid }) {
5 | const requestObj = httpFetch(
6 | `http://artistpicserver.kuwo.cn/pic.web?corp=kuwo&type=rid_pic&pictype=500&size=500&rid=${songmid}`
7 | )
8 | requestObj.promise = requestObj.promise.then(({ body }) => (/^http/.test(body) ? body : null))
9 | return requestObj.promise
10 | },
11 | }
12 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/kw/tipSearch.js:
--------------------------------------------------------------------------------
1 | // import { decodeName } from '../../index'
2 | // import { tokenRequest } from './util'
3 | import { httpFetch } from '../../request'
4 |
5 | export default {
6 | regExps: {
7 | relWord: /RELWORD=(.+)/,
8 | },
9 | requestObj: null,
10 | async tipSearchBySong(str) {
11 | // 报错403,加了referer还是有问题(直接换一个
12 | // this.requestObj = await tokenRequest(`http://www.kuwo.cn/api/www/search/searchKey?key=${encodeURIComponent(str)}`)
13 |
14 | this.cancelTipSearch()
15 | this.requestObj = httpFetch(
16 | `https://tips.kuwo.cn/t.s?corp=kuwo&newver=3&p2p=1¬race=0&c=mbox&w=${encodeURIComponent(str)}&encoding=utf8&rformat=json`,
17 | {
18 | Referer: 'http://www.kuwo.cn/',
19 | }
20 | )
21 | return this.requestObj.promise.then(({ body, statusCode }) => {
22 | if (statusCode != 200 || !body.WORDITEMS) return Promise.reject(new Error('请求失败'))
23 | return body.WORDITEMS
24 | })
25 | },
26 | handleResult(rawData) {
27 | return rawData.map((item) => item.RELWORD)
28 | },
29 | cancelTipSearch() {
30 | if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
31 | },
32 | async search(str) {
33 | return this.tipSearchBySong(str).then((result) => this.handleResult(result))
34 | },
35 | }
36 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/mg/hotSearch.js:
--------------------------------------------------------------------------------
1 | import { httpFetch } from '../../request'
2 |
3 | export default {
4 | _requestObj: null,
5 | async getList(retryNum = 0) {
6 | if (this._requestObj) this._requestObj.cancelHttp()
7 | if (retryNum > 2) return Promise.reject(new Error('try max num'))
8 |
9 | const _requestObj = httpFetch('http://jadeite.migu.cn:7090/music_search/v3/search/hotword')
10 | const { body, statusCode } = await _requestObj.promise
11 | if (statusCode != 200 || body.code !== '000000') throw new Error('获取热搜词失败')
12 | // console.log(body, statusCode)
13 | return { source: 'mg', list: this.filterList(body.data.hotwords[0].hotwordList) }
14 | },
15 | filterList(rawList) {
16 | return rawList.filter((item) => item.resourceType == 'song').map((item) => item.word)
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/mg/index.js:
--------------------------------------------------------------------------------
1 | import { apis } from '../api-source'
2 | import leaderboard from './leaderboard'
3 | import songList from './songList'
4 | import musicSearch from './musicSearch'
5 | import pic from './pic'
6 | import lyric from './lyric'
7 | import hotSearch from './hotSearch'
8 | import comment from './comment'
9 | // import tipSearch from './tipSearch'
10 |
11 | const mg = {
12 | // tipSearch,
13 | songList,
14 | musicSearch,
15 | leaderboard,
16 | hotSearch,
17 | comment,
18 | getMusicUrl(songInfo, type) {
19 | return apis('mg').getMusicUrl(songInfo, type)
20 | },
21 | getLyric(songInfo) {
22 | return lyric.getLyric(songInfo)
23 | },
24 | getPic(songInfo) {
25 | return pic.getPic(songInfo)
26 | },
27 | getMusicDetailPageUrl(songInfo) {
28 | return `http://music.migu.cn/v3/music/song/${songInfo.copyrightId}`
29 | },
30 | }
31 |
32 | export default mg
33 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/mg/pic.js:
--------------------------------------------------------------------------------
1 | import { httpFetch } from '../../request'
2 | import getSongId from './songId'
3 |
4 | export default {
5 | async getPicUrl(songId, tryNum = 0) {
6 | let requestObj = httpFetch(
7 | `http://music.migu.cn/v3/api/music/audioPlayer/getSongPic?songId=${songId}`,
8 | {
9 | headers: {
10 | Referer: 'http://music.migu.cn/v3/music/player/audio?from=migu',
11 | },
12 | }
13 | )
14 | requestObj.promise.then(({ body }) => {
15 | if (body.returnCode !== '000000') {
16 | if (tryNum > 5) return Promise.reject(new Error('图片获取失败'))
17 | let tryRequestObj = this.getPic(songId, ++tryNum)
18 | requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
19 | return tryRequestObj.promise
20 | }
21 | let url = body.largePic || body.mediumPic || body.smallPic
22 | if (!/https?:/.test(url)) url = 'http:' + url
23 | return url
24 | })
25 | return requestObj
26 | },
27 | async getPic(songInfo) {
28 | const songId = await getSongId(songInfo)
29 | return this.getPicUrl(songId)
30 | },
31 | }
32 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/mg/songId.js:
--------------------------------------------------------------------------------
1 | // import { httpFetch } from '../../request'
2 | import { getMusicInfo } from './musicInfo'
3 |
4 | const getSongId = async (mInfo) => {
5 | if (mInfo.songmid != mInfo.copyrightId) return mInfo.songmid
6 | const musicInfo = await getMusicInfo(mInfo.copyrightId)
7 | return musicInfo.songmid
8 | }
9 |
10 | // export const getSongId = async(musicInfo, retry = 0) => {
11 | // if (musicInfo.songmid != musicInfo.copyrightId) return musicInfo.songmid
12 | // if (++retry > 2) return Promise.reject(new Error('max retry'))
13 |
14 | // const requestObj = httpFetch(`https://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/listen-url?netType=00&resourceType=2&songId=${musicInfo.copyrightId}&toneFlag=PQ`, {
15 | // headers: {
16 | // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
17 | // channel: '0146921',
18 | // },
19 | // })
20 |
21 | // return requestObj.promise.then(({ body }) => {
22 | // console.log(body)
23 | // if (!body || body.code !== '000000') return this.getSongId(musicInfo, retry)
24 | // const id = body.data.songItem.songId
25 | // if (!id) throw new Error('failed')
26 | // return id
27 | // })
28 | // }
29 |
30 | export default getSongId
31 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/mg/tipSearch.js:
--------------------------------------------------------------------------------
1 | import { createHttpFetch } from './utils'
2 |
3 | export default {
4 | requestObj: null,
5 | cancelTipSearch() {
6 | if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
7 | },
8 | tipSearchBySong(str) {
9 | this.cancelTipSearch()
10 | this.requestObj = createHttpFetch(
11 | `https://music.migu.cn/v3/api/search/suggest?keyword=${encodeURIComponent(str)}`,
12 | {
13 | headers: {
14 | referer: 'https://music.migu.cn/v3',
15 | },
16 | }
17 | )
18 | return this.requestObj.then((body) => {
19 | return body.songs
20 | })
21 | },
22 | handleResult(rawData) {
23 | return rawData.map((info) => `${info.name} - ${info.singerName}`)
24 | },
25 | async search(str) {
26 | return this.tipSearchBySong(str).then((result) => this.handleResult(result))
27 | },
28 | }
29 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/mg/utils/index.js:
--------------------------------------------------------------------------------
1 | import { httpFetch } from '../../../request'
2 |
3 | /**
4 | * 创建一个适用于MG的Http请求
5 | * @param {*} url
6 | * @param {*} options
7 | * @param {*} retryNum
8 | */
9 | export const createHttpFetch = async (url, options, retryNum = 0) => {
10 | if (retryNum > 2) throw new Error('try max num')
11 | let result
12 | try {
13 | result = await httpFetch(url, options).promise
14 | } catch (err) {
15 | console.log(err)
16 | return createHttpFetch(url, options, ++retryNum)
17 | }
18 | if (
19 | result.statusCode !== 200 ||
20 | (result.body.code !== undefined
21 | ? result.body.code
22 | : result.body.returnCode !== undefined
23 | ? result.body.returnCode
24 | : result.body.code) !== '000000'
25 | )
26 | return createHttpFetch(url, options, ++retryNum)
27 | if (result.body.data) return result.body.data
28 | return result.body
29 | }
30 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/options.js:
--------------------------------------------------------------------------------
1 | export const bHh = '624868746c'
2 |
3 | export const headers = {
4 | [bHh]: [bHh],
5 | }
6 |
7 | export const timeout = 15000
8 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/tx/index.js:
--------------------------------------------------------------------------------
1 | import leaderboard from './leaderboard'
2 | import lyric from './lyric'
3 | import songList from './songList'
4 | import musicSearch from './musicSearch'
5 | import { apis } from '../api-source'
6 | import hotSearch from './hotSearch'
7 | import comment from './comment'
8 | // import tipSearch from './tipSearch'
9 |
10 | const tx = {
11 | // tipSearch,
12 | leaderboard,
13 | songList,
14 | musicSearch,
15 | hotSearch,
16 | comment,
17 |
18 | getMusicUrl(songInfo, type) {
19 | return apis('tx').getMusicUrl(songInfo, type)
20 | },
21 | getLyric(songInfo) {
22 | // let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer
23 | return lyric.getLyric(songInfo.songmid)
24 | },
25 | getPic(songInfo) {
26 | return apis('tx').getPic(songInfo)
27 | },
28 | getMusicDetailPageUrl(songInfo) {
29 | return `https://y.qq.com/n/yqq/song/${songInfo.songmid}.html`
30 | },
31 | }
32 |
33 | export default tx
34 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/tx/lyric.js:
--------------------------------------------------------------------------------
1 | import { httpFetch } from '../../request'
2 | import { b64DecodeUnicode, decodeName } from '../../index'
3 |
4 | export default {
5 | regexps: {
6 | matchLrc: /.+"lyric":"([\w=+/]*)".+/,
7 | },
8 | getLyric(songmid) {
9 | const requestObj = httpFetch(
10 | `https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid=${songmid}&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&platform=yqq`,
11 | {
12 | headers: {
13 | Referer: 'https://y.qq.com/portal/player.html',
14 | },
15 | }
16 | )
17 | requestObj.promise = requestObj.promise.then(({ body }) => {
18 | if (body.code != 0 || !body.lyric) return Promise.reject(new Error('Get lyric failed'))
19 | return {
20 | lyric: decodeName(b64DecodeUnicode(body.lyric)),
21 | tlyric: decodeName(b64DecodeUnicode(body.trans)),
22 | }
23 | })
24 | return requestObj
25 | },
26 | }
27 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/tx/tipSearch.js:
--------------------------------------------------------------------------------
1 | import { httpFetch } from '../../request'
2 |
3 | export default {
4 | // regExps: {
5 | // relWord: /RELWORD=(.+)/,
6 | // },
7 | requestObj: null,
8 | tipSearch(str) {
9 | this.cancelTipSearch()
10 | this.requestObj = httpFetch(
11 | `https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg?is_xml=0&format=json&key=${encodeURIComponent(str)}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0`,
12 | {
13 | headers: {
14 | Referer: 'https://y.qq.com/portal/player.html',
15 | },
16 | }
17 | )
18 | return this.requestObj.promise.then(({ statusCode, body }) => {
19 | if (statusCode != 200 || body.code != 0) return Promise.reject(new Error('请求失败'))
20 | return body.data
21 | })
22 | },
23 | handleResult(rawData) {
24 | return rawData.map((info) => `${info.name} - ${info.singer}`)
25 | },
26 | cancelTipSearch() {
27 | if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
28 | },
29 | async search(str) {
30 | return this.tipSearch(str).then((result) => this.handleResult(result.song.itemlist))
31 | },
32 | }
33 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/utils.js:
--------------------------------------------------------------------------------
1 | import { stringMd5 } from 'react-native-quick-md5'
2 | import { decodeName } from '../index'
3 |
4 | /**
5 | * 获取音乐音质
6 | * @param {*} info
7 | * @param {*} type
8 | */
9 |
10 | export const QUALITYS = ['master', 'atmos_plus', 'atmos', 'hires', 'flac', '320k', '192k', '128k']
11 | export const getMusicType = (info, type) => {
12 | const list = global.lx.qualityList[info.source]
13 | if (!list) return '128k'
14 | if (!list.includes(type)) type = list[list.length - 1]
15 | const rangeType = QUALITYS.slice(QUALITYS.indexOf(type))
16 | for (const type of rangeType) {
17 | if (info._types[type]) return type
18 | }
19 | return '128k'
20 | }
21 |
22 | export const toMD5 = (str) => stringMd5(str)
23 |
24 | /**
25 | * 格式化歌手
26 | * @param singers 歌手数组
27 | * @param nameKey 歌手名键值
28 | * @param join 歌手分割字符
29 | */
30 | export const formatSingerName = (singers, nameKey = 'name', join = '、') => {
31 | if (Array.isArray(singers)) {
32 | const singer = []
33 | singers.forEach((item) => {
34 | let name = item[nameKey]
35 | if (!name) return
36 | singer.push(name)
37 | })
38 | return decodeName(singer.join(join))
39 | }
40 | return decodeName(String(singers ?? ''))
41 | }
42 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/wy/hotSearch.js:
--------------------------------------------------------------------------------
1 | import { eapiRequest } from './utils/index'
2 |
3 | export default {
4 | _requestObj: null,
5 | async getList(retryNum = 0) {
6 | if (this._requestObj) this._requestObj.cancelHttp()
7 | if (retryNum > 2) return Promise.reject(new Error('try max num'))
8 |
9 | const _requestObj = eapiRequest('/api/search/chart/detail', {
10 | id: 'HOT_SEARCH_SONG#@#',
11 | })
12 | const { body, statusCode } = await _requestObj.promise
13 | if (statusCode != 200 || body.code !== 200) throw new Error('获取热搜词失败')
14 |
15 | return { source: 'wy', list: this.filterList(body.data.itemList) }
16 | },
17 | filterList(rawList) {
18 | return rawList.map((item) => item.searchWord)
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/wy/index.js:
--------------------------------------------------------------------------------
1 | import leaderboard from './leaderboard'
2 | import { apis } from '../api-source'
3 | import getLyric from './lyric'
4 | import getMusicInfo from './musicInfo'
5 | import musicSearch from './musicSearch'
6 | import songList from './songList'
7 | import hotSearch from './hotSearch'
8 | import comment from './comment'
9 | // import tipSearch from './tipSearch'
10 |
11 | const wy = {
12 | // tipSearch,
13 | leaderboard,
14 | musicSearch,
15 | songList,
16 | hotSearch,
17 | comment,
18 | getMusicUrl(songInfo, type) {
19 | return apis('wy').getMusicUrl(songInfo, type)
20 | },
21 | getLyric(songInfo) {
22 | return getLyric(songInfo.songmid)
23 | },
24 | getPic(songInfo) {
25 | const requestObj = getMusicInfo(songInfo.songmid)
26 | return requestObj.promise.then((info) => info.al.picUrl)
27 | },
28 | getMusicDetailPageUrl(songInfo) {
29 | return `https://music.163.com/#/song?id=${songInfo.songmid}`
30 | },
31 | }
32 |
33 | export default wy
34 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/wy/musicInfo.js:
--------------------------------------------------------------------------------
1 | // https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js
2 | import { httpFetch } from '../../request'
3 | import { weapi } from './utils/crypto'
4 |
5 | export default (songmid) => {
6 | const requestObj = httpFetch('https://music.163.com/weapi/v3/song/detail', {
7 | method: 'post',
8 | headers: {
9 | 'User-Agent':
10 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
11 | Referer: 'https://music.163.com/song?id=' + songmid,
12 | origin: 'https://music.163.com',
13 | },
14 | form: weapi({
15 | c: `[{"id":${songmid}}]`,
16 | ids: `[${songmid}]`,
17 | }),
18 | })
19 | requestObj.promise = requestObj.promise.then(({ body }) => {
20 | // console.log(body)
21 | if (body.code !== 200 || !body.songs.length)
22 | return Promise.reject(new Error('获取歌曲信息失败'))
23 | return body.songs[0]
24 | })
25 | return requestObj
26 | }
27 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/wy/tipSearch.js:
--------------------------------------------------------------------------------
1 | import { httpFetch } from '../../request'
2 | import { weapi } from './utils/crypto'
3 | import { formatSingerName } from '../utils'
4 |
5 | export default {
6 | requestObj: null,
7 | cancelTipSearch() {
8 | if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
9 | },
10 | tipSearchBySong(str) {
11 | this.cancelTipSearch()
12 | this.requestObj = httpFetch('https://music.163.com/weapi/search/suggest/web', {
13 | method: 'POST',
14 | headers: {
15 | referer: 'https://music.163.com/',
16 | origin: 'https://music.163.com/',
17 | },
18 | form: weapi({
19 | s: str,
20 | }),
21 | })
22 | return this.requestObj.promise.then(({ statusCode, body }) => {
23 | if (statusCode != 200 || body.code != 200) return Promise.reject(new Error('请求失败'))
24 | return body.result.songs
25 | })
26 | },
27 | handleResult(rawData) {
28 | return rawData.map((info) => `${info.name} - ${formatSingerName(info.artists, 'name')}`)
29 | },
30 | async search(str) {
31 | return this.tipSearchBySong(str).then((result) => this.handleResult(result))
32 | },
33 | }
34 |
--------------------------------------------------------------------------------
/src/utils/musicSdk/wy/utils/index.js:
--------------------------------------------------------------------------------
1 | import { httpFetch } from '../../../request'
2 | import { eapi } from './crypto'
3 |
4 | export const eapiRequest = (url, data) => {
5 | return httpFetch('http://interface.music.163.com/eapi/batch', {
6 | method: 'post',
7 | headers: {
8 | 'User-Agent':
9 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
10 | origin: 'https://music.163.com',
11 | // cookie: 'os=pc; deviceId=A9C064BB4584D038B1565B58CB05F95290998EE8B025AA2D07AE; osver=Microsoft-Windows-10-Home-China-build-19043-64bit; appver=2.5.2.197409; channel=netease; MUSIC_A=37a11f2eb9de9930cad479b2ad495b0e4c982367fb6f909d9a3f18f876c6b49faddb3081250c4980dd7e19d4bd9bf384e004602712cf2b2b8efaafaab164268a00b47359f85f22705cc95cb6180f3aee40f5be1ebf3148d888aa2d90636647d0c3061cd18d77b7a0; __csrf=05b50d54082694f945d7de75c210ef94; mode=Z7M-KP5(7)GZ; NMTID=00OZLp2VVgq9QdwokUgq3XNfOddQyIAAAF_6i8eJg; ntes_kaola_ad=1',
12 | },
13 | form: eapi(url, data),
14 | })
15 | // requestObj.promise = requestObj.promise.then(({ body }) => {
16 | // // console.log(raw)
17 | // console.log(body)
18 | // // console.log(eapiDecrypt(raw))
19 | // // return eapiDecrypt(raw)
20 | // return body
21 | // })
22 | // return requestObj
23 | }
24 |
--------------------------------------------------------------------------------
/src/utils/nativeModules/cache.ts:
--------------------------------------------------------------------------------
1 | import { NativeModules } from 'react-native'
2 |
3 | const { CacheModule } = NativeModules
4 |
5 | export const getAppCacheSize = async (): Promise =>
6 | CacheModule.getAppCacheSize().then((size: number) => Math.trunc(size))
7 | export const clearAppCache = CacheModule.clearAppCache as () => Promise
8 |
--------------------------------------------------------------------------------
/src/utils/simplify-chinese-main/.gitignore:
--------------------------------------------------------------------------------
1 | todo.md
2 | node_modules/
3 | npm-debug.log
4 | yarn-debug.log
5 | yarn-error.log
6 | package-lock.json
7 | tsconfig.tsbuildinfo
8 | report.*.json
9 |
10 | .DS_Store
11 | .idea
12 | .vscode
13 | .yarn
14 | *.suo
15 | *.ntvs*
16 | *.njsproj
17 | *.sln
18 |
--------------------------------------------------------------------------------
/src/utils/simplify-chinese-main/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2021 Shigma
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/utils/simplify-chinese-main/README.md:
--------------------------------------------------------------------------------
1 | # simplify-chinese
2 |
3 | Convert chinese characters between simplified form and tranditional form / 汉字简繁体转换工具。
4 |
5 | ```js
6 | const { simplify, tranditionalize } = require('simplify-chinese')
7 |
8 | console.log(simplify('窩窩頭')) // 窝窝头
9 | console.log(tranditionalize('窝窝头')) // 窩窩頭
10 | ```
11 |
--------------------------------------------------------------------------------
/src/utils/simplify-chinese-main/index.d.ts:
--------------------------------------------------------------------------------
1 | export function simplify(source: string): string
2 | export function tranditionalize(source: string): string
3 |
--------------------------------------------------------------------------------
/src/utils/simplify-chinese-main/index.js:
--------------------------------------------------------------------------------
1 | // const { simplified, traditional } = require('./chinese')
2 | import stMap from './chinese'
3 |
4 | // const stMap = {}
5 | // const tsMap = new Map()
6 |
7 | // simplified.split('').forEach((char, index) => {
8 | // stMap[char] = traditional[index]
9 | // // stMap.set(char, traditional[index])
10 | // // tsMap.set(traditional[index], char)
11 | // })
12 | // console.log(JSON.stringify(stMap))
13 | // function simplify(source) {
14 | // let result = []
15 | // for (const char of source) {
16 | // result.push(tsMap.get(char) || char)
17 | // }
18 | // return result.join('')
19 | // }
20 |
21 | function tranditionalize(source) {
22 | let result = []
23 | for (const char of source) {
24 | result.push(stMap[char] || char)
25 | }
26 | return result.join('')
27 | }
28 |
29 | // module.exports = {
30 | // // simplify,
31 | // tranditionalize,
32 | // }
33 | export { tranditionalize }
34 |
--------------------------------------------------------------------------------
/src/utils/simplify-chinese-main/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simplify-chinese",
3 | "description": "Convert chinese characters between simplified form and tranditional form 汉字简繁体转换工具",
4 | "version": "1.1.0",
5 | "main": "index.js",
6 | "typings": "index.d.ts",
7 | "repository": "https://github.com/koishijs/simplify-chinese.git",
8 | "author": "Shigma <1700011071@pku.edu.cn>",
9 | "license": "MIT"
10 | }
11 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json" /* Recommended React Native TSConfig base */,
3 | "compilerOptions": {
4 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
5 |
6 | "module": "ESNext",
7 | "types": ["react-native", "node"],
8 | /* Completeness */
9 | "skipLibCheck": true /* Skip type checking all .d.ts files. */,
10 |
11 | "baseUrl": "./",
12 | "paths": {
13 | "@/*": ["src/*"]
14 | // "@config": ["src/config"],
15 | // "@store": ["src/store"],
16 | // "@components": ["src/components"],
17 | // "@navigation": ["src/navigation"],
18 | // "@screens": ["src/screens"],
19 | // "@theme": ["src/theme"],
20 | // "@utils": ["src/utils"],
21 | }
22 | },
23 | "exclude": ["node_modules", "build", "dist"]
24 | }
25 |
--------------------------------------------------------------------------------