merged branch main into '133-header-hides-and-gets-stuck' and conflit fixes
|
|
@ -1,4 +1,3 @@
|
|||
image: alpine:latest
|
||||
pages: # the job must be named pages
|
||||
image: node:latest
|
||||
stage: deploy
|
||||
|
|
@ -16,3 +15,11 @@ pages: # the job must be named pages
|
|||
- public # artifact path must be /public for GitLab Pages to pick it up
|
||||
only:
|
||||
- main
|
||||
|
||||
s3-deploy:
|
||||
stage: .post
|
||||
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
|
||||
script:
|
||||
- aws s3 sync ./public s3://gp-operations-prod-shared-weblite-bucket --delete --only-show-errors
|
||||
only:
|
||||
- main
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ See [Configuration Reference](https://cli.vuejs.org/config/).
|
|||
|
||||
## Theming
|
||||
|
||||
# Sticker short codes - To enable sticker short codes, follow there steps:
|
||||
* set "useShortCodeStickers" to true in config.json.
|
||||
* Add your sticker pack folders (containing stickers) to /src/assets/stickers/
|
||||
* Create file /src/assets/stickers/order.txt that lists the folders names in order, one folder name per line.
|
||||
# Sticker short codes - To enable sticker short codes, follow these steps:
|
||||
* Run the "create sticker config" script using "npm run create-sticker-config <path-to-sticker-packs>"
|
||||
* Insert the resulting config blob into the "shortCodeStickers" value of the config file (assets/config.json)
|
||||
* Rearrange order of sticker packs by editing the config blob above.
|
||||
45
create_sticker_config.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
if (!process.argv[2]) {
|
||||
console.log('Insufficient number of arguments! Need a path to sticker packs...');
|
||||
}
|
||||
|
||||
var path = require('path'), fs = require('fs');
|
||||
|
||||
function fromDir(startPath, filter, callback) {
|
||||
|
||||
//console.log('Starting from dir '+startPath+'/');
|
||||
|
||||
if (!fs.existsSync(startPath)) {
|
||||
console.log("no dir ", startPath);
|
||||
return;
|
||||
}
|
||||
|
||||
var files = fs.readdirSync(startPath);
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var filename = path.join(startPath, files[i]);
|
||||
var stat = fs.lstatSync(filename);
|
||||
if (stat.isDirectory()) {
|
||||
fromDir(filename, filter, callback); //recurse
|
||||
}
|
||||
else if (filter.test(filename)) callback(filename);
|
||||
};
|
||||
};
|
||||
|
||||
var packs = [];
|
||||
|
||||
fromDir(process.argv[2], /\.png$/, function (filename) {
|
||||
let file = filename.substring(process.argv[2].length);
|
||||
let parts = file.split("/");
|
||||
let pack = parts[1];
|
||||
let sticker = parts[2];
|
||||
if (!packs[pack]) {
|
||||
packs[pack] = [];
|
||||
}
|
||||
packs[pack].push(sticker);
|
||||
});
|
||||
|
||||
var result = { baseUrl: "", packs: [] };
|
||||
for (const pack of Object.keys(packs)) {
|
||||
const stickers = packs[pack];
|
||||
result.packs.push({ name: pack, stickers: stickers });
|
||||
}
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
22789
package-lock.json
generated
28
package.json
|
|
@ -1,28 +1,29 @@
|
|||
{
|
||||
"name": "keanuapp-weblite",
|
||||
"version": "0.1.9",
|
||||
"version": "0.1.19",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
"lint": "vue-cli-service lint",
|
||||
"create-sticker-config": "node ./create_sticker_config.js $1"
|
||||
},
|
||||
"dependencies": {
|
||||
"aes-js": "^3.1.2",
|
||||
"axios": "^0.21.0",
|
||||
"clean-insights-sdk": "^2.4.1",
|
||||
"clean-insights-sdk": "^2.4",
|
||||
"core-js": "^3.6.5",
|
||||
"data-uri-to-buffer": "^3.0.1",
|
||||
"dayjs": "^1.10.3",
|
||||
"fix-webm-duration": "^1.0.0",
|
||||
"image-resize": "^1.1.5",
|
||||
"image-size": "^1.0.0",
|
||||
"intersection-observer": "^0.11.0",
|
||||
"intersection-observer": "^0.12",
|
||||
"js-sha256": "^0.9.0",
|
||||
"json-web-key": "^0.4.0",
|
||||
"linkifyjs": "^2.1.9",
|
||||
"material-design-icons-iconfont": "^5.0.1",
|
||||
"matrix-js-sdk": "^9.4.1",
|
||||
"linkifyjs": "3.0.0-beta.3",
|
||||
"material-design-icons-iconfont": "^6.1",
|
||||
"matrix-js-sdk": "^12.4",
|
||||
"md-gum-polyfill": "^1.0.0",
|
||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
||||
"pretty-bytes": "^5.6.0",
|
||||
|
|
@ -34,7 +35,7 @@
|
|||
"vue": "^2.6.11",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-i18n": "^8.24.4",
|
||||
"vue-resize": "^0.5.0",
|
||||
"vue-resize": "^1.0",
|
||||
"vue-router": "^3.2.0",
|
||||
"vue-sanitize": "^0.2.1",
|
||||
"vue-swipeable-bottom-sheet": "^0.0.5",
|
||||
|
|
@ -47,11 +48,12 @@
|
|||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"copy-webpack-plugin": "^5.1.2",
|
||||
"eslint": "^7.0",
|
||||
"eslint-plugin-vue": "^7.0",
|
||||
"sass": "^1.19.0",
|
||||
"sass-loader": "^8.0.0",
|
||||
"vue-cli-plugin-vuetify": "~2.0.7",
|
||||
"sass-loader": "^10",
|
||||
"vue-cli-plugin-vuetify": "^2.4",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuetify-loader": "^1.3.0"
|
||||
},
|
||||
|
|
@ -84,4 +86,4 @@
|
|||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +1,29 @@
|
|||
{
|
||||
"name": "keanuapp-weblite",
|
||||
"version": "0.1.8",
|
||||
"version": "0.1.18",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
"lint": "vue-cli-service lint",
|
||||
"create-sticker-config": "node ./create_sticker_config.js $1"
|
||||
},
|
||||
"dependencies": {
|
||||
"aes-js": "^3.1.2",
|
||||
"axios": "^0.21.0",
|
||||
"clean-insights-sdk": "^2.4.1",
|
||||
"clean-insights-sdk": "^2.4",
|
||||
"core-js": "^3.6.5",
|
||||
"data-uri-to-buffer": "^3.0.1",
|
||||
"dayjs": "^1.10.3",
|
||||
"fix-webm-duration": "^1.0.0",
|
||||
"image-resize": "^1.1.5",
|
||||
"image-size": "^1.0.0",
|
||||
"intersection-observer": "^0.11.0",
|
||||
"intersection-observer": "^0.12",
|
||||
"js-sha256": "^0.9.0",
|
||||
"json-web-key": "^0.4.0",
|
||||
"linkifyjs": "^2.1.9",
|
||||
"material-design-icons-iconfont": "^5.0.1",
|
||||
"matrix-js-sdk": "^9.4.1",
|
||||
"linkifyjs": "3.0.0-beta.3",
|
||||
"material-design-icons-iconfont": "^6.1",
|
||||
"matrix-js-sdk": "^12.4",
|
||||
"md-gum-polyfill": "^1.0.0",
|
||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
||||
"pretty-bytes": "^5.6.0",
|
||||
|
|
@ -34,7 +35,7 @@
|
|||
"vue": "^2.6.11",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-i18n": "^8.24.4",
|
||||
"vue-resize": "^0.5.0",
|
||||
"vue-resize": "^1.0",
|
||||
"vue-router": "^3.2.0",
|
||||
"vue-sanitize": "^0.2.1",
|
||||
"vue-swipeable-bottom-sheet": "^0.0.5",
|
||||
|
|
@ -47,11 +48,12 @@
|
|||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"copy-webpack-plugin": "^5.1.2",
|
||||
"eslint": "^7.0",
|
||||
"eslint-plugin-vue": "^7.0",
|
||||
"sass": "^1.19.0",
|
||||
"sass-loader": "^8.0.0",
|
||||
"vue-cli-plugin-vuetify": "~2.0.7",
|
||||
"sass-loader": "^10",
|
||||
"vue-cli-plugin-vuetify": "^2.4",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuetify-loader": "^1.3.0"
|
||||
},
|
||||
|
|
@ -84,4 +86,4 @@
|
|||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 6.3 KiB |
|
|
@ -5,7 +5,6 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Titillium+Web:ital,wght@0,300;0,400;0,700;1,300&display=swap" rel="stylesheet">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
106
src/App.vue
|
|
@ -2,17 +2,73 @@
|
|||
<v-app>
|
||||
<v-main>
|
||||
<router-view />
|
||||
|
||||
<!-- Loading indicator -->
|
||||
<v-container
|
||||
fluid
|
||||
fill-height
|
||||
style="
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 20;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
"
|
||||
v-if="loading"
|
||||
>
|
||||
<v-row align="center" justify="center">
|
||||
<v-col class="text-center">
|
||||
<v-progress-circular
|
||||
indeterminate
|
||||
color="primary"
|
||||
></v-progress-circular>
|
||||
<div>{{ $t("menu.loading", { appName: appName }) }}</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "./assets/config";
|
||||
import stickers from "./plugins/stickers";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
};
|
||||
},
|
||||
beforeMount() {
|
||||
if (!this.$store.state.language) {
|
||||
// No language set, default to browser language?
|
||||
var browserLang = (
|
||||
navigator.language ||
|
||||
navigator.userLanguage ||
|
||||
""
|
||||
).toLowerCase();
|
||||
if (this.$i18n.messages[browserLang]) {
|
||||
this.$store.commit("setLanguage", browserLang);
|
||||
} else if (browserLang.includes("-")) {
|
||||
// Try with language name only.
|
||||
let lang = browserLang.split("-")[0];
|
||||
if (this.$i18n.messages[lang]) {
|
||||
this.$store.commit("setLanguage", lang);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set language
|
||||
this.$i18n.locale = this.$store.state.language || "en";
|
||||
},
|
||||
mounted() {
|
||||
if (window.location.protocol == "http" && !window.location.hostname.endsWith('.onion')) {
|
||||
if (
|
||||
window.location.protocol == "http" &&
|
||||
!window.location.hostname.endsWith(".onion")
|
||||
) {
|
||||
// Redirect to HTTPS
|
||||
window.location.href = window.location.href.replace("http:", "https:");
|
||||
return;
|
||||
|
|
@ -25,15 +81,35 @@ export default {
|
|||
})
|
||||
.catch((error) => {
|
||||
console.log("Error creating client", error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
} else {
|
||||
this.loading = false;
|
||||
}
|
||||
this.$config.promise.then(this.onConfigLoaded);
|
||||
},
|
||||
methods: {
|
||||
onConfigLoaded(config) {
|
||||
if (config.shortCodeStickers) {
|
||||
stickers.loadStickersFromConfig(config);
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
currentUser() {
|
||||
return this.$store.state.auth.user;
|
||||
},
|
||||
appName() {
|
||||
var translated = undefined;
|
||||
if (this.$config.appNames) {
|
||||
translated = this.$config.appNames[this.$i18n.locale];
|
||||
}
|
||||
return translated || this.$config.appName;
|
||||
},
|
||||
title() {
|
||||
var title = this.$t(config.appName);
|
||||
var title = this.appName;
|
||||
if (this.$matrix.notificationCount > 0) {
|
||||
title += " [" + this.$matrix.notificationCount + "]";
|
||||
}
|
||||
|
|
@ -44,8 +120,7 @@ export default {
|
|||
if (this.$matrix.currentRoom) {
|
||||
title +=
|
||||
" - " +
|
||||
(this.$matrix.currentRoom.summary.info.title ||
|
||||
this.$matrix.currentRoom.roomId);
|
||||
(this.$matrix.currentRoom.name || this.$matrix.currentRoom.roomId);
|
||||
} else if (this.$matrix.currentRoomId) {
|
||||
title += " - " + this.$matrix.currentRoomId;
|
||||
}
|
||||
|
|
@ -54,8 +129,25 @@ export default {
|
|||
},
|
||||
},
|
||||
watch: {
|
||||
title(title) {
|
||||
document.title = title;
|
||||
"$i18n.locale": {
|
||||
handler(val) {
|
||||
// Locale changed, check file if RTL
|
||||
var isRTL = this.$i18n.messages[val].language_is_rtl || false;
|
||||
if (isRTL) {
|
||||
this.$vuetify.rtl = true;
|
||||
document.documentElement.setAttribute("dir", "rtl");
|
||||
} else {
|
||||
this.$vuetify.rtl = false;
|
||||
document.documentElement.removeAttribute("dir");
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
title: {
|
||||
handler(title) {
|
||||
document.title = title;
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +1,18 @@
|
|||
{
|
||||
"appName": "Keanu Weblite",
|
||||
"product": "Convene",
|
||||
"appName": "Convene",
|
||||
"appNames": {
|
||||
"bo": "ཀེ་ཨ་ནུ་དྲ་གནས།",
|
||||
"si": "කීනු වෙබ්ලයිට්",
|
||||
"zh_Hans": "网络灯"
|
||||
},
|
||||
"productLink": "letsconvene.im",
|
||||
"defaultServer": "https://neo.keanu.im",
|
||||
"useShortCodeStickers": false,
|
||||
"rtl": false,
|
||||
"analytics": {
|
||||
"enabled": true,
|
||||
"config": {
|
||||
"server": "https://metrics.cleaninsights.org/cleaninsights.php",
|
||||
"siteid": 14,
|
||||
"siteId": 14,
|
||||
"timeout": 5,
|
||||
"persistEveryNTimes": 1,
|
||||
"debug": true,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,27 @@
|
|||
$admin-bg: black;
|
||||
$admin-fg: white;
|
||||
|
||||
.home {
|
||||
.v-card {
|
||||
background-color: white;
|
||||
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.25) !important;
|
||||
border-radius: 18px;
|
||||
padding-bottom: 10px;
|
||||
.v-item-group > div:not(:last-of-type):after {
|
||||
/* divider */
|
||||
position: absolute;
|
||||
content: " ";
|
||||
display: block;
|
||||
bottom: 0px;
|
||||
height: 1px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
min-height: 1px;
|
||||
background-color: #e1e1e1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
|
@ -52,6 +73,7 @@ $admin-fg: white;
|
|||
position: absolute;
|
||||
top: 10px;
|
||||
left: 40px;
|
||||
right: initial;
|
||||
color: white;
|
||||
background-color: black;
|
||||
font-size: 10px;
|
||||
|
|
@ -62,6 +84,10 @@ $admin-fg: white;
|
|||
text-align: center;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
[dir="rtl"] & {
|
||||
right: 40px;
|
||||
left: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-root {
|
||||
|
|
@ -140,6 +166,9 @@ $admin-fg: white;
|
|||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: 0 0 0px 20px;
|
||||
[dir="rtl"] & {
|
||||
padding: 0 20px 0px 0px;
|
||||
}
|
||||
margin: 6px 0;
|
||||
font-family: "Inter", sans-serif;
|
||||
font-weight: 300;
|
||||
|
|
@ -183,14 +212,20 @@ $admin-fg: white;
|
|||
.messageIn {
|
||||
margin: 8px;
|
||||
text-align: left;
|
||||
[dir="rtl"] & {
|
||||
text-align: right;
|
||||
}
|
||||
position: relative;
|
||||
.bubble {
|
||||
background-color: #eeeeee;
|
||||
background-color: #ededed;
|
||||
border-radius: 0px 10px 10px 10px;
|
||||
[dir="rtl"] & {
|
||||
border-radius: 10px 0px 10px 0px;
|
||||
}
|
||||
padding: 8px;
|
||||
border-width: 1px !important;
|
||||
border-style: solid !important;
|
||||
border-color: #eeeeee !important;
|
||||
border-color: #ededed !important;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
max-width: 70%;
|
||||
|
|
@ -222,6 +257,10 @@ $admin-fg: white;
|
|||
display: inline-block;
|
||||
vertical-align: top !important;
|
||||
margin-right: 10px;
|
||||
[dir="rtl"] & {
|
||||
margin-right: initial;
|
||||
margin-left: 10px;
|
||||
}
|
||||
top: 0;
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
|
@ -236,6 +275,10 @@ $admin-fg: white;
|
|||
color: rgba(#000000, 0.6);
|
||||
margin-left: 40px;
|
||||
margin-right: 8px;
|
||||
[dir="rtl"] & {
|
||||
margin-left: 8px;
|
||||
margin-right: 40px;
|
||||
}
|
||||
display: inline-block;
|
||||
}
|
||||
.time {
|
||||
|
|
@ -254,10 +297,16 @@ $admin-fg: white;
|
|||
.messageOut {
|
||||
margin: 8px;
|
||||
text-align: right;
|
||||
[dir="rtl"] & {
|
||||
text-align: left;
|
||||
}
|
||||
position: relative;
|
||||
.bubble {
|
||||
background-color: #e5e5e5;
|
||||
border-radius: 10px 10px 0 10px;
|
||||
[dir="rtl"] & {
|
||||
border-radius: 10px 10px 10px 0px;
|
||||
}
|
||||
padding: 8px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
|
@ -266,6 +315,9 @@ $admin-fg: white;
|
|||
.audio-bubble {
|
||||
background-color: #e5e5e5;
|
||||
border-radius: 10px 10px 0 10px;
|
||||
[dir="rtl"] & {
|
||||
border-radius: 10px 10px 10px 0px;
|
||||
}
|
||||
padding: 8px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
|
@ -276,6 +328,9 @@ $admin-fg: white;
|
|||
.video2-bubble {
|
||||
background-color: #e5e5e5;
|
||||
border-radius: 10px 10px 0 10px;
|
||||
[dir="rtl"] & {
|
||||
border-radius: 10px 10px 10px 0px;
|
||||
}
|
||||
}
|
||||
.bubble.image-bubble {
|
||||
padding: 0px;
|
||||
|
|
@ -285,6 +340,9 @@ $admin-fg: white;
|
|||
.v-image,
|
||||
video {
|
||||
border-radius: 10px 10px 0 10px;
|
||||
[dir="rtl"] & {
|
||||
border-radius: 10px 10px 10px 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.bubble.sticker-bubble {
|
||||
|
|
@ -298,6 +356,10 @@ $admin-fg: white;
|
|||
display: inline-block;
|
||||
vertical-align: bottom !important;
|
||||
margin-left: 10px;
|
||||
[dir="rtl"] & {
|
||||
margin-left: initial;
|
||||
margin-right: 10px;
|
||||
}
|
||||
bottom: 0;
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
|
@ -313,6 +375,10 @@ $admin-fg: white;
|
|||
display: inline-block;
|
||||
margin-left: 40px;
|
||||
margin-right: 8px;
|
||||
[dir="rtl"] & {
|
||||
margin-left: 8px;
|
||||
margin-right: 40px;
|
||||
}
|
||||
}
|
||||
.time {
|
||||
font-family: "Inter", sans-serif;
|
||||
|
|
@ -402,6 +468,19 @@ $admin-fg: white;
|
|||
user-select: text;
|
||||
}
|
||||
|
||||
.notice {
|
||||
font-family: "Inter", sans-serif;
|
||||
font-weight: 300;
|
||||
font-size: 15 * $chat-text-size;
|
||||
background-color: #090909;
|
||||
color: white;
|
||||
text-align: start;
|
||||
margin: 20px;
|
||||
user-select: text;
|
||||
padding: 16px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.audio-player {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
|
|
@ -440,6 +519,7 @@ $admin-fg: white;
|
|||
.avatar-operations-strut {
|
||||
position: relative;
|
||||
height: 0px;
|
||||
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
|
@ -447,9 +527,9 @@ $admin-fg: white;
|
|||
position: absolute;
|
||||
width: fit-content;
|
||||
background-color: white;
|
||||
height: 40px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 4px 4px 8px #888888;
|
||||
height: 44px;
|
||||
border-radius: 22px;
|
||||
box-shadow: 4px 4px 8px rgba(0,0,0,0.15);
|
||||
// &.incoming {
|
||||
// right: 30%;
|
||||
// }
|
||||
|
|
@ -466,7 +546,7 @@ $admin-fg: white;
|
|||
height: 40px;
|
||||
border-radius: 20px;
|
||||
padding: 0px 20px;
|
||||
box-shadow: 4px 4px 8px #888888;
|
||||
box-shadow: 4px 4px 8px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.message-operations-picker {
|
||||
|
|
@ -476,19 +556,19 @@ $admin-fg: white;
|
|||
|
||||
.quick-reaction-container {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
left: 50px;
|
||||
position: relative;
|
||||
vertical-align: bottom;
|
||||
transform: translateX(-20px) translateX(-100%);
|
||||
top: 18px;
|
||||
z-index: 2;
|
||||
background-color: #000000;
|
||||
border: 2px solid white;
|
||||
background-color: #f7f7f7;
|
||||
border: 1px solid rgba(white, 0.9);
|
||||
border-radius: 13px;
|
||||
height: 26px;
|
||||
width: max-content;
|
||||
padding: 0px 6px;
|
||||
.messageOut & {
|
||||
left: unset;
|
||||
right: 50px;
|
||||
transform: translateX(20px) translateX(100%);
|
||||
}
|
||||
.quick-reaction {
|
||||
position: relative;
|
||||
|
|
@ -497,7 +577,7 @@ $admin-fg: white;
|
|||
padding: 1px;
|
||||
font-size: 10px;
|
||||
&:hover {
|
||||
border: 1px solid #888888;
|
||||
//border: 1px solid #888888;
|
||||
background-color: #e2e2e2;
|
||||
}
|
||||
.quick-reaction-count {
|
||||
|
|
@ -506,7 +586,7 @@ $admin-fg: white;
|
|||
}
|
||||
}
|
||||
.sent .quick-reaction-count {
|
||||
color: white;
|
||||
color: black;
|
||||
font-weight: 700;
|
||||
// background-color: palegreen;
|
||||
}
|
||||
|
|
@ -568,6 +648,9 @@ $admin-fg: white;
|
|||
top: -4px;
|
||||
background: white;
|
||||
transform: translate(-50%, 0);
|
||||
[dir="rtl"] & {
|
||||
transform: translate(50%, 0);
|
||||
}
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
content: attr(title);
|
||||
|
|
@ -597,6 +680,9 @@ $admin-fg: white;
|
|||
top: -8px;
|
||||
background: white;
|
||||
transform: translate(-50%, 0);
|
||||
[dir="rtl"] & {
|
||||
transform: translate(50%, 0);
|
||||
}
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
content: attr(title);
|
||||
|
|
@ -604,7 +690,7 @@ $admin-fg: white;
|
|||
}
|
||||
|
||||
.room-info {
|
||||
background-color: #e0e0e0;
|
||||
background-color: #e8e8e8;
|
||||
height: 100%;
|
||||
.chat-header {
|
||||
background-color: transparent;
|
||||
|
|
@ -686,6 +772,11 @@ $admin-fg: white;
|
|||
font-size: 14 * $chat-text-size;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
[dir="rtl"] & {
|
||||
margin-left: initial;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
|
@ -697,12 +788,24 @@ $admin-fg: white;
|
|||
margin: 10px 0px;
|
||||
font-weight: bold;
|
||||
font-size: 12 * $chat-text-size;
|
||||
[dir="rtl"] & {
|
||||
right: 0px;
|
||||
left: unset;
|
||||
.v-icon {
|
||||
// Mirror the icon
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-button-right {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
[dir="rtl"] & {
|
||||
left: 0px;
|
||||
right: unset;
|
||||
}
|
||||
margin: 10px 0px;
|
||||
font-weight: bold;
|
||||
font-size: 12 * $chat-text-size;
|
||||
|
|
@ -731,6 +834,10 @@ $admin-fg: white;
|
|||
font-size: 14 * $chat-text-size;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
[dir="rtl"] & {
|
||||
margin-left: initial;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -871,7 +978,7 @@ $admin-fg: white;
|
|||
}
|
||||
|
||||
.created-room-welcome-header {
|
||||
background-color: #e0e0e0;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 25px;
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
|
|
@ -883,7 +990,7 @@ $admin-fg: white;
|
|||
left: 20%;
|
||||
right: 20%;
|
||||
background-color: #888888;
|
||||
height: 50px;
|
||||
height: 40px;
|
||||
border-radius: 25px;
|
||||
color: white;
|
||||
text-align: center;
|
||||
|
|
@ -891,12 +998,40 @@ $admin-fg: white;
|
|||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.avatar-22 {
|
||||
font-size: 14 * $chat-text-size !important;
|
||||
}
|
||||
|
||||
.avatar-32 {
|
||||
font-size: 18 * $chat-text-size !important;
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-48 {
|
||||
font-size: 27 * $chat-text-size !important;
|
||||
}
|
||||
|
||||
.avatar-32.clickable,
|
||||
.avatar-48.clickable,
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new-room {
|
||||
font-family: "Inter", sans-serif;
|
||||
font-size: 16px !important;
|
||||
font-weight: 600 !important;
|
||||
text-decoration: underline;
|
||||
color: black;
|
||||
&::after {
|
||||
content: "\00a0\00a0>";
|
||||
display: inline-block;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.recent-emoji {
|
||||
color: black;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,54 @@
|
|||
$background: #ffffff;
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter&display=swap');
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
src: url("~@/assets/fonts/inter/Inter-Light.ttf") format("truetype");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
src: url("~@/assets/fonts/inter/Inter-Regular.ttf") format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
src: url("~@/assets/fonts/inter/Inter-Bold.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Poppins";
|
||||
src: url("~@/assets/fonts/poppins/Poppins-Regular.ttf") format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Poppins";
|
||||
src: url("~@/assets/fonts/poppins/Poppins-SemiBold.ttf") format("truetype");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Poppins";
|
||||
src: url("~@/assets/fonts/poppins/Poppins-Bold.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Poppins";
|
||||
src: url("~@/assets/fonts/poppins/Poppins-ExtraBold.ttf") format("truetype");
|
||||
font-weight: 800;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
$chat-background: $background;
|
||||
$chat-standard-padding: 32px;
|
||||
|
|
@ -78,7 +125,8 @@ body { position:absolute; top:0; bottom:0; right:0; left:0; }
|
|||
color: white;
|
||||
border: none;
|
||||
border-radius: $chat-standard-padding / 2;
|
||||
height: $chat-standard-padding;
|
||||
height: $chat-standard-padding !important;
|
||||
min-height: $chat-standard-padding !important;
|
||||
margin-top: $chat-standard-padding-xs;
|
||||
margin-bottom: $chat-standard-padding-xs;
|
||||
}
|
||||
|
|
|
|||
BIN
src/assets/fonts/inter/Inter-Bold.ttf
Normal file
BIN
src/assets/fonts/inter/Inter-Light.ttf
Normal file
BIN
src/assets/fonts/inter/Inter-Regular.ttf
Normal file
93
src/assets/fonts/inter/OFL.txt
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
Copyright (c) 2016-2019 The Inter Project Authors (me@rsms.me)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
src/assets/fonts/poppins/OFL.txt
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
BIN
src/assets/fonts/poppins/Poppins-Bold.ttf
Normal file
BIN
src/assets/fonts/poppins/Poppins-ExtraBold.ttf
Normal file
BIN
src/assets/fonts/poppins/Poppins-Regular.ttf
Normal file
BIN
src/assets/fonts/poppins/Poppins-SemiBold.ttf
Normal file
14
src/assets/icons/edit.vue
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<svg
|
||||
width="20"
|
||||
height="21"
|
||||
viewBox="0 0 20 21"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M18.8271 19.027H7.68959L18.9785 7.73448C19.2199 7.49799 19.4117 7.2157 19.5426 6.90414C19.6736 6.59258 19.741 6.25802 19.741 5.92006C19.741 5.5821 19.6736 5.24754 19.5426 4.93599C19.4117 4.62443 19.2199 4.34213 18.9785 4.10565L16.3943 1.52154C16.1571 1.28145 15.8746 1.09084 15.5631 0.960752C15.2516 0.830659 14.9175 0.763672 14.5799 0.763672C14.2424 0.763672 13.9082 0.830659 13.5968 0.960752C13.2853 1.09084 13.0028 1.28145 12.7655 1.52154L0.214124 14.0729C0.145696 14.1419 0.0915588 14.2237 0.0548167 14.3137C0.0180747 14.4036 -0.00054944 14.5 1.23405e-05 14.5971V19.7654C1.23405e-05 19.9612 0.0777991 20.149 0.21626 20.2874C0.354722 20.4259 0.542516 20.5037 0.73833 20.5037H18.8271C19.0229 20.5037 19.2107 20.4259 19.3492 20.2874C19.4876 20.149 19.5654 19.9612 19.5654 19.7654C19.5654 19.5695 19.4876 19.3817 19.3492 19.2433C19.2107 19.1048 19.0229 19.027 18.8271 19.027ZM13.8102 2.56626C14.0167 2.36665 14.2927 2.25507 14.5799 2.25507C14.8671 2.25507 15.1431 2.36665 15.3496 2.56626L17.9337 5.15037C18.0361 5.25072 18.1174 5.37049 18.1729 5.50265C18.2284 5.63481 18.257 5.77672 18.257 5.92006C18.257 6.06341 18.2284 6.20531 18.1729 6.33748C18.1174 6.46964 18.0361 6.5894 17.9337 6.68976L16.0879 8.53555L11.9792 4.40098L13.8102 2.56626ZM4.79907 19.027H1.47665V14.9035L10.9345 5.442L15.0617 9.56919L5.60015 19.027H4.79907Z"
|
||||
fill="black"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
14
src/assets/icons/globe.vue
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<svg
|
||||
width="23"
|
||||
height="23"
|
||||
viewBox="0 0 23 23"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M21.0495 17.2908C22.1446 15.5067 22.7229 13.4536 22.72 11.3602L22.72 11.36L22.72 11.3598C22.7229 9.26641 22.1446 7.21331 21.0495 5.42919L21.0433 5.41946C20.0273 3.76355 18.6036 2.39584 16.9084 1.44703C15.2131 0.49822 13.3028 7.9387e-06 11.36 0C9.4173 -7.93851e-06 7.50702 0.498188 5.81172 1.44698C4.11643 2.39578 2.69277 3.76347 1.67678 5.41938L1.67047 5.42924C0.578083 7.21458 8.10833e-06 9.26694 0 11.36C-8.10816e-06 13.453 0.57805 15.5054 1.67042 17.2907L1.67684 17.3007C2.69283 18.9566 4.11649 20.3243 5.81177 21.273C7.50706 22.2218 9.41733 22.72 11.3601 22.72C13.3028 22.72 15.213 22.2218 16.9083 21.273C18.6036 20.3242 20.0272 18.9565 21.0432 17.3006L21.0495 17.2908ZM12.8121 20.773C12.5956 20.9819 12.3464 21.154 12.0745 21.2825C11.8512 21.3886 11.6072 21.4436 11.36 21.4436C11.1128 21.4436 10.8688 21.3886 10.6455 21.2825C10.128 21.0181 9.68489 20.6282 9.35685 20.1484C8.68691 19.1804 8.19042 18.1031 7.8896 16.9649C9.04523 16.8938 10.202 16.8576 11.36 16.8563C12.5175 16.8563 13.6744 16.8925 14.8306 16.9649C14.6641 17.5502 14.4561 18.1228 14.2082 18.6784C13.8818 19.4599 13.4078 20.1711 12.8121 20.7731V20.773ZM1.29861 11.9982H5.88065C5.91049 13.2768 6.04907 14.5505 6.29491 15.8055C5.04236 15.9157 3.79301 16.0671 2.54686 16.2597C1.81713 14.9505 1.39057 13.4942 1.29861 11.9982ZM2.54685 6.46034C3.79251 6.65337 5.0423 6.80479 6.29622 6.91459C6.0499 8.16956 5.91101 9.44324 5.88106 10.7218H1.29861C1.39056 9.22581 1.81712 7.76952 2.54684 6.46034H2.54685ZM9.90793 1.94694C10.1244 1.73806 10.3735 1.56598 10.6455 1.4375C10.8688 1.33142 11.1128 1.27639 11.36 1.27639C11.6072 1.27639 11.8512 1.33142 12.0745 1.4375C12.592 1.70187 13.0351 2.09177 13.3631 2.57154C14.0331 3.53962 14.5296 4.61685 14.8304 5.75506C13.6748 5.82617 12.518 5.8624 11.36 5.86374C10.2025 5.86372 9.04565 5.82749 7.88939 5.75504C8.05591 5.16983 8.26389 4.59722 8.51178 4.04156C8.83818 3.26009 9.31222 2.5489 9.90793 1.94694ZM21.4214 10.7218H16.8394C16.8095 9.44324 16.6709 8.16954 16.4251 6.91448C17.6777 6.80426 18.927 6.65288 20.1732 6.46033C20.9029 7.76952 21.3295 9.2258 21.4214 10.7218ZM7.57867 15.7063C7.32906 14.4851 7.18808 13.2442 7.15739 11.9982H15.5628C15.5323 13.2442 15.3916 14.4851 15.1422 15.7063C13.8829 15.6237 12.6221 15.5815 11.36 15.5799C10.0988 15.5799 8.83835 15.622 7.57867 15.7063ZM15.1413 7.01371C15.3909 8.23488 15.5319 9.47576 15.5626 10.7218H7.15725C7.18766 9.47576 7.32838 8.23486 7.57776 7.01365C8.83712 7.09628 10.0979 7.13844 11.36 7.14013C12.6212 7.14013 13.8817 7.09799 15.1413 7.01372V7.01371ZM16.8389 11.9982H21.4214C21.3294 13.4942 20.9029 14.9505 20.1732 16.2597C18.9275 16.0666 17.6777 15.9152 16.4238 15.8054C16.6701 14.5504 16.809 13.2768 16.8389 11.9982ZM19.4078 5.28553C18.3187 5.44267 17.2264 5.56708 16.1307 5.65876C15.9338 4.92561 15.6798 4.209 15.3712 3.51546C15.0893 2.87725 14.7347 2.27374 14.3143 1.71694C16.3458 2.33996 18.1285 3.58894 19.4078 5.28553ZM4.22979 4.22979C5.39513 3.06335 6.8288 2.20057 8.40514 1.71709C8.38122 1.74806 8.35662 1.77766 8.33305 1.80928C7.52281 2.97565 6.93263 4.28032 6.59161 5.65895C5.49592 5.56615 4.40277 5.44168 3.31216 5.28553C3.59344 4.91297 3.90004 4.56022 4.22979 4.22979ZM3.31216 17.4345C4.40126 17.2773 5.49363 17.1529 6.58927 17.0612C6.78619 17.7944 7.04015 18.511 7.34885 19.2045C7.63069 19.8427 7.98531 20.4463 8.40567 21.0031C6.37418 20.38 4.59149 19.1311 3.31216 17.4345V17.4345ZM18.4902 18.4902C17.3249 19.6567 15.8912 20.5194 14.3149 21.0029C14.3388 20.9719 14.3634 20.9423 14.387 20.9107C15.1972 19.7443 15.7874 18.4397 16.1284 17.061C17.2241 17.1538 18.3172 17.2783 19.4079 17.4345C19.1266 17.807 18.82 18.1598 18.4902 18.4902V18.4902Z"
|
||||
fill="black"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
45
src/assets/icons/password.vue
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<template>
|
||||
<svg
|
||||
width="24"
|
||||
height="22"
|
||||
viewBox="0 0 24 22"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M20.685 21.0489H8.32761C6.80505 21.0489 5.56665 19.8096 5.56665 18.287V16.1513C5.56665 14.6288 6.80505 13.3894 8.32761 13.3894H20.6847C22.2073 13.3894 23.4466 14.6288 23.4466 16.1513V18.287C23.4466 19.8096 22.2073 21.0489 20.685 21.0489ZM8.32761 14.8297C7.59929 14.8297 7.00665 15.423 7.00665 16.1516V18.2873C7.00665 19.0156 7.59929 19.6092 8.32761 19.6092H20.6847C21.413 19.6092 22.0066 19.016 22.0066 18.2873V16.1516C22.0066 15.4233 21.4134 14.8297 20.6847 14.8297H8.32761Z"
|
||||
fill="black"
|
||||
fill-opacity="0.7"
|
||||
/>
|
||||
<path
|
||||
d="M9.75095 18.2263C10.3066 18.2263 10.757 17.7758 10.757 17.2202C10.757 16.6646 10.3066 16.2141 9.75095 16.2141C9.19531 16.2141 8.74487 16.6646 8.74487 17.2202C8.74487 17.7758 9.19531 18.2263 9.75095 18.2263Z"
|
||||
fill="black"
|
||||
fill-opacity="0.7"
|
||||
/>
|
||||
<path
|
||||
d="M12.7998 18.2263C13.3554 18.2263 13.8059 17.7758 13.8059 17.2202C13.8059 16.6646 13.3554 16.2141 12.7998 16.2141C12.2441 16.2141 11.7937 16.6646 11.7937 17.2202C11.7937 17.7758 12.2441 18.2263 12.7998 18.2263Z"
|
||||
fill="black"
|
||||
fill-opacity="0.7"
|
||||
/>
|
||||
<path
|
||||
d="M15.8473 18.2263C16.4033 18.2263 16.854 17.7756 16.854 17.2196C16.854 16.6636 16.4033 16.2129 15.8473 16.2129C15.2913 16.2129 14.8406 16.6636 14.8406 17.2196C14.8406 17.7756 15.2913 18.2263 15.8473 18.2263Z"
|
||||
fill="black"
|
||||
fill-opacity="0.7"
|
||||
/>
|
||||
<path
|
||||
d="M18.8964 16.2144C19.4512 16.2144 19.9015 16.6643 19.9015 17.2195C19.9015 17.7753 19.4516 18.2262 18.8964 18.2262C18.3396 18.2262 17.8896 17.7753 17.8896 17.2195C17.8893 16.6643 18.3396 16.2144 18.8964 16.2144Z"
|
||||
fill="black"
|
||||
fill-opacity="0.7"
|
||||
/>
|
||||
<path
|
||||
d="M9.8905 8.91388C9.49306 8.91388 9.1705 8.59132 9.1705 8.19388V5.0102C9.1705 3.5654 7.99578 2.39068 6.55098 2.39068C5.10618 2.39068 3.93146 3.5654 3.93146 5.0102V8.19388C3.93146 8.59132 3.6089 8.91388 3.21146 8.91388C2.81402 8.91388 2.49146 8.59132 2.49146 8.19388V5.0102C2.49146 2.77148 4.31194 0.950684 6.55098 0.950684C8.79002 0.950684 10.6105 2.77116 10.6105 5.0102V8.19388C10.6105 8.59164 10.2879 8.91388 9.8905 8.91388Z"
|
||||
fill="black"
|
||||
fill-opacity="0.7"
|
||||
/>
|
||||
<path
|
||||
d="M4.17562 18.1775H2.62874C1.4841 18.1775 0.553223 17.2457 0.553223 16.1001V9.56474C0.553223 8.41914 1.4841 7.4873 2.62874 7.4873H10.8956C12.0403 7.4873 12.9711 8.41914 12.9711 9.56474V13.2201C12.9711 13.6175 12.6486 13.9401 12.2511 13.9401C11.8537 13.9401 11.5311 13.6175 11.5311 13.2201V9.56474C11.5311 9.21307 11.246 8.9273 10.8956 8.9273H2.62874C2.27802 8.9273 1.99322 9.21338 1.99322 9.56474V16.1001C1.99322 16.4518 2.27834 16.7375 2.62874 16.7375H4.17562C4.57306 16.7375 4.89562 17.0601 4.89562 17.4575C4.89562 17.855 4.57306 18.1775 4.17562 18.1775Z"
|
||||
fill="black"
|
||||
fill-opacity="0.7"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
10
src/assets/icons/timer.svg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.08333 5.77777H0.361111C0.161778 5.77777 0 5.93955 0 6.13888C0 6.33822 0.161778 6.49999 0.361111 6.49999H1.08333C1.28267 6.49999 1.44444 6.33822 1.44444 6.13888C1.44444 5.93955 1.28267 5.77777 1.08333 5.77777Z" fill="black"/>
|
||||
<path d="M1.08333 5.77777H0.361111C0.161778 5.77777 0 5.93955 0 6.13888C0 6.33822 0.161778 6.49999 0.361111 6.49999H1.08333C1.28267 6.49999 1.44444 6.33822 1.44444 6.13888C1.44444 5.93955 1.28267 5.77777 1.08333 5.77777Z" fill="black"/>
|
||||
<path d="M1.08333 8.66669H0.361111C0.161778 8.66669 0 8.82846 0 9.0278C0 9.22713 0.161778 9.38891 0.361111 9.38891H1.08333C1.28267 9.38891 1.44444 9.22713 1.44444 9.0278C1.44444 8.82846 1.28267 8.66669 1.08333 8.66669Z" fill="black"/>
|
||||
<path d="M1.08333 8.66669H0.361111C0.161778 8.66669 0 8.82846 0 9.0278C0 9.22713 0.161778 9.38891 0.361111 9.38891H1.08333C1.28267 9.38891 1.44444 9.22713 1.44444 9.0278C1.44444 8.82846 1.28267 8.66669 1.08333 8.66669Z" fill="black"/>
|
||||
<path d="M11.6553 4.02206L12.1724 3.50494C12.3132 3.36411 12.3132 3.13517 12.1724 2.99433C12.0316 2.8535 11.8026 2.8535 11.6618 2.99433L11.1447 3.51144C10.2766 2.75239 9.1658 2.26633 7.94453 2.18472V0.722222H9.02786C9.22719 0.722222 9.38897 0.560444 9.38897 0.361111C9.38897 0.161778 9.22719 0 9.02786 0H6.13897C5.93964 0 5.77786 0.161778 5.77786 0.361111C5.77786 0.560444 5.93964 0.722222 6.13897 0.722222H7.2223V2.18472C4.40419 2.37178 2.16675 4.71828 2.16675 7.58333C2.16675 10.5704 4.5963 13 7.58341 13C10.5705 13 13.0001 10.5704 13.0001 7.58333C13.0001 6.21978 12.4895 4.97611 11.6553 4.02206ZM7.58341 12.2778C4.99497 12.2778 2.88897 10.1718 2.88897 7.58333C2.88897 4.99489 4.99497 2.88889 7.58341 2.88889C10.1719 2.88889 12.2779 4.99489 12.2779 7.58333C12.2779 10.1718 10.1719 12.2778 7.58341 12.2778Z" fill="black"/>
|
||||
<path d="M11.6553 4.02206L12.1724 3.50494C12.3132 3.36411 12.3132 3.13517 12.1724 2.99433C12.0316 2.8535 11.8026 2.8535 11.6618 2.99433L11.1447 3.51144C10.2766 2.75239 9.1658 2.26633 7.94453 2.18472V0.722222H9.02786C9.22719 0.722222 9.38897 0.560444 9.38897 0.361111C9.38897 0.161778 9.22719 0 9.02786 0H6.13897C5.93964 0 5.77786 0.161778 5.77786 0.361111C5.77786 0.560444 5.93964 0.722222 6.13897 0.722222H7.2223V2.18472C4.40419 2.37178 2.16675 4.71828 2.16675 7.58333C2.16675 10.5704 4.5963 13 7.58341 13C10.5705 13 13.0001 10.5704 13.0001 7.58333C13.0001 6.21978 12.4895 4.97611 11.6553 4.02206ZM7.58341 12.2778C4.99497 12.2778 2.88897 10.1718 2.88897 7.58333C2.88897 4.99489 4.99497 2.88889 7.58341 2.88889C10.1719 2.88889 12.2779 4.99489 12.2779 7.58333C12.2779 10.1718 10.1719 12.2778 7.58341 12.2778Z" fill="black"/>
|
||||
<path d="M7.58328 4.33331C7.38395 4.33331 7.22217 4.49509 7.22217 4.69442V7.58331C7.22217 7.78265 7.38395 7.94442 7.58328 7.94442C7.78261 7.94442 7.94439 7.78265 7.94439 7.58331V4.69442C7.94439 4.49509 7.78261 4.33331 7.58328 4.33331Z" fill="black"/>
|
||||
<path d="M7.58328 4.33331C7.38395 4.33331 7.22217 4.49509 7.22217 4.69442V7.58331C7.22217 7.78265 7.38395 7.94442 7.58328 7.94442C7.78261 7.94442 7.94439 7.78265 7.94439 7.58331V4.69442C7.94439 4.49509 7.78261 4.33331 7.58328 4.33331Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
5
src/assets/icons/trash.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="28" height="33" viewBox="0 0 28 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.4989 0C9.22596 0 8.16477 1.0612 8.16477 2.33409V4.66819C5.83219 4.66819 3.49962 4.66819 1.16705 4.66819C0.52251 4.66819 0 5.1907 0 5.83524C0 6.47977 0.52251 7.00228 1.16705 7.00228H2.32953V29.1762C2.32953 31.0956 3.91123 32.6773 5.83067 32.6773H22.1693C24.0888 32.6773 25.6705 31.0956 25.6705 29.1762V7.00228H26.833C27.4775 7.00228 28 6.47977 28 5.83524C28 5.1907 27.4775 4.66819 26.833 4.66819C24.5004 4.66819 22.1678 4.66819 19.8352 4.66819V2.33409C19.8352 1.0612 18.774 0 17.5011 0H10.4989ZM10.4989 2.33409H17.5011V4.66819H10.4989V2.33409ZM4.66362 7.00228C10.8879 7.00228 17.1121 7.00228 23.3364 7.00228V29.1762C23.3364 29.8429 22.8361 30.3432 22.1693 30.3432H5.83067C5.16394 30.3432 4.66362 29.8429 4.66362 29.1762V7.00228Z" fill="white"/>
|
||||
<path d="M10.4991 12.8374C10.1896 12.8374 9.89272 12.9604 9.67385 13.1792C9.45499 13.3981 9.33203 13.6949 9.33203 14.0044V23.3408C9.33203 23.6503 9.45499 23.9472 9.67385 24.1661C9.89272 24.3849 10.1896 24.5079 10.4991 24.5079C10.8086 24.5079 11.1054 24.3849 11.3243 24.1661C11.5432 23.9472 11.6661 23.6503 11.6661 23.3408V14.0044C11.6661 13.6949 11.5432 13.3981 11.3243 13.1792C11.1054 12.9604 10.8086 12.8374 10.4991 12.8374Z" fill="white"/>
|
||||
<path d="M17.5015 12.8374C17.192 12.8374 16.8952 12.9604 16.6763 13.1792C16.4574 13.3981 16.3345 13.6949 16.3345 14.0044V23.3408C16.3345 23.6503 16.4574 23.9472 16.6763 24.1661C16.8952 24.3849 17.192 24.5079 17.5015 24.5079C17.811 24.5079 18.1079 24.3849 18.3267 24.1661C18.5456 23.9472 18.6686 23.6503 18.6686 23.3408V14.0044C18.6686 13.6949 18.5456 13.3981 18.3267 13.1792C18.1079 12.9604 17.811 12.8374 17.5015 12.8374Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
5
src/assets/icons/trash_black.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="28" height="33" viewBox="0 0 28 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.4989 0C9.22596 0 8.16477 1.0612 8.16477 2.33409V4.66819C5.83219 4.66819 3.49962 4.66819 1.16705 4.66819C0.52251 4.66819 0 5.1907 0 5.83524C0 6.47977 0.52251 7.00228 1.16705 7.00228H2.32953V29.1762C2.32953 31.0956 3.91123 32.6773 5.83067 32.6773H22.1693C24.0888 32.6773 25.6705 31.0956 25.6705 29.1762V7.00228H26.833C27.4775 7.00228 28 6.47977 28 5.83524C28 5.1907 27.4775 4.66819 26.833 4.66819C24.5004 4.66819 22.1678 4.66819 19.8352 4.66819V2.33409C19.8352 1.0612 18.774 0 17.5011 0H10.4989ZM10.4989 2.33409H17.5011V4.66819H10.4989V2.33409ZM4.66362 7.00228C10.8879 7.00228 17.1121 7.00228 23.3364 7.00228V29.1762C23.3364 29.8429 22.8361 30.3432 22.1693 30.3432H5.83067C5.16394 30.3432 4.66362 29.8429 4.66362 29.1762V7.00228Z" fill="currentColor"/>
|
||||
<path d="M10.4991 12.8374C10.1896 12.8374 9.89272 12.9604 9.67385 13.1792C9.45499 13.3981 9.33203 13.6949 9.33203 14.0044V23.3408C9.33203 23.6503 9.45499 23.9472 9.67385 24.1661C9.89272 24.3849 10.1896 24.5079 10.4991 24.5079C10.8086 24.5079 11.1054 24.3849 11.3243 24.1661C11.5432 23.9472 11.6661 23.6503 11.6661 23.3408V14.0044C11.6661 13.6949 11.5432 13.3981 11.3243 13.1792C11.1054 12.9604 10.8086 12.8374 10.4991 12.8374Z" fill="currentColor"/>
|
||||
<path d="M17.5015 12.8374C17.192 12.8374 16.8952 12.9604 16.6763 13.1792C16.4574 13.3981 16.3345 13.6949 16.3345 14.0044V23.3408C16.3345 23.6503 16.4574 23.9472 16.6763 24.1661C16.8952 24.3849 17.192 24.5079 17.5015 24.5079C17.811 24.5079 18.1079 24.3849 18.3267 24.1661C18.5456 23.9472 18.6686 23.6503 18.6686 23.3408V14.0044C18.6686 13.6949 18.5456 13.3981 18.3267 13.1792C18.1079 12.9604 17.811 12.8374 17.5015 12.8374Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
15
src/assets/icons/user.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.9999 0.639893C5.72789 0.639893 0.639893 5.72789 0.639893 11.9999C0.639893 18.2719 5.72789 23.3599 11.9999 23.3599C18.2719 23.3599 23.3599 18.2719 23.3599 11.9999C23.3599 5.72789 18.2719 0.639893 11.9999 0.639893ZM5.27989 19.0719C7.13589 16.6719 9.50389 15.3599 11.9999 15.3599C14.4959 15.3599 16.8639 16.6719 18.7199 19.0719C16.9599 20.7359 14.5919 21.7599 11.9999 21.7599C9.40789 21.7599 7.03989 20.7359 5.27989 19.0719ZM11.9999 13.4399C10.1439 13.4399 8.67189 11.9359 8.67189 10.1119C8.67189 8.28789 10.1439 6.78389 11.9999 6.78389C13.8559 6.78389 15.3279 8.28789 15.3279 10.1119C15.3279 11.9359 13.8559 13.4399 11.9999 13.4399ZM19.8079 17.8559C18.3679 16.0639 16.6399 14.8159 14.7839 14.2079C16.0959 13.3119 16.9599 11.8079 16.9599 10.1119C16.9599 7.39189 14.7519 5.18389 12.0319 5.18389C9.31189 5.18389 7.10389 7.39189 7.10389 10.1119C7.10389 11.8079 7.96789 13.3119 9.27989 14.2079C7.42389 14.8159 5.69589 16.0639 4.25589 17.8559C3.00789 16.2239 2.30389 14.1759 2.30389 11.9999C2.30389 6.62389 6.68789 2.23989 12.0639 2.23989C17.4399 2.23989 21.7599 6.62389 21.7599 11.9999C21.7599 14.2079 21.0239 16.2239 19.8079 17.8559Z"
|
||||
fill="black"
|
||||
fill-opacity="0.7"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
|
@ -1 +1,5 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>
|
||||
<svg width="39" height="39" viewBox="0 0 39 39" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="19.5" cy="19.5" r="19.5" fill="#242424"/>
|
||||
<circle cx="19.5" cy="19.5" r="19.5" fill="#8C9EFF"/>
|
||||
<path d="M11.0143 27.6034C12.3294 27.6034 13.5598 27.0986 14.4507 26.2572L14.4931 26.2151C14.7052 26.0048 14.8749 25.8365 15.0446 25.5841L19.4992 20.7881L23.9539 25.6262C24.1236 25.8365 24.3357 26.0469 24.5054 26.2572L24.5478 26.2993C25.4387 27.1407 26.669 27.6455 27.9842 27.6455C30.7843 27.6455 33.0752 25.3737 33.0752 22.5971C33.0752 19.8205 30.7843 17.5487 27.9842 17.5487C26.0751 17.5487 24.3781 18.6005 23.5296 20.1991L21.7902 18.306L22.9781 17.0018C23.9539 16.0342 24.5902 14.7301 24.5902 13.2997C24.5902 10.5231 22.2993 8.2513 19.4992 8.2513C16.6992 8.2513 14.4083 10.5231 14.4083 13.2997C14.4083 14.7301 15.0446 16.0342 16.0204 16.9598L17.2083 18.2639L15.4689 20.1571C14.6204 18.6005 12.9234 17.5067 11.0143 17.5067C8.21424 17.5067 5.9233 19.7784 5.9233 22.5551C5.9233 25.3317 8.21424 27.6034 11.0143 27.6034ZM27.9842 20.8723C28.9176 20.8723 29.6812 21.6295 29.6812 22.5551C29.6812 23.4806 28.9176 24.2378 27.9842 24.2378C27.56 24.2378 27.1781 24.0696 26.8812 23.8172L26.5418 23.4806C26.3721 23.2282 26.2448 22.8916 26.2448 22.5551C26.2872 21.6295 27.0509 20.8723 27.9842 20.8723ZM19.4992 11.6169C20.4326 11.6169 21.1962 12.3741 21.1962 13.2997C21.1962 13.6362 21.1114 13.9728 20.8993 14.2252L20.6023 14.5618C20.3053 14.8142 19.9235 14.9825 19.4992 14.9825C19.075 14.9825 18.6932 14.8142 18.3962 14.5618L18.0992 14.2252C17.9295 13.9728 17.8023 13.6362 17.8023 13.2997C17.8023 12.3741 18.5659 11.6169 19.4992 11.6169ZM11.0143 20.8723C11.9476 20.8723 12.7113 21.6295 12.7113 22.5551C12.7113 22.8916 12.6264 23.2282 12.4143 23.4806L12.0749 23.8172C11.7779 24.0696 11.3961 24.2379 10.9719 24.2379C10.0385 24.2379 9.27486 23.4806 9.27486 22.5551C9.27486 21.6295 10.0809 20.8723 11.0143 20.8723Z" fill="#242424"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 539 B After Width: | Height: | Size: 1.9 KiB |
|
|
@ -136,7 +136,7 @@
|
|||
{
|
||||
"quote":"I didn’t fail the test. I just found 100 ways to do it wrong.","author":"Benjamin Franklin"},
|
||||
{
|
||||
"quote":"In order to succeed, your desire for success should be greater than your fear of failure.","author":"Bill Cosby"},
|
||||
"quote":"In order to succeed, your desire for success should be greater than your fear of failure.","author":"Unknown"},
|
||||
{
|
||||
"quote":"A person who never made a mistake never tried anything new.","author":" Albert Einstein"},
|
||||
{
|
||||
|
|
@ -206,4 +206,4 @@
|
|||
{
|
||||
"quote":"If you can dream it, you can achieve it.","author":"Zig Ziglar"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
230
src/assets/translations/bo.json
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
{
|
||||
"fallbacks": {
|
||||
"download_name": "ཕབ་ལེན།",
|
||||
"original_text": "<མ་ཡིག>",
|
||||
"audio_file": "སྒྲ་ཟློས་ཡིག་ཆ།",
|
||||
"video_file": "བརྙན་ཟློས་ཡིག་ཆ།"
|
||||
},
|
||||
"room_info": {
|
||||
"title": "ཁ་བརྡ་ཁང་གི་ཞིབ་ཕྲའི་གནས་ཚུལ།",
|
||||
"version_info": "གྷར་ཌིན་ལས་འཆར་གྱིས་ནུས་ཤུགས་བསྩལ། ཐོན་རིམ། : {version}",
|
||||
"leave_room_info": "གསལ་བཤད། གོ་རིམ་འདི་ཕྱིར་ཟློག་ཐབས་མེད། ཁྱེད་རང་ཕྱིར་ཐོན་རྒྱུ་ཡིན་མིན་དང་གླེང་མོལ་ཁག་གཏན་དུ་གསུབ་རྒྱུ་ཡིན་མིན་ཁག་ཐེག་བྱོས།",
|
||||
"leave_room": "ཕྱིར་ཐོན།",
|
||||
"view_profile": "ལྟ་ཞིབ།",
|
||||
"identity_temporary": "ཁྱེད་ཀྱི་ངོ་བོ {displayName} འདི་གནས་སྐབས་ཙམ་ཡིན། ཁྱེད་ཀྱིས་སོ་སོའི་མིང་དང་གསང་ཚིག་བརྗེས་ཏེ་འདི་ཉར་ཚགས་བྱེད་ཆོག",
|
||||
"identity": "ཁྱེད་རང་{displayName} མིང་ཐོག་ནས་ནང་འཛུལ་བྱེད་བཞིན་འདུག",
|
||||
"my_profile": "ངའི་ཡིག་ཆ།",
|
||||
"show_all": "ཚང་མ་སྟོན། >",
|
||||
"hide_all": "སྦེད།",
|
||||
"user_you": "{user} (ཁྱེད།)",
|
||||
"user": "{user}",
|
||||
"members": "ཚོགས་མི་ཁག",
|
||||
"purge": "ཁ་བརྡ་ཁང་མེད་པར་བཟོས།",
|
||||
"link_copied": "འབྲེལ་ཐག་པར་བཤུས་བརྒྱབ་ཚར།",
|
||||
"join_public": "འབྲེལ་ཐག་ཡོད་པའི་མི་གང་རུང་།",
|
||||
"join_invite": "ཁ་སྣོན་བྱས་པའི་གྲོགས་ཁོ་ན།",
|
||||
"permissions": "ནང་འཛུལ་གྱི་ཆོག་མཆན་ཁག",
|
||||
"created_by": "{user} བཟོས།",
|
||||
"copy_link": "གདན་ཞུ་འབྲེལ་ཐག་པར་བཤུས་རྒྱོབས།",
|
||||
"scan_code": "བཤེར་རིས་བཤེར་ཏེ་ཁ་བརྡ་ཁང་དུ་འཛུལ།"
|
||||
},
|
||||
"invite": {
|
||||
"done": "ཚར་སོང་།",
|
||||
"status_error": "གྲོགས་གཅིག་གམ་དེ་ལས་མང་བ་གདན་ཞུ་གནང་ཐུབ་མ་སོང་།",
|
||||
"status_inviting": "གྲོགས་གདན་ཞུ་གནང་བཞིན་པ། {count} ཡི་{index}",
|
||||
"send_invites_to": "གདན་ཞུ་ཐོངས།",
|
||||
"title": "གྲོགས་པོ་ཁ་སྣོན།"
|
||||
},
|
||||
"login": {
|
||||
"login": "ནང་འཛུལ།",
|
||||
"password": "གསང་ཚིག་གཏགས།",
|
||||
"username": "སྤྱོད་མིང། (དཔེར་ན། marta ལྟ་བུ།)",
|
||||
"title": "ནང་འཛུལ།",
|
||||
"password_required": "གསང་ཚིག་དགོས་ཀྱི་ཡོད།",
|
||||
"username_required": "སྤྱོད་མིང་དགོས་ཀྱི་ཡོད།",
|
||||
"create_room": "ཐོ་འགོད་དང་ཁ་བརྡ་ཁང་གསར་སྐྲུན།",
|
||||
"or": "ཡང་ན།"
|
||||
},
|
||||
"new_room": {
|
||||
"next": "རྗེས་མ།",
|
||||
"done": "ཚར་སོང་།",
|
||||
"status_avatar": "མགོ་པར་ཡར་འཇུག་བྱེད་བཞིན་པ།: {count}",
|
||||
"status_avatar_total": "མགོ་པར་ཡར་འཇུག་བྱེད་བཞིན་པ། {total} ཡི་{count}",
|
||||
"status_creating": "ཁ་བརྡ་ཁང་བཟོ་བཞིན་པ།",
|
||||
"invite_description": "ཐོ་ཁོངས་ID བརྒྱུད་ནས། འཚོལ་བཤེར་རམ་ཐོ་གཞུང་གི་ཁོངས་ནས་འདེམས།",
|
||||
"invite_info": "ཁ་སྣོན་བྱས་པའི་མི་རྣམས་ཁོ་ན།",
|
||||
"public_description": "བརྒྱུད་སྐུར་བྱེད་པར་འབྲེལ་ཐག་ཅིག་རག་པར་བྱོས།",
|
||||
"public_info": "འབྲེལ་ཐག་ཡོད་མཁན་གྱི་མི་གང་རུང་།",
|
||||
"link_copied": "འབྲེལ་ཐག་པར་བཤུས་བརྒྱབ་ཚར།",
|
||||
"add_people": "མི་ཁ་སྣོན།",
|
||||
"get_link": "འབྲེལ་ཐག་རག་པར་བྱོས།",
|
||||
"join_permissions_info": "ཆོག་མཆན་འདི་དག་གིས་ཁ་བརྡ་ཁང་ལ་ཇི་ལྟར་འཛུལ་ཐུབ་མིན་དང་། ཇི་ལྟར་མི་གཞན་དག་ལས་སླ་པོའི་ཐོག་ནས་གདན་ཞུ་བྱེད་ཐུབ་མིན་སོགས་ཐག་གཅོད་བྱེད་ཀྱི་ཡོད། ཆོག་མཆན་དེ་དག་ག་དུས་ཡིན་ཡང་འགྱུར་བ་གཏོང་ཆོག",
|
||||
"set_join_permissions": "ནང་འཛུལ་གྱི་ཆོག་མཆན་སྒྲིག་འགོད་བྱོས།",
|
||||
"join_permissions": "ནང་འཛུལ་གྱི་ཆོག་མཆན་ཁག",
|
||||
"new_room": "ཁ་བརྡ་ཁང་གསར་པ།",
|
||||
"name_room": "ཁ་བརྡ་ཁང་ལ་མིང་ཐོགས།",
|
||||
"room_topic": "གལ་ཏེ་འདོད་པ་ཡོད་ན། ཚོགས་པའི་སྐོར་གྱི་འགྲེལ་བཤད་ཐུང་ངུ་ཞིག་འབྲི་ཆོག",
|
||||
"create": "བཟོས།"
|
||||
},
|
||||
"menu": {
|
||||
"logout": "ཕྱིར་ཐོན།",
|
||||
"login": "ནང་འཛུལ།",
|
||||
"download": "ཕབ་ལེན།",
|
||||
"delete": "སུབས།",
|
||||
"edit": "ཕྱོགས་སྒྲིག",
|
||||
"ok": "ལེགས་སོ།",
|
||||
"send": "ཐོངས།",
|
||||
"back": "ཕྱིར་ལོག",
|
||||
"cancel": "ཕྱིར་འཐེན།",
|
||||
"reply": "ཡ་ལན།",
|
||||
"start_private_chat": "སྤྱོད་མཁན་འདི་སྒེར་ལ་ཁ་བརྡ་བྱོས།",
|
||||
"new_room": "ཁ་བརྡ་ཁང་གསར་པ།",
|
||||
"loading": "{appName} སྣོན་འཇུག་བྱེད་བཞིན་པ།",
|
||||
"ignore": "སྣང་མེད་ཐོངས།",
|
||||
"join": "ཞུགས།",
|
||||
"undo": "ཕྱིར་བསྡུ།"
|
||||
},
|
||||
"profile": {
|
||||
"change_password": "གསང་ཚིག་རྗེས།",
|
||||
"password_new": "གསང་ཚིག་གསར་པ།",
|
||||
"password_old": "གསང་ཚིག་རྙིང་པ།",
|
||||
"set_password": "གསང་ཚིག་བཟོས།",
|
||||
"password_repeat": "གསང་ཚིག་གསར་པ་བསྐྱར་དུ་གཏགས།",
|
||||
"change_name": "མིང་རྗེས།",
|
||||
"temporary_identity": "ངོ་བོ་འདི་གནས་སྐབས་ཙམ་ཡིན། དེ་བསྐྱར་དུ་བཀོལ་བར་གསང་ཚིག་ཅིག་བཟོས།",
|
||||
"title": "ངའི་ཡིག་ཆ།",
|
||||
"display_name": "འཆར་མིང་།",
|
||||
"select_language": "སྐད་ཡིག"
|
||||
},
|
||||
"device_list": {
|
||||
"not_verified": "ར་སྤྲོད་བྱས་མི་འདུག",
|
||||
"verified": "ར་སྤྲོད་བྱས་ཟིན།",
|
||||
"blocked": "བཀག་ཚར།",
|
||||
"title": "ཡོ་ཆས་ཁག"
|
||||
},
|
||||
"room_welcome": {
|
||||
"info_permissions": "ཁྱེད་ཀྱིས་ག་དུས་ཡིན་ཡང་སྒྲིག་བཀོད་ཀྱི་ཁོངས་ནས་ཁ་བརྡ་ཁང་གི་'ནང་འཛུལ་གྱི་ཆོག་མཆན'ལ་འགྱུར་བ་གཏོང་ཆོག",
|
||||
"join_invite": "ཁྱེད་ཀྱིས་གདན་ཞུ་གནང་བའི་མི་ཁོ་ན་མ་གཏོགས་འཛུལ་མི་ཐུབ།",
|
||||
"join_public": "སུ་ཡིན་རུང་འབྲེལ་ཐག་འདིའི་ཐོག་ལ་མནན་ཏེ་འཛུལ་ཆོག: {link}.",
|
||||
"info": "འདིར་ཁྱེད་ཀྱིས་སོ་སོའི་ཚོགས་པའི་སྐོར་ལ་ཤེས་དགོས་པའི་དོན་གནད་འགའ་ཡོད།:",
|
||||
"welcome": "དགའ་བསུ་ཞུ།",
|
||||
"got_it": "ཧ་གོ་སོང་།",
|
||||
"room_history_joined": "ཚོགས་མི་ཁག་ཁ་བརྡ་ཁང་དུ་ཞུགས་པའི་རྗེས་སུ། ད་གཟོད་དེའི་ནང་དུ་བཏང་ཡོད་པའི་འཕྲིན་ཐུང་ཁག་མཐོང་ཐུབ།",
|
||||
"room_history_is": "ཁ་བརྡ་ཁང་གི་ཟིན་ཐོ་ཁག {type}.",
|
||||
"encrypted": "འཕྲིན་ཐུང་ཁག་ལ་སྣེ་གཉིས་བར་གྱི་གསང་སྡོམ་བྱས་ཡོད།"
|
||||
},
|
||||
"room": {
|
||||
"leave": "ཕྱིར་ཐོན།",
|
||||
"members": "ཚོགས་མི་མི་འདུག| ཚོགས་མི ༡| ཚོགས་མི {count}",
|
||||
"room_list_rooms": "ཁ་བརྡ་ཁང་།",
|
||||
"room_list_invites": "གདན་ཞུ་ཁག",
|
||||
"purge_failed": "ཁ་བརྡ་ཁང་བཤིག་ཐུབ་མ་སོང་།",
|
||||
"purge_removing_members": "ཚོགས་མི་ཁག་ཕྱིར་འདོན།",
|
||||
"purge_redacting_events": "ཁ་བརྡ་གཙང་གསུབ།",
|
||||
"purge_set_room_state": "ཁ་བརྡ་ཁང་གི་རྣམ་པ་སྒྲིག་འགོད།"
|
||||
},
|
||||
"message": {
|
||||
"users_are_typing": "{count} ཚོགས་མི་ཡིས་གཏགས་བཞིན་འདུག",
|
||||
"user_is_typing": "{user}གཏགས་བཞིན་འདུག",
|
||||
"scale_image": "པར་རིས་རྐྱོང་སྐུམ།",
|
||||
"your_message": "ཁྱེད་ཀྱི་བརྡ་ལན...",
|
||||
"replying_to_event": "དོན་རྐྱེན་ལ་ལན་འདེབས།: {message}",
|
||||
"unread_messages": "བཀླགས་མེད་པའི་འཕྲིན་ཐུང་ཁག",
|
||||
"user_changed_room_topic": "{user} ཁ་བརྡ་ཁང་གི་བརྗོད་གཞི་{topic} ལ་བརྗེ་སོང་།",
|
||||
"user_changed_room_name": "{user} ཁ་བརྡ་ཁང་གི་མིང་ {name} ལ་བརྗེས་སོང་།",
|
||||
"room_joinrule_public": "ཡོངས་ཁྱབ།",
|
||||
"user_changed_room_history": "{user} ཁ་བརྡ་ཁང་གི་ཟིན་ཐོ་བཟོས་སོང་། {type}",
|
||||
"room_joinrule_invite": "གདན་ཞུ་ཁོ་ན།",
|
||||
"user_changed_join_rules": "{user} ཁ་བརྡ་ཁང་བཟོས་སོང་། {type}",
|
||||
"room_history_joined": "ཚོགས་མི་ཁ་བརྡ་ཁང་དུ་ཞུགས་པའི་དུས་ནས་ཀློག་ཐུབ།",
|
||||
"room_history_invited": "ཚོགས་མི་ཁ་བརྡ་ཁང་དུ་གདན་ཞུ་གནང་བའི་དུས་ནས་ཀློག་ཐུབ།",
|
||||
"room_history_shared": "ཁ་བརྡ་ཁང་ནང་གི་ཚོགས་མི་ཚང་མས་ཀློག་ཐུབ།",
|
||||
"room_history_world_readable": "སུས་ཀྱང་ཀློག་ཐུབ།",
|
||||
"upload_progress_with_total": "ཡར་འཇུག་བྱས་ཚར་བའི་{total} ཡི་{count}",
|
||||
"upload_progress": "ཡར་འཇུག {count}",
|
||||
"download_progress": "ཕབ་ལེན་གྲུབ་ཚར་བའི་བརྒྱ་ཆ།{percentage}%",
|
||||
"edited": "(བཅོས་སྒྲིག་བྱས།)",
|
||||
"file_prefix": "ཡིག་ཆ། ",
|
||||
"user_said": "{user} བཤད་རྒྱུར།:",
|
||||
"user_left": "{user} ཁ་བརྡའི་ཁོངས་ནས་ཕྱིར་ཐོན་སོང་།",
|
||||
"user_joined": "{user} ཁ་བརྡ་བྱེད་པར་སླེབས་སོང་།",
|
||||
"user_was_invited": "{user} ཁ་བརྡ་ཁང་ལ་གདན་ཞུ་གནང་ཚར...",
|
||||
"user_encrypted_room": "{user} ཁ་བརྡ་ཁང་ལ་གསང་སྡོམ་བསྐྲུན་སོང་།",
|
||||
"user_changed_room_avatar": "{user} ཁ་བརྡ་ཁང་གི་པར་མགོ་བརྗེ་སོང་།",
|
||||
"user_changed_avatar": "{user} མགོ་པར་བརྗེ་སོང་།",
|
||||
"user_changed_display_name": "{user} འཆར་མིང་ {displayName} ལ་བསྒྱུར་སོང་།",
|
||||
"user_aliased_room": "{user} ཁ་བརྡ་ཁང་ལ་མིང་ཞིག་བཏགས་སོང་། {alias}",
|
||||
"user_created_room": "{user} ཁ་བརྡ་བྱ་སའི་ཁང་པ་བཟོས་སོང་།",
|
||||
"you": "ཁྱེད།",
|
||||
"user_powerlevel_change_from_to": "{powerOld} ཡི{user} ནས {powerNew}་ལ།",
|
||||
"room_powerlevel_change": "{user} {changes} ཡི་སྟོབས་ཤུགས་གནས་རིམ་བརྗེས་སོང་།",
|
||||
"user_changed_guest_access_open": "{user} མགྲོན་པོ་ཁ་བརྡ་ཁང་དུ་འཛུལ་བཅུག་སོང་།",
|
||||
"user_changed_guest_access_closed": "{user} མགྲོན་པོ་ཁ་བརྡ་ཁང་དུ་འཛུལ་བཅུག་མ་སོང་།"
|
||||
},
|
||||
"power_level": {
|
||||
"moderator": "མདོ་འཛིན་པ།",
|
||||
"restricted": "དམ་བསྒྲགས།",
|
||||
"custom": "སྒེར་གྱི་རྣམ་པ། ({level})",
|
||||
"default": "སོར་བཞག",
|
||||
"admin": "དོ་དམ་པ།"
|
||||
},
|
||||
"voice_recorder": {
|
||||
"not_supported_text": "སྟབས་མ་ལེགས་པ་ཞིག་ལ། དྲ་རྒྱ་བཤེར་ཆས་འདིས་སྒྲ་ཡར་འཇུག་བྱེད་པར་རྒྱབ་སྐྱོར་མི་བྱེད།",
|
||||
"not_supported_title": "རྒྱབ་སྐྱོར་མི་བྱེད།",
|
||||
"failed_to_record": "སྒྲ་འཇུག་བྱེད་ཐུབ་མ་སོང་།",
|
||||
"release_to_cancel": "གློད་ནས་སུབས།",
|
||||
"swipe_to_cancel": "ཤུད་འདེད་བྱས་ཏེ་སུབས།"
|
||||
},
|
||||
"room_info_sheet": {
|
||||
"create_room": "ཚོགས་པ་བཟོས།",
|
||||
"view_details": "ཞིབ་ཕྲར་གཟིགས།",
|
||||
"this_room": "ཁ་བརྡ་ཁང་འདི།"
|
||||
},
|
||||
"purge_room": {
|
||||
"button": "སུབས།",
|
||||
"info": "འཕྲིན་ཐུང་ཁག་དང་ཚོགས་མི་ཚང་མ་མེད་པར་བཟོ་ངེས། བྱེད་ལས་འདི་ཕྱིར་ཟློག་ཐབས་མེད།",
|
||||
"title": "ཁ་བརྡ་ཁང་མེད་པར་བཟོ་རྒྱུ་ཡིན་ནམ།",
|
||||
"room_deletion_notice": "བདེ་མོ་འཇོག་པའི་དུས་ལ་སླེབས་སོང་། {user} ཁ་བརྡ་ཁང་མེད་པར་བཟོས་སོང་། སྐར་ཆ་ཁ་ཤས་ཀྱི་ནང་དུ་མེད་པར་བཟོ་ངེས།",
|
||||
"notified": "ང་ཚོས་ཚོགས་མི་ཚོར་བརྡ་ཁྱབ་བཏང་ཟིན།",
|
||||
"deleting": "ཁ་བརྡ་ཁང་མེད་པར་བཟོ་བཞིན་པ།:",
|
||||
"self_destruct": "སྐར་ཆ་ཁ་ཤས་ཀྱི་ནང་དུ་ཁ་བརྡ་ཁང་རང་བཞིན་གྱིས་མེད་པར་ཆགས་འགྲོ",
|
||||
"n_seconds": "{seconds} སྐར་ཆ།"
|
||||
},
|
||||
"leave": {
|
||||
"leave": "ཕྱིར་ཐོན།",
|
||||
"go_back": "ཕྱིར་ལོག",
|
||||
"create_account": "ཁ་བྱང་ཞིག་བཟོས།",
|
||||
"text_invite": "ཁ་བརྡ་ཁང་འདིར་ཟྭ་བརྒྱབ་འདུག དམིགས་བསལ་གྱི་ཆོག་མཆན་མེད་པར། ཁྱེད་རང་བསྐྱར་དུ་འཛུལ་མི་ཐུབ།",
|
||||
"title_invite": "ཁྱེད་རང་ཕྱིར་ཐོན་རྒྱུ་གཏན་འཁེལ་བ་ཡིན་ནམ།",
|
||||
"text_public_lastroom": "ཁྱེད་རང་ཁ་བརྡ་ཁང་འདིའི་ནང་དུ་བསྐྱར་དུ་འཛུལ་འདོད་ཚེ། ངོ་བོ་གསར་བ་ཞིག་གི་ཐོག་ནས་འཛུལ་ཆོག {user}་ཉར་བར།, {action}.",
|
||||
"text_public": "གལ་ཏེ་ཁྱེད་ཀྱིས་འབྲེལ་ཐག་དེ་ཧ་གོ་ཚེ། ག་དུས་ཡིན་ཡང་། ཁ་བརྡ་ཁང་དུ་འཛུལ་ཆོག",
|
||||
"title_public": "{user} ག་ལེེར་ཕེབས།"
|
||||
},
|
||||
"join": {
|
||||
"status_joining": "ཁ་བརྡ་ཁང་དུ་འཛུལ་བཞིན་པ།...",
|
||||
"status_logging_in": "ནང་འཛུལ་བྱེད་བཞིན་པ།...",
|
||||
"join_guest": "སྐུ་མགྲོན་གྱི་མིང་ཐོག་ནས་ཞུགས།",
|
||||
"join": "ཁ་བརྡ་ཁང་དུ་འཛུལ།",
|
||||
"joining_as": "ཁྱེད་རང་ཞུགས་བཞིན་པ།:",
|
||||
"shared_computer": "འདི་ནི་མཉམ་སྤྱོད་བྱས་པའི་ཡོ་ཆས་ཤིག་ཡིན།",
|
||||
"user_name_label": "སྤྱོད་མིང་།",
|
||||
"title": "{roomName} ནང་དུ་ཕེབས་པར་དགའ་བསུ་ཞུ།",
|
||||
"join_failed": "ཁ་བརྡ་ཁང་དུ་འཛུལ་ཐུབ་མ་སོང་།"
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"powered_by": "ཁ་བརྡ་ཁང་འདི་{product} ནུས་ཤུགས་བསྩལ་ཡོད། {productLink} ནས་དེ་ལས་མང་བ་སྦྱོང་ཆོག་ལ། མདུན་དུ་བསྐྱོད་དེ་ཁ་བརྡ་ཁང་གཞན་ཞིག་བསྐྲུན་ཆོག",
|
||||
"new_room": "+ ཁ་བརྡ་ཁང་གསར་པ།",
|
||||
"want_more": "དེ་ལས་མང་བ་དགོས་སམ།",
|
||||
"logout": "ཕྱིར་ཐོན།",
|
||||
"edit_profile": "སྒེར་གྱི་ཡིག་ཆ་བཅོས་སྒྲིག",
|
||||
"identity_temporary": "{displayName}",
|
||||
"identity": "{displayName}",
|
||||
"you_are": "ཁྱེད་ནི།"
|
||||
},
|
||||
"goodbye": {
|
||||
"view_other_rooms": "ཁ་བརྡ་ཁང་གཞན་དག་ལ་གཟིགས།",
|
||||
"close_tab": "བཤེར་ཆས་ཁ་རྒྱོབས།",
|
||||
"room_deleted": "ཁ་བརྡ་ཁང་མེད་པར་བཟོས་སོང་།"
|
||||
},
|
||||
"language_display_name": "བོད་ཡིག"
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"Keanu Weblite": "Keanu Weblite",
|
||||
"language_display_name": "English",
|
||||
"menu": {
|
||||
"start_private_chat": "Private chat with this user",
|
||||
"reply": "Reply",
|
||||
|
|
@ -12,7 +12,11 @@
|
|||
"back": "BACK",
|
||||
"login": "Login",
|
||||
"logout": "Logout",
|
||||
"new_room": "New Room"
|
||||
"new_room": "New Room",
|
||||
"undo": "Undo",
|
||||
"join": "Join",
|
||||
"ignore": "Ignore",
|
||||
"loading": "Loading {appName}"
|
||||
},
|
||||
"message": {
|
||||
"you": "You",
|
||||
|
|
@ -63,24 +67,24 @@
|
|||
"room_list_rooms": "Rooms"
|
||||
},
|
||||
"room_welcome": {
|
||||
"welcome": "Welcome!",
|
||||
"info": "Here are a few things to know about your group:",
|
||||
"info": "Welcome! Here are a few things to know about your room:",
|
||||
"encrypted": "Messages are end-to-end encrypted.",
|
||||
"room_history_is": "Room history is {type}.",
|
||||
"room_history_joined": "People can only see the messages sent after they join.",
|
||||
"join_public": "Anyone can join by opening this link: {link}.",
|
||||
"join_invite": "Only people you invite can join.",
|
||||
"info_permissions": "You can change 'join permissions' and 'message history' at any time in the group settings.",
|
||||
"info_permissions": "You can change ‘join permissions’ at any time in the room settings.",
|
||||
"got_it": "Got it"
|
||||
},
|
||||
"new_room": {
|
||||
"new_room": "New Room",
|
||||
"create": "Create",
|
||||
"next": "Next",
|
||||
"name_room": "Name group",
|
||||
"name_room": "Name room",
|
||||
"room_topic": "Add a description if you like",
|
||||
"join_permissions": "Join permissions",
|
||||
"set_join_permissions": "Set Join Permissions",
|
||||
"join_permissions_info": "These permissions determine how people can join the group and how easily others can be invited. They can be changed anytime.",
|
||||
"join_permissions_info": "These permissions determine how people can join the room and how easily others can be invited. They can be changed anytime.",
|
||||
"get_link": "Get link",
|
||||
"add_people": "Add people",
|
||||
"link_copied": "Link copied!",
|
||||
|
|
@ -100,11 +104,13 @@
|
|||
},
|
||||
"login": {
|
||||
"title": "Login",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"username": "Username (ex: marta)",
|
||||
"password": "Enter Password",
|
||||
"username_required": "Username is required",
|
||||
"password_required": "Password is required",
|
||||
"login": "Login"
|
||||
"login": "Login",
|
||||
"create_room": "Register & Create Room",
|
||||
"or": "OR"
|
||||
},
|
||||
"profile": {
|
||||
"title": "My Profile",
|
||||
|
|
@ -112,6 +118,7 @@
|
|||
"set_password": "Set password",
|
||||
"change_name": "Change name",
|
||||
"change_password": "Change password",
|
||||
"select_language": "Language",
|
||||
"password_old": "Old password",
|
||||
"password_new": "New password",
|
||||
"password_repeat": "Repeat new password",
|
||||
|
|
@ -130,12 +137,13 @@
|
|||
"join": {
|
||||
"title": "Welcome to {roomName}",
|
||||
"user_name_label": "User name",
|
||||
"shared_computer": "Using a shared computer",
|
||||
"shared_computer": "This is a shared device",
|
||||
"joining_as": "You are joining as:",
|
||||
"join": "Join room",
|
||||
"join_guest": "Join as guest",
|
||||
"status_logging_in": "Logging in...",
|
||||
"status_joining": "Joining room..."
|
||||
"status_joining": "Joining room...",
|
||||
"join_failed": "Failed to join room."
|
||||
},
|
||||
"invite": {
|
||||
"title": "Add Friends",
|
||||
|
|
@ -147,17 +155,27 @@
|
|||
"leave": {
|
||||
"title_public": "Goodbye, {user}",
|
||||
"text_public": "You can always join this room again if you know the link.",
|
||||
"text_public_lastroom": "If you want to join this group again, you can join under a new identity. To keep {user}, {action}.",
|
||||
"text_public_lastroom": "If you want to join this room again, you can join under a new identity. To keep {user}, {action}.",
|
||||
"title_invite": "Are you sure you want to leave?",
|
||||
"text_invite": "This group is locked. You cannot rejoin without a special permission.",
|
||||
"text_invite": "This room is locked. You cannot rejoin without a special permission.",
|
||||
"create_account": "create an account",
|
||||
"go_back": "Go back",
|
||||
"leave": "Leave"
|
||||
},
|
||||
"purge_room": {
|
||||
"title": "Delete room?",
|
||||
"info": "This will close the room for all members. It cannot be undone.",
|
||||
"button": "Delete room"
|
||||
"info": "All members and messages will be removed. This action cannot be undone.",
|
||||
"button": "Delete",
|
||||
"n_seconds": "{seconds} seconds",
|
||||
"self_destruct": "Room will self destruct in seconds.",
|
||||
"deleting": "Deleting room:",
|
||||
"notified": "We've notified members.",
|
||||
"room_deletion_notice": "Time to say goodbye! This room has been deleted by {user}. It will self destruct in seconds."
|
||||
},
|
||||
"goodbye": {
|
||||
"room_deleted": "Room deleted.",
|
||||
"close_tab": "Close browser tab",
|
||||
"view_other_rooms": "View other rooms"
|
||||
},
|
||||
"room_info": {
|
||||
"title": "Room Details",
|
||||
|
|
@ -174,10 +192,11 @@
|
|||
"hide_all": "Hide",
|
||||
"show_all": "Show all >",
|
||||
"leave_room": "Leave",
|
||||
"version_info": "Powered by Guardian Project. Version: {version}"
|
||||
"version_info": "Powered by Guardian Project. Version: {version}",
|
||||
"scan_code": "Scan to join the room"
|
||||
},
|
||||
"room_info_sheet": {
|
||||
"this_room": "This group",
|
||||
"this_room": "This room",
|
||||
"view_details": "View details"
|
||||
},
|
||||
"voice_recorder": {
|
||||
|
|
|
|||
188
src/assets/translations/es.json
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
{
|
||||
"language_display_name": "Español",
|
||||
"room_info": {
|
||||
"identity": "Has iniciado sesión como {displayName}.",
|
||||
"my_profile": "Mi perfil",
|
||||
"show_all": "Mostrar todos",
|
||||
"hide_all": "Ocultar",
|
||||
"user_you": "{user} (you)",
|
||||
"user": "{user}",
|
||||
"members": "Miembros",
|
||||
"purge": "Purgar la Sala",
|
||||
"link_copied": "¡Liga copiada!",
|
||||
"join_public": "Cualquiera con la liga se puede unir",
|
||||
"join_invite": "Solo st puedes unir a la sala mediante una invitación",
|
||||
"permissions": "Permisos",
|
||||
"created_by": "Creado por {user}",
|
||||
"title": "Información",
|
||||
"version_info": "Creado por Guardian Project. Version: {version}",
|
||||
"leave_room_info": "Nota: este paso no se puede deshacer. Asegúrate de que deseas cerrar la sesión y eliminar el chat para siempre.",
|
||||
"leave_room": "Salir del grupo",
|
||||
"view_profile": "Vista",
|
||||
"identity_temporary": "Tu identidad {displayName} es temporal. Puedes cambiar tu nombre o establecer una contraseña para conservarla."
|
||||
},
|
||||
"purge_room": {
|
||||
"button": "Purgar la sala",
|
||||
"info": "Esta operación cerrará la sala para todos los miembros. No se puede deshacer.",
|
||||
"title": "¿Purgar la sala?"
|
||||
},
|
||||
"leave": {
|
||||
"leave": "Salir",
|
||||
"go_back": "Regresar",
|
||||
"create_account": "crea una cuenta",
|
||||
"text_invite": "Este grupo está bloqueado. No puedes volver a unirte sin un permiso especial.",
|
||||
"title_invite": "Estas seguro que deseeas salir?",
|
||||
"text_public_lastroom": "Si desea unirse a este grupo nuevamente, puede unirse con una nueva identidad. Para mantener {user}, {action}.",
|
||||
"text_public": "Siempre puedes volver a unirte a esta sala si conoces el enlace.",
|
||||
"title_public": "Adios, [user}"
|
||||
},
|
||||
"invite": {
|
||||
"status_error": "No se pudo invitar a uno o más amigos!",
|
||||
"status_inviting": "Invitando amigo {index} de {count}",
|
||||
"send_invites_to": "Enviar invitaciones a",
|
||||
"done": "Listo",
|
||||
"title": "Agregar Amigos"
|
||||
},
|
||||
"join": {
|
||||
"status_joining": "Uniendose a la sala...",
|
||||
"status_logging_in": "Iniciando sesión...",
|
||||
"join_guest": "Unirse como invitado",
|
||||
"join": "Unirse a la sala",
|
||||
"joining_as": "Te estas uniendo como:",
|
||||
"shared_computer": "Usando una computadora compartida",
|
||||
"user_name_label": "Nombre de usuario",
|
||||
"title": "Bienvenido a {roomName}"
|
||||
},
|
||||
"profile": {
|
||||
"display_name": "Nombre para mostrar",
|
||||
"password_repeat": "Repite la nueva contraseña",
|
||||
"password_new": "Nueva contraseña",
|
||||
"password_old": "Vieja contraseña",
|
||||
"change_password": "Cambia la contraseña",
|
||||
"change_name": "Cambia el nombre",
|
||||
"set_password": "Crea una contraseña",
|
||||
"temporary_identity": "Esta identidad es temporal. Establezca una contraseña para usarla nuevamente",
|
||||
"title": "Mi perfil"
|
||||
},
|
||||
"login": {
|
||||
"login": "Iniciar sesión",
|
||||
"password_required": "Contraseña es necesaria",
|
||||
"username_required": "Nombre de usuario es necesario",
|
||||
"password": "Contraseña",
|
||||
"username": "Nombre de Usuario",
|
||||
"title": "Iniciar sesión",
|
||||
"create_room": "",
|
||||
"or": ""
|
||||
},
|
||||
"device_list": {
|
||||
"not_verified": "No ha sido Verificado",
|
||||
"verified": "Verificado",
|
||||
"blocked": "Bloqueado",
|
||||
"title": "DISPOSITIVOS"
|
||||
},
|
||||
"new_room": {
|
||||
"status_avatar": "Subiendo avatar: {count}",
|
||||
"status_avatar_total": "Subiendo Avatar: {count} de {total}",
|
||||
"status_creating": "Creando Sala",
|
||||
"invite_description": "Escoge de una lista o busca por ID de cuenta",
|
||||
"invite_info": "Solo personas agregadas",
|
||||
"public_description": "Obten la liga para compartirla",
|
||||
"public_info": "Cualquiera con la liga",
|
||||
"link_copied": "Liga copiada!",
|
||||
"add_people": "Agregar personas",
|
||||
"get_link": "Obtener la liga",
|
||||
"join_permissions_info": "Estos permisos determinan cómo las personas pueden unirse al grupo y con qué facilidad se puede invitar a otras personas. Pueden cambiarse en cualquier momento.",
|
||||
"set_join_permissions": "Establecer permisos para unirse",
|
||||
"join_permissions": "Permisos para unirse",
|
||||
"name_room": "Nombra el Grupo",
|
||||
"next": "Siguiente",
|
||||
"done": "Listo",
|
||||
"new_room": "Nuevo Grupo"
|
||||
},
|
||||
"room_welcome": {
|
||||
"join_public": "Cualquiera puede unirse abriendo esta liga: {link}",
|
||||
"got_it": "Entiendo",
|
||||
"info_permissions": "Puedes cambiar los 'permisos de participación' y el 'historial de mensajes' en cualquier momento en la configuración del grupo.",
|
||||
"join_invite": "Solo personas que invitas se pueden unir.",
|
||||
"info": "Aquí estan algunas cosas acerca de tu grupo:",
|
||||
"welcome": "Bienvenido!"
|
||||
},
|
||||
"room": {
|
||||
"leave": "Salir",
|
||||
"members": "no miembros | 1 miembro| {count} miembros"
|
||||
},
|
||||
"message": {
|
||||
"user_powerlevel_change_from_to": "{user} de {powerOld} a {powerNew}",
|
||||
"room_powerlevel_change": "{user} cambio el powerlevel de {changes}",
|
||||
"users_are_typing": "{count} miembros estan escribiendo",
|
||||
"user_is_typing": "{user} esta escribiendo",
|
||||
"scale_image": "Escala de imagen",
|
||||
"your_message": "Tu mensaje...",
|
||||
"replying_to_event": "RESPONDIENDO A UN EVENTO: {message}",
|
||||
"unread_messages": "Mensajes no leídos",
|
||||
"user_changed_room_name": "{user} cambió el nombre de la sala {name}",
|
||||
"user_changed_room_topic": "{user} cambió el tema de la sala a {topic}",
|
||||
"room_joinrule_public": "público",
|
||||
"room_joinrule_invite": "sólo con invitación",
|
||||
"user_changed_join_rules": "{user} creo la sala {type}",
|
||||
"room_history_joined": "Todos los miembros de la sala pueden leerlo desde el momento en que se unieron",
|
||||
"room_history_invited": "Puede ser leido por todos los miembros de la sala desde que fueron invitados",
|
||||
"room_history_shared": "Todos los miembros de la sala pueden leerlo",
|
||||
"room_history_world_readable": "cualquiera puede leerlo",
|
||||
"user_changed_room_history": "{user} Hizo historia en la habitación {type}",
|
||||
"upload_progress_with_total": "Subido {count} de {total}",
|
||||
"upload_progress": "Subido {count}",
|
||||
"download_progress": "{percentage}% descargado",
|
||||
"edited": "(editado)",
|
||||
"file_prefix": "Archivo: ",
|
||||
"user_said": "{user} dijo:",
|
||||
"user_left": "{user} abandono el chat",
|
||||
"user_joined": "{user} se unio al chat",
|
||||
"user_was_invited": "{user} fue invitado al chat...",
|
||||
"user_encrypted_room": "{user} hizo la habitación encriptada",
|
||||
"user_changed_room_avatar": "{user} cambio el avatar de la sala",
|
||||
"user_changed_avatar": "{user} cambio su avatar",
|
||||
"user_created_room": "{user} creo la sala",
|
||||
"user_aliased_room": "{user} hizo el alias de la sala {alias}",
|
||||
"user_changed_display_name": "{user}cambio su nombre para mostrar a {displayName}",
|
||||
"you": "Tú"
|
||||
},
|
||||
"menu": {
|
||||
"login": "Iniciar sesión",
|
||||
"logout": "Cerrar sesión",
|
||||
"back": "Regresar",
|
||||
"send": "Enviar",
|
||||
"cancel": "Cancelar",
|
||||
"ok": "OK",
|
||||
"download": "Descargar",
|
||||
"delete": "Eliminar",
|
||||
"reply": "Responder",
|
||||
"start_private_chat": "Chat privado con este usuario",
|
||||
"edit": "Editar"
|
||||
},
|
||||
"fallbacks": {
|
||||
"download_name": "Descargar",
|
||||
"original_text": "<original text>",
|
||||
"video_file": "Archivo de video",
|
||||
"audio_file": "Archivo de audio"
|
||||
},
|
||||
"power_level": {
|
||||
"restricted": "Restringido",
|
||||
"custom": "personalizado ({level})",
|
||||
"default": "predeterminado",
|
||||
"moderator": "Moderador",
|
||||
"admin": "Administrador"
|
||||
},
|
||||
"voice_recorder": {
|
||||
"not_supported_text": "Desafortunadamente, este navegador no admite la grabación de audio.",
|
||||
"not_supported_title": "No esta permitido",
|
||||
"failed_to_record": "No se pudo grabar el audio",
|
||||
"release_to_cancel": "Suelta para cancelar",
|
||||
"swipe_to_cancel": "Desliza para cancelar"
|
||||
},
|
||||
"room_info_sheet": {
|
||||
"create_room": "Crear Grupo",
|
||||
"view_details": "Ver detalles",
|
||||
"this_room": "Este grupo"
|
||||
}
|
||||
}
|
||||
151
src/assets/translations/nb_NO.json
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
{
|
||||
"message": {
|
||||
"user_changed_guest_access_open": "{user} tillot gjester å ta del i rommet",
|
||||
"user_powerlevel_change_from_to": "{user} fra {powerOld} til {powerNew}",
|
||||
"room_powerlevel_change": "{user} endret maktnivå for {changes}",
|
||||
"room_history_world_readable": "lesbart for hvem som helst",
|
||||
"room_history_shared": "lesbart for alle rommets medlemmer",
|
||||
"user_left": "{user} forlot sludringen",
|
||||
"user_joined": "{user} tok del i sludringen",
|
||||
"user_was_invited": "{user} ble invitert til sludringen …",
|
||||
"user_changed_room_avatar": "{user} endret romavataren",
|
||||
"user_encrypted_room": "{user} gjorde rommet kryptert",
|
||||
"user_changed_avatar": "{user} endret avataren",
|
||||
"user_changed_display_name": "{user} endret visningsnavn til {displayName}",
|
||||
"user_aliased_room": "{user} lagde et rom ved navn {alias}",
|
||||
"your_message": "Din melding …",
|
||||
"user_changed_room_topic": "{user} endret rom-emne til {topic}",
|
||||
"room_joinrule_public": "offentlig",
|
||||
"room_joinrule_invite": "kun ved invitasjon",
|
||||
"upload_progress_with_total": "Opplastet {count} av {total}",
|
||||
"upload_progress": "Opplastet {count}",
|
||||
"download_progress": "{percentage}% nedlastet",
|
||||
"edited": "(redigert)",
|
||||
"file_prefix": "Fil: ",
|
||||
"user_said": "{user} sa:",
|
||||
"user_created_room": "{user} opprettet rommet",
|
||||
"you": "Deg"
|
||||
},
|
||||
"device_list": {
|
||||
"title": "Enheter",
|
||||
"not_verified": "Ikke bekreftet",
|
||||
"verified": "Bekreftet",
|
||||
"blocked": "Blokkert"
|
||||
},
|
||||
"fallbacks": {
|
||||
"original_text": "<opprinnelig tekst>",
|
||||
"video_file": "Videofil",
|
||||
"audio_file": "Lydfil"
|
||||
},
|
||||
"power_level": {
|
||||
"restricted": "begrenset",
|
||||
"default": "forvalg",
|
||||
"moderator": "moderator",
|
||||
"admin": "administrator"
|
||||
},
|
||||
"voice_recorder": {
|
||||
"release_to_cancel": "Slipp for å avbryte"
|
||||
},
|
||||
"room_info_sheet": {
|
||||
"this_room": "Denne gruppen",
|
||||
"view_details": "Vis detaljer"
|
||||
},
|
||||
"room_info": {
|
||||
"hide_all": "Skjul",
|
||||
"members": "Medlemmer",
|
||||
"purge": "Slett rom",
|
||||
"link_copied": "Lenke kopiert.",
|
||||
"copy_link": "Kopier invitasjonslenke",
|
||||
"created_by": "Opprettet av {user}",
|
||||
"title": "Romdetaljer"
|
||||
},
|
||||
"goodbye": {
|
||||
"view_other_rooms": "Vis andre rom",
|
||||
"room_deleted": "Rom slettet."
|
||||
},
|
||||
"join": {
|
||||
"join": "Ta del i rom",
|
||||
"status_joining": "Tar del i rom…",
|
||||
"status_logging_in": "Logger inn …",
|
||||
"join_guest": "Ta del som gjest",
|
||||
"title": "Velkommen til {roomName}"
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"identity_temporary": "{displayName}",
|
||||
"identity": "{displayName}",
|
||||
"logout": "Logg ut",
|
||||
"edit_profile": "Rediger profil",
|
||||
"you_are": "Du er"
|
||||
},
|
||||
"profile": {
|
||||
"password_new": "Nytt passord",
|
||||
"password_old": "Gammelt passord",
|
||||
"select_language": "Språk",
|
||||
"change_password": "Endre passord",
|
||||
"change_name": "Endre navn",
|
||||
"set_password": "Sett passord",
|
||||
"title": "Min profil",
|
||||
"display_name": "Visningsnavn",
|
||||
"password_repeat": "Gjenta nytt passord"
|
||||
},
|
||||
"login": {
|
||||
"password_required": "Passord kreves",
|
||||
"username_required": "Brukernavn kreves",
|
||||
"password": "Passord",
|
||||
"username": "Brukernavn",
|
||||
"create_room": "",
|
||||
"or": ""
|
||||
},
|
||||
"new_room": {
|
||||
"add_people": "Legg til folk",
|
||||
"link_copied": "Lenke kopiert.",
|
||||
"next": "Neste",
|
||||
"create": "Opprett",
|
||||
"new_room": "Nytt rom"
|
||||
},
|
||||
"room_welcome": {
|
||||
"room_history_is": "Romhistorikken er {type}.",
|
||||
"encrypted": "Meldinger er ende-til-ende -kryptert.",
|
||||
"got_it": "Skjønner",
|
||||
"join_invite": "Kun folk du inviterer kan ta del."
|
||||
},
|
||||
"room": {
|
||||
"room_list_rooms": "Rom",
|
||||
"room_list_invites": "Invitasjoner",
|
||||
"purge_set_room_state": "Setter romtilstand",
|
||||
"leave": "Forlat"
|
||||
},
|
||||
"purge_room": {
|
||||
"n_seconds": "{seconds} sekunder",
|
||||
"button": "Slett",
|
||||
"title": "Slett rom?"
|
||||
},
|
||||
"leave": {
|
||||
"go_back": "Gå tilbake",
|
||||
"create_account": "opprett en konto",
|
||||
"title_public": "Adjø, {user}"
|
||||
},
|
||||
"invite": {
|
||||
"status_inviting": "Inviterer venn {index} av {count}",
|
||||
"send_invites_to": "Send invitasjoner til",
|
||||
"done": "Ferdig",
|
||||
"title": "Legg til venner"
|
||||
},
|
||||
"menu": {
|
||||
"logout": "Logg ut",
|
||||
"back": "Tilbake",
|
||||
"loading": "Laster inn {appName}",
|
||||
"undo": "Angre",
|
||||
"new_room": "Nytt rom",
|
||||
"login": "Logg inn",
|
||||
"send": "Send",
|
||||
"ok": "OK",
|
||||
"cancel": "Avbryt",
|
||||
"download": "last ned",
|
||||
"delete": "Slett",
|
||||
"edit": "Rediger",
|
||||
"reply": "Svar",
|
||||
"start_private_chat": "Privat sludring med denne brukeren"
|
||||
},
|
||||
"language_display_name": "Engelsk"
|
||||
}
|
||||
222
src/assets/translations/pt_BR.json
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
{
|
||||
"invite": {
|
||||
"title": "Adiciona amigos",
|
||||
"done": "Feito",
|
||||
"send_invites_to": "Envie os convites para",
|
||||
"status_inviting": "Convidando amigo {index} de {count}",
|
||||
"status_error": "Houve uma falha ao convidar um ou mais amigos!"
|
||||
},
|
||||
"fallbacks": {
|
||||
"original_text": "<texto original>",
|
||||
"download_name": "Baixar",
|
||||
"audio_file": "Arquivo de áudio",
|
||||
"video_file": "Arquivo de vídeo"
|
||||
},
|
||||
"language_display_name": "Inglês",
|
||||
"menu": {
|
||||
"start_private_chat": "Bate-papo privado com este usuário",
|
||||
"reply": "Responder",
|
||||
"edit": "Editar",
|
||||
"delete": "Excluir",
|
||||
"download": "Baixar",
|
||||
"ok": "OK",
|
||||
"cancel": "Cancela",
|
||||
"send": "Envia",
|
||||
"back": "RETORNA",
|
||||
"login": "Entrar",
|
||||
"logout": "Sair",
|
||||
"new_room": "Nova sala",
|
||||
"undo": "Desfazer",
|
||||
"join": "Entrar",
|
||||
"ignore": "Ignore",
|
||||
"loading": "Carregando {appName}"
|
||||
},
|
||||
"message": {
|
||||
"you": "Você",
|
||||
"user_created_room": "{user} criou a sala",
|
||||
"user_aliased_room": "{user} criou o codinome da sala {alias}",
|
||||
"user_changed_display_name": "{user} alterou o nome de exibição para {displayName}",
|
||||
"user_changed_avatar": "{user} alterou o avatar",
|
||||
"user_changed_room_avatar": "{user} alterou o avatar da sala",
|
||||
"user_encrypted_room": "{user} criptografou a sala",
|
||||
"user_was_invited": "{user} foi convidado para a conversa...",
|
||||
"user_joined": "{user} entrou no chat",
|
||||
"user_left": "{user} saiu do chat",
|
||||
"user_said": "{user} disse:",
|
||||
"file_prefix": "Arquivo: ",
|
||||
"edited": "(editado)",
|
||||
"download_progress": "{percentage}% baixado",
|
||||
"upload_progress": "Carregado {count}",
|
||||
"upload_progress_with_total": "Enviado {count} de {total}",
|
||||
"user_changed_room_history": "{user} fez o histórico da sala {type}",
|
||||
"room_history_world_readable": "legível por qualquer pessoa",
|
||||
"room_history_shared": "legível para todos os membros da sala",
|
||||
"room_history_invited": "legível para os membros a partir do momento em que foram convidados",
|
||||
"room_history_joined": "legível para os membros a partir de quando eles aderiram",
|
||||
"user_changed_join_rules": "{user} criou a sala {type}",
|
||||
"room_joinrule_invite": "através de convite apenas",
|
||||
"room_joinrule_public": "público",
|
||||
"user_changed_room_name": "{user} alterou o nome da sala para {name}",
|
||||
"user_changed_room_topic": "{user} alterou o assunto da sala para {topic}",
|
||||
"unread_messages": "Mensagens não lidas",
|
||||
"replying_to_event": "RESPONDENDO AO EVENTO: {message}",
|
||||
"your_message": "A sua mensagem...",
|
||||
"scale_image": "Escala da imagem",
|
||||
"user_is_typing": "{user} está digitando",
|
||||
"users_are_typing": "{count} membros estão digitando",
|
||||
"room_powerlevel_change": "{user} alterou o nível de {changes}",
|
||||
"user_powerlevel_change_from_to": "{user} de {powerOld} para {powerNew}",
|
||||
"user_changed_guest_access_closed": "{user} não permitiu que os convidados entrassem na sala",
|
||||
"user_changed_guest_access_open": "{user} permitiu que convidados entrassem na sala"
|
||||
},
|
||||
"room": {
|
||||
"members": "sem membros | 1 membro | {count} membros",
|
||||
"leave": "Sair",
|
||||
"purge_set_room_state": "Configurando o estado da sala",
|
||||
"purge_redacting_events": "Redigindo os eventos",
|
||||
"purge_removing_members": "Removendo os membros",
|
||||
"purge_failed": "Houve uma falha ao eliminar a sala!",
|
||||
"room_list_invites": "Convites",
|
||||
"room_list_rooms": "Salas"
|
||||
},
|
||||
"room_welcome": {
|
||||
"info": "Bem-vindo! Aqui estão algumas coisas que você deve saber sobre a sua sala:",
|
||||
"encrypted": "As mensagens são criptografadas de ponta a ponta.",
|
||||
"room_history_is": "O histórico da sala é {type}.",
|
||||
"room_history_joined": "As pessoas só podem ver as mensagens enviadas após entrarem.",
|
||||
"join_public": "Qualquer pessoa pode participar abrindo este link: {link}.",
|
||||
"join_invite": "Apenas as pessoas que você convidar podem participar.",
|
||||
"info_permissions": "Você pode alterar as \"permissões de participação\" a qualquer momento nas configurações da sala.",
|
||||
"got_it": "Entendi"
|
||||
},
|
||||
"new_room": {
|
||||
"new_room": "Nova sala",
|
||||
"create": "Criar",
|
||||
"next": "Próximo",
|
||||
"name_room": "Nome da sala",
|
||||
"room_topic": "Se desejar, adicione uma descrição",
|
||||
"join_permissions": "Permissões de ingresso",
|
||||
"set_join_permissions": "Defina as permissões de ingresso",
|
||||
"join_permissions_info": "Essas permissões determinam como as pessoas podem entrar na sala e com que facilidade as outras pessoas podem ser convidadas. Eles podem ser alteradas a qualquer momento.",
|
||||
"get_link": "Obter o link",
|
||||
"add_people": "Adicionar pessoas",
|
||||
"link_copied": "Link copiado!",
|
||||
"public_info": "Qualquer pessoa com um link",
|
||||
"public_description": "Obtenha um link para compartilhar",
|
||||
"invite_info": "Apenas pessoas que foram adicionadas",
|
||||
"invite_description": "Escolha numa lista ou busque pelo ID da conta",
|
||||
"status_creating": "Criando a sala",
|
||||
"status_avatar_total": "Enviando o avatar: {count} de {total}",
|
||||
"status_avatar": "Enviando avatar: {count}"
|
||||
},
|
||||
"device_list": {
|
||||
"title": "DISPOSITIVOS",
|
||||
"blocked": "Bloqueado",
|
||||
"verified": "Verificado",
|
||||
"not_verified": "Não verificado"
|
||||
},
|
||||
"login": {
|
||||
"title": "Entrar",
|
||||
"username": "Nome de usuário (ex: marta)",
|
||||
"password": "Digite a senha",
|
||||
"username_required": "É obrigatório um nome de usuário",
|
||||
"password_required": "A senha é obrigatória",
|
||||
"login": "Entrar",
|
||||
"create_room": "Cadastre-se e crie uma sala",
|
||||
"or": "OU"
|
||||
},
|
||||
"profile": {
|
||||
"title": "Meu perfil",
|
||||
"temporary_identity": "Essa identidade é temporária. Defina uma senha para usá-la novamente",
|
||||
"set_password": "Defina a senha",
|
||||
"change_name": "Altere o nome",
|
||||
"change_password": "Altere a senha",
|
||||
"select_language": "Idioma",
|
||||
"password_old": "Senha antiga",
|
||||
"password_new": "Nova senha",
|
||||
"password_repeat": "Repita a nova senha",
|
||||
"display_name": "Nome de exibição"
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"you_are": "Você é",
|
||||
"identity": "{displayName}",
|
||||
"identity_temporary": "{displayName}",
|
||||
"edit_profile": "Edite o perfil",
|
||||
"logout": "Sair",
|
||||
"want_more": "Quer mais?",
|
||||
"powered_by": "Esta sala é oferecida por {product}. Saiba mais em {productLink} ou vá em frente e crie outra sala!",
|
||||
"new_room": "+ Nova sala"
|
||||
},
|
||||
"join": {
|
||||
"title": "Bem-vindo ao {roomName}",
|
||||
"user_name_label": "Nome do usuário",
|
||||
"shared_computer": "Este é um dispositivo compartilhado",
|
||||
"joining_as": "Você está entrando como:",
|
||||
"join": "Entrar na sala",
|
||||
"join_guest": "Entrar como um convidado",
|
||||
"status_logging_in": "Fazendo login...",
|
||||
"status_joining": "Entrando na sala...",
|
||||
"join_failed": "Houve uma falha ao entrar na sala."
|
||||
},
|
||||
"leave": {
|
||||
"title_public": "Adeus, {user}",
|
||||
"text_public": "Você sempre pode entrar nesta sala novamente caso saiba o link.",
|
||||
"text_public_lastroom": "Caso queira entrar nesta sala novamente, você pode entrar com uma nova identidade. Para manter o {user}, {action}.",
|
||||
"title_invite": "Tem certeza que quer sair?",
|
||||
"text_invite": "Esta sala está trancada. Você não pode voltar a participar sem uma permissão especial.",
|
||||
"create_account": "Crie uma conta",
|
||||
"go_back": "Retorna",
|
||||
"leave": "Sair"
|
||||
},
|
||||
"purge_room": {
|
||||
"title": "Exclui a sala?",
|
||||
"info": "Todos os membros e as mensagens serão excluídos. Essa ação não pode ser desfeita.",
|
||||
"button": "Excluir",
|
||||
"n_seconds": "{seconds} segundos",
|
||||
"self_destruct": "A sala se autodestruirá em segundos.",
|
||||
"deleting": "Excluindo a sala:",
|
||||
"notified": "Nós notificamos os membros.",
|
||||
"room_deletion_notice": "Hora de dizer adeus! Esta sala foi excluída pelo {user}. Ela se autodestruirá em segundos."
|
||||
},
|
||||
"goodbye": {
|
||||
"room_deleted": "A sala foi excluída.",
|
||||
"close_tab": "Feche a guia do navegador",
|
||||
"view_other_rooms": "Ver as outras salas"
|
||||
},
|
||||
"room_info": {
|
||||
"title": "Detalhes da sala",
|
||||
"created_by": "Criado pelo {user}",
|
||||
"permissions": "Permissões de ingresso",
|
||||
"join_invite": "Somente pessoas adicionadas",
|
||||
"join_public": "Qualquer pessoa com um link",
|
||||
"copy_link": "Copie o link do convite",
|
||||
"link_copied": "Link copiado!",
|
||||
"purge": "Excluir a sala",
|
||||
"members": "Membros",
|
||||
"user": "{user}",
|
||||
"user_you": "{user} (você)",
|
||||
"hide_all": "Esconda",
|
||||
"show_all": "Mostre tudo >",
|
||||
"leave_room": "Sair",
|
||||
"version_info": "Desenvolvido por Guardian Project. Versão: {version}",
|
||||
"scan_code": "Faça a varredura para entrar na sala"
|
||||
},
|
||||
"room_info_sheet": {
|
||||
"this_room": "Esta sala",
|
||||
"view_details": "Ver os detalhes"
|
||||
},
|
||||
"voice_recorder": {
|
||||
"swipe_to_cancel": "Deslize para cancelar",
|
||||
"release_to_cancel": "Solte para cancelar",
|
||||
"failed_to_record": "Houve uma falha ao gravar o áudio",
|
||||
"not_supported_title": "Não suportado",
|
||||
"not_supported_text": "Infelizmente, este navegador não oferece suporte para gravação de áudio."
|
||||
},
|
||||
"power_level": {
|
||||
"admin": "administrador",
|
||||
"moderator": "moderador",
|
||||
"default": "padrão",
|
||||
"custom": "personalizado ({level})",
|
||||
"restricted": "restrito"
|
||||
}
|
||||
}
|
||||
222
src/assets/translations/ro.json
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
{
|
||||
"menu": {
|
||||
"ok": "OK",
|
||||
"loading": "Se încarcă {appName}",
|
||||
"undo": "Anulează",
|
||||
"new_room": "Cameră nouă",
|
||||
"logout": "Ieșire",
|
||||
"login": "Autentificare",
|
||||
"back": "ÎNAPOI",
|
||||
"send": "Trimiteți",
|
||||
"cancel": "Anulează",
|
||||
"download": "Descărcați",
|
||||
"delete": "Ștergeți",
|
||||
"edit": "Editați",
|
||||
"reply": "Răspuns",
|
||||
"start_private_chat": "Chat privat cu acest utilizator",
|
||||
"ignore": "Ignoră",
|
||||
"join": "Alătură-te"
|
||||
},
|
||||
"power_level": {
|
||||
"restricted": "restricționat",
|
||||
"custom": "personalizat ({nivel})",
|
||||
"default": "implicit",
|
||||
"moderator": "coordonator",
|
||||
"admin": "administrator"
|
||||
},
|
||||
"voice_recorder": {
|
||||
"not_supported_text": "Din păcate, acest browser nu acceptă înregistrarea audio.",
|
||||
"not_supported_title": "Nu se acceptă",
|
||||
"failed_to_record": "Nu a reușit să înregistreze audio",
|
||||
"release_to_cancel": "Eliberare pentru anulare",
|
||||
"swipe_to_cancel": "Glisați pentru a anula"
|
||||
},
|
||||
"room_info_sheet": {
|
||||
"view_details": "Vezi detalii",
|
||||
"this_room": "Această cameră"
|
||||
},
|
||||
"room_info": {
|
||||
"scan_code": "Scanare pentru a intra în cameră",
|
||||
"version_info": "Realizat de Guardian Project. Versiune: {version}",
|
||||
"leave_room": "Plecare",
|
||||
"show_all": "Arată tot >",
|
||||
"hide_all": "Ascundeți",
|
||||
"user_you": "{user} (tu)",
|
||||
"user": "{user}",
|
||||
"members": "Membri",
|
||||
"purge": "Ștergeți camera",
|
||||
"link_copied": "Link copiat!",
|
||||
"copy_link": "Copiați link-ul de invitație",
|
||||
"join_public": "Oricine are un link",
|
||||
"join_invite": "Numai persoane adăugate",
|
||||
"permissions": "Permisiuni de aderare",
|
||||
"created_by": "Creat de {user}",
|
||||
"title": "Detalii despre cameră"
|
||||
},
|
||||
"goodbye": {
|
||||
"view_other_rooms": "Vezi alte camere",
|
||||
"close_tab": "Închideți fila din browser",
|
||||
"room_deleted": "Cameră ștearsă."
|
||||
},
|
||||
"purge_room": {
|
||||
"room_deletion_notice": "E timpul să ne luăm rămas bun! Această cameră a fost ștearsă de {user}. Se va autodistruge în câteva secunde.",
|
||||
"notified": "Am notificat membrii.",
|
||||
"deleting": "Ștergerea camerei:",
|
||||
"self_destruct": "Camera se va autodistruge în câteva secunde.",
|
||||
"n_seconds": "{seconds} secunde",
|
||||
"button": "Ștergeți",
|
||||
"info": "Toți membrii și toate mesajele vor fi eliminate. Această acțiune nu poate fi anulată.",
|
||||
"title": "Ștergeți camera?"
|
||||
},
|
||||
"leave": {
|
||||
"leave": "Lăsați",
|
||||
"go_back": "Înapoi",
|
||||
"create_account": "creați un cont",
|
||||
"text_invite": "Această cameră este închisă. Nu puteți intra din nou fără o permisiune specială.",
|
||||
"title_invite": "Ești sigur că vrei să pleci?",
|
||||
"text_public_lastroom": "Dacă doriți să vă înscrieți din nou în această cameră, vă puteți înscrie sub o nouă identitate. Pentru a păstra {user}, {action}.",
|
||||
"text_public": "Puteți oricând să vă alăturați din nou acestei camere dacă știți link-ul.",
|
||||
"title_public": "La revedere, {user}"
|
||||
},
|
||||
"invite": {
|
||||
"status_error": "Nu ați reușit să invitați unul sau mai mulți prieteni!",
|
||||
"status_inviting": "Invitați prietenul {index} din {count}",
|
||||
"send_invites_to": "Trimiteți invitații la",
|
||||
"done": "Realizat",
|
||||
"title": "Adăugați prieteni"
|
||||
},
|
||||
"join": {
|
||||
"join_failed": "Nu a reușit să intre în cameră.",
|
||||
"status_joining": "Intrarea în cameră...",
|
||||
"status_logging_in": "Autentificare...",
|
||||
"join_guest": "Alăturați-vă ca invitat",
|
||||
"join": "Alăturați-vă camerei",
|
||||
"joining_as": "Vă înscrieți ca:",
|
||||
"shared_computer": "Acesta este un dispozitiv partajat",
|
||||
"user_name_label": "Numele utilizatorului",
|
||||
"title": "Bine ați venit la {roomName}"
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"new_room": "+ Cameră nouă",
|
||||
"powered_by": "Această cameră este alimentată de {product}. Aflați mai multe la {productLink} sau mergeți mai departe și creați o altă cameră!",
|
||||
"want_more": "Vrei mai mult?",
|
||||
"logout": "Deconectare",
|
||||
"edit_profile": "Editați profilul",
|
||||
"identity_temporary": "{displayName}",
|
||||
"identity": "{displayName}",
|
||||
"you_are": "Tu ești"
|
||||
},
|
||||
"profile": {
|
||||
"display_name": "Afișați numele",
|
||||
"password_repeat": "Repetați noua parolă",
|
||||
"password_new": "Parolă nouă",
|
||||
"password_old": "Parolă veche",
|
||||
"select_language": "Limbă",
|
||||
"change_password": "Schimbați parola",
|
||||
"change_name": "Schimbă numele",
|
||||
"set_password": "Setați parola",
|
||||
"temporary_identity": "Această identitate este temporară. Setați o parolă pentru a o utiliza din nou",
|
||||
"title": "Profilul meu"
|
||||
},
|
||||
"login": {
|
||||
"login": "Autentificare",
|
||||
"password_required": "Parola este necesară",
|
||||
"username_required": "Numele de utilizator este necesar",
|
||||
"password": "Introduceți Parola",
|
||||
"username": "Nume utilizator (ex: marta)",
|
||||
"title": "Autentificare",
|
||||
"create_room": "",
|
||||
"or": ""
|
||||
},
|
||||
"device_list": {
|
||||
"not_verified": "Nu a fost verificat",
|
||||
"verified": "Verificat",
|
||||
"blocked": "Blocat",
|
||||
"title": "DEVICES"
|
||||
},
|
||||
"new_room": {
|
||||
"status_avatar": "Încărcarea avatarului: {count}",
|
||||
"status_avatar_total": "Încărcarea avatarului: {count} din {total}",
|
||||
"status_creating": "Crearea camerei",
|
||||
"invite_description": "Alegeți dintr-o listă sau căutați după ID-ul contului",
|
||||
"invite_info": "Numai persoanele adăugate",
|
||||
"public_description": "Obțineți un link pentru a partaja",
|
||||
"public_info": "Oricine are un link",
|
||||
"link_copied": "Link copiat!",
|
||||
"add_people": "Adăugați persoane",
|
||||
"get_link": "Obțineți link-ul",
|
||||
"join_permissions_info": "Aceste permisiuni determină modul în care oamenii se pot alătura camerei și cât de ușor pot fi invitați alții. Acestea pot fi modificate oricând.",
|
||||
"set_join_permissions": "Setați permisiunile de asociere",
|
||||
"join_permissions": "Permisiuni de aderare",
|
||||
"room_topic": "Adăugați o descriere dacă doriți",
|
||||
"name_room": "Nume cameră",
|
||||
"next": "Următorul",
|
||||
"create": "Creați",
|
||||
"new_room": "Cameră nouă"
|
||||
},
|
||||
"room_welcome": {
|
||||
"got_it": "L-am prins",
|
||||
"info_permissions": "Puteți modifica oricând \"permisiunile de alăturare\" în setările camerei.",
|
||||
"join_invite": "Numai persoanele pe care le invitați se pot înscrie.",
|
||||
"join_public": "Oricine se poate înscrie deschizând acest link: {link}.",
|
||||
"room_history_joined": "Oamenii pot vedea doar mesajele trimise după ce se înscriu.",
|
||||
"room_history_is": "Istoricul camerei este {type}.",
|
||||
"encrypted": "Mesajele sunt criptate de la un capăt la altul.",
|
||||
"info": "Bine ați venit! Iată câteva lucruri pe care trebuie să le știți despre camera dumneavoastră:"
|
||||
},
|
||||
"room": {
|
||||
"room_list_rooms": "Camere",
|
||||
"room_list_invites": "Invită",
|
||||
"purge_failed": "Nu a reușit să curețe camera!",
|
||||
"purge_removing_members": "Îndepărtarea membrilor",
|
||||
"purge_redacting_events": "Redactarea evenimentelor",
|
||||
"purge_set_room_state": "Setarea stării camerei",
|
||||
"leave": "Lăsați",
|
||||
"members": "fără membri | 1 membru | {count} membri"
|
||||
},
|
||||
"message": {
|
||||
"user_changed_guest_access_open": "{user} a permis oaspeților să intre în cameră",
|
||||
"user_changed_guest_access_closed": "{user} a interzis oaspeților să intre în cameră",
|
||||
"user_powerlevel_change_from_to": "{user} de la {powerOld} la {powerNew}",
|
||||
"room_powerlevel_change": "{user} a schimbat nivelul de putere al {changes}",
|
||||
"users_are_typing": "{count} membrii tastează",
|
||||
"user_is_typing": "{user} tastează",
|
||||
"scale_image": "Imagine la scară",
|
||||
"your_message": "Mesajul dumneavoastră...",
|
||||
"replying_to_event": "RĂSPUNS LA EVENIMENT: {message}",
|
||||
"unread_messages": "Mesaje necitite",
|
||||
"user_changed_room_topic": "{user} a schimbat subiectul camerei în {topic}",
|
||||
"user_changed_room_name": "{user} a schimbat numele camerei în {name}",
|
||||
"room_joinrule_public": "publică",
|
||||
"room_joinrule_invite": "numai pe bază de invitație",
|
||||
"user_changed_join_rules": "{user} a făcut camera {type}",
|
||||
"room_history_joined": "care pot fi citite de membri din momentul în care au aderat",
|
||||
"room_history_invited": "care pot fi citite de membri din momentul în care au fost invitați",
|
||||
"room_history_shared": "poate fi citit de toți membrii din cameră",
|
||||
"room_history_world_readable": "poate fi citit de oricine",
|
||||
"user_changed_room_history": "{user} a făcut istoricul camerei {type}",
|
||||
"upload_progress_with_total": "Ați încărcat {count} din {total}",
|
||||
"upload_progress": "Încărcat {count}",
|
||||
"download_progress": "{percentage}% descărcat",
|
||||
"edited": "(editat)",
|
||||
"file_prefix": "Fișier: ",
|
||||
"user_said": "{user} a spus:",
|
||||
"user_left": "{user} a părăsit chat-ul",
|
||||
"user_joined": "{user} s-a alăturat chat-ului",
|
||||
"user_was_invited": "{user} a fost invitat la chat...",
|
||||
"user_encrypted_room": "{user} a făcut camera criptată",
|
||||
"user_changed_room_avatar": "{user} a schimbat avatarul camerei",
|
||||
"user_changed_avatar": "{user} a schimbat avatarul",
|
||||
"user_changed_display_name": "{user} a schimbat numele de afișare în {displayName}",
|
||||
"user_aliased_room": "{user} a creat aliasul camerei {alias}",
|
||||
"user_created_room": "{user} a creat camera",
|
||||
"you": "Tu"
|
||||
},
|
||||
"language_display_name": "Engleză",
|
||||
"fallbacks": {
|
||||
"download_name": "Descărcați",
|
||||
"original_text": "<original text>",
|
||||
"video_file": "Fișier video",
|
||||
"audio_file": "Fișier audio"
|
||||
}
|
||||
}
|
||||
22
src/assets/translations/si.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"menu": {
|
||||
"ok": "හරි",
|
||||
"download": "බාගන්න",
|
||||
"edit": "සංස්කරණය",
|
||||
"reply": "පිලිතුර",
|
||||
"start_private_chat": "මෙම පරිශීලක සමඟ පුද්ගලික සංවාදය",
|
||||
"undo": "පෙරසේ",
|
||||
"new_room": "නව කාමරය",
|
||||
"logout": "නික්මෙන්න",
|
||||
"login": "පිවිසෙන්න",
|
||||
"back": "ආපසු",
|
||||
"send": "යවන්න",
|
||||
"cancel": "අවලංගු කරන්න"
|
||||
},
|
||||
"language_display_name": "ඉංග්රීසි",
|
||||
"message": {
|
||||
"download_progress": "{percentage}% බාගත වී ඇත",
|
||||
"file_prefix": "ගොනුව: ",
|
||||
"you": "ඔබ"
|
||||
}
|
||||
}
|
||||
223
src/assets/translations/ug.json
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
{
|
||||
"language_is_rtl": true,
|
||||
"menu": {
|
||||
"ok": "تامام",
|
||||
"download": "چۈشۈرۈش",
|
||||
"delete": "ئۆچۈرۈش",
|
||||
"edit": "تەھرىر",
|
||||
"loading": "يۈكلەۋاتىدۇ",
|
||||
"ignore": "سەل قاراڭ",
|
||||
"join": "قوشۇلۇش",
|
||||
"undo": "بىكارلاش",
|
||||
"new_room": "يېڭى ئۆي",
|
||||
"logout": "چېكىنىش",
|
||||
"login": "كىرىش",
|
||||
"back": "قايتۇرۇش",
|
||||
"send": "يوللاش",
|
||||
"cancel": "ئەمەلدىن قالدۇرۇڭ",
|
||||
"reply": "جاۋاب",
|
||||
"start_private_chat": "قوللانغۇچى بىلەن شەخسى ئۇچۇرلاشماق"
|
||||
},
|
||||
"message": {
|
||||
"upload_progress_with_total": "{ئومۇمىي} نىڭ {سان} يۈكلەندى",
|
||||
"upload_progress": "يۈكلەندى {سان}",
|
||||
"download_progress": "{پىرسەنت}% چۈشۈرۈلدى",
|
||||
"edited": "تەھرىرلەندى",
|
||||
"file_prefix": "ھۆججەت ",
|
||||
"user_said": "قوللانغۇچى سۆزلىدى:",
|
||||
"user_left": "قوللانغۇچى مۇنازىرىدىن چېكىندى",
|
||||
"user_joined": "قوللانغۇچى مۇنازىرىغا قاتناشتى",
|
||||
"user_was_invited": "قوللانغۇچى مۇنازىرە ئۆيىگە تەكلىپ قىلىندى...",
|
||||
"user_encrypted_room": "قوللانغۇچى مۇنازىرە-خانىنى سىفىرلاشتۇردى",
|
||||
"user_changed_room_avatar": "قوللانغۇچى مۇنازىرە-خانىدىكى كۆرىنىشىنى ئۆزگەرتتى",
|
||||
"user_changed_avatar": "قوللانغۇچى كۆرىنىشىنى ئۆزگەرتتى",
|
||||
"user_changed_display_name": "قوللانغۇچىنىڭ ئىسمى«يېڭى ئىسىمغا» ئۆزگەرتىلدى",
|
||||
"user_aliased_room": "مۇنازىرە ئۆيىنىڭ ئىسمى ئۆزگەرتىلدى",
|
||||
"user_created_room": "يېڭى مۇنازىرە ئۆيى قۇرۇلدى",
|
||||
"you": "سىز",
|
||||
"room_powerlevel_change": "{قوللانغۇچى} دەرىجىسىنى ئۆزگەرتىش {ئۆزگەرتىش}",
|
||||
"user_changed_guest_access_open": "قوللانغۇچى ئەزالارنىڭ مۇنازىرەخانىغا قوشۇلىشىغا رۇخسەت قىلدى",
|
||||
"user_changed_guest_access_closed": "قوللانغۇچى ئەزالارنىڭ مۇنازىرەخانىغا قوشۇلۇشتىن رەت قىلىندى",
|
||||
"user_powerlevel_change_from_to": "قوللانغۇچى بۇرۇنقى دەرىجىسىدىن يېڭى دەرىجىسىگە كۆتۈرىلدى",
|
||||
"scale_image": "كىچىكلەنمە رەسىم",
|
||||
"users_are_typing": "ئەزالىرى يېزىۋاتىدۇ {نەپەر}",
|
||||
"user_is_typing": "قوللانغۇچى يېزىۋاتىدۇ",
|
||||
"your_message": "ئۇچۇرىڭىز ...",
|
||||
"replying_to_event": "پائالىيەتكە جاۋاب: {ئۇچۇر}",
|
||||
"unread_messages": "ئوقۇلمىغان ئۇچۇرلار",
|
||||
"user_changed_room_topic": "قوللانغۇچى مۇنازىرەخانىنىڭ تېمىسىنى ئۆزگەرتتى",
|
||||
"user_changed_room_name": "قوللانغۇچى مۇنازىرەخانىنىڭ ئىسمىنى ئۆزگەرتتى",
|
||||
"room_joinrule_public": "كۆپچىلىك",
|
||||
"room_joinrule_invite": "تەكلىپ قىلىڭ",
|
||||
"user_changed_join_rules": "قوللانغۇچى مۇنازىرەخانىنى مەلۇم تىپقا ئۆزگەرتتى",
|
||||
"room_history_joined": "ئەزالار قاتناشقاندىن باشلاپ ئوقۇغىلى بولىدۇ",
|
||||
"room_history_invited": "ئەزالار تەكلىپ قىلىنغان ۋاقىتتىن باشلاپ ئوقۇغىلى بولىدۇ",
|
||||
"room_history_shared": "مۇنازىرەخانىدىكى ھەركىم ئوقۇيالايدۇ",
|
||||
"room_history_world_readable": "ھەركىم ئوقۇيالايدۇ",
|
||||
"user_changed_room_history": "قوللانغۇچى» مۇنازىرەخانىنىڭ تارىخىنى قۇردى»"
|
||||
},
|
||||
"language_display_name": "ئىنگىلزچە",
|
||||
"new_room": {
|
||||
"link_copied": "ئۇلىنىش كۆچۈرۈلدى!",
|
||||
"status_avatar": "باش سۈرىتى يۈكلىنىۋاتىدۇ: {ئومۇمىي}",
|
||||
"status_avatar_total": "باش سۈرىتىنى يۈكلەش: {ئومۇمىي} نىڭ {سان}",
|
||||
"status_creating": "مۇنازىرە ئۆيى قۇرۇڭ",
|
||||
"invite_description": "تىزىملىكتىن تاللاڭ ياكى ھېسابات كىملىكى ئارقىلىق ئىزدەڭ",
|
||||
"invite_info": "پەقەت ئەزالارنى قوشۇش",
|
||||
"public_description": "ئورتاقلىشىش ئۈچۈن ئۇلىنىشقا ئېرىشىڭ",
|
||||
"public_info": "ئۇلىنىشى بارلار",
|
||||
"add_people": "ئەزا قوشۇش",
|
||||
"get_link": "ئۇلىنىشقا ئېرىشىڭ",
|
||||
"join_permissions_info": "بۇ ئىجازەتلەر كىشىلەرنىڭ مۇنازىرەخانىغا قانداق قوشۇلالايدىغانلىقىنى ۋە باشقىلارنى قانچىلىك ئاسانلا تەكلىپ قىلغىلى بولىدىغانلىقىنى بەلگىلەيدۇ. ئۇلارنى خالىغان ۋاقىتتا ئۆزگەرتكىلى بولىدۇ.",
|
||||
"set_join_permissions": "قوشۇلۇش ئىجازەتنامىسىنى بەلگىلەڭ",
|
||||
"join_permissions": "قوشۇلۇش ئىجازىتى",
|
||||
"room_topic": "خالىسىڭىز چۈشەندۈرۈش قوشۇڭ",
|
||||
"name_room": "مۇنازىرەخانىغا ئىسىم قويۇڭ",
|
||||
"next": "كېيىنكى",
|
||||
"create": "قۇرۇش",
|
||||
"new_room": "يېڭى مۇنازىرەخانا"
|
||||
},
|
||||
"room": {
|
||||
"purge_failed": "مۇنازىرەخانىنى يۇيۇش مەغلۇب بولدى!",
|
||||
"room_list_rooms": "مۇنازىرەخانا",
|
||||
"room_list_invites": "تەكلىپ قىلىش",
|
||||
"purge_removing_members": "ئەزالارنى چىقىرىۋېتىش",
|
||||
"purge_redacting_events": "پائالىيەتلەرنى تەھرىرلەش",
|
||||
"purge_set_room_state": "مۇنازىرەخانىنىڭ شەرتىنى قۇرۇش",
|
||||
"leave": "كېتىش",
|
||||
"members": "ئەزالار يوق | بىر ئەزا | [نەپەر] ئەزا"
|
||||
},
|
||||
"leave": {
|
||||
"text_public_lastroom": "ئەگەر بۇ ئۆيگە يەنە قوشۇلماقچى بولسىڭىز ، يېڭى سالاھىيەت ئاستىدا قاتناشسىڭىز بولىدۇ. {ئىشلەتكۈچى} ، {ھەرىكەت} نى ساقلاش.",
|
||||
"leave": "كېتىڭ",
|
||||
"go_back": "قايتىڭ",
|
||||
"create_account": "ھېسابات قۇرۇڭ",
|
||||
"text_invite": "بۇ ئۆي قۇلۇپلانغان. ئالاھىدە ئىجازەت ئالماي تۇرۇپ قايتا جەم بولالمايسىز.",
|
||||
"title_invite": "كەتمەكچىمۇ؟",
|
||||
"text_public": "ئۇلىنىشنى بىلسىڭىز ھەمىشە بۇ ئۆيگە قايتا كىرەلەيسىز.",
|
||||
"title_public": "خەير خوش ، {ئىشلەتكۈچى}"
|
||||
},
|
||||
"join": {
|
||||
"join_failed": "مۇنازىرە ئۆيىگە قوشۇلۇش مەغلۇب بولدى.",
|
||||
"status_joining": "مۇنازىرىگە كىرىش...",
|
||||
"status_logging_in": "كىرىش ...",
|
||||
"join_guest": "مېھمان سۈپىتىدە قاتنىشىڭ",
|
||||
"join": "مۇنازىرىگە قوشۇلۇڭ",
|
||||
"joining_as": "سىز تۆۋەندىكىدەك قاتنىشىۋاتىسىز:",
|
||||
"shared_computer": "بۇ ئورتاق ئىشلىتىلىدىغان ئۈسكۈنە",
|
||||
"user_name_label": "قوللانغۇچى ئىسمى",
|
||||
"title": "{ياتاق ئىسمى} غا خۇش كەپسىز"
|
||||
},
|
||||
"room_welcome": {
|
||||
"info_permissions": "ياتاق تەڭشىكىدە خالىغان ۋاقىتتا «قوشۇلۇش ئىجازەتنامىسى» نى ئۆزگەرتەلەيسىز.",
|
||||
"got_it": "چۈشەندىم",
|
||||
"join_invite": "سىز تەكلىپ قىلغان كىشىلەرلا قاتناشسا بولىدۇ.",
|
||||
"join_public": "ھەركىم بۇ ئۇلىنىشنى ئېچىش ئارقىلىق قوشۇلالايدۇ: {ئۇلىنىش}.",
|
||||
"room_history_joined": "ئەزالارقوشۇلغاندىن كېيىنلا ئەۋەتىلگەن ئۇچۇرلارنى كۆرەلەيدۇ.",
|
||||
"room_history_is": "مۇنازىرەخانا تارىخى {تىپى}.",
|
||||
"encrypted": "ئۇچۇرلار ئاخىرىغىچە مەخپىيلەشتۈرۈلگەن.",
|
||||
"info": "خۇش كەپسىز! مۇنازىرەخانا ھەققىدە بىلىشكە تېگىشلىك بىر قانچە ئىش:"
|
||||
},
|
||||
"fallbacks": {
|
||||
"download_name": "چۈشۈرۈش",
|
||||
"original_text": "<ئەسلى تېكىست>",
|
||||
"video_file": "سىن ھۆججىتى",
|
||||
"audio_file": "ئاۋاز ھۆججىتى"
|
||||
},
|
||||
"power_level": {
|
||||
"restricted": "چەكلەنگەن",
|
||||
"custom": "خاس دەرىجىسى",
|
||||
"default": "سۈكۈت",
|
||||
"moderator": "رىياسەتچى",
|
||||
"admin": "باشقۇرغۇچى"
|
||||
},
|
||||
"voice_recorder": {
|
||||
"not_supported_text": "ئەپسۇس ، بۇ توركۆرگۈچ ئاۋاز خاتىرىسىنى قوللىمايدۇ.",
|
||||
"not_supported_title": "قوللىمايدۇ",
|
||||
"failed_to_record": "ئاۋاز خاتىرىلىيەلمىدى",
|
||||
"release_to_cancel": "بىكار قىلىش",
|
||||
"swipe_to_cancel": "ئەمەلدىن قالدۇرۇش"
|
||||
},
|
||||
"room_info_sheet": {
|
||||
"view_details": "تەپسىلاتلارنى كۆرۈڭ",
|
||||
"this_room": "بۇ ئۆي"
|
||||
},
|
||||
"room_info": {
|
||||
"scan_code": "سىكانېرلاپ ئۆيگە قوشۇلۇڭ",
|
||||
"version_info": "قوغدىغۇچى تۈرى تەرىپىدىن ئىشلەنگەن. نەشرى: {نەشرى}",
|
||||
"leave_room": "ئايرىلماق",
|
||||
"show_all": "<ھەممىنى كۆرسەتمەك",
|
||||
"hide_all": "يوشۇرۇن",
|
||||
"user_you": "قوللانغۇچى ( سىز)",
|
||||
"user": "قوللانغۇچى",
|
||||
"members": "ئەزالار",
|
||||
"purge": "ئۆينى ئۆچۈرۈڭ",
|
||||
"link_copied": "ئۇلىنىش كۆچۈرۈلدى!",
|
||||
"copy_link": "تەكلىپ ئۇلانمىسىنى كۆچۈرۈڭ",
|
||||
"join_public": "ئۇلىنىشى بارلار",
|
||||
"join_invite": "پەقەت كىشىلەر قوشۇلدى",
|
||||
"permissions": "ئىجازەتكە قوشۇلۇڭ",
|
||||
"created_by": "{ئىشلەتكۈچى قۇرغان",
|
||||
"title": "ياتاق تەپسىلاتلىرى"
|
||||
},
|
||||
"goodbye": {
|
||||
"view_other_rooms": "باشقا ئۆيلەرنى كۆرۈڭ",
|
||||
"close_tab": "توركۆرگۈ بەتكۈچىنى تاقاڭ",
|
||||
"room_deleted": "ئۆي ئۆچۈرۈلدى."
|
||||
},
|
||||
"purge_room": {
|
||||
"room_deletion_notice": "خوشلىشىش ۋاقتى! بۇ ئۆي {ئىشلەتكۈچى} تەرىپىدىن ئۆچۈرۈلدى. ئۇ سېكۇنتتا ئۆزىنى ھالاك قىلىدۇ.",
|
||||
"notified": "ئەزالارغا ئۇقتۇرۇش قىلدۇق.",
|
||||
"deleting": "ئۆچۈرۈش ئۆيى:",
|
||||
"self_destruct": "ئۆي سېكۇنتتا ئۆزىنى ھالاك قىلىدۇ.",
|
||||
"n_seconds": "{سېكۇنت} سېكۇنت",
|
||||
"button": "ئۆچۈرۈش",
|
||||
"info": "بارلىق ئەزالار ۋە ئۇچۇرلار ئۆچۈرۈلىدۇ. بۇ ھەرىكەتنى ئەمەلدىن قالدۇرغىلى بولمايدۇ.",
|
||||
"title": "ئۆينى ئۆچۈرەمسىز؟"
|
||||
},
|
||||
"invite": {
|
||||
"status_error": "بىر ياكى بىر نەچچە دوستنى تەكلىپ قىلىش مەغلۇب بولدى!",
|
||||
"status_inviting": "{سان} نىڭ دوستى {كۆرسەتكۈچ} نى تەكلىپ قىلىش",
|
||||
"send_invites_to": "تەكلىپ قىلىش",
|
||||
"done": "تاماملاندى",
|
||||
"title": "دوست قوشۇڭ"
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"new_room": "+ يېڭى ئۆي",
|
||||
"powered_by": "بۇ مۇنازىرە ئۆيى {مەھسۇلات} ئىشلىتىلگەن.xx دىن تېخىمۇ كۆپ بىلىمگە ئېرىشىڭ ياكى ئىلگىرىلەپ باشقا ئۆي قۇرۇڭ!",
|
||||
"want_more": "تېخىمۇ كۆپ خالامسىز؟",
|
||||
"logout": "چېكىنىش",
|
||||
"edit_profile": "ئارخىپنى تەھرىرلەش",
|
||||
"identity_temporary": "{كۆرسىتىش ئىسمى}",
|
||||
"identity": "{كۆرسىتىش ئىسمى}",
|
||||
"you_are": "سىز"
|
||||
},
|
||||
"profile": {
|
||||
"display_name": "كۆرسىتىش ئىسمى",
|
||||
"password_repeat": "يېڭى پارولنى تەكرارلاڭ",
|
||||
"password_new": "يېڭى پارول",
|
||||
"password_old": "كونا پارول",
|
||||
"select_language": "تىل",
|
||||
"change_password": "پارولنى ئۆزگەرتىڭ",
|
||||
"change_name": "ئىسىم ئۆزگەرتىش",
|
||||
"set_password": "پارول بەلگىلەڭ",
|
||||
"temporary_identity": "بۇ كىملىك ۋاقىتلىق. قايتا ئىشلىتىش ئۈچۈن پارول بەلگىلەڭ",
|
||||
"title": "مېنىڭ ئارخىپىم"
|
||||
},
|
||||
"login": {
|
||||
"login": "كىرىش",
|
||||
"password_required": "پارول تەلەپ قىلىنىدۇ",
|
||||
"username_required": "قوللانغۇچى ئىسمى تەلەپ قىلىنىدۇ",
|
||||
"password": "پارول كىرگۈزۈڭ",
|
||||
"username": "قوللانغۇچى ئىسمى (مەسىلەن: marta)",
|
||||
"title": "كىرىش",
|
||||
"create_room": "",
|
||||
"or": ""
|
||||
},
|
||||
"device_list": {
|
||||
"not_verified": "دەلىللەنمىدى",
|
||||
"verified": "دەلىللەندى",
|
||||
"blocked": "چەكلەنگەن",
|
||||
"title": "ئۈسكۈنىلەر"
|
||||
}
|
||||
}
|
||||
230
src/assets/translations/zh_Hans.json
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
{
|
||||
"fallbacks": {
|
||||
"download_name": "下载",
|
||||
"original_text": "<原文>",
|
||||
"video_file": "视频文件",
|
||||
"audio_file": "音频文件"
|
||||
},
|
||||
"room_info": {
|
||||
"hide_all": "隐藏",
|
||||
"title": "聊天室详情",
|
||||
"version_info": "由守护者计划提供支持.版本:{version}",
|
||||
"leave_room_info": "注意:此步骤无法撤消。 确保您要注销并永久删除聊天记录。",
|
||||
"leave_room": "离开",
|
||||
"view_profile": "查看",
|
||||
"identity_temporary": "您的身份 {displayName} 是临时的。 您可以更改您的姓名或设置密码来保留它。",
|
||||
"identity": "您以 {displayName} 的身份登录。",
|
||||
"my_profile": "我的简历",
|
||||
"show_all": "显示所有 >",
|
||||
"user_you": "{user} (你)",
|
||||
"user": "{user}",
|
||||
"members": "成员",
|
||||
"purge": "删除聊天室",
|
||||
"link_copied": "链接已复制!",
|
||||
"join_public": "知道链接的任何人",
|
||||
"join_invite": "仅添加人员",
|
||||
"permissions": "加入权限",
|
||||
"created_by": "由 {user} 创建",
|
||||
"copy_link": "复制邀请链接",
|
||||
"scan_code": "扫一扫加入聊天室"
|
||||
},
|
||||
"leave": {
|
||||
"leave": "离开",
|
||||
"go_back": "返回",
|
||||
"create_account": "创建一个帐户",
|
||||
"text_invite": "此聊天室已锁定。未经特别许可,您不能重新加入。",
|
||||
"title_invite": "你确定要离开吗?",
|
||||
"text_public_lastroom": "如果您想再次加入此聊天室,您可以以新身份加入。 要保留{user},{action}。",
|
||||
"text_public": "如果您知道链接,您可以随时再次加入此聊天室。",
|
||||
"title_public": "再见,{user}"
|
||||
},
|
||||
"login": {
|
||||
"login": "登录",
|
||||
"password": "输入密码",
|
||||
"title": "登录",
|
||||
"password_required": "需要密码",
|
||||
"username_required": "需要用户名",
|
||||
"username": "用户名 (如: marta)",
|
||||
"create_room": "",
|
||||
"or": ""
|
||||
},
|
||||
"device_list": {
|
||||
"title": "设备",
|
||||
"not_verified": "未验证",
|
||||
"verified": "已验证",
|
||||
"blocked": "被封锁"
|
||||
},
|
||||
"room": {
|
||||
"leave": "离开",
|
||||
"members": "无成员 | 1 名成员 | {count} 名成员",
|
||||
"room_list_rooms": "聊天室",
|
||||
"room_list_invites": "邀请",
|
||||
"purge_failed": "删除聊天室失败了!",
|
||||
"purge_removing_members": "移除成员",
|
||||
"purge_redacting_events": "编辑事件",
|
||||
"purge_set_room_state": "设置聊天室状态"
|
||||
},
|
||||
"message": {
|
||||
"you": "您",
|
||||
"user_aliased_room": "{user} 把聊天室别名设为 {alias}",
|
||||
"user_created_room": "{user} 创建了聊天室",
|
||||
"user_changed_room_topic": "{user} 把聊天室的话题更改为{topic}",
|
||||
"user_changed_room_name": "{user} 把聊天室的名更改为 {name}",
|
||||
"room_joinrule_public": "公开",
|
||||
"room_joinrule_invite": "只邀请",
|
||||
"user_changed_join_rules": "{user} 已创建了聊天室 {type}",
|
||||
"room_history_joined": "成员从加入时就可以阅读",
|
||||
"room_history_invited": "成员从被邀请的那刻起可以阅读",
|
||||
"room_history_shared": "聊天室里的所有成员都可以阅读",
|
||||
"room_history_world_readable": "任何人都可读",
|
||||
"user_changed_room_history": "{user} 创建了聊天室的历史 {type}",
|
||||
"upload_progress_with_total": "已上传 {total} 的 {count}",
|
||||
"upload_progress": "已上传 {count}",
|
||||
"download_progress": "{percentage}% 已下载",
|
||||
"edited": "(已编辑)",
|
||||
"file_prefix": "文件: ",
|
||||
"user_said": "{user} 说:",
|
||||
"user_left": "{user} 已退出聊天",
|
||||
"user_joined": "{user} 已加入聊天",
|
||||
"user_was_invited": "{user} 被邀请加入聊天...",
|
||||
"user_encrypted_room": "{user} 把聊天室加密了",
|
||||
"user_changed_room_avatar": "{user} 更改了聊天室的头像",
|
||||
"user_changed_avatar": "{user} 更改了头像",
|
||||
"user_changed_display_name": "{user} 把显示名更改为 {displayName}",
|
||||
"unread_messages": "未读信息",
|
||||
"user_powerlevel_change_from_to": "{user} 从 {powerOld} 到 {powerNew}",
|
||||
"room_powerlevel_change": "{user}更改了{changes} 的能量等级",
|
||||
"users_are_typing": "{count} 成员正在输入",
|
||||
"user_is_typing": "{user} 正在输入",
|
||||
"scale_image": "缩放图像",
|
||||
"your_message": "你的信息...",
|
||||
"replying_to_event": "回复事件:{message}",
|
||||
"user_changed_guest_access_open": "{user} 允许客人加入聊天室",
|
||||
"user_changed_guest_access_closed": "{user} 不允许客人加入聊天室"
|
||||
},
|
||||
"menu": {
|
||||
"login": "登录",
|
||||
"send": "发送",
|
||||
"cancel": "取消",
|
||||
"download": "下载",
|
||||
"delete": "删除",
|
||||
"edit": "编辑",
|
||||
"start_private_chat": "与该用户私人聊天",
|
||||
"logout": "退出",
|
||||
"ok": "好的",
|
||||
"reply": "回复",
|
||||
"back": "返回",
|
||||
"new_room": "新的聊天室",
|
||||
"loading": "正在加载 {appName}",
|
||||
"ignore": "忽略",
|
||||
"join": "加入",
|
||||
"undo": "撤销"
|
||||
},
|
||||
"power_level": {
|
||||
"restricted": "被限制",
|
||||
"custom": "自定义({level})",
|
||||
"default": "默认",
|
||||
"moderator": "版主",
|
||||
"admin": "行政人员"
|
||||
},
|
||||
"voice_recorder": {
|
||||
"not_supported_text": "很遗憾,此浏览器不支持录音。",
|
||||
"not_supported_title": "不支持",
|
||||
"failed_to_record": "无法录制音频",
|
||||
"release_to_cancel": "释放取消",
|
||||
"swipe_to_cancel": "滑动取消"
|
||||
},
|
||||
"room_info_sheet": {
|
||||
"create_room": "创建群组",
|
||||
"view_details": "查看详情",
|
||||
"this_room": "这个聊天室"
|
||||
},
|
||||
"purge_room": {
|
||||
"button": "删除",
|
||||
"title": "删除聊天室?",
|
||||
"info": "所有成员和消息将被删除。无法撤销这一操作。",
|
||||
"room_deletion_notice": "该说再见了!此聊天室已被{user}删除。 它将在几秒钟内自毁。",
|
||||
"notified": "我们已经通知了会员。",
|
||||
"deleting": "删除聊天室:",
|
||||
"self_destruct": "聊天室将在几秒钟内自毁。",
|
||||
"n_seconds": "{seconds} 秒"
|
||||
},
|
||||
"invite": {
|
||||
"title": "添加好友",
|
||||
"status_inviting": "正在邀请好友{index} 的 {count}",
|
||||
"status_error": "邀请一位或多位好友失败!",
|
||||
"send_invites_to": "发送邀请至",
|
||||
"done": "完毕"
|
||||
},
|
||||
"join": {
|
||||
"status_joining": "正在加入聊天室...",
|
||||
"status_logging_in": "正在登录中...",
|
||||
"join_guest": "以访客身份加入",
|
||||
"join": "加入聊天室",
|
||||
"joining_as": "您以以下身份加入:",
|
||||
"shared_computer": "这是一台共享设备",
|
||||
"user_name_label": "用户名",
|
||||
"title": "欢迎来到 {roomName}",
|
||||
"join_failed": "加入聊天室失败。"
|
||||
},
|
||||
"profile": {
|
||||
"display_name": "显示名称",
|
||||
"password_repeat": "重复新密码",
|
||||
"password_new": "新的密码",
|
||||
"password_old": "旧密码",
|
||||
"change_password": "更改密码",
|
||||
"change_name": "更改名称",
|
||||
"set_password": "设置密码",
|
||||
"temporary_identity": "这个身份是暂时的。为了再次使用这个暂时身份,请设置密码",
|
||||
"title": "我的简历",
|
||||
"select_language": "语言"
|
||||
},
|
||||
"new_room": {
|
||||
"status_avatar": "正在上传头像:{count}",
|
||||
"status_avatar_total": "正在上传头像:{total}的 {count}",
|
||||
"status_creating": "创建聊天室",
|
||||
"invite_description": "从列表中选择或者按帐户 ID 搜索",
|
||||
"invite_info": "仅添加人员",
|
||||
"public_description": "获取链接以分享",
|
||||
"public_info": "有链接的任何人",
|
||||
"link_copied": "链接已复制!",
|
||||
"add_people": "添加人员",
|
||||
"get_link": "获取链接",
|
||||
"join_permissions_info": "这些权限决定了人们如何加入聊天室以及邀请其他人的难易程度。 你可以随时更改它们。",
|
||||
"set_join_permissions": "设置加入权限",
|
||||
"join_permissions": "加入权限",
|
||||
"name_room": "命名聊天室",
|
||||
"next": "下一步",
|
||||
"done": "完毕",
|
||||
"new_room": "新的聊天室",
|
||||
"room_topic": "如果您愿意,请添加说明",
|
||||
"create": "创建"
|
||||
},
|
||||
"room_welcome": {
|
||||
"got_it": "知道了",
|
||||
"info_permissions": "在群组设置中,您随时可以更改“加入权限”。",
|
||||
"info": "欢迎!关于您的群组,您需要了解以下几点:",
|
||||
"join_invite": "只有您邀请的人可以加入。",
|
||||
"join_public": "任何人都可以加入通过打开此链接: {link}。",
|
||||
"welcome": "欢迎!",
|
||||
"room_history_joined": "只有加入后,人们才可以看到发送的信息。",
|
||||
"room_history_is": "聊天室纪录是{type}.",
|
||||
"encrypted": "信息是端到端加密的。"
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"new_room": "+ 新的聊天室",
|
||||
"powered_by": "这个聊天室由 {product} 提供支持。 在 {productLink} 了解更多信息或继续创建另一个聊天室!",
|
||||
"want_more": "想要更多?",
|
||||
"logout": "退出登录",
|
||||
"edit_profile": "编辑个人资料",
|
||||
"identity_temporary": "{displayName}",
|
||||
"identity": "{displayName}",
|
||||
"you_are": "您是"
|
||||
},
|
||||
"goodbye": {
|
||||
"view_other_rooms": "查看其它聊天室",
|
||||
"close_tab": "关闭浏览器标签",
|
||||
"room_deleted": "聊天室已删除。"
|
||||
},
|
||||
"language_display_name": "英语"
|
||||
}
|
||||
|
|
@ -1,6 +1,11 @@
|
|||
<template>
|
||||
<v-row class="action-row ma-0 pa-0" no-gutters align-content="center" v-on="$listeners">
|
||||
<v-col cols="auto" class="mr-2">
|
||||
<v-row
|
||||
class="action-row ma-0 pa-0"
|
||||
no-gutters
|
||||
align-content="center"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<v-col cols="auto" class="me-2">
|
||||
<v-icon size="22">{{ icon }}</v-icon>
|
||||
</v-col>
|
||||
<v-col>{{ text }}</v-col>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
:style="{ top: `${isMove ? y : calcY()}px` }"
|
||||
>
|
||||
<div class="bottom-sheet-handle"><div class="bottom-sheet-handle-decoration" /></div>
|
||||
<div style="position:absolute;top:20px;left:0;right:0;bottom:0;overflow-y:auto;padding:20px">
|
||||
<div ref="sheetContent" style="position:absolute;top:20px;left:0;right:0;bottom:0;overflow-y:auto;padding:20px">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -119,6 +119,9 @@ export default {
|
|||
},
|
||||
setState(state) {
|
||||
this.state = state;
|
||||
if (state == "closed") {
|
||||
this.scrollToTop();
|
||||
}
|
||||
},
|
||||
onBackgroundClick() {
|
||||
if (this.state == "open") {
|
||||
|
|
@ -127,6 +130,12 @@ export default {
|
|||
this.setState("closed");
|
||||
}
|
||||
},
|
||||
scrollToTop() {
|
||||
const container = this.$refs.sheetContent;
|
||||
if (container) {
|
||||
container.scrollTo(0, 0);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div class="chat-root fill-height d-flex flex-column" ma-0 pa-0>
|
||||
<ChatHeader
|
||||
class="chat-header flex-grow-0 flex-shrink-0"
|
||||
v-on:header-click="$refs.roomInfoSheet.open()"
|
||||
v-on:header-click="onHeaderClick"
|
||||
/>
|
||||
<div
|
||||
class="chat-content flex-grow-1 flex-shrink-1"
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
showContextMenu = false;
|
||||
showContextMenuAnchor = null;
|
||||
"
|
||||
v-if="selectedEvent && showContextMenu"
|
||||
v-if="showMessageOperations"
|
||||
v-on:addreaction="addReaction"
|
||||
v-on:addquickreaction="addQuickReaction"
|
||||
v-on:addreply="addReply(selectedEvent)"
|
||||
|
|
@ -93,13 +93,13 @@
|
|||
:event="event"
|
||||
:nextEvent="events[index + 1]"
|
||||
:reactions="
|
||||
timelineWindow._timelineSet.getRelationsForEvent(
|
||||
timelineSet.getRelationsForEvent(
|
||||
event.getId(),
|
||||
'm.annotation',
|
||||
'm.reaction'
|
||||
)
|
||||
"
|
||||
:timelineSet="timelineWindow._timelineSet"
|
||||
:timelineSet="timelineSet"
|
||||
v-on:send-quick-reaction="sendQuickReaction"
|
||||
v-on:context-menu="showContextMenuForEvent($event)"
|
||||
v-on:own-avatar-clicked="viewProfile"
|
||||
|
|
@ -235,7 +235,7 @@
|
|||
</v-col>
|
||||
|
||||
<v-col
|
||||
v-if="config.useShortCodeStickers"
|
||||
v-if="$config.shortCodeStickers"
|
||||
class="input-area-button text-center flex-grow-0 flex-shrink-1"
|
||||
>
|
||||
<v-btn
|
||||
|
|
@ -355,7 +355,10 @@
|
|||
</v-dialog>
|
||||
</div>
|
||||
|
||||
<MessageOperationsBottomSheet ref="messageOperationsSheet">
|
||||
<MessageOperationsBottomSheet
|
||||
ref="messageOperationsSheet"
|
||||
style="background-color: #f776777"
|
||||
>
|
||||
<MessageOperationsPicker
|
||||
v-on:close="showEmojiPicker = false"
|
||||
v-if="selectedEvent"
|
||||
|
|
@ -369,7 +372,7 @@
|
|||
/>
|
||||
<VEmojiPicker
|
||||
ref="emojiPicker"
|
||||
style="width: 100%"
|
||||
style="width: 100%; background-color: #ffffff"
|
||||
@select="emojiSelected"
|
||||
/>
|
||||
</MessageOperationsBottomSheet>
|
||||
|
|
@ -406,8 +409,11 @@
|
|||
width="80%"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title>{{ $t("voice_recorder.not_supported_title") }}</v-card-title>
|
||||
<v-card-text>{{ $t("voice_recorder.not_supported_text") }}
|
||||
<v-card-title>{{
|
||||
$t("voice_recorder.not_supported_title")
|
||||
}}</v-card-title>
|
||||
<v-card-text
|
||||
>{{ $t("voice_recorder.not_supported_text") }}
|
||||
</v-card-text>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions>
|
||||
|
|
@ -453,6 +459,7 @@ import RoomJoinRules from "./messages/RoomJoinRules.vue";
|
|||
import RoomPowerLevelsChanged from "./messages/RoomPowerLevelsChanged.vue";
|
||||
import RoomGuestAccessChanged from "./messages/RoomGuestAccessChanged.vue";
|
||||
import RoomEncrypted from "./messages/RoomEncrypted.vue";
|
||||
import RoomDeletionNotice from "./messages/RoomDeletionNotice.vue";
|
||||
import DebugEvent from "./messages/DebugEvent.vue";
|
||||
import util from "../plugins/utils";
|
||||
import MessageOperations from "./messages/MessageOperations.vue";
|
||||
|
|
@ -466,7 +473,6 @@ import MessageOperationsBottomSheet from "./MessageOperationsBottomSheet";
|
|||
import stickers from "../plugins/stickers";
|
||||
import StickerPickerBottomSheet from "./StickerPickerBottomSheet";
|
||||
import BottomSheet from "./BottomSheet.vue";
|
||||
import config from "../assets/config";
|
||||
import ImageResize from "image-resize";
|
||||
const sizeOf = require("image-size");
|
||||
const dataUriToBuffer = require("data-uri-to-buffer");
|
||||
|
|
@ -533,6 +539,7 @@ export default {
|
|||
RoomPowerLevelsChanged,
|
||||
RoomGuestAccessChanged,
|
||||
RoomEncrypted,
|
||||
RoomDeletionNotice,
|
||||
DebugEvent,
|
||||
MessageOperations,
|
||||
MessageOperationsPicker,
|
||||
|
|
@ -547,10 +554,10 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
config: config,
|
||||
events: [],
|
||||
currentInput: "",
|
||||
typingMembers: [],
|
||||
timelineSet: null,
|
||||
timelineWindow: null,
|
||||
|
||||
/** true if we are currently paginating */
|
||||
|
|
@ -601,6 +608,9 @@ export default {
|
|||
|
||||
/** An array of recent emojis. Used in the "message operations" popup. */
|
||||
recentEmojis: [],
|
||||
|
||||
/** Calculated style for message operations. We position the "popup" at the selected message. */
|
||||
opStyle: "",
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -682,24 +692,8 @@ export default {
|
|||
return "";
|
||||
}
|
||||
},
|
||||
opStyle() {
|
||||
// Calculate where to show the context menu.
|
||||
//
|
||||
const ref = this.selectedEvent && this.$refs[this.selectedEvent.getId()];
|
||||
var top = 0;
|
||||
var left = 0;
|
||||
if (ref && ref[0]) {
|
||||
if (this.showContextMenuAnchor) {
|
||||
var rectAnchor = this.showContextMenuAnchor.getBoundingClientRect();
|
||||
var rectChat = this.$refs.messageOperationsStrut.getBoundingClientRect();
|
||||
top = rectAnchor.top - rectChat.top;
|
||||
left = rectAnchor.left - rectChat.left;
|
||||
if (left + 250 > rectChat.right) {
|
||||
left = rectChat.right - 250; // Pretty ugly, but we want to make sure it does not escape the screen, and we don't have the exakt width of it (yet)!
|
||||
}
|
||||
}
|
||||
}
|
||||
return "top:" + top + "px;left:" + left + "px";
|
||||
showMessageOperations() {
|
||||
return this.selectedEvent && this.showContextMenu;
|
||||
},
|
||||
avatarOpStyle() {
|
||||
// Calculate where to show the context menu.
|
||||
|
|
@ -710,7 +704,8 @@ export default {
|
|||
if (ref && ref[0]) {
|
||||
if (this.showAvatarMenuAnchor) {
|
||||
var rectAnchor = this.showAvatarMenuAnchor.getBoundingClientRect();
|
||||
var rectChat = this.$refs.avatarOperationsStrut.getBoundingClientRect();
|
||||
var rectChat =
|
||||
this.$refs.avatarOperationsStrut.getBoundingClientRect();
|
||||
top = rectAnchor.top - rectChat.top;
|
||||
left = rectAnchor.left - rectChat.left;
|
||||
// if (left + 250 > rectChat.right) {
|
||||
|
|
@ -773,6 +768,34 @@ export default {
|
|||
}
|
||||
},
|
||||
},
|
||||
showMessageOperations() {
|
||||
if (this.showMessageOperations) {
|
||||
this.$nextTick(() => {
|
||||
// Calculate where to show the context menu.
|
||||
//
|
||||
const ref =
|
||||
this.selectedEvent && this.$refs[this.selectedEvent.getId()];
|
||||
var top = 0;
|
||||
var left = 0;
|
||||
if (ref && ref[0]) {
|
||||
if (this.showContextMenuAnchor) {
|
||||
var rectAnchor =
|
||||
this.showContextMenuAnchor.getBoundingClientRect();
|
||||
var rectChat =
|
||||
this.$refs.messageOperationsStrut.getBoundingClientRect();
|
||||
var rectOps =
|
||||
this.$refs.messageOperations.$el.getBoundingClientRect();
|
||||
top = rectAnchor.top - rectChat.top;
|
||||
left = rectAnchor.left - rectChat.left;
|
||||
if (left + rectOps.width >= rectChat.right) {
|
||||
left = rectChat.right - rectOps.width - 10; // No overflow
|
||||
}
|
||||
}
|
||||
}
|
||||
this.opStyle = "top:" + top + "px;left:" + left + "px";
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
@ -800,10 +823,10 @@ export default {
|
|||
console.log("Read up to " + initialEventId);
|
||||
|
||||
//initialEventId = null;
|
||||
|
||||
this.timelineSet = this.room.getUnfilteredTimelineSet();
|
||||
this.timelineWindow = new TimelineWindow(
|
||||
this.$matrix.matrixClient,
|
||||
this.room.getUnfilteredTimelineSet(),
|
||||
this.timelineSet,
|
||||
{}
|
||||
);
|
||||
const self = this;
|
||||
|
|
@ -879,15 +902,17 @@ export default {
|
|||
) {
|
||||
this.loading = true;
|
||||
// Instead of paging though ALL history, just reload a timeline at the live marker...
|
||||
var timelineSet = this.room.getUnfilteredTimelineSet();
|
||||
var timelineWindow = new TimelineWindow(
|
||||
this.$matrix.matrixClient,
|
||||
this.room.getUnfilteredTimelineSet(),
|
||||
timelineSet,
|
||||
{}
|
||||
);
|
||||
const self = this;
|
||||
timelineWindow
|
||||
.load(null, 20)
|
||||
.then(() => {
|
||||
self.timelineSet = timelineSet;
|
||||
self.timelineWindow = timelineWindow;
|
||||
self.events = self.timelineWindow.getEvents();
|
||||
})
|
||||
|
|
@ -1056,6 +1081,23 @@ export default {
|
|||
|
||||
case "m.room.encryption":
|
||||
return RoomEncrypted;
|
||||
|
||||
case "im.keanu.room_deletion_notice": {
|
||||
// Custom event for notice 30 seconds before a room is deleted/purged.
|
||||
const deletionNotices = this.room.currentState.getStateEvents(
|
||||
"im.keanu.room_deletion_notice"
|
||||
);
|
||||
if (
|
||||
deletionNotices &&
|
||||
deletionNotices.length > 0 &&
|
||||
deletionNotices[deletionNotices.length - 1] == event
|
||||
) {
|
||||
// This is the latest/last one. Look at the status flag. Show nothing if it is "cancel".
|
||||
if (event.getContent().status != "cancel") {
|
||||
return RoomDeletionNotice;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.debugging ? DebugEvent : null;
|
||||
},
|
||||
|
|
@ -1663,6 +1705,12 @@ export default {
|
|||
updateRecentEmojis() {
|
||||
if (this.$refs.emojiPicker) {
|
||||
this.recentEmojis = this.$refs.emojiPicker.mapEmojis["Frequently"];
|
||||
if (this.recentEmojis.length < 20) {
|
||||
let peoples = this.$refs.emojiPicker.mapEmojis["Peoples"];
|
||||
for (var p of peoples) {
|
||||
this.recentEmojis.push(p);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.recentEmojis = [];
|
||||
|
|
@ -1671,6 +1719,20 @@ export default {
|
|||
formatBytes(bytes) {
|
||||
return prettyBytes(bytes);
|
||||
},
|
||||
|
||||
onHeaderClick() {
|
||||
const joinedRooms = this.$matrix.joinedRooms;
|
||||
if (
|
||||
joinedRooms &&
|
||||
joinedRooms.length == 1 &&
|
||||
joinedRooms[0].roomId == this.room.roomId
|
||||
) {
|
||||
// Only joined to this room, go directly to room details!
|
||||
this.$navigation.push({ name: "RoomInfo" });
|
||||
return;
|
||||
}
|
||||
this.$refs.roomInfoSheet.open();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -2,58 +2,77 @@
|
|||
<v-container fluid v-if="room">
|
||||
<v-row class="chat-header-row flex-nowrap">
|
||||
<v-col
|
||||
cols="auto"
|
||||
cols="auto"
|
||||
class="chat-header-members text-start ma-0 pa-0"
|
||||
style="overflow:hidden;cursor:pointer" @click.stop="onHeaderClicked"
|
||||
style="overflow: hidden; cursor: pointer"
|
||||
@click.stop="onHeaderClicked"
|
||||
>
|
||||
<v-avatar size="40" class="mr-2">
|
||||
<v-avatar size="40" class="me-2">
|
||||
<v-img :src="room.avatar" />
|
||||
</v-avatar>
|
||||
</v-col>
|
||||
|
||||
<v-col class="ma-0 pa-0 flex-shrink-1 flex-nowrap" style="overflow:hidden;cursor:pointer" @click.stop="onHeaderClicked">
|
||||
<div class="d-flex flex-nowrap room-name-inline">{{ room.summary.info.title }} <!--<v-icon>expand_more</v-icon>--></div>
|
||||
<div class="num-members">{{ $tc('room.members', memberCount) }}</div>
|
||||
<v-col
|
||||
class="ma-0 pa-0 flex-shrink-1 flex-nowrap"
|
||||
style="overflow: hidden; cursor: pointer"
|
||||
@click.stop="onHeaderClicked"
|
||||
>
|
||||
<div class="d-flex flex-nowrap room-name-inline">
|
||||
{{ room.name }}
|
||||
<!--<v-icon>expand_more</v-icon>-->
|
||||
</div>
|
||||
<div class="num-members">{{ $tc("room.members", memberCount) }}</div>
|
||||
</v-col>
|
||||
<v-col cols="auto" class="text-end ma-0 pa-0">
|
||||
<v-btn text class="leave-button" @click.stop="leaveRoom">{{$t('room.leave')}}</v-btn>
|
||||
<v-btn text class="leave-button" @click.stop="leaveRoom">{{
|
||||
$t("room.leave")
|
||||
}}</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="auto" class="text-end ma-0 pa-0 ml-2">
|
||||
<v-avatar class="avatar-32 clickable" size="32" color="#e0e0e0" @click.stop="showProfileInfo = true">
|
||||
<v-col cols="auto" class="text-end ma-0 pa-0 ms-2">
|
||||
<v-avatar
|
||||
class="avatar-32 clickable"
|
||||
size="32"
|
||||
color="#e0e0e0"
|
||||
@click.stop="showProfileInfo = true"
|
||||
>
|
||||
<img v-if="userAvatar" :src="userAvatar" />
|
||||
<span v-else class="white--text">{{
|
||||
userAvatarLetter
|
||||
}}</span>
|
||||
</v-avatar>
|
||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||
</v-avatar>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- "REALLY LEAVE?" dialog -->
|
||||
<LeaveRoomDialog :show="showLeaveConfirmation" :room="room" @close="showLeaveConfirmation = false" />
|
||||
<LeaveRoomDialog
|
||||
:show="showLeaveConfirmation"
|
||||
:room="room"
|
||||
@close="showLeaveConfirmation = false"
|
||||
/>
|
||||
|
||||
<!-- PROFILE INFO POPUP -->
|
||||
<ProfileInfoPopup :show="showProfileInfo" @close="showProfileInfo = false" />
|
||||
|
||||
<ProfileInfoPopup
|
||||
:show="showProfileInfo"
|
||||
@close="showProfileInfo = false"
|
||||
/>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LeaveRoomDialog from '../components/LeaveRoomDialog';
|
||||
import ProfileInfoPopup from '../components/ProfileInfoPopup';
|
||||
import profileInfoMixin from '../components/profileInfoMixin';
|
||||
import LeaveRoomDialog from "../components/LeaveRoomDialog";
|
||||
import ProfileInfoPopup from "../components/ProfileInfoPopup";
|
||||
import profileInfoMixin from "../components/profileInfoMixin";
|
||||
|
||||
export default {
|
||||
name: "ChatHeader",
|
||||
mixins: [profileInfoMixin],
|
||||
components: {
|
||||
LeaveRoomDialog,
|
||||
ProfileInfoPopup
|
||||
ProfileInfoPopup,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
memberCount: null,
|
||||
showLeaveConfirmation: false,
|
||||
showProfileInfo: false
|
||||
showProfileInfo: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -85,16 +104,16 @@ export default {
|
|||
|
||||
methods: {
|
||||
onEvent(event) {
|
||||
if (event.getRoomId() !== this.roomId) {
|
||||
if (!this.room || event.getRoomId() !== this.room.roomId) {
|
||||
return; // Not for this room
|
||||
}
|
||||
if (event.getType() == "m.room.member") {
|
||||
this.updateMemberCount();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
onHeaderClicked() {
|
||||
this.$emit("header-click", {event: this.event});
|
||||
this.$emit("header-click", { event: this.event });
|
||||
},
|
||||
|
||||
updateMemberCount() {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div class="create-room">
|
||||
<div>
|
||||
<v-container fluid>
|
||||
<div class="room-name">{{ $t("new_room.new_room") }}</div>
|
||||
<div class="room-name no-upper">{{ $t("new_room.new_room") }}</div>
|
||||
<v-btn
|
||||
text
|
||||
class="header-button-left"
|
||||
|
|
@ -63,10 +63,11 @@
|
|||
<v-avatar size="32">
|
||||
<v-img :src="data.item.image" />
|
||||
</v-avatar>
|
||||
<div class="ml-2">{{ data.item.name }}</div>
|
||||
<div class="ms-2">{{ data.item.name }}</div>
|
||||
</template>
|
||||
</v-select>
|
||||
<v-switch
|
||||
<v-checkbox
|
||||
class="mt-0"
|
||||
v-model="sharedComputer"
|
||||
:label="$t('join.shared_computer')"
|
||||
/>
|
||||
|
|
@ -103,20 +104,20 @@
|
|||
:disabled="step > steps.INITIAL"
|
||||
></v-text-field>
|
||||
<v-btn
|
||||
color="black"
|
||||
depressed
|
||||
class="filled-button"
|
||||
@click.stop="next"
|
||||
:disabled="roomName.length == 0"
|
||||
>{{$t('new_room.create')}}</v-btn
|
||||
>
|
||||
color="black"
|
||||
depressed
|
||||
class="filled-button"
|
||||
@click.stop="next"
|
||||
:disabled="roomName.length == 0"
|
||||
>{{ $t("new_room.create") }}</v-btn
|
||||
>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-fade-transition>
|
||||
<!-- <div class="section ma-3" flat v-if="step > steps.INITIAL"> -->
|
||||
<!-- <div class="h4 text-left">{{ $t("new_room.join_permissions") }}</div>
|
||||
<!-- <div class="h4 text-left">{{ $t("new_room.join_permissions") }}</div>
|
||||
<div class="h2 text-left">
|
||||
{{ $t("new_room.set_join_permissions") }}
|
||||
</div>
|
||||
|
|
@ -171,7 +172,7 @@
|
|||
depressed
|
||||
class="outlined-button"
|
||||
@click.stop="getPublicLink"
|
||||
><v-icon class="mr-2">link</v-icon
|
||||
><v-icon class="me-2">link</v-icon
|
||||
>{{ $t("new_room.get_link") }}</v-btn
|
||||
>
|
||||
<v-btn
|
||||
|
|
@ -180,7 +181,7 @@
|
|||
depressed
|
||||
class="outlined-button"
|
||||
@click.stop="addPeople"
|
||||
><v-icon class="mr-2">person_add</v-icon
|
||||
><v-icon class="me-2">person_add</v-icon
|
||||
>{{ $t("new_room.add_people") }}</v-btn
|
||||
>
|
||||
|
||||
|
|
@ -188,7 +189,15 @@
|
|||
{{ $t("new_room.link_copied") }}
|
||||
</div>
|
||||
-->
|
||||
<div v-if="status">{{ status }}</div>
|
||||
<div v-if="status" class="text-center">
|
||||
<v-progress-circular
|
||||
v-if="step == steps.CREATING"
|
||||
indeterminate
|
||||
color="primary"
|
||||
size="20"
|
||||
></v-progress-circular>
|
||||
{{ status }}
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
</v-fade-transition>
|
||||
<input
|
||||
|
|
@ -278,6 +287,14 @@ export default {
|
|||
}
|
||||
return true;
|
||||
},
|
||||
sharedComputer: {
|
||||
get: function () {
|
||||
return !this.$store.state.useLocalStorage;
|
||||
},
|
||||
set: function (sharedComputer) {
|
||||
this.$store.commit("setUseLocalStorage", !sharedComputer);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
@ -292,8 +309,8 @@ export default {
|
|||
if (this.step == steps.CREATED) {
|
||||
this.openRoom();
|
||||
} else if (this.step == steps.INITIAL) {
|
||||
// this.step = steps.NAME_SET;
|
||||
//} else if (this.step == steps.NAME_SET) {
|
||||
// this.step = steps.NAME_SET;
|
||||
//} else if (this.step == steps.NAME_SET) {
|
||||
// Create room with deafult setting
|
||||
this.createRoom().then((roomId) => {
|
||||
this.roomId = roomId;
|
||||
|
|
@ -341,15 +358,6 @@ export default {
|
|||
);
|
||||
});
|
||||
},
|
||||
createRoomDebug() {
|
||||
this.step = steps.CREATING;
|
||||
return new Promise((resolve, ignoredreject) => {
|
||||
setTimeout(() => {
|
||||
this.step = steps.CREATED;
|
||||
resolve("#NpexPublicRoom2:neo.keanu.im");
|
||||
}, 5000);
|
||||
});
|
||||
},
|
||||
createRoom() {
|
||||
this.step = steps.CREATING;
|
||||
|
||||
|
|
@ -362,7 +370,6 @@ export default {
|
|||
if (this.joinRule == "public") {
|
||||
createRoomOptions = {
|
||||
visibility: "private", // Not listed!
|
||||
room_alias_name: this.roomName.replace(/\s/g, "").toLowerCase(),
|
||||
name: this.roomName,
|
||||
preset: "public_chat",
|
||||
initial_state: [
|
||||
|
|
@ -373,6 +380,13 @@ export default {
|
|||
algorithm: "m.megolm.v1.aes-sha2",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "m.room.history_visibility",
|
||||
state_key: "",
|
||||
content: {
|
||||
history_visibility: "joined"
|
||||
}
|
||||
}
|
||||
],
|
||||
};
|
||||
} else {
|
||||
|
|
@ -396,6 +410,13 @@ export default {
|
|||
guest_access: "forbidden",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "m.room.history_visibility",
|
||||
state_key: "",
|
||||
content: {
|
||||
history_visibility: "joined"
|
||||
}
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
|
|
@ -457,6 +478,23 @@ export default {
|
|||
}
|
||||
}.bind(this)
|
||||
)
|
||||
.then(() => {
|
||||
if (this.joinRule == "public") {
|
||||
// Promise to get a unique alias and use it in room creation options.
|
||||
//
|
||||
return util
|
||||
.getUniqueAliasForRoomName(
|
||||
this.$matrix.matrixClient,
|
||||
this.roomName,
|
||||
this.$matrix.currentUserHomeServer
|
||||
)
|
||||
.then((alias) => {
|
||||
createRoomOptions.room_alias_name = alias;
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
return this.$matrix.matrixClient
|
||||
.createRoom(createRoomOptions)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
<template>
|
||||
<div class="created-room-welcome-header">
|
||||
<div class="h4">{{ $t("room_welcome.welcome") }}</div>
|
||||
<div class="mt-2">{{ $t("room_welcome.info") }}</div>
|
||||
<div class="mt-2" v-if="roomIsEncrypted">
|
||||
{{ $t("room_welcome.encrypted") }}
|
||||
</div>
|
||||
<div class="mt-2" v-if="roomHistoryDescription">
|
||||
{{ roomHistoryDescription }}
|
||||
</div>
|
||||
<div>{{ $t("room_welcome.info") }}</div>
|
||||
<div class="mt-2" v-if="roomJoinRule == 'public'">
|
||||
<i18n path="room_welcome.join_public" tag="span">
|
||||
<template v-slot:link>
|
||||
|
|
@ -19,8 +12,14 @@
|
|||
{{ $t("room_welcome.join_invite") }}
|
||||
</div>
|
||||
<div class="mt-2">{{ $t("room_welcome.info_permissions") }}</div>
|
||||
<div class="mt-2" v-if="roomIsEncrypted">
|
||||
{{ $t("room_welcome.encrypted") }}
|
||||
</div>
|
||||
<div class="mt-2" v-if="roomHistoryDescription">
|
||||
{{ roomHistoryDescription }}
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<v-btn text @click.stop="$emit('close')">{{
|
||||
<v-btn text @click.stop="$emit('close')" style="text-transform: none">{{
|
||||
$t("room_welcome.got_it")
|
||||
}}</v-btn>
|
||||
</div>
|
||||
|
|
@ -50,9 +49,7 @@ export default {
|
|||
type: this.$t("message.room_history_invited"),
|
||||
});
|
||||
case "joined":
|
||||
return this.$t("room_welcome.room_history_is", {
|
||||
type: this.$t("message.room_history_joined"),
|
||||
});
|
||||
return this.$t("room_welcome.room_history_joined");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,13 +1,32 @@
|
|||
<template>
|
||||
<div class="pa-4">
|
||||
<RoomList showInvites showCreate :title="$t('room.room_list_rooms')" :invitesTitle="$t('room.room_list_invites')" v-on:newroom="createRoom" />
|
||||
<v-btn block depressed class="outlined-button" @click.stop="logout">{{$t('menu.logout')}}</v-btn>
|
||||
<div class="home">
|
||||
<YouAre class="mt-4" v-if="!loading" />
|
||||
<v-container fluid class="text-center mt-8">
|
||||
<v-row align="center" justify="center">
|
||||
<v-col class="text-center" cols="auto">
|
||||
<v-img src="@/assets/logo.svg" width="64" height="64" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-card class="members ma-3" flat>
|
||||
<v-card-title class="h2">{{ $t("room.room_list_rooms") }}</v-card-title>
|
||||
<v-card-text class="pa-0">
|
||||
<RoomList
|
||||
showInvites
|
||||
showCreate
|
||||
title=""
|
||||
:invitesTitle="$t('room.room_list_invites')"
|
||||
v-on:newroom="createRoom"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- Loading indicator -->
|
||||
<v-container
|
||||
fluid
|
||||
fill-height
|
||||
style="position: absolute;background-color:rgba(0,0,0,0.2)"
|
||||
style="position: absolute; background-color: rgba(0, 0, 0, 0.2)"
|
||||
v-if="loading"
|
||||
>
|
||||
<v-row align="center" justify="center">
|
||||
|
|
@ -23,30 +42,32 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import RoomList from '../components/RoomList';
|
||||
import RoomList from "../components/RoomList";
|
||||
import YouAre from "../components/YouAre.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
RoomList
|
||||
RoomList,
|
||||
YouAre,
|
||||
},
|
||||
computed: {
|
||||
loading() {
|
||||
return !this.$matrix.ready;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
logout() {
|
||||
//TODO - For guest accounts, show warning about not being able to rejoin.
|
||||
this.$store.dispatch("logout");
|
||||
this.$nextTick(() => {
|
||||
this.$navigation.push({path: "/login"}, -1);
|
||||
})
|
||||
this.$navigation.push({ path: "/login" }, -1);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
createRoom() {
|
||||
this.$navigation.push({ name: "CreateRoom" });
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
@click.stop="handleLogin"
|
||||
:loading="loading"
|
||||
v-if="!currentUser"
|
||||
>{{$t('menu.login')}}</v-btn
|
||||
>{{ $t("menu.login") }}</v-btn
|
||||
>
|
||||
|
||||
<v-avatar class="join-avatar">
|
||||
|
|
@ -17,7 +17,9 @@
|
|||
roomName.substring(0, 1).toUpperCase()
|
||||
}}</span>
|
||||
</v-avatar>
|
||||
<div class="join-title">{{$t('join.title', {roomName: roomName})}}</div>
|
||||
<div class="join-title">
|
||||
{{ $t("join.title", { roomName: roomName }) }}
|
||||
</div>
|
||||
<div class="join-message">
|
||||
<!-- Join the group chat in a web browser or with the Keanu app. -->
|
||||
</div>
|
||||
|
|
@ -57,15 +59,15 @@
|
|||
<v-avatar size="32">
|
||||
<v-img :src="data.item.image" />
|
||||
</v-avatar>
|
||||
<div class="ml-2">{{ data.item.name }}</div>
|
||||
<div class="ms-2">{{ data.item.name }}</div>
|
||||
</template>
|
||||
</v-select>
|
||||
<v-switch v-model="sharedComputer" :label="$t('join.shared_computer')" />
|
||||
<v-checkbox class="mt-0" v-model="sharedComputer" :label="$t('join.shared_computer')" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-else>
|
||||
<v-col>
|
||||
{{$t('join.joining_as')}}
|
||||
{{ $t("join.joining_as") }}
|
||||
<div style="display: inline-block">
|
||||
<v-avatar color="#e0e0e0" style="">
|
||||
<v-img v-if="userAvatar" :src="userAvatar" />
|
||||
|
|
@ -97,7 +99,7 @@
|
|||
@click.stop="handleJoin"
|
||||
:loading="loading"
|
||||
v-if="!currentUser"
|
||||
>{{$t('join.join_guest')}}</v-btn
|
||||
>{{ $t("join.join_guest") }}</v-btn
|
||||
>
|
||||
<v-btn
|
||||
class="btn-dark"
|
||||
|
|
@ -106,7 +108,7 @@
|
|||
@click.stop="handleJoin"
|
||||
:loading="loading"
|
||||
v-else
|
||||
>{{$t('join.join')}}</v-btn
|
||||
>{{ $t("join.join") }}</v-btn
|
||||
>
|
||||
|
||||
<!-- <div class="join-privacy">
|
||||
|
|
@ -138,9 +140,11 @@ export default {
|
|||
mounted() {
|
||||
this.$matrix.on("Room.myMembership", this.onMyMembership);
|
||||
this.availableAvatars = util.getDefaultAvatars();
|
||||
this.selectAvatar(this.availableAvatars[
|
||||
Math.floor(Math.random() * this.availableAvatars.length)
|
||||
]);
|
||||
this.selectAvatar(
|
||||
this.availableAvatars[
|
||||
Math.floor(Math.random() * this.availableAvatars.length)
|
||||
]
|
||||
);
|
||||
},
|
||||
destroyed() {
|
||||
this.$matrix.off("Room.myMembership", this.onMyMembership);
|
||||
|
|
@ -194,7 +198,9 @@ export default {
|
|||
if (!this.currentUser || !this.currentUser.userId) {
|
||||
return null;
|
||||
}
|
||||
return (this.currentUserDisplayName || this.currentUser.userId.substring(1))
|
||||
return (
|
||||
this.currentUserDisplayName || this.currentUser.userId.substring(1)
|
||||
)
|
||||
.substring(0, 1)
|
||||
.toUpperCase();
|
||||
},
|
||||
|
|
@ -203,9 +209,9 @@ export default {
|
|||
return !this.$store.state.useLocalStorage;
|
||||
},
|
||||
set: function (sharedComputer) {
|
||||
this.$store.commit('setUseLocalStorage', !sharedComputer);
|
||||
this.$store.commit("setUseLocalStorage", !sharedComputer);
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
roomId: {
|
||||
|
|
@ -221,41 +227,42 @@ export default {
|
|||
|
||||
this.waitingForInfo = true;
|
||||
const self = this;
|
||||
this.waitingForMembership = true;
|
||||
this.waitingForMembership = true;
|
||||
if (this.currentUser) {
|
||||
this.$matrix.getLoginPromise()
|
||||
.then(() => {
|
||||
self.$matrix.setCurrentRoomId(self.roomAliasOrId); // Go to this room, now or when joined.
|
||||
const room = self.$matrix.getRoom(self.roomAliasOrId);
|
||||
this.$matrix
|
||||
.getLoginPromise()
|
||||
.then(() => {
|
||||
self.$matrix.setCurrentRoomId(self.roomAliasOrId); // Go to this room, now or when joined.
|
||||
const room = self.$matrix.getRoom(self.roomAliasOrId);
|
||||
|
||||
// Already joined?
|
||||
if (
|
||||
room &&
|
||||
room.hasMembershipState(self.currentUser.user_id, "join")
|
||||
) {
|
||||
// Yes, go to room
|
||||
self.$navigation.push(
|
||||
{
|
||||
name: "Chat",
|
||||
params: { roomId: util.sanitizeRoomId(this.roomAliasOrId) },
|
||||
},
|
||||
-1
|
||||
);
|
||||
return;
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log("Error logging in: ", err)
|
||||
})
|
||||
.finally(() => {
|
||||
this.waitingForMembership = false;
|
||||
this.getRoomInfo();
|
||||
});
|
||||
// Already joined?
|
||||
if (
|
||||
room &&
|
||||
room.hasMembershipState(self.currentUser.user_id, "join")
|
||||
) {
|
||||
// Yes, go to room
|
||||
self.$navigation.push(
|
||||
{
|
||||
name: "Chat",
|
||||
params: { roomId: util.sanitizeRoomId(this.roomAliasOrId) },
|
||||
},
|
||||
-1
|
||||
);
|
||||
return;
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Error logging in: ", err);
|
||||
})
|
||||
.finally(() => {
|
||||
this.waitingForMembership = false;
|
||||
this.getRoomInfo();
|
||||
});
|
||||
} else {
|
||||
this.waitingForMembership = false;
|
||||
this.getRoomInfo();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -300,7 +307,7 @@ export default {
|
|||
},
|
||||
|
||||
handleLogin() {
|
||||
this.$navigation.push({path: "/login"}, 1);
|
||||
this.$navigation.push({ path: "/login" }, 1);
|
||||
},
|
||||
|
||||
handleOpenApp() {
|
||||
|
|
@ -309,10 +316,11 @@ export default {
|
|||
|
||||
handleJoin() {
|
||||
this.loading = true;
|
||||
this.loadingMessage = this.$t('join.status_logging_in');
|
||||
this.loadingMessage = this.$t("join.status_logging_in");
|
||||
const hasUser = this.currentUser ? true : false;
|
||||
var setProfileData = false;
|
||||
return this.$matrix.getLoginPromise()
|
||||
return this.$matrix
|
||||
.getLoginPromise()
|
||||
.then(
|
||||
function (user) {
|
||||
if (user.is_guest && !hasUser) {
|
||||
|
|
@ -322,15 +330,24 @@ export default {
|
|||
setProfileData = true;
|
||||
|
||||
// Set display name and avatar directly on the matrix object.
|
||||
if (this.selectedProfile.name && this.selectedProfile.name.length > 0) {
|
||||
if (
|
||||
this.selectedProfile.name &&
|
||||
this.selectedProfile.name.length > 0
|
||||
) {
|
||||
this.$matrix.userDisplayName = this.selectedProfile.name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!setProfileData || !this.selectedProfile.name || this.selectedProfile.name.length == 0) {
|
||||
if (
|
||||
!setProfileData ||
|
||||
!this.selectedProfile.name ||
|
||||
this.selectedProfile.name.length == 0
|
||||
) {
|
||||
return Promise.resolve(user);
|
||||
} else {
|
||||
console.log("Join: Set display name to: " + this.selectedProfile.name);
|
||||
console.log(
|
||||
"Join: Set display name to: " + this.selectedProfile.name
|
||||
);
|
||||
return this.$matrix.matrixClient.setDisplayName(
|
||||
this.selectedProfile.name,
|
||||
undefined
|
||||
|
|
@ -358,7 +375,7 @@ export default {
|
|||
.then(
|
||||
function (ignoreduser) {
|
||||
console.log("Join: joining room");
|
||||
this.loadingMessage = this.$t('join.status_joining');
|
||||
this.loadingMessage = this.$t("join.status_joining");
|
||||
return this.$matrix.matrixClient.joinRoom(this.roomId);
|
||||
}.bind(this)
|
||||
)
|
||||
|
|
@ -379,7 +396,7 @@ export default {
|
|||
// TODO - handle error
|
||||
console.log("Failed to join room", err);
|
||||
this.loading = false;
|
||||
this.loadingMessage = err.toString();
|
||||
this.loadingMessage = this.$t("join.join_failed");
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -4,27 +4,35 @@
|
|||
<template v-if="roomJoinRule == 'public'">
|
||||
<h1>👋</h1>
|
||||
<h2 class="dialog-title">
|
||||
{{$t('leave.title_public',{user: $matrix.currentUserDisplayName})}}
|
||||
{{
|
||||
$t("leave.title_public", { user: $matrix.currentUserDisplayName })
|
||||
}}
|
||||
</h2>
|
||||
<div
|
||||
v-if="$matrix.currentUser.is_guest && lastRoom"
|
||||
class="dialog-text"
|
||||
>
|
||||
<i18n path="leave.text_public_lastroom" tag="p">
|
||||
<template v-slot:user>
|
||||
<span>{{ $matrix.currentUserDisplayName }}</span>
|
||||
</template>
|
||||
<template v-slot:action>
|
||||
<a @click.prevent="viewProfile">{{ $t('leave.create_account') }}</a>
|
||||
</template>
|
||||
<i18n path="leave.text_public_lastroom" tag="p">
|
||||
<template v-slot:user>
|
||||
<span>{{ $matrix.currentUserDisplayName }}</span>
|
||||
</template>
|
||||
<template v-slot:action>
|
||||
<a @click.prevent="viewProfile">{{
|
||||
$t("leave.create_account")
|
||||
}}</a>
|
||||
</template>
|
||||
</i18n>
|
||||
</div>
|
||||
<div v-else class="dialog-text">{{$t('leave.text_public')}}</div>
|
||||
<div v-else class="dialog-text">{{ $t("leave.text_public") }}</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-icon color="black" size="30">lock</v-icon>
|
||||
<h2 class="dialog-title">{{$t('leave.title_invite',{user: $matrix.currentUserDisplayName})}}</h2>
|
||||
<div class="dialog-text">{{$t('leave.text_invite')}}</div>
|
||||
<h2 class="dialog-title">
|
||||
{{
|
||||
$t("leave.title_invite", { user: $matrix.currentUserDisplayName })
|
||||
}}
|
||||
</h2>
|
||||
<div class="dialog-text">{{ $t("leave.text_invite") }}</div>
|
||||
</template>
|
||||
<v-container fluid>
|
||||
<v-row cols="12">
|
||||
|
|
@ -35,7 +43,7 @@
|
|||
block
|
||||
class="text-button"
|
||||
@click="showDialog = false"
|
||||
>{{$t('leave.go_back')}}</v-btn
|
||||
>{{ $t("leave.go_back") }}</v-btn
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="6" align="center">
|
||||
|
|
@ -45,7 +53,7 @@
|
|||
block
|
||||
class="filled-button"
|
||||
@click.stop="onLeaveRoom()"
|
||||
>{{$t('leave.leave')}}</v-btn
|
||||
>{{ $t("leave.leave") }}</v-btn
|
||||
>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,37 @@
|
|||
<template>
|
||||
<div class="login-root">
|
||||
<v-btn v-if="showBackArrow" icon @click.stop="$navigation.pop">
|
||||
<v-icon>arrow_back</v-icon>
|
||||
</v-btn>
|
||||
<div class="pa-4">
|
||||
<div class="chat-header">
|
||||
<v-container fluid>
|
||||
<v-row no-gutters>
|
||||
<v-col>
|
||||
<v-img
|
||||
src="@/assets/logo.svg"
|
||||
width="32"
|
||||
height="32"
|
||||
xclass="d-inline-block header-button-left"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<div class="room-name no-upper">{{ $t("login.title") }}</div>
|
||||
</v-col>
|
||||
<v-col class="text-right">
|
||||
<v-btn text v-if="showCloseButton" @click.stop="$navigation.pop">
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
|
||||
<div color="rgba(255,255,255,0.1)" class="text-center">
|
||||
<div class="h2">{{$t('login.title')}}</div>
|
||||
<v-form v-model="isValid">
|
||||
<v-text-field
|
||||
prepend-inner-icon="$vuetify.icons.user"
|
||||
v-model="user.user_id"
|
||||
:label="$t('login.username')"
|
||||
color="black"
|
||||
background-color="white"
|
||||
outlined
|
||||
solo
|
||||
:rules="[(v) => !!v || $t('login.username_required')]"
|
||||
:error="userErrorMessage != null"
|
||||
:error-messages="userErrorMessage"
|
||||
|
|
@ -20,28 +39,50 @@
|
|||
v-on:keyup.enter="$refs.password.focus()"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
prepend-inner-icon="$vuetify.icons.password"
|
||||
ref="password"
|
||||
v-model="user.password"
|
||||
:label="$t('login.password')"
|
||||
color="black"
|
||||
background-color="white"
|
||||
outlined
|
||||
background-color="#f5f5f5"
|
||||
filled
|
||||
type="password"
|
||||
:rules="[(v) => !!v || $t('login.password_required')]"
|
||||
:error="passErrorMessage != null"
|
||||
:error-messages="passErrorMessage"
|
||||
required
|
||||
v-on:keyup.enter="() => { if (isValid && !loading) { handleLogin() }}"
|
||||
v-on:keyup.enter="
|
||||
() => {
|
||||
if (isValid && !loading) {
|
||||
handleLogin();
|
||||
}
|
||||
}
|
||||
"
|
||||
></v-text-field>
|
||||
<v-btn
|
||||
:disabled="!isValid || loading"
|
||||
primary
|
||||
large
|
||||
block
|
||||
@click.stop="handleLogin"
|
||||
:loading="loading"
|
||||
>{{$t('login.login')}}</v-btn
|
||||
>
|
||||
<v-checkbox
|
||||
class="mt-0"
|
||||
v-model="sharedComputer"
|
||||
:label="$t('join.shared_computer')"
|
||||
/>
|
||||
<v-btn
|
||||
:disabled="!isValid || loading"
|
||||
color="black"
|
||||
depressed
|
||||
block
|
||||
@click.stop="handleLogin"
|
||||
:loading="loading"
|
||||
class="filled-button mt-4"
|
||||
>{{ $t("login.login") }}</v-btn
|
||||
>
|
||||
<div class="mt-2 overline">{{ $t("login.or") }}</div>
|
||||
<v-btn
|
||||
color="primary"
|
||||
depressed
|
||||
block
|
||||
@click.stop="handleCreateRoom"
|
||||
class="filled-button mt-2"
|
||||
>{{ $t("login.create_room") }}</v-btn
|
||||
>
|
||||
</v-form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -50,13 +91,12 @@
|
|||
<script>
|
||||
import User from "../models/user";
|
||||
import util from "../plugins/utils";
|
||||
import config from "../assets/config";
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
data() {
|
||||
return {
|
||||
user: new User(config.defaultServer, "", ""),
|
||||
user: new User(this.$config.defaultServer, "", ""),
|
||||
isValid: true,
|
||||
loading: false,
|
||||
message: "",
|
||||
|
|
@ -71,13 +111,27 @@ export default {
|
|||
currentUser() {
|
||||
return this.$store.state.auth.user;
|
||||
},
|
||||
showBackArrow() {
|
||||
showCloseButton() {
|
||||
return this.$navigation && this.$navigation.canPop();
|
||||
}
|
||||
},
|
||||
sharedComputer: {
|
||||
get: function () {
|
||||
return !this.$store.state.useLocalStorage;
|
||||
},
|
||||
set: function (sharedComputer) {
|
||||
this.$store.commit("setUseLocalStorage", !sharedComputer);
|
||||
},
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.loggedIn) {
|
||||
this.$navigation.push({name: "Chat", params: { roomId: util.sanitizeRoomId(this.$matrix.currentRoomId) }}, -1);
|
||||
this.$navigation.push(
|
||||
{
|
||||
name: "Chat",
|
||||
params: { roomId: util.sanitizeRoomId(this.$matrix.currentRoomId) },
|
||||
},
|
||||
-1
|
||||
);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -90,18 +144,12 @@ export default {
|
|||
deep: true,
|
||||
},
|
||||
message() {
|
||||
if (
|
||||
this.message &&
|
||||
this.message.toLowerCase().includes("user")
|
||||
) {
|
||||
if (this.message && this.message.toLowerCase().includes("user")) {
|
||||
this.userErrorMessage = this.message;
|
||||
} else {
|
||||
this.userErrorMessage = null;
|
||||
}
|
||||
if (
|
||||
this.message &&
|
||||
this.message.toLowerCase().includes("pass")
|
||||
) {
|
||||
if (this.message && this.message.toLowerCase().includes("pass")) {
|
||||
this.passErrorMessage = this.message;
|
||||
} else {
|
||||
this.passErrorMessage = null;
|
||||
|
|
@ -111,9 +159,8 @@ export default {
|
|||
methods: {
|
||||
handleLogin() {
|
||||
if (this.user.user_id && this.user.password) {
|
||||
|
||||
// Reset errors
|
||||
this.message = null;
|
||||
// Reset errors
|
||||
this.message = null;
|
||||
|
||||
// Is it a full matrix user id? Modify a copy, so that the UI will still show the full ID.
|
||||
var user = Object.assign({}, this.user);
|
||||
|
|
@ -123,9 +170,17 @@ export default {
|
|||
this.$store.dispatch("login", user).then(
|
||||
() => {
|
||||
if (this.$matrix.currentRoomId) {
|
||||
this.$navigation.push({name: "Chat", params: { roomId: util.sanitizeRoomId(this.$matrix.currentRoomId) }}, -1);
|
||||
this.$navigation.push(
|
||||
{
|
||||
name: "Chat",
|
||||
params: {
|
||||
roomId: util.sanitizeRoomId(this.$matrix.currentRoomId),
|
||||
},
|
||||
},
|
||||
-1
|
||||
);
|
||||
} else {
|
||||
this.$navigation.push({name: "Home"}, -1);
|
||||
this.$navigation.push({ name: "Home" }, -1);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
|
|
@ -139,6 +194,9 @@ export default {
|
|||
);
|
||||
}
|
||||
},
|
||||
handleCreateRoom() {
|
||||
this.$navigation.push({ name: "CreateRoom" });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
background-color: rgba(0, 0, 0, 0.15);
|
||||
"
|
||||
/>
|
||||
</v-fade-transition>
|
||||
|
|
|
|||
|
|
@ -2,66 +2,117 @@
|
|||
<div v-if="user" class="profile">
|
||||
<div class="chat-header">
|
||||
<v-container fluid>
|
||||
<div class="room-name">{{$t('profile.title')}}</div>
|
||||
<v-btn
|
||||
text
|
||||
class="header-button-right"
|
||||
v-show="$navigation && $navigation.canPop()"
|
||||
@click.stop="$navigation.pop"
|
||||
>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<div class="room-name no-upper">{{ $t("profile.title") }}</div>
|
||||
<v-btn
|
||||
text
|
||||
class="header-button-right"
|
||||
v-show="$navigation && $navigation.canPop()"
|
||||
@click.stop="$navigation.pop"
|
||||
>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-container>
|
||||
</div>
|
||||
|
||||
|
||||
<v-container class="user-info">
|
||||
<v-row>
|
||||
<v-col class="flex-grow-0 flex-shrink-0">
|
||||
<v-avatar class="avatar" size="48" color="#e0e0e0">
|
||||
<img v-if="userAvatar" :src="userAvatar" />
|
||||
<span v-else class="white--text headline">{{
|
||||
userAvatarLetter
|
||||
}}</span>
|
||||
<v-row>
|
||||
<v-col class="flex-grow-0 flex-shrink-0">
|
||||
<v-avatar
|
||||
class="avatar-48 clickable"
|
||||
size="48"
|
||||
color="#e0e0e0"
|
||||
@click="showAvatarPicker"
|
||||
>
|
||||
<img v-if="userAvatar" :src="userAvatar" />
|
||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||
<input
|
||||
ref="avatar"
|
||||
type="file"
|
||||
name="avatar"
|
||||
@change="handlePickedAvatar($event)"
|
||||
accept="image/*"
|
||||
style="display: none"
|
||||
/>
|
||||
</v-avatar>
|
||||
</v-col>
|
||||
<v-col class="flex-shrink-1 flex-grow-1">
|
||||
<div class="h1">{{ displayName }}</div>
|
||||
<div class="text-center">{{ $matrix.currentUser.user_id }}</div>
|
||||
<div v-if="$matrix.currentUser.is_guest">{{$t('profile.temporary_identity')}}</div>
|
||||
<v-btn depressed block class="outlined-button" @click.stop="logout">{{$t('menu.logout')}}</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-col>
|
||||
<v-col class="flex-shrink-1 flex-grow-1">
|
||||
<div class="h1">{{ displayName }}</div>
|
||||
<div class="text-center">{{ $matrix.currentUser.user_id }}</div>
|
||||
<!-- <div v-if="$matrix.currentUser.is_guest">
|
||||
{{ $t("profile.temporary_identity") }}
|
||||
</div> -->
|
||||
<v-btn depressed block class="outlined-button" @click.stop="logout">{{
|
||||
$t("menu.logout")
|
||||
}}</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-container class="mt-2 pa-5">
|
||||
<ActionRow @click="showEditPasswordDialog = true" :icon="'lock'" :text="$t('profile.set_password')" />
|
||||
<ActionRow @click="editValue = displayName;showEditDisplaynameDialog = true" :icon="'edit'" :text="$t('profile.change_name')" />
|
||||
<ActionRow
|
||||
@click="showEditPasswordDialog = true"
|
||||
:icon="'$vuetify.icons.password'"
|
||||
:text="$t('profile.set_password')"
|
||||
/>
|
||||
<ActionRow
|
||||
@click="
|
||||
editValue = displayName;
|
||||
showEditDisplaynameDialog = true;
|
||||
"
|
||||
:icon="'$vuetify.icons.edit'"
|
||||
:text="$t('profile.change_name')"
|
||||
/>
|
||||
<ActionRow
|
||||
@click="showSelectLanguageDialog = true"
|
||||
:icon="'$vuetify.icons.globe'"
|
||||
:text="$t('profile.select_language')"
|
||||
/>
|
||||
</v-container>
|
||||
|
||||
<!-- edit password dialog -->
|
||||
<v-dialog v-model="showEditPasswordDialog" class="ma-0 pa-0" width="50%">
|
||||
<v-card :disabled="settingPassword">
|
||||
<v-card-title>{{$t('profile.change_password')}}</v-card-title>
|
||||
<v-card-title>{{ $t("profile.change_password") }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-text-field v-if="!$matrix.currentUser.is_guest" v-model="password" :label="$t('profile.password_old')" type="password" />
|
||||
<v-text-field v-model="newPassword1" :label="$t('profile.password_new')" type="password" />
|
||||
<v-text-field v-model="newPassword2" :label="$t('profile.password_repeat')" type="password" />
|
||||
<div class="red--text" v-if="passwordErrorMessage">{{ passwordErrorMessage }}</div>
|
||||
<v-text-field
|
||||
v-if="!$matrix.currentUser.is_guest"
|
||||
v-model="password"
|
||||
:label="$t('profile.password_old')"
|
||||
type="password"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="newPassword1"
|
||||
:label="$t('profile.password_new')"
|
||||
type="password"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="newPassword2"
|
||||
:label="$t('profile.password_repeat')"
|
||||
type="password"
|
||||
/>
|
||||
<div class="red--text" v-if="passwordErrorMessage">
|
||||
{{ passwordErrorMessage }}
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn text @click="closeEditPasswordDialog">{{$t('menu.cancel')}}</v-btn>
|
||||
<v-btn text @click="closeEditPasswordDialog">{{
|
||||
$t("menu.cancel")
|
||||
}}</v-btn>
|
||||
<v-btn
|
||||
:disabled="!passwordsMatch"
|
||||
color="primary"
|
||||
text
|
||||
@click="
|
||||
setPassword($matrix.currentUser.is_guest ? $matrix.currentUser.password : password, newPassword1);
|
||||
setPassword(
|
||||
$matrix.currentUser.is_guest
|
||||
? $matrix.currentUser.password
|
||||
: password,
|
||||
newPassword1
|
||||
)
|
||||
"
|
||||
>{{$t('menu.ok')}}</v-btn
|
||||
>{{ $t("menu.ok") }}</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
|
@ -70,14 +121,16 @@
|
|||
<!-- edit display name dialog -->
|
||||
<v-dialog v-model="showEditDisplaynameDialog" class="ma-0 pa-0" width="50%">
|
||||
<v-card>
|
||||
<v-card-title>{{$t('profile.display_name')}}</v-card-title>
|
||||
<v-card-title>{{ $t("profile.display_name") }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-text-field v-model="editValue" />
|
||||
</v-card-text>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn text @click="showEditDisplaynameDialog = false">{{$t('menu.cancel')}}</v-btn>
|
||||
<v-btn text @click="showEditDisplaynameDialog = false">{{
|
||||
$t("menu.cancel")
|
||||
}}</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
text
|
||||
|
|
@ -85,32 +138,47 @@
|
|||
setDisplayName(editValue);
|
||||
showEditDisplaynameDialog = false;
|
||||
"
|
||||
>{{$t('menu.ok')}}</v-btn
|
||||
>{{ $t("menu.ok") }}</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<SelectLanguageDialog
|
||||
v-model="showSelectLanguageDialog"
|
||||
v-on:close="showSelectLanguageDialog = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SelectLanguageDialog from "./SelectLanguageDialog.vue";
|
||||
import dataUriToBuffer from "data-uri-to-buffer";
|
||||
import ActionRow from "./ActionRow.vue";
|
||||
import ImageResize from "image-resize";
|
||||
const sizeOf = require("image-size");
|
||||
//const dataUriToBuffer = require("data-uri-to-buffer");
|
||||
import util from "../plugins/utils";
|
||||
import profileInfoMixin from "./profileInfoMixin";
|
||||
|
||||
export default {
|
||||
name: "Profile",
|
||||
mixins: [profileInfoMixin],
|
||||
components: {
|
||||
ActionRow
|
||||
ActionRow,
|
||||
SelectLanguageDialog,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showEditPasswordDialog: false,
|
||||
showEditDisplaynameDialog: false,
|
||||
showSelectLanguageDialog: false,
|
||||
editValue: null,
|
||||
password: null,
|
||||
newPassword1: null,
|
||||
newPassword2: null,
|
||||
settingPassword: false,
|
||||
passwordErrorMessage: null
|
||||
passwordErrorMessage: null,
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -126,26 +194,38 @@ export default {
|
|||
if (!this.user) {
|
||||
return null;
|
||||
}
|
||||
return (this.user.displayName || this.user.userId);
|
||||
return this.user.displayName || this.user.userId;
|
||||
},
|
||||
|
||||
userAvatar() {
|
||||
if (!this.user || !this.user.avatarUrl) {
|
||||
return null;
|
||||
}
|
||||
return this.$matrix.matrixClient.mxcUrlToHttp(this.user.avatarUrl, 80, 80, 'scale', true);
|
||||
return this.$matrix.matrixClient.mxcUrlToHttp(
|
||||
this.user.avatarUrl,
|
||||
80,
|
||||
80,
|
||||
"scale",
|
||||
true
|
||||
);
|
||||
},
|
||||
|
||||
userAvatarLetter() {
|
||||
if (!this.user) {
|
||||
return null;
|
||||
}
|
||||
return (this.user.displayName || this.user.userId.substring(1)).substring(0, 1).toUpperCase();
|
||||
return (this.user.displayName || this.user.userId.substring(1))
|
||||
.substring(0, 1)
|
||||
.toUpperCase();
|
||||
},
|
||||
|
||||
passwordsMatch() {
|
||||
return this.newPassword1 && this.newPassword2 && this.newPassword1 == this.newPassword2;
|
||||
}
|
||||
return (
|
||||
this.newPassword1 &&
|
||||
this.newPassword2 &&
|
||||
this.newPassword1 == this.newPassword2
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
@ -156,17 +236,20 @@ export default {
|
|||
setPassword(oldPassword, newPassword) {
|
||||
this.settingPassword = true;
|
||||
this.passwordErrorMessage = null;
|
||||
this.$matrix.setPassword(oldPassword, newPassword)
|
||||
.then(success => {
|
||||
console.log(success ? "Password changed" : "Failed to change password");
|
||||
this.closeEditPasswordDialog();
|
||||
})
|
||||
.catch(error => {
|
||||
this.passwordErrorMessage = error.message;
|
||||
})
|
||||
.finally(() => {
|
||||
this.settingPassword = false;
|
||||
});
|
||||
this.$matrix
|
||||
.setPassword(oldPassword, newPassword)
|
||||
.then((success) => {
|
||||
console.log(
|
||||
success ? "Password changed" : "Failed to change password"
|
||||
);
|
||||
this.closeEditPasswordDialog();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.passwordErrorMessage = error.message;
|
||||
})
|
||||
.finally(() => {
|
||||
this.settingPassword = false;
|
||||
});
|
||||
},
|
||||
|
||||
closeEditPasswordDialog() {
|
||||
|
|
@ -175,7 +258,72 @@ export default {
|
|||
this.newPassword1 = null;
|
||||
this.newPassword2 = null;
|
||||
this.showEditPasswordDialog = false;
|
||||
}
|
||||
},
|
||||
|
||||
showAvatarPicker() {
|
||||
this.$refs.avatar.click();
|
||||
},
|
||||
|
||||
handlePickedAvatar(event) {
|
||||
const self = this;
|
||||
if (event.target.files && event.target.files[0]) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const file = event.target.files[0];
|
||||
if (file.type.startsWith("image/")) {
|
||||
try {
|
||||
var image = e.target.result;
|
||||
|
||||
var dimens = sizeOf(dataUriToBuffer(e.target.result));
|
||||
|
||||
// Need to resize?
|
||||
const w = dimens.width;
|
||||
const h = dimens.height;
|
||||
if (w > 640 || h > 640) {
|
||||
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({
|
||||
format: "png",
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
outputType: "blob",
|
||||
});
|
||||
imageResize
|
||||
.play(event.target)
|
||||
.then((img) => {
|
||||
var resizedImageFile = new File([img], file.name, {
|
||||
type: img.type,
|
||||
lastModified: Date.now(),
|
||||
});
|
||||
var reader2 = new FileReader();
|
||||
reader2.onload = (e) => {
|
||||
self.setAvatar(e.target.result);
|
||||
};
|
||||
reader2.readAsDataURL(resizedImageFile);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Resize failed:", err);
|
||||
});
|
||||
} else {
|
||||
self.setAvatar(image);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to get image dimensions: " + error);
|
||||
}
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(event.target.files[0]);
|
||||
}
|
||||
},
|
||||
|
||||
setAvatar(image) {
|
||||
return util.setAvatar(this.$matrix, image, function (progress) {
|
||||
console.log("Progress: " + JSON.stringify(progress));
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -41,20 +41,36 @@
|
|||
</v-container>
|
||||
|
||||
<v-container class="mt-4 pa-0">
|
||||
<ActionRow @click="viewProfile" :icon="'account_circle'" :text="$t('profile_info_popup.edit_profile')" />
|
||||
<ActionRow @click="logout" :icon="'logout'" :text="$t('profile_info_popup.logout')" />
|
||||
<ActionRow
|
||||
@click="viewProfile"
|
||||
:icon="'account_circle'"
|
||||
:text="$t('profile_info_popup.edit_profile')"
|
||||
/>
|
||||
<ActionRow
|
||||
@click="logout"
|
||||
:icon="'logout'"
|
||||
:text="$t('profile_info_popup.logout')"
|
||||
/>
|
||||
</v-container>
|
||||
|
||||
<div class="more-container">
|
||||
<div class="want_more">🙌 {{$t('profile_info_popup.want_more')}}</div>
|
||||
<div class="want_more">
|
||||
🙌 {{ $t("profile_info_popup.want_more") }}
|
||||
</div>
|
||||
<i18n path="profile_info_popup.powered_by" tag="div">
|
||||
<template v-slot:product>{{ product }}</template>
|
||||
<template v-slot:productLink>
|
||||
<a :href="productLink">{{ productLink }}</a>
|
||||
</template>
|
||||
</i18n>
|
||||
<div style="position:relative;width:100%;height: 40px">
|
||||
<v-btn class="new_room" right absolute text @click="createRoom">{{ $t('profile_info_popup.new_room') }}</v-btn></div>
|
||||
<div
|
||||
style="position: relative; width: 100%; height: 40px"
|
||||
class="text-end"
|
||||
>
|
||||
<v-btn class="new_room" text @click="createRoom">{{
|
||||
$t("profile_info_popup.new_room")
|
||||
}}</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
|
@ -63,13 +79,12 @@
|
|||
<script>
|
||||
import profileInfoMixin from "./profileInfoMixin";
|
||||
import ActionRow from "./ActionRow.vue";
|
||||
import config from "../assets/config";
|
||||
|
||||
export default {
|
||||
name: "ProfileInfoPopup",
|
||||
mixins: [profileInfoMixin],
|
||||
components: {
|
||||
ActionRow
|
||||
ActionRow,
|
||||
},
|
||||
props: {
|
||||
show: {
|
||||
|
|
@ -86,11 +101,11 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
product() {
|
||||
return config.product;
|
||||
return this.$config.appName;
|
||||
},
|
||||
productLink() {
|
||||
return config.productLink;
|
||||
}
|
||||
return this.$config.productLink;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
show: {
|
||||
|
|
@ -113,7 +128,7 @@ export default {
|
|||
createRoom() {
|
||||
this.showDialog = false;
|
||||
this.$navigation.push({ name: "CreateRoom" });
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -121,42 +136,50 @@ export default {
|
|||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
.profile-info-popup {
|
||||
font-family: "Inter", sans-serif !important;
|
||||
font-size: 16px;
|
||||
font-family: "Inter", sans-serif !important;
|
||||
font-size: 16px;
|
||||
position: fixed;
|
||||
margin: 0px;
|
||||
top: 70px;
|
||||
right: 10px;
|
||||
[dir="rtl"] & {
|
||||
right: inherit;
|
||||
left: 10px;
|
||||
}
|
||||
border-radius: 40px;
|
||||
&::before {
|
||||
content: "▲";
|
||||
position: fixed;
|
||||
margin: 0px;
|
||||
top: 70px;
|
||||
right: 10px;
|
||||
border-radius: 40px;
|
||||
&::before {
|
||||
content: '▲';
|
||||
position: fixed;
|
||||
top: 57px;
|
||||
right: 22px;
|
||||
color: white;
|
||||
top: 57px;
|
||||
right: 22px;
|
||||
[dir="rtl"] & {
|
||||
left: 22px;
|
||||
right: inherit;
|
||||
}
|
||||
.you-are {
|
||||
padding-top: 20px;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
}
|
||||
.you-are {
|
||||
padding-top: 20px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.username {
|
||||
border-radius: 4px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.more-container {
|
||||
border-radius: 10px;
|
||||
background-color: #f5f5f5;
|
||||
padding: 20px;
|
||||
.want_more {
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 13 * $chat-text-size;
|
||||
}
|
||||
.username {
|
||||
border-radius: 4px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.more-container {
|
||||
border-radius: 10px;
|
||||
background-color: #f5f5f5;
|
||||
padding: 20px;
|
||||
.want_more {
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 13 * $chat-text-size;
|
||||
}
|
||||
.new_room .v-btn__content {
|
||||
font-family: "Poppins", sans-serif !important;
|
||||
font-weight: 700 !important;
|
||||
font-size: 13 * $chat-text-size !important;
|
||||
}
|
||||
.new_room .v-btn__content {
|
||||
font-family: "Poppins", sans-serif !important;
|
||||
font-weight: 700 !important;
|
||||
font-size: 13 * $chat-text-size !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
<template>
|
||||
<v-dialog v-model="showDialog" v-show="room" class="ma-0 pa-0" width="80%">
|
||||
<div class="dialog-content text-center">
|
||||
<div v-if="timeout == -1" class="dialog-content text-center">
|
||||
<template>
|
||||
<v-img contain height="28" src="@/assets/icons/trash_black.svg" />
|
||||
<h2 class="dialog-title">{{ $t("purge_room.title") }}</h2>
|
||||
<div class="dialog-text">
|
||||
{{ $t("purge_room.info") }}
|
||||
|
|
@ -37,6 +38,48 @@
|
|||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
|
||||
<!-- Timer -->
|
||||
<div v-if="timeout >= 0 && !isPurging" class="dialog-content text-center">
|
||||
<template>
|
||||
<v-img
|
||||
contain
|
||||
width="20"
|
||||
class="d-inline-block me-2"
|
||||
src="@/assets/icons/timer.svg"
|
||||
/>{{ $t("purge_room.n_seconds", { seconds: timeout }) }}
|
||||
<h2 class="dialog-title">{{ $t("purge_room.self_destruct") }}</h2>
|
||||
<div class="dialog-text">
|
||||
{{ $t("purge_room.notified") }}
|
||||
</div>
|
||||
<div class="dialog-text">
|
||||
{{ status }}
|
||||
</div>
|
||||
</template>
|
||||
<v-container fluid>
|
||||
<v-row cols="12">
|
||||
<v-col cols="12">
|
||||
<v-btn
|
||||
depressed
|
||||
text
|
||||
block
|
||||
class="text-button"
|
||||
:disabled="isPurging"
|
||||
@click="undo"
|
||||
>{{ $t("menu.undo") }}</v-btn
|
||||
>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
|
||||
<!-- Purging -->
|
||||
<div v-if="isPurging" class="dialog-content text-center">
|
||||
<h2 class="dialog-title">{{ $t("purge_room.deleting") }}</h2>
|
||||
<div class="dialog-text">
|
||||
{{ status }}
|
||||
</div>
|
||||
</div>
|
||||
</v-dialog>
|
||||
</template>
|
||||
<script>
|
||||
|
|
@ -55,6 +98,8 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
timeout: -1,
|
||||
timeoutTimer: null,
|
||||
showDialog: false,
|
||||
isPurging: false,
|
||||
status: null,
|
||||
|
|
@ -74,7 +119,41 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
undo() {
|
||||
if (this.timeoutTimer) {
|
||||
clearInterval(this.timeoutTimer);
|
||||
this.timeoutTimer = null;
|
||||
}
|
||||
this.timeout = -1;
|
||||
|
||||
// Cancel the state event for deletion
|
||||
this.$matrix.matrixClient.sendStateEvent(
|
||||
this.room.roomId,
|
||||
"im.keanu.room_deletion_notice",
|
||||
{ status: "cancel" }
|
||||
);
|
||||
|
||||
this.showDialog = false;
|
||||
},
|
||||
onPurgeRoom() {
|
||||
// Send custom state event!
|
||||
this.$matrix.matrixClient.sendStateEvent(
|
||||
this.room.roomId,
|
||||
"im.keanu.room_deletion_notice",
|
||||
{ status: "delete" }
|
||||
);
|
||||
|
||||
this.timeout = 30;
|
||||
this.timeoutTimer = setInterval(() => {
|
||||
this.timeout = this.timeout - 1;
|
||||
if (this.timeout == 0) {
|
||||
clearInterval(this.timeoutTimer);
|
||||
this.timeoutTimer = null;
|
||||
this.onDoPurgeRoom();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
onDoPurgeRoom() {
|
||||
this.isPurging = true;
|
||||
this.$matrix
|
||||
.purgeRoom(this.room.roomId, this.onPurgeStatus)
|
||||
|
|
@ -101,4 +180,4 @@ export default {
|
|||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
100
src/components/QRCodePopup.vue
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<template>
|
||||
<v-dialog v-model="showDialog" class="ma-0 pa-0" width="80%">
|
||||
<div class="dialog-content text-center" ref="qrContainer">
|
||||
<div class="d-flex flex-column text-center" style="align-items: center">
|
||||
<canvas ref="qr" class="qr" id="qr" :style="qrStyle"></canvas>
|
||||
</div>
|
||||
<div>{{ $t("room_info.scan_code") }}</div>
|
||||
</div>
|
||||
</v-dialog>
|
||||
</template>
|
||||
<script>
|
||||
import roomInfoMixin from "./roomInfoMixin";
|
||||
import QRCode from "qrcode";
|
||||
|
||||
export default {
|
||||
name: "QRCodePopup",
|
||||
mixins: [roomInfoMixin],
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: function () {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
message: {
|
||||
type: String,
|
||||
default: function () {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showDialog: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.updateQR(this.message);
|
||||
},
|
||||
computed: {
|
||||
qrStyle() {
|
||||
const w = document.documentElement.clientWidth;
|
||||
const h = document.documentElement.clientHeight;
|
||||
const s = 0.6 * Math.min(w, h);
|
||||
return "width: " + s + "px;height:" + s + "px;";
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
show: {
|
||||
handler(newVal, ignoredOldVal) {
|
||||
this.showDialog = newVal;
|
||||
},
|
||||
},
|
||||
showDialog() {
|
||||
if (!this.showDialog) {
|
||||
this.$emit("close");
|
||||
} else {
|
||||
this.$nextTick(() => {
|
||||
this.updateQR(this.message);
|
||||
});
|
||||
}
|
||||
},
|
||||
message: {
|
||||
handler(message) {
|
||||
this.updateQR(message);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateQR(message) {
|
||||
var canvas = this.$refs.qr;
|
||||
var canvasContainer = this.$refs.qrContainer;
|
||||
if (message && canvas && canvasContainer) {
|
||||
this.$nextTick(() => {
|
||||
QRCode.toCanvas(
|
||||
canvas,
|
||||
message,
|
||||
{
|
||||
type: "image/png",
|
||||
margin: 1,
|
||||
width: Math.min(
|
||||
0.7 * canvasContainer.clientWidth,
|
||||
0.7 * canvasContainer.clientHeight
|
||||
),
|
||||
},
|
||||
function (error) {
|
||||
if (error) console.error(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
</style>
|
||||
|
|
@ -1,49 +1,130 @@
|
|||
<template>
|
||||
<transition name="slow-fade">
|
||||
<div
|
||||
v-if="mounted"
|
||||
style="
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background-color: black;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px;
|
||||
"
|
||||
class="text-center d-flex flex-column"
|
||||
>
|
||||
|
||||
<div class="quote white--text">{{ quote }}</div>
|
||||
<div class="author white--text mt-4">- {{ author }}</div>
|
||||
|
||||
<v-btn color="white" text class="close" @click.stop="closeBrowserTab">Close your browser tab</v-btn>
|
||||
</div>
|
||||
</transition>
|
||||
<div>
|
||||
<transition name="slow-fade">
|
||||
<div
|
||||
v-if="mounted"
|
||||
style="
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background-color: black;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px;
|
||||
"
|
||||
class="text-center d-flex flex-column"
|
||||
>
|
||||
<div v-if="roomWasPurged" style="width: 28px">
|
||||
<v-img src="@/assets/icons/trash.svg" />
|
||||
</div>
|
||||
<h2 v-if="roomWasPurged" class="white--text mt-2 mb-8">
|
||||
{{ $t("goodbye.room_deleted") }}
|
||||
</h2>
|
||||
<div class="quote white--text">{{ quote }}</div>
|
||||
<div class="author white--text mt-4">- {{ author }}</div>
|
||||
|
||||
<v-btn
|
||||
v-if="joinedToAnyRoom"
|
||||
color="white"
|
||||
text
|
||||
class="close"
|
||||
@click.stop="viewOtherRooms"
|
||||
>{{ $t("goodbye.view_other_rooms") }}</v-btn
|
||||
>
|
||||
<v-btn
|
||||
v-else
|
||||
color="white"
|
||||
text
|
||||
class="close"
|
||||
@click.stop="closeBrowserTab"
|
||||
>{{ $t("goodbye.close_tab") }}</v-btn
|
||||
>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- PROFILE INFO IN TOP RIGHT -->
|
||||
<transition name="slow-fade">
|
||||
<div
|
||||
v-if="mounted"
|
||||
style="
|
||||
position: fixed;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
z-index: 101;
|
||||
padding: 10px 20px;
|
||||
height: 50px;
|
||||
border-radius: 25px;
|
||||
background-color: #242424;
|
||||
"
|
||||
>
|
||||
<div class="d-inline-block me-2 white--text">
|
||||
{{ $t("profile_info_popup.you_are") }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="$matrix.currentUser.is_guest"
|
||||
class="d-inline-block me-2 white--text"
|
||||
>
|
||||
<i18n path="profile_info_popup.identity_temporary" tag="span">
|
||||
<template v-slot:displayName>
|
||||
<b>{{ displayName }}</b>
|
||||
</template>
|
||||
</i18n>
|
||||
</div>
|
||||
<div v-else class="d-inline-block me-2 white--text">
|
||||
<i18n path="profile_info_popup.identity" tag="span">
|
||||
<template v-slot:displayName>
|
||||
<b>{{ displayName }}</b>
|
||||
</template>
|
||||
</i18n>
|
||||
</div>
|
||||
<v-avatar
|
||||
class="avatar-32 d-inline-block"
|
||||
size="32"
|
||||
color="#e0e0e0"
|
||||
@click.stop="showProfileInfo = true"
|
||||
>
|
||||
<img v-if="userAvatar" :src="userAvatar" />
|
||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||
</v-avatar>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import profileInfoMixin from "./profileInfoMixin";
|
||||
|
||||
export default {
|
||||
name: "QuoteView",
|
||||
mixins: [profileInfoMixin],
|
||||
props: {
|
||||
roomWasPurged: {
|
||||
type: Boolean,
|
||||
default: function () {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mounted: false,
|
||||
quote: "",
|
||||
author: "",
|
||||
}
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
var quotes;
|
||||
try {
|
||||
quotes = require('@/assets/quotes/' + this.$i18n.locale + '/quotes');
|
||||
} catch (error) {
|
||||
console.error("No quotes for language");
|
||||
quotes = undefined;
|
||||
quotes = require("@/assets/quotes/" + this.$i18n.locale + "/quotes");
|
||||
} catch (error) {
|
||||
console.error("No quotes for language");
|
||||
quotes = undefined;
|
||||
}
|
||||
if (!quotes) {
|
||||
quotes = require('@/assets/quotes/en/quotes'); // Default fallback
|
||||
quotes = require("@/assets/quotes/en/quotes"); // Default fallback
|
||||
}
|
||||
const n = quotes.quotes.length;
|
||||
const quote = quotes.quotes[Math.floor(Math.random() * n)];
|
||||
|
|
@ -52,12 +133,24 @@ export default {
|
|||
this.mounted = true;
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Return true if we are still joined to any rooms
|
||||
*/
|
||||
joinedToAnyRoom() {
|
||||
const joinedRooms = this.$matrix.joinedRooms;
|
||||
return joinedRooms.length > 0;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
closeBrowserTab() {
|
||||
window.location.href="about:blank";
|
||||
}
|
||||
}
|
||||
|
||||
window.location.href = "about:blank";
|
||||
},
|
||||
viewOtherRooms() {
|
||||
this.$navigation.push({ name: "Home" }, -1);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -80,5 +173,4 @@ export default {
|
|||
.slow-fade-enter, .slow-fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
<v-btn
|
||||
color="black"
|
||||
depressed
|
||||
class="header-button-right filled-button mr-3"
|
||||
class="header-button-right filled-button me-3"
|
||||
@click.stop="showLeaveConfirmation = true"
|
||||
>👋 {{ $t("room_info.leave_room") }}</v-btn
|
||||
>
|
||||
|
|
@ -40,7 +40,12 @@
|
|||
<v-container fluid class="pa-0" v-show="publicRoomLink">
|
||||
<v-row cols="12" class="qr-container ma-3">
|
||||
<v-col cols="auto">
|
||||
<canvas ref="roomQr" class="qr" id="room-qr"></canvas>
|
||||
<canvas
|
||||
@click.stop="showFullScreenQR = true"
|
||||
ref="roomQr"
|
||||
class="qr"
|
||||
id="room-qr"
|
||||
></canvas>
|
||||
</v-col>
|
||||
<v-col align-self="center">
|
||||
<div class="link">{{ publicRoomLink }}</div>
|
||||
|
|
@ -83,7 +88,7 @@
|
|||
item-value="id"
|
||||
>
|
||||
<template v-slot:selection="{ item }">
|
||||
<v-icon color="black" class="mr-2">{{ item.icon }}</v-icon>
|
||||
<v-icon color="black" class="me-2">{{ item.icon }}</v-icon>
|
||||
{{ item.text }}
|
||||
</template>
|
||||
<template v-slot:item="{ item, attrs, on }">
|
||||
|
|
@ -185,6 +190,9 @@
|
|||
:room="room"
|
||||
@close="showPurgeConfirmation = false"
|
||||
/>
|
||||
|
||||
<QRCodePopup :show="showFullScreenQR" :message="publicRoomLink" @close="showFullScreenQR = false" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -194,6 +202,7 @@ import PurgeRoomDialog from "../components/PurgeRoomDialog";
|
|||
import DeviceList from "../components/DeviceList";
|
||||
import QRCode from "qrcode";
|
||||
import roomInfoMixin from "./roomInfoMixin";
|
||||
import QRCodePopup from './QRCodePopup.vue';
|
||||
|
||||
export default {
|
||||
name: "RoomInfo",
|
||||
|
|
@ -202,6 +211,7 @@ export default {
|
|||
LeaveRoomDialog,
|
||||
PurgeRoomDialog,
|
||||
DeviceList,
|
||||
QRCodePopup,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -211,6 +221,7 @@ export default {
|
|||
showAllMembers: false,
|
||||
showLeaveConfirmation: false,
|
||||
showPurgeConfirmation: false,
|
||||
showFullScreenQR: false,
|
||||
expandedMembers: [],
|
||||
buildVersion: "",
|
||||
updatingJoinRule: false, // Flag if we are processing update curerntly
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
>{{$t('room_info_sheet.view_details')}}</v-btn
|
||||
>
|
||||
</div>
|
||||
<room-list :title="'Other groups'" v-on:close="close" v-on:newroom="createRoom" :showCreate="true" />
|
||||
<room-list :title="'Other rooms'" v-on:close="close" v-on:newroom="createRoom" :showCreate="true" />
|
||||
</div>
|
||||
</BottomSheet>
|
||||
</template>
|
||||
|
|
@ -63,4 +63,4 @@ export default {
|
|||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,41 +1,61 @@
|
|||
<template>
|
||||
<v-list dense class="room-list">
|
||||
<div v-if="showInvites && $matrix.invites.length > 0" class="h4">{{invitesTitle}}</div>
|
||||
<v-list-item-group v-if="showInvites" v-model="currentRoomId" color="primary">
|
||||
<v-slide-y-transition group>
|
||||
<v-list-item :disabled="roomsProcessing[room.roomId]" v-for="room in $matrix.invites" :key="room.roomId" :value="room.roomId">
|
||||
<v-list-item-avatar size="40" color="#e0e0e0">
|
||||
<v-img :src="room.avatar" />
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ room.name || room.summary.info.title }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ room.topic }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<v-btn @click.stop="acceptInvitation(room)" icon><v-icon>check_circle</v-icon></v-btn>
|
||||
<v-btn @click.stop="rejectInvitation(room)" icon><v-icon>cancel</v-icon></v-btn>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</v-slide-y-transition>
|
||||
</v-list-item-group>
|
||||
<div class="h4">{{title}}</div>
|
||||
<div class="h4">{{ title }}</div>
|
||||
<v-list-item-group v-model="currentRoomId" color="primary">
|
||||
<v-list-item v-if="showCreate" @click.stop="$emit('newroom')">
|
||||
<v-list-item-avatar size="40" color="#e0e0e0">
|
||||
<v-img />
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{$t('menu.new_room')}}</v-list-item-title>
|
||||
<v-list-item-title class="new-room">{{
|
||||
$t("menu.new_room")
|
||||
}}</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item v-for="room in $matrix.joinedRooms" :key="room.roomId" :value="room.roomId">
|
||||
<!-- invites -->
|
||||
<v-list-item
|
||||
:disabled="roomsProcessing[room.roomId]"
|
||||
v-for="room in invitedRooms"
|
||||
:key="room.roomId"
|
||||
:value="room.roomId"
|
||||
>
|
||||
<v-list-item-avatar size="40" color="#e0e0e0">
|
||||
<v-img :src="room.avatar" />
|
||||
</v-list-item-avatar>
|
||||
<div class="room-list-notification-count">{{ notificationCount(room) }}</div>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ room.summary.info.title }}</v-list-item-title>
|
||||
<v-list-item-title>{{ room.name }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ room.topic }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<v-btn
|
||||
class="filled-button"
|
||||
depressed
|
||||
color="black"
|
||||
@click.stop="acceptInvitation(room)"
|
||||
>{{ $t("menu.join") }}</v-btn
|
||||
>
|
||||
<v-btn
|
||||
class="filled-button"
|
||||
color="black"
|
||||
@click.stop="rejectInvitation(room)"
|
||||
text
|
||||
>{{ $t("menu.ignore") }}</v-btn
|
||||
>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item
|
||||
v-for="room in joinedRooms"
|
||||
:key="room.roomId"
|
||||
:value="room.roomId"
|
||||
style="position: relative"
|
||||
>
|
||||
<v-list-item-avatar size="40" color="#e0e0e0">
|
||||
<v-img :src="room.avatar" />
|
||||
</v-list-item-avatar>
|
||||
<div class="room-list-notification-count">
|
||||
{{ notificationCount(room) }}
|
||||
</div>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ room.name }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ room.topic }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
|
@ -45,7 +65,7 @@
|
|||
|
||||
<script>
|
||||
import util from "../plugins/utils";
|
||||
import Vue from 'vue';
|
||||
import Vue from "vue";
|
||||
|
||||
export default {
|
||||
name: "RoomList",
|
||||
|
|
@ -53,20 +73,16 @@ export default {
|
|||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: "Rooms"
|
||||
},
|
||||
invitesTitle: {
|
||||
type: String,
|
||||
default: "Invites"
|
||||
default: "Rooms",
|
||||
},
|
||||
showInvites: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
showCreate: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
|
|
@ -75,20 +91,50 @@ export default {
|
|||
roomsProcessing: {},
|
||||
}),
|
||||
|
||||
computed: {
|
||||
invitedRooms() {
|
||||
return this.sortItemsOnName(this.$matrix.invites);
|
||||
},
|
||||
joinedRooms() {
|
||||
return this.sortItemsOnName(this.$matrix.joinedRooms);
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
sortItemsOnName(items) {
|
||||
if (items == null) {
|
||||
return [];
|
||||
}
|
||||
return items.sort(function (a, b) {
|
||||
const titleA = a.name || a.summary.info.title;
|
||||
const titleB = b.name || b.summary.info.title;
|
||||
if (titleA == null) {
|
||||
return 1;
|
||||
} else if (titleB == null) {
|
||||
return -1;
|
||||
}
|
||||
return titleA.localeCompare(titleB);
|
||||
});
|
||||
},
|
||||
|
||||
notificationCount(room) {
|
||||
return room.getUnreadNotificationCount('total') || 0;
|
||||
return room.getUnreadNotificationCount("total") || 0;
|
||||
},
|
||||
|
||||
acceptInvitation(room) {
|
||||
Vue.set(this.roomsProcessing, room.roomId, true);
|
||||
this.$matrix.matrixClient.joinRoom(room.roomId)
|
||||
this.$matrix.matrixClient
|
||||
.joinRoom(room.roomId)
|
||||
.then((ignoredRoom) => {
|
||||
this.$nextTick(() => {
|
||||
this.$navigation.push(
|
||||
{
|
||||
name: "Chat",
|
||||
params: { roomId: util.sanitizeRoomId(room.getCanonicalAlias() || room.roomId) },
|
||||
params: {
|
||||
roomId: util.sanitizeRoomId(
|
||||
room.getCanonicalAlias() || room.roomId
|
||||
),
|
||||
},
|
||||
},
|
||||
-1
|
||||
);
|
||||
|
|
@ -104,7 +150,8 @@ export default {
|
|||
|
||||
rejectInvitation(room) {
|
||||
Vue.set(this.roomsProcessing, room.roomId, true);
|
||||
this.$matrix.leaveRoom(room.roomId)
|
||||
this.$matrix
|
||||
.leaveRoom(room.roomId)
|
||||
.catch((err) => {
|
||||
console.error("Failed to reject invite: ", err);
|
||||
})
|
||||
|
|
@ -121,7 +168,13 @@ export default {
|
|||
return;
|
||||
}
|
||||
this.$emit("close");
|
||||
this.$navigation.push({name: 'Chat', params: { roomId: util.sanitizeRoomId(this.currentRoomId) }}, -1);
|
||||
this.$navigation.push(
|
||||
{
|
||||
name: "Chat",
|
||||
params: { roomId: util.sanitizeRoomId(this.currentRoomId) },
|
||||
},
|
||||
-1
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
58
src/components/SelectLanguageDialog.vue
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<v-dialog
|
||||
class="ma-0 pa-0"
|
||||
width="80%"
|
||||
v-bind="{ ...$props, ...$attrs }"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<v-card class="dialog-card">
|
||||
<v-card-title class="dialog-title"
|
||||
><h3>{{ $t("profile.select_language") }}</h3></v-card-title
|
||||
>
|
||||
<v-card-text>
|
||||
<v-select
|
||||
v-model="$i18n.locale"
|
||||
:items="languages"
|
||||
menu-props="auto"
|
||||
:label="$t('profile.select_language')"
|
||||
v-on:change="$store.commit('setLanguage', $i18n.locale)"
|
||||
hide-details
|
||||
prepend-icon="language"
|
||||
single-line
|
||||
></v-select>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn
|
||||
color="black"
|
||||
depressed
|
||||
block
|
||||
class="filled-button"
|
||||
@click="$emit('close')"
|
||||
>{{ $t("menu.ok") }}</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
languages: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
for (const locale of Object.keys(this.$i18n.messages)) {
|
||||
this.languages.push({
|
||||
text: this.$i18n.messages[locale].language_display_name || locale,
|
||||
value: locale,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
</style>
|
||||
|
|
@ -56,7 +56,9 @@
|
|||
</div>
|
||||
</v-col>
|
||||
<v-col cols="6" v-if="ptt">
|
||||
<div class="swipe-info"><< {{$t('voice_recorder.swipe_to_cancel')}}</div>
|
||||
<div class="swipe-info">
|
||||
<< {{ $t("voice_recorder.swipe_to_cancel") }}
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
|
@ -72,7 +74,9 @@
|
|||
<v-icon color="white">delete_outline</v-icon>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<div class="swipe-info">{{$t('voice_recorder.release_to_cancel')}}</div>
|
||||
<div class="swipe-info">
|
||||
{{ $t("voice_recorder.release_to_cancel") }}
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
|
@ -93,9 +97,9 @@
|
|||
</div>
|
||||
</v-col>
|
||||
<v-col cols="3">
|
||||
<v-btn @click.stop="cancelRecording" text class="swipe-info"
|
||||
>{{$t('menu.cancel')}}</v-btn
|
||||
>
|
||||
<v-btn @click.stop="cancelRecording" text class="swipe-info">{{
|
||||
$t("menu.cancel")
|
||||
}}</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="3">
|
||||
<v-btn @click.stop="stopRecording" icon class="swipe-info"
|
||||
|
|
@ -116,7 +120,7 @@
|
|||
<v-row align="center">
|
||||
<v-col>
|
||||
<div class="swipe-info">
|
||||
{{ errorMessage || $t('voice_recorder.failed_to_record') }}
|
||||
{{ errorMessage || $t("voice_recorder.failed_to_record") }}
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col align="right">
|
||||
|
|
@ -236,6 +240,7 @@ export default {
|
|||
this.startRecording();
|
||||
} else {
|
||||
console.log("Not PTT");
|
||||
//eslint-disable-next-line
|
||||
this.micButtonRef.$el.style.display = "none";
|
||||
}
|
||||
} else {
|
||||
|
|
@ -250,6 +255,7 @@ export default {
|
|||
this.startCoordinateX = null;
|
||||
this.startCoordinateY = null;
|
||||
this.recordingLocked = false;
|
||||
//eslint-disable-next-line
|
||||
this.micButtonRef.$el.style.display = "block";
|
||||
}
|
||||
},
|
||||
|
|
@ -341,7 +347,7 @@ export default {
|
|||
mimeType: "audio/webm",
|
||||
sampleRate: 16000,
|
||||
desiredSampRate: 16000,
|
||||
numberOfAudioChannels: 1
|
||||
numberOfAudioChannels: 1,
|
||||
});
|
||||
this.recorder.startRecording();
|
||||
this.state = State.RECORDING;
|
||||
|
|
|
|||
59
src/components/YouAre.vue
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<div class="d-flex flex-row-reverse">
|
||||
<v-chip
|
||||
@click="viewProfile"
|
||||
class="ma-2"
|
||||
:color="dark ? 'black' : '#ededed'"
|
||||
:text-color="dark ? 'white' : 'black'"
|
||||
:outlined="!dark"
|
||||
style="white-space: pre"
|
||||
>{{ $t("profile_info_popup.you_are") }}
|
||||
<span v-if="$matrix.currentUser.is_guest">
|
||||
<i18n path="profile_info_popup.identity_temporary" tag="span">
|
||||
<template v-slot:displayName>
|
||||
<b>{{ displayName }}</b>
|
||||
</template>
|
||||
</i18n>
|
||||
</span>
|
||||
<span v-else>
|
||||
<i18n path="profile_info_popup.identity" tag="span">
|
||||
<template v-slot:displayName>
|
||||
<b>{{ displayName }}</b>
|
||||
</template>
|
||||
</i18n>
|
||||
</span>
|
||||
<v-avatar color="#e0e0e0" right @click.stop="showProfileInfo = true">
|
||||
<img v-if="userAvatar" :src="userAvatar" />
|
||||
<span v-else class="white--text">{{ userAvatarLetter }}</span>
|
||||
</v-avatar>
|
||||
</v-chip>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import profileInfoMixin from "./profileInfoMixin";
|
||||
|
||||
export default {
|
||||
name: "YouAre",
|
||||
mixins: [profileInfoMixin],
|
||||
props: {
|
||||
dark: {
|
||||
type: Boolean,
|
||||
default: function () {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
viewProfile() {
|
||||
this.$navigation.push({ name: "Profile" }, 1);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
</style>
|
||||
|
|
@ -1,13 +1,18 @@
|
|||
<template>
|
||||
<!-- BASE CLASS FOR INCOMING MESSAGE -->
|
||||
<div :class="messageClasses">
|
||||
<div v-if="showSenderAndTime" class="senderAndTime">
|
||||
<div class="sender">{{ messageEventDisplayName(event) }}</div>
|
||||
<div class="time">
|
||||
{{ formatTime(event.event.origin_server_ts) }}
|
||||
</div>
|
||||
</div>
|
||||
<v-avatar class="avatar" ref="avatar" size="32" color="#ededed" @click.stop="otherAvatarClicked($refs.avatar.$el)">
|
||||
<img v-if="messageEventAvatar(event)" :src="messageEventAvatar(event)" />
|
||||
<span v-else class="white--text headline">{{
|
||||
messageEventDisplayName(event).substring(0, 1).toUpperCase()
|
||||
}}</span>
|
||||
</v-avatar>
|
||||
<QuickReactions :event="event" :reactions="reactions" />
|
||||
<!-- SLOT FOR CONTENT -->
|
||||
<slot></slot>
|
||||
<div class="op-button" ref="opbutton">
|
||||
|
|
@ -15,12 +20,7 @@
|
|||
><v-icon>more_vert</v-icon></v-btn
|
||||
>
|
||||
</div>
|
||||
<div v-if="showSenderAndTime" class="senderAndTime">
|
||||
<div class="sender">{{ messageEventDisplayName(event) }}</div>
|
||||
<div class="time">
|
||||
{{ formatTime(event.event.origin_server_ts) }}
|
||||
</div>
|
||||
</div>
|
||||
<QuickReactions :event="event" :reactions="reactions" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<template>
|
||||
<div :class="{'message-operations':true,'incoming':incoming,'outgoing':!incoming}">
|
||||
<template v-for="(item,index) in emojis">
|
||||
<v-btn v-if="index < maxRecents" :key="item.data" icon @click.stop="addQuickReaction(item.data)" class="ma-0 pa-0">
|
||||
<span class="recent-emoji" >{{ item.data }}</span>
|
||||
<v-btn v-if="index < maxRecents" :key="item.data" icon @click.stop="addQuickReaction(item.data)" class="ma-0 pa-0" dense>
|
||||
<span class="recent-emoji">{{ item.data }}</span>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-btn v-if="incoming" icon @click.stop="addReply" class="ma-0 pa-0">
|
||||
<v-btn v-if="incoming" icon @click.stop="addReply" class="ma-0 pa-0" large>
|
||||
<v-icon>reply</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click.stop="more" class="ma-0 pa-0">
|
||||
<v-btn icon @click.stop="more" class="ma-0 pa-0" large>
|
||||
<v-icon>more_horiz</v-icon>
|
||||
</v-btn> </div>
|
||||
</template>
|
||||
|
|
@ -46,9 +46,4 @@ export default {
|
|||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
|
||||
// .recent-emoji {
|
||||
// width: 30px;
|
||||
// }
|
||||
|
||||
</style>
|
||||
|
|
@ -1,12 +1,19 @@
|
|||
<template>
|
||||
<!-- BASE CLASS FOR OUTGOING MESSAGE -->
|
||||
<div class="messageOut">
|
||||
<div class="senderAndTime">
|
||||
<div class="time">
|
||||
{{ formatTime(event.event.origin_server_ts) }}
|
||||
</div>
|
||||
<div class="status">{{ event.status }}</div>
|
||||
</div>
|
||||
|
||||
<QuickReactions :event="event" :reactions="reactions" />
|
||||
<div class="op-button" ref="opbutton">
|
||||
<v-btn icon @click.stop="showContextMenu($refs.opbutton)"
|
||||
><v-icon>more_vert</v-icon></v-btn
|
||||
>
|
||||
</div>
|
||||
<QuickReactions :event="event" :reactions="reactions" />
|
||||
<!-- SLOT FOR CONTENT -->
|
||||
<slot></slot>
|
||||
<v-avatar
|
||||
|
|
@ -18,13 +25,6 @@
|
|||
<img v-if="userAvatar" :src="userAvatar" />
|
||||
<span v-else class="white--text headline">{{ userAvatarLetter }}</span>
|
||||
</v-avatar>
|
||||
<!-- <div class="sender">{{ $t('message.you') }}</div> -->
|
||||
<div class="senderAndTime">
|
||||
<div class="time">
|
||||
{{ formatTime(event.event.origin_server_ts) }}
|
||||
</div>
|
||||
<div class="status">{{ event.status }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
23
src/components/messages/RoomDeletionNotice.vue
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<!-- ROOM WILL BE DELETED -->
|
||||
<div class="notice">
|
||||
👋
|
||||
{{
|
||||
$t("purge_room.room_deletion_notice", {
|
||||
user: stateEventDisplayName(event),
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import messageMixin from "./messageMixin";
|
||||
|
||||
export default {
|
||||
mixins: [messageMixin],
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
</style>
|
||||
19
src/main.js
|
|
@ -2,9 +2,11 @@ import Vue from 'vue'
|
|||
import App from './App.vue'
|
||||
import vuetify from './plugins/vuetify';
|
||||
import store from './store'
|
||||
import i18n from './plugins/lang';
|
||||
import router from './router'
|
||||
import matrix from './services/matrix.service'
|
||||
import navigation from './services/navigation.service'
|
||||
import config from './services/config.service'
|
||||
import cleaninsights from './services/cleaninsights.service'
|
||||
import 'roboto-fontface/css/roboto/roboto-fontface.css'
|
||||
import 'material-design-icons-iconfont/dist/material-design-icons.css'
|
||||
|
|
@ -13,7 +15,6 @@ import VueResize from 'vue-resize';
|
|||
import 'vue-resize/dist/vue-resize.css';
|
||||
import VueClipboard from 'vue-clipboard2'
|
||||
import VueSanitize from "vue-sanitize";
|
||||
import i18n from './plugins/lang';
|
||||
|
||||
var defaultOptions = VueSanitize.defaults;
|
||||
defaultOptions.disallowedTagsMode = "recursiveEscape";
|
||||
|
|
@ -25,6 +26,7 @@ Vue.config.productionTip = false
|
|||
Vue.use(VueResize);
|
||||
Vue.use(VEmojiPicker);
|
||||
Vue.use(matrix, { store: store, i18n: i18n });
|
||||
Vue.use(config); // Use this before cleaninsights below, it depends on config!
|
||||
Vue.use(cleaninsights);
|
||||
Vue.use(VueClipboard);
|
||||
|
||||
|
|
@ -87,12 +89,12 @@ Vue.directive('longTap', {
|
|||
};
|
||||
|
||||
const touchStart = function (e) {
|
||||
el.longTapHandled = false;
|
||||
el.longTapStartX = touchX(e);
|
||||
el.longTapStartY = touchY(e);
|
||||
el.longTapTimer = setTimeout(touchTimerElapsed, el.longTapTimeout);
|
||||
el.classList.add("waiting-for-long-tap");
|
||||
e.preventDefault();
|
||||
el.longTapHandled = false;
|
||||
el.longTapStartX = touchX(e);
|
||||
el.longTapStartY = touchY(e);
|
||||
el.longTapTimer = setTimeout(touchTimerElapsed, el.longTapTimeout);
|
||||
el.classList.add("waiting-for-long-tap");
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const touchCancel = function () {
|
||||
|
|
@ -153,9 +155,10 @@ Vue.use(navigation, router);
|
|||
new Vue({
|
||||
vuetify,
|
||||
store,
|
||||
router,
|
||||
i18n,
|
||||
router,
|
||||
matrix,
|
||||
config,
|
||||
cleaninsights,
|
||||
render: h => h(App)
|
||||
}).$mount('#app');
|
||||
|
|
@ -6,7 +6,7 @@ export default class User {
|
|||
this.is_guest = is_guest || false
|
||||
}
|
||||
|
||||
normalize = function() {
|
||||
normalize = function () {
|
||||
if (this.user_id.startsWith('@') && this.user_id.includes(':')) {
|
||||
const parts = this.user_id.split(":");
|
||||
this.user_id = parts[0].substring(1);
|
||||
|
|
@ -14,7 +14,7 @@ export default class User {
|
|||
}
|
||||
};
|
||||
|
||||
static homeServerUrl = function(home_server) {
|
||||
static homeServerUrl = function (home_server) {
|
||||
if (home_server && !home_server.startsWith("https://")) {
|
||||
return "https://" + home_server;
|
||||
}
|
||||
|
|
@ -28,4 +28,12 @@ export default class User {
|
|||
}
|
||||
return user_id;
|
||||
}
|
||||
|
||||
static serverName(user_id) {
|
||||
if (user_id && user_id.startsWith('@') && user_id.includes(':')) {
|
||||
const parts = user_id.split(":");
|
||||
return parts[1];
|
||||
}
|
||||
return user_id;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,39 +2,24 @@ const stickerPacks = {};
|
|||
stickerPacks.ordering = [];
|
||||
const stickerCodeMap = {};
|
||||
|
||||
try {
|
||||
const stickerPackInfo = require("!!raw-loader!@/assets/stickers/order.txt").default;
|
||||
const packInfo = stickerPackInfo.split("\n");
|
||||
for (let i = 0; i < packInfo.length; i++) {
|
||||
const pack = packInfo[i];
|
||||
if (pack && pack.length > 0 && !pack.startsWith('#')) {
|
||||
stickerPacks[pack] = [];
|
||||
stickerPacks.ordering.push(pack);
|
||||
}
|
||||
}
|
||||
} catch (ignorederr) {
|
||||
//
|
||||
}
|
||||
|
||||
function importAll(r) {
|
||||
return r.keys().map(res => {
|
||||
// Remove"./"
|
||||
const parts = res.split("/");
|
||||
const pack = parts[1];
|
||||
const sticker = parts[2].split(".")[0];
|
||||
const image = r(res);
|
||||
if (stickerPacks[pack] !== undefined) {
|
||||
stickerPacks[pack].push({ image: image, name: sticker });
|
||||
stickerCodeMap[":" + pack + "-" + sticker + ":"] = image;
|
||||
}
|
||||
});
|
||||
}
|
||||
importAll(require.context('@/assets/stickers/', true, /\.png$/));
|
||||
|
||||
class Stickers {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
loadStickersFromConfig(config) {
|
||||
const stickerConfig = config.shortCodeStickers;
|
||||
for (const pack of stickerConfig.packs) {
|
||||
stickerPacks[pack.name] = [];
|
||||
stickerPacks.ordering.push(pack.name);
|
||||
for (const sticker of pack.stickers) {
|
||||
const stickerName = sticker.split(".")[0];
|
||||
const stickerUrl = stickerConfig.baseUrl + pack.name + "/" + sticker;
|
||||
stickerPacks[pack.name].push({ image: stickerUrl, name: stickerName });
|
||||
stickerCodeMap[":" + pack.name + "-" + stickerName + ":"] = stickerUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isStickerShortcode(messageBody) {
|
||||
if (messageBody && messageBody.startsWith(":") && messageBody.startsWith(":") && messageBody.length >= 5) {
|
||||
const image = this.getStickerImage(messageBody);
|
||||
|
|
|
|||
|
|
@ -580,6 +580,36 @@ class Util {
|
|||
browserCanRecordAudio() {
|
||||
return _browserCanRecordAudio;
|
||||
}
|
||||
|
||||
getUniqueAliasForRoomName(matrixClient, roomName, homeServer, iterationCount) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var preferredAlias = roomName.replace(/\s/g, "").toLowerCase();
|
||||
var tryAlias = "#" + preferredAlias + ":" + homeServer;
|
||||
matrixClient.getRoomIdForAlias(tryAlias)
|
||||
.then(ignoredid => {
|
||||
// We got a response, this means the tryAlias already exists.
|
||||
// Try again, with appended random chars
|
||||
if (iterationCount) {
|
||||
// Not the first time around. Reached max tries?
|
||||
if (iterationCount == 5) {
|
||||
reject("Failed to get unique room alias");
|
||||
return;
|
||||
}
|
||||
// Else, strip random chars from end so we can try again
|
||||
roomName = roomName.substring(0, roomName.length - 5);
|
||||
}
|
||||
const randomChars = this.randomString(4, "abcdefghijklmnopqrstuvwxyz0123456789");
|
||||
resolve(this.getUniqueAliasForRoomName(matrixClient, roomName + "-" + randomChars, homeServer, (iterationCount || 0) + 1))
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.errcode == 'M_NOT_FOUND') {
|
||||
resolve(preferredAlias);
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
export default new Util();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,29 @@
|
|||
import Vue from 'vue';
|
||||
import Vuetify from 'vuetify/lib';
|
||||
import icUser from '@/assets/icons/user.vue';
|
||||
import icPassword from '@/assets/icons/password.vue';
|
||||
import icEdit from '@/assets/icons/edit.vue';
|
||||
import icGlobe from '@/assets/icons/globe.vue';
|
||||
|
||||
Vue.use(Vuetify);
|
||||
|
||||
export default new Vuetify({
|
||||
icons: {
|
||||
iconfont: 'md',
|
||||
values: {
|
||||
user: {
|
||||
component: icUser
|
||||
},
|
||||
password: {
|
||||
component: icPassword
|
||||
},
|
||||
edit: {
|
||||
component: icEdit
|
||||
},
|
||||
globe: {
|
||||
component: icGlobe
|
||||
},
|
||||
},
|
||||
user: icUser
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ const routes = [
|
|||
path: '/goodbye',
|
||||
name: 'Goodbye',
|
||||
component: () => import('../components/QuoteView.vue'),
|
||||
props: true
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -82,10 +83,22 @@ const router = new VueRouter({
|
|||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const publicPages = ['/login','/createroom'];
|
||||
const publicPages = ['/login', '/createroom'];
|
||||
var authRequired = !publicPages.includes(to.path);
|
||||
const loggedIn = router.app.$store.state.auth.user;
|
||||
|
||||
if (to.query && to.query.lang) {
|
||||
// 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 (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.
|
||||
|
|
@ -113,7 +126,7 @@ router.beforeEach((to, from, next) => {
|
|||
}
|
||||
});
|
||||
|
||||
router.getRoomLink = function(roomId) {
|
||||
router.getRoomLink = function (roomId) {
|
||||
return window.location.origin + window.location.pathname + "#/room/" + encodeURIComponent(util.sanitizeRoomId(roomId));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import { CleanInsights, ConsentRequestUi } from 'clean-insights-sdk';
|
||||
import config from "../assets/config";
|
||||
|
||||
export default {
|
||||
install(Vue) {
|
||||
|
||||
class RequestUi extends ConsentRequestUi {
|
||||
showForCampaign(campaignId, campaign, complete) {
|
||||
const period = campaign.nextTotalMeasurementPeriod
|
||||
const period = campaign.nextTotalMeasurementPeriod
|
||||
if (!period) {
|
||||
return ''
|
||||
}
|
||||
|
|
@ -27,13 +26,17 @@ export default {
|
|||
}
|
||||
},
|
||||
created() {
|
||||
const analytics = config.analytics || {};
|
||||
if (analytics.enabled && analytics.config) {
|
||||
this.ci = new CleanInsights(analytics.config);
|
||||
const self = this;
|
||||
this.$config.promise.then(function (config) {
|
||||
const analytics = config.analytics || {};
|
||||
if (analytics.enabled && analytics.config) {
|
||||
self.ci = new CleanInsights(analytics.config);
|
||||
|
||||
// Get name of first campaign in the config.
|
||||
this.campaignId = Object.keys(analytics.config.campaigns || { invalid: {} })[0];
|
||||
}
|
||||
// Get name of first campaign in the config.
|
||||
self.campaignId = Object.keys(analytics.config.campaigns || { invalid: {} })[0];
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
event(category, action) {
|
||||
|
|
|
|||
18
src/services/config.service.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
export default {
|
||||
install(Vue) {
|
||||
var config = Vue.observable(require('@/assets/config.json'));
|
||||
const getRuntimeConfig = async () => {
|
||||
const runtimeConfig = await fetch('./config.json');
|
||||
return await runtimeConfig.json()
|
||||
}
|
||||
|
||||
config.promise = getRuntimeConfig();
|
||||
config.promise.then(function (json) {
|
||||
// Reactively use all the config values
|
||||
for (const key of Object.keys(json)) {
|
||||
Vue.set(config, key, json[key]);
|
||||
}
|
||||
});
|
||||
Vue.prototype.$config = config;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ import sdk from "matrix-js-sdk";
|
|||
import { TimelineWindow, EventTimeline } from "matrix-js-sdk";
|
||||
import util from "../plugins/utils";
|
||||
import User from "../models/user";
|
||||
import config from "../assets/config";
|
||||
|
||||
const LocalStorageCryptoStore = require("matrix-js-sdk/lib/crypto/store/localStorage-crypto-store")
|
||||
.LocalStorageCryptoStore;
|
||||
|
|
@ -14,6 +13,15 @@ export default {
|
|||
throw new Error('Please initialise plugin with a Vuex store.')
|
||||
}
|
||||
|
||||
// Set User-Agent headers.
|
||||
// Update: browser do not allow this, "Refused to set unsafe header "User-Agent""
|
||||
// Keep this code around however, since it's an example of how to add headers to a request...
|
||||
// sdk.wrapRequest((orig, opts, callback) => {
|
||||
// opts.headers = opts.headers || {}
|
||||
// opts.headers['User-Agent'] = "Keanu";
|
||||
// var ret = orig(opts, callback);
|
||||
// return ret;
|
||||
// });
|
||||
const store = options.store;
|
||||
const i18n = options.i18n;
|
||||
|
||||
|
|
@ -57,19 +65,23 @@ export default {
|
|||
return null;
|
||||
},
|
||||
|
||||
currentUserHomeServer() {
|
||||
return User.serverName(this.currentUserId);
|
||||
},
|
||||
|
||||
currentRoomId() {
|
||||
return this.$store.state.currentRoomId;
|
||||
},
|
||||
|
||||
joinedRooms() {
|
||||
return this.rooms.filter(room => {
|
||||
return room._selfMembership === 'join'
|
||||
return room.selfMembership === 'join'
|
||||
});
|
||||
},
|
||||
|
||||
invites() {
|
||||
return this.rooms.filter(room => {
|
||||
return room._selfMembership === 'invite'
|
||||
return room.selfMembership === 'invite'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
@ -107,29 +119,33 @@ export default {
|
|||
promiseLogin = tempMatrixClient
|
||||
.register(user, pass, null, {
|
||||
type: "m.login.dummy",
|
||||
initial_device_display_name: this.$config.appName
|
||||
})
|
||||
.then((response) => {
|
||||
console.log("Response", response);
|
||||
response.password = pass;
|
||||
response.is_guest = true;
|
||||
this.$store.commit("setUser", response);
|
||||
return response;
|
||||
var u = Object.assign({}, response);
|
||||
u.home_server = tempMatrixClient.baseUrl; // Don't use deprecated field from response.
|
||||
u.password = pass;
|
||||
u.is_guest = true;
|
||||
this.$store.commit("setUser", u);
|
||||
return u;
|
||||
})
|
||||
} else {
|
||||
var data = { user: User.localPart(user.user_id), password: user.password, type: "m.login.password" };
|
||||
var data = { user: User.localPart(user.user_id), password: user.password, type: "m.login.password", initial_device_display_name: this.$config.appName };
|
||||
if (user.device_id) {
|
||||
data.device_id = user.device_id;
|
||||
}
|
||||
promiseLogin = tempMatrixClient
|
||||
.login("m.login.password", data)
|
||||
.then((response) => {
|
||||
var u = response;
|
||||
var u = Object.assign({}, response);
|
||||
if (user.is_guest) {
|
||||
// Copy over needed properties
|
||||
u = Object.assign(user, response);
|
||||
}
|
||||
u.home_server = tempMatrixClient.baseUrl; // Don't use deprecated field from response.
|
||||
this.$store.commit("setUser", u);
|
||||
return response;
|
||||
return u;
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -280,7 +296,7 @@ export default {
|
|||
if (this.ready) {
|
||||
return Promise.resolve(this.currentUser);
|
||||
}
|
||||
return this.$store.dispatch("login", this.currentUser || new User(config.defaultServer, "", "", true));
|
||||
return this.$store.dispatch("login", this.currentUser || new User(this.$config.defaultServer, "", "", true));
|
||||
},
|
||||
|
||||
addMatrixClientListeners(client) {
|
||||
|
|
@ -318,18 +334,29 @@ export default {
|
|||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "m.room.member": {
|
||||
if (this.currentRoom && event.getRoomId() == this.currentRoom.roomId) { // Don't use this.currentRoomId, may be an alias. We need the real id!
|
||||
if (event.getContent().membership == "leave" && (event.getPrevContent() || {}).membership == "join" && event.getStateKey() == this.currentUserId && event.getSender() != this.currentUserId) {
|
||||
// We were kicked
|
||||
const wasPurged = (event.getContent().reason == "Room Deleted");
|
||||
this.$navigation.push({ name: "Goodbye", params: { roomWasPurged: wasPurged } }, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.updateNotificationCount();
|
||||
},
|
||||
|
||||
onRoom(ignoredroom) {
|
||||
console.log("Got room: " + ignoredroom);
|
||||
console.log("Got room", ignoredroom);
|
||||
this.reloadRooms();
|
||||
this.updateNotificationCount();
|
||||
},
|
||||
|
||||
onRoomMyMembership(room) {
|
||||
if (room._selfMembership === "invite") {
|
||||
if (room.selfMembership === "invite") {
|
||||
// Invitation. Need to call "recalculate" to pick
|
||||
// up room name, not sure why exactly.
|
||||
room.recalculate();
|
||||
|
|
@ -369,7 +396,7 @@ export default {
|
|||
// each time!
|
||||
var updatedRooms = this.matrixClient.getVisibleRooms();
|
||||
updatedRooms = updatedRooms.filter(room => {
|
||||
return room._selfMembership && (room._selfMembership == "invite" || room._selfMembership == "join");
|
||||
return room.selfMembership && (room.selfMembership == "invite" || room.selfMembership == "join");
|
||||
});
|
||||
updatedRooms.forEach(room => {
|
||||
if (!room.avatar) {
|
||||
|
|
@ -413,7 +440,7 @@ export default {
|
|||
var ids = {};
|
||||
const ret = [];
|
||||
for (const room of this.rooms) {
|
||||
if (room._selfMembership == 'join' && this.getRoomJoinRule(room) == 'invite') {
|
||||
if (room.selfMembership == 'join' && this.getRoomJoinRule(room) == 'invite') {
|
||||
for (const member of room.getJoinedMembers()) {
|
||||
if (member.userId != this.currentUserId && !ids[member.userId]) {
|
||||
ids[member.userId] = member;
|
||||
|
|
@ -488,7 +515,7 @@ export default {
|
|||
);
|
||||
const self = this;
|
||||
|
||||
console.log("Purge: set invite only");
|
||||
//console.log("Purge: set invite only");
|
||||
statusCallback(this.$t('room.purge_set_room_state'));
|
||||
this.matrixClient.sendStateEvent(
|
||||
roomId,
|
||||
|
|
@ -497,7 +524,7 @@ export default {
|
|||
""
|
||||
)
|
||||
.then(() => {
|
||||
console.log("Purge: forbid guest access");
|
||||
//console.log("Purge: forbid guest access");
|
||||
return this.matrixClient.sendStateEvent(
|
||||
roomId,
|
||||
"m.room.guest_access",
|
||||
|
|
@ -506,20 +533,20 @@ export default {
|
|||
);
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Purge: set history visibility to 'joined'");
|
||||
//console.log("Purge: set history visibility to 'joined'");
|
||||
return this.matrixClient.sendStateEvent(roomId, "m.room.history_visibility", {
|
||||
history_visibility: "joined",
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Purge: create timeline");
|
||||
//console.log("Purge: create timeline");
|
||||
return timelineWindow.load(null, 100)
|
||||
})
|
||||
.then(() => {
|
||||
const getMoreIfAvailable = function _getMoreIfAvailable() {
|
||||
if (timelineWindow.canPaginate(EventTimeline.BACKWARDS)
|
||||
) {
|
||||
console.log("Purge: page back");
|
||||
//console.log("Purge: page back");
|
||||
return timelineWindow
|
||||
.paginate(EventTimeline.BACKWARDS, 100, true, 5)
|
||||
.then((ignoredsuccess) => {
|
||||
|
|
@ -532,7 +559,7 @@ export default {
|
|||
return getMoreIfAvailable();
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Purge: redact events");
|
||||
//console.log("Purge: redact events");
|
||||
statusCallback(this.$t('room.purge_redacting_events'));
|
||||
// First ignore unknown device errors
|
||||
this.matrixClient.setGlobalErrorOnUnknownDevices(false);
|
||||
|
|
@ -546,7 +573,7 @@ export default {
|
|||
return Promise.all(redactionPromises);
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Purge: kick members");
|
||||
//console.log("Purge: kick members");
|
||||
statusCallback(this.$t('room.purge_removing_members'));
|
||||
var joined = room.getMembersWithMembership("join");
|
||||
var invited = room.getMembersWithMembership("invite");
|
||||
|
|
@ -555,7 +582,7 @@ export default {
|
|||
var kickPromises = [];
|
||||
members.forEach(member => {
|
||||
if (member.userId != self.currentUserId) {
|
||||
kickPromises.push(this.matrixClient.kick(roomId, member.userId));
|
||||
kickPromises.push(this.matrixClient.kick(roomId, member.userId, "Room Deleted"));
|
||||
}
|
||||
});
|
||||
return Promise.all(kickPromises);
|
||||
|
|
@ -563,6 +590,9 @@ export default {
|
|||
.then(() => {
|
||||
statusCallback(null);
|
||||
this.matrixClient.setGlobalErrorOnUnknownDevices(oldGlobalErrorSetting);
|
||||
return this.leaveRoom(roomId);
|
||||
})
|
||||
.then(() => {
|
||||
resolve(true); // Done!
|
||||
})
|
||||
.catch((err) => {
|
||||
|
|
@ -584,8 +614,9 @@ export default {
|
|||
// Is the other member the one we are looking for?
|
||||
if (this.isDirectRoomWith(room, userId)) {
|
||||
var member = room.getMember(userId);
|
||||
if (member && member.membership == "invite") {
|
||||
// TODO Resend invite
|
||||
if (member && member.membership != "join") {
|
||||
// Resend invite
|
||||
this.matrixClient.invite(room.roomId, userId);
|
||||
}
|
||||
resolve(room);
|
||||
return;
|
||||
|
|
@ -595,25 +626,32 @@ export default {
|
|||
// No room found, create one
|
||||
//
|
||||
const createRoomOptions = {
|
||||
visibility: "private", // Not listed!
|
||||
preset: "private_chat",
|
||||
initial_state: [
|
||||
{
|
||||
type: "m.room.encryption",
|
||||
state_key: "",
|
||||
content: {
|
||||
algorithm: "m.megolm.v1.aes-sha2",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "m.room.guest_access",
|
||||
state_key: "",
|
||||
content: {
|
||||
guest_access: "forbidden",
|
||||
},
|
||||
},
|
||||
],
|
||||
invite: [userId]
|
||||
visibility: "private", // Not listed!
|
||||
preset: "private_chat",
|
||||
initial_state: [
|
||||
{
|
||||
type: "m.room.encryption",
|
||||
state_key: "",
|
||||
content: {
|
||||
algorithm: "m.megolm.v1.aes-sha2",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "m.room.guest_access",
|
||||
state_key: "",
|
||||
content: {
|
||||
guest_access: "forbidden",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "m.room.history_visibility",
|
||||
state_key: "",
|
||||
content: {
|
||||
history_visibility: "joined",
|
||||
},
|
||||
},
|
||||
],
|
||||
invite: [userId],
|
||||
};
|
||||
return this.matrixClient
|
||||
.createRoom(createRoomOptions)
|
||||
|
|
@ -632,7 +670,7 @@ export default {
|
|||
* @param {*} userId
|
||||
*/
|
||||
isDirectRoomWith(room, userId) {
|
||||
if (room._selfMembership == "join" && room.getInvitedAndJoinedMemberCount() == 2) {
|
||||
if (room.selfMembership == "join" && room.getInvitedAndJoinedMemberCount() == 2) {
|
||||
// Is the other member the one we are looking for?
|
||||
if (room.getMembersWithMembership("join").some(item => item.userId == userId)) {
|
||||
return true;
|
||||
|
|
@ -704,7 +742,7 @@ export default {
|
|||
return this.matrixClient;
|
||||
})
|
||||
} else {
|
||||
const tempMatrixClient = sdk.createClient(config.defaultServer);
|
||||
const tempMatrixClient = sdk.createClient(this.$config.defaultServer);
|
||||
var tempUserString = this.$store.state.tempuser;
|
||||
var tempUser = null;
|
||||
if (tempUserString) {
|
||||
|
|
@ -721,6 +759,7 @@ export default {
|
|||
clientPromise = tempMatrixClient
|
||||
.register(user, pass, null, {
|
||||
type: "m.login.dummy",
|
||||
initial_device_display_name: this.$config.appName
|
||||
})
|
||||
.then((response) => {
|
||||
console.log("Response", response);
|
||||
|
|
@ -733,7 +772,7 @@ export default {
|
|||
|
||||
// Get an access token
|
||||
clientPromise = clientPromise.then(user => {
|
||||
var data = { user: User.localPart(user.user_id), password: user.password, type: "m.login.password" };
|
||||
var data = { user: User.localPart(user.user_id), password: user.password, type: "m.login.password", initial_device_display_name: this.$config.appName };
|
||||
if (user.device_id) {
|
||||
data.device_id = user.device_id;
|
||||
}
|
||||
|
|
@ -746,7 +785,7 @@ export default {
|
|||
// Only used to get public room info from.
|
||||
clientPromise = clientPromise.then(user => {
|
||||
var opts = {
|
||||
baseUrl: config.defaultServer,
|
||||
baseUrl: this.$config.defaultServer,
|
||||
userId: user.user_id,
|
||||
accessToken: user.access_token,
|
||||
timelineSupport: false,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ const vuexPersistLocalStorage = new VuexPersist({
|
|||
reducer: state => {
|
||||
if (state.useLocalStorage) {
|
||||
return {
|
||||
language: state.language,
|
||||
currentRoomId: state.currentRoomId,
|
||||
};
|
||||
} else {
|
||||
|
|
@ -51,6 +52,7 @@ const vuexPersistSessionStorage = new VuexPersist({
|
|||
reducer: state => {
|
||||
if (!state.useLocalStorage) {
|
||||
return {
|
||||
language: state.language,
|
||||
currentRoomId: state.currentRoomId,
|
||||
};
|
||||
} else {
|
||||
|
|
@ -62,7 +64,7 @@ const vuexPersistSessionStorage = new VuexPersist({
|
|||
const defaultUseSessionStorage = (sessionStorage.getItem('user') != null);
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: { currentRoomId: null, auth: null, tempuser: null, useLocalStorage: !defaultUseSessionStorage },
|
||||
state: { language: null, currentRoomId: null, auth: null, tempuser: null, useLocalStorage: !defaultUseSessionStorage },
|
||||
mutations: {
|
||||
loginSuccess(state, user) {
|
||||
state.auth.status.loggedIn = true;
|
||||
|
|
@ -76,6 +78,9 @@ export default new Vuex.Store({
|
|||
state.auth.status.loggedIn = false;
|
||||
state.auth.user = null;
|
||||
},
|
||||
setLanguage(state, locale) {
|
||||
state.language = locale;
|
||||
},
|
||||
setCurrentRoomId(state, roomId) {
|
||||
state.currentRoomId = roomId;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
|
||||
module.exports = {
|
||||
"transpileDependencies": [
|
||||
"vuetify"
|
||||
|
|
@ -15,6 +17,16 @@ module.exports = {
|
|||
})
|
||||
},
|
||||
|
||||
configureWebpack: {
|
||||
devtool: 'source-map',
|
||||
plugins: [
|
||||
new CopyWebpackPlugin([{
|
||||
from: "./src/assets/config.json",
|
||||
to: "./",
|
||||
}])
|
||||
]
|
||||
},
|
||||
|
||||
devServer: {
|
||||
//https: true
|
||||
},
|
||||
|
|
|
|||