make NotificationsController work off of rowIds
This commit is contained in:
parent
281bab0202
commit
ead463262c
12 changed files with 116 additions and 71 deletions
|
|
@ -1,17 +1,16 @@
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:nexus/models/event.dart";
|
|
||||||
|
|
||||||
class ContextualEventController(String? _) extends Notifier<Event?> {
|
class ContextualEventController(String? _) extends Notifier<int?> {
|
||||||
@override
|
@override
|
||||||
Event? build() => null;
|
int? build() => null;
|
||||||
|
|
||||||
void set(Event? event) => state = event;
|
void set(int? event) => state = event;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotify(_, _) => true;
|
bool updateShouldNotify(_, _) => true;
|
||||||
|
|
||||||
static final provider = NotifierProvider.family
|
static final provider = NotifierProvider.family
|
||||||
.autoDispose<ContextualEventController, Event?, String?>(
|
.autoDispose<ContextualEventController, int?, String?>(
|
||||||
ContextualEventController.new,
|
ContextualEventController.new,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,14 @@ class NotificationController
|
||||||
macOS: darwin,
|
macOS: darwin,
|
||||||
),
|
),
|
||||||
onDidReceiveNotificationResponse: (details) {
|
onDidReceiveNotificationResponse: (details) {
|
||||||
if (details.payload case final eventId?) {
|
if (details.payload == null) return;
|
||||||
|
|
||||||
|
if (int.tryParse(details.payload!) case final rowId?) {
|
||||||
if (navigatorKey.currentContext case final context?) {
|
if (navigatorKey.currentContext case final context?) {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => NotificationsPage(
|
builder: (_) => NotificationsPage(
|
||||||
highlightedEventId: eventId,
|
highlightedEvent: rowId,
|
||||||
defaultToAllNotifications: true,
|
defaultToAllNotifications: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -55,18 +57,18 @@ class NotificationController
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final eventId = call.arguments as String;
|
if (int.tryParse(call.arguments) case int rowId) {
|
||||||
|
|
||||||
if (navigatorKey.currentContext case final context?) {
|
if (navigatorKey.currentContext case final context?) {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => NotificationsPage(
|
builder: (_) => NotificationsPage(
|
||||||
highlightedEventId: eventId,
|
highlightedEvent: rowId,
|
||||||
defaultToAllNotifications: true,
|
defaultToAllNotifications: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ref.onDispose(() => notificationChannel.setMethodCallHandler(null));
|
ref.onDispose(() => notificationChannel.setMethodCallHandler(null));
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,61 @@
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:nexus/controllers/client.dart";
|
import "package:nexus/controllers/client.dart";
|
||||||
|
import "package:nexus/controllers/rooms.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
|
|
||||||
typedef NotificationsRequest = (UnreadType? unreadType, String? roomId);
|
typedef NotificationsRequest = (UnreadType? unreadType, String? roomId);
|
||||||
|
|
||||||
class NotificationsController([final NotificationsRequest? request])
|
class NotificationsController([final NotificationsRequest? request])
|
||||||
extends AsyncNotifier<IList<Event>> {
|
extends AsyncNotifier<IList<(int, String)>> {
|
||||||
|
static const virtualRoomId = "!notifications";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<IList<Event>> build() async {
|
Future<IList<(int, String)>> build() async {
|
||||||
final client = ref.read(ClientController.provider.notifier);
|
final client = ref.read(ClientController.provider.notifier);
|
||||||
|
|
||||||
final (unreadType, roomId) = request ?? (null, null);
|
final (unreadType, roomId) = request ?? (null, null);
|
||||||
|
|
||||||
return await client.getMentions(
|
final mentions = await client.getMentions(
|
||||||
.new(
|
.new(
|
||||||
maxTimestamp: .now(),
|
maxTimestamp: .now(),
|
||||||
unreadType: unreadType ?? .highlight,
|
unreadType: unreadType ?? .highlight,
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ref
|
||||||
|
.watch(RoomsController.provider.notifier)
|
||||||
|
.update(
|
||||||
|
.new({
|
||||||
|
virtualRoomId: .new(
|
||||||
|
events: IMap.fromIterable(
|
||||||
|
mentions,
|
||||||
|
keyMapper: (event) => event.rowId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
.new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return .new(mentions.map((event) => (event.rowId, event.roomId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadOlder() async {
|
Future<void> loadOlder() async {
|
||||||
final currentNotifications = await future;
|
final currentNotifications = await future;
|
||||||
state = .loading();
|
state = .loading();
|
||||||
state = await .guard(() async {
|
state = await .guard(() async {
|
||||||
final lastTs = currentNotifications.lastOrNull?.timestamp;
|
final lastNotification = currentNotifications.lastOrNull?.$1;
|
||||||
if (lastTs == null) return const .empty();
|
final lastTs = lastNotification == null
|
||||||
|
? null
|
||||||
|
: ref.watch(
|
||||||
|
RoomsController.provider.select(
|
||||||
|
(rooms) =>
|
||||||
|
rooms[virtualRoomId]?.events[lastNotification]?.timestamp,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (lastTs == null) return currentNotifications;
|
||||||
|
|
||||||
final client = ref.read(ClientController.provider.notifier);
|
final client = ref.read(ClientController.provider.notifier);
|
||||||
final (unreadType, roomId) = request ?? (null, null);
|
final (unreadType, roomId) = request ?? (null, null);
|
||||||
|
|
@ -40,14 +68,30 @@ class NotificationsController([final NotificationsRequest? request])
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return currentNotifications.addAll(newNotifications);
|
ref
|
||||||
|
.watch(RoomsController.provider.notifier)
|
||||||
|
.update(
|
||||||
|
.new({
|
||||||
|
virtualRoomId: .new(
|
||||||
|
events: IMap.fromIterable(
|
||||||
|
newNotifications,
|
||||||
|
keyMapper: (event) => event.rowId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
.new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return currentNotifications.addAll(
|
||||||
|
newNotifications.map((event) => (event.rowId, event.roomId)),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static final provider = AsyncNotifierProvider.family
|
static final provider = AsyncNotifierProvider.family
|
||||||
.autoDispose<
|
.autoDispose<
|
||||||
NotificationsController,
|
NotificationsController,
|
||||||
IList<Event>,
|
IList<(int, String)>,
|
||||||
NotificationsRequest?
|
NotificationsRequest?
|
||||||
>(NotificationsController.new);
|
>(NotificationsController.new);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,7 @@ import "package:nexus/models/requests/send_message.dart";
|
||||||
import "package:nexus/models/room.dart";
|
import "package:nexus/models/room.dart";
|
||||||
import "package:nexus/models/room_chat.dart";
|
import "package:nexus/models/room_chat.dart";
|
||||||
|
|
||||||
typedef ChatParams = ({
|
typedef ChatParams = ({String roomId, int? contextualEvent});
|
||||||
String roomId,
|
|
||||||
({int rowId, String eventId})? contextualEvent,
|
|
||||||
});
|
|
||||||
|
|
||||||
class RoomChatController(final ChatParams info)
|
class RoomChatController(final ChatParams info)
|
||||||
extends AsyncNotifier<RoomChat?> {
|
extends AsyncNotifier<RoomChat?> {
|
||||||
|
|
@ -47,7 +44,7 @@ class RoomChatController(final ChatParams info)
|
||||||
.nonNulls
|
.nonNulls
|
||||||
.toIList();
|
.toIList();
|
||||||
if (info.contextualEvent == null ||
|
if (info.contextualEvent == null ||
|
||||||
timeline.contains(info.contextualEvent!.rowId)) {
|
timeline.contains(info.contextualEvent)) {
|
||||||
ref.watch(
|
ref.watch(
|
||||||
RoomsController.provider.select(
|
RoomsController.provider.select(
|
||||||
(rooms) =>
|
(rooms) =>
|
||||||
|
|
@ -62,7 +59,10 @@ class RoomChatController(final ChatParams info)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final context = await client.getEventContext(
|
final context = await client.getEventContext(
|
||||||
.new(roomId: info.roomId, eventId: info.contextualEvent!.eventId),
|
.new(
|
||||||
|
roomId: info.roomId,
|
||||||
|
eventId: room.events[info.contextualEvent!]!.eventId,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final events = context.before.add(context.event).addAll(context.after);
|
final events = context.before.add(context.event).addAll(context.after);
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ class UnifiedPushController extends AsyncNotifier<bool> {
|
||||||
id: event.eventId.hashCode & 0x7fffffff,
|
id: event.eventId.hashCode & 0x7fffffff,
|
||||||
title: roomMetadata.name ?? "New Event",
|
title: roomMetadata.name ?? "New Event",
|
||||||
icon: icon,
|
icon: icon,
|
||||||
payload: event.eventId,
|
payload: event.rowId.toString(),
|
||||||
body: switch (event.content) {
|
body: switch (event.content) {
|
||||||
MessageContent(:final body?) ||
|
MessageContent(:final body?) ||
|
||||||
StickerContent(:final body) => body,
|
StickerContent(:final body) => body,
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,17 @@ import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
import "package:nexus/models/direction.dart";
|
import "package:nexus/models/direction.dart";
|
||||||
import "package:nexus/models/event.dart";
|
|
||||||
import "package:nexus/models/room_chat.dart";
|
import "package:nexus/models/room_chat.dart";
|
||||||
|
|
||||||
|
typedef JumpToEvent = Future<void> Function(int rowId);
|
||||||
|
|
||||||
final class ChatScroll({
|
final class ChatScroll({
|
||||||
required final IList<int> historyRows,
|
required final IList<int> historyRows,
|
||||||
required final IList<int> liveRows,
|
required final IList<int> liveRows,
|
||||||
required final GlobalKey centerKey,
|
required final GlobalKey centerKey,
|
||||||
required final ScrollController scrollController,
|
required final ScrollController scrollController,
|
||||||
required final bool atBottom,
|
required final bool atBottom,
|
||||||
required final Future<void> Function(Event event) jumpToEvent,
|
required final JumpToEvent jumpToEvent,
|
||||||
required final Future<void> Function() jumpToBottom,
|
required final Future<void> Function() jumpToBottom,
|
||||||
required final GlobalKey Function(int eventRowId) keyFor,
|
required final GlobalKey Function(int eventRowId) keyFor,
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -22,7 +23,7 @@ final class ChatScroll({
|
||||||
required AsyncValue<RoomChat?> controllerData,
|
required AsyncValue<RoomChat?> controllerData,
|
||||||
required Future<void> Function(Direction direction) paginate,
|
required Future<void> Function(Direction direction) paginate,
|
||||||
required Future<void> Function() markRead,
|
required Future<void> Function() markRead,
|
||||||
required ValueNotifier<Event?> contextualEvent,
|
required ValueNotifier<int?> contextualEvent,
|
||||||
}) {
|
}) {
|
||||||
final anchorId = useState<int?>(null);
|
final anchorId = useState<int?>(null);
|
||||||
|
|
||||||
|
|
@ -43,11 +44,11 @@ final class ChatScroll({
|
||||||
if (controllerData case AsyncData(:final value?)
|
if (controllerData case AsyncData(:final value?)
|
||||||
when value.timeline.isNotEmpty) {
|
when value.timeline.isNotEmpty) {
|
||||||
final hasContextualEvent = value.timeline.any(
|
final hasContextualEvent = value.timeline.any(
|
||||||
(event) => event == contextualEvent.value?.rowId,
|
(event) => event == contextualEvent.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
anchorId.value = hasContextualEvent
|
anchorId.value = hasContextualEvent
|
||||||
? contextualEvent.value?.rowId
|
? contextualEvent.value
|
||||||
: value.timeline.last;
|
: value.timeline.last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -157,10 +158,10 @@ final class ChatScroll({
|
||||||
centerKey: centerKey,
|
centerKey: centerKey,
|
||||||
scrollController: scrollController,
|
scrollController: scrollController,
|
||||||
atBottom: atBottom.value,
|
atBottom: atBottom.value,
|
||||||
jumpToEvent: (Event event) async {
|
jumpToEvent: (int rowId) async {
|
||||||
if (!scrollController.hasClients) return;
|
if (!scrollController.hasClients) return;
|
||||||
|
|
||||||
final existing = keyFor(event.rowId).currentContext;
|
final existing = keyFor(rowId).currentContext;
|
||||||
if (existing != null && existing.mounted) {
|
if (existing != null && existing.mounted) {
|
||||||
// Already mounted, just scroll
|
// Already mounted, just scroll
|
||||||
await Scrollable.ensureVisible(
|
await Scrollable.ensureVisible(
|
||||||
|
|
@ -172,8 +173,8 @@ final class ChatScroll({
|
||||||
} else {
|
} else {
|
||||||
final completer = Completer<BuildContext>();
|
final completer = Completer<BuildContext>();
|
||||||
anchorMountedCompleter.value = completer;
|
anchorMountedCompleter.value = completer;
|
||||||
pendingAnchorTarget.value = event.rowId;
|
pendingAnchorTarget.value = rowId;
|
||||||
contextualEvent.value = event;
|
contextualEvent.value = rowId;
|
||||||
|
|
||||||
final context = await completer.future;
|
final context = await completer.future;
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class const ChatPage({super.key}) extends HookConsumerWidget {
|
||||||
key: ValueKey((roomId, initialHighlight)),
|
key: ValueKey((roomId, initialHighlight)),
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
initialHighlightedEvent: initialHighlight,
|
initialHighlight: initialHighlight,
|
||||||
showMembersByDefault: showMembersByDefault,
|
showMembersByDefault: showMembersByDefault,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import "package:nexus/widgets/renderers/event.dart";
|
||||||
import "package:super_sliver_list/super_sliver_list.dart";
|
import "package:super_sliver_list/super_sliver_list.dart";
|
||||||
|
|
||||||
class const NotificationsPage({
|
class const NotificationsPage({
|
||||||
final String? highlightedEventId,
|
final int? highlightedEvent,
|
||||||
final bool defaultToAllNotifications = false,
|
final bool defaultToAllNotifications = false,
|
||||||
super.key,
|
super.key,
|
||||||
}) extends HookConsumerWidget {
|
}) extends HookConsumerWidget {
|
||||||
|
|
@ -34,7 +34,7 @@ class const NotificationsPage({
|
||||||
};
|
};
|
||||||
final unreadTypeIndex = useState(defaultToAllNotifications ? 1 : 0);
|
final unreadTypeIndex = useState(defaultToAllNotifications ? 1 : 0);
|
||||||
|
|
||||||
final highlightedId = useState(highlightedEventId);
|
final highlightedId = useState(highlightedEvent);
|
||||||
final listController = useRef(ListController());
|
final listController = useRef(ListController());
|
||||||
final scrollController = useScrollController();
|
final scrollController = useScrollController();
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ class const NotificationsPage({
|
||||||
final notifications = await ref.watch(provider.future);
|
final notifications = await ref.watch(provider.future);
|
||||||
|
|
||||||
final index = notifications.indexWhere(
|
final index = notifications.indexWhere(
|
||||||
(element) => element.eventId == highlightedId.value!,
|
(tuple) => tuple.$1 == highlightedId.value!,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (index == -1) return;
|
if (index == -1) return;
|
||||||
|
|
@ -120,23 +120,23 @@ class const NotificationsPage({
|
||||||
),
|
),
|
||||||
reverse: true,
|
reverse: true,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final event = value[index];
|
final (rowId, roomId) = value[index];
|
||||||
final isHighlighted =
|
final isHighlighted =
|
||||||
event.eventId == highlightedId.value;
|
rowId == highlightedId.value;
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: .only(top: 8),
|
padding: .only(top: 8),
|
||||||
child: HighlightWrapper(
|
child: HighlightWrapper(
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await ref.focusRoom(event.roomId);
|
await ref.focusRoom(roomId);
|
||||||
ref
|
ref
|
||||||
.watch(
|
.watch(
|
||||||
ContextualEventController.provider(
|
ContextualEventController.provider(
|
||||||
event.roomId,
|
roomId,
|
||||||
).notifier,
|
).notifier,
|
||||||
)
|
)
|
||||||
.set(event);
|
.set(rowId);
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
@ -144,8 +144,9 @@ class const NotificationsPage({
|
||||||
},
|
},
|
||||||
child: IgnorePointer(
|
child: IgnorePointer(
|
||||||
child: EventRenderer(
|
child: EventRenderer(
|
||||||
event.rowId,
|
rowId,
|
||||||
roomId: event.roomId,
|
roomId: NotificationsController
|
||||||
|
.virtualRoomId,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
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/pinned_events.dart";
|
import "package:nexus/controllers/pinned_events.dart";
|
||||||
|
import "package:nexus/helpers/hooks/chat_scroll.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.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";
|
||||||
|
|
@ -10,7 +11,7 @@ import "package:nexus/widgets/renderers/event.dart";
|
||||||
final class const PinnedEventsDrawer(
|
final class const PinnedEventsDrawer(
|
||||||
final String roomId, {
|
final String roomId, {
|
||||||
required final IList<PopupMenuEntry> Function(Event event) getEventOptions,
|
required final IList<PopupMenuEntry> Function(Event event) getEventOptions,
|
||||||
required final Future<void> Function(Event event) jumpToEvent,
|
required final JumpToEvent jumpToEvent,
|
||||||
super.key,
|
super.key,
|
||||||
}) extends HookConsumerWidget {
|
}) extends HookConsumerWidget {
|
||||||
@override
|
@override
|
||||||
|
|
@ -62,7 +63,7 @@ final class const PinnedEventsDrawer(
|
||||||
borderRadius: .circular(12),
|
borderRadius: .circular(12),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
jumpToEvent(event);
|
jumpToEvent(event.rowId);
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: .symmetric(vertical: 4),
|
padding: .symmetric(vertical: 4),
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:nexus/controllers/event.dart";
|
import "package:nexus/controllers/event.dart";
|
||||||
import "package:nexus/controllers/rooms.dart";
|
import "package:nexus/controllers/rooms.dart";
|
||||||
import "package:nexus/helpers/extensions/show_context_menu.dart";
|
import "package:nexus/helpers/extensions/show_context_menu.dart";
|
||||||
|
import "package:nexus/helpers/hooks/chat_scroll.dart";
|
||||||
import "package:nexus/models/content/avatar.dart";
|
import "package:nexus/models/content/avatar.dart";
|
||||||
import "package:nexus/models/content/canonical_alias.dart";
|
import "package:nexus/models/content/canonical_alias.dart";
|
||||||
import "package:nexus/models/content/content.dart";
|
import "package:nexus/models/content/content.dart";
|
||||||
|
|
@ -35,7 +36,7 @@ class const EventRenderer(
|
||||||
final int? previousEventRowId,
|
final int? previousEventRowId,
|
||||||
final bool textOnly = false,
|
final bool textOnly = false,
|
||||||
final int? maxLines,
|
final int? maxLines,
|
||||||
final Future<void> Function(Event event)? jumpToEvent,
|
final JumpToEvent? jumpToEvent,
|
||||||
final IList<PopupMenuEntry> Function(Event event)? getEventOptions,
|
final IList<PopupMenuEntry> Function(Event event)? getEventOptions,
|
||||||
super.key,
|
super.key,
|
||||||
}) extends HookConsumerWidget {
|
}) extends HookConsumerWidget {
|
||||||
|
|
@ -106,7 +107,8 @@ class const EventRenderer(
|
||||||
.new(roomId: roomId, eventId: event.replyTo!),
|
.new(roomId: roomId, eventId: event.replyTo!),
|
||||||
).future,
|
).future,
|
||||||
);
|
);
|
||||||
if (replyEvent != null) await jumpToEvent!(replyEvent);
|
if (replyEvent != null)
|
||||||
|
await jumpToEvent!(replyEvent.rowId);
|
||||||
},
|
},
|
||||||
isGrouped: isGrouped,
|
isGrouped: isGrouped,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import "package:super_sliver_list/super_sliver_list.dart";
|
||||||
class const ChatTimeline({
|
class const ChatTimeline({
|
||||||
required final String roomId,
|
required final String roomId,
|
||||||
required final ChatScroll scroll,
|
required final ChatScroll scroll,
|
||||||
required final Future<void> Function(Event event) jumpToEvent,
|
required final JumpToEvent jumpToEvent,
|
||||||
required final IList<PopupMenuEntry> Function(Event) getEventOptions,
|
required final IList<PopupMenuEntry> Function(Event) getEventOptions,
|
||||||
required final int? highlightedEvent,
|
required final int? highlightedEvent,
|
||||||
required final double composerHeight,
|
required final double composerHeight,
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,14 @@ 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 Event? initialHighlightedEvent,
|
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<Event?>(null);
|
final relatedEvent = useState<Event?>(null);
|
||||||
final relationType = useState(RelationType.reply);
|
final relationType = useState(RelationType.reply);
|
||||||
final contextualEvent = useState<Event?>(initialHighlightedEvent);
|
final contextualEvent = useState<int?>(initialHighlight);
|
||||||
final highlightedEvent = useState<int?>(null);
|
final highlightedEvent = useState<int?>(null);
|
||||||
|
|
||||||
final composerSize = useState<double>(64);
|
final composerSize = useState<double>(64);
|
||||||
|
|
@ -63,12 +63,7 @@ final class const RoomChat({
|
||||||
|
|
||||||
final controllerProvider = RoomChatController.provider((
|
final controllerProvider = RoomChatController.provider((
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
contextualEvent: contextualEvent.value == null
|
contextualEvent: contextualEvent.value,
|
||||||
? null
|
|
||||||
: (
|
|
||||||
rowId: contextualEvent.value!.rowId,
|
|
||||||
eventId: contextualEvent.value!.eventId,
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
final notifier = ref.watch(controllerProvider.notifier);
|
final notifier = ref.watch(controllerProvider.notifier);
|
||||||
|
|
||||||
|
|
@ -102,25 +97,25 @@ final class const RoomChat({
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> jumpToEvent(Event event) async {
|
Future<void> jumpToEvent(int rowId) async {
|
||||||
highlightedEvent.value = event.rowId;
|
highlightedEvent.value = rowId;
|
||||||
|
|
||||||
await scroll.jumpToEvent(event);
|
await scroll.jumpToEvent(rowId);
|
||||||
await Future.delayed(.new(milliseconds: 700), () {
|
await Future.delayed(.new(milliseconds: 700), () {
|
||||||
if (highlightedEvent.value == event.rowId) {
|
if (highlightedEvent.value == rowId) {
|
||||||
highlightedEvent.value = null;
|
highlightedEvent.value = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
if (initialHighlightedEvent == null) return null;
|
if (initialHighlight == null) return null;
|
||||||
|
|
||||||
void check() {
|
void check() {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
|
|
||||||
if (scroll.scrollController.hasClients) {
|
if (scroll.scrollController.hasClients) {
|
||||||
jumpToEvent(initialHighlightedEvent!);
|
jumpToEvent(initialHighlight!);
|
||||||
} else {
|
} else {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => check());
|
WidgetsBinding.instance.addPostFrameCallback((_) => check());
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +124,7 @@ final class const RoomChat({
|
||||||
check();
|
check();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}, [initialHighlightedEvent]);
|
}, [initialHighlight]);
|
||||||
|
|
||||||
IList<PopupMenuEntry> getEventOptions(Event event) =>
|
IList<PopupMenuEntry> getEventOptions(Event event) =>
|
||||||
event.buildEventOptions(
|
event.buildEventOptions(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue