我们将游戏开发逻辑移植到 Telegram:使用 React 和 RxJS 构建触觉 LifeOS
I’m happy to translate the article for you, but I’ll need the full text of the post (the content you’d like translated). Could you please paste the article’s body here? Once I have the text, I’ll provide a Simplified‑Chinese translation while preserving the source link, markdown formatting, code blocks, and any technical terms.
介绍
我们想要构建 UltyMyLife —— 一个统一的 LifeOS,它就在你花费 90 % 时间的地方:Telegram。不需要 VPN,不需要 Notion,没有摩擦感。
但我们遇到了瓶颈:标准的 Telegram Mini Apps 往往像 2010 年的网站。我们需要那种 “Apple vibe” — 高端、暗色、拥有深邃阴影,最重要的是具备触感。
代码背后的协同效应
| 人员 | 角色 | 备注 |
|---|---|---|
| Dmitriy Spirihin (我) | 系统架构 & 全栈 | 来自 Unity 开发。当你习惯了 UniRx 和游戏循环时,标准的网页开发会显得“纸板感”。我的使命是把游戏开发的架构移植到 React 技术栈中。 |
| Demian Avolstyny | 产品愿景、设计 & Vibe 编码 | “氛围”守护者。专注于美感、触觉反馈以及让应用像是你自身延伸的无缝交互逻辑。 |
在本文中我们将探讨:
- 游戏开发思维如何帮助我们使用 RxJS 构建复杂的状态系统。
- 为什么 Framer Motion 是实现触感 UI 的秘密武器。
- 我们如何利用 AI 将枯燥的日志转化为可操作的、“残酷诚实”的洞察。
问题
市场上充斥着各种追踪器:
- 一千个习惯追踪器。
- 再来一千个健身日志。
- 一百个冥想应用。
问题是什么? 它们彼此碎片化。显示 “我睡得不好” 的数据在其他应用的锻炼计划中毫无影响。冥想孤立存在,未与我的压力水平关联。
我们想要创建 LifeOS —— 一个统一的自我管理系统,运行在我花费 90 % 时间的地方:Telegram。无需 VPN、无需 Notion、无摩擦。
但有个问题:标准的 Telegram WebApp 往往看起来像 2010 年的网页。我想要一种 “Apple 风格” —— 高端、暗色、带有深阴影,最重要的是触感。因此,UltyMyLife 诞生了。
进度计算逻辑(HabitCalendar.js)
// Progress calculation logic in HabitCalendar.js
const dayKey = formatDateKey(new Date(cellYear, cellMonth, day));
let percentNum = 0;
if (Object.keys(AppData.habitsByDate).includes(dayKey)) {
const dayHabits = Array.from(Object.values(AppData.habitsByDate[dayKey]));
// Calculate percentage of completed (v > 0) habits
percentNum = dayHabits.length > 0
? Math.round((dayHabits.filter((v) => v > 0).length / dayHabits.length) * 100)
: 0;
}
// Render cell with dynamic color
{day}
ScrollPicker:击败系统输入
标准的 <select> 或 <input type="date"> 在 Web 应用中显得格格不入。
- 在 iOS 上会弹出系统滚轮。
- 在 Android 上则是一个模态框。
这两者都会瞬间破坏沉浸感。我们编写了自己的 ScrollPicker 来实现:
- 滚动捕捉 – 项目“卡住”在中心位置。
- 首次挂载 – 在没有突兀动画的情况下立即跳转到当前值。
// Instant scroll trick on mount
useEffect(() => {
if (scrollRef.current) {
const selectedIndex = items.findIndex((item) => item === value);
if (selectedIndex !== -1) {
// Jump instantly
scrollRef.current.scrollTop = selectedIndex * ITEM_HEIGHT;
}
// Enable smooth scrolling for the user only after the jump
requestAnimationFrame(() => {
setIsLoaded(true);
});
}
}, []);
持久化存储 – 使用 IndexedDB 替代 localStorage
许多 Mini‑App 开发者会掉进 localStorage 的陷阱。在 Telegram 中,你的应用运行在系统 WebView 中;如果设备内存不足或清理缓存,localStorage 可能会被清除。
对于 UltyMyLife,我们选择了 IndexedDB(通过 idb 包装器)。这使我们能够存储数兆字节的数据:
- 多年的锻炼历史。
- 自定义图标。
- 睡眠日志。
我们使用 class‑transformer 将业务逻辑类序列化为扁平的 JSON 并再反序列化,确保得到带有可用方法的对象,而不仅仅是原始数据。
Source: …
云存储限制 → 压缩与自定义后端
Telegram 的 CloudStorage 有严格的限制,一年的训练日志会变得非常庞大。我们需要掌控。
解决方案
- Pako Compression (
zlib) + Base64 编码。 - 100 KB 的 JSON 可以压缩 70‑80 %,变成一个极小的字符串,即使在 2G 网络下也能瞬间上传。
防御式编程
我们的恢复逻辑是一把瑞士军刀,能够:
- 检测数据是已压缩的还是“旧”JSON。
- 清理 Base64 的残留(感谢 Safari 的 bug)。
- 处理模式迁移。
后端设计
我们保持后端简洁:PostgreSQL 仅作为 “盲目守护者” 使用。服务器不解析你的使用习惯,只是将二进制块 (BYTEA) 与你的 ID 关联存储。这使得扩展变得轻而易举。
Source: …
响应式状态管理 – RxJS 作为“神经系统”
作为 Unity 开发者,我习惯使用 UniRx。项目规模扩大后,我不再只需要一个简单的 “store”(Zustand)——我需要一个 神经系统。
RxJS 让我们把用户交互视为事件流,形成一种 “游戏循环” 效果,使 UI 能即时响应。
- BehaviorSubjects – 用于长期存在的状态(主题、当前页面)。
- Subjects – 用于一次性事件(弹窗、触觉反馈)。
// Our Event Bus (HabitsBus.js)
export const theme$ = new BehaviorSubject('dark');
export const setPage$ = new BehaviorSubject('LoadPanel');
export const showPopUpPanel$ = new BehaviorSubject({
show: false,
header: '',
isPositive: true,
});
export const setPage = (page) => {
setPage$.next(page);
// Auto‑switch bottom menu context
if (page.startsWith('Habit')) bottomBtnPanel$.next('BtnsHabits');
else if (page.startsWith('Training')) bottomBtnPanel$.next('BtnsTraining');
};
AI‑驱动的洞察
We taught the app to think. It doesn’t just say “You slept 6 hours.” It says:
“你的深蹲在使用 UltyMyLife 的夜晚后下降了 15 %,这证明即使在 WebView 的限制下,你也能提供下一代用户体验——无需 VPN、无需 Notion、无摩擦。”
玻璃拟态性能
“在浏览器中实现真正的玻璃拟态是性能噩梦。当 Demian 带来设计稿时,我内心的 Unity 开发者大喊 ‘Draw calls!’,而我内心的全栈开发者则立即开始着手解决。”
解决方案:双层防御
-
我们使用了
backdrop-filter: blur(15px),但由于在某些 WebView 中不可靠,添加了opacity: 0.85作为后备。 -
为了让应用保持 60 FPS 的运行,玻璃效果仅限于最关键的 UI 元素:
- 导航栏
- 模态框
- 习惯卡片
-
添加了内部渐变和
box‑shadow来模拟 “LED 边缘灯光”,让面板呈现漂浮在界面之上的效果。
XP 系统
大多数应用仅在打开时就给予 XP。 在 UltyMyLife 中,XP 必须通过努力获得。
TotalXP = (Training × 50) + (Mental × 30) + (Sleep × 20) + (Habits × 10)
- 等级(Level Up)采用指数计算,类似 RPG:每一级需要比前一级多 15 % 的 XP。
- 我们不使用卡通角色,而是采用等级和颜色的可视化层级。
- 这不是在玩游戏;你是在 提升自我。
Source: …
技术栈
| 层 | 技术 |
|---|---|
| UltyMyLife | 不仅仅是一个 Telegram 机器人——它是一个 LifeOS,随时随地为你而在。无需安装、无需权限、无摩擦。60 FPS 的纯粹自律。 |
如果你对特定组件的代码感兴趣(例如,可滑动的习惯卡片或自定义选择器),请在评论中告诉我!