From 43a5c923b449584876b2b294e87f2f216a99b3c2 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Sun, 14 Jun 2026 08:46:35 -0600 Subject: [PATCH] Plan submit formatting button state --- ...26-06-14-submit-formatting-button-state.md | 259 ++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 docs/superpowers/plans/2026-06-14-submit-formatting-button-state.md diff --git a/docs/superpowers/plans/2026-06-14-submit-formatting-button-state.md b/docs/superpowers/plans/2026-06-14-submit-formatting-button-state.md new file mode 100644 index 0000000..13ba1f7 --- /dev/null +++ b/docs/superpowers/plans/2026-06-14-submit-formatting-button-state.md @@ -0,0 +1,259 @@ +# Submit Formatting Button State 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:** Show `格式化中` on the submit button during automatic formatting, then show `正在提交` continuously while the submission request is pending. + +**Architecture:** Extract the button presentation rules into a small pure TypeScript function so the state priority can be tested without adding a frontend test framework. Keep formatter and submission-request flags local to `SubmitCode.vue`, with `finally` blocks ensuring both flags clear on every outcome. + +**Tech Stack:** Vue 3 Composition API, TypeScript, Node.js built-in test runner, Rsbuild + +--- + +### Task 1: Define and test submit button presentation rules + +**Files:** +- Create: `tests/submitButtonState.test.ts` +- Create: `src/oj/problem/components/submitButtonState.ts` + +- [ ] **Step 1: Write the failing test** + +Create `tests/submitButtonState.test.ts`: + +```ts +import assert from "node:assert/strict" +import test from "node:test" +import { getSubmitButtonState } from "../src/oj/problem/components/submitButtonState.ts" + +const idleInput = { + isAuthed: true, + hasCode: true, + isFormatting: false, + isSubmitting: false, + isJudging: false, + isCooldown: false, +} + +test("shows a disabled loading state while formatting", () => { + assert.deepEqual( + getSubmitButtonState({ ...idleInput, isFormatting: true }), + { + disabled: true, + label: "格式化中", + icon: "eos-icons:loading", + }, + ) +}) + +test("shows submitting immediately after formatting", () => { + assert.deepEqual( + getSubmitButtonState({ ...idleInput, isSubmitting: true }), + { + disabled: true, + label: "正在提交", + icon: "eos-icons:loading", + }, + ) +}) + +test("preserves existing login, judging, cooldown, and idle states", () => { + assert.deepEqual( + getSubmitButtonState({ ...idleInput, isAuthed: false }), + { + disabled: true, + label: "请先登录", + icon: "ph:play-fill", + }, + ) + assert.deepEqual(getSubmitButtonState({ ...idleInput, isJudging: true }), { + disabled: true, + label: "正在评分", + icon: "eos-icons:loading", + }) + assert.deepEqual(getSubmitButtonState({ ...idleInput, isCooldown: true }), { + disabled: true, + label: "正在冷却", + icon: "ph:lightbulb-fill", + }) + assert.deepEqual(getSubmitButtonState(idleInput), { + disabled: false, + label: "提交代码", + icon: "ph:play-fill", + }) +}) +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: + +```bash +node --test tests/submitButtonState.test.ts +``` + +Expected: FAIL with `ERR_MODULE_NOT_FOUND` for `submitButtonState.ts`. + +- [ ] **Step 3: Implement the pure state function** + +Create `src/oj/problem/components/submitButtonState.ts`: + +```ts +export interface SubmitButtonStateInput { + isAuthed: boolean + hasCode: boolean + isFormatting: boolean + isSubmitting: boolean + isJudging: boolean + isCooldown: boolean +} + +export interface SubmitButtonState { + disabled: boolean + label: string + icon: string +} + +export function getSubmitButtonState({ + isAuthed, + hasCode, + isFormatting, + isSubmitting, + isJudging, + isCooldown, +}: SubmitButtonStateInput): SubmitButtonState { + const disabled = + !isAuthed || + !hasCode || + isFormatting || + isSubmitting || + isJudging || + isCooldown + + let label = "提交代码" + if (!isAuthed) { + label = "请先登录" + } else if (isFormatting) { + label = "格式化中" + } else if (isSubmitting) { + label = "正在提交" + } else if (isJudging) { + label = "正在评分" + } else if (isCooldown) { + label = "正在冷却" + } + + const icon = + isFormatting || isSubmitting || isJudging + ? "eos-icons:loading" + : isCooldown + ? "ph:lightbulb-fill" + : "ph:play-fill" + + return { disabled, label, icon } +} +``` + +- [ ] **Step 4: Run the test to verify it passes** + +Run: + +```bash +node --test tests/submitButtonState.test.ts +``` + +Expected: 3 tests pass. + +### Task 2: Connect formatting and submission request lifecycle to the button + +**Files:** +- Modify: `src/oj/problem/components/SubmitCode.vue` + +- [ ] **Step 1: Add local request states and computed presentation** + +Import `getSubmitButtonState`, add `isFormatting` and `isSubmittingRequest` refs, and replace the three existing button computed properties with: + +```ts +const buttonState = computed(() => + getSubmitButtonState({ + isAuthed: userStore.isAuthed, + hasCode: codeStore.code.value.trim() !== "", + isFormatting: isFormatting.value, + isSubmitting: isSubmittingRequest.value || submitting.value, + isJudging: judging.value || pending.value, + isCooldown: isCooldown.value, + }), +) +``` + +Use `buttonState.disabled`, `buttonState.icon`, and `buttonState.label` in the template. + +- [ ] **Step 2: Guard and track the formatting request** + +At the start of `submit`, return when `buttonState.value.disabled` is true. Around `formatCode`, set `isFormatting.value = true` before the request and clear it in `finally`: + +```ts +isFormatting.value = true +try { + const res = await formatCode({ + code: codeStore.code.value, + language: formatLang, + }) + codeStore.setCode(res.data.code) +} catch (e: any) { + if (e?.error === "format-error") { + message.warning(`代码格式化失败:${e.data},请检查代码后重试`) + return + } +} finally { + isFormatting.value = false +} +``` + +- [ ] **Step 3: Track the submission API request** + +Set `isSubmittingRequest.value = true` immediately before `submitCode`, keep the existing success flow inside the `try`, and clear the request state in `finally`: + +```ts +isSubmittingRequest.value = true +try { + const res = await submitCode(data) + console.log(`[Submit] 代码已提交: ID=${res.data.submission_id}`) + + startCooldown() + startMonitoring(res.data.submission_id) + showResult.value = true +} finally { + isSubmittingRequest.value = false +} +``` + +- [ ] **Step 4: Run focused tests** + +Run: + +```bash +node --test tests/submitButtonState.test.ts +``` + +Expected: 3 tests pass. + +- [ ] **Step 5: Run the production build** + +Run: + +```bash +npm run build +``` + +Expected: Rsbuild exits with status 0. + +- [ ] **Step 6: Check the final diff** + +Run: + +```bash +git diff --check +git diff -- src/oj/problem/components/SubmitCode.vue src/oj/problem/components/submitButtonState.ts tests/submitButtonState.test.ts +``` + +Expected: no whitespace errors; diff is limited to the button state feature and its test.