Add the ability to reconnect to chat with notification (#180)
* Add ability to refresh chat & new settings menu * Improve chat notifications
This commit is contained in:
parent
51a4ae8235
commit
a5560ee9cc
4 changed files with 89 additions and 71 deletions
|
|
@ -73,18 +73,19 @@ class Chat extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
Button(
|
||||
onPressed: () async {
|
||||
// Paste clipboard text into the text controller.
|
||||
final data = await Clipboard.getData(Clipboard.kTextPlain);
|
||||
if (chatStore.notification!.contains('copied'))
|
||||
Button(
|
||||
onPressed: () async {
|
||||
// Paste clipboard text into the text controller.
|
||||
final data = await Clipboard.getData(Clipboard.kTextPlain);
|
||||
|
||||
if (data != null) chatStore.textController.text = data.text!;
|
||||
if (data != null) chatStore.textController.text = data.text!;
|
||||
|
||||
chatStore.updateNotification('');
|
||||
},
|
||||
fill: false,
|
||||
child: const Text('Paste'),
|
||||
),
|
||||
chatStore.updateNotification('');
|
||||
},
|
||||
fill: false,
|
||||
child: const Text('Paste'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
|||
import 'package:frosty/screens/channel/chat/emote_menu/emote_menu_panel.dart';
|
||||
import 'package:frosty/screens/channel/chat/emote_menu/recent_emotes_panel.dart';
|
||||
import 'package:frosty/screens/channel/stores/chat_store.dart';
|
||||
import 'package:frosty/widgets/alert_message.dart';
|
||||
import 'package:frosty/widgets/button.dart';
|
||||
|
||||
class EmoteMenu extends StatefulWidget {
|
||||
|
|
@ -68,28 +67,6 @@ class _EmoteMenuState extends State<EmoteMenu> {
|
|||
),
|
||||
),
|
||||
),
|
||||
Button(
|
||||
onPressed: () async {
|
||||
await widget.chatStore.getAssets();
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: AlertMessage(message: 'Emotes refreshed'),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
},
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
child: const Text(
|
||||
'REFRESH EMOTES',
|
||||
style: TextStyle(
|
||||
letterSpacing: 0.8,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ abstract class ChatStoreBase with Store {
|
|||
/// The Twitch IRC WebSocket channel.
|
||||
WebSocketChannel? _channel;
|
||||
|
||||
/// The subscription that handles the WebSocket connection.
|
||||
StreamSubscription? _channelListener;
|
||||
|
||||
// The retry counter for exponential backoff.
|
||||
var _retries = 0;
|
||||
|
||||
|
|
@ -322,7 +325,7 @@ abstract class ChatStoreBase with Store {
|
|||
_channel = WebSocketChannel.connect(Uri.parse('wss://irc-ws.chat.twitch.tv:443'));
|
||||
|
||||
// Listen for new messages and forward them to the handler.
|
||||
_channel?.stream.listen(
|
||||
_channelListener = _channel?.stream.listen(
|
||||
(data) => Future.delayed(Duration(seconds: settings.chatDelay.toInt()), () => _handleIRCData(data.toString())),
|
||||
onError: (error) => debugPrint('Chat error: ${error.toString()}'),
|
||||
onDone: () async {
|
||||
|
|
@ -342,6 +345,7 @@ abstract class ChatStoreBase with Store {
|
|||
// Increment the retry count and attempt the reconnect.
|
||||
_retries++;
|
||||
_messageBuffer.add(IRCMessage.createNotice(message: 'Reconnecting to chat (attempt $_retries)...'));
|
||||
_channelListener?.cancel();
|
||||
connectToChat();
|
||||
},
|
||||
);
|
||||
|
|
@ -453,6 +457,7 @@ abstract class ChatStoreBase with Store {
|
|||
|
||||
_channel?.sink.close(1001);
|
||||
_channel = null;
|
||||
_channelListener?.cancel();
|
||||
|
||||
for (final reactionDisposer in reactions) {
|
||||
reactionDisposer();
|
||||
|
|
|
|||
|
|
@ -77,6 +77,67 @@ class _VideoChatState extends State<VideoChat> {
|
|||
Widget build(BuildContext context) {
|
||||
final settingsStore = _chatStore.settings;
|
||||
|
||||
void showSettings() {
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Colors.transparent,
|
||||
context: context,
|
||||
builder: (context) => FrostyModal(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.app_settings_alt),
|
||||
title: const Text('App settings'),
|
||||
onTap: () => showModalBottomSheet(
|
||||
backgroundColor: Colors.transparent,
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (context) => FrostyModal(
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.8,
|
||||
child: Settings(settingsStore: settingsStore),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.refresh),
|
||||
title: const Text('Reconnect to chat'),
|
||||
onTap: () {
|
||||
_chatStore.updateNotification('Reconnecting to chat...');
|
||||
|
||||
_chatStore.connectToChat();
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.refresh),
|
||||
title: const Text('Refresh badges and emotes'),
|
||||
onTap: () async {
|
||||
await _chatStore.getAssets();
|
||||
|
||||
_chatStore.updateNotification('Badges and emotes refreshed');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final appBar = AppBar(
|
||||
title: Text(
|
||||
regexEnglish.hasMatch(_chatStore.displayName) ? _chatStore.displayName : '${_chatStore.displayName} (${_chatStore.channelName})',
|
||||
style: const TextStyle(fontSize: 20),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: 'Settings',
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: showSettings,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
final player = GestureDetector(
|
||||
onLongPress: _videoStore.handleToggleOverlay,
|
||||
child: _Video(
|
||||
|
|
@ -85,7 +146,10 @@ class _VideoChatState extends State<VideoChat> {
|
|||
),
|
||||
);
|
||||
|
||||
final videoOverlay = _VideoOverlay(videoStore: _videoStore);
|
||||
final videoOverlay = _VideoOverlay(
|
||||
videoStore: _videoStore,
|
||||
onSettingsPressed: showSettings,
|
||||
);
|
||||
|
||||
final overlay = GestureDetector(
|
||||
onLongPress: _videoStore.handleToggleOverlay,
|
||||
|
|
@ -138,30 +202,6 @@ class _VideoChatState extends State<VideoChat> {
|
|||
chatStore: _chatStore,
|
||||
);
|
||||
|
||||
final appBar = AppBar(
|
||||
title: Text(
|
||||
regexEnglish.hasMatch(_chatStore.displayName) ? _chatStore.displayName : '${_chatStore.displayName} (${_chatStore.channelName})',
|
||||
style: const TextStyle(fontSize: 20),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: 'Settings',
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () => showModalBottomSheet(
|
||||
backgroundColor: Colors.transparent,
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (context) => FrostyModal(
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.8,
|
||||
child: Settings(settingsStore: settingsStore),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
final videoChat = Scaffold(
|
||||
body: OrientationBuilder(
|
||||
builder: (context, orientation) {
|
||||
|
|
@ -306,8 +346,13 @@ class _Video extends StatelessWidget {
|
|||
/// Creates a widget containing controls which enable interactions with an underlying [_Video] widget.
|
||||
class _VideoOverlay extends StatelessWidget {
|
||||
final VideoStore videoStore;
|
||||
final void Function() onSettingsPressed;
|
||||
|
||||
const _VideoOverlay({Key? key, required this.videoStore}) : super(key: key);
|
||||
const _VideoOverlay({
|
||||
Key? key,
|
||||
required this.videoStore,
|
||||
required this.onSettingsPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
Future<void> _showSleepTimerDialog(BuildContext context) {
|
||||
return showDialog(
|
||||
|
|
@ -401,17 +446,7 @@ class _VideoOverlay extends StatelessWidget {
|
|||
Icons.settings,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () => showModalBottomSheet(
|
||||
backgroundColor: Colors.transparent,
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (context) => FrostyModal(
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.8,
|
||||
child: Settings(settingsStore: videoStore.settingsStore),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: onSettingsPressed,
|
||||
);
|
||||
|
||||
final chatOverlayButton = Observer(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue