add web session indicator to profile card, clean up cookie extraction
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f27de6dc3d
commit
d048875490
4 changed files with 54 additions and 10 deletions
|
|
@ -11,7 +11,6 @@ import 'package:frosty/models/stream.dart';
|
||||||
import 'package:frosty/screens/channel/video/video_player_interface.dart';
|
import 'package:frosty/screens/channel/video/video_player_interface.dart';
|
||||||
import 'package:frosty/screens/settings/stores/auth_store.dart';
|
import 'package:frosty/screens/settings/stores/auth_store.dart';
|
||||||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||||
import 'package:frosty/services/cookie_extractor.dart';
|
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
|
@ -102,10 +101,8 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
||||||
@action
|
@action
|
||||||
Future<void> _initPlayer() async {
|
Future<void> _initPlayer() async {
|
||||||
try {
|
try {
|
||||||
// Prefer web cookie token (works with web Client-ID) for ad-free playback.
|
// Use web cookie token (works with web Client-ID) for ad-free playback.
|
||||||
// Fall back to direct cookie extraction if AuthStore hasn't resolved it yet.
|
final authToken = authStore.gqlToken;
|
||||||
var authToken = authStore.gqlToken;
|
|
||||||
authToken ??= await CookieExtractor.extractTwitchAuthToken();
|
|
||||||
late final PlaybackAccessToken token;
|
late final PlaybackAccessToken token;
|
||||||
try {
|
try {
|
||||||
token = await twitchGqlApi.getPlaybackAccessToken(
|
token = await twitchGqlApi.getPlaybackAccessToken(
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,63 @@ class ProfileCard extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (authStore.isLoggedIn && authStore.user.details != null) {
|
if (authStore.isLoggedIn && authStore.user.details != null) {
|
||||||
|
final hasToken = authStore.gqlToken != null;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: ProfilePicture(
|
leading: ProfilePicture(
|
||||||
userLogin: authStore.user.details!.login,
|
userLogin: authStore.user.details!.login,
|
||||||
radius: 12,
|
radius: 12,
|
||||||
),
|
),
|
||||||
title: Text(authStore.user.details!.displayName),
|
title: Text(authStore.user.details!.displayName),
|
||||||
trailing: const Icon(Icons.chevron_right_rounded),
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
tooltip: hasToken
|
||||||
|
? 'Web session linked'
|
||||||
|
: 'Web session not linked',
|
||||||
|
icon: Icon(
|
||||||
|
hasToken
|
||||||
|
? Icons.check_circle_outline_rounded
|
||||||
|
: Icons.link_off_rounded,
|
||||||
|
size: 20,
|
||||||
|
color: hasToken ? Colors.green : null,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Web session'),
|
||||||
|
content: Text(
|
||||||
|
hasToken
|
||||||
|
? 'Your Twitch web session is linked. When using the native player, ads will be avoided on channels where you have a subscription or Twitch Turbo.'
|
||||||
|
: 'Your Twitch web session is not linked. Log in again to avoid ads when using the native player on channels where you have a subscription or Twitch Turbo.',
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
if (!hasToken)
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => LoginWebView(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Log in'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('OK'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Icon(Icons.chevron_right_rounded),
|
||||||
|
],
|
||||||
|
),
|
||||||
onTap: () => _showAccountOptionsModalBottomSheet(context),
|
onTap: () => _showAccountOptionsModalBottomSheet(context),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -275,9 +275,6 @@ abstract class AuthBase with Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try extracting a fresh GQL token from the WebView cookie store.
|
|
||||||
if (_isLoggedIn) await _extractGqlToken();
|
|
||||||
|
|
||||||
FirebaseCrashlytics.instance.setCustomKey('is_logged_in', _isLoggedIn);
|
FirebaseCrashlytics.instance.setCustomKey('is_logged_in', _isLoggedIn);
|
||||||
_error = null;
|
_error = null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ class VideoSettings extends StatelessWidget {
|
||||||
SettingsListSwitch(
|
SettingsListSwitch(
|
||||||
title: 'Native player (experimental)',
|
title: 'Native player (experimental)',
|
||||||
subtitle: const Text(
|
subtitle: const Text(
|
||||||
'Uses a native video player with improved picture-in-picture.',
|
'Native video player with auto picture-in-picture and quality selection.',
|
||||||
),
|
),
|
||||||
value: settingsStore.useNativePlayer,
|
value: settingsStore.useNativePlayer,
|
||||||
onChanged: (newValue) =>
|
onChanged: (newValue) =>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue