跨端: 顶栏不再自己铺白条 + 日历改左右两栏 + 常驻窗格动画真的会播(用户三处实测指出)
用户三条反馈,逐条对应:
① 「底栏数字为什么显示在图标下面?」
WebUI 的徽标是 `absolute top-1 right-[22%]`(脱离文档流、浮在图标右上角),
我写成了 `Column` 的第三个子节点 ⇒ 参与竖向布局、掉到文字下面。
改用 `Stack({ alignContent: Alignment.TopEnd })` 锚在**图标**上。
(顺带撞了 skill 里明写的坑:Stack 没有 `.justifyContent()`。)
② 「一个横着过去的白条,我真的服了」/「期望:融进背景」
WebUI 的顶栏**自身没有底色** —— 只有 `border-b border-gray-200`
(`ContactPanel.tsx:64`、`CommTabs.tsx:40`、`CalendarView.tsx:295`),
底色由所在面板给;壁纸开启时那层面板是玻璃色(`index.css:876`)。
鸿蒙三个窗格顶栏写死了 `Theme.surface`(实心白)⇒ 无论壁纸开没开,
顶上都是一条不通明白带。改成与**页面底**同一口径
(`bgActive ? Transparent : surface`)+ 补下边框。
③ 「日历页面和webui布局完全不同」
WebUI 是**左右两栏**(`CalendarView.tsx:452-457`):左 `flex-1` 网格、
右 `400px` 常驻面板(日程 / 编辑器 / 小时网格三态互斥)。
鸿蒙原来是**单栏竖堆**。重搭为两栏,`paneWide` 由 `.onAreaChange`
量本页**自己的**宽度(不是屏幕宽度 —— 宽屏下这一页已被侧栏占掉一截);
编辑器改占右栏位置(不再整页盖掉正在看的那个月)。
④ 「最严重的动画问题你一点也不该改」
日历是**常驻挂载**(`visibility` 控制,因为它里面 today 要随时间重算、
也要保住"正在看哪个月"),而 `.transition()` 只在**挂载/卸载**时触发
(SDK 原话 \"when it **appears and disappears**\")⇒ 挂在它上面的
`.transition(paneRiseIn())` **一帧也不会播**,切过去是硬弹。
WebUI 踩过同一个坑并把错法写进了 `index.css:1305-1320`
(「只挂了类,却没让触发窗口出现 ⇒ 类挂着、动画永远不播」),
它的解法是 `html.view-switch` 重放窗口。ArkUI 对应物是
`animateTo` + 显式 `calPaneIn` 属性(`opacity` + `translate`)。
★ reset 必须在 `animateTo` **外**:写进回调里会与同帧的 1 相抵,
渲染层只看得见最终值 ⇒ 动画退化成一个瞬移。
顺带修:
· 宽屏侧栏 = 3 项(通信/日历/**联系**)+ 底部一簇(头像/主题/退出),
与底栏的四项(含「我的」)**不是同一份清单** —— WebUI 的 Sidebar 与
NarrowNav 本就不同(`Sidebar.tsx:26-44` vs `NarrowNav.tsx:37-40`+143)。
新增 `NAV_SIDEBAR_ITEMS` / `ME_PANE_INDEX` / `SIDEBAR_ITEM_*`。
· 退出登录抽成 `api/Logout.ets` 的 `performLogout()`:侧栏底簇与「我的」页
两个入口必须做同一件事(尤其"先注销推送 token"那一步),复制一份就会不一致。
· 徽标 `'plain'` 档底色:WebUI 是石板灰 `--c-chrome-600`(#475569),
我写成与未读共用红色 ⇒ 「联系」的徽标看起来像"有未读"。
· 主题快捷开关(侧栏底簇):对齐 `ThemeToggleButton` —— 从 `system` 翻转时
落到**当前生效值的反面**(不是回 system;那可能毫无变化、让按钮看起来坏了)。
· 「浓度」→「压暗」+ 数值带单位(原先屏上印 `56.000000`;WebUI 是 `suffix="%"`)。
判据(8 个套件全绿:logic 28 / nav 18 / widescreen 7 / window 9 /
arkts 5 / contacts 5 / calendar 30 / system-api 5):
· `harmony-widescreen` ② 重写:**回读 `Sidebar.tsx` 数 `short:` 的个数**
要求鸿蒙同数,并断言底部一簇三键真的被调用(`this.onToggleTheme()` ——
第一版写成 `/onToggleTheme/`,变异测试当场证明它不咬:属性**声明**还在,
正则照样匹上)。新增 ⑧:三档色调各自底色,期望值从 `--c-chrome-600` 读出。
· `harmony-nav` 动画条重写:改判**机制真的存在且被驱动**
(旧断言 `.visibility(...).transition(...)` 锁的正是那个 bug ——
判据引自己写的注释当依据,就会把错误锁死)。新增 reset-在-animateTo-外
这条断言(否则动画退化成瞬移)。
· `harmony-nav` 设备条:`navItemsOf` 的宽屏过滤从"左边缘靠左 1/6"
改成"**整个盒子在侧栏轨道内**" —— 旧条件把日历网格的格子
(实测 `[229,511][366,794]`)也当成导航项,一屏数出 11~12 个。
新增 `navRailItemsOf`:导航轨贴顶、底部簇在屏底,按位置切一刀。
· `harmony-logic` 两处:从 `NAV_ITEMS` / `NAV_SIDEBAR_ITEMS`
**各自的数组**里取 label —— 原先把全文件 `label:` 一网打尽,
得到 7 个(4+3 混在一起),任何一边改对了它都会红。
设备实测(HATriple 三折叠,3184×2232):侧栏 3 项 + 底簇 / 底栏徽标回到
图标右上角 / 日历左右两栏与 WebUI 并排同构。
server/go.mod:补 2da38bb 漏提交的 lunar-go 依赖。
This commit is contained in:
@ -1,12 +1,18 @@
|
||||
// 宽屏侧栏图标轨(WideSidebar)判据 —— 一比一复刻 WebUI 的 `Sidebar`(60px 图标轨)。
|
||||
// 宽屏侧栏(WideSidebar)判据 —— 逐项复刻 WebUI 的 `Sidebar`。
|
||||
//
|
||||
// WebUI 的 `Sidebar`(components/Sidebar.tsx)是 60px 宽的图标列:
|
||||
// 品牌标(顶) / 通信·日历·联系人(中) / 设置(底)
|
||||
// 选中态 = 图标+文字变色 + 品牌浅底,**不加背景块/指示条**。
|
||||
// ★★ 2026-09-19 重写本文件的头部说明(上面那一版是错的,与它判的东西矛盾):
|
||||
// 旧文写的是「60px 图标轨 / 通信·日历·联系人(中)/ 设置(底)/
|
||||
// 选中态只换颜色 + 品牌浅底,**不加背景块**」—— 那个描述是**编的**:
|
||||
// · `Sidebar.tsx:26-44` 是**三项**(通信/日历/**联系**)——「我的」不在导航轨里,
|
||||
// 而是 **底部头像按钮**(`Sidebar.tsx:186`);
|
||||
// · `Sidebar.tsx:110` 有 `<span className="text-3xs">{short}</span>`(有文字);
|
||||
// · `index.css:1590` 的 `.nav-item[data-active='true']` **有底色块**,
|
||||
// 而且 CSS 注释专门说明侧栏**必须有**(“图标底下那一块底色是它唯一的选中线索”)。
|
||||
// 更糟的是:判据当时**引用了这段自编的描述当依据**,于是把错误锁死 —— 全绿。
|
||||
// 现在的口径:每一项都回读 WebUI 源码取值,不再引“我上次写的那句话”。
|
||||
//
|
||||
// 鸿蒙侧原来是**没有**宽屏布局的(只有底部导航条),WideSidebar 补的就是
|
||||
// "宽屏模式"这半:≥768vp 时 MainPage 挂侧栏、藏底部条(见 MainPage.build 的 onAreaChange)。
|
||||
// 这条判据钉的是 Sidebar 自己的形状:宽 60vp、三项导航 + 设置、选中态只换颜色。
|
||||
// 鸿蒙侧原来**没有**宽屏布局(只有底部导航条),WideSidebar 补的就是"宽屏模式"这半:
|
||||
// ≥768vp 时 MainPage 挂侧栏、藏底部条(见 MainPage.build 的 onAreaChange)。
|
||||
|
||||
import { code, prose, stripComments } from './lib/read.mjs';
|
||||
import { readFileSync } from 'node:fs';
|
||||
@ -18,8 +24,12 @@ import assert from 'node:assert/strict';
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = join(HERE, '..', '..', '..');
|
||||
const HARMONY_ETS = join(ROOT, 'client/harmony/entry/src/main/ets');
|
||||
/** WebUI 源码根:判据要**回读**它取值,而不是引注释里的话 */
|
||||
const WEBUI_SRC = join(ROOT, 'client/electron/src');
|
||||
|
||||
const read = p => prose(join(HARMONY_ETS, p));
|
||||
/** 读 WebUI 的源文件(**不去注释**:注释里就有真实理由) */
|
||||
const readWeb = p => readFileSync(join(WEBUI_SRC, p), 'utf8');
|
||||
|
||||
/** 取某个成员/方法正文(按行切到下一个成员声明) */
|
||||
function memberBody(src, signature) {
|
||||
@ -41,41 +51,84 @@ test('① 宽度:SIDEBAR_WIDTH = 60vp(WebUI 的 `w-[60px]` 是同一数字
|
||||
assert.match(main, /WideSidebar\(/, 'MainPage 要真正挂 WideSidebar');
|
||||
});
|
||||
|
||||
test('② 四项导航(与底栏同源):不再有单列的"设置"入口', () => {
|
||||
test('② 侧栏**三项**(通信/日历/联系)+ 底部一簇 —— 与底栏四项不是同一份清单', () => {
|
||||
/*
|
||||
* ★★ 2026-09-18 重写。原来这条判的是「三项导航 + 设置按钮」,
|
||||
* 依据是"设置单列在下面、遍历 NAV_ITEMS 会出现两个 person 图标"。
|
||||
* 那个依据**两半都错**:
|
||||
* · `NAV_CONTENT_ITEMS = NAV_ITEMS.slice(0, NAV_CONTENT_COUNT)` 而
|
||||
* `NAV_CONTENT_COUNT = 4` ⇒ **四项全在**(含「我的」),不存在"只有前三项";
|
||||
* · 单列的"设置"走 `onSettings → pushUrl('pages/SettingsPage')`,
|
||||
* 而那正是用户 2026-09-17 报过的「我的页面完全没有遵守 nav 的导航规则」
|
||||
* (底栏那一支改成了窗格,侧栏这一支漏了)。
|
||||
* ★★ 2026-09-19 重写(用户:「你写的app和webui大面积不符,问题特别大」)。
|
||||
*
|
||||
* WebUI `Sidebar.tsx` 的真实形状:`navItems` 三项 + **底部一簇**
|
||||
* (账号头像 / 主题切换 / 退出)。「我的」在 WebUI 是 `viewMode === 'account'`,
|
||||
* 由侧栏底部那个**头像按钮**进入 —— 也就是说它是**导航项**,不是"推出去的页"。
|
||||
* 鸿蒙的对应物是第 4 项「我的」窗格(与底栏一致)。
|
||||
* 上一版这条判的是「遍历 `NAV_CONTENT_ITEMS`」—— 而 `NAV_CONTENT_ITEMS` 是
|
||||
* **四项**(含「我的」)。那正是**错的形状**:四项只对**底栏**。
|
||||
*
|
||||
* WebUI 两套导航**本来就不同**(同一份源码里两个文件):
|
||||
* · `NarrowNav.tsx:37-40` items = 通信/日历/**联系人**,
|
||||
* 再加第 4 个「我的」按钮(`NarrowNav.tsx:143`)⇒ 底栏 **4 项**;
|
||||
* · `Sidebar.tsx:26-44` navItems = 通信/日历/**联系** ⇒ 侧栏 **3 项**,
|
||||
* 「我的」是底部**头像按钮**(`Sidebar.tsx:186`,旁边的注释就说它是
|
||||
* `title=...点击管理账号`)。
|
||||
*
|
||||
* 这条件判据的写法变了:不再只查鸿蒙源码的字符串,而是
|
||||
* **回读 `Sidebar.tsx` 数出 `short:` 的个数**,要求鸿蒙那一份也是同一数字 ——
|
||||
* 否则下次 WebUI 改了项数,这里只会继续绿。
|
||||
*/
|
||||
const sidebar = read('pages/WideSidebar.ets');
|
||||
const code_ = stripComments(sidebar);
|
||||
assert.match(code_, /ForEach\(NAV_CONTENT_ITEMS,/,
|
||||
'侧栏要遍历 NAV_CONTENT_ITEMS(与底栏同一个清单 —— 两处各留一份会漂移)');
|
||||
const sidebarSrc = readWeb('components/Sidebar.tsx');
|
||||
const narrowSrc = readWeb('components/NarrowNav.tsx');
|
||||
|
||||
// 从 WebUI 源码里数导航项:`short: 'xx'` 的行(两处各数一遍)
|
||||
const shortOf = (src) => [...src.matchAll(/short:\s*'([^']+)'/g)].map(m => m[1]);
|
||||
const webSidebarShort = shortOf(sidebarSrc);
|
||||
const webNarrowShort = shortOf(narrowSrc);
|
||||
|
||||
assert.deepEqual(webSidebarShort, ['通信', '日历', '联系'],
|
||||
`WebUI 侧栏应为 3 项(读到的是 ${JSON.stringify(webSidebarShort)})——若这里变了,下面的期望值要跟着改`);
|
||||
assert.deepEqual(webNarrowShort, ['通信', '日历', '联系人'],
|
||||
`WebUI 底栏内容项应为 3 项(读到的是 ${JSON.stringify(webNarrowShort)})——与侧栏的第三项措辞**故意不同**`);
|
||||
|
||||
// 鸿蒙侧:侧栏清单必须与 WebUI 侧栏逐字一致(项数与文案)
|
||||
const navItems = read('model/NavItems.ts');
|
||||
const sidebarList = navItems.match(/export const NAV_SIDEBAR_ITEMS[\s\S]*?\];/);
|
||||
assert.ok(sidebarList, 'model/NavItems.ts 要有 NAV_SIDEBAR_ITEMS(侧栏专用的三项清单)');
|
||||
const harmonySidebarShort = [...sidebarList[0].matchAll(/label:\s*'([^']+)'/g)].map(m => m[1]);
|
||||
assert.deepEqual(harmonySidebarShort, webSidebarShort,
|
||||
`NAV_SIDEBAR_ITEMS 的 label 要与 WebUI 侧栏逐字一致(现在读到 ${JSON.stringify(harmonySidebarShort)})`);
|
||||
|
||||
// 侧栏**不许**用四项那份清单(那正是这次报的“多出一个我的”)
|
||||
const sidebar = stripComments(read('pages/WideSidebar.ets'));
|
||||
assert.match(sidebar, /ForEach\(NAV_SIDEBAR_ITEMS,/,
|
||||
'侧栏要遍历 NAV_SIDEBAR_ITEMS(三项)');
|
||||
assert.ok(!/ForEach\(NAV_CONTENT_ITEMS,/.test(sidebar),
|
||||
'侧栏不许再遍历 NAV_CONTENT_ITEMS(那是**底栏**的四项清单,含「我的」)');
|
||||
|
||||
// 品牌标:WebUI 是 `<button data-testid="brand-mark"><BrandMarkIcon/>` —— 可点 + brandMark 图标
|
||||
assert.match(code_, /iconName: 'brandMark'/, '要有品牌标(WebUI `brand-mark` 的对应物,图标是 brandMark)');
|
||||
// 「我的」必须在侧栏里作为一个**导航项**出现,且走 onSelect 而不是推页
|
||||
assert.ok(!/onSettings/.test(code_),
|
||||
assert.match(sidebar, /iconName: 'brandMark'/, '要有品牌标(WebUI `brand-mark` 的对应物,图标是 brandMark)');
|
||||
assert.ok(!/onSettings/.test(sidebar),
|
||||
'侧栏不许留 onSettings(它推 `pages/SettingsPage` ⇒ 侧栏整条消失,正是用户报过的形状)');
|
||||
/*
|
||||
* 「我的」由上面那个 `ForEach(NAV_CONTENT_ITEMS)` 覆盖(`NAV_CONTENT_ITEMS`
|
||||
* 含第 4 项)—— 所以这里判的是"没有另起一个推页入口",
|
||||
* 以及 onSelect 能把 index 传到第 4 项(`normalizeNavIndex` 不截到 3 项)。
|
||||
*/
|
||||
assert.match(code_, /onClick\(\(\) => \{ this\.onSelect\(index\); \}\)/,
|
||||
'导航项点击要原样传 index(第 4 项「我的」因此走 onSelect(3) = 窗格)');
|
||||
// 品牌标点它要回第一项(WebUI `onClick={() => setViewMode('inbox')}`)
|
||||
assert.match(code_, /onClick\(\(\) => \{ this\.onSelect\(0\); \}\)/,
|
||||
assert.match(sidebar, /onClick\(\(\) => \{ this\.onSelect\(0\); \}\)/,
|
||||
'品牌标点击要回收件箱(WebUI brand-mark 的一致习惯:点左上角 logo 回家)');
|
||||
|
||||
/*
|
||||
* 底部一簇 —— `Sidebar.tsx:183-212`:头像(含连接点)/ 主题切换 / 退出。
|
||||
* 三者漏一个都会在并排截图里缺一块。
|
||||
*
|
||||
* ★ 判**真的被调用**(`this.onToggleTheme()`),不是"文件里出现过这个名字":
|
||||
* 第一版就写了 `/onToggleTheme/` —— 变异测试当场证明它不咬:
|
||||
* 把 `this.onToggleTheme()` 换成 `this.onSelect(0)` 后,
|
||||
* 属性**声明**还在,正则照样匹上,判据全绿。同 `navBadgeCount` 那一课的同一个错。
|
||||
*/
|
||||
assert.match(sidebar, /this\.onToggleTheme\(\)/, '底部主题键要真的调 onToggleTheme()');
|
||||
assert.match(sidebar, /this\.onLogout\(\)/, '底部退出键要真的调 onLogout()');
|
||||
/* 头像:WebUI 取用户名前两字(`Sidebar.tsx:190` 的 `.slice(0, 2)`) */
|
||||
assert.match(sidebar, /\.slice\(0, 2\)/, '头像要取展示名前两字(WebUI `.slice(0, 2)`)');
|
||||
/* 头像右下角要带 SSE 连接状态点(WebUI 的 `<ConnectionIndicator/>`) */
|
||||
assert.match(sidebar, /sseColorOf\(/, '头像要带连接状态点(颜色由 SSE 状态决定)');
|
||||
const webInd = readWeb('components/ConnectionIndicator.tsx');
|
||||
for (const [status, hex] of [['connected', '#22C55E'], ['connecting', '#FACC15'], ['reconnecting', '#FB923C'], ['disconnected', '#F87171']]) {
|
||||
/* WebUI 用的是 tailwind 类名,这里判的是"四档都有"这个结构,不是具体色值 */
|
||||
assert.ok(new RegExp(`${status}:`).test(webInd), `WebUI 的连接指示器要有 ${status} 档`);
|
||||
assert.ok(sidebar.includes(hex), `鸿蒙的连接点要有 ${status} 对应的色值 ${hex}`);
|
||||
}
|
||||
/* 「我的」由**头像**进入(不是导航轨里的一项) */
|
||||
assert.match(sidebar, /this\.onSelect\(ME_PANE_INDEX\)/,
|
||||
'头像点击要进「我的」窗格(ME_PANE_INDEX)—— WebUI 是 setViewMode(\'account\')');
|
||||
});
|
||||
|
||||
test('③ 侧栏选中态 = **浅蓝底块** + 变色(与底栏"只变色"是两条纪律)', () => {
|
||||
@ -108,17 +161,29 @@ test('③ 侧栏选中态 = **浅蓝底块** + 变色(与底栏"只变色"是
|
||||
`Theme.navActiveBg 要等于 WebUI 的 --nav-active-bg(${hex})`);
|
||||
|
||||
// 选中态必须**有底块**(WebUI 侧栏唯一的选中线索)
|
||||
assert.match(code_, /backgroundColor\(this\.currentIndex === index \? Theme\.navActiveBg : Color\.Transparent\)/,
|
||||
/*
|
||||
* ★ 2026-09-19:表达式从 `this.currentIndex === index` 改成
|
||||
* `this.currentIndex === sidebarContentIndex(item.key)` ——
|
||||
* 侧栏现在只画三项,而 currentIndex 是**四项**那套(`me` = 3),
|
||||
* 所以不能拿构图下标直接比。两条都接受(比的是"这个项是不是选中项")。
|
||||
*/
|
||||
assert.match(code_, /backgroundColor\(this\.currentIndex === (index|sidebarContentIndex\(item\.key\)) \? Theme\.navActiveBg : Color\.Transparent\)/,
|
||||
'导航项选中要有底块(WebUI `.nav-item[data-active]` 的 --nav-active-bg)—— 侧栏不是底栏,不能只变色');
|
||||
|
||||
// 文字标签:WebUI `Sidebar.tsx:110` 有 <span>{short}</span>,所以侧栏**有** label
|
||||
assert.match(code_, /Text\(label\)/,
|
||||
/*
|
||||
* 文字标签:WebUI `Sidebar.tsx:110` 有 `<span>{short}</span>`,所以侧栏**有** label。
|
||||
* ★ 2026-09-19:侧栏改成 `Text(item.label)`(从清单取,不是参数传)——
|
||||
* 两种写法都接受,只要真的把 label 交给了 `Text`。
|
||||
*/
|
||||
assert.match(code_, /Text\((label|item\.label)\)/,
|
||||
'侧栏导航项要有文字标签(WebUI Sidebar 有 <span>{short}</span>)');
|
||||
|
||||
// ★ 自检:这三条都必须能判红
|
||||
assert.ok(!/backgroundColor\(this\.currentIndex === index \? Theme\.navActiveBg : Color\.Transparent\)/.test(
|
||||
'backgroundColor(this.currentIndex === index ? Color.Transparent : Theme.navActiveBg)'),
|
||||
'自检:三元式写反了必须判红');
|
||||
assert.ok(!/Text\((label|item\.label)\)/.test('Text(short)'),
|
||||
'自检:标签没交给 Text 必须判红');
|
||||
});
|
||||
|
||||
test('④ MainPage 接线:宽屏才挂侧栏、宽屏藏底部条、断点 768', () => {
|
||||
@ -275,4 +340,29 @@ test('⑦ 导航项徽标:取值/色调与 WebUI Sidebar 同口径(纯逻辑
|
||||
assert.ok(main.includes(key), `MainPage 要读 ${key}`);
|
||||
assert.ok(sidebar.includes(key), `WideSidebar 要读 ${key}(与底栏同一组键)`);
|
||||
}
|
||||
|
||||
/*
|
||||
* ⑧ 三档色调都要有**各自的底色**(不能把三档压成两档)。
|
||||
*
|
||||
* ★★ 2026-09-19 修 bug:底栏与侧栏两处原先都写
|
||||
* `tone === 'perm' ? warnFg : danger` —— 于是 `'plain'`(联系人数)走了**红**,
|
||||
* 看起来像"有未读"。并排截图一眼可见:WebUI 是石板灰的 4,鸿蒙是红的。
|
||||
*
|
||||
* ★ 期望值从 WebUI 的 CSS 里**读出来**,不写死:
|
||||
* `Sidebar.tsx:203-204` 的 `'plain'` 档是 `bg-chrome-600`,
|
||||
* 而 `--c-chrome-600: 71 85 105`(`index.css:92`)= #475569。
|
||||
*/
|
||||
const cssSrc = readWeb('index.css');
|
||||
const chromeM = cssSrc.match(/--c-chrome-600:\s*(\d+ \d+ \d+)/);
|
||||
assert.ok(chromeM, 'WebUI index.css 要有 --c-chrome-600(中性徽标底色)');
|
||||
const [cr, cg, cb] = chromeM[1].split(' ').map(Number);
|
||||
const plainHex = '#' + [cr, cg, cb].map(v => v.toString(16).padStart(2, '0').toUpperCase()).join('');
|
||||
const themeSrc = readFileSync(join(HARMONY_ETS, 'common', 'Theme.ets'), 'utf8');
|
||||
assert.match(themeSrc, new RegExp(`badgePlain: string = '${plainHex}'`, 'i'),
|
||||
`Theme.badgePlain 要等于 WebUI 的 --c-chrome-600(${plainHex})`);
|
||||
|
||||
for (const [name, src] of [['底栏', main], ['侧栏', sidebar]]) {
|
||||
assert.match(src, /Theme\.badgePlain/,
|
||||
`${name}的徽标底色要引用 Theme.badgePlain('plain' 档不能跟未读共用红色)`);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user