diff --git a/src/domain/session/room/timeline/ReactionsViewModel.js b/src/domain/session/room/timeline/ReactionsViewModel.js index 55e965a9..6a813d03 100644 --- a/src/domain/session/room/timeline/ReactionsViewModel.js +++ b/src/domain/session/room/timeline/ReactionsViewModel.js @@ -79,6 +79,7 @@ class ReactionViewModel { this._annotation = annotation; this._pendingCount = pendingCount; this._parentEntry = parentEntry; + this._isToggling = false; } _tryUpdate(annotation) { @@ -143,14 +144,24 @@ class ReactionViewModel { } } - toggleReaction() { - 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); + async toggleReaction() { + if (this._isToggling) { + console.log("blocking toggleReaction, call ongoing"); + return; + } + this._isToggling = true; + try { + const haveLocalRedaction = this._pendingCount < 0; + const havePendingReaction = this._pendingCount > 0; + const haveRemoteReaction = this._annotation?.me; + const haveReaction = havePendingReaction || (haveRemoteReaction && !haveLocalRedaction); + if (haveReaction) { + await this._parentEntry.redactReaction(this.key); + } else { + await this._parentEntry.react(this.key); + } + } finally { + this._isToggling = false; } } } diff --git a/src/domain/session/room/timeline/tiles/BaseMessageTile.js b/src/domain/session/room/timeline/tiles/BaseMessageTile.js index c8602b83..d4bbcbaa 100644 --- a/src/domain/session/room/timeline/tiles/BaseMessageTile.js +++ b/src/domain/session/room/timeline/tiles/BaseMessageTile.js @@ -124,27 +124,23 @@ export class BaseMessageTile extends SimpleTile { } 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) { + return this.logger.wrapOrRun(log, "react", async log => { + const existingAnnotation = await this._entry.getOwnAnnotationEntry(this._timeline, key); + const redaction = existingAnnotation?.pendingRedaction; + if (redaction && !redaction.pendingEvent.hasStartedSending) { log.set("abort_redaction", true); - return redaction.pendingEvent.abort(); + await redaction.pendingEvent.abort(); } else { - return this._room.sendEvent("m.reaction", this._entry.annotate(key), null, log); + await this._room.sendEvent("m.reaction", this._entry.annotate(key), null, log); } }); } - 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); + redactReaction(key, log = null) { + return this.logger.wrapOrRun(log, "redactReaction", async log => { + const entry = await this._entry.getOwnAnnotationEntry(this._timeline, key); + if (entry) { + await this._room.sendRedaction(entry.id, null, log); } }); } diff --git a/src/domain/session/room/timeline/tiles/SimpleTile.js b/src/domain/session/room/timeline/tiles/SimpleTile.js index 8abcc57d..6ec913c0 100644 --- a/src/domain/session/room/timeline/tiles/SimpleTile.js +++ b/src/domain/session/room/timeline/tiles/SimpleTile.js @@ -129,8 +129,12 @@ export class SimpleTile extends ViewModel { return this._options.room; } + get _timeline() { + return this._options.timeline; + } + get _powerLevels() { - return this._options.timeline.powerLevels; + return this._timeline.powerLevels; } get _ownMember() { diff --git a/src/matrix/room/BaseRoom.js b/src/matrix/room/BaseRoom.js index 2513f67b..42dc6162 100644 --- a/src/matrix/room/BaseRoom.js +++ b/src/matrix/room/BaseRoom.js @@ -452,7 +452,7 @@ export class BaseRoom extends EventEmitter { return observable; } - async getOwnAnnotationEventId(targetId, key) { + async getOwnAnnotationEntry(targetId, key) { const txn = await this._storage.readWriteTxn([ this._storage.storeNames.timelineEvents, this._storage.storeNames.timelineRelations, @@ -461,7 +461,9 @@ export class BaseRoom extends EventEmitter { for (const relation of relations) { const annotation = await txn.timelineEvents.getByEventId(this.id, relation.sourceEventId); if (annotation.event.sender === this._user.id && getRelation(annotation.event).key === key) { - return annotation.event.event_id; + const eventEntry = new EventEntry(annotation, this._fragmentIdComparer); + // add local relations + return eventEntry; } } return null; diff --git a/src/matrix/room/Room.js b/src/matrix/room/Room.js index 0361e069..62b5c3ff 100644 --- a/src/matrix/room/Room.js +++ b/src/matrix/room/Room.js @@ -303,7 +303,7 @@ export class Room extends BaseRoom { /** @public */ sendEvent(eventType, content, attachments, log = null) { - this._platform.logger.wrapOrRun(log, "send", log => { + return this._platform.logger.wrapOrRun(log, "send", log => { log.set("id", this.id); return this._sendQueue.enqueueEvent(eventType, content, attachments, log); }); @@ -311,7 +311,7 @@ export class Room extends BaseRoom { /** @public */ sendRedaction(eventIdOrTxnId, reason, log = null) { - this._platform.logger.wrapOrRun(log, "redact", log => { + return this._platform.logger.wrapOrRun(log, "redact", log => { log.set("id", this.id); return this._sendQueue.enqueueRedaction(eventIdOrTxnId, reason, log); }); diff --git a/src/matrix/room/sending/PendingEvent.js b/src/matrix/room/sending/PendingEvent.js index f1672448..9f54e3c3 100644 --- a/src/matrix/room/sending/PendingEvent.js +++ b/src/matrix/room/sending/PendingEvent.js @@ -117,7 +117,7 @@ export class PendingEvent { get error() { return this._error; } get hasStartedSending() { - return this._status !== SendStatus.Sending && this._status !== SendStatus.Sent; + return this._status === SendStatus.Sending || this._status === SendStatus.Sent; } get attachmentsTotalBytes() { diff --git a/src/matrix/room/timeline/Timeline.js b/src/matrix/room/timeline/Timeline.js index b567eff8..806c86e3 100644 --- a/src/matrix/room/timeline/Timeline.js +++ b/src/matrix/room/timeline/Timeline.js @@ -22,7 +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"; +import {getRelationFromContent, getRelation, ANNOTATION_RELATION_TYPE} from "./relations.js"; export class Timeline { constructor({roomId, storage, closeCallback, fragmentIdComparer, pendingEvents, clock}) { @@ -124,7 +124,6 @@ export class Timeline { } return params; }); - console.log("redactedEntry", redactedEntry); if (redactedEntry) { const redactedRelation = getRelationFromContent(redactedEntry.content); if (redactedRelation?.event_id) { @@ -132,7 +131,6 @@ export class Timeline { e => e.id === redactedRelation.event_id, relationTarget => relationTarget.addLocalRelation(redactedEntry) || false ); - console.log("found", found); } } } @@ -182,6 +180,24 @@ export class Timeline { } } + + async getOwnAnnotationEntry(targetId, key) { + const txn = await this._storage.readWriteTxn([ + this._storage.storeNames.timelineEvents, + this._storage.storeNames.timelineRelations, + ]); + const relations = await txn.timelineRelations.getForTargetAndType(this._roomId, targetId, ANNOTATION_RELATION_TYPE); + for (const relation of relations) { + const annotation = await txn.timelineEvents.getByEventId(this._roomId, relation.sourceEventId); + if (annotation.event.sender === this._ownMember.userId && getRelation(annotation.event).key === key) { + const eventEntry = new EventEntry(annotation, this._fragmentIdComparer); + this._addLocalRelationsToNewRemoteEntries([eventEntry]); + return eventEntry; + } + } + return null; + } + updateOwnMember(member) { this._ownMember = member; } diff --git a/src/matrix/room/timeline/entries/BaseEventEntry.js b/src/matrix/room/timeline/entries/BaseEventEntry.js index b32494e8..09132a21 100644 --- a/src/matrix/room/timeline/entries/BaseEventEntry.js +++ b/src/matrix/room/timeline/entries/BaseEventEntry.js @@ -117,11 +117,7 @@ export class BaseEventEntry extends BaseEntry { return this._pendingAnnotations?.aggregatedAnnotations; } - async getOwnAnnotationId(room, key) { - return this.getPendingAnnotationEntry(key)?.id; - } - - getPendingAnnotationEntry(key) { + async getOwnAnnotationEntry(timeline, key) { return this._pendingAnnotations?.findForKey(key); } } diff --git a/src/matrix/room/timeline/entries/EventEntry.js b/src/matrix/room/timeline/entries/EventEntry.js index a106ef7b..2aa9cba0 100644 --- a/src/matrix/room/timeline/entries/EventEntry.js +++ b/src/matrix/room/timeline/entries/EventEntry.js @@ -131,12 +131,12 @@ export class EventEntry extends BaseEventEntry { return this._eventEntry.annotations; } - async getOwnAnnotationId(room, key) { - const localId = await super.getOwnAnnotationId(room, key); + async getOwnAnnotationEntry(timeline, key) { + const localId = await super.getOwnAnnotationEntry(timeline, key); if (localId) { return localId; } else { - return room.getOwnAnnotationEventId(this.id, key); + return timeline.getOwnAnnotationEntry(this.id, key); } } -} \ No newline at end of file +} diff --git a/src/platform/web/ui/css/themes/element/timeline.css b/src/platform/web/ui/css/themes/element/timeline.css index 785dc1cc..b2a425ff 100644 --- a/src/platform/web/ui/css/themes/element/timeline.css +++ b/src/platform/web/ui/css/themes/element/timeline.css @@ -243,7 +243,7 @@ only loads when the top comes into view*/ .Timeline_messageReactions button.haveReacted.isPending { animation-name: glow-reaction-border; - animation-duration: 1s; + animation-duration: 0.8s; animation-direction: alternate; animation-iteration-count: infinite; animation-timing-function: linear;