12 KiB
正念 APP(客户端)
本目录用于存放「正念 APP」客户端工程,定位是面向宝妈人群的情绪价值与正念练习支持应用。
功能概览(一期)
- APP-PUSH 推送:定时推送情绪文字(固定时间 / 用户自定义时间),推送内容可由后端配置更新
- iOS 小组件(Widget):展示当前情绪文字与背景图/渐变色,支持多尺寸与可配置刷新频率
- 情绪卡片滑动:卡片列表左右滑动切换(类似 Tinder),收藏/分享可作为后续迭代
技术栈
- React Native + Expo
- TypeScript
- React Navigation:页面路由
- 状态管理:Zustand(或 Redux Toolkit)
- 网络请求:Axios(或 Fetch)
- 推送:Expo Notifications
- 环境区分:Expo App Config +
.env.dev/.env.prod - iOS 打包:EAS Build
开发环境要求
- Node.js:建议使用 20+(LTS)
- 包管理器:pnpm
- Expo CLI:推荐通过
npx expo使用(避免全局版本漂移) - iOS 真机调试:macOS + Xcode(如需)
本项目已内置
postinstall补丁脚本用于兼容(安装依赖后会自动修复),但仍建议升级到 Node 20+。
快速开始
说明:本仓库已包含 Expo 工程文件;如果你只是想把客户端跑起来,从「安装依赖」开始即可。
1)初始化工程(若尚未创建)
在 client/ 目录下创建 Expo 工程(示例):
cd client
npx create-expo-app@latest .
执行后按提示选择模板即可;如果你希望固定使用 TypeScript 模板,也可以在创建时选择对应模板(以实际 CLI 提示为准)。
2)安装依赖
cd client
pnpm install
3)配置环境变量(dev / prod)
按根目录约定,客户端使用 .env.dev 与 .env.prod 区分环境;.env 文件不提交到 Git,仓库内应提供 .env.example 作为模板。
本地开发如何注入环境变量(推荐)
Expo 在本地开发时会自动读取当前目录下的 .env / .env.local(仅以 EXPO_PUBLIC_ 前缀变量注入到客户端运行时)。推荐做法:
cd client
cp .env.example .env.local
# 然后按需修改 .env.local
pnpm start
修改
.env*后需要重启pnpm start才会生效。
本地开发如何区分 dev / prod(可选)
如果你希望坚持使用 .env.dev / .env.prod 文件名,可以在启动前导出环境变量(zsh 示例):
cd client
set -a
source .env.dev
set +a
pnpm start
注意:
source方式要求.env.dev内容是合法的 shell 格式(例如KEY=value),不要带export以外的复杂语法;值包含空格时需要加引号。
必填环境变量
EXPO_PUBLIC_API_BASE_URL:后端 API 基地址- 本地:
http://localhost:8000 - 真机联调:改为电脑局域网 IP(例如
http://192.168.1.10:8000)
- 本地:
EXPO_PUBLIC_ENV:环境标识(建议dev/prod)
可选环境变量
EXPO_PUBLIC_DEFAULT_LANGUAGE:默认语言策略auto:优先设备语言(默认)zh-CN/en/es/pt/zh-TW:固定默认语言(仍允许用户在设置中切换并持久化)
建议字段(示例):
EXPO_PUBLIC_API_BASE_URL=http://localhost:8000
EXPO_PUBLIC_ENV=dev
EXPO_PUBLIC_DEFAULT_LANGUAGE=auto
提示:在 Expo 中建议使用
EXPO_PUBLIC_前缀,方便在运行时读取并支持 EAS 构建注入。
4)启动开发服务器
cd client
pnpm start
启动后你会看到终端输出的二维码(QR Code)与可用命令提示。
5)运行到 iOS / Android / 真机 / Web
- iOS 模拟器(macOS + Xcode):
- 方式一:在
pnpm start的终端里按i - 方式二:直接运行:
- 方式一:在
cd client
pnpm ios
- Android 模拟器(Android Studio):
- 方式一:在
pnpm start的终端里按a - 方式二:直接运行:
- 方式一:在
cd client
pnpm android
-
真机(推荐使用 Expo Go):
- 手机安装 Expo Go
- 确保手机与电脑在同一局域网
- 运行
pnpm start后,用 Expo Go 扫描终端二维码即可打开 App
-
Web(可选):
cd client
pnpm web
常用命令(脚本):
pnpm ios
pnpm android
pnpm web
6)常见问题
- 真机打不开 / 扫码后卡住:
- 检查手机与电脑是否同一 Wi-Fi
- 尝试重启
pnpm start -- --clear清缓存
- 启动时提示
Networking has been disabled/Network connection is unreliable:- 这是 Expo CLI 的网络探测/请求失败导致的,按下面方式跳过网络请求即可:
cd client
EXPO_OFFLINE=1 pnpm start
- 如果你本机设置了代理(例如
HTTP_PROXY/ALL_PROXY),也可以临时关闭代理后再启动:
cd client
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY
pnpm start
- Watchman 提示 Recrawl(不影响运行,但建议处理):
- 按提示执行一次即可清除警告:
watchman watch-del '/Users/jojo/Desktop/lxy/gitea/mindfulness/client' ; watchman watch-project '/Users/jojo/Desktop/lxy/gitea/mindfulness/client'
- 联调时请求打到 localhost:
- 真机上
localhost指向手机自身,请把EXPO_PUBLIC_API_BASE_URL改成电脑的局域网 IP(例如http://192.168.1.10:8000),或使用内网穿透方案
- 真机上
与后端联调
- 后端基座:FastAPI(详见
server/README.md) - API Base URL:通过
EXPO_PUBLIC_API_BASE_URL配置 - 本地真机注意:如果在手机上调试,请将
localhost替换为电脑局域网 IP,或使用内网穿透方案
多语言(i18n):CN / EN / ES / PT / TC
语言码约定
- CN(简体中文):
zh-CN - EN(英语):
en - ES(西班牙语):
es - PT(葡萄牙语):
pt - TC(繁体中文):
zh-TW
推荐选型(Expo/RN 常用组合)
- 文案管理:
i18next+react-i18next - 系统语言读取:
expo-localization
建议目录结构
src/
└── i18n/
├── index.ts # i18n 初始化(默认语言、回退语言、资源注册)
├── locales/
│ ├── zh-CN.json
│ ├── en.json
│ ├── es.json
│ ├── pt.json
│ └── zh-TW.json
└── types.ts #(可选)语言码与 key 的类型定义
约定与最佳实践
- key 规则:使用稳定的点分层 key,例如
common.ok、push.permissionTitle、cards.swipeHint - 默认与回退:默认优先跟随用户当前设备语言(在支持列表内时生效);设备语言不支持时回退到
zh-CN(或团队指定默认) - 插值与复数:优先使用 i18next 的插值(例如
{{name}}),避免在代码里拼字符串 - 动态切换:提供“语言设置”入口允许手动切换;切换后持久化(例如 AsyncStorage),后续启动优先生效,并立即刷新文案
- 语言优先级:用户设置(若存在)> 设备语言(在支持列表内)> 默认回退(
zh-CN或团队指定默认) - 设置入口建议:设置页 -> 语言(或 设置页 -> 通用 -> 语言)
- 与服务端一致性:若后端也需要多语言(推送文案等),建议统一语言码(
zh-CN/en/es/pt/zh-TW)并在接口里明确lang
推送(Expo Notifications)
客户端侧通常需要:
- 申请通知权限
- 获取 Expo Push Token
- 将 Token 上报后端(用于定时推送任务)
后端侧通常需要:
- 保存用户 Push Token
- 按策略触发推送(固定时间 / 用户自定义时间)
- 支持后台配置推送文案并更新
具体实现以客户端工程代码为准;当代码接入后,建议在 README 补充「Token 上报接口」与「字段定义」。
iOS 小组件(Widget)
小组件通常需要:
- 数据源:来自本地缓存或后端拉取(需要设计刷新策略与缓存)
- 展示内容:当前情绪文字 + 背景图/渐变
- 尺寸:小 / 中 / 大
- 刷新频率:如每小时/每天(iOS 对频率有系统限制,以实际效果为准)
若你计划使用 Expo 的相关能力,请在工程中明确选型与实现路径,并补充到本 README。
EAS Build(iOS 打包)
建议按根目录约定区分 dev/prod:
- dev:
com.damer.mindfulness.dev - prod:
com.damer.mindfulness
常见流程(示例):
cd client
eas login
eas build --platform ios --profile development
实际 profile、证书、bundle id、环境变量注入方式以工程内
eas.json/ app config 为准。
目录结构(建议)
当客户端工程落地后,建议使用更标准、可扩展的目录结构(Expo + TypeScript 常见组织方式):
client/
├── app/ #(推荐)expo-router 路由目录(若使用 expo-router)
│ ├── (tabs)/ # Tabs 分组(可选)
│ ├── _layout.tsx # 根布局
│ └── index.tsx # 首页
├── src/ # 业务源码(与路由/平台代码解耦)
│ ├── components/ # 通用 UI 组件
│ ├── features/ # 按功能域拆分(推荐)
│ │ ├── push/ # 推送相关(权限、token、上报等)
│ │ ├── widget/ # 小组件数据与样式相关
│ │ └── cards/ # 情绪卡片滑动相关
│ ├── hooks/ # 自定义 hooks
│ ├── navigation/ #(若不用 expo-router)React Navigation 配置
│ ├── screens/ #(若不用 expo-router)页面
│ ├── services/ # API 封装(request、接口定义)
│ ├── store/ # 状态管理(Zustand/RTK)
│ ├── utils/ # 工具函数(时间、格式化、校验等)
│ ├── constants/ # 常量与配置(主题、枚举等)
│ └── types/ # 全局类型声明
├── assets/ # 静态资源(图片/字体/音频等)
├── app.json / app.config.ts # Expo 配置(环境区分可在此处理)
├── eas.json # EAS Build 配置(如使用)
├── package.json
└── README.md
说明:如果你不使用
expo-router,可以删除app/,并以src/navigation/+src/screens/作为主路由结构;其余目录保持不变即可。
iOS 小组件开发(V1:写死文案)
重要:Expo Go 不支持 WidgetKit。要做真正的小组件,必须预构建 iOS 工程并用 Xcode 跑。
1)生成 iOS 工程
cd client
npx expo prebuild -p ios --no-install
生成后会得到 client/ios/(Xcode 工程文件都在里面)。
2)在 Xcode 创建 Widget Extension
- 用 Xcode 打开:
client/ios/client.xcworkspace - 菜单:
File -> New -> Target... -> Widget Extension - Target 名称示例:
MindfulnessWidget
3)写死文案与三尺寸布局
仓库里已提供 SwiftUI 代码骨架(写死文案 + Small/Medium/Large + 点击跳转):
client/ios/情绪小组件/EmotionWidget.swift
创建 Widget target 后,把该文件加入到 Widget target 中即可(Target Membership 勾选:情绪小组件)。
4)点击跳转到 Home(Deep Link)
Widget 点击跳转使用:
client:///(app)/home
代码里已通过 widgetURL 设置(见 MindfulnessWidget.swift)。