Merge branch '537-non-thumbnailed-media-shows-as-a-grey-box' into 'dev'
Resolve "Non-thumbnailed media shows as a grey box" See merge request keanuapp/keanuapp-weblite!273
This commit is contained in:
commit
ce653db8d8
31 changed files with 380 additions and 61 deletions
|
|
@ -52,6 +52,7 @@ The app loads runtime configutation from the server at "./config.json" and merge
|
|||
* **logo** - An url or base64-encoded image data url that represents the app logotype.
|
||||
* **accentColor** - The accent color of the app UI. Use a HTML-style color value string, like "#ff0080".
|
||||
* **show_status_messages** - Whether to show only user joins/leaves and display name updates, or the full range of room status updates. Possible values are "never" (only the above), "moderators" (moderators will see all status updates) or "always" (everyone will see all status updates). Defaults to "always".
|
||||
* **maxSizeAutoDownloads** - Attachments smaller than this will be auto downloaded. Default is 10Mb.
|
||||
|
||||
|
||||
### Sticker short codes - To enable sticker short codes, follow these steps:
|
||||
|
|
|
|||
14
package-lock.json
generated
14
package-lock.json
generated
|
|
@ -28,7 +28,7 @@
|
|||
"jszip": "^3.9.1",
|
||||
"linkify-html": "^4.1.0",
|
||||
"linkifyjs": "^4.1.0",
|
||||
"material-design-icons-iconfont": "^6.1",
|
||||
"material-design-icons-iconfont": "^6.7.0",
|
||||
"matrix-js-sdk": "^23.4.0",
|
||||
"md-gum-polyfill": "^1.0.0",
|
||||
"mic-recorder-to-mp3": "^2.2.2",
|
||||
|
|
@ -9936,9 +9936,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/material-design-icons-iconfont": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/material-design-icons-iconfont/-/material-design-icons-iconfont-6.1.0.tgz",
|
||||
"integrity": "sha512-wRJtOo1v1ch+gN8PRsj0IGJznk+kQ8mz13ds/nuhLI+Qyf/931ZlRpd92oq0IRPpZIb+bhX8pRjzIVdcPDKmiQ=="
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/material-design-icons-iconfont/-/material-design-icons-iconfont-6.7.0.tgz",
|
||||
"integrity": "sha512-lSj71DgVv20kO0kGbs42icDzbRot61gEDBLQACzkUuznRQBUYmbxzEkGU6dNBb5fRWHMaScYlAXX96HQ4/cJWA=="
|
||||
},
|
||||
"node_modules/matrix-events-sdk": {
|
||||
"version": "0.0.1",
|
||||
|
|
@ -24023,9 +24023,9 @@
|
|||
}
|
||||
},
|
||||
"material-design-icons-iconfont": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/material-design-icons-iconfont/-/material-design-icons-iconfont-6.1.0.tgz",
|
||||
"integrity": "sha512-wRJtOo1v1ch+gN8PRsj0IGJznk+kQ8mz13ds/nuhLI+Qyf/931ZlRpd92oq0IRPpZIb+bhX8pRjzIVdcPDKmiQ=="
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/material-design-icons-iconfont/-/material-design-icons-iconfont-6.7.0.tgz",
|
||||
"integrity": "sha512-lSj71DgVv20kO0kGbs42icDzbRot61gEDBLQACzkUuznRQBUYmbxzEkGU6dNBb5fRWHMaScYlAXX96HQ4/cJWA=="
|
||||
},
|
||||
"matrix-events-sdk": {
|
||||
"version": "0.0.1",
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
"jszip": "^3.9.1",
|
||||
"linkify-html": "^4.1.0",
|
||||
"linkifyjs": "^4.1.0",
|
||||
"material-design-icons-iconfont": "^6.1",
|
||||
"material-design-icons-iconfont": "^6.7.0",
|
||||
"matrix-js-sdk": "^23.4.0",
|
||||
"md-gum-polyfill": "^1.0.0",
|
||||
"mic-recorder-to-mp3": "^2.2.2",
|
||||
|
|
|
|||
|
|
@ -783,12 +783,23 @@ body {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.download-text {
|
||||
width: 100%;
|
||||
color: white;
|
||||
}
|
||||
.download-size {
|
||||
font-size: 70%;
|
||||
color: white;
|
||||
}
|
||||
.download-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.room-name,
|
||||
|
|
|
|||
8
src/assets/icons/ic_apk.vue
Normal file
8
src/assets/icons/ic_apk.vue
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<template>
|
||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="56" height="56" rx="13" fill="#FCFCFC" />
|
||||
<path
|
||||
d="M19.8333 24.3817C19.8333 23.4434 19.0217 22.665 18.0833 22.6667C17.1433 22.665 16.3333 23.4434 16.3333 24.3834V31.5067C16.3333 32.4467 17.1433 33.1667 18.0833 33.1667C19.0217 33.1667 19.8333 32.4484 19.8333 31.5067V24.3817ZM35 22.6667H21L21.0017 33.685C21.0017 34.69 21.8133 35.5 22.8183 35.5H23.3333V39.6317C23.3333 40.57 24.1567 41.3334 25.095 41.3334C26.035 41.3334 26.8333 40.57 26.8333 39.6317V35.5H29.1667V39.6317C29.1667 40.57 29.9933 41.3334 30.93 41.3334C31.87 41.3334 32.6667 40.57 32.6667 39.6317V35.5H33.1867C34.1867 35.5 35.0017 34.6884 35.0017 33.6834L35 22.6667ZM35 21.4967C35 18.865 33.595 17.0567 31.4433 15.8517L32.54 13.6834C32.5967 13.57 32.56 13.4267 32.4517 13.3617C32.345 13.3 32.2133 13.3417 32.155 13.4567L31.05 15.6467C29.265 14.7867 26.9167 14.6984 24.9483 15.6467L23.8417 13.455C23.785 13.3417 23.6533 13.2984 23.5467 13.36C23.44 13.425 23.4017 13.5684 23.46 13.6834L24.5567 15.8517C22.405 17.0567 21 18.8667 21 21.4967H35ZM39.6667 24.3817C39.6667 23.4434 38.8567 22.665 37.9167 22.6667C36.9783 22.665 36.1667 23.4434 36.1667 24.3834V31.5067C36.1667 32.4467 36.9767 33.1667 37.9167 33.1667C38.8567 33.1667 39.6667 32.4484 39.6667 31.5067V24.3817Z"
|
||||
fill="#1D1D1D" />
|
||||
</svg>
|
||||
</template>
|
||||
8
src/assets/icons/ic_ipa.vue
Normal file
8
src/assets/icons/ic_ipa.vue
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<template>
|
||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="56" height="56" rx="13" fill="#FCFCFC" />
|
||||
<path
|
||||
d="M33.31 13C33.6517 16.1183 30.865 19.3717 27.965 19.145C27.6067 16.4217 30.1683 13.1267 33.31 13ZM32.9283 39.6317C31.1417 39.6667 30.5683 38.5733 28.5267 38.5733C26.485 38.5733 25.8483 39.6 24.1583 39.6667C21.3 39.775 16.89 33.19 16.89 27.4483C16.89 22.1733 20.565 19.5583 23.7767 19.5117C25.4983 19.48 27.125 20.6717 28.1767 20.6717C29.2317 20.6717 31.2083 19.2383 33.285 19.45C34.1517 19.485 36.5917 19.8 38.1617 22.0917C34.0017 24.8033 34.65 30.4783 39.1117 32.565C38.2383 35.0967 35.6233 39.5833 32.9283 39.6317Z"
|
||||
fill="#1D1D1D" />
|
||||
</svg>
|
||||
</template>
|
||||
8
src/assets/icons/ic_pdf.vue
Normal file
8
src/assets/icons/ic_pdf.vue
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<template>
|
||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="56" height="56" rx="13" fill="#FCFCFC" />
|
||||
<path
|
||||
d="M20.7408 23.42H18.0965C17.8358 23.42 17.5858 23.5235 17.4014 23.7078C17.217 23.8923 17.1135 24.1422 17.1135 24.403V31.4257C17.1135 31.7769 17.3008 32.1014 17.605 32.2769C17.9091 32.4526 18.2838 32.4526 18.588 32.2769C18.8921 32.1014 19.0795 31.7769 19.0795 31.4257V28.9033H20.7408C21.4683 28.8996 22.1649 28.6081 22.678 28.0922C23.1913 27.5764 23.4794 26.8785 23.4794 26.1509C23.4852 25.4234 23.1983 24.7242 22.6832 24.2107C22.1681 23.6971 21.4682 23.4121 20.7408 23.4201L20.7408 23.42ZM20.7408 26.9312H19.0795V25.386H20.7408C20.9471 25.381 21.1466 25.4609 21.2925 25.6068C21.4384 25.7529 21.5182 25.9522 21.5134 26.1586C21.515 26.3645 21.4343 26.5626 21.2891 26.7088C21.1442 26.8549 20.9467 26.9372 20.7408 26.9372L20.7408 26.9312ZM28.4947 23.4081H25.8405C25.5797 23.4081 25.3296 23.5117 25.1453 23.6961C24.961 23.8804 24.8575 24.1305 24.8575 24.3912V31.4352C24.8575 31.6958 24.961 31.9459 25.1453 32.1302C25.3296 32.3147 25.5797 32.4182 25.8405 32.4182H28.4947H28.4945C29.2246 32.4182 29.9247 32.1281 30.4409 31.612C30.957 31.0958 31.2471 30.3957 31.2471 29.6658V26.1506C31.2444 25.4223 30.9533 24.7247 30.4373 24.2107C29.9215 23.6966 29.2228 23.408 28.4945 23.408L28.4947 23.4081ZM29.281 29.674C29.281 29.8824 29.1982 30.0826 29.0506 30.2299C28.9031 30.3774 28.7031 30.4603 28.4945 30.4603H26.8234L26.8235 25.3741H28.4947H28.4945C28.7031 25.3741 28.9031 25.457 29.0506 25.6045C29.1982 25.7519 29.281 25.952 29.281 26.1605V29.674ZM39.0381 24.3911C39.0381 24.6519 38.9346 24.9019 38.7502 25.0863C38.5658 25.2706 38.3158 25.3742 38.0551 25.3742H34.6166V26.9371H36.8952C37.2464 26.9371 37.5708 27.1245 37.7465 27.4286C37.922 27.7328 37.922 28.1075 37.7465 28.4116C37.5708 28.7158 37.2464 28.9031 36.8952 28.9031H34.6166V31.4413C34.6166 31.7925 34.4292 32.1169 34.125 32.2926C33.8209 32.4681 33.4462 32.4681 33.142 32.2926C32.8379 32.1169 32.6505 31.7925 32.6505 31.4413V24.3911C32.6505 24.1305 32.7542 23.8804 32.9385 23.6961C33.1228 23.5116 33.3729 23.4081 33.6336 23.4081H38.0472C38.3079 23.4081 38.5579 23.5116 38.7423 23.6961C38.9266 23.8804 39.0302 24.1305 39.0302 24.3911L39.0381 24.3911ZM39.6162 10H16.4882C15.23 10.0005 14.0236 10.5012 13.1349 11.3918C12.2461 12.2823 11.7479 13.4898 11.75 14.7478V41.0923C11.75 42.3506 12.2487 43.5578 13.1367 44.4494C14.0246 45.3412 15.2297 45.8447 16.4882 45.85H34.1626C34.4912 45.8466 34.7984 45.6857 34.9883 45.4174L44.1106 36.2951L44.1104 36.2953C44.1808 36.2225 44.2345 36.1353 44.2677 36.0396C44.3217 35.9227 44.3488 35.795 44.3464 35.6662V14.7479C44.3485 13.4899 43.8503 12.2824 42.9615 11.3919C42.0727 10.5014 40.8664 10.0006 39.6082 10.0001L39.6162 10ZM33.1874 39.4114V43.8742H16.4882C15.7524 43.8706 15.0479 43.576 14.5286 43.0549C14.0091 42.5336 13.7171 41.8281 13.716 41.0924V14.748C13.715 14.0125 14.0067 13.3067 14.5268 12.7868C15.047 12.2666 15.7525 11.975 16.4882 11.9759H39.6084C40.3441 11.975 41.0497 12.2666 41.5698 12.7868C42.0899 13.3067 42.3816 14.0125 42.3806 14.748V34.6833L37.9375 34.6834C36.6796 34.6797 35.4718 35.1757 34.5795 36.0624C33.6874 36.9491 33.1839 38.1538 33.1797 39.4116L33.1874 39.4114Z"
|
||||
fill="#1D1D1D" />
|
||||
</svg>
|
||||
</template>
|
||||
8
src/assets/icons/ic_zip.vue
Normal file
8
src/assets/icons/ic_zip.vue
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<template>
|
||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="56" height="56" rx="13" fill="#FCFCFC" />
|
||||
<path
|
||||
d="M30.5667 36.6733C30.5667 38.8627 25.4307 38.8453 25.4307 36.6773C25.4307 34.4267 30.5667 34.3747 30.5667 36.6733ZM44 18.6667V37.3333C44 41.0147 41.016 44 37.3333 44H18.6667C14.984 44 12 41.0147 12 37.3333V18.6667C12 14.9853 14.984 12 18.6667 12H37.3333C41.016 12 44 14.9853 44 18.6667ZM25.3333 21.3333H30.6667V20H25.3333V21.3333ZM25.3333 18.6667H30.6667V17.3333H25.3333V18.6667ZM25.3333 16H30.6667V14.6667H25.3333V16ZM25.3333 24H30.6667V22.6667H25.3333V24ZM25.3333 26.6667H30.6667V25.3333H25.3333V26.6667ZM32.1573 35.7147C31.64 33.056 30.668 28 30.668 28H25.3347C25.3347 28 24.3907 32.944 23.8453 35.7147C23.3053 38.4493 25.2173 40 28.0013 40C30.788 40 32.6893 38.4467 32.1573 35.7147Z"
|
||||
fill="#1D1D1D" />
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -213,7 +213,7 @@
|
|||
</v-container>
|
||||
|
||||
<input ref="attachment" type="file" name="attachment" @change="handlePickedAttachment($event)"
|
||||
accept="image/*, audio/*, video/*, .pdf" class="d-none" multiple/>
|
||||
accept="image/*,audio/*,video/*,.pdf,application/pdf,.apk,application/vnd.android.package-archive,.ipa,.zip,application/zip,application/x-zip-compressed,multipart/x-zip" class="d-none" multiple/>
|
||||
|
||||
<div v-if="currentFileInputsDialog && !useFileModeNonAdmin">
|
||||
<v-dialog v-model="currentFileInputsDialog" class="ma-0 pa-0" :width="$vuetify.breakpoint.smAndUp ? '50%' : '85%'" persistent scrollable>
|
||||
|
|
@ -1507,7 +1507,7 @@ export default {
|
|||
|
||||
setReplyToImage(event) {
|
||||
util
|
||||
.getThumbnail(this.$matrix.matrixClient, event)
|
||||
.getThumbnail(this.$matrix.matrixClient, event, this.$config)
|
||||
.then((url) => {
|
||||
this.replyToImg = url;
|
||||
})
|
||||
|
|
|
|||
|
|
@ -287,6 +287,7 @@ export default {
|
|||
var imageFolder = zip.folder("images");
|
||||
var audioFolder = zip.folder("audio");
|
||||
var videoFolder = zip.folder("video");
|
||||
var filesFolder = zip.folder("files");
|
||||
|
||||
var downloadPromises = [];
|
||||
let components = this.$refs.exportedEvent;
|
||||
|
|
@ -321,7 +322,8 @@ export default {
|
|||
for (let imageIndex = 0; imageIndex < images.length; imageIndex++) {
|
||||
const img = images[imageIndex];
|
||||
img.onerror = undefined;
|
||||
img.src = './avatars/' + fileName;
|
||||
img.removeAttribute("src");
|
||||
img.setAttribute("data-exported-src", './avatars/' + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -421,13 +423,15 @@ export default {
|
|||
var extension = ".mp3";
|
||||
let fileName = comp.event.getId() + extension;
|
||||
audioFolder.file(fileName, blob); // TODO calc bytes
|
||||
//this.$nextTick(() => {
|
||||
let elements = comp.$el.getElementsByTagName("audio");
|
||||
let element = elements && elements[0];
|
||||
if (element) {
|
||||
element.src = "./audio/" + fileName;
|
||||
element.setAttribute("data-exported-src", "./audio/" + fileName);
|
||||
}
|
||||
this.processedEvents += 1;
|
||||
resolve(true);
|
||||
//});
|
||||
});
|
||||
}
|
||||
})
|
||||
|
|
@ -449,13 +453,36 @@ export default {
|
|||
var extension = ".mp4";
|
||||
let fileName = comp.event.getId() + extension;
|
||||
videoFolder.file(fileName, blob); // TODO calc bytes
|
||||
// comp.src = "./video/" + fileName;
|
||||
let elements = comp.$el.getElementsByTagName("video");
|
||||
let element = elements && elements[0];
|
||||
if (element) {
|
||||
element.src = "./video/" + fileName;
|
||||
element.setAttribute("data-exported-src", "./video/" + fileName);
|
||||
}
|
||||
this.processedEvents += 1;
|
||||
|
||||
resolve(true);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((ignoredErr) => {
|
||||
this.processedEvents += 1;
|
||||
})
|
||||
);
|
||||
break;
|
||||
case "MessageIncomingFileExport":
|
||||
case "MessageOutgoingFileExport":
|
||||
downloadPromises.push(
|
||||
util
|
||||
.getAttachment(this.$matrix.matrixClient, comp.event, null, true)
|
||||
.then((blob) => {
|
||||
if (currentMediaSize + blob.size <= maxMediaSize) {
|
||||
currentMediaSize += blob.size;
|
||||
return new Promise((resolve, ignoredReject) => {
|
||||
var extension = util.getFileExtension(comp.event);
|
||||
let fileName = comp.event.getId() + extension;
|
||||
filesFolder.file(fileName, blob);
|
||||
comp.href="./files/" + fileName;
|
||||
this.processedEvents += 1;
|
||||
resolve(true);
|
||||
});
|
||||
}
|
||||
|
|
@ -504,7 +531,8 @@ export default {
|
|||
getCssRules(root);
|
||||
|
||||
this.$nextTick(() => {
|
||||
doc += this.$refs.exportRoot.outerHTML;
|
||||
const contentHtml = this.$refs.exportRoot.outerHTML;
|
||||
doc += contentHtml.replaceAll("data-exported-src=", "src=");
|
||||
doc += "</div></body></html>";
|
||||
|
||||
zip.file("chat.html", doc);
|
||||
|
|
|
|||
|
|
@ -19,10 +19,12 @@ import MessageIncomingImageExport from "./messages/export/MessageIncomingImageEx
|
|||
import MessageIncomingAudioExport from "./messages/export/MessageIncomingAudioExport";
|
||||
import MessageIncomingVideoExport from "./messages/export/MessageIncomingVideoExport";
|
||||
import MessageIncomingThreadExport from "./messages/export/MessageIncomingThreadExport";
|
||||
import MessageIncomingFileExport from "./messages/export/MessageIncomingFileExport";
|
||||
import MessageOutgoingImageExport from "./messages/export/MessageOutgoingImageExport";
|
||||
import MessageOutgoingAudioExport from "./messages/export/MessageOutgoingAudioExport";
|
||||
import MessageOutgoingVideoExport from "./messages/export/MessageOutgoingVideoExport";
|
||||
import MessageOutgoingThreadExport from "./messages/export/MessageOutgoingThreadExport";
|
||||
import MessageOutgoingFileExport from "./messages/export/MessageOutgoingFileExport";
|
||||
import ContactJoin from "./messages/ContactJoin.vue";
|
||||
import ContactLeave from "./messages/ContactLeave.vue";
|
||||
import ContactInvited from "./messages/ContactInvited.vue";
|
||||
|
|
@ -172,6 +174,9 @@ export default {
|
|||
event.getContent().info.mimetype &&
|
||||
event.getContent().info.mimetype.startsWith("image/svg")
|
||||
) {
|
||||
if (isForExport) {
|
||||
return MessageIncomingFileExport;
|
||||
}
|
||||
return MessageIncomingFile;
|
||||
}
|
||||
if (isForExport) {
|
||||
|
|
@ -189,6 +194,9 @@ export default {
|
|||
}
|
||||
return MessageIncomingVideo;
|
||||
} else if (event.getContent().msgtype == "m.file") {
|
||||
if (isForExport) {
|
||||
return MessageIncomingFileExport;
|
||||
}
|
||||
return MessageIncomingFile;
|
||||
} else if (stickers.isStickerShortcode(event.getContent().body)) {
|
||||
return MessageIncomingSticker;
|
||||
|
|
@ -223,6 +231,9 @@ export default {
|
|||
}
|
||||
return MessageOutgoingVideo;
|
||||
} else if (event.getContent().msgtype == "m.file") {
|
||||
if (isForExport) {
|
||||
return MessageOutgoingFileExport;
|
||||
}
|
||||
return MessageOutgoingFile;
|
||||
} else if (stickers.isStickerShortcode(event.getContent().body)) {
|
||||
return MessageOutgoingSticker;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,22 @@
|
|||
<template>
|
||||
<v-responsive v-if="item.event.getContent().msgtype == 'm.video'" :class="{'thumbnail-item': true, 'preview': previewOnly}"
|
||||
<v-responsive v-if="item.event.getContent().msgtype == 'm.video' && item.src" :class="{'thumbnail-item': true, 'preview': previewOnly}"
|
||||
@click.stop="$emit('itemclick', {item: item})">
|
||||
<video :src="item.src" :controls="!previewOnly" class="w-100 h-100">
|
||||
{{ $t('fallbacks.video_file') }}
|
||||
</video>
|
||||
</v-responsive>
|
||||
<v-img v-else-if="item.event.getContent().msgtype == 'm.image'" :aspect-ratio="previewOnly ? (16 / 9) : undefined" :class="{'thumbnail-item': true, 'preview': previewOnly}" :src="item.src" :contain="!previewOnly" :cover="previewOnly"
|
||||
<v-img v-else-if="item.event.getContent().msgtype == 'm.image' && item.src" :aspect-ratio="previewOnly ? (16 / 9) : undefined" :class="{'thumbnail-item': true, 'preview': previewOnly}" :src="item.src" :contain="!previewOnly" :cover="previewOnly"
|
||||
@click.stop="$emit('itemclick', {item: item})" />
|
||||
<div v-else :class="{'thumbnail-item': true, 'preview': previewOnly, 'file-item': true}" @click.stop="$emit('itemclick', {item: item})">
|
||||
<v-icon>description</v-icon>
|
||||
{{ $sanitize(item.event.getContent().body) }}
|
||||
<v-icon>{{ fileTypeIcon }}</v-icon>
|
||||
<b>{{ $sanitize(fileName) }}</b>
|
||||
<div>{{ fileSize }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import util from "../../plugins/utils";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
|
|
@ -32,6 +35,26 @@ export default {
|
|||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
fileTypeIcon() {
|
||||
if (util.isFileTypeAPK(this.item.event)) {
|
||||
return "$vuetify.icons.ic_apk";
|
||||
} else if (util.isFileTypeIPA(this.item.event)) {
|
||||
return "$vuetify.icons.ic_ipa";
|
||||
} else if (util.isFileTypePDF(this.item.event)) {
|
||||
return "$vuetify.icons.ic_pdf";
|
||||
} else if (util.isFileTypeZip(this.item.event)) {
|
||||
return "$vuetify.icons.ic_zip";
|
||||
}
|
||||
return "description"
|
||||
},
|
||||
fileName() {
|
||||
return util.getFileName(this.item.event);
|
||||
},
|
||||
fileSize() {
|
||||
return util.getFileSizeFormatted(this.item.event);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,7 @@
|
|||
</div>
|
||||
|
||||
<div class="message">
|
||||
<span>{{ $t('message.file_prefix') }}</span>
|
||||
<span
|
||||
class="cursor-pointer"
|
||||
@click.stop="$emit('download')"
|
||||
v-html="linkify($sanitize(messageText))"
|
||||
/>
|
||||
<ThumbnailView class="clickable" v-on:itemclick="$emit('download')" :item="{ event: event, src: null }" />
|
||||
<span class="edit-marker" v-if="event.replacingEventId()"
|
||||
>{{ $t('message.edited') }}</span
|
||||
>
|
||||
|
|
@ -25,11 +20,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ThumbnailView from '../file_mode/ThumbnailView.vue';
|
||||
import MessageIncoming from "./MessageIncoming.vue";
|
||||
|
||||
export default {
|
||||
extends: MessageIncoming,
|
||||
components: { MessageIncoming }
|
||||
components: { MessageIncoming, ThumbnailView }
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export default {
|
|||
const width = this.$refs.image.$el.clientWidth;
|
||||
const height = (width * 9) / 16;
|
||||
util
|
||||
.getThumbnail(this.$matrix.matrixClient, this.event, width, height)
|
||||
.getThumbnail(this.$matrix.matrixClient, this.event, this.$config, width, height)
|
||||
.then((url) => {
|
||||
const info = this.event.getContent().info;
|
||||
// JPEGs use cover, PNG and GIF ect contain. This is because PNG and GIF are expected to
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export default {
|
|||
};
|
||||
ret.promise =
|
||||
util
|
||||
.getThumbnail(this.$matrix.matrixClient, e, 100, 100)
|
||||
.getThumbnail(this.$matrix.matrixClient, e, this.$config, 100, 100)
|
||||
.then((url) => {
|
||||
ret.src = url;
|
||||
})
|
||||
|
|
|
|||
|
|
@ -10,6 +10,15 @@
|
|||
{{ $t('message.download_progress',{percentage: downloadProgress}) }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="userInitiatedDownloadsOnly && !src" class="download-overlay">
|
||||
<div class="text-center download-text">
|
||||
{{ fileName }}
|
||||
</div>
|
||||
<div class="text-center download-size">
|
||||
{{ fileSize }}
|
||||
</div>
|
||||
<v-icon size="32" color="white" class="clickable" @click="loadAttachmentSource(event, true)">download</v-icon>
|
||||
</div>
|
||||
</v-responsive>
|
||||
</div>
|
||||
</message-incoming>
|
||||
|
|
|
|||
|
|
@ -11,12 +11,7 @@
|
|||
|
||||
|
||||
<div class="message">
|
||||
<span>{{ $t('message.file_prefix') }}</span>
|
||||
<span
|
||||
class="cursor-pointer"
|
||||
@click.stop="$emit('download')"
|
||||
v-html="linkify($sanitize(messageText))"
|
||||
/>
|
||||
<ThumbnailView class="clickable" v-on:itemclick="$emit('download')" :item="{ event: event, src: null }" />
|
||||
<span class="edit-marker" v-if="event.replacingEventId()"
|
||||
>{{ $t('message.edited') }}</span
|
||||
>
|
||||
|
|
@ -26,11 +21,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ThumbnailView from '../file_mode/ThumbnailView.vue';
|
||||
import MessageOutgoing from "./MessageOutgoing.vue";
|
||||
|
||||
export default {
|
||||
extends: MessageOutgoing,
|
||||
components: { MessageOutgoing },
|
||||
components: { MessageOutgoing, ThumbnailView },
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export default {
|
|||
const width = this.$refs.image.$el.clientWidth;
|
||||
const height = (width * 9) / 16;
|
||||
util
|
||||
.getThumbnail(this.$matrix.matrixClient, this.event, width, height)
|
||||
.getThumbnail(this.$matrix.matrixClient, this.event, this.$config, width, height)
|
||||
.then((url) => {
|
||||
const info = this.event.getContent().info;
|
||||
// JPEGs use cover, PNG and GIF ect contain. This is because PNG and GIF are expected to
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ export default {
|
|||
};
|
||||
ret.promise =
|
||||
util
|
||||
.getThumbnail(this.$matrix.matrixClient, e, 100, 100)
|
||||
.getThumbnail(this.$matrix.matrixClient, e, this.$config, 100, 100)
|
||||
.then((url) => {
|
||||
ret.src = url;
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,20 @@
|
|||
<video :src="src" controls class="w-100 h-100">
|
||||
{{$t('fallbacks.video_file')}}
|
||||
</video>
|
||||
<div v-if="downloadProgress" class="download-overlay">
|
||||
<div class="text-center download-text">
|
||||
{{ $t('message.download_progress',{percentage: downloadProgress}) }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="userInitiatedDownloadsOnly && !src" class="download-overlay">
|
||||
<div class="text-center download-text">
|
||||
{{ fileName }}
|
||||
</div>
|
||||
<div class="text-center download-size">
|
||||
{{ fileSize }}
|
||||
</div>
|
||||
<v-icon size="32" color="white" class="clickable" @click="loadAttachmentSource(event, true)">download</v-icon>
|
||||
</div>
|
||||
</v-responsive>
|
||||
</div>
|
||||
</message-outgoing>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
src: null,
|
||||
downloadProgress: null
|
||||
downloadProgress: null,
|
||||
userInitiatedDownloadsOnly: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -21,14 +22,27 @@ export default {
|
|||
beforeDestroy() {
|
||||
this.loadAttachmentSource(null); // Release
|
||||
},
|
||||
computed: {
|
||||
fileName() {
|
||||
return util.getFileName(this.event);
|
||||
},
|
||||
fileSize() {
|
||||
return util.getFileSizeFormatted(this.event);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadAttachmentSource(event) {
|
||||
loadAttachmentSource(event, userInitiated = false) {
|
||||
if (this.src) {
|
||||
const objectUrl = this.src;
|
||||
this.src = null;
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
}
|
||||
if (event) {
|
||||
const fileSize = util.getFileSize(event);
|
||||
if (!userInitiated && (fileSize == 0 || fileSize > this.$config.maxSizeAutoDownloads)) {
|
||||
this.userInitiatedDownloadsOnly = true;
|
||||
return;
|
||||
}
|
||||
util
|
||||
.getAttachment(this.$matrix.matrixClient, event, (progress) => {
|
||||
this.downloadProgress = progress;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<message-incoming v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
|
||||
<audio controls :src="src">{{ $t("fallbacks.audio_file") }}</audio>
|
||||
<audio controls>{{ $t("fallbacks.audio_file") }}</audio>
|
||||
</message-incoming>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import attachmentMixin from "../attachmentMixin";
|
||||
import exportedAttachmentMixin from "./exportedAttachmentMixin";
|
||||
import MessageIncoming from "../MessageIncoming.vue";
|
||||
|
||||
export default {
|
||||
name: "MessageIncomingAudioExport",
|
||||
extends: MessageIncoming,
|
||||
mixins: [attachmentMixin],
|
||||
mixins: [exportedAttachmentMixin],
|
||||
components: { MessageIncoming },
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
51
src/components/messages/export/MessageIncomingFileExport.vue
Normal file
51
src/components/messages/export/MessageIncomingFileExport.vue
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<message-incoming v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
|
||||
<a style="text-decoration: none;color: currentColor" class="bubble" target="_blank" :href="href" >
|
||||
<div :class="{ 'thumbnail-item': true, 'preview': true, 'file-item': true }">
|
||||
<b>{{ $sanitize(fileName) }}</b>
|
||||
<div>{{ fileSize }}</div>
|
||||
</div>
|
||||
</a>
|
||||
</message-incoming>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import exportedAttachmentMixin from "./exportedAttachmentMixin";
|
||||
import MessageIncoming from "../MessageIncoming.vue";
|
||||
|
||||
export default {
|
||||
name: "MessageIncomingFileExport",
|
||||
extends: MessageIncoming,
|
||||
mixins: [exportedAttachmentMixin],
|
||||
components: { MessageIncoming },
|
||||
data() {
|
||||
return {
|
||||
href: ""
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
|
||||
.thumbnail-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.6rem;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
.v-icon {
|
||||
margin-bottom: 10px;
|
||||
color: currentColor;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<message-incoming v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
|
||||
<div class="bubble image-bubble">
|
||||
<v-responsive :aspect-ratio="16 / 9" :src="src">
|
||||
<video :src="src" controls class="w-100 h-100">
|
||||
<video controls class="w-100 h-100">
|
||||
{{ $t("fallbacks.video_file") }}
|
||||
</video>
|
||||
</v-responsive>
|
||||
|
|
@ -11,14 +11,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import attachmentMixin from "../attachmentMixin";
|
||||
import exportedAttachmentMixin from "./exportedAttachmentMixin";
|
||||
import MessageIncoming from "../MessageIncoming.vue";
|
||||
|
||||
export default {
|
||||
name: "MessageIncomingVideoExport",
|
||||
extends: MessageIncoming,
|
||||
components: { MessageIncoming },
|
||||
mixins: [attachmentMixin],
|
||||
mixins: [exportedAttachmentMixin],
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
<template>
|
||||
<message-outgoing v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
|
||||
<audio controls :src="src">{{ $t("fallbacks.audio_file") }}</audio>
|
||||
<audio controls>{{ $t("fallbacks.audio_file") }}</audio>
|
||||
</message-outgoing>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import attachmentMixin from "../attachmentMixin";
|
||||
import exportedAttachmentMixin from "./exportedAttachmentMixin";
|
||||
import MessageOutgoing from "../MessageOutgoing.vue";
|
||||
|
||||
export default {
|
||||
name: "MessageOutgoingAudioExport",
|
||||
extends: MessageOutgoing,
|
||||
components: { MessageOutgoing },
|
||||
mixins: [attachmentMixin],
|
||||
mixins: [exportedAttachmentMixin],
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
51
src/components/messages/export/MessageOutgoingFileExport.vue
Normal file
51
src/components/messages/export/MessageOutgoingFileExport.vue
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<message-outgoing v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
|
||||
<a style="text-decoration: none;color: currentColor" class="bubble" target="_blank" :href="href" >
|
||||
<div :class="{ 'thumbnail-item': true, 'preview': true, 'file-item': true }">
|
||||
<b>{{ $sanitize(fileName) }}</b>
|
||||
<div>{{ fileSize }}</div>
|
||||
</div>
|
||||
</a>
|
||||
</message-outgoing>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import exportedAttachmentMixin from "./exportedAttachmentMixin";
|
||||
import MessageOutgoing from "../MessageOutgoing.vue";
|
||||
|
||||
export default {
|
||||
name: "MessageOutgoingFileExport",
|
||||
extends: MessageOutgoing,
|
||||
mixins: [exportedAttachmentMixin],
|
||||
components: { MessageOutgoing },
|
||||
data() {
|
||||
return {
|
||||
href: ""
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
|
||||
.thumbnail-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.6rem;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
.v-icon {
|
||||
margin-bottom: 10px;
|
||||
color: currentColor;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<message-outgoing v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
|
||||
<div class="bubble image-bubble">
|
||||
<v-responsive :aspect-ratio="16 / 9" class="ma-0 pa-0">
|
||||
<video :src="src" controls class="w-100 h-100">
|
||||
<video controls class="w-100 h-100">
|
||||
{{ $t("fallbacks.video_file") }}
|
||||
</video>
|
||||
</v-responsive>
|
||||
|
|
@ -11,14 +11,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import attachmentMixin from "../attachmentMixin";
|
||||
import exportedAttachmentMixin from "./exportedAttachmentMixin";
|
||||
import MessageOutgoing from "../MessageOutgoing.vue";
|
||||
|
||||
export default {
|
||||
name: "MessageOutgoingVideoExport",
|
||||
extends: MessageOutgoing,
|
||||
components: { MessageOutgoing },
|
||||
mixins: [attachmentMixin],
|
||||
mixins: [exportedAttachmentMixin],
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
17
src/components/messages/export/exportedAttachmentMixin.js
Normal file
17
src/components/messages/export/exportedAttachmentMixin.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import util from "../../../plugins/utils";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
src: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fileName() {
|
||||
return util.getFileName(this.event);
|
||||
},
|
||||
fileSize() {
|
||||
return util.getFileSizeFormatted(this.event);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -74,7 +74,7 @@ export default {
|
|||
if (item.status !== this.sendStatuses.INITIAL) {
|
||||
return getItemPromise(++index);
|
||||
}
|
||||
const itemPromise = util.sendImage(this.$matrix.matrixClient, this.room.roomId, item.attachment, ({ loaded, total }) => {
|
||||
const itemPromise = util.sendFile(this.$matrix.matrixClient, this.room.roomId, item.attachment, ({ loaded, total }) => {
|
||||
if (loaded == total) {
|
||||
item.progress = 100;
|
||||
} else if (total > 0) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import dataUriToBuffer from "data-uri-to-buffer";
|
|||
import ImageResize from "image-resize";
|
||||
import { AutoDiscovery } from 'matrix-js-sdk';
|
||||
import User from '../models/user';
|
||||
const prettyBytes = require("pretty-bytes");
|
||||
|
||||
export const STATE_EVENT_ROOM_DELETION_NOTICE = "im.keanu.room_deletion_notice";
|
||||
export const STATE_EVENT_ROOM_DELETED = "im.keanu.room_deleted";
|
||||
|
|
@ -129,7 +130,7 @@ class Util {
|
|||
});
|
||||
}
|
||||
|
||||
getThumbnail(matrixClient, event, ignoredw, ignoredh) {
|
||||
getThumbnail(matrixClient, event, config, ignoredw, ignoredh) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const content = event.getContent();
|
||||
if (content.url != null) {
|
||||
|
|
@ -159,7 +160,7 @@ class Util {
|
|||
// true
|
||||
// );
|
||||
url = matrixClient.mxcUrlToHttp(file.url);
|
||||
} else if (content.file && content.file.url) {
|
||||
} else if (content.file && content.file.url && this.getFileSize(event) > 0 && this.getFileSize(event) < config.maxSizeAutoDownloads) {
|
||||
// No thumb, use real url
|
||||
file = content.file;
|
||||
url = matrixClient.mxcUrlToHttp(file.url);
|
||||
|
|
@ -348,7 +349,7 @@ class Util {
|
|||
});
|
||||
}
|
||||
|
||||
sendImage(matrixClient, roomId, file, onUploadProgress, threadRoot) {
|
||||
sendFile(matrixClient, roomId, file, onUploadProgress, threadRoot) {
|
||||
const uploadPromise = new UploadPromise(undefined);
|
||||
uploadPromise.wrappedPromise = new Promise((resolve, reject) => {
|
||||
var reader = new FileReader();
|
||||
|
|
@ -371,13 +372,13 @@ class Util {
|
|||
}
|
||||
|
||||
var description = file.name;
|
||||
var msgtype = 'm.image';
|
||||
if (file.type.startsWith("audio/")) {
|
||||
var msgtype = 'm.file';
|
||||
if (file.type.startsWith("image/")) {
|
||||
msgtype = 'm.image';
|
||||
} else if (file.type.startsWith("audio/")) {
|
||||
msgtype = 'm.audio';
|
||||
} else if (file.type.startsWith("video/")) {
|
||||
msgtype = 'm.video';
|
||||
} else if (file.type.startsWith("application/pdf")) {
|
||||
msgtype = 'm.file';
|
||||
}
|
||||
|
||||
const opts = {
|
||||
|
|
@ -913,7 +914,6 @@ class Util {
|
|||
link.download = event.getContent().body || this.$t("fallbacks.download_name");
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
setTimeout(function () {
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
|
|
@ -951,6 +951,68 @@ class Util {
|
|||
}
|
||||
return Promise.resolve(config.defaultBaseUrl);
|
||||
}
|
||||
|
||||
getMimeType(event) {
|
||||
const content = event.getContent();
|
||||
return (content.info && content.info.mimetype) ? content.info.mimetype : (content.file && content.file.mimetype) ? content.file.mimetype : "";
|
||||
}
|
||||
|
||||
getFileName(event) {
|
||||
const content = event.getContent();
|
||||
return (content.body || content.filename || "").toLowerCase();
|
||||
}
|
||||
|
||||
getFileExtension(event) {
|
||||
const fileName = this.getFileName(event);
|
||||
const parts = fileName.split(".");
|
||||
if (parts.length > 1) {
|
||||
return "." + parts[parts.length - 1].toLowerCase();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
getFileSize(event) {
|
||||
const content = event.getContent();
|
||||
if (content.info) {
|
||||
return content.info.size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
getFileSizeFormatted(event) {
|
||||
return prettyBytes(this.getFileSize(event));
|
||||
}
|
||||
|
||||
isFileTypeAPK(event) {
|
||||
const mime = this.getMimeType(event);
|
||||
if (mime === "application/vnd.android.package-archive" || this.getFileName(event).endsWith(".apk")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isFileTypeIPA(event) {
|
||||
if (this.getFileName(event).endsWith(".ipa")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isFileTypePDF(event) {
|
||||
const mime = this.getMimeType(event);
|
||||
if (mime === "application/pdf" || this.getFileName(event).endsWith(".pdf")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isFileTypeZip(event) {
|
||||
const mime = this.getMimeType(event);
|
||||
if (["application/zip", "application/x-zip-compressed", "multipart/x-zip"].includes(mime) || this.getFileName(event).endsWith(".zip")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export default new Util();
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ export default {
|
|||
if (json.useFullyQualifiedDMLinks == undefined) {
|
||||
Vue.set(config, "useFullyQualifiedDMLinks", true); // Default to true
|
||||
}
|
||||
if (!json.maxSizeAutoDownloads) {
|
||||
Vue.set(config, "maxSizeAutoDownloads", 10 * 1024 * 1024);
|
||||
}
|
||||
Vue.set(config, "loaded", true);
|
||||
|
||||
// Tell callback we are done loading runtime config
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue