change how cross-room jumping works

This commit is contained in:
Henry Hiles 2026-09-29 20:23:16 -04:00
commit da7f2dcf4e
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
5 changed files with 45 additions and 48 deletions

View file

@ -1,16 +0,0 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
class ContextualEventController(String? _) extends Notifier<int?> {
@override
int? build() => null;
void set(int? event) => state = event;
@override
bool updateShouldNotify(_, _) => true;
static final provider =
NotifierProvider.family<ContextualEventController, int?, String?>(
ContextualEventController.new,
);
}

View file

@ -0,0 +1,17 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
typedef JumpRequest = ({String roomId, int rowId});
class JumpRequestController extends Notifier<JumpRequest?> {
@override
JumpRequest? build() => null;
void request(String roomId, int rowId) =>
state = (roomId: roomId, rowId: rowId);
void consume() => state = null;
static final provider = NotifierProvider<JumpRequestController, JumpRequest?>(
JumpRequestController.new,
);
}

View file

@ -1,6 +1,6 @@
import "package:collection/collection.dart"; import "package:collection/collection.dart";
import "package:flutter_riverpod/experimental/mutation.dart"; import "package:flutter_riverpod/experimental/mutation.dart";
import "package:nexus/controllers/contextual_event.dart"; import "package:nexus/controllers/jump_request.dart";
import "package:nexus/controllers/key.dart"; import "package:nexus/controllers/key.dart";
import "package:nexus/controllers/spaces.dart"; import "package:nexus/controllers/spaces.dart";
import "package:nexus/models/space.dart"; import "package:nexus/models/space.dart";
@ -31,16 +31,16 @@ extension FocusRoom on MutationTarget {
.read(KeyController.provider(KeyController.spaceKey).notifier) .read(KeyController.provider(KeyController.spaceKey).notifier)
.set(parent.id); .set(parent.id);
if (eventRowId != null) {
container
.read(JumpRequestController.provider.notifier)
.request(roomId, eventRowId);
}
await container await container
.read(KeyController.provider(KeyController.roomKey).notifier) .read(KeyController.provider(KeyController.roomKey).notifier)
.set(roomId); .set(roomId);
if (eventRowId != null) {
container
.read(ContextualEventController.provider(roomId).notifier)
.set(eventRowId);
}
return true; return true;
} }
} }

View file

@ -1,7 +1,6 @@
import "package:material_ui/material_ui.dart"; import "package:material_ui/material_ui.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/init_complete.dart"; import "package:nexus/controllers/init_complete.dart";
import "package:nexus/controllers/contextual_event.dart";
import "package:nexus/controllers/key.dart"; import "package:nexus/controllers/key.dart";
import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/appbar.dart";
import "package:nexus/widgets/sidebar.dart"; import "package:nexus/widgets/sidebar.dart";
@ -27,19 +26,11 @@ class const ChatPage({super.key}) extends HookConsumerWidget {
children: [ children: [
if (isDesktop) Sidebar(), if (isDesktop) Sidebar(),
Expanded( Expanded(
child: Consumer( child: RoomChat(
builder: (context, ref, _) { key: ValueKey(roomId),
final initialHighlight = ref.watch( roomId: roomId,
ContextualEventController.provider(roomId), isDesktop: isDesktop,
); showMembersByDefault: showMembersByDefault,
return RoomChat(
key: ValueKey((roomId, initialHighlight)),
roomId: roomId,
isDesktop: isDesktop,
initialHighlight: initialHighlight,
showMembersByDefault: showMembersByDefault,
);
},
), ),
), ),
], ],

View file

@ -21,19 +21,24 @@ import "package:nexus/widgets/room_appbar.dart";
import "package:nexus/main.dart"; import "package:nexus/main.dart";
import "package:nexus/widgets/room_chat/chat_timeline.dart"; import "package:nexus/widgets/room_chat/chat_timeline.dart";
import "package:nexus/helpers/extensions/build_event_options.dart"; import "package:nexus/helpers/extensions/build_event_options.dart";
import "package:nexus/controllers/jump_request.dart";
final class const RoomChat({ final class const RoomChat({
required final String? roomId, required final String? roomId,
required final bool isDesktop, required final bool isDesktop,
required final bool showMembersByDefault, required final bool showMembersByDefault,
final int? initialHighlight,
super.key, super.key,
}) extends HookConsumerWidget { }) extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final relatedEvent = useState<int?>(null); final relatedEvent = useState<int?>(null);
final relationType = useState(RelationType.reply); final relationType = useState(RelationType.reply);
final contextualEvent = useState<int?>(initialHighlight); final pendingJump = ref.read(JumpRequestController.provider);
final contextualEvent = useState<int?>(
pendingJump != null && pendingJump.roomId == this.roomId
? pendingJump.rowId
: null,
);
final highlightedEvent = useState<int?>(null); final highlightedEvent = useState<int?>(null);
final composerSize = useState<double>(64); final composerSize = useState<double>(64);
@ -110,23 +115,23 @@ final class const RoomChat({
}); });
} }
useEffect(() { ref.listen(JumpRequestController.provider, (_, request) {
if (initialHighlight == null) return null; if (request == null || request.roomId != roomId) return;
void check() { void tryJump() {
if (!context.mounted) return; if (!context.mounted) return;
if (ref.read(JumpRequestController.provider) != request) return;
if (scroll.scrollController.hasClients) { if (scroll.scrollController.hasClients) {
jumpToEvent(initialHighlight!); ref.read(JumpRequestController.provider.notifier).consume();
jumpToEvent(request.rowId);
} else { } else {
WidgetsBinding.instance.addPostFrameCallback((_) => check()); WidgetsBinding.instance.addPostFrameCallback((_) => tryJump());
} }
} }
check(); WidgetsBinding.instance.addPostFrameCallback((_) => tryJump());
});
return null;
}, [initialHighlight]);
IList<PopupMenuEntry> getEventOptions(Event event) => IList<PopupMenuEntry> getEventOptions(Event event) =>
event.buildEventOptions( event.buildEventOptions(