Compare commits

..
6 changed files with 54 additions and 71 deletions

View file

@ -17,13 +17,13 @@ import "package:nexus/models/requests/send_message.dart";
import "package:nexus/models/room.dart";
import "package:nexus/models/room_chat.dart";
class RoomChatController(final (String roomId, String? contextualEvent) info)
class RoomChatController(final (String roomId, String? withEventId) info)
extends AsyncNotifier<RoomChat?> {
@override
Future<RoomChat?> build() async {
final (roomId, eventId) = info;
final client = ref.read(ClientController.provider.notifier);
final room = ref.read(
final room = ref.watch(
RoomsController.provider.select((rooms) => rooms[roomId]),
);
@ -34,43 +34,31 @@ class RoomChatController(final (String roomId, String? contextualEvent) info)
await ref.read(RoomsController.provider.notifier).addState(roomId, state);
}
final timeline = room.timeline
.toEntryIList(compare: (a, b) => (a?.key ?? 0).compareTo(b?.key ?? 0))
.map((element) => element.value)
.toIList()
.addAll(room.clientSticky)
.map((entry) {
final foundEvent = entry == null ? null : room.events[entry];
return .new(
events: room.timeline
.toEntryIList(compare: (a, b) => (a?.key ?? 0).compareTo(b?.key ?? 0))
.map((element) => element.value)
.toIList()
.addAll(room.clientSticky)
.map((entry) {
final foundEvent = entry == null ? null : room.events[entry];
final editedEvent =
foundEvent == null || foundEvent.lastEditRowId == 0
? null
: room.events[foundEvent.lastEditRowId];
final editedEvent =
foundEvent == null || foundEvent.lastEditRowId == 0
? null
: room.events[foundEvent.lastEditRowId];
return editedEvent == null
? foundEvent
: foundEvent?.copyWith(
content: editedEvent.content,
localContent: editedEvent.localContent,
);
})
.nonNulls
.toIList();
if (info.$2 == null || timeline.map((e) => e.eventId).contains(info.$2)) {
ref.watch(RoomsController.provider.select((rooms) => rooms[roomId]));
return .new(timeline: timeline, hasMore: room.hasMore);
} else {
final context = await client.getEventContext(
.new(roomId: roomId, eventId: info.$2!, limit: 20),
);
return .new(
timeline: context.before.add(context.event).addAll(context.after),
hasMore: true,
historicalData: .new(start: context.start, end: context.end),
);
}
return editedEvent == null
? foundEvent
: foundEvent?.copyWith(
content: editedEvent.content,
localContent: editedEvent.localContent,
);
})
.nonNulls
.toIList(),
hasMore: room.hasMore,
);
}
Future<void> deleteMessage(Event event, {String? reason}) => ref

View file

@ -32,8 +32,8 @@ final class ChatScroll({
useEffect(() {
if (anchorId.value == null) {
if (controllerData case AsyncData(:final value?)
when value.timeline.isNotEmpty) {
anchorId.value = value.timeline.last.eventId;
when value.events.isNotEmpty) {
anchorId.value = value.events.last.eventId;
}
}
@ -41,7 +41,7 @@ final class ChatScroll({
}, [controllerData]);
final ({IList<Event> history, IList<Event> live}) split = useMemoized(() {
final items = controllerData.value?.timeline;
final items = controllerData.value?.events;
final anchor = anchorIdValue;
if (items == null || anchor == null) {
@ -92,14 +92,19 @@ final class ChatScroll({
(item) => item.eventId == itemId,
);
double? offset;
if (historyIndex != -1) {
// TODO: Replace SuperSliverView because of the bug that requires this: #94
// ignore: invalid_use_of_visible_for_testing_member
offset = historyListController.value.getOffsetToReveal(
final offset = historyListController.value.getOffsetToReveal(
historyIndex,
0.5,
);
await scrollController.animateTo(
offset,
duration: const Duration(milliseconds: 700),
curve: Curves.easeInOut,
);
} else {
final liveIndex = split.live.indexWhere(
(item) => item.eventId == itemId,
@ -107,21 +112,18 @@ final class ChatScroll({
if (liveIndex != -1) {
// ignore: invalid_use_of_visible_for_testing_member
offset = liveListController.value.getOffsetToReveal(liveIndex, 0.5);
final offset = liveListController.value.getOffsetToReveal(
liveIndex,
0.5,
);
await scrollController.animateTo(
offset,
duration: const Duration(milliseconds: 700),
curve: Curves.easeInOut,
);
}
}
if (offset == null) {
// not in current timeline
}
if (offset != null) {
await scrollController.animateTo(
offset,
duration: const Duration(milliseconds: 700),
curve: Curves.easeInOut,
);
}
}
return .new(

View file

@ -8,7 +8,7 @@ part "event_context.g.dart";
@Freezed(toJson: false, fromJson: false)
@JsonSerializable()
class const EventContext({
required final Event event,
required final IList<Event> events,
required final IList<Event> before,
required final IList<Event> after,
required final String start,

View file

@ -12,26 +12,17 @@ part "room.g.dart";
class const Room({
@JsonKey(name: "meta") final RoomMetadata? metadata,
/// [timeline] is an IMap of timelineRowId to eventRowId
@JsonKey(fromJson: Room.timelineTupleJsonToIMap)
final IMap<int, int?> timeline = const IMap.empty(),
/// [clientSticky] is an ISet of eventRowId
@JsonKey(includeFromJson: false, includeToJson: false)
final ISet<int> clientSticky = const ISet.empty(),
/// [events] is an IMap of eventRowId to event
@JsonKey(fromJson: Room.eventsJsonToIMap)
final IMap<int, Event> events = const IMap.empty(),
final bool reset = false,
@JsonKey(includeFromJson: false, includeToJson: false)
final bool hasFetchedState = false,
@JsonKey(includeFromJson: false, includeToJson: false)
final bool hasFetchedMembers = false,
final IMap<String, IMap<String, int>> state = const IMap.empty(),
final IMap<String, IList<ReadReceipt>> receipts = const IMap.empty(),
@ -41,6 +32,9 @@ class const Room({
// IMap<String, AccountData> accountData,
// IList<Notification> notifications,
}) with _$Room {
/// [timeline] is an IMap of timelineRowId to eventRowId
/// [events] is an IMap of eventRowId to event
/// [clientSticky] is an ISet of eventRowId
static IMap<int, int?> timelineTupleJsonToIMap(List<dynamic> json) =>
IMap.fromEntries(
json.map(

View file

@ -8,7 +8,7 @@ part "room_chat.g.dart";
@Freezed(toJson: false, fromJson: false)
@JsonSerializable()
class const RoomChat({
required final IList<Event> timeline,
required final IList<Event> events,
required final bool hasMore,
final HistoricalData? historicalData,
}) with _$RoomChat {
@ -21,8 +21,8 @@ class const RoomChat({
@Freezed(toJson: false, fromJson: false)
@JsonSerializable()
class const HistoricalData({
required final String start,
required final String end,
required final String start,
}) with _$HistoricalData {
Map<String, Object?> toJson() => _$HistoricalDataToJson(this);

View file

@ -31,7 +31,6 @@ final class const RoomChat({
Widget build(BuildContext context, WidgetRef ref) {
final relatedEvent = useState<Event?>(null);
final relationType = useState(RelationType.reply);
final contextualEvent = useState<String?>(initialHighlightedEvent);
final highlightedEvent = useState<String?>(initialHighlightedEvent);
final composerSize = useState<double>(64);
@ -63,7 +62,7 @@ final class const RoomChat({
final controllerProvider = RoomChatController.provider((
roomId,
contextualEvent.value,
initialHighlightedEvent,
));
final notifier = ref.watch(controllerProvider.notifier);