diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 33b8ddf4..06611429 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -85,7 +85,7 @@ SPEC CHECKSUMS: shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de - webview_flutter_wkwebview: 005fbd90c888a42c5690919a1527ecc6649e1162 + webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c diff --git a/lib/core/auth/auth_store.dart b/lib/core/auth/auth_store.dart index cf0db451..1e19be2e 100644 --- a/lib/core/auth/auth_store.dart +++ b/lib/core/auth/auth_store.dart @@ -64,18 +64,18 @@ abstract class _AuthBase with Store { await _storage.write(key: _defaultTokenKey, value: _token); } } else { + // Validate the existing token. + _tokenIsValid = await twitchApi.validateToken(token: _token!); + + // If the token is invalid, logout. + if (!_tokenIsValid) return await logout(); + // Initialize the user store. await user.init(headers: headersTwitch); if (user.details != null) _isLoggedIn = true; } - // Validate the token. - _tokenIsValid = await twitchApi.validateToken(token: _token!); - - // If the token is invalid, logout. - if (!_tokenIsValid) await logout(); - _error = null; } catch (e) { debugPrint(e.toString()); diff --git a/lib/main.dart b/lib/main.dart index 9e4935b9..34903a98 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -74,6 +74,9 @@ void main() async { ); } +// Navigator key for sleep timer. Allows navigation popping without context. +final navigatorKey = GlobalKey(); + class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @@ -164,6 +167,7 @@ class MyApp extends StatelessWidget { ? ThemeMode.light : ThemeMode.dark, home: const Home(), + navigatorKey: navigatorKey, ); }, ); diff --git a/lib/models/irc.dart b/lib/models/irc.dart index 0b3c786a..4c7ac2b5 100644 --- a/lib/models/irc.dart +++ b/lib/models/irc.dart @@ -581,10 +581,7 @@ class IRCMessage { return TextSpan( text: text, style: style?.copyWith(color: Colors.blue), - recognizer: TapGestureRecognizer() - ..onTap = () async { - if (await canLaunch(text)) launch(text); - }, + recognizer: TapGestureRecognizer()..onTap = () => launch(text), ); } else { return TextSpan(text: text, style: style); diff --git a/lib/screens/channel/chat/chat.dart b/lib/screens/channel/chat/chat.dart index 1c6293ad..afeb5f00 100644 --- a/lib/screens/channel/chat/chat.dart +++ b/lib/screens/channel/chat/chat.dart @@ -1,4 +1,5 @@ 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'; @@ -14,103 +15,109 @@ class Chat extends StatelessWidget { @override Widget build(BuildContext context) { return Observer( - builder: (context) => Column( - children: [ - Expanded( - child: GestureDetector( - onTap: () { - if (chatStore.assetsStore.showEmoteMenu) { - chatStore.assetsStore.showEmoteMenu = false; - } else if (chatStore.textFieldFocusNode.hasFocus) { - chatStore.textFieldFocusNode.unfocus(); - } - }, - child: Stack( - alignment: AlignmentDirectional.bottomCenter, - children: [ - MediaQuery( - data: MediaQuery.of(context).copyWith(textScaleFactor: chatStore.settings.messageScale), - child: DefaultTextStyle( - style: DefaultTextStyle.of(context).style.copyWith(fontSize: chatStore.settings.fontSize), - child: Observer( - builder: (context) { - final showDividers = chatStore.settings.showChatMessageDividers; + builder: (context) { + SchedulerBinding.instance?.addPostFrameCallback((_) { + if (chatStore.scrollController.hasClients) chatStore.scrollController.jumpTo(chatStore.scrollController.position.maxScrollExtent); + }); - return ListView.separated( - padding: const EdgeInsets.symmetric(vertical: 5.0), - addAutomaticKeepAlives: false, - addRepaintBoundaries: false, - itemCount: chatStore.messages.length, - controller: chatStore.scrollController, - separatorBuilder: (context, index) => showDividers - ? Divider( - height: chatStore.settings.messageSpacing, - thickness: 1.0, - ) - : SizedBox(height: chatStore.settings.messageSpacing), - itemBuilder: (context, index) => Observer( - builder: (context) { - final message = chatStore.messages[index]; - final chatMessage = ChatMessage( - ircMessage: message, - assetsStore: chatStore.assetsStore, - settingsStore: chatStore.settings, - ); + return Column( + children: [ + Expanded( + child: GestureDetector( + onTap: () { + if (chatStore.assetsStore.showEmoteMenu) { + chatStore.assetsStore.showEmoteMenu = false; + } else if (chatStore.textFieldFocusNode.hasFocus) { + chatStore.textFieldFocusNode.unfocus(); + } + }, + child: Stack( + alignment: AlignmentDirectional.bottomCenter, + children: [ + MediaQuery( + data: MediaQuery.of(context).copyWith(textScaleFactor: chatStore.settings.messageScale), + child: DefaultTextStyle( + style: DefaultTextStyle.of(context).style.copyWith(fontSize: chatStore.settings.fontSize), + child: Observer( + builder: (context) { + final showDividers = chatStore.settings.showChatMessageDividers; - if (message.user != null && message.user != chatStore.auth.user.details?.login) { - return InkWell( - onTap: () { - FocusScope.of(context).unfocus(); - if (chatStore.assetsStore.showEmoteMenu) chatStore.assetsStore.showEmoteMenu = false; - }, - onLongPress: () => showModalBottomSheet( - context: context, - builder: (context) => ChatUserModal( - chatStore: chatStore, - username: message.user!, - userId: message.tags['user-id']!, - displayName: message.tags['display-name']!, - ), - ), - child: chatMessage, + return ListView.separated( + padding: const EdgeInsets.symmetric(vertical: 5.0), + addAutomaticKeepAlives: false, + addRepaintBoundaries: false, + itemCount: chatStore.messages.length, + controller: chatStore.scrollController, + separatorBuilder: (context, index) => showDividers + ? Divider( + height: chatStore.settings.messageSpacing, + thickness: 1.0, + ) + : SizedBox(height: chatStore.settings.messageSpacing), + itemBuilder: (context, index) => Observer( + builder: (context) { + final message = chatStore.messages[index]; + final chatMessage = ChatMessage( + ircMessage: message, + assetsStore: chatStore.assetsStore, + settingsStore: chatStore.settings, ); - } - return chatMessage; - }, - ), - ); - }, + + if (message.user != null && message.user != chatStore.auth.user.details?.login) { + return InkWell( + onTap: () { + FocusScope.of(context).unfocus(); + if (chatStore.assetsStore.showEmoteMenu) chatStore.assetsStore.showEmoteMenu = false; + }, + onLongPress: () => showModalBottomSheet( + context: context, + builder: (context) => ChatUserModal( + chatStore: chatStore, + username: message.user!, + userId: message.tags['user-id']!, + displayName: message.tags['display-name']!, + ), + ), + child: chatMessage, + ); + } + return chatMessage; + }, + ), + ); + }, + ), ), ), - ), - Observer( - builder: (_) => AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: chatStore.autoScroll - ? null - : Container( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - width: double.infinity, - child: ElevatedButton.icon( - onPressed: chatStore.resumeScroll, - label: const Text('Resume Scroll'), - icon: const Icon(Icons.arrow_circle_down), + Observer( + builder: (_) => AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: chatStore.autoScroll + ? null + : Container( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + width: double.infinity, + child: ElevatedButton.icon( + onPressed: chatStore.resumeScroll, + label: const Text('Resume Scroll'), + icon: const Icon(Icons.arrow_circle_down), + ), ), - ), + ), ), - ), - ], + ], + ), ), ), - ), - if (chatStore.settings.showBottomBar) ChatBottomBar(chatStore: chatStore), - if (chatStore.assetsStore.showEmoteMenu) - SizedBox( - height: MediaQuery.of(context).size.height / 3, - child: EmoteMenu(chatStore: chatStore), - ), - ], - ), + if (chatStore.settings.showBottomBar) ChatBottomBar(chatStore: chatStore), + if (chatStore.assetsStore.showEmoteMenu) + SizedBox( + height: MediaQuery.of(context).size.height / 3, + child: EmoteMenu(chatStore: chatStore), + ), + ], + ); + }, ); } } diff --git a/lib/screens/channel/chat/details/chat_users_list.dart b/lib/screens/channel/chat/details/chat_users_list.dart index 90a303c4..7a9cefae 100644 --- a/lib/screens/channel/chat/details/chat_users_list.dart +++ b/lib/screens/channel/chat/details/chat_users_list.dart @@ -71,7 +71,7 @@ class _ChattersListState extends State { onChanged: (text) => chatDetailStore.filterText = text, decoration: InputDecoration( isDense: true, - hintText: 'Filter', + labelText: 'Filter', contentPadding: const EdgeInsets.all(10.0), suffixIcon: IconButton( tooltip: 'Clear Filter', diff --git a/lib/screens/channel/chat/widgets/chat_bottom_bar.dart b/lib/screens/channel/chat/widgets/chat_bottom_bar.dart index cc1e4cbd..746c12ab 100644 --- a/lib/screens/channel/chat/widgets/chat_bottom_bar.dart +++ b/lib/screens/channel/chat/widgets/chat_bottom_bar.dart @@ -95,7 +95,7 @@ class ChatBottomBar extends StatelessWidget { border: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(15.0)), ), - hintText: chatStore.auth.isLoggedIn ? 'Send a message' : 'Log in to chat', + labelText: chatStore.auth.isLoggedIn ? 'Send a message' : 'Log in to chat', ), controller: chatStore.textController, onSubmitted: chatStore.sendMessage, diff --git a/lib/screens/channel/stores/chat_store.dart b/lib/screens/channel/stores/chat_store.dart index 63a5712e..1190b050 100644 --- a/lib/screens/channel/stores/chat_store.dart +++ b/lib/screens/channel/stores/chat_store.dart @@ -90,24 +90,6 @@ abstract class _ChatStoreBase with Store { required this.channelId, required this.displayName, }) { - // Create a reaction where anytime the emote menu is shown or hidden, - // scroll to the bottom of the list. This will prevent the emote menu - // from covering the latest messages when summoned. - reactions.add(reaction( - (_) => assetsStore.showEmoteMenu, - (_) => SchedulerBinding.instance?.addPostFrameCallback((_) { - if (scrollController.hasClients) scrollController.jumpTo(scrollController.position.maxScrollExtent); - }), - )); - - // Create a reaction for scrolling to bottom when exiting fullscreen. - reactions.add(reaction( - (_) => settings.fullScreen, - (_) => SchedulerBinding.instance?.addPostFrameCallback((_) { - if (scrollController.hasClients) scrollController.jumpTo(scrollController.position.maxScrollExtent); - }), - )); - // Create a reaction that will reconnect to chat when logging in or out. // Closing the channel will trigger a reconnect with the new credentials. reactions.add(reaction( @@ -132,18 +114,9 @@ abstract class _ChatStoreBase with Store { } }); - // Add a listener to the textfield focus that will do the following when focused: - // 1. Hide the emote menu if it is currently shown - // 2. Scroll to the latest message (after a brief delay) + // Add a listener to the textfield focus that will hide the emote menu if it is currently shown. textFieldFocusNode.addListener(() { - if (textFieldFocusNode.hasFocus) { - if (assetsStore.showEmoteMenu) assetsStore.showEmoteMenu = false; - - Future.delayed( - const Duration(milliseconds: 500), - () => scrollController.jumpTo(scrollController.position.maxScrollExtent), - ); - } + if (textFieldFocusNode.hasFocus && assetsStore.showEmoteMenu) assetsStore.showEmoteMenu = false; }); // Add a listener to the textfield that will show/hide the autocomplete bar if focused. @@ -327,29 +300,33 @@ abstract class _ChatStoreBase with Store { if (_channel == null || _channel?.closeCode != null) { _messages.add(IRCMessage.createNotice(message: 'Failed to send message: disconnected from chat.')); - return; - } + } else { + // Send the message to the IRC chat room. + _channel?.sink.add('PRIVMSG #$channelName :$message'); - // Send the message to the IRC chat room. - _channel?.sink.add('PRIVMSG #$channelName :$message'); + // Obtain the logged-in user's appearance in chat with USERSTATE and create the full message to render. + var userStateString = _userState.raw; + if (userStateString != null) { + if (message.length > 3 && message.substring(0, 3) == '/me') { + userStateString += ' :\x01ACTION ${message.replaceRange(0, 3, '').trim()}\x01'; + } else { + userStateString += ' :' + message.trim(); + } - // Obtain the logged-in user's appearance in chat with USERSTATE and create the full message to render. - var userStateString = _userState.raw; - if (userStateString != null) { - if (message.length > 3 && message.substring(0, 3) == '/me') { - userStateString += ' :\x01ACTION ${message.replaceRange(0, 3, '').trim()}\x01'; - } else { - userStateString += ' :' + message.trim(); + final userChatMessage = IRCMessage.fromString(userStateString); + userChatMessage.localEmotes?.addAll(assetsStore.userEmoteToObject); + + toSend = userChatMessage; } - final userChatMessage = IRCMessage.fromString(userStateString); - userChatMessage.localEmotes?.addAll(assetsStore.userEmoteToObject); - - toSend = userChatMessage; + // Clear the previous input in the TextField. + textController.clear(); } - // Clear the previous input in the TextField. - textController.clear(); + // Scroll to the latest message after sending. + SchedulerBinding.instance?.addPostFrameCallback((_) { + if (scrollController.hasClients) scrollController.jumpTo(scrollController.position.maxScrollExtent); + }); } /// Adds the given [emote] to the chat textfield. diff --git a/lib/screens/channel/video/video_overlay.dart b/lib/screens/channel/video/video_overlay.dart index c145637e..2fe003be 100644 --- a/lib/screens/channel/video/video_overlay.dart +++ b/lib/screens/channel/video/video_overlay.dart @@ -4,21 +4,17 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:frosty/constants/constants.dart'; +import 'package:frosty/main.dart'; import 'package:frosty/screens/channel/stores/video_store.dart'; import 'package:frosty/screens/settings/settings.dart'; import 'package:intl/intl.dart'; -class VideoOverlay extends StatefulWidget { +class VideoOverlay extends StatelessWidget { final VideoStore videoStore; const VideoOverlay({Key? key, required this.videoStore}) : super(key: key); - @override - State createState() => _VideoOverlayState(); -} - -class _VideoOverlayState extends State { - Future _showSleepTimerDialog(BuildContext oldContext) { + Future _showSleepTimerDialog(BuildContext context) { return showDialog( context: context, builder: (context) => AlertDialog( @@ -30,9 +26,9 @@ class _VideoOverlayState extends State { Row( children: [ DropdownButton( - value: widget.videoStore.sleepHours, + value: videoStore.sleepHours, items: List.generate(24, (index) => index).map((e) => DropdownMenuItem(value: e, child: Text(e.toString()))).toList(), - onChanged: (int? hours) => widget.videoStore.sleepHours = hours!, + onChanged: (int? hours) => videoStore.sleepHours = hours!, menuMaxHeight: 200, ), const SizedBox(width: 10.0), @@ -42,24 +38,24 @@ class _VideoOverlayState extends State { Row( children: [ DropdownButton( - value: widget.videoStore.sleepMinutes, + value: videoStore.sleepMinutes, items: List.generate(60, (index) => index).map((e) => DropdownMenuItem(value: e, child: Text(e.toString()))).toList(), - onChanged: (int? minutes) => widget.videoStore.sleepMinutes = minutes!, + onChanged: (int? minutes) => videoStore.sleepMinutes = minutes!, menuMaxHeight: 200, ), const SizedBox(width: 10.0), const Text('Minutes'), ], ), - if (widget.videoStore.sleepTimer != null && widget.videoStore.sleepTimer!.isActive) + if (videoStore.sleepTimer != null && videoStore.sleepTimer!.isActive) Row( children: [ const Icon(Icons.timer), - Text(' ${widget.videoStore.timeRemaining.toString().split('.')[0]}'), + Text(' ${videoStore.timeRemaining.toString().split('.')[0]}'), const Spacer(), IconButton( tooltip: 'Cancel Timer', - onPressed: widget.videoStore.cancelSleepTimer, + onPressed: videoStore.cancelSleepTimer, icon: const Icon(Icons.cancel), ), ], @@ -75,13 +71,10 @@ class _VideoOverlayState extends State { ), Observer( builder: (context) => ElevatedButton( - onPressed: widget.videoStore.sleepHours == 0 && widget.videoStore.sleepMinutes == 0 + onPressed: videoStore.sleepHours == 0 && videoStore.sleepMinutes == 0 ? null - : () => widget.videoStore.updateSleepTimer( - onTimerFinished: () => Navigator.popUntil( - oldContext, - (route) => route.isFirst, - ), + : () => videoStore.updateSleepTimer( + onTimerFinished: () => navigatorKey.currentState?.popUntil((route) => route.isFirst), ), child: const Text('Set Timer'), ), @@ -118,7 +111,7 @@ class _VideoOverlayState extends State { context: context, builder: (context) => SizedBox( height: MediaQuery.of(context).size.height * 0.8, - child: Settings(settingsStore: widget.videoStore.settingsStore), + child: Settings(settingsStore: videoStore.settingsStore), ), ), ); @@ -129,12 +122,12 @@ class _VideoOverlayState extends State { Icons.refresh, color: Colors.white, ), - onPressed: widget.videoStore.handleRefresh, + onPressed: videoStore.handleRefresh, ); final fullScreenButton = IconButton( - tooltip: widget.videoStore.settingsStore.fullScreen ? 'Exit Fullscreen' : 'Enter Fullscreen', - icon: widget.videoStore.settingsStore.fullScreen + tooltip: videoStore.settingsStore.fullScreen ? 'Exit Fullscreen' : 'Enter Fullscreen', + icon: videoStore.settingsStore.fullScreen ? const Icon( Icons.fullscreen_exit, color: Colors.white, @@ -143,7 +136,7 @@ class _VideoOverlayState extends State { Icons.fullscreen, color: Colors.white, ), - onPressed: () => widget.videoStore.settingsStore.fullScreen = !widget.videoStore.settingsStore.fullScreen, + onPressed: () => videoStore.settingsStore.fullScreen = !videoStore.settingsStore.fullScreen, ); final sleepTimerButton = IconButton( @@ -155,7 +148,7 @@ class _VideoOverlayState extends State { onPressed: () => _showSleepTimerDialog(context), ); - final streamInfo = widget.videoStore.streamInfo; + final streamInfo = videoStore.streamInfo; if (streamInfo == null) { return Stack( @@ -208,9 +201,9 @@ class _VideoOverlayState extends State { if (Platform.isAndroid) Center( child: IconButton( - tooltip: widget.videoStore.paused ? 'Play' : 'Pause', + tooltip: videoStore.paused ? 'Play' : 'Pause', iconSize: 50.0, - icon: widget.videoStore.paused + icon: videoStore.paused ? const Icon( Icons.play_arrow, color: Colors.white, @@ -219,10 +212,10 @@ class _VideoOverlayState extends State { Icons.pause, color: Colors.white, ), - onPressed: widget.videoStore.handlePausePlay, + onPressed: videoStore.handlePausePlay, ), ) - else if (!widget.videoStore.paused) + else if (!videoStore.paused) Center( child: IconButton( tooltip: 'Pause', @@ -231,7 +224,7 @@ class _VideoOverlayState extends State { Icons.pause, color: Colors.white, ), - onPressed: widget.videoStore.handlePausePlay, + onPressed: videoStore.handlePausePlay, ), ), Align( @@ -241,10 +234,10 @@ class _VideoOverlayState extends State { children: [ Expanded( child: GestureDetector( - onTap: widget.videoStore.handleExpand, + onTap: videoStore.handleExpand, child: Padding( padding: const EdgeInsets.all(10.0), - child: widget.videoStore.settingsStore.expandInfo + child: videoStore.settingsStore.expandInfo ? Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.start, @@ -252,11 +245,11 @@ class _VideoOverlayState extends State { streamer, const SizedBox(height: 5.0), Tooltip( - message: widget.videoStore.streamInfo!.title.trim(), + message: videoStore.streamInfo!.title.trim(), preferBelow: false, padding: const EdgeInsets.all(10.0), child: Text( - widget.videoStore.streamInfo!.title.trim(), + videoStore.streamInfo!.title.trim(), maxLines: orientation == Orientation.portrait ? 1 : 5, overflow: TextOverflow.ellipsis, style: const TextStyle( @@ -267,7 +260,7 @@ class _VideoOverlayState extends State { ), const SizedBox(height: 5.0), Text( - '${widget.videoStore.streamInfo?.gameName} \u2022 ${NumberFormat().format(widget.videoStore.streamInfo?.viewerCount)} viewers', + '${videoStore.streamInfo?.gameName} \u2022 ${NumberFormat().format(videoStore.streamInfo?.viewerCount)} viewers', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w300, @@ -280,14 +273,14 @@ class _VideoOverlayState extends State { ), ), refreshButton, - if (Platform.isIOS && widget.videoStore.settingsStore.pictureInPicture) + if (Platform.isIOS && videoStore.settingsStore.pictureInPicture) IconButton( tooltip: 'Picture-in-Picture', icon: const Icon( Icons.picture_in_picture_alt_rounded, color: Colors.white, ), - onPressed: widget.videoStore.requestPictureInPicture, + onPressed: videoStore.requestPictureInPicture, ), if (orientation == Orientation.landscape) fullScreenButton ], @@ -297,10 +290,4 @@ class _VideoOverlayState extends State { ), ); } - - @override - void dispose() { - widget.videoStore.cancelSleepTimer(); - super.dispose(); - } } diff --git a/lib/screens/channel/video_chat.dart b/lib/screens/channel/video_chat.dart index 7c12646f..667fdccf 100644 --- a/lib/screens/channel/video_chat.dart +++ b/lib/screens/channel/video_chat.dart @@ -146,12 +146,12 @@ class _VideoChatState extends State { return Scaffold( body: OrientationBuilder( builder: (context, orientation) { - if (orientation == Orientation.landscape) { - // Add a post-frame callback to scroll to bottom when rotating. - SchedulerBinding.instance?.addPostFrameCallback((_) { - if (_chatStore.scrollController.hasClients) _chatStore.scrollController.jumpTo(_chatStore.scrollController.position.maxScrollExtent); - }); + // Scroll to bottom when summoning keyboard or rotating. + SchedulerBinding.instance?.addPostFrameCallback((_) { + if (_chatStore.scrollController.hasClients) _chatStore.scrollController.jumpTo(_chatStore.scrollController.position.maxScrollExtent); + }); + if (orientation == Orientation.landscape) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); return Observer( @@ -228,6 +228,8 @@ class _VideoChatState extends State { void dispose() { _chatStore.dispose(); + _videoStore.cancelSleepTimer(); + SystemChrome.setEnabledSystemUIMode( SystemUiMode.manual, overlays: SystemUiOverlay.values, diff --git a/lib/screens/home/search/search.dart b/lib/screens/home/search/search.dart index c88a9098..69729e24 100644 --- a/lib/screens/home/search/search.dart +++ b/lib/screens/home/search/search.dart @@ -37,7 +37,7 @@ class _SearchState extends State { autocorrect: false, decoration: InputDecoration( isDense: true, - hintText: 'Find a channel or category', + labelText: 'Find a channel or category', contentPadding: const EdgeInsets.all(10.0), border: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(15.0)), diff --git a/lib/screens/settings/sections/account_settings.dart b/lib/screens/settings/sections/account_settings.dart index a96ef206..1a11442f 100644 --- a/lib/screens/settings/sections/account_settings.dart +++ b/lib/screens/settings/sections/account_settings.dart @@ -124,7 +124,7 @@ class ProfileCard extends StatelessWidget { child: TextField( textAlign: TextAlign.center, decoration: const InputDecoration( - hintText: 'Token', + labelText: 'Token', ), onSubmitted: (token) { authStore.login(customToken: token); diff --git a/pubspec.lock b/pubspec.lock index d3184e5d..c41c83c1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -505,14 +505,14 @@ packages: name: package_info_plus url: "https://pub.dartlang.org" source: hosted - version: "1.4.0" + version: "1.4.2" package_info_plus_linux: dependency: transitive description: name: package_info_plus_linux url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.0.5" package_info_plus_macos: dependency: transitive description: @@ -533,14 +533,14 @@ packages: name: package_info_plus_web url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "1.0.5" package_info_plus_windows: dependency: transitive description: name: package_info_plus_windows url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "1.0.5" path: dependency: transitive description: @@ -979,14 +979,14 @@ packages: name: webview_flutter url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.0.2" webview_flutter_android: dependency: transitive description: name: webview_flutter_android url: "https://pub.dartlang.org" source: hosted - version: "2.8.4" + version: "2.8.5" webview_flutter_platform_interface: dependency: transitive description: @@ -1000,7 +1000,7 @@ packages: name: webview_flutter_wkwebview url: "https://pub.dartlang.org" source: hosted - version: "2.7.1" + version: "2.7.2" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9f7f6299..ec753df3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.2.1+19 +version: 1.2.2+20 environment: sdk: ">=2.15.0 <3.0.0"