1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-06 12:02:40 +03:00

Support for cancelling pending events

Implement client.cancelPendingEvent which will cancel queued or not_sent events
This commit is contained in:
Richard van der Hoff
2016-03-17 20:34:58 +00:00
parent fdbc7a3112
commit 02be0f659a
5 changed files with 168 additions and 5 deletions

View File

@@ -725,6 +725,28 @@ MatrixClient.prototype.resendEvent = function(event, room) {
return _sendEvent(this, room, event);
};
/**
* Cancel a queued or unsent event.
*
* @param {MatrixEvent} event Event to cancel
* @throws Error if the event is not in QUEUED or NOT_SENT state
*/
MatrixClient.prototype.cancelPendingEvent = function(event) {
if ([EventStatus.QUEUED, EventStatus.NOT_SENT].indexOf(event.status) < 0) {
throw new Error("cannot cancel an event with status " + event.status);
}
// first tell the scheduler to forget about it, if it's queued
if (this.scheduler) {
this.scheduler.removeEventFromQueue(event);
}
// then tell the room about the change of state, which will remove it
// from the room's list of pending events.
var room = this.getRoom(event.getRoomId());
_updatePendingEventStatus(room, event, EventStatus.CANCELLED);
};
/**
* @param {string} roomId
* @param {string} name

View File

@@ -36,6 +36,9 @@ module.exports.EventStatus = {
/** The event has been sent to the server, but we have not yet received the
* echo. */
SENT: "sent",
/** The event was cancelled before it was successfully sent. */
CANCELLED: "cancelled",
};
/**

View File

@@ -729,13 +729,16 @@ ALLOWED_TRANSITIONS[EventStatus.SENDING] =
[EventStatus.QUEUED, EventStatus.NOT_SENT, EventStatus.SENT];
ALLOWED_TRANSITIONS[EventStatus.QUEUED] =
[EventStatus.SENDING];
[EventStatus.SENDING, EventStatus.CANCELLED];
ALLOWED_TRANSITIONS[EventStatus.SENT] =
[];
ALLOWED_TRANSITIONS[EventStatus.NOT_SENT] =
[EventStatus.SENDING, EventStatus.QUEUED];
[EventStatus.SENDING, EventStatus.QUEUED, EventStatus.CANCELLED];
ALLOWED_TRANSITIONS[EventStatus.CANCELLED] =
[];
/**
* Update the status / event id on a pending event, to reflect its transmission
@@ -797,6 +800,17 @@ Room.prototype.updatePendingEvent = function(event, newStatus, newEventId) {
this._eventIdToTimeline[newEventId] = existingTimeline;
}
}
else if (newStatus == EventStatus.CANCELLED) {
// remove it from the pending event list, or the timeline.
if (this._pendingEventList) {
utils.removeElement(
this._pendingEventList,
function(ev) { return ev.getId() == oldEventId; },
false
);
}
this.removeEvent(oldEventId);
}
this.emit("Room.localEchoUpdated", event, this, event.getId(), oldStatus);
};
@@ -1479,9 +1493,10 @@ module.exports = Room;
* arrived, the event is updated with a new event id and the status is set to
* 'SENT'. The server-generated fields are of course not updated yet.
*
* <p>Finally, the /send might fail. In this case, the event's status is set to
* <p>If the /send fails, In this case, the event's status is set to
* 'NOT_SENT'. If it is later resent, the process starts again, setting the
* status to 'SENDING'.
* status to 'SENDING'. Alternatively, the message may be cancelled, which
* removes the event from the room, and sets the status to 'CANCELLED'.
*
* <p>This event is raised to reflect each of the transitions above.
*

View File

@@ -2,22 +2,30 @@
var sdk = require("../..");
var HttpBackend = require("../mock-request");
var utils = require("../test-utils");
var EventStatus = sdk.EventStatus;
describe("MatrixClient retrying", function() {
var baseUrl = "http://localhost.or.something";
var client, httpBackend;
var scheduler;
var userId = "@alice:localhost";
var accessToken = "aseukfgwef";
var roomId = "!room:here";
var room;
beforeEach(function() {
utils.beforeEach(this);
httpBackend = new HttpBackend();
sdk.request(httpBackend.requestFn);
scheduler = new sdk.MatrixScheduler();
client = sdk.createClient({
baseUrl: baseUrl,
userId: userId,
accessToken: accessToken
accessToken: accessToken,
scheduler: scheduler,
});
room = new sdk.Room(roomId);
client.store.storeRoom(room);
});
afterEach(function() {
@@ -40,6 +48,49 @@ describe("MatrixClient retrying", function() {
});
it("should mark events as EventStatus.CANCELLED when cancelled", function(done) {
// send a couple of events; the second will be queued
var ev1, ev2;
client.sendMessage(roomId, "m1").then(function(ev) {
expect(ev).toEqual(ev1);
});
client.sendMessage(roomId, "m2").then(function(ev) {
expect(ev).toEqual(ev2);
});
// both events should be in the timeline at this point
var tl = room.getLiveTimeline().getEvents();
expect(tl.length).toEqual(2);
ev1 = tl[0];
ev2 = tl[1];
expect(ev1.status).toEqual(EventStatus.SENDING);
expect(ev2.status).toEqual(EventStatus.QUEUED);
// now we can cancel the second and check everything looks sane
client.cancelPendingEvent(ev2);
expect(ev2.status).toEqual(EventStatus.CANCELLED);
expect(tl.length).toEqual(1);
// shouldn't be able to cancel the first message yet
expect(function() { client.cancelPendingEvent(ev1); })
.toThrow();
// fail the first send
httpBackend.when("PUT", "/send/m.room.message/")
.respond(400);
httpBackend.flush().then(function() {
expect(ev1.status).toEqual(EventStatus.NOT_SENT);
expect(tl.length).toEqual(1);
// cancel the first message
client.cancelPendingEvent(ev1);
expect(ev1.status).toEqual(EventStatus.CANCELLED);
expect(tl.length).toEqual(0);
}).catch(utils.failTest).done(done);
});
describe("resending", function() {
xit("should be able to resend a NOT_SENT event", function() {

View File

@@ -1225,4 +1225,76 @@ describe("Room", function() {
);
});
});
describe("updatePendingEvent", function() {
it("should remove cancelled events from the pending list", function() {
var room = new Room(roomId, {
pendingEventOrdering: "detached"
});
var eventA = utils.mkMessage({
room: roomId, user: userA, event: true
});
eventA.status = EventStatus.SENDING;
var eventId = eventA.getId();
room.addPendingEvent(eventA, "TXN1");
expect(room.getPendingEvents()).toEqual(
[eventA]
);
// the event has to have been failed or queued before it can be
// cancelled
room.updatePendingEvent(eventA, EventStatus.NOT_SENT);
var callCount = 0;
room.on("Room.localEchoUpdated",
function(event, emitRoom, oldEventId, oldStatus) {
expect(event).toEqual(eventA);
expect(event.status).toEqual(EventStatus.CANCELLED);
expect(emitRoom).toEqual(room);
expect(oldEventId).toEqual(eventId);
expect(oldStatus).toEqual(EventStatus.NOT_SENT);
callCount++;
});
room.updatePendingEvent(eventA, EventStatus.CANCELLED);
expect(room.getPendingEvents()).toEqual([]);
expect(callCount).toEqual(1);
});
it("should remove cancelled events from the timeline", function() {
var room = new Room(roomId);
var eventA = utils.mkMessage({
room: roomId, user: userA, event: true
});
eventA.status = EventStatus.SENDING;
var eventId = eventA.getId();
room.addPendingEvent(eventA, "TXN1");
expect(room.getLiveTimeline().getEvents()).toEqual(
[eventA]
);
// the event has to have been failed or queued before it can be
// cancelled
room.updatePendingEvent(eventA, EventStatus.NOT_SENT);
var callCount = 0;
room.on("Room.localEchoUpdated",
function(event, emitRoom, oldEventId, oldStatus) {
expect(event).toEqual(eventA);
expect(event.status).toEqual(EventStatus.CANCELLED);
expect(emitRoom).toEqual(room);
expect(oldEventId).toEqual(eventId);
expect(oldStatus).toEqual(EventStatus.NOT_SENT);
callCount++;
});
room.updatePendingEvent(eventA, EventStatus.CANCELLED);
expect(room.getLiveTimeline().getEvents()).toEqual([]);
expect(callCount).toEqual(1);
});
});
});