initial jump mechanism implemented

This commit is contained in:
Henry Hiles 2026-09-25 14:04:13 -04:00
commit dfb8eb8a48
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
4 changed files with 35 additions and 39 deletions

View file

@ -23,7 +23,7 @@ class RoomChatController(final (String roomId, String? contextualEvent) info)
Future<RoomChat?> build() async { Future<RoomChat?> build() async {
final (roomId, eventId) = info; final (roomId, eventId) = info;
final client = ref.read(ClientController.provider.notifier); final client = ref.read(ClientController.provider.notifier);
final room = ref.watch( final room = ref.read(
RoomsController.provider.select((rooms) => rooms[roomId]), RoomsController.provider.select((rooms) => rooms[roomId]),
); );
@ -34,43 +34,39 @@ class RoomChatController(final (String roomId, String? contextualEvent) info)
await ref.read(RoomsController.provider.notifier).addState(roomId, state); await ref.read(RoomsController.provider.notifier).addState(roomId, state);
} }
if (info.$2 == null || final timeline = room.timeline
room.events.values.map((e) => e.eventId).contains(info.$2)) { .toEntryIList(compare: (a, b) => (a?.key ?? 0).compareTo(b?.key ?? 0))
return .new( .map((element) => element.value)
events: room.timeline .toIList()
.toEntryIList( .addAll(room.clientSticky)
compare: (a, b) => (a?.key ?? 0).compareTo(b?.key ?? 0), .map((entry) {
) final foundEvent = entry == null ? null : room.events[entry];
.map((element) => element.value)
.toIList()
.addAll(room.clientSticky)
.map((entry) {
final foundEvent = entry == null ? null : room.events[entry];
final editedEvent = final editedEvent =
foundEvent == null || foundEvent.lastEditRowId == 0 foundEvent == null || foundEvent.lastEditRowId == 0
? null ? null
: room.events[foundEvent.lastEditRowId]; : room.events[foundEvent.lastEditRowId];
return editedEvent == null return editedEvent == null
? foundEvent ? foundEvent
: foundEvent?.copyWith( : foundEvent?.copyWith(
content: editedEvent.content, content: editedEvent.content,
localContent: editedEvent.localContent, localContent: editedEvent.localContent,
); );
}) })
.nonNulls .nonNulls
.toIList(), .toIList();
hasMore: room.hasMore,
); 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( final context = await client.getEventContext(
.new(roomId: roomId, eventId: info.$2!, limit: 20), .new(roomId: roomId, eventId: info.$2!, limit: 20),
); );
return .new( return .new(
events: context.events, timeline: context.before.add(context.event).addAll(context.after),
hasMore: true, hasMore: true,
historicalData: .new(start: context.start, end: context.end), historicalData: .new(start: context.start, end: context.end),
); );

View file

@ -32,8 +32,8 @@ final class ChatScroll({
useEffect(() { useEffect(() {
if (anchorId.value == null) { if (anchorId.value == null) {
if (controllerData case AsyncData(:final value?) if (controllerData case AsyncData(:final value?)
when value.events.isNotEmpty) { when value.timeline.isNotEmpty) {
anchorId.value = value.events.last.eventId; anchorId.value = value.timeline.last.eventId;
} }
} }
@ -41,7 +41,7 @@ final class ChatScroll({
}, [controllerData]); }, [controllerData]);
final ({IList<Event> history, IList<Event> live}) split = useMemoized(() { final ({IList<Event> history, IList<Event> live}) split = useMemoized(() {
final items = controllerData.value?.events; final items = controllerData.value?.timeline;
final anchor = anchorIdValue; final anchor = anchorIdValue;
if (items == null || anchor == null) { if (items == null || anchor == null) {

View file

@ -8,7 +8,7 @@ part "event_context.g.dart";
@Freezed(toJson: false, fromJson: false) @Freezed(toJson: false, fromJson: false)
@JsonSerializable() @JsonSerializable()
class const EventContext({ class const EventContext({
required final IList<Event> events, required final Event event,
required final IList<Event> before, required final IList<Event> before,
required final IList<Event> after, required final IList<Event> after,
required final String start, required final String start,

View file

@ -8,7 +8,7 @@ part "room_chat.g.dart";
@Freezed(toJson: false, fromJson: false) @Freezed(toJson: false, fromJson: false)
@JsonSerializable() @JsonSerializable()
class const RoomChat({ class const RoomChat({
required final IList<Event> events, required final IList<Event> timeline,
required final bool hasMore, required final bool hasMore,
final HistoricalData? historicalData, final HistoricalData? historicalData,
}) with _$RoomChat { }) with _$RoomChat {