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:
parent
e4acb1f510
commit
501c457ea1
|
|
@ -1312,6 +1312,9 @@ class Client extends MatrixApi {
|
||||||
|
|
||||||
final CachedStreamController<Event> onGroupMember = CachedStreamController();
|
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
|
/// When a state in a room has been updated this will return the room ID
|
||||||
/// and the state event.
|
/// and the state event.
|
||||||
final CachedStreamController<({String roomId, StrippedStateEvent state})>
|
final CachedStreamController<({String roomId, StrippedStateEvent state})>
|
||||||
|
|
|
||||||
|
|
@ -322,28 +322,27 @@ class Event extends MatrixEvent {
|
||||||
return receiptsList;
|
return receiptsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes this event if the status is [sending], [error] or [removed].
|
@Deprecated('Use [cancelSend()] instead.')
|
||||||
/// This event will just be removed from the database and the timelines.
|
|
||||||
/// Returns [false] if not removed.
|
|
||||||
Future<bool> remove() async {
|
Future<bool> remove() async {
|
||||||
final room = this.room;
|
try {
|
||||||
|
await cancelSend();
|
||||||
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...'}
|
|
||||||
},
|
|
||||||
));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
} catch (_) {
|
||||||
return false;
|
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.
|
/// Try to send this event again. Only works with events of status -1.
|
||||||
Future<String?> sendAgain({String? txid}) async {
|
Future<String?> sendAgain({String? txid}) async {
|
||||||
|
|
@ -358,7 +357,7 @@ class Event extends MatrixEvent {
|
||||||
}.contains(messageType)) {
|
}.contains(messageType)) {
|
||||||
final file = room.sendingFilePlaceholders[eventId];
|
final file = room.sendingFilePlaceholders[eventId];
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
await remove();
|
await cancelSend();
|
||||||
throw Exception('Can not try to send again. File is no longer cached.');
|
throw Exception('Can not try to send again. File is no longer cached.');
|
||||||
}
|
}
|
||||||
final thumbnail = room.sendingFileThumbnails[eventId];
|
final thumbnail = room.sendingFileThumbnails[eventId];
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
/// - synced: (event came from sync loop)
|
/// - synced: (event came from sync loop)
|
||||||
/// - roomState
|
/// - roomState
|
||||||
enum EventStatus {
|
enum EventStatus {
|
||||||
removed,
|
|
||||||
error,
|
error,
|
||||||
sending,
|
sending,
|
||||||
sent,
|
sent,
|
||||||
|
|
@ -33,7 +32,6 @@ EventStatus latestEventStatus(EventStatus status1, EventStatus status2) =>
|
||||||
extension EventStatusExtension on EventStatus {
|
extension EventStatusExtension on EventStatus {
|
||||||
/// Returns int value of the event status.
|
/// Returns int value of the event status.
|
||||||
///
|
///
|
||||||
/// - -2 == removed;
|
|
||||||
/// - -1 == error;
|
/// - -1 == error;
|
||||||
/// - 0 == sending;
|
/// - 0 == sending;
|
||||||
/// - 1 == sent;
|
/// - 1 == sent;
|
||||||
|
|
@ -41,9 +39,6 @@ extension EventStatusExtension on EventStatus {
|
||||||
/// - 3 == roomState;
|
/// - 3 == roomState;
|
||||||
int get intValue => (index - 2);
|
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`.
|
/// Return `true` if the `EventStatus` equals `error`.
|
||||||
bool get isError => this == EventStatus.error;
|
bool get isError => this == EventStatus.error;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ class Timeline {
|
||||||
StreamSubscription<EventUpdate>? sub;
|
StreamSubscription<EventUpdate>? sub;
|
||||||
StreamSubscription<SyncUpdate>? roomSub;
|
StreamSubscription<SyncUpdate>? roomSub;
|
||||||
StreamSubscription<String>? sessionIdReceivedSub;
|
StreamSubscription<String>? sessionIdReceivedSub;
|
||||||
|
StreamSubscription<String>? cancelSendEventSub;
|
||||||
bool isRequestingHistory = false;
|
bool isRequestingHistory = false;
|
||||||
bool isRequestingFuture = false;
|
bool isRequestingFuture = false;
|
||||||
|
|
||||||
|
|
@ -278,6 +279,8 @@ class Timeline {
|
||||||
|
|
||||||
sessionIdReceivedSub =
|
sessionIdReceivedSub =
|
||||||
room.onSessionKeyReceived.stream.listen(_sessionKeyReceived);
|
room.onSessionKeyReceived.stream.listen(_sessionKeyReceived);
|
||||||
|
cancelSendEventSub =
|
||||||
|
room.client.onCancelSendEvent.stream.listen(_cleanUpCancelledEvent);
|
||||||
|
|
||||||
// we want to populate our aggregated events
|
// we want to populate our aggregated events
|
||||||
for (final e in 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.
|
/// Removes all entries from [events] which are not in this SyncUpdate.
|
||||||
void _removeEventsNotInThisSync(SyncUpdate sync) {
|
void _removeEventsNotInThisSync(SyncUpdate sync) {
|
||||||
final newSyncEvents = sync.rooms?.join?[room.id]?.timeline?.events ?? [];
|
final newSyncEvents = sync.rooms?.join?[room.id]?.timeline?.events ?? [];
|
||||||
|
|
@ -306,6 +319,8 @@ class Timeline {
|
||||||
roomSub?.cancel();
|
roomSub?.cancel();
|
||||||
// ignore: discarded_futures
|
// ignore: discarded_futures
|
||||||
sessionIdReceivedSub?.cancel();
|
sessionIdReceivedSub?.cancel();
|
||||||
|
// ignore: discarded_futures
|
||||||
|
cancelSendEventSub?.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sessionKeyReceived(String sessionId) async {
|
void _sessionKeyReceived(String sessionId) async {
|
||||||
|
|
@ -462,14 +477,6 @@ class Timeline {
|
||||||
: null) ??
|
: null) ??
|
||||||
EventStatus.synced.intValue);
|
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(
|
final i = _findEvent(
|
||||||
event_id: eventUpdate.content['event_id'],
|
event_id: eventUpdate.content['event_id'],
|
||||||
unsigned_txid: eventUpdate.content['unsigned'] is Map
|
unsigned_txid: eventUpdate.content['unsigned'] is Map
|
||||||
|
|
@ -511,7 +518,6 @@ class Timeline {
|
||||||
|
|
||||||
addAggregatedEvent(newEvent);
|
addAggregatedEvent(newEvent);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Handle redaction events
|
// Handle redaction events
|
||||||
if (eventUpdate.content['type'] == EventTypes.Redaction) {
|
if (eventUpdate.content['type'] == EventTypes.Redaction) {
|
||||||
|
|
|
||||||
|
|
@ -242,12 +242,12 @@ void main() {
|
||||||
|
|
||||||
test('remove', () async {
|
test('remove', () async {
|
||||||
final event = Event.fromJson(
|
final event = Event.fromJson(
|
||||||
jsonObj, Room(id: '1234', client: Client('testclient')));
|
jsonObj,
|
||||||
final removed1 = await event.remove();
|
Room(id: '1234', client: Client('testclient')),
|
||||||
|
);
|
||||||
|
expect(() async => await event.cancelSend(), throwsException);
|
||||||
event.status = EventStatus.sending;
|
event.status = EventStatus.sending;
|
||||||
final removed2 = await event.remove();
|
await event.cancelSend();
|
||||||
expect(removed1, false);
|
|
||||||
expect(removed2, true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sendAgain', () async {
|
test('sendAgain', () async {
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,7 @@ void main() {
|
||||||
));
|
));
|
||||||
await waitForCount(7);
|
await waitForCount(7);
|
||||||
|
|
||||||
await timeline.events[0].remove();
|
await timeline.events[0].cancelSend();
|
||||||
|
|
||||||
await waitForCount(8);
|
await waitForCount(8);
|
||||||
expect(updateCount, 8);
|
expect(updateCount, 8);
|
||||||
|
|
|
||||||
|
|
@ -507,7 +507,7 @@ void main() {
|
||||||
));
|
));
|
||||||
await waitForCount(1);
|
await waitForCount(1);
|
||||||
|
|
||||||
await timeline.events[0].remove();
|
await timeline.events[0].cancelSend();
|
||||||
|
|
||||||
await waitForCount(2);
|
await waitForCount(2);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue