feat: 轮播图
This commit is contained in:
parent
399460a259
commit
b428010a9c
17
assets/icons/svg/proIcon.svg
Normal file
17
assets/icons/svg/proIcon.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 262 KiB |
BIN
assets/images/png/owner/proIcon.png
Normal file
BIN
assets/images/png/owner/proIcon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@ -125,9 +125,9 @@ const AskNavbar = ({ wsStatus }: AskNavbarProps) => {
|
|||||||
},
|
},
|
||||||
centerButton: {
|
centerButton: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: width / 2,
|
left: '50%',
|
||||||
top: -30,
|
top: -30,
|
||||||
marginLeft: -49,
|
transform: [{ translateX: -42.5 }],
|
||||||
width: 85,
|
width: 85,
|
||||||
height: 85,
|
height: 85,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@ -162,7 +162,7 @@ const AskNavbar = ({ wsStatus }: AskNavbarProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Image source={require('@/assets/images/png/owner/ask.png')} style={{ width: width }} />
|
<Image source={require('@/assets/images/png/owner/ask.png')} style={{ width: "100%" }} />
|
||||||
<View style={styles.navContainer}>
|
<View style={styles.navContainer}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => navigateTo('/memo-list')}
|
onPress={() => navigateTo('/memo-list')}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ const width = Dimensions.get("window").width;
|
|||||||
|
|
||||||
function CarouselComponent(props: Props) {
|
function CarouselComponent(props: Props) {
|
||||||
const { data } = props;
|
const { data } = props;
|
||||||
|
const [currentIndex, setCurrentIndex] = React.useState(0);
|
||||||
const [carouselDataValue, setCarouselDataValue] = React.useState<CarouselData[]>([]);
|
const [carouselDataValue, setCarouselDataValue] = React.useState<CarouselData[]>([]);
|
||||||
const dataHandle = () => {
|
const dataHandle = () => {
|
||||||
const carouselData = { ...data?.category_count, total_count: data?.total_count }
|
const carouselData = { ...data?.category_count, total_count: data?.total_count }
|
||||||
@ -44,7 +45,7 @@ function CarouselComponent(props: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const totleItem = (data: UserCountData) => {
|
const totleItem = (data: UserCountData) => {
|
||||||
return <View style={styles.container}>
|
return <View style={[styles.container, { width: width * 0.7 }]}>
|
||||||
{Object?.entries(data)?.filter(([key]) => key !== 'cover_url')?.map((item, index) => (
|
{Object?.entries(data)?.filter(([key]) => key !== 'cover_url')?.map((item, index) => (
|
||||||
<View style={styles.item} key={index}>
|
<View style={styles.item} key={index}>
|
||||||
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8, width: "75%", overflow: 'hidden' }}>
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8, width: "75%", overflow: 'hidden' }}>
|
||||||
@ -68,37 +69,43 @@ function CarouselComponent(props: Props) {
|
|||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ flex: 1 }}>
|
<View
|
||||||
|
style={{
|
||||||
|
flex: 1
|
||||||
|
}}>
|
||||||
<Carousel
|
<Carousel
|
||||||
width={width}
|
width={width}
|
||||||
height={width * 0.75}
|
height={width * 0.75}
|
||||||
data={carouselDataValue || []}
|
data={carouselDataValue || []}
|
||||||
mode="parallax"
|
mode="parallax"
|
||||||
// defaultIndex={
|
onSnapToItem={(index) => setCurrentIndex(index)}
|
||||||
// carouselDataValue?.length
|
defaultIndex={
|
||||||
// ? Math.max(0, Math.min(
|
carouselDataValue?.length
|
||||||
// carouselDataValue.length - 1,
|
? Math.max(0, Math.min(
|
||||||
// carouselDataValue.findIndex((item) => item?.key === 'total_count') - 1
|
carouselDataValue.length - 1,
|
||||||
// ))
|
carouselDataValue.findIndex((item) => item?.key === 'total_count') - 1
|
||||||
// : 0
|
))
|
||||||
// }
|
: 0
|
||||||
|
}
|
||||||
modeConfig={{
|
modeConfig={{
|
||||||
parallaxScrollingScale: 1,
|
parallaxScrollingScale: 1,
|
||||||
parallaxScrollingOffset: 150,
|
parallaxScrollingOffset: 140,
|
||||||
parallaxAdjacentItemScale: 0.7
|
parallaxAdjacentItemScale: 0.7
|
||||||
}}
|
}}
|
||||||
renderItem={({ item, index }) => {
|
renderItem={({ item, index }) => {
|
||||||
|
const isActive = index === currentIndex;
|
||||||
const style: ViewStyle = {
|
const style: ViewStyle = {
|
||||||
width: width,
|
width: width,
|
||||||
height: width * 0.8,
|
height: width * 0.8,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
// paddingTop: isActive && item?.key === 'total_count' ? 0 : 40
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<View key={index} style={style}>
|
<View key={index} style={[style]}>
|
||||||
{item?.key === 'total_count' ? (
|
{item?.key === 'total_count' ? (
|
||||||
totleItem(item.value)
|
totleItem(item.value)
|
||||||
) : (
|
) : (
|
||||||
<View style={{ flex: 1, width: width * 0.65 }}>
|
<View>
|
||||||
{CategoryComponent({
|
{CategoryComponent({
|
||||||
title: item?.key,
|
title: item?.key,
|
||||||
data: [
|
data: [
|
||||||
@ -107,6 +114,7 @@ function CarouselComponent(props: Props) {
|
|||||||
{ title: 'Length', number: formatDuration(item?.value?.video_length || 0) }
|
{ title: 'Length', number: formatDuration(item?.value?.video_length || 0) }
|
||||||
],
|
],
|
||||||
bgSvg: item?.value?.cover_url,
|
bgSvg: item?.value?.cover_url,
|
||||||
|
width: width
|
||||||
})}
|
})}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
@ -124,13 +132,13 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
borderRadius: 32,
|
borderRadius: 32,
|
||||||
padding: 4
|
padding: 6
|
||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
backgroundColor: "#FFB645",
|
backgroundColor: "#FFB645",
|
||||||
paddingVertical: 8,
|
paddingVertical: 8,
|
||||||
paddingHorizontal: 16,
|
paddingLeft: 32,
|
||||||
borderRadius: 16,
|
borderRadius: 32,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@ -157,10 +165,10 @@ const styles = StyleSheet.create({
|
|||||||
number: {
|
number: {
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
fontWeight: "700",
|
fontWeight: "700",
|
||||||
fontSize: 26,
|
fontSize: 28,
|
||||||
|
lineHeight: 30,
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
flex: 1,
|
flex: 1
|
||||||
paddingTop: 8
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -6,16 +6,45 @@ import VideoTotalSvg from "@/assets/icons/svg/videoTotalWhite.svg";
|
|||||||
import { BlurView } from "expo-blur";
|
import { BlurView } from "expo-blur";
|
||||||
import { Image, StyleProp, StyleSheet, View, ViewStyle } from "react-native";
|
import { Image, StyleProp, StyleSheet, View, ViewStyle } from "react-native";
|
||||||
import { ThemedText } from "../ThemedText";
|
import { ThemedText } from "../ThemedText";
|
||||||
|
|
||||||
interface CategoryProps {
|
interface CategoryProps {
|
||||||
title: string;
|
title: string;
|
||||||
data: { title: string, number: string | number }[];
|
data: { title: string, number: { s: number, m: number, h: number } | number }[];
|
||||||
bgSvg: string | null;
|
bgSvg: string | null;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
|
width: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CategoryComponent = ({ title, data, bgSvg, style }: CategoryProps) => {
|
const TimeUnit = ({ value, unit }: { value: number; unit: string }) => (
|
||||||
|
value > 0 && (
|
||||||
|
<>
|
||||||
|
<ThemedText style={styles.itemNumber}>{value}</ThemedText>
|
||||||
|
<ThemedText style={[styles.itemNumber, { fontSize: 10 }]}>{unit}</ThemedText>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const CategoryComponent = ({ title, data, bgSvg, style, width }: CategoryProps) => {
|
||||||
|
const renderTimeDisplay = (time: { s: number; m: number; h: number }) => {
|
||||||
|
const { h, m, s } = time;
|
||||||
|
const showSeconds = s > 0 || (s === 0 && m === 0 && h === 0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemedText style={{ flexDirection: 'row', alignItems: 'flex-end', gap: 2 }}>
|
||||||
|
<TimeUnit value={h} unit="h" />
|
||||||
|
<TimeUnit value={m} unit="m" />
|
||||||
|
{showSeconds && (
|
||||||
|
<>
|
||||||
|
<ThemedText style={styles.itemNumber}>{s}</ThemedText>
|
||||||
|
<ThemedText style={[styles.itemNumber, { fontSize: 10 }]}>s</ThemedText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ThemedText>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, style]}>
|
<View style={[styles.container, style, { width: width * 0.7 }]}>
|
||||||
<View style={styles.backgroundContainer}>
|
<View style={styles.backgroundContainer}>
|
||||||
<Image
|
<Image
|
||||||
source={bgSvg !== "" && bgSvg !== null ? { uri: bgSvg } : require('@/assets/images/png/owner/people.png')}
|
source={bgSvg !== "" && bgSvg !== null ? { uri: bgSvg } : require('@/assets/images/png/owner/people.png')}
|
||||||
@ -38,7 +67,19 @@ const CategoryComponent = ({ title, data, bgSvg, style }: CategoryProps) => {
|
|||||||
</View>
|
</View>
|
||||||
<ThemedText style={styles.itemTitle}>{item.title}</ThemedText>
|
<ThemedText style={styles.itemTitle}>{item.title}</ThemedText>
|
||||||
</View>
|
</View>
|
||||||
<ThemedText style={styles.itemNumber}>{item.number}</ThemedText>
|
<View style={{ alignSelf: 'flex-start', flex: 1 }}>
|
||||||
|
{item?.title === "Length" ? (
|
||||||
|
typeof item.number === 'object' ? (
|
||||||
|
renderTimeDisplay(item.number)
|
||||||
|
) : (
|
||||||
|
<ThemedText style={[styles.itemNumber]}>{item.number}</ThemedText>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<ThemedText style={[styles.itemNumber]}>
|
||||||
|
{typeof item.number === 'number' ? item.number : 0}
|
||||||
|
</ThemedText>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
<View style={styles.titleContent}>
|
<View style={styles.titleContent}>
|
||||||
@ -68,7 +109,7 @@ const styles = StyleSheet.create({
|
|||||||
backdropFilter: 'blur(5px)',
|
backdropFilter: 'blur(5px)',
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
padding: 16,
|
padding: 32,
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
@ -108,11 +149,11 @@ const styles = StyleSheet.create({
|
|||||||
itemNumber: {
|
itemNumber: {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
|
lineHeight: 30,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
marginLeft: 8,
|
marginLeft: 8,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
paddingTop: 8
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import MemberBgSvg from '@/assets/icons/svg/memberBg.svg';
|
|
||||||
import ProTextSvg from '@/assets/icons/svg/proText.svg';
|
import ProTextSvg from '@/assets/icons/svg/proText.svg';
|
||||||
import GradientText from '@/components/textLinear';
|
import GradientText from '@/components/textLinear';
|
||||||
import { ThemedText } from '@/components/ThemedText';
|
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Dimensions, StyleSheet, TouchableOpacity, View } from "react-native";
|
import { Dimensions, StyleSheet, TouchableOpacity, View } from "react-native";
|
||||||
@ -29,20 +27,20 @@ const MemberCard = ({ pro }: { pro: string }) => {
|
|||||||
<IpSvg pro={pro} />
|
<IpSvg pro={pro} />
|
||||||
</View>
|
</View>
|
||||||
{/* 会员标识 */}
|
{/* 会员标识 */}
|
||||||
<View style={[styles.memberContainer, { left: width * 0.25, top: width * 0.1, opacity: 1 }]}>
|
{/* <View style={[styles.memberContainer, { left: width * 0.25, top: width * 0.1, opacity: 1 }]}>
|
||||||
<MemberBgSvg />
|
<MemberBgSvg />
|
||||||
<ThemedText style={{ fontSize: 12, color: "#2D3D60", position: "absolute", left: 0, top: 0, bottom: 0, right: 0, textAlign: "center", textAlignVertical: "center" }}>{t("personal:member.goPremium")}</ThemedText>
|
<ThemedText style={{ fontSize: 12, color: "#2D3D60", position: "absolute", left: 0, top: 0, bottom: 0, right: 0, textAlign: "center", textAlignVertical: "center" }}>{t("personal:member.goPremium")}</ThemedText>
|
||||||
</View>
|
</View> */}
|
||||||
{/* 解锁更多魔法 */}
|
{/* 解锁更多魔法 */}
|
||||||
<View style={{ position: "absolute", bottom: width * 0.02, left: -width * 0.01, opacity: pro === "pro" ? 1 : 0.5, width: width * 0.1, flexWrap: "wrap" }}>
|
<View style={{ position: "absolute", bottom: width * 0.05, left: -width * 0.12, opacity: pro === "pro" ? 1 : 0.5, flexWrap: "wrap" }}>
|
||||||
<GradientText
|
<GradientText
|
||||||
text={t("personal:member.unlock")}
|
text={t("personal:member.unlock")}
|
||||||
width={width * 0.4}
|
|
||||||
fontSize={16}
|
fontSize={16}
|
||||||
lineHeight={1.5}
|
lineHeight={1.5}
|
||||||
color={[
|
color={[
|
||||||
{ offset: "0%", color: "#FF512F" },
|
{ offset: "0%", color: "#D0BFB0" },
|
||||||
{ offset: "100%", color: "#F09819" }
|
{ offset: "32.89%", color: "#FFE57D" },
|
||||||
|
{ offset: "81.1%", color: "#FFFFFF" }
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { ThemedText } from '@/components/ThemedText';
|
|||||||
import { UserInfoDetails } from '@/types/user';
|
import { UserInfoDetails } from '@/types/user';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Image, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native';
|
import { Image, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||||
import CopyButton from '../copy';
|
import CopyButton from '../copy';
|
||||||
|
|
||||||
export default function UserInfo({ userInfo }: { userInfo: UserInfoDetails }) {
|
export default function UserInfo({ userInfo }: { userInfo: UserInfoDetails }) {
|
||||||
@ -14,7 +14,7 @@ export default function UserInfo({ userInfo }: { userInfo: UserInfoDetails }) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='flex flex-row justify-between items-center mt-[1rem] gap-[1rem] w-full'>
|
<View style={styles.container}>
|
||||||
{/* 头像 */}
|
{/* 头像 */}
|
||||||
<View className='w-auto'>
|
<View className='w-auto'>
|
||||||
{userInfo?.user_info?.avatar_file_url && !imageError ? (
|
{userInfo?.user_info?.avatar_file_url && !imageError ? (
|
||||||
@ -30,45 +30,30 @@ export default function UserInfo({ userInfo }: { userInfo: UserInfoDetails }) {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
{/* 用户名 */}
|
{/* 用户名 */}
|
||||||
<View className='flex flex-col w-[75%] gap-1'>
|
<View className='flex flex-col w-[60%]'>
|
||||||
<View className='flex flex-row items-center justify-between w-full'>
|
<View className='flex flex-row items-center justify-between w-full'>
|
||||||
<View className='flex flex-row items-center gap-2 w-full'>
|
<View className='flex flex-row items-center gap-2 w-full justify-between'>
|
||||||
<ThemedText
|
<View style={{ width: "100%", flexDirection: "row", alignItems: "center", gap: 2 }}>
|
||||||
className='max-w-[80%] !text-textSecondary !font-semibold !text-2xl'
|
<ThemedText
|
||||||
numberOfLines={1} // 限制为1行
|
style={{ maxWidth: "90%", color: '#4C320C', fontSize: 32, lineHeight: 36, fontWeight: '700' }}
|
||||||
ellipsizeMode="tail"
|
numberOfLines={1}
|
||||||
>
|
ellipsizeMode="tail"
|
||||||
{userInfo?.user_info?.nickname}
|
>
|
||||||
</ThemedText>
|
{userInfo?.user_info?.nickname}
|
||||||
<ScrollView
|
</ThemedText>
|
||||||
className='max-w-[20%]'
|
|
||||||
horizontal // 水平滚动
|
|
||||||
showsHorizontalScrollIndicator={false} // 隐藏滚动条
|
|
||||||
contentContainerStyle={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
gap: 8, // 间距,
|
|
||||||
alignItems: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{
|
{
|
||||||
userInfo?.medal_infos?.map((item, index) => (
|
userInfo?.membership_level && (
|
||||||
<Image
|
<Image
|
||||||
key={index}
|
source={require('@/assets/images/png/owner/proIcon.png')}
|
||||||
source={{ uri: item.url }}
|
|
||||||
style={{ width: 24, height: 24 }}
|
style={{ width: 24, height: 24 }}
|
||||||
/>
|
/>
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
<View className='flex flex-row items-center gap-2 border border-bgPrimary px-2 py-1 rounded-full'>
|
|
||||||
<StarSvg />
|
|
||||||
<ThemedText style={{ color: 'bgPrimary', fontSize: 14, fontWeight: '700' }}>{userInfo?.remain_points}</ThemedText>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View className='flex flex-row items-center justify-between w-full'>
|
<View className='flex flex-row items-center justify-between w-full'>
|
||||||
<View style={{ flex: 1, flexDirection: "row", alignItems: "center", gap: 2, maxWidth: '80%' }}>
|
<View style={{ flex: 1, flexDirection: "row", alignItems: "center", gap: 2, maxWidth: '100%' }}>
|
||||||
<ThemedText
|
<ThemedText
|
||||||
style={{
|
style={{
|
||||||
color: '#AC7E35',
|
color: '#AC7E35',
|
||||||
@ -84,23 +69,56 @@ export default function UserInfo({ userInfo }: { userInfo: UserInfoDetails }) {
|
|||||||
</ThemedText>
|
</ThemedText>
|
||||||
<CopyButton textToCopy={userInfo?.user_info?.user_id || ""} />
|
<CopyButton textToCopy={userInfo?.user_info?.user_id || ""} />
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => {
|
|
||||||
router.push('/setting');
|
|
||||||
}}
|
|
||||||
activeOpacity={0.7}
|
|
||||||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
||||||
style={styles.text}
|
|
||||||
>
|
|
||||||
<SettingSvg />
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
<View style={{ flexDirection: "column", alignItems: "flex-end", gap: 4 }}>
|
||||||
|
<View style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 8,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#FFB645',
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
paddingVertical: 4,
|
||||||
|
borderRadius: 16,
|
||||||
|
}}>
|
||||||
|
<StarSvg />
|
||||||
|
<ThemedText
|
||||||
|
style={{
|
||||||
|
color: '#4C320C',
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '700',
|
||||||
|
maxWidth: 40,
|
||||||
|
lineHeight: 20
|
||||||
|
}}
|
||||||
|
numberOfLines={1}
|
||||||
|
ellipsizeMode="tail"
|
||||||
|
>
|
||||||
|
{userInfo?.remain_points}
|
||||||
|
</ThemedText>
|
||||||
|
</View>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => {
|
||||||
|
router.push('/setting');
|
||||||
|
}}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||||||
|
style={styles.text}
|
||||||
|
>
|
||||||
|
<SettingSvg />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
</View >
|
</View >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "flex-end",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
text: {
|
text: {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
|
|||||||
@ -3,9 +3,9 @@
|
|||||||
* @param seconds 总秒数
|
* @param seconds 总秒数
|
||||||
* @returns 格式化后的时间字符串
|
* @returns 格式化后的时间字符串
|
||||||
*/
|
*/
|
||||||
export function formatDuration(seconds: number): string {
|
export function formatDuration(seconds: number): { s: number, m: number, h: number } {
|
||||||
if (seconds < 60) {
|
if (seconds < 60) {
|
||||||
return `${seconds}s`;
|
return { s: seconds, m: 0, h: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const minutes = Math.floor(seconds / 60);
|
const minutes = Math.floor(seconds / 60);
|
||||||
@ -13,16 +13,16 @@ export function formatDuration(seconds: number): string {
|
|||||||
|
|
||||||
if (minutes < 60) {
|
if (minutes < 60) {
|
||||||
return remainingSeconds > 0
|
return remainingSeconds > 0
|
||||||
? `${minutes}min${remainingSeconds}s`
|
? { s: remainingSeconds, m: minutes, h: 0 }
|
||||||
: `${minutes}min`;
|
: { s: 0, m: minutes, h: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const hours = Math.floor(minutes / 60);
|
const hours = Math.floor(minutes / 60);
|
||||||
const remainingMinutes = minutes % 60;
|
const remainingMinutes = minutes % 60;
|
||||||
|
|
||||||
if (remainingMinutes === 0) {
|
if (remainingMinutes === 0) {
|
||||||
return `${hours}h`;
|
return { s: 0, m: 0, h: hours };
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${hours}h${remainingMinutes}min`;
|
return { s: seconds, m: minutes, h: hours };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,6 +128,6 @@
|
|||||||
},
|
},
|
||||||
"member": {
|
"member": {
|
||||||
"goPremium": "Go Premium",
|
"goPremium": "Go Premium",
|
||||||
"unlock": "Unlock more memory magic"
|
"unlock": "解锁更多记忆魔法"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user