ReorderableListView's onReorder was deprecated after Flutter 3.41 and
fails flutter analyze on the CI's pinned 3.44.1 toolchain. Switch to
onReorderItem, which adjusts newIndex for the removed item itself, and
drop the now-redundant manual off-by-one correction in reorderTab.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The recent-messages API now serializes empty IRCv3 tags as bare keys
(e.g. `emotes`) instead of `emotes=`, which threw a RangeError in the
tag parser and silently dropped every recent message. Parse tags per the
IRCv3 spec: treat bare and empty values as equivalent, split on the first
`=` so values may contain `=`, and unescape values in a single pass
(\: \s \ \r \n). Also guard user extraction against prefixes without
`!`, and isolate per-message parse failures in _handleIRCData so one bad
message can no longer abort an entire batch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
History was only saved via handleQuery (keyboard submit), but
type-to-search calls _performSearch directly through the debounce
timer, bypassing history. Move the save into _performSearch so all
search paths record history.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix cold-start privacy bug: apply persisted collection preference on launch
- Disable Firebase collection in debug builds to avoid polluting prod data
- Add Crashlytics custom keys (is_logged_in, channel), user identifier, and
WebSocket reconnection breadcrumbs for actionable crash reports
- Record non-fatal errors for server failures, WebSocket errors, and global
asset fetch failures (skip timeouts/network errors to avoid noise)
- Update is_logged_in key and user identifier on login/logout transitions
- Log auth reconnection exhaustion before forced logout
- Add FirebaseAnalyticsObserver with named routes for automatic screen tracking
- Add static routeName constants to avoid duplicated string literals
- Add Firebase Performance Dio interceptor for automatic HTTP request tracing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap fullScreenButton in Observer so icon/tooltip update immediately
when toggling fullscreen. Move system UI immersive mode sync into the
videoChat Observer so it reacts to landscapeForceVerticalChat changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When showLatency was already tracking, toggling autoSyncChatDelay on
would wait up to 60s for the next tracker cycle instead of using the
cached latency value immediately.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace JS-side `window._injected` guard with a Dart-controlled `_needsInit`
flag so `initVideo()` reliably runs after every refresh. The previous approach
loaded `about:blank` then `videoUrl` back-to-back, but `loadRequest` resolves
on navigation start (not page load), so the JS context often survived and
`onPageFinished` skipped re-initialization. Also adds an explicit `play()`
call to handle cases where the Twitch player loads in a paused state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
getRecentMessage had no try/catch, so a failure from the third-party
API would silently prevent connectToChat from ever being called.
getAssets was fire-and-forget without a catchError, risking uncaught
Future errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The script used authorization code flow (response_type=code) which produces
tokens expiring in ~4 hours, while the app uses implicit flow (~60 days).
Switched the script to implicit flow to match, removing the client_secret
requirement and token exchange step.
Also fixed UnauthorizedInterceptor swallowing 401 errors without calling
handler.reject(), which left Dio request Futures hanging forever.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add selectionClick to settings switches, discrete slider steps, bottom
nav tabs, and expandable section headers. Fix intensity levels for cache
clear and random color (medium → light). Add lightImpact to thumbnail
long-press. Fix inconsistent search result long-press level. Replace
blanket lightImpact in updateNotification with targeted haptics at
error/warning call sites to eliminate double-firing and match severity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add menuTheme and menuButtonTheme to ThemeData matching the existing
overlay surface style (12px radius, outline border, compact density).
Remove per-use styling from chat_tabs.dart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
X button on secondary tabs now opens a MenuAnchor popup with
Disconnect (deactivates without removing) and Remove options.
Menu styled to match app overlay surfaces (12px radius, outline border).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge only already-activated tabs; lazy tabs join when tapped. Dedup
messages by IRC id for Twitch shared chat overlap. Preserve draft and
reply state when switching tabs in merged mode. Merge button appears
only when 2+ tabs are loaded.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- extract _scheduleSystemUiModeSync to avoid redundant platform calls on
every build, deferred via addPostFrameCallback with state tracking
- use AnimatedBuilder.child so the video subtree is not rebuilt on every
animation tick during PiP spring-back
- move orientation reset from build() to initState() in home screen
- reformat landscape LayoutBuilder for consistency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
drains delayed chat/7TV callbacks via the existing 200ms message buffer
timer instead of creating one Future.delayed per incoming event, removing
the biggest memory-pressure risk on busy channels with chat delay active
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- split chatDelay into persisted (manual) and session-only (synced) fields
so auto-synced latency values don't carry over between channels
- make latency tracker singleton/idempotent to prevent overlapping JS pollers
- add isCycling guard to prevent re-entrant latency cycles
- deduplicate concurrent stream info API requests
- tie JS cleanup timer to video mode only (skip in chat-only)
- update UI to reflect effective delay instead of persisted value
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
adds a user-controlled toggle (default on) under video settings that
controls whether wakelock is enabled during channel sessions. previously
wakelock was unconditional with no way to turn it off.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 13 occurrences of BorderRadius.all(Radius.circular(8)) with a
shared kCardBorderRadius constant in constants.dart. All uses represent
the same design token: card/thumbnail corner radius across stream cards,
category cards, and their skeleton loader placeholders.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce FrostyColors ThemeExtension with 7 semantic color fields
(highlightedMessage, emoteOnly, followersOnly, slowMode, subOnly,
uniqueMode, overlayOnSurface) and brightness-aware light/dark variants.
Replace hardcoded colors across 15 files:
- Links use colorScheme.primary instead of Colors.blue
- Mentions use colorScheme.error instead of Colors.red
- First-time chatters share highlightedMessage (Twitch purple)
- Chat mode indicators use frostyColors fields
- Overlay text uses frostyColors.overlayOnSurface
Remove Provider<FrostyThemes> pattern from main.dart, channel.dart,
video_overlay.dart, and frosty_photo_view_dialog.dart.
Replace manual onSurface.withValues(alpha: 0.6) with M3's built-in
onSurfaceVariant for secondary text in section headers, chat modes,
alerts, loading indicators, and input hints. Fix double-muted
onSurfaceVariant.withValues(alpha: 0.6) in navigation icons and
notification dismiss button.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>