diff --git a/lib/controllers/room_chat.dart b/lib/controllers/room_chat.dart index 7e1aeb2..4043922 100644 --- a/lib/controllers/room_chat.dart +++ b/lib/controllers/room_chat.dart @@ -17,13 +17,13 @@ import "package:nexus/models/requests/send_message.dart"; import "package:nexus/models/room.dart"; import "package:nexus/models/room_chat.dart"; -class RoomChatController(final (String roomId, String? contextualEvent) info) +class RoomChatController(final (String roomId, String? withEventId) info) extends AsyncNotifier { @override Future build() async { final (roomId, eventId) = info; final client = ref.read(ClientController.provider.notifier); - final room = ref.read( + final room = ref.watch( RoomsController.provider.select((rooms) => rooms[roomId]), ); @@ -34,43 +34,31 @@ class RoomChatController(final (String roomId, String? contextualEvent) info) await ref.read(RoomsController.provider.notifier).addState(roomId, state); } - final timeline = room.timeline - .toEntryIList(compare: (a, b) => (a?.key ?? 0).compareTo(b?.key ?? 0)) - .map((element) => element.value) - .toIList() - .addAll(room.clientSticky) - .map((entry) { - final foundEvent = entry == null ? null : room.events[entry]; + return .new( + events: room.timeline + .toEntryIList(compare: (a, b) => (a?.key ?? 0).compareTo(b?.key ?? 0)) + .map((element) => element.value) + .toIList() + .addAll(room.clientSticky) + .map((entry) { + final foundEvent = entry == null ? null : room.events[entry]; - final editedEvent = - foundEvent == null || foundEvent.lastEditRowId == 0 - ? null - : room.events[foundEvent.lastEditRowId]; + final editedEvent = + foundEvent == null || foundEvent.lastEditRowId == 0 + ? null + : room.events[foundEvent.lastEditRowId]; - return editedEvent == null - ? foundEvent - : foundEvent?.copyWith( - content: editedEvent.content, - localContent: editedEvent.localContent, - ); - }) - .nonNulls - .toIList(); - - if (info.$2 == null || timeline.map((e) => e.eventId).contains(info.$2)) { - ref.watch(RoomsController.provider.select((rooms) => rooms[roomId])); - - return .new(timeline: timeline, hasMore: room.hasMore); - } else { - final context = await client.getEventContext( - .new(roomId: roomId, eventId: info.$2!, limit: 20), - ); - return .new( - timeline: context.before.add(context.event).addAll(context.after), - hasMore: true, - historicalData: .new(start: context.start, end: context.end), - ); - } + return editedEvent == null + ? foundEvent + : foundEvent?.copyWith( + content: editedEvent.content, + localContent: editedEvent.localContent, + ); + }) + .nonNulls + .toIList(), + hasMore: room.hasMore, + ); } Future deleteMessage(Event event, {String? reason}) => ref diff --git a/lib/helpers/hooks/chat_scroll.dart b/lib/helpers/hooks/chat_scroll.dart index 065a215..9efd3b2 100644 --- a/lib/helpers/hooks/chat_scroll.dart +++ b/lib/helpers/hooks/chat_scroll.dart @@ -32,8 +32,8 @@ final class ChatScroll({ useEffect(() { if (anchorId.value == null) { if (controllerData case AsyncData(:final value?) - when value.timeline.isNotEmpty) { - anchorId.value = value.timeline.last.eventId; + when value.events.isNotEmpty) { + anchorId.value = value.events.last.eventId; } } @@ -41,7 +41,7 @@ final class ChatScroll({ }, [controllerData]); final ({IList history, IList live}) split = useMemoized(() { - final items = controllerData.value?.timeline; + final items = controllerData.value?.events; final anchor = anchorIdValue; if (items == null || anchor == null) { @@ -92,14 +92,19 @@ final class ChatScroll({ (item) => item.eventId == itemId, ); - double? offset; if (historyIndex != -1) { // TODO: Replace SuperSliverView because of the bug that requires this: #94 // ignore: invalid_use_of_visible_for_testing_member - offset = historyListController.value.getOffsetToReveal( + final offset = historyListController.value.getOffsetToReveal( historyIndex, 0.5, ); + + await scrollController.animateTo( + offset, + duration: const Duration(milliseconds: 700), + curve: Curves.easeInOut, + ); } else { final liveIndex = split.live.indexWhere( (item) => item.eventId == itemId, @@ -107,21 +112,18 @@ final class ChatScroll({ if (liveIndex != -1) { // ignore: invalid_use_of_visible_for_testing_member - offset = liveListController.value.getOffsetToReveal(liveIndex, 0.5); + final offset = liveListController.value.getOffsetToReveal( + liveIndex, + 0.5, + ); + + await scrollController.animateTo( + offset, + duration: const Duration(milliseconds: 700), + curve: Curves.easeInOut, + ); } } - - if (offset == null) { - // not in current timeline - } - - if (offset != null) { - await scrollController.animateTo( - offset, - duration: const Duration(milliseconds: 700), - curve: Curves.easeInOut, - ); - } } return .new( diff --git a/lib/models/event_context.dart b/lib/models/event_context.dart index 6103c7d..e7b1cb1 100644 --- a/lib/models/event_context.dart +++ b/lib/models/event_context.dart @@ -8,7 +8,7 @@ part "event_context.g.dart"; @Freezed(toJson: false, fromJson: false) @JsonSerializable() class const EventContext({ - required final Event event, + required final IList events, required final IList before, required final IList after, required final String start, diff --git a/lib/models/room.dart b/lib/models/room.dart index 96b29b4..6d7553f 100644 --- a/lib/models/room.dart +++ b/lib/models/room.dart @@ -12,26 +12,17 @@ part "room.g.dart"; class const Room({ @JsonKey(name: "meta") final RoomMetadata? metadata, - /// [timeline] is an IMap of timelineRowId to eventRowId @JsonKey(fromJson: Room.timelineTupleJsonToIMap) final IMap timeline = const IMap.empty(), - /// [clientSticky] is an ISet of eventRowId - @JsonKey(includeFromJson: false, includeToJson: false) final ISet clientSticky = const ISet.empty(), - /// [events] is an IMap of eventRowId to event @JsonKey(fromJson: Room.eventsJsonToIMap) final IMap events = const IMap.empty(), final bool reset = false, - - @JsonKey(includeFromJson: false, includeToJson: false) final bool hasFetchedState = false, - - @JsonKey(includeFromJson: false, includeToJson: false) final bool hasFetchedMembers = false, - final IMap> state = const IMap.empty(), final IMap> receipts = const IMap.empty(), @@ -41,6 +32,9 @@ class const Room({ // IMap accountData, // IList notifications, }) with _$Room { + /// [timeline] is an IMap of timelineRowId to eventRowId + /// [events] is an IMap of eventRowId to event + /// [clientSticky] is an ISet of eventRowId static IMap timelineTupleJsonToIMap(List json) => IMap.fromEntries( json.map( diff --git a/lib/models/room_chat.dart b/lib/models/room_chat.dart index 6044909..e952d42 100644 --- a/lib/models/room_chat.dart +++ b/lib/models/room_chat.dart @@ -8,7 +8,7 @@ part "room_chat.g.dart"; @Freezed(toJson: false, fromJson: false) @JsonSerializable() class const RoomChat({ - required final IList timeline, + required final IList events, required final bool hasMore, final HistoricalData? historicalData, }) with _$RoomChat { @@ -21,8 +21,8 @@ class const RoomChat({ @Freezed(toJson: false, fromJson: false) @JsonSerializable() class const HistoricalData({ - required final String start, required final String end, + required final String start, }) with _$HistoricalData { Map toJson() => _$HistoricalDataToJson(this); diff --git a/lib/widgets/room_chat/room_chat.dart b/lib/widgets/room_chat/room_chat.dart index 2aee4a5..186b2a6 100644 --- a/lib/widgets/room_chat/room_chat.dart +++ b/lib/widgets/room_chat/room_chat.dart @@ -31,7 +31,6 @@ final class const RoomChat({ Widget build(BuildContext context, WidgetRef ref) { final relatedEvent = useState(null); final relationType = useState(RelationType.reply); - final contextualEvent = useState(initialHighlightedEvent); final highlightedEvent = useState(initialHighlightedEvent); final composerSize = useState(64); @@ -63,7 +62,7 @@ final class const RoomChat({ final controllerProvider = RoomChatController.provider(( roomId, - contextualEvent.value, + initialHighlightedEvent, )); final notifier = ref.watch(controllerProvider.notifier);