Files
wordpress-theme-aksrt/.specs/plans/frontend-modal-runtime.md
T
2026-08-05 02:04:55 +08:00

73 lines
7.6 KiB
Markdown

# Plan: Frontend Modal Runtime
## Architectural Decisions
- **Canonical runtime**: `assets/js/core/modal-runtime.js` is the single `AksrtModalRuntime` seam for shell creation, lifecycle, focus, loading, error, cancellation, and refresh. `assets/js/core/modal-triggers.js` only binds the new declarative trigger contract; no legacy modal alias or compatibility bridge exists.
- **Loader contract**: A loader receives a domain context and abort signal and returns a minimal view model plus render/update callbacks. `AksrtModalRuntime.openRemote({responseType: 'json', renderData})` owns the request, cache, cancellation, and lifecycle while the domain renderer owns only view-model markup. It may read only the active modal's data.
- **Cache contract**: Use in-memory, user-scoped cache entries with short TTLs, in-flight request deduplication, explicit invalidation tags, and complete clearing on logout/account switch. Do not persist private modal payloads in localStorage.
- **State contract**: All domains use the same `idle`, `loading`, `ready`, `empty`, `submitting`, `success`, `error`, `unauthorized`, and `stale` states. Skeletons match the final layout and never represent business truth.
- **API boundary**: Existing `aksrt/v1` domain routes remain the service boundary; HTML modal responses are replaced domain by domain with minimal JSON view models. Every changed REST route keeps a `permission_callback`; AJAX is not a new data path.
- **Request adapter**: Add one thin `AksrtRequest` adapter for the injected REST root, nonce headers, JSON/error envelopes, 401 handling, and abort signals. Domain files stop maintaining their own REST-root and response-parsing variants.
- **Rehydrate and feedback**: Every runtime-rendered HTML response calls `AksrtFrontend.rehydrate(body, 'modal')`; shared feedback calls the existing callable `AksrtToast` API directly. There is one binding path and no compatibility fallback.
- **Rollout**: Add a temporary per-domain rollout section under the existing Tools configuration surface, read through `AKSRT_Settings`. One canonical new implementation is enabled per domain; the switch is for staged release, not a compatibility data layer.
- **Telemetry**: Use browser `performance.mark/measure` and server `Server-Timing`/existing deployment observability by default. Do not add a private payload or payment/identity-content event store in this foundation plan.
- **Build boundary**: Frontend assets are plain theme JavaScript, not the Admin SPA bundle. Run `node --check` for changed JS and run the admin contract audit only when the rollout schema/controller changes.
---
## Phase 1 — Modal latency is measurable and rollout is controllable
**User stories**: US1, US2, US3, US14.
### What to build
Create the typed rollout reader and configuration contract, then add a small telemetry module that marks the existing VIP, order, identity, and badge open flows without changing their business behavior. Establish cold-cache and warm-cache baselines for click-to-shell, click-to-first-content, click-to-ready, request count, SQL count, response size, and error outcome. The rollout reader must be consumed through `AKSRT_Settings` and the existing Tools schema/controller path, not a raw option read.
Primary integration points:
- `AKSRT_Settings` and the Tools schema/controller for `frontend_modal_rollout`.
- `assets/js/core/modal-telemetry.js` (new) and the existing feature enqueue/config injection in `src/Core/ScriptsPart1c.php` and the surface script enqueue modules.
- Existing flows in `assets/js/pay-vip.js`, `assets/js/order-center.js`, `assets/js/routes/user-center-auth.js`, and `assets/js/user-badges.js` for baseline marks only.
### Done when
- [x] The Tools schema, REST response, and `AKSRT_Settings` reader expose independent VIP, order, identity, and badge rollout values; `php tools/audit-admin-contracts.php` exits 0.
- [x] `assets/js/core/modal-telemetry.js` emits click, shell, first-content, ready, action-start, action-end, and error marks without including payment credentials, order contents, or identity evidence.
- [ ] Browser scenario `modal-baseline` records cold and warm measurements for all four current entry points, and the baseline artifact names the request/SQL counts used for later comparison.
- [x] `node --check assets/js/core/modal-telemetry.js assets/js/pay-vip.js assets/js/order-center.js assets/js/routes/user-center-auth.js assets/js/user-badges.js` passes.
---
## Phase 2 — Any modal opens with a cancellable shell and reusable cache
**User stories**: US1, US2, US3, US12, US13.
### What to build
Build the canonical `AksrtModalRuntime` in `assets/js/core/modal-runtime.js` with focused request, cache, state, and trigger modules. The runtime must show a skeleton before data, use the single new modal trigger contract, abort requests on close/replacement, deduplicate reads, invalidate explicit tags, restore focus, support keyboard/overlay close, mobile bottom-sheet behavior, retryable errors, and call `AksrtFrontend.rehydrate` after every HTML render. All domain callers use the canonical request/runtime/Toast APIs directly; no legacy alias, second host, or migration compatibility path is retained.
### Done when
- [x] `assets/js/core/modal-runtime.js` exposes one canonical `AksrtModalRuntime.open/close/refresh` lifecycle, `AksrtRequest` owns REST root/nonce/envelope handling, and no domain script creates a second global modal host.
- [x] `node --check assets/js/core/modal-triggers.js assets/js/core/request.js assets/js/core/modal-runtime.js assets/js/core/modal-cache.js assets/js/core/modal-state.js assets/js/pay-vip.js` passes.
- [ ] Browser scenario `modal-shell-lifecycle` verifies shell visibility within the 100ms initial budget, skeleton/error/retry states, ESC and overlay close, focus return, request cancellation, mobile bottom-sheet behavior, callable Toast, and `AksrtFrontend.rehydrate(body, 'modal')` after runtime HTML.
- [ ] Browser scenario `modal-cache-dedupe` verifies one read for concurrent/repeated opens, invalidation after a successful write, no reuse after logout/account switch, and one pay-vip binding; `git grep -n "AksrtRemoteModal\|RefreshModal\|data-aksrt-remote-modal" -- '*.js' '*.php' '*.css'` returns no matches.
---
## Out of Scope
- 修改会员价格、会员权益、支付网关规则、订单账务、退款核算、认证资格/审核政策或徽章授予条件;这些规则只允许被新的流程正确调用和展示。
- 新增支付渠道、替换支付供应商、在浏览器保存支付凭证,或改变支付回调/异步结算机制。
- 新增公开认证资料库、向其他用户暴露认证文档、跨用户查看订单或徽章数据。
- 把订单、售后、认证审核或徽章运营工作台迁移进配置型 SPA;日常运营仍属于原生 WordPress 后台。
- 全站页面导航、用户中心信息架构、品牌设计系统或所有非弹窗页面的全面重做;本期只统一弹窗运行时和四类流程所需的轻量体验。
- 强制页面加载时预取所有弹窗数据、长期持久化敏感弹窗数据、建立订单/认证/徽章的第二套事实存储或长期保留旧新双路实现。
- 以新增兼容别名、旧 option 双读、静默降级或数据迁移层解决旧实现问题;流程切换后以新的规范化链路为唯一实现。
- 建立独立的数据仓库、复杂转化漏斗或第三方用户行为追踪系统;本期观测只服务于弹窗性能、错误和业务状态闭环。
## Open Questions
- 部署环境是否已有 RUM/Server-Timing 收集端?默认先交付浏览器 marks 与响应头,不在主题内新增持久化指标表。
- `frontend_modal_rollout` 的临时开关在所有领域切换完成后的删除版本,需要在发布记录中确定。