add support for jumping to out-of-context events (#95)

Fixes #84.

Reviewed-on: #95
This commit is contained in:
Henry Hiles 2026-09-26 13:03:16 -04:00 • committed by Henry Hiles
commit 389b65ae61
16 changed files with 463 additions and 132 deletions

View file

@ -64,8 +64,10 @@ class const ReactionRow(final Event event, {super.key}) extends ConsumerWidget {
enabled.value = false;
try {
final controller = ref.watch(
RoomChatController.provider(event.roomId)
.notifier,
RoomChatController.provider((
event.roomId,
null,
)).notifier,
);
if (selected) {

View file

@ -8,7 +8,7 @@ import "package:nexus/widgets/highlight_wrapper.dart";
import "package:super_sliver_list/super_sliver_list.dart";
class const ChatTimeline({
required final ChatScroll<Event> scroll,
required final ChatScroll scroll,
required final Future<void> Function(String) jumpToId,
required final IList<PopupMenuEntry> Function(Event) getEventOptions,
required final String? highlightedEvent,
@ -28,6 +28,7 @@ class const ChatTimeline({
required Future<void> Function(String) jumpToId,
required IList<PopupMenuEntry> Function(Event) getEventOptions,
required String? highlightedEvent,
required Key key,
}) => HighlightWrapper(
EventRenderer(
event,
@ -35,7 +36,7 @@ class const ChatTimeline({
getEventOptions: getEventOptions,
isGrouped: isGrouped(event, previousEvent),
),
key: ValueKey(event.eventId),
key: key,
isHighlighted: highlightedEvent == event.eventId,
);
@ -59,6 +60,9 @@ class const ChatTimeline({
jumpToId: jumpToId,
getEventOptions: getEventOptions,
highlightedEvent: highlightedEvent,
key: index == 0
? scroll.anchorItemKey
: ValueKey(scroll.liveItems[index].eventId),
),
),
@ -72,6 +76,7 @@ class const ChatTimeline({
jumpToId: jumpToId,
getEventOptions: getEventOptions,
highlightedEvent: highlightedEvent,
key: ValueKey(scroll.historyItems[index].eventId),
),
),
],

View file

@ -24,13 +24,15 @@ final class const RoomChat({
required final String? roomId,
required final bool isDesktop,
required final bool showMembersByDefault,
final String? initialHighlightedEvent,
super.key,
}) extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final relatedEvent = useState<Event?>(null);
final relationType = useState(RelationType.reply);
final highlightedEvent = useState<String?>(null);
final contextualEvent = useState<String?>(initialHighlightedEvent);
final highlightedEvent = useState<String?>(initialHighlightedEvent);
final composerSize = useState<double>(64);
@ -59,7 +61,10 @@ final class const RoomChat({
final roomId = this.roomId!;
final controllerProvider = RoomChatController.provider(roomId);
final controllerProvider = RoomChatController.provider((
roomId,
contextualEvent.value,
));
final notifier = ref.watch(controllerProvider.notifier);
final client = ref.read(ClientController.provider.notifier);
@ -68,9 +73,9 @@ final class const RoomChat({
final scroll = ChatScroll.use(
controllerData: controllerData,
id: (event) => event.eventId,
loadOlder: notifier.loadOlder,
onReachedBottom: () async {
paginate: notifier.paginate,
contextualEvent: contextualEvent,
markRead: () async {
final room = ref.read(
RoomsController.provider.select((rooms) => rooms[roomId]),
);
@ -153,6 +158,21 @@ final class const RoomChat({
),
),
),
Positioned(
right: 16,
bottom: composerSize.value,
child: IgnorePointer(
ignoring: scroll.atBottom,
child: AnimatedOpacity(
opacity: scroll.atBottom ? 0 : 1,
duration: const Duration(milliseconds: 200),
child: FloatingActionButton.small(
onPressed: scroll.jumpToBottom,
child: const Icon(Icons.keyboard_arrow_down),
),
),
),
),
Positioned(
bottom: 0,
left: 0,