From 4b739dd194b1f38573c5bc0f2c3c614ca3608ad5 Mon Sep 17 00:00:00 2001 From: 1 <1> Date: Mon, 2 Feb 2026 17:17:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=88=86=E5=8F=91=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=B0=8F=E7=BB=84=E4=BB=B6=E8=83=8C=E6=99=AF=E9=80=82=E9=85=8D?= =?UTF-8?q?=E4=B8=8E=E7=82=B9=E8=B5=9E=E5=9B=BE=E6=A0=87=E4=BA=A4=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/app.json | 8 +- client/app/(onboarding)/onboarding.tsx | 3 +- client/app/index.tsx | 16 +- client/assets/images/home/like_filled.svg | 2 +- client/assets/images/icon/like_icon.svg | 2 +- client/ios/情绪小组件/EmotionWidget.swift | 227 +++++++++++++++------- 6 files changed, 179 insertions(+), 79 deletions(-) diff --git a/client/app.json b/client/app.json index 00ddbaf..6c4afd0 100644 --- a/client/app.json +++ b/client/app.json @@ -1,11 +1,11 @@ { "expo": { - "name": "Hey Mama", - "slug": "hey-mama", + "name": "client", + "slug": "client", "version": "1.0.0", "orientation": "portrait", "icon": "./assets/images/icon.png", - "scheme": "heymama", + "scheme": "client", "userInterfaceStyle": "automatic", "newArchEnabled": true, "splash": { @@ -15,7 +15,7 @@ }, "ios": { "supportsTablet": true, - "bundleIdentifier": "com.heymama.app" + "bundleIdentifier": "com.anonymous.client" }, "android": { "adaptiveIcon": { diff --git a/client/app/(onboarding)/onboarding.tsx b/client/app/(onboarding)/onboarding.tsx index df0ef33..929ce6e 100644 --- a/client/app/(onboarding)/onboarding.tsx +++ b/client/app/(onboarding)/onboarding.tsx @@ -97,7 +97,8 @@ export default function OnboardingScreen() { } }; - const onSkip = () => { + const onSkip = async () => { + await setOnboardingCompleted(true); router.replace('/(app)/home'); }; diff --git a/client/app/index.tsx b/client/app/index.tsx index e9a5ae3..64ba2ad 100644 --- a/client/app/index.tsx +++ b/client/app/index.tsx @@ -14,9 +14,9 @@ export default function Index() { useEffect(() => { let cancelled = false; (async () => { - // 【完全重置】:清除本地存储的所有数据(收藏、设置、引导状态等) + // 【临时清除数据】:用于测试完整流程 await AsyncStorage.clear(); - console.log('AsyncStorage has been cleared.'); + console.log('AsyncStorage has been cleared for testing.'); // 1. 检查是否同意协议 const consentAccepted = await getConsentAccepted(); @@ -27,9 +27,17 @@ export default function Index() { return; } - // 2. 检查 Onboarding + // 2. 检查 Onboarding 是否已完成 const completed = await getOnboardingCompleted(); - router.replace(completed ? '/(app)/home' : '/(onboarding)/onboarding'); + if (cancelled) return; + + if (completed) { + // 如果已经完成过流程,直接进 Home + router.replace('/(app)/home'); + } else { + // 如果是首次进入(或未完成流程),进入 Onboarding + router.replace('/(onboarding)/onboarding'); + } })(); return () => { cancelled = true; diff --git a/client/assets/images/home/like_filled.svg b/client/assets/images/home/like_filled.svg index cb3c2c1..f8749df 100644 --- a/client/assets/images/home/like_filled.svg +++ b/client/assets/images/home/like_filled.svg @@ -1,4 +1,4 @@ - + diff --git a/client/assets/images/icon/like_icon.svg b/client/assets/images/icon/like_icon.svg index bd94d57..a52fe32 100644 --- a/client/assets/images/icon/like_icon.svg +++ b/client/assets/images/icon/like_icon.svg @@ -1,3 +1,3 @@ - + diff --git a/client/ios/情绪小组件/EmotionWidget.swift b/client/ios/情绪小组件/EmotionWidget.swift index 510cc41..3376cfa 100644 --- a/client/ios/情绪小组件/EmotionWidget.swift +++ b/client/ios/情绪小组件/EmotionWidget.swift @@ -1,54 +1,28 @@ import WidgetKit import SwiftUI -// V2:纯色背景 + 随机文案小组件(Small/Medium/Large + 点击跳转 Home) +// V1:写死文案的小组件(Small/Medium/Large + 点击跳转 Home) struct EmotionProvider: TimelineProvider { - private let quotes = [ - "你已经很努力了,今天也值得被温柔对待。", - "轻轻呼吸,感受当下的每一刻。", - "所有的压力,都会在深呼吸中慢慢消散。", - "给生活一点留白,给自己一点温柔。", - "不要走得太快,等一等落下的灵魂。", - "世界虽嘈杂,但你可以拥有一颗宁静的心。", - "每一个瞬间,都是生命最好的安排。", - "抱抱自己,辛苦了,亲爱的。", - "慢一点也没关系,只要你在前行。", - "今天,你对自己微笑了吗?", - "愿你历经山河,仍觉得人间值得。", - "心简单,世界就简单;心平顺,生活就平顺。", - "即使生活偶尔晦暗,你也要成为自己的光。", - "别让琐事挤走生活的快乐,别让压力消磨奋斗的激情。" - ] - func placeholder(in context: Context) -> EmotionEntry { - EmotionEntry(date: Date(), text: quotes[0]) + EmotionEntry(date: Date()) } func getSnapshot(in context: Context, completion: @escaping (EmotionEntry) -> ()) { - let entry = EmotionEntry(date: Date(), text: quotes.randomElement() ?? quotes[0]) - completion(entry) + completion(EmotionEntry(date: Date())) } func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { - var entries: [EmotionEntry] = [] - let currentDate = Date() - - // 生成未来 24 小时的 6 个条目,每 4 小时更换一次随机文案 - for hourOffset in 0..<6 { - let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset * 4, to: currentDate)! - let entry = EmotionEntry(date: entryDate, text: quotes.randomElement() ?? quotes[0]) - entries.append(entry) - } - - let timeline = Timeline(entries: entries, policy: .atEnd) - completion(timeline) + // V1:内容写死,不做数据更新;给一个较长的刷新间隔(系统仍可能自行调度) + let entry = EmotionEntry(date: Date()) + let nextUpdate = Calendar.current.date(byAdding: .day, value: 7, to: Date()) + ?? Date().addingTimeInterval(60 * 60 * 24 * 7) + completion(Timeline(entries: [entry], policy: .after(nextUpdate))) } } struct EmotionEntry: TimelineEntry { let date: Date - let text: String } struct EmotionWidgetView: View { @@ -56,37 +30,160 @@ struct EmotionWidgetView: View { @Environment(\.widgetFamily) var family private let title = "正念" + private let text = "你已经很努力了,今天也值得被温柔对待。" private let deepLink = URL(string: "client:///(app)/home") - - // 背景色 #F7D9BF - private let backgroundColor = Color(red: 247/255, green: 217/255, blue: 191/255) - // 文本颜色(深咖色,适合搭配浅橘色背景) - private let textColor = Color(red: 74/255, green: 52/255, blue: 40/255) var body: some View { - VStack(alignment: .center, spacing: 0) { - Spacer(minLength: 0) - - Text(entry.text) - .font(.system(size: family == .systemSmall ? 17 : 20, weight: .medium)) - .foregroundColor(textColor) - .lineSpacing(6) - .multilineTextAlignment(.center) - .minimumScaleFactor(0.7) - .fixedSize(horizontal: false, vertical: true) - - Spacer(minLength: 0) - - if family != .systemSmall { - Text("Hey Mama") - .font(.system(size: 10, weight: .semibold)) - .foregroundColor(textColor.opacity(0.3)) - .padding(.bottom, 4) - } + switch family { + case .systemSmall: + smallView() + case .systemMedium: + mediumView() + case .systemLarge: + largeView() + default: + smallView() + } + } + + // 统一的“卡片背景”风格(iOS 15 兼容) + private func cardBackground(colors: [Color]) -> some View { + ZStack { + LinearGradient( + colors: colors, + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + // 轻微光斑,增加层次 + RadialGradient( + gradient: Gradient(colors: [Color.white.opacity(0.16), Color.white.opacity(0.0)]), + center: .topTrailing, + startRadius: 10, + endRadius: 180 + ) + } + .overlay( + RoundedRectangle(cornerRadius: 18, style: .continuous) + .stroke(Color.white.opacity(0.14), lineWidth: 1) + ) + .cornerRadius(18) + } + + private func chip(_ text: String) -> some View { + Text(text) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(Color.white.opacity(0.9)) + .padding(.horizontal, 10) + .padding(.vertical, 6) + .background(Color.white.opacity(0.14)) + .cornerRadius(999) + } + + private func smallView() -> some View { + ZStack { + cardBackground(colors: [ + Color(red: 0.06, green: 0.08, blue: 0.12), + Color(red: 0.13, green: 0.16, blue: 0.22), + ]) + + VStack(alignment: .leading, spacing: 10) { + HStack { + chip(title) + Spacer(minLength: 0) + } + + Text(text) + .font(.system(size: 15, weight: .semibold)) + .foregroundColor(Color.white.opacity(0.92)) + .lineSpacing(2) + .lineLimit(4) + + Spacer(minLength: 0) + + Text("点我回到 App") + .font(.system(size: 11, weight: .medium)) + .foregroundColor(Color.white.opacity(0.65)) + } + .padding(14) + } + .widgetURL(deepLink) + } + + private func mediumView() -> some View { + ZStack { + cardBackground(colors: [ + Color(red: 0.06, green: 0.08, blue: 0.12), + Color(red: 0.09, green: 0.11, blue: 0.17), + ]) + + HStack(alignment: .top, spacing: 14) { + VStack(alignment: .leading, spacing: 10) { + chip(title) + Text(text) + .font(.system(size: 17, weight: .semibold)) + .foregroundColor(Color.white.opacity(0.92)) + .lineSpacing(3) + .lineLimit(5) + + Spacer(minLength: 0) + + Text("轻轻呼吸,回到当下") + .font(.system(size: 12, weight: .medium)) + .foregroundColor(Color.white.opacity(0.7)) + } + + // 右侧装饰区:让版面更饱满 + VStack(alignment: .trailing, spacing: 8) { + Text(entry.date, style: .time) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(Color.white.opacity(0.8)) + Spacer(minLength: 0) + Text("今日") + .font(.system(size: 28, weight: .bold)) + .foregroundColor(Color.white.opacity(0.12)) + } + } + .padding(16) + } + .widgetURL(deepLink) + } + + private func largeView() -> some View { + ZStack { + cardBackground(colors: [ + Color(red: 0.06, green: 0.08, blue: 0.12), + Color(red: 0.14, green: 0.18, blue: 0.28), + ]) + + VStack(alignment: .leading, spacing: 14) { + HStack { + chip(title) + Spacer(minLength: 0) + Text(entry.date, style: .time) + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(Color.white.opacity(0.78)) + } + + Text(text) + .font(.system(size: 20, weight: .semibold)) + .foregroundColor(Color.white.opacity(0.92)) + .lineSpacing(4) + .lineLimit(8) + + Spacer(minLength: 0) + + HStack { + Text("点我回到 Home") + .font(.system(size: 12, weight: .medium)) + .foregroundColor(Color.white.opacity(0.7)) + Spacer(minLength: 0) + Text("🌿") + .font(.system(size: 18)) + .opacity(0.9) + } + } + .padding(18) } - .padding(family == .systemSmall ? 16 : 24) - .frame(maxWidth: .infinity, maxHeight: .infinity) // 强制撑开容器 - .background(backgroundColor) // 将背景色直接应用到容器上 .widgetURL(deepLink) } } @@ -97,13 +194,7 @@ struct EmotionWidget: Widget { var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: EmotionProvider()) { entry in - if #available(iOS 17.0, *) { - EmotionWidgetView(entry: entry) - .containerBackground(Color(red: 247/255, green: 217/255, blue: 191/255), for: .widget) - } else { - EmotionWidgetView(entry: entry) - .background(Color(red: 247/255, green: 217/255, blue: 191/255)) - } + EmotionWidgetView(entry: entry) } .configurationDisplayName("情绪小组件") .description("一段温柔提醒,陪你回到当下。")