Remove flutter chat #26
10 changed files with 130 additions and 117 deletions
some performance improvements
commit
e00cd12bb9
|
|
@ -1,7 +1,7 @@
|
||||||
import "dart:ffi";
|
import "dart:ffi";
|
||||||
import "dart:io";
|
import "dart:io";
|
||||||
import "dart:isolate";
|
import "dart:isolate";
|
||||||
import "package:collection/collection.dart";
|
import "dart:math";
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:ffi/ffi.dart";
|
import "package:ffi/ffi.dart";
|
||||||
import "package:flutter/foundation.dart";
|
import "package:flutter/foundation.dart";
|
||||||
|
|
@ -237,9 +237,8 @@ class ClientController extends AsyncNotifier<int> {
|
||||||
_sendCommand("set_membership", request.toJson());
|
_sendCommand("set_membership", request.toJson());
|
||||||
|
|
||||||
Future<void> markRead(Room room) async {
|
Future<void> markRead(Room room) async {
|
||||||
final event = room.events.firstWhereOrNull(
|
final eventRowId = room.timeline[room.timeline.keys.reduce(max)];
|
||||||
(event) => event.rowId == room.timeline.last.eventRowId,
|
final event = eventRowId == null ? null : room.events[eventRowId];
|
||||||
);
|
|
||||||
if (event == null || room.metadata == null) return;
|
if (event == null || room.metadata == null) return;
|
||||||
|
|
||||||
await _sendCommand("mark_read", {
|
await _sendCommand("mark_read", {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ class EventController extends AsyncNotifier<Event?> {
|
||||||
final room = ref.watch(
|
final room = ref.watch(
|
||||||
RoomsController.provider.select((value) => value[request.roomId]),
|
RoomsController.provider.select((value) => value[request.roomId]),
|
||||||
);
|
);
|
||||||
final event = room?.events.firstWhereOrNull(
|
final event = room?.events.values.firstWhereOrNull(
|
||||||
(event) => event.eventId == request.eventId,
|
(event) => event.eventId == request.eventId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ import "package:nexus/models/configs/members_by_status_config.dart";
|
||||||
import "package:nexus/models/content/membership.dart";
|
import "package:nexus/models/content/membership.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
|
|
||||||
class MembersByStatusController extends AsyncNotifier<IList<Event>> {
|
class MembersByStatusController extends AsyncNotifier<ISet<Event>> {
|
||||||
final MembersByStatusConfig config;
|
final MembersByStatusConfig config;
|
||||||
MembersByStatusController(this.config);
|
MembersByStatusController(this.config);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<IList<Event>> build() => ref.watch(
|
Future<ISet<Event>> build() => ref.watch(
|
||||||
MembersController.provider(config.roomId).selectAsync(
|
MembersController.provider(config.roomId).selectAsync(
|
||||||
(members) => members
|
(members) => members
|
||||||
.where(
|
.where(
|
||||||
|
|
@ -19,14 +19,14 @@ class MembersByStatusController extends AsyncNotifier<IList<Event>> {
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.toIList(),
|
.toISet(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
static final provider =
|
static final provider =
|
||||||
AsyncNotifierProvider.family<
|
AsyncNotifierProvider.family<
|
||||||
MembersByStatusController,
|
MembersByStatusController,
|
||||||
IList<Event>,
|
ISet<Event>,
|
||||||
MembersByStatusConfig
|
MembersByStatusConfig
|
||||||
>(MembersByStatusController.new);
|
>(MembersByStatusController.new);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import "package:collection/collection.dart";
|
|
||||||
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_controller.dart";
|
import "package:nexus/controllers/client_controller.dart";
|
||||||
|
|
@ -7,17 +6,17 @@ import "package:nexus/models/content/content.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
import "package:nexus/models/requests/get_room_state_request.dart";
|
import "package:nexus/models/requests/get_room_state_request.dart";
|
||||||
|
|
||||||
class MembersController extends AsyncNotifier<IList<Event>> {
|
class MembersController extends AsyncNotifier<ISet<Event>> {
|
||||||
final String roomId;
|
final String roomId;
|
||||||
MembersController(this.roomId);
|
MembersController(this.roomId);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<IList<Event>> build() async {
|
Future<ISet<Event>> build() async {
|
||||||
final room = ref.watch(
|
final room = ref.watch(
|
||||||
RoomsController.provider.select((value) => value[roomId]),
|
RoomsController.provider.select((value) => value[roomId]),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (room == null) return const IList.empty();
|
if (room == null) return const ISet.empty();
|
||||||
|
|
||||||
if (!room.hasFetchedMembers) {
|
if (!room.hasFetchedMembers) {
|
||||||
final fetchedState = await ref
|
final fetchedState = await ref
|
||||||
|
|
@ -30,21 +29,18 @@ class MembersController extends AsyncNotifier<IList<Event>> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
ref
|
await ref
|
||||||
.read(RoomsController.provider.notifier)
|
.read(RoomsController.provider.notifier)
|
||||||
.addState(roomId, fetchedState, isMembers: true);
|
.addState(roomId, fetchedState, isMembers: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return room.state[EventType.membership.type]?.values
|
return room.state[EventType.membership.type]?.values
|
||||||
.map(
|
.map((rowId) => room.events[rowId])
|
||||||
(rowId) =>
|
|
||||||
room.events.firstWhereOrNull((event) => event.rowId == rowId),
|
|
||||||
)
|
|
||||||
.nonNulls
|
.nonNulls
|
||||||
.toIList() ??
|
.toISet() ??
|
||||||
const IList.empty();
|
const ISet.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
static final provider = AsyncNotifierProvider.autoDispose
|
static final provider = AsyncNotifierProvider.autoDispose
|
||||||
.family<MembersController, IList<Event>, String>(MembersController.new);
|
.family<MembersController, ISet<Event>, String>(MembersController.new);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import "package:collection/collection.dart";
|
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:nexus/controllers/client_state_controller.dart";
|
import "package:nexus/controllers/client_state_controller.dart";
|
||||||
import "package:nexus/controllers/rooms_controller.dart";
|
import "package:nexus/controllers/rooms_controller.dart";
|
||||||
|
|
@ -24,9 +23,9 @@ class PowerLevelController extends Notifier<bool> {
|
||||||
RoomsController.provider.select((value) => value[config.roomId]),
|
RoomsController.provider.select((value) => value[config.roomId]),
|
||||||
);
|
);
|
||||||
|
|
||||||
final event = room?.events.firstWhereOrNull(
|
final eventRowId = room?.state[EventType.powerLevels.type]?[""];
|
||||||
(event) => event.rowId == room.state[EventType.powerLevels.type]?[""],
|
|
||||||
);
|
final event = eventRowId == null ? null : room?.events[eventRowId];
|
||||||
final content = event?.content is PowerLevelsContent
|
final content = event?.content is PowerLevelsContent
|
||||||
? event!.content
|
? event!.content
|
||||||
: PowerLevelsContent();
|
: PowerLevelsContent();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import "dart:async";
|
import "dart:async";
|
||||||
|
import "dart:math";
|
||||||
import "package:collection/collection.dart";
|
import "package:collection/collection.dart";
|
||||||
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";
|
||||||
|
|
@ -33,7 +34,7 @@ class RoomChatController extends AsyncNotifier<IList<Event>> {
|
||||||
GetRoomStateRequest(roomId: roomId),
|
GetRoomStateRequest(roomId: roomId),
|
||||||
);
|
);
|
||||||
|
|
||||||
ref.read(RoomsController.provider.notifier).addState(roomId, state);
|
await ref.read(RoomsController.provider.notifier).addState(roomId, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// While there are under 30 messages, try up to load more messages until there's no more or we have 20 messages.
|
// While there are under 30 messages, try up to load more messages until there's no more or we have 20 messages.
|
||||||
|
|
@ -41,21 +42,21 @@ class RoomChatController extends AsyncNotifier<IList<Event>> {
|
||||||
loadOlder();
|
loadOlder();
|
||||||
}
|
}
|
||||||
|
|
||||||
return room.timeline.reversed
|
return room.timeline
|
||||||
.map((timeline) {
|
.toEntryIList(compare: (a, b) => (a?.key ?? 0).compareTo(b?.key ?? 0))
|
||||||
final foundEvent = room.events.firstWhereOrNull(
|
.map((entry) {
|
||||||
(event) => event.rowId == timeline.eventRowId,
|
if (entry.value == null) return null;
|
||||||
);
|
|
||||||
|
|
||||||
final editedEvent = foundEvent?.lastEditRowId == 0
|
final foundEvent = room.events[entry.value!];
|
||||||
|
|
||||||
|
final editedEvent =
|
||||||
|
foundEvent == null || foundEvent.lastEditRowId == 0
|
||||||
? null
|
? null
|
||||||
: room.events.firstWhereOrNull(
|
: room.events[foundEvent.lastEditRowId];
|
||||||
(event) => event.rowId == foundEvent?.lastEditRowId,
|
|
||||||
);
|
|
||||||
|
|
||||||
return foundEvent?.copyWith(
|
return editedEvent == null
|
||||||
content: editedEvent?.content ?? foundEvent.content,
|
? foundEvent
|
||||||
);
|
: foundEvent?.copyWith(content: editedEvent.content);
|
||||||
})
|
})
|
||||||
.nonNulls
|
.nonNulls
|
||||||
.toIList();
|
.toIList();
|
||||||
|
|
@ -72,34 +73,37 @@ class RoomChatController extends AsyncNotifier<IList<Event>> {
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<bool> loadOlder() async {
|
Future<bool> loadOlder() async {
|
||||||
|
final timelineKeys = ref
|
||||||
|
.read(RoomsController.provider.select((value) => value[roomId]))
|
||||||
|
?.timeline
|
||||||
|
.keys;
|
||||||
final response = await ref
|
final response = await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.watch(ClientController.provider.notifier)
|
||||||
.paginate(
|
.paginate(
|
||||||
PaginateRequest(
|
PaginateRequest(
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
maxTimelineId: ref
|
maxTimelineId: timelineKeys?.isNotEmpty == true
|
||||||
.read(RoomsController.provider.select((value) => value[roomId]))
|
? timelineKeys?.reduce(min)
|
||||||
?.timeline
|
: null,
|
||||||
.firstOrNull
|
|
||||||
?.timelineRowId,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
ref
|
await ref
|
||||||
.watch(RoomsController.provider.notifier)
|
.watch(RoomsController.provider.notifier)
|
||||||
.update(
|
.update(
|
||||||
IMap({
|
IMap({
|
||||||
roomId: Room(
|
roomId: Room(
|
||||||
events: response.events.addAll(response.relatedEvents),
|
events: IMap.fromIterable(
|
||||||
|
response.events.addAll(response.relatedEvents),
|
||||||
|
keyMapper: (event) => event.rowId,
|
||||||
|
valueMapper: (event) => event,
|
||||||
|
),
|
||||||
hasMore: response.hasMore,
|
hasMore: response.hasMore,
|
||||||
timeline: response.events
|
timeline: IMap.fromIterable(
|
||||||
.map(
|
response.events,
|
||||||
(event) => TimelineRowTuple(
|
keyMapper: (event) => event.timelineRowId,
|
||||||
timelineRowId: event.timelineRowId,
|
valueMapper: (event) => event.rowId,
|
||||||
eventRowId: event.rowId,
|
),
|
||||||
),
|
|
||||||
)
|
|
||||||
.toIList(),
|
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
const ISet.empty(),
|
const ISet.empty(),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import "package:collection/collection.dart";
|
|
||||||
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/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
|
|
@ -9,47 +8,49 @@ class RoomsController extends Notifier<IMap<String, Room>> {
|
||||||
@override
|
@override
|
||||||
IMap<String, Room> build() => const IMap.empty();
|
IMap<String, Room> build() => const IMap.empty();
|
||||||
|
|
||||||
void addState(String roomId, IList<Event> state, {bool isMembers = false}) =>
|
Future<void> addState(
|
||||||
update(
|
String roomId,
|
||||||
{
|
IList<Event> state, {
|
||||||
roomId: Room(
|
bool isMembers = false,
|
||||||
events: state,
|
}) => update(
|
||||||
hasFetchedState: true,
|
{
|
||||||
hasFetchedMembers: isMembers,
|
roomId: Room(
|
||||||
state: state.fold(
|
events: IMap.fromEntries(
|
||||||
const IMap.empty(),
|
state.map((event) => MapEntry(event.rowId, event)),
|
||||||
(previousValue, stateEvent) => previousValue.add(
|
),
|
||||||
stateEvent.type,
|
hasFetchedState: true,
|
||||||
(previousValue[stateEvent.type] ?? const IMap.empty()).addAll(
|
hasFetchedMembers: isMembers,
|
||||||
IMap({
|
state: state.fold(
|
||||||
if (stateEvent.stateKey != null)
|
const IMap.empty(),
|
||||||
stateEvent.stateKey!: stateEvent.rowId,
|
(previousValue, stateEvent) => previousValue.add(
|
||||||
}),
|
stateEvent.type,
|
||||||
),
|
(previousValue[stateEvent.type] ?? const IMap.empty()).addAll(
|
||||||
),
|
IMap({
|
||||||
|
if (stateEvent.stateKey != null)
|
||||||
|
stateEvent.stateKey!: stateEvent.rowId,
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
}.toIMap(),
|
),
|
||||||
const ISet.empty(),
|
),
|
||||||
);
|
}.toIMap(),
|
||||||
|
const ISet.empty(),
|
||||||
|
);
|
||||||
|
|
||||||
void update(IMap<String, Room> rooms, ISet<String> leftRooms) {
|
Future<void> update(IMap<String, Room> rooms, ISet<String> leftRooms) async {
|
||||||
final merged = rooms.entries.fold(state, (acc, entry) {
|
final merged = rooms.entries.fold(state, (acc, entry) {
|
||||||
final roomId = entry.key;
|
final roomId = entry.key;
|
||||||
final incoming = entry.value;
|
final incoming = entry.value;
|
||||||
final existing = acc[roomId];
|
final existing = acc[roomId];
|
||||||
|
|
||||||
final events = existing?.events.updateById(
|
|
||||||
incoming.events,
|
|
||||||
(item) => item.eventId,
|
|
||||||
);
|
|
||||||
|
|
||||||
return acc.add(
|
return acc.add(
|
||||||
roomId,
|
roomId,
|
||||||
existing?.copyWith(
|
existing?.copyWith(
|
||||||
hasMore: incoming.hasMore,
|
hasMore: incoming.hasMore,
|
||||||
metadata: incoming.metadata ?? existing.metadata,
|
metadata: incoming.metadata ?? existing.metadata,
|
||||||
events: events!,
|
events: incoming.events.isEmpty
|
||||||
|
? existing.events
|
||||||
|
: existing.events.addAll(incoming.events),
|
||||||
state: incoming.state.entries.fold(
|
state: incoming.state.entries.fold(
|
||||||
existing.state,
|
existing.state,
|
||||||
(previousValue, event) => previousValue.add(
|
(previousValue, event) => previousValue.add(
|
||||||
|
|
@ -64,15 +65,9 @@ class RoomsController extends Notifier<IMap<String, Room>> {
|
||||||
incoming.hasFetchedMembers || existing.hasFetchedMembers,
|
incoming.hasFetchedMembers || existing.hasFetchedMembers,
|
||||||
hasFetchedState:
|
hasFetchedState:
|
||||||
incoming.hasFetchedState || existing.hasFetchedState,
|
incoming.hasFetchedState || existing.hasFetchedState,
|
||||||
timeline:
|
timeline: (incoming.reset
|
||||||
(incoming.reset
|
? incoming.timeline
|
||||||
? incoming.timeline
|
: existing.timeline.addAll(incoming.timeline)),
|
||||||
: existing.timeline.updateById(
|
|
||||||
incoming.timeline,
|
|
||||||
(item) => item.timelineRowId,
|
|
||||||
))
|
|
||||||
.sortedBy((element) => element.timelineRowId)
|
|
||||||
.toIList(),
|
|
||||||
receipts: incoming.receipts.entries.fold(
|
receipts: incoming.receipts.entries.fold(
|
||||||
existing.receipts,
|
existing.receipts,
|
||||||
(receiptAcc, event) => receiptAcc.add(
|
(receiptAcc, event) => receiptAcc.add(
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,10 @@ class ViaController extends Notifier<String> {
|
||||||
|
|
||||||
addUserId(ref.watch(ClientStateController.provider)?.userId);
|
addUserId(ref.watch(ClientStateController.provider)?.userId);
|
||||||
|
|
||||||
final powerLevels = room.events.firstWhereOrNull(
|
final powerLevelsEventId = room.state[EventType.powerLevels.type]?[""];
|
||||||
(event) => event.rowId == room.state[EventType.powerLevels.type]?[""],
|
final powerLevels = powerLevelsEventId == null
|
||||||
);
|
? null
|
||||||
|
: room.events[powerLevelsEventId];
|
||||||
|
|
||||||
if (powerLevels?.content case PowerLevelsContent(:final users)) {
|
if (powerLevels?.content case PowerLevelsContent(:final users)) {
|
||||||
for (final userId in users.keys) {
|
for (final userId in users.keys) {
|
||||||
|
|
@ -38,9 +39,10 @@ class ViaController extends Notifier<String> {
|
||||||
|
|
||||||
final members = room.state[EventType.membership.type]?.values.toIList();
|
final members = room.state[EventType.membership.type]?.values.toIList();
|
||||||
for (var i = 0; servers.length < 5; i++) {
|
for (var i = 0; servers.length < 5; i++) {
|
||||||
final member = room.events.firstWhereOrNull(
|
final membershipEventId = members?.getOrNull(i);
|
||||||
(event) => event.rowId == members?.getOrNull(i),
|
final member = membershipEventId == null
|
||||||
);
|
? null
|
||||||
|
: room.events[membershipEventId];
|
||||||
|
|
||||||
if (member?.content case MembershipContent(:final status)) {
|
if (member?.content case MembershipContent(:final status)) {
|
||||||
if (status == MembershipStatus.join) {
|
if (status == MembershipStatus.join) {
|
||||||
|
|
|
||||||
|
|
@ -8,31 +8,48 @@ part "room.g.dart";
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
abstract class Room with _$Room {
|
abstract class Room with _$Room {
|
||||||
|
static IMap<int, int?> timelineTupleJsonToIMap(List<dynamic> json) =>
|
||||||
|
IMap.fromEntries(
|
||||||
|
json.map(
|
||||||
|
(timelineTuple) => MapEntry(
|
||||||
|
timelineTuple["timeline_rowid"],
|
||||||
|
timelineTuple["event_rowid"],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static IMap<int, Event> eventsJsonToIMap(List<dynamic> json) =>
|
||||||
|
IMap.fromEntries(
|
||||||
|
json.map((eventJson) {
|
||||||
|
final event = Event.fromJson(eventJson);
|
||||||
|
return MapEntry(event.rowId, event);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// [timeline] is an IMap of timelineRowId to eventRowId
|
||||||
|
/// [events] is an IMap of eventRowId to event
|
||||||
const factory Room({
|
const factory Room({
|
||||||
@JsonKey(name: "meta") RoomMetadata? metadata,
|
@JsonKey(name: "meta") RoomMetadata? metadata,
|
||||||
@Default(IList.empty()) IList<TimelineRowTuple> timeline,
|
@Default(IMap.empty())
|
||||||
|
@JsonKey(fromJson: Room.timelineTupleJsonToIMap)
|
||||||
|
IMap<int, int?> timeline,
|
||||||
|
|
||||||
|
@Default(IMap.empty())
|
||||||
|
@JsonKey(fromJson: Room.eventsJsonToIMap)
|
||||||
|
IMap<int, Event> events,
|
||||||
|
|
||||||
@Default(false) bool reset,
|
@Default(false) bool reset,
|
||||||
@Default(false) bool hasFetchedState,
|
@Default(false) bool hasFetchedState,
|
||||||
@Default(false) bool hasFetchedMembers,
|
@Default(false) bool hasFetchedMembers,
|
||||||
@Default(IMap.empty()) IMap<String, IMap<String, int>> state,
|
@Default(IMap.empty()) IMap<String, IMap<String, int>> state,
|
||||||
// required IMap<String, AccountData> accountData,
|
|
||||||
@Default(IList.empty()) IList<Event> events,
|
|
||||||
@Default(IMap.empty()) IMap<String, IList<ReadReceipt>> receipts,
|
@Default(IMap.empty()) IMap<String, IList<ReadReceipt>> receipts,
|
||||||
@Default(false) bool dismissNotifications,
|
@Default(false) bool dismissNotifications,
|
||||||
@Default(true) bool hasMore,
|
@Default(true) bool hasMore,
|
||||||
|
|
||||||
|
// required IMap<String, AccountData> accountData,
|
||||||
// required IList<Notification> notifications,
|
// required IList<Notification> notifications,
|
||||||
}) = _Room;
|
}) = _Room;
|
||||||
|
|
||||||
factory Room.fromJson(Map<String, Object?> json) => _$RoomFromJson(json);
|
factory Room.fromJson(Map<String, Object?> json) => _$RoomFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
|
||||||
abstract class TimelineRowTuple with _$TimelineRowTuple {
|
|
||||||
const factory TimelineRowTuple({
|
|
||||||
@JsonKey(name: "timeline_rowid") required int timelineRowId,
|
|
||||||
@JsonKey(name: "event_rowid") int? eventRowId,
|
|
||||||
}) = _TimelineRowTuple;
|
|
||||||
|
|
||||||
factory TimelineRowTuple.fromJson(Map<String, Object?> json) =>
|
|
||||||
_$TimelineRowTupleFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -81,14 +81,15 @@ class RoomChat extends HookConsumerWidget {
|
||||||
Future<void> listener() async {
|
Future<void> listener() async {
|
||||||
if (!scrollController.position.atEdge) return;
|
if (!scrollController.position.atEdge) return;
|
||||||
|
|
||||||
|
final room = ref.watch(
|
||||||
|
RoomsController.provider.select((value) => value[roomId]),
|
||||||
|
);
|
||||||
|
if (room == null) return;
|
||||||
|
|
||||||
if (scrollController.position.pixels == 0) {
|
if (scrollController.position.pixels == 0) {
|
||||||
context.mounted;
|
await client.markRead(room);
|
||||||
final room = ref.watch(
|
|
||||||
RoomsController.provider.select((value) => value[roomId]),
|
|
||||||
);
|
|
||||||
if (room != null) client.markRead(room);
|
|
||||||
} else {
|
} else {
|
||||||
await notifier.loadOlder();
|
if (room.hasMore) await notifier.loadOlder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue