refactor: delete not sent events without eventupdate stream workaround

This fixes several problems. First
sending a fake event through the
onEventUpdate stream was not a
good design and lead to problems
if you expect a timeline event but
then are unable to build the
event from json. This now uses
a new stream in the Client which
is listened to in the timeline to
delete an event which should be
much more reliable.
It also now throws an exception
if deleting the event fails
instead of returning true or false.
A deprecation note is added.
This commit is contained in:
Krille 2024-05-06 14:38:23 +02:00
parent e4acb1f510
commit 501c457ea1
No known key found for this signature in database
GPG Key ID: E067ECD60F1A0652
7 changed files with 78 additions and 75 deletions

View File

@ -1312,6 +1312,9 @@ class Client extends MatrixApi {
final CachedStreamController<Event> onGroupMember = CachedStreamController();
final CachedStreamController<String> onCancelSendEvent =
CachedStreamController();
/// When a state in a room has been updated this will return the room ID
/// and the state event.
final CachedStreamController<({String roomId, StrippedStateEvent state})>

View File

@ -322,28 +322,27 @@ class Event extends MatrixEvent {
return receiptsList;
}
/// Removes this event if the status is [sending], [error] or [removed].
/// This event will just be removed from the database and the timelines.
/// Returns [false] if not removed.
@Deprecated('Use [cancelSend()] instead.')
Future<bool> remove() async {
final room = this.room;
if (!status.isSent) {
await room.client.database?.removeEvent(eventId, room.id);
room.client.onEvent.add(EventUpdate(
roomID: room.id,
type: EventUpdateType.timeline,
content: {
'event_id': eventId,
'status': EventStatus.removed.intValue,
'content': {'body': 'Removed...'}
},
));
try {
await cancelSend();
return true;
}
} catch (_) {
return false;
}
}
/// Removes an unsent or yet-to-send event from the database and timeline.
/// These are events marked with the status `SENDING` or `ERROR`.
/// Throws an exception if used for an already sent event!
Future<void> cancelSend() async {
if (status.isSent) {
throw Exception('Can only delete events which are not sent yet!');
}
await room.client.database?.removeEvent(eventId, room.id);
room.client.onCancelSendEvent.add(eventId);
}
/// Try to send this event again. Only works with events of status -1.
Future<String?> sendAgain({String? txid}) async {
@ -358,7 +357,7 @@ class Event extends MatrixEvent {
}.contains(messageType)) {
final file = room.sendingFilePlaceholders[eventId];
if (file == null) {
await remove();
await cancelSend();
throw Exception('Can not try to send again. File is no longer cached.');
}
final thumbnail = room.sendingFileThumbnails[eventId];

View File

@ -6,7 +6,6 @@
/// - synced: (event came from sync loop)
/// - roomState
enum EventStatus {
removed,
error,
sending,
sent,
@ -33,7 +32,6 @@ EventStatus latestEventStatus(EventStatus status1, EventStatus status2) =>
extension EventStatusExtension on EventStatus {
/// Returns int value of the event status.
///
/// - -2 == removed;
/// - -1 == error;
/// - 0 == sending;
/// - 1 == sent;
@ -41,9 +39,6 @@ extension EventStatusExtension on EventStatus {
/// - 3 == roomState;
int get intValue => (index - 2);
/// Return `true` if the `EventStatus` equals `removed`.
bool get isRemoved => this == EventStatus.removed;
/// Return `true` if the `EventStatus` equals `error`.
bool get isError => this == EventStatus.error;

View File

@ -44,6 +44,7 @@ class Timeline {
StreamSubscription<EventUpdate>? sub;
StreamSubscription<SyncUpdate>? roomSub;
StreamSubscription<String>? sessionIdReceivedSub;
StreamSubscription<String>? cancelSendEventSub;
bool isRequestingHistory = false;
bool isRequestingFuture = false;
@ -278,6 +279,8 @@ class Timeline {
sessionIdReceivedSub =
room.onSessionKeyReceived.stream.listen(_sessionKeyReceived);
cancelSendEventSub =
room.client.onCancelSendEvent.stream.listen(_cleanUpCancelledEvent);
// we want to populate our aggregated events
for (final e in events) {
@ -291,6 +294,16 @@ class Timeline {
}
}
void _cleanUpCancelledEvent(String eventId) {
final i = _findEvent(event_id: eventId);
if (i < events.length) {
removeAggregatedEvent(events[i]);
events.removeAt(i);
onRemove?.call(i);
onUpdate?.call();
}
}
/// Removes all entries from [events] which are not in this SyncUpdate.
void _removeEventsNotInThisSync(SyncUpdate sync) {
final newSyncEvents = sync.rooms?.join?[room.id]?.timeline?.events ?? [];
@ -306,6 +319,8 @@ class Timeline {
roomSub?.cancel();
// ignore: discarded_futures
sessionIdReceivedSub?.cancel();
// ignore: discarded_futures
cancelSendEventSub?.cancel();
}
void _sessionKeyReceived(String sessionId) async {
@ -462,14 +477,6 @@ class Timeline {
: null) ??
EventStatus.synced.intValue);
if (status.isRemoved) {
final i = _findEvent(event_id: eventUpdate.content['event_id']);
if (i < events.length) {
removeAggregatedEvent(events[i]);
events.removeAt(i);
onRemove?.call(i);
}
} else {
final i = _findEvent(
event_id: eventUpdate.content['event_id'],
unsigned_txid: eventUpdate.content['unsigned'] is Map
@ -511,7 +518,6 @@ class Timeline {
addAggregatedEvent(newEvent);
}
}
// Handle redaction events
if (eventUpdate.content['type'] == EventTypes.Redaction) {

View File

@ -242,12 +242,12 @@ void main() {
test('remove', () async {
final event = Event.fromJson(
jsonObj, Room(id: '1234', client: Client('testclient')));
final removed1 = await event.remove();
jsonObj,
Room(id: '1234', client: Client('testclient')),
);
expect(() async => await event.cancelSend(), throwsException);
event.status = EventStatus.sending;
final removed2 = await event.remove();
expect(removed1, false);
expect(removed2, true);
await event.cancelSend();
});
test('sendAgain', () async {

View File

@ -270,7 +270,7 @@ void main() {
));
await waitForCount(7);
await timeline.events[0].remove();
await timeline.events[0].cancelSend();
await waitForCount(8);
expect(updateCount, 8);

View File

@ -507,7 +507,7 @@ void main() {
));
await waitForCount(1);
await timeline.events[0].remove();
await timeline.events[0].cancelSend();
await waitForCount(2);