Skip to content

[Epic] 單字超人 2.0:上架可靠性、離線優先與核心學習體驗優化 #5

Description

@github-world192

背景

目前 App 已具備 7 級單字、搜尋、收藏、快取與學習進度,已能作為 MVP 使用;但現行版本仍偏向「單字瀏覽器」,且正式上架可靠性、簽章安全、資料來源與可測試性仍有風險。

本 Issue 的目標是在不過度擴張第一版範圍的前提下,完成一個可正式發布、可離線使用,並具有實際學習循環的版本。

產品目標

讓使用者可以完成以下核心流程:

  1. 選擇程度
  2. 瀏覽或搜尋單字
  3. 查看完整解釋並播放發音
  4. 使用翻卡進行快速複習
  5. 透過「不熟/學習中/已掌握」記錄真實進度
  6. 離線時仍可正常學習

本期成功標準

  • 首次開啟即使沒有網路也能看到單字。
  • Android release 版能正常載入線上更新。
  • 使用者可以播放單字發音。
  • 使用者可以完成一輪翻卡學習並記錄結果。
  • 「已掌握」不再只因點開詳細頁就自動成立。
  • 收藏、學習狀態與進度在重新啟動後仍保留。
  • 核心資料與狀態邏輯具備自動化測試。

P0:上架可靠性與安全

  • 在主要 Android Manifest 加入網路權限:
    <uses-permission android:name="android.permission.INTERNET" />
  • 移除 android/app/build.gradle 內硬編碼的 keystore 密碼。
  • 改由未納入版本控制的 key.properties 或 CI secrets 注入簽章資訊。
  • key.properties*.jks*.keystore 加入 .gitignore
  • 若目前金鑰或密碼曾用於正式發布,輪替密碼/金鑰並確認 Google Play App Signing 狀態。
  • HTTP 請求加入 10 秒 timeout,並區分 timeout、無網路、HTTP 錯誤及資料格式錯誤。
  • 所有 async callback 在更新 UI 前確認 mounted
  • 補上 Android release smoke test:安裝 release build 後能載入單字或使用離線資料。

P0:離線優先資料策略

目前首次載入依賴第三方 GitHub Raw JSON;來源改名、刪除或格式變更都可能使 App 無法使用。

  • 將一份經過驗證的 1~7 級單字資料放入 Flutter assets,作為內建基準資料。
  • 啟動順序改為:本地快取 → assets 基準資料 → 背景檢查線上更新。
  • 線上更新失敗時維持現有內容,不清空畫面。
  • 為快取加入資料版本與更新時間,而不是永久無條件信任快取。
  • 驗證遠端 JSON schema;格式不合法時不覆蓋最後一份有效快取。
  • 長期將遠端資料移至本專案可控制的 repository 或 API。

P1:建立可維護的 Flutter 架構

建議先使用 Flutter SDK 原生工具完成,不為此版本引入大型架構框架。

  • 建立強型別 VocabularyWordDefinition model,移除 UI 中的 List<dynamic> 與零散 Map 解析。
  • 拆分責任:
    • models/:資料模型與 JSON parsing
    • services/:HTTP、assets、SharedPreferences
    • repositories/:整合本地與遠端資料
    • screens/:頁面
    • widgets/:共用 UI 元件
  • 將資料載入、搜尋、收藏及學習狀態移出 Widget。
  • 共用同一個 SharedPreferences/service instance,避免每次操作重新取得。
  • 搜尋加入約 250ms debounce,並支援英文單字與中文定義。
  • 對大型列表避免在每次 build 重複產生不必要的新 List。

建議目錄:

lib/
├── main.dart
├── models/
│   └── vocabulary_word.dart
├── services/
│   ├── vocabulary_data_source.dart
│   └── learning_storage.dart
├── repositories/
│   └── vocabulary_repository.dart
├── screens/
│   ├── level_selection_screen.dart
│   ├── vocabulary_list_screen.dart
│   ├── word_detail_screen.dart
│   └── flashcard_screen.dart
└── widgets/
    ├── level_card.dart
    └── vocabulary_card.dart

P1:核心學習體驗

單字詳情

  • 顯示所有 definitions,不只顯示第一筆。
  • 若資料包含詞性,依詞性分組顯示。
  • 加入英文 TTS 播放按鈕;播放失敗時提供非阻斷提示。
  • 加入上一個/下一個單字,保留目前篩選與搜尋順序。
  • 詳情頁可直接收藏及調整學習狀態。

學習狀態

現行行為是「點開單字即標為已學習」,無法反映真實掌握程度。

  • 狀態改為:未學習學習中已掌握
  • 點開詳細頁最多標為「學習中」,不可自動標為「已掌握」。
  • 使用者可手動變更狀態。
  • 進度以已掌握數量為主,另顯示學習中數量。
  • 同一單字以穩定 ID 儲存狀態,避免文字大小寫或資料更新造成紀錄失聯。

翻卡模式(本期主要新功能)

  • 可從目前級別開始一輪翻卡。
  • 正面顯示英文;點擊後翻面顯示中文定義。
  • 提供「不熟」與「記得」兩個明確操作。
  • 「不熟」進入學習中並在本輪稍後再次出現。
  • 「記得」累積正確紀錄;達到規則後才標為已掌握。
  • 顯示本輪進度與完成摘要。
  • 本期不做完整 SM-2/SRS 排程,先保留未來擴充所需的答題次數與最後複習時間欄位。

P1:UI/UX 與無障礙

  • 首頁每個級別卡片顯示「已掌握/總數」及小型進度條。
  • 列表提供全部、收藏、學習中、已掌握篩選。
  • 搜尋無結果、收藏為空、首次離線、背景更新失敗各有明確 empty/error state。
  • 背景更新時不使用遮住全部內容的 loading 畫面。
  • 互動元件至少符合 48×48 logical pixels 點擊範圍。
  • IconButton、狀態標籤與進度資訊補齊 tooltip/Semantics。
  • 支援系統深色模式與文字縮放,避免固定黑白色造成可讀性問題。
  • 主要狀態不可只靠顏色辨識。

測試與品質門檻

  • Model JSON parsing:正常、缺欄位、錯誤型別。
  • Repository:快取命中、assets fallback、遠端更新、timeout、非法 JSON。
  • Storage:收藏及三態學習紀錄可持久化。
  • 搜尋:英文、大小寫、中文定義及篩選組合。
  • 進度:未學習/學習中/已掌握計算正確。
  • Widget tests:首頁、列表 empty/error、詳情、翻卡流程。
  • 加入 GitHub Actions,在 PR 執行:
    • flutter pub get
    • flutter analyze
    • flutter test
  • 合併前 flutter analyze 無 error,所有測試通過。
  • Android release build 成功,並以實機完成離線及重新連線測試。

不納入本期

為控制開發量,本 Issue 暫不包含:

  • 登入與跨裝置雲端同步
  • 排行榜或社交功能
  • AI 自動產生例句
  • 完整 SM-2/FSRS 排程
  • 訂閱或廣告
  • 大型後端服務

建議實作順序

  1. P0 簽章安全、Manifest 與網路錯誤處理
  2. assets 離線資料與 repository
  3. 強型別 model 與程式拆分
  4. 三態學習紀錄
  5. TTS、完整 definitions、上一個/下一個
  6. 翻卡模式
  7. UI/無障礙調整
  8. 測試、CI 與 release 實機驗收

Definition of Done

  • 所有 P0 與 P1 項目完成。
  • App 在首次離線啟動時可進入任一級別學習。
  • 線上更新失敗不影響既有學習。
  • 使用者可完成「選級別 → 翻卡 → 判斷熟悉度 → 查看結果」流程。
  • 收藏與學習狀態重啟後仍正確。
  • Repository 不再包含簽章密碼或金鑰檔案。
  • Analyze、tests、Android release build 全數通過。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions