Files
im_webapp/docs/superpowers/plans/2026-09-30-web-brand-and-message-sound.md
T

5.7 KiB
Raw Blame History

Web Brand And Message Sound Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 将生产 Web 品牌统一为“迅工捷使”,精简登录页,并为符合提醒条件的新消息播放用户提供的提示音。

Architecture: 继续以 web/src/lib/config.ts 作为显示品牌名来源,认证页和系统消息只消费该常量;品牌图标和 MP3 作为 Vite public/ 静态资源。消息声音接入现有 push.ts 新消息扫描链路,在浏览器通知权限判断之前复用“本人消息、当前可见会话、会话免打扰”的过滤结果。

Tech Stack: React 19、TypeScript、Vite、Node test runner、Jiti。


Task 1: 写品牌和提示音红测

Files:

  • Create: web/tests/brand-and-notification-sound.test.mjs

  • Step 1: 写失败测试

创建 Node 测试,核心断言如下;可按实际导出补齐 fake Audio 的类型字段,但不得降低断言:

const webRoot = new URL('../', import.meta.url);
assert.match(await readFile(new URL('index.html', webRoot), 'utf8'), /<title>迅工捷使<\/title>/);
assert.doesNotMatch(await readFile(new URL('src/features/auth/LoginPage.tsx', webRoot), 'utf8'), /桌面端|下载桌面客户端|iOS \/ Android|帮助中心/);
assert.doesNotMatch(await readFile(new URL('src/features/auth/TenantEntryPage.tsx', webRoot), 'utf8'), /MeetingIM/);
assert.doesNotMatch(await readFile(new URL('src/features/system/SystemMessagesView.tsx', webRoot), 'utf8'), /MeetingIM 安全团队/);
assert.equal(audioSource, '/message-notification.mp3');
assert.equal(playCount, 1);
  • Step 2: 运行测试确认失败

Run: node --experimental-strip-types --test tests/brand-and-notification-sound.test.mjs

Expected: FAIL,因为品牌仍是 MeetingIM,提示音模块和静态资源尚不存在。

Task 2: 更新品牌和登录页

Files:

  • Modify: web/index.html

  • Modify: web/src/lib/config.ts

  • Modify: web/src/features/auth/LoginPage.tsx

  • Modify: web/src/features/auth/TenantEntryPage.tsx

  • Modify: web/src/features/system/SystemMessagesView.tsx

  • Create: web/public/app-icon.png

  • Delete: web/public/favicon.svg

  • Modify: web/src/lib/notifications/tabIndicator.ts

  • Modify: web/src/lib/notifications/push.ts

  • Step 1: 复制官方品牌图标

将 ../im-ios-website/dist/assets/app-icon.png 原样复制为 web/public/app-icon.png,所有 Web 图标引用统一指向它。

  • Step 2: 最小化更新可见品牌

把浏览器标题、认证页和系统消息署名改为“迅工捷使”;APP_NAME 保留 MeetingIM/<version> 作为现有 Tinode 客户端技术标识,避免把显示品牌替换扩大成协议改名。

export const BASE_APP_NAME = '迅工捷使';
export const APP_NAME = `MeetingIM/${__APP_VERSION__}`;

认证页统一使用:

<img src="/app-icon.png" alt="" className="h-full w-full rounded-[inherit]" />
<strong>{BASE_APP_NAME}</strong>
  • Step 3: 精简登录页

说明文案改为“单聊、群聊、通讯录、文件与收藏、音视频通话,消息与回执在手机端保持一致。”,删除底部三个入口。

<p className="mt-4 max-w-md text-base text-white/80">
  单聊、群聊、通讯录、文件与收藏、音视频通话,消息与回执在手机端保持一致。
</p>

Task 3: 接入消息提示音

Files:

  • Create: web/src/lib/notifications/sound.ts

  • Modify: web/src/lib/notifications/push.ts

  • Create: web/public/message-notification.mp3

  • Step 1: 复制用户提供的提示音

将 /Users/aa/Downloads/universfield-new-notification-012-363675.mp3 原样复制为 web/public/message-notification.mp3。

  • Step 2: 实现安全播放器

首次需要时创建 Audio('/message-notification.mp3'),每次播放前设置 currentTime = 0;对同步异常和 play() Promise 拒绝均静默处理。

let messageAudio: HTMLAudioElement | null = null;

export function playMessageSound(): void {
  try {
    messageAudio ||= new Audio('/message-notification.mp3');
    messageAudio.currentTime = 0;
    void messageAudio.play().catch(() => {});
  } catch {
    // Optional audio must never interrupt message delivery.
  }
}
  • Step 3: 接入新消息过滤链路

把“本人消息、当前可见会话、会话免打扰”抽成通知与声音共享的判断。通过判断后先播放声音;仅当 Notification API 可用且权限为 granted 时再显示系统通知。

if (!shouldAlert(topicName, latest)) return;
playMessageSound();
if (typeof Notification !== 'undefined' && Notification.permission === 'granted') {
  fireNotification(topicName, latest);
}
  • Step 4: 跑绿测

Run: node --experimental-strip-types --test tests/*.test.ts tests/*.test.mjs

Expected: 现有 6 项和新增测试全部 PASS。

Task 4: 验证、提交与部署

Files:

  • Verify only

  • Step 1: 静态验证

Run: npm run build && npm run lint

Expected: 构建退出码 0;lint 无 error,允许记录既有 warning。

  • Step 2: 完整性验证

Run: git diff --check

Expected: 无输出,退出码 0;MP3 和应用图标的 SHA-256 与来源文件一致。

  • Step 3: 提交并推送

提交精确修改文件到 dev,推送前先 fetch 并确认无远端分叉。

  • Step 4: 部署和公网回归

触发 Gitea deploy.yml,等待成功,确认 K3s Deployment 2/2;访问 https://web.118im.com 验证标题、品牌、登录页删减及双账号新消息提醒路径。