1 Commits

Author SHA1 Message Date
1
996999453a feat(client): i18n copy + reco lang refresh + home polish 2026-02-02 20:11:09 +08:00
33 changed files with 1018 additions and 560 deletions

View File

@@ -1,5 +0,0 @@
docker exec -it gitea-runner bash
# 然后在容器里安装 Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs
node -v

View File

@@ -1,101 +0,0 @@
name: Build and Push Server Docker Image
# 手动触发 workflow从哪个分支运行就打包哪个分支的代码
on:
workflow_dispatch:
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
# 1⃣ Checkout 仓库代码
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# 需要能 push tag请在仓库 Secrets 配置 RUNNER_TOKEN
token: ${{ secrets.RUNNER_TOKEN }}
persist-credentials: true
# 2⃣ 自动递增 tag 并推送回 Gitea 仓库(默认按 vX.Y.Z 的 patch +1
- name: Auto bump tag and push to repository
shell: bash
run: |
set -euo pipefail
# 配置提交信息(用于创建注释 tag
git config user.name "gitea-actions"
git config user.email "actions@local"
# 确保本地有最新 tags
git fetch --tags --force
# 取最新的 semver tagvX.Y.Z按版本号排序
LATEST_TAG="$(git tag --list 'v*' --sort=-v:refname | head -n 1 || true)"
echo "LATEST_TAG=${LATEST_TAG}"
if [[ -z "${LATEST_TAG}" ]]; then
NEXT_TAG="v1.0.0"
else
if [[ "${LATEST_TAG}" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
MAJOR="${BASH_REMATCH[1]}"
MINOR="${BASH_REMATCH[2]}"
PATCH="${BASH_REMATCH[3]}"
NEXT_TAG="v${MAJOR}.${MINOR}.$((PATCH + 1))"
else
# 如果最新 tag 不符合 vX.Y.Z回退到 v1.0.0,避免误解析
NEXT_TAG="v1.0.0"
fi
fi
echo "NEXT_TAG=${NEXT_TAG}"
# 如果 tag 已存在则直接复用(避免重复运行失败)
if git rev-parse -q --verify "refs/tags/${NEXT_TAG}" >/dev/null; then
echo "Tag ${NEXT_TAG} 已存在,跳过创建。"
else
git tag -a "${NEXT_TAG}" -m "Release ${NEXT_TAG}"
git push origin "${NEXT_TAG}"
fi
# 输出给后续步骤使用
if [[ -n "${GITHUB_ENV:-}" ]]; then
echo "IMAGE_TAG=${NEXT_TAG}" >> "$GITHUB_ENV"
fi
# 兼容部分 Gitea Runner 环境变量命名
if [[ -n "${GITEA_ENV:-}" ]]; then
echo "IMAGE_TAG=${NEXT_TAG}" >> "$GITEA_ENV"
fi
# 3⃣ 设置 Docker 镜像名称
- name: Set image variables
shell: bash
run: |
# 修改为你的 Docker Hub 仓库名例如yourname/mindfulness-server
IMAGE_NAME=docker.damer.fun/damer/mindfulness-server
if [[ -n "${GITHUB_ENV:-}" ]]; then
echo "IMAGE_NAME=$IMAGE_NAME" >> "$GITHUB_ENV"
fi
if [[ -n "${GITEA_ENV:-}" ]]; then
echo "IMAGE_NAME=$IMAGE_NAME" >> "$GITEA_ENV"
fi
# 4⃣ 登录 Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# 5⃣ 构建 Docker 镜像(使用 server/ 作为构建上下文)
- name: Build Docker Image
shell: bash
run: |
docker build -f server/Dockerfile -t "$IMAGE_NAME:$IMAGE_TAG" server
# 6⃣ 推送 Docker 镜像到 Docker Hub
- name: Push Docker Image
shell: bash
run: |
docker push "$IMAGE_NAME:$IMAGE_TAG"

View File

@@ -15,7 +15,7 @@
}, },
"ios": { "ios": {
"supportsTablet": true, "supportsTablet": true,
"bundleIdentifier": "com.damer.mindfulness" "bundleIdentifier": "com.anonymous.client"
}, },
"android": { "android": {
"adaptiveIcon": { "adaptiveIcon": {

View File

@@ -14,19 +14,18 @@ import Animated, {
import { MOCK_CONTENT } from '@/src/constants/mockContent'; import { MOCK_CONTENT } from '@/src/constants/mockContent';
import { import {
addFavorite, addFavorite,
getRecoFeedCache,
getRecoFeedHistory,
getThemeMode, getThemeMode,
getUserProfile, getUserProfile,
getUserProfileScoring,
recordRecoFeedServed,
recordRecoFeedTouched,
setRecoFeedCache,
setReaction, setReaction,
setThemeMode, setThemeMode,
type RecoFeedCacheItem, getRecoFeedCache,
setRecoFeedCache,
getUserProfileScoring,
getRecoFeedHistory,
recordRecoFeedServed,
type ThemeMode, type ThemeMode,
} from '@/src/storage/appStorage'; } from '@/src/storage/appStorage';
import { fetchRecoFeed } from '@/src/services/recoApi'; import { fetchRecoFeed } from '@/src/services/recoApi';
import ProfileModal from '@/components/home/ProfileModal'; import ProfileModal from '@/components/home/ProfileModal';
@@ -73,8 +72,12 @@ const THEME_COLORS = [
'#CBD9F2', '#CBD9F2',
]; ];
type FeedItem = { content_id: string; text: string };
export default function HomeScreen() { export default function HomeScreen() {
const { t } = useTranslation(); const { t, i18n } = useTranslation();
const isEnglish = i18n.language?.startsWith('en');
const recoLang: 'en' | 'tc' = i18n.language?.toLowerCase().startsWith('zh') ? 'tc' : 'en';
const navigation = useNavigation(); const navigation = useNavigation();
const [index, setIndex] = useState(0); const [index, setIndex] = useState(0);
const [themeMode, setThemeModeState] = useState<ThemeMode>('scenery'); const [themeMode, setThemeModeState] = useState<ThemeMode>('scenery');
@@ -83,83 +86,115 @@ export default function HomeScreen() {
const [profileName, setProfileName] = useState<string | undefined>(undefined); const [profileName, setProfileName] = useState<string | undefined>(undefined);
const [busy, setBusy] = useState(false); const [busy, setBusy] = useState(false);
const [likeFilled, setLikeFilled] = useState(false); const [likeFilled, setLikeFilled] = useState(false);
const [feedItems, setFeedItems] = useState<Array<{ content_id: number; text: string }>>([]); const [feedItems, setFeedItems] = useState<FeedItem[]>([]);
const [isFetching, setIsFetching] = useState(false);
const currentList = feedItems.length > 0 ? feedItems : MOCK_CONTENT; // 解决语言切换时重复触发拉取/清空导致“文案不停跳动”的问题:
const item = useMemo(() => currentList[index % currentList.length], [currentList, index]); // 用 ref 持有最新状态,避免 useCallback 依赖 feedItems/isFetching 造成函数 identity 变化 → effect 重复执行
const currentContentId = typeof (item as any)?.content_id === 'number' ? Number((item as any).content_id) : null; const feedItemsRef = useRef<FeedItem[]>([]);
const isFetchingRef = useRef(false);
useEffect(() => {
feedItemsRef.current = feedItems;
}, [feedItems]);
useEffect(() => {
isFetchingRef.current = isFetching;
}, [isFetching]);
// 动画相关 Shared Values // 动画相关 Shared Values
const translateY = useSharedValue(0); const translateY = useSharedValue(0);
const opacity = useSharedValue(1); const opacity = useSharedValue(1);
const likeScale = useSharedValue(1); const likeScale = useSharedValue(1);
// 每次进入页面或页面获得焦点时刷新个人信息 // 统一文案对象结构
const currentFeed = useMemo(() => {
if (feedItems.length > 0) {
return feedItems;
}
return MOCK_CONTENT.map(item => ({
content_id: item.id,
text: t(item.textKey)
}));
}, [feedItems, t]);
const item = useMemo(() => {
const data = currentFeed[index % currentFeed.length];
return {
id: String(data.content_id),
text: data.text
};
}, [currentFeed, index]);
// 异步拉取新文案
const fetchNewFeed = useCallback(async () => {
if (isFetchingRef.current) return;
isFetchingRef.current = true;
setIsFetching(true);
try {
const scoringProfile = await getUserProfileScoring();
if (!scoringProfile) return;
const history = await getRecoFeedHistory();
const { items, meta } = await fetchRecoFeed({
k: 30,
user_profile: scoringProfile,
already_recommended_ids: history.already_recommended_ids,
touched_or_viewed_ids: history.touched_or_viewed_ids,
});
if (items.length > 0) {
const wasEmpty = feedItemsRef.current.length === 0;
const newCache = {
saved_at: new Date().toISOString(),
lang: recoLang,
items: items.map((x) => ({ content_id: x.content_id, text: x.text })),
meta: meta as Record<string, unknown>,
};
await setRecoFeedCache(newCache);
await recordRecoFeedServed(items.map((x) => x.content_id));
setFeedItems(newCache.items.map((x) => ({ content_id: String(x.content_id), text: x.text })));
// 如果当前是 mock 数据,切换到新数据的第一条
if (wasEmpty) {
setIndex(0);
}
}
} catch (error) {
console.error('Failed to fetch new feed:', error);
} finally {
isFetchingRef.current = false;
setIsFetching(false);
}
}, [recoLang]);
// 每次进入页面或页面获得焦点时刷新个人信息和缓存文案
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
let cancelled = false; let cancelled = false;
(async () => { (async () => {
const mode = await getThemeMode(); const mode = await getThemeMode();
const profile = await getUserProfile(); const profile = await getUserProfile();
const cache = await getRecoFeedCache();
if (cancelled) return; if (cancelled) return;
setThemeModeState(mode); setThemeModeState(mode);
setProfileName(profile.name); setProfileName(profile.name);
// 语言切换时:旧语言缓存不复用,触发重新拉取
if (cache && cache.items.length > 0 && (cache.lang ?? 'en') === recoLang) {
setFeedItems(cache.items.map((x) => ({ content_id: String(x.content_id), text: x.text })));
} else {
// 语言不匹配或没有缓存:先清空回落到本地 mock会立即随语言切换再拉取对应语言的推荐文案
setFeedItems([]);
setIndex(0);
fetchNewFeed();
}
})(); })();
return () => { return () => {
cancelled = true; cancelled = true;
}; };
}, []) }, [fetchNewFeed, recoLang])
); );
// 首次进入:先读缓存,再拉后端 feed失败则保持 mock/缓存)
useEffect(() => {
let cancelled = false;
(async () => {
const cache = await getRecoFeedCache();
if (!cancelled && cache?.items?.length) {
setFeedItems(cache.items.map((x: RecoFeedCacheItem) => ({ content_id: x.content_id, text: x.text })));
}
const scoring = await getUserProfileScoring();
if (!scoring) return;
try {
const hist = await getRecoFeedHistory();
const out = await fetchRecoFeed({
k: 30,
user_profile: {
profile_version: scoring.profile_version,
profile_source: scoring.profile_source,
profile_generated_at: scoring.profile_generated_at,
profile_confidence: scoring.profile_confidence,
profile_answered: scoring.profile_answered,
stage: scoring.stage,
emotion_score: scoring.emotion_score,
context: scoring.context,
need: scoring.need,
},
already_recommended_ids: hist.already_recommended_ids,
touched_or_viewed_ids: hist.touched_or_viewed_ids,
});
if (!cancelled && out.items?.length) {
setFeedItems(out.items.map((x) => ({ content_id: x.content_id, text: x.text })));
await setRecoFeedCache({
saved_at: new Date().toISOString(),
items: out.items.map((x) => ({ content_id: x.content_id, text: x.text })),
meta: out.meta as Record<string, unknown>,
});
await recordRecoFeedServed(out.items.map((x) => x.content_id));
}
} catch {
// 忽略:保持缓存/本地 mock
}
})();
return () => {
cancelled = true;
};
}, []);
const backgroundColor = useMemo(() => { const backgroundColor = useMemo(() => {
if (themeMode === 'color') { if (themeMode === 'color') {
const colorIndex = Math.floor(index / 10) % THEME_COLORS.length; const colorIndex = Math.floor(index / 10) % THEME_COLORS.length;
@@ -178,8 +213,10 @@ export default function HomeScreen() {
useLayoutEffect(() => { useLayoutEffect(() => {
navigation.setOptions({ navigation.setOptions({
headerShadowVisible: false, headerShadowVisible: false,
headerStyle: { backgroundColor: themeMode === 'scenery' ? 'transparent' : backgroundColor }, // 为了让风景/颜色两种主题下“文案的视觉居中位置”一致,统一使用透明 Header
headerTransparent: themeMode === 'scenery', // 颜色主题下 Header 透明也不会影响观感(背景就是纯色)
headerStyle: { backgroundColor: 'transparent' },
headerTransparent: true,
headerRight: () => ( headerRight: () => (
<View style={styles.headerRight}> <View style={styles.headerRight}>
<CircleIconButton <CircleIconButton
@@ -213,11 +250,6 @@ export default function HomeScreen() {
if (busy) return; if (busy) return;
setBusy(true); setBusy(true);
// 记录“看过/划过”的内容 id用于下一次向后端请求时去重/频控)
if (typeof currentContentId === 'number') {
void recordRecoFeedTouched(currentContentId);
}
// 1. 当前文案向上移动并消失 // 1. 当前文案向上移动并消失
translateY.value = withTiming(-40, { duration: 300, easing: Easing.out(Easing.quad) }); translateY.value = withTiming(-40, { duration: 300, easing: Easing.out(Easing.quad) });
opacity.value = withTiming(0, { duration: 300 }, (finished) => { opacity.value = withTiming(0, { duration: 300 }, (finished) => {
@@ -226,6 +258,11 @@ export default function HomeScreen() {
runOnJS(setIndex)(index + 1); runOnJS(setIndex)(index + 1);
runOnJS(setLikeFilled)(false); runOnJS(setLikeFilled)(false);
// 检查是否需要拉取新文案(当接近当前列表末尾时,例如还剩 5 条)
if (index + 5 >= currentFeed.length && !isFetching) {
runOnJS(fetchNewFeed)();
}
// 3. 准备下一条文案:先瞬移到下方 40pt // 3. 准备下一条文案:先瞬移到下方 40pt
translateY.value = 40; translateY.value = 40;
@@ -238,7 +275,7 @@ export default function HomeScreen() {
}); });
} }
}); });
}, [busy, currentContentId, index, translateY, opacity]); }, [busy, index, currentFeed.length, isFetching, fetchNewFeed, translateY, opacity]);
const lastTapRef = useRef<number>(0); const lastTapRef = useRef<number>(0);
@@ -288,20 +325,28 @@ export default function HomeScreen() {
const dateStr = `${now.getFullYear()}.${String(now.getMonth() + 1).padStart(2, '0')}.${String(now.getDate()).padStart(2, '0')}`; const dateStr = `${now.getFullYear()}.${String(now.getMonth() + 1).padStart(2, '0')}.${String(now.getDate()).padStart(2, '0')}`;
// 2. 保存到收藏夹,包含当前背景信息 // 2. 保存到收藏夹,包含当前背景信息
await addFavorite({ const favItem = {
favId: String(Date.now()), // 生成唯一 ID favId: String(Date.now()), // 生成唯一 ID
id: item.id, id: item.id,
text: item.text,
date: dateStr, date: dateStr,
themeMode: themeMode, themeMode: themeMode,
background: themeMode === 'scenery' ? String(natureImageIndex) : backgroundColor, background: themeMode === 'scenery' ? String(natureImageIndex) : backgroundColor,
}); };
console.log('Home: Triggering addFavorite', JSON.stringify(favItem));
await addFavorite(favItem);
// 3. 爱心缩放动画 // 3. 记录到后端 Reaction喜欢
console.log('Home: Triggering setReaction', item.id);
await setReaction(item.id, 'like');
// 4. 爱心缩放动画
likeScale.value = withSequence( likeScale.value = withSequence(
withTiming(0.8, { duration: 100 }), withTiming(0.8, { duration: 100 }),
withTiming(1.2, { duration: 150 }), withTiming(1.2, { duration: 150 }),
withTiming(1, { duration: 100 }, (finished) => { withTiming(1, { duration: 100 }, (finished) => {
if (finished) { if (finished) {
console.log('Home: Like animation finished, triggering next content');
runOnJS(triggerNextContent)(); runOnJS(triggerNextContent)();
} }
}) })
@@ -324,7 +369,9 @@ export default function HomeScreen() {
/> />
)} )}
<Animated.View style={[styles.card, textAnimatedStyle, themeMode === 'scenery' && styles.sceneryCard]}> <Animated.View style={[styles.card, textAnimatedStyle, themeMode === 'scenery' && styles.sceneryCard]}>
<Text style={[styles.text, themeMode === 'scenery' && styles.sceneryText]}>{item.text}</Text> <Text style={[styles.text, isEnglish && styles.textEnglish, themeMode === 'scenery' && styles.sceneryText]}>
{item.text}
</Text>
</Animated.View> </Animated.View>
<View style={styles.actions}> <View style={styles.actions}>
@@ -419,6 +466,11 @@ const styles = StyleSheet.create({
fontWeight: '700', fontWeight: '700',
textAlign: 'center', textAlign: 'center',
}, },
textEnglish: {
fontFamily: 'STIXTwoText',
// 英文字体观感更细一点,避免过粗
fontWeight: '600',
},
sceneryCard: { sceneryCard: {
// 风景模式下稍微收窄文案宽度,增加呼吸感 // 风景模式下稍微收窄文案宽度,增加呼吸感
paddingHorizontal: 50, paddingHorizontal: 50,

View File

@@ -1,6 +1,7 @@
import { useState } from 'react'; import { useMemo, useState } from 'react';
import { useRouter } from 'expo-router'; import { useRouter } from 'expo-router';
import * as Notifications from 'expo-notifications'; import * as Notifications from 'expo-notifications';
import { useTranslation } from 'react-i18next';
import { OnboardingLayout } from '@/components/onboarding/OnboardingLayout'; import { OnboardingLayout } from '@/components/onboarding/OnboardingLayout';
import { NameInputStep } from '@/components/onboarding/NameInputStep'; import { NameInputStep } from '@/components/onboarding/NameInputStep';
import { SelectionStep } from '@/components/onboarding/SelectionStep'; import { SelectionStep } from '@/components/onboarding/SelectionStep';
@@ -16,64 +17,37 @@ import {
setRecoFeedCache, setRecoFeedCache,
} from '@/src/storage/appStorage'; } from '@/src/storage/appStorage';
const STEPS = [ type Step =
{ id: 'name', type: 'name', title: '我可以怎么称呼你?' }, | { id: 'name'; type: 'name' }
{ | { id: 'status' | 'emotion' | 'influence' | 'support'; type: 'selection'; optionIds: string[] }
id: 'status', | { id: 'reminder'; type: 'reminder' };
type: 'selection',
title: '媽媽的狀態?', const STEPS: Step[] = [
options: [ { id: 'name', type: 'name' },
{ id: 'pregnant', label: '懷孕中/準備成為媽媽' }, { id: 'status', type: 'selection', optionIds: ['pregnant', 'has_kids', 'no_fill'] },
{ id: 'has_kids', label: '已經有孩子' }, { id: 'emotion', type: 'selection', optionIds: ['happy', 'calm', 'stressed', 'low'] },
{ id: 'no_fill', label: '不想填寫' }, { id: 'influence', type: 'selection', optionIds: ['family', 'work', 'relationship', 'friends', 'health'] },
] { id: 'support', type: 'selection', optionIds: ['emotional', 'parenting', 'self_worth', 'anxiety', 'balance'] },
}, { id: 'reminder', type: 'reminder' },
{
id: 'emotion',
type: 'selection',
title: '當下情緒狀態?',
options: [
{ id: 'happy', label: '愉悅、滿足' },
{ id: 'calm', label: '平靜、安穩' },
{ id: 'stressed', label: '被壓得有點喘不過氣' },
{ id: 'low', label: '情緒低落' },
]
},
{
id: 'influence',
type: 'selection',
title: '是什麼影響了你最近的感受?',
options: [
{ id: 'family', label: '家庭與孩子' },
{ id: 'work', label: '工作或學習' },
{ id: 'relationship', label: '親密關係' },
{ id: 'friends', label: '朋友與人際' },
{ id: 'health', label: '身心健康' },
]
},
{
id: 'support',
type: 'selection',
title: '最需要什麼支持?',
options: [
{ id: 'emotional', label: '情緒支持' },
{ id: 'parenting', label: '育兒壓力' },
{ id: 'self_worth', label: '自我價值' },
{ id: 'anxiety', label: '焦慮舒緩' },
{ id: 'balance', label: '休息與平衡' },
]
},
{ id: 'reminder', type: 'reminder', title: '你需要每天几次提醒?' },
]; ];
export default function OnboardingScreen() { export default function OnboardingScreen() {
const router = useRouter(); const router = useRouter();
const { t, i18n } = useTranslation();
const [stepIndex, setStepIndex] = useState(0); const [stepIndex, setStepIndex] = useState(0);
const [name, setName] = useState(''); const [name, setName] = useState('');
const [selections, setSelections] = useState<Record<string, string[]>>({}); const [selections, setSelections] = useState<Record<string, string[]>>({});
const [reminderTimes, setReminderTimes] = useState(3); const [reminderTimes, setReminderTimes] = useState(3);
const currentStep = STEPS[stepIndex]; const currentStep = STEPS[stepIndex];
const currentTitle = useMemo(() => t(`onboardingSurvey.steps.${currentStep.id}.title`), [t, currentStep.id]);
const currentOptions = useMemo(() => {
if (currentStep.type !== 'selection') return [];
return currentStep.optionIds.map((optId) => ({
id: optId,
label: t(`onboardingSurvey.steps.${currentStep.id}.options.${optId}`),
}));
}, [t, currentStep]);
async function onFinish() { async function onFinish() {
// 请求推送权限 // 请求推送权限
@@ -89,6 +63,7 @@ export default function OnboardingScreen() {
// Onboarding 结束后预拉取一次 Feed 文案(失败不阻塞进入首页) // Onboarding 结束后预拉取一次 Feed 文案(失败不阻塞进入首页)
try { try {
const lang = i18n.language?.toLowerCase().startsWith('zh') ? 'tc' : 'en';
const { items, meta } = await fetchRecoFeed({ const { items, meta } = await fetchRecoFeed({
k: 30, k: 30,
user_profile: { user_profile: {
@@ -106,6 +81,7 @@ export default function OnboardingScreen() {
await setRecoFeedCache({ await setRecoFeedCache({
saved_at: new Date().toISOString(), saved_at: new Date().toISOString(),
lang,
items: items.map((x) => ({ content_id: x.content_id, text: x.text })), items: items.map((x) => ({ content_id: x.content_id, text: x.text })),
meta: meta as Record<string, unknown>, meta: meta as Record<string, unknown>,
}); });
@@ -150,11 +126,13 @@ export default function OnboardingScreen() {
router.replace('/(app)/home'); router.replace('/(app)/home');
}; };
// 题目为选:再次点击可取消;选择其他选项会替换为唯一选项 // 题目为选:点击切换选中状态
const handleToggleSelection = (id: string) => { const handleToggleSelection = (id: string) => {
setSelections(prev => { setSelections(prev => {
const currentIds = prev[currentStep.id] || []; const currentIds = prev[currentStep.id] || [];
const nextIds = currentIds.includes(id) ? [] : [id]; const nextIds = currentIds.includes(id)
? currentIds.filter(i => i !== id)
: [...currentIds, id];
return { ...prev, [currentStep.id]: nextIds }; return { ...prev, [currentStep.id]: nextIds };
}); });
}; };
@@ -166,7 +144,7 @@ export default function OnboardingScreen() {
return ( return (
<OnboardingLayout <OnboardingLayout
title={currentStep.title} title={currentTitle}
currentStep={stepIndex} currentStep={stepIndex}
totalSteps={STEPS.length - 1} totalSteps={STEPS.length - 1}
onSkip={onSkip} onSkip={onSkip}
@@ -183,11 +161,10 @@ export default function OnboardingScreen() {
{currentStep.type === 'selection' && ( {currentStep.type === 'selection' && (
<SelectionStep <SelectionStep
options={currentStep.options!} options={currentOptions}
selectedIds={selections[currentStep.id] || []} selectedIds={selections[currentStep.id] || []}
onToggle={handleToggleSelection} onToggle={handleToggleSelection}
onNext={onNext} onNext={onNext}
onSkip={handleSkipStep}
/> />
)} )}

View File

@@ -34,7 +34,7 @@ SplashScreen.preventAutoHideAsync();
export default function RootLayout() { export default function RootLayout() {
const [loaded, error] = useFonts({ const [loaded, error] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), STIXTwoText: require('../assets/fonts/STIXTwoText-VariableFont_wght.ttf'),
...FontAwesome.font, ...FontAwesome.font,
}); });
const [i18nReady, setI18nReady] = useState(false); const [i18nReady, setI18nReady] = useState(false);
@@ -48,7 +48,7 @@ export default function RootLayout() {
initI18n() initI18n()
.catch((e) => { .catch((e) => {
// i18n 初始化失败不应阻塞 App 启动,先打印错误再继续 // i18n 初始化失败不应阻塞 App 启动,先打印错误再继续
console.error('i18n 初始化失败', e); console.error('i18n init failed', e);
}) })
.finally(() => setI18nReady(true)); .finally(() => setI18nReady(true));
}, []); }, []);

Binary file not shown.

View File

@@ -1,3 +1,3 @@
<svg width="32" height="27" viewBox="0 0 32 27" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="32" height="27" viewBox="0 0 32 27" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.0879 1C24.9887 1 26.7592 1.93575 28.0762 3.42188C29.3956 4.91097 30.2001 6.88999 30.2002 8.84082C30.2002 13.2337 27.1054 17.3696 23.5225 20.499C21.7556 22.0422 19.9254 23.2909 18.4199 24.1494C17.6667 24.5789 17.0062 24.904 16.4863 25.1182C16.2262 25.2253 16.0121 25.3001 15.8467 25.3467C15.6728 25.3956 15.5993 25.4002 15.5996 25.4004C15.5928 25.3997 15.5178 25.3932 15.3525 25.3467C15.1871 25.3001 14.973 25.2253 14.7129 25.1182C14.193 24.904 13.5333 24.5788 12.7803 24.1494C11.2748 23.2909 9.44467 22.0423 7.67773 20.499C4.0947 17.3696 1 13.2338 1 8.84082C1.00008 6.89007 1.80381 4.91108 3.12207 3.42188C4.43787 1.93557 6.20587 1.00033 8.10059 1C9.51294 1.00117 10.8927 1.41742 12.0635 2.19434C12.7589 2.65582 13.3618 3.2322 13.8486 3.89355C14.2951 4.50017 15.0032 4.73135 15.5996 4.73145C16.1961 4.73144 16.905 4.5003 17.3516 3.89355C17.8376 3.23336 18.4389 2.65737 19.1328 2.19629C20.3012 1.4199 21.6779 1.00339 23.0879 1Z" stroke="#5E2A28" stroke-width="2"/> <path d="M23.0879 1C24.9887 1 26.7592 1.93575 28.0762 3.42188C29.3956 4.91097 30.2001 6.88999 30.2002 8.84082C30.2002 13.2337 27.1054 17.3696 23.5225 20.499C21.7556 22.0422 19.9254 23.2909 18.4199 24.1494C17.6667 24.5789 17.0062 24.904 16.4863 25.1182C16.2262 25.2253 16.0121 25.3001 15.8467 25.3467C15.6728 25.3956 15.5993 25.4002 15.5996 25.4004C15.5928 25.3997 15.5178 25.3932 15.3525 25.3467C15.1871 25.3001 14.973 25.2253 14.7129 25.1182C14.193 24.904 13.5333 24.5788 12.7803 24.1494C11.2748 23.2909 9.44467 22.0423 7.67773 20.499C4.0947 17.3696 1 13.2338 1 8.84082C1.00008 6.89007 1.80381 4.91108 3.12207 3.42188C4.43787 1.93557 6.20587 1.00033 8.10059 1C9.51294 1.00117 10.8927 1.41742 12.0635 2.19434C12.7589 2.65582 13.3618 3.2322 13.8486 3.89355C14.2951 4.50017 15.0032 4.73135 15.5996 4.73145C16.1961 4.73144 16.905 4.5003 17.3516 3.89355C17.8376 3.23336 18.4389 2.65737 19.1328 2.19629C20.3012 1.4199 21.6779 1.00339 23.0879 1Z" stroke="currentColor" stroke-width="2"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,4 +1,4 @@
<svg width="32" height="27" viewBox="0 0 32 27" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="32" height="27" viewBox="0 0 32 27" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.0879 1C24.9887 1 26.7592 1.93575 28.0762 3.42188C29.3956 4.91097 30.2001 6.88999 30.2002 8.84082C30.2002 13.2337 27.1054 17.3696 23.5225 20.499C21.7556 22.0422 19.9254 23.2909 18.4199 24.1494C17.6667 24.5789 17.0062 24.904 16.4863 25.1182C16.2262 25.2253 16.0121 25.3001 15.8467 25.3467C15.6728 25.3956 15.5993 25.4002 15.5996 25.4004C15.5928 25.3997 15.5178 25.3932 15.3525 25.3467C15.1871 25.3001 14.973 25.2253 14.7129 25.1182C14.193 24.904 13.5333 24.5788 12.7803 24.1494C11.2748 23.2909 9.44467 22.0423 7.67773 20.499C4.0947 17.3696 1 13.2338 1 8.84082C1.00008 6.89007 1.80381 4.91108 3.12207 3.42188C4.43787 1.93557 6.20587 1.00033 8.10059 1C9.51294 1.00117 10.8927 1.41742 12.0635 2.19434C12.7589 2.65582 13.3618 3.2322 13.8486 3.89355C14.2951 4.50017 15.0032 4.73135 15.5996 4.73145C16.1961 4.73144 16.905 4.5003 17.3516 3.89355C17.8376 3.23336 18.4389 2.65737 19.1328 2.19629C20.3012 1.4199 21.6779 1.00339 23.0879 1Z" fill="#5E2A28" stroke="#5E2A28" stroke-width="2"/> <path d="M23.0879 1C24.9887 1 26.7592 1.93575 28.0762 3.42188C29.3956 4.91097 30.2001 6.88999 30.2002 8.84082C30.2002 13.2337 27.1054 17.3696 23.5225 20.499C21.7556 22.0422 19.9254 23.2909 18.4199 24.1494C17.6667 24.5789 17.0062 24.904 16.4863 25.1182C16.2262 25.2253 16.0121 25.3001 15.8467 25.3467C15.6728 25.3956 15.5993 25.4002 15.5996 25.4004C15.5928 25.3997 15.5178 25.3932 15.3525 25.3467C15.1871 25.3001 14.973 25.2253 14.7129 25.1182C14.193 24.904 13.5333 24.5788 12.7803 24.1494C11.2748 23.2909 9.44467 22.0423 7.67773 20.499C4.0947 17.3696 1 13.2338 1 8.84082C1.00008 6.89007 1.80381 4.91108 3.12207 3.42188C4.43787 1.93557 6.20587 1.00033 8.10059 1C9.51294 1.00117 10.8927 1.41742 12.0635 2.19434C12.7589 2.65582 13.3618 3.2322 13.8486 3.89355C14.2951 4.50017 15.0032 4.73135 15.5996 4.73145C16.1961 4.73144 16.905 4.5003 17.3516 3.89355C17.8376 3.23336 18.4389 2.65737 19.1328 2.19629C20.3012 1.4199 21.6779 1.00339 23.0879 1Z" fill="currentColor" stroke="currentColor" stroke-width="2"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,3 +1,3 @@
<svg width="35" height="36" viewBox="0 0 35 36" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="35" height="36" viewBox="0 0 35 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.0879 6C26.9887 6 28.7592 6.93575 30.0762 8.42188C31.3956 9.91097 32.2001 11.89 32.2002 13.8408C32.2002 18.2337 29.1054 22.3696 25.5225 25.499C23.7556 27.0422 21.9254 28.2909 20.4199 29.1494C19.6667 29.5789 19.0062 29.904 18.4863 30.1182C18.2262 30.2253 18.0121 30.3001 17.8467 30.3467C17.6728 30.3956 17.5993 30.4002 17.5996 30.4004C17.5928 30.3997 17.5178 30.3932 17.3525 30.3467C17.1871 30.3001 16.973 30.2253 16.7129 30.1182C16.193 29.904 15.5333 29.5788 14.7803 29.1494C13.2748 28.2909 11.4447 27.0423 9.67773 25.499C6.0947 22.3696 3 18.2338 3 13.8408C3.00008 11.8901 3.80381 9.91108 5.12207 8.42188C6.43787 6.93557 8.20587 6.00033 10.1006 6C11.5129 6.00117 12.8927 6.41742 14.0635 7.19434C14.7589 7.65582 15.3618 8.2322 15.8486 8.89355C16.2951 9.50017 17.0032 9.73135 17.5996 9.73145C18.1961 9.73144 18.905 9.5003 19.3516 8.89355C19.8376 8.23336 20.4389 7.65737 21.1328 7.19629C22.3012 6.4199 23.6779 6.00339 25.0879 6Z" stroke="#5E2A28" stroke-width="2"/> <path d="M25.0879 6C26.9887 6 28.7592 6.93575 30.0762 8.42188C31.3956 9.91097 32.2001 11.89 32.2002 13.8408C32.2002 18.2337 29.1054 22.3696 25.5225 25.499C23.7556 27.0422 21.9254 28.2909 20.4199 29.1494C19.6667 29.5789 19.0062 29.904 18.4863 30.1182C18.2262 30.2253 18.0121 30.3001 17.8467 30.3467C17.6728 30.3956 17.5993 30.4002 17.5996 30.4004C17.5928 30.3997 17.5178 30.3932 17.3525 30.3467C17.1871 30.3001 16.973 30.2253 16.7129 30.1182C16.193 29.904 15.5333 29.5788 14.7803 29.1494C13.2748 28.2909 11.4447 27.0423 9.67773 25.499C6.0947 22.3696 3 18.2338 3 13.8408C3.00008 11.8901 3.80381 9.91108 5.12207 8.42188C6.43787 6.93557 8.20587 6.00033 10.1006 6C11.5129 6.00117 12.8927 6.41742 14.0635 7.19434C14.7589 7.65582 15.3618 8.2322 15.8486 8.89355C16.2951 9.50017 17.0032 9.73135 17.5996 9.73145C18.1961 9.73144 18.905 9.5003 19.3516 8.89355C19.8376 8.23336 20.4389 7.65737 21.1328 7.19629C22.3012 6.4199 23.6779 6.00339 25.0879 6Z" stroke="currentColor" stroke-width="2"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
import SheetModal from '@/components/ui/SheetModal'; import SheetModal from '@/components/ui/SheetModal';
import { MOCK_CONTENT } from '@/src/constants/mockContent'; import { MOCK_CONTENT } from '@/src/constants/mockContent';
import { getFavorites } from '@/src/storage/appStorage'; import { getFavorites, getRecoFeedCache, type FavoriteItem } from '@/src/storage/appStorage';
type Props = { type Props = {
visible: boolean; visible: boolean;
@@ -13,25 +13,50 @@ type Props = {
export default function FavoritesModal({ visible, onClose }: Props) { export default function FavoritesModal({ visible, onClose }: Props) {
const { t } = useTranslation(); const { t } = useTranslation();
const [ids, setIds] = useState<string[]>([]); const [items, setItems] = useState<(FavoriteItem & { text: string })[]>([]);
// 每次打开时刷新一次,确保展示最新“喜欢” // 每次打开时刷新一次,确保展示最新“喜欢”
useEffect(() => { useEffect(() => {
if (!visible) return; if (!visible) return;
let cancelled = false; let cancelled = false;
(async () => { (async () => {
const list = await getFavorites(); const [favList, cache] = await Promise.all([
if (!cancelled) setIds(list); getFavorites(),
getRecoFeedCache()
]);
console.log('FavoritesModal: Loaded favList', favList.length, 'items');
console.log('FavoritesModal: Loaded cache items', cache?.items?.length || 0);
if (cancelled) return;
// 建立文案查找表
const textMap = new Map<string, string>();
// 1. 放入 Mock 数据
MOCK_CONTENT.forEach(c => textMap.set(String(c.id), t(c.textKey)));
// 2. 放入缓存数据
if (cache?.items) {
cache.items.forEach(c => textMap.set(String(c.content_id), c.text));
}
// 3. 组装最终展示列表
const enriched = favList.map(fav => {
const favIdStr = String(fav.id);
const text = fav.text || textMap.get(favIdStr);
console.log(`FavoritesModal: Matching fav.id=${favIdStr}, found text=${!!text}, textValue=${text?.substring(0, 10)}...`);
return {
...fav,
text: text || t('favorites.unknownText')
};
});
setItems(enriched);
})(); })();
return () => { return () => {
cancelled = true; cancelled = true;
}; };
}, [visible]); }, [visible, t]);
const items = useMemo(() => {
const map = new Map(MOCK_CONTENT.map((c) => [c.id, c]));
return ids.map((id) => map.get(id)).filter(Boolean) as { id: string; text: string }[];
}, [ids]);
return ( return (
<SheetModal visible={visible} title={t('profile.favorites')} onClose={onClose}> <SheetModal visible={visible} title={t('profile.favorites')} onClose={onClose}>
@@ -41,7 +66,7 @@ export default function FavoritesModal({ visible, onClose }: Props) {
) : ( ) : (
<FlatList <FlatList
data={items} data={items}
keyExtractor={(it) => it.id} keyExtractor={(it) => it.favId}
contentContainerStyle={styles.list} contentContainerStyle={styles.list}
renderItem={({ item }) => ( renderItem={({ item }) => (
<View style={styles.row}> <View style={styles.row}>

View File

@@ -22,6 +22,7 @@ import {
getFavorites, getFavorites,
setDailyReminderSettings, setDailyReminderSettings,
removeFavorite, removeFavorite,
getRecoFeedCache,
getUserProfile, getUserProfile,
type DailyReminderSettings, type DailyReminderSettings,
type FavoriteItem, type FavoriteItem,
@@ -271,14 +272,20 @@ function FavoritesPage({ visible, page }: { visible: boolean; page: Page }) {
async function refreshFavorites() { async function refreshFavorites() {
const storedFavs = await getFavorites(); const storedFavs = await getFavorites();
const map = new Map(MOCK_CONTENT.map((c) => [c.id, c.text])); const textMap = new Map<string, string>();
const list = storedFavs // 1) Mock 文案
.map((fav) => ({ MOCK_CONTENT.forEach((c) => textMap.set(String(c.id), t(c.textKey)));
// 2) 后端推荐缓存文案(避免收藏后 cache 覆盖就丢文案)
const cache = await getRecoFeedCache();
cache?.items?.forEach((c) => textMap.set(String(c.content_id), c.text));
// 3) 组装:优先使用收藏时写入的 text其次从 map 回填
const list = storedFavs.map((fav) => ({
...fav, ...fav,
text: map.get(fav.id) || '' text: fav.text || textMap.get(String(fav.id)) || t('favorites.unknownText'),
})) }));
.filter(item => item.text !== '');
setFavorites(list); setFavorites(list);
} }
@@ -390,7 +397,7 @@ function DailyReminderPage({ visible, onDone }: { visible: boolean; onDone: () =
if (settings.status === 'denied') { if (settings.status === 'denied') {
Alert.alert( Alert.alert(
t('common.notice'), t('common.notice'),
"系统权限已被拒绝,请前往手机设置开启通知。" t('permissions.notificationsDenied')
); );
setPushEnabled(false); setPushEnabled(false);
return; return;
@@ -607,12 +614,12 @@ function WidgetHowToPage() {
} }
function LanguagePage() { function LanguagePage() {
const { i18n } = useTranslation(); const { t, i18n } = useTranslation();
const currentLang = i18n.language; const currentLang = i18n.language;
const languages = [ const languages = [
{ id: 'zh-TW', label: '繁体' }, { id: 'zh-TW', label: t('language.zhTW') },
{ id: 'en', label: 'English' }, { id: 'en', label: t('language.en') },
]; ];
return ( return (

View File

@@ -1,13 +1,14 @@
import React from 'react'; import React from 'react';
import { View, StyleSheet, TouchableOpacity } from 'react-native'; import { View, StyleSheet, TouchableOpacity } from 'react-native';
import { useTranslation } from 'react-i18next';
import { SerifText } from './SerifText'; import { SerifText } from './SerifText';
import { OnboardingColors } from '@/constants/OnboardingTheme'; import { OnboardingColors } from '@/constants/OnboardingTheme';
export const INTENTS = [ export const INTENTS = [
{ id: 'love', label: '爱情', icon: '❤️' }, { id: 'love', labelKey: 'intent.love', icon: '❤️' },
{ id: 'life', label: '生活', icon: '⛅' }, { id: 'life', labelKey: 'intent.life', icon: '⛅' },
{ id: 'travel', label: '旅游', icon: '🌴' }, { id: 'travel', labelKey: 'intent.travel', icon: '🌴' },
{ id: 'work', label: '职场', icon: '💼' }, { id: 'work', labelKey: 'intent.work', icon: '💼' },
]; ];
interface IntentSelectionStepProps { interface IntentSelectionStepProps {
@@ -16,9 +17,10 @@ interface IntentSelectionStepProps {
} }
export function IntentSelectionStep({ selectedIds, onToggle }: IntentSelectionStepProps) { export function IntentSelectionStep({ selectedIds, onToggle }: IntentSelectionStepProps) {
const { t } = useTranslation();
return ( return (
<View style={styles.container}> <View style={styles.container}>
<SerifText style={styles.title}></SerifText> <SerifText style={styles.title}>{t('intent.title')}</SerifText>
<View style={styles.grid}> <View style={styles.grid}>
{INTENTS.map((intent) => { {INTENTS.map((intent) => {
@@ -35,7 +37,7 @@ export function IntentSelectionStep({ selectedIds, onToggle }: IntentSelectionSt
> >
<SerifText style={styles.icon}>{intent.icon}</SerifText> <SerifText style={styles.icon}>{intent.icon}</SerifText>
<SerifText style={[styles.label, isSelected && styles.labelSelected]}> <SerifText style={[styles.label, isSelected && styles.labelSelected]}>
{intent.label} {t(intent.labelKey)}
</SerifText> </SerifText>
</TouchableOpacity> </TouchableOpacity>
); );

View File

@@ -49,19 +49,11 @@ export function SelectionStep({ options, selectedIds, onToggle, onNext, onSkip }
{/* 底部按钮:距离底部 12% 高度 */} {/* 底部按钮:距离底部 12% 高度 */}
<View style={styles.footer}> <View style={styles.footer}>
<View style={styles.footerRow}>
{onSkip && (
<TouchableOpacity onPress={onSkip} activeOpacity={0.8} style={styles.skipBtn}>
<SerifText style={styles.skipText}></SerifText>
</TouchableOpacity>
)}
<TouchableOpacity onPress={onNext} disabled={!hasSelection} activeOpacity={0.8}> <TouchableOpacity onPress={onNext} disabled={!hasSelection} activeOpacity={0.8}>
{hasSelection ? <BtnClicked width={87} height={57} /> : <BtnNotClicked width={87} height={57} />} {hasSelection ? <BtnClicked width={87} height={57} /> : <BtnNotClicked width={87} height={57} />}
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</View> </View>
</View>
); );
} }

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useMemo, useState, useRef } from 'react'; import React, { useEffect, useMemo, useState, useRef } from 'react';
import { Modal, Pressable, StyleSheet, Text, View, PanResponder, Animated as RNAnimated, Dimensions, Image, ImageSourcePropType } from 'react-native'; import { Modal, Pressable, StyleSheet, Text, View, PanResponder, Animated as RNAnimated, Dimensions, Image, ImageSourcePropType } from 'react-native';
import { useTranslation } from 'react-i18next';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
import Animated, { import Animated, {
Easing, Easing,
@@ -27,6 +28,7 @@ type Props = {
* - 高度固定:默认距离顶部固定间距,也支持传入指定高度 * - 高度固定:默认距离顶部固定间距,也支持传入指定高度
*/ */
export default function SheetModal({ visible, title, onClose, children, leftIcon, height: customHeight }: Props) { export default function SheetModal({ visible, title, onClose, children, leftIcon, height: customHeight }: Props) {
const { t } = useTranslation();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
const progress = useSharedValue(0); // 0: 关闭, 1: 打开 const progress = useSharedValue(0); // 0: 关闭, 1: 打开
@@ -121,7 +123,7 @@ export default function SheetModal({ visible, title, onClose, children, leftIcon
</Text> </Text>
<Pressable <Pressable
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={leftIcon ? "返回" : "关闭"} accessibilityLabel={leftIcon ? t('common.back') : t('common.close')}
onPress={onClose} onPress={onClose}
hitSlop={10} hitSlop={10}
style={styles.close} style={styles.close}

View File

@@ -59,13 +59,5 @@ target 'client' do
:mac_catalyst_enabled => false, :mac_catalyst_enabled => false,
:ccache_enabled => ccache_enabled?(podfile_properties), :ccache_enabled => ccache_enabled?(podfile_properties),
) )
# 生成并随归档产物携带 dSYM用于崩溃符号化与 Upload Symbols Failed 修复)
installer.pods_project.targets.each do |target|
target.build_configurations.each do |build_config|
build_config.build_settings['DEBUG_INFORMATION_FORMAT'] = 'dwarf-with-dsym'
build_config.build_settings['DWARF_DSYM_FILE_SHOULD_ACCOMPANY_PRODUCT'] = 'YES'
end
end
end end
end end

View File

@@ -3,6 +3,9 @@ PODS:
- ExpoModulesCore - ExpoModulesCore
- EXConstants (18.0.13): - EXConstants (18.0.13):
- ExpoModulesCore - ExpoModulesCore
- EXJSONUtils (0.15.0)
- EXManifests (1.0.10):
- ExpoModulesCore
- EXNotifications (0.32.16): - EXNotifications (0.32.16):
- ExpoModulesCore - ExpoModulesCore
- Expo (54.0.32): - Expo (54.0.32):
@@ -30,6 +33,177 @@ PODS:
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- ReactNativeDependencies - ReactNativeDependencies
- Yoga - Yoga
- expo-dev-client (6.0.20):
- EXManifests
- expo-dev-launcher
- expo-dev-menu
- expo-dev-menu-interface
- EXUpdatesInterface
- expo-dev-launcher (6.0.20):
- EXManifests
- expo-dev-launcher/Main (= 6.0.20)
- expo-dev-menu
- expo-dev-menu-interface
- ExpoModulesCore
- EXUpdatesInterface
- hermes-engine
- RCTRequired
- RCTTypeSafety
- React-Core
- React-Core-prebuilt
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-jsinspector
- React-NativeModulesApple
- React-RCTAppDelegate
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactAppDependencyProvider
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- expo-dev-launcher/Main (6.0.20):
- EXManifests
- expo-dev-launcher/Unsafe
- expo-dev-menu
- expo-dev-menu-interface
- ExpoModulesCore
- EXUpdatesInterface
- hermes-engine
- RCTRequired
- RCTTypeSafety
- React-Core
- React-Core-prebuilt
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-jsinspector
- React-NativeModulesApple
- React-RCTAppDelegate
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactAppDependencyProvider
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- expo-dev-launcher/Unsafe (6.0.20):
- EXManifests
- expo-dev-menu
- expo-dev-menu-interface
- ExpoModulesCore
- EXUpdatesInterface
- hermes-engine
- RCTRequired
- RCTTypeSafety
- React-Core
- React-Core-prebuilt
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-jsinspector
- React-NativeModulesApple
- React-RCTAppDelegate
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactAppDependencyProvider
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- expo-dev-menu (7.0.18):
- expo-dev-menu/Main (= 7.0.18)
- expo-dev-menu/ReactNativeCompatibles (= 7.0.18)
- hermes-engine
- RCTRequired
- RCTTypeSafety
- React-Core
- React-Core-prebuilt
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- expo-dev-menu-interface (2.0.0)
- expo-dev-menu/Main (7.0.18):
- EXManifests
- expo-dev-menu-interface
- ExpoModulesCore
- hermes-engine
- RCTRequired
- RCTTypeSafety
- React-Core
- React-Core-prebuilt
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-jsinspector
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- expo-dev-menu/ReactNativeCompatibles (7.0.18):
- hermes-engine
- RCTRequired
- RCTTypeSafety
- React-Core
- React-Core-prebuilt
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- ExpoAsset (12.0.12): - ExpoAsset (12.0.12):
- ExpoModulesCore - ExpoModulesCore
- ExpoFileSystem (19.0.21): - ExpoFileSystem (19.0.21):
@@ -74,6 +248,8 @@ PODS:
- ExpoModulesCore - ExpoModulesCore
- ExpoWebBrowser (15.0.10): - ExpoWebBrowser (15.0.10):
- ExpoModulesCore - ExpoModulesCore
- EXUpdatesInterface (2.0.0):
- ExpoModulesCore
- FBLazyVector (0.81.5) - FBLazyVector (0.81.5)
- hermes-engine (0.81.5): - hermes-engine (0.81.5):
- hermes-engine/Pre-built (= 0.81.5) - hermes-engine/Pre-built (= 0.81.5)
@@ -1798,6 +1974,28 @@ PODS:
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- ReactNativeDependencies - ReactNativeDependencies
- Yoga - Yoga
- RNGestureHandler (2.30.0):
- hermes-engine
- RCTRequired
- RCTTypeSafety
- React-Core
- React-Core-prebuilt
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- RNReanimated (4.1.6): - RNReanimated (4.1.6):
- hermes-engine - hermes-engine
- RCTRequired - RCTRequired
@@ -2040,19 +2238,26 @@ PODS:
DEPENDENCIES: DEPENDENCIES:
- "EXApplication (from `../node_modules/.pnpm/expo-application@7.0.8_expo@54.0.32/node_modules/expo-application/ios`)" - "EXApplication (from `../node_modules/.pnpm/expo-application@7.0.8_expo@54.0.32/node_modules/expo-application/ios`)"
- "EXConstants (from `../node_modules/.pnpm/expo-constants@18.0.13_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0_/node_modules/expo-constants/ios`)" - "EXConstants (from `../node_modules/.pnpm/expo-constants@18.0.13_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0_/node_modules/expo-constants/ios`)"
- "EXNotifications (from `../node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@1_nvlvke5tn7wk5pigfsu7j4ieeq/node_modules/expo-notifications/ios`)" - "EXJSONUtils (from `../node_modules/.pnpm/expo-json-utils@0.15.0/node_modules/expo-json-utils/ios`)"
- "Expo (from `../node_modules/.pnpm/expo@54.0.32_@babel+core@7.28.6_@expo+metro-runtime@6.1.2_expo-router@6.0.22_react-native@0.8_7rhpxisdkrzvrgzbu7ct455kta/node_modules/expo`)" - "EXManifests (from `../node_modules/.pnpm/expo-manifests@1.0.10_expo@54.0.32/node_modules/expo-manifests/ios`)"
- "EXNotifications (from `../node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+r_758952db70529f49bda448def1c13c49/node_modules/expo-notifications/ios`)"
- "Expo (from `../node_modules/.pnpm/expo@54.0.32_@babel+core@7.28.6_@expo+metro-runtime@6.1.2_expo-router@6.0.22_react-nati_18ad48ba284ee86e6eb1cb0f939697b0/node_modules/expo`)"
- "expo-dev-client (from `../node_modules/.pnpm/expo-dev-client@6.0.20_expo@54.0.32/node_modules/expo-dev-client/ios`)"
- "expo-dev-launcher (from `../node_modules/.pnpm/expo-dev-launcher@6.0.20_expo@54.0.32/node_modules/expo-dev-launcher`)"
- "expo-dev-menu (from `../node_modules/.pnpm/expo-dev-menu@7.0.18_expo@54.0.32/node_modules/expo-dev-menu`)"
- "expo-dev-menu-interface (from `../node_modules/.pnpm/expo-dev-menu-interface@2.0.0_expo@54.0.32/node_modules/expo-dev-menu-interface/ios`)"
- "ExpoAsset (from `../node_modules/.pnpm/expo-asset@12.0.12_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-asset/ios`)" - "ExpoAsset (from `../node_modules/.pnpm/expo-asset@12.0.12_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-asset/ios`)"
- "ExpoFileSystem (from `../node_modules/.pnpm/expo-file-system@19.0.21_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0_/node_modules/expo-file-system/ios`)" - "ExpoFileSystem (from `../node_modules/.pnpm/expo-file-system@19.0.21_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0_/node_modules/expo-file-system/ios`)"
- "ExpoFont (from `../node_modules/.pnpm/expo-font@14.0.11_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-font/ios`)" - "ExpoFont (from `../node_modules/.pnpm/expo-font@14.0.11_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-font/ios`)"
- "ExpoHead (from `../node_modules/.pnpm/expo-router@6.0.22_@expo+metro-runtime@6.1.2_@types+react@19.1.17_expo-constants@18.0.13_expo_rjurfbyy5kjn57nkkfxix5iqea/node_modules/expo-router/ios`)" - "ExpoHead (from `../node_modules/.pnpm/expo-router@6.0.22_@expo+metro-runtime@6.1.2_@types+react@19.1.17_expo-constants@18.0.1_bd9aa16746ed7110429f931eb008e6d2/node_modules/expo-router/ios`)"
- "ExpoKeepAwake (from `../node_modules/.pnpm/expo-keep-awake@15.0.8_expo@54.0.32_react@19.1.0/node_modules/expo-keep-awake/ios`)" - "ExpoKeepAwake (from `../node_modules/.pnpm/expo-keep-awake@15.0.8_expo@54.0.32_react@19.1.0/node_modules/expo-keep-awake/ios`)"
- "ExpoLinearGradient (from `../node_modules/.pnpm/expo-linear-gradient@15.0.8_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@_e6k2hjkd5k4lph2ersbp3gfshy/node_modules/expo-linear-gradient/ios`)" - "ExpoLinearGradient (from `../node_modules/.pnpm/expo-linear-gradient@15.0.8_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+_53aef72480df9baa4504f4743d9c64bb/node_modules/expo-linear-gradient/ios`)"
- "ExpoLinking (from `../node_modules/.pnpm/expo-linking@8.0.11_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-linking/ios`)" - "ExpoLinking (from `../node_modules/.pnpm/expo-linking@8.0.11_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-linking/ios`)"
- "ExpoLocalization (from `../node_modules/.pnpm/expo-localization@17.0.8_expo@54.0.32_react@19.1.0/node_modules/expo-localization/ios`)" - "ExpoLocalization (from `../node_modules/.pnpm/expo-localization@17.0.8_expo@54.0.32_react@19.1.0/node_modules/expo-localization/ios`)"
- "ExpoModulesCore (from `../node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-modules-core`)" - "ExpoModulesCore (from `../node_modules/.pnpm/expo-modules-core@3.0.29_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-modules-core`)"
- "ExpoSplashScreen (from `../node_modules/.pnpm/expo-splash-screen@31.0.13_expo@54.0.32/node_modules/expo-splash-screen/ios`)" - "ExpoSplashScreen (from `../node_modules/.pnpm/expo-splash-screen@31.0.13_expo@54.0.32/node_modules/expo-splash-screen/ios`)"
- "ExpoWebBrowser (from `../node_modules/.pnpm/expo-web-browser@15.0.10_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0_/node_modules/expo-web-browser/ios`)" - "ExpoWebBrowser (from `../node_modules/.pnpm/expo-web-browser@15.0.10_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0_/node_modules/expo-web-browser/ios`)"
- "EXUpdatesInterface (from `../node_modules/.pnpm/expo-updates-interface@2.0.0_expo@54.0.32/node_modules/expo-updates-interface/ios`)"
- "FBLazyVector (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/Libraries/FBLazyVector`)" - "FBLazyVector (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/Libraries/FBLazyVector`)"
- "hermes-engine (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)" - "hermes-engine (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)"
- "RCTDeprecation (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)" - "RCTDeprecation (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)"
@@ -2089,7 +2294,7 @@ DEPENDENCIES:
- "React-logger (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/logger`)" - "React-logger (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/logger`)"
- "React-Mapbuffer (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon`)" - "React-Mapbuffer (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon`)"
- "React-microtasksnativemodule (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)" - "React-microtasksnativemodule (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)"
- "react-native-safe-area-context (from `../node_modules/.pnpm/react-native-safe-area-context@5.6.2_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1_azuxgonsvxb2yngtegtuvyxcpi/node_modules/react-native-safe-area-context`)" - "react-native-safe-area-context (from `../node_modules/.pnpm/react-native-safe-area-context@5.6.2_react-native@0.81.5_@babel+core@7.28.6_@types+reac_14122a3aa345cfabcc022a0f638ef16d/node_modules/react-native-safe-area-context`)"
- "React-NativeModulesApple (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)" - "React-NativeModulesApple (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)"
- "React-oscompat (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/oscompat`)" - "React-oscompat (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/oscompat`)"
- "React-perflogger (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/reactperflogger`)" - "React-perflogger (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/reactperflogger`)"
@@ -2121,11 +2326,12 @@ DEPENDENCIES:
- ReactCodegen (from `build/generated/ios`) - ReactCodegen (from `build/generated/ios`)
- "ReactCommon/turbomodule/core (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon`)" - "ReactCommon/turbomodule/core (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon`)"
- "ReactNativeDependencies (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/third-party-podspecs/ReactNativeDependencies.podspec`)" - "ReactNativeDependencies (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/third-party-podspecs/ReactNativeDependencies.podspec`)"
- "RNCAsyncStorage (from `../node_modules/.pnpm/@react-native-async-storage+async-storage@2.2.0_react-native@0.81.5_@babel+core@7.28.6_@types_fp4qq3a7mejmut52v6jrlvxlzi/node_modules/@react-native-async-storage/async-storage`)" - "RNCAsyncStorage (from `../node_modules/.pnpm/@react-native-async-storage+async-storage@2.2.0_react-native@0.81.5_@babel+core@7.28.6__ce3c4972004f3d6791573ec5b64bee38/node_modules/@react-native-async-storage/async-storage`)"
- "RNReanimated (from `../node_modules/.pnpm/react-native-reanimated@4.1.6_@babel+core@7.28.6_react-native-worklets@0.5.1_@babel+core@7.28_ky3sbxf6i7nkyacc2hzg3xcz4q/node_modules/react-native-reanimated`)" - "RNGestureHandler (from `../node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.5_@babel+core@7.28.6_@types+react_39cf7da47c9c8531caaa923ee740e293/node_modules/react-native-gesture-handler`)"
- "RNReanimated (from `../node_modules/.pnpm/react-native-reanimated@4.1.6_@babel+core@7.28.6_react-native-worklets@0.5.1_@babel+cor_c7c888bd389fb93c9cfe2d3c1c8b0777/node_modules/react-native-reanimated`)"
- "RNScreens (from `../node_modules/.pnpm/react-native-screens@4.16.0_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/react-native-screens`)" - "RNScreens (from `../node_modules/.pnpm/react-native-screens@4.16.0_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/react-native-screens`)"
- "RNSVG (from `../node_modules/.pnpm/react-native-svg@15.12.1_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/react-native-svg`)" - "RNSVG (from `../node_modules/.pnpm/react-native-svg@15.12.1_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/react-native-svg`)"
- "RNWorklets (from `../node_modules/.pnpm/react-native-worklets@0.5.1_@babel+core@7.28.6_react-native@0.81.5_@babel+core@7.28.6_@types+_5atwepuw3zy3crkgvetf35tkve/node_modules/react-native-worklets`)" - "RNWorklets (from `../node_modules/.pnpm/react-native-worklets@0.5.1_@babel+core@7.28.6_react-native@0.81.5_@babel+core@7.28.6_@_40f69326ce21d3f9f6d74d3965fd9adf/node_modules/react-native-worklets`)"
- "Yoga (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/yoga`)" - "Yoga (from `../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/yoga`)"
EXTERNAL SOURCES: EXTERNAL SOURCES:
@@ -2133,10 +2339,22 @@ EXTERNAL SOURCES:
:path: "../node_modules/.pnpm/expo-application@7.0.8_expo@54.0.32/node_modules/expo-application/ios" :path: "../node_modules/.pnpm/expo-application@7.0.8_expo@54.0.32/node_modules/expo-application/ios"
EXConstants: EXConstants:
:path: "../node_modules/.pnpm/expo-constants@18.0.13_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0_/node_modules/expo-constants/ios" :path: "../node_modules/.pnpm/expo-constants@18.0.13_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0_/node_modules/expo-constants/ios"
EXJSONUtils:
:path: "../node_modules/.pnpm/expo-json-utils@0.15.0/node_modules/expo-json-utils/ios"
EXManifests:
:path: "../node_modules/.pnpm/expo-manifests@1.0.10_expo@54.0.32/node_modules/expo-manifests/ios"
EXNotifications: EXNotifications:
:path: "../node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@1_nvlvke5tn7wk5pigfsu7j4ieeq/node_modules/expo-notifications/ios" :path: "../node_modules/.pnpm/expo-notifications@0.32.16_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+r_758952db70529f49bda448def1c13c49/node_modules/expo-notifications/ios"
Expo: Expo:
:path: "../node_modules/.pnpm/expo@54.0.32_@babel+core@7.28.6_@expo+metro-runtime@6.1.2_expo-router@6.0.22_react-native@0.8_7rhpxisdkrzvrgzbu7ct455kta/node_modules/expo" :path: "../node_modules/.pnpm/expo@54.0.32_@babel+core@7.28.6_@expo+metro-runtime@6.1.2_expo-router@6.0.22_react-nati_18ad48ba284ee86e6eb1cb0f939697b0/node_modules/expo"
expo-dev-client:
:path: "../node_modules/.pnpm/expo-dev-client@6.0.20_expo@54.0.32/node_modules/expo-dev-client/ios"
expo-dev-launcher:
:path: "../node_modules/.pnpm/expo-dev-launcher@6.0.20_expo@54.0.32/node_modules/expo-dev-launcher"
expo-dev-menu:
:path: "../node_modules/.pnpm/expo-dev-menu@7.0.18_expo@54.0.32/node_modules/expo-dev-menu"
expo-dev-menu-interface:
:path: "../node_modules/.pnpm/expo-dev-menu-interface@2.0.0_expo@54.0.32/node_modules/expo-dev-menu-interface/ios"
ExpoAsset: ExpoAsset:
:path: "../node_modules/.pnpm/expo-asset@12.0.12_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-asset/ios" :path: "../node_modules/.pnpm/expo-asset@12.0.12_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-asset/ios"
ExpoFileSystem: ExpoFileSystem:
@@ -2144,11 +2362,11 @@ EXTERNAL SOURCES:
ExpoFont: ExpoFont:
:path: "../node_modules/.pnpm/expo-font@14.0.11_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-font/ios" :path: "../node_modules/.pnpm/expo-font@14.0.11_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-font/ios"
ExpoHead: ExpoHead:
:path: "../node_modules/.pnpm/expo-router@6.0.22_@expo+metro-runtime@6.1.2_@types+react@19.1.17_expo-constants@18.0.13_expo_rjurfbyy5kjn57nkkfxix5iqea/node_modules/expo-router/ios" :path: "../node_modules/.pnpm/expo-router@6.0.22_@expo+metro-runtime@6.1.2_@types+react@19.1.17_expo-constants@18.0.1_bd9aa16746ed7110429f931eb008e6d2/node_modules/expo-router/ios"
ExpoKeepAwake: ExpoKeepAwake:
:path: "../node_modules/.pnpm/expo-keep-awake@15.0.8_expo@54.0.32_react@19.1.0/node_modules/expo-keep-awake/ios" :path: "../node_modules/.pnpm/expo-keep-awake@15.0.8_expo@54.0.32_react@19.1.0/node_modules/expo-keep-awake/ios"
ExpoLinearGradient: ExpoLinearGradient:
:path: "../node_modules/.pnpm/expo-linear-gradient@15.0.8_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@_e6k2hjkd5k4lph2ersbp3gfshy/node_modules/expo-linear-gradient/ios" :path: "../node_modules/.pnpm/expo-linear-gradient@15.0.8_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+_53aef72480df9baa4504f4743d9c64bb/node_modules/expo-linear-gradient/ios"
ExpoLinking: ExpoLinking:
:path: "../node_modules/.pnpm/expo-linking@8.0.11_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-linking/ios" :path: "../node_modules/.pnpm/expo-linking@8.0.11_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/expo-linking/ios"
ExpoLocalization: ExpoLocalization:
@@ -2159,6 +2377,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/.pnpm/expo-splash-screen@31.0.13_expo@54.0.32/node_modules/expo-splash-screen/ios" :path: "../node_modules/.pnpm/expo-splash-screen@31.0.13_expo@54.0.32/node_modules/expo-splash-screen/ios"
ExpoWebBrowser: ExpoWebBrowser:
:path: "../node_modules/.pnpm/expo-web-browser@15.0.10_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0_/node_modules/expo-web-browser/ios" :path: "../node_modules/.pnpm/expo-web-browser@15.0.10_expo@54.0.32_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0_/node_modules/expo-web-browser/ios"
EXUpdatesInterface:
:path: "../node_modules/.pnpm/expo-updates-interface@2.0.0_expo@54.0.32/node_modules/expo-updates-interface/ios"
FBLazyVector: FBLazyVector:
:path: "../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/Libraries/FBLazyVector" :path: "../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/Libraries/FBLazyVector"
hermes-engine: hermes-engine:
@@ -2231,7 +2451,7 @@ EXTERNAL SOURCES:
React-microtasksnativemodule: React-microtasksnativemodule:
:path: "../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/react/nativemodule/microtasks" :path: "../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
react-native-safe-area-context: react-native-safe-area-context:
:path: "../node_modules/.pnpm/react-native-safe-area-context@5.6.2_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1_azuxgonsvxb2yngtegtuvyxcpi/node_modules/react-native-safe-area-context" :path: "../node_modules/.pnpm/react-native-safe-area-context@5.6.2_react-native@0.81.5_@babel+core@7.28.6_@types+reac_14122a3aa345cfabcc022a0f638ef16d/node_modules/react-native-safe-area-context"
React-NativeModulesApple: React-NativeModulesApple:
:path: "../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" :path: "../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios"
React-oscompat: React-oscompat:
@@ -2295,34 +2515,43 @@ EXTERNAL SOURCES:
ReactNativeDependencies: ReactNativeDependencies:
:podspec: "../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/third-party-podspecs/ReactNativeDependencies.podspec" :podspec: "../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/third-party-podspecs/ReactNativeDependencies.podspec"
RNCAsyncStorage: RNCAsyncStorage:
:path: "../node_modules/.pnpm/@react-native-async-storage+async-storage@2.2.0_react-native@0.81.5_@babel+core@7.28.6_@types_fp4qq3a7mejmut52v6jrlvxlzi/node_modules/@react-native-async-storage/async-storage" :path: "../node_modules/.pnpm/@react-native-async-storage+async-storage@2.2.0_react-native@0.81.5_@babel+core@7.28.6__ce3c4972004f3d6791573ec5b64bee38/node_modules/@react-native-async-storage/async-storage"
RNGestureHandler:
:path: "../node_modules/.pnpm/react-native-gesture-handler@2.30.0_react-native@0.81.5_@babel+core@7.28.6_@types+react_39cf7da47c9c8531caaa923ee740e293/node_modules/react-native-gesture-handler"
RNReanimated: RNReanimated:
:path: "../node_modules/.pnpm/react-native-reanimated@4.1.6_@babel+core@7.28.6_react-native-worklets@0.5.1_@babel+core@7.28_ky3sbxf6i7nkyacc2hzg3xcz4q/node_modules/react-native-reanimated" :path: "../node_modules/.pnpm/react-native-reanimated@4.1.6_@babel+core@7.28.6_react-native-worklets@0.5.1_@babel+cor_c7c888bd389fb93c9cfe2d3c1c8b0777/node_modules/react-native-reanimated"
RNScreens: RNScreens:
:path: "../node_modules/.pnpm/react-native-screens@4.16.0_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/react-native-screens" :path: "../node_modules/.pnpm/react-native-screens@4.16.0_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/react-native-screens"
RNSVG: RNSVG:
:path: "../node_modules/.pnpm/react-native-svg@15.12.1_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/react-native-svg" :path: "../node_modules/.pnpm/react-native-svg@15.12.1_react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0__react@19.1.0/node_modules/react-native-svg"
RNWorklets: RNWorklets:
:path: "../node_modules/.pnpm/react-native-worklets@0.5.1_@babel+core@7.28.6_react-native@0.81.5_@babel+core@7.28.6_@types+_5atwepuw3zy3crkgvetf35tkve/node_modules/react-native-worklets" :path: "../node_modules/.pnpm/react-native-worklets@0.5.1_@babel+core@7.28.6_react-native@0.81.5_@babel+core@7.28.6_@_40f69326ce21d3f9f6d74d3965fd9adf/node_modules/react-native-worklets"
Yoga: Yoga:
:path: "../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/yoga" :path: "../node_modules/.pnpm/react-native@0.81.5_@babel+core@7.28.6_@types+react@19.1.17_react@19.1.0/node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS: SPEC CHECKSUMS:
EXApplication: 13420f8139864183f8a04fd6099077bdf8cfb186 EXApplication: 1e98d4b1dccdf30627f92917f4b2c5a53c330e5f
EXConstants: 3feb66fd1d94202fc1f0946d74e029d8b224b60e EXConstants: fce59a631a06c4151602843667f7cfe35f81e271
EXNotifications: 2a3feb7af6194828d9aafda72f63a9a03866230a EXJSONUtils: 1d3e4590438c3ee593684186007028a14b3686cd
Expo: b8d64eb9a496ebe8c71e3dae7eeb7f394b146b80 EXManifests: a8d97683e5c7a3b026ffbd58559c64dc655b747b
ExpoAsset: d999f3bbd998a750f3b74cb913229848901b926b EXNotifications: 9eec98712cc814ceff916d876cb53859003b0597
ExpoFileSystem: aefcd337b94b874f88752ebefc52813b84992fad Expo: 4e503a041c59c4e34c8be262a135848ad5cd3710
ExpoFont: c625dbd97ed57e9089b172b2a7bb99003d074664 expo-dev-client: 425ee077d6754a98cfe3a2e2410d29b440b24c9d
ExpoHead: b691a2ed7ab02ed820b6c6468941832d34969c29 expo-dev-launcher: a4f4cdef064ab1fb8621e5b8c7c457cd6e9568c3
ExpoKeepAwake: 44bf6715bc1d2ddb17afe19d927cd039cda123f0 expo-dev-menu: 05b18812110c175814c6af0d09dd658abcc5e00d
ExpoLinearGradient: 814a21fc4056c3cf606e4f19e31e47074c5b5a86 expo-dev-menu-interface: 600df12ea01efecdd822daaf13cc0ac091775533
ExpoLinking: ebf543fd411d56375cb4eee07f6ab4e31c7ad959 ExpoAsset: f867e55ceb428aab99e1e8c082b5aee7c159ea18
ExpoLocalization: 6ac6f326210f0a3141ef6f58ab8f8f4ed003b485 ExpoFileSystem: 858a44267a3e6e9057e0888ad7c7cfbf55d52063
ExpoModulesCore: 77496909fd3c800f97f7f2007dd26aeac4bb3798 ExpoFont: f543ce20a228dd702813668b1a07b46f51878d47
ExpoSplashScreen: 72fbc6dd9d6404dd9d0725a56c9ac1383bc0b14f ExpoHead: 4425246bc93411f0fe7f6945f95f698e91db8780
ExpoWebBrowser: 88b116cd378d9609c776c0903fe4070fca461588 ExpoKeepAwake: 55f75eca6499bb9e4231ebad6f3e9cb8f99c0296
ExpoLinearGradient: 809102bdb979f590083af49f7fa4805cd931bd58
ExpoLinking: 8f0aaf69aa56f832913030503b6263dc6f647f37
ExpoLocalization: d9168d5300a5b03e5e78b986124d11fb6ec3ebbd
ExpoModulesCore: f3da4f1ab5a8375d0beafab763739dbee8446583
ExpoSplashScreen: bc3cffefca2716e5f22350ca109badd7e50ec14d
ExpoWebBrowser: 17b064c621789e41d4816c95c93f429b84971f52
EXUpdatesInterface: 5adf50cb41e079c861da6d9b4b954c3db9a50734
FBLazyVector: e95a291ad2dadb88e42b06e0c5fb8262de53ec12 FBLazyVector: e95a291ad2dadb88e42b06e0c5fb8262de53ec12
hermes-engine: 9f4dfe93326146a1c99eb535b1cb0b857a3cd172 hermes-engine: 9f4dfe93326146a1c99eb535b1cb0b857a3cd172
RCTDeprecation: 943572d4be82d480a48f4884f670135ae30bf990 RCTDeprecation: 943572d4be82d480a48f4884f670135ae30bf990
@@ -2330,73 +2559,74 @@ SPEC CHECKSUMS:
RCTTypeSafety: 16a4144ca3f959583ab019b57d5633df10b5e97c RCTTypeSafety: 16a4144ca3f959583ab019b57d5633df10b5e97c
React: 914f8695f9bf38e6418228c2ffb70021e559f92f React: 914f8695f9bf38e6418228c2ffb70021e559f92f
React-callinvoker: 1c0808402aee0c6d4a0d8e7220ce6547af9fba71 React-callinvoker: 1c0808402aee0c6d4a0d8e7220ce6547af9fba71
React-Core: 4ae98f9e8135b8ddbd7c98730afb6fdae883db90 React-Core: c61410ef0ca6055e204a963992e363227e0fd1c5
React-Core-prebuilt: 8f4cca589c14e8cf8fc6db4587ef1c2056b5c151 React-Core-prebuilt: 02f0ad625ddd47463c009c2d0c5dd35c0d982599
React-CoreModules: e878a90bb19b8f3851818af997dbae3b3b0a27ac React-CoreModules: 1f6d1744b5f9f2ec684a4bb5ced25370f87e5382
React-cxxreact: 28af9844f6dc87be1385ab521fbfb3746f19563c React-cxxreact: 3af79478e8187b63ffc22b794cd42d3fc1f1f2da
React-debug: 6328c2228e268846161f10082e80dc69eac2e90a React-debug: 6328c2228e268846161f10082e80dc69eac2e90a
React-defaultsnativemodule: afc9d809ec75780f39464a6949c07987fbea488c React-defaultsnativemodule: d635ef36d755321e5d6fc065bd166b2c5a0e9833
React-domnativemodule: 91a233260411d41f27f67aa1358b7f9f0bfd101d React-domnativemodule: dd28f6d96cd21236e020be2eff6fe0b7d4ec3b66
React-Fabric: 21f349b5e93f305a3c38c885902683a9c79cf983 React-Fabric: 2e32c3fdbb1fbcf5fde54607e3abe453c6652ce2
React-FabricComponents: 47ac634cc9ecc64b30a9997192f510eebe4177e4 React-FabricComponents: 5ed0cdb81f6b91656cb4d3be432feaa28a58071a
React-FabricImage: 21873acd6d4a51a0b97c133141051c7acb11cc86 React-FabricImage: 2bc714f818cb24e454f5d3961864373271b2faf8
React-featureflags: 653f469f0c3c9dc271d610373e3b6e66a9fd847d React-featureflags: 847642f41fa71ad4eec5e0351badebcad4fe6171
React-featureflagsnativemodule: c91a8a3880e0f4838286402241ead47db43aed28 React-featureflagsnativemodule: c868a544b2c626fa337bcbd364b1befe749f0d3f
React-graphics: b4bdb0f635b8048c652a5d2b73eb8b1ddd950f24 React-graphics: 192ec701def5b3f2a07db2814dfba5a44986cff6
React-hermes: fcfad3b917400f49026f3232561e039c9d1c34bf React-hermes: e875778b496c86d07ab2ccaa36a9505d248a254b
React-idlecallbacksnativemodule: 8cb83207e39f8179ac1d344b6177c6ab3ccebcdc React-idlecallbacksnativemodule: 4d57965cdf82c14ee3b337189836cd8491632b76
React-ImageManager: 396128004783fc510e629124dce682d38d1088e7 React-ImageManager: bd0b99e370b13de82c9cd15f0f08144ff3de079e
React-jserrorhandler: b58b788d788cdbf8bda7db74a88ebfcffc8a0795 React-jserrorhandler: a2fdef4cbcfdcdf3fa9f5d1f7190f7fd4535248d
React-jsi: d2c3f8555175371c02da6dfe7ed1b64b55a9d6c0 React-jsi: 89d43d1e7d4d0663f8ba67e0b39eb4e4672c27de
React-jsiexecutor: ba537434eb45ee018b590ed7d29ee233fddb8669 React-jsiexecutor: abe4874aaab90dfee5dec480680220b2f8af07e3
React-jsinspector: f21b6654baf96cb9f71748844a32468a5f73ad51 React-jsinspector: a0b3e051aef842b0b2be2353790ae2b2a5a65a8f
React-jsinspectorcdp: 3f8be4830694c3c1c39442e50f8db877966d43f0 React-jsinspectorcdp: 6346013b2247c6263fbf5199adf4a8751e53bd89
React-jsinspectornetwork: 70e41469565712ad60e11d9c8b8f999b9f7f61eb React-jsinspectornetwork: 26281aa50d49fc1ec93abf981d934698fa95714f
React-jsinspectortracing: eccf9bfa4ec7f130d514f215cfb2222dc3c0e270 React-jsinspectortracing: 55eedf6d57540507570259a778663b90060bbd6e
React-jsitooling: b376a695f5a507627f7934748533b24eed1751ca React-jsitooling: 0e001113fa56d8498aa8ac28437ac0d36348e51a
React-jsitracing: 5c8c3273dda2d95191cc0612fb5e71c4d9018d2a React-jsitracing: b713793eb8a5bbc4d86a84e9d9e5023c0f58cbaf
React-logger: c3e2f8a2e284341205f61eef3d4677ab5a309dfd React-logger: 50fdb9a8236da90c0b1072da5c32ee03aeb5bf28
React-Mapbuffer: 603c18db65844bb81dbe62fee8fcc976eaeb7108 React-Mapbuffer: 9050ee10c19f4f7fca8963d0211b2854d624973e
React-microtasksnativemodule: d77e0c426fce34c23227394c96ca1033b30c813c React-microtasksnativemodule: f775db9e991c6f3b8ccbc02bfcde22770f96e23b
react-native-safe-area-context: 53f796cb6c814661bbe99fbdfd0585d07b996cdd react-native-safe-area-context: 37e680fc4cace3c0030ee46e8987d24f5d3bdab2
React-NativeModulesApple: 1664340b8750d64e0ef3907c5e53d9481f74bcbd React-NativeModulesApple: 8969913947d5b576de4ed371a939455a8daf28aa
React-oscompat: ce47230ed20185e91de62d8c6d139ae61763d09c React-oscompat: ce47230ed20185e91de62d8c6d139ae61763d09c
React-perflogger: b1af3cfb3f095f819b2814910000392a8e17ba9f React-perflogger: 02b010e665772c7dcb859d85d44c1bfc5ac7c0e4
React-performancetimeline: f9ec65b77bcadbc7bd8b47a6f4b4b697da7b1490 React-performancetimeline: 130db956b5a83aa4fb41ddf5ae68da89f3fb1526
React-RCTActionSheet: 0b14875b3963e9124a5a29a45bd1b22df8803916 React-RCTActionSheet: 0b14875b3963e9124a5a29a45bd1b22df8803916
React-RCTAnimation: 60f6eca214a62b9673f64db6df3830cee902b5af React-RCTAnimation: a7b90fd2af7bb9c084428867445a1481a8cb112e
React-RCTAppDelegate: 37734b39bac108af30a0fd9d3e1149ec68b82c28 React-RCTAppDelegate: 3262bedd01263f140ec62b7989f4355f57cec016
React-RCTBlob: 83fbcbd57755caf021787324aac2fe9b028cc264 React-RCTBlob: c17531368702f1ebed5d0ada75a7cf5915072a53
React-RCTFabric: a05cb1df484008db3753c8b4a71e4c6d9f1e43a6 React-RCTFabric: 6409edd8cfdc3133b6cc75636d3b858fdb1d11ea
React-RCTFBReactNativeSpec: d58d7ae9447020bbbac651e3b0674422aba18266 React-RCTFBReactNativeSpec: c004b27b4fa3bd85878ad2cf53de3bbec85da797
React-RCTImage: 47aba3be7c6c64f956b7918ab933769602406aac React-RCTImage: c68078a120d0123f4f07a5ac77bea3bb10242f32
React-RCTLinking: 2dbaa4df2e4523f68baa07936bd8efdfa34d5f31 React-RCTLinking: cf8f9391fe7fe471f96da3a5f0435235eca18c5b
React-RCTNetwork: 1fca7455f9dedf7de2b95bec438da06680f3b000 React-RCTNetwork: ca31f7c879355760c2d9832a06ee35f517938a20
React-RCTRuntime: 17819dd1dfc8613efaf4cbb9d8686baae4a83e5b React-RCTRuntime: a6cf4a1e42754fc87f493e538f2ac6b820e45418
React-RCTSettings: 01bf91c856862354d3d2f642ccb82f3697a4284a React-RCTSettings: e0e140b2ff4bf86d34e9637f6316848fc00be035
React-RCTText: cb576a3797dcb64933613c522296a07eaafc0461 React-RCTText: 75915bace6f7877c03a840cc7b6c622fb62bfa6b
React-RCTVibration: 560af8c086741f3525b8456a482cdbe27f9d098e React-RCTVibration: 25f26b85e5e432bb3c256f8b384f9269e9529f25
React-rendererconsistency: 2dac03f448ff337235fd5820b10f81633328870d React-rendererconsistency: 2dac03f448ff337235fd5820b10f81633328870d
React-renderercss: c5c6b7a15948dd28facca39a18ac269073718490 React-renderercss: 477da167bb96b5ac86d30c5d295412fb853f5453
React-rendererdebug: 3c9d5e1634273f5a24d84cc5669f290ce0bdc812 React-rendererdebug: 2a1798c6f3ef5f22d466df24c33653edbabb5b89
React-RuntimeApple: 887637d1e12ea8262df7d32bc100467df2302613 React-RuntimeApple: 28cf4d8eb18432f6a21abbed7d801ab7f6b6f0b4
React-RuntimeCore: 91f779835dc4f8f84777fe5dd24f1a22f96454e4 React-RuntimeCore: 41bf0fd56a00de5660f222415af49879fa49c4f0
React-runtimeexecutor: 8bb6b738f37b0ada4a6269e6f8ab1133dea0285c React-runtimeexecutor: 1afb774dde3011348e8334be69d2f57a359ea43e
React-RuntimeHermes: 4cb93de9fa8b1cc753d200dbe61a01b9ec5f5562 React-RuntimeHermes: f3b158ea40e8212b1a723a68b4315e7a495c5fc6
React-runtimescheduler: 83dc28f530bfbd2fce84ed13aa7feebdc24e5af7 React-runtimescheduler: 3e1e2bec7300bae512533107d8e54c6e5c63fe0f
React-timing: 03c7217455d2bff459b27a3811be25796b600f47 React-timing: 6fa9883de2e41791e5dc4ec404e5e37f3f50e801
React-utils: 6d46795ae0444ec8a5d9a5f201157b286bf5250a React-utils: 6e2035b53d087927768649a11a26c4e092448e34
ReactAppDependencyProvider: c277c5b231881ad4f00cd59e3aa0671b99d7ebee ReactAppDependencyProvider: 1bcd3527ac0390a1c898c114f81ff954be35ed79
ReactCodegen: 88a1f4643f15841573f833b895bfa2a0c6cb4e7f ReactCodegen: fffa79906f5866f6a5ab5d98480b375191190271
ReactCommon: e6e232202a447d353e5531f2be82f50f47cbaa9a ReactCommon: 08810150b1206cc44aecf5f6ae19af32f29151a8
ReactNativeDependencies: 71ce9c28beb282aa720ea7b46980fff9669f428a ReactNativeDependencies: 71ce9c28beb282aa720ea7b46980fff9669f428a
RNCAsyncStorage: e85a99325df9eb0191a6ee2b2a842644c7eb29f4 RNCAsyncStorage: 3a4f5e2777dae1688b781a487923a08569e27fe4
RNReanimated: 10415bc8396eaeac0d7b2c9a1538eae7e607ec9c RNGestureHandler: e0d0bce5599f6120b7adf90c38d2805e2935795f
RNScreens: dd61bc3a3e6f6901ad833efa411917d44827cf51 RNReanimated: 9c6a550b41de91cf374e60afd79db93a362f1126
RNSVG: 2825ee146e0f6a16221e852299943e4cceef4528 RNScreens: d8d6f1792f6e7ac12b0190d33d8d390efc0c1845
RNWorklets: 9ccdc8112b17af6eee2c85a233891cb80db150ad RNSVG: 31d6639663c249b7d5abc9728dde2041eb2a3c34
RNWorklets: 1b50cb7595142f95e70518196ba247ad7f46a52e
Yoga: 5934998fbeaef7845dbf698f698518695ab4cd1a Yoga: 5934998fbeaef7845dbf698f698518695ab4cd1a
PODFILE CHECKSUM: 4d5c52f9fa870c1d398cf59e37c149f66700c061 PODFILE CHECKSUM: dfe3cc75dee014a0abd367bc9e1bdbab0ba64ee3
COCOAPODS: 1.16.2 COCOAPODS: 1.16.2

View File

@@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 77; objectVersion = 56;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@@ -17,8 +17,6 @@
EB3DAF812F2A4B8E00450593 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB3DAF802F2A4B8D00450593 /* WidgetKit.framework */; }; EB3DAF812F2A4B8E00450593 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB3DAF802F2A4B8D00450593 /* WidgetKit.framework */; };
EB3DAF832F2A4B8E00450593 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB3DAF822F2A4B8E00450593 /* SwiftUI.framework */; }; EB3DAF832F2A4B8E00450593 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB3DAF822F2A4B8E00450593 /* SwiftUI.framework */; };
EB3DAF942F2A4B8F00450593 /* 情绪小组件Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = EB3DAF7F2F2A4B8D00450593 /* 情绪小组件Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; EB3DAF942F2A4B8F00450593 /* 情绪小组件Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = EB3DAF7F2F2A4B8D00450593 /* 情绪小组件Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
EB630B302F30BE2700DC761A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EB630B2F2F30BE2700DC761A /* Assets.xcassets */; };
EB630B312F30BE2700DC761A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EB630B2F2F30BE2700DC761A /* Assets.xcassets */; };
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; }; F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@@ -61,7 +59,6 @@
EB3DAF802F2A4B8D00450593 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; EB3DAF802F2A4B8D00450593 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
EB3DAF822F2A4B8E00450593 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; EB3DAF822F2A4B8E00450593 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
EB3DAF9A2F2A4D0900450593 /* MindfulnessWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindfulnessWidget.swift; sourceTree = "<group>"; }; EB3DAF9A2F2A4D0900450593 /* MindfulnessWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindfulnessWidget.swift; sourceTree = "<group>"; };
EB630B2F2F30BE2700DC761A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = client/AppDelegate.swift; sourceTree = "<group>"; }; F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = client/AppDelegate.swift; sourceTree = "<group>"; };
F11748442D0722820044C1D9 /* client-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "client-Bridging-Header.h"; path = "client/client-Bridging-Header.h"; sourceTree = "<group>"; }; F11748442D0722820044C1D9 /* client-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "client-Bridging-Header.h"; path = "client/client-Bridging-Header.h"; sourceTree = "<group>"; };
@@ -159,7 +156,6 @@
83CBB9F61A601CBA00E9B192 = { 83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
EB630B2F2F30BE2700DC761A /* Assets.xcassets */,
13B07FAE1A68108700A75B9A /* client */, 13B07FAE1A68108700A75B9A /* client */,
832341AE1AAA6A7D00B99B32 /* Libraries */, 832341AE1AAA6A7D00B99B32 /* Libraries */,
EB3DAF842F2A4B8E00450593 /* 情绪小组件 */, EB3DAF842F2A4B8E00450593 /* 情绪小组件 */,
@@ -309,7 +305,6 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */, 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
0BE245B56A79D95AB0A7B4BA /* PrivacyInfo.xcprivacy in Resources */, 0BE245B56A79D95AB0A7B4BA /* PrivacyInfo.xcprivacy in Resources */,
EB630B312F30BE2700DC761A /* Assets.xcassets in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -317,7 +312,6 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
EB630B302F30BE2700DC761A /* Assets.xcassets in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -380,6 +374,8 @@
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle",
); );
name = "[CP] Copy Pods Resources"; name = "[CP] Copy Pods Resources";
outputPaths = ( outputPaths = (
@@ -393,6 +389,8 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
@@ -481,11 +479,9 @@
baseConfigurationReference = FFF632A94C7A551AAA096858 /* Pods-client.debug.xcconfig */; baseConfigurationReference = FFF632A94C7A551AAA096858 /* Pods-client.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = client/client.entitlements; CODE_SIGN_ENTITLEMENTS = client/client.entitlements;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = WS92GPX9H2;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)", "$(inherited)",
@@ -497,14 +493,14 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
"-lc++", "-lc++",
); );
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.damer.mindfulness; PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.client;
PRODUCT_NAME = client; PRODUCT_NAME = client;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
@@ -513,7 +509,7 @@
SWIFT_OBJC_BRIDGING_HEADER = "client/client-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "client/client-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Debug; name = Debug;
@@ -523,27 +519,23 @@
baseConfigurationReference = 3C76CA16D0801CBF0D731C7C /* Pods-client.release.xcconfig */; baseConfigurationReference = 3C76CA16D0801CBF0D731C7C /* Pods-client.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = client/client.entitlements; CODE_SIGN_ENTITLEMENTS = client/client.entitlements;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = WS92GPX9H2;
DWARF_DSYM_FILE_SHOULD_ACCOMPANY_PRODUCT = YES;
INFOPLIST_FILE = client/Info.plist; INFOPLIST_FILE = client/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1; IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
"-lc++", "-lc++",
); );
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.damer.mindfulness; PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.client;
PRODUCT_NAME = client; PRODUCT_NAME = client;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
@@ -551,7 +543,7 @@
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "client/client-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "client/client-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Release; name = Release;
@@ -688,9 +680,8 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = WS92GPX9H2;
ENABLE_USER_SCRIPT_SANDBOXING = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -708,7 +699,7 @@
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.damer.mindfulness.emotionwidget; PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.client.emotionwidget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -722,7 +713,7 @@
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
}; };
@@ -741,9 +732,8 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = WS92GPX9H2;
ENABLE_USER_SCRIPT_SANDBOXING = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -760,7 +750,7 @@
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 1.0.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.damer.mindfulness.emotionwidget; PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.client.emotionwidget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -773,7 +763,7 @@
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
}; };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -19,7 +19,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string> <string>1.0.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
@@ -28,12 +28,12 @@
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>client</string> <string>client</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <string>com.anonymous.client</string>
</array> </array>
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>1</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>12.0</string> <string>12.0</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
@@ -52,7 +52,7 @@
<key>RCTNewArchEnabled</key> <key>RCTNewArchEnabled</key>
<true/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string></string> <string>SplashScreen</string>
<key>UIRequiredDeviceCapabilities</key> <key>UIRequiredDeviceCapabilities</key>
<array> <array>
<string>arm64</string> <string>arm64</string>

View File

@@ -3,6 +3,6 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>aps-environment</key> <key>aps-environment</key>
<string>production</string> <string>development</string>
</dict> </dict>
</plist> </plist>

View File

@@ -1,16 +1,16 @@
export type MockContentItem = { export type MockContentItem = {
id: string; id: string;
text: string; textKey: string;
}; };
/** /**
* 本地 mock 内容(后续接后端时可替换) * 本地 mock 内容(后续接后端时可替换)
*/ */
export const MOCK_CONTENT: MockContentItem[] = [ export const MOCK_CONTENT: MockContentItem[] = [
{ id: 'c1', text: '你已经很努力了,今天也值得被温柔对待。' }, { id: 'c1', textKey: 'mock.c1' },
{ id: 'c2', text: '深呼吸三次,把注意力带回当下。' }, { id: 'c2', textKey: 'mock.c2' },
{ id: 'c3', text: '允许自己慢一点,情绪会像云一样飘过。' }, { id: 'c3', textKey: 'mock.c3' },
{ id: 'c4', text: '你不需要完美,你已经足够好。' }, { id: 'c4', textKey: 'mock.c4' },
{ id: 'c5', text: '把手放在心口,对自己说一句:辛苦了。' } { id: 'c5', textKey: 'mock.c5' }
]; ];

View File

@@ -0,0 +1,22 @@
## 应用文案总表(请在此文件对应的 JSON 中修改)
**单一文案源文件**`client/src/i18n/locales/all.json`
- **English**`all.json``en`
- **繁体中文**`all.json``zh-TW`
> 说明:项目运行时只读取 `all.json`;请不要再改 `locales/en.json`、`locales/zh-TW.json`(它们已不再作为运行时数据源)。
### 快速索引(高频文案)
- **Home**`home.*`
- **Push 提示**`push.*`
- **主题**`theme.*`
- **我的/Profile**`profile.*`
- **收藏**`favorites.*`
- **设置**`settings.*`
- **Onboarding问卷**`onboardingSurvey.steps.*`
- **Onboarding兴趣**`intent.*`
- **Mock 文案**`mock.*`

View File

@@ -3,8 +3,9 @@ import * as Localization from 'expo-localization';
import i18n from 'i18next'; import i18n from 'i18next';
import { initReactI18next } from 'react-i18next'; import { initReactI18next } from 'react-i18next';
import en from './locales/en.json'; // 用 require 避免 TS 的 json module 配置差异导致无法编译
import zhTW from './locales/zh-TW.json'; // eslint-disable-next-line @typescript-eslint/no-var-requires
const all = require('./locales/all.json') as { en: Record<string, unknown>; 'zh-TW': Record<string, unknown> };
/** /**
* 语言码约定: * 语言码约定:
@@ -83,8 +84,8 @@ export async function initI18n(): Promise<void> {
await i18n.use(initReactI18next).init({ await i18n.use(initReactI18next).init({
resources: { resources: {
'zh-TW': { translation: zhTW }, 'zh-TW': { translation: all['zh-TW'] as any },
en: { translation: en }, en: { translation: all.en as any },
}, },
lng: initialLang, lng: initialLang,
fallbackLng: DEFAULT_FALLBACK_LANGUAGE, fallbackLng: DEFAULT_FALLBACK_LANGUAGE,

View File

@@ -0,0 +1,314 @@
{
"en": {
"common": {
"ok": "OK",
"cancel": "Cancel",
"error": "Error",
"openLinkError": "Cannot open link",
"back": "Back",
"close": "Close"
},
"onboarding": {
"title": "Welcome",
"progress": "{{current}}/{{total}}",
"next": "Next",
"skip": "Skip",
"skipAll": "Skip onboarding",
"q1Title": "How are you feeling lately?",
"q1Desc": "No right or wrong. You can skip and adjust later.",
"q2Title": "What kind of support do you want?",
"q2Desc": "For example: gentle reminders, mindfulness, emotional support.",
"q3Title": "When do you need comfort the most?",
"q3Desc": "Morning, afternoon, late night, or specific moments.",
"q4Title": "A gentle sentence for yourself",
"q4Desc": "You can skip. Well stay with you along the way."
},
"onboardingSurvey": {
"steps": {
"name": { "title": "What should I call you?" },
"status": {
"title": "Your current stage?",
"options": {
"pregnant": "Pregnant / preparing for motherhood",
"has_kids": "Already have kids",
"no_fill": "Prefer not to say"
}
},
"emotion": {
"title": "How are you feeling right now?",
"options": {
"happy": "Happy / satisfied",
"calm": "Calm / grounded",
"stressed": "Stressed / overwhelmed",
"low": "Down / low mood"
}
},
"influence": {
"title": "What has been affecting you lately?",
"options": {
"family": "Family & kids",
"work": "Work or study",
"relationship": "Intimate relationship",
"friends": "Friends & social life",
"health": "Mental & physical health"
}
},
"support": {
"title": "What support do you need most?",
"options": {
"emotional": "Emotional support",
"parenting": "Parenting stress",
"self_worth": "Self-worth",
"anxiety": "Anxiety relief",
"balance": "Rest & balance"
}
},
"reminder": { "title": "How many reminders do you want per day?" }
}
},
"intent": {
"title": "What kind of help do you want?",
"love": "Love",
"life": "Life",
"travel": "Travel",
"work": "Career"
},
"push": {
"title": "Notifications",
"cardTitle": "Turn on gentle reminders",
"cardDesc": "Well send a short mindful phrase when you may need it. You can change this anytime in Settings.",
"enable": "Enable",
"later": "Later",
"loading": "Working…",
"errorTitle": "Notice",
"errorDesc": "Its okay if enabling fails. You can keep using the app."
},
"home": {
"title": "Mindfulness",
"like": "Like",
"dislike": "Dislike",
"favorites": "Favorites",
"settings": "Settings",
"theme": "Theme",
"profile": "Me"
},
"theme": {
"title": "Theme",
"scenery": "Scenery",
"color": "Color"
},
"profile": {
"title": "Me",
"favorites": "My Likes",
"widget": "Widget",
"dailyReminder": "Daily Reminder",
"privacy": "Privacy Policy",
"terms": "Terms of Use",
"language": "Language",
"todoTitle": "Notice",
"todoDesc": "This feature is a placeholder for this iteration."
},
"dailyReminder": {
"title": "Daily Reminder",
"timesUnit": "times",
"pushLabel": "Push Reminder",
"ok": "Ok",
"minus": "Decrease",
"plus": "Increase"
},
"widget": {
"lockScreen": "Lock Screen Widget",
"homeScreen": "Home Screen Widget",
"previewDate": "Thu, Jan 29",
"previewQuote": "Im proud of who I am, even while becoming who I want to be."
},
"favorites": {
"title": "Favorites",
"empty": "No favorites yet.",
"unknownText": "This quote is no longer available."
},
"settings": {
"title": "Settings",
"language": "Language",
"version": "Version",
"widgetTitle": "iOS Widget",
"widgetDesc": "Put gentle reminders on your home screen: long-press → tap “+” → search “Mindfulness” → add a size you like."
},
"consent": {
"title": "You Are Perfect.",
"subtitle": "Everything Will Be Better.",
"agree": "Agree & Continue",
"privacy": "Privacy Policy",
"terms": "Terms of Use"
},
"permissions": {
"notificationsDenied": "Notifications are denied. Please enable them in Settings."
},
"language": {
"zhTW": "繁體中文",
"en": "English"
},
"mock": {
"c1": "Youve been trying your best. You deserve kindness today.",
"c2": "Take three deep breaths and return to the present moment.",
"c3": "Its okay to slow down. Emotions pass like clouds.",
"c4": "You dont need to be perfect. You are enough.",
"c5": "Place a hand on your heart and say: You did well today."
}
},
"zh-TW": {
"common": {
"ok": "確定",
"cancel": "取消",
"back": "返回",
"close": "關閉"
},
"onboarding": {
"title": "歡迎",
"progress": "{{current}}/{{total}}",
"next": "下一步",
"skip": "跳過",
"skipAll": "跳過整個引導",
"q1Title": "你最近的感受更接近哪一種?",
"q1Desc": "沒有對錯,你可以跳過,之後也能慢慢調整。",
"q2Title": "你更希望獲得哪種支持?",
"q2Desc": "例如:溫柔提醒、正念練習、情緒陪伴。",
"q3Title": "你通常在什麼時候最需要被安慰?",
"q3Desc": "例如:清晨、午后、深夜,或某些特定時刻。",
"q4Title": "給自己一句溫柔的話",
"q4Desc": "你可以直接跳過,我們會在之後繼續陪你。"
},
"onboardingSurvey": {
"steps": {
"name": { "title": "我可以怎麼稱呼你?" },
"status": {
"title": "媽媽的狀態?",
"options": {
"pregnant": "懷孕中/準備成為媽媽",
"has_kids": "已經有孩子",
"no_fill": "不想填寫"
}
},
"emotion": {
"title": "當下情緒狀態?",
"options": {
"happy": "愉悅、滿足",
"calm": "平靜、安穩",
"stressed": "被壓得有點喘不過氣",
"low": "情緒低落"
}
},
"influence": {
"title": "是什麼影響了你最近的感受?",
"options": {
"family": "家庭與孩子",
"work": "工作或學習",
"relationship": "親密關係",
"friends": "朋友與人際",
"health": "身心健康"
}
},
"support": {
"title": "最需要什麼支持?",
"options": {
"emotional": "情緒支持",
"parenting": "育兒壓力",
"self_worth": "自我價值",
"anxiety": "焦慮舒緩",
"balance": "休息與平衡"
}
},
"reminder": { "title": "你需要每天幾次提醒?" }
}
},
"intent": {
"title": "你希望得到什麼幫助?",
"love": "愛情",
"life": "生活",
"travel": "旅遊",
"work": "職場"
},
"push": {
"title": "通知",
"cardTitle": "開啟溫柔提醒",
"cardDesc": "我們會在你需要的時候,送上一句正念短句或溫柔提醒(可隨時在設定中調整)。",
"enable": "立即開啟",
"later": "稍後",
"loading": "處理中…",
"errorTitle": "提示",
"errorDesc": "開啟失敗也沒關係,你仍然可以繼續使用應用。"
},
"home": {
"title": "正念",
"like": "喜歡",
"dislike": "不喜歡",
"favorites": "收藏",
"settings": "設定",
"theme": "主題",
"profile": "我的"
},
"theme": {
"title": "主題",
"scenery": "風景",
"color": "顏色"
},
"profile": {
"title": "我的",
"favorites": "我的喜歡",
"widget": "小工具",
"dailyReminder": "每日提醒",
"privacy": "隱私政策",
"terms": "使用條款",
"language": "語言",
"todoTitle": "提示",
"todoDesc": "此功能本期先占位,後續迭代補齊。"
},
"dailyReminder": {
"title": "每日提醒",
"timesUnit": "次",
"pushLabel": "推送提醒",
"ok": "確定",
"minus": "減少次數",
"plus": "增加次數"
},
"widget": {
"lockScreen": "鎖屏小工具",
"homeScreen": "桌面小工具",
"previewDate": "1月29日週四 · 已至臘月十一",
"previewQuote": "我也對現在的自己感到滿意,即使我仍在努力成為想成為的人。"
},
"favorites": {
"title": "收藏夾",
"empty": "這裡還沒有收藏內容。",
"unknownText": "這條文案暫時無法顯示。"
},
"settings": {
"title": "設定",
"language": "語言",
"version": "版本",
"widgetTitle": "iOS 小工具",
"widgetDesc": "把溫柔提醒放到桌面上:長按主畫面 → 點「+」 → 搜尋「正念」 → 添加你喜歡的尺寸。"
},
"consent": {
"agree": "同意並繼續",
"privacy": "隱私協議",
"terms": "用戶使用協議"
},
"permissions": {
"notificationsDenied": "系統權限已被拒絕,請前往手機設定開啟通知。"
},
"language": {
"zhTW": "繁體中文",
"en": "English"
},
"mock": {
"c1": "你已經很努力了,今天也值得被溫柔對待。",
"c2": "深呼吸三次,把注意力帶回當下。",
"c3": "允許自己慢一點,情緒會像雲一樣飄過。",
"c4": "你不需要完美,你已經足夠好。",
"c5": "把手放在心口,對自己說一句:辛苦了。"
}
}
}

View File

@@ -35,13 +35,14 @@ function withTimeout(ms: number): AbortController {
export async function fetchRecoFeed(req: RecoRequest): Promise<RecoEngineResult> { export async function fetchRecoFeed(req: RecoRequest): Promise<RecoEngineResult> {
const controller = withTimeout(12_000); const controller = withTimeout(12_000);
const url = `${API_BASE_URL}/v1/reco/feed`; const url = `${API_BASE_URL}/v1/reco/feed`;
const acceptLanguage = i18n.language?.toLowerCase().startsWith('zh') ? 'tc' : 'en';
const res = await fetch(url, { const res = await fetch(url, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
// 让后端做 locale 选择(目前后端只区分 en/tc // 让后端做 locale 选择(目前后端只区分 en/tc
'Accept-Language': i18n.language || 'en', 'Accept-Language': acceptLanguage,
}, },
body: JSON.stringify({ body: JSON.stringify({
k: req.k, k: req.k,

View File

@@ -42,6 +42,12 @@ export type RecoFeedCacheItem = {
export type RecoFeedCache = { export type RecoFeedCache = {
saved_at: string; // ISO8601 saved_at: string; // ISO8601
/**
* 缓存文案的语言(后端目前只区分 en / tc
* - en: English
* - tc: 繁体中文
*/
lang?: 'en' | 'tc';
items: RecoFeedCacheItem[]; items: RecoFeedCacheItem[];
meta?: Record<string, unknown>; meta?: Record<string, unknown>;
}; };
@@ -107,6 +113,10 @@ export async function setReaction(contentId: string, reaction: Reaction): Promis
export type FavoriteItem = { export type FavoriteItem = {
favId: string; // 唯一标识,支持重复点赞同一文案 favId: string; // 唯一标识,支持重复点赞同一文案
id: string; id: string;
/**
* 收藏时的文案快照(强烈建议写入,避免后续 cache 覆盖导致无法还原文案)
*/
text?: string;
date: string; date: string;
themeMode: ThemeMode; themeMode: ThemeMode;
background: string; // 颜色值或图片路径 background: string; // 颜色值或图片路径
@@ -120,7 +130,7 @@ export async function addFavorite(item: FavoriteItem): Promise<void> {
const list = await getFavorites(); const list = await getFavorites();
// 允许重复点赞,不再根据 id 去重 // 允许重复点赞,不再根据 id 去重
const newList = [item, ...list]; const newList = [item, ...list];
console.log('Adding to favorites, new list size:', newList.length); console.log('Adding to favorites:', JSON.stringify(item));
await setJson(KEY_FAVORITES_ITEMS, newList); await setJson(KEY_FAVORITES_ITEMS, newList);
} }

View File

@@ -1,20 +0,0 @@
.venv
__pycache__/
*.pyc
*.pyo
*.pyd
.pytest_cache/
.mypy_cache/
.ruff_cache/
# 本地/测试数据
.test.db
*.db
# 测试与开发脚本(按需移除)
tests/
.env*
# Git 元数据
.git/
.gitignore

View File

@@ -1,27 +0,0 @@
FROM python:3.11-slim
# 运行时基础环境
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
# 系统依赖(按需扩展;多数依赖为纯 Python
RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential curl \
&& rm -rf /var/lib/apt/lists/*
# 先复制依赖清单以利用 Docker layer cache
COPY requirements.txt /app/requirements.txt
RUN python -m pip install -U pip \
&& pip install --no-cache-dir -r /app/requirements.txt
# 复制后端代码与迁移配置
COPY app /app/app
COPY alembic /app/alembic
COPY alembic.ini /app/alembic.ini
EXPOSE 8000
# 生产镜像默认不开启 reload
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -46,5 +46,5 @@
## 6. 风险与注意事项 ## 6. 风险与注意事项
- **Expo Go 不支持**:必须用 Xcode 运行预构建后的 App或后续 EAS Build - **Expo Go 不支持**:必须用 Xcode 运行预构建后的 App或后续 EAS Build
- **Bundle Identifier**:当前工程已统一为主 App `com.damer.mindfulness`Widget Extension `com.damer.mindfulness.emotionwidget`;上线前仍需在 Apple Developer / App Store Connect 侧确保 App ID、证书签名匹配 - **Bundle Identifier**:当前 `client/app.json` 使用 `com.anonymous.client`,仅适合本地验证;上线前需替换为真实 bundle id并同步证书/签名

View File

@@ -65,7 +65,7 @@ npx expo prebuild -p ios
- **排查** - **排查**
- 桌面搜索不到:通常是没有 Run 安装过主 App或 Widget target 未加入编译 - 桌面搜索不到:通常是没有 Run 安装过主 App或 Widget target 未加入编译
- 点击不跳转:确认 Widget 里 `widgetURL``client:///(app)/home`,且 `app.json``scheme``client` - 点击不跳转:确认 Widget 里 `widgetURL``client:///(app)/home`,且 `app.json``scheme``client`
- 仍然搜不到:检查 Widget Extension 的 Bundle Identifier 是否有效(本仓库已修正为 `com.damer.mindfulness.emotionwidget`),然后 Clean + 重新安装 App - 仍然搜不到:检查 Widget Extension 的 Bundle Identifier 是否有效(本仓库已修正为 `com.anonymous.client.emotionwidget`),然后 Clean + 重新安装 App
## 6. 文档补充(可选但建议) ## 6. 文档补充(可选但建议)

View File

@@ -36,9 +36,6 @@
- 安全:真实 IP/账号/密码/Token 不写入仓库,仅提供 `.env.example` 结构 - 安全:真实 IP/账号/密码/Token 不写入仓库,仅提供 `.env.example` 结构
- **阶段产物** - **阶段产物**
- `spec_kit/Project Bootstrap/spec.md` - `spec_kit/Project Bootstrap/spec.md`
- **近期变更**
- 新增后端镜像构建基础:`server/Dockerfile``server/.dockerignore`
- 新增后端镜像打包与推送工作流:`.gitea/workflows/server-build.yml`(自动递增 semver tag 并推送到 Docker Hub
## Onboarding App Shell ## Onboarding App Shell
@@ -57,10 +54,6 @@
- `spec_kit/iOS Widget/spec.md` - `spec_kit/iOS Widget/spec.md`
- `spec_kit/iOS Widget/plan.md` - `spec_kit/iOS Widget/plan.md`
- `spec_kit/iOS Widget/tasks.md` - `spec_kit/iOS Widget/tasks.md`
- **近期变更**
- iOS 主 App Bundle ID 已统一为 `com.damer.mindfulness`Widget Extension 为 `com.damer.mindfulness.emotionwidget`
- iOS 构建号已提升到 `2`,并将 `client/ios/client/Info.plist` 改为自动跟随 `MARKETING_VERSION` / `CURRENT_PROJECT_VERSION`
- 推送 entitlements 的 `aps-environment` 已切到 `production`(用于 TestFlight/线上包)
## Splash Consent ## Splash Consent
@@ -89,6 +82,7 @@
- 新增 `SheetModal``ThemeModal``ProfileModal` 并在 Home 中接入 - 新增 `SheetModal``ThemeModal``ProfileModal` 并在 Home 中接入
- 新增 `DailyReminderModal` 并从个人主页弹窗打开 - 新增 `DailyReminderModal` 并从个人主页弹窗打开
- Home右上角 icon 按钮、主题切换背景、喜欢/讨厌 icon + 动效 - Home右上角 icon 按钮、主题切换背景、喜欢/讨厌 icon + 动效
- **修复**:收藏(“我的喜欢”)不展示后端推荐文案的问题——收藏时持久化写入 `text` 快照,并在 `ProfileModal` 的 Favorites 页面同时兼容 Mock 与推荐缓存内容回填
## User Profile Scoring ## User Profile Scoring