Skip to content

Commit

Permalink
新增歌曲标签编辑功能
Browse files Browse the repository at this point in the history
  • Loading branch information
lyswhut committed Dec 15, 2023
1 parent f1d8c95 commit c0d990c
Show file tree
Hide file tree
Showing 21 changed files with 632 additions and 25 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"react-native-exception-handler": "^2.10.10",
"react-native-fast-image": "^8.6.3",
"react-native-fs": "^2.20.0",
"react-native-local-media-metadata": "github:lyswhut/react-native-local-media-metadata#8c6525e20f5787dbcd2962e83b635787341e5be5",
"react-native-local-media-metadata": "github:lyswhut/react-native-local-media-metadata#c2b993093ddd141b4b88673baba7cb4d7b565d04",
"react-native-navigation": "^7.37.2",
"react-native-pager-view": "^6.2.3",
"react-native-quick-base64": "^2.0.8",
Expand Down
1 change: 1 addition & 0 deletions publish/changeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- 新增自定义源(实验性功能),调用方式与PC端一致,但需要注意的是,移动端自定义源的环境与PC端不同,某些环境API不可用,详情看自定义说明文档
- 新增长按收藏列表名自动跳转列表顶部的功能
- 新增实验性的添加本地歌曲到我的收藏支持,与PC端类似,在我的收藏的列表菜单中选择歌曲目录,将添加所选目录下的所有歌曲,目前支持mp3/flac/ogg/wav格式
- 新增歌曲标签编辑功能,允许编辑本地源且文件歌曲存在的歌曲标签信息

### 优化

Expand Down
47 changes: 47 additions & 0 deletions src/components/MetadataEditModal/InputItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { memo } from 'react'

import { StyleSheet, View } from 'react-native'
import type { InputProps } from '@/components/common/Input'
import Input from '@/components/common/Input'
import { useTheme } from '@/store/theme/hook'
import Text from '@/components/common/Text'


export interface InputItemProps extends InputProps {
value: string
label: string
onChanged: (text: string) => void
}

export default memo(({ value, label, onChanged, ...props }: InputItemProps) => {
const theme = useTheme()
return (
<View style={styles.container} onStartShouldSetResponder={() => true}>
<Text style={styles.label} size={14}>{label}</Text>
<Input
value={value}
onChangeText={onChanged}
style={{ ...styles.input, backgroundColor: theme['c-primary-input-background'] }}
{...props}
/>
</View>
)
})

const styles = StyleSheet.create({
container: {
// paddingLeft: 25,
marginBottom: 15,
},
label: {
marginBottom: 2,
},
input: {
flexGrow: 1,
flexShrink: 1,
// borderRadius: 4,
// paddingTop: 3,
// paddingBottom: 3,
// maxWidth: 300,
},
})
128 changes: 128 additions & 0 deletions src/components/MetadataEditModal/MetadataForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { useImperativeHandle, forwardRef, useState, useCallback } from 'react'
import { View } from 'react-native'
import { createStyle } from '@/utils/tools'
import InputItem from './InputItem'
import { useI18n } from '@/lang'
import TextAreaItem from './TextAreaItem'
import PicItem from './PicItem'
import Text from '@/components/common/Text'
import { useTheme } from '@/store/theme/hook'

export interface Metadata {
name: string // 歌曲名
singer: string // 艺术家名
albumName: string // 歌曲专辑名称
pic: string
lyric: string
}
export const defaultData = {
name: '',
singer: '',
albumName: '',
pic: '',
lyric: '',
}

export interface MetadataFormType {
setForm: (path: string, metadata: Metadata) => void
getForm: () => Metadata
}
export default forwardRef<MetadataFormType, {}>((props, ref) => {
const t = useI18n()
const [path, setPath] = useState('')
const [data, setData] = useState({ ...defaultData })
const theme = useTheme()

useImperativeHandle(ref, () => ({
setForm(path, data) {
setPath(path)
setData(data)
},
getForm() {
return {
...data,
name: data.name.trim(),
singer: data.singer.trim(),
albumName: data.albumName.trim(),
}
},
}))

const handleUpdateName = useCallback((name: string) => {
if (name.length > 150) name = name.substring(0, 150)
setData(data => {
return { ...data, name }
})
}, [])
const handleUpdateSinger = useCallback((singer: string) => {
if (singer.length > 150) singer = singer.substring(0, 150)
setData(data => {
return { ...data, singer }
})
}, [])
const handleUpdateAlbumName = useCallback((albumName: string) => {
if (albumName.length > 150) albumName = albumName.substring(0, 150)
setData(data => {
return { ...data, albumName }
})
}, [])
const handleUpdatePic = useCallback((path: string) => {
setData(data => {
return { ...data, pic: path }
})
}, [])
const handleUpdateLyric = useCallback((lyric: string) => {
setData(data => {
return { ...data, lyric }
})
}, [])

return (
<View style={styles.container}>
<View>
<Text size={14}>{global.i18n.t('metadata_edit_modal_file_path')}</Text>
<Text size={14} selectable color={theme['c-primary-font']} style={styles.pathText}>{path}</Text>
</View>

<InputItem
value={data.name}
label={t('metadata_edit_modal_form_name')}
onChanged={handleUpdateName}
keyboardType="name-phone-pad" />
<InputItem
value={data.singer}
label={t('metadata_edit_modal_form_singer')}
onChanged={handleUpdateSinger}
keyboardType="name-phone-pad" />
<InputItem
value={data.albumName}
label={t('metadata_edit_modal_form_album_name')}
onChanged={handleUpdateAlbumName}
keyboardType="name-phone-pad" />
<PicItem
value={data.pic}
label={t('metadata_edit_modal_form_pic')}
onChanged={handleUpdatePic} />
<TextAreaItem
value={data.lyric}
label={t('metadata_edit_modal_form_lyric')}
onChanged={handleUpdateLyric}
keyboardType="default" />
</View>
)
})

const styles = createStyle({
container: {
flexGrow: 1,
flexShrink: 1,
flexDirection: 'column',
width: 360,
maxWidth: '100%',
},
pathText: {
marginBottom: 10,
},
})


86 changes: 86 additions & 0 deletions src/components/MetadataEditModal/PicItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { memo, useCallback, useRef } from 'react'

import { TouchableOpacity, View } from 'react-native'
import { useTheme } from '@/store/theme/hook'
import Text from '@/components/common/Text'
import { createStyle } from '@/utils/tools'
import Image from '@/components/common/Image'
import FileSelect, { type FileSelectType } from '@/components/common/FileSelect'
import { BorderWidths } from '@/theme'


export interface PicItemProps {
value: string
label: string
onChanged: (text: string) => void
}

export default memo(({ value, label, onChanged }: PicItemProps) => {
const theme = useTheme()
const fileSelectRef = useRef<FileSelectType>(null)
const handleRemoveFile = useCallback(() => {
onChanged('')
}, [onChanged])
const handleShowSelectFile = useCallback(() => {
fileSelectRef.current?.show({
title: global.i18n.t('metadata_edit_modal_form_select_pic_title'),
dirOnly: false,
filter: /jpg|jpeg|png/,
}, (path) => {
onChanged(path)
})
}, [onChanged])
return (
<View style={styles.container} onStartShouldSetResponder={() => true}>
<View style={styles.header}>
<Text style={styles.label} size={14}>{label}</Text>
<View style={styles.btns}>
<TouchableOpacity onPress={handleRemoveFile}>
<Text size={13} color={theme['c-button-font']}>{global.i18n.t('metadata_edit_modal_form_remove_pic')}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={handleShowSelectFile}>
<Text size={13} color={theme['c-button-font']}>{global.i18n.t('metadata_edit_modal_form_select_pic')}</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.picContent}>
<Image
url={value}
style={{ ...styles.pic, borderColor: theme['c-border-background'] }}
/>
</View>
<FileSelect ref={fileSelectRef} />
</View>
)
})

const styles = createStyle({
container: {
// paddingLeft: 25,
marginBottom: 15,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
gap: 30,
},
label: {
marginBottom: 2,
},
btns: {
flexDirection: 'row',
gap: 15,
},
picContent: {
// backgroundColor: 'rgba(0,0,0,0.2)',
marginTop: 5,
position: 'relative',
},
pic: {
width: 180,
height: 180,
borderWidth: BorderWidths.normal,
borderStyle: 'dashed',
},
})
53 changes: 53 additions & 0 deletions src/components/MetadataEditModal/TextAreaItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { memo } from 'react'

import { View } from 'react-native'
import type { InputProps } from '@/components/common/Input'
import Input from '@/components/common/Input'
import { useTheme } from '@/store/theme/hook'
import Text from '@/components/common/Text'
import { createStyle } from '@/utils/tools'


export interface TextAreaItemProps extends InputProps {
value: string
label: string
onChanged: (text: string) => void
}

export default memo(({ value, label, onChanged, ...props }: TextAreaItemProps) => {
const theme = useTheme()
return (
<View style={styles.container} onStartShouldSetResponder={() => true}>
<Text style={styles.label} size={14}>{label}</Text>
<Input
value={value}
onChangeText={onChanged}
numberOfLines={6}
scrollEnabled={false}
textAlignVertical='top'
multiline
style={{ ...styles.textarea, backgroundColor: theme['c-primary-input-background'] }}
{...props}
/>
</View>
)
})

const styles = createStyle({
container: {
// paddingLeft: 25,
marginBottom: 15,
},
label: {
marginBottom: 2,
},
textarea: {
flexGrow: 1,
flexShrink: 1,
paddingTop: 3,
paddingBottom: 3,
height: 'auto',
// height: 300,
// maxWidth: 300,
},
})
Loading

0 comments on commit c0d990c

Please sign in to comment.