feat(app): 优化Expo Updates更新机制,改进睡眠阶段时间轴UI设计,升级项目依赖
This commit is contained in:
6
app.json
6
app.json
@@ -36,6 +36,7 @@
|
|||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"expo-router",
|
"expo-router",
|
||||||
|
"expo-updates",
|
||||||
[
|
[
|
||||||
"expo-splash-screen",
|
"expo-splash-screen",
|
||||||
{
|
{
|
||||||
@@ -70,10 +71,13 @@
|
|||||||
"experiments": {
|
"experiments": {
|
||||||
"typedRoutes": true
|
"typedRoutes": true
|
||||||
},
|
},
|
||||||
|
"runtimeVersion": {
|
||||||
|
"policy": "appVersion"
|
||||||
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"package": "com.anonymous.digitalpilates"
|
"package": "com.anonymous.digitalpilates"
|
||||||
},
|
},
|
||||||
"updates": {
|
"updates": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"checkAutomatically": "ON_LOAD",
|
"checkAutomatically": "ON_LOAD",
|
||||||
"url": "https://pilate.richarjiang.com/api/expo-updates/manifest"
|
"url": "https://pilate.richarjiang.com/api/expo-updates/manifest"
|
||||||
|
|||||||
@@ -488,7 +488,15 @@ function Bootstrapper({ children }: { children: React.ReactNode }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function checkUpdate() {
|
async function checkUpdate() {
|
||||||
try {
|
try {
|
||||||
logger.info("Checking for updates...");
|
logger.info(`Checking for updates..., env: ${__DEV__}, Updates.isEnabled: ${Updates.isEnabled}`);
|
||||||
|
|
||||||
|
// 只有在 expo-updates 启用时才检查更新
|
||||||
|
// 开发环境或 Updates 未启用时跳过
|
||||||
|
if (__DEV__ || !Updates.isEnabled) {
|
||||||
|
logger.info('Skipping update check: dev mode or updates not enabled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const update = await Updates.checkForUpdateAsync();
|
const update = await Updates.checkForUpdateAsync();
|
||||||
logger.info("Update check:", update);
|
logger.info("Update check:", update);
|
||||||
|
|
||||||
@@ -499,7 +507,7 @@ function Bootstrapper({ children }: { children: React.ReactNode }) {
|
|||||||
|
|
||||||
if (result.isNew) {
|
if (result.isNew) {
|
||||||
logger.info("Reloading app to apply update...");
|
logger.info("Reloading app to apply update...");
|
||||||
Updates.reloadAsync();
|
await Updates.reloadAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -4,12 +4,15 @@ import { useI18n } from '@/hooks/useI18n';
|
|||||||
import { formatTime, getSleepStageColor, SleepStage, type SleepSample } from '@/utils/sleepHealthKit';
|
import { formatTime, getSleepStageColor, SleepStage, type SleepSample } from '@/utils/sleepHealthKit';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import { LinearGradient } from 'expo-linear-gradient';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
import { Dimensions, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import Svg, { Rect, Text as SvgText } from 'react-native-svg';
|
import Svg, { Defs, LinearGradient as SvgLinearGradient, Rect, Stop, Text as SvgText } from 'react-native-svg';
|
||||||
|
|
||||||
import { StyleProp, ViewStyle } from 'react-native';
|
import { StyleProp, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
||||||
|
|
||||||
export type SleepStageTimelineProps = {
|
export type SleepStageTimelineProps = {
|
||||||
sleepSamples: SleepSample[];
|
sleepSamples: SleepSample[];
|
||||||
bedtime: string;
|
bedtime: string;
|
||||||
@@ -31,14 +34,14 @@ export const SleepStageTimeline = ({
|
|||||||
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
|
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
|
||||||
const colorTokens = Colors[theme];
|
const colorTokens = Colors[theme];
|
||||||
|
|
||||||
// 图表尺寸参数
|
// 图表尺寸参数 - 更宽更现代的设计
|
||||||
const containerWidth = 320;
|
const containerWidth = SCREEN_WIDTH - 64; // 留出左右边距
|
||||||
const chartPadding = 25; // 左右边距,为时间标签预留空间
|
const chartPadding = 24; // 增加左右内边距,避免时间轴和标签被截断
|
||||||
const chartWidth = containerWidth - chartPadding * 2;
|
const chartWidth = containerWidth - chartPadding * 2;
|
||||||
const chartHeight = 80;
|
const chartHeight = 140; // 增加高度以容纳更高的条形图
|
||||||
const timelineHeight = 32;
|
const timelineHeight = 48; // 更高的条形图
|
||||||
const timelineY = 24;
|
const timelineY = 16;
|
||||||
const timeScaleY = timelineY + timelineHeight + 16;
|
const timeScaleY = timelineY + timelineHeight + 24;
|
||||||
|
|
||||||
// 计算时间范围和刻度
|
// 计算时间范围和刻度
|
||||||
const { timelineData, timeLabels } = useMemo(() => {
|
const { timelineData, timeLabels } = useMemo(() => {
|
||||||
@@ -64,7 +67,7 @@ export const SleepStageTimeline = ({
|
|||||||
const duration = sampleEnd.diff(sampleStart, 'minute');
|
const duration = sampleEnd.diff(sampleStart, 'minute');
|
||||||
|
|
||||||
const x = Math.max(0, (startOffset / totalMinutes) * chartWidth) + chartPadding;
|
const x = Math.max(0, (startOffset / totalMinutes) * chartWidth) + chartPadding;
|
||||||
const width = Math.max(2, (duration / totalMinutes) * chartWidth);
|
const width = Math.max(3, (duration / totalMinutes) * chartWidth);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x,
|
x,
|
||||||
@@ -74,29 +77,27 @@ export const SleepStageTimeline = ({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 智能生成时间标签,避免重合
|
// 智能生成时间标签
|
||||||
const labels = [];
|
const labels = [];
|
||||||
const minLabelSpacing = 50; // 最小标签间距(像素)
|
const minLabelSpacing = 60;
|
||||||
|
|
||||||
// 总是显示起始时间
|
// 起始时间标签
|
||||||
labels.push({
|
labels.push({
|
||||||
time: startTime.format('HH:mm'),
|
time: startTime.format('HH:mm'),
|
||||||
x: chartPadding
|
x: chartPadding
|
||||||
});
|
});
|
||||||
|
|
||||||
// 根据睡眠总时长动态调整时间间隔
|
|
||||||
const sleepDurationHours = totalMinutes / 60;
|
const sleepDurationHours = totalMinutes / 60;
|
||||||
let timeStepMinutes;
|
let timeStepMinutes;
|
||||||
|
|
||||||
if (sleepDurationHours <= 4) {
|
if (sleepDurationHours <= 4) {
|
||||||
timeStepMinutes = 60; // 1小时间隔
|
timeStepMinutes = 60;
|
||||||
} else if (sleepDurationHours <= 8) {
|
} else if (sleepDurationHours <= 8) {
|
||||||
timeStepMinutes = 120; // 2小时间隔
|
timeStepMinutes = 120;
|
||||||
} else {
|
} else {
|
||||||
timeStepMinutes = 180; // 3小时间隔
|
timeStepMinutes = 180;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加中间时间标签,确保不重合
|
|
||||||
let currentTime = startTime;
|
let currentTime = startTime;
|
||||||
let stepCount = 1;
|
let stepCount = 1;
|
||||||
|
|
||||||
@@ -104,7 +105,6 @@ export const SleepStageTimeline = ({
|
|||||||
const stepTime = currentTime.add(timeStepMinutes * stepCount, 'minute');
|
const stepTime = currentTime.add(timeStepMinutes * stepCount, 'minute');
|
||||||
const x = (stepTime.diff(startTime, 'minute') / totalMinutes) * chartWidth + chartPadding;
|
const x = (stepTime.diff(startTime, 'minute') / totalMinutes) * chartWidth + chartPadding;
|
||||||
|
|
||||||
// 检查与前一个标签的间距
|
|
||||||
const lastLabel = labels[labels.length - 1];
|
const lastLabel = labels[labels.length - 1];
|
||||||
if (x - lastLabel.x >= minLabelSpacing && x <= containerWidth - chartPadding) {
|
if (x - lastLabel.x >= minLabelSpacing && x <= containerWidth - chartPadding) {
|
||||||
labels.push({
|
labels.push({
|
||||||
@@ -116,7 +116,7 @@ export const SleepStageTimeline = ({
|
|||||||
stepCount++;
|
stepCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 总是显示结束时间,但要确保与前一个标签有足够间距
|
// 结束时间标签
|
||||||
const endX = containerWidth - chartPadding;
|
const endX = containerWidth - chartPadding;
|
||||||
const lastLabel = labels[labels.length - 1];
|
const lastLabel = labels[labels.length - 1];
|
||||||
if (endX - lastLabel.x >= minLabelSpacing) {
|
if (endX - lastLabel.x >= minLabelSpacing) {
|
||||||
@@ -125,7 +125,6 @@ export const SleepStageTimeline = ({
|
|||||||
x: endX
|
x: endX
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 如果空间不够,替换最后一个标签为结束时间
|
|
||||||
labels[labels.length - 1] = {
|
labels[labels.length - 1] = {
|
||||||
time: endTime.format('HH:mm'),
|
time: endTime.format('HH:mm'),
|
||||||
x: endX
|
x: endX
|
||||||
@@ -161,7 +160,7 @@ export const SleepStageTimeline = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, { backgroundColor: colorTokens.background }, style]}>
|
<View style={[styles.container, { backgroundColor: 'transparent' }, style]}>
|
||||||
{/* 标题栏 */}
|
{/* 标题栏 */}
|
||||||
{!hideHeader && (
|
{!hideHeader && (
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
@@ -176,60 +175,104 @@ export const SleepStageTimeline = ({
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 睡眠时间范围 */}
|
{/* 睡眠时间范围 - 更简洁的设计 */}
|
||||||
<View style={styles.timeRange}>
|
<View style={styles.timeRange}>
|
||||||
<View style={styles.timePoint}>
|
<View style={styles.timePoint}>
|
||||||
<Text style={[styles.timeLabel, { color: colorTokens.textSecondary }]}>
|
<Ionicons name="moon" size={16} color="#8B9DC3" style={{ marginBottom: 4 }} />
|
||||||
{t('sleepDetail.infoModalTitles.sleepTime')}
|
<Text style={[styles.timeValue, { color: '#1c1f3a' }]}>
|
||||||
</Text>
|
|
||||||
<Text style={[styles.timeValue, { color: colorTokens.text }]}>
|
|
||||||
{formatTime(bedtime)}
|
{formatTime(bedtime)}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text style={[styles.timeLabel, { color: '#8B9DC3' }]}>
|
||||||
|
{t('sleepDetail.infoModalTitles.sleepTime')}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.timePoint}>
|
<View style={styles.timePoint}>
|
||||||
<Text style={[styles.timeLabel, { color: colorTokens.textSecondary }]}>
|
<Ionicons name="sunny" size={16} color="#F59E0B" style={{ marginBottom: 4 }} />
|
||||||
{t('sleepDetail.sleepDuration')}
|
<Text style={[styles.timeValue, { color: '#1c1f3a' }]}>
|
||||||
</Text>
|
|
||||||
<Text style={[styles.timeValue, { color: colorTokens.text }]}>
|
|
||||||
{formatTime(wakeupTime)}
|
{formatTime(wakeupTime)}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text style={[styles.timeLabel, { color: '#8B9DC3' }]}>
|
||||||
|
{t('sleepDetail.sleepDuration')}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* SVG 图表 */}
|
{/* SVG 图表 - iOS 健康风格 */}
|
||||||
<View style={styles.chartContainer}>
|
<View style={styles.chartContainer}>
|
||||||
<Svg width={containerWidth} height={chartHeight}>
|
{/* 背景轨道 */}
|
||||||
{/* 绘制睡眠阶段条形图 */}
|
<View style={[styles.trackBackground, {
|
||||||
{timelineData.map((segment, index) => (
|
left: chartPadding,
|
||||||
<Rect
|
right: chartPadding,
|
||||||
key={index}
|
width: chartWidth
|
||||||
x={segment.x}
|
}]} />
|
||||||
y={timelineY}
|
|
||||||
width={segment.width}
|
|
||||||
height={timelineHeight}
|
|
||||||
fill={segment.color}
|
|
||||||
rx={2}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{/* 绘制时间刻度标签 */}
|
<Svg width={containerWidth} height={chartHeight}>
|
||||||
|
<Defs>
|
||||||
|
{/* 为每种睡眠阶段定义渐变 */}
|
||||||
|
<SvgLinearGradient id="gradDeep" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<Stop offset="0" stopColor="#60A5FA" stopOpacity="1" />
|
||||||
|
<Stop offset="1" stopColor="#3B82F6" stopOpacity="0.85" />
|
||||||
|
</SvgLinearGradient>
|
||||||
|
<SvgLinearGradient id="gradCore" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<Stop offset="0" stopColor="#A78BFA" stopOpacity="1" />
|
||||||
|
<Stop offset="1" stopColor="#8B5CF6" stopOpacity="0.85" />
|
||||||
|
</SvgLinearGradient>
|
||||||
|
<SvgLinearGradient id="gradREM" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<Stop offset="0" stopColor="#F472B6" stopOpacity="1" />
|
||||||
|
<Stop offset="1" stopColor="#EC4899" stopOpacity="0.85" />
|
||||||
|
</SvgLinearGradient>
|
||||||
|
<SvgLinearGradient id="gradAwake" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<Stop offset="0" stopColor="#FCD34D" stopOpacity="1" />
|
||||||
|
<Stop offset="1" stopColor="#F59E0B" stopOpacity="0.85" />
|
||||||
|
</SvgLinearGradient>
|
||||||
|
<SvgLinearGradient id="gradAsleep" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<Stop offset="0" stopColor="#F472B6" stopOpacity="1" />
|
||||||
|
<Stop offset="1" stopColor="#EC4899" stopOpacity="0.85" />
|
||||||
|
</SvgLinearGradient>
|
||||||
|
</Defs>
|
||||||
|
|
||||||
|
{/* 绘制睡眠阶段条形图 - 使用渐变和圆角 */}
|
||||||
|
{timelineData.map((segment, index) => {
|
||||||
|
const gradientId =
|
||||||
|
segment.stage === SleepStage.Deep ? 'gradDeep' :
|
||||||
|
segment.stage === SleepStage.Core ? 'gradCore' :
|
||||||
|
segment.stage === SleepStage.REM || segment.stage === SleepStage.Asleep ? 'gradREM' :
|
||||||
|
segment.stage === SleepStage.Awake ? 'gradAwake' : 'gradAsleep';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Rect
|
||||||
|
key={index}
|
||||||
|
x={segment.x}
|
||||||
|
y={timelineY}
|
||||||
|
width={segment.width}
|
||||||
|
height={timelineHeight}
|
||||||
|
fill={`url(#${gradientId})`}
|
||||||
|
rx={8}
|
||||||
|
opacity={0.95}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* 绘制时间刻度标签 - 更细腻的设计 */}
|
||||||
{timeLabels.map((label, index) => (
|
{timeLabels.map((label, index) => (
|
||||||
<React.Fragment key={index}>
|
<React.Fragment key={index}>
|
||||||
{/* 刻度线 */}
|
{/* 刻度线 */}
|
||||||
<Rect
|
<Rect
|
||||||
x={label.x - 0.5}
|
x={label.x - 0.5}
|
||||||
y={timelineY + timelineHeight}
|
y={timelineY + timelineHeight + 4}
|
||||||
width={1}
|
width={1}
|
||||||
height={6}
|
height={4}
|
||||||
fill={colorTokens.border}
|
fill="#D1D5DB"
|
||||||
|
opacity={0.4}
|
||||||
/>
|
/>
|
||||||
{/* 时间标签 */}
|
{/* 时间标签 */}
|
||||||
<SvgText
|
<SvgText
|
||||||
x={label.x}
|
x={label.x}
|
||||||
y={timeScaleY}
|
y={timeScaleY}
|
||||||
fontSize={11}
|
fontSize={11}
|
||||||
fill={colorTokens.textSecondary}
|
fill="#8B9DC3"
|
||||||
textAnchor="middle"
|
textAnchor="middle"
|
||||||
|
fontWeight="500"
|
||||||
>
|
>
|
||||||
{label.time}
|
{label.time}
|
||||||
</SvgText>
|
</SvgText>
|
||||||
@@ -238,35 +281,43 @@ export const SleepStageTimeline = ({
|
|||||||
</Svg>
|
</Svg>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 图例 */}
|
{/* 图例 - iOS 风格的标签 */}
|
||||||
<View style={styles.legend}>
|
<View style={styles.legend}>
|
||||||
<View style={styles.legendRow}>
|
<View style={styles.legendItem}>
|
||||||
<View style={styles.legendItem}>
|
<LinearGradient
|
||||||
<View style={[styles.legendDot, { backgroundColor: getSleepStageColor(SleepStage.Deep) }]} />
|
colors={['#60A5FA', '#3B82F6']}
|
||||||
<Text style={[styles.legendText, { color: colorTokens.textSecondary }]}>
|
style={styles.legendPill}
|
||||||
{t('sleepDetail.deep')}
|
start={{ x: 0, y: 0 }}
|
||||||
</Text>
|
end={{ x: 0, y: 1 }}
|
||||||
</View>
|
/>
|
||||||
<View style={styles.legendItem}>
|
<Text style={styles.legendText}>{t('sleepDetail.deep')}</Text>
|
||||||
<View style={[styles.legendDot, { backgroundColor: getSleepStageColor(SleepStage.Core) }]} />
|
|
||||||
<Text style={[styles.legendText, { color: colorTokens.textSecondary }]}>
|
|
||||||
{t('sleepDetail.core')}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.legendRow}>
|
<View style={styles.legendItem}>
|
||||||
<View style={styles.legendItem}>
|
<LinearGradient
|
||||||
<View style={[styles.legendDot, { backgroundColor: getSleepStageColor(SleepStage.REM) }]} />
|
colors={['#A78BFA', '#8B5CF6']}
|
||||||
<Text style={[styles.legendText, { color: colorTokens.textSecondary }]}>
|
style={styles.legendPill}
|
||||||
{t('sleepDetail.rem')}
|
start={{ x: 0, y: 0 }}
|
||||||
</Text>
|
end={{ x: 0, y: 1 }}
|
||||||
</View>
|
/>
|
||||||
<View style={styles.legendItem}>
|
<Text style={styles.legendText}>{t('sleepDetail.core')}</Text>
|
||||||
<View style={[styles.legendDot, { backgroundColor: getSleepStageColor(SleepStage.Awake) }]} />
|
</View>
|
||||||
<Text style={[styles.legendText, { color: colorTokens.textSecondary }]}>
|
<View style={styles.legendItem}>
|
||||||
{t('sleepDetail.awake')}
|
<LinearGradient
|
||||||
</Text>
|
colors={['#F472B6', '#EC4899']}
|
||||||
</View>
|
style={styles.legendPill}
|
||||||
|
start={{ x: 0, y: 0 }}
|
||||||
|
end={{ x: 0, y: 1 }}
|
||||||
|
/>
|
||||||
|
<Text style={styles.legendText}>{t('sleepDetail.rem')}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.legendItem}>
|
||||||
|
<LinearGradient
|
||||||
|
colors={['#FCD34D', '#F59E0B']}
|
||||||
|
style={styles.legendPill}
|
||||||
|
start={{ x: 0, y: 0 }}
|
||||||
|
end={{ x: 0, y: 1 }}
|
||||||
|
/>
|
||||||
|
<Text style={styles.legendText}>{t('sleepDetail.awake')}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -276,14 +327,8 @@ export const SleepStageTimeline = ({
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
padding: 16,
|
paddingVertical: 20,
|
||||||
marginBottom: 24,
|
paddingHorizontal: 16,
|
||||||
shadowColor: '#000',
|
|
||||||
shadowOffset: { width: 0, height: 2 },
|
|
||||||
shadowOpacity: 0.1,
|
|
||||||
shadowRadius: 8,
|
|
||||||
elevation: 3,
|
|
||||||
marginHorizontal: 4,
|
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@@ -294,31 +339,44 @@ const styles = StyleSheet.create({
|
|||||||
title: {
|
title: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
|
fontFamily: 'AliBold',
|
||||||
},
|
},
|
||||||
infoButton: {
|
infoButton: {
|
||||||
padding: 4,
|
padding: 4,
|
||||||
},
|
},
|
||||||
timeRange: {
|
timeRange: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-around',
|
||||||
marginBottom: 20,
|
marginBottom: 28,
|
||||||
|
paddingHorizontal: 20,
|
||||||
},
|
},
|
||||||
timePoint: {
|
timePoint: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
gap: 2,
|
||||||
},
|
},
|
||||||
timeLabel: {
|
timeLabel: {
|
||||||
fontSize: 12,
|
fontSize: 11,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
marginBottom: 4,
|
fontFamily: 'AliRegular',
|
||||||
},
|
},
|
||||||
timeValue: {
|
timeValue: {
|
||||||
fontSize: 16,
|
fontSize: 20,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
letterSpacing: -0.2,
|
fontFamily: 'AliBold',
|
||||||
|
letterSpacing: -0.5,
|
||||||
},
|
},
|
||||||
chartContainer: {
|
chartContainer: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: 16,
|
marginBottom: 20,
|
||||||
|
position: 'relative',
|
||||||
|
},
|
||||||
|
trackBackground: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 16,
|
||||||
|
height: 48,
|
||||||
|
backgroundColor: '#F0F2F9',
|
||||||
|
borderRadius: 24,
|
||||||
|
opacity: 0.5,
|
||||||
},
|
},
|
||||||
emptyState: {
|
emptyState: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -327,27 +385,29 @@ const styles = StyleSheet.create({
|
|||||||
emptyText: {
|
emptyText: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontStyle: 'italic',
|
fontStyle: 'italic',
|
||||||
|
fontFamily: 'AliRegular',
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
legendRow: {
|
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
gap: 24,
|
flexWrap: 'wrap',
|
||||||
|
gap: 16,
|
||||||
|
paddingTop: 8,
|
||||||
},
|
},
|
||||||
legendItem: {
|
legendItem: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 6,
|
gap: 6,
|
||||||
},
|
},
|
||||||
legendDot: {
|
legendPill: {
|
||||||
width: 8,
|
width: 20,
|
||||||
height: 8,
|
height: 10,
|
||||||
borderRadius: 4,
|
borderRadius: 5,
|
||||||
},
|
},
|
||||||
legendText: {
|
legendText: {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
|
color: '#6B7280',
|
||||||
|
fontFamily: 'AliRegular',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -410,6 +410,7 @@
|
|||||||
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoMediaLibrary/ExpoMediaLibrary_privacy.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoMediaLibrary/ExpoMediaLibrary_privacy.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/PurchasesHybridCommon/PurchasesHybridCommon.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/PurchasesHybridCommon/PurchasesHybridCommon.bundle",
|
||||||
|
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo/RNDeviceInfoPrivacyInfo.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo/RNDeviceInfoPrivacyInfo.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
|
||||||
@@ -419,6 +420,8 @@
|
|||||||
"${PODS_CONFIGURATION_BUILD_DIR}/RevenueCat/RevenueCat.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/RevenueCat/RevenueCat.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/Sentry/Sentry.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/Sentry/Sentry.bundle",
|
||||||
|
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
|
||||||
|
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/LottiePrivacyInfo.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/LottiePrivacyInfo.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/lottie-react-native/Lottie_React_Native_Privacy.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/lottie-react-native/Lottie_React_Native_Privacy.bundle",
|
||||||
);
|
);
|
||||||
@@ -435,6 +438,7 @@
|
|||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoMediaLibrary_privacy.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoMediaLibrary_privacy.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/PurchasesHybridCommon.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/PurchasesHybridCommon.bundle",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
|
||||||
@@ -444,6 +448,8 @@
|
|||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RevenueCat.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RevenueCat.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Sentry.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Sentry.bundle",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LottiePrivacyInfo.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LottiePrivacyInfo.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Lottie_React_Native_Privacy.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Lottie_React_Native_Privacy.bundle",
|
||||||
);
|
);
|
||||||
@@ -459,14 +465,10 @@
|
|||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-OutLive/Pods-OutLive-frameworks.sh",
|
"${PODS_ROOT}/Target Support Files/Pods-OutLive/Pods-OutLive-frameworks.sh",
|
||||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/React-Core-prebuilt/React.framework/React",
|
|
||||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/ReactNativeDependencies/ReactNativeDependencies.framework/ReactNativeDependencies",
|
|
||||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
|
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
|
||||||
);
|
);
|
||||||
name = "[CP] Embed Pods Frameworks";
|
name = "[CP] Embed Pods Frameworks";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/React.framework",
|
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReactNativeDependencies.framework",
|
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Release"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
|
|||||||
@@ -10,5 +10,7 @@
|
|||||||
<string>ALWAYS</string>
|
<string>ALWAYS</string>
|
||||||
<key>EXUpdatesLaunchWaitMs</key>
|
<key>EXUpdatesLaunchWaitMs</key>
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
|
<key>EXUpdatesRuntimeVersion</key>
|
||||||
|
<string>1.1.4</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
1471
ios/Podfile.lock
1471
ios/Podfile.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user