diff --git a/lib/src/client.dart b/lib/src/client.dart index 9bbfa9c3..5267a160 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -1389,7 +1389,7 @@ class Client extends MatrixApi { await roomsLoading; await _accountDataLoading; _currentTransaction = database.transaction(() async { - await _handleSync(syncResp, direction: Direction.f); + await _handleSync(syncResp); if (prevBatch != syncResp.nextBatch) { await database.storePrevBatch(syncResp.nextBatch); } @@ -1401,7 +1401,7 @@ class Client extends MatrixApi { ); onSyncStatus.add(SyncStatusUpdate(SyncStatus.cleaningUp)); } else { - await _handleSync(syncResp, direction: Direction.f); + await _handleSync(syncResp); } if (_disposed || _aborted) return; if (prevBatch == null) { @@ -1445,13 +1445,13 @@ class Client extends MatrixApi { } /// Use this method only for testing utilities! - Future handleSync(SyncUpdate sync, {Direction? direction}) async { + Future handleSync(SyncUpdate sync, {bool sortAtTheEnd = false}) async { // ensure we don't upload keys because someone forgot to set a key count sync.deviceOneTimeKeysCount ??= {'signed_curve25519': 100}; - await _handleSync(sync, direction: direction); + await _handleSync(sync, sortAtTheEnd: sortAtTheEnd); } - Future _handleSync(SyncUpdate sync, {Direction? direction}) async { + Future _handleSync(SyncUpdate sync, {bool sortAtTheEnd = false}) async { final syncToDevice = sync.toDevice; if (syncToDevice != null) { await _handleToDeviceEvents(syncToDevice); @@ -1460,15 +1460,15 @@ class Client extends MatrixApi { if (sync.rooms != null) { final join = sync.rooms?.join; if (join != null) { - await _handleRooms(join, direction: direction); + await _handleRooms(join, sortAtTheEnd: sortAtTheEnd); } final invite = sync.rooms?.invite; if (invite != null) { - await _handleRooms(invite, direction: direction); + await _handleRooms(invite, sortAtTheEnd: sortAtTheEnd); } final leave = sync.rooms?.leave; if (leave != null) { - await _handleRooms(leave, direction: direction); + await _handleRooms(leave, sortAtTheEnd: sortAtTheEnd); } } for (final newPresence in sync.presence ?? []) { @@ -1532,7 +1532,7 @@ class Client extends MatrixApi { } Future _handleRooms(Map rooms, - {Direction? direction}) async { + {bool sortAtTheEnd = false}) async { var handledRooms = 0; for (final entry in rooms.entries) { onSyncStatus.add(SyncStatusUpdate( @@ -1548,14 +1548,11 @@ class Client extends MatrixApi { /// Handle now all room events and save them in the database if (room is JoinedRoomUpdate) { final state = room.state; - if (state != null && state.isNotEmpty) { // TODO: This method seems to be comperatively slow for some updates await _handleRoomEvents( - id, - state.map((i) => i.toJson()).toList(), - EventUpdateType.state, - ); + id, state.map((i) => i.toJson()).toList(), EventUpdateType.state, + sortAtTheEnd: sortAtTheEnd); } final timelineEvents = room.timeline?.events; @@ -1563,11 +1560,8 @@ class Client extends MatrixApi { await _handleRoomEvents( id, timelineEvents.map((i) => i.toJson()).toList(), - direction != null - ? (direction == Direction.b - ? EventUpdateType.history - : EventUpdateType.timeline) - : EventUpdateType.timeline); + sortAtTheEnd ? EventUpdateType.history : EventUpdateType.timeline, + sortAtTheEnd: sortAtTheEnd); } final ephemeral = room.ephemeral; @@ -1590,10 +1584,10 @@ class Client extends MatrixApi { final timelineEvents = room.timeline?.events; if (timelineEvents != null && timelineEvents.isNotEmpty) { await _handleRoomEvents( - id, - timelineEvents.map((i) => i.toJson()).toList(), - EventUpdateType.timeline, - ); + id, + timelineEvents.map((i) => i.toJson()).toList(), + EventUpdateType.timeline, + sortAtTheEnd: sortAtTheEnd); } final accountData = room.accountData; if (accountData != null && accountData.isNotEmpty) { @@ -1664,14 +1658,16 @@ class Client extends MatrixApi { } Future _handleRoomEvents( - String chat_id, List events, EventUpdateType type) async { + String chat_id, List events, EventUpdateType type, + {bool sortAtTheEnd = false}) async { for (final event in events) { - await _handleEvent(event, chat_id, type); + await _handleEvent(event, chat_id, type, sortAtTheEnd: sortAtTheEnd); } } Future _handleEvent( - Map event, String roomID, EventUpdateType type) async { + Map event, String roomID, EventUpdateType type, + {bool sortAtTheEnd = false}) async { if (event['type'] is String && event['content'] is Map) { // The client must ignore any new m.room.encryption event to prevent // man-in-the-middle attacks! @@ -1684,7 +1680,11 @@ class Client extends MatrixApi { return; } - var update = EventUpdate(roomID: roomID, type: type, content: event); + var update = EventUpdate( + roomID: roomID, + type: type, + content: event, + ); if (event['type'] == EventTypes.Encrypted && encryptionEnabled) { update = await update.decrypt(room); } diff --git a/lib/src/database/database_api.dart b/lib/src/database/database_api.dart index eff50d85..1cc1807a 100644 --- a/lib/src/database/database_api.dart +++ b/lib/src/database/database_api.dart @@ -83,7 +83,6 @@ abstract class DatabaseApi { Future> getEventList( Room room, { int start = 0, - bool onlySending, int limit, }); diff --git a/lib/src/database/fluffybox_database.dart b/lib/src/database/fluffybox_database.dart index b80c34fa..d42f436e 100644 --- a/lib/src/database/fluffybox_database.dart +++ b/lib/src/database/fluffybox_database.dart @@ -345,7 +345,6 @@ class FluffyBoxDatabase extends DatabaseApi { Future> getEventList( Room room, { int start = 0, - bool onlySending = false, int? limit, }) => runBenchmarked>('Get event list', () async { @@ -367,11 +366,10 @@ class FluffyBoxDatabase extends DatabaseApi { // Combine those two lists while respecting the start and limit parameters. final end = min(timelineEventIds.length, start + (limit ?? timelineEventIds.length)); - - final eventIds = sendingEventIds; - if (start < timelineEventIds.length && !onlySending) { - eventIds.addAll(timelineEventIds.getRange(start, end).toList()); - } + final eventIds = sendingEventIds + + (start < timelineEventIds.length + ? timelineEventIds.getRange(start, end).toList() + : []); return await _getEventsByIds(eventIds.cast(), room); }); diff --git a/lib/src/database/hive_database.dart b/lib/src/database/hive_database.dart index 69332464..02e4b07d 100644 --- a/lib/src/database/hive_database.dart +++ b/lib/src/database/hive_database.dart @@ -387,7 +387,6 @@ class FamedlySdkHiveDatabase extends DatabaseApi { Future> getEventList( Room room, { int start = 0, - bool onlySending = false, int? limit, }) => runBenchmarked>('Get event list', () async { @@ -411,7 +410,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi { final end = min(timelineEventIds.length, start + (limit ?? timelineEventIds.length)); final eventIds = sendingEventIds + - (start < timelineEventIds.length && !onlySending + (start < timelineEventIds.length ? timelineEventIds.getRange(start, end).toList() : []); diff --git a/lib/src/models/timeline_chunk.dart b/lib/src/models/timeline_chunk.dart deleted file mode 100644 index f4296f86..00000000 --- a/lib/src/models/timeline_chunk.dart +++ /dev/null @@ -1,10 +0,0 @@ -import '../../matrix.dart'; - -class TimelineChunk { - String prevBatch; // pos of the first event of the database timeline chunk - String nextBatch; - - List events; - TimelineChunk( - {required this.events, this.prevBatch = '', this.nextBatch = ''}); -} diff --git a/lib/src/room.dart b/lib/src/room.dart index 82eb7e06..c1a1d319 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -22,7 +22,6 @@ import 'dart:typed_data'; import 'package:collection/collection.dart'; import 'package:html_unescape/html_unescape.dart'; -import 'package:matrix/src/models/timeline_chunk.dart'; import 'package:matrix/src/utils/crypto/crypto.dart'; import 'package:matrix/src/utils/file_send_request_credentials.dart'; import 'package:matrix/src/utils/space_child.dart'; @@ -32,8 +31,11 @@ import 'utils/markdown.dart'; import 'utils/marked_unread.dart'; enum PushRuleState { notify, mentionsOnly, dontNotify } + enum JoinRules { public, knock, invite, private } + enum GuestAccess { canJoin, forbidden } + enum HistoryVisibility { invited, joined, shared, worldReadable } const Map _guestAccessMap = { @@ -1081,8 +1083,7 @@ class Room { /// Returns the actual count of received timeline events. Future requestHistory( {int historyCount = defaultHistoryCount, - void Function()? onHistoryReceived, - direction = Direction.b}) async { + void Function()? onHistoryReceived}) async { final prev_batch = this.prev_batch; if (prev_batch == null) { throw 'Tried to request history without a prev_batch token'; @@ -1090,7 +1091,7 @@ class Room { final resp = await client.getRoomEvents( id, prev_batch, - direction, + Direction.b, limit: historyCount, filter: jsonEncode(StateFilter(lazyLoadMembers: true).toJson()), ); @@ -1111,12 +1112,8 @@ class Room { state: resp.state, timeline: TimelineUpdate( limited: false, - events: direction == Direction.b - ? resp.chunk - : resp.chunk?.reversed.toList(), - prevBatch: direction == Direction.b - ? resp.end - : resp.start, + events: resp.chunk, + prevBatch: resp.end, ), ) } @@ -1128,15 +1125,13 @@ class Room { timeline: TimelineUpdate( limited: false, events: resp.chunk, - prevBatch: direction == Direction.b - ? resp.end - : resp.start, + prevBatch: resp.end, ), ), } : null), ), - direction: Direction.b); + sortAtTheEnd: true); }; if (client.database != null) { @@ -1215,34 +1210,6 @@ class Room { return; } - Future getEventContext(String eventId) async { - final resp = await client.getEventContext(id, eventId, - limit: Room.defaultHistoryCount - // filter: jsonEncode(StateFilter(lazyLoadMembers: true).toJson()), - ); - - final events = [ - if (resp.eventsAfter != null) ...resp.eventsAfter!.reversed.toList(), - if (resp.event != null) resp.event!, - if (resp.eventsBefore != null) ...resp.eventsBefore! - ].map((e) => Event.fromMatrixEvent(e, this)).toList(); - - // Try again to decrypt encrypted events but don't update the database. - if (encrypted && client.database != null && client.encryptionEnabled) { - for (var i = 0; i < events.length; i++) { - if (events[i].type == EventTypes.Encrypted && - events[i].content['can_request_session'] == true) { - events[i] = await client.encryption!.decryptRoomEvent(id, events[i]); - } - } - } - - final chunk = TimelineChunk( - nextBatch: resp.end ?? '', prevBatch: resp.start ?? '', events: events); - - return chunk; - } - /// This API updates the marker for the given receipt type to the event ID /// specified. Future postReceipt(String eventId) async { @@ -1261,80 +1228,47 @@ class Room { /// just want to update the whole timeline on every change, use the [onUpdate] /// callback. For updating only the parts that have changed, use the /// [onChange], [onRemove], [onInsert] and the [onHistoryReceived] callbacks. - /// This method can also retrieve the timeline at a specific point by setting - /// the [eventContextId] - Future getTimeline( - {void Function(int index)? onChange, - void Function(int index)? onRemove, - void Function(int insertID)? onInsert, - void Function()? onNewEvent, - void Function()? onUpdate, - String? eventContextId}) async { + Future getTimeline({ + void Function(int index)? onChange, + void Function(int index)? onRemove, + void Function(int insertID)? onInsert, + void Function()? onUpdate, + }) async { await postLoad(); + final events = await client.database?.getEventList( + this, + limit: defaultHistoryCount, + ) ?? + []; - final _events = await client.database?.getEventList( - this, - limit: defaultHistoryCount, - ); - - var chunk = TimelineChunk(events: _events ?? []); - - 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); - } - } - } - - if (encrypted && client.encryptionEnabled) { - // decrypt messages - for (var i = 0; i < chunk.events.length; i++) { - if (chunk.events[i].type == EventTypes.Encrypted) { - if (eventContextId != null) { - // for the fragmented timeline, we don't cache the decrypted - //message in the database - chunk.events[i] = await client.encryption!.decryptRoomEvent( - id, - chunk.events[i], - ); - } else if (client.database != null) { - // else, we need the database - 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); - } - } - }); + // Try again to decrypt encrypted events and update the database. + if (encrypted && client.database != null && client.encryptionEnabled) { + await client.database?.transaction(() async { + for (var i = 0; i < events.length; i++) { + if (events[i].type == EventTypes.Encrypted && + events[i].content['can_request_session'] == true) { + events[i] = await client.encryption! + .decryptRoomEvent(id, events[i], store: true); } } - } + }); + } + + // Fetch all users from database we have got here. + 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); } final timeline = Timeline( - room: this, - chunk: chunk, - onChange: onChange, - onRemove: onRemove, - onInsert: onInsert, - onNewEvent: onNewEvent, - onUpdate: onUpdate); + room: this, + events: events, + onChange: onChange, + onRemove: onRemove, + onInsert: onInsert, + onUpdate: onUpdate, + ); return timeline; } @@ -1862,13 +1796,13 @@ class Room { } Future _handleFakeSync(SyncUpdate syncUpdate, - {Direction? direction}) async { + {bool sortAtTheEnd = false}) async { if (client.database != null) { await client.database?.transaction(() async { - await client.handleSync(syncUpdate, direction: direction); + await client.handleSync(syncUpdate, sortAtTheEnd: sortAtTheEnd); }); } else { - await client.handleSync(syncUpdate, direction: direction); + await client.handleSync(syncUpdate, sortAtTheEnd: sortAtTheEnd); } } diff --git a/lib/src/timeline.dart b/lib/src/timeline.dart index 69073f2b..3b793a8f 100644 --- a/lib/src/timeline.dart +++ b/lib/src/timeline.dart @@ -17,20 +17,17 @@ */ import 'dart:async'; -import 'dart:convert'; import 'package:collection/src/iterable_extensions.dart'; import '../matrix.dart'; -import 'models/timeline_chunk.dart'; /// Represents the timeline of a room. The callback [onUpdate] will be triggered /// automatically. The initial /// event list will be retreived when created by the `room.getTimeline()` method. - class Timeline { final Room room; - List get events => chunk.events; + final List events; /// Map of event ID to map of type to set of aggregated events final Map>> aggregatedEvents = {}; @@ -39,21 +36,14 @@ class Timeline { final void Function(int index)? onChange; final void Function(int index)? onInsert; final void Function(int index)? onRemove; - final void Function()? onNewEvent; StreamSubscription? sub; StreamSubscription? roomSub; StreamSubscription? sessionIdReceivedSub; bool isRequestingHistory = false; - bool isRequestingFuture = false; - - bool allowNewEvent = true; - bool isFragmentedTimeline = false; final Map _eventCache = {}; - TimelineChunk chunk; - /// Searches for the event in this timeline. If not /// found, requests from the server. Requested events /// are cached. @@ -84,41 +74,16 @@ class Timeline { if (isRequestingHistory) { return; } - isRequestingHistory = true; - await _requestEvents(direction: Direction.b, historyCount: historyCount); - isRequestingHistory = false; - } - - bool get canRequestFuture => !allowNewEvent; - - Future requestFuture( - {int historyCount = Room.defaultHistoryCount}) async { - if (allowNewEvent) { - return; // we shouldn't force to add new events if they will autatically be added - } - - if (isRequestingFuture) return; - isRequestingFuture = true; - await _requestEvents(direction: Direction.f, historyCount: historyCount); - isRequestingFuture = false; - } - - Future _requestEvents( - {int historyCount = Room.defaultHistoryCount, - required Direction direction}) async { onUpdate?.call(); try { - // Look up for events in the database first. With fragmented view, we should delete the database cache - final eventsFromStore = isFragmentedTimeline - ? null - : await room.client.database?.getEventList( - room, - start: events.length, - limit: Room.defaultHistoryCount, - ); - + // Look up for events in hive first + final eventsFromStore = await room.client.database?.getEventList( + room, + start: events.length, + limit: Room.defaultHistoryCount, + ); if (eventsFromStore != null && eventsFromStore.isNotEmpty) { // Fetch all users from database we have got here. for (final event in events) { @@ -130,37 +95,20 @@ class Timeline { if (dbUser != null) room.setState(dbUser); } - if (direction == Direction.b) { - events.addAll(eventsFromStore); - final startIndex = events.length - eventsFromStore.length; - final endIndex = events.length; - for (var i = startIndex; i < endIndex; i++) { - onInsert?.call(i); - } - } else { - events.insertAll(0, eventsFromStore); - final startIndex = eventsFromStore.length; - final endIndex = 0; - for (var i = startIndex; i > endIndex; i--) { - onInsert?.call(i); - } + events.addAll(eventsFromStore); + final startIndex = events.length - eventsFromStore.length; + final endIndex = events.length; + for (var i = startIndex; i < endIndex; i++) { + onInsert?.call(i); } } else { - Logs().i('No more events found in the store. Request from server...'); - if (isFragmentedTimeline) { - await getRoomEvents( - historyCount: historyCount, - direction: direction, - ); - } else { - await room.requestHistory( - historyCount: historyCount, - direction: direction, - onHistoryReceived: () { - _collectHistoryUpdates = true; - }, - ); - } + Logs().v('No more events found in the store. Request from server...'); + await room.requestHistory( + historyCount: historyCount, + onHistoryReceived: () { + _collectHistoryUpdates = true; + }, + ); } } finally { _collectHistoryUpdates = false; @@ -169,103 +117,14 @@ class Timeline { } } - /// Request more previous events from the server. [historyCount] defines how much events should - /// be received maximum. When the request is answered, [onHistoryReceived] will be triggered **before** - /// the historical events will be published in the onEvent stream. - /// Returns the actual count of received timeline events. - Future getRoomEvents( - {int historyCount = Room.defaultHistoryCount, - direction = Direction.b}) async { - final resp = await room.client.getRoomEvents( - room.id, - direction == Direction.b ? chunk.prevBatch : chunk.nextBatch, - direction, - limit: historyCount, - filter: jsonEncode(StateFilter(lazyLoadMembers: true).toJson()), - ); - - if (resp.end == null || resp.start == null) { - Logs().w('end or start parameters where not set in the response'); - } - - final newNextBatch = direction == Direction.b ? resp.start : resp.end; - final newPrevBatch = direction == Direction.b ? resp.end : resp.start; - - final type = direction == Direction.b - ? EventUpdateType.history - : EventUpdateType.timeline; - - if ((resp.state?.length ?? 0) == 0 && resp.start != resp.end) { - if (type == EventUpdateType.history) { - Logs().w( - '[nav] we can still request history prevBatch: $type $newPrevBatch'); - } else { - Logs().w( - '[nav] we can still request history nextBatch: $type $newNextBatch'); - } - } - - final newEvents = - resp.chunk?.map((e) => Event.fromMatrixEvent(e, room)).toList() ?? []; - - if (!allowNewEvent) { - if (resp.start == resp.end) allowNewEvent = true; - - if (allowNewEvent) { - Logs().d('We now allow sync update into the timeline.'); - allowNewEvent = true; - newEvents.addAll( - await room.client.database?.getEventList(room, onlySending: true) ?? - []); - } - } - - // Try to decrypt encrypted events but don't update the database. - if (room.encrypted && - room.client.database != null && - room.client.encryptionEnabled) { - for (var i = 0; i < newEvents.length; i++) { - if (newEvents[i].type == EventTypes.Encrypted) { - newEvents[i] = await room.client.encryption! - .decryptRoomEvent(room.id, newEvents[i]); - } - } - } - - // update chunk anchors - if (type == EventUpdateType.history) { - chunk.prevBatch = newPrevBatch ?? ''; - - final offset = chunk.events.length; - - chunk.events.addAll(newEvents); - - for (var i = 0; i < newEvents.length; i++) { - onInsert?.call(i + offset); - } - } else { - chunk.nextBatch = newNextBatch ?? ''; - chunk.events.insertAll(0, newEvents.reversed); - - for (var i = 0; i < newEvents.length; i++) { - onInsert?.call(i); - } - } - - if (onUpdate != null) { - onUpdate!(); - } - return resp.chunk?.length ?? 0; - } - - Timeline( - {required this.room, - this.onUpdate, - this.onChange, - this.onInsert, - this.onRemove, - this.onNewEvent, - required this.chunk}) { + Timeline({ + required this.room, + List? events, + this.onUpdate, + this.onChange, + this.onInsert, + this.onRemove, + }) : events = events ?? [] { sub = room.client.onEvent.stream.listen(_handleEventUpdate); // If the timeline is limited we want to clear our events cache @@ -277,15 +136,9 @@ class Timeline { room.onSessionKeyReceived.stream.listen(_sessionKeyReceived); // we want to populate our aggregated events - for (final e in events) { + for (final e in this.events) { addAggregatedEvent(e); } - - // we are using a fragmented timeline - if (chunk.nextBatch != '') { - allowNewEvent = false; - isFragmentedTimeline = true; - } } /// Removes all entries from [events] which are not in this SyncUpdate. @@ -428,13 +281,6 @@ class Timeline { eventUpdate.type != EventUpdateType.history) { return; } - - if (eventUpdate.type == EventUpdateType.timeline) { - onNewEvent?.call(); - } - - if (!allowNewEvent) return; - final status = eventStatusFromInt(eventUpdate.content['status'] ?? (eventUpdate.content['unsigned'] is Map ? eventUpdate.content['unsigned'][messageSendingStatusKey] diff --git a/lib/src/utils/event_update.dart b/lib/src/utils/event_update.dart index 9571367e..f6f9e74f 100644 --- a/lib/src/utils/event_update.dart +++ b/lib/src/utils/event_update.dart @@ -39,8 +39,11 @@ class EventUpdate { // The json payload of the content of this event. final Map content; - EventUpdate( - {required this.roomID, required this.type, required this.content}); + EventUpdate({ + required this.roomID, + required this.type, + required this.content, + }); Future decrypt(Room room, {bool store = false}) async { final encryption = room.client.encryption; @@ -54,7 +57,10 @@ class EventUpdate { room.id, Event.fromJson(content, room), store: store, updateType: type); return EventUpdate( - roomID: roomID, type: type, content: decrpytedEvent.toJson()); + roomID: roomID, + type: type, + content: decrpytedEvent.toJson(), + ); } catch (e, s) { Logs().e('[LibOlm] Could not decrypt megolm event', e, s); return this; diff --git a/scripts/prepare.sh b/scripts/prepare.sh index a619b421..05d4f1d4 100644 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -23,5 +23,5 @@ fi if which flutter >/dev/null; then flutter pub get else - pub get + dart pub get fi diff --git a/scripts/test_driver.sh b/scripts/test_driver.sh index 50a1e9bb..0fd1ca5b 100644 --- a/scripts/test_driver.sh +++ b/scripts/test_driver.sh @@ -1,2 +1,2 @@ #!/bin/sh -e -pub run test_driver/matrixsdk_test.dart -p vm \ No newline at end of file +dart pub run test_driver/matrixsdk_test.dart -p vm \ No newline at end of file diff --git a/test/database_api_test.dart b/test/database_api_test.dart index 44bf78d6..52168071 100644 --- a/test/database_api_test.dart +++ b/test/database_api_test.dart @@ -232,8 +232,7 @@ void testDatabase( }); test('getEventList', () async { final events = await database.getEventList( - Room(id: '!testroom:example.com', client: Client('testclient')), - ); + Room(id: '!testroom:example.com', client: Client('testclient'))); expect(events.single.type, EventTypes.Message); }); test('getUser', () async { diff --git a/test/event_test.dart b/test/event_test.dart index 7fd81c2a..b67e6a04 100644 --- a/test/event_test.dart +++ b/test/event_test.dart @@ -21,7 +21,6 @@ import 'dart:typed_data'; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; -import 'package:matrix/src/models/timeline_chunk.dart'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; @@ -1108,9 +1107,8 @@ void main() { 'sender': '@example:example.org', 'event_id': '\$edit2', }, room); - final timeline = Timeline( - chunk: TimelineChunk(events: [event, edit1, edit2]), - room: room); + final timeline = + Timeline(events: [event, edit1, edit2], room: room); expect(event.hasAggregatedEvents(timeline, RelationshipTypes.edit), true); expect(event.aggregatedEvents(timeline, RelationshipTypes.edit), {edit1, edit2}); @@ -1206,26 +1204,24 @@ void main() { 'sender': '@bob:example.org', }, room); // no edits - var displayEvent = event.getDisplayEvent( - Timeline(chunk: TimelineChunk(events: [event]), room: room)); + var displayEvent = + event.getDisplayEvent(Timeline(events: [event], room: room)); expect(displayEvent.body, 'blah'); // one edit - displayEvent = event.getDisplayEvent(Timeline( - chunk: TimelineChunk(events: [event, edit1]), room: room)); + displayEvent = event + .getDisplayEvent(Timeline(events: [event, edit1], room: room)); expect(displayEvent.body, 'edit 1'); // two edits - displayEvent = event.getDisplayEvent(Timeline( - chunk: TimelineChunk(events: [event, edit1, edit2]), - room: room)); + displayEvent = event.getDisplayEvent( + Timeline(events: [event, edit1, edit2], room: room)); expect(displayEvent.body, 'edit 2'); // foreign edit - displayEvent = event.getDisplayEvent(Timeline( - chunk: TimelineChunk(events: [event, edit3]), room: room)); + displayEvent = event + .getDisplayEvent(Timeline(events: [event, edit3], room: room)); expect(displayEvent.body, 'blah'); // mixed foreign and non-foreign - displayEvent = event.getDisplayEvent(Timeline( - chunk: TimelineChunk(events: [event, edit1, edit2, edit3]), - room: room)); + displayEvent = event.getDisplayEvent( + Timeline(events: [event, edit1, edit2, edit3], room: room)); expect(displayEvent.body, 'edit 2'); event = Event.fromJson({ 'type': EventTypes.Message, @@ -1243,9 +1239,8 @@ void main() { }, }, }, room); - displayEvent = event.getDisplayEvent(Timeline( - chunk: TimelineChunk(events: [event, edit1, edit2, edit3]), - room: room)); + displayEvent = event.getDisplayEvent( + Timeline(events: [event, edit1, edit2, edit3], room: room)); expect(displayEvent.body, 'Redacted'); }); test('attachments', () async { diff --git a/test/fake_matrix_api.dart b/test/fake_matrix_api.dart index 37cc278a..e8053228 100644 --- a/test/fake_matrix_api.dart +++ b/test/fake_matrix_api.dart @@ -148,7 +148,7 @@ class FakeMatrixApi extends MockClient { return Response.bytes(utf8.encode(json.encode(res)), statusCode); }); - static Map messagesResponsePast = { + static Map messagesResponse = { 'start': 't47429-4392820_219380_26003_2265', 'end': 't47409-4357353_219380_26003_2265', 'chunk': [ @@ -206,70 +206,6 @@ class FakeMatrixApi extends MockClient { ], 'state': [], }; - static Map messagesResponseFuture = { - 'start': 't456', - 'end': 't789', - 'chunk': [ - { - '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': '1143273582443PhrSn:example.org', - 'room_id': '!1234: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': '!1234:example.com', - 'sender': '@example:example.org', - 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234}, - 'state_key': '' - }, - { - 'content': { - 'body': 'This is an example text message', - 'msgtype': 'm.text', - 'format': 'org.matrix.custom.html', - 'formatted_body': 'This is an example text message' - }, - 'type': 'm.room.message', - 'event_id': '3143273582443PhrSn:example.org', - 'room_id': '!1234:example.com', - 'sender': '@example:example.org', - 'origin_server_ts': 1432735824653, - 'unsigned': {'age': 1234} - } - ], - 'state': [], - }; - static Map messagesResponseFutureEnd = { - 'start': 't789', - 'end': 't789', - 'chunk': [], - 'state': [], - }; static Map syncResponse = { 'next_batch': Random().nextDouble().toString(), @@ -1401,19 +1337,11 @@ class FakeMatrixApi extends MockClient { 'unsigned': {'age': 1234} }, '/client/r0/rooms/!localpart%3Aserver.abc/messages?from=1234&dir=b&to=1234&limit=10&filter=%7B%22lazy_load_members%22%3Atrue%7D': - (var req) => messagesResponsePast, + (var req) => messagesResponse, '/client/r0/rooms/!localpart%3Aserver.abc/messages?from=&dir=b&limit=10&filter=%7B%22lazy_load_members%22%3Atrue%7D': - (var req) => messagesResponsePast, + (var req) => messagesResponse, '/client/r0/rooms/!1234%3Aexample.com/messages?from=1234&dir=b&limit=30&filter=%7B%22lazy_load_members%22%3Atrue%7D': - (var req) => messagesResponsePast, - '/client/r0/rooms/!localpart%3Aserver.abc/messages?from=t456&dir=f&to=1234&limit=10&filter=%7B%22lazy_load_members%22%3Atrue%7D': - (var req) => messagesResponseFuture, - '/client/r0/rooms/!1234%3Aexample.com/messages?from=t456&dir=f&limit=30&filter=%7B%22lazy_load_members%22%3Atrue%7D': - (var req) => messagesResponseFuture, - '/client/r0/rooms/!localpart%3Aserver.abc/messages?from=t789&dir=f&to=1234&limit=10&filter=%7B%22lazy_load_members%22%3Atrue%7D': - (var req) => messagesResponseFutureEnd, - '/client/r0/rooms/!1234%3Aexample.com/messages?from=t789&dir=f&limit=30&filter=%7B%22lazy_load_members%22%3Atrue%7D': - (var req) => messagesResponseFutureEnd, + (var req) => messagesResponse, '/client/versions': (var req) => { 'versions': [ 'r0.0.1', diff --git a/test/timeline_context_test.dart b/test/timeline_context_test.dart deleted file mode 100644 index 322250df..00000000 --- a/test/timeline_context_test.dart +++ /dev/null @@ -1,589 +0,0 @@ -/* - * Famedly Matrix SDK - * Copyright (C) 2019, 2020 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 . - */ - -import 'package:matrix/matrix.dart'; -import 'package:matrix/src/models/timeline_chunk.dart'; - -import 'package:test/test.dart'; -import 'package:olm/olm.dart' as olm; -import 'fake_client.dart'; - -void main() { - group('Timeline context', () { - Logs().level = Level.error; - final roomID = '!1234:example.com'; - final testTimeStamp = DateTime.now().millisecondsSinceEpoch; - var updateCount = 0; - final insertList = []; - final changeList = []; - final removeList = []; - var olmEnabled = true; - - late Client client; - late Room room; - late Timeline timeline; - test('create stuff', () 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; - - room = Room( - id: roomID, client: client, prev_batch: 't123', roomAccountData: {}); - timeline = Timeline( - room: room, - chunk: TimelineChunk(events: [], nextBatch: 't456', prevBatch: 't123'), - onUpdate: () { - updateCount++; - }, - onInsert: insertList.add, - onChange: changeList.add, - onRemove: removeList.add, - ); - - expect(timeline.isFragmentedTimeline, true); - expect(timeline.allowNewEvent, false); - }); - - test('Request future', () async { - timeline.events.clear(); - await timeline.requestFuture(); - - await Future.delayed(Duration(milliseconds: 50)); - - expect(updateCount, 3); - expect(insertList, [0, 1, 2]); - expect(timeline.events.length, 3); - expect(timeline.events[0].eventId, '3143273582443PhrSn:example.org'); - expect(timeline.events[1].eventId, '2143273582443PhrSn:example.org'); - expect(timeline.events[2].eventId, '1143273582443PhrSn:example.org'); - expect(timeline.chunk.nextBatch, 't789'); - - expect(timeline.isFragmentedTimeline, true); - expect(timeline.allowNewEvent, false); - }); - - /// We send a message in a fragmented timeline, it didn't reached the end so we shouldn't be displayed. - test('Send message not displayed', () async { - updateCount = 0; - - await room.sendTextEvent('test', txid: '1234'); - await Future.delayed(Duration(milliseconds: 50)); - - expect(updateCount, 0); - expect(insertList, [0, 1, 2]); - expect(insertList.length, - timeline.events.length); // expect no new events to have been added - - final eventId = '1844295642248BcDkn:example.org'; - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'test'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': eventId, - 'unsigned': {'transaction_id': '1234'}, - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch - }, - )); // just assume that it was on the server for this call but not for the following. - - await Future.delayed(Duration(milliseconds: 50)); - - expect(updateCount, 0); - expect(insertList, [0, 1, 2]); - expect(timeline.events.length, - 3); // we still expect the timeline to contain the same numbre of elements - }); - - test('Request future end of timeline', () async { - await timeline.requestFuture(); - - await Future.delayed(Duration(milliseconds: 50)); - - expect(updateCount, 3); - expect(insertList, [0, 1, 2]); - expect(insertList.length, timeline.events.length); - expect(timeline.events[0].eventId, '3143273582443PhrSn:example.org'); - expect(timeline.events[1].eventId, '2143273582443PhrSn:example.org'); - expect(timeline.events[2].eventId, '1143273582443PhrSn:example.org'); - expect(timeline.chunk.nextBatch, 't789'); - - expect(timeline.isFragmentedTimeline, true); - expect(timeline.allowNewEvent, true); - }); - - test('Send message', () async { - await room.sendTextEvent('test', txid: '1234'); - - await Future.delayed(Duration(milliseconds: 50)); - expect(updateCount, 5); - expect(insertList, [0, 1, 2, 0]); - expect(insertList.length, timeline.events.length); - final eventId = timeline.events[0].eventId; - expect(eventId.startsWith('\$event'), true); - expect(timeline.events[0].status, EventStatus.sent); - - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'test'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': eventId, - 'unsigned': {'transaction_id': '1234'}, - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch - }, - )); - - await Future.delayed(Duration(milliseconds: 50)); - - expect(updateCount, 6); - expect(insertList, [0, 1, 2, 0]); - expect(insertList.length, timeline.events.length); - expect(timeline.events[0].eventId, eventId); - expect(timeline.events[0].status, EventStatus.synced); - }); - - test('Send message with error', () async { - updateCount = 0; - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.sending.intValue, - 'event_id': 'abc', - 'origin_server_ts': testTimeStamp - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - - expect(updateCount, 1); - await room.sendTextEvent('test', txid: 'errortxid'); - await Future.delayed(Duration(milliseconds: 50)); - - expect(updateCount, 3); - await room.sendTextEvent('test', txid: 'errortxid2'); - await Future.delayed(Duration(milliseconds: 50)); - await room.sendTextEvent('test', txid: 'errortxid3'); - await Future.delayed(Duration(milliseconds: 50)); - - expect(updateCount, 7); - expect(insertList, [0, 1, 2, 0, 0, 0, 1, 2]); - expect(insertList.length, timeline.events.length); - expect(changeList, [0, 0, 0, 1, 2]); - expect(removeList, []); - expect(timeline.events[0].status, EventStatus.error); - expect(timeline.events[1].status, EventStatus.error); - expect(timeline.events[2].status, EventStatus.error); - }); - - test('Remove message', () async { - updateCount = 0; - await timeline.events[0].remove(); - - await Future.delayed(Duration(milliseconds: 50)); - - expect(updateCount, 1); - - expect(insertList, [0, 1, 2, 0, 0, 0, 1, 2]); - expect(changeList, [0, 0, 0, 1, 2]); - expect(removeList, [0]); - expect(timeline.events.length, 7); - expect(timeline.events[0].status, EventStatus.error); - }); - - test('getEventById', () async { - var event = await timeline.getEventById('abc'); - expect(event?.content, {'msgtype': 'm.text', 'body': 'Testcase'}); - - event = await timeline.getEventById('not_found'); - expect(event, null); - - event = await timeline.getEventById('unencrypted_event'); - expect(event?.body, 'This is an example text message'); - - if (olmEnabled) { - event = await timeline.getEventById('encrypted_event'); - // the event is invalid but should have traces of attempting to decrypt - expect(event?.messageType, MessageTypes.BadEncrypted); - } - }); - - test('Resend message', () async { - timeline.events.clear(); - updateCount = 0; - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.error.intValue, - 'event_id': 'new-test-event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'newresend'}, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.error); - await timeline.events[0].sendAgain(); - - await Future.delayed(Duration(milliseconds: 50)); - - expect(updateCount, 3); - - expect(insertList, [0, 1, 2, 0, 0, 0, 1, 2, 0]); - expect(changeList, [0, 0, 0, 1, 2, 0, 0]); - expect(removeList, [0]); - expect(timeline.events.length, 1); - expect(timeline.events[0].status, EventStatus.sent); - }); - - test('Clear cache on limited timeline', () async { - client.onSync.add( - SyncUpdate( - nextBatch: '1234', - rooms: RoomsUpdate( - join: { - roomID: JoinedRoomUpdate( - timeline: TimelineUpdate( - limited: true, - prevBatch: 'blah', - ), - unreadNotifications: UnreadNotificationCounts( - highlightCount: 0, - notificationCount: 0, - ), - ), - }, - ), - ), - ); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events.isEmpty, true); - }); - - test('sort errors on top', () async { - timeline.events.clear(); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.error.intValue, - 'event_id': 'abc', - 'origin_server_ts': testTimeStamp - }, - )); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': 'def', - 'origin_server_ts': testTimeStamp + 5 - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.error); - expect(timeline.events[1].status, EventStatus.synced); - }); - - test('sending event to failed update', () async { - timeline.events.clear(); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.sending.intValue, - 'event_id': 'will-fail', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.sending); - expect(timeline.events.length, 1); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.error.intValue, - 'event_id': 'will-fail', - 'origin_server_ts': testTimeStamp - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.error); - expect(timeline.events.length, 1); - }); - test('setReadMarker', () async { - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': 'will-work', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - room.notificationCount = 1; - await timeline.setReadMarker(); - expect(room.notificationCount, 0); - }); - test('sending an event and the http request finishes first, 0 -> 1 -> 2', - () async { - timeline.events.clear(); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.sending.intValue, - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.sending); - expect(timeline.events.length, 1); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.sent.intValue, - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'} - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.sent); - expect(timeline.events.length, 1); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'} - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.synced); - expect(timeline.events.length, 1); - }); - test('sending an event where the sync reply arrives first, 0 -> 2 -> 1', - () async { - timeline.events.clear(); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - 'unsigned': { - messageSendingStatusKey: EventStatus.sending.intValue, - 'transaction_id': 'transaction', - }, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.sending); - expect(timeline.events.length, 1); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': { - 'transaction_id': 'transaction', - messageSendingStatusKey: EventStatus.synced.intValue, - }, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.synced); - expect(timeline.events.length, 1); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': { - 'transaction_id': 'transaction', - messageSendingStatusKey: EventStatus.sent.intValue, - }, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.synced); - expect(timeline.events.length, 1); - }); - test('sending an event 0 -> -1 -> 2', () async { - timeline.events.clear(); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.sending.intValue, - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.sending); - expect(timeline.events.length, 1); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.error.intValue, - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.error); - expect(timeline.events.length, 1); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.synced); - expect(timeline.events.length, 1); - }); - test('sending an event 0 -> 2 -> -1', () async { - timeline.events.clear(); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.sending.intValue, - 'event_id': 'transaction', - 'origin_server_ts': DateTime.now().millisecondsSinceEpoch, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.sending); - expect(timeline.events.length, 1); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.synced.intValue, - 'event_id': '\$event', - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.synced); - expect(timeline.events.length, 1); - client.onEvent.add(EventUpdate( - type: EventUpdateType.timeline, - roomID: roomID, - content: { - 'type': 'm.room.message', - 'content': {'msgtype': 'm.text', 'body': 'Testcase'}, - 'sender': '@alice:example.com', - 'status': EventStatus.error.intValue, - 'origin_server_ts': testTimeStamp, - 'unsigned': {'transaction_id': 'transaction'}, - }, - )); - await Future.delayed(Duration(milliseconds: 50)); - expect(timeline.events[0].status, EventStatus.synced); - expect(timeline.events.length, 1); - }); - test('logout', () async { - await client.logout(); - }); - }); -} diff --git a/test/timeline_test.dart b/test/timeline_test.dart index 4a412a08..94ac442a 100644 --- a/test/timeline_test.dart +++ b/test/timeline_test.dart @@ -17,7 +17,6 @@ */ import 'package:matrix/matrix.dart'; -import 'package:matrix/src/models/timeline_chunk.dart'; import 'package:test/test.dart'; import 'package:olm/olm.dart' as olm; @@ -53,7 +52,7 @@ void main() { id: roomID, client: client, prev_batch: '1234', roomAccountData: {}); timeline = Timeline( room: room, - chunk: TimelineChunk(events: []), + events: [], onUpdate: () { updateCount++; },