feat: keep timeline history for archive rooms in memory
This commit is contained in:
parent
f04d8a9f40
commit
2a019eaec3
|
|
@ -32,6 +32,7 @@ import 'package:matrix/src/utils/run_in_root.dart';
|
|||
import 'package:matrix/src/utils/sync_update_item_count.dart';
|
||||
import '../encryption.dart';
|
||||
import '../matrix.dart';
|
||||
import 'models/timeline_chunk.dart';
|
||||
import 'utils/multilock.dart';
|
||||
import 'utils/run_benchmarked.dart';
|
||||
|
||||
|
|
@ -287,7 +288,7 @@ class Client extends MatrixApi {
|
|||
/// found. If you have loaded the [loadArchive()] before, it can also return
|
||||
/// archived rooms.
|
||||
Room? getRoomById(String id) {
|
||||
for (final room in <Room>[...rooms, ..._archivedRooms]) {
|
||||
for (final room in <Room>[...rooms, ..._archivedRooms.map((e) => e.room)]) {
|
||||
if (room.id == id) return room;
|
||||
}
|
||||
|
||||
|
|
@ -781,9 +782,35 @@ class Client extends MatrixApi {
|
|||
avatarUrl: profile.avatarUrl);
|
||||
}
|
||||
|
||||
final List<Room> _archivedRooms = [];
|
||||
final List<ArchivedRoom> _archivedRooms = [];
|
||||
|
||||
/// Return an archive room containing the room and the timeline for a specific archived room.
|
||||
ArchivedRoom? getArchiveRoomFromCache(String roomId) {
|
||||
for (var i = 0; i < _archivedRooms.length; i++) {
|
||||
final archive = _archivedRooms[i];
|
||||
if (archive.room.id == roomId) return archive;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Remove all the archives stored in cache.
|
||||
void clearArchivesFromCache() {
|
||||
_archivedRooms.clear();
|
||||
}
|
||||
|
||||
@Deprecated('Use [loadArchive()] instead.')
|
||||
Future<List<Room>> get archive => loadArchive();
|
||||
|
||||
/// Fetch all the archived rooms from the server and return the list of the
|
||||
/// room. If you want to have the Timelines bundled with it, use
|
||||
/// loadArchiveWithTimeline instead.
|
||||
Future<List<Room>> loadArchive() async {
|
||||
return (await loadArchiveWithTimeline()).map((e) => e.room).toList();
|
||||
}
|
||||
|
||||
/// Fetch the archived rooms from the server and return them as a list of
|
||||
/// [ArchivedRoom] objects containing the [Room] and the associated [Timeline].
|
||||
Future<List<ArchivedRoom>> loadArchiveWithTimeline() async {
|
||||
_archivedRooms.clear();
|
||||
final syncResp = await sync(
|
||||
filter: '{"room":{"include_leave":true,"timeline":{"limit":10}}}',
|
||||
|
|
@ -804,12 +831,32 @@ class Client extends MatrixApi {
|
|||
<String, BasicRoomEvent>{},
|
||||
);
|
||||
|
||||
final timeline = Timeline(
|
||||
room: leftRoom,
|
||||
chunk: TimelineChunk(
|
||||
events: room.timeline?.events?.reversed
|
||||
.toList() // we display the event in the other sence
|
||||
.map((e) => Event.fromMatrixEvent(e, leftRoom))
|
||||
.toList() ??
|
||||
[]));
|
||||
|
||||
for (var i = 0; i < timeline.events.length; i++) {
|
||||
// Try to decrypt encrypted events but don't update the database.
|
||||
if (leftRoom.encrypted && leftRoom.client.encryptionEnabled) {
|
||||
if (timeline.events[i].type == EventTypes.Encrypted) {
|
||||
timeline.events[i] = await leftRoom.client.encryption!
|
||||
.decryptRoomEvent(leftRoom.id, timeline.events[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
room.timeline?.events?.forEach((event) {
|
||||
leftRoom.setState(Event.fromMatrixEvent(
|
||||
event,
|
||||
leftRoom,
|
||||
));
|
||||
});
|
||||
|
||||
leftRoom.prev_batch = room.timeline?.prevBatch;
|
||||
room.state?.forEach((event) {
|
||||
leftRoom.setState(Event.fromMatrixEvent(
|
||||
|
|
@ -817,7 +864,8 @@ class Client extends MatrixApi {
|
|||
leftRoom,
|
||||
));
|
||||
});
|
||||
_archivedRooms.add(leftRoom);
|
||||
|
||||
_archivedRooms.add(ArchivedRoom(room: leftRoom, timeline: timeline));
|
||||
}
|
||||
}
|
||||
return _archivedRooms;
|
||||
|
|
@ -1658,6 +1706,12 @@ class Client extends MatrixApi {
|
|||
await database?.storeRoomUpdate(id, syncRoomUpdate, this);
|
||||
final room = _updateRoomsByRoomUpdate(id, syncRoomUpdate);
|
||||
|
||||
final timelineUpdateType = direction != null
|
||||
? (direction == Direction.b
|
||||
? EventUpdateType.history
|
||||
: EventUpdateType.timeline)
|
||||
: EventUpdateType.timeline;
|
||||
|
||||
/// Handle now all room events and save them in the database
|
||||
if (syncRoomUpdate is JoinedRoomUpdate) {
|
||||
final state = syncRoomUpdate.state;
|
||||
|
|
@ -1673,14 +1727,7 @@ class Client extends MatrixApi {
|
|||
|
||||
final timelineEvents = syncRoomUpdate.timeline?.events;
|
||||
if (timelineEvents != null && timelineEvents.isNotEmpty) {
|
||||
await _handleRoomEvents(
|
||||
room,
|
||||
timelineEvents,
|
||||
direction != null
|
||||
? (direction == Direction.b
|
||||
? EventUpdateType.history
|
||||
: EventUpdateType.timeline)
|
||||
: EventUpdateType.timeline);
|
||||
await _handleRoomEvents(room, timelineEvents, timelineUpdateType);
|
||||
}
|
||||
|
||||
final ephemeral = syncRoomUpdate.ephemeral;
|
||||
|
|
@ -1705,23 +1752,19 @@ class Client extends MatrixApi {
|
|||
if (syncRoomUpdate is LeftRoomUpdate) {
|
||||
final timelineEvents = syncRoomUpdate.timeline?.events;
|
||||
if (timelineEvents != null && timelineEvents.isNotEmpty) {
|
||||
await _handleRoomEvents(
|
||||
room,
|
||||
timelineEvents,
|
||||
EventUpdateType.timeline,
|
||||
);
|
||||
await _handleRoomEvents(room, timelineEvents, timelineUpdateType,
|
||||
store: false);
|
||||
}
|
||||
final accountData = syncRoomUpdate.accountData;
|
||||
if (accountData != null && accountData.isNotEmpty) {
|
||||
await _handleRoomEvents(
|
||||
room,
|
||||
accountData,
|
||||
EventUpdateType.accountData,
|
||||
);
|
||||
room, accountData, EventUpdateType.accountData,
|
||||
store: false);
|
||||
}
|
||||
final state = syncRoomUpdate.state;
|
||||
if (state != null && state.isNotEmpty) {
|
||||
await _handleRoomEvents(room, state, EventUpdateType.state);
|
||||
await _handleRoomEvents(room, state, EventUpdateType.state,
|
||||
store: false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1795,10 +1838,8 @@ class Client extends MatrixApi {
|
|||
}
|
||||
|
||||
Future<void> _handleRoomEvents(
|
||||
Room room,
|
||||
List<BasicEvent> events,
|
||||
EventUpdateType type,
|
||||
) async {
|
||||
Room room, List<BasicEvent> events, EventUpdateType type,
|
||||
{bool store = true}) async {
|
||||
// Calling events can be omitted if they are outdated from the same sync. So
|
||||
// we collect them first before we handle them.
|
||||
final callEvents = <Event>{};
|
||||
|
|
@ -1833,7 +1874,7 @@ class Client extends MatrixApi {
|
|||
}
|
||||
}
|
||||
_updateRoomsByEventUpdate(room, update);
|
||||
if (type != EventUpdateType.ephemeral) {
|
||||
if (type != EventUpdateType.ephemeral && store) {
|
||||
await database?.storeEventUpdate(update, this);
|
||||
}
|
||||
if (encryptionEnabled) {
|
||||
|
|
@ -1941,6 +1982,10 @@ class Client extends MatrixApi {
|
|||
|
||||
// Does the chat already exist in the list rooms?
|
||||
if (!found && membership != Membership.leave) {
|
||||
// Check if the room is not in the rooms in the invited list
|
||||
if (_archivedRooms.isNotEmpty) {
|
||||
_archivedRooms.removeWhere((archive) => archive.room.id == roomId);
|
||||
}
|
||||
final position = membership == Membership.invite ? 0 : rooms.length;
|
||||
// Add the new chat to the list
|
||||
rooms.insert(position, room);
|
||||
|
|
@ -2857,3 +2902,9 @@ class HomeserverSummary {
|
|||
required this.loginFlows,
|
||||
});
|
||||
}
|
||||
|
||||
class ArchivedRoom {
|
||||
final Room room;
|
||||
final Timeline timeline;
|
||||
ArchivedRoom({required this.room, required this.timeline});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1136,6 +1136,9 @@ class Room {
|
|||
void Function()? onHistoryReceived,
|
||||
direction = Direction.b}) async {
|
||||
final prev_batch = this.prev_batch;
|
||||
|
||||
final storeInDatabase = !isArchived;
|
||||
|
||||
if (prev_batch == null) {
|
||||
throw 'Tried to request history without a prev_batch token';
|
||||
}
|
||||
|
|
@ -1179,7 +1182,9 @@ class Room {
|
|||
state: resp.state,
|
||||
timeline: TimelineUpdate(
|
||||
limited: false,
|
||||
events: resp.chunk,
|
||||
events: direction == Direction.b
|
||||
? resp.chunk
|
||||
: resp.chunk.reversed.toList(),
|
||||
prevBatch: direction == Direction.b
|
||||
? resp.end
|
||||
: resp.start,
|
||||
|
|
@ -1193,7 +1198,9 @@ class Room {
|
|||
|
||||
if (client.database != null) {
|
||||
await client.database?.transaction(() async {
|
||||
await client.database?.setRoomPrevBatch(resp.end!, id, client);
|
||||
if (storeInDatabase) {
|
||||
await client.database?.setRoomPrevBatch(resp.end!, id, client);
|
||||
}
|
||||
await loadFn();
|
||||
});
|
||||
} else {
|
||||
|
|
@ -1304,6 +1311,9 @@ class Room {
|
|||
return;
|
||||
}
|
||||
|
||||
/// Is the room archived
|
||||
bool get isArchived => membership == Membership.leave;
|
||||
|
||||
/// Creates a timeline from the store. Returns a [Timeline] object. If you
|
||||
/// just want to update the whole timeline on every change, use the [onUpdate]
|
||||
/// callback. For updating only the parts that have changed, use the
|
||||
|
|
@ -1319,35 +1329,41 @@ class Room {
|
|||
String? eventContextId}) async {
|
||||
await postLoad();
|
||||
|
||||
final _events = await client.database?.getEventList(
|
||||
this,
|
||||
limit: defaultHistoryCount,
|
||||
);
|
||||
var events;
|
||||
|
||||
var chunk = TimelineChunk(events: _events ?? []);
|
||||
if (!isArchived) {
|
||||
events = await client.database?.getEventList(
|
||||
this,
|
||||
limit: defaultHistoryCount,
|
||||
) ??
|
||||
<Event>[];
|
||||
} else {
|
||||
final archive = client.getArchiveRoomFromCache(id);
|
||||
events = archive?.timeline.events.toList() ?? [];
|
||||
}
|
||||
|
||||
if (_events != null) {
|
||||
if (eventContextId != null) {
|
||||
if (_events
|
||||
.firstWhereOrNull((event) => event.eventId == eventContextId) !=
|
||||
null) {
|
||||
chunk = TimelineChunk(events: _events);
|
||||
} else {
|
||||
chunk = await getEventContext(eventContextId) ??
|
||||
TimelineChunk(events: []);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all users from database we have got here.
|
||||
if (eventContextId == null) {
|
||||
for (final event in _events) {
|
||||
if (getState(EventTypes.RoomMember, event.senderId) != null) continue;
|
||||
final dbUser = await client.database?.getUser(event.senderId, this);
|
||||
if (dbUser != null) setState(dbUser);
|
||||
}
|
||||
var chunk = TimelineChunk(events: events);
|
||||
// Load the timeline arround eventContextId if set
|
||||
if (eventContextId != null) {
|
||||
if (events.firstWhereOrNull((event) => event.eventId == eventContextId) !=
|
||||
null) {
|
||||
chunk = TimelineChunk(events: events);
|
||||
} else {
|
||||
chunk =
|
||||
await getEventContext(eventContextId) ?? TimelineChunk(events: []);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all users from database we have got here.
|
||||
if (eventContextId == null) {
|
||||
for (final event in events) {
|
||||
if (getState(EventTypes.RoomMember, event.senderId) != null) continue;
|
||||
final dbUser = await client.database?.getUser(event.senderId, this);
|
||||
if (dbUser != null) setState(dbUser);
|
||||
}
|
||||
}
|
||||
|
||||
// Try again to decrypt encrypted events and update the database.
|
||||
if (encrypted && client.encryptionEnabled) {
|
||||
// decrypt messages
|
||||
for (var i = 0; i < chunk.events.length; i++) {
|
||||
|
|
@ -1364,8 +1380,9 @@ class Room {
|
|||
await client.database?.transaction(() async {
|
||||
for (var i = 0; i < chunk.events.length; i++) {
|
||||
if (chunk.events[i].content['can_request_session'] == true) {
|
||||
chunk.events[i] = await client.encryption!
|
||||
.decryptRoomEvent(id, chunk.events[i], store: true);
|
||||
chunk.events[i] = await client.encryption!.decryptRoomEvent(
|
||||
id, chunk.events[i],
|
||||
store: !isArchived);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -224,9 +224,7 @@ class Timeline {
|
|||
}
|
||||
|
||||
// Try to decrypt encrypted events but don't update the database.
|
||||
if (room.encrypted &&
|
||||
room.client.database != null &&
|
||||
room.client.encryptionEnabled) {
|
||||
if (room.encrypted && room.client.encryptionEnabled) {
|
||||
for (var i = 0; i < newEvents.length; i++) {
|
||||
if (newEvents[i].type == EventTypes.Encrypted) {
|
||||
newEvents[i] = await room.client.encryption!
|
||||
|
|
|
|||
|
|
@ -338,21 +338,6 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
test('get archive', () async {
|
||||
await matrix.abortSync();
|
||||
final archive = await matrix.loadArchive();
|
||||
|
||||
expect(archive.length, 2);
|
||||
expect(archive[0].id, '!5345234234:example.com');
|
||||
expect(archive[0].membership, Membership.leave);
|
||||
expect(archive[0].name, 'The room name');
|
||||
expect(archive[0].lastEvent?.body, 'This is an example text message');
|
||||
expect(archive[0].roomAccountData.length, 1);
|
||||
expect(archive[1].id, '!5345234235:example.com');
|
||||
expect(archive[1].membership, Membership.leave);
|
||||
expect(archive[1].name, 'The room name 2');
|
||||
});
|
||||
|
||||
test('sync state event in-memory handling', () async {
|
||||
final roomId = '!726s6s6q:example.com';
|
||||
final room = matrix.getRoomById(roomId)!;
|
||||
|
|
|
|||
|
|
@ -361,6 +361,65 @@ class FakeMatrixApi extends BaseClient {
|
|||
'state': [],
|
||||
};
|
||||
|
||||
static Map<String, dynamic> archivesMessageResponse = {
|
||||
'start': 't47429-4392820_219380_26003_2265',
|
||||
'end': 't47409-4357353_219380_26003_2265',
|
||||
'chunk': [
|
||||
{
|
||||
'content': {
|
||||
'body': 'This is an example text message',
|
||||
'msgtype': 'm.text',
|
||||
'format': 'org.matrix.custom.html',
|
||||
'formatted_body': '<b>This is an example text message</b>'
|
||||
},
|
||||
'type': 'm.room.message',
|
||||
'event_id': '3143273582443PhrSn:example.org',
|
||||
'room_id': '!5345234234:example.com',
|
||||
'sender': '@example:example.org',
|
||||
'origin_server_ts': 1432735824653,
|
||||
'unsigned': {'age': 1234}
|
||||
},
|
||||
{
|
||||
'content': {'name': 'The room name'},
|
||||
'type': 'm.room.name',
|
||||
'event_id': '2143273582443PhrSn:example.org',
|
||||
'room_id': '!5345234234:example.com',
|
||||
'sender': '@example:example.org',
|
||||
'origin_server_ts': 1432735824653,
|
||||
'unsigned': {'age': 1234},
|
||||
'state_key': ''
|
||||
},
|
||||
{
|
||||
'content': {
|
||||
'body': 'Gangnam Style',
|
||||
'url': 'mxc://example.org/a526eYUSFFxlgbQYZmo442',
|
||||
'info': {
|
||||
'thumbnail_url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe',
|
||||
'thumbnail_info': {
|
||||
'mimetype': 'image/jpeg',
|
||||
'size': 46144,
|
||||
'w': 300,
|
||||
'h': 300
|
||||
},
|
||||
'w': 480,
|
||||
'h': 320,
|
||||
'duration': 2140786,
|
||||
'size': 1563685,
|
||||
'mimetype': 'video/mp4'
|
||||
},
|
||||
'msgtype': 'm.video'
|
||||
},
|
||||
'type': 'm.room.message',
|
||||
'event_id': '1143273582466PhrSn:example.org',
|
||||
'room_id': '!5345234234:example.com',
|
||||
'sender': '@example:example.org',
|
||||
'origin_server_ts': 1432735824654,
|
||||
'unsigned': {'age': 1234}
|
||||
}
|
||||
],
|
||||
'state': [],
|
||||
};
|
||||
|
||||
static Map<String, dynamic> syncResponse = {
|
||||
'next_batch': Random().nextDouble().toString(),
|
||||
'rooms': {
|
||||
|
|
@ -888,19 +947,36 @@ class FakeMatrixApi extends BaseClient {
|
|||
'events': [
|
||||
{
|
||||
'content': {
|
||||
'body': 'This is an example text message',
|
||||
'body': 'This is a second text example message',
|
||||
'msgtype': 'm.text',
|
||||
'format': 'org.matrix.custom.html',
|
||||
'formatted_body': '<b>This is an example text message</b>'
|
||||
'formatted_body':
|
||||
'<b>This is a second text example message</b>'
|
||||
},
|
||||
'type': 'm.room.message',
|
||||
'event_id': '143273582443PhrSn:example.org',
|
||||
'event_id': '143274597446PhrSn:example.org',
|
||||
'room_id': '!5345234234:example.com',
|
||||
'sender': '@example:example.org',
|
||||
'origin_server_ts': 1432735824654,
|
||||
'unsigned': {'age': 1234}
|
||||
},
|
||||
{
|
||||
'content': {
|
||||
'body': 'This is a first text example message',
|
||||
'msgtype': 'm.text',
|
||||
'format': 'org.matrix.custom.html',
|
||||
'formatted_body':
|
||||
'<b>This is a first text example message</b>'
|
||||
},
|
||||
'type': 'm.room.message',
|
||||
'event_id': '143274597443PhrSn:example.org',
|
||||
'room_id': '!5345234234:example.com',
|
||||
'sender': '@example:example.org',
|
||||
'origin_server_ts': 1432735824653,
|
||||
'unsigned': {'age': 1234}
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
'prev_batch': 't_1234a'
|
||||
},
|
||||
'state': {
|
||||
'events': [
|
||||
|
|
@ -940,7 +1016,8 @@ class FakeMatrixApi extends BaseClient {
|
|||
'state_key': ''
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
'prev_batch': 't_1234b'
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1518,6 +1595,8 @@ class FakeMatrixApi extends BaseClient {
|
|||
(var req) => messagesResponseFutureEnd,
|
||||
'/client/v3/rooms/!1234%3Aexample.com/messages?from=t789&dir=f&limit=30&filter=%7B%22lazy_load_members%22%3Atrue%7D':
|
||||
(var req) => messagesResponseFutureEnd,
|
||||
'/client/v3/rooms/!5345234234%3Aexample.com/messages?from=t_1234a&dir=b&limit=30&filter=%7B%22lazy_load_members%22%3Atrue%7D':
|
||||
(var req) => archivesMessageResponse,
|
||||
'/client/versions': (var req) => {
|
||||
'versions': [
|
||||
'v1.1',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Famedly Matrix SDK
|
||||
* Copyright (C) 2022 Famedly GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:olm/olm.dart' as olm;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'fake_client.dart';
|
||||
|
||||
void main() {
|
||||
group('Timeline', () {
|
||||
Logs().level = Level.error;
|
||||
var olmEnabled = true;
|
||||
|
||||
final insertList = <int>[];
|
||||
|
||||
late Client client;
|
||||
|
||||
setUp(() async {
|
||||
try {
|
||||
await olm.init();
|
||||
olm.get_library_version();
|
||||
} catch (e) {
|
||||
olmEnabled = false;
|
||||
Logs().w('[LibOlm] Failed to load LibOlm', e);
|
||||
}
|
||||
Logs().i('[LibOlm] Enabled: $olmEnabled');
|
||||
|
||||
client = await getClient();
|
||||
client.sendMessageTimeoutSeconds = 5;
|
||||
|
||||
await client.abortSync();
|
||||
insertList.clear();
|
||||
});
|
||||
|
||||
tearDown(() => client.dispose().onError((e, s) {}));
|
||||
|
||||
test('archive room not loaded', () async {
|
||||
final archiveRoom =
|
||||
client.getArchiveRoomFromCache('!5345234234:example.com');
|
||||
expect(archiveRoom, null);
|
||||
});
|
||||
|
||||
test('get archive', () async {
|
||||
final archive = await client.loadArchiveWithTimeline();
|
||||
|
||||
expect(archive.length, 2);
|
||||
expect(client.rooms.length, 2);
|
||||
expect(archive[0].room.id, '!5345234234:example.com');
|
||||
expect(archive[0].room.membership, Membership.leave);
|
||||
expect(archive[0].room.name, 'The room name');
|
||||
expect(archive[0].room.lastEvent?.body,
|
||||
'This is a second text example message');
|
||||
expect(archive[0].room.roomAccountData.length, 1);
|
||||
expect(archive[1].room.id, '!5345234235:example.com');
|
||||
expect(archive[1].room.membership, Membership.leave);
|
||||
expect(archive[1].room.name, 'The room name 2');
|
||||
|
||||
final archiveRoom =
|
||||
client.getArchiveRoomFromCache('!5345234234:example.com');
|
||||
expect(archiveRoom != null, true);
|
||||
expect(archiveRoom!.timeline.events.length, 2);
|
||||
});
|
||||
|
||||
test('request history', () async {
|
||||
await client.loadArchiveWithTimeline();
|
||||
final archiveRoom = client.getRoomById('!5345234234:example.com');
|
||||
expect(archiveRoom != null, true);
|
||||
|
||||
final timeline = await archiveRoom!.getTimeline(onInsert: insertList.add);
|
||||
|
||||
expect(timeline.events.length, 2);
|
||||
expect(timeline.events[0].eventId, '143274597443PhrSn:example.org');
|
||||
expect(timeline.events[1].eventId, '143274597446PhrSn:example.org');
|
||||
|
||||
await timeline.requestHistory();
|
||||
|
||||
expect(timeline.events.length, 5);
|
||||
expect(timeline.events[0].eventId, '143274597443PhrSn:example.org');
|
||||
expect(timeline.events[1].eventId, '143274597446PhrSn:example.org');
|
||||
expect(timeline.events[2].eventId, '3143273582443PhrSn:example.org');
|
||||
expect(timeline.events[3].eventId, '2143273582443PhrSn:example.org');
|
||||
expect(timeline.events[4].eventId, '1143273582466PhrSn:example.org');
|
||||
expect(insertList.length, 3);
|
||||
});
|
||||
|
||||
test('expect database to be empty', () async {
|
||||
await client.loadArchiveWithTimeline();
|
||||
final archiveRoom = client.getRoomById('!5345234234:example.com');
|
||||
expect(archiveRoom != null, true);
|
||||
|
||||
final eventsFromStore = await client.database?.getEventList(
|
||||
archiveRoom!,
|
||||
start: 0,
|
||||
limit: Room.defaultHistoryCount,
|
||||
);
|
||||
expect(eventsFromStore?.isEmpty, true);
|
||||
});
|
||||
|
||||
test('discard room from archives when membership change', () async {
|
||||
await client.loadArchiveWithTimeline();
|
||||
expect(client.getArchiveRoomFromCache('!5345234235:example.com') != null,
|
||||
true);
|
||||
await client.handleSync(SyncUpdate(
|
||||
nextBatch: 't_456',
|
||||
rooms: RoomsUpdate(
|
||||
invite: {'!5345234235:example.com': InvitedRoomUpdate()})));
|
||||
expect(client.getArchiveRoomFromCache('!5345234235:example.com'), null);
|
||||
});
|
||||
|
||||
test('clear archive', () async {
|
||||
await client.loadArchiveWithTimeline();
|
||||
client.clearArchivesFromCache();
|
||||
expect(client.getArchiveRoomFromCache('!5345234234:example.com'), null);
|
||||
});
|
||||
|
||||
test('logout', () async {
|
||||
await client.logout();
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue