Support pinning thread messages (media)

This commit is contained in:
N-Pex 2024-10-17 10:22:24 +02:00
parent d3ffc3d15b
commit 5e1223fc01
10 changed files with 152 additions and 27 deletions

View file

@ -727,6 +727,11 @@ export default {
// Filter out relations and redactions
events = this.events.toReversed().filter((e) => !e.isRelation() && !e.isRedaction());
// If Channel, remove all redacted events as well.
if (this.room && this.room.displayType == ROOM_TYPE_CHANNEL) {
events = events.filter((e) => !e.isRedacted());
}
// Add read marker, if it is not newer than the "latest" message we are going to display
//
let showReadMarker = false;
@ -981,20 +986,22 @@ export default {
// 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 (this.room) {
const pinnedEvents = this.$matrix.getPinnedEvents(this.room);
updated.forEach((e) => {
Vue.set(e, "isPinned", pinnedEvents.includes(e.threadParent ? e.threadParent.getId() : 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
}
@ -1781,11 +1788,13 @@ export default {
},
pin(event) {
this.$matrix.setEventPinned(this.room, event, true);
const eventToPin = event.parentThread ? event.parentThread : event;
this.$matrix.setEventPinned(this.room, eventToPin, true);
},
unpin(event) {
this.$matrix.setEventPinned(this.room, event, false);
const eventToUnpin = event.parentThread ? event.parentThread : event;
this.$matrix.setEventPinned(this.room, eventToUnpin, false);
},
cancelEditReply() {

View file

@ -17,7 +17,7 @@
<span ref="messageInOutRef" class="content">
<slot></slot>
</span>
<div class="pin-icon" v-if="event.isPinned"><v-icon>$vuetify.icons.ic_pin_filled</v-icon></div>
<div class="pin-icon" v-if="isPinned"><v-icon>$vuetify.icons.ic_pin_filled</v-icon></div>
<div class="op-button" ref="opbutton" v-if="!event.isRedacted() && room.displayType != ROOM_TYPE_CHANNEL">
<v-btn id="btn-more" icon @click.stop="showContextMenu($refs.opbutton)">
<v-icon>more_vert</v-icon>

View file

@ -10,7 +10,8 @@
</div>
<div class="message">
<v-container v-if="!event.isRedacted()" fluid class="imageCollection">
<SwipeableThumbnailsView :items="items" v-if="!event.isRedacted() && room.displayType == ROOM_TYPE_CHANNEL" />
<v-container v-else-if="!event.isRedacted()" fluid class="imageCollection">
<v-row wrap>
<v-col v-for="({ size, item }) in layoutedItems()" :key="item.event.getId()" :cols="size">
<ThumbnailView :item="item" :previewOnly="true" v-on:itemclick="onItemClick($event)" />
@ -38,16 +39,18 @@
<script>
import MessageIncoming from "./MessageIncoming.vue";
import messageMixin from "./messageMixin";
import util from "../../plugins/utils";
import util, { ROOM_TYPE_CHANNEL } from "../../plugins/utils";
import GalleryItemsView from '../file_mode/GalleryItemsView.vue';
import ThumbnailView from '../file_mode/ThumbnailView.vue';
import SwipeableThumbnailsView from "./channel/SwipeableThumbnailsView.vue";
export default {
extends: MessageIncoming,
components: { MessageIncoming, GalleryItemsView, ThumbnailView },
components: { MessageIncoming, GalleryItemsView, ThumbnailView, SwipeableThumbnailsView },
mixins: [messageMixin],
data() {
return {
ROOM_TYPE_CHANNEL: ROOM_TYPE_CHANNEL,
items: [],
showItem: null,
}

View file

@ -14,7 +14,7 @@
<v-icon>more_vert</v-icon>
</v-btn>
</div>
<div class="pin-icon" v-if="event.isPinned"><v-icon>$vuetify.icons.ic_pin_filled</v-icon></div>
<div class="pin-icon" v-if="isPinned"><v-icon>$vuetify.icons.ic_pin_filled</v-icon></div>
<!-- SLOT FOR CONTENT -->
<span ref="messageInOutRef" class="content">

View file

@ -11,7 +11,8 @@
<div class="message">
<v-container v-if="!event.isRedacted()" fluid class="imageCollection">
<SwipeableThumbnailsView :items="items" v-if="!event.isRedacted() && room.displayType == ROOM_TYPE_CHANNEL" />
<v-container v-else-if="!event.isRedacted()" fluid class="imageCollection">
<v-row wrap>
<v-col v-for="({ size, item }) in layoutedItems()" :key="item.event.getId()" :cols="size">
<ThumbnailView :item="item" :previewOnly="true" v-on:itemclick="onItemClick($event)" />
@ -39,16 +40,18 @@
<script>
import MessageOutgoing from "./MessageOutgoing.vue";
import messageMixin from "./messageMixin";
import util from "../../plugins/utils";
import util, { ROOM_TYPE_CHANNEL } from "../../plugins/utils";
import GalleryItemsView from '../file_mode/GalleryItemsView.vue';
import ThumbnailView from '../file_mode/ThumbnailView.vue';
import SwipeableThumbnailsView from "./channel/SwipeableThumbnailsView.vue";
export default {
extends: MessageOutgoing,
components: { MessageOutgoing, GalleryItemsView, ThumbnailView },
components: { MessageOutgoing, GalleryItemsView, ThumbnailView, SwipeableThumbnailsView },
mixins: [messageMixin],
data() {
return {
ROOM_TYPE_CHANNEL: ROOM_TYPE_CHANNEL,
items: [],
showItem: null,
}

View file

@ -19,6 +19,12 @@
<v-list-item-title>{{ $t("menu.unpin") }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item key="redact" v-if="isRedactable" @click.stop="redact">
<v-list-item-icon><v-icon color="#222222">delete_outline</v-icon></v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ $t("menu.delete") }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</div>
</template>

View file

@ -0,0 +1,41 @@
<template>
<div class="swipeable-thumbnails-view">
<v-responsive :aspect-ratio="16 / 9" class="ma-0 pa-0">
<v-carousel height="100%" hide-delimiters show-arrows-on-hover v-model="currentIndex">
<v-carousel-item v-for="(item,index) in items" :key="item.event.getId()">
<ThumbnailView :item="items[index]" :previewOnly="true" />
</v-carousel-item>
</v-carousel>
</v-responsive>
<div class="indicator-container">
<div v-for="(item,index) in items" :key="index" :class="{'indicator': true, 'current': index == currentIndex}" />
</div>
</div>
</template>
<script>
import messageMixin from "../../messages/messageMixin";
import ThumbnailView from '../../file_mode/ThumbnailView.vue';
export default {
mixins: [messageMixin],
components: { ThumbnailView },
props: {
items: {
type: Array,
default: function () {
return []
}
},
},
data() {
return {
currentIndex: 0,
};
},
};
</script>
<style lang="scss">
@import "@/assets/css/chat.scss";
</style>

View file

@ -109,7 +109,7 @@ export default {
* Don't show sender and time if the next event is within 2 minutes and also from us (= back to back messages)
*/
showSenderAndTime() {
if (!this.event.isPinned && this.nextEvent && this.nextEvent.getSender() == this.event.getSender()) {
if (!this.isPinned && this.nextEvent && this.nextEvent.getSender() == this.event.getSender()) {
const ts1 = this.nextEvent.event.origin_server_ts;
const ts2 = this.event.event.origin_server_ts;
return ts1 - ts2 < 2 * 60 * 1000; // less than 2 minutes
@ -180,15 +180,19 @@ export default {
return this.event.getContent().body;
},
isPinned() {
return this.event.parentThread ? this.event.parentThread.isPinned : this.event.isPinned;
},
/**
* Classes to set for the message. Currently only for "messageIn"
*/
messageClasses() {
if (this.incoming) {
return { messageIn: true, "from-admin": this.senderIsAdminOrModerator(this.event), "pinned": this.event.isPinned };
return { messageIn: true, "from-admin": this.senderIsAdminOrModerator(this.event), "pinned": this.isPinned };
} else {
return { messageOut: true, "pinned": this.event.isPinned };
return { messageOut: true, "pinned": this.isPinned };
}
},