From c6aaf8c468b67c3fb8e945bc310a6151a5eeb675 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Mon, 14 Sep 2026 14:05:35 +0800 Subject: [PATCH] =?UTF-8?q?refactor(harmony):=2023=20=E5=A4=84=E5=BA=9F?= =?UTF-8?q?=E5=BC=83=20API=20=E6=8D=A2=E6=88=90=20UIContext=20=E5=86=99?= =?UTF-8?q?=E6=B3=95=EF=BC=88=E5=85=A8=E5=B1=80=20promptAction.showToast?= =?UTF-8?q?=20=E8=87=AA=20API=2018=20=E5=BA=9F=E5=BC=83=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SDK 里写得很清楚:`@ohos.promptAction.d.ts` 的全局 `showToast` 标着 `@deprecated since 18`, 替代品是 `UIContext.getPromptAction()`。仓库里有 23 处这种调用(6 个页面,历史遗留)—— 这一轮既然在按"用系统方案"整理鸿蒙侧,就一次扫干净,并加判据挡住回潮。 - `promptAction.showToast(...)` → `this.getUIContext().getPromptAction().showToast(...)`(23 处) - 清掉不再需要的 `promptAction` import(多个 → 只留 `router` 等) - 新增判据:不得再用全局写法。防的不是这次,而是**新增页面照抄旧代码**这条回退路径 —— 它编译照样通过、只在真机上行为不同。自检同时验"认得出旧写法"与"不误伤新写法"。 - 顺带把权限徽标那条"点它弹说明"的判据改成钉**非废弃**写法(原来是 `promptAction.showToast(`)。 变异验证:把 `SettingsPage` 的一处改回全局写法 → 判红并点出文件。 验证:`hvigorw assembleHap` BUILD SUCCESSFUL;`npm test` 退出码 0(harmony-logic 20 条)。 --- client/electron/test/harmony-logic.test.mjs | 39 ++++++++++++++++++- .../entry/src/main/ets/pages/ComposePage.ets | 15 ++++--- .../entry/src/main/ets/pages/InboxPage.ets | 2 +- .../entry/src/main/ets/pages/LoginPage.ets | 3 +- .../src/main/ets/pages/MailDetailPage.ets | 9 ++--- .../entry/src/main/ets/pages/MainPage.ets | 5 +-- .../entry/src/main/ets/pages/SettingsPage.ets | 11 +++--- docs/HARMONY-ALIGN-PLAN.md | 14 +++++++ 8 files changed, 71 insertions(+), 27 deletions(-) diff --git a/client/electron/test/harmony-logic.test.mjs b/client/electron/test/harmony-logic.test.mjs index 70b8bf5..108a14f 100644 --- a/client/electron/test/harmony-logic.test.mjs +++ b/client/electron/test/harmony-logic.test.mjs @@ -20,7 +20,7 @@ */ import { test } from 'node:test'; import assert from 'node:assert/strict'; -import { readFileSync } from 'node:fs'; +import { readFileSync, readdirSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; @@ -338,7 +338,10 @@ test('徽标真的挂在界面上,且点它能看到那句说明(触屏没 const clickIdx = pageCode.indexOf('.onClick(', chipIdx); assert.ok(chipIdx > 0 && clickIdx > chipIdx && clickIdx - chipIdx < 600, '徽标上要有自己的 onClick'); const chipClickBody = onClickBodyOf(pageCode, clickIdx); - assert.match(chipClickBody, /promptAction\.showToast\(/, '徽标的 onClick 里要弹说明(不是页面别处的 toast)'); + // 注意用**非废弃**的写法:全局 `promptAction.showToast` 自 API 18 起废弃 + // (SDK `@ohos.promptAction.d.ts` 的 `@deprecated since 18`), + // 要的是 UIContext 上的那个 —— 这条断言顺带把废弃写法挡在门外。 + assert.match(chipClickBody, /\.getPromptAction\(\)\.showToast\(/, '徽标的 onClick 里要弹说明(UIContext 的非废弃写法)'); assert.match(chipClickBody, /permissionHint\(/, '弹出来的必须是那句说明'); assert.match(pageCode, /enforcementLabel\(c\.permission_enforcement\)/, '说明里要带强制力标签'); assert.match(pageCode, /permissionLabel\(mail\.permission_mode\)/, '收件箱行要用中文档位'); @@ -349,3 +352,35 @@ test('徽标真的挂在界面上,且点它能看到那句说明(触屏没 '收件箱每封邮件里没有 permission_enforcement,画强制力标记等于编一个"平台做到了什么"' ); }); + +test('★ 不得再用废弃的全局 promptAction.showToast(API 18 起废弃,要走 UIContext)', () => { + /* + * SDK 里写得很清楚:`@ohos.promptAction.d.ts` 的全局 `showToast` 标着 + * `@deprecated since 18`,替代品是 `UIContext.getPromptAction()`。 + * 本仓库原先有 **23 处**这种调用(不是我写的,是历史)—— 既然这一轮在按 + * "用系统方案"整理鸿蒙侧,就顺手一次扫干净,并用判据挡住回潮: + * 新增页面照抄旧写法是最常见的回退路径,而它**编译照样通过**。 + */ + const dir = join(ROOT, 'client/harmony/entry/src/main/ets'); + const files = []; + const walk = d => { + for (const e of readdirSync(d, { withFileTypes: true })) { + const full = join(d, e.name); + if (e.isDirectory()) walk(full); + else if (/\.(ets|ts)$/.test(e.name)) files.push(full); + } + }; + walk(dir); + assert.ok(files.length >= 10, `应扫到至少 10 个源文件,实际 ${files.length}`); + + const bad = []; + for (const f of files) { + const src = readFileSync(f, 'utf8').replace(/\/\*[\s\S]*?\*\//g, '').replace(/^\s*\/\/.*$/gm, ''); + // `getPromptAction().showToast(` 不算违规:它前面必须有 `get` + for (const m of src.matchAll(/(? 0 ? ae.message : '登录失败'); hilog.error(0x0001, 'LoginPage', 'login failed: code=%{public}d msg=%{public}s', ae.code, msg); - promptAction.showToast({ message: msg }); + this.getUIContext().getPromptAction().showToast({ message: msg }); } finally { this.loading = false; } diff --git a/client/harmony/entry/src/main/ets/pages/MailDetailPage.ets b/client/harmony/entry/src/main/ets/pages/MailDetailPage.ets index 7891efe..492c5bb 100644 --- a/client/harmony/entry/src/main/ets/pages/MailDetailPage.ets +++ b/client/harmony/entry/src/main/ets/pages/MailDetailPage.ets @@ -9,7 +9,6 @@ import { MailApi } from '../api/MailApi'; import { AccountManager, AccountInfo } from '../api/AccountManager'; import { MailDetail, SendMailRequest } from '../model/Models'; import { MailDetailParams } from '../model/RouteParams'; -import { promptAction } from '@kit.ArkUI'; @Entry @Component @@ -103,10 +102,10 @@ struct MailDetailPage { try { await m.setPermissionMode(sid, mode); this.permissionMode = mode; - promptAction.showToast({ message: '权限已切换为 ' + mode }); + this.getUIContext().getPromptAction().showToast({ message: '权限已切换为 ' + mode }); } catch (e) { const ae = e as ApiError; - promptAction.showToast({ message: '切换失败: ' + ae.message }); + this.getUIContext().getPromptAction().showToast({ message: '切换失败: ' + ae.message }); } finally { this.switchingPerm = false; } @@ -306,10 +305,10 @@ struct MailDetailPage { await m.send(req); this.showReplyBox = false; this.replyBody = ''; - promptAction.showToast({ message: '回复已发送' }); + this.getUIContext().getPromptAction().showToast({ message: '回复已发送' }); } catch (e) { const ae = e as ApiError; - promptAction.showToast({ message: '发送失败: ' + ae.message }); + this.getUIContext().getPromptAction().showToast({ message: '发送失败: ' + ae.message }); } finally { this.sending = false; } diff --git a/client/harmony/entry/src/main/ets/pages/MainPage.ets b/client/harmony/entry/src/main/ets/pages/MainPage.ets index 4c1bfa6..1498df2 100644 --- a/client/harmony/entry/src/main/ets/pages/MainPage.ets +++ b/client/harmony/entry/src/main/ets/pages/MainPage.ets @@ -26,7 +26,6 @@ import { enforcementLabel } from '../model/MailGrouping'; import { MailDetailParams, ComposeParams } from '../model/RouteParams'; -import { promptAction } from '@kit.ArkUI'; /** 一页取多少封。取满了就要如实提示"可能还有更多"(服务端 total 是未读数,不是总封数)。 */ const INBOX_PAGE_SIZE: number = 50; @@ -89,7 +88,7 @@ struct InboxTab { break; } } - promptAction.showToast({ message: sourceName.length > 0 ? '📨 ' + sourceName + ' 收到新邮件' : '📨 新邮件到达' }); + this.getUIContext().getPromptAction().showToast({ message: sourceName.length > 0 ? '📨 ' + sourceName + ' 收到新邮件' : '📨 新邮件到达' }); this.loadData(); } }; @@ -737,7 +736,7 @@ struct ContactsTab { .padding({ left: 5, right: 5, top: 1, bottom: 1 }) .margin({ right: 6 }) .onClick(() => { - promptAction.showToast({ + this.getUIContext().getPromptAction().showToast({ message: permissionHint(c.permission_mode, c.permission_enforcement) + '(强制力:' + enforcementLabel(c.permission_enforcement) + ')', duration: 6000 diff --git a/client/harmony/entry/src/main/ets/pages/SettingsPage.ets b/client/harmony/entry/src/main/ets/pages/SettingsPage.ets index 85978c2..93e3dc5 100644 --- a/client/harmony/entry/src/main/ets/pages/SettingsPage.ets +++ b/client/harmony/entry/src/main/ets/pages/SettingsPage.ets @@ -7,7 +7,6 @@ import { Theme } from '../common/Theme'; import { AuthApi } from '../api/AuthApi'; import { AccountManager, AccountInfo } from '../api/AccountManager'; import { SseService } from '../api/SseService'; -import { promptAction } from '@kit.ArkUI'; @Entry @Component @@ -66,7 +65,7 @@ struct SettingsPage { if (account !== null) { client.setBase(account.server); client.setToken(account.token); - promptAction.showToast({ message: '默认发信账号已设为 ' + account.displayName }); + this.getUIContext().getPromptAction().showToast({ message: '默认发信账号已设为 ' + account.displayName }); } this.refreshList(); } @@ -92,7 +91,7 @@ struct SettingsPage { } } this.refreshList(); - promptAction.showToast({ message: '账号已删除' }); + this.getUIContext().getPromptAction().showToast({ message: '账号已删除' }); } async addNewAccount(): Promise { @@ -106,7 +105,7 @@ struct SettingsPage { const token: string = this.newToken.trim(); const optionalUsername: string = this.newUsername.trim(); if (displayName.length === 0 || server.length === 0 || token.length === 0) { - promptAction.showToast({ message: '请填写显示名称、Gateway 地址和 user_key' }); + this.getUIContext().getPromptAction().showToast({ message: '请填写显示名称、Gateway 地址和 user_key' }); return; } @@ -119,7 +118,7 @@ struct SettingsPage { const username: string = optionalUsername.length > 0 ? optionalUsername : user.username; const account: AccountInfo = await manager.addAccount(server, username, token, displayName); SseService.getInstance().connectForAccount(account.id, account.server, account.token); - promptAction.showToast({ message: '✅ 添加成功: ' + account.displayName }); + this.getUIContext().getPromptAction().showToast({ message: '✅ 添加成功: ' + account.displayName }); this.showAddDialog = false; this.newDisplayName = ''; this.newServer = ''; @@ -128,7 +127,7 @@ struct SettingsPage { this.refreshList(); } catch (e) { const apiError = e as ApiError; - promptAction.showToast({ message: '验证失败: ' + apiError.message }); + this.getUIContext().getPromptAction().showToast({ message: '验证失败: ' + apiError.message }); } finally { this.adding = false; } diff --git a/docs/HARMONY-ALIGN-PLAN.md b/docs/HARMONY-ALIGN-PLAN.md index beeef5c..e6876c9 100644 --- a/docs/HARMONY-ALIGN-PLAN.md +++ b/docs/HARMONY-ALIGN-PLAN.md @@ -543,3 +543,17 @@ deb 也不必从 targets 里摘。已写进 `client/electron/BUILD.md`(含排 深色模式下的观感,都只有真机才看得到(模拟器需人在命令行启动)。已验证的是: `hvigorw assembleHap` BUILD SUCCESSFUL、13 条跨端判据 + 4 条系统资源名判据全绿、 四种变异都能判红;`sys.*` 名字全部对着 SDK 名表核过(判据持续盯着)。 + +### 7.14 顺手扫掉 23 处废弃 API(全局 `promptAction.showToast`) + +SDK 里写着:`@ohos.promptAction.d.ts` 的全局 `showToast` 标 `@deprecated since 18`, +替代品是 `UIContext.getPromptAction()`。仓库里原有 **23 处**这种调用(历史遗留,6 个页面)—— +既然这一轮在按"用系统方案"整理鸿蒙侧,就一次扫干净: +`promptAction.showToast(...)` → `this.getUIContext().getPromptAction().showToast(...)`, +并清掉不再需要的 import。 + +**判据**:不得再用全局写法(负向断言,自检里同时验"认得出旧写法"且"不误伤新写法")。 +这条防的不是这次,而是**新增页面照抄旧代码**这条最常见的回退路径 —— 它编译照样通过。 + +(同一批整理里还没做的:全局 `animateTo` 也要走 `getUIContext().animateTo(...)`, +放到动效那一期一起改 —— 那一期本来就是"自定义 transition → 系统 `curves`"。)