Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
1c1f585362 |
|||
|
c90a1c41b0 |
|||
|
9f070fcdba |
12 changed files with 118 additions and 34 deletions
15
lib/controllers/jump_to_event.dart
Normal file
15
lib/controllers/jump_to_event.dart
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
|
|
||||||
|
class JumpToEventController extends Notifier<String?> {
|
||||||
|
@override
|
||||||
|
String? build() => null;
|
||||||
|
|
||||||
|
void set(String? eventId) => state = eventId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(_, _) => true;
|
||||||
|
|
||||||
|
static final provider = NotifierProvider<JumpToEventController, String?>(
|
||||||
|
JumpToEventController.new,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,7 @@ import "package:nexus/controllers/portal.dart";
|
||||||
import "package:nexus/main.dart";
|
import "package:nexus/main.dart";
|
||||||
import "package:flutter_local_notifications/flutter_local_notifications.dart";
|
import "package:flutter_local_notifications/flutter_local_notifications.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:nexus/pages/notifications.dart";
|
import "package:nexus/widgets/pages/notifications.dart";
|
||||||
import "package:xdg_desktop_portal/xdg_desktop_portal.dart";
|
import "package:xdg_desktop_portal/xdg_desktop_portal.dart";
|
||||||
|
|
||||||
class NotificationController
|
class NotificationController
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,14 @@ final class ChatScroll({
|
||||||
if (found || controllerData is AsyncError) {
|
if (found || controllerData is AsyncError) {
|
||||||
if (found) {
|
if (found) {
|
||||||
anchorId.value = target;
|
anchorId.value = target;
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
final context = keyFor(target).currentContext;
|
||||||
|
if (context != null && context.mounted) {
|
||||||
|
anchorMountedCompleter.value?.complete(context);
|
||||||
|
anchorMountedCompleter.value = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
anchorMountedCompleter.value?.completeError(
|
anchorMountedCompleter.value?.completeError(
|
||||||
StateError("Failed to load context for $target"),
|
StateError("Failed to load context for $target"),
|
||||||
|
|
@ -80,21 +88,6 @@ final class ChatScroll({
|
||||||
return null;
|
return null;
|
||||||
}, [controllerData, pendingAnchorTarget.value]);
|
}, [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 ({IList<Event> history, IList<Event> live}) split = useMemoized(() {
|
||||||
final items = controllerData.value?.timeline;
|
final items = controllerData.value?.timeline;
|
||||||
final anchor = anchorId.value;
|
final anchor = anchorId.value;
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,10 @@ import "package:nexus/controllers/settings.dart";
|
||||||
import "package:nexus/controllers/unified_push.dart";
|
import "package:nexus/controllers/unified_push.dart";
|
||||||
import "package:nexus/helpers/extensions/scheme_to_theme.dart";
|
import "package:nexus/helpers/extensions/scheme_to_theme.dart";
|
||||||
import "package:nexus/helpers/font_licenses.dart";
|
import "package:nexus/helpers/font_licenses.dart";
|
||||||
import "package:nexus/pages/chat.dart";
|
import "package:nexus/widgets/pages/chat.dart";
|
||||||
import "package:nexus/pages/select_server.dart";
|
import "package:nexus/widgets/pages/select_server.dart";
|
||||||
import "package:nexus/pages/settings.dart";
|
import "package:nexus/widgets/pages/settings.dart";
|
||||||
import "package:nexus/pages/verify.dart";
|
import "package:nexus/widgets/pages/verify.dart";
|
||||||
import "package:nexus/widgets/appbar.dart";
|
import "package:nexus/widgets/appbar.dart";
|
||||||
import "package:nexus/widgets/error_dialog.dart";
|
import "package:nexus/widgets/error_dialog.dart";
|
||||||
import "package:nexus/widgets/loading.dart";
|
import "package:nexus/widgets/loading.dart";
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
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/jump_to_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";
|
||||||
import "package:nexus/widgets/room_chat/room_chat.dart";
|
import "package:nexus/widgets/room_chat/room_chat.dart";
|
||||||
import "package:nexus/widgets/loading.dart";
|
import "package:nexus/widgets/loading.dart";
|
||||||
|
|
||||||
class const ChatPage({super.key}) extends ConsumerWidget {
|
class const ChatPage({super.key}) extends HookConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) => LayoutBuilder(
|
Widget build(BuildContext context, WidgetRef ref) => LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
|
|
@ -26,11 +27,19 @@ class const ChatPage({super.key}) extends ConsumerWidget {
|
||||||
children: [
|
children: [
|
||||||
if (isDesktop) Sidebar(isDesktop: isDesktop),
|
if (isDesktop) Sidebar(isDesktop: isDesktop),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: RoomChat(
|
child: Consumer(
|
||||||
key: ValueKey(roomId),
|
builder: (context, ref, _) {
|
||||||
roomId: roomId,
|
final initialHighlight = ref.watch(
|
||||||
isDesktop: isDesktop,
|
JumpToEventController.provider,
|
||||||
showMembersByDefault: showMembersByDefault,
|
);
|
||||||
|
return RoomChat(
|
||||||
|
key: ValueKey((roomId, initialHighlight)),
|
||||||
|
roomId: roomId,
|
||||||
|
isDesktop: isDesktop,
|
||||||
|
initialHighlightedEvent: initialHighlight,
|
||||||
|
showMembersByDefault: showMembersByDefault,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
import "dart:async";
|
import "dart:async";
|
||||||
|
|
||||||
|
import "package:collection/collection.dart";
|
||||||
import "package:m3e_buttons/m3e_buttons.dart";
|
import "package:m3e_buttons/m3e_buttons.dart";
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
|
import "package:nexus/controllers/jump_to_event.dart";
|
||||||
|
import "package:nexus/controllers/key.dart";
|
||||||
import "package:nexus/controllers/notifications.dart";
|
import "package:nexus/controllers/notifications.dart";
|
||||||
|
import "package:nexus/controllers/spaces.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
import "package:nexus/widgets/appbar.dart";
|
import "package:nexus/widgets/appbar.dart";
|
||||||
import "package:nexus/widgets/error_dialog.dart";
|
import "package:nexus/widgets/error_dialog.dart";
|
||||||
|
|
@ -127,9 +131,54 @@ class const NotificationsPage({
|
||||||
child: HighlightWrapper(
|
child: HighlightWrapper(
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
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);
|
||||||
|
ref
|
||||||
|
.read(
|
||||||
|
JumpToEventController
|
||||||
|
.provider
|
||||||
|
.notifier,
|
||||||
|
)
|
||||||
|
.set(event.eventId);
|
||||||
|
|
||||||
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: EventRenderer(event),
|
child: IgnorePointer(
|
||||||
|
child: EventRenderer(event),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
isHighlighted: isHighlighted,
|
isHighlighted: isHighlighted,
|
||||||
),
|
),
|
||||||
|
|
@ -9,7 +9,7 @@ import "package:nexus/controllers/client_id.dart";
|
||||||
import "package:nexus/helpers/launch_helper.dart";
|
import "package:nexus/helpers/launch_helper.dart";
|
||||||
import "package:nexus/main.dart";
|
import "package:nexus/main.dart";
|
||||||
import "package:nexus/models/homeserver.dart";
|
import "package:nexus/models/homeserver.dart";
|
||||||
import "package:nexus/pages/settings.dart";
|
import "package:nexus/widgets/pages/settings.dart";
|
||||||
import "package:nexus/widgets/appbar.dart";
|
import "package:nexus/widgets/appbar.dart";
|
||||||
import "package:nexus/widgets/divider_text.dart";
|
import "package:nexus/widgets/divider_text.dart";
|
||||||
|
|
||||||
|
|
@ -8,7 +8,7 @@ import "package:navigation_rail_m3e/navigation_rail_m3e.dart";
|
||||||
import "package:nexus/controllers/settings_sections.dart";
|
import "package:nexus/controllers/settings_sections.dart";
|
||||||
import "package:nexus/helpers/extensions/better_when.dart";
|
import "package:nexus/helpers/extensions/better_when.dart";
|
||||||
import "package:nexus/helpers/extensions/show_about_dialog.dart";
|
import "package:nexus/helpers/extensions/show_about_dialog.dart";
|
||||||
import "package:nexus/pages/settings_category.dart";
|
import "package:nexus/widgets/pages/settings_category.dart";
|
||||||
import "package:nexus/widgets/divider_text.dart";
|
import "package:nexus/widgets/divider_text.dart";
|
||||||
import "package:nexus/widgets/highlight_wrapper.dart";
|
import "package:nexus/widgets/highlight_wrapper.dart";
|
||||||
import "package:super_sliver_list/super_sliver_list.dart";
|
import "package:super_sliver_list/super_sliver_list.dart";
|
||||||
|
|
@ -2,7 +2,7 @@ import "package:material_ui/material_ui.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:nexus/controllers/client.dart";
|
import "package:nexus/controllers/client.dart";
|
||||||
import "package:nexus/pages/settings.dart";
|
import "package:nexus/widgets/pages/settings.dart";
|
||||||
import "package:nexus/widgets/appbar.dart";
|
import "package:nexus/widgets/appbar.dart";
|
||||||
import "package:nexus/helpers/required_validator_helper.dart";
|
import "package:nexus/helpers/required_validator_helper.dart";
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ final class const RoomChat({
|
||||||
final relatedEvent = useState<Event?>(null);
|
final relatedEvent = useState<Event?>(null);
|
||||||
final relationType = useState(RelationType.reply);
|
final relationType = useState(RelationType.reply);
|
||||||
final contextualEvent = useState<String?>(initialHighlightedEvent);
|
final contextualEvent = useState<String?>(initialHighlightedEvent);
|
||||||
final highlightedEvent = useState<String?>(initialHighlightedEvent);
|
final highlightedEvent = useState<String?>(null);
|
||||||
|
|
||||||
final composerSize = useState<double>(64);
|
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) =>
|
IList<PopupMenuEntry> getEventOptions(Event event) =>
|
||||||
event.buildEventOptions(
|
event.buildEventOptions(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import "package:navigation_rail_m3e/navigation_rail_m3e.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/room.dart";
|
import "package:nexus/models/room.dart";
|
||||||
import "package:nexus/pages/notifications.dart";
|
import "package:nexus/widgets/pages/notifications.dart";
|
||||||
import "package:nexus/pages/settings.dart";
|
import "package:nexus/widgets/pages/settings.dart";
|
||||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||||
import "package:nexus/widgets/divider_widget.dart";
|
import "package:nexus/widgets/divider_widget.dart";
|
||||||
import "package:nexus/widgets/join_dialog.dart";
|
import "package:nexus/widgets/join_dialog.dart";
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue