Initial implementation of "audio mode"

This commit is contained in:
N Pex 2023-01-30 08:36:02 +00:00
parent d5942fdb8e
commit 09173a65f1
14 changed files with 944 additions and 410 deletions

View file

@ -4,49 +4,39 @@
{{ $tc("room.invitations", invitationCount) }}
</div>
<ChatHeader class="chat-header flex-grow-0 flex-shrink-0" v-on:header-click="onHeaderClick" />
<div
class="chat-content flex-grow-1 flex-shrink-1"
ref="chatContainer"
v-on:scroll="onScroll"
@click="closeContextMenusIfOpen"
>
<AudioLayout ref="chatContainer" class="auto-audio-player-root" v-if="useAudioLayout" :room="room"
:events="events" :autoplay="!showRecorder"
:timelineSet="timelineSet"
:readMarker="readMarker"
v-on:start-recording="showRecorder = true"
v-on:loadnext="handleScrolledToBottom(false)"
v-on:loadprevious="handleScrolledToTop()"
v-on:mark-read="sendRR"
/>
<VoiceRecorder class="audio-layout" v-if="useAudioLayout" :micButtonRef="$refs.mic_button" :ptt="showRecorderPTT" :show="showRecorder"
v-on:close="showRecorder = false" v-on:file="onVoiceRecording" />
<div v-if="!useAudioLayout" class="chat-content flex-grow-1 flex-shrink-1" ref="chatContainer"
v-on:scroll="onScroll" @click="closeContextMenusIfOpen">
<div ref="messageOperationsStrut" class="message-operations-strut">
<message-operations
ref="messageOperations"
:style="opStyle"
:emojis="recentEmojis"
v-on:close="
showContextMenu = false;
showContextMenuAnchor = null;
"
v-if="showMessageOperations"
v-on:addreaction="addReaction"
v-on:addquickreaction="addQuickReaction"
v-on:addreply="addReply(selectedEvent)"
v-on:edit="edit(selectedEvent)"
v-on:redact="redact(selectedEvent)"
v-on:download="download(selectedEvent)"
v-on:more="
<message-operations ref="messageOperations" :style="opStyle" :emojis="recentEmojis" v-on:close="
showContextMenu = false;
showContextMenuAnchor = null;
" v-if="showMessageOperations" v-on:addreaction="addReaction" v-on:addquickreaction="addQuickReaction"
v-on:addreply="addReply(selectedEvent)" v-on:edit="edit(selectedEvent)" v-on:redact="redact(selectedEvent)"
v-on:download="download(selectedEvent)" v-on:more="
isEmojiQuickReaction= true
showMoreMessageOperations($event)
"
:event="selectedEvent"
/>
" :originalEvent="selectedEvent" />
</div>
<div ref="avatarOperationsStrut" class="avatar-operations-strut">
<avatar-operations
ref="avatarOperations"
:style="avatarOpStyle"
v-on:close="
showAvatarMenu = false;
showAvatarMenuAnchor = null;
"
v-on:start-private-chat="startPrivateChat($event)"
v-if="selectedEvent && showAvatarMenu"
:room="room"
:event="selectedEvent"
/>
<avatar-operations ref="avatarOperations" :style="avatarOpStyle" v-on:close="
showAvatarMenu = false;
showAvatarMenuAnchor = null;
" v-on:start-private-chat="startPrivateChat($event)" v-if="selectedEvent && showAvatarMenu" :room="room"
:originalEvent="selectedEvent" />
</div>
<!-- Handle resizes, e.g. when soft keyboard is shown/hidden -->
@ -59,55 +49,31 @@
<div v-if="showDayMarkerBeforeEvent(event)" class="day-marker" :title="dayForEvent(event)" />
<div v-if="!event.isRelation() && !event.isRedaction()" :ref="event.getId()">
<div
class="message-wrapper"
v-on:touchstart="
(e) => {
touchStart(e, event);
}
"
v-on:touchend="touchEnd"
v-on:touchcancel="touchCancel"
v-on:touchmove="touchMove"
>
<component
:is="componentForEvent(event)"
:room="room"
:event="event"
:nextEvent="events[index + 1]"
:timelineSet="timelineSet"
v-on:send-quick-reaction="sendQuickReaction"
v-on:context-menu="showContextMenuForEvent($event)"
v-on:own-avatar-clicked="viewProfile"
v-on:other-avatar-clicked="showAvatarMenuForEvent($event)"
v-on:download="download(event)"
v-on:poll-closed="pollWasClosed(event)"
/>
<div class="message-wrapper" v-on:touchstart="
(e) => {
touchStart(e, event);
}
" v-on:touchend="touchEnd" v-on:touchcancel="touchCancel" v-on:touchmove="touchMove">
<component :is="componentForEvent(event)" :room="room" :originalEvent="event" :nextEvent="events[index + 1]"
:timelineSet="timelineSet" v-on:send-quick-reaction="sendQuickReaction"
v-on:context-menu="showContextMenuForEvent($event)" v-on:own-avatar-clicked="viewProfile"
v-on:other-avatar-clicked="showAvatarMenuForEvent($event)" v-on:download="download(event)"
v-on:poll-closed="pollWasClosed(event)" />
<!-- <div v-if="debugging" style="user-select:text">EventID: {{ event.getId() }}</div> -->
<!-- <div v-if="debugging" style="user-select:text">Event: {{ JSON.stringify(event) }}</div> -->
<div
v-if="event.getId() == readMarker && index < events.length - 1"
class="read-marker"
:title="$t('message.unread_messages')"
/>
<div v-if="event.getId() == readMarker && index < events.length - 1" class="read-marker"
:title="$t('message.unread_messages')" />
</div>
</div>
</div>
</div>
<!-- Input area -->
<v-container v-if="room" fluid :class="['input-area-outer', replyToEvent ? 'reply-to' : '']">
<v-container v-if="!useAudioLayout && room" fluid :class="['input-area-outer', replyToEvent ? 'reply-to' : '']">
<div :class="[replyToEvent ? 'iput-area-inner-box' : '']">
<!-- "Scroll to end"-button -->
<v-btn
class="scroll-to-end"
v-show="showScrollToEnd"
fab
x-small
elevation="0"
color="black"
@click.stop="scrollToEndOfTimeline"
>
<v-btn v-if="!useAudioLayout" class="scroll-to-end" v-show="showScrollToEnd" fab x-small elevation="0" color="black"
@click.stop="scrollToEndOfTimeline">
<v-icon color="white">arrow_downward</v-icon>
</v-btn>
@ -121,10 +87,11 @@
<div v-if="replyToContentType === 'm.image'">{{ $t("message.reply_image") }}</div>
<div v-if="replyToContentType === 'm.audio'">{{ $t("message.reply_audio_message") }}</div>
<div v-if="replyToContentType === 'm.video'">{{ $t("message.reply_video") }}</div>
<div v-if="replyToContentType === 'm.poll'">{{ $t("message.reply_poll") }}</div>
<div v-if="replyToContentType === 'm.poll'">{{ $t("message.reply_poll") }}</div>
</div>
<div class="col col-auto" v-if="replyToContentType !== 'm.text'">
<img v-if="replyToContentType === 'm.image'" width="50px" height="50px" :src="replyToImg" class="rounded" />
<img v-if="replyToContentType === 'm.image'" width="50px" height="50px" :src="replyToImg"
class="rounded" />
<v-img v-if="replyToContentType === 'm.audio'" src="@/assets/icons/audio_message.svg" />
<v-img v-if="replyToContentType === 'm.video'" src="@/assets/icons/video_message.svg" />
<v-icon v-if="replyToContentType === 'm.poll'" light>$vuetify.icons.poll</v-icon>
@ -143,24 +110,13 @@
</v-row>
<v-row class="input-area-inner align-center">
<v-col class="flex-grow-1 flex-shrink-1 ma-0 pa-0">
<v-textarea
height="undefined"
ref="messageInput"
full-width
auto-grow
rows="1"
v-model="currentInput"
no-resize
class="input-area-text"
:placeholder="$t('message.your_message')"
hide-details
background-color="white"
v-on:keydown.enter.prevent="
<v-textarea height="undefined" ref="messageInput" full-width auto-grow rows="1" v-model="currentInput"
no-resize class="input-area-text" :placeholder="$t('message.your_message')" hide-details
background-color="white" v-on:keydown.enter.prevent="
() => {
sendCurrentTextMessage();
}
"
/>
" />
</v-col>
<v-col class="input-area-button text-center flex-grow-0 flex-shrink-1" v-if="editedEvent">
@ -169,150 +125,82 @@
</v-btn>
</v-col>
<v-col
v-if="(!currentInput || currentInput.length == 0) && !showRecorder && canCreatePoll"
class="input-area-button text-center flex-grow-0 flex-shrink-1"
>
<v-col v-if="(!currentInput || currentInput.length == 0) && !showRecorder && canCreatePoll"
class="input-area-button text-center flex-grow-0 flex-shrink-1">
<v-btn icon large color="black" @click="showCreatePollDialog = true">
<v-icon dark>$vuetify.icons.poll</v-icon>
</v-btn>
</v-col>
<v-col
class="input-area-button text-center flex-grow-0 flex-shrink-1"
v-if="!currentInput || currentInput.length == 0 || showRecorder"
>
<v-btn
v-if="canRecordAudio"
class="mic-button"
ref="mic_button"
fab
small
elevation="0"
v-blur
v-longTap:250="[showRecordingUI, startRecording]"
>
<v-col class="input-area-button text-center flex-grow-0 flex-shrink-1"
v-if="!currentInput || currentInput.length == 0 || showRecorder">
<v-btn v-if="canRecordAudio" class="mic-button" ref="mic_button" fab small elevation="0" v-blur
v-longTap:250="[showRecordingUI, startRecording]">
<v-icon :color="showRecorder ? 'white' : 'black'">mic</v-icon>
</v-btn>
<v-btn
v-else
class="mic-button"
ref="mic_button"
fab
small
elevation="0"
v-blur
@click.stop="showNoRecordingAvailableDialog = true"
>
<v-btn v-else class="mic-button" ref="mic_button" fab small elevation="0" v-blur
@click.stop="showNoRecordingAvailableDialog = true">
<v-icon :color="showRecorder ? 'white' : 'black'">mic</v-icon>
</v-btn>
</v-col>
<v-col class="input-area-button text-center flex-grow-0 flex-shrink-1" v-else>
<v-btn
fab
small
elevation="0"
color="black"
@click.stop="sendCurrentTextMessage"
:disabled="sendButtonDisabled"
>
<v-btn fab small elevation="0" color="black" @click.stop="sendCurrentTextMessage"
:disabled="sendButtonDisabled">
<v-icon color="white">{{ editedEvent ? "save" : "arrow_upward" }}</v-icon>
</v-btn>
</v-col>
<v-col
class="input-area-button text-center flex-grow-0 flex-shrink-1 input-more-icon"
>
<v-btn
fab
small
elevation="0"
v-blur
@click.stop="
isEmojiQuickReaction = false
showMoreMessageOperations($event)
"
>
<v-col class="input-area-button text-center flex-grow-0 flex-shrink-1 input-more-icon">
<v-btn fab small elevation="0" v-blur @click.stop="
isEmojiQuickReaction = false
showMoreMessageOperations($event)
">
<v-icon>$vuetify.icons.addReaction</v-icon>
</v-btn>
</v-col>
<v-col v-if="$config.shortCodeStickers" class="input-area-button text-center flex-grow-0 flex-shrink-1">
<v-btn
v-if="!showRecorder"
id="btn-attach"
icon
large
color="black"
@click="showStickerPicker"
:disabled="attachButtonDisabled"
>
<v-btn v-if="!showRecorder" id="btn-attach" icon large color="black" @click="showStickerPicker"
:disabled="attachButtonDisabled">
<v-icon large>face</v-icon>
</v-btn>
</v-col>
<v-col class="input-area-button text-center flex-grow-0 flex-shrink-1">
<label icon flat ref="attachmentLabel">
<v-btn
v-if="!showRecorder"
icon
large
color="black"
@click="showAttachmentPicker"
:disabled="attachButtonDisabled"
>
<v-btn v-if="!showRecorder" icon large color="black" @click="showAttachmentPicker"
:disabled="attachButtonDisabled">
<v-icon x-large>add_circle_outline</v-icon>
</v-btn>
<input
ref="attachment"
type="file"
name="attachment"
@change="handlePickedAttachment($event)"
accept="image/*|audio/*|video/*|application/pdf"
class="d-none"
/>
</label>
</v-col>
</v-row>
<VoiceRecorder
:micButtonRef="$refs.mic_button"
:ptt="showRecorderPTT"
:show="showRecorder"
v-on:close="showRecorder = false"
v-on:file="onVoiceRecording"
/>
<VoiceRecorder :micButtonRef="$refs.mic_button" :ptt="showRecorderPTT" :show="showRecorder"
v-on:close="showRecorder = false" v-on:file="onVoiceRecording" />
</div>
</v-container>
<input ref="attachment" type="file" name="attachment" @change="handlePickedAttachment($event)"
accept="image/*|audio/*|video/*|application/pdf" class="d-none" />
<div v-if="currentImageInputPath">
<v-dialog v-model="currentImageInputPath" class="ma-0 pa-0" :width="$vuetify.breakpoint.smAndUp ? '50%' : '85%'">
<v-card class="ma-0 pa-0">
<v-card-text class="ma-0 pa-2">
<v-img
v-if="currentImageInput && currentImageInput.image"
:aspect-ratio="1"
:src="currentImageInput.image"
contain
class="current-image-input-path"
/>
<v-img v-if="currentImageInput && currentImageInput.image" :aspect-ratio="1" :src="currentImageInput.image"
contain class="current-image-input-path" />
<div>
file: {{ currentImageInputPath.name }}
<span v-if="currentImageInput && currentImageInput.scaled && currentImageInput.useScaled">
{{ currentImageInput.scaledDimensions.width }} x {{ currentImageInput.scaledDimensions.height }}</span
>
{{ currentImageInput.scaledDimensions.width }} x {{ currentImageInput.scaledDimensions.height }}</span>
<span v-else-if="currentImageInput && currentImageInput.dimensions">
{{ currentImageInput.dimensions.width }} x {{ currentImageInput.dimensions.height }}</span
>
{{ currentImageInput.dimensions.width }} x {{ currentImageInput.dimensions.height }}</span>
<span v-if="currentImageInput && currentImageInput.scaled && currentImageInput.useScaled">
({{ formatBytes(currentImageInput.scaledSize) }})</span
>
({{ formatBytes(currentImageInput.scaledSize) }})</span>
<span v-else> ({{ formatBytes(currentImageInputPath.size) }})</span>
<v-switch
v-if="currentImageInput && currentImageInput.scaled"
:label="$t('message.scale_image')"
v-model="currentImageInput.useScaled"
/>
<v-switch v-if="currentImageInput && currentImageInput.scaled" :label="$t('message.scale_image')"
v-model="currentImageInput.useScaled" />
</div>
<div v-if="currentSendError">{{ currentSendError }}</div>
<div v-else>{{ currentSendProgress }}</div>
@ -321,17 +209,10 @@
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" text @click="cancelSendAttachment" id="btn-attachment-cancel">{{
$t("menu.cancel")
$t("menu.cancel")
}}</v-btn>
<v-btn
id="btn-attachment-send"
color="primary"
text
@click="sendAttachment"
v-if="currentSendShowSendButton"
:disabled="currentSendOperation != null"
>{{ $t("menu.send") }}</v-btn
>
<v-btn id="btn-attachment-send" color="primary" text @click="sendAttachment"
v-if="currentSendShowSendButton" :disabled="currentSendOperation != null">{{ $t("menu.send") }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
@ -363,7 +244,7 @@
<v-card-actions>
<v-spacer></v-spacer>
<v-btn id="btn-ok" color="primary" text @click="showNoRecordingAvailableDialog = false">{{
$t("menu.ok")
$t("menu.ok")
}}</v-btn>
</v-card-actions>
</v-card>
@ -389,6 +270,7 @@ import BottomSheet from "./BottomSheet.vue";
import ImageResize from "image-resize";
import CreatePollDialog from "./CreatePollDialog.vue";
import chatMixin from "./chatMixin";
import AudioLayout from "./AudioLayout.vue";
const sizeOf = require("image-size");
const dataUriToBuffer = require("data-uri-to-buffer");
@ -405,7 +287,7 @@ function ScrollPosition(node) {
this.readyFor = "up";
}
ScrollPosition.prototype.restore = function() {
ScrollPosition.prototype.restore = function () {
if (this.readyFor === "up") {
this.node.scrollTop = this.node.scrollHeight - this.previousScrollHeightMinusTop;
} else {
@ -413,7 +295,7 @@ ScrollPosition.prototype.restore = function() {
}
};
ScrollPosition.prototype.prepareFor = function(direction) {
ScrollPosition.prototype.prepareFor = function (direction) {
this.readyFor = direction || "up";
if (this.readyFor === "up") {
this.previousScrollHeightMinusTop = this.node.scrollHeight - this.node.scrollTop;
@ -436,6 +318,7 @@ export default {
BottomSheet,
AvatarOperations,
CreatePollDialog,
AudioLayout
},
data() {
@ -516,9 +399,13 @@ export default {
},
mounted() {
const container = this.$refs.chatContainer;
this.scrollPosition = new ScrollPosition(container);
this.chatContainerSize = this.$refs.chatContainerResizer.$el.clientHeight;
const container = this.chatContainer;
if (container) {
this.scrollPosition = new ScrollPosition(container);
if (this.$refs.chatContainerResizer) {
this.chatContainerSize = this.$refs.chatContainerResizer.$el.clientHeight;
}
}
},
beforeDestroy() {
@ -531,6 +418,14 @@ export default {
},
computed: {
chatContainer() {
const container = this.$refs.chatContainer;
console.log("GOT CONTAINER", container);
if (this.useAudioLayout) {
return container.$el;
}
return container;
},
senderDisplayName() {
return this.room.getMember(this.replyToEvent.sender.userId).name;
},
@ -627,9 +522,28 @@ export default {
me && this.room.currentState && this.room.currentState.hasSufficientPowerLevelFor("redact", me.powerLevel);
return isAdmin;
},
useAudioLayout: {
get: function () {
if (this.room) {
const tags = this.room.tags;
if (tags && tags["ui_options"]) {
return tags["ui_options"]["audio_layout"] === 1;
}
}
return false;
},
}
},
watch: {
initialLoadDone: {
immediate: true,
handler(value, oldValue) {
if (value && !oldValue) {
console.log("Loading finished!");
}
}
},
roomId: {
immediate: true,
handler(value, oldValue) {
@ -729,6 +643,7 @@ export default {
const getMoreIfNeeded = function _getMoreIfNeeded() {
const container = self.$refs.chatContainer;
if (
container &&
container.scrollHeight <= (1 + 2 * WINDOW_BUFFER_SIZE) * container.clientHeight &&
self.timelineWindow &&
self.timelineWindow.canPaginate(EventTimeline.BACKWARDS)
@ -867,22 +782,22 @@ export default {
handleChatContainerResize({ ignoredWidth, height }) {
const delta = height - this.chatContainerSize;
this.chatContainerSize = height;
const container = this.$refs.chatContainer;
if (delta < 0) {
const container = this.chatContainer;
if (container && delta < 0) {
container.scrollTop -= delta;
}
},
paginateBackIfNeeded() {
this.$nextTick(() => {
const container = this.$refs.chatContainer;
if (container.scrollHeight <= container.clientHeight) {
const container = this.chatContainer;
if (container && container.scrollHeight <= container.clientHeight) {
this.handleScrolledToTop();
}
});
},
onScroll(ignoredevent) {
const container = this.$refs.chatContainer;
const container = this.chatContainer;
if (!container) {
return;
}
@ -898,7 +813,7 @@ export default {
container.scrollHeight === container.clientHeight
? false
: container.scrollHeight - container.scrollTop.toFixed(0) > container.clientHeight ||
(this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.FORWARDS));
(this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.FORWARDS));
this.restartRRTimer();
},
@ -908,19 +823,20 @@ export default {
return; // Not for this room
}
const loadingDone = this.initialLoadDone;
this.$matrix.matrixClient.decryptEventIfNeeded(event, {});
if (this.initialLoadDone) {
if (this.initialLoadDone && !this.useAudioLayout) {
this.paginateBackIfNeeded();
}
// If we are at bottom, scroll to see new events...
const container = this.$refs.chatContainer;
var scrollToSeeNew = event.getSender() == this.$matrix.currentUserId; // When we sent, scroll
if (container.scrollHeight - container.scrollTop.toFixed(0) == container.clientHeight) {
scrollToSeeNew = true;
}
if (this.initialLoadDone && event.forwardLooking && !event.isRelation()) {
if (loadingDone && event.forwardLooking && !event.isRelation()) {
// If we are at bottom, scroll to see new events...
var scrollToSeeNew = event.getSender() == this.$matrix.currentUserId; // When we sent, scroll
const container = this.chatContainer;
if (container && container.scrollHeight - container.scrollTop.toFixed(0) == container.clientHeight) {
scrollToSeeNew = true;
}
this.handleScrolledToBottom(scrollToSeeNew);
}
},
@ -1140,16 +1056,18 @@ export default {
.paginate(EventTimeline.FORWARDS, 10, true)
.then((success) => {
if (success) {
this.scrollPosition.prepareFor("down");
this.events = this.timelineWindow.getEvents();
this.$nextTick(() => {
// restore scroll position!
console.log("Restore scroll!");
this.scrollPosition.restore();
if (scrollToEnd) {
this.smoothScrollToEnd();
}
});
if (!this.useAudioLayout) {
this.scrollPosition.prepareFor("down");
this.$nextTick(() => {
// restore scroll position!
console.log("Restore scroll!");
this.scrollPosition.restore();
if (scrollToEnd) {
this.smoothScrollToEnd();
}
});
}
}
})
.finally(() => {
@ -1162,7 +1080,7 @@ export default {
* Scroll so that the given event is at the middle of the chat view (if more events) or else at the bottom.
*/
scrollToEvent(eventId) {
const container = this.$refs.chatContainer;
const container = this.chatContainer;
const ref = this.$refs[eventId];
if (container && ref) {
const targetY = container.clientHeight / 2;
@ -1172,9 +1090,9 @@ export default {
},
smoothScrollToEnd() {
this.$nextTick(function() {
const container = this.$refs.chatContainer;
if (container.children.length > 0) {
this.$nextTick(function () {
const container = this.chatContainer;
if (container && container.children.length > 0) {
const lastChild = container.children[container.children.length - 1];
console.log("Scroll into view", lastChild);
window.requestAnimationFrame(() => {
@ -1251,7 +1169,7 @@ export default {
document.body.appendChild(link);
link.click();
setTimeout(function() {
setTimeout(function () {
document.body.removeChild(link);
URL.revokeObjectURL(url);
}, 200);
@ -1268,8 +1186,8 @@ export default {
},
emojiSelected(e) {
if(this.isEmojiQuickReaction) {
// When quick emoji picker is clicked
if (this.isEmojiQuickReaction) {
// When quick emoji picker is clicked
if (this.selectedEvent) {
const event = this.selectedEvent;
this.selectedEvent = null;
@ -1369,65 +1287,78 @@ export default {
* Start/restart the timer to Read Receipts.
*/
restartRRTimer() {
console.log("Restart RR timer");
this.stopRRTimer();
this.rrTimer = setTimeout(this.rrTimerElapsed, READ_RECEIPT_TIMEOUT);
let eventIdFirst = null;
let eventIdLast = null;
if (!this.useAudioLayout) {
const container = this.chatContainer;
const elFirst = util.getFirstVisibleElement(container);
const elLast = util.getLastVisibleElement(container);
if (elFirst && elLast) {
eventIdFirst = elFirst.getAttribute("eventId");
eventIdLast = elLast.getAttribute("eventId");
}
}
if (eventIdFirst && eventIdLast) {
this.rrTimer = setTimeout(() => { this.rrTimerElapsed(eventIdFirst, eventIdLast) }, READ_RECEIPT_TIMEOUT);
}
},
rrTimerElapsed() {
rrTimerElapsed(eventIdFirst, eventIdLast) {
console.log("RR timer elapsed", eventIdFirst, eventIdLast);
this.rrTimer = null;
this.sendRR(eventIdFirst, eventIdLast);
this.restartRRTimer();
},
const container = this.$refs.chatContainer;
const elFirst = util.getFirstVisibleElement(container);
const elLast = util.getLastVisibleElement(container);
if (elFirst && elLast) {
const eventIdFirst = elFirst.getAttribute("eventId");
const eventIdLast = elLast.getAttribute("eventId");
if (eventIdLast && this.room) {
var event = this.room.findEventById(eventIdLast);
const index = this.events.indexOf(event);
sendRR(eventIdFirst, eventIdLast) {
console.log("SEND RR", eventIdFirst, eventIdLast);
if (eventIdLast && this.room) {
var event = this.room.findEventById(eventIdLast);
const index = this.events.indexOf(event);
// Walk backwards through visible events to the first one that is incoming
//
var lastTimestamp = 0;
if (this.lastRR) {
lastTimestamp = this.lastRR.getTs();
// Walk backwards through visible events to the first one that is incoming
//
var lastTimestamp = 0;
if (this.lastRR) {
lastTimestamp = this.lastRR.getTs();
}
for (var i = index; i >= 0; i--) {
event = this.events[i];
if (event == this.lastRR || event.getTs() <= lastTimestamp) {
// Already sent this or too old...
break;
}
// Make sure it's not a local echo event...
if (!event.getId().startsWith("~")) {
// Send read receipt
this.$matrix.matrixClient
.sendReadReceipt(event)
.then(() => {
this.$matrix.matrixClient.setRoomReadMarkers(this.room.roomId, event.getId());
})
.then(() => {
console.log("RR sent for event: " + event.getId());
this.lastRR = event;
})
.catch((err) => {
console.log("Failed to update read marker: ", err);
})
.finally(() => {
this.restartRRTimer();
});
return; // Bail out here
}
for (var i = index; i >= 0; i--) {
event = this.events[i];
if (event == this.lastRR || event.getTs() <= lastTimestamp) {
// Already sent this or too old...
break;
}
// Make sure it's not a local echo event...
if (!event.getId().startsWith("~")) {
// Send read receipt
this.$matrix.matrixClient
.sendReadReceipt(event)
.then(() => {
this.$matrix.matrixClient.setRoomReadMarkers(this.room.roomId, event.getId());
})
.then(() => {
console.log("RR sent for event: " + event.getId());
this.lastRR = event;
})
.catch((err) => {
console.log("Failed to update read marker: ", err);
})
.finally(() => {
this.restartRRTimer();
});
return; // Bail out here
}
// Stop iterating at first visible
if (event.getId() == eventIdFirst) {
break;
}
// Stop iterating at first visible
if (event.getId() == eventIdFirst) {
break;
}
}
}
this.restartRRTimer();
},
showRecordingUI() {
@ -1499,9 +1430,9 @@ export default {
let div = document.createElement("div");
div.classList.add("toast");
div.innerText = this.$t("poll_create.results_shared");
this.$refs.chatContainer.parentElement.appendChild(div);
this.chatContainer.parentElement.appendChild(div);
setTimeout(() => {
this.$refs.chatContainer.parentElement.removeChild(div);
this.chatContainer.parentElement.removeChild(div);
}, 3000);
}
},