我们将游戏开发逻辑移植到 Telegram:使用 React 和 RxJS 构建触觉 LifeOS

发布: (2026年1月30日 GMT+8 22:17)
9 min read
原文: Dev.to

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 网络下也能瞬间上传。

防御式编程

我们的恢复逻辑是一把瑞士军刀,能够:

  1. 检测数据是已压缩的还是“旧”JSON。
  2. 清理 Base64 的残留(感谢 Safari 的 bug)。
  3. 处理模式迁移。

后端设计

我们保持后端简洁: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 元素:

    1. 导航栏
    2. 模态框
    3. 习惯卡片
  • 添加了内部渐变和 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 的纯粹自律。

如果你对特定组件的代码感兴趣(例如,可滑动的习惯卡片或自定义选择器),请在评论中告诉我!

👉 查看详情

t.me/UltyMyLife_bot/umlminiapp

Back to Blog

相关文章

阅读更多 »