mirror of
https://github.com/ikun0014/lx-music-mobile.git
synced 2025-07-03 17:12:10 +08:00
播放详情页新增桌面歌词显示/隐藏切换按钮
This commit is contained in:
parent
4cddb9c947
commit
81c8d98508
Binary file not shown.
@ -10,6 +10,7 @@
|
|||||||
- 新增APP全局字体阴影,默认关闭,可到设置-主题设置启用
|
- 新增APP全局字体阴影,默认关闭,可到设置-主题设置启用
|
||||||
- 新增启用竖屏首页横向滚动设置,默认开启(原来的行为),如果你不想要竖屏的首页左右滑动则可以关闭此设置(#397)
|
- 新增启用竖屏首页横向滚动设置,默认开启(原来的行为),如果你不想要竖屏的首页左右滑动则可以关闭此设置(#397)
|
||||||
- 新增“使用系统文件选择器”设置,默认启用,启用该选项后,导入备份文件、自定义源等操作将不需要申请存储权限,但可能在某些系统上不可用
|
- 新增“使用系统文件选择器”设置,默认启用,启用该选项后,导入备份文件、自定义源等操作将不需要申请存储权限,但可能在某些系统上不可用
|
||||||
|
- 播放详情页新增桌面歌词显示/隐藏切换按钮,长按可切换歌词锁定状态
|
||||||
- 添加 Android 5 特别版(版本号包含`android_5`)与墨·状态栏特别版(版本号包含`sl`)的 release 构建
|
- 添加 Android 5 特别版(版本号包含`android_5`)与墨·状态栏特别版(版本号包含`sl`)的 release 构建
|
||||||
|
|
||||||
### 优化
|
### 优化
|
||||||
|
61
src/components/DesktopLyricEnable.tsx
Normal file
61
src/components/DesktopLyricEnable.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { forwardRef, useImperativeHandle, useRef } from 'react'
|
||||||
|
|
||||||
|
import ConfirmAlert, { type ConfirmAlertType } from '@/components/common/ConfirmAlert'
|
||||||
|
|
||||||
|
import { toast } from '@/utils/tools'
|
||||||
|
|
||||||
|
import { useI18n } from '@/lang'
|
||||||
|
import { checkDesktopLyricOverlayPermission, hideDesktopLyric, openDesktopLyricOverlayPermissionActivity, showDesktopLyric } from '@/core/desktopLyric'
|
||||||
|
import { updateSetting } from '@/core/common'
|
||||||
|
|
||||||
|
export interface DesktopLyricEnableType {
|
||||||
|
setEnabled: (enabled: boolean) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default forwardRef<DesktopLyricEnableType, {}>((props, ref) => {
|
||||||
|
const t = useI18n()
|
||||||
|
// const setIsShowDesktopLyric = useDispatch('common', 'setIsShowDesktopLyric')
|
||||||
|
const confirmAlertRef = useRef<ConfirmAlertType>(null)
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
setEnabled(enabled) {
|
||||||
|
void handleChangeEnableDesktopLyric(enabled)
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const handleChangeEnableDesktopLyric = async(isEnable: boolean) => {
|
||||||
|
if (isEnable) {
|
||||||
|
try {
|
||||||
|
await checkDesktopLyricOverlayPermission()
|
||||||
|
await showDesktopLyric()
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
confirmAlertRef.current?.setVisible(true)
|
||||||
|
// return false
|
||||||
|
}
|
||||||
|
} else await hideDesktopLyric()
|
||||||
|
// return true
|
||||||
|
updateSetting({ 'desktopLyric.enable': isEnable })
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTipsCancel = () => {
|
||||||
|
updateSetting({ 'desktopLyric.enable': false })
|
||||||
|
toast(t('disagree_tip'), 'long')
|
||||||
|
}
|
||||||
|
const handleTipsConfirm = () => {
|
||||||
|
confirmAlertRef.current?.setVisible(false)
|
||||||
|
void openDesktopLyricOverlayPermissionActivity()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmAlert
|
||||||
|
ref={confirmAlertRef}
|
||||||
|
onCancel={handleTipsCancel}
|
||||||
|
onConfirm={handleTipsConfirm}
|
||||||
|
bgHide={false}
|
||||||
|
closeBtn={false}
|
||||||
|
cancelText={t('disagree')}
|
||||||
|
confirmText={t('agree_go')}
|
||||||
|
text={t('setting_lyric_dektop_permission_tip')} />
|
||||||
|
)
|
||||||
|
})
|
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -1,58 +1,28 @@
|
|||||||
import { memo, useRef } from 'react'
|
import { memo, useRef } from 'react'
|
||||||
import { View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
|
|
||||||
import ConfirmAlert, { type ConfirmAlertType } from '@/components/common/ConfirmAlert'
|
|
||||||
import CheckBoxItem from '../../components/CheckBoxItem'
|
import CheckBoxItem from '../../components/CheckBoxItem'
|
||||||
|
|
||||||
import { createStyle, toast } from '@/utils/tools'
|
import { createStyle } from '@/utils/tools'
|
||||||
|
|
||||||
import { useI18n } from '@/lang'
|
import { useI18n } from '@/lang'
|
||||||
import { useSettingValue } from '@/store/setting/hook'
|
import { useSettingValue } from '@/store/setting/hook'
|
||||||
import { checkDesktopLyricOverlayPermission, hideDesktopLyric, openDesktopLyricOverlayPermissionActivity, showDesktopLyric } from '@/core/desktopLyric'
|
import DesktopLyricEnable, { type DesktopLyricEnableType } from '@/components/DesktopLyricEnable'
|
||||||
import { updateSetting } from '@/core/common'
|
|
||||||
|
|
||||||
export default memo(() => {
|
export default memo(() => {
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const isEnable = useSettingValue('desktopLyric.enable')
|
const isEnable = useSettingValue('desktopLyric.enable')
|
||||||
// const setIsShowDesktopLyric = useDispatch('common', 'setIsShowDesktopLyric')
|
// const setIsShowDesktopLyric = useDispatch('common', 'setIsShowDesktopLyric')
|
||||||
const confirmAlertRef = useRef<ConfirmAlertType>(null)
|
const desktopLyricEnableRef = useRef<DesktopLyricEnableType>(null)
|
||||||
|
|
||||||
const handleChangeEnableDesktopLyric = async(isEnable: boolean) => {
|
const handleChangeEnableDesktopLyric = async(isEnable: boolean) => {
|
||||||
if (isEnable) {
|
desktopLyricEnableRef.current?.setEnabled(isEnable)
|
||||||
try {
|
|
||||||
await checkDesktopLyricOverlayPermission()
|
|
||||||
await showDesktopLyric()
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
confirmAlertRef.current?.setVisible(true)
|
|
||||||
// return false
|
|
||||||
}
|
|
||||||
} else await hideDesktopLyric()
|
|
||||||
// return true
|
|
||||||
updateSetting({ 'desktopLyric.enable': isEnable })
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleTipsCancel = () => {
|
|
||||||
updateSetting({ 'desktopLyric.enable': false })
|
|
||||||
toast(t('disagree_tip'), 'long')
|
|
||||||
}
|
|
||||||
const handleTipsConfirm = () => {
|
|
||||||
confirmAlertRef.current?.setVisible(false)
|
|
||||||
void openDesktopLyricOverlayPermissionActivity()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
<CheckBoxItem check={isEnable} onChange={(enable) => { void handleChangeEnableDesktopLyric(enable) }} label={t('setting_lyric_desktop_enable')} />
|
<CheckBoxItem check={isEnable} onChange={(enable) => { void handleChangeEnableDesktopLyric(enable) }} label={t('setting_lyric_desktop_enable')} />
|
||||||
<ConfirmAlert
|
<DesktopLyricEnable ref={desktopLyricEnableRef} />
|
||||||
ref={confirmAlertRef}
|
|
||||||
onCancel={handleTipsCancel}
|
|
||||||
onConfirm={handleTipsConfirm}
|
|
||||||
bgHide={false}
|
|
||||||
closeBtn={false}
|
|
||||||
cancelText={t('disagree')}
|
|
||||||
confirmText={t('agree_go')}
|
|
||||||
text={t('setting_lyric_dektop_permission_tip')} />
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -8,15 +8,16 @@ import { HEADER_HEIGHT } from '@/config/constant'
|
|||||||
export const BTN_WIDTH = scaleSizeW(HEADER_HEIGHT)
|
export const BTN_WIDTH = scaleSizeW(HEADER_HEIGHT)
|
||||||
export const BTN_ICON_SIZE = 20
|
export const BTN_ICON_SIZE = 20
|
||||||
|
|
||||||
export default ({ icon, size, color, onPress }: {
|
export default ({ icon, size, color, onPress, onLongPress }: {
|
||||||
icon: string
|
icon: string
|
||||||
size?: number
|
size?: number
|
||||||
color?: string
|
color?: string
|
||||||
onPress: () => void
|
onPress: () => void
|
||||||
|
onLongPress?: () => void
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity style={{ ...styles.cotrolBtn, width: BTN_WIDTH, height: BTN_WIDTH }} activeOpacity={0.5} onPress={onPress}>
|
<TouchableOpacity style={{ ...styles.cotrolBtn, width: BTN_WIDTH, height: BTN_WIDTH }} activeOpacity={0.5} onPress={onPress} onLongPress={onLongPress}>
|
||||||
<Icon name={icon} color={color ?? theme['c-550']} size={size ?? BTN_ICON_SIZE} />
|
<Icon name={icon} color={color ?? theme['c-550']} size={size ?? BTN_ICON_SIZE} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
import Btn from './Btn'
|
||||||
|
import { useSettingValue } from '@/store/setting/hook'
|
||||||
|
import DesktopLyricEnable, { type DesktopLyricEnableType } from '@/components/DesktopLyricEnable'
|
||||||
|
import { memo, useRef } from 'react'
|
||||||
|
import { toggleDesktopLyricLock } from '@/core/desktopLyric'
|
||||||
|
import { updateSetting } from '@/core/common'
|
||||||
|
import settingState from '@/store/setting/state'
|
||||||
|
|
||||||
|
|
||||||
|
export default memo(() => {
|
||||||
|
const enabledLyric = useSettingValue('desktopLyric.enable')
|
||||||
|
const desktopLyricEnableRef = useRef<DesktopLyricEnableType>(null)
|
||||||
|
const update = () => {
|
||||||
|
desktopLyricEnableRef.current?.setEnabled(!enabledLyric)
|
||||||
|
}
|
||||||
|
const updateLock = () => {
|
||||||
|
const isLock = !settingState.setting['desktopLyric.isLock']
|
||||||
|
void toggleDesktopLyricLock(isLock).then(() => {
|
||||||
|
updateSetting({ 'desktopLyric.isLock': isLock })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Btn icon={enabledLyric ? 'lyric-on' : 'lyric-off'} onPress={update} onLongPress={updateLock} />
|
||||||
|
<DesktopLyricEnable ref={desktopLyricEnableRef} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
@ -13,6 +13,7 @@ import commonState from '@/store/common/state'
|
|||||||
import CommentBtn from './CommentBtn'
|
import CommentBtn from './CommentBtn'
|
||||||
import Btn from './Btn'
|
import Btn from './Btn'
|
||||||
import SettingPopup, { type SettingPopupType } from '../../components/SettingPopup'
|
import SettingPopup, { type SettingPopupType } from '../../components/SettingPopup'
|
||||||
|
import DesktopLyricBtn from './DesktopLyricBtn'
|
||||||
|
|
||||||
export const HEADER_HEIGHT = scaleSizeH(_HEADER_HEIGHT)
|
export const HEADER_HEIGHT = scaleSizeH(_HEADER_HEIGHT)
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ export default memo(() => {
|
|||||||
<Icon name="chevron-left" size={18} />
|
<Icon name="chevron-left" size={18} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Title />
|
<Title />
|
||||||
|
<DesktopLyricBtn />
|
||||||
<CommentBtn />
|
<CommentBtn />
|
||||||
<Btn icon="slider" onPress={showSetting} />
|
<Btn icon="slider" onPress={showSetting} />
|
||||||
</View>
|
</View>
|
||||||
|
@ -7,14 +7,15 @@ import { scaleSizeW } from '@/utils/pixelRatio'
|
|||||||
export const BTN_WIDTH = scaleSizeW(36)
|
export const BTN_WIDTH = scaleSizeW(36)
|
||||||
export const BTN_ICON_SIZE = 24
|
export const BTN_ICON_SIZE = 24
|
||||||
|
|
||||||
export default ({ icon, color, onPress }: {
|
export default ({ icon, color, onPress, onLongPress }: {
|
||||||
icon: string
|
icon: string
|
||||||
color?: string
|
color?: string
|
||||||
onPress: () => void
|
onPress: () => void
|
||||||
|
onLongPress?: () => void
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity style={{ ...styles.cotrolBtn, width: BTN_WIDTH, height: BTN_WIDTH }} activeOpacity={0.5} onPress={onPress}>
|
<TouchableOpacity style={{ ...styles.cotrolBtn, width: BTN_WIDTH, height: BTN_WIDTH }} activeOpacity={0.5} onPress={onPress} onLongPress={onLongPress}>
|
||||||
<Icon name={icon} color={color ?? theme['c-font-label']} size={BTN_ICON_SIZE} />
|
<Icon name={icon} color={color ?? theme['c-font-label']} size={BTN_ICON_SIZE} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
import Btn from './Btn'
|
||||||
|
import { useSettingValue } from '@/store/setting/hook'
|
||||||
|
import DesktopLyricEnable, { type DesktopLyricEnableType } from '@/components/DesktopLyricEnable'
|
||||||
|
import { memo, useRef } from 'react'
|
||||||
|
import { toggleDesktopLyricLock } from '@/core/desktopLyric'
|
||||||
|
import { updateSetting } from '@/core/common'
|
||||||
|
import settingState from '@/store/setting/state'
|
||||||
|
|
||||||
|
|
||||||
|
export default memo(() => {
|
||||||
|
const enabledLyric = useSettingValue('desktopLyric.enable')
|
||||||
|
const desktopLyricEnableRef = useRef<DesktopLyricEnableType>(null)
|
||||||
|
const update = () => {
|
||||||
|
desktopLyricEnableRef.current?.setEnabled(!enabledLyric)
|
||||||
|
}
|
||||||
|
const updateLock = () => {
|
||||||
|
const isLock = !settingState.setting['desktopLyric.isLock']
|
||||||
|
void toggleDesktopLyricLock(isLock).then(() => {
|
||||||
|
updateSetting({ 'desktopLyric.isLock': isLock })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Btn icon={enabledLyric ? 'lyric-on' : 'lyric-off'} onPress={update} onLongPress={updateLock} />
|
||||||
|
<DesktopLyricEnable ref={desktopLyricEnableRef} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
@ -2,13 +2,13 @@ import { createStyle } from '@/utils/tools'
|
|||||||
import { View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import PlayModeBtn from './PlayModeBtn'
|
import PlayModeBtn from './PlayModeBtn'
|
||||||
import MusicAddBtn from './MusicAddBtn'
|
import MusicAddBtn from './MusicAddBtn'
|
||||||
import TimeoutExitBtn from './TimeoutExitBtn'
|
import DesktopLyricBtn from './DesktopLyricBtn'
|
||||||
import CommentBtn from './CommentBtn'
|
import CommentBtn from './CommentBtn'
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<TimeoutExitBtn />
|
<DesktopLyricBtn />
|
||||||
<MusicAddBtn />
|
<MusicAddBtn />
|
||||||
<PlayModeBtn />
|
<PlayModeBtn />
|
||||||
<CommentBtn />
|
<CommentBtn />
|
||||||
|
28
src/screens/PlayDetail/Vertical/components/Btn.tsx
Normal file
28
src/screens/PlayDetail/Vertical/components/Btn.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { TouchableOpacity } from 'react-native'
|
||||||
|
import { Icon } from '@/components/common/Icon'
|
||||||
|
import { createStyle } from '@/utils/tools'
|
||||||
|
import { scaleSizeH } from '@/utils/pixelRatio'
|
||||||
|
import { HEADER_HEIGHT as _HEADER_HEIGHT } from '@/config/constant'
|
||||||
|
|
||||||
|
export const HEADER_HEIGHT = scaleSizeH(_HEADER_HEIGHT)
|
||||||
|
|
||||||
|
export default ({ icon, color, onPress }: {
|
||||||
|
icon: string
|
||||||
|
color?: string
|
||||||
|
onPress: () => void
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<TouchableOpacity onPress={onPress} style={{ ...styles.button, width: HEADER_HEIGHT }}>
|
||||||
|
<Icon name={icon} color={color} size={18} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = createStyle({
|
||||||
|
button: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '100%',
|
||||||
|
flex: 0,
|
||||||
|
},
|
||||||
|
})
|
@ -1,8 +1,7 @@
|
|||||||
import { memo, useRef } from 'react'
|
import { memo, useRef } from 'react'
|
||||||
|
|
||||||
import { View, StyleSheet, TouchableOpacity } from 'react-native'
|
import { View, StyleSheet } from 'react-native'
|
||||||
|
|
||||||
import { Icon } from '@/components/common/Icon'
|
|
||||||
import { pop } from '@/navigation'
|
import { pop } from '@/navigation'
|
||||||
import StatusBar from '@/components/common/StatusBar'
|
import StatusBar from '@/components/common/StatusBar'
|
||||||
import { useTheme } from '@/store/theme/hook'
|
import { useTheme } from '@/store/theme/hook'
|
||||||
@ -13,6 +12,8 @@ import { HEADER_HEIGHT as _HEADER_HEIGHT, NAV_SHEAR_NATIVE_IDS } from '@/config/
|
|||||||
import commonState from '@/store/common/state'
|
import commonState from '@/store/common/state'
|
||||||
import SettingPopup, { type SettingPopupType } from '../../components/SettingPopup'
|
import SettingPopup, { type SettingPopupType } from '../../components/SettingPopup'
|
||||||
import { useStatusbarHeight } from '@/store/common/hook'
|
import { useStatusbarHeight } from '@/store/common/hook'
|
||||||
|
import Btn from './Btn'
|
||||||
|
import TimeoutExitBtn from './TimeoutExitBtn'
|
||||||
|
|
||||||
export const HEADER_HEIGHT = scaleSizeH(_HEADER_HEIGHT)
|
export const HEADER_HEIGHT = scaleSizeH(_HEADER_HEIGHT)
|
||||||
|
|
||||||
@ -45,13 +46,10 @@ export default memo(() => {
|
|||||||
<View style={{ height: HEADER_HEIGHT + statusBarHeight, paddingTop: statusBarHeight }} nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_header}>
|
<View style={{ height: HEADER_HEIGHT + statusBarHeight, paddingTop: statusBarHeight }} nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_header}>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<TouchableOpacity onPress={back} style={{ ...styles.button, width: HEADER_HEIGHT }}>
|
<Btn icon="chevron-left" onPress={back} />
|
||||||
<Icon name="chevron-left" size={18} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
<Title />
|
<Title />
|
||||||
<TouchableOpacity onPress={showSetting} style={{ ...styles.button, width: HEADER_HEIGHT }}>
|
<TimeoutExitBtn />
|
||||||
<Icon name="slider" size={19} />
|
<Btn icon="slider" onPress={showSetting} />
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
</View>
|
||||||
<SettingPopup ref={popupRef} direction="vertical" />
|
<SettingPopup ref={popupRef} direction="vertical" />
|
||||||
</View>
|
</View>
|
||||||
@ -65,15 +63,10 @@ const styles = StyleSheet.create({
|
|||||||
// justifyContent: 'center',
|
// justifyContent: 'center',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
},
|
},
|
||||||
button: {
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
height: '100%',
|
|
||||||
flex: 0,
|
|
||||||
},
|
|
||||||
titleContent: {
|
titleContent: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
paddingHorizontal: 5,
|
||||||
|
// alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import { memo, useRef } from 'react'
|
||||||
|
import TimeoutExitEditModal, { type TimeoutExitEditModalType, useTimeInfo } from '@/components/TimeoutExitEditModal'
|
||||||
|
import { useTheme } from '@/store/theme/hook'
|
||||||
|
import Btn from './Btn'
|
||||||
|
|
||||||
|
|
||||||
|
export default memo(() => {
|
||||||
|
const theme = useTheme()
|
||||||
|
const modalRef = useRef<TimeoutExitEditModalType>(null)
|
||||||
|
|
||||||
|
const timeInfo = useTimeInfo()
|
||||||
|
|
||||||
|
const handleShow = () => {
|
||||||
|
modalRef.current?.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Btn icon="music_time" color={timeInfo.active ? theme['c-primary-font-active'] : theme['c-font']} onPress={handleShow} />
|
||||||
|
<TimeoutExitEditModal ref={modalRef} timeInfo={timeInfo} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
Loading…
x
Reference in New Issue
Block a user