首页 / 资讯中心 / 文章详情

MooTool Next Compose 失败 Toast 呈现层收尾:时间/网络/Vault-Git 模块的判定链设计解析

MooTool Next Compose 失败 Toast 呈现层收尾:时间/网络/Vault-Git 模块的判定链设计解析 ★ FEATURED ARTICLE
桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载本文聚焦 MooTool 新一代 Compose Desktop 客户端next-compose中 DIFF-598 所覆盖的失败 Toast 呈现层收尾工作F18 时间戳转换失败提示、F11 网络任务失败提示含ABORTED跳过策略、以及 A03 Vault-Git 面板的 4 类失败提示。通过梳理TimeWiringPresentation、NetWiringPresentation、VaultGitToastPresentation三个呈现层对象及其在屏幕层Screen/Dialog的接入点读者可以完整掌握 MooTool 在引擎抛错 → 呈现层判定 → Toast/内联错误双通道输出这一链路上的设计约定并可直接在本地 JDK 21 环境复现验证命令。背景与范围DIFF-598 在整体迭代中的位置DIFF-598 是 MooTool Next Compose 桌面端一系列失败 Toast 呈现层收尾切片中的一环其基线为 DIFF-597工作区。DIFF-597 已覆盖 F01/F09 及证据帧 192本切片不重复处理这些条目只收尾以下三块F18notifyTimeConvertFailure沿用TimeWiringPresentation.shouldToastConvertFailure判定是否弹错误 ToastF11notifyNetActionResult网络任务完成时的失败 Toast 经shouldToastNetworkError判定跳过ABORTED用户主动取消场景A03VaultGitToastPresentation对象 notifyVaultGitPanelFailure/notifyVaultGitActionFailure/notifyVaultGitFlushBlocked/notifyVaultGitInvalidRemote四个面板层通知函数证据TimeToTimestampTabCaptureTest产生截图193-compose-time-to-timestamp-tab-focus.png。从实现位置看这一切片延续了本项目的统一分层约定纯函数判定逻辑放在domain层可单测IO 与 UI 装配放在features层的 Screen/DialogToast 出口统一收敛到AppContainer。一、统一 Toast 出口AppContainer 的三通道无论哪个模块触发失败提示最终都汇入 AppContainer.kt 提供的三通道方法fun toastSuccess(message: String) { toasts.success(message) } fun toastError(message: String) { toasts.error(message) } fun toastInfo(message: String) { toasts.info(message) }toasts是全局 Toast 队列状态错误、成功、信息三类提示统一走队列呈现。所有notify*函数内部最终都调用container.toastError(...)/container.toastSuccess(...)保证错误视觉样式与队列行为全局一致。二、F18时间戳转换失败 ToastTimeWiringPresentation notifyTimeConvertFailure2.1 呈现层判定函数TimeWiringPresentation.kt 是 F18 的核心它定义了转换动作的密封结果类型与失败判定sealed interface ConvertOutcome { data class ToLocal(val result: TimestampConversion) : ConvertOutcome data class ToTimestamp(val timestamp: String) : ConvertOutcome data class Failure(val error: Throwable) : ConvertOutcome } fun runTimestampToLocal(timestamp: String, unit: TimestampUnit, zone: String): ConvertOutcome runCatching { TimeEngine.timestampToLocal(timestamp, unit, zone) }.fold( onSuccess { ConvertOutcome.ToLocal(it) }, onFailure { ConvertOutcome.Failure(it) }, ) fun runLocalToTimestamp(localTime: String, unit: TimestampUnit, zone: String): ConvertOutcome runCatching { TimeEngine.localToTimestamp(localTime, unit, zone) }.fold( onSuccess { ConvertOutcome.ToTimestamp(it) }, onFailure { ConvertOutcome.Failure(it) }, ) fun shouldToastConvertFailure(error: Throwable): Boolean true要点转换动作不会直接抛异常而是用runCatching包裹后折叠为密封类型ConvertOutcome调用方通过when穷举分支处理shouldToastConvertFailure当前恒为true即所有转换失败都要弹 Toast。它保留为独立函数的意义在于把是否提示的决策集中到呈现层未来若要按错误类型静默如网络抖动类只需改这一处。2.2 屏幕层装配notifyTimeConvertFailureTimeConvertScreen.kt 中两个转换入口convertToLocal与convertToTimestamp均对ConvertOutcome.Failure分支调用notifyTimeConvertFailureis TimeWiringPresentation.ConvertOutcome.Failure - { notifyTimeConvertFailure(container, session, outcome.error) } private fun notifyTimeConvertFailure(container: AppContainer, session: TimeSession, error: Throwable) { session.notice val message messageFor(container, error) session.error message if (TimeWiringPresentation.shouldToastConvertFailure(error)) { container.toastError(message) } }这是本项目典型的双通道错误呈现内联通道把本地化消息写入session.error供屏幕内的错误区域显示Toast 通道经shouldToastConvertFailure判定后用container.toastError(message)弹出全局错误 Toast。同时session.notice被清空避免操作成功提示与操作失败提示同时残留。messageFor会把TimeException的错误码映射为本地化文案例如dst-gap→time.error.dstGap、dst-overlap→time.error.dstOverlap、invalid-local-time→time.error.localTime保证用户看到的是可读的中文说明而非底层异常文本。2.3 测试证据TimeWiringPresentationTest.kt 中明确了shouldToastConvertFailure的行为Test fun shouldToastConvertFailure() { assertTrue(TimeWiringPresentation.shouldToastConvertFailure(TimeException(invalid, ))) }此外本切片要求的证据截图由TimeToTimestampTabCaptureTest生成对应文件为 193-compose-time-to-timestamp-tab-focus.png用于验收时间戳→时间戳 Tab 聚焦场景的呈现。三、F11网络任务结果 Toast 与 ABORTED 跳过策略3.1 判定函数shouldToastNetworkErrorNetWiringPresentation.kt 提供的失败判定是本次切片的重点策略fun shouldToastNetworkError(errorCode: NetworkErrorCode?): Boolean errorCode ! null errorCode ! NetworkErrorCode.ABORTED语义拆解errorCode null任务成功完成不弹错误 Toast成功提示走另一个通道errorCode NetworkErrorCode.ABORTED任务被用户主动取消静默不提示——取消是用户意图弹错误反而构成打扰其余错误码如INVALID_TARGET、TIMEOUT、COMMAND_FAILED、PERMISSION、UNSUPPORTED一律弹错误 Toast。NetworkErrorCode枚举定义在 NetEngine.ktNetEngine内部在取消点统一抛出NetException(NetworkErrorCode.ABORTED, ABORTED)见该文件多处handle.isCancelled()检查因此上层可以从result.errorCode直接判断是失败还是被取消。3.2 屏幕层装配notifyNetActionResultNetScreen.kt 中网络命令ping、端口扫描、whois、解析、flushDNS 等执行完毕后统一调用notifyNetActionResultprivate fun notifyNetActionResult( container: AppContainer, session: NetSession, errorCode: NetworkErrorCode?, successLabel: String, ) { if (session.error.isNotEmpty()) { if (NetWiringPresentation.shouldToastNetworkError(errorCode)) { container.toastError(session.error) } } else { container.toastSuccess(successLabel) } }调用方在 NetScreen.kt#L196-L208 完成输出装配session.error if (result.errorCode ! null result.errorCode ! NetworkErrorCode.ABORTED) { localized ?: result.output } else val label networkActionLabel(container, action) session.notice label notifyNetActionResult(container, session, result.errorCode, label)注意两层过滤的配合session.error只对非 ABORTED的错误码写入内联错误与shouldToastNetworkError的跳过策略一致notifyNetActionResult中session.error非空才走 Toast 判定通道否则弹成功 ToastsuccessLabel来自networkActionLabel如 ping →net.ping。此外ABORTED场景的输出文本会被替换为本地化文案net.error.ABORTED当原始输出仅为ABORTED或空串时避免把引擎内部标记直接展示给用户。3.3 测试证据NetWiringPresentationTest.kt 精确锁定该策略Test fun shouldToastNetworkErrorSkipsAborted() { assertFalse(NetWiringPresentation.shouldToastNetworkError(NetworkErrorCode.ABORTED)) assertTrue(NetWiringPresentation.shouldToastNetworkError(NetworkErrorCode.INVALID_TARGET)) assertFalse(NetWiringPresentation.shouldToastNetworkError(null)) }三个断言分别覆盖取消不弹、真实失败弹、成功不弹三条关键路径。四、A03Vault-Git 面板的四类失败 Toast4.1 呈现层对象VaultGitToastPresentationVaultGitToastPresentation.kt 位于commonMain而非 desktopMain说明它同时服务 Compose 各平台目标object VaultGitToastPresentation { fun shouldToastPanelFailure(): Boolean true fun shouldToastActionFailure(): Boolean true fun shouldToastFlushBlocked(): Boolean true fun shouldToastInvalidRemote(): Boolean true fun panelFailureMessage(error: Throwable?, fallback: String): String error?.message?.takeIf { it.isNotBlank() } ?: fallback fun actionFailureMessage(message: String, fallback: String): String message.ifBlank { fallback } }设计要点四个shouldToast*开关默认全开与 F18 的shouldToastConvertFailure同一思路——开关前置、集中在呈现层panelFailureMessage/actionFailureMessage提供消息回退策略优先取异常/结果的真实消息仅当其为空或空白时使用调用方传入的兜底文案通常是json.notice.failed的本地化版本。4.2 四个 notify 函数的触发场景与实现四个通知函数全部实现在 VaultGitDialog.kt分别对应面板生命周期中的四类失败① 面板加载/操作抛异常 → notifyVaultGitPanelFailure在load()与runAction()的catch (error: Exception)分支触发见 VaultGitDialog.kt#L131-L133 与 VaultGitDialog.kt#L181-L182private fun notifyVaultGitPanelFailure(container: AppContainer, error: Throwable?) { val message VaultGitToastPresentation.panelFailureMessage(error, container.t(json.notice.failed)) if (VaultGitToastPresentation.shouldToastPanelFailure()) { container.toastError(message) } }② Git 操作返回失败结果 → notifyVaultGitActionFailurerunAction中当result.success false时触发VaultGitDialog.kt#L163。注意 Pull 失败后仍会触发onVaultRefresh()刷新工作区而其余操作失败直接returnlaunchprivate fun notifyVaultGitActionFailure(container: AppContainer, result: GitActionResult) { val message VaultGitToastPresentation.actionFailureMessage(result.message, container.t(json.notice.failed)) if (VaultGitToastPresentation.shouldToastActionFailure()) { container.toastError(message) } }③ 编辑器未保存导致 flush 被阻断 → notifyVaultGitFlushBlockedrunAction执行 Git 操作前会先经GitEditorFlushPolicy.shouldFlushEditor(workingTree)判定是否需要先 flush 编辑器若 flush 返回非空存在未保存修改则阻断操作并弹 ToastVaultGitDialog.kt#L148-L153if (GitEditorFlushPolicy.shouldFlushEditor(workingTree)) { val flushed onFlush() if (flushed ! null) { notifyVaultGitFlushBlocked(container, flushed) returnlaunch } }GitEditorFlushPolicy见 GitEditorFlushPolicy.kt对 Pull/Commit/Push 返回shouldFlushEditor true对 Discard 返回false其测试 GitEditorFlushPolicyTest.kt 同时验证了 Pull 成功后刷新 Vault 的行为。④ 远端地址格式非法 → notifyVaultGitInvalidRemote在保存/移除远端按钮的提交校验分支触发VaultGitDialog.kt#L471-L478。SettingsVaultGitNormalize.commitGitRemote返回Rejected时直接拦截按钮并弹错误GitRemoteCommitResult.Rejected - { notifyVaultGitInvalidRemote(container) returnMooButton }其实现使用本地化文案settings.vault.gitRemoteInvalidprivate fun notifyVaultGitInvalidRemote(container: AppContainer) { if (VaultGitToastPresentation.shouldToastInvalidRemote()) { container.toastError(container.t(settings.vault.gitRemoteInvalid)) } }4.3 测试证据VaultGitToastPresentationTest.kt 用 3 个用例覆盖全部核心行为Test fun shouldToastFailures() { assertTrue(VaultGitToastPresentation.shouldToastPanelFailure()) assertTrue(VaultGitToastPresentation.shouldToastActionFailure()) assertTrue(VaultGitToastPresentation.shouldToastFlushBlocked()) assertTrue(VaultGitToastPresentation.shouldToastInvalidRemote()) } Test fun panelFailureMessagePrefersThrowable() { assertEquals(disk, VaultGitToastPresentation.panelFailureMessage(IllegalStateException(disk), fb)) assertEquals(fb, VaultGitToastPresentation.panelFailureMessage(IllegalStateException(), fb)) } Test fun actionFailureMessageUsesResultOrFallback() { assertEquals(pull failed, VaultGitToastPresentation.actionFailureMessage(pull failed, fb)) assertEquals(fb, VaultGitToastPresentation.actionFailureMessage(, fb)) }panelFailureMessagePrefersThrowable特别验证了回退语义异常消息为空时使用兜底文案异常消息非空时优先展示真实原因。五、贯穿三个模块的共性设计约定综合 F18 / F11 / A03可以提炼出 MooTool Next Compose 失败提示的统一模式这也是理解后续同类切片如 585/590/591/592 的 time-host-quicknote、net、config-protobuf-hardware、coderun-encode-ua 等 toast slice的钥匙决策与 I/O 分离shouldToastXxx是纯布尔函数位于domain层不接触 UI 与容器天然可单测通知函数统一命名notifyXxx(container, ...)收口在 features 层内部先拼装本地化消息再经shouldToastXxx开关决定是否调用container.toastError(...)双通道呈现内联错误session.error与全局 Toast 并行shouldToast只控制 Toast 通道内联错误通常始终保留消息回退策略panelFailureMessage/actionFailureMessage这类辅助函数保证任何异常/结果都至少有一个兜底文案不会出现空 Toast主动取消静默ABORTED不弹 Toast 是全局共识翻译模块TranslationWiringPresentation.shouldToastTranslateFailure、HTTP 模块HttpResponsePresentation、代码运行模块CodeRunWiringPresentation.shouldToastRunFailure都有同构的跳过逻辑。六、本地验证方法DIFF-598 的验证命令要求JDK 21在next-compose目录下执行./gradlew :composeApp:desktopTest --offline--offline不访问远程仓库使用本地 Gradle 缓存适合本切片对应的纯桌面端测试涉及desktopMain/desktopTest相关测试类集中在composeApp/src/desktopTest/kotlin/com/rememberber/mootool/next/compose/domain/运行后可重点观察TimeWiringPresentationTest、NetWiringPresentationTest、VaultGitToastPresentationTest、GitEditorFlushPolicyTest四类用例的输出。小结DIFF-598 用极小的改动面完成了时间、网络、Vault-Git 三个模块的失败 Toast 收尾F18 沿用TimeWiringPresentation.shouldToastConvertFailureF11 通过NetWiringPresentation.shouldToastNetworkError跳过ABORTED取消场景A03 新增VaultGitToastPresentation统一承载面板加载失败、操作失败、flush 阻断、远端非法四类提示。这套呈现层开关 通知函数收口 双通道输出 本地化回退的模式让 MooTool 的错误提示既能全局一致也能按模块精细调控是可复制的 Compose Desktop 失败反馈架构范本。赞分享桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载相关推荐Symfony Process 组件 CHANGELOG 深度解读从 Process 到 PhpSubprocess 与 Messenger 的完整演进Symfony Process 组件 CHANGELOG 深度解读从 Process 到 PhpSubprocess 与 Messenger 的完整演进 本篇桌面应用开发工具MooTool Next Compose Vault Git 面板 runAction 异常处理与 toast 呈现对齐实践MooTool Next Compose Vault Git 面板 runAction 异常处理与 toast 呈现对齐实践 Vault Git 面板是 Moo桌面应用开发工具解析 remote-jobs 仓库中的 Checkly 公司档案从 frontmatter 字段到 API/E2E 监控技术栈解析 remote jobs 仓库中的 Checkly 公司档案从 frontmatter 字段到 API/E2E 监控技术栈 导读 本文以开源仓库 GitH桌面应用开发工具上一篇SmolAgents 终极工具集成指南从 MCP 服务器到 LangChain 的无缝接入下一篇手速不够也能抢到票大麦自动抢票Python工具从零到成功下单的完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站