This commit is contained in:
Bruno Windels 2021-06-08 13:20:55 +02:00
parent 280de98858
commit 2ebadb36c3
8 changed files with 126 additions and 37 deletions

View file

@ -34,7 +34,7 @@ export class ReactionsViewModel {
this._map.update(key);
}
} else {
this._map.add(key, new ReactionViewModel(key, annotation, 0, this._parentEntry));
this._map.add(key, new ReactionViewModel(key, annotation, null, this._parentEntry));
}
}
}
@ -61,7 +61,7 @@ export class ReactionsViewModel {
this._map.update(existingKey);
}
} else if (!hasPending) {
if (this._map.get(existingKey)._tryUpdatePending(0)) {
if (this._map.get(existingKey)._tryUpdatePending(null)) {
this._map.update(existingKey);
}
}
@ -109,11 +109,18 @@ class ReactionViewModel {
}
get count() {
return (this._annotation?.count || 0) + this._pendingCount;
let count = 0;
if (this._annotation) {
count += this._annotation.count;
}
if (this._pendingCount !== null) {
count += this._pendingCount;
}
return count;
}
get isPending() {
return this._pendingCount !== 0;
return this._pendingCount !== null;
}
get haveReacted() {
@ -137,7 +144,10 @@ class ReactionViewModel {
}
toggleReaction() {
if (this.haveReacted) {
const havePendingReaction = this._pendingCount > 0;
const haveRemoteReaction = this._annotation?.me;
const haveReaction = havePendingReaction || haveRemoteReaction;
if (haveReaction) {
return this._parentEntry.redactReaction(this.key);
} else {
return this._parentEntry.react(this.key);

View file

@ -123,15 +123,30 @@ export class BaseMessageTile extends SimpleTile {
return true;
}
react(key) {
return this._room.sendEvent("m.reaction", this._entry.annotate(key));
react(key, log = null) {
return this.logger.wrapOrRun(log, "react", log => {
// this assumes the existing reaction is not a remote one
// we would need to do getOwnAnnotation(Id) and see if there are any pending redactions for it
const pee = this._entry.getPendingAnnotationEntry(key);
const redaction = pee?.pendingRedaction;
log.set("has_redaction", !!redaction);
log.set("has_redaction", !!redaction);
if (redaction && !redaction.hasStartedSending) {
log.set("abort_redaction", true);
return redaction.pendingEvent.abort();
} else {
return this._room.sendEvent("m.reaction", this._entry.annotate(key), null, log);
}
});
}
async redactReaction(key) {
const id = await this._entry.getOwnAnnotationId(this._room, key);
if (id) {
this._room.sendRedaction(id);
}
async redactReaction(key, log = null) {
return this.logger.wrapOrRun(log, "redactReaction", log => {
const id = await this._entry.getOwnAnnotationId(this._room, key);
if (id) {
this._room.sendRedaction(id, null, log);
}
});
}
_updateReactions() {

View file

@ -54,8 +54,7 @@ export class SimpleTile extends ViewModel {
get canAbortSending() {
return this._entry.isPending &&
this._entry.pendingEvent.status !== SendStatus.Sending &&
this._entry.pendingEvent.status !== SendStatus.Sent;
!this._entry.pendingEvent.hasStartedSending;
}
abortSending() {

View file

@ -116,6 +116,10 @@ export class PendingEvent {
get status() { return this._status; }
get error() { return this._error; }
get hasStartedSending() {
return this._status !== SendStatus.Sending && this._status !== SendStatus.Sent;
}
get attachmentsTotalBytes() {
return this._attachmentsTotalBytes;
}

View file

@ -157,8 +157,8 @@ export class SendQueue {
}
async _removeEvent(pendingEvent) {
const idx = this._pendingEvents.array.indexOf(pendingEvent);
if (idx !== -1) {
let hasEvent = this._pendingEvents.array.indexOf(pendingEvent) !== -1;
if (hasEvent) {
const txn = await this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);
try {
txn.pendingEvents.remove(pendingEvent.roomId, pendingEvent.queueIndex);
@ -166,7 +166,12 @@ export class SendQueue {
txn.abort();
}
await txn.complete();
this._pendingEvents.remove(idx);
// lookup index after async txn is complete,
// to make sure we're not racing with anything
const idx = this._pendingEvents.array.indexOf(pendingEvent);
if (idx !== -1) {
this._pendingEvents.remove(idx);
}
}
pendingEvent.dispose();
}

View file

@ -22,30 +22,33 @@ export class PendingAnnotations {
this._entries = [];
}
add(pendingEventEntry) {
const relation = getRelationFromContent(pendingEventEntry.content);
/** adds either a pending annotation entry, or a remote annotation entry with a pending redaction */
add(annotationEntry) {
const relation = getRelationFromContent(annotationEntry.content);
const key = relation.key;
if (!key) {
return;
}
const count = this.aggregatedAnnotations.get(key) || 0;
//const addend = pendingEventEntry.isRedacted ? -1 : 1;
//this.aggregatedAnnotations.set(key, count + addend);
this.aggregatedAnnotations.set(key, count + 1);
this._entries.push(pendingEventEntry);
const addend = annotationEntry.isRedacted ? -1 : 1;
console.log("add", count, addend);
this.aggregatedAnnotations.set(key, count + addend);
this._entries.push(annotationEntry);
}
remove(pendingEventEntry) {
const idx = this._entries.indexOf(pendingEventEntry);
/** removes either a pending annotation entry, or a remote annotation entry with a pending redaction */
remove(annotationEntry) {
const idx = this._entries.indexOf(annotationEntry);
if (idx === -1) {
return;
}
this._entries.splice(idx, 1);
const relation = getRelationFromContent(pendingEventEntry.content);
const relation = getRelationFromContent(annotationEntry.content);
const key = relation.key;
let count = this.aggregatedAnnotations.get(key);
if (count !== undefined) {
count -= 1;
const addend = annotationEntry.isRedacted ? 1 : -1;
count += addend;
if (count <= 0) {
this.aggregatedAnnotations.delete(key);
} else {

View file

@ -22,6 +22,7 @@ import {TimelineReader} from "./persistence/TimelineReader.js";
import {PendingEventEntry} from "./entries/PendingEventEntry.js";
import {RoomMember} from "../members/RoomMember.js";
import {PowerLevels} from "./PowerLevels.js";
import {getRelationFromContent} from "./relations.js";
export class Timeline {
constructor({roomId, storage, closeCallback, fragmentIdComparer, pendingEvents, clock}) {
@ -101,20 +102,62 @@ export class Timeline {
if (this._pendingEvents) {
this._localEntries = new MappedList(this._pendingEvents, pe => {
const pee = new PendingEventEntry({pendingEvent: pe, member: this._ownMember, clock: this._clock});
this._applyAndEmitLocalRelationChange(pee.pendingEvent, target => target.addLocalRelation(pee));
this._onAddPendingEvent(pee);
return pee;
}, (pee, params) => {
// is sending but redacted, who do we detect that here to remove the relation?
pee.notifyUpdate(params);
}, pee => {
this._applyAndEmitLocalRelationChange(pee.pendingEvent, target => target.removeLocalRelation(pee));
});
}, pee => this._onRemovePendingEvent(pee));
} else {
this._localEntries = new ObservableArray();
}
this._allEntries = new ConcatList(this._remoteEntries, this._localEntries);
}
_onAddPendingEvent(pee) {
let redactedEntry;
this._applyAndEmitLocalRelationChange(pee.pendingEvent, target => {
const wasRedacted = target.isRedacted;
const params = target.addLocalRelation(pee);
if (!wasRedacted && target.isRedacted) {
redactedEntry = target;
}
return params;
});
console.log("redactedEntry", redactedEntry);
if (redactedEntry) {
const redactedRelation = getRelationFromContent(redactedEntry.content);
if (redactedRelation?.event_id) {
const found = this._remoteEntries.findAndUpdate(
e => e.id === redactedRelation.event_id,
relationTarget => relationTarget.addLocalRelation(redactedEntry) || false
);
console.log("found", found);
}
}
}
_onRemovePendingEvent(pee) {
let unredactedEntry;
this._applyAndEmitLocalRelationChange(pee.pendingEvent, target => {
const wasRedacted = target.isRedacted;
const params = target.removeLocalRelation(pee);
if (wasRedacted && !target.isRedacted) {
unredactedEntry = target;
}
return params;
});
if (unredactedEntry) {
const redactedRelation = getRelationFromContent(unredactedEntry.content);
if (redactedRelation?.event_id) {
this._remoteEntries.findAndUpdate(
e => e.id === redactedRelation.event_id,
relationTarget => relationTarget.removeLocalRelation(unredactedEntry) || false
);
}
}
}
_applyAndEmitLocalRelationChange(pe, updater) {
const updateOrFalse = e => {
const params = updater(e);

View file

@ -42,7 +42,7 @@ export class BaseEventEntry extends BaseEntry {
}
/**
aggregates local relation.
aggregates local relation or local redaction of remote relation.
@return [string] returns the name of the field that has changed, if any
*/
addLocalRelation(entry) {
@ -102,6 +102,13 @@ export class BaseEventEntry extends BaseEntry {
}
}
get pendingRedaction() {
if (this._pendingRedactions) {
return this._pendingRedactions[0];
}
return null;
}
annotate(key) {
return createAnnotation(this.id, key);
}
@ -111,7 +118,10 @@ export class BaseEventEntry extends BaseEntry {
}
async getOwnAnnotationId(room, key) {
const pendingEvent = this._pendingAnnotations?.findForKey(key);
return pendingEvent?.id;
return this.getPendingAnnotationEntry(key)?.id;
}
getPendingAnnotationEntry(key) {
return this._pendingAnnotations?.findForKey(key);
}
}