refactor: Return a better default for lastEventReceivedTime

This commit is contained in:
Christian Kußowski 2025-09-12 16:01:05 +02:00
parent 8a4eda5201
commit bc8164a487
No known key found for this signature in database
GPG Key ID: E067ECD60F1A0652
7 changed files with 71 additions and 23 deletions

View File

@ -61,6 +61,10 @@ abstract class EventTypes {
'org.matrix.call.asserted_identity'; 'org.matrix.call.asserted_identity';
static const String Unknown = 'm.unknown'; static const String Unknown = 'm.unknown';
/// An internal event type indicating that the last event in the room for
/// a room list preview is currently being refreshed.
static const String refreshingLastEvent = 'com.famedly.refreshing_last_event';
// To device event types // To device event types
static const String RoomKey = 'm.room_key'; static const String RoomKey = 'm.room_key';
static const String ForwardedRoomKey = 'm.forwarded_room_key'; static const String ForwardedRoomKey = 'm.forwarded_room_key';

View File

@ -2740,13 +2740,25 @@ class Client extends MatrixApi {
Logs().d('Skip store LeftRoomUpdate for unknown room', id); Logs().d('Skip store LeftRoomUpdate for unknown room', id);
continue; continue;
} }
await database.storeRoomUpdate(id, syncRoomUpdate, room.lastEvent, this);
if (syncRoomUpdate is JoinedRoomUpdate && if (syncRoomUpdate is JoinedRoomUpdate &&
syncRoomUpdate.timeline?.limited == true && (room.lastEvent?.type == EventTypes.refreshingLastEvent ||
room.lastEvent == null) { (syncRoomUpdate.timeline?.limited == true &&
room.lastEvent == null))) {
room.lastEvent = Event(
originServerTs:
syncRoomUpdate.timeline?.events?.firstOrNull?.originServerTs ??
DateTime.now(),
type: EventTypes.refreshingLastEvent,
content: {'body': 'Refreshing last event...'},
room: room,
eventId: generateUniqueTransactionId(),
senderId: userID!,
);
runInRoot(room.refreshLastEvent); runInRoot(room.refreshLastEvent);
} }
await database.storeRoomUpdate(id, syncRoomUpdate, room.lastEvent, this);
} }
} }

View File

@ -379,19 +379,37 @@ class Room {
/// Fetches the most recent event in the timeline from the server to have /// Fetches the most recent event in the timeline from the server to have
/// a valid preview after receiving a limited timeline from the sync. Will /// a valid preview after receiving a limited timeline from the sync. Will
/// be triggered by the sync loop on demand. /// be triggered by the sync loop on demand. Multiple requests will be
Future<Event?> refreshLastEvent() async { /// combined to the same request.
Future<Event?> refreshLastEvent({
timeout = const Duration(seconds: 30),
}) async {
final lastEvent = _refreshingLastEvent ??= _refreshLastEvent();
_refreshingLastEvent = null;
return lastEvent;
}
Future<Event?>? _refreshingLastEvent;
Future<Event?> _refreshLastEvent({
timeout = const Duration(seconds: 30),
}) async {
if (membership != Membership.join) return null; if (membership != Membership.join) return null;
final filter = StateFilter(types: client.roomPreviewLastEvents.toList()); final filter = StateFilter(types: client.roomPreviewLastEvents.toList());
final result = await client.getRoomEvents( final result = await client
id, .getRoomEvents(
Direction.b, id,
limit: 1, Direction.b,
filter: jsonEncode(filter.toJson()), limit: 1,
); filter: jsonEncode(filter.toJson()),
)
.timeout(timeout);
final matrixEvent = result.chunk.firstOrNull; final matrixEvent = result.chunk.firstOrNull;
if (matrixEvent == null) { if (matrixEvent == null) {
if (lastEvent?.type == EventTypes.refreshingLastEvent) {
lastEvent = null;
}
Logs().d('No last event found for room', id); Logs().d('No last event found for room', id);
return null; return null;
} }
@ -492,8 +510,16 @@ class Room {
String get displayname => getLocalizedDisplayname(); String get displayname => getLocalizedDisplayname();
/// When was the last event received. /// When was the last event received.
DateTime get latestEventReceivedTime => DateTime get latestEventReceivedTime {
lastEvent?.originServerTs ?? DateTime.now(); final lastEventTime = lastEvent?.originServerTs;
if (lastEventTime != null) return lastEventTime;
if (membership == Membership.invite) return DateTime.now();
final createEvent = getState(EventTypes.RoomCreate);
if (createEvent is MatrixEvent) return createEvent.originServerTs;
return DateTime(0);
}
/// Call the Matrix API to change the name of this room. Returns the event ID of the /// Call the Matrix API to change the name of this room. Returns the event ID of the
/// new m.room.name event. /// new m.room.name event.

View File

@ -299,5 +299,6 @@ abstract class EventLocalizations {
?.tryGet<String>('key') ?? ?.tryGet<String>('key') ??
body, body,
), ),
EventTypes.refreshingLastEvent: (_, i18n, ___) => i18n.refreshingLastEvent,
}; };
} }

View File

@ -318,4 +318,7 @@ class MatrixDefaultLocalizations extends MatrixLocalizations {
: '${duration.inMinutes.toString().padLeft(2, '0')}:${(duration.inSeconds % 60).toString().padLeft(2, '0')} '; : '${duration.inMinutes.toString().padLeft(2, '0')}:${(duration.inSeconds % 60).toString().padLeft(2, '0')} ';
return '$senderName: ${durationString}Voice message'; return '$senderName: ${durationString}Voice message';
} }
@override
String get refreshingLastEvent => 'Refreshing last event...';
} }

View File

@ -62,6 +62,8 @@ abstract class MatrixLocalizations {
String get cancelledSend; String get cancelledSend;
String get refreshingLastEvent;
String youInvitedBy(String senderName); String youInvitedBy(String senderName);
String invitedBy(String senderName); String invitedBy(String senderName);

View File

@ -201,26 +201,26 @@ void main() {
matrix.getDirectChatFromUserId('@bob:example.com'), matrix.getDirectChatFromUserId('@bob:example.com'),
'!726s6s6q:example.com', '!726s6s6q:example.com',
); );
expect(matrix.rooms[2].directChatMatrixID, '@bob:example.com'); expect(matrix.rooms[1].directChatMatrixID, '@bob:example.com');
expect(matrix.directChats, matrix.accountData['m.direct']?.content); expect(matrix.directChats, matrix.accountData['m.direct']?.content);
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
expect(matrix.presences.length, 1); expect(matrix.presences.length, 1);
expect(matrix.rooms[2].ephemerals.length, 2); expect(matrix.rooms[1].ephemerals.length, 2);
expect(matrix.rooms[2].typingUsers.length, 1); expect(matrix.rooms[1].typingUsers.length, 1);
expect(matrix.rooms[2].typingUsers[0].id, '@alice:example.com'); expect(matrix.rooms[1].typingUsers[0].id, '@alice:example.com');
expect(matrix.rooms[2].roomAccountData.length, 3); expect(matrix.rooms[1].roomAccountData.length, 3);
expect(matrix.rooms[2].encrypted, true); expect(matrix.rooms[1].encrypted, true);
expect( expect(
matrix.rooms[2].encryptionAlgorithm, matrix.rooms[1].encryptionAlgorithm,
Client.supportedGroupEncryptionAlgorithms.first, Client.supportedGroupEncryptionAlgorithms.first,
); );
expect( expect(
matrix matrix
.rooms[2].receiptState.global.otherUsers['@alice:example.com']?.ts, .rooms[1].receiptState.global.otherUsers['@alice:example.com']?.ts,
1436451550453, 1436451550453,
); );
expect( expect(
matrix.rooms[2].receiptState.global.otherUsers['@alice:example.com'] matrix.rooms[1].receiptState.global.otherUsers['@alice:example.com']
?.eventId, ?.eventId,
'\$7365636s6r6432:example.com', '\$7365636s6r6432:example.com',
); );
@ -231,7 +231,7 @@ void main() {
expect(inviteRoom.states[EventTypes.RoomMember]?.length, 1); expect(inviteRoom.states[EventTypes.RoomMember]?.length, 1);
expect(matrix.rooms.length, 3); expect(matrix.rooms.length, 3);
expect( expect(
matrix.rooms[2].canonicalAlias, matrix.rooms[1].canonicalAlias,
"#famedlyContactDiscovery:${matrix.userID!.split(":")[1]}", "#famedlyContactDiscovery:${matrix.userID!.split(":")[1]}",
); );
expect( expect(