forked from mystiq/hydrogen-web
WIP
This commit is contained in:
parent
280de98858
commit
2ebadb36c3
8 changed files with 126 additions and 37 deletions
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
@ -359,4 +364,4 @@ export function tests() {
|
|||
await poll(() => !queue._isSending);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -66,4 +69,4 @@ export class PendingAnnotations {
|
|||
get isEmpty() {
|
||||
return this._entries.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue