Fix vite build release version
Also, make sure "export" strings are not removed (rename to "room_export") and import quotes in the QuoteView correctly.
This commit is contained in:
parent
4c364f93e6
commit
ec992bb14d
34 changed files with 621 additions and 284 deletions
7
src/assets/quotes/fr/quotes.json
Normal file
7
src/assets/quotes/fr/quotes.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"quotes": [
|
||||
|
||||
{
|
||||
"quote":"Je suis un programmeur","author":"Mickey Mouse"}
|
||||
]
|
||||
}
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
"tip_text": "سيتمكن الأعضاء من رؤية نتائج الاستطلاع بعد الإجابة. أغلق الاستطلاع عندما تنتهي لعرض النتائج على الجميع في الغرفة.",
|
||||
"create_poll_menu_option": "إنشاء استطلاع للرأي"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "تم التصدير بتاريخ {date}",
|
||||
"fetched_n_events": "تم جلب {count} من الأحداث",
|
||||
"fetched_n_of_total_events": "تم جلب {count} من أصل {total} من الأحداث",
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@
|
|||
"num_answered": "{count} টি উত্তর",
|
||||
"results_shared": "ফলাফল রুমে প্রকাশ করা হয়েছে।"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "{date} এ এক্সপোর্ট করা হয়েছে",
|
||||
"fetched_n_events": "{count} ইভেন্ট জড় করা হয়েছে",
|
||||
"fetched_n_of_total_events": "{total} টির মধ্যে {count} টি ইভেন্ট জড় করা হয়েছে",
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@
|
|||
"view_results": "གྲུབ་འབྲས་ལ་གཟིགས།",
|
||||
"results_shared": "གྲུབ་འབྲས་ཁ་བརྡ་ཁང་དུ་བརྒྱུད་སྤེལ་བྱེད་ངེས།"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "{date} ཉིན་ལ་ཕྱིར་འདྲེན་བྱས།",
|
||||
"export_filename": "{date} ཉིན་ཕྱིར་འདྲེན་བྱས་པའི་ཁ་བརྡ།",
|
||||
"processed_n_of_total_events": "བྱུང་བ་{total}ཁོངས་ནས་ལས་སྣོན་བྱས་ཟིན་པའི་སྨྱན་སྦྱོར་གྱི་གྲངས {count}",
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@
|
|||
"results_shared": "Die Ergebnisse werden dem Raum mitgeteilt.",
|
||||
"tip_text": "Die Mitglieder sehen die Umfrageergebnisse, nachdem sie geantwortet haben. Schließe die Umfrage, wenn du fertig bist, um die Ergebnisse für alle im Raum anzuzeigen."
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"fetched_n_of_total_events": "{count} von {total} Ereignissen geladen",
|
||||
"exported_date": "Am {date} exportiert",
|
||||
"processed_n_of_total_events": "Medien für {count} von {total} Ereignissen verarbeitet",
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@
|
|||
"num_answered": "{count} answers",
|
||||
"results_shared": "Results shared to the room."
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "Exported on {date}",
|
||||
"fetched_n_events": "Fetched {count} events",
|
||||
"fetched_n_of_total_events": "Fetched {count} of {total} events",
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@
|
|||
"not_supported": "La notificación aún no es compatible con dispositivos móviles",
|
||||
"periodicSync_new_msg_reminder": "Es posible que tengas nuevos mensajes"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"fetched_n_of_total_events": "{count} de {total} eventos recuperados",
|
||||
"export_filename": "Chat exportado el {date}",
|
||||
"exported_date": "Exportado el {date}",
|
||||
|
|
|
|||
|
|
@ -418,7 +418,7 @@
|
|||
"num_answered": "{count} respuestas",
|
||||
"results_shared": "Resultados compartidos a la sala."
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "Exportado el {date}",
|
||||
"fetched_n_events": "Se obtuvieron {count} eventos",
|
||||
"processed_n_of_total_events": "Medios procesados para {count} de {total} eventos",
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@
|
|||
"message_history_warning": "Advertencia: el historial completo de mensajes será visible para los nuevos participantes.",
|
||||
"message_history": "Historial de mensajes"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "Exportado en {date}",
|
||||
"fetched_n_events": "Eventos{count} recuperados",
|
||||
"fetched_n_of_total_events": "Se recuperaron {count} de {total} eventos",
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@
|
|||
"places": "مکان ها"
|
||||
}
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "خروجی گرفته شد در {date}",
|
||||
"fetched_n_events": "{count} رویداد بازیابی شد",
|
||||
"fetched_n_of_total_events": "{count} از {total} رویداد بازیابی شد",
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@
|
|||
"poll_submit": "تسلیم کردن",
|
||||
"num_answered": "{count} جواب"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "خروجی گرفته شد در {date}",
|
||||
"fetched_n_events": "{count} رویداد بازیابی شد",
|
||||
"fetched_n_of_total_events": "{count} از {total} رویداد بازیابی شد",
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@
|
|||
"download_name": "Íoslódáil",
|
||||
"original_text": "<buntéacs>"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "Onnmhairithe ar {date}",
|
||||
"fetched_n_events": "Imeachtaí {count} faighte",
|
||||
"fetched_n_of_total_events": "Fuarthas {count} de {total} imeacht",
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
"info_auto_join": "بەخێر بێیت بۆ {room}.\nتۆ وەکوو {you} بەشداری دەکەیت.",
|
||||
"change": "گۆڕین"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"export_filename": "چەت لە {date} دەرهێنرا",
|
||||
"exported_date": "لە {date} دەرهێنرا",
|
||||
"fetched_n_events": "{count} ڕووداو هێنرا",
|
||||
|
|
|
|||
|
|
@ -407,7 +407,7 @@
|
|||
"poll_submit": "ສົ່ງ",
|
||||
"num_answered": "{count} ຄຳຕອບ"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "ສົ່ງອອກໃນວັນທີ {date}",
|
||||
"fetched_n_events": "ໄດ້ຮັບ {count} ກິດຈະກຳ",
|
||||
"fetched_n_of_total_events": "ໄດ້ຮັບ {count}ຈາກ {total} ກິດຈະກຳ",
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@
|
|||
"num_answered": "အဖြေ {count}ခု",
|
||||
"results_shared": "ရလဒ်များကို အခန်းသို့ မျှဝေခဲ့သည်။"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"fetched_n_events": "ပွဲအစီအစဉ် {count} ခု ရရှိခဲ့သည်",
|
||||
"fetched_n_of_total_events": "ပွဲအစီအစဉ် {total} တွင် {count} ခု ရရှိခဲ့သည်",
|
||||
"processed_n_of_total_events": "ပွဲအစီအစဉ် {total} တွင် {count} ခုအတွက် မီဒီယာကို စီမံဆောင်ရွက်ခဲ့သည်",
|
||||
|
|
|
|||
|
|
@ -421,7 +421,7 @@
|
|||
"original_text": "<original text>",
|
||||
"download_name": "ډانلوډ"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "{date} نېټه خروجې اخیستل شوې ده",
|
||||
"fetched_n_events": "{count} پېښې بېرته تر لاسه شوې دي",
|
||||
"fetched_n_of_total_events": "له {total} پېښو څخه {count} یې بېرته تر لاسه شوې دي",
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@
|
|||
"view_results": "Ver os resultados",
|
||||
"results_shared": "Resultados compartilhados com a sala."
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "Foi exportado em {date}",
|
||||
"fetched_n_events": "{count} eventos buscados",
|
||||
"fetched_n_of_total_events": "Obteve {count} de {total} eventos",
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@
|
|||
"channel_topic": "Опишите его",
|
||||
"error_channel": "Не удалось создать канал"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"fetched_n_events": "Найдено {count} событий",
|
||||
"fetched_n_of_total_events": "Получено {count} из {total} событий",
|
||||
"export_filename": "Экспортированный чат {date}",
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@
|
|||
"close_tab": "Tarayıcı sekmesini kapat",
|
||||
"view_other_rooms": "Diğer odaları görüntüle"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"exported_date": "{date} tarihinde dışarı aktarıldı",
|
||||
"fetched_n_events": "{count} eylem toplandı",
|
||||
"fetched_n_of_total_events": "{total} eylemden {count}tanesi toplandı",
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@
|
|||
"answer_required": "答案不能为空。 请输入一些文本或删除此选项。",
|
||||
"num_answered": "{count} 答案"
|
||||
},
|
||||
"export": {
|
||||
"room_export": {
|
||||
"fetched_n_events": "获取了 {count} 个事件",
|
||||
"exported_date": "于 {date} 导出",
|
||||
"fetched_n_of_total_events": "已获取 {count} 个事件,共 {total} 个事件",
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ import MessageOperationsBottomSheet from "./MessageOperationsBottomSheet";
|
|||
import StickerPickerBottomSheet from "./StickerPickerBottomSheet";
|
||||
import UserProfileDialog from "./UserProfileDialog.vue"
|
||||
import BottomSheet from "./BottomSheet.vue";
|
||||
import ImageResize from "image-resize";
|
||||
import imageResize from "image-resize";
|
||||
import CreatePollDialog from "./CreatePollDialog.vue";
|
||||
import chatMixin, { ROOM_READ_MARKER_EVENT_PLACEHOLDER } from "./chatMixin";
|
||||
import sendAttachmentsMixin from "./sendAttachmentsMixin";
|
||||
|
|
@ -394,8 +394,7 @@ import roomMembersMixin from "./roomMembersMixin";
|
|||
import PurgeRoomDialog from "../components/PurgeRoomDialog";
|
||||
import MessageErrorHandler from "./MessageErrorHandler";
|
||||
import MessageOperationsChannel from './messages/channel/MessageOperationsChannel.vue';
|
||||
import sizeOf from "image-size";
|
||||
import dataUriToBuffer from "data-uri-to-buffer";
|
||||
import { imageSize } from "image-size";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import RoomExport from "./RoomExport.vue";
|
||||
import { VEmojiPicker } from 'v-emoji-picker';
|
||||
|
|
@ -1470,8 +1469,9 @@ export default {
|
|||
fileObj.actualSize = file.size;
|
||||
fileObj.actualFile = file
|
||||
try {
|
||||
fileObj.dimensions = sizeOf(dataUriToBuffer(evt.target.result));
|
||||
|
||||
const buffer = Uint8Array.from(window.atob(evt.target.result.replace(/^data[^,]+,/,'')), v => v.charCodeAt(0));
|
||||
fileObj.dimensions = imageSize(buffer);
|
||||
|
||||
// Need to resize?
|
||||
const w = fileObj.dimensions.width;
|
||||
const h = fileObj.dimensions.height;
|
||||
|
|
@ -1479,14 +1479,12 @@ export default {
|
|||
var aspect = w / h;
|
||||
var newWidth = parseInt((w > h ? 640 : 640 * aspect).toFixed());
|
||||
var newHeight = parseInt((w > h ? 640 / aspect : 640).toFixed());
|
||||
var imageResize = new ImageResize({
|
||||
imageResize(evt.target.result, {
|
||||
format: "png",
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
outputType: "blob",
|
||||
});
|
||||
imageResize
|
||||
.play(evt.target.result)
|
||||
})
|
||||
.then((img) => {
|
||||
Vue.set(
|
||||
fileObj,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import RoomList from "../components/RoomList";
|
||||
import RoomList from "../components/RoomList.vue";
|
||||
import YouAre from "../components/YouAre.vue";
|
||||
import logoMixin from "../components/logoMixin";
|
||||
export default {
|
||||
|
|
|
|||
|
|
@ -99,21 +99,26 @@ export default {
|
|||
};
|
||||
},
|
||||
mounted() {
|
||||
var quotes;
|
||||
try {
|
||||
quotes = require("@/assets/quotes/" + this.$i18n.locale + "/quotes");
|
||||
const quotes = import.meta.glob('@/assets/quotes/*/*.json', {eager: false});
|
||||
let quoteImport = undefined;
|
||||
Object.keys(quotes).forEach(path => {
|
||||
// Remove"./"
|
||||
const parts = path.split("/");
|
||||
const locale = parts[parts.length - 2];
|
||||
if (locale == this.$i18n.locale) {
|
||||
quoteImport = quotes[path];
|
||||
}
|
||||
});
|
||||
if (quoteImport) {
|
||||
quoteImport().then((quotes) => this.selectQuote(quotes));
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("No quotes for language");
|
||||
quotes = undefined;
|
||||
}
|
||||
if (!quotes) {
|
||||
quotes = require("@/assets/quotes/en/quotes"); // Default fallback
|
||||
}
|
||||
const n = quotes.quotes.length;
|
||||
const quote = quotes.quotes[Math.floor(Math.random() * n)];
|
||||
this.quote = quote.quote;
|
||||
this.author = quote.author;
|
||||
this.mounted = true;
|
||||
import("@/assets/quotes/en/quotes") // Default fallback
|
||||
.then((quotes) => this.selectQuote(quotes));
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
|
@ -127,6 +132,17 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
selectQuote(quotes) {
|
||||
const n = quotes.quotes.length;
|
||||
if (n > 0) {
|
||||
const quote = quotes.quotes[Math.floor(Math.random() * n)];
|
||||
this.quote = quote.quote;
|
||||
this.author = quote.author;
|
||||
this.mounted = true;
|
||||
} else {
|
||||
this.mounted = true;
|
||||
}
|
||||
},
|
||||
closeBrowserTab() {
|
||||
window.location.href = "about:blank";
|
||||
},
|
||||
|
|
|
|||
|
|
@ -94,10 +94,10 @@ import BottomSheet from "./BottomSheet.vue";
|
|||
import CreatePollDialog from "./CreatePollDialog.vue";
|
||||
import chatMixin from "./chatMixin";
|
||||
import util from "../plugins/utils";
|
||||
import JSZip from "jszip";
|
||||
import { saveAs } from "file-saver";
|
||||
import { EventTimelineSet } from "matrix-js-sdk";
|
||||
import axios from 'axios';
|
||||
import "../services/jszip.min";
|
||||
import "../services/filesaver.cjs";
|
||||
|
||||
export default {
|
||||
name: "RoomExport",
|
||||
|
|
@ -168,7 +168,7 @@ export default {
|
|||
},
|
||||
watch: {
|
||||
processedEvents() {
|
||||
this.statusText = this.$t("export.processed_n_of_total_events", {
|
||||
this.statusText = this.$t("room_export.processed_n_of_total_events", {
|
||||
count: this.processedEvents,
|
||||
total: this.totalEvents,
|
||||
});
|
||||
|
|
@ -176,7 +176,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
exportDate() {
|
||||
return this.$t("export.exported_date", { date: util.formatDay(Date.now().valueOf()) });
|
||||
return this.$t("room_export.exported_date", { date: util.formatDay(Date.now().valueOf()) });
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -214,13 +214,13 @@ export default {
|
|||
if (result.chunk.length === 0) break;
|
||||
if (nToFetch != null) {
|
||||
nToFetch -= result.chunk.length;
|
||||
this.statusText = this.$t("export.fetched_n_of_total_events", {
|
||||
this.statusText = this.$t("room_export.fetched_n_of_total_events", {
|
||||
count: this.totalEvents - nToFetch,
|
||||
total: this.totalEvents,
|
||||
});
|
||||
} else {
|
||||
this.totalEvents += result.chunk.length;
|
||||
this.statusText = this.$t("export.fetched_n_events", { count: this.totalEvents });
|
||||
this.statusText = this.$t("room_export.fetched_n_events", { count: this.totalEvents });
|
||||
}
|
||||
fetchedEvents.push(...result.chunk.map(eventMapper));
|
||||
|
||||
|
|
@ -254,7 +254,7 @@ export default {
|
|||
.then((events) => {
|
||||
// Create a timeline and add the events to that, so that relations etc are aggregated correctly!
|
||||
this.timelineSet = new EventTimelineSet(null, { unstableClientRelationAggregation: true });
|
||||
this.timelineSet.addEventsToTimeline(events.reverse(), true, this.timelineSet.getLiveTimeline(), "");
|
||||
this.timelineSet.addEventsToTimeline(events.reverse(), true, false, this.timelineSet.getLiveTimeline(), "");
|
||||
this.events = events;
|
||||
|
||||
// Need to set thread root events and replyEvents so stuff is rendered correctly.
|
||||
|
|
@ -538,7 +538,7 @@ export default {
|
|||
zip.generateAsync({ type: "blob" }).then((content) => {
|
||||
saveAs(
|
||||
content,
|
||||
this.$t("export.export_filename", { date: util.formatDay(Date.now().valueOf()) }) + ".zip"
|
||||
this.$t("room_export.export_filename", { date: util.formatDay(Date.now().valueOf()) }) + ".zip"
|
||||
);
|
||||
this.status = "";
|
||||
this.$emit("close");
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import 'vue-resize/dist/vue-resize.css';
|
|||
import VueClipboard from 'vue-clipboard2'
|
||||
import VueSanitize from "vue-sanitize";
|
||||
import createVuetify from './plugins/vuetify';
|
||||
import { Buffer } from 'buffer/'
|
||||
globalThis.Buffer = Buffer;
|
||||
|
||||
var defaultOptions = VueSanitize.defaults;
|
||||
defaultOptions.disallowedTagsMode = "recursiveEscape";
|
||||
|
|
|
|||
|
|
@ -1,21 +1,16 @@
|
|||
import axios from 'axios';
|
||||
import * as ContentHelpers from "matrix-js-sdk/lib/content-helpers";
|
||||
import dataUriToBuffer from "data-uri-to-buffer";
|
||||
import ImageResize from "image-resize";
|
||||
import imageResize from "image-resize";
|
||||
import { AutoDiscovery } from 'matrix-js-sdk';
|
||||
import User from '../models/user';
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import Hammer from "hammerjs";
|
||||
import { Thread } from 'matrix-js-sdk/lib/models/thread';
|
||||
import sizeOf from "image-size";
|
||||
import { imageSize } 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_DELETED = "im.keanu.room_deleted";
|
||||
|
|
@ -27,9 +22,6 @@ export const ROOM_TYPE_CHANNEL = "im.keanu.room_type_channel";
|
|||
|
||||
export const STATE_EVENT_ROOM_TYPE = "im.keanu.room_type";
|
||||
|
||||
|
||||
var sha256 = jssha256.sha256;
|
||||
|
||||
// Install extended localized format
|
||||
dayjs.extend(localizedFormat)
|
||||
dayjs.extend(duration);
|
||||
|
|
@ -97,11 +89,27 @@ class Util {
|
|||
var file = null;
|
||||
let decrypt = true;
|
||||
if (content.url != null) {
|
||||
url = matrixClient.mxcUrlToHttp(content.url, undefined, undefined, undefined, undefined, undefined, useAuthedMedia);
|
||||
url = matrixClient.mxcUrlToHttp(
|
||||
content.url,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
useAuthedMedia
|
||||
);
|
||||
decrypt = false;
|
||||
} else if (content.file && content.file.url) {
|
||||
file = content.file;
|
||||
url = matrixClient.mxcUrlToHttp(file.url, undefined, undefined, undefined, undefined, undefined, useAuthedMedia);
|
||||
url = matrixClient.mxcUrlToHttp(
|
||||
file.url,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
useAuthedMedia
|
||||
);
|
||||
}
|
||||
|
||||
if (url == null) {
|
||||
|
|
@ -123,7 +131,7 @@ class Util {
|
|||
},
|
||||
})
|
||||
.then((response) => {
|
||||
return decrypt ? this.decryptIfNeeded(file, response) : Promise.resolve({buffer:response.data});
|
||||
return decrypt ? this.decryptIfNeeded(file, response) : Promise.resolve({ buffer: response.data });
|
||||
})
|
||||
.then((bytes) => {
|
||||
if (asBlob) {
|
||||
|
|
@ -151,7 +159,15 @@ class Util {
|
|||
var file = null;
|
||||
let decrypt = true;
|
||||
if (content.url != null) {
|
||||
url = matrixClient.mxcUrlToHttp(content.url, undefined, undefined, undefined, undefined, undefined, useAuthedMedia);
|
||||
url = matrixClient.mxcUrlToHttp(
|
||||
content.url,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
useAuthedMedia
|
||||
);
|
||||
decrypt = false;
|
||||
} else if (content && content.info && content.info.thumbnail_file && content.info.thumbnail_file.url) {
|
||||
file = content.info.thumbnail_file;
|
||||
|
|
@ -167,7 +183,15 @@ class Util {
|
|||
// "scale",
|
||||
// true
|
||||
// );
|
||||
url = matrixClient.mxcUrlToHttp(file.url, undefined, undefined, undefined, undefined, undefined, useAuthedMedia);
|
||||
url = matrixClient.mxcUrlToHttp(
|
||||
file.url,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
useAuthedMedia
|
||||
);
|
||||
} else if (
|
||||
content.file &&
|
||||
content.file.url &&
|
||||
|
|
@ -176,7 +200,15 @@ class Util {
|
|||
) {
|
||||
// No thumb, use real url
|
||||
file = content.file;
|
||||
url = matrixClient.mxcUrlToHttp(file.url, undefined, undefined, undefined, undefined, undefined, useAuthedMedia);
|
||||
url = matrixClient.mxcUrlToHttp(
|
||||
file.url,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
useAuthedMedia
|
||||
);
|
||||
}
|
||||
|
||||
if (url == null) {
|
||||
|
|
@ -185,11 +217,14 @@ class Util {
|
|||
}
|
||||
|
||||
axios
|
||||
.get(url, { responseType: "arraybuffer", headers: {
|
||||
Authorization: `Bearer ${matrixClient.getAccessToken()}`,
|
||||
}})
|
||||
.get(url, {
|
||||
responseType: "arraybuffer",
|
||||
headers: {
|
||||
Authorization: `Bearer ${matrixClient.getAccessToken()}`,
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
return decrypt ? this.decryptIfNeeded(file, response) : Promise.resolve({buffer:response.data});
|
||||
return decrypt ? this.decryptIfNeeded(file, response) : Promise.resolve({ buffer: response.data });
|
||||
})
|
||||
.then((bytes) => {
|
||||
resolve(URL.createObjectURL(new Blob([bytes.buffer], { type: file.mimetype })));
|
||||
|
|
@ -201,27 +236,34 @@ class Util {
|
|||
});
|
||||
}
|
||||
|
||||
b64toBuffer(val) {
|
||||
const baseValue = val.replaceAll("-", "+").replaceAll("_", "/");
|
||||
return Buffer.from(baseValue, "base64");
|
||||
}
|
||||
|
||||
decryptIfNeeded(file, response) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var key = decode(file.key.k);
|
||||
var iv = decode(file.iv);
|
||||
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(iv));
|
||||
const key = this.b64toBuffer(file.key.k);
|
||||
const iv = this.b64toBuffer(file.iv);
|
||||
const originalHash = this.b64toBuffer(file.hashes.sha256);
|
||||
|
||||
var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(iv));
|
||||
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
|
||||
var hash = new Uint8Array(sha256.create().update(data).arrayBuffer());
|
||||
const originalHash = decode(file.hashes.sha256);
|
||||
if (Buffer.compare(Buffer.from(hash), Buffer.from(originalHash.buffer)) != 0) {
|
||||
reject("Hashes don't match!");
|
||||
return;
|
||||
}
|
||||
|
||||
var decryptedBytes = aesCtr.decrypt(data);
|
||||
resolve(decryptedBytes);
|
||||
crypto.subtle
|
||||
.digest("SHA-256", data)
|
||||
.then((hash) => {
|
||||
// Calculate sha256 and compare hashes
|
||||
if (Buffer.compare(Buffer.from(hash), originalHash) != 0) {
|
||||
reject("Hashes don't match!");
|
||||
return;
|
||||
}
|
||||
var decryptedBytes = aesCtr.decrypt(data);
|
||||
resolve(decryptedBytes);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject("Failed to calculate hash value");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -466,7 +508,7 @@ class Util {
|
|||
kty: "oct",
|
||||
key_ops: ["encrypt", "decrypt"],
|
||||
alg: "A256CTR",
|
||||
k: encode(key),
|
||||
k: key.toString("base64").replaceAll(/\//g, "_").replaceAll(/\+/g, "-"),
|
||||
ext: true,
|
||||
};
|
||||
|
||||
|
|
@ -814,8 +856,8 @@ class Util {
|
|||
return matrixClient.sendStateEvent(roomId, "m.room.avatar", messageContent);
|
||||
})
|
||||
.then((result) => {
|
||||
resolve(matrixClient.mxcUrlToHttp(messageContent.url, 80, 80, "scale", undefined, undefined, true))
|
||||
// resolve(result);
|
||||
resolve(matrixClient.mxcUrlToHttp(messageContent.url, 80, 80, "scale", undefined, undefined, true));
|
||||
// resolve(result);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
|
|
@ -837,7 +879,10 @@ class Util {
|
|||
try {
|
||||
var image = e.target.result;
|
||||
|
||||
var dimens = sizeOf(dataUriToBuffer(e.target.result));
|
||||
const buffer = Uint8Array.from(window.atob(e.target.result.replace(/^data[^,]+,/, "")), (v) =>
|
||||
v.charCodeAt(0)
|
||||
);
|
||||
var dimens = imageSize(buffer);
|
||||
|
||||
// Need to resize?
|
||||
const w = dimens.width;
|
||||
|
|
@ -846,14 +891,12 @@ class Util {
|
|||
var aspect = w / h;
|
||||
var newWidth = parseInt((w > h ? 640 : 640 * aspect).toFixed());
|
||||
var newHeight = parseInt((w > h ? 640 / aspect : 640).toFixed());
|
||||
var imageResize = new ImageResize({
|
||||
imageResize(image, {
|
||||
format: "png",
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
outputType: "blob",
|
||||
});
|
||||
imageResize
|
||||
.play(event.target)
|
||||
})
|
||||
.then((img) => {
|
||||
var resizedImageFile = new File([img], file.name, {
|
||||
type: img.type,
|
||||
|
|
|
|||
171
src/services/filesaver.cjs
Normal file
171
src/services/filesaver.cjs
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* FileSaver.js
|
||||
* A saveAs() FileSaver implementation.
|
||||
*
|
||||
* By Eli Grey, http://eligrey.com
|
||||
*
|
||||
* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
|
||||
* source : http://purl.eligrey.com/github/FileSaver.js
|
||||
*/
|
||||
|
||||
// The one and only way of getting global scope in all environments
|
||||
// https://stackoverflow.com/q/3277182/1008999
|
||||
var _global = typeof window === 'object' && window.window === window
|
||||
? window : typeof self === 'object' && self.self === self
|
||||
? self : typeof global === 'object' && global.global === global
|
||||
? global
|
||||
: this
|
||||
|
||||
function bom (blob, opts) {
|
||||
if (typeof opts === 'undefined') opts = { autoBom: false }
|
||||
else if (typeof opts !== 'object') {
|
||||
console.warn('Deprecated: Expected third argument to be a object')
|
||||
opts = { autoBom: !opts }
|
||||
}
|
||||
|
||||
// prepend BOM for UTF-8 XML and text/* types (including HTML)
|
||||
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
|
||||
if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
|
||||
return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type })
|
||||
}
|
||||
return blob
|
||||
}
|
||||
|
||||
function download (url, name, opts) {
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open('GET', url)
|
||||
xhr.responseType = 'blob'
|
||||
xhr.onload = function () {
|
||||
saveAs(xhr.response, name, opts)
|
||||
}
|
||||
xhr.onerror = function () {
|
||||
console.error('could not download file')
|
||||
}
|
||||
xhr.send()
|
||||
}
|
||||
|
||||
function corsEnabled (url) {
|
||||
var xhr = new XMLHttpRequest()
|
||||
// use sync to avoid popup blocker
|
||||
xhr.open('HEAD', url, false)
|
||||
try {
|
||||
xhr.send()
|
||||
} catch (e) {}
|
||||
return xhr.status >= 200 && xhr.status <= 299
|
||||
}
|
||||
|
||||
// `a.click()` doesn't work for all browsers (#465)
|
||||
function click (node) {
|
||||
try {
|
||||
node.dispatchEvent(new MouseEvent('click'))
|
||||
} catch (e) {
|
||||
var evt = document.createEvent('MouseEvents')
|
||||
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
|
||||
20, false, false, false, false, 0, null)
|
||||
node.dispatchEvent(evt)
|
||||
}
|
||||
}
|
||||
|
||||
// Detect WebView inside a native macOS app by ruling out all browsers
|
||||
// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
|
||||
// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
|
||||
var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent)
|
||||
|
||||
var saveAs = _global.saveAs || (
|
||||
// probably in some web worker
|
||||
(typeof window !== 'object' || window !== _global)
|
||||
? function saveAs () { /* noop */ }
|
||||
|
||||
// Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
|
||||
: ('download' in HTMLAnchorElement.prototype && !isMacOSWebView)
|
||||
? function saveAs (blob, name, opts) {
|
||||
var URL = _global.URL || _global.webkitURL
|
||||
var a = document.createElement('a')
|
||||
name = name || blob.name || 'download'
|
||||
|
||||
a.download = name
|
||||
a.rel = 'noopener' // tabnabbing
|
||||
|
||||
// TODO: detect chrome extensions & packaged apps
|
||||
// a.target = '_blank'
|
||||
|
||||
if (typeof blob === 'string') {
|
||||
// Support regular links
|
||||
a.href = blob
|
||||
if (a.origin !== location.origin) {
|
||||
corsEnabled(a.href)
|
||||
? download(blob, name, opts)
|
||||
: click(a, a.target = '_blank')
|
||||
} else {
|
||||
click(a)
|
||||
}
|
||||
} else {
|
||||
// Support blobs
|
||||
a.href = URL.createObjectURL(blob)
|
||||
setTimeout(function () { URL.revokeObjectURL(a.href) }, 4E4) // 40s
|
||||
setTimeout(function () { click(a) }, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Use msSaveOrOpenBlob as a second approach
|
||||
: 'msSaveOrOpenBlob' in navigator
|
||||
? function saveAs (blob, name, opts) {
|
||||
name = name || blob.name || 'download'
|
||||
|
||||
if (typeof blob === 'string') {
|
||||
if (corsEnabled(blob)) {
|
||||
download(blob, name, opts)
|
||||
} else {
|
||||
var a = document.createElement('a')
|
||||
a.href = blob
|
||||
a.target = '_blank'
|
||||
setTimeout(function () { click(a) })
|
||||
}
|
||||
} else {
|
||||
navigator.msSaveOrOpenBlob(bom(blob, opts), name)
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to using FileReader and a popup
|
||||
: function saveAs (blob, name, opts, popup) {
|
||||
// Open a popup immediately do go around popup blocker
|
||||
// Mostly only available on user interaction and the fileReader is async so...
|
||||
popup = popup || open('', '_blank')
|
||||
if (popup) {
|
||||
popup.document.title =
|
||||
popup.document.body.innerText = 'downloading...'
|
||||
}
|
||||
|
||||
if (typeof blob === 'string') return download(blob, name, opts)
|
||||
|
||||
var force = blob.type === 'application/octet-stream'
|
||||
var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari
|
||||
var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent)
|
||||
|
||||
if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && typeof FileReader !== 'undefined') {
|
||||
// Safari doesn't allow downloading of blob URLs
|
||||
var reader = new FileReader()
|
||||
reader.onloadend = function () {
|
||||
var url = reader.result
|
||||
url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;')
|
||||
if (popup) popup.location.href = url
|
||||
else location = url
|
||||
popup = null // reverse-tabnabbing #460
|
||||
}
|
||||
reader.readAsDataURL(blob)
|
||||
} else {
|
||||
var URL = _global.URL || _global.webkitURL
|
||||
var url = URL.createObjectURL(blob)
|
||||
if (popup) popup.location = url
|
||||
else location.href = url
|
||||
popup = null // reverse-tabnabbing #460
|
||||
setTimeout(function () { URL.revokeObjectURL(url) }, 4E4) // 40s
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
_global.saveAs = saveAs.saveAs = saveAs
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = saveAs;
|
||||
}
|
||||
13
src/services/jszip.min.js
vendored
Normal file
13
src/services/jszip.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,3 @@
|
|||
import olm from "@matrix-org/olm/olm";
|
||||
global.Olm = olm;
|
||||
import * as sdk 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";
|
||||
|
|
@ -530,7 +528,7 @@ export default {
|
|||
|
||||
const resolvedId =
|
||||
this.currentRoomId && this.currentRoomId.startsWith("#")
|
||||
? this.matrixClient.resolveRoomAlias(this.currentRoomId).then((r) => r.room_id)
|
||||
? this.matrixClient.getRoomIdForAlias(this.currentRoomId).then((r) => r.room_id)
|
||||
: Promise.resolve(this.currentRoomId);
|
||||
resolvedId
|
||||
.then((roomId) => {
|
||||
|
|
@ -690,7 +688,7 @@ export default {
|
|||
if (roomIdOrAlias && this.matrixClient) {
|
||||
try {
|
||||
const resolvedRoomId = roomIdOrAlias.startsWith("#")
|
||||
? this.matrixClient.resolveRoomAlias(roomIdOrAlias).then((res) => res.room_id)
|
||||
? this.matrixClient.getRoomIdForAlias(roomIdOrAlias).then((res) => res.room_id)
|
||||
: Promise.resolve(roomIdOrAlias);
|
||||
return resolvedRoomId.then((roomId) => {
|
||||
return this.matrixClient.getJoinedRooms().then((rooms) => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import VuexPersist from 'vuex-persist'
|
||||
import VuexPersistence from 'vuex-persist'
|
||||
|
||||
Vue.use(Vuex)
|
||||
const USER = `convene_${ window.location.hostname }_user`
|
||||
|
|
@ -32,7 +32,7 @@ const persistUserPlugin = store => {
|
|||
}
|
||||
|
||||
|
||||
const vuexPersistLocalStorage = new VuexPersist({
|
||||
const vuexPersistLocalStorage = new VuexPersistence({
|
||||
key: SETTINGS,
|
||||
storage: localStorage,
|
||||
reducer: state => {
|
||||
|
|
@ -49,7 +49,7 @@ const vuexPersistLocalStorage = new VuexPersist({
|
|||
}
|
||||
})
|
||||
|
||||
const vuexPersistSessionStorage = new VuexPersist({
|
||||
const vuexPersistSessionStorage = new VuexPersistence({
|
||||
key: SETTINGS,
|
||||
storage: sessionStorage,
|
||||
reducer: state => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue