Merge pull request #117 from tommyxchow/113-flutter-3

Upgrade to Flutter 3
This commit is contained in:
Tommy Chow 2022-05-22 22:08:04 -04:00 • committed by GitHub
commit 936bf9aa70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 556 additions and 610 deletions

1
.gitignore vendored
View file

@ -8,6 +8,7 @@
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml

View file

@ -6,7 +6,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.4'
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

View file

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip

View file

@ -13,13 +13,13 @@ PODS:
- Flutter
- path_provider_ios (0.0.1):
- Flutter
- Sentry (7.10.2):
- Sentry/Core (= 7.10.2)
- Sentry/Core (7.10.2)
- Sentry (7.11.0):
- Sentry/Core (= 7.11.0)
- Sentry/Core (7.11.0)
- sentry_flutter (0.0.1):
- Flutter
- FlutterMacOS
- Sentry (~> 7.10.1)
- Sentry (~> 7.11.0)
- shared_preferences_ios (0.0.1):
- Flutter
- sqflite (0.0.2):
@ -80,8 +80,8 @@ SPEC CHECKSUMS:
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
Sentry: 7bf9bfe713692cf87812e55f0999260494ba7982
sentry_flutter: 77ccdac346608b8ce7e428e7284e7a3e4e7f4a02
Sentry: 0c5cd63d714187b4a39c331c1f0eb04ba7868341
sentry_flutter: efb3df2c203cd03aad255892a8d628a458656d14
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de

View file

@ -52,5 +52,7 @@
<true/>
<key>UIStatusBarHidden</key>
<false/>
</dict>
</plist>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict>
</plist>

View file

@ -8,9 +8,9 @@ import 'package:mobx/mobx.dart';
part 'auth_store.g.dart';
class AuthStore = _AuthBase with _$AuthStore;
class AuthStore = AuthBase with _$AuthStore;
abstract class _AuthBase with Store {
abstract class AuthBase with Store {
/// Secure storage to store tokens.
static const _storage = FlutterSecureStorage();
@ -44,7 +44,7 @@ abstract class _AuthBase with Store {
@readonly
String? _error;
_AuthBase({required this.twitchApi}) : user = UserStore(twitchApi: twitchApi);
AuthBase({required this.twitchApi}) : user = UserStore(twitchApi: twitchApi);
/// Initialize by retrieving a token if it does not already exist.
@action

View file

@ -6,18 +6,18 @@ part of 'auth_store.dart';
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
mixin _$AuthStore on _AuthBase, Store {
mixin _$AuthStore on AuthBase, Store {
Computed<Map<String, String>>? _$headersTwitchComputed;
@override
Map<String, String> get headersTwitch => (_$headersTwitchComputed ??=
Computed<Map<String, String>>(() => super.headersTwitch,
name: '_AuthBase.headersTwitch'))
name: 'AuthBase.headersTwitch'))
.value;
final _$_tokenAtom = Atom(name: '_AuthBase._token');
late final _$_tokenAtom = Atom(name: 'AuthBase._token', context: context);
String? get token {
_$_tokenAtom.reportRead();
@ -34,7 +34,8 @@ mixin _$AuthStore on _AuthBase, Store {
});
}
final _$_isLoggedInAtom = Atom(name: '_AuthBase._isLoggedIn');
late final _$_isLoggedInAtom =
Atom(name: 'AuthBase._isLoggedIn', context: context);
bool get isLoggedIn {
_$_isLoggedInAtom.reportRead();
@ -51,7 +52,7 @@ mixin _$AuthStore on _AuthBase, Store {
});
}
final _$_errorAtom = Atom(name: '_AuthBase._error');
late final _$_errorAtom = Atom(name: 'AuthBase._error', context: context);
String? get error {
_$_errorAtom.reportRead();
@ -68,21 +69,23 @@ mixin _$AuthStore on _AuthBase, Store {
});
}
final _$initAsyncAction = AsyncAction('_AuthBase.init');
late final _$initAsyncAction = AsyncAction('AuthBase.init', context: context);
@override
Future<void> init() {
return _$initAsyncAction.run(() => super.init());
}
final _$loginAsyncAction = AsyncAction('_AuthBase.login');
late final _$loginAsyncAction =
AsyncAction('AuthBase.login', context: context);
@override
Future<void> login({String? customToken}) {
return _$loginAsyncAction.run(() => super.login(customToken: customToken));
}
final _$logoutAsyncAction = AsyncAction('_AuthBase.logout');
late final _$logoutAsyncAction =
AsyncAction('AuthBase.logout', context: context);
@override
Future<void> logout() {

View file

@ -4,9 +4,9 @@ import 'package:mobx/mobx.dart';
part 'user_store.g.dart';
class UserStore = _UserStoreBase with _$UserStore;
class UserStore = UserStoreBase with _$UserStore;
abstract class _UserStoreBase with Store {
abstract class UserStoreBase with Store {
final TwitchApi twitchApi;
/// The current user's info.
@ -19,7 +19,7 @@ abstract class _UserStoreBase with Store {
ReactionDisposer? _disposeReaction;
_UserStoreBase({required this.twitchApi});
UserStoreBase({required this.twitchApi});
@action
Future<void> init({required Map<String, String> headers}) async {

View file

@ -6,10 +6,11 @@ part of 'user_store.dart';
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
mixin _$UserStore on _UserStoreBase, Store {
final _$_detailsAtom = Atom(name: '_UserStoreBase._details');
mixin _$UserStore on UserStoreBase, Store {
late final _$_detailsAtom =
Atom(name: 'UserStoreBase._details', context: context);
UserTwitch? get details {
_$_detailsAtom.reportRead();
@ -26,7 +27,8 @@ mixin _$UserStore on _UserStoreBase, Store {
});
}
final _$_blockedUsersAtom = Atom(name: '_UserStoreBase._blockedUsers');
late final _$_blockedUsersAtom =
Atom(name: 'UserStoreBase._blockedUsers', context: context);
ObservableList<UserBlockedTwitch> get blockedUsers {
_$_blockedUsersAtom.reportRead();
@ -43,14 +45,16 @@ mixin _$UserStore on _UserStoreBase, Store {
});
}
final _$initAsyncAction = AsyncAction('_UserStoreBase.init');
late final _$initAsyncAction =
AsyncAction('UserStoreBase.init', context: context);
@override
Future<void> init({required Map<String, String> headers}) {
return _$initAsyncAction.run(() => super.init(headers: headers));
}
final _$blockAsyncAction = AsyncAction('_UserStoreBase.block');
late final _$blockAsyncAction =
AsyncAction('UserStoreBase.block', context: context);
@override
Future<void> block(
@ -61,7 +65,8 @@ mixin _$UserStore on _UserStoreBase, Store {
.block(targetId: targetId, displayName: displayName, headers: headers));
}
final _$unblockAsyncAction = AsyncAction('_UserStoreBase.unblock');
late final _$unblockAsyncAction =
AsyncAction('UserStoreBase.unblock', context: context);
@override
Future<void> unblock(
@ -70,8 +75,8 @@ mixin _$UserStore on _UserStoreBase, Store {
.run(() => super.unblock(targetId: targetId, headers: headers));
}
final _$refreshBlockedUsersAsyncAction =
AsyncAction('_UserStoreBase.refreshBlockedUsers');
late final _$refreshBlockedUsersAsyncAction =
AsyncAction('UserStoreBase.refreshBlockedUsers', context: context);
@override
Future<void> refreshBlockedUsers({required Map<String, String> headers}) {
@ -79,17 +84,17 @@ mixin _$UserStore on _UserStoreBase, Store {
.run(() => super.refreshBlockedUsers(headers: headers));
}
final _$_UserStoreBaseActionController =
ActionController(name: '_UserStoreBase');
late final _$UserStoreBaseActionController =
ActionController(name: 'UserStoreBase', context: context);
@override
void dispose() {
final _$actionInfo = _$_UserStoreBaseActionController.startAction(
name: '_UserStoreBase.dispose');
final _$actionInfo = _$UserStoreBaseActionController.startAction(
name: 'UserStoreBase.dispose');
try {
return super.dispose();
} finally {
_$_UserStoreBaseActionController.endAction(_$actionInfo);
_$UserStoreBaseActionController.endAction(_$actionInfo);
}
}

View file

@ -151,7 +151,7 @@ class Badge {
factory Badge.fromFFZ(BadgeInfoFFZ badge) => Badge(
name: badge.title,
url: 'https:' + badge.urls.url4x,
url: 'https:${badge.urls.url4x}',
color: badge.color,
type: BadgeType.ffz,
);

View file

@ -293,7 +293,7 @@ class Emote {
zeroWidth: false,
width: emote.width,
height: emote.height,
url: 'https:' + (emote.urls.url4x ?? emote.urls.url2x ?? emote.urls.url1x),
url: 'https:${emote.urls.url4x ?? emote.urls.url2x ?? emote.urls.url1x}',
type: type,
);

View file

@ -85,6 +85,7 @@ class IRCMessage {
required double badgeScale,
required double emoteScale,
required bool isLightTheme,
required bool launchExternal,
bool showMessage = true,
bool useZeroWidth = false,
bool useReadableColors = false,
@ -147,7 +148,7 @@ class IRCMessage {
badgeUrl = botBadge.url;
skipBot = true;
} else if (ffzRoomInfo?.modUrls != null) {
badgeUrl = 'https:' + (ffzRoomInfo!.modUrls?.url4x ?? ffzRoomInfo.modUrls?.url2x ?? ffzRoomInfo.modUrls!.url1x);
badgeUrl = 'https:${ffzRoomInfo!.modUrls?.url4x ?? ffzRoomInfo.modUrls?.url2x ?? ffzRoomInfo.modUrls!.url1x}';
}
final newBadge = Badge(
@ -169,7 +170,7 @@ class IRCMessage {
// Add custom FFZ vip badge if it exists
if (badgeInfo.name == 'VIP' && ffzRoomInfo?.vipBadge != null) {
badgeUrl = 'https:' + (ffzRoomInfo!.vipBadge?.url4x ?? ffzRoomInfo.vipBadge?.url2x ?? ffzRoomInfo.vipBadge!.url1x);
badgeUrl = 'https:${ffzRoomInfo!.vipBadge?.url4x ?? ffzRoomInfo.vipBadge?.url2x ?? ffzRoomInfo.vipBadge!.url1x}';
}
final newBadge = Badge(
@ -393,7 +394,7 @@ class IRCMessage {
// If the next word is neither an emote or emoji, add it as a text span.
if (nextEmote == null && !nextWordIsEmoji) {
localSpan.add(const TextSpan(text: ' '));
localSpan.add(_createTextSpan(text: words[index], style: textStyle));
localSpan.add(_createTextSpan(text: words[index], style: textStyle, launchExternal: launchExternal));
}
} else {
localSpan.add(
@ -408,7 +409,7 @@ class IRCMessage {
if (regexEmoji.hasMatch(word)) {
localSpan.add(_createEmojiSpan(emoji: word, style: textStyle?.copyWith(fontSize: emoteSize - 5)));
} else {
localSpan.add(_createTextSpan(text: word, style: textStyle));
localSpan.add(_createTextSpan(text: word, style: textStyle, launchExternal: launchExternal));
}
}
localSpan.add(const TextSpan(text: ' '));
@ -435,7 +436,7 @@ class IRCMessage {
if (regexEmoji.hasMatch(word)) {
span.add(_createEmojiSpan(emoji: word, style: textStyle?.copyWith(fontSize: emoteSize - 5)));
} else {
span.add(_createTextSpan(text: word, style: textStyle));
span.add(_createTextSpan(text: word, style: textStyle, launchExternal: launchExternal));
}
}
}
@ -574,14 +575,15 @@ class IRCMessage {
);
}
static TextSpan _createTextSpan({required String text, TextStyle? style}) {
static TextSpan _createTextSpan({required String text, required bool launchExternal, TextStyle? style}) {
if (text.startsWith('@')) {
return TextSpan(text: text, style: style?.copyWith(fontWeight: FontWeight.bold));
} else if (RegExp(r'https?:\/\/').hasMatch(text)) {
return TextSpan(
text: text,
style: style?.copyWith(color: Colors.blue),
recognizer: TapGestureRecognizer()..onTap = () => launch(text),
recognizer: TapGestureRecognizer()
..onTap = () => launchUrl(Uri.parse(text), mode: launchExternal ? LaunchMode.externalApplication : LaunchMode.inAppWebView),
);
} else {
return TextSpan(text: text, style: style);

View file

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/screens/channel/chat/emote_menu/emote_menu.dart';
import 'package:frosty/screens/channel/chat/widgets/chat_bottom_bar.dart';
@ -16,7 +15,7 @@ class Chat extends StatelessWidget {
Widget build(BuildContext context) {
return Observer(
builder: (context) {
SchedulerBinding.instance?.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (chatStore.scrollController.hasClients) chatStore.scrollController.jumpTo(chatStore.scrollController.position.maxScrollExtent);
});

View file

@ -25,7 +25,7 @@ class ChattersList extends StatefulWidget {
}) : super(key: key);
@override
_ChattersListState createState() => _ChattersListState();
State<ChattersList> createState() => _ChattersListState();
}
class _ChattersListState extends State<ChattersList> {

View file

@ -28,8 +28,8 @@ class _RecentEmotesPanelState extends State<RecentEmotesPanel> {
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text('Cancel'),
style: TextButton.styleFrom(primary: Colors.red),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () {

View file

@ -36,6 +36,7 @@ class ChatMessage extends StatelessWidget {
useZeroWidth: settingsStore.showZeroWidth,
useReadableColors: settingsStore.useReadableColors,
isLightTheme: Theme.of(context).brightness == Brightness.light,
launchExternal: settingsStore.launchUrlExternal,
timestamp: settingsStore.timestampType,
),
),
@ -76,6 +77,7 @@ class ChatMessage extends StatelessWidget {
useZeroWidth: settingsStore.showZeroWidth,
useReadableColors: settingsStore.useReadableColors,
isLightTheme: Theme.of(context).brightness == Brightness.light,
launchExternal: settingsStore.launchUrlExternal,
timestamp: settingsStore.timestampType,
),
),
@ -142,6 +144,7 @@ class ChatMessage extends StatelessWidget {
useZeroWidth: settingsStore.showZeroWidth,
useReadableColors: settingsStore.useReadableColors,
isLightTheme: Theme.of(context).brightness == Brightness.light,
launchExternal: settingsStore.launchUrlExternal,
timestamp: settingsStore.timestampType,
),
),

View file

@ -6,7 +6,7 @@ import 'package:frosty/widgets/block_report_modal.dart';
import 'package:frosty/widgets/profile_picture.dart';
import 'package:frosty/widgets/section_header.dart';
class ChatUserModal extends StatelessWidget {
class ChatUserModal extends StatefulWidget {
final ChatStore chatStore;
final String username;
final String displayName;
@ -20,9 +20,14 @@ class ChatUserModal extends StatelessWidget {
required this.userId,
}) : super(key: key);
@override
State<ChatUserModal> createState() => _ChatUserModalState();
}
class _ChatUserModalState extends State<ChatUserModal> {
@override
Widget build(BuildContext context) {
final userMessages = chatStore.messages.reversed.where((message) => message.user == username).toList();
final userMessages = widget.chatStore.messages.reversed.where((message) => message.user == widget.username).toList();
return Scaffold(
body: SafeArea(
@ -34,12 +39,12 @@ class ChatUserModal extends StatelessWidget {
padding: const EdgeInsets.all(5.0),
child: ListTile(
leading: ProfilePicture(
userLogin: username,
userLogin: widget.username,
),
title: Row(
children: [
Text(
displayName,
widget.displayName,
style: const TextStyle(fontWeight: FontWeight.bold),
),
IconButton(
@ -47,22 +52,22 @@ class ChatUserModal extends StatelessWidget {
onPressed: () => showModalBottomSheet(
context: context,
builder: (context) => BlockReportModal(
authStore: chatStore.auth,
name: displayName,
userLogin: username,
userId: userId,
authStore: widget.chatStore.auth,
name: widget.displayName,
userLogin: widget.username,
userId: widget.userId,
),
),
icon: Icon(Icons.adaptive.more),
),
],
),
trailing: chatStore.auth.isLoggedIn
trailing: widget.chatStore.auth.isLoggedIn
? OutlinedButton(
onPressed: () {
chatStore.textController.text = '@$username ';
widget.chatStore.textController.text = '@${widget.username} ';
Navigator.pop(context);
chatStore.textFieldFocusNode.requestFocus();
widget.chatStore.textFieldFocusNode.requestFocus();
},
child: const Text('Reply'),
)
@ -75,14 +80,16 @@ class ChatUserModal extends StatelessWidget {
),
Expanded(
child: MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: chatStore.settings.messageScale),
data: MediaQuery.of(context).copyWith(textScaleFactor: widget.chatStore.settings.messageScale),
child: DefaultTextStyle(
style: DefaultTextStyle.of(context).style.copyWith(fontSize: chatStore.settings.fontSize),
style: DefaultTextStyle.of(context).style.copyWith(fontSize: widget.chatStore.settings.fontSize),
child: ListView.separated(
reverse: true,
itemBuilder: (context, index) => InkWell(
onLongPress: () async {
await Clipboard.setData(ClipboardData(text: userMessages[index].message));
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Message copied!'),
@ -92,16 +99,16 @@ class ChatUserModal extends StatelessWidget {
},
child: ChatMessage(
ircMessage: userMessages[index],
assetsStore: chatStore.assetsStore,
settingsStore: chatStore.settings,
assetsStore: widget.chatStore.assetsStore,
settingsStore: widget.chatStore.settings,
),
),
separatorBuilder: (context, index) => chatStore.settings.showChatMessageDividers
separatorBuilder: (context, index) => widget.chatStore.settings.showChatMessageDividers
? Divider(
height: chatStore.settings.messageSpacing,
height: widget.chatStore.settings.messageSpacing,
thickness: 1.0,
)
: SizedBox(height: chatStore.settings.messageSpacing),
: SizedBox(height: widget.chatStore.settings.messageSpacing),
itemCount: userMessages.length,
),
),

View file

@ -11,9 +11,9 @@ import 'package:shared_preferences/shared_preferences.dart';
part 'chat_assets_store.g.dart';
class ChatAssetsStore = _ChatAssetsStoreBase with _$ChatAssetsStore;
class ChatAssetsStore = ChatAssetsStoreBase with _$ChatAssetsStore;
abstract class _ChatAssetsStoreBase with Store {
abstract class ChatAssetsStoreBase with Store {
final TwitchApi twitchApi;
final BTTVApi bttvApi;
final FFZApi ffzApi;
@ -79,7 +79,7 @@ abstract class _ChatAssetsStoreBase with Store {
late final ReactionDisposer _disposeReaction;
_ChatAssetsStoreBase({
ChatAssetsStoreBase({
required this.twitchApi,
required this.bttvApi,
required this.ffzApi,

View file

@ -6,32 +6,33 @@ part of 'chat_assets_store.dart';
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
mixin _$ChatAssetsStore on ChatAssetsStoreBase, Store {
Computed<List<Emote>>? _$bttvEmotesComputed;
@override
List<Emote> get bttvEmotes =>
(_$bttvEmotesComputed ??= Computed<List<Emote>>(() => super.bttvEmotes,
name: '_ChatAssetsStoreBase.bttvEmotes'))
name: 'ChatAssetsStoreBase.bttvEmotes'))
.value;
Computed<List<Emote>>? _$ffzEmotesComputed;
@override
List<Emote> get ffzEmotes =>
(_$ffzEmotesComputed ??= Computed<List<Emote>>(() => super.ffzEmotes,
name: '_ChatAssetsStoreBase.ffzEmotes'))
name: 'ChatAssetsStoreBase.ffzEmotes'))
.value;
Computed<List<Emote>>? _$sevenTVEmotesComputed;
@override
List<Emote> get sevenTVEmotes => (_$sevenTVEmotesComputed ??=
Computed<List<Emote>>(() => super.sevenTVEmotes,
name: '_ChatAssetsStoreBase.sevenTVEmotes'))
name: 'ChatAssetsStoreBase.sevenTVEmotes'))
.value;
final _$_recentEmotesAtom = Atom(name: '_ChatAssetsStoreBase._recentEmotes');
late final _$_recentEmotesAtom =
Atom(name: 'ChatAssetsStoreBase._recentEmotes', context: context);
ObservableList<Emote> get recentEmotes {
_$_recentEmotesAtom.reportRead();
@ -48,8 +49,8 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
});
}
final _$_emoteToObjectAtom =
Atom(name: '_ChatAssetsStoreBase._emoteToObject');
late final _$_emoteToObjectAtom =
Atom(name: 'ChatAssetsStoreBase._emoteToObject', context: context);
Map<String, Emote> get emoteToObject {
_$_emoteToObjectAtom.reportRead();
@ -66,8 +67,8 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
});
}
final _$_userEmoteToObjectAtom =
Atom(name: '_ChatAssetsStoreBase._userEmoteToObject');
late final _$_userEmoteToObjectAtom =
Atom(name: 'ChatAssetsStoreBase._userEmoteToObject', context: context);
Map<String, Emote> get userEmoteToObject {
_$_userEmoteToObjectAtom.reportRead();
@ -84,8 +85,8 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
});
}
final _$_userToFFZBadgesAtom =
Atom(name: '_ChatAssetsStoreBase._userToFFZBadges');
late final _$_userToFFZBadgesAtom =
Atom(name: 'ChatAssetsStoreBase._userToFFZBadges', context: context);
Map<String, List<Badge>> get userToFFZBadges {
_$_userToFFZBadgesAtom.reportRead();
@ -102,8 +103,8 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
});
}
final _$_userTo7TVBadgesAtom =
Atom(name: '_ChatAssetsStoreBase._userTo7TVBadges');
late final _$_userTo7TVBadgesAtom =
Atom(name: 'ChatAssetsStoreBase._userTo7TVBadges', context: context);
Map<String, List<Badge>> get userTo7TVBadges {
_$_userTo7TVBadgesAtom.reportRead();
@ -120,8 +121,8 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
});
}
final _$_userToBTTVBadgesAtom =
Atom(name: '_ChatAssetsStoreBase._userToBTTVBadges');
late final _$_userToBTTVBadgesAtom =
Atom(name: 'ChatAssetsStoreBase._userToBTTVBadges', context: context);
Map<String, Badge> get userToBTTVBadges {
_$_userToBTTVBadgesAtom.reportRead();
@ -138,8 +139,8 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
});
}
final _$emoteMenuIndexAtom =
Atom(name: '_ChatAssetsStoreBase.emoteMenuIndex');
late final _$emoteMenuIndexAtom =
Atom(name: 'ChatAssetsStoreBase.emoteMenuIndex', context: context);
@override
int get emoteMenuIndex {
@ -154,7 +155,8 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
});
}
final _$showEmoteMenuAtom = Atom(name: '_ChatAssetsStoreBase.showEmoteMenu');
late final _$showEmoteMenuAtom =
Atom(name: 'ChatAssetsStoreBase.showEmoteMenu', context: context);
@override
bool get showEmoteMenu {
@ -169,15 +171,16 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
});
}
final _$initAsyncAction = AsyncAction('_ChatAssetsStoreBase.init');
late final _$initAsyncAction =
AsyncAction('ChatAssetsStoreBase.init', context: context);
@override
Future<void> init() {
return _$initAsyncAction.run(() => super.init());
}
final _$_ChatAssetsStoreBaseActionController =
ActionController(name: '_ChatAssetsStoreBase');
late final _$ChatAssetsStoreBaseActionController =
ActionController(name: 'ChatAssetsStoreBase', context: context);
@override
Future<void> assetsFuture(
@ -186,8 +189,8 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
required Map<String, String> headers,
required Function onEmoteError,
required Function onBadgeError}) {
final _$actionInfo = _$_ChatAssetsStoreBaseActionController.startAction(
name: '_ChatAssetsStoreBase.assetsFuture');
final _$actionInfo = _$ChatAssetsStoreBaseActionController.startAction(
name: 'ChatAssetsStoreBase.assetsFuture');
try {
return super.assetsFuture(
channelName: channelName,
@ -196,7 +199,7 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
onEmoteError: onEmoteError,
onBadgeError: onBadgeError);
} finally {
_$_ChatAssetsStoreBaseActionController.endAction(_$actionInfo);
_$ChatAssetsStoreBaseActionController.endAction(_$actionInfo);
}
}
@ -206,8 +209,8 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
required String channelName,
required Map<String, String> headers,
required Function onError}) {
final _$actionInfo = _$_ChatAssetsStoreBaseActionController.startAction(
name: '_ChatAssetsStoreBase.emotesFuture');
final _$actionInfo = _$ChatAssetsStoreBaseActionController.startAction(
name: 'ChatAssetsStoreBase.emotesFuture');
try {
return super.emotesFuture(
channelId: channelId,
@ -215,7 +218,7 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
headers: headers,
onError: onError);
} finally {
_$_ChatAssetsStoreBaseActionController.endAction(_$actionInfo);
_$ChatAssetsStoreBaseActionController.endAction(_$actionInfo);
}
}
@ -224,13 +227,13 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
{required String channelId,
required Map<String, String> headers,
required Function onError}) {
final _$actionInfo = _$_ChatAssetsStoreBaseActionController.startAction(
name: '_ChatAssetsStoreBase.badgesFuture');
final _$actionInfo = _$ChatAssetsStoreBaseActionController.startAction(
name: 'ChatAssetsStoreBase.badgesFuture');
try {
return super.badgesFuture(
channelId: channelId, headers: headers, onError: onError);
} finally {
_$_ChatAssetsStoreBaseActionController.endAction(_$actionInfo);
_$ChatAssetsStoreBaseActionController.endAction(_$actionInfo);
}
}
@ -239,13 +242,13 @@ mixin _$ChatAssetsStore on _ChatAssetsStoreBase, Store {
{required List<String> emoteSets,
required Map<String, String> headers,
required Function onError}) {
final _$actionInfo = _$_ChatAssetsStoreBaseActionController.startAction(
name: '_ChatAssetsStoreBase.userEmotesFuture');
final _$actionInfo = _$ChatAssetsStoreBaseActionController.startAction(
name: 'ChatAssetsStoreBase.userEmotesFuture');
try {
return super.userEmotesFuture(
emoteSets: emoteSets, headers: headers, onError: onError);
} finally {
_$_ChatAssetsStoreBaseActionController.endAction(_$actionInfo);
_$ChatAssetsStoreBaseActionController.endAction(_$actionInfo);
}
}

View file

@ -8,9 +8,9 @@ import 'package:mobx/mobx.dart';
part 'chat_details_store.g.dart';
class ChatDetailsStore = _ChatDetailsStoreBase with _$ChatDetailsStore;
class ChatDetailsStore = ChatDetailsStoreBase with _$ChatDetailsStore;
abstract class _ChatDetailsStoreBase with Store {
abstract class ChatDetailsStoreBase with Store {
final TwitchApi twitchApi;
/// The rules and modes being used in the chat.
@ -41,7 +41,7 @@ abstract class _ChatDetailsStoreBase with Store {
@readonly
String? _error;
_ChatDetailsStoreBase({required this.twitchApi});
ChatDetailsStoreBase({required this.twitchApi});
@action
Future<void> updateChatters(String userLogin) async {

View file

@ -6,18 +6,19 @@ part of 'chat_details_store.dart';
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
mixin _$ChatDetailsStore on _ChatDetailsStoreBase, Store {
mixin _$ChatDetailsStore on ChatDetailsStoreBase, Store {
Computed<Iterable<List<String>>>? _$filteredUsersComputed;
@override
Iterable<List<String>> get filteredUsers => (_$filteredUsersComputed ??=
Computed<Iterable<List<String>>>(() => super.filteredUsers,
name: '_ChatDetailsStoreBase.filteredUsers'))
name: 'ChatDetailsStoreBase.filteredUsers'))
.value;
final _$roomStateAtom = Atom(name: '_ChatDetailsStoreBase.roomState');
late final _$roomStateAtom =
Atom(name: 'ChatDetailsStoreBase.roomState', context: context);
@override
ROOMSTATE get roomState {
@ -32,8 +33,8 @@ mixin _$ChatDetailsStore on _ChatDetailsStoreBase, Store {
});
}
final _$showJumpButtonAtom =
Atom(name: '_ChatDetailsStoreBase.showJumpButton');
late final _$showJumpButtonAtom =
Atom(name: 'ChatDetailsStoreBase.showJumpButton', context: context);
@override
bool get showJumpButton {
@ -48,7 +49,8 @@ mixin _$ChatDetailsStore on _ChatDetailsStoreBase, Store {
});
}
final _$filterTextAtom = Atom(name: '_ChatDetailsStoreBase.filterText');
late final _$filterTextAtom =
Atom(name: 'ChatDetailsStoreBase.filterText', context: context);
@override
String get filterText {
@ -63,7 +65,8 @@ mixin _$ChatDetailsStore on _ChatDetailsStoreBase, Store {
});
}
final _$_chatUsersAtom = Atom(name: '_ChatDetailsStoreBase._chatUsers');
late final _$_chatUsersAtom =
Atom(name: 'ChatDetailsStoreBase._chatUsers', context: context);
ChatUsers? get chatUsers {
_$_chatUsersAtom.reportRead();
@ -80,7 +83,8 @@ mixin _$ChatDetailsStore on _ChatDetailsStoreBase, Store {
});
}
final _$_errorAtom = Atom(name: '_ChatDetailsStoreBase._error');
late final _$_errorAtom =
Atom(name: 'ChatDetailsStoreBase._error', context: context);
String? get error {
_$_errorAtom.reportRead();
@ -97,8 +101,8 @@ mixin _$ChatDetailsStore on _ChatDetailsStoreBase, Store {
});
}
final _$updateChattersAsyncAction =
AsyncAction('_ChatDetailsStoreBase.updateChatters');
late final _$updateChattersAsyncAction =
AsyncAction('ChatDetailsStoreBase.updateChatters', context: context);
@override
Future<void> updateChatters(String userLogin) {

View file

@ -1,7 +1,6 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/models/badges.dart';
@ -16,9 +15,9 @@ import 'package:web_socket_channel/web_socket_channel.dart';
part 'chat_store.g.dart';
/// The store and view-model for chat-related activities.
class ChatStore = _ChatStoreBase with _$ChatStore;
class ChatStore = ChatStoreBase with _$ChatStore;
abstract class _ChatStoreBase with Store {
abstract class ChatStoreBase with Store {
static const _messageUpperLimit = 7500;
static const _messageLimit = 5000;
@ -81,7 +80,7 @@ abstract class _ChatStoreBase with Store {
final reactions = <ReactionDisposer>[];
_ChatStoreBase({
ChatStoreBase({
required this.auth,
required this.chatDetailsStore,
required this.assetsStore,
@ -188,7 +187,7 @@ abstract class _ChatStoreBase with Store {
if (_autoScroll) {
if (_messages.length >= _messageLimit) _messages.removeAt(0);
SchedulerBinding.instance?.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (scrollController.hasClients) scrollController.jumpTo(scrollController.position.maxScrollExtent);
});
}
@ -237,7 +236,7 @@ abstract class _ChatStoreBase with Store {
scrollController.jumpTo(scrollController.position.maxScrollExtent);
// Schedule a postFrameCallback in the event a new message is added at the same time.
SchedulerBinding.instance?.addPostFrameCallback((_) => scrollController.jumpTo(scrollController.position.maxScrollExtent));
WidgetsBinding.instance.addPostFrameCallback((_) => scrollController.jumpTo(scrollController.position.maxScrollExtent));
}
@action
@ -310,7 +309,7 @@ abstract class _ChatStoreBase with Store {
if (message.length > 3 && message.substring(0, 3) == '/me') {
userStateString += ' :\x01ACTION ${message.replaceRange(0, 3, '').trim()}\x01';
} else {
userStateString += ' :' + message.trim();
userStateString += ' :${message.trim()}';
}
final userChatMessage = IRCMessage.fromString(userStateString);
@ -324,7 +323,7 @@ abstract class _ChatStoreBase with Store {
}
// Scroll to the latest message after sending.
SchedulerBinding.instance?.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (scrollController.hasClients) scrollController.jumpTo(scrollController.position.maxScrollExtent);
});
}
@ -333,15 +332,15 @@ abstract class _ChatStoreBase with Store {
@action
void addEmote(Emote emote, {bool autocompleteMode = false}) {
if (textController.text.isEmpty || textController.text.endsWith(' ')) {
textController.text += emote.name + ' ';
textController.text += '${emote.name} ';
} else if (autocompleteMode && _showAutocomplete && textController.text.endsWith('')) {
final split = textController.text.split(' ')
..removeLast()
..add(emote.name + ' ');
..add('${emote.name} ');
textController.text = split.join(' ');
} else {
textController.text += ' ' + emote.name + ' ';
textController.text += ' ${emote.name} ';
}
assetsStore.recentEmotes

View file

@ -6,10 +6,11 @@ part of 'chat_store.dart';
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
mixin _$ChatStore on _ChatStoreBase, Store {
final _$_messagesAtom = Atom(name: '_ChatStoreBase._messages');
mixin _$ChatStore on ChatStoreBase, Store {
late final _$_messagesAtom =
Atom(name: 'ChatStoreBase._messages', context: context);
ObservableList<IRCMessage> get messages {
_$_messagesAtom.reportRead();
@ -26,7 +27,8 @@ mixin _$ChatStore on _ChatStoreBase, Store {
});
}
final _$_autoScrollAtom = Atom(name: '_ChatStoreBase._autoScroll');
late final _$_autoScrollAtom =
Atom(name: 'ChatStoreBase._autoScroll', context: context);
bool get autoScroll {
_$_autoScrollAtom.reportRead();
@ -43,8 +45,8 @@ mixin _$ChatStore on _ChatStoreBase, Store {
});
}
final _$_showAutocompleteAtom =
Atom(name: '_ChatStoreBase._showAutocomplete');
late final _$_showAutocompleteAtom =
Atom(name: 'ChatStoreBase._showAutocomplete', context: context);
bool get showAutocomplete {
_$_showAutocompleteAtom.reportRead();
@ -61,7 +63,8 @@ mixin _$ChatStore on _ChatStoreBase, Store {
});
}
final _$_userStateAtom = Atom(name: '_ChatStoreBase._userState');
late final _$_userStateAtom =
Atom(name: 'ChatStoreBase._userState', context: context);
USERSTATE get userState {
_$_userStateAtom.reportRead();
@ -78,61 +81,61 @@ mixin _$ChatStore on _ChatStoreBase, Store {
});
}
final _$_ChatStoreBaseActionController =
ActionController(name: '_ChatStoreBase');
late final _$ChatStoreBaseActionController =
ActionController(name: 'ChatStoreBase', context: context);
@override
void _handleIRCData(String data) {
final _$actionInfo = _$_ChatStoreBaseActionController.startAction(
name: '_ChatStoreBase._handleIRCData');
final _$actionInfo = _$ChatStoreBaseActionController.startAction(
name: 'ChatStoreBase._handleIRCData');
try {
return super._handleIRCData(data);
} finally {
_$_ChatStoreBaseActionController.endAction(_$actionInfo);
_$ChatStoreBaseActionController.endAction(_$actionInfo);
}
}
@override
void resumeScroll() {
final _$actionInfo = _$_ChatStoreBaseActionController.startAction(
name: '_ChatStoreBase.resumeScroll');
final _$actionInfo = _$ChatStoreBaseActionController.startAction(
name: 'ChatStoreBase.resumeScroll');
try {
return super.resumeScroll();
} finally {
_$_ChatStoreBaseActionController.endAction(_$actionInfo);
_$ChatStoreBaseActionController.endAction(_$actionInfo);
}
}
@override
void connectToChat() {
final _$actionInfo = _$_ChatStoreBaseActionController.startAction(
name: '_ChatStoreBase.connectToChat');
final _$actionInfo = _$ChatStoreBaseActionController.startAction(
name: 'ChatStoreBase.connectToChat');
try {
return super.connectToChat();
} finally {
_$_ChatStoreBaseActionController.endAction(_$actionInfo);
_$ChatStoreBaseActionController.endAction(_$actionInfo);
}
}
@override
void sendMessage(String message) {
final _$actionInfo = _$_ChatStoreBaseActionController.startAction(
name: '_ChatStoreBase.sendMessage');
final _$actionInfo = _$ChatStoreBaseActionController.startAction(
name: 'ChatStoreBase.sendMessage');
try {
return super.sendMessage(message);
} finally {
_$_ChatStoreBaseActionController.endAction(_$actionInfo);
_$ChatStoreBaseActionController.endAction(_$actionInfo);
}
}
@override
void addEmote(Emote emote, {bool autocompleteMode = false}) {
final _$actionInfo = _$_ChatStoreBaseActionController.startAction(
name: '_ChatStoreBase.addEmote');
final _$actionInfo = _$ChatStoreBaseActionController.startAction(
name: 'ChatStoreBase.addEmote');
try {
return super.addEmote(emote, autocompleteMode: autocompleteMode);
} finally {
_$_ChatStoreBaseActionController.endAction(_$actionInfo);
_$ChatStoreBaseActionController.endAction(_$actionInfo);
}
}

View file

@ -11,9 +11,9 @@ import 'package:webview_flutter/webview_flutter.dart';
part 'video_store.g.dart';
class VideoStore = _VideoStoreBase with _$VideoStore;
class VideoStore = VideoStoreBase with _$VideoStore;
abstract class _VideoStoreBase with Store {
abstract class VideoStoreBase with Store {
final TwitchApi twitchApi;
WebViewController? controller;
@ -60,7 +60,7 @@ abstract class _VideoStoreBase with Store {
final AuthStore authStore;
final SettingsStore settingsStore;
_VideoStoreBase({
VideoStoreBase({
required this.userLogin,
required this.twitchApi,
required this.authStore,

View file

@ -6,18 +6,19 @@ part of 'video_store.dart';
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
mixin _$VideoStore on _VideoStoreBase, Store {
mixin _$VideoStore on VideoStoreBase, Store {
Computed<String>? _$videoUrlComputed;
@override
String get videoUrl =>
(_$videoUrlComputed ??= Computed<String>(() => super.videoUrl,
name: '_VideoStoreBase.videoUrl'))
name: 'VideoStoreBase.videoUrl'))
.value;
final _$sleepHoursAtom = Atom(name: '_VideoStoreBase.sleepHours');
late final _$sleepHoursAtom =
Atom(name: 'VideoStoreBase.sleepHours', context: context);
@override
int get sleepHours {
@ -32,7 +33,8 @@ mixin _$VideoStore on _VideoStoreBase, Store {
});
}
final _$sleepMinutesAtom = Atom(name: '_VideoStoreBase.sleepMinutes');
late final _$sleepMinutesAtom =
Atom(name: 'VideoStoreBase.sleepMinutes', context: context);
@override
int get sleepMinutes {
@ -47,7 +49,8 @@ mixin _$VideoStore on _VideoStoreBase, Store {
});
}
final _$timeRemainingAtom = Atom(name: '_VideoStoreBase.timeRemaining');
late final _$timeRemainingAtom =
Atom(name: 'VideoStoreBase.timeRemaining', context: context);
@override
Duration get timeRemaining {
@ -62,7 +65,8 @@ mixin _$VideoStore on _VideoStoreBase, Store {
});
}
final _$_pausedAtom = Atom(name: '_VideoStoreBase._paused');
late final _$_pausedAtom =
Atom(name: 'VideoStoreBase._paused', context: context);
bool get paused {
_$_pausedAtom.reportRead();
@ -79,7 +83,8 @@ mixin _$VideoStore on _VideoStoreBase, Store {
});
}
final _$_overlayVisibleAtom = Atom(name: '_VideoStoreBase._overlayVisible');
late final _$_overlayVisibleAtom =
Atom(name: 'VideoStoreBase._overlayVisible', context: context);
bool get overlayVisible {
_$_overlayVisibleAtom.reportRead();
@ -96,7 +101,8 @@ mixin _$VideoStore on _VideoStoreBase, Store {
});
}
final _$_streamInfoAtom = Atom(name: '_VideoStoreBase._streamInfo');
late final _$_streamInfoAtom =
Atom(name: 'VideoStoreBase._streamInfo', context: context);
StreamTwitch? get streamInfo {
_$_streamInfoAtom.reportRead();
@ -113,16 +119,16 @@ mixin _$VideoStore on _VideoStoreBase, Store {
});
}
final _$updateStreamInfoAsyncAction =
AsyncAction('_VideoStoreBase.updateStreamInfo');
late final _$updateStreamInfoAsyncAction =
AsyncAction('VideoStoreBase.updateStreamInfo', context: context);
@override
Future<void> updateStreamInfo() {
return _$updateStreamInfoAsyncAction.run(() => super.updateStreamInfo());
}
final _$handleToggleOverlayAsyncAction =
AsyncAction('_VideoStoreBase.handleToggleOverlay');
late final _$handleToggleOverlayAsyncAction =
AsyncAction('VideoStoreBase.handleToggleOverlay', context: context);
@override
Future<void> handleToggleOverlay() {
@ -130,39 +136,39 @@ mixin _$VideoStore on _VideoStoreBase, Store {
.run(() => super.handleToggleOverlay());
}
final _$_VideoStoreBaseActionController =
ActionController(name: '_VideoStoreBase');
late final _$VideoStoreBaseActionController =
ActionController(name: 'VideoStoreBase', context: context);
@override
void handlePausePlay() {
final _$actionInfo = _$_VideoStoreBaseActionController.startAction(
name: '_VideoStoreBase.handlePausePlay');
final _$actionInfo = _$VideoStoreBaseActionController.startAction(
name: 'VideoStoreBase.handlePausePlay');
try {
return super.handlePausePlay();
} finally {
_$_VideoStoreBaseActionController.endAction(_$actionInfo);
_$VideoStoreBaseActionController.endAction(_$actionInfo);
}
}
@override
void handleVideoTap() {
final _$actionInfo = _$_VideoStoreBaseActionController.startAction(
name: '_VideoStoreBase.handleVideoTap');
final _$actionInfo = _$VideoStoreBaseActionController.startAction(
name: 'VideoStoreBase.handleVideoTap');
try {
return super.handleVideoTap();
} finally {
_$_VideoStoreBaseActionController.endAction(_$actionInfo);
_$VideoStoreBaseActionController.endAction(_$actionInfo);
}
}
@override
void handleExpand() {
final _$actionInfo = _$_VideoStoreBaseActionController.startAction(
name: '_VideoStoreBase.handleExpand');
final _$actionInfo = _$VideoStoreBaseActionController.startAction(
name: 'VideoStoreBase.handleExpand');
try {
return super.handleExpand();
} finally {
_$_VideoStoreBaseActionController.endAction(_$actionInfo);
_$VideoStoreBaseActionController.endAction(_$actionInfo);
}
}

View file

@ -67,8 +67,8 @@ class VideoOverlay extends StatelessWidget {
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text('Dismiss'),
style: TextButton.styleFrom(primary: Colors.red),
child: const Text('Dismiss'),
),
Observer(
builder: (context) => ElevatedButton(
@ -176,7 +176,7 @@ class VideoOverlay extends StatelessWidget {
);
}
final streamerName = regexEnglish.hasMatch(streamInfo.userName) ? streamInfo.userName : streamInfo.userName + ' (${streamInfo.userLogin})';
final streamerName = regexEnglish.hasMatch(streamInfo.userName) ? streamInfo.userName : '${streamInfo.userName} (${streamInfo.userLogin})';
final streamer = Row(
children: [

View file

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/api/bttv_api.dart';
@ -32,7 +31,7 @@ class VideoChat extends StatefulWidget {
}) : super(key: key);
@override
_VideoChatState createState() => _VideoChatState();
State<VideoChat> createState() => _VideoChatState();
}
class _VideoChatState extends State<VideoChat> {
@ -124,7 +123,7 @@ class _VideoChatState extends State<VideoChat> {
final appBar = AppBar(
title: Text(
regexEnglish.hasMatch(_chatStore.displayName) ? _chatStore.displayName : _chatStore.displayName + ' (${_chatStore.channelName})',
regexEnglish.hasMatch(_chatStore.displayName) ? _chatStore.displayName : '${_chatStore.displayName} (${_chatStore.channelName})',
style: const TextStyle(fontSize: 20),
),
actions: [
@ -147,7 +146,7 @@ class _VideoChatState extends State<VideoChat> {
body: OrientationBuilder(
builder: (context, orientation) {
// Scroll to bottom when summoning keyboard or rotating.
SchedulerBinding.instance?.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_chatStore.scrollController.hasClients) _chatStore.scrollController.jumpTo(_chatStore.scrollController.position.maxScrollExtent);
});

View file

@ -13,7 +13,7 @@ class Search extends StatefulWidget {
const Search({Key? key}) : super(key: key);
@override
_SearchState createState() => _SearchState();
State<Search> createState() => _SearchState();
}
class _SearchState extends State<Search> {

View file

@ -9,7 +9,7 @@ import 'package:frosty/widgets/loading_indicator.dart';
import 'package:frosty/widgets/profile_picture.dart';
import 'package:mobx/mobx.dart';
class SearchResultsChannels extends StatelessWidget {
class SearchResultsChannels extends StatefulWidget {
final SearchStore searchStore;
final String query;
@ -19,9 +19,16 @@ class SearchResultsChannels extends StatelessWidget {
required this.query,
}) : super(key: key);
@override
State<SearchResultsChannels> createState() => _SearchResultsChannelsState();
}
class _SearchResultsChannelsState extends State<SearchResultsChannels> {
Future<void> _handleSearch(BuildContext context, String search) async {
try {
final channelInfo = await searchStore.searchChannel(search);
final channelInfo = await widget.searchStore.searchChannel(search);
if (!mounted) return;
Navigator.push(
context,
MaterialPageRoute(
@ -45,7 +52,7 @@ class SearchResultsChannels extends StatelessWidget {
Widget build(BuildContext context) {
return Observer(
builder: (context) {
final future = searchStore.channelFuture;
final future = widget.searchStore.channelFuture;
switch (future!.status) {
case FutureStatus.pending:
@ -62,14 +69,14 @@ class SearchResultsChannels extends StatelessWidget {
);
case FutureStatus.fulfilled:
final results = (future.result as List<ChannelQuery>)
.where((channel) => !searchStore.authStore.user.blockedUsers.map((blockedUser) => blockedUser.userId).contains(channel.id));
.where((channel) => !widget.searchStore.authStore.user.blockedUsers.map((blockedUser) => blockedUser.userId).contains(channel.id));
return SliverList(
delegate: SliverChildListDelegate.fixed([
...results.map(
(channel) {
final displayName =
regexEnglish.hasMatch(channel.displayName) ? channel.displayName : channel.displayName + ' (${channel.broadcasterLogin})';
regexEnglish.hasMatch(channel.displayName) ? channel.displayName : '${channel.displayName} (${channel.broadcasterLogin})';
return ListTile(
title: Text(displayName),
@ -106,7 +113,7 @@ class SearchResultsChannels extends StatelessWidget {
onLongPress: () => showModalBottomSheet(
context: context,
builder: (context) => BlockReportModal(
authStore: searchStore.authStore,
authStore: widget.searchStore.authStore,
name: displayName,
userLogin: channel.broadcasterLogin,
userId: channel.id,
@ -116,8 +123,8 @@ class SearchResultsChannels extends StatelessWidget {
},
),
ListTile(
title: Text('Go to channel "$query"'),
onTap: () => _handleSearch(context, query),
title: Text('Go to channel "${widget.query}"'),
onTap: () => _handleSearch(context, widget.query),
trailing: Icon(Icons.adaptive.arrow_forward),
)
]),

View file

@ -8,9 +8,9 @@ import 'package:mobx/mobx.dart';
part 'categories_store.g.dart';
class CategoriesStore = _CategoriesStoreBase with _$CategoriesStore;
class CategoriesStore = CategoriesStoreBase with _$CategoriesStore;
abstract class _CategoriesStoreBase with Store {
abstract class CategoriesStoreBase with Store {
/// The authentication store.
final AuthStore authStore;
@ -42,7 +42,7 @@ abstract class _CategoriesStoreBase with Store {
@readonly
String? _error;
_CategoriesStoreBase({required this.authStore, required this.twitchApi}) {
CategoriesStoreBase({required this.authStore, required this.twitchApi}) {
scrollController.addListener(() {
if (scrollController.position.atEdge || scrollController.position.outOfRange) {
showJumpButton = false;

View file

@ -6,11 +6,11 @@ part of 'categories_store.dart';
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
mixin _$CategoriesStore on _CategoriesStoreBase, Store {
final _$showJumpButtonAtom =
Atom(name: '_CategoriesStoreBase.showJumpButton');
mixin _$CategoriesStore on CategoriesStoreBase, Store {
late final _$showJumpButtonAtom =
Atom(name: 'CategoriesStoreBase.showJumpButton', context: context);
@override
bool get showJumpButton {
@ -25,7 +25,8 @@ mixin _$CategoriesStore on _CategoriesStoreBase, Store {
});
}
final _$_categoriesAtom = Atom(name: '_CategoriesStoreBase._categories');
late final _$_categoriesAtom =
Atom(name: 'CategoriesStoreBase._categories', context: context);
ObservableList<CategoryTwitch> get categories {
_$_categoriesAtom.reportRead();
@ -42,7 +43,8 @@ mixin _$CategoriesStore on _CategoriesStoreBase, Store {
});
}
final _$_errorAtom = Atom(name: '_CategoriesStoreBase._error');
late final _$_errorAtom =
Atom(name: 'CategoriesStoreBase._error', context: context);
String? get error {
_$_errorAtom.reportRead();
@ -59,25 +61,25 @@ mixin _$CategoriesStore on _CategoriesStoreBase, Store {
});
}
final _$getCategoriesAsyncAction =
AsyncAction('_CategoriesStoreBase.getCategories');
late final _$getCategoriesAsyncAction =
AsyncAction('CategoriesStoreBase.getCategories', context: context);
@override
Future<void> getCategories() {
return _$getCategoriesAsyncAction.run(() => super.getCategories());
}
final _$_CategoriesStoreBaseActionController =
ActionController(name: '_CategoriesStoreBase');
late final _$CategoriesStoreBaseActionController =
ActionController(name: 'CategoriesStoreBase', context: context);
@override
Future<void> refreshCategories() {
final _$actionInfo = _$_CategoriesStoreBaseActionController.startAction(
name: '_CategoriesStoreBase.refreshCategories');
final _$actionInfo = _$CategoriesStoreBaseActionController.startAction(
name: 'CategoriesStoreBase.refreshCategories');
try {
return super.refreshCategories();
} finally {
_$_CategoriesStoreBaseActionController.endAction(_$actionInfo);
_$CategoriesStoreBaseActionController.endAction(_$actionInfo);
}
}

View file

@ -1,15 +1,16 @@
import 'package:frosty/core/auth/auth_store.dart';
import 'package:mobx/mobx.dart';
part 'home_store.g.dart';
class HomeStore = _HomeStoreBase with _$HomeStore;
class HomeStore = HomeStoreBase with _$HomeStore;
abstract class _HomeStoreBase with Store {
abstract class HomeStoreBase with Store {
final AuthStore authStore;
late final ReactionDisposer _disposeReaction;
_HomeStoreBase({required this.authStore}) {
HomeStoreBase({required this.authStore}) {
_disposeReaction = reaction(
(_) => authStore.isLoggedIn,
(_) => _selectedIndex = 0,

View file

@ -6,10 +6,11 @@ part of 'home_store.dart';
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
mixin _$HomeStore on _HomeStoreBase, Store {
final _$_selectedIndexAtom = Atom(name: '_HomeStoreBase._selectedIndex');
mixin _$HomeStore on HomeStoreBase, Store {
late final _$_selectedIndexAtom =
Atom(name: 'HomeStoreBase._selectedIndex', context: context);
int get selectedIndex {
_$_selectedIndexAtom.reportRead();
@ -26,17 +27,17 @@ mixin _$HomeStore on _HomeStoreBase, Store {
});
}
final _$_HomeStoreBaseActionController =
ActionController(name: '_HomeStoreBase');
late final _$HomeStoreBaseActionController =
ActionController(name: 'HomeStoreBase', context: context);
@override
void handleTap(int index) {
final _$actionInfo = _$_HomeStoreBaseActionController.startAction(
name: '_HomeStoreBase.handleTap');
final _$actionInfo = _$HomeStoreBaseActionController.startAction(
name: 'HomeStoreBase.handleTap');
try {
return super.handleTap(index);
} finally {
_$_HomeStoreBaseActionController.endAction(_$actionInfo);
_$HomeStoreBaseActionController.endAction(_$actionInfo);
}
}

View file

@ -9,9 +9,9 @@ import 'package:mobx/mobx.dart';
part 'list_store.g.dart';
class ListStore = _ListStoreBase with _$ListStore;
class ListStore = ListStoreBase with _$ListStore;
abstract class _ListStoreBase with Store {
abstract class ListStoreBase with Store {
/// The authentication store.
final AuthStore authStore;
@ -55,7 +55,7 @@ abstract class _ListStoreBase with Store {
@readonly
String? _error;
_ListStoreBase({
ListStoreBase({
required this.authStore,
required this.twitchApi,
required this.listType,

View file

@ -6,18 +6,19 @@ part of 'list_store.dart';
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
mixin _$ListStore on _ListStoreBase, Store {
mixin _$ListStore on ListStoreBase, Store {
Computed<ObservableList<StreamTwitch>>? _$streamsComputed;
@override
ObservableList<StreamTwitch> get streams => (_$streamsComputed ??=
Computed<ObservableList<StreamTwitch>>(() => super.streams,
name: '_ListStoreBase.streams'))
name: 'ListStoreBase.streams'))
.value;
final _$showJumpButtonAtom = Atom(name: '_ListStoreBase.showJumpButton');
late final _$showJumpButtonAtom =
Atom(name: 'ListStoreBase.showJumpButton', context: context);
@override
bool get showJumpButton {
@ -32,7 +33,8 @@ mixin _$ListStore on _ListStoreBase, Store {
});
}
final _$_allStreamsAtom = Atom(name: '_ListStoreBase._allStreams');
late final _$_allStreamsAtom =
Atom(name: 'ListStoreBase._allStreams', context: context);
ObservableList<StreamTwitch> get allStreams {
_$_allStreamsAtom.reportRead();
@ -49,7 +51,8 @@ mixin _$ListStore on _ListStoreBase, Store {
});
}
final _$_errorAtom = Atom(name: '_ListStoreBase._error');
late final _$_errorAtom =
Atom(name: 'ListStoreBase._error', context: context);
String? get error {
_$_errorAtom.reportRead();
@ -66,24 +69,25 @@ mixin _$ListStore on _ListStoreBase, Store {
});
}
final _$getStreamsAsyncAction = AsyncAction('_ListStoreBase.getStreams');
late final _$getStreamsAsyncAction =
AsyncAction('ListStoreBase.getStreams', context: context);
@override
Future<void> getStreams() {
return _$getStreamsAsyncAction.run(() => super.getStreams());
}
final _$_ListStoreBaseActionController =
ActionController(name: '_ListStoreBase');
late final _$ListStoreBaseActionController =
ActionController(name: 'ListStoreBase', context: context);
@override
Future<void> refreshStreams() {
final _$actionInfo = _$_ListStoreBaseActionController.startAction(
name: '_ListStoreBase.refreshStreams');
final _$actionInfo = _$ListStoreBaseActionController.startAction(
name: 'ListStoreBase.refreshStreams');
try {
return super.refreshStreams();
} finally {
_$_ListStoreBaseActionController.endAction(_$actionInfo);
_$ListStoreBaseActionController.endAction(_$actionInfo);
}
}

View file

@ -7,9 +7,9 @@ import 'package:shared_preferences/shared_preferences.dart';
part 'search_store.g.dart';
class SearchStore = _SearchStoreBase with _$SearchStore;
class SearchStore = SearchStoreBase with _$SearchStore;
abstract class _SearchStoreBase with Store {
abstract class SearchStoreBase with Store {
final AuthStore authStore;
final TwitchApi twitchApi;
@ -23,7 +23,7 @@ abstract class _SearchStoreBase with Store {
@readonly
ObservableFuture<CategoriesTwitch?>? _categoryFuture;
_SearchStoreBase({required this.authStore, required this.twitchApi}) {
SearchStoreBase({required this.authStore, required this.twitchApi}) {
init();
}

View file

@ -6,10 +6,11 @@ part of 'search_store.dart';
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
mixin _$SearchStore on _SearchStoreBase, Store {
final _$_searchHistoryAtom = Atom(name: '_SearchStoreBase._searchHistory');
mixin _$SearchStore on SearchStoreBase, Store {
late final _$_searchHistoryAtom =
Atom(name: 'SearchStoreBase._searchHistory', context: context);
ObservableList<String> get searchHistory {
_$_searchHistoryAtom.reportRead();
@ -26,7 +27,8 @@ mixin _$SearchStore on _SearchStoreBase, Store {
});
}
final _$_channelFutureAtom = Atom(name: '_SearchStoreBase._channelFuture');
late final _$_channelFutureAtom =
Atom(name: 'SearchStoreBase._channelFuture', context: context);
ObservableFuture<List<ChannelQuery>>? get channelFuture {
_$_channelFutureAtom.reportRead();
@ -43,7 +45,8 @@ mixin _$SearchStore on _SearchStoreBase, Store {
});
}
final _$_categoryFutureAtom = Atom(name: '_SearchStoreBase._categoryFuture');
late final _$_categoryFutureAtom =
Atom(name: 'SearchStoreBase._categoryFuture', context: context);
ObservableFuture<CategoriesTwitch?>? get categoryFuture {
_$_categoryFutureAtom.reportRead();
@ -60,14 +63,16 @@ mixin _$SearchStore on _SearchStoreBase, Store {
});
}
final _$initAsyncAction = AsyncAction('_SearchStoreBase.init');
late final _$initAsyncAction =
AsyncAction('SearchStoreBase.init', context: context);
@override
Future<void> init() {
return _$initAsyncAction.run(() => super.init());
}
final _$handleQueryAsyncAction = AsyncAction('_SearchStoreBase.handleQuery');
late final _$handleQueryAsyncAction =
AsyncAction('SearchStoreBase.handleQuery', context: context);
@override
Future<void> handleQuery(String query) {

View file

@ -13,7 +13,7 @@ class Categories extends StatefulWidget {
const Categories({Key? key}) : super(key: key);
@override
_CategoriesState createState() => _CategoriesState();
State<Categories> createState() => _CategoriesState();
}
class _CategoriesState extends State<Categories> with AutomaticKeepAliveClientMixin {
@ -42,6 +42,8 @@ class _CategoriesState extends State<Categories> with AutomaticKeepAliveClientMi
content: Text(_categoriesStore.error!),
behavior: SnackBarBehavior.floating,
);
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
},

View file

@ -9,11 +9,16 @@ import 'package:frosty/widgets/loading_indicator.dart';
import 'package:frosty/widgets/scroll_to_top_button.dart';
import 'package:provider/provider.dart';
class CategoryStreams extends StatelessWidget {
class CategoryStreams extends StatefulWidget {
final ListStore listStore;
const CategoryStreams({Key? key, required this.listStore}) : super(key: key);
@override
State<CategoryStreams> createState() => _CategoryStreamsState();
}
class _CategoryStreamsState extends State<CategoryStreams> {
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
@ -29,19 +34,21 @@ class CategoryStreams extends StatelessWidget {
body: RefreshIndicator(
onRefresh: () async {
HapticFeedback.lightImpact();
await listStore.refreshStreams();
await widget.listStore.refreshStreams();
if (listStore.error != null) {
if (widget.listStore.error != null) {
final snackBar = SnackBar(
content: Text(listStore.error!),
content: Text(widget.listStore.error!),
behavior: SnackBarBehavior.floating,
);
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
},
child: Observer(
builder: (context) {
if (listStore.streams.isEmpty && listStore.isLoading && listStore.error == null) {
if (widget.listStore.streams.isEmpty && widget.listStore.isLoading && widget.listStore.error == null) {
return const LoadingIndicator(subtitle: Text('Loading streams...'));
}
return Stack(
@ -49,7 +56,7 @@ class CategoryStreams extends StatelessWidget {
children: [
CustomScrollView(
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
controller: listStore.scrollController,
controller: widget.listStore.scrollController,
slivers: [
SliverAppBar(
stretch: true,
@ -62,14 +69,14 @@ class CategoryStreams extends StatelessWidget {
],
centerTitle: true,
title: Text(
listStore.categoryInfo!.name,
widget.listStore.categoryInfo!.name,
style: Theme.of(context).textTheme.headline6?.copyWith(fontWeight: FontWeight.w800),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
background: CachedNetworkImage(
imageUrl: listStore.categoryInfo!.boxArtUrl.replaceRange(
listStore.categoryInfo!.boxArtUrl.lastIndexOf('-') + 1,
imageUrl: widget.listStore.categoryInfo!.boxArtUrl.replaceRange(
widget.listStore.categoryInfo!.boxArtUrl.lastIndexOf('-') + 1,
null,
'${artWidth}x$artHeight.jpg',
),
@ -85,13 +92,13 @@ class CategoryStreams extends StatelessWidget {
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index > listStore.streams.length / 2 && listStore.hasMore) {
listStore.getStreams();
if (index > widget.listStore.streams.length / 2 && widget.listStore.hasMore) {
widget.listStore.getStreams();
}
return Observer(
builder: (context) => StreamCard(
listStore: listStore,
streamInfo: listStore.streams[index],
listStore: widget.listStore,
streamInfo: widget.listStore.streams[index],
width: thumbnailWidth,
height: thumbnailHeight,
showUptime: context.read<SettingsStore>().showThumbnailUptime,
@ -100,7 +107,7 @@ class CategoryStreams extends StatelessWidget {
),
);
},
childCount: listStore.streams.length,
childCount: widget.listStore.streams.length,
),
),
),
@ -110,7 +117,7 @@ class CategoryStreams extends StatelessWidget {
child: Observer(
builder: (context) => AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: listStore.showJumpButton ? ScrollToTopButton(scrollController: listStore.scrollController) : null,
child: widget.listStore.showJumpButton ? ScrollToTopButton(scrollController: widget.listStore.scrollController) : null,
),
),
),

View file

@ -14,7 +14,7 @@ import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
/// A tappable card widget that displays a stream's thumbnail and details.
class StreamCard extends StatelessWidget {
class StreamCard extends StatefulWidget {
final ListStore listStore;
final StreamTwitch streamInfo;
final int width;
@ -34,6 +34,11 @@ class StreamCard extends StatelessWidget {
this.showCategory = true,
}) : super(key: key);
@override
State<StreamCard> createState() => _StreamCardState();
}
class _StreamCardState extends State<StreamCard> {
@override
Widget build(BuildContext context) {
final time = DateTime.now();
@ -42,42 +47,44 @@ class StreamCard extends StatelessWidget {
final thumbnail = AspectRatio(
aspectRatio: 16 / 9,
child: CachedNetworkImage(
imageUrl: streamInfo.thumbnailUrl.replaceFirst('-{width}x{height}', '-${width}x$height') + cacheUrlExtension,
imageUrl: widget.streamInfo.thumbnailUrl.replaceFirst('-{width}x{height}', '-${widget.width}x${widget.height}') + cacheUrlExtension,
placeholder: (context, url) => const LoadingIndicator(),
useOldImageOnUrlChange: true,
),
);
final streamerName = regexEnglish.hasMatch(streamInfo.userName) ? streamInfo.userName : streamInfo.userName + ' (${streamInfo.userLogin})';
final streamerName = regexEnglish.hasMatch(widget.streamInfo.userName)
? widget.streamInfo.userName
: '${widget.streamInfo.userName} (${widget.streamInfo.userLogin})';
return InkWell(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => VideoChat(
userId: streamInfo.userId,
userName: streamInfo.userName,
userLogin: streamInfo.userLogin,
userId: widget.streamInfo.userId,
userName: widget.streamInfo.userName,
userLogin: widget.streamInfo.userLogin,
),
),
),
onLongPress: () => showModalBottomSheet(
context: context,
builder: (context) => BlockReportModal(
authStore: listStore.authStore,
authStore: widget.listStore.authStore,
name: streamerName,
userLogin: streamInfo.userLogin,
userId: streamInfo.userId,
userLogin: widget.streamInfo.userLogin,
userId: widget.streamInfo.userId,
),
),
child: Padding(
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: showThumbnail ? 15.0 : 5.0),
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: widget.showThumbnail ? 15.0 : 5.0),
child: Row(
children: [
if (showThumbnail)
if (widget.showThumbnail)
Flexible(
flex: 1,
child: showUptime
child: widget.showUptime
? Stack(
alignment: AlignmentDirectional.bottomEnd,
children: [
@ -86,7 +93,7 @@ class StreamCard extends StatelessWidget {
color: const Color.fromRGBO(0, 0, 0, 0.5),
padding: const EdgeInsets.symmetric(horizontal: 2.0),
child: Text(
DateTime.now().difference(DateTime.parse(streamInfo.startedAt)).toString().split('.')[0],
DateTime.now().difference(DateTime.parse(widget.streamInfo.startedAt)).toString().split('.')[0],
style: const TextStyle(fontSize: 12, color: Colors.white),
),
)
@ -104,7 +111,7 @@ class StreamCard extends StatelessWidget {
Row(
children: [
ProfilePicture(
userLogin: streamInfo.userLogin,
userLogin: widget.streamInfo.userLogin,
radius: 10.0,
),
const SizedBox(width: 5.0),
@ -126,23 +133,23 @@ class StreamCard extends StatelessWidget {
),
const SizedBox(height: 5.0),
Tooltip(
message: streamInfo.title.trim(),
message: widget.streamInfo.title.trim(),
preferBelow: false,
padding: const EdgeInsets.all(10.0),
child: Text(
streamInfo.title.trim(),
widget.streamInfo.title.trim(),
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w500),
),
),
const SizedBox(height: 5.0),
if (showCategory) ...[
if (widget.showCategory) ...[
InkWell(
child: Tooltip(
message: streamInfo.gameName,
message: widget.streamInfo.gameName,
preferBelow: false,
child: Text(
streamInfo.gameName,
widget.streamInfo.gameName,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12.0,
@ -154,8 +161,9 @@ class StreamCard extends StatelessWidget {
onTap: () async {
final category = await context
.read<TwitchApi>()
.getCategory(headers: context.read<AuthStore>().headersTwitch, gameId: streamInfo.gameId);
.getCategory(headers: context.read<AuthStore>().headersTwitch, gameId: widget.streamInfo.gameId);
if (!mounted) return;
Navigator.push(
context,
MaterialPageRoute(
@ -174,7 +182,7 @@ class StreamCard extends StatelessWidget {
const SizedBox(height: 5.0),
],
Text(
'${NumberFormat().format(streamInfo.viewerCount)} viewers',
'${NumberFormat().format(widget.streamInfo.viewerCount)} viewers',
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w300,

View file

@ -22,7 +22,7 @@ class StreamsList extends StatefulWidget {
}) : super(key: key);
@override
_StreamsListState createState() => _StreamsListState();
State<StreamsList> createState() => _StreamsListState();
}
class _StreamsListState extends State<StreamsList> with AutomaticKeepAliveClientMixin {
@ -52,6 +52,8 @@ class _StreamsListState extends State<StreamsList> with AutomaticKeepAliveClient
content: Text(_listStore.error!),
behavior: SnackBarBehavior.floating,
);
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
},

View file

@ -146,8 +146,8 @@ class ProfileCard extends StatelessWidget {
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text('Cancel'),
style: TextButton.styleFrom(primary: Colors.red),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () {
@ -241,7 +241,7 @@ class BlockedUsers extends StatelessWidget {
return ListView(
children: authStore.user.blockedUsers.map(
(user) {
final displayName = regexEnglish.hasMatch(user.displayName) ? user.displayName : user.displayName + ' (${user.userLogin})';
final displayName = regexEnglish.hasMatch(user.displayName) ? user.displayName : '${user.displayName} (${user.userLogin})';
return ListTile(
title: Text(displayName),
trailing: BlockButton(

View file

@ -11,7 +11,7 @@ class ChatSettings extends StatefulWidget {
const ChatSettings({Key? key, required this.settingsStore}) : super(key: key);
@override
_ChatSettingsState createState() => _ChatSettingsState();
State<ChatSettings> createState() => _ChatSettingsState();
}
class _ChatSettingsState extends State<ChatSettings> {

View file

@ -35,6 +35,13 @@ class GeneralSettings extends StatelessWidget {
.toList(),
),
),
SwitchListTile.adaptive(
isThreeLine: true,
title: const Text('Launch URLs in External Browser'),
subtitle: const Text('Opens links in the default external browser.'),
value: settingsStore.launchUrlExternal,
onChanged: (newValue) => settingsStore.launchUrlExternal = newValue,
),
SwitchListTile.adaptive(
title: const Text('Stream Card Thumbnails'),
value: settingsStore.showThumbnails,

View file

@ -8,13 +8,19 @@ import 'package:package_info_plus/package_info_plus.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
class OtherSettings extends StatelessWidget {
class OtherSettings extends StatefulWidget {
final SettingsStore settingsStore;
const OtherSettings({
Key? key,
required this.settingsStore,
}) : super(key: key);
@override
State<OtherSettings> createState() => _OtherSettingsState();
}
class _OtherSettingsState extends State<OtherSettings> {
Future<void> _showConfirmDialog(BuildContext context) {
return showDialog(
context: context,
@ -24,12 +30,12 @@ class OtherSettings extends StatelessWidget {
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text('Cancel'),
style: TextButton.styleFrom(primary: Colors.red),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () {
settingsStore.reset();
widget.settingsStore.reset();
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
@ -78,19 +84,20 @@ class OtherSettings extends StatelessWidget {
ListTile(
leading: const Icon(Icons.launch),
title: const Text('FAQ'),
onTap: () => launch('https://github.com/tommyxchow/frosty#faq'),
onTap: () => launchUrl(Uri.parse('https://github.com/tommyxchow/frosty#faq'),
mode: widget.settingsStore.launchUrlExternal ? LaunchMode.externalApplication : LaunchMode.inAppWebView),
),
Observer(
builder: (_) => SwitchListTile.adaptive(
title: const Text('Send Anonymous Crash Logs'),
value: settingsStore.sendCrashLogs,
value: widget.settingsStore.sendCrashLogs,
onChanged: (newValue) {
if (newValue == true) {
SentryFlutter.init((options) => options.tracesSampleRate = sampleRate);
} else {
Sentry.close();
}
settingsStore.sendCrashLogs = newValue;
widget.settingsStore.sendCrashLogs = newValue;
},
),
),
@ -102,6 +109,8 @@ class OtherSettings extends StatelessWidget {
label: const Text('Clear Image Cache'),
onPressed: () async {
await DefaultCacheManager().emptyCache();
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Image cache cleared!'),

View file

@ -26,12 +26,14 @@ class Settings extends StatelessWidget {
if (Platform.isAndroid)
IconButton(
tooltip: 'Support the App',
onPressed: () => launch('https://www.buymeacoffee.com/tommychow'),
onPressed: () => launchUrl(Uri.parse('https://www.buymeacoffee.com/tommychow'),
mode: settingsStore.launchUrlExternal ? LaunchMode.externalApplication : LaunchMode.inAppWebView),
icon: const FaIcon(FontAwesomeIcons.circleDollarToSlot),
),
IconButton(
tooltip: 'View Source on GitHub',
onPressed: () => launch('https://github.com/tommyxchow/frosty'),
onPressed: () => launchUrl(Uri.parse('https://github.com/tommyxchow/frosty'),
mode: settingsStore.launchUrlExternal ? LaunchMode.externalApplication : LaunchMode.inAppWebView),
icon: const FaIcon(FontAwesomeIcons.github),
),
],

View file

@ -19,6 +19,8 @@ abstract class _SettingsStoreBase with Store {
static const defaultShowThumbnails = true;
static const defaultLaunchUrlExternal = false;
@JsonKey(defaultValue: defaultThemeType, unknownEnumValue: ThemeType.system)
@observable
var themeType = defaultThemeType;
@ -31,6 +33,10 @@ abstract class _SettingsStoreBase with Store {
@observable
var showThumbnails = defaultShowThumbnails;
@JsonKey(defaultValue: defaultLaunchUrlExternal)
@observable
var launchUrlExternal = defaultLaunchUrlExternal;
// * Video Settings
static const defaultShowVideo = true;
@ -155,6 +161,7 @@ abstract class _SettingsStoreBase with Store {
themeType = defaultThemeType;
showThumbnailUptime = defaultShowThumbnailUptime;
showThumbnails = defaultShowThumbnails;
launchUrlExternal = defaultLaunchUrlExternal;
// * Video Settings
showVideo = defaultShowVideo;

View file

@ -13,6 +13,7 @@ SettingsStore _$SettingsStoreFromJson(Map<String, dynamic> json) =>
ThemeType.system
..showThumbnailUptime = json['showThumbnailUptime'] as bool? ?? false
..showThumbnails = json['showThumbnails'] as bool? ?? true
..launchUrlExternal = json['launchUrlExternal'] as bool? ?? false
..showVideo = json['showVideo'] as bool? ?? true
..showOverlay = json['showOverlay'] as bool? ?? true
..toggleableOverlay = json['toggleableOverlay'] as bool? ?? false
@ -42,6 +43,7 @@ Map<String, dynamic> _$SettingsStoreToJson(SettingsStore instance) =>
'themeType': _$ThemeTypeEnumMap[instance.themeType],
'showThumbnailUptime': instance.showThumbnailUptime,
'showThumbnails': instance.showThumbnails,
'launchUrlExternal': instance.launchUrlExternal,
'showVideo': instance.showVideo,
'showOverlay': instance.showOverlay,
'toggleableOverlay': instance.toggleableOverlay,
@ -80,10 +82,11 @@ const _$TimestampTypeEnumMap = {
// StoreGenerator
// **************************************************************************
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
mixin _$SettingsStore on _SettingsStoreBase, Store {
final _$themeTypeAtom = Atom(name: '_SettingsStoreBase.themeType');
late final _$themeTypeAtom =
Atom(name: '_SettingsStoreBase.themeType', context: context);
@override
ThemeType get themeType {
@ -98,8 +101,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$showThumbnailUptimeAtom =
Atom(name: '_SettingsStoreBase.showThumbnailUptime');
late final _$showThumbnailUptimeAtom =
Atom(name: '_SettingsStoreBase.showThumbnailUptime', context: context);
@override
bool get showThumbnailUptime {
@ -114,7 +117,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$showThumbnailsAtom = Atom(name: '_SettingsStoreBase.showThumbnails');
late final _$showThumbnailsAtom =
Atom(name: '_SettingsStoreBase.showThumbnails', context: context);
@override
bool get showThumbnails {
@ -129,7 +133,24 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$showVideoAtom = Atom(name: '_SettingsStoreBase.showVideo');
late final _$launchUrlExternalAtom =
Atom(name: '_SettingsStoreBase.launchUrlExternal', context: context);
@override
bool get launchUrlExternal {
_$launchUrlExternalAtom.reportRead();
return super.launchUrlExternal;
}
@override
set launchUrlExternal(bool value) {
_$launchUrlExternalAtom.reportWrite(value, super.launchUrlExternal, () {
super.launchUrlExternal = value;
});
}
late final _$showVideoAtom =
Atom(name: '_SettingsStoreBase.showVideo', context: context);
@override
bool get showVideo {
@ -144,7 +165,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$showOverlayAtom = Atom(name: '_SettingsStoreBase.showOverlay');
late final _$showOverlayAtom =
Atom(name: '_SettingsStoreBase.showOverlay', context: context);
@override
bool get showOverlay {
@ -159,8 +181,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$toggleableOverlayAtom =
Atom(name: '_SettingsStoreBase.toggleableOverlay');
late final _$toggleableOverlayAtom =
Atom(name: '_SettingsStoreBase.toggleableOverlay', context: context);
@override
bool get toggleableOverlay {
@ -175,8 +197,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$pictureInPictureAtom =
Atom(name: '_SettingsStoreBase.pictureInPicture');
late final _$pictureInPictureAtom =
Atom(name: '_SettingsStoreBase.pictureInPicture', context: context);
@override
bool get pictureInPicture {
@ -191,7 +213,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$showBottomBarAtom = Atom(name: '_SettingsStoreBase.showBottomBar');
late final _$showBottomBarAtom =
Atom(name: '_SettingsStoreBase.showBottomBar', context: context);
@override
bool get showBottomBar {
@ -206,8 +229,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$showDeletedMessagesAtom =
Atom(name: '_SettingsStoreBase.showDeletedMessages');
late final _$showDeletedMessagesAtom =
Atom(name: '_SettingsStoreBase.showDeletedMessages', context: context);
@override
bool get showDeletedMessages {
@ -222,7 +245,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$showZeroWidthAtom = Atom(name: '_SettingsStoreBase.showZeroWidth');
late final _$showZeroWidthAtom =
Atom(name: '_SettingsStoreBase.showZeroWidth', context: context);
@override
bool get showZeroWidth {
@ -237,8 +261,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$showChatMessageDividersAtom =
Atom(name: '_SettingsStoreBase.showChatMessageDividers');
late final _$showChatMessageDividersAtom = Atom(
name: '_SettingsStoreBase.showChatMessageDividers', context: context);
@override
bool get showChatMessageDividers {
@ -254,7 +278,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$timestampTypeAtom = Atom(name: '_SettingsStoreBase.timestampType');
late final _$timestampTypeAtom =
Atom(name: '_SettingsStoreBase.timestampType', context: context);
@override
TimestampType get timestampType {
@ -269,8 +294,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$useReadableColorsAtom =
Atom(name: '_SettingsStoreBase.useReadableColors');
late final _$useReadableColorsAtom =
Atom(name: '_SettingsStoreBase.useReadableColors', context: context);
@override
bool get useReadableColors {
@ -285,7 +310,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$fontSizeAtom = Atom(name: '_SettingsStoreBase.fontSize');
late final _$fontSizeAtom =
Atom(name: '_SettingsStoreBase.fontSize', context: context);
@override
double get fontSize {
@ -300,7 +326,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$messageSpacingAtom = Atom(name: '_SettingsStoreBase.messageSpacing');
late final _$messageSpacingAtom =
Atom(name: '_SettingsStoreBase.messageSpacing', context: context);
@override
double get messageSpacing {
@ -315,7 +342,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$messageScaleAtom = Atom(name: '_SettingsStoreBase.messageScale');
late final _$messageScaleAtom =
Atom(name: '_SettingsStoreBase.messageScale', context: context);
@override
double get messageScale {
@ -330,7 +358,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$badgeScaleAtom = Atom(name: '_SettingsStoreBase.badgeScale');
late final _$badgeScaleAtom =
Atom(name: '_SettingsStoreBase.badgeScale', context: context);
@override
double get badgeScale {
@ -345,7 +374,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$emoteScaleAtom = Atom(name: '_SettingsStoreBase.emoteScale');
late final _$emoteScaleAtom =
Atom(name: '_SettingsStoreBase.emoteScale', context: context);
@override
double get emoteScale {
@ -360,8 +390,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$emoteAutocompleteAtom =
Atom(name: '_SettingsStoreBase.emoteAutocomplete');
late final _$emoteAutocompleteAtom =
Atom(name: '_SettingsStoreBase.emoteAutocomplete', context: context);
@override
bool get emoteAutocomplete {
@ -376,7 +406,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$sendCrashLogsAtom = Atom(name: '_SettingsStoreBase.sendCrashLogs');
late final _$sendCrashLogsAtom =
Atom(name: '_SettingsStoreBase.sendCrashLogs', context: context);
@override
bool get sendCrashLogs {
@ -391,7 +422,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$fullScreenAtom = Atom(name: '_SettingsStoreBase.fullScreen');
late final _$fullScreenAtom =
Atom(name: '_SettingsStoreBase.fullScreen', context: context);
@override
bool get fullScreen {
@ -406,7 +438,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$expandInfoAtom = Atom(name: '_SettingsStoreBase.expandInfo');
late final _$expandInfoAtom =
Atom(name: '_SettingsStoreBase.expandInfo', context: context);
@override
bool get expandInfo {
@ -421,8 +454,8 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
final _$_SettingsStoreBaseActionController =
ActionController(name: '_SettingsStoreBase');
late final _$_SettingsStoreBaseActionController =
ActionController(name: '_SettingsStoreBase', context: context);
@override
void reset() {
@ -441,6 +474,7 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
themeType: ${themeType},
showThumbnailUptime: ${showThumbnailUptime},
showThumbnails: ${showThumbnails},
launchUrlExternal: ${launchUrlExternal},
showVideo: ${showVideo},
showOverlay: ${showOverlay},
toggleableOverlay: ${toggleableOverlay},

View file

@ -31,8 +31,8 @@ class BlockButton extends StatelessWidget {
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text('Cancel'),
style: TextButton.styleFrom(primary: Colors.red),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () {

View file

@ -20,9 +20,9 @@ class ScrollToTopButton extends StatelessWidget {
duration: const Duration(milliseconds: 500),
);
},
child: const Icon(Icons.keyboard_arrow_up),
mini: true,
heroTag: null,
child: const Icon(Icons.keyboard_arrow_up),
),
);
}

View file

@ -7,14 +7,14 @@ packages:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "38.0.0"
version: "40.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "3.4.1"
version: "4.1.0"
archive:
dependency: transitive
description:
@ -28,7 +28,7 @@ packages:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
version: "2.3.1"
async:
dependency: transitive
description:
@ -49,7 +49,7 @@ packages:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
version: "2.3.0"
build_config:
dependency: transitive
description:
@ -63,21 +63,21 @@ packages:
name: build_daemon
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
version: "3.1.0"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6"
version: "2.0.8"
build_runner:
dependency: "direct dev"
description:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.8"
version: "2.1.11"
build_runner_core:
dependency: transitive
description:
@ -98,14 +98,14 @@ packages:
name: built_value
url: "https://pub.dartlang.org"
source: hosted
version: "8.1.4"
version: "8.3.0"
cached_network_image:
dependency: "direct main"
description:
name: cached_network_image
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.0"
version: "3.2.1"
cached_network_image_platform_interface:
dependency: transitive
description:
@ -156,12 +156,12 @@ packages:
source: hosted
version: "4.1.0"
collection:
dependency: transitive
dependency: "direct main"
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
version: "1.16.0"
convert:
dependency: transitive
description:
@ -175,7 +175,7 @@ packages:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
version: "3.0.2"
cupertino_icons:
dependency: "direct main"
description:
@ -189,21 +189,21 @@ packages:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.2"
version: "2.2.3"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.0"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
version: "1.2.1"
file:
dependency: transitive
description:
@ -217,7 +217,7 @@ packages:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
version: "1.0.1"
flutter:
dependency: "direct main"
description: flutter
@ -229,9 +229,9 @@ packages:
name: flutter_blurhash
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.4"
version: "0.7.0"
flutter_cache_manager:
dependency: transitive
dependency: "direct main"
description:
name: flutter_cache_manager
url: "https://pub.dartlang.org"
@ -250,21 +250,21 @@ packages:
name: flutter_lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "2.0.1"
flutter_mobx:
dependency: "direct main"
description:
name: flutter_mobx
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
version: "2.0.6+1"
flutter_native_splash:
dependency: "direct dev"
description:
name: flutter_native_splash
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3+1"
version: "2.2.0+1"
flutter_secure_storage:
dependency: "direct main"
description:
@ -344,7 +344,7 @@ packages:
name: frontend_server_client
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
version: "2.1.3"
glob:
dependency: transitive
description:
@ -379,7 +379,7 @@ packages:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
version: "4.0.1"
image:
dependency: transitive
description:
@ -407,28 +407,35 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
version: "0.6.4"
json_annotation:
dependency: "direct main"
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "4.4.0"
version: "4.5.0"
json_serializable:
dependency: "direct dev"
description:
name: json_serializable
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.5"
version: "6.2.0"
lint:
dependency: transitive
description:
name: lint
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.2"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "2.0.0"
logging:
dependency: transitive
description:
@ -449,7 +456,7 @@ packages:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
version: "0.1.4"
meta:
dependency: transitive
description:
@ -463,21 +470,21 @@ packages:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.0.2"
mobx:
dependency: "direct main"
description:
name: mobx
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6+1"
version: "2.0.7+2"
mobx_codegen:
dependency: "direct dev"
description:
name: mobx_codegen
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5+2"
version: "2.0.7"
nested:
dependency: transitive
description:
@ -491,7 +498,7 @@ packages:
name: octo_image
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.0.2"
package_config:
dependency: transitive
description:
@ -547,7 +554,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
version: "1.8.1"
path_drawing:
dependency: transitive
description:
@ -568,49 +575,49 @@ packages:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.9"
version: "2.0.10"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.12"
version: "2.0.14"
path_provider_ios:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
version: "2.0.9"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.5"
version: "2.1.6"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "2.0.6"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
version: "2.0.4"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "2.0.6"
pedantic:
dependency: transitive
description:
@ -624,7 +631,7 @@ packages:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "4.4.0"
version: "5.0.0"
platform:
dependency: transitive
description:
@ -659,7 +666,7 @@ packages:
name: provider
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.2"
version: "6.0.3"
pub_semver:
dependency: transitive
description:
@ -680,7 +687,7 @@ packages:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1+1"
version: "3.1.0"
rxdart:
dependency: transitive
description:
@ -694,49 +701,49 @@ packages:
name: sentry
url: "https://pub.dartlang.org"
source: hosted
version: "6.4.0"
version: "6.5.1"
sentry_flutter:
dependency: "direct main"
description:
name: sentry_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "6.4.0"
version: "6.5.1"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.13"
version: "2.0.15"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.11"
version: "2.0.12"
shared_preferences_ios:
dependency: transitive
description:
name: shared_preferences_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
version: "2.0.4"
shared_preferences_platform_interface:
dependency: transitive
description:
@ -750,14 +757,14 @@ packages:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
version: "2.0.4"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
shelf:
dependency: transitive
description:
@ -783,35 +790,35 @@ packages:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
version: "1.2.2"
source_helper:
dependency: transitive
description:
name: source_helper
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
version: "1.3.2"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.2"
sqflite:
dependency: transitive
description:
name: sqflite
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
version: "2.0.2+1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
version: "2.2.1+1"
stack_trace:
dependency: transitive
description:
@ -860,7 +867,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.8"
version: "0.4.9"
timing:
dependency: transitive
description:
@ -881,7 +888,7 @@ packages:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.3.1"
universal_io:
dependency: transitive
description:
@ -895,35 +902,35 @@ packages:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.20"
version: "6.1.2"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.15"
version: "6.0.17"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.15"
version: "6.0.17"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.0.1"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.0.1"
url_launcher_platform_interface:
dependency: transitive
description:
@ -937,14 +944,14 @@ packages:
name: url_launcher_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.9"
version: "2.0.11"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.0.1"
uuid:
dependency: transitive
description:
@ -958,7 +965,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "2.1.2"
watcher:
dependency: transitive
description:
@ -972,42 +979,42 @@ packages:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.2.0"
webview_flutter:
dependency: "direct main"
description:
name: webview_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
version: "3.0.4"
webview_flutter_android:
dependency: transitive
description:
name: webview_flutter_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.5"
version: "2.8.8"
webview_flutter_platform_interface:
dependency: transitive
description:
name: webview_flutter_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.9.0"
webview_flutter_wkwebview:
dependency: transitive
description:
name: webview_flutter_wkwebview
url: "https://pub.dartlang.org"
source: hosted
version: "2.7.2"
version: "2.7.5"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.1"
version: "2.6.1"
xdg_directories:
dependency: transitive
description:
@ -1021,14 +1028,14 @@ packages:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "5.3.1"
version: "5.4.1"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
version: "3.1.1"
sdks:
dart: ">=2.16.0 <3.0.0"
flutter: ">=2.10.0"
dart: ">=2.17.0 <3.0.0"
flutter: ">=3.0.0"

View file

@ -29,7 +29,7 @@ environment:
dependencies:
flutter:
sdk: flutter
json_annotation: ^4.4.0
json_annotation: ^4.5.0
# The following adds the Cupertino Icons font to your application.
@ -52,6 +52,8 @@ dependencies:
url_launcher: ^6.0.17
font_awesome_flutter: ^10.0.0
package_info_plus: ^1.3.0
collection: ^1.16.0
flutter_cache_manager: ^3.3.0
dev_dependencies:
flutter_test:
@ -62,10 +64,10 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^1.0.0
flutter_lints: ^2.0.1
build_runner: ^2.1.1
json_serializable: ^6.0.0
mobx_codegen: ^2.0.0
mobx_codegen: ^2.0.6
flutter_native_splash: ^2.0.5
flutter_launcher_icons: "^0.9.2"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View file

@ -1,112 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="frosty">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>frosty</title>
<link rel="manifest" href="manifest.json">
<link rel="stylesheet" type="text/css" href="splash/style.css">
<script src="splash/splash.js"></script>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
</head>
<body>
<picture id="splash">
<source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x" media="(prefers-color-scheme: light)">
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)">
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" />
</picture>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing || reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
</body>
</html>

View file

@ -1,35 +0,0 @@
{
"name": "frosty",
"short_name": "frosty",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

View file

@ -1,7 +0,0 @@
function removeSplashFromWeb() {
const elem = document.getElementById("splash");
if (elem) {
elem.remove();
}
document.body.style.background = "transparent";
}

View file

@ -1,43 +0,0 @@
body {
margin:0;
height:100%;
background: #000000;
background-image: url("img/light-background.png");
background-size: 100% 100%;
}
.center {
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.contain {
display:block;
width:100%; height:100%;
object-fit: contain;
}
.stretch {
display:block;
width:100%; height:100%;
}
.cover {
display:block;
width:100%; height:100%;
object-fit: cover;
}
@media (prefers-color-scheme: dark) {
body {
margin:0;
height:100%;
background: #000000;
background-image: url("img/dark-background.png");
background-size: 100% 100%;
}
}