Fix message buffer behavior (#154)
This commit is contained in:
parent
717d989954
commit
47894f4746
3 changed files with 63 additions and 53 deletions
|
|
@ -36,12 +36,17 @@ class IRCMessage {
|
|||
});
|
||||
|
||||
/// Returns a list of messages where the given CLEARCHAT message is applied.
|
||||
static List<IRCMessage> clearChat({required List<IRCMessage> messages, required IRCMessage ircMessage}) {
|
||||
static void clearChat({
|
||||
required List<IRCMessage> messages,
|
||||
required List<IRCMessage> bufferedMessages,
|
||||
required IRCMessage ircMessage,
|
||||
}) {
|
||||
// If there is no message, it means that entire chat was cleared.
|
||||
if (ircMessage.message == null) {
|
||||
messages.clear();
|
||||
bufferedMessages.clear();
|
||||
messages.add(IRCMessage.createNotice(message: 'Chat was cleared by a moderator'));
|
||||
return messages;
|
||||
return;
|
||||
}
|
||||
|
||||
final bannedUser = ircMessage.message;
|
||||
|
|
@ -60,11 +65,26 @@ class IRCMessage {
|
|||
}
|
||||
});
|
||||
|
||||
return messages;
|
||||
// Search the buffered messages for the banned/timed-out user.
|
||||
bufferedMessages.asMap().forEach((i, bufferedMessage) {
|
||||
if (bufferedMessage.user == bannedUser) {
|
||||
// Mark the message for removal.
|
||||
bufferedMessages[i].command = Command.clearChat;
|
||||
|
||||
// If timed-out, indicate the duration.
|
||||
if (banDuration != null) {
|
||||
bufferedMessages[i].tags['ban-duration'] = banDuration;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a list of messages where the gievn CLEARMSG message is applied.
|
||||
static List<IRCMessage> clearMessage({required List<IRCMessage> messages, required IRCMessage ircMessage}) {
|
||||
static void clearMessage({
|
||||
required List<IRCMessage> messages,
|
||||
required List<IRCMessage> bufferedMessages,
|
||||
required IRCMessage ircMessage,
|
||||
}) {
|
||||
final targetId = ircMessage.tags['target-msg-id'];
|
||||
|
||||
// Search for the message associated with the ID and mark the message for deletion.
|
||||
|
|
@ -75,7 +95,13 @@ class IRCMessage {
|
|||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
// Search for the buffered message associated with the ID and mark the message for deletion.
|
||||
for (var i = 0; i < bufferedMessages.length; i++) {
|
||||
if (bufferedMessages[i].tags['id'] == targetId) {
|
||||
bufferedMessages[i].command = Command.clearMessage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an [InlineSpan] list that corresponds to the badges, username, words, and emotes of the given [IRCMessage].
|
||||
|
|
|
|||
|
|
@ -18,9 +18,8 @@ part 'chat_store.g.dart';
|
|||
class ChatStore = ChatStoreBase with _$ChatStore;
|
||||
|
||||
abstract class ChatStoreBase with Store {
|
||||
static const _messageUpperLimit = 15000;
|
||||
static const _messageLimit = 10000;
|
||||
static const _renderMessageLimit = 100;
|
||||
static const _messageLimit = 5000;
|
||||
static const _renderMessageLimit = 88;
|
||||
|
||||
/// The provided auth store to determine login status, get the token, and use the headers for requests.
|
||||
final AuthStore auth;
|
||||
|
|
@ -69,8 +68,7 @@ abstract class ChatStoreBase with Store {
|
|||
final _messageBuffer = <IRCMessage>[];
|
||||
|
||||
/// The list of chat messages to render and display.
|
||||
@readonly
|
||||
var _messages = ObservableList<IRCMessage>();
|
||||
final messages = ObservableList<IRCMessage>();
|
||||
|
||||
@computed
|
||||
List<IRCMessage> get renderMessages {
|
||||
|
|
@ -80,10 +78,10 @@ abstract class ChatStoreBase with Store {
|
|||
// Else, when autoscroll is enabled, only show the first [_renderMessageLimit] messages.
|
||||
// This will improve performance by only rendering a limited amount of messages
|
||||
// instead of the entire history at all times.
|
||||
if (!_autoScroll || _messages.length < _renderMessageLimit) {
|
||||
return _messages;
|
||||
if (!_autoScroll || messages.length < _renderMessageLimit) {
|
||||
return messages;
|
||||
} else {
|
||||
return _messages.sublist(_messages.length - _renderMessageLimit);
|
||||
return messages.sublist(messages.length - _renderMessageLimit);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,7 +129,7 @@ abstract class ChatStoreBase with Store {
|
|||
(_) => _autoScroll,
|
||||
(_) {
|
||||
if (_messageBuffer.isNotEmpty) {
|
||||
_messages.addAll(_messageBuffer);
|
||||
messages.addAll(_messageBuffer);
|
||||
_messageBuffer.clear();
|
||||
}
|
||||
},
|
||||
|
|
@ -140,10 +138,10 @@ abstract class ChatStoreBase with Store {
|
|||
assetsStore.init();
|
||||
chatDetailsStore.updateChatters();
|
||||
|
||||
_messages.add(IRCMessage.createNotice(message: 'Connecting to chat...'));
|
||||
messages.add(IRCMessage.createNotice(message: 'Connecting to chat...'));
|
||||
|
||||
if (settings.chatDelay > 0) {
|
||||
_messages.add(IRCMessage.createNotice(
|
||||
messages.add(IRCMessage.createNotice(
|
||||
message: 'Waiting ${settings.chatDelay.toInt()} ${settings.chatDelay == 1.0 ? 'second' : 'seconds'} due to message delay setting...'));
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +191,7 @@ abstract class ChatStoreBase with Store {
|
|||
// The IRC data can contain more than one message separated by CRLF.
|
||||
// To account for this, split by CRLF, then loop and process each message.
|
||||
for (final message in data.trimRight().split('\r\n')) {
|
||||
// debugPrint(message);
|
||||
debugPrint('$message\n');
|
||||
if (message.startsWith('@')) {
|
||||
final parsedIRCMessage = IRCMessage.fromString(message, userLogin: auth.user.details?.login);
|
||||
|
||||
|
|
@ -207,17 +205,25 @@ abstract class ChatStoreBase with Store {
|
|||
case Command.notice:
|
||||
case Command.userNotice:
|
||||
if (_autoScroll) {
|
||||
_messages.add(parsedIRCMessage);
|
||||
messages.add(parsedIRCMessage);
|
||||
} else {
|
||||
// If audoscroll is disabled, buffer the message to prevent the chat from shifting.
|
||||
_messageBuffer.add(parsedIRCMessage);
|
||||
}
|
||||
break;
|
||||
case Command.clearChat:
|
||||
_messages = IRCMessage.clearChat(messages: _messages, ircMessage: parsedIRCMessage).asObservable();
|
||||
IRCMessage.clearChat(
|
||||
messages: messages,
|
||||
bufferedMessages: _messageBuffer,
|
||||
ircMessage: parsedIRCMessage,
|
||||
);
|
||||
break;
|
||||
case Command.clearMessage:
|
||||
_messages = IRCMessage.clearMessage(messages: _messages, ircMessage: parsedIRCMessage).asObservable();
|
||||
IRCMessage.clearMessage(
|
||||
messages: messages,
|
||||
bufferedMessages: _messageBuffer,
|
||||
ircMessage: parsedIRCMessage,
|
||||
);
|
||||
break;
|
||||
case Command.roomState:
|
||||
chatDetailsStore.roomState = chatDetailsStore.roomState.fromIRCMessage(parsedIRCMessage);
|
||||
|
|
@ -225,7 +231,12 @@ abstract class ChatStoreBase with Store {
|
|||
case Command.userState:
|
||||
_userState = _userState.fromIRCMessage(parsedIRCMessage);
|
||||
if (toSend != null) {
|
||||
_messages.add(toSend!);
|
||||
if (_autoScroll) {
|
||||
messages.add(toSend!);
|
||||
} else {
|
||||
// If audoscroll is disabled, buffer the sent message to prevent the chat from shifting.
|
||||
_messageBuffer.add(toSend!);
|
||||
}
|
||||
toSend = null;
|
||||
}
|
||||
break;
|
||||
|
|
@ -248,18 +259,12 @@ abstract class ChatStoreBase with Store {
|
|||
}
|
||||
|
||||
// If the message limit is reached, remove the oldest message.
|
||||
if (_messages.length >= _messageLimit) _messages.removeAt(0);
|
||||
|
||||
// Remove messages when an upper limit is reached.
|
||||
// This will prevent an infinite amount of messages building up when autoscroll is off.
|
||||
if (_messages.length >= _messageUpperLimit) _messages.removeRange(0, 500);
|
||||
|
||||
// Hard upper-limit of 5000 messages to prevent infinite messages being added when scrolling.
|
||||
if (_autoScroll && messages.length >= _messageLimit) messages.removeRange(0, 500);
|
||||
} else if (message == 'PING :tmi.twitch.tv') {
|
||||
_channel?.sink.add('PONG :tmi.twitch.tv');
|
||||
return;
|
||||
} else if (message.contains('Welcome, GLHF!')) {
|
||||
_messages.add(IRCMessage.createNotice(message: "Connected to $displayName${regexEnglish.hasMatch(displayName) ? '' : ' ($channelName)'}'s chat!"));
|
||||
messages.add(IRCMessage.createNotice(message: "Connected to $displayName${regexEnglish.hasMatch(displayName) ? '' : ' ($channelName)'}'s chat!"));
|
||||
|
||||
getAssets();
|
||||
|
||||
|
|
@ -309,7 +314,7 @@ abstract class ChatStoreBase with Store {
|
|||
if (_backoffTime > 0) {
|
||||
// Add notice that chat was disconnected and then wait the backoff time before reconnecting.
|
||||
final notice = 'Disconnected from chat, waiting $_backoffTime ${_backoffTime == 1 ? 'second' : 'seconds'} before reconnecting...';
|
||||
_messages.add(IRCMessage.createNotice(message: notice));
|
||||
messages.add(IRCMessage.createNotice(message: notice));
|
||||
}
|
||||
|
||||
await Future.delayed(Duration(seconds: _backoffTime));
|
||||
|
|
@ -319,7 +324,7 @@ abstract class ChatStoreBase with Store {
|
|||
|
||||
// Increment the retry count and attempt the reconnect.
|
||||
_retries++;
|
||||
_messages.add(IRCMessage.createNotice(message: 'Reconnecting to chat (attempt $_retries)...'));
|
||||
messages.add(IRCMessage.createNotice(message: 'Reconnecting to chat (attempt $_retries)...'));
|
||||
connectToChat();
|
||||
},
|
||||
);
|
||||
|
|
@ -353,7 +358,7 @@ abstract class ChatStoreBase with Store {
|
|||
if (message.isEmpty) return;
|
||||
|
||||
if (_channel == null || _channel?.closeCode != null) {
|
||||
_messages.add(IRCMessage.createNotice(message: 'Failed to send message: disconnected from chat.'));
|
||||
messages.add(IRCMessage.createNotice(message: 'Failed to send message: disconnected from chat.'));
|
||||
} else {
|
||||
// Send the message to the IRC chat room.
|
||||
_channel?.sink.add('PRIVMSG #$channelName :$message');
|
||||
|
|
@ -376,9 +381,6 @@ abstract class ChatStoreBase with Store {
|
|||
// Clear the previous input in the TextField.
|
||||
textController.clear();
|
||||
}
|
||||
|
||||
// Scroll to the latest message after sending.
|
||||
if (scrollController.hasClients) scrollController.jumpTo(0);
|
||||
}
|
||||
|
||||
/// Adds the given [emote] to the chat textfield.
|
||||
|
|
|
|||
|
|
@ -17,24 +17,6 @@ mixin _$ChatStore on ChatStoreBase, Store {
|
|||
name: 'ChatStoreBase.renderMessages'))
|
||||
.value;
|
||||
|
||||
late final _$_messagesAtom =
|
||||
Atom(name: 'ChatStoreBase._messages', context: context);
|
||||
|
||||
ObservableList<IRCMessage> get messages {
|
||||
_$_messagesAtom.reportRead();
|
||||
return super._messages;
|
||||
}
|
||||
|
||||
@override
|
||||
ObservableList<IRCMessage> get _messages => messages;
|
||||
|
||||
@override
|
||||
set _messages(ObservableList<IRCMessage> value) {
|
||||
_$_messagesAtom.reportWrite(value, super._messages, () {
|
||||
super._messages = value;
|
||||
});
|
||||
}
|
||||
|
||||
late final _$_autoScrollAtom =
|
||||
Atom(name: 'ChatStoreBase._autoScroll', context: context);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue