Use vite as bundler
This commit is contained in:
parent
16dc5df9e5
commit
b6f7f75fdd
44 changed files with 4308 additions and 15961 deletions
|
|
@ -4,8 +4,8 @@
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
<link rel="icon" id="favicon" href="<%= BASE_URL %>favicon.ico" />
|
<link rel="icon" id="favicon" href="/favicon.ico" />
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title></title>
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<link rel="apple-touch-icon" href="./icons/icon-72x72.png" sizes="72x72" />
|
<link rel="apple-touch-icon" href="./icons/icon-72x72.png" sizes="72x72" />
|
||||||
<link rel="apple-touch-icon" href="./icons/icon-96x96.png" sizes="96x96" />
|
<link rel="apple-touch-icon" href="./icons/icon-96x96.png" sizes="96x96" />
|
||||||
|
|
@ -31,11 +31,12 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
<strong
|
<strong
|
||||||
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please
|
>We're sorry but the app doesn't work properly without JavaScript enabled. Please
|
||||||
enable it to continue.</strong
|
enable it to continue.</strong
|
||||||
>
|
>
|
||||||
</noscript>
|
</noscript>
|
||||||
17736
package-lock.json
generated
17736
package-lock.json
generated
File diff suppressed because it is too large
Load diff
22
package.json
22
package.json
|
|
@ -3,25 +3,26 @@
|
||||||
"version": "0.1.44",
|
"version": "0.1.44",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"dev": "vite",
|
||||||
"build": "vue-cli-service build",
|
"build": "vite build",
|
||||||
"lint": "vue-cli-service lint",
|
"serve": "vite preview",
|
||||||
"create-sticker-config": "node ./create_sticker_config.js $1"
|
"create-sticker-config": "node ./create_sticker_config.js $1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@matrix-org/olm": "^3.2.12",
|
"@matrix-org/olm": "^3.2.12",
|
||||||
|
"@vitejs/plugin-vue2": "^2.3.3",
|
||||||
"aes-js": "^3.1.2",
|
"aes-js": "^3.1.2",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"browserify-fs": "^1.0.0",
|
"browserify-fs": "^1.0.0",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"clean-insights-sdk": "^2.4",
|
"clean-insights-sdk": "^2.4",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.1",
|
||||||
"data-uri-to-buffer": "^3.0.1",
|
"data-uri-to-buffer": "^3.0.1",
|
||||||
"dayjs": "^1.10.3",
|
"dayjs": "^1.10.3",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"fix-webm-duration": "^1.0.0",
|
"fix-webm-duration": "^1.0.0",
|
||||||
"image-resize": "^1.1.5",
|
"image-resize": "^1.4.1",
|
||||||
"image-size": "^1.0.0",
|
"image-size": "^1.0.0",
|
||||||
"intersection-observer": "^0.12",
|
"intersection-observer": "^0.12",
|
||||||
"js-sha256": "^0.9.0",
|
"js-sha256": "^0.9.0",
|
||||||
|
|
@ -30,7 +31,7 @@
|
||||||
"linkify-html": "^4.1.0",
|
"linkify-html": "^4.1.0",
|
||||||
"linkifyjs": "^4.1.0",
|
"linkifyjs": "^4.1.0",
|
||||||
"material-design-icons-iconfont": "^6.7.0",
|
"material-design-icons-iconfont": "^6.7.0",
|
||||||
"matrix-js-sdk": "^23.4.0",
|
"matrix-js-sdk": "^37.2.0",
|
||||||
"md-gum-polyfill": "^1.0.0",
|
"md-gum-polyfill": "^1.0.0",
|
||||||
"mic-recorder-to-mp3": "^2.2.2",
|
"mic-recorder-to-mp3": "^2.2.2",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
|
|
@ -54,15 +55,16 @@
|
||||||
"vuex-persist": "^3.1.3"
|
"vuex-persist": "^3.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^5.0.8",
|
|
||||||
"@vue/cli-plugin-eslint": "^5.0.8",
|
|
||||||
"@vue/cli-service": "^5.0.8",
|
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"eslint": "^7.0",
|
"eslint": "^7.0",
|
||||||
"eslint-plugin-vue": "^7.0",
|
"eslint-plugin-vue": "^7.0",
|
||||||
"sass": "^1.19.0",
|
"rollup-plugin-polyfill-node": "^0.13.0",
|
||||||
|
"sass": "^1.86.0",
|
||||||
"sass-loader": "^10",
|
"sass-loader": "^10",
|
||||||
|
"unplugin-vue-components": "^28.4.1",
|
||||||
|
"vite": "^6.2.2",
|
||||||
|
"vite-plugin-static-copy": "^2.3.0",
|
||||||
"vue-cli-plugin-vuetify": "^2.5.8",
|
"vue-cli-plugin-vuetify": "^2.5.8",
|
||||||
"vue-template-compiler": "^2.7.16",
|
"vue-template-compiler": "^2.7.16",
|
||||||
"vuetify-loader": "^1.3.0"
|
"vuetify-loader": "^1.3.0"
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="typing-users">
|
<div class="typing-users">
|
||||||
<transition-group name="list" tag="div">
|
<transition-group name="list" tag="div">
|
||||||
<v-avatar v-for="(member) in recordingMembersExceptMe" :key="member.userId" class="typing-user" size="32" color="grey">
|
<v-avatar v-for="(member) in recordingMembersExceptMe" :key="member.userId" class="typing-user" size="32" color="grey">
|
||||||
<img v-if="memberAvatar(member)" :src="memberAvatar(member)" />
|
<AuthedImage v-if="memberAvatar(member)" :src="memberAvatar(member)" />
|
||||||
<span v-else class="white--text headline">{{
|
<span v-else class="white--text headline">{{
|
||||||
member.name.substring(0, 1).toUpperCase()
|
member.name.substring(0, 1).toUpperCase()
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
<div class="typing-users">
|
<div class="typing-users">
|
||||||
<transition-group name="list" tag="div">
|
<transition-group name="list" tag="div">
|
||||||
<v-avatar v-for="reaction in reactions" :key="reaction.member.userId" class="typing-user" size="32" color="grey">
|
<v-avatar v-for="reaction in reactions" :key="reaction.member.userId" class="typing-user" size="32" color="grey">
|
||||||
<img v-if="memberAvatar(reaction.member)" :src="memberAvatar(reaction.member)" />
|
<AuthedImage v-if="memberAvatar(reaction.member)" :src="memberAvatar(reaction.member)" />
|
||||||
<span v-else class="white--text headline">{{
|
<span v-else class="white--text headline">{{
|
||||||
reaction.member.name.substring(0, 1).toUpperCase()
|
reaction.member.name.substring(0, 1).toUpperCase()
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
@ -85,10 +85,12 @@
|
||||||
<script>
|
<script>
|
||||||
import messageMixin from "./messages/messageMixin";
|
import messageMixin from "./messages/messageMixin";
|
||||||
import util from "../plugins/utils";
|
import util from "../plugins/utils";
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
|
import clapping from "@/assets/sounds/clapping.mp3";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [messageMixin],
|
mixins: [messageMixin],
|
||||||
components: {},
|
components: { AuthedImage },
|
||||||
props: {
|
props: {
|
||||||
autoplay: {
|
autoplay: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
@ -270,7 +272,7 @@ export default {
|
||||||
},
|
},
|
||||||
audioPlaybackReaction(reaction) {
|
audioPlaybackReaction(reaction) {
|
||||||
// Play sound!
|
// Play sound!
|
||||||
const audio = new Audio(require("@/assets/sounds/clapping.mp3"));
|
const audio = new Audio(clapping);
|
||||||
audio.volume = 0.6;
|
audio.volume = 0.6;
|
||||||
audio.play();
|
audio.play();
|
||||||
|
|
||||||
|
|
@ -407,7 +409,9 @@ export default {
|
||||||
40,
|
40,
|
||||||
40,
|
40,
|
||||||
"scale",
|
"scale",
|
||||||
true
|
true,
|
||||||
|
false,
|
||||||
|
this.$matrix.useAuthedMedia,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
56
src/components/AuthedImage.vue
Normal file
56
src/components/AuthedImage.vue
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
<template>
|
||||||
|
<img v-if="imageSrc" :src="imageSrc" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "AuthedImage",
|
||||||
|
props: {
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
default: function () {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.src) {
|
||||||
|
console.error("GOT URL", this.src);
|
||||||
|
if (this.$matrix.useAuthedMedia) {
|
||||||
|
axios
|
||||||
|
.get(this.src, { responseType: "blob", headers: {
|
||||||
|
Authorization: `Bearer ${this.$matrix.matrixClient.getAccessToken()}`,
|
||||||
|
}})
|
||||||
|
.then((response) => {
|
||||||
|
this.imageSrc = URL.createObjectURL(response.data);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("Download error: ", err);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.imageSrc = this.src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
if (this.imageSrc && this.src != this.imageSrc) {
|
||||||
|
const url = this.imageSrc;
|
||||||
|
this.imageSrc = null;
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
imageSrc: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
|
|
@ -4,11 +4,13 @@
|
||||||
v-on:header-click="onHeaderClick"
|
v-on:header-click="onHeaderClick"
|
||||||
v-on:view-room-details="viewRoomDetails"
|
v-on:view-room-details="viewRoomDetails"
|
||||||
v-on:purge="showPurgeConfirmation = true"
|
v-on:purge="showPurgeConfirmation = true"
|
||||||
|
v-on:download="downloadingChat = true"
|
||||||
v-if="!useFileModeNonAdmin && $matrix.isDirectRoom(room)" />
|
v-if="!useFileModeNonAdmin && $matrix.isDirectRoom(room)" />
|
||||||
<ChatHeader class="chat-header flex-grow-0 flex-shrink-0"
|
<ChatHeader class="chat-header flex-grow-0 flex-shrink-0"
|
||||||
v-on:header-click="onHeaderClick"
|
v-on:header-click="onHeaderClick"
|
||||||
v-on:view-room-details="viewRoomDetails"
|
v-on:view-room-details="viewRoomDetails"
|
||||||
v-on:purge="showPurgeConfirmation = true"
|
v-on:purge="showPurgeConfirmation = true"
|
||||||
|
v-on:download="downloadingChat = true"
|
||||||
v-else-if="!useFileModeNonAdmin" />
|
v-else-if="!useFileModeNonAdmin" />
|
||||||
<AudioLayout ref="chatContainer" class="auto-audio-player-root" v-if="useVoiceMode" :room="room"
|
<AudioLayout ref="chatContainer" class="auto-audio-player-root" v-if="useVoiceMode" :room="room"
|
||||||
:events="events" :autoplay="!showRecorder"
|
:events="events" :autoplay="!showRecorder"
|
||||||
|
|
@ -311,7 +313,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MessageOperationsBottomSheet ref="messageOperationsSheet">
|
<MessageOperationsBottomSheet ref="messageOperationsSheet">
|
||||||
<VEmojiPicker ref="emojiPicker" @select="emojiSelected" :i18n="i18nEmoji"/>
|
<!-- <VEmojiPicker ref="emojiPicker" @select="emojiSelected" :i18n="i18nEmoji"/> -->
|
||||||
</MessageOperationsBottomSheet>
|
</MessageOperationsBottomSheet>
|
||||||
|
|
||||||
<StickerPickerBottomSheet ref="stickerPickerSheet" v-on:selectSticker="sendSticker" />
|
<StickerPickerBottomSheet ref="stickerPickerSheet" v-on:selectSticker="sendSticker" />
|
||||||
|
|
@ -354,6 +356,8 @@
|
||||||
<!-- PURGE ROOM POPUP -->
|
<!-- PURGE ROOM POPUP -->
|
||||||
<PurgeRoomDialog :show="showPurgeConfirmation" :room="room" @close="showPurgeConfirmation = false" />
|
<PurgeRoomDialog :show="showPurgeConfirmation" :room="room" @close="showPurgeConfirmation = false" />
|
||||||
|
|
||||||
|
<RoomExport :room="room" v-if="downloadingChat" v-on:close="downloadingChat = false" />
|
||||||
|
|
||||||
<!-- Heart animation -->
|
<!-- Heart animation -->
|
||||||
<div :class="['heart-wrapper', { 'is-active': heartAnimation }]" :style="hearAnimationPosition">
|
<div :class="['heart-wrapper', { 'is-active': heartAnimation }]" :style="hearAnimationPosition">
|
||||||
<div :class="['heart', { 'is-active': heartAnimation }]" />
|
<div :class="['heart', { 'is-active': heartAnimation }]" />
|
||||||
|
|
@ -390,10 +394,13 @@ import roomMembersMixin from "./roomMembersMixin";
|
||||||
import PurgeRoomDialog from "../components/PurgeRoomDialog";
|
import PurgeRoomDialog from "../components/PurgeRoomDialog";
|
||||||
import MessageErrorHandler from "./MessageErrorHandler";
|
import MessageErrorHandler from "./MessageErrorHandler";
|
||||||
import MessageOperationsChannel from './messages/channel/MessageOperationsChannel.vue';
|
import MessageOperationsChannel from './messages/channel/MessageOperationsChannel.vue';
|
||||||
|
import sizeOf from "image-size";
|
||||||
|
import dataUriToBuffer from "data-uri-to-buffer";
|
||||||
|
import prettyBytes from "pretty-bytes";
|
||||||
|
import RoomExport from "./RoomExport.vue";
|
||||||
|
|
||||||
|
//import { VEmojiPicker } from 'v-emoji-picker';
|
||||||
|
|
||||||
const sizeOf = require("image-size");
|
|
||||||
const dataUriToBuffer = require("data-uri-to-buffer");
|
|
||||||
const prettyBytes = require("pretty-bytes");
|
|
||||||
|
|
||||||
const READ_RECEIPT_TIMEOUT = 5000; /* How long a message must have been visible before the read marker is updated */
|
const READ_RECEIPT_TIMEOUT = 5000; /* How long a message must have been visible before the read marker is updated */
|
||||||
const WINDOW_BUFFER_SIZE = 0.3; /** Relative window height of when we start paginating. Always keep this much loaded before and after our scroll position! */
|
const WINDOW_BUFFER_SIZE = 0.3; /** Relative window height of when we start paginating. Always keep this much loaded before and after our scroll position! */
|
||||||
|
|
@ -445,7 +452,9 @@ export default {
|
||||||
PurgeRoomDialog,
|
PurgeRoomDialog,
|
||||||
WelcomeHeaderChannelUser,
|
WelcomeHeaderChannelUser,
|
||||||
MessageErrorHandler,
|
MessageErrorHandler,
|
||||||
MessageOperationsChannel
|
MessageOperationsChannel,
|
||||||
|
RoomExport,
|
||||||
|
//VEmojiPicker
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -540,7 +549,8 @@ export default {
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0
|
left: 0
|
||||||
},
|
},
|
||||||
reverseOrder: false
|
reverseOrder: false,
|
||||||
|
downloadingChat: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -1748,7 +1758,7 @@ export default {
|
||||||
|
|
||||||
setReplyToImage(event) {
|
setReplyToImage(event) {
|
||||||
util
|
util
|
||||||
.getThumbnail(this.$matrix.matrixClient, event, this.$config)
|
.getThumbnail(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, event, this.$config)
|
||||||
.then((url) => {
|
.then((url) => {
|
||||||
this.replyToImg = url;
|
this.replyToImg = url;
|
||||||
})
|
})
|
||||||
|
|
@ -1796,9 +1806,9 @@ export default {
|
||||||
download(event) {
|
download(event) {
|
||||||
if ((event.isThreadRoot || event.isMxThread) && this.timelineSet) {
|
if ((event.isThreadRoot || event.isMxThread) && this.timelineSet) {
|
||||||
const children = this.timelineSet.relations.getAllChildEventsForEvent(event.getId()).filter(e => util.downloadableTypes().includes(e.getContent().msgtype));
|
const children = this.timelineSet.relations.getAllChildEventsForEvent(event.getId()).filter(e => util.downloadableTypes().includes(e.getContent().msgtype));
|
||||||
children.forEach(child => util.download(this.$matrix.matrixClient, child));
|
children.forEach(child => util.download(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, child));
|
||||||
} else {
|
} else {
|
||||||
util.download(this.$matrix.matrixClient, event);
|
util.download(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
class="chat-header-members text-start ma-0 pa-0"
|
class="chat-header-members text-start ma-0 pa-0"
|
||||||
>
|
>
|
||||||
<v-avatar size="48" class="clickable me-2 chat-header-avatar" color="grey" @click.stop="onAvatarClicked">
|
<v-avatar size="48" class="clickable me-2 chat-header-avatar" color="grey" @click.stop="onAvatarClicked">
|
||||||
<v-img v-if="roomAvatar" :src="roomAvatar" />
|
<AuthedImage v-if="roomAvatar" :src="roomAvatar" />
|
||||||
<span v-else class="white--text headline">{{
|
<span v-else class="white--text headline">{{
|
||||||
room.name.substring(0, 1).toUpperCase()
|
room.name.substring(0, 1).toUpperCase()
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
<v-col cols="auto" class="text-end ma-0 pa-0 ms-1">
|
<v-col cols="auto" class="text-end ma-0 pa-0 ms-1">
|
||||||
<v-avatar :class="{ 'avatar-32': true, 'clickable': true, 'popup-open': showProfileInfo }" size="26"
|
<v-avatar :class="{ 'avatar-32': true, 'clickable': true, 'popup-open': showProfileInfo }" size="26"
|
||||||
color="#e0e0e0" @click.stop="showProfileInfo = true">
|
color="#e0e0e0" @click.stop="showProfileInfo = true">
|
||||||
<img v-if="userAvatar" :src="userAvatar" />
|
<AuthedImage v-if="userAvatar" :src="userAvatar" />
|
||||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
@ -65,8 +65,6 @@
|
||||||
<MoreMenuPopup :show="showMoreMenu" :menuItems="moreMenuItems" @close="showMoreMenu = false"
|
<MoreMenuPopup :show="showMoreMenu" :menuItems="moreMenuItems" @close="showMoreMenu = false"
|
||||||
v-on:leave="showLeaveConfirmation = true" />
|
v-on:leave="showLeaveConfirmation = true" />
|
||||||
|
|
||||||
<RoomExport :room="room" v-if="downloadingChat" v-on:close="downloadingChat = false" />
|
|
||||||
|
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -75,8 +73,7 @@ import LeaveRoomDialog from "../components/LeaveRoomDialog";
|
||||||
import ProfileInfoPopup from "../components/ProfileInfoPopup";
|
import ProfileInfoPopup from "../components/ProfileInfoPopup";
|
||||||
import MoreMenuPopup from "../components/MoreMenuPopup";
|
import MoreMenuPopup from "../components/MoreMenuPopup";
|
||||||
import profileInfoMixin from "../components/profileInfoMixin";
|
import profileInfoMixin from "../components/profileInfoMixin";
|
||||||
import RoomExport from "../components/RoomExport";
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
|
|
||||||
import roomInfoMixin from "./roomInfoMixin";
|
import roomInfoMixin from "./roomInfoMixin";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -86,7 +83,7 @@ export default {
|
||||||
LeaveRoomDialog,
|
LeaveRoomDialog,
|
||||||
ProfileInfoPopup,
|
ProfileInfoPopup,
|
||||||
MoreMenuPopup,
|
MoreMenuPopup,
|
||||||
RoomExport
|
AuthedImage
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -94,7 +91,6 @@ export default {
|
||||||
showLeaveConfirmation: false,
|
showLeaveConfirmation: false,
|
||||||
showProfileInfo: false,
|
showProfileInfo: false,
|
||||||
showMoreMenu: false,
|
showMoreMenu: false,
|
||||||
downloadingChat: false,
|
|
||||||
showMissedItemsInfo: false,
|
showMissedItemsInfo: false,
|
||||||
|
|
||||||
/** Timer for showing the "missed items" hint */
|
/** Timer for showing the "missed items" hint */
|
||||||
|
|
@ -142,7 +138,7 @@ export default {
|
||||||
if (this.userCanExportChat) {
|
if (this.userCanExportChat) {
|
||||||
items.push({
|
items.push({
|
||||||
icon: '$vuetify.icons.ic_download', text: this.$t('room_info.download_chat'), handler: () => {
|
icon: '$vuetify.icons.ic_download', text: this.$t('room_info.download_chat'), handler: () => {
|
||||||
this.downloadingChat = true;
|
this.$emit("download", { event: this.event });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -171,7 +167,9 @@ export default {
|
||||||
40,
|
40,
|
||||||
40,
|
40,
|
||||||
"scale",
|
"scale",
|
||||||
true
|
true,
|
||||||
|
false,
|
||||||
|
this.$matrix.useAuthedMedia
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
<v-col v-if="$matrix.joinedRooms.length > 1" cols="auto" class="text-end ma-0 pa-0 ms-1">
|
<v-col v-if="$matrix.joinedRooms.length > 1" cols="auto" class="text-end ma-0 pa-0 ms-1">
|
||||||
<v-avatar :class="{ 'avatar-32': true, 'clickable': true, 'popup-open': showProfileInfo }" size="26"
|
<v-avatar :class="{ 'avatar-32': true, 'clickable': true, 'popup-open': showProfileInfo }" size="26"
|
||||||
color="#e0e0e0" @click.stop="showProfileInfo = true">
|
color="#e0e0e0" @click.stop="showProfileInfo = true">
|
||||||
<img v-if="userAvatar" :src="userAvatar" />
|
<AuthedImage v-if="userAvatar" :src="userAvatar" />
|
||||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
@ -75,8 +75,6 @@
|
||||||
<MoreMenuPopup :show="showMoreMenu" :menuItems="moreMenuItems" @close="showMoreMenu = false"
|
<MoreMenuPopup :show="showMoreMenu" :menuItems="moreMenuItems" @close="showMoreMenu = false"
|
||||||
v-on:leave="showLeaveConfirmation = true" />
|
v-on:leave="showLeaveConfirmation = true" />
|
||||||
|
|
||||||
<RoomExport :room="room" v-if="downloadingChat" v-on:close="downloadingChat = false" />
|
|
||||||
|
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -84,8 +82,7 @@
|
||||||
import LeaveRoomDialog from "../components/LeaveRoomDialog";
|
import LeaveRoomDialog from "../components/LeaveRoomDialog";
|
||||||
import ProfileInfoPopup from "../components/ProfileInfoPopup";
|
import ProfileInfoPopup from "../components/ProfileInfoPopup";
|
||||||
import MoreMenuPopup from "../components/MoreMenuPopup";
|
import MoreMenuPopup from "../components/MoreMenuPopup";
|
||||||
import RoomExport from "../components/RoomExport";
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
|
|
||||||
import ChatHeader from "./ChatHeader.vue";
|
import ChatHeader from "./ChatHeader.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -95,7 +92,7 @@ export default {
|
||||||
LeaveRoomDialog,
|
LeaveRoomDialog,
|
||||||
ProfileInfoPopup,
|
ProfileInfoPopup,
|
||||||
MoreMenuPopup,
|
MoreMenuPopup,
|
||||||
RoomExport
|
AuthedImage
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
>
|
>
|
||||||
<template v-slot:default="{ active }">
|
<template v-slot:default="{ active }">
|
||||||
<v-list-item-avatar color="grey">
|
<v-list-item-avatar color="grey">
|
||||||
<img v-if="memberAvatar(member)" :src="memberAvatar(member)" />
|
<AuthedImage v-if="memberAvatar(member)" :src="memberAvatar(member)" />
|
||||||
<span v-else class="white--text headline">{{
|
<span v-else class="white--text headline">{{
|
||||||
member.name.substring(0, 1).toUpperCase()
|
member.name.substring(0, 1).toUpperCase()
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
@ -64,9 +64,11 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import util from "../plugins/utils";
|
import util from "../plugins/utils";
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Invite",
|
name: "Invite",
|
||||||
|
comments: { AuthedImage },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
status: "",
|
status: "",
|
||||||
|
|
@ -155,7 +157,9 @@ export default {
|
||||||
40,
|
40,
|
||||||
40,
|
40,
|
||||||
"scale",
|
"scale",
|
||||||
true
|
true,
|
||||||
|
false,
|
||||||
|
this.$matrix.useAuthedMedia
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<v-avatar class="join-avatar">
|
<v-avatar class="join-avatar">
|
||||||
<v-img v-if="roomAvatar" :src="roomAvatar" />
|
<AuthedImage v-if="roomAvatar" :src="roomAvatar" />
|
||||||
<span v-else class="white--text headline">
|
<span v-else class="white--text headline">
|
||||||
{{ roomName.substring(0, 1).toUpperCase() }}
|
{{ roomName.substring(0, 1).toUpperCase() }}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
{{ $t("join.joining_as") }}
|
{{ $t("join.joining_as") }}
|
||||||
<div class="d-inline-block">
|
<div class="d-inline-block">
|
||||||
<v-avatar color="#e0e0e0">
|
<v-avatar color="#e0e0e0">
|
||||||
<v-img v-if="userAvatar" :src="userAvatar" />
|
<AuthedImage v-if="userAvatar" :src="userAvatar" />
|
||||||
<span v-else class="white--text headline">{{ userAvatarLetter }}</span>
|
<span v-else class="white--text headline">{{ userAvatarLetter }}</span>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -134,13 +134,15 @@ import LanguageMixin from "./languageMixin";
|
||||||
import rememberMeMixin from "./rememberMeMixin";
|
import rememberMeMixin from "./rememberMeMixin";
|
||||||
import logoMixin from "./logoMixin";
|
import logoMixin from "./logoMixin";
|
||||||
import SelectLanguageDialog from "./SelectLanguageDialog.vue";
|
import SelectLanguageDialog from "./SelectLanguageDialog.vue";
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Join",
|
name: "Join",
|
||||||
mixins: [LanguageMixin, rememberMeMixin, logoMixin],
|
mixins: [LanguageMixin, rememberMeMixin, logoMixin],
|
||||||
components: {
|
components: {
|
||||||
SelectLanguageDialog,
|
SelectLanguageDialog,
|
||||||
InteractiveAuth
|
InteractiveAuth,
|
||||||
|
AuthedImage
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -194,7 +196,7 @@ export default {
|
||||||
if (!this.$matrix.userAvatar) {
|
if (!this.$matrix.userAvatar) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.$matrix.matrixClient.mxcUrlToHttp(this.$matrix.userAvatar, 80, 80, "scale", true);
|
return this.$matrix.matrixClient.mxcUrlToHttp(this.$matrix.userAvatar, 80, 80, "scale", true, undefined, this.$matrix.useAuthedMedia);
|
||||||
},
|
},
|
||||||
|
|
||||||
userAvatarLetter() {
|
userAvatarLetter() {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ export default {
|
||||||
},
|
},
|
||||||
errorCaptured(err, ignoredvm, ignoredinfo) {
|
errorCaptured(err, ignoredvm, ignoredinfo) {
|
||||||
this.err = err;
|
this.err = err;
|
||||||
|
console.error("IGNORE", err, ignoredvm, ignoredinfo);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<v-row v-if="showProfile" class="profile-row clickable" @click="viewProfile" no-gutters align-content="center">
|
<v-row v-if="showProfile" class="profile-row clickable" @click="viewProfile" no-gutters align-content="center">
|
||||||
<v-col cols="auto" class="me-2">
|
<v-col cols="auto" class="me-2">
|
||||||
<v-avatar class="avatar-32" size="32" color="#e0e0e0" @click.stop="viewProfile">
|
<v-avatar class="avatar-32" size="32" color="#e0e0e0" @click.stop="viewProfile">
|
||||||
<img v-if="userAvatar" :src="userAvatar" />
|
<AuthedImage v-if="userAvatar" :src="userAvatar" />
|
||||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
@ -28,11 +28,12 @@
|
||||||
<script>
|
<script>
|
||||||
import profileInfoMixin from "./profileInfoMixin";
|
import profileInfoMixin from "./profileInfoMixin";
|
||||||
import ActionRow from "./ActionRow.vue";
|
import ActionRow from "./ActionRow.vue";
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MoreMenuPopup",
|
name: "MoreMenuPopup",
|
||||||
mixins: [profileInfoMixin],
|
mixins: [profileInfoMixin],
|
||||||
components: { ActionRow },
|
components: { ActionRow, AuthedImage },
|
||||||
props: {
|
props: {
|
||||||
show: {
|
show: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
@click="showAvatarPicker"
|
@click="showAvatarPicker"
|
||||||
v-if="isAvatarLoaded"
|
v-if="isAvatarLoaded"
|
||||||
>
|
>
|
||||||
<img v-if="userAvatar" :src="userAvatar" />
|
<AuthedImage v-if="userAvatar" :src="userAvatar" />
|
||||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||||
<input
|
<input
|
||||||
id="avatar-picker"
|
id="avatar-picker"
|
||||||
|
|
@ -261,6 +261,7 @@ import ActionRow from "./ActionRow.vue";
|
||||||
import util from "../plugins/utils";
|
import util from "../plugins/utils";
|
||||||
import profileInfoMixin from "./profileInfoMixin";
|
import profileInfoMixin from "./profileInfoMixin";
|
||||||
import LogoutRoomDialog from './LogoutRoomDialog.vue';
|
import LogoutRoomDialog from './LogoutRoomDialog.vue';
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
import CopyLink from "./CopyLink.vue"
|
import CopyLink from "./CopyLink.vue"
|
||||||
import { requestNotificationPermission, windowNotificationPermission } from "../plugins/notificationAndServiceWorker.js"
|
import { requestNotificationPermission, windowNotificationPermission } from "../plugins/notificationAndServiceWorker.js"
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
|
@ -272,7 +273,8 @@ export default {
|
||||||
ActionRow,
|
ActionRow,
|
||||||
SelectLanguageDialog,
|
SelectLanguageDialog,
|
||||||
LogoutRoomDialog,
|
LogoutRoomDialog,
|
||||||
CopyLink
|
CopyLink,
|
||||||
|
AuthedImage
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="auto" class="pa-2">
|
<v-col cols="auto" class="pa-2">
|
||||||
<v-avatar class="avatar-32" size="32" color="#e0e0e0" @click.stop="viewProfile">
|
<v-avatar class="avatar-32" size="32" color="#e0e0e0" @click.stop="viewProfile">
|
||||||
<img v-if="userAvatar" :src="userAvatar" />
|
<AuthedImage v-if="userAvatar" :src="userAvatar" />
|
||||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
@ -64,10 +64,12 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import profileInfoMixin from "./profileInfoMixin";
|
import profileInfoMixin from "./profileInfoMixin";
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ProfileInfoPopup",
|
name: "ProfileInfoPopup",
|
||||||
mixins: [profileInfoMixin],
|
mixins: [profileInfoMixin],
|
||||||
|
components: { AuthedImage },
|
||||||
props: {
|
props: {
|
||||||
show: {
|
show: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
size="32"
|
size="32"
|
||||||
color="#e0e0e0"
|
color="#e0e0e0"
|
||||||
>
|
>
|
||||||
<img v-if="userAvatar" :src="userAvatar" />
|
<AuthedImage v-if="userAvatar" :src="userAvatar" />
|
||||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -77,10 +77,12 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import profileInfoMixin from "./profileInfoMixin";
|
import profileInfoMixin from "./profileInfoMixin";
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "QuoteView",
|
name: "QuoteView",
|
||||||
mixins: [profileInfoMixin],
|
mixins: [profileInfoMixin],
|
||||||
|
components: { AuthedImage },
|
||||||
props: {
|
props: {
|
||||||
roomWasPurged: {
|
roomWasPurged: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<v-avatar :class="{'room-avatar':true, 'cursor-pointer':userCanPurgeRoom}" @click="userCanPurgeRoom?showRoomAvatarPicker():null" v-if="isRoomAvatarLoaded">
|
<v-avatar :class="{'room-avatar':true, 'cursor-pointer':userCanPurgeRoom}" @click="userCanPurgeRoom?showRoomAvatarPicker():null" v-if="isRoomAvatarLoaded">
|
||||||
<v-img v-if="roomAvatar" :src="roomAvatar"/>
|
<AuthedImage v-if="roomAvatar" :src="roomAvatar"/>
|
||||||
<span v-else class="white--text headline">{{
|
<span v-else class="white--text headline">{{
|
||||||
roomName.substring(0, 1).toUpperCase()
|
roomName.substring(0, 1).toUpperCase()
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
@ -28,10 +28,12 @@
|
||||||
<script>
|
<script>
|
||||||
import util from "../plugins/utils";
|
import util from "../plugins/utils";
|
||||||
import roomInfoMixin from "./roomInfoMixin";
|
import roomInfoMixin from "./roomInfoMixin";
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "RoomAvatarPicker",
|
name: "RoomAvatarPicker",
|
||||||
mixins: [roomInfoMixin],
|
mixins: [roomInfoMixin],
|
||||||
|
components: { AuthedImage },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isRoomAvatarLoaded: true,
|
isRoomAvatarLoaded: true,
|
||||||
|
|
@ -56,7 +58,10 @@ export default {
|
||||||
self.isRoomAvatarLoaded = true;
|
self.isRoomAvatarLoaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
).then((url) => {
|
||||||
|
console.error("UPDATE AVATAR", url);
|
||||||
|
this.room.avatar = url;
|
||||||
|
})
|
||||||
},
|
},
|
||||||
handleRoomPickedAvatar(event) {
|
handleRoomPickedAvatar(event) {
|
||||||
if (event.target.files && event.target.files[0]) {
|
if (event.target.files && event.target.files[0]) {
|
||||||
|
|
|
||||||
|
|
@ -328,7 +328,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!avatarFolder.file(fileName)) {
|
if (!avatarFolder.file(fileName)) {
|
||||||
const url = member.getAvatarUrl(this.$matrix.matrixClient.getHomeserverUrl(), 40, 40, "scale", true);
|
const url = member.getAvatarUrl(this.$matrix.matrixClient.getHomeserverUrl(), 40, 40, "scale", true, false, this.$matrix.useAuthedMedia);
|
||||||
if (url) {
|
if (url) {
|
||||||
avatarFolder.file(fileName, "empty");
|
avatarFolder.file(fileName, "empty");
|
||||||
downloadPromises.push(
|
downloadPromises.push(
|
||||||
|
|
@ -367,7 +367,7 @@ export default {
|
||||||
|
|
||||||
downloadPromises.push(
|
downloadPromises.push(
|
||||||
util
|
util
|
||||||
.getAttachment(this.$matrix.matrixClient, comp.event, null, true)
|
.getAttachment(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, comp.event, null, true)
|
||||||
.then((blob) => {
|
.then((blob) => {
|
||||||
return new Promise((resolve, ignoredReject) => {
|
return new Promise((resolve, ignoredReject) => {
|
||||||
let mime = blob.type;
|
let mime = blob.type;
|
||||||
|
|
@ -413,7 +413,7 @@ export default {
|
||||||
case "MessageOutgoingAudioExport":
|
case "MessageOutgoingAudioExport":
|
||||||
downloadPromises.push(
|
downloadPromises.push(
|
||||||
util
|
util
|
||||||
.getAttachment(this.$matrix.matrixClient, comp.event, null, true)
|
.getAttachment(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, comp.event, null, true)
|
||||||
.then((blob) => {
|
.then((blob) => {
|
||||||
if (currentMediaSize + blob.size <= maxMediaSize) {
|
if (currentMediaSize + blob.size <= maxMediaSize) {
|
||||||
currentMediaSize += blob.size;
|
currentMediaSize += blob.size;
|
||||||
|
|
@ -443,7 +443,7 @@ export default {
|
||||||
case "MessageOutgoingVideoExport":
|
case "MessageOutgoingVideoExport":
|
||||||
downloadPromises.push(
|
downloadPromises.push(
|
||||||
util
|
util
|
||||||
.getAttachment(this.$matrix.matrixClient, comp.event, null, true)
|
.getAttachment(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, comp.event, null, true)
|
||||||
.then((blob) => {
|
.then((blob) => {
|
||||||
if (currentMediaSize + blob.size <= maxMediaSize) {
|
if (currentMediaSize + blob.size <= maxMediaSize) {
|
||||||
currentMediaSize += blob.size;
|
currentMediaSize += blob.size;
|
||||||
|
|
@ -472,7 +472,7 @@ export default {
|
||||||
case "MessageOutgoingFileExport":
|
case "MessageOutgoingFileExport":
|
||||||
downloadPromises.push(
|
downloadPromises.push(
|
||||||
util
|
util
|
||||||
.getAttachment(this.$matrix.matrixClient, comp.event, null, true)
|
.getAttachment(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, comp.event, null, true)
|
||||||
.then((blob) => {
|
.then((blob) => {
|
||||||
if (currentMediaSize + blob.size <= maxMediaSize) {
|
if (currentMediaSize + blob.size <= maxMediaSize) {
|
||||||
currentMediaSize += blob.size;
|
currentMediaSize += blob.size;
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="user-icon-with-badge">
|
<div class="user-icon-with-badge">
|
||||||
<v-avatar class="avatar" size="32" color="grey">
|
<v-avatar class="avatar" size="32" color="grey">
|
||||||
<img v-if="memberAvatar(member)" :src="memberAvatar(member)" />
|
<AuthedImage v-if="memberAvatar(member)" :src="memberAvatar(member)" />
|
||||||
<span v-else class="white--text headline">{{
|
<span v-else class="white--text headline">{{
|
||||||
member.name.substring(0, 1).toUpperCase()
|
member.name.substring(0, 1).toUpperCase()
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
@ -311,10 +311,12 @@ import ReportRoomDialog from "../components/ReportRoomDialog";
|
||||||
import RoomExport from "../components/RoomExport";
|
import RoomExport from "../components/RoomExport";
|
||||||
import RoomAvatarPicker from "../components/RoomAvatarPicker";
|
import RoomAvatarPicker from "../components/RoomAvatarPicker";
|
||||||
import CopyLink from "../components/CopyLink.vue"
|
import CopyLink from "../components/CopyLink.vue"
|
||||||
import UserProfileDialog from "./UserProfileDialog.vue"
|
import UserProfileDialog from "./UserProfileDialog.vue";
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
import roomInfoMixin from "./roomInfoMixin";
|
import roomInfoMixin from "./roomInfoMixin";
|
||||||
import roomTypeMixin from "./roomTypeMixin";
|
import roomTypeMixin from "./roomTypeMixin";
|
||||||
import util, { STATE_EVENT_ROOM_TYPE } from "../plugins/utils";
|
import util, { STATE_EVENT_ROOM_TYPE } from "../plugins/utils";
|
||||||
|
import buildVersion from "../assets/version.txt?raw";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "RoomInfo",
|
name: "RoomInfo",
|
||||||
|
|
@ -327,7 +329,8 @@ export default {
|
||||||
UserProfileDialog,
|
UserProfileDialog,
|
||||||
RoomExport,
|
RoomExport,
|
||||||
RoomAvatarPicker,
|
RoomAvatarPicker,
|
||||||
CopyLink
|
CopyLink,
|
||||||
|
AuthedImage
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -365,9 +368,7 @@ export default {
|
||||||
this.user = this.$matrix.matrixClient.getUser(this.$matrix.currentUserId);
|
this.user = this.$matrix.matrixClient.getUser(this.$matrix.currentUserId);
|
||||||
|
|
||||||
// Display build version
|
// Display build version
|
||||||
const version = require("!!raw-loader!../assets/version.txt").default;
|
this.buildVersion = buildVersion;
|
||||||
console.log("Version", version);
|
|
||||||
this.buildVersion = version;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
destroyed() {
|
destroyed() {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
<v-list-item :disabled="roomsProcessing[room.roomId]" v-for="room in invitedRooms" :key="room.roomId"
|
<v-list-item :disabled="roomsProcessing[room.roomId]" v-for="room in invitedRooms" :key="room.roomId"
|
||||||
:value="room" class="room-list-room">
|
:value="room" class="room-list-room">
|
||||||
<v-list-item-avatar size="42" color="#d9d9d9">
|
<v-list-item-avatar size="42" color="#d9d9d9">
|
||||||
<v-img v-if="roomAvatar(room)" :src="roomAvatar(room)" />
|
<AuthedImage v-if="roomAvatar(room)" :src="roomAvatar(room)" />
|
||||||
<span v-else class="white--text headline">{{
|
<span v-else class="white--text headline">{{
|
||||||
room.name.substring(0, 1).toUpperCase()
|
room.name.substring(0, 1).toUpperCase()
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
<v-list-item v-for="room in joinedRooms" :key="room.roomId" :value="room" class="room-list-room">
|
<v-list-item v-for="room in joinedRooms" :key="room.roomId" :value="room" class="room-list-room">
|
||||||
<v-list-item-avatar size="42" color="#d9d9d9" :class="[{'rounded-circle': isDirect(room)}]">
|
<v-list-item-avatar size="42" color="#d9d9d9" :class="[{'rounded-circle': isDirect(room)}]">
|
||||||
<v-img v-if="roomAvatar(room)" :src="roomAvatar(room)" />
|
<AuthedImage v-if="roomAvatar(room)" :src="roomAvatar(room)" />
|
||||||
<span v-else class="white--text headline">{{
|
<span v-else class="white--text headline">{{
|
||||||
room.name.substring(0, 1).toUpperCase()
|
room.name.substring(0, 1).toUpperCase()
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
@ -61,10 +61,11 @@
|
||||||
<script>
|
<script>
|
||||||
import util from "../plugins/utils";
|
import util from "../plugins/utils";
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "RoomList",
|
name: "RoomList",
|
||||||
|
components: { AuthedImage },
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
@ -111,7 +112,9 @@ export default {
|
||||||
42,
|
42,
|
||||||
42,
|
42,
|
||||||
"scale",
|
"scale",
|
||||||
true
|
true,
|
||||||
|
false,
|
||||||
|
this.$matrix.useAuthedMedia
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="dialog-content text-center member-action-dialog">
|
<div class="dialog-content text-center member-action-dialog">
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
<v-avatar class="avatar" size="56" color="grey">
|
<v-avatar class="avatar" size="56" color="grey">
|
||||||
<img v-if="memberAvatarComp" :src="memberAvatarComp" />
|
<AuthedImage v-if="memberAvatarComp" :src="memberAvatarComp" />
|
||||||
<span v-else class="white--text headline">{{ firstLetterUserName }}</span>
|
<span v-else class="white--text headline">{{ firstLetterUserName }}</span>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -63,13 +63,15 @@
|
||||||
<script>
|
<script>
|
||||||
import roomInfoMixin from "./roomInfoMixin";
|
import roomInfoMixin from "./roomInfoMixin";
|
||||||
import DeviceList from "../components/DeviceList";
|
import DeviceList from "../components/DeviceList";
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
import util from "../plugins/utils";
|
import util from "../plugins/utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "UserProfileDialog",
|
name: "UserProfileDialog",
|
||||||
mixins: [roomInfoMixin],
|
mixins: [roomInfoMixin],
|
||||||
components: {
|
components: {
|
||||||
DeviceList
|
DeviceList,
|
||||||
|
AuthedImage
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
show: {
|
show: {
|
||||||
|
|
|
||||||
|
|
@ -167,10 +167,11 @@ const State = {
|
||||||
};
|
};
|
||||||
import util from "../plugins/utils";
|
import util from "../plugins/utils";
|
||||||
import VoiceRecorderLock from "./VoiceRecorderLock";
|
import VoiceRecorderLock from "./VoiceRecorderLock";
|
||||||
require("md-gum-polyfill");
|
import "md-gum-polyfill";
|
||||||
import MicRecorder from "mic-recorder-to-mp3";
|
import MicRecorder from "mic-recorder-to-mp3";
|
||||||
import ysFixWebmDuration from "fix-webm-duration";
|
import ysFixWebmDuration from "fix-webm-duration";
|
||||||
//import { duration } from "dayjs";
|
//import { duration } from "dayjs";
|
||||||
|
import recordStop from "@/assets/sounds/record_stop.mp3";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "VoiceRecorder",
|
name: "VoiceRecorder",
|
||||||
|
|
@ -400,7 +401,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
playRecordedSound() {
|
playRecordedSound() {
|
||||||
const audio = new Audio(require("@/assets/sounds/record_stop.mp3"));
|
const audio = new Audio(recordStop);
|
||||||
audio.play();
|
audio.play();
|
||||||
},
|
},
|
||||||
aquireWakeLock() {
|
aquireWakeLock() {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
</i18n>
|
</i18n>
|
||||||
</span>
|
</span>
|
||||||
<v-avatar color="#e0e0e0" right @click.stop="viewProfile">
|
<v-avatar color="#e0e0e0" right @click.stop="viewProfile">
|
||||||
<img v-if="userAvatar" :src="userAvatar" />
|
<AuthedImage v-if="userAvatar" :src="userAvatar" />
|
||||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</v-chip>
|
</v-chip>
|
||||||
|
|
@ -30,10 +30,12 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import profileInfoMixin from "./profileInfoMixin";
|
import profileInfoMixin from "./profileInfoMixin";
|
||||||
|
import AuthedImage from "./AuthedImage.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "YouAre",
|
name: "YouAre",
|
||||||
mixins: [profileInfoMixin],
|
mixins: [profileInfoMixin],
|
||||||
|
components: { AuthedImage },
|
||||||
props: {
|
props: {
|
||||||
dark: {
|
dark: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="create-room-avatar" color="#ededed">
|
<div class="create-room-avatar" color="#ededed">
|
||||||
<v-img class="create-room-avatar__icon clickable" @click.stop="showRoomAvatarPicker" v-if="modelValue && modelValue.image" :src="modelValue.image" />
|
<AuthedImage class="create-room-avatar__icon clickable" @click.stop="showRoomAvatarPicker" v-if="modelValue && modelValue.image" :src="modelValue.image" />
|
||||||
<v-icon class="create-room-avatar__icon default" v-else>$vuetify.icons.room_avatar_placeholder</v-icon>
|
<v-icon class="create-room-avatar__icon default" v-else>$vuetify.icons.room_avatar_placeholder</v-icon>
|
||||||
<v-icon class="create-room-avatar__camera clickable" v-if="!modelValue || !modelValue.image" @click.stop="showRoomAvatarPicker">$vuetify.icons.ic_camera</v-icon>
|
<v-icon class="create-room-avatar__camera clickable" v-if="!modelValue || !modelValue.image" @click.stop="showRoomAvatarPicker">$vuetify.icons.ic_camera</v-icon>
|
||||||
<input id="room-avatar-picker" ref="roomAvatar" type="file" name="roomAvatar"
|
<input id="room-avatar-picker" ref="roomAvatar" type="file" name="roomAvatar"
|
||||||
|
|
@ -9,8 +9,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import AuthedImage from '../AuthedImage.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CreateRoomAvatar",
|
name: "CreateRoomAvatar",
|
||||||
|
components: { AuthedImage },
|
||||||
model: {
|
model: {
|
||||||
prop: "modelValue",
|
prop: "modelValue",
|
||||||
event: "update:modelValue",
|
event: "update:modelValue",
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
<script>
|
<script>
|
||||||
import messageMixin from "../messages/messageMixin";
|
import messageMixin from "../messages/messageMixin";
|
||||||
import sendAttachmentsMixin from "../sendAttachmentsMixin";
|
import sendAttachmentsMixin from "../sendAttachmentsMixin";
|
||||||
const prettyBytes = require("pretty-bytes");
|
import prettyBytes from "pretty-bytes";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [messageMixin, sendAttachmentsMixin],
|
mixins: [messageMixin, sendAttachmentsMixin],
|
||||||
|
|
|
||||||
|
|
@ -95,11 +95,11 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
downloadOne() {
|
downloadOne() {
|
||||||
if (this.currentItemIndex >= 0 && this.currentItemIndex < this.items.length) {
|
if (this.currentItemIndex >= 0 && this.currentItemIndex < this.items.length) {
|
||||||
util.download(this.$matrix.matrixClient, this.items[this.currentItemIndex].event);
|
util.download(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, this.items[this.currentItemIndex].event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
downloadAll() {
|
downloadAll() {
|
||||||
this.items.forEach(item => util.download(this.$matrix.matrixClient, item.event));
|
this.items.forEach(item => util.download(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, item.event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
|
import defaultLogo from "@/assets/logo.svg";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
logotype() {
|
logotype() {
|
||||||
if (this.$config.logo) {
|
if (this.$config.logo) {
|
||||||
return this.$config.logo;
|
return this.$config.logo;
|
||||||
}
|
}
|
||||||
return require("@/assets/logo.svg");
|
return defaultLogo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ export default {
|
||||||
const width = this.$refs.image.$el.clientWidth;
|
const width = this.$refs.image.$el.clientWidth;
|
||||||
const height = (width * 9) / 16;
|
const height = (width * 9) / 16;
|
||||||
util
|
util
|
||||||
.getThumbnail(this.$matrix.matrixClient, this.event, this.$config, width, height)
|
.getThumbnail(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, this.event, this.$config, width, height)
|
||||||
.then((url) => {
|
.then((url) => {
|
||||||
const info = this.event.getContent().info;
|
const info = this.event.getContent().info;
|
||||||
// JPEGs use cover, PNG and GIF ect contain. This is because PNG and GIF are expected to
|
// JPEGs use cover, PNG and GIF ect contain. This is because PNG and GIF are expected to
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ export default {
|
||||||
};
|
};
|
||||||
ret.promise =
|
ret.promise =
|
||||||
util
|
util
|
||||||
.getThumbnail(this.$matrix.matrixClient, e, this.$config, 100, 100)
|
.getThumbnail(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, e, this.$config, 100, 100)
|
||||||
.then((url) => {
|
.then((url) => {
|
||||||
ret.src = url;
|
ret.src = url;
|
||||||
})
|
})
|
||||||
|
|
@ -130,7 +130,7 @@ export default {
|
||||||
return rows
|
return rows
|
||||||
},
|
},
|
||||||
downloadAll() {
|
downloadAll() {
|
||||||
this.items.forEach(item => util.download(this.$matrix.matrixClient, item.event));
|
this.items.forEach(item => util.download(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, item.event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
color="#ededed"
|
color="#ededed"
|
||||||
@click.stop="ownAvatarClicked"
|
@click.stop="ownAvatarClicked"
|
||||||
>
|
>
|
||||||
<img v-if="userAvatar" :src="userAvatar" />
|
<AuthedImage v-if="userAvatar" :src="userAvatar" />
|
||||||
<span v-else class="white--text headline">{{ userAvatarLetter }}</span>
|
<span v-else class="white--text headline">{{ userAvatarLetter }}</span>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
<QuickReactionsChannel v-if="room.displayType == ROOM_TYPE_CHANNEL" :event="eventForReactions" :timelineSet="timelineSet" v-on="$listeners"/>
|
<QuickReactionsChannel v-if="room.displayType == ROOM_TYPE_CHANNEL" :event="eventForReactions" :timelineSet="timelineSet" v-on="$listeners"/>
|
||||||
|
|
@ -41,10 +41,11 @@ import messageMixin from "./messageMixin";
|
||||||
import util, { ROOM_TYPE_CHANNEL } from "../../plugins/utils";
|
import util, { ROOM_TYPE_CHANNEL } from "../../plugins/utils";
|
||||||
import QuickReactions from "./QuickReactions.vue";
|
import QuickReactions from "./QuickReactions.vue";
|
||||||
import QuickReactionsChannel from "./channel/QuickReactionsChannel.vue";
|
import QuickReactionsChannel from "./channel/QuickReactionsChannel.vue";
|
||||||
|
import AuthedImage from "../AuthedImage.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [messageMixin],
|
mixins: [messageMixin],
|
||||||
components: { QuickReactions, QuickReactionsChannel, SeenBy },
|
components: { QuickReactions, QuickReactionsChannel, SeenBy, AuthedImage },
|
||||||
data() {
|
data() {
|
||||||
return { ROOM_TYPE_CHANNEL: ROOM_TYPE_CHANNEL }
|
return { ROOM_TYPE_CHANNEL: ROOM_TYPE_CHANNEL }
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ export default {
|
||||||
const width = this.$refs.image.$el.clientWidth;
|
const width = this.$refs.image.$el.clientWidth;
|
||||||
const height = (width * 9) / 16;
|
const height = (width * 9) / 16;
|
||||||
util
|
util
|
||||||
.getThumbnail(this.$matrix.matrixClient, this.event, this.$config, width, height)
|
.getThumbnail(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, this.event, this.$config, width, height)
|
||||||
.then((url) => {
|
.then((url) => {
|
||||||
const info = this.event.getContent().info;
|
const info = this.event.getContent().info;
|
||||||
// JPEGs use cover, PNG and GIF ect contain. This is because PNG and GIF are expected to
|
// JPEGs use cover, PNG and GIF ect contain. This is because PNG and GIF are expected to
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ export default {
|
||||||
};
|
};
|
||||||
ret.promise =
|
ret.promise =
|
||||||
util
|
util
|
||||||
.getThumbnail(this.$matrix.matrixClient, e, this.$config, 100, 100)
|
.getThumbnail(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, e, this.$config, 100, 100)
|
||||||
.then((url) => {
|
.then((url) => {
|
||||||
ret.src = url;
|
ret.src = url;
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
<transition-group name="list" tag="div" v-if="seenBy.length > 0">
|
<transition-group name="list" tag="div" v-if="seenBy.length > 0">
|
||||||
<v-avatar v-for="(member, index) in seenBy" :key="member.roomMember.userId" class="seen-by-user" size="16" color="grey"
|
<v-avatar v-for="(member, index) in seenBy" :key="member.roomMember.userId" class="seen-by-user" size="16" color="grey"
|
||||||
v-show="index < SHOW_LIMIT" @click="open">
|
v-show="index < SHOW_LIMIT" @click="open">
|
||||||
<img v-if="memberAvatar(member.roomMember)" :src="memberAvatar(member.roomMember)" />
|
<AuthedImage v-if="memberAvatar(member.roomMember)" :src="memberAvatar(member.roomMember)" />
|
||||||
<span v-else class="white--text headline">{{
|
<span v-else class="white--text headline">{{
|
||||||
member.roomMember.name.substring(0, 1).toUpperCase()
|
member.roomMember.name.substring(0, 1).toUpperCase()
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<v-list-item v-for="(member, index) in seenBy" :key="index">
|
<v-list-item v-for="(member, index) in seenBy" :key="index">
|
||||||
<v-list-item-icon>
|
<v-list-item-icon>
|
||||||
<v-avatar size="40" color="grey">
|
<v-avatar size="40" color="grey">
|
||||||
<img v-if="memberAvatar(member.roomMember)" :src="memberAvatar(member.roomMember)" />
|
<AuthedImage v-if="memberAvatar(member.roomMember)" :src="memberAvatar(member.roomMember)" />
|
||||||
<span v-else class="white--text headline">{{
|
<span v-else class="white--text headline">{{
|
||||||
member.roomMember.name.substring(0, 1).toUpperCase()
|
member.roomMember.name.substring(0, 1).toUpperCase()
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
@ -45,12 +45,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BottomSheet from "../BottomSheet.vue"
|
import BottomSheet from "../BottomSheet.vue";
|
||||||
|
import AuthedImage from "../AuthedImage.vue";
|
||||||
import utils from "../../plugins/utils.js";
|
import utils from "../../plugins/utils.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
BottomSheet
|
BottomSheet,
|
||||||
|
AuthedImage
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
room: {
|
room: {
|
||||||
|
|
@ -114,7 +116,9 @@ export default {
|
||||||
16,
|
16,
|
||||||
16,
|
16,
|
||||||
"scale",
|
"scale",
|
||||||
true
|
true,
|
||||||
|
false,
|
||||||
|
this.$matrix.useAuthedMedia
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
util
|
util
|
||||||
.getAttachment(this.$matrix.matrixClient, event, (progress) => {
|
.getAttachment(this.$matrix.matrixClient, this.$matrix.useAuthedMedia, event, (progress) => {
|
||||||
this.downloadProgress = progress;
|
this.downloadProgress = progress;
|
||||||
console.log("Progress: " + progress);
|
console.log("Progress: " + progress);
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ export default {
|
||||||
if (!this.$matrix.userAvatar) {
|
if (!this.$matrix.userAvatar) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.$matrix.matrixClient.mxcUrlToHttp(this.$matrix.userAvatar, 80, 80, "scale", true);
|
return this.$matrix.matrixClient.mxcUrlToHttp(this.$matrix.userAvatar, 80, 80, "scale", true, undefined, this.$matrix.useAuthedMedia);
|
||||||
},
|
},
|
||||||
|
|
||||||
userAvatarLetter() {
|
userAvatarLetter() {
|
||||||
|
|
@ -268,7 +268,7 @@ export default {
|
||||||
if (this.room) {
|
if (this.room) {
|
||||||
const member = this.room.getMember(event.getSender());
|
const member = this.room.getMember(event.getSender());
|
||||||
if (member) {
|
if (member) {
|
||||||
return member.getAvatarUrl(this.$matrix.matrixClient.getHomeserverUrl(), 40, 40, "scale", true);
|
return member.getAvatarUrl(this.$matrix.matrixClient.getHomeserverUrl(), 40, 40, "scale", true, false, this.$matrix.useAuthedMedia);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export default {
|
||||||
if (!this.$matrix.userAvatar) {
|
if (!this.$matrix.userAvatar) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.$matrix.matrixClient.mxcUrlToHttp(this.$matrix.userAvatar, 80, 80, 'scale', true);
|
return this.$matrix.matrixClient.mxcUrlToHttp(this.$matrix.userAvatar, 80, 80, 'scale', true, undefined, this.$matrix.useAuthedMedia);
|
||||||
},
|
},
|
||||||
|
|
||||||
userAvatarLetter() {
|
userAvatarLetter() {
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,9 @@ export default {
|
||||||
40,
|
40,
|
||||||
40,
|
40,
|
||||||
"scale",
|
"scale",
|
||||||
true
|
true,
|
||||||
|
false,
|
||||||
|
this.$matrix.useAuthedMedia
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -243,7 +245,7 @@ export default {
|
||||||
privatePartyAvatar(size) {
|
privatePartyAvatar(size) {
|
||||||
const other = this.privateParty;
|
const other = this.privateParty;
|
||||||
if (other) {
|
if (other) {
|
||||||
return other.getAvatarUrl(this.$matrix.matrixClient.getHomeserverUrl(), size, size, "scale", true);
|
return other.getAvatarUrl(this.$matrix.matrixClient.getHomeserverUrl(), size, size, "scale", true, false, this.$matrix.useAuthedMedia);
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import analytics from './services/analytics.service'
|
||||||
import audioPlayer from './services/audio.service';
|
import audioPlayer from './services/audio.service';
|
||||||
import 'roboto-fontface/css/roboto/roboto-fontface.css'
|
import 'roboto-fontface/css/roboto/roboto-fontface.css'
|
||||||
import 'material-design-icons-iconfont/dist/material-design-icons.css'
|
import 'material-design-icons-iconfont/dist/material-design-icons.css'
|
||||||
import VEmojiPicker from 'v-emoji-picker';
|
|
||||||
import VueResize from 'vue-resize';
|
import VueResize from 'vue-resize';
|
||||||
import 'vue-resize/dist/vue-resize.css';
|
import 'vue-resize/dist/vue-resize.css';
|
||||||
import VueClipboard from 'vue-clipboard2'
|
import VueClipboard from 'vue-clipboard2'
|
||||||
|
|
@ -25,7 +24,6 @@ Vue.use(VueSanitize, defaultOptions);
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
Vue.use(VueResize);
|
Vue.use(VueResize);
|
||||||
Vue.use(VEmojiPicker);
|
|
||||||
Vue.use(matrix, { store: store, i18n: i18n });
|
Vue.use(matrix, { store: store, i18n: i18n });
|
||||||
|
|
||||||
const configLoadedPromise = new Promise((resolve, ignoredreject) => {
|
const configLoadedPromise = new Promise((resolve, ignoredreject) => {
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,13 @@ Vue.use(VueI18n)
|
||||||
|
|
||||||
var messages = {}
|
var messages = {}
|
||||||
|
|
||||||
function importAll(r) {
|
const modules = import.meta.glob('@/assets/translations/*.json', {eager: true});
|
||||||
return r.keys().map(res => {
|
Object.keys(modules).map(path => {
|
||||||
// Remove"./"
|
// Remove"./"
|
||||||
const parts = res.split("/");
|
const parts = path.split("/");
|
||||||
const locale = parts[1].split(".")[0];
|
const locale = parts[parts.length - 1].split(".")[0];
|
||||||
messages[locale] = r(res);
|
messages[locale] = modules[path];
|
||||||
});
|
});
|
||||||
}
|
|
||||||
importAll(require.context('@/assets/translations/', true, /\.json$/));
|
|
||||||
|
|
||||||
const vue18n = new VueI18n({
|
const vue18n = new VueI18n({
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,18 @@ import dataUriToBuffer from "data-uri-to-buffer";
|
||||||
import ImageResize from "image-resize";
|
import ImageResize from "image-resize";
|
||||||
import { AutoDiscovery } from 'matrix-js-sdk';
|
import { AutoDiscovery } from 'matrix-js-sdk';
|
||||||
import User from '../models/user';
|
import User from '../models/user';
|
||||||
const prettyBytes = require("pretty-bytes");
|
import prettyBytes from "pretty-bytes";
|
||||||
import Hammer from "hammerjs";
|
import Hammer from "hammerjs";
|
||||||
import { Thread } from 'matrix-js-sdk/lib/models/thread';
|
import { Thread } from 'matrix-js-sdk/lib/models/thread';
|
||||||
|
import sizeOf from "image-size";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import jssha256 from "js-sha256";
|
||||||
|
import aesjs from "aes-js";
|
||||||
|
import { encode, decode } from 'json-web-key/lib/base64url';
|
||||||
|
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||||
|
import duration from 'dayjs/plugin/duration';
|
||||||
|
import { Buffer } from 'buffer/'
|
||||||
|
window.Buffer = Buffer;
|
||||||
|
|
||||||
export const STATE_EVENT_ROOM_DELETION_NOTICE = "im.keanu.room_deletion_notice";
|
export const STATE_EVENT_ROOM_DELETION_NOTICE = "im.keanu.room_deletion_notice";
|
||||||
export const STATE_EVENT_ROOM_DELETED = "im.keanu.room_deleted";
|
export const STATE_EVENT_ROOM_DELETED = "im.keanu.room_deleted";
|
||||||
|
|
@ -18,17 +27,11 @@ export const ROOM_TYPE_CHANNEL = "im.keanu.room_type_channel";
|
||||||
|
|
||||||
export const STATE_EVENT_ROOM_TYPE = "im.keanu.room_type";
|
export const STATE_EVENT_ROOM_TYPE = "im.keanu.room_type";
|
||||||
|
|
||||||
const sizeOf = require("image-size");
|
|
||||||
|
|
||||||
var dayjs = require('dayjs');
|
var sha256 = jssha256.sha256;
|
||||||
var sha256 = require('js-sha256').sha256;
|
|
||||||
var aesjs = require('aes-js');
|
|
||||||
var base64Url = require('json-web-key/lib/base64url');
|
|
||||||
|
|
||||||
// Install extended localized format
|
// Install extended localized format
|
||||||
var localizedFormat = require('dayjs/plugin/localizedFormat')
|
|
||||||
dayjs.extend(localizedFormat)
|
dayjs.extend(localizedFormat)
|
||||||
var duration = require('dayjs/plugin/duration')
|
|
||||||
dayjs.extend(duration);
|
dayjs.extend(duration);
|
||||||
|
|
||||||
// Store info about getUserMedia BEFORE we aply polyfill(s)!
|
// Store info about getUserMedia BEFORE we aply polyfill(s)!
|
||||||
|
|
@ -68,9 +71,8 @@ class UploadPromise {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Util {
|
class Util {
|
||||||
|
|
||||||
threadMessageType() {
|
threadMessageType() {
|
||||||
return Thread.hasServerSideSupport ? "m.thread" : "io.element.thread"
|
return Thread.hasServerSideSupport ? "m.thread" : "io.element.thread";
|
||||||
}
|
}
|
||||||
|
|
||||||
getAttachmentUrlAndDuration(event) {
|
getAttachmentUrlAndDuration(event) {
|
||||||
|
|
@ -88,45 +90,49 @@ class Util {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getAttachment(matrixClient, event, progressCallback, asBlob = false, abortController = undefined) {
|
getAttachment(matrixClient, useAuthedMedia, event, progressCallback, asBlob = false, abortController = undefined) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const content = event.getContent();
|
const content = event.getContent();
|
||||||
if (content.url != null) {
|
|
||||||
// Unencrypted, just return!
|
|
||||||
resolve(matrixClient.mxcUrlToHttp(content.url));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var url = null;
|
var url = null;
|
||||||
var file = null;
|
var file = null;
|
||||||
if (content.file && content.file.url) {
|
let decrypt = true;
|
||||||
|
if (content.url != null) {
|
||||||
|
url = matrixClient.mxcUrlToHttp(content.url, undefined, undefined, undefined, undefined, undefined, useAuthedMedia);
|
||||||
|
decrypt = false;
|
||||||
|
} else if (content.file && content.file.url) {
|
||||||
file = content.file;
|
file = content.file;
|
||||||
url = matrixClient.mxcUrlToHttp(file.url);
|
url = matrixClient.mxcUrlToHttp(file.url, undefined, undefined, undefined, undefined, undefined, useAuthedMedia);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
reject("No url found!");
|
reject("No url found!");
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.get(url, {
|
axios
|
||||||
|
.get(url, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${matrixClient.getAccessToken()}`,
|
||||||
|
},
|
||||||
signal: abortController ? abortController.signal : undefined,
|
signal: abortController ? abortController.signal : undefined,
|
||||||
responseType: 'arraybuffer', onDownloadProgress: progressEvent => {
|
responseType: "arraybuffer",
|
||||||
|
onDownloadProgress: (progressEvent) => {
|
||||||
let percentCompleted = Math.floor((progressEvent.loaded * 100) / progressEvent.total);
|
let percentCompleted = Math.floor((progressEvent.loaded * 100) / progressEvent.total);
|
||||||
if (progressCallback) {
|
if (progressCallback) {
|
||||||
progressCallback(percentCompleted);
|
progressCallback(percentCompleted);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
return this.decryptIfNeeded(file, response);
|
return decrypt ? this.decryptIfNeeded(file, response) : Promise.resolve({buffer:response.data});
|
||||||
})
|
})
|
||||||
.then(bytes => {
|
.then((bytes) => {
|
||||||
if (asBlob) {
|
if (asBlob) {
|
||||||
resolve(new Blob([bytes.buffer], { type: file.mimetype }));
|
resolve(new Blob([bytes.buffer], { type: file.mimetype }));
|
||||||
} else {
|
} else {
|
||||||
resolve(URL.createObjectURL(new Blob([bytes.buffer], { type: file.mimetype })));
|
resolve(URL.createObjectURL(new Blob([bytes.buffer], { type: file.mimetype })));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
console.log("Download error: ", err);
|
console.log("Download error: ", err);
|
||||||
reject(err);
|
reject(err);
|
||||||
})
|
})
|
||||||
|
|
@ -138,22 +144,16 @@ class Util {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getThumbnail(matrixClient, event, config, ignoredw, ignoredh) {
|
getThumbnail(matrixClient, useAuthedMedia, event, config, ignoredw, ignoredh) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const content = event.getContent();
|
const content = event.getContent();
|
||||||
if (content.url != null) {
|
|
||||||
// Unencrypted, just return!
|
|
||||||
resolve(matrixClient.mxcUrlToHttp(content.url));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var url = null;
|
var url = null;
|
||||||
var file = null;
|
var file = null;
|
||||||
if (
|
let decrypt = true;
|
||||||
content &&
|
if (content.url != null) {
|
||||||
content.info &&
|
url = matrixClient.mxcUrlToHttp(content.url, undefined, undefined, undefined, undefined, undefined, useAuthedMedia);
|
||||||
content.info.thumbnail_file &&
|
decrypt = false;
|
||||||
content.info.thumbnail_file.url
|
} else if (content && content.info && content.info.thumbnail_file && content.info.thumbnail_file.url) {
|
||||||
) {
|
|
||||||
file = content.info.thumbnail_file;
|
file = content.info.thumbnail_file;
|
||||||
// var width = w;
|
// var width = w;
|
||||||
// var height = h;
|
// var height = h;
|
||||||
|
|
@ -167,25 +167,34 @@ class Util {
|
||||||
// "scale",
|
// "scale",
|
||||||
// true
|
// true
|
||||||
// );
|
// );
|
||||||
url = matrixClient.mxcUrlToHttp(file.url);
|
url = matrixClient.mxcUrlToHttp(file.url, undefined, undefined, undefined, undefined, undefined, useAuthedMedia);
|
||||||
} else if (content.file && content.file.url && this.getFileSize(event) > 0 && this.getFileSize(event) < config.maxSizeAutoDownloads) {
|
} else if (
|
||||||
|
content.file &&
|
||||||
|
content.file.url &&
|
||||||
|
this.getFileSize(event) > 0 &&
|
||||||
|
this.getFileSize(event) < config.maxSizeAutoDownloads
|
||||||
|
) {
|
||||||
// No thumb, use real url
|
// No thumb, use real url
|
||||||
file = content.file;
|
file = content.file;
|
||||||
url = matrixClient.mxcUrlToHttp(file.url);
|
url = matrixClient.mxcUrlToHttp(file.url, undefined, undefined, undefined, undefined, undefined, useAuthedMedia);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
reject("No url found!");
|
reject("No url found!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
axios.get(url, { responseType: 'arraybuffer' })
|
|
||||||
.then(response => {
|
axios
|
||||||
return this.decryptIfNeeded(file, response);
|
.get(url, { responseType: "arraybuffer", headers: {
|
||||||
|
Authorization: `Bearer ${matrixClient.getAccessToken()}`,
|
||||||
|
}})
|
||||||
|
.then((response) => {
|
||||||
|
return decrypt ? this.decryptIfNeeded(file, response) : Promise.resolve({buffer:response.data});
|
||||||
})
|
})
|
||||||
.then(bytes => {
|
.then((bytes) => {
|
||||||
resolve(URL.createObjectURL(new Blob([bytes.buffer], { type: file.mimetype })));
|
resolve(URL.createObjectURL(new Blob([bytes.buffer], { type: file.mimetype })));
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
console.log("Download error: ", err);
|
console.log("Download error: ", err);
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
|
|
@ -194,15 +203,18 @@ class Util {
|
||||||
|
|
||||||
decryptIfNeeded(file, response) {
|
decryptIfNeeded(file, response) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var key = base64Url.decode(file.key.k);
|
var key = decode(file.key.k);
|
||||||
var iv = base64Url.decode(file.iv);
|
var iv = decode(file.iv);
|
||||||
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(iv));
|
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(iv));
|
||||||
|
|
||||||
const data = new Uint8Array(response.data);
|
const data = new Uint8Array(response.data);
|
||||||
|
|
||||||
|
// const areEqual = (first, second) =>
|
||||||
|
// first.length === second.length && first.every((value, index) => value === second[index]);
|
||||||
|
|
||||||
// Calculate sha256 and compare hashes
|
// Calculate sha256 and compare hashes
|
||||||
var hash = new Uint8Array(sha256.create().update(data).arrayBuffer());
|
var hash = new Uint8Array(sha256.create().update(data).arrayBuffer());
|
||||||
const originalHash = base64Url.decode(file.hashes.sha256);
|
const originalHash = decode(file.hashes.sha256);
|
||||||
if (Buffer.compare(Buffer.from(hash), Buffer.from(originalHash.buffer)) != 0) {
|
if (Buffer.compare(Buffer.from(hash), Buffer.from(originalHash.buffer)) != 0) {
|
||||||
reject("Hashes don't match!");
|
reject("Hashes don't match!");
|
||||||
return;
|
return;
|
||||||
|
|
@ -216,27 +228,32 @@ class Util {
|
||||||
sendTextMessage(matrixClient, roomId, text, editedEvent, replyToEvent) {
|
sendTextMessage(matrixClient, roomId, text, editedEvent, replyToEvent) {
|
||||||
var content = ContentHelpers.makeTextMessage(text);
|
var content = ContentHelpers.makeTextMessage(text);
|
||||||
if (editedEvent) {
|
if (editedEvent) {
|
||||||
content['m.relates_to'] = {
|
content["m.relates_to"] = {
|
||||||
rel_type: 'm.replace',
|
rel_type: "m.replace",
|
||||||
event_id: editedEvent.getId()
|
event_id: editedEvent.getId(),
|
||||||
}
|
};
|
||||||
content['m.new_content'] = {
|
content["m.new_content"] = {
|
||||||
body: content.body,
|
body: content.body,
|
||||||
msgtype: content.msgtype
|
msgtype: content.msgtype,
|
||||||
}
|
};
|
||||||
} else if (replyToEvent) {
|
} else if (replyToEvent) {
|
||||||
content['m.relates_to'] = {
|
content["m.relates_to"] = {
|
||||||
'm.in_reply_to': {
|
"m.in_reply_to": {
|
||||||
event_id: replyToEvent.getId()
|
event_id: replyToEvent.getId(),
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
let senderContent = replyToEvent.getContent()
|
let senderContent = replyToEvent.getContent();
|
||||||
|
|
||||||
const senderContentBody = Object.getOwnPropertyDescriptor(senderContent, 'body') ? senderContent.body : Object.values(senderContent)[0].question.body
|
const senderContentBody = Object.getOwnPropertyDescriptor(senderContent, "body")
|
||||||
|
? senderContent.body
|
||||||
|
: Object.values(senderContent)[0].question.body;
|
||||||
// Prefix the content with reply info (seems to be a legacy thing)
|
// Prefix the content with reply info (seems to be a legacy thing)
|
||||||
const prefix = senderContentBody.split('\n').map((item, index) => {
|
const prefix = senderContentBody
|
||||||
return "> " + (index == 0 ? ("<" + replyToEvent.getSender() + "> ") : "") + item;
|
.split("\n")
|
||||||
}).join('\n');
|
.map((item, index) => {
|
||||||
|
return "> " + (index == 0 ? "<" + replyToEvent.getSender() + "> " : "") + item;
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
content.body = prefix + "\n\n" + content.body;
|
content.body = prefix + "\n\n" + content.body;
|
||||||
}
|
}
|
||||||
return this.sendMessage(matrixClient, roomId, "m.room.message", content);
|
return this.sendMessage(matrixClient, roomId, "m.room.message", content);
|
||||||
|
|
@ -244,30 +261,30 @@ class Util {
|
||||||
|
|
||||||
sendQuickReaction(matrixClient, roomId, emoji, event, extraData = {}) {
|
sendQuickReaction(matrixClient, roomId, emoji, event, extraData = {}) {
|
||||||
const content = {
|
const content = {
|
||||||
'm.relates_to': Object.assign(extraData, {
|
"m.relates_to": Object.assign(extraData, {
|
||||||
key: emoji,
|
key: emoji,
|
||||||
rel_type: 'm.annotation',
|
rel_type: "m.annotation",
|
||||||
event_id: event.getId()
|
event_id: event.getId(),
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
return this.sendMessage(matrixClient, roomId, "m.reaction", content);
|
return this.sendMessage(matrixClient, roomId, "m.reaction", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
createPoll(matrixClient, roomId, question, answers, isDisclosed) {
|
createPoll(matrixClient, roomId, question, answers, isDisclosed) {
|
||||||
var idx = 0;
|
var idx = 0;
|
||||||
let answerData = answers.map(a => {
|
let answerData = answers.map((a) => {
|
||||||
idx++;
|
idx++;
|
||||||
return { id: "" + idx, 'org.matrix.msc1767.text': a.text }
|
return { id: "" + idx, "org.matrix.msc1767.text": a.text };
|
||||||
});
|
});
|
||||||
const content = {
|
const content = {
|
||||||
'org.matrix.msc3381.poll.start': {
|
"org.matrix.msc3381.poll.start": {
|
||||||
question: {
|
question: {
|
||||||
'org.matrix.msc1767.text': question,
|
"org.matrix.msc1767.text": question,
|
||||||
body: question
|
body: question,
|
||||||
},
|
},
|
||||||
kind: isDisclosed ? "org.matrix.msc3381.poll.disclosed" : "org.matrix.msc3381.poll.undisclosed",
|
kind: isDisclosed ? "org.matrix.msc3381.poll.disclosed" : "org.matrix.msc3381.poll.undisclosed",
|
||||||
max_selections: 1,
|
max_selections: 1,
|
||||||
answers: answerData
|
answers: answerData,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return this.sendMessage(matrixClient, roomId, "org.matrix.msc3381.poll.start", content);
|
return this.sendMessage(matrixClient, roomId, "org.matrix.msc3381.poll.start", content);
|
||||||
|
|
@ -275,24 +292,23 @@ class Util {
|
||||||
|
|
||||||
closePoll(matrixClient, roomId, event) {
|
closePoll(matrixClient, roomId, event) {
|
||||||
const content = {
|
const content = {
|
||||||
'm.relates_to': {
|
"m.relates_to": {
|
||||||
rel_type: 'm.reference',
|
rel_type: "m.reference",
|
||||||
event_id: event.getId()
|
event_id: event.getId(),
|
||||||
},
|
|
||||||
'org.matrix.msc3381.poll.end': {
|
|
||||||
},
|
},
|
||||||
|
"org.matrix.msc3381.poll.end": {},
|
||||||
};
|
};
|
||||||
return this.sendMessage(matrixClient, roomId, "org.matrix.msc3381.poll.end", content);
|
return this.sendMessage(matrixClient, roomId, "org.matrix.msc3381.poll.end", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendPollAnswer(matrixClient, roomId, answers, event) {
|
sendPollAnswer(matrixClient, roomId, answers, event) {
|
||||||
const content = {
|
const content = {
|
||||||
'm.relates_to': {
|
"m.relates_to": {
|
||||||
rel_type: 'm.reference',
|
rel_type: "m.reference",
|
||||||
event_id: event.getId()
|
event_id: event.getId(),
|
||||||
},
|
},
|
||||||
'org.matrix.msc3381.poll.response': {
|
"org.matrix.msc3381.poll.response": {
|
||||||
answers: answers
|
answers: answers,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return this.sendMessage(matrixClient, roomId, "org.matrix.msc3381.poll.response", content);
|
return this.sendMessage(matrixClient, roomId, "org.matrix.msc3381.poll.response", content);
|
||||||
|
|
@ -300,12 +316,13 @@ class Util {
|
||||||
|
|
||||||
sendMessage(matrixClient, roomId, eventType, content) {
|
sendMessage(matrixClient, roomId, eventType, content) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
matrixClient.sendEvent(roomId, eventType, content, undefined, undefined)
|
matrixClient
|
||||||
|
.sendEvent(roomId, eventType, content, undefined, undefined)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
console.log("Message sent: ", result);
|
console.log("Message sent: ", result);
|
||||||
resolve(result.event_id);
|
resolve(result.event_id);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
console.log("Send error: ", err);
|
console.log("Send error: ", err);
|
||||||
if (err && err.name == "UnknownDeviceError") {
|
if (err && err.name == "UnknownDeviceError") {
|
||||||
console.log("Unknown devices. Mark as known before retrying.");
|
console.log("Unknown devices. Mark as known before retrying.");
|
||||||
|
|
@ -315,31 +332,28 @@ class Util {
|
||||||
for (var deviceId of Object.keys(userDevices)) {
|
for (var deviceId of Object.keys(userDevices)) {
|
||||||
const deviceInfo = userDevices[deviceId];
|
const deviceInfo = userDevices[deviceId];
|
||||||
if (!deviceInfo.known) {
|
if (!deviceInfo.known) {
|
||||||
setAsKnownPromises.push(
|
setAsKnownPromises.push(matrixClient.setDeviceKnown(user, deviceId, true));
|
||||||
matrixClient.setDeviceKnown(
|
|
||||||
user,
|
|
||||||
deviceId,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Promise.all(setAsKnownPromises)
|
Promise.all(setAsKnownPromises).then(() => {
|
||||||
.then(() => {
|
|
||||||
// All devices now marked as "known", try to resend
|
// All devices now marked as "known", try to resend
|
||||||
let event = err.event;
|
let event = err.event;
|
||||||
if (!event) {
|
if (!event) {
|
||||||
// Seems event is no longer send in the UnknownDevices error...
|
// Seems event is no longer send in the UnknownDevices error...
|
||||||
const room = matrixClient.getRoom(roomId);
|
const room = matrixClient.getRoom(roomId);
|
||||||
if (room) {
|
if (room) {
|
||||||
event = room.getLiveTimeline().getEvents().find(e => {
|
event = room
|
||||||
|
.getLiveTimeline()
|
||||||
|
.getEvents()
|
||||||
|
.find((e) => {
|
||||||
// Find the exact match (= object equality)
|
// Find the exact match (= object equality)
|
||||||
return e.error === err
|
return e.error === err;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
matrixClient.resendEvent(event, matrixClient.getRoom(event.getRoomId()))
|
matrixClient
|
||||||
|
.resendEvent(event, matrixClient.getRoom(event.getRoomId()))
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
console.log("Message sent: ", result);
|
console.log("Message sent: ", result);
|
||||||
resolve(result.event_id);
|
resolve(result.event_id);
|
||||||
|
|
@ -349,8 +363,7 @@ class Util {
|
||||||
reject(err.toLocaleString());
|
reject(err.toLocaleString());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
reject(err.toLocaleString());
|
reject(err.toLocaleString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -371,7 +384,7 @@ class Util {
|
||||||
|
|
||||||
const info = {
|
const info = {
|
||||||
mimetype: file.type,
|
mimetype: file.type,
|
||||||
size: file.size
|
size: file.size,
|
||||||
};
|
};
|
||||||
|
|
||||||
// If audio, send duration in ms as well
|
// If audio, send duration in ms as well
|
||||||
|
|
@ -380,38 +393,38 @@ class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
var description = file.name;
|
var description = file.name;
|
||||||
var msgtype = 'm.file';
|
var msgtype = "m.file";
|
||||||
if (file.type.startsWith("image/")) {
|
if (file.type.startsWith("image/")) {
|
||||||
msgtype = 'm.image';
|
msgtype = "m.image";
|
||||||
} else if (file.type.startsWith("audio/")) {
|
} else if (file.type.startsWith("audio/")) {
|
||||||
msgtype = 'm.audio';
|
msgtype = "m.audio";
|
||||||
} else if (file.type.startsWith("video/")) {
|
} else if (file.type.startsWith("video/")) {
|
||||||
msgtype = 'm.video';
|
msgtype = "m.video";
|
||||||
}
|
}
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
type: file.type,
|
type: file.type,
|
||||||
name: description,
|
name: description,
|
||||||
progressHandler: onUploadProgress,
|
progressHandler: onUploadProgress,
|
||||||
onlyContentUri: false
|
onlyContentUri: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
var messageContent = {
|
var messageContent = {
|
||||||
body: description,
|
body: description,
|
||||||
info: info,
|
info: info,
|
||||||
msgtype: msgtype
|
msgtype: msgtype,
|
||||||
}
|
};
|
||||||
|
|
||||||
// If thread root (an eventId) is set, add that here
|
// If thread root (an eventId) is set, add that here
|
||||||
if (threadRoot) {
|
if (threadRoot) {
|
||||||
messageContent["m.relates_to"] = {
|
messageContent["m.relates_to"] = {
|
||||||
"rel_type": this.threadMessageType(),
|
rel_type: this.threadMessageType(),
|
||||||
"event_id": threadRoot
|
event_id: threadRoot,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set filename for files
|
// Set filename for files
|
||||||
if (msgtype == 'm.file') {
|
if (msgtype == "m.file") {
|
||||||
messageContent.filename = file.name;
|
messageContent.filename = file.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -424,23 +437,22 @@ class Util {
|
||||||
promise
|
promise
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
messageContent.url = response.content_uri;
|
messageContent.url = response.content_uri;
|
||||||
return (msgtype == 'm.audio' ? this.generateWaveform(fileContents, messageContent) : true);
|
return msgtype == "m.audio" ? this.generateWaveform(fileContents, messageContent) : true;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.sendMessage(matrixClient, roomId, "m.room.message", messageContent)
|
return this.sendMessage(matrixClient, roomId, "m.room.message", messageContent);
|
||||||
})
|
})
|
||||||
.then(result => {
|
.then((result) => {
|
||||||
resolve(result);
|
resolve(result);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
return; // Don't fall through
|
return; // Don't fall through
|
||||||
}
|
}
|
||||||
|
|
||||||
const crypto = require('crypto');
|
let key = Buffer.from(crypto.getRandomValues(new Uint8Array(256 / 8)));
|
||||||
let key = crypto.randomBytes(256 / 8);
|
let iv = Buffer.concat([Buffer.from(crypto.getRandomValues(new Uint8Array(8))), Buffer.alloc(8)]); // Initialization vector.
|
||||||
let iv = Buffer.concat([crypto.randomBytes(8), Buffer.alloc(8)]); // Initialization vector.
|
|
||||||
|
|
||||||
// Encrypt
|
// Encrypt
|
||||||
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(iv));
|
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(iv));
|
||||||
|
|
@ -451,19 +463,19 @@ class Util {
|
||||||
var hash = new Uint8Array(sha256.create().update(data).arrayBuffer());
|
var hash = new Uint8Array(sha256.create().update(data).arrayBuffer());
|
||||||
|
|
||||||
const jwk = {
|
const jwk = {
|
||||||
kty: 'oct',
|
kty: "oct",
|
||||||
key_ops: ['encrypt', 'decrypt'],
|
key_ops: ["encrypt", "decrypt"],
|
||||||
alg: 'A256CTR',
|
alg: "A256CTR",
|
||||||
k: base64Url.encode(key),
|
k: encode(key),
|
||||||
ext: true
|
ext: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const encryptedFile = {
|
const encryptedFile = {
|
||||||
mimetype: file.type,
|
mimetype: file.type,
|
||||||
key: jwk,
|
key: jwk,
|
||||||
iv: Buffer.from(iv).toString('base64').replace(/=/g, ''),
|
iv: Buffer.from(iv).toString("base64").replace(/=/g, ""),
|
||||||
hashes: { sha256: Buffer.from(hash).toString('base64').replace(/=/g, '') },
|
hashes: { sha256: Buffer.from(hash).toString("base64").replace(/=/g, "") },
|
||||||
v: 'v2'
|
v: "v2",
|
||||||
};
|
};
|
||||||
|
|
||||||
messageContent.file = encryptedFile;
|
messageContent.file = encryptedFile;
|
||||||
|
|
@ -481,21 +493,21 @@ class Util {
|
||||||
return reject(response.error);
|
return reject(response.error);
|
||||||
}
|
}
|
||||||
encryptedFile.url = response.content_uri;
|
encryptedFile.url = response.content_uri;
|
||||||
return (msgtype == 'm.audio' ? this.generateWaveform(fileContents, messageContent) : true);
|
return msgtype == "m.audio" ? this.generateWaveform(fileContents, messageContent) : true;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.sendMessage(matrixClient, roomId, "m.room.message", messageContent)
|
return this.sendMessage(matrixClient, roomId, "m.room.message", messageContent);
|
||||||
})
|
})
|
||||||
.then(result => {
|
.then((result) => {
|
||||||
resolve(result);
|
resolve(result);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
reader.onerror = (err) => {
|
reader.onerror = (err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
};
|
||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file);
|
||||||
});
|
});
|
||||||
return uploadPromise;
|
return uploadPromise;
|
||||||
|
|
@ -507,8 +519,7 @@ class Util {
|
||||||
}
|
}
|
||||||
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
if (audioCtx) {
|
if (audioCtx) {
|
||||||
return audioCtx.decodeAudioData(data)
|
return audioCtx.decodeAudioData(data).then((audioBuffer) => {
|
||||||
.then((audioBuffer) => {
|
|
||||||
const rawData = audioBuffer.getChannelData(0); // TODO - currently using only 1 channel
|
const rawData = audioBuffer.getChannelData(0); // TODO - currently using only 1 channel
|
||||||
const samples = 1000; // Number of samples
|
const samples = 1000; // Number of samples
|
||||||
const blockSize = Math.floor(rawData.length / samples);
|
const blockSize = Math.floor(rawData.length / samples);
|
||||||
|
|
@ -517,18 +528,17 @@ class Util {
|
||||||
let blockStart = blockSize * i; // the location of the first sample in the block
|
let blockStart = blockSize * i; // the location of the first sample in the block
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
for (let j = 0; j < blockSize; j++) {
|
for (let j = 0; j < blockSize; j++) {
|
||||||
sum = sum + Math.abs(rawData[blockStart + j]) // find the sum of all the samples in the block
|
sum = sum + Math.abs(rawData[blockStart + j]); // find the sum of all the samples in the block
|
||||||
}
|
}
|
||||||
filteredData.push(sum / blockSize); // divide the sum by the block size to get the average
|
filteredData.push(sum / blockSize); // divide the sum by the block size to get the average
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize
|
// Normalize
|
||||||
const multiplier = Math.pow(Math.max(...filteredData), -1);
|
const multiplier = Math.pow(Math.max(...filteredData), -1);
|
||||||
filteredData = filteredData.map(n => n * multiplier);
|
filteredData = filteredData.map((n) => n * multiplier);
|
||||||
|
|
||||||
// Integerize
|
// Integerize
|
||||||
filteredData = filteredData.map(n => parseInt((n * 255).toFixed()));
|
filteredData = filteredData.map((n) => parseInt((n * 255).toFixed()));
|
||||||
|
|
||||||
|
|
||||||
// Generate SVG of waveform
|
// Generate SVG of waveform
|
||||||
let svg = `<svg viewBox="0 0 ${samples} 255" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">`;
|
let svg = `<svg viewBox="0 0 ${samples} 255" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">`;
|
||||||
|
|
@ -542,7 +552,7 @@ class Util {
|
||||||
|
|
||||||
messageContent.format = "org.matrix.custom.html";
|
messageContent.format = "org.matrix.custom.html";
|
||||||
messageContent.formatted_body = svg;
|
messageContent.formatted_body = svg;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -596,24 +606,18 @@ class Util {
|
||||||
|
|
||||||
/** Generate a random user name */
|
/** Generate a random user name */
|
||||||
randomUser(prefix) {
|
randomUser(prefix) {
|
||||||
var pfx = prefix ? prefix.replace(/[^0-9a-zA-Z\-_]/gi, '') : null;
|
var pfx = prefix ? prefix.replace(/[^0-9a-zA-Z\-_]/gi, "") : null;
|
||||||
if (!pfx || pfx.length == 0) {
|
if (!pfx || pfx.length == 0) {
|
||||||
pfx = "weblite-";
|
pfx = "weblite-";
|
||||||
}
|
}
|
||||||
return pfx + this.randomString(
|
return pfx + this.randomString(12, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
|
||||||
12,
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate random 12 char password
|
* Generate random 12 char password
|
||||||
*/
|
*/
|
||||||
randomPass() {
|
randomPass() {
|
||||||
return this.randomString(
|
return this.randomString(12, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#_-*+");
|
||||||
12,
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#_-*+"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// From here: https://stackoverflow.com/questions/1349404/generate-random-string-characters-in-javascript
|
// From here: https://stackoverflow.com/questions/1349404/generate-random-string-characters-in-javascript
|
||||||
|
|
@ -621,9 +625,7 @@ class Util {
|
||||||
var result = "";
|
var result = "";
|
||||||
var charactersLength = characters.length;
|
var charactersLength = characters.length;
|
||||||
for (var i = 0; i < length; i++) {
|
for (var i = 0; i < length; i++) {
|
||||||
result += characters.charAt(
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
Math.floor(Math.random() * charactersLength)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -690,16 +692,18 @@ class Util {
|
||||||
isChildVisible(parentNode, childNode) {
|
isChildVisible(parentNode, childNode) {
|
||||||
const rect1 = parentNode.getBoundingClientRect();
|
const rect1 = parentNode.getBoundingClientRect();
|
||||||
const rect2 = childNode.getBoundingClientRect();
|
const rect2 = childNode.getBoundingClientRect();
|
||||||
var overlap = !(rect1.right <= rect2.left ||
|
var overlap = !(
|
||||||
|
rect1.right <= rect2.left ||
|
||||||
rect1.left >= rect2.right ||
|
rect1.left >= rect2.right ||
|
||||||
rect1.bottom <= rect2.top ||
|
rect1.bottom <= rect2.top ||
|
||||||
rect1.top >= rect2.bottom)
|
rect1.top >= rect2.bottom
|
||||||
|
);
|
||||||
return overlap;
|
return overlap;
|
||||||
}
|
}
|
||||||
|
|
||||||
findOneVisibleElement(parentNode) {
|
findOneVisibleElement(parentNode) {
|
||||||
let start = 0;
|
let start = 0;
|
||||||
let end = (parentNode && parentNode.children) ? parentNode.children.length - 1 : -1;
|
let end = parentNode && parentNode.children ? parentNode.children.length - 1 : -1;
|
||||||
while (start <= end) {
|
while (start <= end) {
|
||||||
let middle = Math.floor((start + end) / 2);
|
let middle = Math.floor((start + end) / 2);
|
||||||
let childNode = parentNode.children[middle];
|
let childNode = parentNode.children[middle];
|
||||||
|
|
@ -720,7 +724,7 @@ class Util {
|
||||||
|
|
||||||
_importAll(r) {
|
_importAll(r) {
|
||||||
var images = [];
|
var images = [];
|
||||||
r.keys().forEach(res => {
|
r.keys().forEach((res) => {
|
||||||
console.log("Avatar", res);
|
console.log("Avatar", res);
|
||||||
// // Remove"./"
|
// // Remove"./"
|
||||||
var name = res.split("_")[1];
|
var name = res.split("_")[1];
|
||||||
|
|
@ -734,38 +738,49 @@ class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultAvatars() {
|
getDefaultAvatars() {
|
||||||
return this._importAll(require.context('../assets/avatars/', true, /\.(jpeg|jpg|png)$/));
|
var images = [];
|
||||||
|
const modules = import.meta.glob("../assets/avatars/*.(jpeg|jpg|png)", { eager: true });
|
||||||
|
Object.keys(modules).map((path) => {
|
||||||
|
var name = path.split("_")[1];
|
||||||
|
name = name.slice(0, name.indexOf("."));
|
||||||
|
name = name.charAt(0).toUpperCase() + name.slice(1);
|
||||||
|
const image = modules[path].default;
|
||||||
|
const randomNumber = parseInt(this.randomString(4, "0123456789")).toFixed();
|
||||||
|
images.push({ id: path, image: image, name: "Guest " + name + " " + randomNumber });
|
||||||
|
});
|
||||||
|
return images;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAvatar(matrix, file, onUploadProgress) {
|
setAvatar(matrix, file, onUploadProgress) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios.get(file, { responseType: 'arraybuffer' })
|
axios
|
||||||
.then(response => {
|
.get(file, { responseType: "arraybuffer" })
|
||||||
|
.then((response) => {
|
||||||
const opts = {
|
const opts = {
|
||||||
type: response.headers['content-type'].split(';')[0],
|
type: response.headers["content-type"].split(";")[0],
|
||||||
name: "Avatar",
|
name: "Avatar",
|
||||||
progressHandler: onUploadProgress,
|
progressHandler: onUploadProgress,
|
||||||
onlyContentUri: false
|
onlyContentUri: false,
|
||||||
};
|
};
|
||||||
var avatarUri;
|
var avatarUri;
|
||||||
matrix.matrixClient.uploadContent(response.data, opts)
|
matrix.matrixClient
|
||||||
|
.uploadContent(response.data, opts)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
avatarUri = response.content_uri;
|
avatarUri = response.content_uri;
|
||||||
return matrix.matrixClient.setAvatarUrl(avatarUri);
|
return matrix.matrixClient.setAvatarUrl(avatarUri);
|
||||||
})
|
})
|
||||||
.then(result => {
|
.then((result) => {
|
||||||
matrix.userAvatar = avatarUri;
|
matrix.userAvatar = avatarUri;
|
||||||
resolve(result);
|
resolve(result);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setRoomAvatar(matrixClient, roomId, file, onUploadProgress) {
|
setRoomAvatar(matrixClient, roomId, file, onUploadProgress) {
|
||||||
|
|
@ -777,36 +792,38 @@ class Util {
|
||||||
|
|
||||||
const info = {
|
const info = {
|
||||||
mimetype: file.type,
|
mimetype: file.type,
|
||||||
size: file.size
|
size: file.size,
|
||||||
};
|
};
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
type: file.type,
|
type: file.type,
|
||||||
name: "Room Avatar",
|
name: "Room Avatar",
|
||||||
progressHandler: onUploadProgress,
|
progressHandler: onUploadProgress,
|
||||||
onlyContentUri: false
|
onlyContentUri: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
var messageContent = {
|
var messageContent = {
|
||||||
body: file.name,
|
body: file.name,
|
||||||
info: info
|
info: info,
|
||||||
}
|
};
|
||||||
|
|
||||||
matrixClient.uploadContent(data, opts)
|
matrixClient
|
||||||
|
.uploadContent(data, opts)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
messageContent.url = response.content_uri;
|
messageContent.url = response.content_uri;
|
||||||
return matrixClient.sendStateEvent(roomId, "m.room.avatar", messageContent);
|
return matrixClient.sendStateEvent(roomId, "m.room.avatar", messageContent);
|
||||||
})
|
})
|
||||||
.then(result => {
|
.then((result) => {
|
||||||
resolve(result);
|
resolve(matrixClient.mxcUrlToHttp(messageContent.url, 80, 80, "scale", undefined, undefined, true))
|
||||||
|
// resolve(result);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
reader.onerror = (err) => {
|
reader.onerror = (err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
};
|
||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -828,9 +845,7 @@ class Util {
|
||||||
if (w > 640 || h > 640) {
|
if (w > 640 || h > 640) {
|
||||||
var aspect = w / h;
|
var aspect = w / h;
|
||||||
var newWidth = parseInt((w > h ? 640 : 640 * aspect).toFixed());
|
var newWidth = parseInt((w > h ? 640 : 640 * aspect).toFixed());
|
||||||
var newHeight = parseInt(
|
var newHeight = parseInt((w > h ? 640 / aspect : 640).toFixed());
|
||||||
(w > h ? 640 / aspect : 640).toFixed()
|
|
||||||
);
|
|
||||||
var imageResize = new ImageResize({
|
var imageResize = new ImageResize({
|
||||||
format: "png",
|
format: "png",
|
||||||
width: newWidth,
|
width: newWidth,
|
||||||
|
|
@ -871,20 +886,20 @@ class Util {
|
||||||
* @param {*} ts2
|
* @param {*} ts2
|
||||||
*/
|
*/
|
||||||
dayDiff(ts1, ts2) {
|
dayDiff(ts1, ts2) {
|
||||||
var t1 = dayjs(ts1).endOf('day');
|
var t1 = dayjs(ts1).endOf("day");
|
||||||
var t2 = dayjs(ts2).endOf('day');
|
var t2 = dayjs(ts2).endOf("day");
|
||||||
return t2.diff(t1, 'day');
|
return t2.diff(t1, "day");
|
||||||
}
|
}
|
||||||
|
|
||||||
dayDiffToday(timestamp) {
|
dayDiffToday(timestamp) {
|
||||||
var then = dayjs(timestamp).endOf('day');
|
var then = dayjs(timestamp).endOf("day");
|
||||||
var now = dayjs().endOf('day');
|
var now = dayjs().endOf("day");
|
||||||
return now.diff(then, 'day');
|
return now.diff(then, "day");
|
||||||
}
|
}
|
||||||
|
|
||||||
formatDay(timestamp) {
|
formatDay(timestamp) {
|
||||||
var then = dayjs(timestamp).endOf('day');
|
var then = dayjs(timestamp).endOf("day");
|
||||||
return then.format('L');
|
return then.format("L");
|
||||||
}
|
}
|
||||||
|
|
||||||
formatTime(time) {
|
formatTime(time) {
|
||||||
|
|
@ -908,7 +923,7 @@ class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
formatDuration(ms) {
|
formatDuration(ms) {
|
||||||
if (ms >= (60 * 60000)) {
|
if (ms >= 60 * 60000) {
|
||||||
return dayjs.duration(ms).format("H:mm:ss");
|
return dayjs.duration(ms).format("H:mm:ss");
|
||||||
}
|
}
|
||||||
return dayjs.duration(ms).format("m:ss");
|
return dayjs.duration(ms).format("m:ss");
|
||||||
|
|
@ -916,7 +931,7 @@ class Util {
|
||||||
|
|
||||||
formatRecordStartTime(timestamp) {
|
formatRecordStartTime(timestamp) {
|
||||||
var then = dayjs(timestamp);
|
var then = dayjs(timestamp);
|
||||||
return then.format('lll');
|
return then.format("lll");
|
||||||
}
|
}
|
||||||
|
|
||||||
browserCanRecordAudio() {
|
browserCanRecordAudio() {
|
||||||
|
|
@ -924,8 +939,8 @@ class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
getRoomNameFromAlias(alias) {
|
getRoomNameFromAlias(alias) {
|
||||||
if (alias && alias.startsWith('#') && alias.indexOf(':') > 0) {
|
if (alias && alias.startsWith("#") && alias.indexOf(":") > 0) {
|
||||||
return alias.slice(1).split(':')[0];
|
return alias.slice(1).split(":")[0];
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
@ -934,8 +949,9 @@ class Util {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var preferredAlias = roomName.replace(/\s/g, "").toLowerCase();
|
var preferredAlias = roomName.replace(/\s/g, "").toLowerCase();
|
||||||
var tryAlias = "#" + preferredAlias + ":" + defaultMatrixDomainPart;
|
var tryAlias = "#" + preferredAlias + ":" + defaultMatrixDomainPart;
|
||||||
matrixClient.getRoomIdForAlias(tryAlias)
|
matrixClient
|
||||||
.then(ignoredid => {
|
.getRoomIdForAlias(tryAlias)
|
||||||
|
.then((ignoredid) => {
|
||||||
// We got a response, this means the tryAlias already exists.
|
// We got a response, this means the tryAlias already exists.
|
||||||
// Try again, with appended random chars
|
// Try again, with appended random chars
|
||||||
if (iterationCount) {
|
if (iterationCount) {
|
||||||
|
|
@ -948,26 +964,32 @@ class Util {
|
||||||
roomName = roomName.substring(0, roomName.length - 5);
|
roomName = roomName.substring(0, roomName.length - 5);
|
||||||
}
|
}
|
||||||
const randomChars = this.randomString(4, "abcdefghijklmnopqrstuvwxyz0123456789");
|
const randomChars = this.randomString(4, "abcdefghijklmnopqrstuvwxyz0123456789");
|
||||||
resolve(this.getUniqueAliasForRoomName(matrixClient, roomName + "-" + randomChars, defaultMatrixDomainPart, (iterationCount || 0) + 1))
|
resolve(
|
||||||
|
this.getUniqueAliasForRoomName(
|
||||||
|
matrixClient,
|
||||||
|
roomName + "-" + randomChars,
|
||||||
|
defaultMatrixDomainPart,
|
||||||
|
(iterationCount || 0) + 1
|
||||||
|
)
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
if (err.errcode == 'M_NOT_FOUND') {
|
if (err.errcode == "M_NOT_FOUND") {
|
||||||
resolve(preferredAlias);
|
resolve(preferredAlias);
|
||||||
} else {
|
} else {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadableTypes() {
|
downloadableTypes() {
|
||||||
return ['m.video','m.audio','m.image','m.file'];
|
return ["m.video", "m.audio", "m.image", "m.file"];
|
||||||
}
|
}
|
||||||
|
|
||||||
download(matrixClient, event) {
|
download(matrixClient, useAuthedMedia, event) {
|
||||||
console.log("DOWNLOAD");
|
console.log("DOWNLOAD");
|
||||||
this
|
this.getAttachment(matrixClient, useAuthedMedia, event)
|
||||||
.getAttachment(matrixClient, event)
|
|
||||||
.then((url) => {
|
.then((url) => {
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = url;
|
link.href = url;
|
||||||
|
|
@ -990,6 +1012,7 @@ class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
getMatrixBaseUrl(user, config) {
|
getMatrixBaseUrl(user, config) {
|
||||||
|
console.error("getMatrixBaseUrl", user, config);
|
||||||
if (user) {
|
if (user) {
|
||||||
const domain = User.domainPart(user.user_id);
|
const domain = User.domainPart(user.user_id);
|
||||||
if (domain) {
|
if (domain) {
|
||||||
|
|
@ -1000,7 +1023,7 @@ class Util {
|
||||||
}
|
}
|
||||||
return AutoDiscovery.findClientConfig(domain)
|
return AutoDiscovery.findClientConfig(domain)
|
||||||
.then((clientConfig) => {
|
.then((clientConfig) => {
|
||||||
const hs = clientConfig['m.homeserver'];
|
const hs = clientConfig["m.homeserver"];
|
||||||
if (hs && !hs.error && hs.base_url) {
|
if (hs && !hs.error && hs.base_url) {
|
||||||
console.log("Use home server returned from well-known", hs.base_url);
|
console.log("Use home server returned from well-known", hs.base_url);
|
||||||
return hs.base_url;
|
return hs.base_url;
|
||||||
|
|
@ -1019,7 +1042,11 @@ class Util {
|
||||||
|
|
||||||
getMimeType(event) {
|
getMimeType(event) {
|
||||||
const content = event.getContent();
|
const content = event.getContent();
|
||||||
return (content.info && content.info.mimetype) ? content.info.mimetype : (content.file && content.file.mimetype) ? content.file.mimetype : "";
|
return content.info && content.info.mimetype
|
||||||
|
? content.info.mimetype
|
||||||
|
: content.file && content.file.mimetype
|
||||||
|
? content.file.mimetype
|
||||||
|
: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
getFileName(event) {
|
getFileName(event) {
|
||||||
|
|
@ -1073,7 +1100,10 @@ class Util {
|
||||||
|
|
||||||
isFileTypeZip(event) {
|
isFileTypeZip(event) {
|
||||||
const mime = this.getMimeType(event);
|
const mime = this.getMimeType(event);
|
||||||
if (["application/zip", "application/x-zip-compressed", "multipart/x-zip"].includes(mime) || this.getFileName(event).endsWith(".zip")) {
|
if (
|
||||||
|
["application/zip", "application/x-zip-compressed", "multipart/x-zip"].includes(mime) ||
|
||||||
|
this.getFileName(event).endsWith(".zip")
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1090,12 +1120,12 @@ class Util {
|
||||||
// reference: https://codepen.io/jtangelder/pen/xxYyJQ
|
// reference: https://codepen.io/jtangelder/pen/xxYyJQ
|
||||||
const hm = new Hammer.Manager(element);
|
const hm = new Hammer.Manager(element);
|
||||||
|
|
||||||
hm.add(new Hammer.Tap({ event: 'doubletap', taps: 2 }));
|
hm.add(new Hammer.Tap({ event: "doubletap", taps: 2 }));
|
||||||
hm.add(new Hammer.Tap({ event: 'singletap' }) );
|
hm.add(new Hammer.Tap({ event: "singletap" }));
|
||||||
|
|
||||||
hm.get('doubletap').recognizeWith('singletap');
|
hm.get("doubletap").recognizeWith("singletap");
|
||||||
hm.get('singletap').requireFailure('doubletap');
|
hm.get("singletap").requireFailure("doubletap");
|
||||||
return hm
|
return hm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default new Util();
|
export default new Util();
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,14 @@ import Vuetify from 'vuetify/lib';
|
||||||
// Import all .vue icons and process them, so they can be used
|
// Import all .vue icons and process them, so they can be used
|
||||||
// as $vuetify.icons.<iconname>
|
// as $vuetify.icons.<iconname>
|
||||||
var icons = {}
|
var icons = {}
|
||||||
function importAll(r) {
|
const modules = import.meta.glob('@/assets/icons/*.vue', {eager: true});
|
||||||
return r.keys().map(res => {
|
Object.keys(modules).map(path => {
|
||||||
// Remove"./"
|
// Remove"./"
|
||||||
const parts = res.split("/");
|
const parts = path.split("/");
|
||||||
const iconName = parts[1].split(".")[0];
|
const iconName = parts[parts.length - 1].split(".")[0];
|
||||||
icons[iconName] = { component: r(res).default };
|
icons[iconName] = { component: modules[path].default }
|
||||||
});
|
});
|
||||||
}
|
|
||||||
importAll(require.context('@/assets/icons/', true, /\.vue$/));
|
|
||||||
|
|
||||||
Vue.use(Vuetify);
|
Vue.use(Vuetify);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ export default {
|
||||||
info.loading = true;
|
info.loading = true;
|
||||||
info.abortController = new AbortController();
|
info.abortController = new AbortController();
|
||||||
utils
|
utils
|
||||||
.getAttachment(this.$root.$matrix.matrixClient, event, (progress) => {
|
.getAttachment(this.$root.$matrix.matrixClient, this.$root.$matrix.useAuthedMedia, event, (progress) => {
|
||||||
info.loadPercent = progress;
|
info.loadPercent = progress;
|
||||||
}, false, info.abortController)
|
}, false, info.abortController)
|
||||||
.then((url) => {
|
.then((url) => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
import * as defaultConfig from "@/assets/config.json";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
install(Vue, defaultServerFromLocation, onloaded) {
|
install(Vue, defaultServerFromLocation, onloaded) {
|
||||||
var config = Vue.observable(require('@/assets/config.json'));
|
var config = Vue.observable(defaultConfig.default);
|
||||||
Vue.set(config, "loaded", false);
|
Vue.set(config, "loaded", false);
|
||||||
const getRuntimeConfig = () => {
|
const getRuntimeConfig = () => {
|
||||||
return fetch('./config.json?ms=' + Date.now()).then((res) => res.json()).catch(err => {
|
return fetch('./config.json?ms=' + Date.now()).then((res) => res.json()).catch(err => {
|
||||||
|
|
@ -42,6 +44,8 @@ export default {
|
||||||
}
|
}
|
||||||
Vue.set(config, "loaded", true);
|
Vue.set(config, "loaded", true);
|
||||||
|
|
||||||
|
document.title = config.appName || "";
|
||||||
|
|
||||||
// Tell callback we are done loading runtime config
|
// Tell callback we are done loading runtime config
|
||||||
if (onloaded) {
|
if (onloaded) {
|
||||||
onloaded(config);
|
onloaded(config);
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import * as sdk from "matrix-js-sdk";
|
||||||
import { TimelineWindow, EventTimeline, EventStatus } from "matrix-js-sdk";
|
import { TimelineWindow, EventTimeline, EventStatus } from "matrix-js-sdk";
|
||||||
import util, { STATE_EVENT_ROOM_DELETED, STATE_EVENT_ROOM_TYPE, ROOM_TYPE_CHANNEL, ROOM_TYPE_FILE_MODE, ROOM_TYPE_VOICE_MODE, ROOM_TYPE_DEFAULT } from "../plugins/utils";
|
import util, { STATE_EVENT_ROOM_DELETED, STATE_EVENT_ROOM_TYPE, ROOM_TYPE_CHANNEL, ROOM_TYPE_FILE_MODE, ROOM_TYPE_VOICE_MODE, ROOM_TYPE_DEFAULT } from "../plugins/utils";
|
||||||
import User from "../models/user";
|
import User from "../models/user";
|
||||||
|
import * as LocalStorageCryptoStoreClass from "matrix-js-sdk/lib/crypto/store/localStorage-crypto-store";
|
||||||
|
|
||||||
const LocalStorageCryptoStore =
|
const LocalStorageCryptoStore = LocalStorageCryptoStoreClass.LocalStorageCryptoStore;
|
||||||
require("matrix-js-sdk/lib/crypto/store/localStorage-crypto-store").LocalStorageCryptoStore;
|
|
||||||
|
|
||||||
export const CHANNEL_POWER_LEVELS = {
|
export const CHANNEL_POWER_LEVELS = {
|
||||||
"m.room.encrypted": 0, // NOTE! Since practically all events in encrypted rooms get sent as "m.room.encrypted" we need to set
|
"m.room.encrypted": 0, // NOTE! Since practically all events in encrypted rooms get sent as "m.room.encrypted" we need to set
|
||||||
|
|
@ -50,6 +50,7 @@ export default {
|
||||||
userCanSendReactionAndAnswerPollInCurrentRoom: true,
|
userCanSendReactionAndAnswerPollInCurrentRoom: true,
|
||||||
currentRoomBeingPurged: false,
|
currentRoomBeingPurged: false,
|
||||||
notificationCount: 0,
|
notificationCount: 0,
|
||||||
|
useAuthedMedia: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -297,14 +298,16 @@ export default {
|
||||||
accessToken: user.access_token,
|
accessToken: user.access_token,
|
||||||
timelineSupport: true,
|
timelineSupport: true,
|
||||||
unstableClientRelationAggregation: true,
|
unstableClientRelationAggregation: true,
|
||||||
|
cryptoStore: this.createCryptoStore()
|
||||||
//useAuthorizationHeader: true
|
//useAuthorizationHeader: true
|
||||||
};
|
};
|
||||||
this.matrixClient = sdk.createClient(opts);
|
this.matrixClient = sdk.createClient(opts);
|
||||||
// if (user.is_guest) {
|
// if (user.is_guest) {
|
||||||
// this.matrixClient.setGuest(true);
|
// this.matrixClient.setGuest(true);
|
||||||
// }
|
// }
|
||||||
|
console.error("Created client", this.matrixClient);
|
||||||
return this.matrixClient
|
return this.matrixClient
|
||||||
.initCrypto()
|
.initRustCrypto()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log("Crypto initialized");
|
console.log("Crypto initialized");
|
||||||
|
|
||||||
|
|
@ -331,6 +334,11 @@ export default {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
return this.matrixClient.isVersionSupported("v1.11");
|
||||||
|
})
|
||||||
|
.then((authedMediaSupported) => {
|
||||||
|
this.useAuthedMedia = authedMediaSupported;
|
||||||
|
|
||||||
// Ready to use! Start by loading rooms.
|
// Ready to use! Start by loading rooms.
|
||||||
this.initClient();
|
this.initClient();
|
||||||
return user;
|
return user;
|
||||||
|
|
@ -391,7 +399,7 @@ export default {
|
||||||
Vue.set(
|
Vue.set(
|
||||||
room,
|
room,
|
||||||
"avatar",
|
"avatar",
|
||||||
room.getAvatarUrl(this.matrixClient.getHomeserverUrl(), 80, 80, "scale", true)
|
room.getAvatarUrl(this.matrixClient.getHomeserverUrl(), 80, 80, "scale", true, this.useAuthedMedia)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -517,7 +525,7 @@ export default {
|
||||||
});
|
});
|
||||||
updatedRooms.forEach((room) => {
|
updatedRooms.forEach((room) => {
|
||||||
if (!room.avatar) {
|
if (!room.avatar) {
|
||||||
Vue.set(room, "avatar", room.getAvatarUrl(this.matrixClient.getHomeserverUrl(), 80, 80, "scale", true));
|
Vue.set(room, "avatar", room.getAvatarUrl(this.matrixClient.getHomeserverUrl(), 80, 80, "scale", true, this.useAuthedMedia));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Vue.set(this, "rooms", updatedRooms);
|
Vue.set(this, "rooms", updatedRooms);
|
||||||
|
|
@ -1208,34 +1216,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getPublicUserInfo(userId) {
|
|
||||||
if (!userId) {
|
|
||||||
return Promise.reject("Invalid parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts = userId.split(":");
|
|
||||||
if (parts.length != 2) {
|
|
||||||
return Promise.reject("Unknown home server");
|
|
||||||
}
|
|
||||||
|
|
||||||
const clientPromise = this.getPublicQueryMatrixClient();
|
|
||||||
let matrixClient;
|
|
||||||
return clientPromise
|
|
||||||
.then((client) => {
|
|
||||||
matrixClient = client;
|
|
||||||
return client.getProfileInfo(userId);
|
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
if (response.avatar_url) {
|
|
||||||
response.avatar = matrixClient.mxcUrlToHttp(response.avatar_url, 80, 80, "scale", true);
|
|
||||||
}
|
|
||||||
return Promise.resolve(response);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
return Promise.reject("Failed to find user info: " + err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getPublicRoomInfo(roomId) {
|
getPublicRoomInfo(roomId) {
|
||||||
if (!roomId) {
|
if (!roomId) {
|
||||||
return Promise.reject("Invalid parameters");
|
return Promise.reject("Invalid parameters");
|
||||||
|
|
@ -1249,14 +1229,14 @@ export default {
|
||||||
|
|
||||||
const clientPromise = this.getPublicQueryMatrixClient();
|
const clientPromise = this.getPublicQueryMatrixClient();
|
||||||
|
|
||||||
const findOrGetMore = function _findOrGetMore(client, response) {
|
const findOrGetMore = function _findOrGetMore(client, useAuthedMedia, response) {
|
||||||
for (var room of response.chunk) {
|
for (var room of response.chunk) {
|
||||||
if (
|
if (
|
||||||
(roomId.startsWith("#") && room.canonical_alias == roomId) ||
|
(roomId.startsWith("#") && room.canonical_alias == roomId) ||
|
||||||
(roomId.startsWith("!") && room.room_id == roomId)
|
(roomId.startsWith("!") && room.room_id == roomId)
|
||||||
) {
|
) {
|
||||||
if (room.avatar_url) {
|
if (room.avatar_url) {
|
||||||
room.avatar = client.mxcUrlToHttp(room.avatar_url, 80, 80, "scale", true);
|
room.avatar = client.mxcUrlToHttp(room.avatar_url, 80, 80, "scale", true, undefined, useAuthedMedia);
|
||||||
}
|
}
|
||||||
return Promise.resolve(room);
|
return Promise.resolve(room);
|
||||||
}
|
}
|
||||||
|
|
@ -1280,13 +1260,18 @@ export default {
|
||||||
};
|
};
|
||||||
|
|
||||||
var matrixClient;
|
var matrixClient;
|
||||||
|
let useAuthedMedia = false;
|
||||||
return clientPromise
|
return clientPromise
|
||||||
.then((client) => {
|
.then((client) => {
|
||||||
matrixClient = client;
|
matrixClient = client;
|
||||||
|
return client.isVersionSupported("v1.11");
|
||||||
|
})
|
||||||
|
.then((version1_11) => {
|
||||||
|
useAuthedMedia = version1_11;
|
||||||
return matrixClient.publicRooms({ server: server, limit: 1000 });
|
return matrixClient.publicRooms({ server: server, limit: 1000 });
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
return findOrGetMore(matrixClient, response);
|
return findOrGetMore(matrixClient, useAuthedMedia, response);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
return Promise.reject("Failed to find room: " + err);
|
return Promise.reject("Failed to find room: " + err);
|
||||||
|
|
|
||||||
53
vite.config.mjs
Normal file
53
vite.config.mjs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
import vue from "@vitejs/plugin-vue2";
|
||||||
|
import { fileURLToPath, URL } from "node:url";
|
||||||
|
import Components from "unplugin-vue-components/vite";
|
||||||
|
import { VuetifyResolver } from "unplugin-vue-components/resolvers";
|
||||||
|
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
||||||
|
import nodePolyfills from 'rollup-plugin-polyfill-node';
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
Components({
|
||||||
|
resolvers: [VuetifyResolver()],
|
||||||
|
}),
|
||||||
|
viteStaticCopy({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
src: "src/assets/config.json",
|
||||||
|
dest: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "node_modules/@matrix-org/olm/olm.wasm",
|
||||||
|
dest: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'node_modules/@matrix-org/matrix-sdk-crypto-wasm/pkg/matrix_sdk_crypto_wasm_bg.wasm',
|
||||||
|
dest: '/node_modules/.vite/deps/pkg'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
nodePolyfills()
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue", ".wasm"],
|
||||||
|
alias: {
|
||||||
|
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||||
|
"~vuetify": fileURLToPath(new URL("./node_modules/vuetify", import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
define: {
|
||||||
|
global: "window",
|
||||||
|
Lame: "window.Lame",
|
||||||
|
Presets: "window.Presets",
|
||||||
|
GainAnalysis: "window.GainAnalysis",
|
||||||
|
QuantizePVT: "window.QuantizePVT",
|
||||||
|
Quantize: "window.Quantize",
|
||||||
|
Takehiro: "window.Takehiro",
|
||||||
|
Reservoir: "window.Reservoir",
|
||||||
|
MPEGMode: "window.MPEGMode",
|
||||||
|
BitStream: "window.BitStream",
|
||||||
|
},
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue