Compare commits
19 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cd8ce0a60 | ||
|
|
b1b793edc4 | ||
| 102fb79089 | |||
|
|
220bc2038b | ||
|
|
7870a7c68f | ||
|
|
fe95d53cd1 | ||
|
|
0b61def352 | ||
|
|
9304912ce1 | ||
|
|
2a7927db72 | ||
|
|
d5917dcdb5 | ||
|
|
1fb26d6c84 | ||
|
|
bda93a71e4 | ||
|
|
47ebc55f80 | ||
|
|
f41aec59bb | ||
|
|
edc1824130 | ||
|
|
f7032f659a | ||
|
|
af96e3db5f | ||
|
|
7cec56fb50 | ||
|
|
6bb0189078 |
18 changed files with 891 additions and 202 deletions
25
.github/workflows/ci.yaml
vendored
25
.github/workflows/ci.yaml
vendored
|
|
@ -1,25 +0,0 @@
|
|||
name: ci
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
build_and_publish:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: ghcr.io/catthehacker/ubuntu:runner-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- run: npm install
|
||||
- run: sudo apt update && sudo apt install -y tar
|
||||
- run: export NODE_OPTIONS=--openssl-legacy-provider
|
||||
- run: npm run build
|
||||
- run: tar cvzf keanu-weblite.tar.gz dist/*
|
||||
- run: |
|
||||
curl -H "Authorization: token ${{ secrets.PACKAGE_TOKEN }}" -X DELETE "https://guardianproject.dev/api/packages/butter/generic/keanu-weblite/latest"
|
||||
- run: |
|
||||
curl -H "Authorization: token ${{ secrets.PACKAGE_TOKEN }}" -X PUT --upload-file keanu-weblite.tar.gz https://guardianproject.dev/api/packages/butter/generic/keanu-weblite/latest/keanu-weblite.tar.gz
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "keanuapp-weblite",
|
||||
"version": "0.1.91",
|
||||
"version": "0.1.96",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "keanuapp-weblite",
|
||||
"version": "0.1.90",
|
||||
"version": "0.1.95",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
|
|
|||
172
public/offline.html
Normal file
172
public/offline.html
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
|
||||
|
||||
<script lang="javascript">
|
||||
const STORE_KEY_USER = `convene_${window.location.hostname}_user`;
|
||||
const STORE_KEY_SETTINGS = `convene_${window.location.hostname}_settings`;
|
||||
|
||||
let windowProxy = null;
|
||||
|
||||
function updateNetworkState() {
|
||||
if (navigator.onLine) {
|
||||
document.body.classList.remove("offline");
|
||||
} else {
|
||||
document.body.classList.add("offline");
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("load", (event) => {
|
||||
updateNetworkState();
|
||||
});
|
||||
|
||||
window.addEventListener("offline", (e) => {
|
||||
updateNetworkState();
|
||||
});
|
||||
|
||||
window.addEventListener("online", (e) => {
|
||||
updateNetworkState();
|
||||
});
|
||||
|
||||
function go() {
|
||||
if (navigator.onLine) {
|
||||
let url = "<!--MIRROR_URL-->" + window.location.pathname + "#/?migrate=1";
|
||||
if (!url.includes("://")) {
|
||||
url = window.location.protocol + "//" + url;
|
||||
}
|
||||
windowProxy = window.open(url, "_blank");
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", function (e) {
|
||||
// if (e.origin !== origin) { // TODO - What should we check here?
|
||||
// return
|
||||
// }
|
||||
try {
|
||||
const data = JSON.parse(e.data);
|
||||
if (data !== null && data.cmd == "getMigrationData") {
|
||||
let migrationInfo = {
|
||||
};
|
||||
if (window.localStorage.getItem(STORE_KEY_USER)) {
|
||||
migrationInfo.type = "local";
|
||||
migrationInfo.user = window.localStorage.getItem(STORE_KEY_USER);
|
||||
migrationInfo.settings = window.localStorage.getItem(STORE_KEY_SETTINGS);
|
||||
} else {
|
||||
migrationInfo.type = "session";
|
||||
migrationInfo.user = window.sessionStorage.getItem(STORE_KEY_USER);
|
||||
migrationInfo.settings = window.sessionStorage.getItem(STORE_KEY_SETTINGS);
|
||||
}
|
||||
if (migrationInfo.user) {
|
||||
try {
|
||||
const user = JSON.parse(migrationInfo.user);
|
||||
delete user.access_token;
|
||||
delete user.device_id;
|
||||
migrationInfo.user = JSON.stringify(user);
|
||||
} catch (error) { }
|
||||
}
|
||||
event.source.postMessage(JSON.stringify(migrationInfo), event.origin);
|
||||
} else if (data !== null && data.cmd == "migrationDone") {
|
||||
if (windowProxy) {
|
||||
windowProxy.close();
|
||||
let url = "<!--MIRROR_URL-->" + window.location.pathname + "#/";
|
||||
if (!url.includes("://")) {
|
||||
url = window.location.protocol + "//" + url;
|
||||
}
|
||||
window.location.href = url;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: "Inter", sans-serif;
|
||||
font-weight: 700;
|
||||
font-style: Bold;
|
||||
font-size: 16px;
|
||||
leading-trim: NONE;
|
||||
line-height: 125%;
|
||||
letter-spacing: 0.4px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
|
||||
}
|
||||
|
||||
.message {
|
||||
font-family: "Inter", sans-serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-size: 16px;
|
||||
leading-trim: NONE;
|
||||
line-height: 125%;
|
||||
letter-spacing: 0.4px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 16px;
|
||||
height: 40px;
|
||||
border: 1px solid black;
|
||||
border-radius: 20px;
|
||||
background-color: white;
|
||||
padding: 8px 16px;
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 600;
|
||||
font-style: SemiBold;
|
||||
font-size: 14px;
|
||||
leading-trim: NONE;
|
||||
line-height: 18px;
|
||||
letter-spacing: 0.34px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
opacity: 0.8;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.offline-message {
|
||||
margin-top: 40px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.offline .offline-message {
|
||||
visibility: visible;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="title"><!--OFFLINE_TITLE_START-->Having trouble connecting?<!--OFFLINE_TITLE_END--></div>
|
||||
<div class="message"><!--OFFLINE_MESSAGE_START-->Redirect to an alternate link to join the room<!--OFFLINE_MESSAGE_END--></div>
|
||||
<button onclick="go()"><!--OFFLINE_REDIRECT_START-->Redirect me<!--OFFLINE_REDIRECT_END--></button>
|
||||
<div class="offline-message">You are offline</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
100
public/sw.js
100
public/sw.js
|
|
@ -1,5 +1,19 @@
|
|||
var periodicSyncNewMsgReminderText;
|
||||
|
||||
const OFFLINE_CACHE = `offline`;
|
||||
const offlineCacheFiles = ['offline.html','config.json'];
|
||||
|
||||
// on install we download the routes we want to cache for offline
|
||||
self.addEventListener('install', evt => evt.waitUntil(caches.open(OFFLINE_CACHE).then(cache => {
|
||||
console.log("SW Caching offline files");
|
||||
self.skipWaiting();
|
||||
return cache.addAll(offlineCacheFiles);
|
||||
})));
|
||||
|
||||
self.addEventListener("activate", event => {
|
||||
event.waitUntil(clients.claim());
|
||||
});
|
||||
|
||||
// Notification click event listener
|
||||
self.addEventListener("notificationclick", (e) => {
|
||||
e.notification.close();
|
||||
|
|
@ -40,3 +54,89 @@ self.addEventListener('periodicsync', (event) => {
|
|||
event.waitUntil(checkNewMessages());
|
||||
}
|
||||
});
|
||||
|
||||
self.addEventListener("fetch", (event) => {
|
||||
if (event.request.mode === 'navigate') {
|
||||
return event.respondWith(
|
||||
fetch(event.request).catch((e) => {
|
||||
console.log("OFFLINE, serve offline page", e);
|
||||
return serveOfflinePage();
|
||||
}));
|
||||
} else if (event.request.url.endsWith("config.json")) {
|
||||
return fetch(event.request)
|
||||
.then((response) => {
|
||||
console.log("Caching a new version of config.json");
|
||||
let responseClone = response.clone();
|
||||
caches
|
||||
.open(OFFLINE_CACHE)
|
||||
.then((cache) => cache.put(event.request, responseClone));
|
||||
return response;
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
async function serveOfflinePage() {
|
||||
let mirrorUrl = null;
|
||||
const rConfig = await caches.match("config.json", { cacheName: OFFLINE_CACHE});
|
||||
if (rConfig) {
|
||||
const json = await rConfig.json();
|
||||
const mirrors = json.mirrors;
|
||||
if (mirrors && Array.isArray(mirrors) && mirrors.length > 0) {
|
||||
mirrorUrl = json.mirrors[Math.floor(Math.random() * mirrors.length)];
|
||||
}
|
||||
}
|
||||
const offlinePage = await caches.match("offline.html", { cacheName: OFFLINE_CACHE});
|
||||
if (mirrorUrl && offlinePage) {
|
||||
let text = await offlinePage.text();
|
||||
text = text.replaceAll("<!--MIRROR_URL-->", mirrorUrl);
|
||||
|
||||
let title = undefined;
|
||||
let message = undefined;
|
||||
let redirect = undefined;
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
var open = indexedDB.open("ServiceWorker", 1);
|
||||
open.onerror = function() {
|
||||
resolve(false);
|
||||
}
|
||||
open.onsuccess = function() {
|
||||
// Start a new transaction
|
||||
var db = open.result;
|
||||
var tx = db.transaction("offline_strings", "readonly");
|
||||
var store = tx.objectStore("offline_strings");
|
||||
|
||||
var get1 = store.get("offline_title");
|
||||
var get2 = store.get("offline_message");
|
||||
var get3 = store.get("offline_redirect");
|
||||
|
||||
get1.onsuccess = function() {
|
||||
title = get1.result.translation;
|
||||
};
|
||||
get2.onsuccess = function() {
|
||||
message = get2.result.translation;
|
||||
};
|
||||
get3.onsuccess = function() {
|
||||
redirect = get3.result.translation;
|
||||
};
|
||||
|
||||
// Close the db when the transaction is done
|
||||
tx.oncomplete = function() {
|
||||
db.close();
|
||||
resolve(true);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
if (title) {
|
||||
text = text.replaceAll(/<!--OFFLINE_TITLE_START-->(.*?)<!--OFFLINE_TITLE_END-->/g, title);
|
||||
}
|
||||
if (message) {
|
||||
text = text.replaceAll(/<!--OFFLINE_MESSAGE_START-->(.*?)<!--OFFLINE_MESSAGE_END-->/g, message);
|
||||
}
|
||||
if (redirect) {
|
||||
text = text.replaceAll(/<!--OFFLINE_REDIRECT_START-->(.*?)<!--OFFLINE_REDIRECT_END-->/g, redirect);
|
||||
}
|
||||
return new Response(text, { headers: {"content-type": "text/html"}});
|
||||
}
|
||||
throw new Error("Offline");
|
||||
}
|
||||
27
src/App.vue
27
src/App.vue
|
|
@ -121,6 +121,33 @@ export default {
|
|||
|
||||
// Set language
|
||||
this.$i18n.locale = this.$store.state.language || "en";
|
||||
|
||||
// Store offline strings.
|
||||
// TODO - Better way to to this? The service worker needs to be able to get these, OR we need multiple translated version of offline.html
|
||||
//
|
||||
// Inspired by https://gist.github.com/JamesMessinger/a0d6389a5d0e3a24814b
|
||||
var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
|
||||
var open = indexedDB.open("ServiceWorker", 1);
|
||||
// Create the schema
|
||||
open.onupgradeneeded = () => {
|
||||
var db = open.result;
|
||||
var store = db.createObjectStore("offline_strings", {keyPath: "id"});
|
||||
};
|
||||
open.onsuccess = () => {
|
||||
// Start a new transaction
|
||||
var db = open.result;
|
||||
var tx = db.transaction("offline_strings", "readwrite");
|
||||
var store = tx.objectStore("offline_strings");
|
||||
|
||||
store.put({id: "offline_title", translation: this.$t("offline.offline_title")});
|
||||
store.put({id: "offline_message", translation: this.$t("offline.offline_message")});
|
||||
store.put({id: "offline_redirect", translation: this.$t("offline.offline_redirect")});
|
||||
|
||||
// Close the db when the transaction is done
|
||||
tx.oncomplete = function() {
|
||||
db.close();
|
||||
};
|
||||
}
|
||||
},
|
||||
showNotification() {
|
||||
if(document.visibilityState === "hidden") {
|
||||
|
|
|
|||
|
|
@ -253,6 +253,7 @@
|
|||
"title": "My Profile",
|
||||
"temporary_identity": "This identity is temporary. Set a password to use it again",
|
||||
"set_password": "Set password",
|
||||
"set_password_desc": "Convene does not store passwords and cannot recover them. We recommend storing your password in a secure location before clicking save.",
|
||||
"change_name": "Change name",
|
||||
"change_password": "Change password",
|
||||
"my_rooms": "My rooms",
|
||||
|
|
@ -323,7 +324,8 @@
|
|||
"copy_credentials": "Copy Username & Password",
|
||||
"copied_credentials": "Copied Username & Password",
|
||||
"copied_credentials_value": "Username: {userId} \nPassword: {password}",
|
||||
"copy_credentials_desc": "Your username and password are required to regain access to your chats from a new device or browser. We recommend storing these credentials in a secure location."
|
||||
"copy_credentials_desc": "Your username and password are required to regain access to your chats from a new device or browser. We recommend storing these credentials in a secure location.",
|
||||
"copy_credentials_desc_for_real_user": "Your username (ex: {'@user:server'}) and your password are required to regain access to your chats from a new device or browser."
|
||||
},
|
||||
"delete_post": {
|
||||
"confirm_text": "Are you sure you want to delete this message?",
|
||||
|
|
@ -539,5 +541,10 @@
|
|||
"generated_with_ai": "Generated with AI.",
|
||||
"modified": "Modified.",
|
||||
"older_than_n_months": "Older than {n} months."
|
||||
},
|
||||
"offline": {
|
||||
"offline_title": "Having trouble connecting?",
|
||||
"offline_message": "Redirect to an alternate link to join the room",
|
||||
"offline_redirect": "Redirect me"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,7 +161,9 @@
|
|||
"learn_more": "بیشتر بدانید",
|
||||
"compressed": "فشرده",
|
||||
"cc_source": "منبع",
|
||||
"cc_location": "مکان"
|
||||
"cc_location": "مکان",
|
||||
"captured_screenshot": "نماگرفت. ",
|
||||
"screenshot": "نماگرفت. "
|
||||
},
|
||||
"project": {
|
||||
"name": "تجمع",
|
||||
|
|
@ -465,9 +467,11 @@
|
|||
"screenshot_probably": "تصویر شبیه به نماگرفت است.",
|
||||
"generated_with_ai": "ساخته شده با هوشی.",
|
||||
"modified": "دستکاری شده.",
|
||||
"older_than_n_months": "قدیمیتر از {n} ماه."
|
||||
"older_than_n_months": "قدیمیتر از {n} ماه.",
|
||||
"download_image": "بارگیری تصویر"
|
||||
},
|
||||
"delete_post": {
|
||||
"confirm_text_desc": "این عمل قابل بازگشت نیست."
|
||||
"confirm_text_desc": "این عمل قابل بازگشت نیست.",
|
||||
"confirm_text": "مطمئنید که میخواهید این پیام را حذف کنید؟"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,5 +259,10 @@
|
|||
},
|
||||
"delete_post": {
|
||||
"confirm_text_desc": "Tätä toimintoa ei voi perua."
|
||||
},
|
||||
"offline": {
|
||||
"offline_title": "Ongelmia yhdistämisessä?",
|
||||
"offline_message": "Siirry vaihtoehtoiseen linkkiin liittyäksesi huoneeseen",
|
||||
"offline_redirect": "Siirrä minut"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@
|
|||
"registration_token": "Cuir isteach comhartha clárúcháin",
|
||||
"send_token": "Seol comhartha",
|
||||
"token_not_valid": "Comhartha míbhailí",
|
||||
"username": "Ainm úsáideora (m.sh.: marta)"
|
||||
"username": "Ainm úsáideora (mar shampla: '@user:server')"
|
||||
},
|
||||
"join": {
|
||||
"you_have_been_banned": "Cuireadh cosc ort ón seomra seo.",
|
||||
|
|
@ -438,7 +438,8 @@
|
|||
"display_name": "Ainm taispeána",
|
||||
"display_name_required": "Tá ainm taispeána ag teastáil",
|
||||
"notification_label": "Fógra",
|
||||
"my_rooms": "Mo sheomraí"
|
||||
"my_rooms": "Mo sheomraí",
|
||||
"set_password_desc": "Ní stórálann Convene pasfhocail agus ní féidir leo iad a aisghabháil. Molaimid do phasfhocal a stóráil in áit shábháilte sula gcliceálann tú ar 'sábháil'."
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"you_are": "Tá tú",
|
||||
|
|
@ -484,7 +485,8 @@
|
|||
"copy_credentials": "Cóipeáil Ainm Úsáideora & Pasfhocal",
|
||||
"copied_credentials": "Ainm úsáideora & Pasfhocal cóipeáilte",
|
||||
"copied_credentials_value": "Ainm úsáideora: {userId} \nPasfhocal: {password}",
|
||||
"copy_credentials_desc": "Tá d’ainm úsáideora agus do phasfhocal ag teastáil chun rochtain a fháil ar do chomhráite arís ó ghléas nó brabhsálaí nua. Molaimid na dintiúir seo a stóráil in áit shábháilte."
|
||||
"copy_credentials_desc": "Tá d’ainm úsáideora agus do phasfhocal ag teastáil chun rochtain a fháil ar do chomhráite arís ó ghléas nó brabhsálaí nua. Molaimid na dintiúir seo a stóráil in áit shábháilte.",
|
||||
"copy_credentials_desc_for_real_user": "Tá d’ainm úsáideora (mar shampla: '@user:server') agus do phasfhocal ag teastáil chun rochtain a fháil ar do chomhráite arís ó ghléas nó brabhsálaí nua."
|
||||
},
|
||||
"fallbacks": {
|
||||
"audio_file": "Comhad fuaime",
|
||||
|
|
@ -539,5 +541,10 @@
|
|||
"delete_post": {
|
||||
"confirm_text": "An bhfuil tú cinnte gur mian leat an teachtaireacht seo a scriosadh?",
|
||||
"confirm_text_desc": "Ní féidir an gníomh seo a chealú."
|
||||
},
|
||||
"offline": {
|
||||
"offline_title": "An bhfuil deacracht agat ceangal?",
|
||||
"offline_message": "Atreoraigh chuig nasc malartach chun páirt a ghlacadh sa seomra",
|
||||
"offline_redirect": "Athsheol mé"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
188
src/assets/translations/ta.json
Normal file
188
src/assets/translations/ta.json
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
{
|
||||
"global": {
|
||||
"save": "சேமி",
|
||||
"show_less": "குறைவாகக் காட்டு",
|
||||
"show_more": "மேலும் காட்டு",
|
||||
"time": {
|
||||
"recently": "இப்போது",
|
||||
"minutes": "1 மணித்துளி முன்பு | {n} நிமிடங்களுக்கு முன்பு",
|
||||
"hours": "1 மணி நேரத்திற்கு முன்பு | {n} மணிநேரங்களுக்கு முன்பு",
|
||||
"days": "1 நாள் முன்பு | {n} சில நாட்களுக்கு முன்பு"
|
||||
},
|
||||
"notify": "அறிவிக்கவும்",
|
||||
"retry": "மீண்டும் முயற்சிக்கவும்"
|
||||
},
|
||||
"menu": {
|
||||
"direct_chat": "நேரடி அரட்டை",
|
||||
"reply": "பதில்",
|
||||
"edit": "தொகு",
|
||||
"delete": "அழி",
|
||||
"download": "பதிவிறக்கம்",
|
||||
"done": "முடிந்தது",
|
||||
"cancel": "நீக்கு",
|
||||
"send": "அனுப்பு",
|
||||
"login": "புகுபதிவு",
|
||||
"logout": "வெளியேற்றம்",
|
||||
"undo": "செயல்தவிர்",
|
||||
"join": "சேர",
|
||||
"ignore": "புறக்கணிக்கவும்",
|
||||
"user_make_admin": "நிர்வாகி",
|
||||
"user_make_moderator": "மதிப்பீட்டாளரை உருவாக்குங்கள்",
|
||||
"upgrade": "மேம்படுத்தல்"
|
||||
},
|
||||
"message": {
|
||||
"someone": "யாரோ",
|
||||
"edited": "(திருத்தப்பட்டது)",
|
||||
"room_joinrule_public": "பொது",
|
||||
"reply_image": "படம்",
|
||||
"reply_video": "ஒளிதோற்றம்",
|
||||
"reply_poll": "வாக்கெடுப்பு",
|
||||
"file": "கோப்பு",
|
||||
"files": "கோப்புகள்",
|
||||
"images": "படங்கள்",
|
||||
"download_media": "மீடியாவைப் பதிவிறக்கவும்",
|
||||
"download_all": "அனைத்தையும் பதிவிறக்கு",
|
||||
"room_upgraded_link": "இங்கே"
|
||||
},
|
||||
"room": {
|
||||
"leave": "விடுப்பு",
|
||||
"room_list_invites": "அழைப்புகள்",
|
||||
"room_list_rooms": "அறைகள்"
|
||||
},
|
||||
"room_welcome": {
|
||||
"got_it": "கிடைத்தது",
|
||||
"change": "மாற்றம்"
|
||||
},
|
||||
"new_room": {
|
||||
"create": "உருவாக்கு",
|
||||
"next": "அடுத்தது",
|
||||
"options": "விருப்பங்கள்"
|
||||
},
|
||||
"device_list": {
|
||||
"blocked": "தடுக்கப்பட்டது",
|
||||
"verified": "சரிபார்க்கப்பட்டது",
|
||||
"not_verified": "சரிபார்க்கப்படவில்லை"
|
||||
},
|
||||
"login": {
|
||||
"title": "புகுபதிவு",
|
||||
"password": "கடவுச்சொல்லை உள்ளிடவும்",
|
||||
"login": "புகுபதிவு",
|
||||
"invalid_message": "தவறான பயனர்பெயர் அல்லது கடவுச்சொல்",
|
||||
"accept_terms": "ஏற்றுக்கொள்",
|
||||
"resend_verification": "சரிபார்ப்பு மின்னஞ்சல் மீண்டும் அனுப்பவும்",
|
||||
"token_not_valid": "தவறான கிள்ளாக்கு"
|
||||
},
|
||||
"getlink": {
|
||||
"next": "அடுத்தது",
|
||||
"continue": "தொடரவும்"
|
||||
},
|
||||
"create": {
|
||||
"room_type_channel_name": "வாய்க்கால்",
|
||||
"field_required": "இந்த புலம் தேவை"
|
||||
},
|
||||
"profile": {
|
||||
"set_password": "கடவுச்சொல்லை அமைக்கவும்",
|
||||
"change_password": "கடவுச்சொல்லை மாற்றவும்",
|
||||
"select_language": "மொழி",
|
||||
"password_new": "புதிய கடவுச்சொல்",
|
||||
"display_name": "காட்சி பெயர்",
|
||||
"notification_label": "அறிவிப்பு"
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"edit_profile": "தொகு சுயவிவரம்",
|
||||
"logout": "வெளியேற்றம்"
|
||||
},
|
||||
"join": {
|
||||
"user_name_label": "பயனர் பெயர்",
|
||||
"remember_me": "என்னை நினைவில் கொள்ளுங்கள்",
|
||||
"join": "அறையில் சேரவும்",
|
||||
"knock": "தட்டவும்",
|
||||
"enter_room": "அறையை உள்ளிடவும்"
|
||||
},
|
||||
"invite": {
|
||||
"title": "நண்பர்களைச் சேர்க்கவும்",
|
||||
"done": "முடிந்தது"
|
||||
},
|
||||
"leave": {
|
||||
"go_back": "திரும்பிச் செல்லுங்கள்",
|
||||
"leave": "விடுப்பு"
|
||||
},
|
||||
"delete_post": {
|
||||
"confirm_text_desc": "இந்த செயலை செயல்தவிர்க்க முடியாது."
|
||||
},
|
||||
"purge_room": {
|
||||
"button": "அழி"
|
||||
},
|
||||
"room_info": {
|
||||
"copy_link": "இணைப்பை நகலெடு",
|
||||
"members": "உறுப்பினர்கள்",
|
||||
"accept_knock": "ஏற்றுக்கொள்",
|
||||
"reject_knock": "மறுக்கவும்",
|
||||
"hide_all": "மறை",
|
||||
"leave_room": "விடுப்பு",
|
||||
"user_creator": "உருவாக்கியவர்",
|
||||
"user_admin": "நிர்வாகி",
|
||||
"user_moderator": "மதிப்பீட்டாளர்",
|
||||
"moderation": "மிதமான",
|
||||
"room_type_default": "இயல்புநிலை",
|
||||
"read_only_room": "படிக்கவும்",
|
||||
"message_retention_2_week": "2 வாரங்கள்",
|
||||
"message_retention_1_week": "1 வாரம்",
|
||||
"message_retention_1_day": "1 நாள்",
|
||||
"message_retention_8_hours": "8 மணி நேரம்",
|
||||
"message_retention_1_hour": "1 மணி நேரம்",
|
||||
"report": "அறிக்கை",
|
||||
"report_reason": "காரணம்"
|
||||
},
|
||||
"room_info_sheet": {
|
||||
"view_details": "விபரங்களை பார்"
|
||||
},
|
||||
"voice_recorder": {
|
||||
"not_supported_title": "ஆதரிக்கப்படவில்லை"
|
||||
},
|
||||
"power_level": {
|
||||
"default": "இயல்புநிலை"
|
||||
},
|
||||
"fallbacks": {
|
||||
"audio_file": "ஆடியோ கோப்பு",
|
||||
"video_file": "வீடியோ கோப்பு",
|
||||
"download_name": "பதிவிறக்கம்"
|
||||
},
|
||||
"poll_create": {
|
||||
"create": "வெளியிடுங்கள்",
|
||||
"answer_label_n": "பதில்",
|
||||
"create_poll_menu_option": "வாக்கெடுப்பை உருவாக்கவும்",
|
||||
"view_results": "முடிவுகளைக் காண்க",
|
||||
"poll_submit": "சமர்ப்பிக்கவும்"
|
||||
},
|
||||
"notification": {
|
||||
"dialog": {
|
||||
"enable": "இயக்கு"
|
||||
}
|
||||
},
|
||||
"emoji": {
|
||||
"categories": {
|
||||
"activity": "செய்கைப்பாடு",
|
||||
"flags": "கொடிகள்",
|
||||
"objects": "பொருள்கள்",
|
||||
"nature": "இயற்கை",
|
||||
"symbols": "குறியிடுகள்",
|
||||
"places": "இடங்கள்"
|
||||
}
|
||||
},
|
||||
"file_mode": {
|
||||
"sending_progress": "அனுப்புகிறது ...",
|
||||
"sending": "அனுப்புகிறது",
|
||||
"close": "மூடு",
|
||||
"files": "கோப்புகள்",
|
||||
"quality": "தகுதி",
|
||||
"original": "அசல்",
|
||||
"learn_more": "மேலும் அறிய",
|
||||
"cc_source": "மூலம்",
|
||||
"cc_location": "இடம்"
|
||||
},
|
||||
"cc": {
|
||||
"details": "விவரங்கள்",
|
||||
"download_image": "படத்தைப் பதிவிறக்கவும்"
|
||||
}
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@
|
|||
"options": "Опції"
|
||||
},
|
||||
"login": {
|
||||
"username": "Ім'я користувача (наприклад: Марта)",
|
||||
"username": "Ім'я користувача (ex: '@user:server')",
|
||||
"username_required": "Потрібне ім'я користувача",
|
||||
"password_required": "Потрібен пароль",
|
||||
"create_room": "Зареєструватися та створити кімнату",
|
||||
|
|
@ -261,7 +261,8 @@
|
|||
"password_old": "Старий пароль",
|
||||
"password_new": "Новий пароль",
|
||||
"display_name": "Відображуване ім'я",
|
||||
"notification_label": "Сповіщення"
|
||||
"notification_label": "Сповіщення",
|
||||
"set_password_desc": "Convene не зберігає паролі та не може їх відновити. Ми рекомендуємо зберігати пароль у безпечному місці, перш ніж натискати кнопку «Зберегти»."
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"you_are": "Ви є",
|
||||
|
|
@ -318,7 +319,8 @@
|
|||
"copy_credentials": "Скопіювати ім'я користувача та пароль",
|
||||
"copied_credentials": "Скопійовано ім'я користувача та пароль",
|
||||
"copied_credentials_value": "Ім'я користувача: {userId} \nПароль: {password}",
|
||||
"copy_credentials_desc": "Для відновлення доступу до чатів з нового пристрою або браузера потрібні ваші ім’я користувача та пароль. Рекомендуємо зберігати ці облікові дані в безпечному місці."
|
||||
"copy_credentials_desc": "Для відновлення доступу до чатів з нового пристрою або браузера потрібні ваші ім’я користувача та пароль. Рекомендуємо зберігати ці облікові дані в безпечному місці.",
|
||||
"copy_credentials_desc_for_real_user": "Ваше ім’я користувача (наприклад: '@user:server') та пароль потрібні для відновлення доступу до ваших чатів з нового пристрою або браузера."
|
||||
},
|
||||
"purge_room": {
|
||||
"title": "Видалити кімнату?",
|
||||
|
|
@ -539,5 +541,10 @@
|
|||
"delete_post": {
|
||||
"confirm_text": "Ви впевнені, що хочете видалити це повідомлення?",
|
||||
"confirm_text_desc": "Цю дію не можна скасувати."
|
||||
},
|
||||
"offline": {
|
||||
"offline_title": "Виникли проблеми з підключенням?",
|
||||
"offline_message": "Перенаправте на альтернативне посилання для приєднання до кімнати",
|
||||
"offline_redirect": "Перенаправити мене"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
@click:outside="$emit('onOutsideLogoutPopupClicked')">
|
||||
<div class="dialog-content text-center">
|
||||
<h2 class="dialog-title">{{ $t("logout.confirm_text") }}</h2>
|
||||
<div class="dialog-text">{{ $t("logout.copy_credentials_desc") }}</div>
|
||||
<v-row>
|
||||
<div class="dialog-text">{{ copyCredentialsSubText }}</div>
|
||||
<v-row v-if="this.$matrix.currentUser.is_guest">
|
||||
<v-col sm="12" md="6" offset-md="3" class="d-flex justify-center">
|
||||
<v-btn
|
||||
ref="copyCredentialsBtn"
|
||||
|
|
@ -59,7 +59,10 @@ export default {
|
|||
]),
|
||||
credentials() {
|
||||
return this.$t(`logout.copied_credentials_value`, { userId: this.auth.user.user_id, password: this.auth.user.password})
|
||||
}
|
||||
},
|
||||
copyCredentialsSubText() {
|
||||
return this.$t(`${this.$matrix.currentUser.is_guest ? 'logout.copy_credentials_desc': 'logout.copy_credentials_desc_for_real_user'}`)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onCopyCredentials() {
|
||||
|
|
|
|||
55
src/components/MigratingView.vue
Normal file
55
src/components/MigratingView.vue
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<div>
|
||||
<transition name="slow-fade">
|
||||
<div
|
||||
class="text-center d-flex flex-column goodbye-wrapper"
|
||||
>
|
||||
<div class="quote">Migrating...</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "MigratingView",
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use "@/assets/css/chat.scss" as *;
|
||||
.author {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
}
|
||||
|
||||
.slow-fade-enter-active,
|
||||
.slow-fade-leave-active {
|
||||
transition: opacity 2.5s;
|
||||
}
|
||||
.slow-fade-enter, .slow-fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
}
|
||||
.goodbye-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
color: black;
|
||||
background-color: white;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -116,6 +116,7 @@
|
|||
>
|
||||
<v-card :disabled="settingPassword" class="dialog-content">
|
||||
<h2 class="dialog-title">{{ $matrix.currentUser.is_guest ? $t("profile.set_password") : $t("profile.change_password") }}</h2>
|
||||
<div class="dialog-text mb-4">{{ $t("profile.set_password_desc") }}</div>
|
||||
<div class="dialog-text">
|
||||
<v-text-field
|
||||
v-if="!$matrix.currentUser.is_guest"
|
||||
|
|
@ -149,28 +150,30 @@
|
|||
{{ passwordErrorMessage }}
|
||||
</div>
|
||||
</div>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn id="btn-password-cancel" variant="text" @click="closeEditPasswordDialog">{{
|
||||
$t("menu.cancel")
|
||||
}}</v-btn>
|
||||
<v-btn
|
||||
id="btn-password-set"
|
||||
:disabled="!passwordsMatch"
|
||||
color="primary"
|
||||
variant="text"
|
||||
@click="
|
||||
setPassword(
|
||||
$matrix.currentUser.is_guest
|
||||
? $matrix.currentUser.password
|
||||
: password,
|
||||
newPassword1
|
||||
)
|
||||
"
|
||||
>{{ $t("global.save") }}</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
<v-container fluid>
|
||||
<v-row cols="12">
|
||||
<v-col cols="6">
|
||||
<v-btn variant="flat" block class="text-button" @click.stop="closeEditPasswordDialog">{{
|
||||
$t("menu.cancel") }}</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="6" align="center">
|
||||
<v-btn
|
||||
:disabled="!passwordsMatch"
|
||||
color="#4642F1"
|
||||
variant="flat"
|
||||
block
|
||||
class="filled-button"
|
||||
@click="
|
||||
setPassword(
|
||||
$matrix.currentUser.is_guest
|
||||
? $matrix.currentUser.password
|
||||
: password,
|
||||
newPassword1
|
||||
)
|
||||
">{{ $t("global.save") }}</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
|
|
@ -180,33 +183,35 @@
|
|||
class="ma-0 pa-0"
|
||||
:width="$vuetify.display.smAndUp ? '940px' : '80%'"
|
||||
>
|
||||
<v-card>
|
||||
<v-card class="dialog-content">
|
||||
<v-card-title>{{ $t("profile.display_name") }}</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="dialog-text">
|
||||
<v-text-field
|
||||
v-model="editValue"
|
||||
:rules="[(v) => !!v || $t('profile.display_name_required')]"
|
||||
required
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn id="btn-displayname-cancel" variant="text" @click="showEditDisplaynameDialog = false">{{
|
||||
$t("menu.cancel")
|
||||
}}</v-btn>
|
||||
<v-btn
|
||||
id="btn-displayname-set"
|
||||
color="primary"
|
||||
variant="text"
|
||||
@click="
|
||||
updateDisplayName(editValue);
|
||||
showEditDisplaynameDialog = false;
|
||||
"
|
||||
:disabled="!editValue"
|
||||
>{{ $t("global.save") }}</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
<v-container fluid>
|
||||
<v-row cols="12">
|
||||
<v-col cols="6">
|
||||
<v-btn variant="flat" block class="text-button" @click="showEditDisplaynameDialog = false">{{
|
||||
$t("menu.cancel") }}</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="6" align="center">
|
||||
<v-btn
|
||||
:disabled="!editValue"
|
||||
color="#4642F1"
|
||||
variant="flat"
|
||||
block
|
||||
class="filled-button"
|
||||
@click="
|
||||
updateDisplayName(editValue);
|
||||
showEditDisplaynameDialog = false;
|
||||
">{{ $t("global.save") }}</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</v-card>
|
||||
</RoundedDialog>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,126 +1,141 @@
|
|||
import Home from '../components/Home.vue'
|
||||
import Chat from '../components/Chat.vue'
|
||||
import Join from '../components/Join.vue'
|
||||
import Login from '../components/Login.vue'
|
||||
import Profile from '../components/Profile.vue'
|
||||
import CreateRoom from '../components/CreateRoom.vue'
|
||||
import GetLink from '../components/GetLink.vue'
|
||||
import Create from '../components/Create.vue'
|
||||
import CreateChannel from '../components/CreateChannel.vue'
|
||||
import CreateFileDrop from '../components/CreateFileDrop.vue'
|
||||
import User from '../models/user'
|
||||
import util from '../plugins/utils'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import Home from "../components/Home.vue";
|
||||
import Chat from "../components/Chat.vue";
|
||||
import Join from "../components/Join.vue";
|
||||
import Login from "../components/Login.vue";
|
||||
import Profile from "../components/Profile.vue";
|
||||
import CreateRoom from "../components/CreateRoom.vue";
|
||||
import GetLink from "../components/GetLink.vue";
|
||||
import Create from "../components/Create.vue";
|
||||
import CreateChannel from "../components/CreateChannel.vue";
|
||||
import CreateFileDrop from "../components/CreateFileDrop.vue";
|
||||
import User from "../models/user";
|
||||
import util from "../plugins/utils";
|
||||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
import { defineAsyncComponent } from "vue";
|
||||
import { STORE_KEY_SETTINGS, STORE_KEY_USER } from "../store";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home
|
||||
path: "/",
|
||||
name: "Home",
|
||||
component: Home,
|
||||
},
|
||||
{
|
||||
path: '/room/:join(join/)?:roomId?',
|
||||
name: 'Chat',
|
||||
path: "/room/:join(join/)?:roomId?",
|
||||
name: "Chat",
|
||||
component: Chat,
|
||||
meta: {
|
||||
includeRoom: true,
|
||||
includeFavicon: true
|
||||
includeFavicon: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/info',
|
||||
name: 'RoomInfo',
|
||||
component: () => import('../components/RoomInfo.vue'),
|
||||
path: "/info",
|
||||
name: "RoomInfo",
|
||||
component: () => import("../components/RoomInfo.vue"),
|
||||
props: true,
|
||||
meta: {
|
||||
title: 'Info',
|
||||
title: "Info",
|
||||
includeRoom: true,
|
||||
includeFavicon: true
|
||||
}
|
||||
includeFavicon: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/profile',
|
||||
name: 'Profile',
|
||||
path: "/profile",
|
||||
name: "Profile",
|
||||
component: Profile,
|
||||
meta: {
|
||||
title: 'Profile',
|
||||
includeFavicon: true
|
||||
}
|
||||
title: "Profile",
|
||||
includeFavicon: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/createroom',
|
||||
name: 'CreateRoom',
|
||||
path: "/createroom",
|
||||
name: "CreateRoom",
|
||||
component: CreateRoom,
|
||||
meta: {
|
||||
title: 'Create room'
|
||||
}
|
||||
title: "Create room",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/getlink',
|
||||
name: 'GetLink',
|
||||
path: "/getlink",
|
||||
name: "GetLink",
|
||||
component: GetLink,
|
||||
},
|
||||
{
|
||||
path: '/create',
|
||||
name: 'Create',
|
||||
path: "/create",
|
||||
name: "Create",
|
||||
component: Create,
|
||||
},
|
||||
{
|
||||
path: '/createchannel',
|
||||
name: 'CreateChannel',
|
||||
path: "/createchannel",
|
||||
name: "CreateChannel",
|
||||
component: CreateChannel,
|
||||
},
|
||||
{
|
||||
path: '/createfiledrop',
|
||||
name: 'CreateFileDrop',
|
||||
path: "/createfiledrop",
|
||||
name: "CreateFileDrop",
|
||||
component: CreateFileDrop,
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
path: "/login",
|
||||
name: "Login",
|
||||
component: Login,
|
||||
props: true
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/join/:join(join/)?:roomId?',
|
||||
name: 'Join',
|
||||
path: "/join/:join(join/)?:roomId?",
|
||||
name: "Join",
|
||||
component: Join,
|
||||
props: route => {
|
||||
props: (route) => {
|
||||
return {
|
||||
roomNeedsKnock: (route.query.knock ? true : false),
|
||||
roomDisplayName: route.query.roomName
|
||||
}
|
||||
}
|
||||
roomNeedsKnock: route.query.knock ? true : false,
|
||||
roomDisplayName: route.query.roomName,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/user/:userId?',
|
||||
name: 'User',
|
||||
component: Join
|
||||
path: "/user/:userId?",
|
||||
name: "User",
|
||||
component: Join,
|
||||
},
|
||||
{
|
||||
path: '/invite/:roomId?',
|
||||
name: 'Invite',
|
||||
component: defineAsyncComponent(() => import('../components/Invite.vue')),
|
||||
path: "/invite/:roomId?",
|
||||
name: "Invite",
|
||||
component: defineAsyncComponent(() => import("../components/Invite.vue")),
|
||||
meta: {
|
||||
title: 'Add Friends'
|
||||
}
|
||||
title: "Add Friends",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/goodbye',
|
||||
name: 'Goodbye',
|
||||
component: defineAsyncComponent(() => import('../components/QuoteView.vue')),
|
||||
props: true
|
||||
}
|
||||
]
|
||||
path: "/goodbye",
|
||||
name: "Goodbye",
|
||||
component: defineAsyncComponent(() => import("../components/QuoteView.vue")),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "/migrating",
|
||||
name: "Migrating",
|
||||
component: defineAsyncComponent(() => import("../components/MigratingView.vue")),
|
||||
props: false,
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: routes
|
||||
routes: routes,
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const publicPages = ['/login', '/createroom', '/getlink', '/create', '/createchannel', '/createfiledrop'];
|
||||
const publicPages = [
|
||||
"/login",
|
||||
"/createroom",
|
||||
"/getlink",
|
||||
"/create",
|
||||
"/createchannel",
|
||||
"/createfiledrop",
|
||||
"/migrating",
|
||||
];
|
||||
var authRequired = !publicPages.includes(to.path);
|
||||
const loggedIn = router.app.$store.state.auth.user;
|
||||
|
||||
|
|
@ -128,15 +143,45 @@ router.beforeEach((to, from, next) => {
|
|||
// Set language via query param
|
||||
const lang = to.query.lang;
|
||||
// Check if valid translation
|
||||
if (router.app.$i18n.messages[lang]) {
|
||||
router.app.$store.commit('setLanguage', lang);
|
||||
if (router.app.$i18n) {
|
||||
router.app.$i18n.locale = to.query.lang;
|
||||
}
|
||||
if (router.app.$i18n && router.app.$i18n.global && router.app.$i18n.global.messages[lang]) {
|
||||
router.app.$store.commit("setLanguage", lang);
|
||||
router.app.$i18n.global.locale = to.query.lang;
|
||||
}
|
||||
}
|
||||
|
||||
if (to.name == 'Chat' || to.name == 'Join') {
|
||||
if (to.query && to.query.migrate) {
|
||||
try {
|
||||
if (window.opener) {
|
||||
console.log("Start listening for migration messages");
|
||||
window.addEventListener("message", function (e) {
|
||||
// if (e.origin !== origin) { // TODO - What should we check here?
|
||||
// return
|
||||
// }
|
||||
try {
|
||||
const data = JSON.parse(e.data);
|
||||
if (data !== null) {
|
||||
router.app.$store
|
||||
.dispatch("migrate", data)
|
||||
.then(() => {
|
||||
console.log("Migration success");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Migration error", error);
|
||||
})
|
||||
.finally(() => {
|
||||
window.opener.postMessage(JSON.stringify({ cmd: "migrationDone" }), "*");
|
||||
router.app.$navigation.push({ name: "Home" }, -1);
|
||||
});
|
||||
}
|
||||
} catch (error) {}
|
||||
});
|
||||
window.opener.postMessage(JSON.stringify({ cmd: "getMigrationData" }), "*");
|
||||
}
|
||||
return next("/migrating");
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
if (to.name == "Chat" || to.name == "Join") {
|
||||
if (!to.params.roomId && to.hash) {
|
||||
// Public rooms start with '#', confuses the router. If hash but no roomId param, set it.
|
||||
to.params.roomId = to.hash;
|
||||
|
|
@ -147,62 +192,84 @@ router.beforeEach((to, from, next) => {
|
|||
//Invite to public room
|
||||
authRequired = false;
|
||||
}
|
||||
} else if (to.name == 'Home') {
|
||||
} else if (to.name == "Home") {
|
||||
if (to.params.roomId !== undefined) {
|
||||
const roomId = to.params.roomId ? util.sanitizeRoomId(to.params.roomId) : null;
|
||||
router.app.$matrix.setCurrentRoomId(roomId);
|
||||
}
|
||||
} else if (to.name == 'User') {
|
||||
} else if (to.name == "User") {
|
||||
if (to.params.userId) {
|
||||
let roomId = util.sanitizeUserId(to.params.userId);
|
||||
if (roomId && !roomId.startsWith("@")) {
|
||||
// Not a full username. Assume local name on this server.
|
||||
return router.app.$config.load().then((config) => {
|
||||
const domain = config.defaultMatrixDomainPart;
|
||||
if (!domain) throw new Error("No domain part for user invite!");
|
||||
roomId = "@" + roomId + ":" + domain;
|
||||
router.app.$matrix.setCurrentRoomId(roomId);
|
||||
}).catch(err => console.error(err)).finally(() => next());
|
||||
return router.app.$config
|
||||
.load()
|
||||
.then((config) => {
|
||||
const domain = config.defaultMatrixDomainPart;
|
||||
if (!domain) throw new Error("No domain part for user invite!");
|
||||
roomId = "@" + roomId + ":" + domain;
|
||||
router.app.$matrix.setCurrentRoomId(roomId);
|
||||
})
|
||||
.catch((err) => console.error(err))
|
||||
.finally(() => next());
|
||||
} else {
|
||||
router.app.$matrix.setCurrentRoomId(roomId);
|
||||
authRequired = false;
|
||||
}
|
||||
}
|
||||
} else if (to.name == 'Invite') {
|
||||
} else if (to.name == "Invite") {
|
||||
if (to.params.roomId) {
|
||||
const roomId = util.sanitizeRoomId(to.params.roomId);
|
||||
router.app.$matrix.setCurrentRoomId(roomId);
|
||||
}
|
||||
} else if (to.name == 'CreateRoom') {
|
||||
return router.app.$config.load().then((config) => {
|
||||
if (config.hide_add_room_on_home || !config.roomTypes.includes("group_chat")) {
|
||||
next('/');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}).catch(err => { console.error(err); next('/'); });
|
||||
} else if (to.name == 'CreateChannel') {
|
||||
return router.app.$config.load().then((config) => {
|
||||
if (!config.roomTypes.includes("channel")) {
|
||||
next('/');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}).catch(err => { console.error(err); next('/'); });
|
||||
} else if (to.name == 'CreateFileDrop') {
|
||||
return router.app.$config.load().then((config) => {
|
||||
if (!config.roomTypes.includes("file_drop")) {
|
||||
next('/');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}).catch(err => { console.error(err); next('/'); });
|
||||
} else if (to.name == "CreateRoom") {
|
||||
return router.app.$config
|
||||
.load()
|
||||
.then((config) => {
|
||||
if (config.hide_add_room_on_home || !config.roomTypes.includes("group_chat")) {
|
||||
next("/");
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
next("/");
|
||||
});
|
||||
} else if (to.name == "CreateChannel") {
|
||||
return router.app.$config
|
||||
.load()
|
||||
.then((config) => {
|
||||
if (!config.roomTypes.includes("channel")) {
|
||||
next("/");
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
next("/");
|
||||
});
|
||||
} else if (to.name == "CreateFileDrop") {
|
||||
return router.app.$config
|
||||
.load()
|
||||
.then((config) => {
|
||||
if (!config.roomTypes.includes("file_drop")) {
|
||||
next("/");
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
next("/");
|
||||
});
|
||||
}
|
||||
|
||||
// trying to access a restricted page + not logged in
|
||||
// redirect to login page
|
||||
if (authRequired && !loggedIn) {
|
||||
next('/login');
|
||||
next("/login");
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
|
|
@ -225,12 +292,21 @@ router.getRoomLink = function (alias, roomId, roomName, mode, autojoin, knock, m
|
|||
const autoJoinSegment = autojoin ? "join/" : "";
|
||||
let queryString = "";
|
||||
if (Object.entries(params).length > 0) {
|
||||
queryString = "?" + Object.entries(params)
|
||||
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
||||
.join('&')
|
||||
queryString =
|
||||
"?" +
|
||||
Object.entries(params)
|
||||
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
||||
.join("&");
|
||||
}
|
||||
return (mirror ? (window.location.protocol + "//" + mirror) : window.location.origin) + window.location.pathname + "#/room/" + autoJoinSegment + encodeURIComponent(util.sanitizeRoomId(alias || roomId)) + queryString;
|
||||
}
|
||||
return (
|
||||
(mirror ? window.location.protocol + "//" + mirror : window.location.origin) +
|
||||
window.location.pathname +
|
||||
"#/room/" +
|
||||
autoJoinSegment +
|
||||
encodeURIComponent(util.sanitizeRoomId(alias || roomId)) +
|
||||
queryString
|
||||
);
|
||||
};
|
||||
|
||||
router.getDMLink = function (user, config, mirror) {
|
||||
let userId = user.user_id;
|
||||
|
|
@ -238,7 +314,9 @@ router.getDMLink = function (user, config, mirror) {
|
|||
// Using default server, don't include it in the link
|
||||
userId = User.localPart(user.user_id);
|
||||
}
|
||||
return `${(mirror ? (window.location.protocol + "//" + mirror) : window.location.origin) + window.location.pathname}#/user/${encodeURIComponent(userId)}`
|
||||
}
|
||||
return `${
|
||||
(mirror ? window.location.protocol + "//" + mirror : window.location.origin) + window.location.pathname
|
||||
}#/user/${encodeURIComponent(userId)}`;
|
||||
};
|
||||
|
||||
export default router
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -141,7 +141,32 @@ export default {
|
|||
}
|
||||
return this.legacyCryptoStore;
|
||||
},
|
||||
login(user, registrationFlowHandler, createUser = false) {
|
||||
migrate(user) {
|
||||
return util.getMatrixBaseUrl(user, this.$config)
|
||||
.then((baseUrl) => {
|
||||
console.log("Create temp client", user);
|
||||
const tempMatrixClient = sdk.createClient({
|
||||
baseUrl: baseUrl,
|
||||
idBaseUrl: this.$config.identityServer,
|
||||
});
|
||||
user.access_token = undefined;
|
||||
user.device_id = tempMatrixClient.getSessionId();
|
||||
if (user.refresh_token) {
|
||||
return this.refreshAccessToken(tempMatrixClient, user.refresh_token);
|
||||
} else {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}).then(() => {
|
||||
console.log("Refresh done, normal login");
|
||||
return this.login(user, undefined, false, true);
|
||||
});
|
||||
},
|
||||
login(user, registrationFlowHandler, createUser = false, isMigration = false) {
|
||||
if (!isMigration && window.location.href.includes("migrate=1")) {
|
||||
console.log("Migrate, so no login!");
|
||||
return Promise.reject("Migrating, so no login");
|
||||
}
|
||||
|
||||
return util.getMatrixBaseUrl(user, this.$config).then((baseUrl) => {
|
||||
const tempMatrixClient = sdk.createClient({
|
||||
baseUrl: baseUrl,
|
||||
|
|
@ -510,12 +535,16 @@ export default {
|
|||
},
|
||||
|
||||
onSessionRefresh(refreshToken) {
|
||||
return this.refreshAccessToken(this.matrixClient, refreshToken);
|
||||
},
|
||||
|
||||
refreshAccessToken(matrixClient, refreshToken) {
|
||||
if (this.tokenRefreshPromise) {
|
||||
return this.tokenRefreshPromise;
|
||||
}
|
||||
const now = Date.now();
|
||||
this.tokenRefreshPromise =
|
||||
this.matrixClient.http.request(sdk.Method.Post, "/refresh", undefined, { refresh_token: refreshToken}, { prefix: sdk.ClientPrefix.V3, inhibitLogoutEmit: true }).then((result) => {
|
||||
matrixClient.http.request(sdk.Method.Post, "/refresh", undefined, { refresh_token: refreshToken}, { prefix: sdk.ClientPrefix.V3, inhibitLogoutEmit: true }).then((result) => {
|
||||
// Store new one!
|
||||
var user = this.$store.state.auth.user;
|
||||
user.access_token = result.access_token;
|
||||
|
|
|
|||
|
|
@ -136,6 +136,33 @@ const store = createStore({
|
|||
this.$matrix.logout();
|
||||
commit('logout');
|
||||
},
|
||||
migrate({ state }, migrationData) {
|
||||
if (migrationData && migrationData.type) {
|
||||
let storage = migrationData.type == "session" ? sessionStorage : localStorage;
|
||||
if (migrationData.settings) {
|
||||
try {
|
||||
const settings = JSON.parse(migrationData.settings);
|
||||
storage.setItem(STORE_KEY_SETTINGS, migrationData.settings);
|
||||
state.useLocalStorage = migrationData.type != "session";
|
||||
} catch (error) {
|
||||
console.error("Failed to migrate settings", error);
|
||||
}
|
||||
}
|
||||
if (migrationData.user) {
|
||||
const user = JSON.parse(migrationData.user);
|
||||
state.auth.user = user;
|
||||
return this.$matrix.migrate(user).then(
|
||||
(user) => {
|
||||
return Promise.resolve(user);
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
return Promise.reject("Migration error");
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
storage: state => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue