working jumps from notif page

Doesn't work from notif page pushed from notif controller
This commit is contained in:
Henry Hiles 2026-09-26 19:13:30 -04:00
commit c90a1c41b0
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
6 changed files with 136 additions and 57 deletions

View file

@ -37,6 +37,7 @@ class NotificationController
MaterialPageRoute(
builder: (_) => NotificationsPage(
highlightedEventId: eventId,
jumpToEvent: (eventId) {},
defaultToAllNotifications: true,
),
),
@ -62,6 +63,7 @@ class NotificationController
MaterialPageRoute(
builder: (_) => NotificationsPage(
highlightedEventId: eventId,
jumpToEvent: (eventId) {},
defaultToAllNotifications: true,
),
),

View file

@ -68,6 +68,14 @@ final class ChatScroll({
if (found || controllerData is AsyncError) {
if (found) {
anchorId.value = target;
WidgetsBinding.instance.addPostFrameCallback((_) {
final context = keyFor(target).currentContext;
if (context != null && context.mounted) {
anchorMountedCompleter.value?.complete(context);
anchorMountedCompleter.value = null;
}
});
} else {
anchorMountedCompleter.value?.completeError(
StateError("Failed to load context for $target"),
@ -80,21 +88,6 @@ final class ChatScroll({
return null;
}, [controllerData, pendingAnchorTarget.value]);
useEffect(() {
final completer = anchorMountedCompleter.value;
if (completer == null) return null;
WidgetsBinding.instance.addPostFrameCallback((_) {
final context = keyFor(anchorId.value!).currentContext;
if (context != null && context.mounted) {
anchorMountedCompleter.value?.complete(context);
anchorMountedCompleter.value = null;
}
});
return null;
}, [anchorId.value]);
final ({IList<Event> history, IList<Event> live}) split = useMemoized(() {
final items = controllerData.value?.timeline;
final anchor = anchorId.value;

View file

@ -1,3 +1,4 @@
import "package:flutter_hooks/flutter_hooks.dart";
import "package:material_ui/material_ui.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/init_complete.dart";
@ -7,9 +8,12 @@ import "package:nexus/widgets/sidebar.dart";
import "package:nexus/widgets/room_chat/room_chat.dart";
import "package:nexus/widgets/loading.dart";
class const ChatPage({super.key}) extends ConsumerWidget {
class const ChatPage({super.key}) extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) => LayoutBuilder(
Widget build(BuildContext context, WidgetRef ref) {
final initialHighlightedEvent = useState<String?>(null);
return LayoutBuilder(
builder: (context, constraints) {
final isDesktop = constraints.maxWidth > 650;
final showMembersByDefault = constraints.maxWidth > 1000;
@ -24,13 +28,23 @@ class const ChatPage({super.key}) extends ConsumerWidget {
body: initComplete
? Row(
children: [
if (isDesktop) Sidebar(isDesktop: isDesktop),
if (isDesktop)
Sidebar(
isDesktop: isDesktop,
jumpToEvent: (eventId) =>
initialHighlightedEvent.value = eventId,
),
Expanded(
child: RoomChat(
key: ValueKey(roomId),
key: ValueKey((
roomId,
initialHighlightedEvent.value,
)),
roomId: roomId,
isDesktop: isDesktop,
showMembersByDefault: showMembersByDefault,
initialHighlightedEvent:
initialHighlightedEvent.value,
),
),
],
@ -43,9 +57,14 @@ class const ChatPage({super.key}) extends ConsumerWidget {
),
drawer: isDesktop || !initComplete
? null
: Sidebar(isDesktop: isDesktop),
: Sidebar(
isDesktop: isDesktop,
jumpToEvent: (eventId) =>
initialHighlightedEvent.value = eventId,
),
),
);
},
);
}
}

View file

@ -1,10 +1,13 @@
import "dart:async";
import "package:collection/collection.dart";
import "package:m3e_buttons/m3e_buttons.dart";
import "package:material_ui/material_ui.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/key.dart";
import "package:nexus/controllers/notifications.dart";
import "package:nexus/controllers/spaces.dart";
import "package:nexus/models/event.dart";
import "package:nexus/widgets/appbar.dart";
import "package:nexus/widgets/error_dialog.dart";
@ -16,6 +19,7 @@ import "package:super_sliver_list/super_sliver_list.dart";
class const NotificationsPage({
final String? highlightedEventId,
final bool defaultToAllNotifications = false,
required final void Function(String eventId) jumpToEvent,
super.key,
}) extends HookConsumerWidget {
@override
@ -127,10 +131,49 @@ class const NotificationsPage({
child: HighlightWrapper(
InkWell(
onTap: () {
// TODO: Jump to event
final spaces = ref.read(
SpacesController.provider,
);
final space = spaces.firstWhereOrNull(
(space) =>
space.children.any(
(room) =>
room.metadata?.id ==
event.roomId,
) ||
space.subSpaces.any(
(subSpace) =>
subSpace.children.any(
(room) =>
room.metadata?.id ==
event.roomId,
),
),
);
if (space == null) return;
ref
.read(
KeyController.provider(
KeyController.spaceKey,
).notifier,
)
.set(space.id);
ref
.read(
KeyController.provider(
KeyController.roomKey,
).notifier,
)
.set(event.roomId);
jumpToEvent(event.eventId);
Navigator.of(context).pop();
},
child: IgnorePointer(
child: EventRenderer(event),
),
),
isHighlighted: isHighlighted,
),
);

View file

@ -32,7 +32,7 @@ final class const RoomChat({
final relatedEvent = useState<Event?>(null);
final relationType = useState(RelationType.reply);
final contextualEvent = useState<String?>(initialHighlightedEvent);
final highlightedEvent = useState<String?>(initialHighlightedEvent);
final highlightedEvent = useState<String?>(null);
final composerSize = useState<double>(64);
@ -106,6 +106,24 @@ final class const RoomChat({
});
}
useEffect(() {
if (initialHighlightedEvent == null) return null;
void check() {
if (!context.mounted) return;
if (scroll.scrollController.hasClients) {
jumpToId(initialHighlightedEvent!);
} else {
WidgetsBinding.instance.addPostFrameCallback((_) => check());
}
}
check();
return null;
}, [initialHighlightedEvent]);
IList<PopupMenuEntry> getEventOptions(Event event) =>
event.buildEventOptions(
context: context,

View file

@ -16,8 +16,11 @@ import "package:nexus/widgets/room_menu.dart";
// Needed for navigation_rail_m3e (#65).
import "package:flutter/material.dart" as old_mat;
class const Sidebar({required final bool isDesktop, super.key})
extends HookConsumerWidget {
class const Sidebar({
required final bool isDesktop,
required final void Function(String eventId) jumpToEvent,
super.key,
}) extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedSpaceProvider = KeyController.provider(
@ -194,7 +197,8 @@ class const Sidebar({required final bool isDesktop, super.key})
tooltip: "Open notifications",
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => NotificationsPage(),
builder: (_) =>
NotificationsPage(jumpToEvent: jumpToEvent),
),
),
icon: Icon(Icons.notifications),