Lots of channel related fixes and updates
This commit is contained in:
parent
e3bfede77e
commit
ca777a83be
17 changed files with 508 additions and 59 deletions
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div :class="{'chat-root': true, 'fill-height': true, 'd-flex': true, 'flex-column': true, 'channel': roomDisplayType == 'im.keanu.room_type_channel'}" :style="chatContainerStyle">
|
||||
<div :class="{'chat-root': true, 'fill-height': true, 'd-flex': true, 'flex-column': true, 'channel': roomDisplayType == ROOM_TYPE_CHANNEL}" :style="chatContainerStyle">
|
||||
<ChatHeaderPrivate class="chat-header flex-grow-0 flex-shrink-0"
|
||||
v-on:header-click="onHeaderClick"
|
||||
v-on:view-room-details="viewRoomDetails"
|
||||
|
|
@ -35,9 +35,14 @@
|
|||
<div v-if="!useVoiceMode && !useFileModeNonAdmin" :class="{'chat-content': true, 'flex-grow-1': true, 'flex-shrink-1': true, 'invisible': !initialLoadDone}" ref="chatContainer"
|
||||
v-on:scroll="onScroll" @click="closeContextMenusIfOpen">
|
||||
<div ref="messageOperationsStrut" class="message-operations-strut">
|
||||
<message-operations-channel ref="messageOperations" :style="opStyle" v-on:close="showContextMenu = false;" v-if="showMessageOperations && room.displayType == ROOM_TYPE_CHANNEL" :userCanPin="true"
|
||||
:originalEvent="selectedEvent" :timelineSet="timelineSet"
|
||||
v-on:pin="pin(selectedEvent)"
|
||||
v-on:unpin="unpin(selectedEvent)"
|
||||
/>
|
||||
<message-operations ref="messageOperations" :style="opStyle" :emojis="recentEmojis" v-on:close="
|
||||
showContextMenu = false;
|
||||
" v-if="showMessageOperations" v-on:addreaction="addReaction" v-on:addquickreaction="addQuickReaction"
|
||||
" v-else-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
|
||||
|
|
@ -56,11 +61,12 @@
|
|||
<!-- If we have a retention timer, it means we have active message retention. Show header. -->
|
||||
<WelcomeHeaderChannelUser v-if="retentionTimer && !roomWelcomeHeader && newlyJoinedRoom" />
|
||||
|
||||
<div v-for="(event, index) in filteredEvents" :key="event.getId()" :eventId="event.getId()">
|
||||
<div v-for="(event) in filteredEvents" :key="event.getId()" :eventId="event !== ROOM_READ_MARKER_EVENT_PLACEHOLDER ? event.getId() : undefined">
|
||||
|
||||
<!-- DAY Marker, shown for every new day in the timeline -->
|
||||
<div v-if="!reverseOrder && showDayMarkerBeforeEvent(event) && !!componentForEvent(event, isForExport = false)" class="day-marker"><div class="line"></div><div class="text">{{ dayForEvent(event) }}</div><div class="line"></div></div>
|
||||
<div v-if="!reverseOrder && showDayMarkerBeforeEvent(event) && !!event.component && event !== ROOM_READ_MARKER_EVENT_PLACEHOLDER" class="day-marker"><div class="line"></div><div class="text">{{ dayForEvent(event) }}</div><div class="line"></div></div>
|
||||
|
||||
<div v-if="!event.isRelation() && !event.isRedaction()" :ref="event.getId()">
|
||||
<div :ref="event.getId()">
|
||||
<MessageErrorHandler>
|
||||
<div class="message-wrapper" v-on:touchstart="
|
||||
(e) => {
|
||||
|
|
@ -68,14 +74,12 @@
|
|||
}
|
||||
" v-on:touchend="touchEnd" v-on:touchcancel="touchCancel" v-on:touchmove="touchMove">
|
||||
|
||||
<div v-if="reverseOrder && event.getId() == readMarker && index > 0" class="read-marker"><div class="line"></div><div class="text">{{ $t('message.unread_messages') }}</div><div class="line"></div></div>
|
||||
|
||||
<!-- Note: For threaded media messages, IF there is only one item we show that media item as a single component.
|
||||
We might therefore get calls to v-on:context-menu that has the event set to that single media item, not the top level thread event
|
||||
that is really displayed in the flow. Therefore, we rewrite these events with "{event: event, anchor: $event.anchor}",
|
||||
see below. Otherwise things like context menus won't work as designed.
|
||||
-->
|
||||
<component :is="componentForEvent(event)" :room="room" :originalEvent="event" :nextEvent="filteredEvents[index + 1]"
|
||||
<component :is="event.component" :room="room" :originalEvent="event" :nextEvent="event.nextDisplayedEvent"
|
||||
:timelineSet="timelineSet" v-on:send-quick-reaction.stop="sendQuickReaction"
|
||||
:componentFn="componentForEvent"
|
||||
v-on:context-menu="showContextMenuForEvent({event: event, anchor: $event.anchor})"
|
||||
|
|
@ -92,13 +96,12 @@
|
|||
/>
|
||||
<!-- <div v-if="debugging" style="user-select:text">EventID: {{ event.getId() }}</div> -->
|
||||
<!-- <div v-if="debugging" style="user-select:text">Event: {{ JSON.stringify(event) }}<br /><br /></div> -->
|
||||
<div v-if="!reverseOrder && event.getId() == readMarker && index < filteredEvents.length - 1" class="read-marker"><div class="line"></div><div class="text">{{ $t('message.unread_messages') }}</div><div class="line"></div></div>
|
||||
</div>
|
||||
</MessageErrorHandler>
|
||||
</div>
|
||||
|
||||
<!-- Day marker when reverseOrder is set -->
|
||||
<div v-if="reverseOrder && showDayMarkerBeforeEvent(event) && !!componentForEvent(event, isForExport = false)" class="day-marker"><div class="line"></div><div class="text">{{ dayForEvent(event) }}</div><div class="line"></div></div>
|
||||
<div v-if="reverseOrder && showDayMarkerBeforeEvent(event) && !!event.component && event !== ROOM_READ_MARKER_EVENT_PLACEHOLDER" class="day-marker"><div class="line"></div><div class="text">{{ dayForEvent(event) }}</div><div class="line"></div></div>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -369,7 +372,7 @@ import UserProfileDialog from "./UserProfileDialog.vue"
|
|||
import BottomSheet from "./BottomSheet.vue";
|
||||
import ImageResize from "image-resize";
|
||||
import CreatePollDialog from "./CreatePollDialog.vue";
|
||||
import chatMixin from "./chatMixin";
|
||||
import chatMixin, { ROOM_READ_MARKER_EVENT_PLACEHOLDER } from "./chatMixin";
|
||||
import sendAttachmentsMixin from "./sendAttachmentsMixin";
|
||||
import AudioLayout from "./AudioLayout.vue";
|
||||
import FileDropLayout from "./file_mode/FileDropLayout";
|
||||
|
|
@ -377,6 +380,7 @@ import roomTypeMixin from "./roomTypeMixin";
|
|||
import roomMembersMixin from "./roomMembersMixin";
|
||||
import PurgeRoomDialog from "../components/PurgeRoomDialog";
|
||||
import MessageErrorHandler from "./MessageErrorHandler";
|
||||
import MessageOperationsChannel from './messages/channel/MessageOperationsChannel.vue';
|
||||
|
||||
const sizeOf = require("image-size");
|
||||
const dataUriToBuffer = require("data-uri-to-buffer");
|
||||
|
|
@ -431,11 +435,15 @@ export default {
|
|||
UserProfileDialog,
|
||||
PurgeRoomDialog,
|
||||
WelcomeHeaderChannelUser,
|
||||
MessageErrorHandler
|
||||
MessageErrorHandler,
|
||||
MessageOperationsChannel
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
ROOM_TYPE_CHANNEL: ROOM_TYPE_CHANNEL,
|
||||
ROOM_READ_MARKER_EVENT_PLACEHOLDER: ROOM_READ_MARKER_EVENT_PLACEHOLDER,
|
||||
|
||||
waitingForRoomObject: false,
|
||||
events: [],
|
||||
currentInput: "",
|
||||
|
|
@ -690,6 +698,8 @@ export default {
|
|||
},
|
||||
|
||||
filteredEvents() {
|
||||
let events = this.events;
|
||||
|
||||
if (this.room && this.$matrix.matrixClient.isRoomEncrypted(this.room.roomId)) {
|
||||
if (this.room.getHistoryVisibility() == "joined") {
|
||||
// For encrypted rooms where history is set to "joined" we can't read old events.
|
||||
|
|
@ -702,12 +712,37 @@ export default {
|
|||
(!e.getPrevContent() || e.getPrevContent().membership != "join") &&
|
||||
e.getStateKey() == this.$matrix.currentUserId) {
|
||||
// Our own join event.
|
||||
return this.reverseOrder ? this.events.slice(idx + 1).toReversed() : this.events.slice(idx + 1);
|
||||
events = this.events.slice(idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.reverseOrder ? this.events.toReversed() : this.events;
|
||||
|
||||
// Filter out relations and redactions
|
||||
events = this.events.toReversed().filter((e) => !e.isRelation() && !e.isRedaction());
|
||||
|
||||
// Add read marker, if it is not newer than the "latest" message we are going to display
|
||||
//
|
||||
let lastDisplayedEvent = undefined;
|
||||
events = events.flatMap((e) => {
|
||||
let result = [];
|
||||
Vue.set(e, "component", this.componentForEvent(e, false));
|
||||
if (e.getId() == this.readMarker && lastDisplayedEvent !== undefined) {
|
||||
const readMarkerEvent = ROOM_READ_MARKER_EVENT_PLACEHOLDER;
|
||||
Vue.set(readMarkerEvent, "component", this.componentForEvent(readMarkerEvent, false));
|
||||
if (readMarkerEvent.component) {
|
||||
Vue.set(e, "nextDisplayedEvent", lastDisplayedEvent);
|
||||
}
|
||||
result.push(readMarkerEvent);
|
||||
}
|
||||
if (e.component) {
|
||||
Vue.set(e, "nextDisplayedEvent", lastDisplayedEvent);
|
||||
lastDisplayedEvent = e;
|
||||
}
|
||||
result.push(e);
|
||||
return result;
|
||||
})
|
||||
return (this.reverseOrder ? events : events.toReversed()) // Reverse back if needed
|
||||
},
|
||||
|
||||
roomCreatedByUsRecently() {
|
||||
|
|
@ -886,6 +921,12 @@ export default {
|
|||
var rectAnchor = this.showContextMenuAnchor.getBoundingClientRect();
|
||||
var rectChat = this.$refs.messageOperationsStrut.getBoundingClientRect();
|
||||
var rectOps = this.$refs.messageOperations.$el.getBoundingClientRect();
|
||||
if (this.room.displayType == ROOM_TYPE_CHANNEL) {
|
||||
top = rectAnchor.top - rectChat.top;
|
||||
let right = rectChat.right - rectAnchor.right;
|
||||
this.opStyle = "top:" + top + "px;right:" + right + "px";
|
||||
return;
|
||||
} else {
|
||||
top = rectAnchor.top - rectChat.top - 50;
|
||||
left = rectAnchor.left - rectChat.left - 75;
|
||||
if (left + rectOps.width + 10 >= rectChat.right) {
|
||||
|
|
@ -894,6 +935,7 @@ export default {
|
|||
left = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.opStyle = "top:" + top + "px;left:" + left + "px";
|
||||
});
|
||||
|
|
@ -916,10 +958,30 @@ export default {
|
|||
},
|
||||
|
||||
/**
|
||||
* Set events to display. At the same time, filter out messages that are past rentention period etc.
|
||||
* Set events to display. At the same time, filter out messages that are past rentention period etc. Also, filter pinned events "at the top"
|
||||
*/
|
||||
setEvents(events) {
|
||||
this.events = this.filterOutOldAndInvisible(events);
|
||||
setEvents(events, onlyIfLengthChanges = false) {
|
||||
let updated = this.filterOutOldAndInvisible(events);
|
||||
|
||||
// Handle pinning
|
||||
//
|
||||
const pinnedEvents = this.$matrix.getPinnedEvents(this.room);
|
||||
console.log("Pinned events in room", JSON.stringify(pinnedEvents));
|
||||
events.forEach((e) => {
|
||||
Vue.set(e, "isPinned", pinnedEvents.includes(e.getId()));
|
||||
});
|
||||
updated = updated.sort((e1, e2) => {
|
||||
if (!e1.isPinned && !e2.isPinned) return 0;
|
||||
else if (e1.isPinned && !e2.isPinned) return this.reverseOrder ? 1 : -1;
|
||||
else if (e2.isPinned && !e1.isPinned) return this.reverseOrder ? -1 : 1;
|
||||
else {
|
||||
// Look at order in "pinned" value in the m.room.pinned_events event!
|
||||
return pinnedEvents.indexOf(e1.getId()) < pinnedEvents.indexOf(e2.getId()) ? (this.reverseOrder ? 1 : -1) : (this.reverseOrder ? -1 : 1)
|
||||
}
|
||||
});
|
||||
if (!onlyIfLengthChanges || updated.length != this.events.length) {
|
||||
this.events = updated; // Changed
|
||||
}
|
||||
},
|
||||
|
||||
filterOutOldAndInvisible(events) {
|
||||
|
|
@ -962,10 +1024,7 @@ export default {
|
|||
},
|
||||
|
||||
onRetentionTimer() {
|
||||
const events = this.filterOutOldAndInvisible(this.events);
|
||||
if (events.length != this.events.length) {
|
||||
this.events = events; // Changed
|
||||
}
|
||||
this.setEvents(this.events, true);
|
||||
},
|
||||
|
||||
onRoomJoined(initialEventId) {
|
||||
|
|
@ -980,6 +1039,7 @@ export default {
|
|||
}
|
||||
|
||||
this.reverseOrder = (this.room && this.roomDisplayType == ROOM_TYPE_CHANNEL);
|
||||
Vue.set(this.room, "displayType", this.roomDisplayType);
|
||||
|
||||
// Listen to events
|
||||
this.$matrix.on("Room.timeline", this.onEvent);
|
||||
|
|
@ -1280,7 +1340,7 @@ export default {
|
|||
this.paginateBackIfNeeded();
|
||||
}
|
||||
|
||||
if (loadingDone && event.forwardLooking && (!event.isRelation() || event.isMxThread || event.threadRootId || event.parentThread)) {
|
||||
if (loadingDone && event.forwardLooking && (!(event.isRelation() || event.isRedaction()) || event.isMxThread || event.threadRootId || event.parentThread)) {
|
||||
// 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;
|
||||
|
|
@ -1704,6 +1764,14 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
pin(event) {
|
||||
this.$matrix.setEventPinned(this.room, event, true);
|
||||
},
|
||||
|
||||
unpin(event) {
|
||||
this.$matrix.setEventPinned(this.room, event, false);
|
||||
},
|
||||
|
||||
cancelEditReply() {
|
||||
this.currentInput = "";
|
||||
this.editedEvent = null;
|
||||
|
|
@ -2006,9 +2074,9 @@ export default {
|
|||
if (!this.useVoiceMode) { // Voice mode has own autoplay handling inside "AudioLayout"!
|
||||
// Auto play consecutive audio messages, either incoming or sent.
|
||||
const filteredEvents = this.filteredEvents;
|
||||
const index = filteredEvents.findIndex(e => e.getId() === matrixEventId);
|
||||
if (index >= 0 && index < (filteredEvents.length - 1)) {
|
||||
const nextEvent = filteredEvents[index + 1];
|
||||
const event = filteredEvents.find(e => e.getId() === matrixEventId);
|
||||
if (event && event.nextDisplayedEvent) {
|
||||
const nextEvent = event.nextDisplayedEvent;
|
||||
if (nextEvent.getContent().msgtype === "m.audio") {
|
||||
// Yes, audio event!
|
||||
this.$audioPlayer.play(nextEvent, this.timelineSet);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue