Start on Vue 3 changes

This commit is contained in:
N-Pex 2025-05-06 09:27:53 +02:00
parent dcc4784bfd
commit c913a40e18
35 changed files with 3570 additions and 1913 deletions

View file

@ -53,7 +53,7 @@
v-on:redact="redact(selectedEvent)"
v-on:download="download(selectedEvent)"
v-on:more="
isEmojiQuickReaction= true
isEmojiQuickReaction=true;
showMoreMessageOperations({event: selectedEvent, anchor: $event.anchor})
"
:userCanSendReactionAndAnswerPoll="$matrix.userCanSendReactionAndAnswerPollInCurrentRoom"
@ -97,7 +97,7 @@
v-on:download="download(event)"
v-on:poll-closed="pollWasClosed(event)"
v-on:more="
isEmojiQuickReaction = true
isEmojiQuickReaction = true;
showMoreMessageOperations({event: event, anchor: $event.anchor})
"
v-on:layout-change="onLayoutChange"
@ -205,7 +205,7 @@
<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
isEmojiQuickReaction = false;
showMoreMessageOperations($event)
">
<v-icon>$vuetify.icons.addReaction</v-icon>
@ -313,7 +313,7 @@
</div>
<MessageOperationsBottomSheet ref="messageOperationsSheet">
<VEmojiPicker ref="emojiPicker" @select="emojiSelected" :i18n="i18nEmoji"/>
<EmojiPicker ref="emojiPicker" @select="emojiSelected" :i18n="i18nEmoji"/>
</MessageOperationsBottomSheet>
<StickerPickerBottomSheet ref="stickerPickerSheet" v-on:selectSticker="sendSticker" />
@ -366,7 +366,6 @@
</template>
<script>
import Vue from "vue";
import { TimelineWindow, EventTimeline } from "matrix-js-sdk";
import util, { ROOM_TYPE_VOICE_MODE, ROOM_TYPE_FILE_MODE, ROOM_TYPE_CHANNEL } from "../plugins/utils";
import MessageOperations from "./messages/MessageOperations.vue";
@ -397,7 +396,7 @@ import MessageOperationsChannel from './messages/channel/MessageOperationsChanne
import { imageSize } from "image-size";
import prettyBytes from "pretty-bytes";
import RoomExport from "./RoomExport.vue";
import { VEmojiPicker } from 'v-emoji-picker';
import EmojiPicker from 'vue3-emoji-picker';
const READ_RECEIPT_TIMEOUT = 5000; /* How long a message must have been visible before the read marker is updated */
@ -452,7 +451,7 @@ export default {
MessageErrorHandler,
MessageOperationsChannel,
RoomExport,
VEmojiPicker
EmojiPicker
},
data() {
@ -755,17 +754,17 @@ export default {
let lastDisplayedEvent = undefined;
events = events.flatMap((e) => {
let result = [];
Vue.set(e, "component", this.componentForEvent(e, false));
e.component = this.componentForEvent(e, false);
if (e.getId() == this.readMarker && showReadMarker) {
const readMarkerEvent = ROOM_READ_MARKER_EVENT_PLACEHOLDER;
Vue.set(readMarkerEvent, "component", this.componentForEvent(readMarkerEvent, false));
readMarkerEvent["component"] = this.componentForEvent(readMarkerEvent, false);
if (readMarkerEvent.component) {
Vue.set(e, "nextDisplayedEvent", lastDisplayedEvent);
e["nextDisplayedEvent"] = lastDisplayedEvent;
}
result.push(readMarkerEvent);
}
if (e.component) {
Vue.set(e, "nextDisplayedEvent", lastDisplayedEvent);
e["nextDisplayedEvent"] = lastDisplayedEvent;
lastDisplayedEvent = e;
if (e.getSender() !== this.$matrix.currentUserId) {
showReadMarker = true;
@ -1006,8 +1005,8 @@ export default {
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()));
Vue.set(e, "isChannelMessage", (this.room && this.roomDisplayType == ROOM_TYPE_CHANNEL));
e["isPinned"] = pinnedEvents.includes(e.threadParent ? e.threadParent.getId() : e.getId());
e["isChannelMessage"] = (this.room && this.roomDisplayType == ROOM_TYPE_CHANNEL);
});
updated = updated.sort((e1, e2) => {
@ -1080,7 +1079,7 @@ export default {
}
this.reverseOrder = (this.room && this.roomDisplayType == ROOM_TYPE_CHANNEL);
Vue.set(this.room, "displayType", this.roomDisplayType);
this.room["displayType"] = this.roomDisplayType;
// Listen to events
this.$matrix.on("Room.timeline", this.onEvent);
@ -1300,8 +1299,8 @@ export default {
setParentThread(event) {
const parentEvent = this.timelineSet.findEventById(event.threadRootId) || this.room.findEventById(event.threadRootId);
if (parentEvent) {
Vue.set(parentEvent, "isMxThread", true);
Vue.set(event, "parentThread", parentEvent);
parentEvent["isMxThread"] = true;
event["parentThread"] = parentEvent;
} else {
// Try to load from server.
this.$matrix.matrixClient.getEventTimeline(this.timelineSet, event.threadRootId)
@ -1311,8 +1310,8 @@ export default {
if (parentEvent) {
this.setEvents(this.timelineWindow.getEvents());
const fn = () => {
Vue.set(parentEvent, "isMxThread", true);
Vue.set(event, "parentThread", parentEvent);
parentEvent["isMxThread"] = true;
event["parentThread"] = parentEvent;
};
if (this.initialLoadDone) {
const sel = "[eventId=\"" + parentEvent.getId() + "\"]";
@ -1334,7 +1333,7 @@ export default {
setReplyToEvent(event) {
const parentEvent = this.timelineSet.findEventById(event.replyEventId) || this.room.findEventById(event.replyEventId);
if (parentEvent) {
Vue.set(event, "replyEvent", parentEvent);
event["replyEvent"] = parentEvent;
} else {
// Try to load from server.
this.$matrix.matrixClient.getEventTimeline(this.timelineSet, event.replyEventId)
@ -1343,7 +1342,7 @@ export default {
const parentEvent = tl.getEvents().find((e) => e.getId() === event.replyEventId);
if (parentEvent) {
this.setEvents(this.timelineWindow.getEvents());
const fn = () => {Vue.set(event, "replyEvent", parentEvent);};
const fn = () => {event["replyEvent"] = parentEvent;};
if (this.initialLoadDone) {
const sel = "[eventId=\"" + parentEvent.getId() + "\"]";
const element = document.querySelector(sel);
@ -1486,20 +1485,17 @@ export default {
outputType: "blob",
})
.then((img) => {
Vue.set(
fileObj,
"scaled",
fileObj["scaled"] =
new File([img], file.name, {
type: img.type,
lastModified: Date.now(),
})
);
Vue.set(fileObj, "useScaled", true);
Vue.set(fileObj, "scaledSize", img.size);
Vue.set(fileObj, "scaledDimensions", {
});
fileObj["useScaled"] = true;
fileObj["scaledSize"] = img.size;
fileObj["scaledDimensions"] = {
width: newWidth,
height: newHeight,
});
};
})
.catch((err) => {
console.error("Resize failed:", err);

View file

@ -107,11 +107,11 @@
<v-col class="py-0">
<v-checkbox id="chk-accept-ua" class="mt-0" v-model="acceptUA">
<template slot="label">
<i18n path="join.accept_ua" tag="span">
<i18n-t keypath="join.accept_ua" tag="span">
<template v-slot:agreement>
<a href="./ua.html" target="_blank" @click.stop>{{ $t("join.ua") }}</a>
</template>
</i18n>
</i18n-t>
</template>
</v-checkbox>
</v-col>

View file

@ -7,12 +7,10 @@
:value="device.deviceId"
>
<template v-slot:default="{ active }">
<v-list-item-content>
<v-list-item-title>{{ displayName(device) }}</v-list-item-title>
<v-list-item-subtitle>{{
verificationStatus(device)
}}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action v-if="active">
<v-btn icon>
<v-icon

View file

@ -43,12 +43,10 @@
member.name.substring(0, 1).toUpperCase()
}}</span>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title>{{ memberName(member) }}</v-list-item-title>
<v-list-item-subtitle
v-text="member.userId"
></v-list-item-subtitle>
</v-list-item-content>
<v-list-item-subtitle
v-text="member.userId"
></v-list-item-subtitle>
<v-list-item-action>
<v-btn icon v-if="active">
<v-icon color="grey lighten-1">check</v-icon>

View file

@ -60,11 +60,11 @@
<v-checkbox id="chk-accept-ua" class="mt-0" v-model="acceptUA">
<template slot="label">
<i18n path="join.accept_ua" tag="span">
<i18n-t keypath="join.accept_ua" tag="span">
<template v-slot:agreement>
<a href="./ua.html" target="_blank" @click.stop>{{ $t("join.ua") }}</a>
</template>
</i18n>
</i18n-t>
</template>
</v-checkbox>

View file

@ -17,7 +17,7 @@
v-if="$matrix.currentUser.is_guest && lastRoom"
class="dialog-text"
>
<i18n path="leave.text_public_lastroom" tag="p">
<i18n-t keypath="leave.text_public_lastroom" tag="p">
<template v-slot:user>
<span>{{ $matrix.currentUserDisplayName }}</span>
</template>
@ -26,7 +26,7 @@
$t("leave.create_account")
}}</a>
</template>
</i18n>
</i18n-t>
</div>
<div v-else class="dialog-text">{{ $t("leave.text_public") }}</div>
</template>

View file

@ -269,6 +269,7 @@ import { mapState } from 'vuex'
export default {
name: "Profile",
mixins: [profileInfoMixin],
inject: ['$matrix'],
components: {
ActionRow,
SelectLanguageDialog,

View file

@ -7,7 +7,7 @@
<v-row>
<v-col :class="['username', { 'editable': editDisplayName }]" cols="pa-2" ref="username">
<div v-if="$matrix.currentUser.is_guest">
<i18n path="profile_info_popup.identity_temporary" tag="span">
<i18n-t keypath="profile_info_popup.identity_temporary" tag="span">
<template v-slot:displayName>
<input v-model="displayName"
@keyup.enter="$event => $event.target.blur()"
@ -16,10 +16,10 @@
editDisplayName = !editDisplayName;
" @focus="editDisplayName = !editDisplayName" />
</template>
</i18n>
</i18n-t>
</div>
<div v-else>
<i18n path="profile_info_popup.identity" tag="span">
<i18n-t keypath="profile_info_popup.identity" tag="span">
<template v-slot:displayName>
<input
v-model="displayName"
@ -28,7 +28,7 @@
@focus="editDisplayName = !editDisplayName"
/>
</template>
</i18n>
</i18n-t>
</div>
</v-col>
<v-col cols="auto" class="pa-2">
@ -44,12 +44,12 @@
<div class="want_more">
🙌 {{ $t("profile_info_popup.want_more") }}
</div>
<i18n path="profile_info_popup.powered_by" tag="div">
<i18n-t keypath="profile_info_popup.powered_by" tag="div">
<template v-slot:product>{{ product }}</template>
<template v-slot:productLink>
<a :href="'//' + productLink">{{ productLink }}</a>
</template>
</i18n>
</i18n-t>
<div class="text-end" v-if="!$config.hide_add_room_on_home">
<v-btn id="btn-new-room" class="new_room" text @click="createRoom">
{{ $t("profile_info_popup.new_room") }}

View file

@ -50,18 +50,18 @@
v-if="$matrix.currentUser.is_guest"
class="d-inline-block me-2 white--text"
>
<i18n path="profile_info_popup.identity_temporary" tag="span">
<i18n-t keypath="profile_info_popup.identity_temporary" tag="span">
<template v-slot:displayName>
<b>{{ displayName }}</b>
</template>
</i18n>
</i18n-t>
</div>
<div v-else class="d-inline-block me-2 white--text">
<i18n path="profile_info_popup.identity" tag="span">
<i18n-t keypath="profile_info_popup.identity" tag="span">
<template v-slot:displayName>
<b>{{ displayName }}</b>
</template>
</i18n>
</i18n-t>
</div>
<v-avatar
class="avatar-32 d-inline-block"

View file

@ -53,7 +53,6 @@
</template>
<script>
import Vue from "vue";
import MessageIncomingText from "./messages/MessageIncomingText.vue";
import MessageIncomingFile from "./messages/MessageIncomingFile.vue";
import MessageIncomingImage from "./messages/MessageIncomingImage.vue";
@ -261,14 +260,14 @@ export default {
this.events.filter(event => (event.threadRootId && !event.parentThread)).forEach(event => {
const parentEvent = this.timelineSet.findEventById(event.threadRootId) || this.room.findEventById(event.threadRootId);
if (parentEvent) {
Vue.set(parentEvent, "isMxThread", true);
Vue.set(event, "parentThread", parentEvent);
parentEvent["isMxThread"] = true;
event["parentThread"] = parentEvent;
}
});
this.events.filter(event => (event.replyEventId && !event.replyEvent)).forEach(event => {
const parentEvent = this.timelineSet.findEventById(event.replyEventId) || this.room.findEventById(event.replyEventId);
if (parentEvent) {
Vue.set(event, "replyEvent", parentEvent);
event["replyEvent"] = parentEvent;
}
});

View file

@ -112,9 +112,7 @@
<v-list-item-avatar>
<v-icon color="black">{{ item.icon }}</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="item.text"></v-list-item-title>
</v-list-item-content>
<v-list-item-title v-text="item.text"></v-list-item-title>
<v-list-item-action>
<v-btn icon v-if="active">
<v-icon color="grey lighten-1">check</v-icon>
@ -142,7 +140,7 @@
<v-card-title class="h2">{{ $t("room_info.message_retention") }}</v-card-title>
<v-card-text v-if="canViewRetentionPolicy">
<v-list v-if="canChangeRetentionPolicy">
<v-list-item link v-on:click="showMessageRetentionDialog = true" class="px-0">
<v-list-item link v-on:click="showMessageRetentionDialog = true" class="px-0 pb-0">
<v-list-item-avatar class="mr-0 pb-0 mb-0">
<v-img
contain
@ -151,9 +149,7 @@
src="@/assets/icons/timer.svg"
/>
</v-list-item-avatar>
<v-list-item-content class="pb-0">
{{ messageRetentionDisplay }}
</v-list-item-content>
{{ messageRetentionDisplay }}
<v-list-item-action class="pb-0 mb-0">
<v-icon>arrow_drop_down</v-icon>

View file

@ -6,11 +6,9 @@
<v-list-item-avatar class="round" size="42" color="#d9d9d9">
<v-icon size="11">$vuetify.icons.ic_new_room</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class="room-list-new-room">{{
$t("menu.new_room")
}}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<!-- invites -->
@ -22,10 +20,8 @@
room.name.substring(0, 1).toUpperCase()
}}</span>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class="room-list-name">{{ room.name }}</v-list-item-title>
<v-list-item-subtitle>{{ room.topic }}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-btn id="btn-accept" class="filled-button" depressed color="black" @click.stop="acceptInvitation(room)">{{
$t("menu.join") }}</v-btn>
@ -41,14 +37,12 @@
room.name.substring(0, 1).toUpperCase()
}}</span>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class="room-list-name">{{ room.name }}
<!-- <v-icon class="ml-2 mb-1" size="10" v-if="isPublic(room)">$vuetify.icons.ic_public</v-icon> -->
</v-list-item-title>
<v-list-item-subtitle class="room-list-new-messages" v-if="notificationCount(room) > 0">
{{ $t("room.room_list_new_messages", { count: notificationCount(room) }) }}
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-icon size="16" v-if="room.roomId == $matrix.currentRoomId">$vuetify.icons.ic_circle_filled</v-icon>
<v-icon size="16" v-else>$vuetify.icons.ic_circle</v-icon>
@ -60,7 +54,6 @@
<script>
import util from "../plugins/utils";
import Vue from "vue";
import AuthedImage from "./AuthedImage.vue";
export default {
@ -143,7 +136,7 @@ export default {
},
acceptInvitation(room) {
Vue.set(this.roomsProcessing, room.roomId, true);
this.roomsProcessing[room.roomId] = true;
this.$matrix.matrixClient
.joinRoom(room.roomId)
.then((ignoredRoom) => {
@ -165,19 +158,19 @@ export default {
console.error("Failed to accept invite: ", err);
})
.finally(() => {
Vue.delete(this.roomsProcessing, room.roomId);
delete this.roomsProcessing[room.roomId];
});
},
rejectInvitation(room) {
Vue.set(this.roomsProcessing, room.roomId, true);
this.roomsProcessing[room.roomId] = true;
this.$matrix
.leaveRoom(room.roomId)
.catch((err) => {
console.error("Failed to reject invite: ", err);
})
.finally(() => {
Vue.delete(this.roomsProcessing, room.roomId);
delete this.roomsProcessing[room.roomId];
});
},

View file

@ -6,10 +6,8 @@
<template v-slot:selection="{ item }">{{ item.title }}</template>
<template v-slot:item="{ item, attrs, on }">
<v-list-item v-on="on" v-bind="attrs" #default="{}">
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
<v-list-item-subtitle>{{ item.description }}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
<v-list-item-subtitle>{{ item.description }}</v-list-item-subtitle>
</v-list-item>
</template>
</v-select>

View file

@ -8,18 +8,18 @@
:outlined="!dark"
>{{ $t("profile_info_popup.you_are") }}&nbsp;
<span v-if="$matrix.currentUser.is_guest">
<i18n path="profile_info_popup.identity_temporary" tag="span">
<i18n-t keypath="profile_info_popup.identity_temporary" tag="span">
<template v-slot:displayName>
<b>{{ displayName }}</b>
</template>
</i18n>
</i18n-t>
</span>
<span v-else>
<i18n path="profile_info_popup.identity" tag="span">
<i18n-t keypath="profile_info_popup.identity" tag="span">
<template v-slot:displayName>
<b>{{ displayName }}</b>
</template>
</i18n>
</i18n-t>
</span>
<v-avatar color="#e0e0e0" right @click.stop="viewProfile">
<AuthedImage v-if="userAvatar" :src="userAvatar" />

View file

@ -25,7 +25,7 @@
>
<v-list>
<v-subheader class="text-uppercase"> {{ $tc("message.seen_by") }}</v-subheader>
<v-list-item v-for="(member, index) in seenBy" :key="index">
<v-list-item v-for="(member, index) in seenBy" :key="index" class="text-left">
<v-list-item-icon>
<v-avatar size="40" color="grey">
<AuthedImage v-if="memberAvatar(member.roomMember)" :src="memberAvatar(member.roomMember)" />
@ -34,10 +34,8 @@
}}</span>
</v-avatar>
</v-list-item-icon>
<v-list-item-content class="text-left">
<v-list-item-title>{{member.roomMember.name}}</v-list-item-title>
<v-list-item-subtitle>{{ seenByTimeStamp(member.readTimestamp) }}</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-title>{{member.roomMember.name}}</v-list-item-title>
<v-list-item-subtitle>{{ seenByTimeStamp(member.readTimestamp) }}</v-list-item-subtitle>
</v-list-item>
</v-list>
</BottomSheet>

View file

@ -3,27 +3,19 @@
<v-list dense>
<v-list-item key="edit" v-if="isEditable" @click.stop="edit">
<v-list-item-icon><v-icon>$vuetify.icons.ic_edit</v-icon></v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ $t("menu.edit") }}</v-list-item-title>
</v-list-item-content>
<v-list-item-title>{{ $t("menu.edit") }}</v-list-item-title>
</v-list-item>
<v-list-item key="pin" v-if="userCanPin && !event.isPinned" @click.stop="pin">
<v-list-item-icon><v-icon>$vuetify.icons.ic_pin</v-icon></v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ $t("menu.pin") }}</v-list-item-title>
</v-list-item-content>
<v-list-item-title>{{ $t("menu.pin") }}</v-list-item-title>
</v-list-item>
<v-list-item key="unpin" v-if="userCanPin && event.isPinned" @click.stop="unpin">
<v-list-item-icon><v-icon>$vuetify.icons.ic_pin</v-icon></v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ $t("menu.unpin") }}</v-list-item-title>
</v-list-item-content>
<v-list-item-title>{{ $t("menu.unpin") }}</v-list-item-title>
</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-title>{{ $t("menu.delete") }}</v-list-item-title>
</v-list-item>
</v-list>
</div>

View file

@ -9,6 +9,7 @@ linkify.options.defaults.className = "link";
linkify.options.defaults.target = { url: "_blank" };
export default {
inject: ['$matrix'],
components: {
QuickReactions,
},

View file

@ -1,7 +1,7 @@
<template>
<div class="created-room-welcome-header">
<div class="mt-2" v-if="roomJoinRule == 'public'">
<i18n path="room_welcome.join_channel" tag="span">
<i18n-t keypath="room_welcome.join_channel" tag="span">
<template v-slot:link>
<div style="position:relative;display:inline-block">
<a @click.stop="copyPublicLink" :href="publicRoomLink" class="text-break">{{ publicRoomLink }}</a>
@ -10,17 +10,17 @@
$t("room_info.link_copied") }}</v-btn>
</div>
</template>
</i18n>
</i18n-t>
</div>
<div class="mt-2" v-else-if="roomJoinRule == 'invite'">
{{ $t("room_welcome.join_invite") }}
</div>
<div class="mt-2" v-if="roomMessageRetention() > 0">
<i18n path="room_welcome.info_retention" tag="span">
<i18n-t keypath="room_welcome.info_retention" tag="span">
<template v-slot:time>
<b>{{ messageRetentionDisplay }}</b>
</template>
</i18n>&nbsp;
</i18n-t>&nbsp;
<a href="#" text @click.prevent="showMessageRetentionDialog = true" style="white-space: pre-line;">{{ $t("room_welcome.change") }}</a>
</div>
<div class="text-end">

View file

@ -7,11 +7,11 @@
</a>
</div>
<div class="mt-2" v-if="roomMessageRetention() > 0">
<i18n path="room_welcome.info_retention_user" tag="span">
<i18n-t keypath="room_welcome.info_retention_user" tag="span">
<template v-slot:time>
<b>{{ messageRetentionDisplay }}</b>
</template>
</i18n>
</i18n-t>
</div>
</div>
</template>

View file

@ -2,7 +2,7 @@
<div class="created-room-welcome-header">
<div>{{ $t("room_welcome.info") }}</div>
<div class="mt-2" v-if="roomJoinRule == 'public'">
<i18n path="room_welcome.join_public" tag="span">
<i18n-t keypath="room_welcome.join_public" tag="span">
<template v-slot:link>
<div style="position:relative;display:inline-block">
<a @click.stop="copyPublicLink" :href="publicRoomLink" class="text-break">{{ publicRoomLink }}</a>
@ -17,7 +17,7 @@
>
</div>
</template>
</i18n>
</i18n-t>
</div>
<div class="mt-2" v-else-if="roomJoinRule == 'invite'">
{{ $t("room_welcome.join_invite") }}