handleDiff function
dynamic
handleDiff( - dynamic state,
- dynamic animatedList,
- dynamic diff
)
Implementation
@visibleForTesting
ChatRoomState handleDiff(
ChatRoomState state, // the current state
AnimatedListState? animatedList, // the animated list connected to this state
RoomMessageDiff diff, // the diff to apply
) {
final action = diff.action();
switch (action) {
case 'Append':
List<RoomMessage> incoming =
diff.values().expect('append diff must contain values').toList();
final messageList = state.messageList.toList();
final startLen = messageList.length;
final messages = Map.fromEntries(state.messages.entries);
for (final m in incoming) {
final uniqueId = m.uniqueId();
messages[uniqueId] = m;
messageList.add(uniqueId);
}
final endLen = messageList.length;
animatedList?.insertAllItems(startLen, endLen - startLen);
return state.copyWith(
messageList: messageList,
messages: messages,
);
case 'Set': // used to update UnableToDecrypt message
RoomMessage m = diff.value().expect('set diff must contain value');
final index = diff.index().expect('set diff must contain index');
final uniqueId = m.uniqueId();
if (state.messageList.isEmpty) {
animatedList?.insertItem(0);
return state.copyWith(
messageList: [uniqueId],
messages: {uniqueId: m},
);
}
final messageList = state.messageList.toList();
final removedItem = messageList.removeAt(index);
messageList.insert(index, uniqueId);
final messages = Map.fromEntries(
state.messages.entries.where((entry) => entry.key != removedItem),
);
messages[uniqueId] = m;
return state.copyWith(
messageList: messageList,
messages: messages,
);
case 'Insert':
RoomMessage m = diff.value().expect('insert diff must contain value');
final index = diff.index().expect('insert diff must contain index');
return state.copyWithNewMessageAt(index, m, animatedList);
case 'Remove':
int index = diff.index().expect('remove diff must contain index');
return state.copyWithRemovedMessageAt(index, animatedList);
case 'PushBack':
RoomMessage m = diff.value().expect('push back diff must contain value');
if (state.messageList.isEmpty) {
final uniqueId = m.uniqueId();
animatedList?.insertItem(0);
return state.copyWith(messageList: [uniqueId], messages: {uniqueId: m});
}
return state.copyWithNewMessageAt(
state.messageList.length, m, animatedList,);
case 'PushFront':
RoomMessage m = diff.value().expect('push front diff must contain value');
return state.copyWithNewMessageAt(0, m, animatedList);
case 'PopBack':
if (state.messageList.isEmpty) {
return state;
}
return state.copyWithRemovedMessageAt(
state.messageList.length - 1,
animatedList,
);
case 'PopFront':
return state.copyWithRemovedMessageAt(0, animatedList);
case 'Clear':
if (state.messageList.isNotEmpty && animatedList != null) {
animatedList.removeAllItems((b, a) => const SizedBox.shrink());
}
return state.copyWith(messageList: [], messages: {});
case 'Reset':
List<RoomMessage> incoming =
diff.values().expect('reset diff must contain values').toList();
final (messageList, messages) = incoming
.fold((List<String>.empty(growable: true), <String, RoomMessage>{}),
(val, m) {
final (list, map) = val;
final uniqueId = m.uniqueId();
list.add(uniqueId);
map[uniqueId] = m;
return (list, map);
});
if (animatedList != null) {
animatedList.removeAllItems((b, a) => const SizedBox.shrink());
animatedList.insertAllItems(0, messageList.length);
}
return state.copyWith(
messageList: messageList,
messages: messages,
);
case 'Truncate':
if (state.messageList.isEmpty) {
return state;
}
final index = diff.index().expect('truncate diff must contain index');
final (before, after) =
state.messageList.fold((<String>[], <String>[]), (f, e) {
final (before, after) = f;
if (before.length >= index) {
after.add(e);
} else {
before.add(e);
}
return (before, after);
});
if (after.isEmpty) {
animatedList?.removeAllItems((a, b) => const SizedBox.shrink());
return state.copyWith(
messageList: before,
messages: Map.fromEntries(
state.messages.entries,
),
);
} else {
if (animatedList != null) {
for (var x = state.messageList.length; x >= after.length; x--) {
// remove from the bottom up
animatedList.removeItem(x - 1, (a, b) => const SizedBox.shrink());
}
}
// we have to remove some
final messages = Map.fromEntries(
state.messages.entries.where((entry) => !after.contains(entry.key)),
);
return state.copyWith(messageList: before, messages: messages);
}
default:
_log.severe('Unsupported action $action when diffing room messages');
break;
}
return state;
}