Skip to content

TTS機能のリモートキルスイッチ(tts_enabled)を追加し、無効時は設定画面のトグルを無効化してサービスステータスへのリンクを表示#6427

Merged
TinyKitten merged 3 commits into
devfrom
claude/tts-toggle-feature-flag-h4dfd7
Jul 16, 2026
Merged

Conversation

@TinyKitten

@TinyKitten TinyKitten commented Jul 16, 2026

Copy link
Copy Markdown
Member

概要

TTS(自動アナウンス)機能に Remote Config 配信のサーバー側キルスイッチ tts_enabled を追加しました。フラグが false のとき、設定画面のTTS有効トグルを自動的に無効化し、利用できない旨の説明と「サービスステータス」リンク(タップで https://status.trainlcd.app/ をブラウザで開く)を表示します。

変更の種類

  • バグ修正
  • 新機能
  • リファクタリング
  • ドキュメント
  • CI/CD
  • その他

変更内容

  • src/lib/remoteConfig.ts: /config/remote の設定キー tts_enabled を追加し、isTTSFeatureEnabled() で同期的に参照できるようにした。キルスイッチ用途のため、未配信・取得失敗時のフォールバックは true(従来どおり利用可能)。さらに subscribeRemoteConfig() でキャッシュ更新(起動時の非同期取得完了)を購読できるようにした
  • src/hooks/useTTSFeatureEnabled.ts(新規): useSyncExternalStore でキルスイッチをリアクティブに読むフック。コールドスタートでフォールバック描画後に tts_enabled: false が届いても確実に再レンダーされる
  • src/screens/TTSSettings.tsx: フラグ false 時にTTS有効トグルをOFF表示かつ操作不可にし、バックグラウンド再生・言語トグルも連動して無効化。説明文の最後に利用不可の注記と「サービスステータス」リンクを追加(Linking.openURL で既存定数 STATUS_URL を開き、起動失敗時はローカライズ済みエラーAlertを表示)。保存済みのユーザー設定(MMKV / atom)は書き換えないため、フラグ復帰時は元の設定に戻る
  • src/components/FxTTS.tsx(新規): TTS再生ホスト FxTTSMain.tsx から切り出し、ユーザー設定と Remote Config の両方が有効なときのみ useTTS をマウントするゲートを単体テスト可能にした(Main.tsx はこれをマウントするだけに変更)
  • assets/translations/ja.json / en.json: ttsFeatureDisabledTextserviceStatusfailedToOpenLink を追加
  • docs/state-management.md: FxTTS の定義場所とマウント条件の記述を現状に合わせて更新
  • テスト追加: src/lib/remoteConfig.test.ts(フラグの取得・フォールバック・取得失敗時・購読通知 7件)、src/screens/TTSSettings.test.tsx(フラグ false 時のトグル無効化・設定保持・リンク動作・リンク失敗時Alert 5件)、src/components/FxTTS.test.tsx(ユーザー設定×Remote Config の4通りのマウント判定+非同期到着時のマウント/アンマウント 6件)

リグレッションリスクと対策: フォールバックが true のため、BFF 側が tts_enabled を配信していなくても既存挙動は変わらない。フラグ false 時もユーザー設定は破棄せず表示・再生のみ抑止するため、復帰時に設定が失われることはない。既存テスト全件パスで回帰がないことを確認済み。

備考: フラグを実際に配信するには TrainLCD/BFF 側の /config/remote レスポンスへ tts_enabled(boolean)を追加する必要がある。

実行コマンド: npm run lint && npm test && npm run typecheck(すべてパス、193 suites / 2062 tests)

テスト

  • npm run lint が通ること
  • npm test が通ること
  • npm run typecheck が通ること

関連Issue

スクリーンショット(任意)

🤖 Generated with Claude Code

https://claude.ai/code/session_01Wspdw1SDvTZKMPFuoFSQgT

Summary by CodeRabbit

  • 機能改善
    • TTS(自動アナウンス)をサービス設定により一時的に無効化できるようになりました。
    • 無効時は関連設定が操作不可となり、サービス状況ページへのリンクが表示されます。
    • リンクを開けない場合にエラーメッセージを表示します。
  • ドキュメント
    • TTSの有効条件に関する説明を更新しました。

@TinyKitten TinyKitten self-assigned this Jul 16, 2026
@github-actions github-actions Bot added the react label Jul 16, 2026
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dbd81819-8e59-46b9-a3da-1aa51653643c

📥 Commits

Reviewing files that changed from the base of the PR and between f08e8ba and 72f03b4.

📒 Files selected for processing (12)
  • assets/translations/en.json
  • assets/translations/ja.json
  • docs/state-management.md
  • src/components/FxTTS.test.tsx
  • src/components/FxTTS.tsx
  • src/hooks/index.ts
  • src/hooks/useTTSFeatureEnabled.ts
  • src/lib/remoteConfig.test.ts
  • src/lib/remoteConfig.ts
  • src/screens/Main.tsx
  • src/screens/TTSSettings.test.tsx
  • src/screens/TTSSettings.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • assets/translations/en.json
  • assets/translations/ja.json
  • src/lib/remoteConfig.test.ts
  • src/screens/TTSSettings.test.tsx
  • src/screens/TTSSettings.tsx

📝 Walkthrough

Walkthrough

Remote Config に TTS の有効状態と購読APIを追加し、設定画面の操作制御、停止案内、サービスステータス遷移、TTS 再生処理へ反映しました。英語・日本語の翻訳、公開フック、関連テスト、状態管理ドキュメントも更新されています。

Changes

TTS Remote Config 制御

Layer / File(s) Summary
Remote Config の TTS 設定
src/lib/remoteConfig.ts, src/lib/remoteConfig.test.ts, src/hooks/useTTSFeatureEnabled.ts, src/hooks/index.ts
tts_enabled の取得、キャッシュ、購読通知、未設定時の true フォールバック、isTTSFeatureEnabled()、リアクティブフックとテストを追加。
TTS 設定画面の制御
src/screens/TTSSettings.tsx, src/screens/TTSSettings.test.tsx, assets/translations/*.json
TTS 無効時に設定項目を操作不可とし、停止案内とサービスステータスリンクを表示。リンク失敗時のアラート、翻訳、UIテストを追加。
TTS 再生処理への反映
src/components/FxTTS.tsx, src/components/FxTTS.test.tsx, src/screens/Main.tsx, docs/state-management.md
FxTTS を独立コンポーネントへ移し、ユーザー設定と Remote Config の両方が有効な場合のみ useTTS をマウント。状態更新時の切り替えをテストし、構成説明を更新。

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

  • TrainLCD/MobileApp#6422: Main.tsx の TTS 副作用ホストを分離する変更とコードレベルで関連しています。

Sequence Diagram(s)

sequenceDiagram
  participant RemoteConfig
  participant TTSSettings
  participant FxTTS
  participant useTTS

  RemoteConfig->>TTSSettings: tts_enabled を通知
  TTSSettings->>TTSSettings: 設定項目を有効化または無効化
  RemoteConfig->>FxTTS: 機能状態を再評価
  FxTTS->>useTTS: 両方有効な場合のみマウント
Loading

Poem

ぴょんと旗が立ちました
TTS の音をそっと守る
止まれば案内、リンクをひらき
状況ページへ耳を澄ます
うさぎも安心、ぴょんぴょん跳ねる

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed TTSキルスイッチ追加と設定画面での無効化・ステータスリンク表示という主要変更を具体的に要約しています。
Description check ✅ Passed 概要、変更の種類、変更内容、テストが揃っており、任意項目の不足もあるためテンプレート要件を満たしています。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/tts-toggle-feature-flag-h4dfd7

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/lib/remoteConfig.test.ts (1)

168-190: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

取得失敗時のフォールバックもテストしてください。

PR要件には「取得失敗時は true」が含まれます。ok: falsesetupRemoteConfig() が reject した後、isTTSFeatureEnabled()true を返すケースを追加してください。

テスト追加例
+  it('falls back to true when fetching remote config fails', async () => {
+    mockRemoteConfig({}, false);
+
+    await expect(setupRemoteConfig()).rejects.toThrow(
+      'remote config fetch failed: 503'
+    );
+    expect(isTTSFeatureEnabled()).toBe(true);
+  });

As per coding guidelines, 「挙動を変更する際は、同じ変更セット内でテストも更新または追加すること」に従ってください。

🤖 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 `@src/lib/remoteConfig.test.ts` around lines 168 - 190, Extend the
isTTSFeatureEnabled test suite with a setupRemoteConfig failure case: mock an
unsuccessful response with ok: false, assert setupRemoteConfig() rejects, then
verify isTTSFeatureEnabled() returns true. Keep the existing success and
missing-value fallback tests unchanged.

Source: Coding guidelines

🤖 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 `@src/screens/Main.tsx`:
- Around line 177-181: Add tests for the FxTTS rendering gate covering all four
combinations of user speechState.enabled and isTTSFeatureEnabled(), especially
enabled=true with Remote Config disabled. Assert that FxTTSInner/useTTS is not
mounted when either condition is false and is mounted only when both are true,
using the existing test setup and mocks.

In `@src/screens/TTSSettings.tsx`:
- Around line 424-428: Update handleServiceStatusPress so its Linking.openURL
failure path displays a localized error Alert to the user in addition to the
existing error handling. Reuse the screen’s established localization and Alert
patterns, and cover the rejected-link path with a test.

---

Nitpick comments:
In `@src/lib/remoteConfig.test.ts`:
- Around line 168-190: Extend the isTTSFeatureEnabled test suite with a
setupRemoteConfig failure case: mock an unsuccessful response with ok: false,
assert setupRemoteConfig() rejects, then verify isTTSFeatureEnabled() returns
true. Keep the existing success and missing-value fallback tests unchanged.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3f9291f2-a34e-4e0a-ba1e-bd2ee61b7f0e

📥 Commits

Reviewing files that changed from the base of the PR and between 8251d6d and f08e8ba.

📒 Files selected for processing (7)
  • assets/translations/en.json
  • assets/translations/ja.json
  • src/lib/remoteConfig.test.ts
  • src/lib/remoteConfig.ts
  • src/screens/Main.tsx
  • src/screens/TTSSettings.test.tsx
  • src/screens/TTSSettings.tsx

Comment thread src/screens/Main.tsx Outdated
Comment thread src/screens/TTSSettings.tsx
@TinyKitten

Copy link
Copy Markdown
Member Author

@coderabbitai approve

@TinyKitten

Copy link
Copy Markdown
Member Author

@coderabbitai 問題あるなら指摘してください。ないならapproveしてください

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Changed files ==='
git diff --name-only HEAD^ HEAD

echo '=== PR diff ==='
git diff --unified=80 HEAD^ HEAD -- \
  src/lib/remoteConfig.ts \
  src/lib/remoteConfig.test.ts \
  src/screens/Main.tsx \
  src/screens/TTSSettings.tsx \
  src/screens/TTSSettings.test.tsx \
  assets/translations/en.json \
  assets/translations/ja.json

echo '=== Relevant source maps ==='
ast-grep outline src/lib/remoteConfig.ts --items all || true
ast-grep outline src/screens/Main.tsx --items all || true
ast-grep outline src/screens/TTSSettings.tsx --items all || true

echo '=== Remote-config setup and TTS feature usages ==='
rg -n -C 5 --glob '!**/node_modules/**' \
  '(setupRemoteConfig|resetRemoteConfigCache|isTTSFeatureEnabled|tts_enabled|FxTTS|TTSSettings)' \
  src

Length of output: 79933


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base_ref=""
for ref in origin/main origin/master main master; do
  if git rev-parse --verify --quiet "$ref" >/dev/null; then
    base_ref="$ref"
    break
  fi
done
if [ -z "$base_ref" ]; then
  echo "Could not identify base branch" >&2
  git branch -a
  exit 1
fi
base="$(git merge-base HEAD "$base_ref")"
echo "=== Base: $base ($base_ref) ==="
echo '=== Complete PR changed files ==='
git diff --name-only "$base"...HEAD
echo '=== Complete PR diff stat ==='
git diff --stat "$base"...HEAD
echo '=== Complete PR diff (source/docs) ==='
git diff --unified=50 "$base"...HEAD -- \
  src/components/FxTTS.tsx \
  src/components/FxTTS.test.tsx \
  src/lib/remoteConfig.ts \
  src/lib/remoteConfig.test.ts \
  src/screens/Main.tsx \
  src/screens/TTSSettings.tsx \
  src/screens/TTSSettings.test.tsx \
  docs/state-management.md

echo '=== Full TTS settings component ==='
cat -n src/screens/TTSSettings.tsx

echo '=== Full FxTTS component and tests ==='
cat -n src/components/FxTTS.tsx
cat -n src/components/FxTTS.test.tsx

echo '=== Remote Config startup call sites ==='
rg -n -C 8 --glob '!**/node_modules/**' '\bsetupRemoteConfig\s*\(' .

echo '=== Translation key declarations/type validation references ==='
rg -n -C 3 --glob '!**/node_modules/**' '(failedToOpenLink|ttsFeatureDisabledText|serviceStatus)' src assets

Length of output: 1780


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base="$(git merge-base HEAD origin/dev)"
echo "=== Base: $base (origin/dev) ==="
echo '=== Complete PR changed files ==='
git diff --name-only "$base"...HEAD
echo '=== Complete PR diff stat ==='
git diff --stat "$base"...HEAD
echo '=== Complete PR diff (relevant files) ==='
git diff --unified=50 "$base"...HEAD -- \
  src/components/FxTTS.tsx \
  src/components/FxTTS.test.tsx \
  src/lib/remoteConfig.ts \
  src/lib/remoteConfig.test.ts \
  src/screens/Main.tsx \
  src/screens/TTSSettings.tsx \
  src/screens/TTSSettings.test.tsx \
  docs/state-management.md

echo '=== TTSSettings implementation ==='
sed -n '1,500p' src/screens/TTSSettings.tsx
echo '=== FxTTS implementation and tests ==='
cat src/components/FxTTS.tsx
cat src/components/FxTTS.test.tsx

echo '=== Remote Config initialization call sites ==='
rg -n -C 8 --glob '!**/node_modules/**' '\bsetupRemoteConfig\s*\(' .

echo '=== New translation key references ==='
rg -n -C 3 --glob '!**/node_modules/**' '(failedToOpenLink|ttsFeatureDisabledText|serviceStatus)' src assets

Length of output: 79405


@TinyKitten 1点、修正をお願いします。

setupRemoteConfig()index.js で非同期・非ブロッキングに開始されますが、FxTTSTTSSettingsScreen はモジュール内キャッシュを同期的に読むだけで、そのキャッシュ更新を購読していません。

そのためコールドスタート時は true のフォールバックで TTS/UI が先に描画され、後から tts_enabled: false を取得しても React の再レンダーは保証されません。結果として、キルスイッチが反映されるまで TTS が再生され続けたり、設定画面のトグルが有効なまま残ったりします。

Remote Config の取得結果を atom / state / context などのリアクティブな値として公開するか、設定取得完了後に TTS 関連の UI をマウントするようにして、false 到着時に確実に停止・無効化されるようにしてください。現状では approve できません。

@TinyKitten

Copy link
Copy Markdown
Member Author

@coderabbitai コード上で指摘してください

Comment thread src/screens/Main.tsx
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="TrainLCD/MobileApp"
pr_number="6427"

echo '=== PR head SHA ==='
head_sha=$(gh api "repos/${repo}/pulls/${pr_number}" --jq '.head.sha')
printf '%s\n' "$head_sha"

echo '=== Relevant changed lines ==='
gh api "repos/${repo}/pulls/${pr_number}/files?per_page=100" --paginate \
  --jq '.[] | select(.filename == "src/screens/Main.tsx" or .filename == "src/screens/TTSSettings.tsx") | "FILE: \(.filename)\n\(.patch)\n"'

comment_body='`setupRemoteConfig()` は非ブロッキングで完了する一方、ここでの `isTTSFeatureEnabled()` はモジュールキャッシュを一度だけ同期的に読むため、取得後に `tts_enabled: false` になってもこのコンポーネントを再レンダーさせられません。コールドスタートではフォールバックの `true` で `FxTTSInner` がマウントされ、そのまま再生が継続する可能性があります。Remote Config の値を atom/state/context などの購読可能な状態として公開し、この判定が `false` 到着時に確実に再評価されるようにしてください。`'

echo '=== Posting inline review comment ==='
gh api --method POST "repos/${repo}/pulls/${pr_number}/comments" \
  -f body="$comment_body" \
  -f commit_id="$head_sha" \
  -f path="src/screens/Main.tsx" \
  -F line=30 \
  -f side="RIGHT" \
  --jq '.html_url'

Length of output: 8316


@TinyKitten インラインコメントの投稿が完了しました。

@TinyKitten

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@TinyKitten
TinyKitten merged commit ad7aec7 into dev Jul 16, 2026
6 checks passed
@TinyKitten
TinyKitten deleted the claude/tts-toggle-feature-flag-h4dfd7 branch July 16, 2026 12:57
This was referenced Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants