From 64020966396e0fd582da99be904734f95542b58b Mon Sep 17 00:00:00 2001 From: kris Date: Sun, 29 Mar 2026 23:47:59 +0800 Subject: [PATCH] docs: add wechat ui polish plan --- ...03-29-wechat-conversations-me-ui-polish.md | 343 ++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 docs/superpowers/plans/2026-03-29-wechat-conversations-me-ui-polish.md diff --git a/docs/superpowers/plans/2026-03-29-wechat-conversations-me-ui-polish.md b/docs/superpowers/plans/2026-03-29-wechat-conversations-me-ui-polish.md new file mode 100644 index 0000000..4009311 --- /dev/null +++ b/docs/superpowers/plans/2026-03-29-wechat-conversations-me-ui-polish.md @@ -0,0 +1,343 @@ +# 微信式会话页与我的页 UI 收口 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:** 把原生 Android 的 `会话` 根页和 `我的` 根页统一收口到同一套微信式产品语言,同时保持现有会话、返回、群聊、附件和登录链路不回退。 + +**Architecture:** 这次只重写原生 UI helper 和 `MainActivity` 的两处根页渲染绑定,不改数据模型或 API。实现顺序按 TDD 走:先补 helper 和根页渲染测试,再做最小实现,再跑 Android 单测、release 构建和 OPPO 真机回归。 + +**Tech Stack:** Java, Android AppCompat, Robolectric/JUnit, Gradle, ADB, Next.js verification scripts + +--- + +### Task 1: 为会话页和我的页补 UI 基线测试 + +**Files:** +- Modify: `android/app/src/test/java/com/hyzq/boss/BossUiConversationRowTest.java` +- Create: `android/app/src/test/java/com/hyzq/boss/BossUiRootSurfaceTest.java` +- Test: `android/app/src/test/java/com/hyzq/boss/BossUiConversationRowTest.java` +- Test: `android/app/src/test/java/com/hyzq/boss/BossUiRootSurfaceTest.java` + +- [ ] **Step 1: 扩展会话列表项测试,明确“非卡片流”的结构约束** + +```java +@Test +public void buildConversationRow_usesWechatListSpacingInsteadOfCardChrome() { + Context context = ApplicationProvider.getApplicationContext(); + WechatSurfaceMapper.ConversationRow row = new WechatSurfaceMapper.ConversationRow( + "北区试产线回归", + "归档确认", + "现场摄像头关键帧", + "09:26", + 2, + "置顶", + 2, + false, + "M", + "W", + new WechatSurfaceMapper.GroupAvatarMember[0] + ); + + LinearLayout view = BossUi.buildConversationRow(context, row, null); + + assertEquals(Color.WHITE, ((ColorDrawable) view.getBackground()).getColor()); + assertEquals(BossUi.dp(context, 16), view.getPaddingLeft()); + assertEquals(BossUi.dp(context, 12), view.getPaddingTop()); +} +``` + +- [ ] **Step 2: 运行单测,确认它先失败** + +Run: + +```bash +cd /Users/kris/code/boss/android +./gradlew testDebugUnitTest --tests com.hyzq.boss.BossUiConversationRowTest --no-daemon +``` + +Expected: FAIL,原因是当前 `buildConversationRow` 仍保留卡片样式或 padding 不符合新约束。 + +- [ ] **Step 3: 新增根页测试,锁定“我的”页头部和菜单列表结构** + +```java +@Test +public void renderMeRoot_usesWechatProfileHeaderAndFlatMenuRows() { + MainActivity activity = Robolectric.buildActivity(MainActivity.class).create().start().resume().get(); + + activity.runOnUiThread(() -> { + activity.sessionData = new StubJSONObject() + .withString("displayName", "Kris") + .withString("account", "17600003315") + .withString("role", "最高管理员"); + activity.setActiveTab("me", false); + }); + Shadows.shadowOf(Looper.getMainLooper()).idle(); + + LinearLayout content = activity.findViewById(R.id.screen_content); + + assertTrue(findText(content, "Kris")); + assertTrue(findText(content, "17600003315")); + assertTrue(findText(content, "账号与安全")); + assertTrue(findText(content, "AI 账号")); +} +``` + +- [ ] **Step 4: 运行新的根页测试,确认它先失败** + +Run: + +```bash +cd /Users/kris/code/boss/android +./gradlew testDebugUnitTest --tests com.hyzq.boss.BossUiRootSurfaceTest --no-daemon +``` + +Expected: FAIL,原因是当前 `renderMeRoot` / `buildSimpleProfileHeader` 仍不满足新的微信式结构断言。 + +- [ ] **Step 5: 提交测试基线** + +```bash +cd /Users/kris/code/boss +git add android/app/src/test/java/com/hyzq/boss/BossUiConversationRowTest.java android/app/src/test/java/com/hyzq/boss/BossUiRootSurfaceTest.java +git commit -m "test: lock wechat root surface expectations" +``` + +### Task 2: 重写 BossUi 的会话行、资料区和菜单行 helper + +**Files:** +- Modify: `android/app/src/main/java/com/hyzq/boss/BossUi.java` +- Modify: `android/app/src/test/java/com/hyzq/boss/BossUiConversationRowTest.java` +- Modify: `android/app/src/test/java/com/hyzq/boss/BossUiRootSurfaceTest.java` +- Test: `android/app/src/test/java/com/hyzq/boss/BossUiConversationRowTest.java` +- Test: `android/app/src/test/java/com/hyzq/boss/BossUiRootSurfaceTest.java` + +- [ ] **Step 1: 实现新的会话列表行和我的页基础 helper** + +```java +public static LinearLayout buildWechatMenuRow( + Context context, + String title, + @Nullable String subtitle, + @Nullable String meta, + @Nullable String badge, + @Nullable View.OnClickListener listener +) { + LinearLayout row = buildListRow(context, title, subtitle, meta, badge, listener); + row.setBackgroundColor(Color.WHITE); + row.setPadding(dp(context, 18), dp(context, 15), dp(context, 18), dp(context, 15)); + row.setElevation(0f); + return row; +} + +public static LinearLayout buildSimpleProfileHeader( + Context context, + String name, + String subtitle, + @Nullable String detail +) { + LinearLayout header = new LinearLayout(context); + header.setOrientation(LinearLayout.HORIZONTAL); + header.setBackgroundColor(Color.WHITE); + header.setPadding(dp(context, 20), dp(context, 18), dp(context, 20), dp(context, 18)); + return header; +} +``` + +- [ ] **Step 2: 把 `buildConversationRow` 从卡片改成白底列表项** + +```java +public static LinearLayout buildConversationRow( + Context context, + WechatSurfaceMapper.ConversationRow row, + @Nullable View.OnClickListener listener +) { + LinearLayout container = new LinearLayout(context); + container.setOrientation(LinearLayout.HORIZONTAL); + container.setBackgroundColor(Color.WHITE); + container.setPadding(dp(context, 16), dp(context, 12), dp(context, 16), dp(context, 12)); + if (listener != null) { + container.setClickable(true); + container.setFocusable(true); + container.setOnClickListener(listener); + } + return container; +} +``` + +- [ ] **Step 3: 运行 helper 相关单测,确认现在全部通过** + +Run: + +```bash +cd /Users/kris/code/boss/android +./gradlew testDebugUnitTest --tests com.hyzq.boss.BossUiConversationRowTest --tests com.hyzq.boss.BossUiRootSurfaceTest --no-daemon +``` + +Expected: PASS,`BossUiConversationRowTest` 和 `BossUiRootSurfaceTest` 全绿。 + +- [ ] **Step 4: 自查复用面** + +```bash +cd /Users/kris/code/boss +rg -n "buildSimpleProfileHeader|buildWechatMenuRow|buildConversationRow" android/app/src/main/java/com/hyzq/boss -S +``` + +Expected: 输出覆盖 `MainActivity / ConversationInfoActivity / GroupInfoActivity / GroupCreateActivity / AboutActivity` 等现有入口,确认 helper 改动会统一带到深层页。 + +- [ ] **Step 5: 提交 helper 重写** + +```bash +cd /Users/kris/code/boss +git add android/app/src/main/java/com/hyzq/boss/BossUi.java android/app/src/test/java/com/hyzq/boss/BossUiConversationRowTest.java android/app/src/test/java/com/hyzq/boss/BossUiRootSurfaceTest.java +git commit -m "style: align conversations and me root surfaces" +``` + +### Task 3: 收口 MainActivity 根页渲染和点击区 + +**Files:** +- Modify: `android/app/src/main/java/com/hyzq/boss/MainActivity.java` +- Modify: `android/app/src/test/java/com/hyzq/boss/BossUiRootSurfaceTest.java` +- Test: `android/app/src/test/java/com/hyzq/boss/BossUiRootSurfaceTest.java` + +- [ ] **Step 1: 调整会话页和我的页根渲染,让新 helper 真正落到主界面** + +```java +private void renderConversationsRoot() { + topTitle.setText("会话"); + topSubtitle.setVisibility(View.GONE); + screenContent.removeAllViews(); + for (int i = 0; i < conversationsData.length(); i++) { + JSONObject item = conversationsData.optJSONObject(i); + if (item == null) continue; + WechatSurfaceMapper.ConversationRow row = WechatSurfaceMapper.toConversationRow(item); + screenContent.addView(BossUi.buildConversationRow(this, row, v -> openConversation(item))); + } +} + +private void renderMeRoot() { + topTitle.setText("我的"); + topSubtitle.setVisibility(View.GONE); + screenContent.removeAllViews(); + screenContent.addView(BossUi.buildSimpleProfileHeader(this, displayName, account, roleLabel)); + for (WechatSurfaceMapper.MeMenuItem item : WechatSurfaceMapper.rootMeMenuItems()) { + screenContent.addView(BossUi.buildWechatMenuRow(this, item.title, item.description, null, null, v -> openMeItem(item.key))); + } +} +``` + +- [ ] **Step 2: 补充点击区测试,保证菜单行和会话行整行可点** + +```java +@Test +public void renderConversationsRoot_keepsConversationRowsClickable() { + MainActivity activity = buildConversationReadyActivity(); + LinearLayout content = activity.findViewById(R.id.screen_content); + View firstRow = content.getChildAt(1); + assertTrue(firstRow.isClickable()); +} +``` + +- [ ] **Step 3: 跑根页测试,确认主界面结构和点击区正确** + +Run: + +```bash +cd /Users/kris/code/boss/android +./gradlew testDebugUnitTest --tests com.hyzq.boss.BossUiRootSurfaceTest --no-daemon +``` + +Expected: PASS,根页结构、菜单项和点击区断言全部通过。 + +- [ ] **Step 4: 提交 MainActivity 收口** + +```bash +cd /Users/kris/code/boss +git add android/app/src/main/java/com/hyzq/boss/MainActivity.java android/app/src/test/java/com/hyzq/boss/BossUiRootSurfaceTest.java +git commit -m "style: polish native conversations and me roots" +``` + +### Task 4: 合并现有关于页版本号修复并完成全链验证 + +**Files:** +- Modify: `android/app/src/main/java/com/hyzq/boss/AboutActivity.java` +- Modify: `android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java` +- Modify: `README.md` +- Modify: `docs/architecture/current_runtime_and_deploy_status_cn.md` +- Test: `android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java` + +- [ ] **Step 1: 保留并整理当前“关于页显示已安装版本号”的修复** + +```java +private static String resolveInstalledVersionLabel( + @Nullable JSONObject user, + JSONObject ota, + @Nullable String packageVersionName +) { + if (packageVersionName != null && !packageVersionName.isEmpty()) { + return packageVersionName; + } + if (user != null) { + String userVersion = user.optString("version", ""); + if (!userVersion.isEmpty()) { + return userVersion; + } + } + return ota.optString("currentVersion", "-"); +} +``` + +- [ ] **Step 2: 跑关于页测试,确认已安装版本优先级正确** + +Run: + +```bash +cd /Users/kris/code/boss/android +./gradlew testDebugUnitTest --tests com.hyzq.boss.WechatSurfaceMapperTest --no-daemon +``` + +Expected: PASS,`aboutActivity_prefersInstalledPackageVersionOverServerVersion` 通过。 + +- [ ] **Step 3: 运行完整本地验证** + +Run: + +```bash +cd /Users/kris/code/boss +npm run lint +npm run build +curl -sS http://127.0.0.1:3000/api/health +curl -sS http://127.0.0.1:4317/health +cd android && ./gradlew testDebugUnitTest --no-daemon +cd /Users/kris/code/boss/android && ./gradlew clean assembleRelease --no-daemon +``` + +Expected: +- `lint` PASS +- `build` PASS +- 两个 health 接口返回 `ok` +- Android 单测 PASS +- release 构建成功输出 APK + +- [ ] **Step 4: 用 OPPO `PLB110` 做真机回归** + +Run: + +```bash +adb devices -l +adb -t 501 install -r /Users/kris/code/boss/android/app/build/outputs/apk/release/app-release.apk +adb -t 501 shell am start -W -n com.hyzq.boss/.MainActivity +``` + +Manual checks: +- 会话首页视觉不再是厚卡片流 +- 点击任意会话能进入聊天并返回 +- 我的首页顶部是微信式资料区 +- 我的 > 关于 正确显示已安装版本号 +- 底部 `会话 / 设备 / 我的` 切换正常 + +- [ ] **Step 5: 同步文档并提交** + +```bash +cd /Users/kris/code/boss +git add android/app/src/main/java/com/hyzq/boss/AboutActivity.java android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java README.md docs/architecture/current_runtime_and_deploy_status_cn.md +git commit -m "docs: record native root ui polish validation" +```