clean up native video player implementation

Extract shared stream quality key constant, add null guard to
playback token parsing, fix handleRefresh race condition and
concurrency, and replace Container with SizedBox placeholder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Tommy Chow 2026-03-09 01:45:15 -07:00
parent d22c980454
commit 6695c13455
6 changed files with 20 additions and 9 deletions

View file

@ -6,6 +6,9 @@ const clientId = String.fromEnvironment('CLIENT_ID');
/// Twitch GQL client ID (web player, used for playback access tokens).
const twitchGqlClientId = 'kimne78kx3ncx6brgo4mv6wki5h1ko';
/// SharedPreferences key for the last-selected stream quality.
const kLastStreamQualityKey = 'last_stream_quality';
/// Twitch API client secret.
const secret = String.fromEnvironment('SECRET');

View file

@ -8,8 +8,14 @@ class PlaybackAccessToken {
});
factory PlaybackAccessToken.fromGqlResponse(Map<String, dynamic> json) {
final data = json['data']['streamPlaybackAccessToken']
as Map<String, dynamic>;
final data =
(json['data'] as Map<String, dynamic>?)?['streamPlaybackAccessToken']
as Map<String, dynamic>?;
if (data == null) {
throw const FormatException(
'streamPlaybackAccessToken missing from GQL response',
);
}
return PlaybackAccessToken(
value: data['value'] as String,
signature: data['signature'] as String,

View file

@ -727,7 +727,7 @@ class _VideoChatState extends State<VideoChat>
if (settingsStore.showVideo) ...[
AspectRatio(
aspectRatio: 16 / 9,
child: Container(), // Placeholder for video space
child: SizedBox(),
),
],
Expanded(child: chat),

View file

@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/apis/twitch_gql_api.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/models/channel.dart';
import 'package:frosty/models/playback_access_token.dart';
import 'package:frosty/models/stream.dart';
@ -188,7 +189,7 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
} else {
SharedPreferences.getInstance().then((prefs) {
if (_disposed) return;
final lastQuality = prefs.getString('last_stream_quality');
final lastQuality = prefs.getString(kLastStreamQualityKey);
if (lastQuality != null) {
final index = _availableStreamQualities.indexOf(lastQuality);
if (index != -1) _pendingQualityIndex = index;
@ -210,7 +211,7 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
} catch (e) {
if (_disposed) return;
// Wait for stream info so we can distinguish "offline" from "broken".
await updateStreamInfo();
await updateStreamInfo(forceUpdate: true);
if (_disposed) return;
runInAction(() {
_loading = false;
@ -374,7 +375,6 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
@action
Future<void> handleRefresh() async {
HapticFeedback.lightImpact();
_disposed = false;
_loading = true;
_paused = true;
_userPaused = false;
@ -399,7 +399,7 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
);
_controller!.addActivityListener(_handleActivityEvent);
await _initPlayer();
_initPlayer();
updateStreamInfo();
}

View file

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/screens/channel/chat/details/chat_users_list.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/screens/channel/video/native_video_store.dart';
@ -109,7 +110,7 @@ class VideoOverlay extends StatelessWidget {
videoStore.setStreamQuality(quality);
SharedPreferences.getInstance().then(
(prefs) => prefs.setString(
'last_stream_quality',
kLastStreamQualityKey,
quality,
),
);

View file

@ -5,6 +5,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/models/channel.dart';
import 'package:frosty/models/stream.dart';
import 'package:frosty/screens/channel/video/video_player_interface.dart';
@ -88,7 +89,7 @@ abstract class VideoStoreBase with Store implements VideoPlayerInterface {
return;
}
final prefs = await SharedPreferences.getInstance();
final lastStreamQuality = prefs.getString('last_stream_quality');
final lastStreamQuality = prefs.getString(kLastStreamQualityKey);
if (lastStreamQuality == null) return;
setStreamQuality(lastStreamQuality);
}