fix: モバイルでタブ切り替え時のオフライン誤検知を防止 (#137) - #150
Conversation
モバイルでタブを切り替えて戻った際、visibilitychange でのネットワーク 判定が初回しきい値(5000ms)で実行され、無線復帰遅延により実際はオンライン でも誤ってオフライン判定されることがあった。 - evaluateNetwork に allowRetry オプションを追加 - タブ復帰(visibilitychange)/再接続(online)時、オンライン→オフラインへ 反転しそうな場合に少し待ってから緩和しきい値(10000ms)で1度だけ再判定 - navigator-offline はブラウザ確定のため再判定せず即オフライン - 強制オフライン(forcedOffline)は従来通り別経路で維持 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughタブ復帰や再接続時にネットワーク状態が誤検知される問題に対処するため、RTT判定のしきい値をパラメータ化し、特定の条件下で遅延後に緩和したしきい値での再判定を行うリトライ機構を実装。イベントハンドラからこのオプションを指定して利用。 Changesネットワーク誤検知対策とリトライ機構
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Visit the preview URL for this PR (updated for commit 47ad644): https://tabi-share-8ef6b--pr150-fix-issue137-mobile-qrpkqgtf.web.app (expires Sat, 20 Jun 2026 20:29:38 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: 9f2a87ede127df7673322845e34cf22c1372d720 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/lib/networkDetection.ts`:
- Around line 107-123: Concurrent evaluateNetwork calls can race so a delayed
retry may overwrite a newer result; add a per-call sequence guard to ensure only
the latest invocation writes state. Implement by generating a
monotonically-incrementing token (e.g. local seq number or timestamp) at the
start of evaluateNetwork, store it in a module-scoped variable like
currentEvalSeq, and capture it in the async flow; before applying the final
result (after delay/recheck and before updating isOfflineAtom or any store
writes), compare the captured token with currentEvalSeq and bail out if it no
longer matches. Update only inside the block where token matches so older/slow
evaluations cannot overwrite newer ones; keep existing logic around
options.allowRetry, RESUME_RETRY_DELAY_MS, checkNetworkStatus and
navigator.offLine unchanged except for this guard.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 07af1b07-646f-4759-8868-c53693025254
📒 Files selected for processing (3)
frontend/src/lib/networkDetection.test.tsfrontend/src/lib/networkDetection.tsfrontend/src/main.tsx
| export const evaluateNetwork = async ( | ||
| store: ReturnType<typeof createStore>, | ||
| options: EvaluateNetworkOptions = {} | ||
| ): Promise<void> => { | ||
| const wasPreviouslyOffline = store.get(isOfflineAtom); | ||
| const result = await checkNetworkStatus(); | ||
| let result = await checkNetworkStatus(); | ||
|
|
||
| // タブ復帰・再接続時の誤検知対策: | ||
| // それまでオンラインだったのに navigator.onLine は true のままヘルスチェックだけ | ||
| // 失敗した場合(モバイル無線の復帰遅延など)、少し待ってから緩和しきい値で再判定する。 | ||
| // navigator-offline はブラウザ確定のオフラインなので再判定しない。 | ||
| if (options.allowRetry && !wasPreviouslyOffline && result.isOffline && result.reason !== 'navigator-offline') { | ||
| await delay(RESUME_RETRY_DELAY_MS); | ||
| result = navigator.onLine | ||
| ? await checkNetworkStatus(undefined, RELAXED_RTT_THRESHOLD_MS) | ||
| : { isOffline: true, reason: 'navigator-offline' }; | ||
| } |
There was a problem hiding this comment.
並行実行で古いネットワーク判定が上書きされる競合があります。
Line 118-123 の待機付き再判定により、evaluateNetwork が重複起動すると完了順が逆転し、古い呼び出しが後から isOfflineAtom を上書きできます(online / visibilitychange の同時発火経路で再現可能)。最新リクエストのみ反映するガードを入れてください。
差分案
+let latestEvaluateRequestId = 0;
+
export const evaluateNetwork = async (
store: ReturnType<typeof createStore>,
options: EvaluateNetworkOptions = {}
): Promise<void> => {
+ const requestId = ++latestEvaluateRequestId;
const wasPreviouslyOffline = store.get(isOfflineAtom);
let result = await checkNetworkStatus();
@@
if (options.allowRetry && !wasPreviouslyOffline && result.isOffline && result.reason !== 'navigator-offline') {
await delay(RESUME_RETRY_DELAY_MS);
result = navigator.onLine
? await checkNetworkStatus(undefined, RELAXED_RTT_THRESHOLD_MS)
: { isOffline: true, reason: 'navigator-offline' };
}
+
+ // より新しい evaluateNetwork が開始済みなら、この結果は破棄する
+ if (requestId !== latestEvaluateRequestId) return;
const changed = wasPreviouslyOffline !== result.isOffline;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/lib/networkDetection.ts` around lines 107 - 123, Concurrent
evaluateNetwork calls can race so a delayed retry may overwrite a newer result;
add a per-call sequence guard to ensure only the latest invocation writes state.
Implement by generating a monotonically-incrementing token (e.g. local seq
number or timestamp) at the start of evaluateNetwork, store it in a
module-scoped variable like currentEvalSeq, and capture it in the async flow;
before applying the final result (after delay/recheck and before updating
isOfflineAtom or any store writes), compare the captured token with
currentEvalSeq and bail out if it no longer matches. Update only inside the
block where token matches so older/slow evaluations cannot overwrite newer ones;
keep existing logic around options.allowRetry, RESUME_RETRY_DELAY_MS,
checkNetworkStatus and navigator.offLine unchanged except for this guard.
概要
Issue #137 の修正。モバイルでブラウザのタブを切り替えて戻ると、実際はオンラインなのにオフラインモードになってしまう(自動判定の誤検知)事象を防ぐ。
根本原因
visibilitychange(visible) でevaluateNetwork()が実行される/healthへの fetch RTT がこれを超えやすく、rtt-exceededで誤オフライン判定されるonlineイベントが発火しないケースではvisibilitychangeだけが頼りになり、厳しい初回しきい値がそのまま効いてしまう修正内容
evaluateNetwork(store, { allowRetry })オプションを追加RESUME_RETRY_DELAY_MS(1500ms) 待機してから緩和しきい値(10000ms)で 1度だけ再判定 してから状態を確定するnavigator.onLineがfalseになった場合はオフライン確定(ブラウザ確定の信号を優先)navigator-offline(ブラウザがオフライン確定)は再判定せず即オフラインmain.tsxのvisibilitychange/onlineハンドラで{ allowRetry: true }を指定offlineイベントの挙動は従来通り(変更なし)forcedOffline) は別経路のため影響なしテスト
networkDetection.test.tsに allowRetry のケースを追加:rtt-exceededでも再判定でオンラインなら誤オフライン判定しないnavigator.onLineが false になればオフライン確定navigator-offlineは再判定せず即オフライン確認
pnpm run type-check✅pnpm run lint:check✅pnpm run format:check✅pnpm test✅ (15 files / 139 tests pass)🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests