Repository navigation
feat: 最後のバックアップ日時を表示し、.knitの書き出しを勧める - #120
Conversation
Record when each chart was last exported as .knit and show it in the backup section. Suggest an export with a slim bar between the tools and the board when a changed chart has not been exported for a week or after 50 edits, only once editing pauses. "Later" hides it for three days. Dates live in the settings store, so existing data and .knit files keep their format. Closes #83 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
K0mork
left a comment
There was a problem hiding this comment.
レビュー(マージはしていません)
CI
コミットe8d9b4fのCI(CI and deploy Pages)はすべて成功しました。web、ios_web、release_archive、app_store_docs、ios (iPhone 17)、ios (iPad (A16))、app_update (iPhone 17)、app_update (iPad (A16))、ci-gateが成功、CodeQLも成功です(deployとsmokeはPRなのでスキップ)。帯で盤面が1段下がることによる既存のiOS UIテスト・E2Eへの影響は出ていません。既存のテストは50回以上編集したり古い作成日の編み図を使ったりしないので、帯が出ません。
良い点
- 日時を編み図の記録(
ChartDocument)ではなく設定ストアのlastBackupAt:<編み図ID>に置いており、DBの版・編み図の記録・.knitの形式は変わっていません。既存データは「まだバックアップしていない」として読め、移行テスト(storage/lastBackup.test.ts)もあります。 - 削除時は同じトランザクションで日時も消し、複製と復元は新しいIDと
createdAt/updatedAtを持つので、記録を持ち込みません。 - 表示条件(
state/backupReminder.ts)はReact/DOMから独立した純粋関数で、境目のテストがあります。 docs/dev-log/2026-10-06-backup-reminder.mdは書式どおりで、PR本文の検証内容と合っています。差分・本文・コミットメッセージに機密情報は見当たりません。Closes #83とenhancementラベルがあります。
直したほうがよい点
1.[中]なぞり描きの途中で帯が出て、盤面が1段下がる
countEdit()は指を離したとき(commitStroke)だけ呼ばれ、2.5秒の待ち時間は次に指を置いたときには延びません。そのため、1回目の編集から2.5秒以内に次のなぞり描きを始め、指を置いたまま2.5秒を過ぎると、描いている途中で帯が出ます。BoardCanvasは毎回getBoundingClientRect()からマスを求めるので、以降の描画は別の段に入ります。
手元で一時的なPlaywrightスクリプト(コミットしていません)で、8日前に作った未変更の編み図を開き、1回タップしてすぐ次のなぞり描きを始め、指を置いたまま3秒待つと、帯が出て盤面が47px(Desktop Chrome)下がることを確かめました。
直し方の案: 盤面で指が置かれている間(pointerdown〜pointerup/pointercancel)は待ち時間を止め、指を離してから数え直す。たとえばBoardCanvasのpointerdownでuseBackupReminderの待ちを取り消す口(pauseIdleなど)を呼ぶ。2本指の移動・拡大や範囲選択の途中も同じです。この場合を確かめるテスト(Vitestの偽タイマーかE2E)も足してください。
2.[中]書き出しを取りやめても「最後のバックアップ」として記録される
backup()はplatform.saveFile()が戻った時点で記録しますが、次の経路ではsaveFile()は利用者が保存する前に戻ります。
- Web版のiPhone/iPad Safari:
createWebPlatformのsaveFileはofferShare(file)で確認ダイアログを出すだけで、すぐ戻ります。ダイアログで「閉じる」を押しても、共有シートを取り消しても(AbortError)、「最後のバックアップ」が更新され帯も消えます。この経路はアプリ側のダイアログなので、結果を返すことができます。 - iOS版:
saveBlobWithNativeBridgeはメッセージを送った時点でtrueを返します。Swift側はdocumentPickerWasCancelledやUIActivityViewControllerのcompletionWithItemsHandlerで結果を知っていますが、Webへ返していません。
Safariの7日の期限で消えやすいのはまさにiPhone/iPadのSafariなので、取り消しで「バックアップ済み」になると、勧めの目的と逆になります。PR本文の「ホストは保存を終えたかを返さない」はWeb版Safariについては当たりません。
直し方の案:EditorPlatform.saveFileが保存したかどうか(例:Promise<boolean | undefined>、undefinedは不明)を返し、falseのときは記録しない。Web版はShareFileDialogの結果をPromiseで返す。iOSのネイティブ側の結果通知は別Issueに分けてもよいと思います。分ける場合は、PR本文・dev-log・使い方に「書き出しを始めた日時」であることが分かる書き方にしてください。
気になる点(判断をお願いしたい点)
- [低〜中]勧める頻度: 書き出した後も、同じ画面で50回編集するとまた帯が出ます(新しい編み図でも、50回タップすればその日のうちに出ます)。「控えめに」というIssueの趣旨に対して多くないか、閾値(50回)と「あとで」の期間(3日)を確認してください。
- [低]アップデート直後の既存利用者: これまで
.knitを書き出していた人も記録が無いので「まだバックアップしていません」と出ます(作成から7日以上で変更のある編み図を開くと、すぐ帯が出ます)。事実と違って見える可能性があるので、文言を「このアプリで書き出した記録がありません」に寄せるかを検討してください。 - [低]名前の変更:
renameDocumentはupdatedAtを進めるので、名前を変えただけでも「変更あり」になります。.knitに名前が入るので妥当とも言えます。 - [低]App Storeスクリーンショット準備:
testPrepareScreenshotBoardは40×32で32回の盤面設定と6回のタップ(計38回)なので今は帯が出ませんが、もっと大きな盤面を用意すると50回を超え、タップの途中で盤面がずれたり撮影画面に帯が写ったりします。 - [低]ファイル名:
ui/BackupReminder.tsxの中身はBackupReminderBarです。 - [低]E2Eの「出ない」確認:
page.waitForTimeout(300)の後にtoBeHidden()で確かめているため、遅い環境では見逃しうるテストになっています(失敗はしない側なので、壊れても気づきにくい)。
Issue #83の完了条件で残っているもの
- 「作業の邪魔にならないことをiPhoneとiPadで確かめる」: Simulatorでは確かめていますが、上の1のとおり、なぞり描きの途中でずれる場合が残っています。実機での確認もまだです。
- UIの変更なので、AGENTS.mdにあるとおりPRへのスクリーンショット添付(狭い画面とデスクトップ)がまだです。
Stop the idle countdown for the backup reminder while any finger or pointer is on the screen, and restart it after all are released, so the bar no longer appears mid-stroke and shifts the board. saveFile now returns whether the user saved or shared the file. The Web share dialog on iPhone and iPad Safari reports closing and share-sheet cancellation, and a cancelled .knit export is no longer recorded as the last backup. The iOS native bridge does not report the result yet (#122), so the app still records when the export started. Rename ui/BackupReminder.tsx to BackupReminderBar.tsx to match the component, and check that the bar stays hidden without fixed waits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
レビュー指摘の修正(a0d189c)直したこと
頻度(50回・3日)、既存利用者への文言、名前変更の扱いは、PR本文の「判断していただきたい点」に挙げています。 確認
まだ: 実機での確認と、PRへのスクリーンショット添付。マージはしていません。 |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
デプロイ確認
途中の 本番(https://knittingeditor.com/)での確認Playwright で、Android Chrome 相当(Pixel 7)、iPhone Safari 相当(iPhone 14、WebKit)、デスクトップ幅(1440×900)の Chromium と WebKit の4通りで確かめました。どれも成功し、ページのエラーは出ませんでした。
|
変更内容
.knitを書き出した日時を記録し、「保存」パネルのバックアップ欄に「この編み図の最後のバックアップ:2026年10月6日 14:05」(未書き出しなら「この編み図はまだバックアップしていません。」)と表示します。「この編み図」は開いている編み図、「全データ」はその時点の全編み図の日時を記録します。.knitを書き出し、「あとで」で一定期間出しません。packages/editor-core)。使い方ページ(Web・iOS)のバックアップの節に説明を1段落足しました。ios/docs/WEB_SYNC.mdに共通ファイルとWeb版の扱いを書きました。レビュー後の修正(a0d189c)
pointerdown・pointerup・pointercancelを見るので、BoardCanvasは変えていません(盤面はポインタを捕まえるので、離した通知は盤面の外でも届きます)。EditorPlatform.saveFileは{ saved }を返します。savedは保存・共有を終えたらtrue、取りやめたらfalse、hostが結果を返さないときはundefinedです。falseのときは日時を記録せず、帯も残します。iPhone・iPadのSafariでは、確認ダイアログの「閉じる」・背景のタップ・Escapeと、共有シートの取り消しでfalseになります。finishExport、completionWithItemsHandler、cleanupPendingExport)がPR #121の変更とそのまま重なるためです。iOS版はこれまでどおり書き出しを始めた日時を記録し、iOSの使い方ページにもそう書きました(「保存画面を途中で閉じた場合も含みます」)。ui/BackupReminder.tsxを中身のコンポーネント名に合わせてui/BackupReminderBar.tsxへ改名しました。waitForTimeout(300)の後に確かめていた箇所は、「保存」パネルに日時が出る(記録を読み終えた)のを待ち、パネルを閉じた同じ描画で帯が無いことを確かめる形にしました。そのため、「あとで」の記録も読み終えてから日時を出すようにしています。決めたこと
利用者が不在のため、Issue本文と既存UIに合わせて次のように決めました。数値はいずれも定数1か所で変えられます(
packages/editor-core/state/backupReminder.ts・useBackupReminder.ts)。lastBackupAt:<編み図ID>として置く。編み図の記録(ChartDocument)には入れない。入れると.knitに書き出され、復元した編み図が「書き出し済み」の日時を持ち込むため。DBの版・編み図の記録・.knitの形式は変えていないので、既存データは「まだバックアップしていない」として読める(移行テストあり)。編み図を削除すると記録も消す。EditorView.tsxは帯の追加とExportControlsへの1属性だけにとどめ、PR #115が変える道具列・記号ピッカーの周辺には触れていない。WEB_SYNC.mdは#115と離れた行に足した。E2Eは別ファイル(tests/e2e/backup-reminder.spec.ts)にした。Swift(#121が変えるWebViewContainer.swift)は変えていない。判断していただきたい点
レビューで挙がった次の点は、利用者の判断事項としてそのままにしています。
.knitを書き出していた人も記録が無いので「この編み図はまだバックアップしていません。」と出ます(作成から7日以上で変更のある編み図を開くとすぐ帯が出ます)。「このアプリで書き出した記録がありません」のような文言に寄せるか。updatedAtが進むので「変更あり」になります(.knitに名前が入るので妥当とも言えます)。このままでよいか。testPrepareScreenshotBoard)は今は38回の編集で帯が出ませんが、盤面を大きくして50回を超えると撮影画面に帯が写ります。実行した確認
レビュー後の修正(a0d189c)で実行したもの:
npm run typecheck:成功npm test:29ファイル179件成功npm run build:成功npm run check:dist:成功(dist/CNAMEがknittingeditor.com、dist/guide/index.htmlに改めた文言が入ることも確認)npm run test:e2e:chromium-mobile・webkit-mobile・chromium-desktopで107件成功(iPhone Safari専用の4件は設定どおりスキップ)。tests/e2e/backup-reminder.spec.tsは--repeat-each 5で50件成功ios/Web):tsc -p tsconfig.app.json --noEmit、vitest run --config vite.config.ts(3ファイル8件)成功最初のコミット(e8d9b4f)で実行したもの:上の5つと iOS Web の確認に加え、
xcodegen generateとSimulator向けDebugビルド、閾値を一時的に3回へ下げたビルドでiPhone 16・iPad(第10世代)のSimulatorに帯が出ることの目視、PlaywrightでiPhone SE・iPhone 14・Pixel 7・iPad(gen 7、縦横)・デスクトップ(1440×900)のスクリーンショットによる見た目の確認。追加・更新したテスト:
packages/editor-core/state/backupReminder.test.ts:表示条件(変更なし、閾値、7日の境目、作成日から数える、「あとで」の期間)と文言・日時の書式packages/editor-core/storage/lastBackup.test.ts:既存データ(記録前のアプリと同じDB名・版)を未書き出しとして読む移行、1件・全件の記録、.knitと復元した編み図に日時が入らない、壊れた記録を無視、削除で記録も消えるpackages/editor-core/ui/BackupReminderBar.test.tsx:新しい編み図には出ない、帯から書き出して日時が出る、保存を取りやめると記録せず帯が残る、「あとで」が再表示後も続く、50回目の編集から手を止めるまで出ない、指を置いている間(2本指で1本だけ離した間を含む)は出さず、すべて離してから待ち時間の後に出る、ドロワー中は出ないsrc/ShareFileDialog.test.tsx・src/platform.test.ts:共有でtrue、取り消し・「閉じる」でfalse、ダウンロードへの切り替えとダウンロードでundefinedを返す、閉じる前に次のファイルが来たら前はfalsetests/e2e/backup-reminder.spec.ts:書き出し後に日時が出て再読み込み後も残る、帯が盤面に重ならない・横スクロールなし、「あとで」が再読み込み後も続く、期間後にまた出る、「書き出す」で消えて再読み込み後も出ない、レビューの再現(8日前に作ったまま変更していない編み図で1回タップし、すぐ次のなぞり描きを始めて3.5秒押したままにする)で押している間は盤面が動かず、帯は指を離した後に初めて出る、iPhone Safari相当で確認ダイアログを閉じても共有シートを取り消しても記録せず、共有を終えたら記録するできなかった確認
Closes #83
🤖 Generated with Claude Code