Merge branch 'dev' into 'main'

Build 43 - merged from dev

See merge request keanuapp/keanuapp-weblite!339
This commit is contained in:
N Pex 2025-03-31 14:41:48 +00:00
commit ab01d4ddc3
68 changed files with 1936 additions and 985 deletions

View file

@ -53,6 +53,9 @@ The app loads runtime configutation from the server at "./config.json" and merge
* **accentColor** - The accent color of the app UI. Use a HTML-style color value string, like "#ff0080".
* **show_status_messages** - Whether to show only user joins/leaves and display name updates, or the full range of room status updates. Possible values are "never" (only the above), "moderators" (moderators will see all status updates) or "always" (everyone will see all status updates). Defaults to "always".
* **maxSizeAutoDownloads** - Attachments smaller than this will be auto downloaded. Default is 10Mb.
* **roomTypes** - Available room types. This affects what is shown on the /create route, as well as access to routes /createroom, /createchannel and /createfiledrop. It should be an array with possible values "group_chat", "channel" or "file_drop".
Defaults to all values, ["group_chat", "channel", "file_drop"].
### 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>"

View file

@ -1,6 +1,6 @@
{
"name": "keanuapp-weblite",
"version": "0.1.42",
"version": "0.1.43",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",

View file

@ -1,6 +1,6 @@
{
"name": "keanuapp-weblite",
"version": "0.1.41",
"version": "0.1.42",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",

87
public/ua.html Normal file
View file

@ -0,0 +1,87 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Convene User Agreement</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
max-width: 800px;
margin: 20px auto;
padding: 20px;
}
h1, h2 {
color: #333;
}
h1 {
text-align: center;
}
</style>
</head>
<body>
<h1>Convene User Agreement</h1>
<p><strong>Effective Date: March 11, 2025</strong></p>
<h2>1. Your Access to the Services</h2>
<p>No one under 13 is allowed to use or access the Services. By using the Services, you state that:</p>
<ul>
<li>You are at least 13 years old and over the minimum age required by the laws of your country of residence.</li>
<li>You can form a binding contract with Convene.</li>
<li>You are not barred from using the Services under applicable laws.</li>
<li>You have not been permanently suspended or removed from the Services.</li>
</ul>
<h2>2. Privacy</h2>
<p>Convenes Privacy Policy explains how and why we collect, use, and share information about you when you access or use our Services.</p>
<h2>3. Your Use of the Services</h2>
<p>Subject to compliance with these Terms, Convene grants you a non-transferable, non-exclusive, revocable, limited license to:</p>
<ul>
<li>Install and use a copy of our mobile application from a legitimate marketplace on a device you own.</li>
<li>Access and use the Services.</li>
</ul>
<h2>4. Your Convene Account and Account Security</h2>
<p>You are responsible for maintaining the security of your Account and immediately notifying Convene of unauthorized access.</p>
<h2>5. Your Content</h2>
<p>By submitting Your Content, you represent that you have the necessary rights and agree that Convene may store, display, or distribute it as needed.</p>
<h2>6. Third-Party Content</h2>
<p>Convene does not control or endorse third-party content and is not responsible for their availability or accuracy.</p>
<h2>7. Prohibited Conduct</h2>
<ul>
<li>Violating laws or regulations.</li>
<li>Interfering with security measures.</li>
<li>Transmitting malware or harmful code.</li>
<li>Harassing, threatening, or harming others.</li>
</ul>
<h2>8. Intellectual Property</h2>
<p>All materials within the Services are owned by Convene or its licensors.</p>
<h2>9. Indemnity</h2>
<p>You agree to indemnify and hold Convene harmless from any claims, damages, or expenses arising from your use of the Services.</p>
<h2>10. Limitation of Liability</h2>
<p>Convene is not liable for indirect, incidental, special, or consequential damages arising from your use of the Services.</p>
<h2>11. Governing Law</h2>
<p>These Terms are governed by the laws of the State of New York, United States. Any disputes shall be resolved in the courts of New York.</p>
<h2>12. Changes to These Terms</h2>
<p>We may update these Terms and will provide notice of significant changes.</p>
<h2>13. Termination</h2>
<p>You may terminate these Terms by deleting your Account. Convene reserves the right to suspend accounts that violate these Terms.</p>
<h2>14. Miscellaneous</h2>
<p>If any provision of these Terms is deemed invalid, the remaining provisions will remain in effect.</p>
<p>For questions or concerns, contact us at info@letsconvene.im.</p>
</body>
</html>

View file

@ -4,6 +4,7 @@ $app-background: #f6f6f6;
$main-desktop-width: 900px;
$dialog-desktop-width: 940px;
$very-very-purple: #536dfe;
$light-purple: #8E9FF8;
$lighter-gray: #FDFBF9;
$chat-background: $background;

View file

@ -1066,7 +1066,7 @@ body {
.option-text {
font-size: 13 * $chat-text-size;
border-top: 1px solid #e1e1e1;
// border-top: 1px solid #e1e1e1;
padding-top: 8px;
}
@ -1075,6 +1075,17 @@ body {
font-size: 16 * $chat-text-size;
}
.back-button {
font-weight: bold;
font-size: 12 * $chat-text-size;
[dir="rtl"] & {
.v-icon {
// Mirror the icon
transform: scale(-1, 1);
}
}
}
.header-button-left {
position: absolute;
top: 0px;
@ -1281,57 +1292,6 @@ body {
}
}
.create-room {
background-color: $background;
.v-avatar {
border: 1px solid #808080 !important;
}
.section {
background-color: white;
border-radius: 20px;
padding: 20px;
border: 1px solid #808080 !important;
position: relative;
}
.options {
display: flex;
width: 100%;
justify-content: flex-end;
color: black;
font-size: 14 * $chat-text-size;
font-weight: bold;
margin-left: 10px;
[dir="rtl"] & {
margin-left: initial;
margin-right: 10px;
}
text-transform: none !important;
}
.room-option {
.v-input {
margin: 0px;
}
}
.option-warning {
background: linear-gradient(0deg, #FFF3F3, #FFF3F3), #FFFBED;
border-radius: 8px;
padding: 18px;
font-family: 'Inter', sans-serif;
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 17px;
.v-icon {
margin-right: 16px;
}
}
}
.room-link .v-input__slot::before {
/* Remove text underline */
border-style: none !important;

View file

@ -1,8 +1,64 @@
@import "@/assets/css/main.scss";
.create-root {
background-color: $background;
user-select: none;
.v-avatar {
border: 1px solid #808080 !important;
}
.section {
background-color: white;
border-radius: 20px;
padding: 20px;
border: 1px solid #808080 !important;
position: relative;
}
.options {
display: flex;
width: 100%;
justify-content: flex-end;
color: black;
font-size: 14 * $chat-text-size;
font-weight: bold;
margin-left: 10px;
[dir="rtl"] & {
margin-left: initial;
margin-right: 10px;
}
text-transform: none !important;
}
.room-option {
.v-input {
margin: 0px;
}
}
.option-warning {
background: linear-gradient(0deg, #FFF3F3, #FFF3F3), #FFFBED;
border-radius: 8px;
padding: 18px;
font-family: 'Inter', sans-serif;
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 17px;
.v-icon {
margin-right: 16px;
}
}
/* Make secondary inputs (e.g. topic) */
.v-input.v-text-field--filled.v-text-field--rounded .v-input__slot {
background-color: #f5f5f5;
border-radius: 4px;
}
.create-loggedin {
text-align: center;
button {
@ -34,15 +90,12 @@
.create-title {
color: #000;
text-align: center;
font-family: "Poppins";
font-size: 28px;
font-style: normal;
font-weight: 700;
line-height: 108.5%; /* 30.38px */
letter-spacing: -0.8px;
white-space: pre-line;
margin-top: 50px;
&.no-icon {
margin-top: 122px;
}
}
.create-info {
@ -135,4 +188,10 @@
flex-direction: column;
margin-top: -20px;
}
.v-btn:not(.back-button) {
border-radius: 24px;
min-height: 48px !important;
min-width: 200px !important;
}
}

View file

@ -0,0 +1,76 @@
@import "@/assets/css/main.scss";
.h3-bold {
font-family: "Poppins";
font-style: normal;
font-size: 24 * $chat-text-size;
font-weight: 700;
line-height: 28.8 * $chat-text-size;
text-align: center;
text-underline-position: from-font;
text-decoration-skip-ink: none;
}
.paragraph {
font-family: "Inter";
font-size: 16 * $chat-text-size;
font-weight: 400;
line-height: 18.72 * $chat-text-size;
letter-spacing: 0.40 * $chat-text-size;
text-align: left;
text-underline-position: from-font;
text-decoration-skip-ink: none;
color: rgba(0,0,0, 0.7);
&.paragraph-button {
color: $very-very-purple;
}
}
.paragraph-bold {
font-family: "Inter";
font-size: 16 * $chat-text-size;
font-weight: 600;
line-height: 18.72 * $chat-text-size;
letter-spacing: 0.40 * $chat-text-size;
text-align: left;
text-underline-position: from-font;
text-decoration-skip-ink: none;
color: rgba(0,0,0);
margin-bottom: 3 * $chat-text-size;
}
.caption-1 {
font-family: "Inter";
font-size: 14 * $chat-text-size;
font-weight: 400;
line-height: 16.38 * $chat-text-size;
letter-spacing: 0.40 * $chat-text-size;
text-align: left;
text-underline-position: from-font;
text-decoration-skip-ink: none;
color: rgba(0,0,0,0.65);
}
.caption-1-gray {
font-family: "Inter";
font-size: 14 * $chat-text-size;
font-weight: 400;
line-height: 16.38 * $chat-text-size;
letter-spacing: 0.40 * $chat-text-size;
text-align: left;
text-underline-position: from-font;
text-decoration-skip-ink: none;
color: rgba(0,0,0,0.65);
}
.caption-2 {
font-family: "Inter";
font-size: 10 * $chat-text-size;
font-weight: 400;
line-height: 11.7 * $chat-text-size;
letter-spacing: 0.40 * $chat-text-size;
text-align: left;
text-underline-position: from-font;
text-decoration-skip-ink: none;
color: rgba(0,0,0,0.60);
}

View file

@ -0,0 +1,17 @@
<template>
<svg width="23" height="22" viewBox="0 0 23 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M0.700195 4.59961C0.700195 2.39047 2.49106 0.599609 4.7002 0.599609H18.0967C20.3059 0.599609 22.0967 2.39047 22.0967 4.59961V21.9962H4.7002C2.49106 21.9962 0.700195 20.2053 0.700195 17.9962V4.59961Z"
fill="#FF3913" />
<g clip-path="url(#clip0_704_4275)">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M17.9502 7.48911C18.1987 7.58811 18.4272 7.73711 18.6202 7.92961C18.9917 8.30111 19.2002 8.80511 19.2002 9.33011C19.2002 11.1126 19.2002 14.0866 19.2002 15.8691C19.2002 16.3941 18.9917 16.8981 18.6202 17.2696C18.2487 17.6411 17.7447 17.8496 17.2197 17.8496C14.5297 17.8496 8.8707 17.8496 6.1807 17.8496C5.6557 17.8496 5.1517 17.6411 4.7802 17.2696C4.4087 16.8981 4.2002 16.3941 4.2002 15.8691C4.2002 13.9451 4.2002 10.6311 4.2002 8.92861C4.2002 8.50961 4.3667 8.10811 4.6627 7.81211C4.9587 7.51611 5.3602 7.34961 5.7792 7.34961C5.7797 7.34961 5.7797 7.34961 5.7802 7.34961C6.0082 7.34961 6.2037 7.18611 6.2437 6.96161C6.2557 6.89561 6.2677 6.82861 6.2797 6.76161C6.4252 5.94461 7.1357 5.34961 7.9657 5.34961C9.1847 5.34961 11.2157 5.34961 12.4347 5.34961C13.2647 5.34961 13.9752 5.94461 14.1207 6.76161L14.1212 6.76211C14.1742 7.06111 14.4097 7.28861 14.7002 7.33911V6.96061C14.7002 6.73261 14.7907 6.51311 14.9522 6.35161C15.1137 6.19011 15.3332 6.09961 15.5612 6.09961C15.9812 6.09961 16.6692 6.09961 17.0892 6.09961C17.3172 6.09961 17.5367 6.19011 17.6982 6.35161C17.8597 6.51311 17.9502 6.73261 17.9502 6.96061V7.48911ZM10.2002 8.34961C7.9927 8.34961 6.2002 10.1421 6.2002 12.3496C6.2002 14.5571 7.9927 16.3496 10.2002 16.3496C12.4077 16.3496 14.2002 14.5571 14.2002 12.3496C14.2002 10.1421 12.4077 8.34961 10.2002 8.34961ZM17.9502 9.76911C17.9502 9.52511 17.8532 9.29111 17.6807 9.11861C17.5087 8.94661 17.2747 8.84961 17.0307 8.84961C16.7552 8.84961 16.3952 8.84961 16.1197 8.84961C15.8757 8.84961 15.6417 8.94661 15.4697 9.11861C15.2972 9.29111 15.2002 9.52511 15.2002 9.76911C15.2002 9.97561 15.2002 10.2236 15.2002 10.4301C15.2002 10.6741 15.2972 10.9081 15.4697 11.0806C15.6417 11.2526 15.8757 11.3496 16.1197 11.3496C16.3952 11.3496 16.7552 11.3496 17.0307 11.3496C17.2747 11.3496 17.5087 11.2526 17.6807 11.0806C17.8532 10.9081 17.9502 10.6741 17.9502 10.4301V9.76911Z"
fill="white" />
</g>
<defs>
<clipPath id="clip0_704_4275">
<rect width="16" height="16" fill="white" transform="translate(3.7002 3.59961)" />
</clipPath>
</defs>
</svg>
</template>

View file

@ -0,0 +1,12 @@
<template>
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M35.9999 30.4961L25.2856 49.0538H46.7142L35.9999 30.4961ZM35.9999 35.4448L29.5714 46.5794H42.4285L35.9999 35.4448Z"
fill="#F4F952" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M35.9997 22.248L18.1426 53.1775H53.8569L35.9997 22.248ZM35.9997 27.1968L22.4283 50.7032H49.5711L35.9997 27.1968Z"
fill="#F4F952" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M36 14L11 57.3013H61L36 14ZM36 18.9487L15.2857 54.8269H56.7143L36 18.9487Z" fill="#F4F952" />
</svg>
</template>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,10 @@
<template>
<svg width="27" height="26" viewBox="0 0 27 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M25.3037 15.9221C24.8876 15.9222 24.5503 16.2596 24.5503 16.6758V20.421H2.39235V16.6758C2.39235 16.2595 2.05496 15.9221 1.63867 15.9221C1.22242 15.9221 0.885 16.2595 0.885 16.6758V22.3857C0.885 23.8906 2.1093 25.1146 3.61446 25.1146H23.33C24.8342 25.1146 26.0577 23.8905 26.0577 22.3857V16.6758V16.6757C26.0573 16.2596 25.7203 15.9222 25.304 15.9221C25.3039 15.9221 25.3038 15.9221 25.3037 15.9221ZM23.3303 23.6073H3.61443C2.94038 23.6073 2.39231 23.0592 2.39229 22.3857C2.39229 22.3857 2.39229 22.3857 2.39229 22.3857L2.39238 21.9282H24.5506V22.3854C24.5506 22.3854 24.5506 22.3854 24.5506 22.3855C24.5503 23.0592 24.0029 23.6073 23.3303 23.6073Z"
fill="black" stroke="black" stroke-width="0.23" />
<path
d="M20.9884 11.8001L20.9071 11.7188C21.1567 11.4692 21.1567 11.0648 20.9071 10.8155L20.9884 10.7342C20.9884 10.7342 20.9884 10.7341 20.9884 10.7341C20.6938 10.4396 20.2169 10.44 19.9227 10.7342L14.2263 16.4301V1.63867C14.2263 1.22242 13.8889 0.885 13.4726 0.885C13.0563 0.885 12.719 1.22239 12.719 1.63867V16.43L7.02366 10.7342L6.94234 10.8155L7.02365 10.7342C6.72909 10.4396 6.25212 10.44 5.95794 10.7342L5.95794 10.7342C5.66345 11.0287 5.66337 11.5059 5.958 11.8001L12.9395 18.7825L13.0209 18.7012L12.9395 18.7825C13.081 18.9239 13.2725 19.0032 13.4725 19.0032C13.6726 19.0032 13.8641 18.924 14.0055 18.7825L20.9884 11.8001Z"
fill="black" stroke="black" stroke-width="0.23" />
</svg>
</template>

View file

@ -0,0 +1,9 @@
<template>
<svg width="29" height="29" viewBox="0 0 29 29" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="11" width="18" height="13" rx="3" fill="currentColor" />
<path d="M22.1029 28L14.2058 22.5L22 22.5L22.1029 28Z" fill="currentColor" />
<path
d="M25.0811 9.52329H19.6726V3.91892C19.6726 1.76351 17.9091 0 15.7537 0H3.91891C1.76351 0 0 1.76351 0 3.91892V11.4828C0 13.6382 1.76351 15.4017 3.91891 15.4017H5.68243V18.6929C5.68243 18.9669 5.83857 19.2409 6.07432 19.3588C6.19219 19.4369 6.31007 19.4767 6.46621 19.4767C6.62235 19.4767 6.74023 19.4369 6.8581 19.3588L9.28753 17.9091V21.0044C9.28753 23.1599 11.051 24.9234 13.2064 24.9234H15.518L22.1021 28.8821C22.22 28.9602 22.3761 29 22.494 29C22.6119 29 22.768 28.9602 22.8859 28.8821C23.1216 28.726 23.2778 28.4902 23.2778 28.2162V24.925H25.0811C27.2365 24.925 29 23.1615 29 21.006V13.4422C29 11.2868 27.2365 9.52329 25.0811 9.52329ZM7.24999 17.3213V14.6561C7.24999 14.2244 6.89791 13.8723 6.46621 13.8723H3.91891C2.62538 13.8723 1.56757 12.8145 1.56757 11.521V3.91895C1.56757 2.62541 2.62538 1.5676 3.91891 1.5676H15.7537C17.0472 1.5676 18.1051 2.62541 18.1051 3.91895V9.48349H13.2463C11.0909 9.48349 9.32734 11.247 9.32734 13.4024V16.0676L7.24999 17.3213ZM27.4324 21.006C27.4324 22.2996 26.3746 23.3574 25.0811 23.3574H22.5338C22.1021 23.3574 21.75 23.7095 21.75 24.1412V26.8063L16.1854 23.4753C16.0676 23.3972 15.9114 23.3574 15.7936 23.3574H13.2463C11.9527 23.3574 10.8949 22.2996 10.8949 21.006V13.4422C10.8949 12.1487 11.9527 11.0908 13.2463 11.0908H25.0811C26.3746 11.0908 27.4324 12.1487 27.4324 13.4422V21.006Z"
fill="black" />
</svg>
</template>

View file

@ -79,12 +79,14 @@
"message_retention_1_day": "يوم واحد",
"message_retention_8_hours": "8 ساعات",
"message_retention_1_hour": "ساعة واحدة",
"make_public": "اجعلها عمومية",
"make_public_warning": "تحذير: سيكون تاريخ الرسائل الكامل مرئيا للمشاركين الجدد",
"direct_link": "رابطي المباشر",
"direct_link_desc": "لقد أصبح جاهزا للمشاركة! سيتم فتح غرفة مباشِرة جديدة في كل مرة يفتح فيها شخص ما الرابط.",
"shared_room_number": "أنت تشارك{count} غرف مع {name}",
"shared_room_number_more": "أنت تشارك أكثر من {count} غرف مع {name}"
"shared_room_number_more": "أنت تشارك أكثر من {count} غرف مع {name}",
"message_history": "تاريخ الرسائل",
"message_history_warning": "تحذير: سيكون تاريخ الرسائل الكامل مرئيا للمشاركين الجدد",
"report": "تقرير",
"report_reason": "سبب"
},
"poll_create": {
"poll_status_closed": "تم إغلاق استطلاع الرأي",
@ -281,7 +283,6 @@
"invite_info": "فقط الأشخاص الذين تم إضافتهم",
"invite_description": "الاختيار من قائمة أو البحث عن طريق مُعرِّف الحساب",
"status_creating": "يجري إنشاء الغرفة",
"status_avatar_total": "يجري تحميل الصورة الرمزية: {count} من أصل {total}",
"status_avatar": "يجري تحميل الصورة الرمزية: {count}",
"room_name_limit_error_msg": "الحد الأقصى المسموح به هو 50 حرفا",
"colon_not_allowed": "النقطتان (:) غير مسموح بهما",
@ -297,7 +298,6 @@
"info": "قم ببث الأخبار أو المعارف مهما كانت صيغتها — بفيديو أو بالبث الحي أو بالنصوص أو بالصور أو على هيئة ملفات PDF",
"channel_name": "تسمية قناتك",
"channel_topic": "وصفها",
"name_required": "اسم القناة مطلوب",
"error_channel": "فشل إنشاء القناة"
},
"profile": {
@ -440,5 +440,9 @@
"send_more_files": "إرسال المزيد من الملفات",
"close": "إغلاق",
"files": "الملفات"
},
"create": {
"room_type_channel_name": "قناة",
"field_required": "هذا الحقل مطلوب"
}
}

View file

@ -164,7 +164,6 @@
"invite_info": "শুধুমাত্র যুক্ত আছে এমন সকল",
"invite_description": "তালিকা থেকে নির্বাচন করুন বা অ্যাকাউন্ট আইডি দিয়ে খুঁজুন",
"status_creating": "রুম তৈরি করা হচ্ছে",
"status_avatar_total": "প্রোফাইলের ছবি আপলোড করা হচ্ছে: {total} টির মধ্যে {count}",
"status_avatar": "প্রোফাইলের ছবি আপলোড করা হচ্ছে: {count}",
"room_name_limit_error_msg": "সর্বাধিক ৫০ অক্ষর লেখা যাবে",
"colon_not_allowed": "কোলন দেওয়া যাবেনা",
@ -215,7 +214,6 @@
"info": "যে কোনো ফরম্যাটে সংবাদ বা জ্ঞান সম্প্রচার করুন—ভিডিও, পডকাস্ট, টেক্সট, ছবি বা PDF।",
"channel_name": "আপনার চ্যানেলের নাম দিন",
"channel_topic": "এটার বিবরণ লিখুন",
"name_required": "চ্যানেলের নাম আবশ্যক",
"error_channel": "চ্যানেল তৈরি করতে ব্যর্থ হয়েছে"
},
"profile": {
@ -338,12 +336,13 @@
"message_retention_1_day": "১ দিন",
"message_retention_8_hours": "৮ ঘণ্টা",
"message_retention_1_hour": "১ ঘণ্টা",
"make_public": "উন্মুক্ত করুন",
"make_public_warning": "সতর্কতা: নতুন অংশগ্রহণকারীগন পূর্বের সকল বার্তা ইতিহাস দেখতে পাবেন।",
"direct_link": "আমার সরাসরি লিংক",
"direct_link_desc": "এটি শেয়ার করার জন্য প্রস্তুত! কেউ লিংকটি ওপেন করলে প্রতিবার একটি নতুন সরাসরি রুম তৈরি হবে।",
"shared_room_number": "আপনি {name} এর সাথে {count} টি রুম শেয়ার করছেন",
"shared_room_number_more": "আপনি {name} এর সাথে {count} এর বেশি রুম শেয়ার করছেন"
"shared_room_number_more": "আপনি {name} এর সাথে {count} এর বেশি রুম শেয়ার করছেন",
"message_history": "বার্তার ইতিহাস",
"message_history_warning": "সতর্কতা: নতুন অংশগ্রহণকারীগন পূর্বের সকল বার্তা ইতিহাস দেখতে পাবেন।",
"report": "প্রতিবেদন"
},
"room_info_sheet": {
"this_room": "এই রুম",
@ -440,5 +439,9 @@
"send_more_files": "আরও ফাইল পাঠান",
"close": "বন্ধ করুন",
"files": "ফাইলগুলি"
},
"create": {
"room_type_channel_name": "Channel",
"field_required": "এটি প্রয়োজনীয় ক্ষেত্র"
}
}

View file

@ -36,17 +36,15 @@
"download_chat": "ཁ་བརྡ་ཕབ་ལེན།",
"read_only_room": "ལྟ་ཀློག་ཁོ་ན།",
"direct_link": "ང་དང་ཐད་ཀར་ཁ་བརྡ་བྱེད་སའི་འབྲེལ་ཐག",
"make_public_warning": "ཉེན་བརྡ།: ཆ་འཕྲིན་གྱི་ལོ་རྒྱུས་ཡོངས་རྫོགས་འཛུལ་ཞུགས་པ་གསར་པ་ཚོས་མཐོང་ཐུབ།",
"room_type": "ཁ་བརྡ་ཁང་གི་དབྱེ་བ།",
"file_mode_info": "གླེང་མོལ་གྱི་མཐུད་ངོས་ཡིག་ཆ་བརྒྱུད་གཏོང་གི་རྣམ་པར་བསྒྱུར།",
"room_type_default": "སྔར་ཡོད།",
"file_mode": "ཡིག་ཆའི་རྣམ་པ།",
"direct_link_desc": "བརྒྱུད་སྐུར་བྱེད་པར་གྲ་སྒྲིག་ཡིན། མི་ཞིག་གིས་འབྲེལ་ཐག་དེ་ཁ་འབྱེད་ཐེངས་རེར། ང་དང་ཐད་ཀར་གླེང་མོལ་བྱ་སའི་ཁ་བརྡ་ཁང་ཞིག་གི་སྒོ་འབྱེད་ངེས།",
"copy_link": "འབྲེལ་ཐག་པར་བཤུས་རྒྱོབས།",
"make_public": "ཡོངས་ཁྱབ་བཟོས།",
"message_retention_1_hour": "ཆུ་ཚོད་ ༡",
"message_retention_info": "དུས་ཡུན་འདིའི་ནང་དུ་བཏང་བའི་ཆ་འཕྲིན་ཁག་འབྲེལ་ཐག་བརྒྱུད་ནས་ཚང་མས་མཐོང་ཐུབ།",
"message_retention": "ཆ་འཕྲིན་གྱི་ལོ་རྒྱུས།",
"message_retention_info": "ཆ་འཕྲིན་ཁག་དུས་ཡུན་ག་ཚོད་རིང་ཉར་དགོས་མིན་ཚད་བཀག་བཟོས།",
"message_retention": "ཁ་བརྡའི་ཟིན་ཐོ་ཚད་བཀག་བཟོས།",
"message_retention_none": "སྒོ་རྒྱོབ།",
"message_retention_4_week": "གཟའ་འཁོར་༤",
"message_retention_2_week": "གཟའ་འཁོར་༢",
@ -55,7 +53,14 @@
"message_retention_8_hours": "ཆུ་ཚོད་༨",
"shared_room_number": "ཁྱེད་ཀྱིས་{name}་དང་མཉམ་དུ་ཁ་བརྡ་ཁང་ {count}་མཉམ་སྤྱོད་བྱེད་ཀྱི་འདུག",
"shared_room_number_more": "ཁྱེད་ཀྱིས་{name}་དང་མཉམ་དུ་ཁ་བརྡ་ཁང་ {count}་ལས་མང་བ་མཉམ་སྤྱོད་བྱེད་ཀྱི་འདུག",
"moderation": "དོ་དམ།"
"moderation": "དོ་དམ།",
"message_history": "ཆ་འཕྲིན་གྱི་ལོ་རྒྱུས།",
"message_history_warning": "ཉེན་བརྡ།: ཆ་འཕྲིན་གྱི་ལོ་རྒྱུས་ཡོངས་རྫོགས་འཛུལ་ཞུགས་པ་གསར་པ་ཚོས་མཐོང་ཐུབ།",
"report_reason": "རྒྱུ་མཚན།",
"message_history_info": "ཚོགས་མི་གསར་པ་ཚོར་ཚོགས་པའི་ནང་མ་ཞུགས་པའི་སྔོན་དུ་བཏང་ཡོད་པའི་ཆ་འཕྲིན་ཁག་མཐོང་དུ་བཅུག",
"limit_history_info": "{period}་རིང་ལ་ཆ་འཕྲིན་ཁག་ཉར།",
"report_info": "ཁྲིམས་འགལ་དང་ལོག་སྤྱོད། ཡང་ན་གནོད་འཚེ་ཅན་གྱི་ནང་དོན་ལྡན་པའི་ཚོགས་པ་འདི་ཞབས་ཞུའི་དོ་དམ་པར་སྙན་སེང་ཞུས།",
"report": "སྙན་སེང་ཞུས།"
},
"invite": {
"done": "ལེགས་འགྲུབ།",
@ -88,8 +93,7 @@
},
"new_room": {
"next": "རྗེས་མ།",
"status_avatar": "མགོ་པར་ཡར་འཇུག་བྱེད་བཞིན་པ།: {count}",
"status_avatar_total": "མགོ་པར་ཡར་འཇུག་བྱེད་བཞིན་པ། {total} ཡི་{count}",
"status_avatar": "མགོ་པར་ཡར་འཇུག་བྱེད་བཞིན་པ།",
"status_creating": "ཁ་བརྡ་ཁང་བཟོ་བཞིན་པ།",
"invite_description": "ཐོ་ཁོངས་ID བརྒྱུད་ནས། འཚོལ་བཤེར་རམ་ཐོ་གཞུང་གི་ཁོངས་ནས་འདེམས།",
"invite_info": "ཁ་སྣོན་བྱས་པའི་མི་རྣམས་ཁོ་ན།",
@ -103,11 +107,12 @@
"join_permissions": "ནང་འཛུལ་གྱི་ཆོག་མཆན་ཁག",
"new_room": "ཁ་བརྡ་ཁང་གསར་པ།",
"name_room": "ཁ་བརྡ་ཁང་གི་མིང་།",
"room_topic": "གལ་ཏེ་འདོད་པ་ཡོད་ན། ཚོགས་པའི་སྐོར་གྱི་འགྲེལ་བཤད་ཐུང་ངུ་ཞིག་འབྲི་ཆོག",
"room_topic": "ཁྱེད་ལ་འདོད་པ་ཡོད་ན། ཚོགས་པའི་སྐོར་གྱི་འགྲེལ་བཤད་ིག་འབྲི་ཆོག",
"create": "བཟོས།",
"colon_not_allowed": ": རྟགས་འདི་བཀོལ་མི་ཆོག",
"options": "གདམ་ག",
"room_name_limit_error_msg": "ཡིག་འབྲུ་གྲངས་༥༠ ལས་བརྒལ་མི་ཆོག"
"room_name_limit_error_msg": "ཡིག་འབྲུ་གྲངས་༥༠ ལས་བརྒལ་མི་ཆོག",
"title": "ཚོགས་པའི་གླེང་མོལ་གསར་པ།"
},
"menu": {
"logout": "ཕྱིར་ཐོན།",
@ -132,7 +137,9 @@
"user_revoke_moderator": "གཙོ་སྐྱོང་བའི་དབང་ཚད་མེད་པར་བཟོས།",
"delete_now": "བསུབ་རོགས།",
"direct_chat": "ཐད་ཀར་གླེང་མོལ།",
"user_kick_and_ban": "སྒོར་ཕུད།"
"user_kick_and_ban": "སྒོར་ཕུད།",
"pin": "སྤེལ་གཏམ་རྩེ་རུ་ཞོག",
"unpin": "རྩེ་རུ་བཞག་པའི་སྤེལ་གཏམ་ཕྱིར་འཐེན།"
},
"profile": {
"change_password": "གསང་ཚིག་རྗེས།",
@ -181,7 +188,7 @@
"room_list_rooms": "ཁ་བརྡ་ཁང་།",
"room_list_invites": "གདན་ཞུ་ཁག",
"purge_failed": "ཁ་བརྡ་ཁང་བཤིག་ཐུབ་མ་སོང་།",
"purge_removing_members": "ཚོགས་མི་ཁག་ཕྱིར་འདོན། {total})་ཀྱི་({members}",
"purge_removing_members": "ཚོགས་མི་ཕྱིར་འབུད། ({total})་ཀྱི་{count} )",
"purge_redacting_events": "ཁ་བརྡ་གཙང་གསུབ། {total})་ཀྱི་({count}",
"purge_set_room_state": "ཁ་བརྡ་ཁང་གི་རྣམ་པ་སྒྲིག་འགོད།",
"room_list_new_messages": "{count} ཆ་འཕྲིན་གསར་པ།",
@ -308,7 +315,9 @@
"enter_room_user": "ཁ་བརྡ་འགོ་རྩོམ།",
"choose_name": "མིང་ཞིག་འདེམས་ཏེ་བཀོལ།",
"title_user": "དགའ་བསུ་ཞུ། ཁྱེད་རང་ཁ་བརྡ་བྱེད་པར་གདན་ཞུ་གནང་སོང་།",
"you_have_been_banned": "ཁྱེད་རང་ཚོགས་པ་འདིའི་ནང་ནས་བཀག་འདུག"
"you_have_been_banned": "ཁྱེད་རང་ཚོགས་པ་འདིའི་ནང་ནས་བཀག་འདུག",
"ua": "ཞབས་ཞུ་སྤྱོད་མཁན་གྱི་གྲོས་མཐུན།",
"accept_ua": "ང་ {agreement}་ལ་མོས་མཐུན་ཡོད།"
},
"profile_info_popup": {
"powered_by": "ཁ་བརྡ་ཁང་འདི་{product} ནུས་ཤུགས་བསྩལ་ཡོད། {productLink} ནས་དེ་ལས་མང་བ་སྦྱོང་ཆོག་ལ། མདུན་དུ་བསྐྱོད་དེ་ཁ་བརྡ་ཁང་གཞན་ཞིག་བསྐྲུན་ཆོག",
@ -318,7 +327,8 @@
"edit_profile": "སྒེར་གྱི་ཡིག་ཆ་བཅོས་སྒྲིག",
"identity_temporary": "{displayName}",
"identity": "{displayName}",
"you_are": "ཁྱེད་ནི།"
"you_are": "ཁྱེད་ནི།",
"review_ua": "ཞབས་ཞུ་སྤྱོད་མཁན་གྱི་གྲོས་མཐུན་བསྐྱར་ཞིབ།"
},
"goodbye": {
"view_other_rooms": "ཁ་བརྡ་ཁང་གཞན་དག་ལ་གཟིགས།",
@ -434,11 +444,30 @@
"different_link": "འབྲེལ་ཐག་གཞན་པ་ཞིག་རག་པར་བྱོས།"
},
"createchannel": {
"error_channel": "བརྒྱུད་ལམ་བཟོ་ཐུབ་མ་སོང་།",
"name_required": "བརྒྱུད་ལམ་གྱི་མིང་ངེས་པར་དུ་དགོས།",
"title": "བརྒྱུད་ལམ་ཞིག་བཟོས།",
"error_channel": "རྒྱུ་ལམ་བཟོ་ཐུབ་མ་སོང་།",
"title": "རྒྱུ་ལམ་ཞིག་བཟོས།",
"channel_topic": "དེར་འགྲེལ་བཤད་བྱོས།",
"channel_name": "རང་གི་བརྒྱུད་ལམ་ལ་མིང་ཞིག་ཐོགས།",
"info": "ཤེས་བྱ་དང་གསར་འགྱུར་ཅི་རིགས་བརྙན་དང་། གཏམ་བཤད་གྱི་ལེ་ཚན། དཔེ་དེབ། པར་རིས། PDF སོགས་ཀྱི་རྣམ་པའི་ཐོག་བརྒྱུད་གཏོང་བྱོས།"
"channel_name": "རང་གི་རྒྱུ་ལམ་ལ་མིང་ཞིག་ཐོགས།",
"info": "ཤེས་བྱ་དང་གསར་འགྱུར་ཅི་རིགས་བརྙན་དང་། གཏམ་བཤད་གྱི་ལེ་ཚན། དཔེ་དེབ། པར་རིས། PDF སོགས་ཀྱི་རྣམ་པའི་ཐོག་བརྒྱུད་གཏོང་བྱོས།",
"status_creating": "རྒྱུ་ལམ་གསར་བཟོ།",
"channel_topic_label": "དྲ་གྲོགས་ཚོ་རྒྱུ་ལམ་དུ་ཞུགས་སྐབས་ཁྱེད་ཀྱི་འགྲེལ་བཤད་དེ་མཐོང་གི་ཡོད།"
},
"create": {
"title": "མྱོང་ཚོར་ཞིག་འདེམས།",
"room_type_filedrop_description": "ཡིག་ཆ་དང་ལམ་སྟོན་ལེན་པ།",
"field_required": "ཡིག་དུམ་འདི་ངེས་པར་དགོས།",
"topic_too_long": "ཡིག་འབྲུ500་ལས་བརྒལ་མི་ཆོག",
"room_type_room_name": "ཚོགས་པའི་ཁ་བརྡ།",
"room_type_channel_name": "རྒྱུ་ལམ།",
"room_type_channel_description": "གསར་འགྱུར་དང་བསམ་ཚུལ་བརྒྱུད་སྤེལ།",
"room_type_filedrop_name": "ཡིག་ཆ་འཇོག་སྒྲོམ།",
"room_type_room_description": "མི་ཁག་ཅིག་ལ་འབྲེལ་མཐུད་བྱོས།"
},
"createfiledrop": {
"title": "ཡིག་ཆ་འཇོག་སྒྲོམ་ཞིག་བཟོས།",
"info": "ཡིག་ཆ་འཇོག་སྒྲོམ་ནི་མི་གང་རུང་གི་ཕྱོགས་ནས་ཡིག་ཆ་སྣ་ཚོགས་ལེན་ཆོག་སའི་བདེ་འཇགས་ཀྱི་བར་སྟོང་ཞིག་ཡིན།",
"filedrop_name": "ཁྱེད་ཀྱི་ཡིག་ཆ་འཇོག་སྒྲོམ་ལ་མིང་ཞིག་ཐོགས།",
"status_creating": "ཡིག་ཆ་འཇོག་སྒྲོམ་བཟོ་བཞིན་པ།",
"error_filedrop": "ཡིག་ཆ་འཇོག་སྒྲོམ་བཟོ་ཐུབ་མ་སོང་།"
}
}

View file

@ -90,7 +90,8 @@
"seen_by_count": "Von keinem Mitglied gesehen | Von 1 Mitglied gesehen | Von {count} Mitgliedern gesehen",
"not_allowed_to_send": "Nur Admins und Moderatoren dürfen an diesen Raum senden",
"upload_exceeded_file_limit": "Dateihöchstgröße von ({configFormattedUploadSize}) überschritten. ",
"reaction_count_more": "{reactionCount} mehr"
"reaction_count_more": "{reactionCount} mehr",
"failed_to_render": "Ereignis konnte nicht gerendert werden"
},
"room": {
"leave": "Verlassen",
@ -137,8 +138,7 @@
"invite_info": "Nur hinzugefügte Personen",
"invite_description": "Wähle aus einer Liste oder suche nach der Konto-ID",
"status_creating": "Erstellung des Raums",
"status_avatar_total": "Avatar wird hochgeladen: {count} von {total}",
"status_avatar": "Avatar wird hochgeladen: {count}",
"status_avatar": "Avatar wird hochgeladen",
"new_room": "Neuer Raum",
"create": "Erstellen",
"next": "Nächste",
@ -146,7 +146,8 @@
"room_topic": "Füge eine Beschreibung hinzu, wenn du möchtest",
"room_name_limit_error_msg": "Maximal 50 Zeichen erlaubt",
"options": "Optionen",
"colon_not_allowed": "Doppelpunkt ist nicht erlaubt"
"colon_not_allowed": "Doppelpunkt ist nicht erlaubt",
"title": "Neuer Gruppen-Chat"
},
"device_list": {
"blocked": "Blockiert",
@ -283,21 +284,25 @@
"experimental_features": "Experimentelle Funktionen",
"file_mode": "Dateimodus",
"message_retention_4_week": "4 Wochen",
"make_public": "Öffentlich machen",
"direct_link": "Mein Direktlink",
"voice_mode": "Sprachmodus",
"download_chat": "Chat herunterladen",
"message_retention": "Nachrichtenverlauf",
"message_retention": "History begrenzen",
"message_retention_none": "Aus",
"room_type": "Raumart",
"make_public_warning": "Achtung: der komplette Nachrichtenverlauf wird für neue Teilnehmer sichtbar",
"read_only_room_info": "Nur Admins und Moderatoren dürfen in diesen Raum senden.",
"message_retention_info": "Innerhalb dieses Zeitrahmens gesendete Nachrichten sind für alle mit dem Link sichtbar.",
"message_retention_info": "Festlegen eines Zeitlimits für die Aufbewahrung von Nachrichten",
"voice_mode_info": "Schaltet die Chat-Schnittstelle in den Modus 'Zuhören und Aufzeichnen'",
"file_mode_info": "Schaltet die Chat-Schnittstelle in den Modus 'Datei schicken'",
"shared_room_number": "Du teilst {count} Räume mit {name}",
"direct_link_desc": "Bereit zum Teilen! Jedes Mal, wenn jemand den Link öffnet, wird ein neuer Direktraum geöffnet.",
"shared_room_number_more": "Du teilst mehr als {count} Räume mit {name}"
"shared_room_number_more": "Du teilst mehr als {count} Räume mit {name}",
"message_history": "Nachrichtenverlauf",
"message_history_warning": "Achtung: der komplette Nachrichtenverlauf wird für neue Teilnehmer sichtbar",
"limit_history_info": "Nachrichten aufbewahren für {period}",
"message_history_info": "Erlaube Personen, Nachrichten zu sehen, die vor ihrem Beitritt gesendet wurden",
"report": "Bericht",
"report_reason": "Grund"
},
"room_info_sheet": {
"this_room": "Dieser Raum",
@ -350,7 +355,9 @@
"view_results": "Ergebnisse ansehen",
"answer_label_1": "Antwort*",
"please_complete": "Bitte vervollständigen",
"tip_title": "PRO-TIPP"
"tip_title": "PRO-TIPP",
"results_shared": "Die Ergebnisse werden dem Raum mitgeteilt.",
"tip_text": "Die Mitglieder sehen die Umfrageergebnisse, nachdem sie geantwortet haben. Schließe die Umfrage, wenn du fertig bist, um die Ergebnisse für alle im Raum anzuzeigen."
},
"export": {
"fetched_n_of_total_events": "{count} von {total} Ereignissen geladen",
@ -413,7 +420,8 @@
},
"title": "Neue Nachricht erhalten",
"blocked_message": "Benachrichtigung ist blockiert. Gehe zu den Einstellungen deines Geräts oder Browsers, um die Benachrichtigung zu aktivieren",
"periodicSync_new_msg_reminder": "Du hast vielleicht neue Nachrichten"
"periodicSync_new_msg_reminder": "Du hast vielleicht neue Nachrichten",
"not_supported": "Die Benachrichtigung wird in Mobile noch nicht unterstützt"
},
"getlink": {
"next": "Weiter",
@ -429,14 +437,33 @@
"ready_to_share": "Bereit zu teilen! Ein neuer Direktraum wird sich jedes Mal öffnen, wenn der Link geöffnet wird."
},
"createchannel": {
"name_required": "Kanalname ist erforderlich",
"channel_name": "Benenne deinen Kanal",
"channel_topic": "Beschreibe ihn",
"error_channel": "Einrichtung des Kanals fehlgeschlagen",
"title": "Einen Kanal einrichten",
"info": "Neuigkeiten oder Wissen in jedem Format verbreiten — als Video, Podcast, Text, Bild oder PDF."
"info": "Neuigkeiten oder Wissen in jedem Format verbreiten — als Video, Podcast, Text, Bild oder PDF.",
"status_creating": "Kanal erstellen",
"channel_topic_label": "Deine Beschreibung wird angezeigt, wenn Personen deinem Kanal beitreten."
},
"logout": {
"confirm_text": "Bist du sicher, dass du dich abmelden möchtest?"
},
"create": {
"room_type_channel_name": "Kanal",
"field_required": "Pflichtfeld",
"topic_too_long": "Maximal 500 Zeichen erlaubt",
"title": "Wähle eine Erfahrung",
"room_type_room_name": "Gruppen-Chat",
"room_type_room_description": "Verbinde eine Gruppe von Menschen",
"room_type_channel_description": "Nachrichten und Ideen verbreiten",
"room_type_filedrop_name": "Dateiablage",
"room_type_filedrop_description": "Erhalte Dateien und Tipps"
},
"createfiledrop": {
"title": "Erstelle eine Dateiablage",
"info": "Dateiablagen sind ein sicherer Ort, um Dateien von jedermann zu erhalten.",
"filedrop_name": "Benenne deine Dateiablage",
"error_filedrop": "Dateiablage konnte nicht erstellt werden",
"status_creating": "Erstelle Dateiablage"
}
}

View file

@ -150,11 +150,12 @@
"change": "Change"
},
"new_room": {
"title": "New Group Chat",
"new_room": "New Room",
"create": "Create",
"next": "Next",
"name_room": "Room name",
"room_topic": "Add a description if you like",
"room_topic": "Add a description if you'd like",
"join_permissions": "Join permissions",
"set_join_permissions": "Set Join Permissions",
"join_permissions_info": "These permissions determine how people can join the room and how easily others can be invited. They can be changed anytime.",
@ -166,8 +167,7 @@
"invite_info": "Only people added",
"invite_description": "Choose from a list or search by account ID",
"status_creating": "Creating room",
"status_avatar_total": "Uploading avatar: {count} of {total}",
"status_avatar": "Uploading avatar: {count}",
"status_avatar": "Uploading avatar",
"room_name_limit_error_msg": "Maximum 50 characters allowed",
"colon_not_allowed": "Colon is not allowed",
"options": "Options"
@ -212,13 +212,32 @@
"share_qr": "Share QR",
"qr_image_copied": "Image copied to clipboard"
},
"create": {
"title": "Choose an experience",
"room_type_room_name": "Group Chat",
"room_type_room_description": "Connect a group of people",
"room_type_channel_name": "Channel",
"room_type_channel_description": "Broadcast news and ideas",
"room_type_filedrop_name": "File Drop",
"room_type_filedrop_description": "Receive files and tips",
"field_required": "This field is required",
"topic_too_long": "Maximum 500 characters allowed"
},
"createchannel": {
"title": "Create a Channel",
"info": "Broadcast news or knowledge in any format—video, podcast, text, pictures or PDFs.",
"channel_name": "Name your channel",
"channel_topic": "Describe it",
"name_required": "Channel name is required",
"error_channel": "Failed to create channel"
"channel_topic_label": "Your description will be displayed when people join your channel.",
"error_channel": "Failed to create channel",
"status_creating": "Creating channel"
},
"createfiledrop": {
"title": "Create a File Drop",
"info": "File drops are a safe space to receive files from anyone.",
"filedrop_name": "Name your file drop",
"error_filedrop": "Failed to create file drop",
"status_creating": "Creating file drop"
},
"profile": {
"title": "My Profile",
@ -246,7 +265,8 @@
"logout": "Logout",
"want_more": "Want more?",
"powered_by": "This room is powered by {product}. Learn more at {productLink} or go ahead and create another room!",
"new_room": "New room"
"new_room": "New room",
"review_ua": "Review the Service User Agreement"
},
"join": {
"title": "Welcome you have been invited to join",
@ -262,7 +282,9 @@
"status_joining": "Joining room...",
"join_failed": "Failed to join room.",
"choose_name": "Choose a name to use",
"you_have_been_banned": "You have been banned from this room."
"you_have_been_banned": "You have been banned from this room.",
"accept_ua": "I agree to the {agreement}",
"ua": "service user agreement"
},
"invite": {
"title": "Add Friends",
@ -331,8 +353,9 @@
"download_chat": "Download chat",
"read_only_room": "Read Only",
"read_only_room_info": "Only admins and moderators are allowed to send to the room.",
"message_retention": "Message History",
"message_retention_info": "Messages sent within this time frame are viewable by anyone with the link.",
"message_retention": "Limit History",
"message_retention_info": "Set a limit for how long to keep messages",
"limit_history_info": "Keep messages for {period}",
"message_retention_none": "Off",
"message_retention_4_week": "4 weeks",
"message_retention_2_week": "2 weeks",
@ -340,12 +363,16 @@
"message_retention_1_day": "1 day",
"message_retention_8_hours": "8 hours",
"message_retention_1_hour": "1 Hour",
"make_public": "Make Public",
"make_public_warning": "warning: Full message history will be visible to new participants",
"direct_link": "My Direct Link",
"direct_link_desc": "It's ready to share! A new direct room will open each time someone opens the link.",
"shared_room_number": "You share {count} rooms with {name}",
"shared_room_number_more": "You share more than {count} rooms with {name}"
"shared_room_number_more": "You share more than {count} rooms with {name}",
"message_history": "Message History",
"message_history_info": "Allow people to see messages sent before they joined",
"message_history_warning": "warning: Full message history will be visible to new participants",
"report": "Report",
"report_info": "Report this room to service administrators for illegal, abusive or otherwise harmful content",
"report_reason": "Reason"
},
"room_info_sheet": {
"this_room": "This room",

View file

@ -28,7 +28,6 @@
"direct_link": "Mi enlace directo",
"read_only_room": "Solo lectura",
"voice_mode": "Por voz",
"make_public_warning": "Advertencia: El historial completo de mensajes será visible para los nuevos participantes",
"room_type": "Genero de la sala",
"experimental_features": "Funciones experimentales",
"file_mode_info": "Cambia la interfaz del chat al modo \"soltar archivos\"",
@ -36,7 +35,6 @@
"file_mode": "Modo del archivo",
"direct_link_desc": "¡Ya está listo para compartir! Se abrirá una nueva sala privada cada vez que alguien abra el enlace.",
"download_chat": "Descargar el chat",
"make_public": "Hacer público",
"voice_mode_info": "Cambia la interfaz del chat al modo \"escuchar y grabar\"",
"export_room": "Exportar el chat",
"message_retention_none": "Apagado",
@ -50,7 +48,11 @@
"shared_room_number_more": "Compartes más de {count} y salas con {name}",
"message_retention": "Historial de mensajes",
"message_retention_info": "Los mensajes enviados dentro de este plazo pueden ser vistos por cualquiera que tenga el enlace.",
"moderation": "Moderado"
"moderation": "Moderado",
"message_history_warning": "Advertencia: El historial completo de mensajes será visible para los nuevos participantes",
"message_history": "Historial de mensajes",
"report": "Informe",
"report_reason": "Razón"
},
"purge_room": {
"button": "Borrar",
@ -142,7 +144,6 @@
},
"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",
@ -440,7 +441,10 @@
"info": "Difunde noticias o conocimientos en cualquier formato: vídeo, podcast, texto, imágenes o PDF.",
"channel_name": "Nombre de tu canal",
"channel_topic": "Descríbelo",
"name_required": "El nombre del canal es obligatorio",
"error_channel": "Error al crear el canal"
},
"create": {
"room_type_channel_name": "Canal",
"field_required": "Este campo es obligatorio"
}
}

View file

@ -186,7 +186,6 @@
"public_info": "Cualquiera con un enlace",
"public_description": "Obtener un enlace para compartir",
"invite_info": "Sólo personas añadidas",
"status_avatar_total": "Subiendo avatar: {count} de {total}",
"status_avatar": "Subiendo avatar: {count}",
"invite_description": "Elegir de una lista o buscar por ID de cuenta",
"status_creating": "Creando sala",
@ -235,7 +234,6 @@
"qr_image_copied": "Imagen copiada al portapapeles"
},
"createchannel": {
"name_required": "Se requiere el nombre del canal",
"error_channel": "Falló al crear el canal",
"title": "Crear un Canal",
"info": "Transmite noticias o conocimientos en cualquier formato—video, podcast, texto, imágenes o PDFs.",
@ -362,12 +360,12 @@
"message_retention_1_day": "1 día",
"message_retention_8_hours": "8 horas",
"message_retention_1_hour": "1 hora",
"make_public": "Hacer Público",
"make_public_warning": "advertencia: El historial de mensajes completo será visible para nueva(o)s participantes",
"direct_link": "Mi Enlace Directo",
"direct_link_desc": "¡Está listo para compartir! Una nueva sala directa se abrirá cada vez que alguien abra el enlace.",
"shared_room_number": "Compartes {count} salas con {name}",
"shared_room_number_more": "Compartes más de {count} salas con {name}"
"shared_room_number_more": "Compartes más de {count} salas con {name}",
"message_history": "Historial de Mensajes",
"message_history_warning": "advertencia: El historial de mensajes completo será visible para nueva(o)s participantes"
},
"room_info_sheet": {
"this_room": "Esta sala",

View file

@ -138,11 +138,11 @@
"message_retention_1_day": "1 día",
"message_retention_8_hours": "8 horas",
"message_retention_1_hour": "1 hora",
"make_public": "Hacer público",
"make_public_warning": "Advertencia: el historial completo de mensajes será visible para los nuevos participantes.",
"direct_link_desc": "¡Está listo para compartir! Se abrirá una nueva sala directa cada vez que alguien abra el enlace.",
"shared_room_number": "Compartes {count} salas con {name}",
"shared_room_number_more": "Comparte más de{count}salas con {name}"
"shared_room_number_more": "Comparte más de{count}salas con {name}",
"message_history_warning": "Advertencia: el historial completo de mensajes será visible para los nuevos participantes.",
"message_history": "Historial de mensajes"
},
"export": {
"exported_date": "Exportado en {date}",
@ -260,7 +260,6 @@
"invite_info": "Solo se agregaron personas",
"invite_description": "Elige de una lista o busca por ID de cuenta",
"status_creating": "Creando sala",
"status_avatar_total": "Cargando avatar:{count} de {total}",
"status_avatar": "Cargando avatar: {count}",
"room_name_limit_error_msg": "Máximo 50 caracteres permitidos",
"colon_not_allowed": "No se permite el uso de dos puntos",
@ -311,7 +310,6 @@
"info": "Transmite noticias o conocimientos en cualquier formato: vídeo, podcast, texto, imágenes o archivos PDF.",
"channel_name": "Ponle nombre a tu canal",
"channel_topic": "Agrega descripción",
"name_required": "Tu canal necesita nombre",
"error_channel": "Falló la creación del canal"
},
"profile": {

View file

@ -107,7 +107,6 @@
"invite_info": "فقط افرادی که اضافه شده‌اند",
"invite_description": "از یک لیست انتخاب کنید یا با شناسه حساب جستجو کنید",
"status_creating": "ایجاد اتاق",
"status_avatar_total": "در حال بارگذاری آواتار: {count} از {total}",
"status_avatar": "در حال بارگذاری آواتار: {count}",
"room_name_limit_error_msg": "حداکثر 50 کاراکتر مجاز است",
"colon_not_allowed": "دو نقطه مجاز نیست",
@ -235,7 +234,6 @@
"info": "اخبار یا اطلاعات را در هر قالبی—ویدیو، پادکست، متن، تصاویر یا پی‌دی‌اف‌—پخش کنید.",
"channel_name": "نام کانال خود را وارد کنید",
"channel_topic": "آن را شرح دهید",
"name_required": "نام کانال الزامی است",
"error_channel": "ایجاد کانال ناموفق بود"
},
"profile": {
@ -351,12 +349,14 @@
"message_retention_1_day": "1 روز",
"message_retention_8_hours": "8 ساعت",
"message_retention_1_hour": "1 ساعت",
"make_public": "عمومی کنید",
"make_public_warning": "هشدار: تاریخچه کامل پیام‌ها برای شرکت‌کنندگان جدید قابل مشاهده خواهد بود.",
"direct_link": "لینک مستقیم من",
"direct_link_desc": "برای اشتراک‌گذاری آماده است! هر بار که کسی لینک را باز کند، یک اتاق مستقیم جدید باز خواهد شد.",
"shared_room_number": "شما {count} اتاق با نام {name} به اشتراک می‌گذارید",
"shared_room_number_more": "شما بیش از {count} اتاق با نام {name} به اشتراک می‌گذارید"
"shared_room_number_more": "شما بیش از {count} اتاق با نام {name} به اشتراک می‌گذارید",
"message_history_warning": "هشدار: تاریخچه کامل پیام‌ها برای شرکت‌کنندگان جدید قابل مشاهده خواهد بود.",
"message_history": "تاریخچه پیام",
"report_reason": "دلیل",
"report": "گزارش"
},
"room_info_sheet": {
"this_room": "این اتاق",
@ -440,5 +440,10 @@
"blocked_message": "اعلان مسدود شده است. به تنظیمات دستگاه یا مرورگر خود بروید تا اطلاعیه ها را فعال کنید.",
"not_supported": "اطلاعیه ها هنوز در موبایل پشتیبانی نمی‌شوند.",
"periodicSync_new_msg_reminder": "ممکن است پیام‌های جدیدی داشته باشید"
},
"create": {
"field_required": "این فیلد ضروری است",
"room_type_channel_name": "کانال",
"topic_too_long": "بیشینه ۵۰ نویسه مجاز است"
}
}

View file

@ -115,7 +115,6 @@
"invite_info": "تنها مردمانی که اضافه شده اند",
"invite_description": "از یک لیست انتخاب کنید یا با ID جستجو کنید",
"status_creating": "ایجاد اطاق",
"status_avatar_total": "در حال اپلود آواتار: {count} از {total}",
"status_avatar": "در حال اپلود آواتار: {count}",
"room_name_limit_error_msg": "حداکثر 50 حرف و عدد مجاز است",
"colon_not_allowed": "شارحه مجاز نیست",
@ -162,12 +161,12 @@
"message_retention_1_day": "1 روز",
"message_retention_8_hours": "8 ساعت",
"message_retention_1_hour": "1 ساعت",
"make_public": "عمومی کنید",
"make_public_warning": "هشدار: تاریخچه کامل پیغام‌ها برای شرکت‌کنندگان جدید قابل مشاهده خواهد بود",
"direct_link": "لینک مستقیم من",
"direct_link_desc": "برای اشتراک‌گذاری آماده است! هر بار که کسی لینک را باز کند، یک اطاق مستقیم جدید باز خواهد شد.",
"shared_room_number": "شما {count} اطاق با نام {name} به اشتراک می‌گذارید",
"shared_room_number_more": "شما بیش از {count} اطاق با نام {name} به اشتراک می‌گذارید"
"shared_room_number_more": "شما بیش از {count} اطاق با نام {name} به اشتراک می‌گذارید",
"message_history": "تاریخچه پیغام",
"message_history_warning": "هشدار: تاریخچه کامل پیغام‌ها برای شرکت‌کنندگان جدید قابل مشاهده خواهد بود"
},
"poll_create": {
"create_poll_menu_option": "ایجاد نظرسنجی",
@ -311,7 +310,6 @@
"info": "اخبار یا دانش را در هر قالبی—ویدیو، پادکست، متن، تصاویر یا PDFs—پخش کنید.",
"channel_name": "به کانال خود نام بدهید",
"channel_topic": "آن را شرح دهید",
"name_required": "نام کانال لازمی است",
"error_channel": "ایجاد کانال با شکست روبرو شد"
},
"profile": {

View file

@ -65,10 +65,11 @@
"password_required": "Salasana vaaditaan",
"create_room": "Rekisteröidy ja luo huone",
"accept_terms": "Hyväksy",
"token_not_valid": "Virheellinen koodi"
"token_not_valid": "Virheellinen koodi",
"send_verification": "Lähetä vahvistussähköposti"
},
"join": {
"title": "Tervetuloa huoneen {roomName}",
"title": "Tervetuloa, sinut on kutsuttu mukaan",
"user_name_label": "Käyttäjätunnus",
"joining_as": "Liityt jäsenenä:",
"join": "Liity huoneeseen",
@ -175,7 +176,9 @@
"message_retention_8_hours": "8 tuntia",
"message_retention_1_hour": "1 tunti",
"read_only_room": "Lue Ainoastaan",
"moderation": "Moderointi"
"moderation": "Moderointi",
"report": "Raportoi",
"report_reason": "Syy"
},
"power_level": {
"restricted": "rajoitettu",
@ -196,7 +199,11 @@
},
"notify": "Ilmoitus",
"show_less": "Näytä vähemmän",
"show_more": "Näytä lisää"
"show_more": "Näytä lisää",
"password_didnot_match": "Salasana ei täsmäänyt",
"password_hint": "Vähintään 12 merkkiä, jotka sisältävät vähintään yhden numeron, yhden ison ja yhden pienen kirjaimen",
"click_to_remove": "Napsauta poistaaksesi",
"add_reaction": "Lisää reaktio"
},
"poll_create": {
"create": "Julkaise",
@ -227,5 +234,13 @@
"sending_progress": "Lähetetään…",
"close": "Sulje",
"files": "Tiedostot"
},
"project": {
"name": "Kokoonnu",
"tag_line": "Yhdistä vain"
},
"create": {
"field_required": "Tämä kenttä on vaadittu",
"room_type_channel_name": "Kanava"
}
}

View file

@ -100,7 +100,6 @@
"public_description": "Obtenir un lien à partager",
"invite_info": "Seules les peronnes ajoutées",
"invite_description": "Choisissez dans une liste ou recherchez par identifiant de compte",
"status_avatar_total": "Téléversement davatar : {count} sur {total}",
"status_avatar": "Téléversement davatar : {count}",
"new_room": "Nouveau salon",
"create": "Créer",
@ -224,7 +223,9 @@
"message_retention_8_hours": "8 heures",
"message_retention_1_hour": "1 heure",
"read_only_room": "Lecture seule",
"moderation": "Modération"
"moderation": "Modération",
"report": "Signaler",
"report_reason": "Motif"
},
"room_info_sheet": {
"this_room": "Ce salon",
@ -293,5 +294,9 @@
"sending": "Envoi",
"close": "Fermer",
"files": "Fichier"
},
"create": {
"room_type_channel_name": "Canal",
"field_required": "Ce champ est requis"
}
}

View file

@ -119,7 +119,9 @@
"status_logging_in": "Logáil isteach...",
"status_joining": "Seomra a ghlacadh...",
"join_failed": "Theip ar theip ar an seomra.",
"choose_name": "Roghnaigh ainm le húsáid"
"choose_name": "Roghnaigh ainm le húsáid",
"accept_ua": "Aontaím leis an {agreement}",
"ua": "comhaontú úsáideora seirbhíse"
},
"invite": {
"title": "Cuir Cairde",
@ -172,8 +174,6 @@
"message_retention_1_week": "1 seachtain",
"message_retention_1_day": "1 lá",
"message_retention_8_hours": "8 uair an chloig",
"make_public": "Déan Phoiblí",
"make_public_warning": "rabhadh: Beidh stair iomlán teachtaireachtaí le feiceáil do rannpháirtithe",
"direct_link": "Mo Nasc Díreach",
"direct_link_desc": "Tá sé réidh le roinnt! Osclófar seomra díreach nua gach uair a osclaíonn duine an nasc.",
"shared_room_number": "Roinneann tú seomraí {count} le {name}",
@ -188,8 +188,15 @@
"download_chat": "Íoslódáil comhrá",
"read_only_room": "Léigh Amháin",
"read_only_room_info": "Ní cheadaítear ach riarthóirí agus modhnóirí a sheoladh chuig an seomra.",
"message_retention": "Stair Teachtaireachtaí",
"message_retention_info": "Is féidir le duine ar bith leis an nasc teachtaireachtaí a sheoltar laistigh den fhráma ama seo a fheiceáil."
"message_retention": "Stair Teorainn",
"message_retention_info": "Socraigh teorainn maidir le cé chomh fada agus is féidir teachtaireachtaí a choinneáil",
"message_history": "Stair Teachtaireachtaí",
"message_history_warning": "rabhadh: Beidh stair iomlán teachtaireachtaí le feiceáil do rannpháirtithe",
"limit_history_info": "Coinnigh teachtaireachtaí ar feadh {period}",
"message_history_info": "Lig do dhaoine teachtaireachtaí a seoladh a fheiceáil sula ndeachaigh siad isteach ann",
"report": "Tuairisc",
"report_reason": "Cúis",
"report_info": "Tuairiscigh an seomra seo do riarthóirí seirbhíse maidir le hábhar mídhleathach, maslach nó díobhálach eile"
},
"poll_create": {
"answer_label_1": "Freagra*",
@ -299,7 +306,7 @@
"delete_now": "Scrios anois",
"join": "Bí páirt",
"pin": "Post bioráin",
"unpin": "Unpin post",
"unpin": "Díphionnáil an postáil",
"ignore": "Neamhaird",
"loading": "Á lódáil {appName}",
"user_kick_and_ban": "Cuir amach",
@ -334,8 +341,7 @@
"status_creating": "Seomra a chruthú",
"next": "Ar Aghaidh",
"name_room": "Ainm an tseomra",
"status_avatar_total": "Avatar a uaslódáil: {count} de {total}",
"status_avatar": "Avatar a uaslódáil: {count}",
"status_avatar": "Avatar íoslódáil",
"room_name_limit_error_msg": "Uasmhéid 50 carachtar cead",
"colon_not_allowed": "Ní cheadaítear colon",
"options": "Roghanna",
@ -343,7 +349,8 @@
"join_permissions_info": "Cinneann na ceadanna seo conas is féidir le daoine dul isteach sa seomra agus cé chomh héasca is féidir cuireadh a thabhairt do dhaoine Is féidir iad a athrú am ar bith.",
"get_link": "Faigh nasc",
"add_people": "Cuir daoine leis",
"link_copied": "Nasc cóipeáilte!"
"link_copied": "Nasc cóipeáilte!",
"title": "Comhrá Grúpa Nua"
},
"device_list": {
"blocked": "Blocáilte",
@ -368,8 +375,9 @@
"info": "Craoladh nuacht nó eolas in aon fhormáid — físeán, podchraoladh, téacs, pictiúir nó PDFanna.",
"channel_name": "Ainmnigh do chainéal",
"channel_topic": "Déan cur síos air",
"name_required": "Tá ainm cainéal ag teastáil",
"error_channel": "Theip ar chainéal a chruthú"
"error_channel": "Theip ar chainéal a chruthú",
"status_creating": "Cainéal a chruthú",
"channel_topic_label": "Taispeánfar do chur síos nuair a théann daoine isteach i do chainéal."
},
"profile": {
"title": "Mo Phróifíl",
@ -397,7 +405,8 @@
"edit_profile": "Athraigh próifíl",
"logout": "Logáil amach",
"want_more": "Ag iarraidh níos mó?",
"powered_by": "Tá an seomra seo faoi thiomáint ag {product}. Foghlaim tuilleadh ag {productLink} nó téigh ar aghaidh agus cruthaigh seomra eile!"
"powered_by": "Tá an seomra seo faoi thiomáint ag {product}. Foghlaim tuilleadh ag {productLink} nó téigh ar aghaidh agus cruthaigh seomra eile!",
"review_ua": "Athbhreithniú a dhéanamh ar an gComhaontú Úsáideoir Seirbhíse"
},
"purge_room": {
"button": "Scrios",
@ -442,5 +451,23 @@
"fetched_n_of_total_events": "Fuarthas {count} de {total} imeacht",
"processed_n_of_total_events": "Meáin phróiseáilte le haghaidh {count} de {total} imeacht",
"export_filename": "Comhrá onnmhairithe {date}"
},
"create": {
"room_type_channel_name": "Cainéal",
"title": "Roghnaigh taithí",
"room_type_room_name": "Comhrá Grúpa",
"room_type_room_description": "Ceangail grúpa daoine",
"room_type_channel_description": "Craoladh nuacht agus smaointe",
"room_type_filedrop_name": "Buail Comhad",
"room_type_filedrop_description": "Faigh comhaid agus leideanna",
"field_required": "Tá an réimse seo ag teastáil",
"topic_too_long": "500 carachtar ar a mhéad a cheadaítear"
},
"createfiledrop": {
"title": "Cruthaigh Buail Comhad",
"info": "Is spás sábháilte iad titeann comhad chun comhaid a fháil ó aon duine.",
"error_filedrop": "Theip ar chruthú anuas comhaid",
"status_creating": "Titim comhaid a chruthú",
"filedrop_name": "Ainmnigh do chomhad anuas"
}
}

View file

@ -98,7 +98,6 @@
"link_copied": "Collegamento copiato!",
"invite_description": "Scegli da un elenco o cerca per identificativo di account",
"status_creating": "Creazione della stanza",
"status_avatar_total": "Caricamento dellavatar: {count} di {total}",
"status_avatar": "Caricamento dellavatar: {count}",
"create": "Crea",
"public_description": "Ottieni un collegamento da condividere",
@ -222,7 +221,9 @@
"read_only_room": "Sola lettura",
"moderation": "Moderazione",
"message_retention_1_week": "1 settimana",
"message_retention_2_week": "2 settimane"
"message_retention_2_week": "2 settimane",
"report": "Segnala",
"report_reason": "Motivo"
},
"voice_recorder": {
"failed_to_record": "Impossibile registrare laudio",
@ -295,5 +296,9 @@
"getlink": {
"next": "Prossimo",
"continue": "Continua"
},
"create": {
"room_type_channel_name": "Canale",
"field_required": "Questo campo è richiesto"
}
}

View file

@ -171,7 +171,6 @@
"invite_info": "تەنیا کەسانی زێدەکراو",
"invite_description": "لە لیستێک هەڵبژێرە یان بەپێی ناسنامەی ئەکاونت بگەڕێ",
"status_creating": "دروستکردنی ژوور",
"status_avatar_total": "بارکردنی ئاڤاتار: {count} لە {total}",
"status_avatar": "بارکردنی ئاڤاتار: {count}",
"room_name_limit_error_msg": "ئەوپەڕی 50 کاراکتەر ڕێگە دراوە",
"colon_not_allowed": "جووتخاڵ ڕێگە نەدراوە",
@ -222,7 +221,6 @@
"info": "هەواڵ یان زانیاری بە هەر فۆرماتێک — ڤیدیۆ، پۆدکاست، دەق، وێنە یان PDF.",
"channel_name": "ناوێک لە کەناڵەکەت دابنێ",
"channel_topic": "وەسفی بکە",
"name_required": "ناوی کەناڵ پێویستە",
"error_channel": "کەناڵ دروست نەکرا"
},
"profile": {
@ -340,12 +338,13 @@
"message_retention_1_day": "1 ڕۆژ",
"message_retention_8_hours": "8 کاتژمێر",
"message_retention_1_hour": "1 کاتژمێر",
"make_public": "بیکە بە گشتی",
"make_public_warning": "ئاگاداری: بەشداربووانی نوێ دەتوانن مێژووی تەواوی پەیامەکان ببینن",
"direct_link": "لینکی ڕاستەوخۆی من",
"direct_link_desc": "ئامادەیە بۆ هاوبەشکردن! هەر جارێک کەسێک لینکەکە دەکاتەوە ژوورێکی ڕاستەوخۆی نوێ دەکرێتەوە.",
"shared_room_number": "تۆ {count} ژوور هاوبەش دەکەیت لەگەڵ {name}",
"shared_room_number_more": "زیاتر لە {count} ژوور هاوبەش دەکەیت لەگەڵ {name}"
"shared_room_number_more": "زیاتر لە {count} ژوور هاوبەش دەکەیت لەگەڵ {name}",
"message_history_warning": "ئاگاداری: بەشداربووانی نوێ دەتوانن مێژووی تەواوی پەیامەکان ببینن",
"message_history": "مێژووی پەیام",
"report": "سکاڵا"
},
"goodbye": {
"room_deleted": "ژوورەکە سڕدرایەوە.",
@ -440,5 +439,8 @@
"send_more_files": "فایلی زیاتر بنێرە",
"close": "داخستن",
"files": "فایلەکان"
},
"create": {
"room_type_channel_name": "کەناڵ"
}
}

View file

@ -217,12 +217,12 @@
"message_retention_1_day": "1 ວັນ",
"message_retention_8_hours": "8 ຊົ່ວໂມງ",
"message_retention_1_hour": "1 ຊົ່ວໂມງ",
"make_public": "ເປີດເປັນສາທາລະນະ",
"make_public_warning": "ຄຳເຕືອນ: ຜູ້ເຂົ້າຮ່ວມໃໝ່ຈະສາມາດເບິ່ງປະຫວັດການສົນທະນາທັງໝົດໄດ້",
"direct_link": "ລິ້ງໂດຍກົງຂອງຂ້ອຍ",
"direct_link_desc": "ພ້ອມທີ່ຈະແບ່ງປັນແລ້ວ! ຫ້ອງໂດຍກົງຫ້ອງໃໝ່ຈະເປີດຂຶ້ນທຸກຄັ້ງທີ່ມີຄົນເປີດລິ້ງ.",
"shared_room_number": "ທ່ານໄດ້ແບ່ງປັນ {count} ຫ້ອງກັບ {name}",
"shared_room_number_more": "ທ່ານໄດ້ແບ່ງປັນຫຼາຍກວ່າ {count} ຫ້ອງກັບ {name}"
"shared_room_number_more": "ທ່ານໄດ້ແບ່ງປັນຫຼາຍກວ່າ {count} ຫ້ອງກັບ {name}",
"message_history": "ປະຫວັດການສົນທະນາ",
"message_history_warning": "ຄຳເຕືອນ: ຜູ້ເຂົ້າຮ່ວມໃໝ່ຈະສາມາດເບິ່ງປະຫວັດການສົນທະນາທັງໝົດໄດ້"
},
"fallbacks": {
"download_name": "ດາວໂຫຼດ",
@ -282,7 +282,6 @@
"invite_info": "ຄົນທີ່ຖືກເພີ່ມເຂົ້າເທົ່ານັ້ນ",
"invite_description": "ເລືອກຈາກລາຍການ ຫຼື ຄົ້ນຫາໂດຍ ID ບັນຊີ",
"status_creating": "ກຳລັງສ້າງຫ້ອງ",
"status_avatar_total": "ກຳລັງອັບໂຫຼດອາວະຕາ: {count} ຈາກ {total}",
"status_avatar": "ກຳລັງອັບໂຫຼດອາວະຕາ: {count}",
"room_name_limit_error_msg": "ອະນຸຍາດໃຫ້ໃຊ້ສູງສຸດ 50 ຕົວອັກສອນ",
"colon_not_allowed": "ບໍ່ອະນຸຍາດໃຫ້ໃຊ້ໝາຍສອງຈ້ຳ",
@ -326,7 +325,6 @@
"info": "ເຜີຍແຜ່ຂ່າວສານ ຫຼື ຄວາມຮູ້ໃໝ່ໃນຮູບແບບອື່ນໆ— ວີດີໂອ, ພັອດແຄຊ, ຂໍ້ຄວາມ, ຮູບພາບ ຫຼື PDF.",
"channel_name": "ໃສ່ຊື່ຊ່ອງຂອງທ່ານ",
"channel_topic": "ອະທິບາຍມັນ",
"name_required": "ຕ້ອງມີຊື່ຊ່ອງ",
"error_channel": "ການສ້າງຊ່ອງບໍ່ສຳເລັດ"
},
"profile_info_popup": {

View file

@ -120,7 +120,6 @@
"qr_image_copied": "clipboard တွင် ကူးယူထားသော ရုပ်ပုံ"
},
"createchannel": {
"name_required": "ချန်နယ်လ်အမည် လိုအပ်သည်",
"error_channel": "ချန်နယ်လ်ဖန်တီး၍ မရပါ",
"title": "ချန်နယ်လ်ဖွင့်ရန်",
"info": "သတင်း သို့မဟုတ် အသိပညာများကို ပုံစံမျိုးစုံဖြင့် ထုတ်လွှင့်ပါ—ဗီဒီယို၊ ပေါ့တ်ကာစ်၊ စာသား၊ ရုပ်ပုံများ သို့မဟုတ် PDF ဖိုင်များ။",
@ -172,7 +171,6 @@
"message_retention_1_day": "1 ရက်",
"message_retention_8_hours": "8 နာရီ",
"message_retention_1_hour": "1 နာရီ",
"make_public": "အများမြင်ပြုလုပ်မည်",
"copy_invite_link": "ဖိတ်ခေါ်လင့်ခ်ကို ကူးယူမည်",
"copy_link": "လင့်ခ်ကို ကူးယူမည်",
"link_copied": "လင့်ခ် ကူးပြီးပါပြီ",
@ -204,9 +202,11 @@
"message_retention_4_week": "4 ပတ်",
"direct_link": "ကျွန်ုပ်၏ တိုက်ရိုက်လင့်ခ်",
"direct_link_desc": "မျှဝေရန် အသင့်ဖြစ်ပါပြီ။ တစ်စုံတစ်ဦးက လင့်ခ်ကို ဖွင့်တိုင်း တိုက်ရိုက်အခန်းအသစ် ပွင့်ပါမည်။",
"make_public_warning": "သတိ- မက်ဆေ့ချ်မှတ်တမ်းအပြည့်အစုံကို ပါဝင်သူအသစ်များ မြင်နိုင်ပါမည်",
"shared_room_number": "သင်သည် {name} နှင့် {count} ခန်းကို မျှဝေသည်",
"shared_room_number_more": "သင်သည် {name} နှင့် {count} ခန်းထက်ပို၍ မျှဝေသည်"
"shared_room_number_more": "သင်သည် {name} နှင့် {count} ခန်းထက်ပို၍ မျှဝေသည်",
"message_history": "မက်ဆေ့ချ်မှတ်တမ်း",
"message_history_warning": "သတိ- မက်ဆေ့ချ်မှတ်တမ်းအပြည့်အစုံကို ပါဝင်သူအသစ်များ မြင်နိုင်ပါမည်",
"report": "တိုင်ကြားမည်"
},
"poll_create": {
"failed": "စစ်တမ်း ဖန်တီး၍ မရပါ၊ နောက်မှ ထပ်မံကြိုးစားကြည့်ပါ။",
@ -311,7 +311,6 @@
"invite_info": "ထည့်ထားသူများသာ",
"invite_description": "စာရင်းတစ်ခုမှ ရွေးမည် သို့မဟုတ် အကောင့်အိုင်ဒီဖြင့် ရှာမည်",
"status_creating": "အခန်းဖန်တီးနေသည်",
"status_avatar_total": "ကိုယ်စားပြုရုပ်ပုံတင်ခြင်း- {total} တွင် {count}",
"status_avatar": "ကိုယ်စားပြုရုပ်ပုံတင်ခြင်း- {count}",
"room_name_limit_error_msg": "အများဆုံးစာလုံးရေ 50 ခွင့်ပြုထားသည်",
"colon_not_allowed": "ကော်လံသုံး၍ မရပါ",

View file

@ -81,7 +81,9 @@
"message_retention_1_day": "1 dag",
"message_retention_8_hours": "åtte timer",
"message_retention_1_hour": "Én time",
"read_only_room": "Skrivebeskyttet"
"read_only_room": "Skrivebeskyttet",
"report": "Rapporter",
"report_reason": "Grunn"
},
"goodbye": {
"view_other_rooms": "Vis andre rom",
@ -226,5 +228,9 @@
"getlink": {
"next": "Neste",
"continue": "Fortsett"
},
"create": {
"room_type_channel_name": "Kanal",
"field_required": "Dette feltet er obligatorisk"
}
}

View file

@ -125,7 +125,6 @@
"invite_info": "یوازې اضافه شوي خلک",
"invite_description": "له یوه لیست څخه یې وټاکئ یا هم د ګڼون د ID له مخې یې وپلټئ",
"status_creating": "د خونې رامنځته کول",
"status_avatar_total": "اواتار اپلوډ کوي: {count} له {total} څخه",
"status_avatar": "اواتار اپلوډ کوي: {count}",
"room_name_limit_error_msg": "تر زیاتې کچې پورې 50 تورو ته اجازه شته",
"colon_not_allowed": "شارحې ته اجازه نشته"
@ -287,7 +286,6 @@
"info": "خبرونه یا پوهه په هر فارمټ - ویډیو، پاډکسټ، متن، انځورونه یا PDFs کې خپاره کړئ.",
"channel_name": "خپل چاینل ته نوم ورکړئ",
"channel_topic": "توضیح یې کړئ",
"name_required": "د چاینل نوم اړین ده",
"error_channel": "د چاینل جوړول تر سره نه شو"
},
"profile_info_popup": {
@ -392,12 +390,12 @@
"message_retention_1_day": "1 اوونۍ",
"message_retention_8_hours": "8 ساعتونه",
"message_retention_1_hour": "1 ساعت",
"make_public": "عمومي یې کړئ",
"make_public_warning": "خبرداری: د پیغام بشپړه تاریخچه به نویو ګډونوالو ته د لیدلو وړ وي",
"direct_link": "زما مستقیم لینک",
"direct_link_desc": "دا د شریکولو لپاره چمتو ده! هر کله چې یو کس دا لینک پرانیزي نو یوه نوې مستقیمه خونه به پرانیستل شي.",
"shared_room_number": "تاسې له {name} سره {count} خونې شریکې کړي دي",
"shared_room_number_more": "تاسې له {name} سره له {count} زیاتې خونې شریکې کړي دي"
"shared_room_number_more": "تاسې له {name} سره له {count} زیاتې خونې شریکې کړي دي",
"message_history": "د پیغام تاریخچه",
"message_history_warning": "خبرداری: د پیغام بشپړه تاریخچه به نویو ګډونوالو ته د لیدلو وړ وي"
},
"room_info_sheet": {
"this_room": "دغه خونه",

View file

@ -179,11 +179,11 @@
"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}",
"status_avatar": "Enviando avatar",
"room_name_limit_error_msg": "O máximo de 50 caracteres são permitidos",
"colon_not_allowed": "Dois pontos não são permitidos",
"options": "Opções"
"options": "Opções",
"title": "Novo bate-papo em grupo"
},
"device_list": {
"blocked": "Bloqueado",
@ -238,7 +238,8 @@
"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"
"new_room": "Nova sala",
"review_ua": "Analise o contrato de uso do serviço"
},
"join": {
"title": "Bem-vindo, você foi convidado a participar",
@ -254,7 +255,9 @@
"status_joining": "Entrando na sala...",
"join_failed": "Houve uma falha ao entrar na sala.",
"choose_name": "Escolha um nome para usar",
"you_have_been_banned": "Você foi banido desta sala."
"you_have_been_banned": "Você foi banido desta sala.",
"accept_ua": "Eu concordo com o {agreement}",
"ua": "contrato de serviço do usuário"
},
"leave": {
"title_public": "Adeus, {user}",
@ -311,10 +314,8 @@
"download_chat": "Baixar o chat",
"read_only_room_info": "Apenas os administradores e os moderadores podem postar na sala.",
"copy_link": "Copiar o link",
"make_public": "Tornar público",
"room_type": "Tipo de sala",
"file_mode_info": "Alterna a interface de bate-papo para um modo de 'baixa de arquivo'",
"make_public_warning": "aviso: o histórico completo das mensagens ficará visível para os novos participantes",
"room_type_default": "Padrão",
"file_mode": "Modo de arquivo",
"direct_link": "Meu link direto",
@ -325,12 +326,19 @@
"message_retention_1_week": "1 semana",
"message_retention_8_hours": "8 horas",
"message_retention_1_hour": "1 hora",
"message_retention": "Histórico da mensagem",
"message_retention_info": "As mensagens enviadas dentro desse período podem ser visualizadas por qualquer pessoa que tenha o link.",
"message_retention": "Histórico de limites",
"message_retention_info": "Estipule um limite de tempo para manter as mensagens",
"message_retention_4_week": "4 semanas",
"shared_room_number": "Você compartilha {count} quartos com {name}",
"shared_room_number_more": "Você compartilha mais de {count} quartos com {name}",
"moderation": "Moderação"
"moderation": "Moderação",
"message_history": "Histórico da mensagem",
"message_history_warning": "aviso: o histórico completo das mensagens ficará visível para os novos participantes",
"limit_history_info": "Manter as mensagens por {period}",
"message_history_info": "Permitir que as pessoas vejam as mensagens enviadas antes de se associarem",
"report": "Relatório",
"report_reason": "Motivo",
"report_info": "Denunciar esta sala aos administradores do serviço por conteúdo ilegal, abusivo ou prejudicial"
},
"room_info_sheet": {
"this_room": "Esta sala",
@ -437,10 +445,29 @@
},
"createchannel": {
"channel_topic": "Descreva-o",
"name_required": "O nome do canal é obrigatório",
"info": "Transmita notícias ou conhecimento em qualquer formato: vídeo, podcast, texto, imagens ou PDFs.",
"error_channel": "Houve uma falha ao criar o canal",
"title": "Criar um canal",
"channel_name": "Dê um nome ao seu canal"
"channel_name": "Dê um nome ao seu canal",
"channel_topic_label": "Sua descrição será exibida quando as pessoas entrarem no seu canal.",
"status_creating": "Criando canal"
},
"create": {
"room_type_channel_name": "Canal",
"field_required": "Este campo é obrigatório",
"title": "Escolha uma experiência",
"room_type_room_name": "Bate-papo em grupo",
"room_type_room_description": "Conectar um grupo de pessoas",
"room_type_channel_description": "Transmitir notícias e ideias",
"room_type_filedrop_name": "Entregar arquivo",
"room_type_filedrop_description": "Receber arquivos e dicas",
"topic_too_long": "O limite é de 500 caracteres"
},
"createfiledrop": {
"title": "Criar um file drop",
"info": "O File drops é um espaço seguro para receber arquivos de qualquer pessoa.",
"error_filedrop": "Houve uma falha ao criar o file drop",
"filedrop_name": "Nomeie o seu file drop",
"status_creating": "Criando um file drop"
}
}

View file

@ -64,7 +64,9 @@
"message_retention_none": "Dezactivat",
"message_retention_1_day": "1 zi",
"message_retention_1_hour": "O oră",
"message_retention_8_hours": "8 ore"
"message_retention_8_hours": "8 ore",
"report": "Raportați",
"report_reason": "Motiv"
},
"goodbye": {
"view_other_rooms": "Vezi alte camere",
@ -155,7 +157,6 @@
},
"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",
@ -285,5 +286,8 @@
"dialog": {
"enable": "Activează"
}
},
"create": {
"room_type_channel_name": "Channel"
}
}

View file

@ -60,26 +60,28 @@
"download_chat": "Скачать чат",
"read_only_room_info": "Отправлять сообщения в комнате могут только администраторы и модераторы.",
"message_retention_4_week": "4 недели",
"make_public_warning": "предупреждение: Вся история сообщений будет доступна новым участникам",
"shared_room_number": "Вы делите {count} комнат с {name}",
"file_mode_info": "Переключает интерфейс чата в режим \"передачи файлов\"",
"make_public": "Сделать общедоступной",
"direct_link_desc": "Она готова к обмену! Каждый раз, когда кто-то переходит по ссылке, будет открываться новая комната с личным чатом.",
"version_info": "Разработчик: Guardian Project. Версия: {version}",
"shared_room_number_more": "Вы делите более {count} комнат с {name}",
"experimental_features": "Экспериментальные возможности"
"experimental_features": "Экспериментальные возможности",
"message_history": "История сообщений",
"message_history_warning": "предупреждение: Вся история сообщений будет доступна новым участникам",
"report_reason": "Причина",
"report": "Сообщить об ошибке"
},
"file_mode": {
"sending": "Отправка",
"sending_progress": "Отправка...",
"close": "Закрыть",
"files": "Файлы",
"files_sent": "Отправлен {count} файл! | Отправлены {count} файлов! | Отправлены {count} файлов!",
"files_sent": "Отправлен 1 файл! | Отправлены {count} файлов!",
"any_file_format_accepted": "Принимаются файлы любого формата",
"send_more_files": "Отправить больше файлов",
"secure_file_send": "безопасная отправка файлов",
"add_a_message": "Добавить сообщение",
"files_sent_with_note": "Отправлен {count} файл с примечанием! | Отправлены {count} файлов с примечанием! | Отправлены {count} файлов с примечанием!",
"files_sent_with_note": "Отправлен 1 файл с примечанием! | Отправлены {count} файлов с примечанием!",
"choose_files": "Выбрать файлы"
},
"global": {
@ -163,7 +165,7 @@
"time_ago": "Сегодня | Вчера | {count} дней назад | {count} дней назад",
"not_allowed_to_send": "Только администраторы и модераторы могут отправлять сообщения в комнате",
"reaction_count_more": "{reactionCount} больше",
"seen_by_count": "Не просмотрено | Просмотрено {count} участником | Просмотрено {count} участниками | Просмотрено {count} участниками",
"seen_by_count": "Не просмотрено | Просмотрено 1 участником | Просмотрено {count} участниками",
"send_attachements_dialog_title": "Вы хотите отправить следующие вложения?",
"failed_to_render": "Не удалось обработать событие"
},
@ -186,7 +188,6 @@
"invite_info": "Только добавленные участники",
"invite_description": "Выберите из списка или выполните поиск по ID пользователя",
"status_creating": "Создание комнаты",
"status_avatar_total": "Загрузка аватара: {count} из {total}",
"status_avatar": "Загрузка аватара: {count}",
"room_name_limit_error_msg": "Не более 50 символов"
},
@ -245,13 +246,13 @@
"room_list_invites": "Приглашения",
"room_list_rooms": "Комнаты",
"unseen_messages": "У вас нет непросмотренных сообщений | У вас {count} непросмотренное сообщение | У вас {count} непросмотренных сообщений | У вас {count} непросмотренных сообщений",
"members": "нет участников | {count} участник | {count} участников | {count} участников",
"members": "нет участников | 1 участник | {count} участников",
"purge_set_room_state": "Установка статуса комнаты",
"purge_removing_members": "Удаление участников ({count} из {total})",
"purge_failed": "Не удалось очистить комнату!",
"room_name_required": "Название комнаты обязательно",
"room_topic_required": "Описание комнаты обязательно",
"invitations": "У вас нет приглашений | У вас {count} приглашение | У вас {count} приглашений | У вас {count} приглашений",
"invitations": "У вас нет приглашений | У вас 1 приглашение | У вас {count} приглашений",
"purge_redacting_events": "Удаление сообщений ({count} из {total})",
"room_list_new_messages": "{count} новых сообщений"
},
@ -431,7 +432,6 @@
"info": "Транслируйте новости или знания в любом формате видео, подкаст, текст, картинки или PDF-файлы.",
"channel_name": "Дайте название вашему каналу",
"channel_topic": "Опишите его",
"name_required": "Имя канала обязательно",
"error_channel": "Не удалось создать канал"
},
"export": {
@ -440,5 +440,9 @@
"export_filename": "Экспортированный чат {date}",
"processed_n_of_total_events": "Обработано {count} из {total} событий",
"exported_date": "Экспортировано в {date}"
},
"create": {
"room_type_channel_name": "Канал",
"field_required": "Поле обязательно"
}
}

View file

@ -38,7 +38,8 @@
"copy_link": "Copy link",
"message_retention_none": "ඕෆ් කරන්න",
"message_retention_1_day": "දවස් 1",
"members": "සාමාජිකයන්"
"members": "සාමාජිකයන්",
"report": "වාර්තා කරන්න"
},
"leave": {
"go_back": "Go back",
@ -119,5 +120,8 @@
},
"room_welcome": {
"change": "වෙනස් කරන්න"
},
"create": {
"room_type_channel_name": "නාලිකාව"
}
}

View file

@ -223,7 +223,6 @@
"invite_info": "Yalnızca eklenen kişiler",
"invite_description": "Listeden seçin veya hesap kimliği ile arayın",
"status_creating": "Oda oluşturuluyor",
"status_avatar_total": "Avatar yükleniyor {count}/{total}",
"status_avatar": "Avatar yükleniyor: {count}",
"room_name_limit_error_msg": "En fazla 50 karaktere izin veriliyor",
"colon_not_allowed": "Virgül kullanılamaz",
@ -331,12 +330,14 @@
"message_retention": "İleti Geçmişi",
"message_retention_info": "Bu zaman dilimi içinde gönderilen iletiler, bağlantıya sahip herkes tarafından görüntülenebilir.",
"message_retention_4_week": "4 hafta",
"make_public_warning": "uyarı: Tüm ileti geçmişi yeni katılımcılara görünür olacaktır",
"direct_link": "Benim Doğrudan Bağlantım",
"direct_link_desc": "Paylaşıma hazır! Bu bağlantıyı açan her kişi için yeni bir oda oluşturulacaktır.",
"shared_room_number": "{name} ile {count} odayı paylaşıyorsunuz",
"shared_room_number_more": "{name} ile {count} veya daha fazla odayı paylaşıyorsunuz",
"make_public": "Herkese Açık Yap"
"message_history": "İleti Geçmişi",
"message_history_warning": "uyarı: Tüm ileti geçmişi yeni katılımcılara görünür olacaktır",
"report": "Rapor",
"report_reason": "Sebep"
},
"room_info_sheet": {
"view_details": "Ayrıntıları görüntüle",
@ -423,7 +424,6 @@
"info": "Herhangi bir biçimde haber veya bilgi paylaşın, video, podcast, yazı, görsel veya PDF.",
"channel_name": "Kanalınıza isim verin",
"channel_topic": "Açıklayın",
"name_required": "Kanal adı gereklidir",
"error_channel": "Kanal oluşturulamadı"
},
"logout": {
@ -440,5 +440,9 @@
"fetched_n_of_total_events": "{total} eylemden {count}tanesi toplandı",
"processed_n_of_total_events": "{total} eylemden {count} tanesinin içeriği işlendi",
"export_filename": "İleti dışarı aktarıldı {date}"
},
"create": {
"room_type_channel_name": "Kanal",
"field_required": "Bu alan gereklidir"
}
}

View file

@ -67,7 +67,6 @@
"new_room": {
"link_copied": "ئۇلىنىش كۆچۈرۈلدى!",
"status_avatar": "باش سۈرىتى يۈكلىنىۋاتىدۇ: {count}",
"status_avatar_total": "باش سۈرىتىنى يۈكلەش: {count} نىڭ {total}",
"status_creating": "مۇنازىرە ئۆيى قۇرۇڭ",
"invite_description": "تىزىملىكتىن تاللاڭ ياكى ھېسابات كىملىكى ئارقىلىق ئىزدەڭ",
"invite_info": "پەقەت ئەزالارنى قوشۇش",
@ -167,7 +166,8 @@
"created_by": "{ئىشلەتكۈچى قۇرغان",
"title": "ياتاق تەپسىلاتلىرى",
"room_type_default": "كۆڭۈلدىكى",
"message_retention_none": "تاقاق"
"message_retention_none": "تاقاق",
"message_retention_8_hours": "8 سائەت"
},
"goodbye": {
"view_other_rooms": "باشقا ئۆيلەرنى كۆرۈڭ",
@ -230,7 +230,8 @@
},
"global": {
"save": "ساقلاش",
"close": "ياپماق"
"close": "ياپماق",
"notify": "ئۇقتۇر"
},
"emoji": {
"categories": {

View file

@ -36,14 +36,12 @@
"user_moderator": "版主",
"experimental_features": "实验功能",
"direct_link": "我的直接链接",
"make_public_warning": "警告:新参与者将可见完整的信息历史记录",
"room_type": "聊天室种类",
"file_mode_info": "将聊天界面切换到“文件投送”模式",
"room_type_default": "默认",
"file_mode": "文件模式",
"direct_link_desc": "已准备好分享了! 每次有人打开链接时,会打开一个新的直接聊天室。",
"copy_link": "复制链接",
"make_public": "公开",
"message_retention_none": "关闭",
"message_retention_1_day": "1 天",
"message_retention_8_hours": "8 小时",
@ -51,11 +49,18 @@
"message_retention_2_week": "2周",
"message_retention_1_week": "1周",
"moderation": "调节面板",
"message_retention_info": "有链接的任何人都可以查看在此时间范围内发送的信息。",
"message_retention_info": "设置信息保留时长限制",
"shared_room_number_more": "您与 {name} 共享 {count} 个以上的聊天室。",
"message_retention": "信息历史记录",
"message_retention": "限制历史记录",
"shared_room_number": "您与 {name} 共享 {count} 个聊天室。",
"message_retention_4_week": "4 周"
"message_retention_4_week": "4 周",
"message_history": "信息历史记录",
"message_history_warning": "警告:新参与者将可见完整的信息历史记录",
"report": "报告",
"report_reason": "理由",
"limit_history_info": "保留信息 {period}",
"message_history_info": "允许用户查看他们加入群聊之前发送的信息",
"report_info": "向服务管理员举报此房间存在非法、辱骂或其他有害内容"
},
"leave": {
"leave": "离开",
@ -162,7 +167,7 @@
"not_allowed_to_send": "只允许管理员和版主发送到聊天室",
"reaction_count_more": "{reactionCount} 更多",
"images": "图片",
"send_attachements_dialog_title": "您想发送以下附件吗?",
"send_attachements_dialog_title": "您想发送以下附件吗?",
"preparing_to_upload": "准备上传...",
"seen_by": "查看者",
"seen_by_count": "没有被群成员看到 |已被 1 位成员查看 | 已被 {count} 位成员查看",
@ -198,7 +203,9 @@
"user_revoke_moderator": "撤销版主身份",
"direct_chat": "私聊",
"user_kick_and_ban": "退出",
"delete_now": "立即删除"
"delete_now": "立即删除",
"pin": "置顶帖",
"unpin": "取消置顶帖子"
},
"power_level": {
"restricted": "被限制",
@ -249,7 +256,9 @@
"join_user": "开始聊天",
"enter_room_user": "开始聊天",
"choose_name": "选择要使用的名称",
"you_have_been_banned": "您已被禁止进入该聊天室。"
"you_have_been_banned": "您已被禁止进入该聊天室。",
"accept_ua": "我同意 {agreement}",
"ua": "服务使用协议"
},
"profile": {
"display_name": "显示名称",
@ -270,8 +279,7 @@
"notification_label": "通知"
},
"new_room": {
"status_avatar": "正在上传头像:{count}",
"status_avatar_total": "正在上传头像:{total}的 {count}",
"status_avatar": "正在上传头像",
"status_creating": "创建聊天室",
"invite_description": "从列表中选择或者按帐户 ID 搜索",
"invite_info": "仅添加人员",
@ -286,11 +294,12 @@
"name_room": "聊天室名称",
"next": "下一步",
"new_room": "新的聊天室",
"room_topic": "如果您愿意,请添加说明",
"room_topic": "如果您愿意,请添加描述",
"create": "创建",
"colon_not_allowed": "冒号是不允许的",
"options": "选项",
"room_name_limit_error_msg": "最多允许 50 个字符"
"room_name_limit_error_msg": "最多允许 50 个字符",
"title": "新群聊"
},
"room_welcome": {
"got_it": "知道了",
@ -318,7 +327,8 @@
"edit_profile": "编辑个人资料",
"identity_temporary": "{displayName}",
"identity": "{displayName}",
"you_are": "您是"
"you_are": "您是",
"review_ua": "查看服务用户协议"
},
"goodbye": {
"view_other_rooms": "查看其它聊天室",
@ -437,8 +447,27 @@
"title": "创建频道",
"info": "以任何格式广播新闻或知识—视频、播客、文本、图片或 PDF。",
"channel_topic": "描述一下",
"name_required": "频道名称必填",
"channel_name": "命名您的频道",
"error_channel": "创建频道失败"
"error_channel": "创建频道失败",
"channel_topic_label": "当有人加入您的频道时,将显示您的描述。",
"status_creating": "创建频道"
},
"create": {
"room_type_channel_name": "频道",
"field_required": "此项必填",
"room_type_room_description": "连接一群人",
"room_type_channel_description": "传播新闻和观点",
"room_type_filedrop_description": "接收文件和提示",
"topic_too_long": "最多允许 500 个字符",
"room_type_room_name": "群聊",
"title": "选择一种体验",
"room_type_filedrop_name": "文件投递"
},
"createfiledrop": {
"title": "创建文件投递",
"info": "文件投递是接收来自任何人的文件的安全空间。",
"filedrop_name": "命名您的文件投递",
"error_filedrop": "无法创建文件投递",
"status_creating": "正在创建文件投递"
}
}

View file

@ -30,6 +30,7 @@
v-on:remove-file="currentFileInputs.splice($event, 1)"
v-on:reset="resetAttachments"
:attachments="currentFileInputs"
v-on:close="closeFileMode"
/>
<div v-if="!useVoiceMode && !useFileModeNonAdmin" :class="{'chat-content': true, 'flex-grow-1': true, 'flex-shrink-1': true, 'invisible': !initialLoadDone}" ref="chatContainer"
@ -699,8 +700,7 @@ export default {
},
useFileModeNonAdmin: {
get: function() {
if (!this.$config.experimental_file_mode) return false;
return (util.roomDisplayTypeOverride(this.room) || this.roomDisplayType) === ROOM_TYPE_FILE_MODE && !this.canCreatePoll; // TODO - Check user or admin
return this.roomDisplayType === ROOM_TYPE_FILE_MODE && !this.canCreatePoll; // TODO - Check user or admin
}
},
@ -1556,6 +1556,7 @@ export default {
const text = withText || "";
const promise = this.sendAttachments(text, this.currentFileInputs);
promise.then(() => {
this.sendingAttachments = [];
this.currentFileInputs = null;
this.attachmentCaption = undefined;
this.sendingStatus = this.sendStatuses.INITIAL;
@ -1965,7 +1966,7 @@ export default {
let eventsToConsider = onlyTheseEvents;
if (!eventsToConsider) {
// If events to consider is not given (used by Audio layout) then consider all visible events (with some restrictions).
let visibleEventIds = util.findVisibleElements(this.chatContainer).filter(el => el.hasAttribute("eventId")).map(el => el.getAttribute("eventId"));
let visibleEventIds = (util.findVisibleElements(this.chatContainer) || []).filter(el => el.hasAttribute("eventId")).map(el => el.getAttribute("eventId"));
eventsToConsider = this.events.filter(e => {
if (e.getTs() > lastTimestamp &&
@ -2113,6 +2114,14 @@ export default {
}
}
}
},
closeFileMode() {
this.resetAttachments();
this.$matrix.leaveRoomAndNavigate(this.room.roomId)
.catch((err) => {
console.log("Error leaving", err);
});
}
},
};

135
src/components/Create.vue Normal file
View file

@ -0,0 +1,135 @@
<template>
<div class="create-root fill-height">
<div class="mt-4 d-flex flex-row align-center">
<v-btn id="btn-back" text class="back-button" v-show="$navigation && $navigation.canPop()" @click.stop="goBack">
<v-icon>arrow_back</v-icon>
</v-btn>
<YouAre class="flex-grow-1 text-end" v-if="loggedIn" />
</div>
<div class="mt-40 text-center px-4">
<div class="create-title no-icon h3-bold">{{ $t("create.title") }}</div>
<div class="room-type" @click="createRoom" v-if="$config.roomTypes.includes('group_chat')">
<div class="room-type__icon">
<v-icon>$vuetify.icons.room_type_room</v-icon>
</div>
<div class="room-type__name">{{ $t("create.room_type_room_name") }}</div>
<div class="room-type__description">{{ $t("create.room_type_room_description") }}</div>
</div>
<div class="room-type" @click="createChannel" v-if="$config.roomTypes.includes('channel')">
<div class="room-type__icon">
<v-icon>$vuetify.icons.room_type_channel</v-icon>
</div>
<div class="room-type__name">{{ $t("create.room_type_channel_name") }}</div>
<div class="room-type__description">{{ $t("create.room_type_channel_description") }}</div>
</div>
<div class="room-type" @click="createFiledrop" v-if="$config.roomTypes.includes('file_drop')">
<div class="room-type__icon">
<v-icon>$vuetify.icons.room_type_filedrop</v-icon>
</div>
<div class="room-type__name">{{ $t("create.room_type_filedrop_name") }}</div>
<div class="room-type__description">{{ $t("create.room_type_filedrop_description") }}</div>
</div>
</div>
</div>
</template>
<script>
import YouAre from "../components/YouAre.vue";
export default {
name: "Create",
components: { YouAre },
computed: {
loggedIn() {
return this.$store.state.auth.status.loggedIn;
},
},
methods: {
goBack() {
this.$navigation.pop();
},
createRoom() {
this.$navigation.push({ name: "CreateRoom" });
},
createChannel() {
this.$navigation.push({ name: "CreateChannel" });
},
createFiledrop() {
this.$navigation.push({ name: "CreateFileDrop" });
},
},
};
</script>
<style lang="scss">
@import "@/assets/css/typography.scss";
@import "@/assets/css/create.scss";
.create-title {
margin-bottom: 24 * $chat-text-size;
}
.room-type {
padding: 15 * $chat-text-size;
margin-bottom: 12 * $chat-text-size;
box-sizing: border-box;
position: relative;
user-select: none;
&::after {
content: " ";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 8 * $chat-text-size;
border: 1px solid #ededed;
}
&:active {
background: #efeffe;
}
&:hover::after {
border: 2px solid $very-very-purple;
}
&:active::after {
border: 1px solid $poll-hilite-color;
}
.room-type__icon svg {
color: transparent;
}
&:hover .room-type__icon svg, &:active .room-type__icon svg {
color: $very-very-purple;
}
.room-type__name {
color: black;
font-family: Inter;
font-size: 16 * $chat-text-size;
font-weight: 600;
line-height: 18.72 * $chat-text-size;
letter-spacing: 0.40 * $chat-text-size;
text-align: center;
text-underline-position: from-font;
text-decoration-skip-ink: none;
margin-top: 8 * $chat-text-size;
margin-bottom: 6 * $chat-text-size;
}
.room-type__description {
color: rgba(0, 0, 0, 0.65);
font-family: Inter;
font-size: 13 * $chat-text-size;
font-weight: 400;
line-height: 15.21 * $chat-text-size;
letter-spacing: 0.40 * $chat-text-size;
text-align: center;
text-underline-position: from-font;
text-decoration-skip-ink: none;
}
}
</style>

View file

@ -1,39 +1,46 @@
<template>
<div class="pa-4 create-root fill-height">
<YouAre v-if="loggedIn" class="mt-4" />
<div class="text-center">
<v-avatar class="create-image-small" size="50" color="#ededed" @click.stop="showAvatarPicker">
<v-img v-if="roomAvatar" :src="roomAvatar" />
<v-icon v-else>camera_alt</v-icon>
</v-avatar>
<div class="create-title">{{ $t("createchannel.title") }}</div>
<div class="create-root fill-height">
<div class="mt-4 d-flex flex-row align-center">
<v-btn id="btn-back" text class="back-button" v-show="$navigation && $navigation.canPop()" @click.stop="goBack"
:disabled="creatingRoom">
<v-icon>arrow_back</v-icon>
</v-btn>
<YouAre class="flex-grow-1 text-end" v-if="loggedIn" />
</div>
<div class="mt-40 px-4 text-center">
<CreateRoomAvatar v-model="roomAvatar" />
<div class="create-title h3-bold">{{ $t("createchannel.title") }}</div>
<div class="create-info">{{ $t("createchannel.info") }}</div>
<div color="rgba(255,255,255,0.1)" class="text-center">
<v-form v-model="isValid" ref="form">
<v-text-field v-model="roomName" :label="$t('createchannel.channel_name')" color="black"
background-color="white" solo :rules="[(v) => !!v || $t('createchannel.name_required')]" required
v-on:keydown="message = null"></v-text-field>
<v-text-field v-model="roomTopic" :label="$t('createchannel.channel_topic')" color="black"
background-color="#F5F5F5" filled v-on:keydown="message = null"></v-text-field>
<!-- <div class="error--text" v-if="loadingLoginFlows">Loading login flows...</div> -->
<div class="error--text" v-if="message != null">{{ this.message }}</div>
<v-form v-model="formIsValid">
<v-text-field v-model="roomName" :label="$t('createchannel.channel_name')" v-bind="{ ...roomNameInputFields }"
v-on="{ ...roomNameInputListeners }"></v-text-field>
<v-text-field v-model="roomTopic" :label="$t('createchannel.channel_topic')"
v-bind="{ ...roomTopicInputFields }" v-on="{ ...roomTopicInputListeners }"></v-text-field>
<div class="caption-2 text-center mt-2 mb-8">{{ $t('createchannel.channel_topic_label') }}</div>
<div class="error--text" v-if="errorMessage != null">{{ this.errorMessage }}</div>
<interactive-auth ref="interactiveAuth" />
<v-btn :disabled="!isValid || loading" color="primary" depressed block @click.stop="handleNext"
:loading="loading" class="filled-button mt-4">{{ $t("getlink.next") }}</v-btn>
<v-btn v-if="!loggedIn" color="black" depressed text block @click.stop="goToLoginPage" class="text-button">{{
$t("menu.login")
}}</v-btn>
<div class="create-buttons">
<!-- Create button -->
<v-btn :disabled="!formIsValid || creatingRoom" color="#6360F0" depressed @click.stop="handleNext"
class="filled-button mt-4">
<div v-if="creatingRoom" class="text-center">
{{ creatingRoomStatus }}
<v-progress-circular v-if="creatingRoom" :indeterminate="creatingRoomProgress == null"
:value="creatingRoomProgress" color="primary" width="2" size="20"></v-progress-circular>
</div>
<span v-else>{{ $t("getlink.next") }}</span>
</v-btn>
<v-btn v-if="!loggedIn" color="black" depressed text @click.stop="goToLoginPage" class="text-button">{{
$t("menu.login")
}}</v-btn>
</div>
</v-form>
</div>
</div>
<input id="room-avatar-picker" ref="avatar" type="file" name="avatar" @change="handlePickedAvatar($event)"
accept="image/*" class="d-none" />
<div :class="{ 'toast-at-bottom': true, 'visible': showQRCopiedToast }">{{ $t("getlink.qr_image_copied") }}</div>
</div>
</template>
@ -47,14 +54,19 @@ import util, { ROOM_TYPE_CHANNEL } from "../plugins/utils";
import YouAre from "../components/YouAre.vue";
import User from "../models/user";
import { CHANNEL_POWER_LEVELS } from "../services/matrix.service";
import createRoomMixin from "./createRoomMixin";
import CreateRoomAvatar from "./create/CreateRoomAvatar.vue";
export default {
name: "CreateChannel",
mixins: [rememberMeMixin, logoMixin],
components: { InteractiveAuth, YouAre },
mixins: [rememberMeMixin, logoMixin, createRoomMixin],
components: { InteractiveAuth, YouAre, CreateRoomAvatar },
data() {
return this.defaultData();
},
mounted() {
this.$store.commit("setCurrentRoomId", null);
},
computed: {
loggedIn() {
return this.$store.state.auth.status.loggedIn;
@ -63,53 +75,80 @@ export default {
return this.$store.state.auth.user;
},
},
watch: {
},
methods: {
defaultData() {
return {
roomName: "",
roomTopic: "",
isValid: false,
loading: false,
message: null,
currentLoginServer: "",
loadingLoginFlows: false,
loginFlows: null,
showQRCopiedToast: false,
roomAvatar: null,
roomAvatarFile: null,
};
},
goHome() {
this.$navigation.push({ name: "Home" }, -1);
},
goToLoginPage() {
this.$navigation.push({ name: "Login", params: { showCreateRoomOption: false, redirect: "GetLink" } }, 1);
this.$navigation.push({ name: "Login", params: { showCreateRoomOption: false, redirect: "CreateChannel" } }, 1);
},
handleNext() {
preCreateRoom() {
const prefix = this.$config.userIdPrefix;
const user = new User(util.randomUser(prefix), util.randomPass(), false);
// Reset errors
this.message = null;
this.loading = true;
const login = this.loggedIn ? Promise.resolve(this.currentUser) : this.loadLoginFlows().then(() => {
return this.$store.dispatch("createUser", { user, registrationFlowHandler: this.$refs.interactiveAuth.registrationFlowHandler })
});
login
.then(
(ignoreduser) => {
return this.createRoom();
})
.catch(
(ignorederror) => {
this.message = this.$t("createchannel.error_channel");
})
.finally(() => {
this.loading = false;
})
return login;
},
generateAliasForRoom() {
return true;
},
getRoomCreationOptions() {
const createRoomOptions = {
visibility: "private", // Not listed!
name: this.roomName,
preset: "public_chat",
initial_state:
[
{
type: "m.room.history_visibility",
state_key: "",
content: {
history_visibility: "shared"
}
},
{
type: "m.room.retention",
state_key: "",
content: {
max_lifetime: 3600 * 24 * 7 * 1000
}
}
]
};
if (this.roomTopic && this.roomTopic.length > 0) {
// Add topic
createRoomOptions.topic = this.roomTopic;
}
createRoomOptions.creation_content = {
type: ROOM_TYPE_CHANNEL
}
// Set power level event. Need to do that here, because we might not have the userId when the options object is created.
const powerLevels = {};
powerLevels[this.$matrix.currentUserId] = 100;
let powerLevelContent = {
users: powerLevels,
events_default: 50,
state_default: 50,
events: CHANNEL_POWER_LEVELS
}
createRoomOptions.initial_state.push(
{
type: "m.room.power_levels",
state_key: "",
content: powerLevelContent
});
return createRoomOptions;
},
handleNext() {
this.createRoom(this.$t("createchannel.status_creating"), this.$t("createchannel.error_channel"));
},
loadLoginFlows() {
var user = Object.assign({}, this.user);
@ -138,138 +177,11 @@ export default {
supportedLoginFlow(flow) {
return ["m.login.password"].includes(flow.type);
},
createRoom() {
const createRoomOptions = {
visibility: "private", // Not listed!
name: this.roomName,
preset: "public_chat",
initial_state:
[
// {
// type: "m.room.encryption",
// state_key: "",
// content: {
// algorithm: "m.megolm.v1.aes-sha2",
// },
// },
{
type: "m.room.history_visibility",
state_key: "",
content: {
history_visibility: "shared"
}
},
{
type: "m.room.retention",
state_key: "",
content: {
max_lifetime: 3600 * 24 * 7 * 1000
}
}
]
};
if (this.roomTopic && this.roomTopic.length > 0) {
// Add topic
createRoomOptions.topic = this.roomTopic;
}
createRoomOptions.creation_content = {
type: ROOM_TYPE_CHANNEL
}
let roomId = "";
return util
.getUniqueAliasForRoomName(
this.$matrix.matrixClient,
this.roomName,
this.$matrix.currentUserMXDomain
)
.then((alias) => {
createRoomOptions.room_alias_name = alias;
})
.then(() => {
// Set power level event. Need to do that here, because we might not have the userId when the options object is created.
const powerLevels = {};
powerLevels[this.$matrix.currentUserId] = 100;
let powerLevelContent = {
users: powerLevels,
events_default: 50,
state_default: 50,
events: CHANNEL_POWER_LEVELS
}
createRoomOptions.initial_state.push(
{
type: "m.room.power_levels",
state_key: "",
content: powerLevelContent
});
return this.$matrix.matrixClient
.createRoom(createRoomOptions)
.then(({ room_id, room_alias }) => {
roomId = room_alias || room_id;
if (!this.roomAvatarFile) {
return true;
}
const self = this;
return util.setRoomAvatar(
this.$matrix.matrixClient,
room_id,
this.roomAvatarFile,
(p) => {
if (p.total) {
self.status = this.$t("new_room.status_avatar_total", {
count: p.loaded || 0,
total: p.total,
});
} else {
self.status = this.$t("new_room.status_avatar", {
count: p.loaded || 0,
});
}
}
);
});
})
.then(() => {
this.$navigation.push(
{
name: "Chat",
params: { roomId: util.sanitizeRoomId(roomId) },
},
-1
);
})
},
/**
* Show picker to select room avatar file
*/
showAvatarPicker() {
this.$refs.avatar.click();
},
/**
* Handle picked avatar
*/
handlePickedAvatar(event) {
if (event.target.files && event.target.files[0]) {
var reader = new FileReader();
reader.onload = (e) => {
this.roomAvatar = e.target.result;
this.roomAvatarFile = event.target.files[0];
};
reader.readAsDataURL(event.target.files[0]);
}
},
},
};
</script>
<style lang="scss">
@import "@/assets/css/typography.scss";
@import "@/assets/css/create.scss";
</style>

View file

@ -0,0 +1,195 @@
<template>
<div class="create-root fill-height">
<div class="mt-4 d-flex flex-row align-center">
<v-btn id="btn-back" text class="back-button" v-show="$navigation && $navigation.canPop()" @click.stop="goBack"
:disabled="creatingRoom">
<v-icon>arrow_back</v-icon>
</v-btn>
<YouAre class="flex-grow-1 text-end" v-if="loggedIn" />
</div>
<div class="mt-40 px-4 text-center">
<CreateRoomAvatar v-model="roomAvatar" class="v-hidden" />
<div class="create-title h3-bold">{{ $t("createfiledrop.title") }}</div>
<div class="create-info">{{ $t("createfiledrop.info") }}</div>
<div color="rgba(255,255,255,0.1)" class="text-center">
<v-form v-model="formIsValid">
<v-text-field v-model="roomName" :label="$t('createfiledrop.filedrop_name')"
v-bind="{ ...roomNameInputFields }" v-on="{ ...roomNameInputListeners }"></v-text-field>
<div class="error--text" v-if="errorMessage != null">{{ this.errorMessage }}</div>
<interactive-auth ref="interactiveAuth" />
<div class="create-buttons">
<!-- Create button -->
<v-btn :disabled="!formIsValid || creatingRoom" color="#6360F0" depressed @click.stop="handleNext"
class="filled-button mt-4">
<div v-if="creatingRoom" class="text-center">
{{ creatingRoomStatus }}
<v-progress-circular v-if="creatingRoom" :indeterminate="creatingRoomProgress == null"
:value="creatingRoomProgress" color="primary" width="2" size="20"></v-progress-circular>
</div>
<span v-else>{{ $t("getlink.next") }}</span>
</v-btn>
<v-btn v-if="!loggedIn" color="black" depressed text @click.stop="goToLoginPage" class="text-button">{{
$t("menu.login")
}}</v-btn>
</div>
</v-form>
</div>
</div>
<div :class="{ 'toast-at-bottom': true, 'visible': showQRCopiedToast }">{{ $t("getlink.qr_image_copied") }}</div>
</div>
</template>
<script>
import rememberMeMixin from "./rememberMeMixin";
import * as sdk from "matrix-js-sdk";
import logoMixin from "./logoMixin";
import InteractiveAuth from './InteractiveAuth.vue';
import util, { ROOM_TYPE_FILE_MODE } from "../plugins/utils";
import YouAre from "../components/YouAre.vue";
import User from "../models/user";
import createRoomMixin from "./createRoomMixin";
import CreateRoomAvatar from "./create/CreateRoomAvatar.vue";
export default {
name: "CreateFileDrop",
mixins: [rememberMeMixin, logoMixin, createRoomMixin],
components: { InteractiveAuth, YouAre, CreateRoomAvatar },
data() {
return this.defaultData();
},
mounted() {
this.$store.commit("setCurrentRoomId", null);
},
computed: {
loggedIn() {
return this.$store.state.auth.status.loggedIn;
},
currentUser() {
return this.$store.state.auth.user;
},
},
methods: {
defaultData() {
return {
currentLoginServer: "",
loadingLoginFlows: false,
loginFlows: null,
showQRCopiedToast: false,
};
},
goToLoginPage() {
this.$navigation.push({ name: "Login", params: { showCreateRoomOption: false, redirect: "CreateFileDrop" } }, 1);
},
preCreateRoom() {
const prefix = this.$config.userIdPrefix;
const user = new User(util.randomUser(prefix), util.randomPass(), false);
const login = this.loggedIn ? Promise.resolve(this.currentUser) : this.loadLoginFlows().then(() => {
return this.$store.dispatch("createUser", { user, registrationFlowHandler: this.$refs.interactiveAuth.registrationFlowHandler })
});
return login;
},
generateAliasForRoom() {
return true;
},
getRoomCreationOptions() {
const createRoomOptions = {
visibility: "private", // Not listed!
name: this.roomName,
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"
}
},
{
type: "m.room.join_rules",
state_key: "",
content: {
join_rule: "public"
}
}
],
};
if (this.roomTopic && this.roomTopic.length > 0) {
// Add topic
createRoomOptions.topic = this.roomTopic;
}
createRoomOptions.creation_content = {
type: ROOM_TYPE_FILE_MODE
}
// Set power level event. Need to do that here, because we might not have the userId when the options object is created.
const powerLevels = {};
powerLevels[this.$matrix.currentUserId] = 100;
let powerLevelContent = {
users: powerLevels,
events_default: 0,
}
createRoomOptions.initial_state.push(
{
type: "m.room.power_levels",
state_key: "",
content: powerLevelContent
});
return createRoomOptions;
},
handleNext() {
this.createRoom(this.$t("createfiledrop.status_creating"), this.$t("createfiledrop.error_filedrop"));
},
loadLoginFlows() {
var user = Object.assign({}, this.user);
return util.getMatrixBaseUrl(user, this.$config)
.then((baseUrl) => {
if (baseUrl !== this.currentLoginServer) {
this.currentLoginServer = baseUrl;
this.loadingLoginFlows = true;
const matrixClient = sdk.createClient({ baseUrl: baseUrl });
return matrixClient.loginFlows().then((response) => {
console.log("FLOWS", response.flows);
this.loginFlows = response.flows.filter(this.supportedLoginFlow);
this.loadingLoginFlows = false;
if (this.loginFlows.length == 0) {
this.message = this.$t('login.no_supported_flow')
} else {
this.message = null;
}
});
} else {
return Promise.resolve();
}
})
},
supportedLoginFlow(flow) {
return ["m.login.password"].includes(flow.type);
},
},
};
</script>
<style lang="scss">
@import "@/assets/css/typography.scss";
@import "@/assets/css/create.scss";
</style>

View file

@ -1,112 +1,74 @@
<template>
<div class="create-room fill-height">
<div>
<v-container fluid>
<div class="room-name no-upper">{{ $t("new_room.new_room") }}</div>
<v-btn id="btn-back" text class="header-button-left" v-show="$navigation && $navigation.canPop()"
@click.stop="goBack" :disabled="step > steps.NAME_SET">
<v-icon>arrow_back</v-icon>
<span class="d-none d-sm-block">{{ $t("menu.back") }}</span>
</v-btn>
<!-- <v-btn
text
:disabled="
!roomName || (step != steps.INITIAL && step != steps.CREATED)
"
class="header-button-right"
@click.stop="onCreate"
>
<span>{{
step == steps.CREATED ? $t("new_room.done") : $t("new_room.next")
}}</span>
</v-btn> -->
</v-container>
<div class="create-root fill-height">
<div class="mt-4 d-flex flex-row align-center">
<v-btn id="btn-back" text class="back-button" v-show="$navigation && $navigation.canPop()" @click.stop="goBack"
:disabled="creatingRoom">
<v-icon>arrow_back</v-icon>
</v-btn>
<YouAre class="flex-grow-1 text-end" v-if="loggedIn" />
</div>
<v-container fluid class="mt-40 px-4 text-center">
<CreateRoomAvatar v-model="roomAvatar" />
<div class="create-title h3-bold">{{ $t("new_room.title") }}</div>
<div color="rgba(255,255,255,0.1)" class="text-center">
<v-form v-model="formIsValid">
<v-text-field v-model="roomName" :label="$t('new_room.name_room')" v-bind="{ ...roomNameInputFields }"
v-on="{ ...roomNameInputListeners }"></v-text-field>
<v-text-field v-model="roomTopic" :label="$t('new_room.room_topic')" v-bind="{ ...roomTopicInputFields }"
v-on="{ ...roomTopicInputListeners }"></v-text-field>
<v-container fluid class="mt-40">
<v-row align="center">
<v-col align="center">
<v-avatar size="50" color="#ededed" @click.stop="showAvatarPicker">
<v-img v-if="roomAvatar" :src="roomAvatar" />
<v-icon v-else>camera_alt</v-icon>
</v-avatar>
</v-col>
</v-row>
<v-row cols="12" align="center" justify="center">
<v-col sm="8" align="center">
<div class="text-start font-weight-light">{{ $t("new_room.name_room") }}</div>
<v-text-field v-model="roomName" color="black" :rules="roomNamerules" counter="50" maxlength="50"
background-color="white" v-on:keyup.enter="$refs.topic.focus()" :disabled="step > steps.INITIAL" autofocus
solo @update:error="updateErrorState"></v-text-field>
<div class="text-start font-weight-light" v-show="roomName.length > 0">{{ $t("new_room.room_topic") }}</div>
<v-textarea v-model="roomTopic" v-show="roomName.length > 0" ref="topic" color="black" background-color="white"
v-on:keydown.enter.prevent="
() => {
$refs.create.$el.focus()
}
" :disabled="step > steps.INITIAL" solo full-width auto-grow rows="1" no-resize hide-details></v-textarea>
<!-- Message History (public room) -->
<v-card class="ma-0 mt-10" flat>
<v-card-text class="with-right-label">
<div>
<div class="paragraph-bold">{{ $t('room_info.message_history') }}</div>
<div class="caption-1-gray">{{ $t('room_info.message_history_info') }}</div>
</div>
<v-switch class="ma-0" v-model="publicRoom"></v-switch>
</v-card-text>
<div class="option-warning" v-if="publicRoom"><v-icon size="18">$vuetify.icons.ic_warning</v-icon>{{
$t("room_info.message_history_warning") }}</div>
</v-card>
<!-- Check if we have any options enabled in config -->
<template v-if="$config.experimental_voice_mode || $config.experimental_read_only_room || $config.experimental_public_room || $config.experimental_file_mode">
<div @click.stop="showOptions = !showOptions" v-show="roomName.length > 0" class="options clickable">
<div>{{ $t("new_room.options") }}</div>
<v-icon v-if="!showOptions">expand_more</v-icon>
<v-icon v-else>expand_less</v-icon>
<hr color="#ededed" />
<!-- Limit History (message retention) -->
<v-card class="ma-0 mt-2" flat>
<v-card-text class="with-right-label">
<div>
<div class="paragraph-bold">{{ $t('room_info.message_retention') }}</div>
<div class="caption-1-gray">{{ limitHistory ? $t('room_info.limit_history_info', {period:
roomMessageRetentionDisplayString(limitHistoryMilliseconds)}) : $t('room_info.message_retention_info')
}}</div>
<div v-show="limitHistory" @click="showRetentionDialog"
class="paragraph paragraph-button mt-4 clickable">{{ $t('menu.edit') }}</div>
</div>
<v-switch class="ma-0" v-model="limitHistory"></v-switch>
</v-card-text>
</v-card>
<div class="error--text" v-if="errorMessage != null">{{ this.errorMessage }}</div>
<!-- Create button -->
<v-btn :disabled="!formIsValid || creatingRoom" color="#6360F0" depressed @click.stop="onCreate"
class="filled-button mt-4">
<div v-if="creatingRoom && !enterRoomDialog" class="text-center">
{{ creatingRoomStatus }}
<v-progress-circular v-if="creatingRoom" :indeterminate="creatingRoomProgress == null"
:value="creatingRoomProgress" color="primary" width="2" size="20"></v-progress-circular>
</div>
<v-card v-if="$config.experimental_public_room" v-show="showOptions" class="room-option account ma-0" flat>
<v-card-text class="with-right-label">
<div>
<div class="option-title">{{ $t('room_info.make_public') }}</div>
<!-- <div class="option-text">{{ $t('room_info.read_only_room_info') }}</div> -->
</div>
<v-switch v-model="unencryptedRoom"></v-switch>
</v-card-text>
<div class="option-warning" v-if="unencryptedRoom"><v-icon size="18">$vuetify.icons.ic_warning</v-icon>{{ $t("room_info.make_public_warning")}}</div>
</v-card>
<v-card v-if="availableRoomTypes.length > 1" v-show="showOptions" class="room-option account ma-0" flat>
<v-card-text>
<div class="d-flex flex-wrap text-left">
<div class="col-12 col-md-6 mr-auto pa-0">
<div class="option-title">{{ $t('room_info.room_type') }}</div>
</div>
<div class="col-12 col-md-6 pa-0">
<RoomTypeSelector v-model="roomType" />
</div>
</div>
</v-card-text>
</v-card>
<v-card v-if="$config.experimental_read_only_room" v-show="showOptions" class="room-option account ma-0" flat>
<v-card-text class="with-right-label">
<div>
<div class="option-title">{{ $t('room_info.read_only_room') }}</div>
<div class="option-text">{{ $t('room_info.read_only_room_info') }}</div>
</div>
<v-switch v-model="readOnlyRoom"></v-switch>
</v-card-text>
</v-card>
</template>
<div class="error--text" v-if="roomCreationErrorMsg"> {{ roomCreationErrorMsg }}</div>
<v-btn id="btn-room-create" color="black" depressed class="filled-button" @click.stop="onCreate"
:disabled="isDisabled" ref="create">
<div v-if="status && !enterRoomDialog" class="text-center">
{{ status }}
<v-progress-circular v-if="step == steps.CREATING" indeterminate color="primary"
size="20"></v-progress-circular>
</div>
<span v-else>{{ $t("new_room.create") }}</span>
<span v-else>{{ $t("getlink.next") }}</span>
</v-btn>
</v-col>
</v-row>
</v-form>
</div>
</v-container>
<interactive-auth ref="interactiveAuth" />
<input id="room-avatar-picker" ref="avatar" type="file" name="avatar" @change="handlePickedAvatar($event)"
accept="image/*" class="d-none" />
<input id="user-avatar-picker" ref="useravatar" type="file" name="user-avatar" @change="handlePickedUserAvatar($event)" accept="image/*" class="d-none" />
<input id="user-avatar-picker" ref="useravatar" type="file" name="user-avatar"
@change="handlePickedUserAvatar($event)" accept="image/*" class="d-none" />
<v-dialog v-model="enterRoomDialog" :width="$vuetify.breakpoint.smAndUp ? '50%' : '90%'">
<v-card>
@ -117,9 +79,8 @@
<v-select ref="avatar" :items="availableAvatars" cache-items outlined dense @change="selectAvatar"
:value="selectedProfile">
<template v-slot:selection>
<v-text-field background-color="transparent" solo flat hide-details
@click.native.stop="(event) => event.target.focus()"
@focus="$event.target.select()"
<v-text-field background-color="transparent" solo flat hide-details
@click.native.stop="(event) => event.target.focus()" @focus="$event.target.select()"
v-model="selectedProfile.name"></v-text-field>
</template>
<template v-slot:item="data">
@ -142,61 +103,48 @@
:label="$t('join.remember_me')" />
</v-col>
</v-row>
<v-row class="mt-0">
<v-col class="py-0">
<v-checkbox id="chk-accept-ua" class="mt-0" v-model="acceptUA">
<template slot="label">
<i18n path="join.accept_ua" tag="span">
<template v-slot:agreement>
<a href="./ua.html" target="_blank" @click.stop>{{ $t("join.ua") }}</a>
</template>
</i18n>
</template>
</v-checkbox>
</v-col>
</v-row>
<v-btn color="black" depressed class="filled-button" @click.stop="onEnterRoom"
:disabled="!selectedProfile.name">
:disabled="!acceptUA || !selectedProfile.name">
{{ $t("join.enter_room") }}
</v-btn>
</v-container>
</v-card>
</v-dialog>
<MessageRetentionDialog :initialValue="limitHistoryMilliseconds" :show="selectRetentionDialog"
v-on:close="selectRetentionDialog = false" v-on:message-retention-update="onUpdateHistoryLimit" />
</div>
</template>
<script>
import util, { ROOM_TYPE_DEFAULT } from "../plugins/utils";
import util from "../plugins/utils";
import InteractiveAuth from './InteractiveAuth.vue';
import rememberMeMixin from "./rememberMeMixin";
import roomTypeMixin from "./roomTypeMixin";
import RoomTypeSelector from './RoomTypeSelector.vue';
const steps = Object.freeze({
INITIAL: 0,
//NAME_SET: 1,
CREATING: 2,
CREATED: 3,
});
import YouAre from "../components/YouAre.vue";
import createRoomMixin from "./createRoomMixin";
import roomRetentionMixin from "./roomRetentionMixin";
import CreateRoomAvatar from "./create/CreateRoomAvatar.vue";
import MessageRetentionDialog from "./MessageRetentionDialog.vue";
export default {
name: "CreateRoom",
components: { InteractiveAuth, RoomTypeSelector },
mixins: [rememberMeMixin, roomTypeMixin],
components: { InteractiveAuth, YouAre, CreateRoomAvatar, MessageRetentionDialog },
mixins: [rememberMeMixin, createRoomMixin, roomRetentionMixin],
data() {
return {
steps,
step: steps.INITIAL,
roomId: null,
roomName: "",
roomTopic: "",
roomAvatar: null,
roomAvatarFile: null,
status: "",
joinRule: 0,
joinRules: [
{
id: "public",
text: this.$t("new_room.public_info"),
icon: "link",
descr: this.$t("new_room.public_description"),
},
{
id: "invite",
text: this.$t("new_room.invite_info"),
icon: "person_add",
descr: this.$t("new_room.invite_description"),
},
],
publicRoomLink: null,
publicRoomLinkCopied: false,
availableAvatars: [],
selectedProfile: {
id: "",
@ -204,21 +152,16 @@ export default {
name: ""
},
enterRoomDialog: false,
roomNamerules: [
v => v.length <= 50 || this.$t("new_room.room_name_limit_error_msg"),
v => !v.includes(':') || this.$t("new_room.colon_not_allowed")
],
roomNameHasError: false,
roomCreationErrorMsg: "",
showOptions: false,
unencryptedRoom: false,
readOnlyRoom: false,
roomType: ROOM_TYPE_DEFAULT,
selectRetentionDialog: false,
publicRoom: false,
limitHistory: false,
limitHistoryMilliseconds: 2 * 7 * 24 * 3600 * 1000, // 2 weeks default,
acceptUA: false
};
},
mounted() {
this.joinRule = this.joinRules[0].id; // Set default
this.$store.commit("setCurrentRoomId", null);
this.availableAvatars = util.getDefaultAvatars();
this.selectAvatar(
this.availableAvatars[
@ -227,21 +170,7 @@ export default {
);
},
watch: {
joinRule() {
console.log("Join rule changed to", this.joinRule);
},
},
computed: {
isDisabled() {
return this.status ? true : this.roomName.length === 0 || this.roomName.length > 50 || this.roomNameHasError
},
roomAvatarLetter() {
if (!this.roomName) {
return null;
}
return this.roomName.substring(0, 1).toUpperCase();
},
currentUser() {
return this.$store.state.auth.user;
},
@ -255,17 +184,6 @@ export default {
},
methods: {
updateErrorState(errorState) {
this.roomNameHasError = errorState
},
goBack() {
if (this.step == steps.NAME_SET) {
this.step = steps.INITIAL;
} else {
this.$navigation.pop();
}
},
onCreate() {
if (this.currentUser) {
this.onEnterRoom();
@ -276,105 +194,87 @@ export default {
onEnterRoom() {
this.enterRoomDialog = false;
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) {
// Create room with deafult setting
this.createRoom().then((roomId) => {
this.roomId = roomId;
this.openRoom(); // Open room (if id is set!)
});
}
this.createRoom(this.$t("new_room.status_creating"), undefined);
},
openRoom() {
if (this.roomId) {
this.$navigation.push(
{
name: "Chat",
params: { roomId: util.sanitizeRoomId(this.roomId) },
},
-1
);
}
},
getPublicLink() {
this.createRoom().then((roomId) => {
this.roomId = roomId;
var room = null;
if (roomId) {
room = this.$matrix.getRoom(roomId);
}
if (room) {
this.publicRoomLink = this.$router.getRoomLink(
room.getCanonicalAlias(), roomId, room.name
);
}
});
},
addPeople() {
// For now, jump straight to create
this.createRoom().then((roomId) => {
this.roomId = roomId;
this.$matrix.setCurrentRoomId(roomId);
this.$navigation.push(
{
name: "Invite",
},
1
);
});
},
createRoom() {
this.step = steps.CREATING;
preCreateRoom() {
const hasUser = this.currentUser ? true : false;
var setProfileData = false;
var roomId;
this.status = this.$t("new_room.status_creating");
return this.$matrix
.getLoginPromise(this.$refs.interactiveAuth.registrationFlowHandler)
.then((user) => {
if (user.is_guest && !hasUser) {
// Newly created account, joining first room.
// Set avatar and display name to either the randomly chosen ones, or the
// ones the users has changed to.
setProfileData = true;
// Set display name and avatar directly on the matrix object.
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
) {
return Promise.resolve(user);
} else {
console.log(
"CreateRoom: Set display name to: " + this.selectedProfile.name
);
return this.$matrix.setUserDisplayName(this.selectedProfile.name);
}
}
)
.then(() => {
if (!setProfileData || !this.selectedProfile.image) {
console.log("CreateRoom: No avatar change");
return Promise.resolve("no avatar");
} else {
console.log("CreateRoom: Updating avatar");
return util.setAvatar(
this.$matrix,
this.selectedProfile.image,
function (progress) {
console.log("Progress: " + JSON.stringify(progress));
}
);
}
}
);
},
generateAliasForRoom() {
return this.publicRoom;
},
getRoomCreationOptions() {
var createRoomOptions = {};
if (this.joinRule == "public") {
if (this.publicRoom) {
createRoomOptions = {
visibility: "private", // Not listed!
name: this.roomName,
preset: "public_chat",
initial_state:
this.unencryptedRoom ? [
{
type: "m.room.history_visibility",
state_key: "",
content: {
history_visibility: "shared"
initial_state: [
{
type: "m.room.history_visibility",
state_key: "",
content: {
history_visibility: "shared"
}
}
}
] :
[
{
type: "m.room.encryption",
state_key: "",
content: {
algorithm: "m.megolm.v1.aes-sha2",
},
},
{
type: "m.room.history_visibility",
state_key: "",
content: {
history_visibility: "joined"
}
},
],
]
};
} else {
//if (this.joinRule == "invite") {
createRoomOptions = {
visibility: "private", // Not listed!
name: this.roomName,
preset: "private_chat",
preset: "public_chat",
initial_state: [
{
type: "m.room.encryption",
@ -404,184 +304,32 @@ export default {
// Add topic
createRoomOptions.topic = this.roomTopic;
}
if (this.roomType != ROOM_TYPE_DEFAULT) {
createRoomOptions.creation_content = {
type: this.roomType
}
}
return this.$matrix
.getLoginPromise(this.$refs.interactiveAuth.registrationFlowHandler)
.then(
function (user) {
if (user.is_guest && !hasUser) {
// Newly created account, joining first room.
// Set avatar and display name to either the randomly chosen ones, or the
// ones the users has changed to.
setProfileData = true;
// Set display name and avatar directly on the matrix object.
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
) {
return Promise.resolve(user);
} else {
console.log(
"CreateRoom: Set display name to: " + this.selectedProfile.name
);
return this.$matrix.setUserDisplayName(this.selectedProfile.name);
}
}.bind(this)
)
.then(
function () {
if (!setProfileData || !this.selectedProfile.image) {
console.log("CreateRoom: No avatar change");
return Promise.resolve("no avatar");
} else {
console.log("CreateRoom: Updating avatar");
return util.setAvatar(
this.$matrix,
this.selectedProfile.image,
function (progress) {
console.log("Progress: " + JSON.stringify(progress));
}
);
}
}.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.currentUserMXDomain
)
.then((alias) => {
createRoomOptions.room_alias_name = alias;
});
} else {
return Promise.resolve(true);
// Limit history?
if (this.limitHistory && this.limitHistoryMilliseconds > 0) {
createRoomOptions.initial_state.push({
type: "m.room.retention",
state_key: "",
content: {
max_lifetime: this.limitHistoryMilliseconds
}
})
.then(() => {
// Set power level event. Need to do that here, because we might not have the userId when the options object is created.
const powerLevels = {};
powerLevels[this.$matrix.currentUserId] = 100;
let powerLevelContent = {
users: powerLevels,
events_default: this.readOnlyRoom ? 50 : 0
}
if (this.readOnlyRoom) {
powerLevelContent.events = {
"m.room.encrypted": 0, // NOTE! Since practically all events in encrypted rooms get sent as "m.room.encrypted" we need to set
// power to 0 here. Otherwise we would not be able to send quick reactions or poll responses...
"m.poll.response": 0,
"org.matrix.msc3381.poll.response": 0,
"m.reaction": 0,
"m.room.redaction": 0,
};
}
createRoomOptions.initial_state.push(
{
type: "m.room.power_levels",
state_key: "",
content: powerLevelContent
});
}
return this.$matrix.matrixClient
.createRoom(createRoomOptions)
.then(({ room_id, room_alias }) => {
roomId = room_alias || room_id;
if (!this.roomAvatarFile) {
return true;
}
const self = this;
return util.setRoomAvatar(
this.$matrix.matrixClient,
room_id,
this.roomAvatarFile,
(p) => {
if (p.total) {
self.status = this.$t("new_room.status_avatar_total", {
count: p.loaded || 0,
total: p.total,
});
} else {
self.status = this.$t("new_room.status_avatar", {
count: p.loaded || 0,
});
}
}
);
});
})
.then(() => {
this.status = "";
this.step = steps.CREATED;
return roomId;
})
.catch((error) => {
this.status = ""
this.roomCreationErrorMsg = (error.data && error.data.error) || error.message || error.toString();
this.step = steps.INITIAL; // revert
return null;
// Set power level event. Need to do that here, because we might not have the userId when the options object is created.
const powerLevels = {};
powerLevels[this.$matrix.currentUserId] = 100;
let powerLevelContent = {
users: powerLevels,
events_default: 0
}
createRoomOptions.initial_state.push(
{
type: "m.room.power_levels",
state_key: "",
content: powerLevelContent
});
},
/**
* Show picker to select room avatar file
*/
showAvatarPicker() {
if (this.step == steps.INITIAL) {
this.$refs.avatar.click();
}
},
/**
* Handle picked avatar
*/
handlePickedAvatar(event) {
if (event.target.files && event.target.files[0]) {
var reader = new FileReader();
reader.onload = (e) => {
this.roomAvatar = e.target.result;
this.roomAvatarFile = event.target.files[0];
};
reader.readAsDataURL(event.target.files[0]);
}
},
copyRoomLink() {
const self = this;
this.$copyText(this.publicRoomLink).then(
function (ignored) {
// Success!
self.publicRoomLinkCopied = true;
setInterval(() => {
// Hide again
self.publicRoomLinkCopied = false;
}, 3000);
},
function (e) {
// Failure!
console.log(e);
}
);
return createRoomOptions;
},
selectAvatar(value) {
@ -595,10 +343,8 @@ export default {
/**
* Show picker to select user avatar file
*/
showUserAvatarPicker() {
if (this.step == steps.INITIAL) {
this.$refs.useravatar.click();
}
showUserAvatarPicker() {
this.$refs.useravatar.click();
},
handlePickedUserAvatar(event) {
@ -606,10 +352,22 @@ export default {
this.selectedProfile.image = image;
});
},
showRetentionDialog() {
this.selectRetentionDialog = true;
},
onUpdateHistoryLimit(retention) {
this.limitHistoryMilliseconds = retention;
if (retention == 0) {
this.limitHistory = false;
}
}
}
};
</script>
<style lang="scss">
@import "@/assets/css/chat.scss";
@import "@/assets/css/create.scss";
</style>

View file

@ -2,13 +2,14 @@
<div class="pa-4 create-root fill-height">
<div v-if="!loggedIn" class="text-center">
<v-icon class="create-image">$vuetify.icons.getlink</v-icon>
<div class="create-title">{{ $t("getlink.title") }}</div>
<div class="create-title h3-bold">{{ $t("getlink.title") }}</div>
<div class="create-info">{{ $t("getlink.info") }}</div>
<div color="rgba(255,255,255,0.1)" class="text-center">
<v-form v-model="isValid" ref="form">
<v-text-field v-model="user.user_id" :label="$t('getlink.username')" color="black" background-color="white" solo
:rules="[(v) => !!v || $t('login.username_required')]" :error="userErrorMessage != null"
:error-messages="userErrorMessage" required v-on:keyup.enter="onUsernameEnter" v-on:keydown="hasError = false"></v-text-field>
<v-text-field v-model="user.user_id" :label="$t('getlink.username')" color="black" background-color="white"
solo :rules="[(v) => !!v || $t('login.username_required')]" :error="userErrorMessage != null"
:error-messages="userErrorMessage" required v-on:keyup.enter="onUsernameEnter"
v-on:keydown="hasError = false"></v-text-field>
<!-- <div class="error--text" v-if="loadingLoginFlows">Loading login flows...</div> -->
@ -16,16 +17,19 @@
<interactive-auth ref="interactiveAuth" />
<v-btn id="btn-login" :disabled="!isValid || loading" color="primary" depressed block @click.stop="handleLogin"
:loading="loading" class="filled-button mt-4">{{ $t("getlink.next") }}</v-btn>
<v-btn color="black" depressed text block @click.stop="goToLoginPage" class="text-button">{{ $t("menu.login")
}}</v-btn>
<div class="create-buttons">
<v-btn id="btn-login" :disabled="!isValid || loading" color="primary" depressed block
@click.stop="handleLogin" :loading="loading" class="filled-button mt-4">{{ $t("getlink.next") }}</v-btn>
<v-btn color="black" depressed text block @click.stop="goToLoginPage" class="text-button">{{
$t("menu.login")
}}</v-btn>
</div>
</v-form>
</div>
</div>
<div v-else style="position:relative" class="create-loggedin">
<!-- Logged in/account created -->
<div class="create-title">{{ $t("getlink.hello", { user: $matrix.currentUserDisplayName }) }}</div>
<div class="create-title h3-bold">{{ $t("getlink.hello", { user: $matrix.currentUserDisplayName }) }}</div>
<div class="create-subtitle">{{ $t("getlink.ready_to_share") }}</div>
<copy-link ref="qr" :locationLink="directMessageLink" i18nQrPopupTitleKey="getlink.scan_title"
v-on:long-tap="copyQRImage">
@ -37,7 +41,8 @@
</template>
</copy-link>
<div class="create-buttons">
<v-btn color="black" depressed @click.stop="goHome" class="outlined-button">{{ $t("getlink.continue") }}</v-btn>
<v-btn color="black" style="color:black;background-color:white !important;" depressed @click.stop="goHome"
class="outlined-button">{{ $t("getlink.continue") }}</v-btn>
<v-btn color="black" depressed text block @click.stop="getDifferentLink" class="text-button">{{
$t("getlink.different_link") }}</v-btn>
</div>
@ -243,5 +248,6 @@ export default {
</script>
<style lang="scss">
@import "@/assets/css/typography.scss";
@import "@/assets/css/create.scss";
</style>

View file

@ -1,6 +1,9 @@
<template>
<div class="home">
<YouAre class="mt-4" />
<div class="mt-4 d-flex flex-row align-center">
<YouAre class="flex-grow-1 text-end" />
</div>
<v-container fluid class="text-center mt-8">
<v-row align="center" justify="center">
<v-col class="text-center" cols="auto">
@ -64,7 +67,7 @@ export default {
},
createRoom() {
this.$navigation.push({ name: "CreateRoom" });
this.$navigation.push({ name: "Create" });
},
},
};

View file

@ -22,27 +22,12 @@
<v-row v-if="canEditProfile">
<v-col cols="10" sm="7" class="py-0">
<v-select
ref="avatar"
:items="availableAvatars"
cache-items
outlined
dense
@change="selectAvatar"
:value="selectedProfile"
single-line
autofocus
>
<v-select ref="avatar" :items="availableAvatars" cache-items outlined dense @change="selectAvatar"
:value="selectedProfile" single-line autofocus>
<template v-slot:selection>
<v-text-field
background-color="transparent"
solo
flat
hide-details
@click.native.stop="(event) => event.target.focus()"
@focus="$event.target.select()"
v-model="selectedProfile.name"
></v-text-field>
<v-text-field background-color="transparent" solo flat hide-details
@click.native.stop="(event) => event.target.focus()" @focus="$event.target.select()"
v-model="selectedProfile.name"></v-text-field>
</template>
<template v-slot:item="data">
<v-avatar size="32">
@ -51,13 +36,8 @@
<div class="ms-2">{{ data.item.name }}</div>
</template>
</v-select>
<v-checkbox
id="chk-remember-me"
class="mt-0"
v-model="rememberMe"
:label="$t('join.remember_me')"
@change="onRememberMe"
/>
<v-checkbox id="chk-remember-me" class="mt-0" v-model="rememberMe" :label="$t('join.remember_me')"
@change="onRememberMe" />
</v-col>
<v-col cols="2" sm="5" class="py-0">
<v-avatar @click="showAvatarPicker">
@ -78,29 +58,34 @@
</v-col>
</v-row>
<v-checkbox id="chk-accept-ua" class="mt-0" v-model="acceptUA">
<template slot="label">
<i18n path="join.accept_ua" tag="span">
<template v-slot:agreement>
<a href="./ua.html" target="_blank" @click.stop>{{ $t("join.ua") }}</a>
</template>
</i18n>
</template>
</v-checkbox>
<interactive-auth ref="interactiveAuth" />
<v-btn id="btn-join" class="btn-dark" :disabled="room && room.selfMembership == 'ban'" large @click.stop="handleJoin" :loading="loading" v-if="!currentUser">{{
<v-btn id="btn-join" class="btn-dark" :disabled="!acceptUA || (room && room.selfMembership == 'ban')" large
@click.stop="handleJoin" :loading="loading" v-if="!currentUser">{{
roomId && roomId.startsWith("@") ? $t("join.enter_room_user") : $t("join.enter_room")
}}</v-btn>
<v-btn id="btn-join" class="btn-dark" :disabled="room && room.selfMembership == 'ban'" large block @click.stop="handleJoin" :loading="loading" v-else>{{
}}</v-btn>
<v-btn id="btn-join" class="btn-dark" :disabled="!acceptUA || (room && room.selfMembership == 'ban')" large
block @click.stop="handleJoin" :loading="loading" v-else>{{
roomId && roomId.startsWith("@") ? $t("join.join_user") : $t("join.join")
}}</v-btn>
}}</v-btn>
<div v-if="loadingMessage" class="text-center">{{ loadingMessage }}</div>
<div v-if="room && room.selfMembership == 'ban'" class="text-center">{{ $t("join.you_have_been_banned") }}</div>
</div>
</div>
<input
id="room-avatar-picker"
ref="avatar"
type="file"
name="avatar"
@change="handlePickedAvatar($event)"
accept="image/*"
class="d-none"
/>
<input id="room-avatar-picker" ref="avatar" type="file" name="avatar" @change="handlePickedAvatar($event)"
accept="image/*" class="d-none" />
<div class="join-lang">
<h3 class="mb-2">{{ $t("profile.select_language") }}</h3>
@ -131,20 +116,12 @@
</div>
</div>
<!-- Loading indicator -->
<v-container
v-else
fluid
fill-height
class="loading-indicator transparent"
>
<!-- Loading indicator -->
<v-container v-else fluid fill-height class="loading-indicator transparent">
<v-row align="center" justify="center">
<v-col class="text-center">
<interactive-auth ref="interactiveAuth" v-if="waitingForRoomCreation" />
<v-progress-circular v-else
indeterminate
color="primary"
></v-progress-circular>
<v-progress-circular v-else indeterminate color="primary"></v-progress-circular>
</v-col>
</v-row>
</v-container>
@ -178,6 +155,7 @@ export default {
selectedProfile: null,
showEditDisplaynameDialog: false,
showSelectLanguageDialog: false,
acceptUA: false,
};
},
computed: {

View file

@ -222,7 +222,7 @@ export default {
}
},
handleCreateRoom() {
this.$navigation.push({ name: "CreateRoom" });
this.$navigation.push({ name: "Create" });
},
onUsernameEnter() {
this.$refs.password.focus();

View file

@ -1,6 +1,5 @@
<template>
<v-dialog v-model="showDialog" v-show="room" class="ma-0 pa-0"
:width="$vuetify.breakpoint.smAndUp ? '688px' : '95%'">
<v-dialog v-model="showDialog" v-show="room" class="ma-0 pa-0" :width="$vuetify.breakpoint.smAndUp ? '688px' : '95%'">
<div class="dialog-content text-center">
<template>
<h2>{{ $t("room_info.message_retention") }}</h2>
@ -10,8 +9,8 @@
<v-col cols="12">
<v-form v-model="isValid">
<v-radio-group v-model="retention" class="my-0 py-0">
<v-radio active-class="radio-active" class="flex-row-reverse mb-0" v-for="p in retentionPeriods" :key="p.text" :label="p.text"
:value="p.value" />
<v-radio active-class="radio-active" class="flex-row-reverse mb-0" v-for="p in retentionPeriods"
:key="p.text" :label="p.text" :value="p.value" />
</v-radio-group>
</v-form>
</v-col>
@ -40,6 +39,12 @@ export default {
return false;
},
},
initialValue: {
type: Number,
default: function () {
return 0;
},
},
},
data() {
return {
@ -71,23 +76,29 @@ export default {
methods: {
setInitialValue() {
this.isValid = true;
this.retention = this.roomMessageRetention();
if (this.room) {
this.retention = this.roomMessageRetention();
} else {
this.retention = this.initialValue;
}
},
setRetention() {
let ms = this.retention;
if (ms === 0 || !ms) {
// No expiry
this.$matrix.matrixClient.sendStateEvent(
this.room.roomId,
"m.room.retention",
{ max_lifetime: 0 }
);
} else if (ms >= 1000 * this.retentionMinValue && ms <= 1000 * this.retentionMaxValue) {
this.$matrix.matrixClient.sendStateEvent(
this.room.roomId,
"m.room.retention",
{ max_lifetime: ms }
);
if (this.room) {
if (ms === 0 || !ms) {
// No expiry
this.$matrix.matrixClient.sendStateEvent(
this.room.roomId,
"m.room.retention",
{ max_lifetime: 0 }
);
} else if (ms >= 1000 * this.retentionMinValue && ms <= 1000 * this.retentionMaxValue) {
this.$matrix.matrixClient.sendStateEvent(
this.room.roomId,
"m.room.retention",
{ max_lifetime: ms }
);
}
}
this.$emit('message-retention-update', ms)
this.showDialog = false;

View file

@ -56,6 +56,8 @@
</v-btn>
</div>
</div>
<a href="./ua.html" class="review-ua-link">{{ $t("profile_info_popup.review_ua") }}</a>
</v-card-text>
</v-card>
</v-dialog>
@ -108,7 +110,7 @@ export default {
},
createRoom() {
this.showDialog = false;
this.$navigation.push({ name: "CreateRoom" });
this.$navigation.push({ name: "Create" });
},
},
};
@ -198,6 +200,16 @@ export default {
}
}
.review-ua-link {
margin-top: 10px;
text-decoration: none;
color: #181719;
font-family: 'Inter', sans-serif;
font-style: normal;
font-weight: 400;
font-size: 10 * $chat-text-size !important;
}
@media #{map-get($display-breakpoints, 'sm-and-up')} {
width: 70%;
}

View file

@ -0,0 +1,98 @@
<template>
<v-dialog
v-model="showDialog"
v-show="room"
class="ma-0 pa-0"
:width="$vuetify.breakpoint.smAndUp ? '688px' : '95%'"
>
<div class="dialog-content text-center">
<h2 class="dialog-title">{{ $t("room_info.report") }}</h2>
<div class="dialog-text">{{ $t("room_info.report_info") }}</div>
<v-text-field v-model="reason" :label="$t('room_info.report_reason')"></v-text-field>
<v-container fluid>
<v-row cols="12">
<v-col cols="6">
<v-btn
id="btn-back"
depressed
text
block
class="text-button"
@click="showDialog = false"
>{{ $t("menu.cancel") }}</v-btn
>
</v-col>
<v-col cols="6" align="center">
<v-btn
id="btn-report"
color="red"
depressed
block
class="filled-button"
@click.stop="onReport()"
>{{ $t("room_info.report") }}</v-btn
>
</v-col>
</v-row>
</v-container>
</div>
</v-dialog>
</template>
<script>
import roomInfoMixin from "./roomInfoMixin";
export default {
name: "ReportRoomDialog",
mixins: [roomInfoMixin],
props: {
show: {
type: Boolean,
default: function () {
return false;
},
},
},
data() {
return {
showDialog: false,
reason: ""
};
},
watch: {
show: {
handler(newVal, ignoredOldVal) {
this.showDialog = newVal;
},
},
showDialog() {
if (!this.showDialog) {
this.$emit("close");
}
},
},
methods: {
onReport() {
const events = this.room.getLiveTimeline().getEvents();
if (events && events.length > 0) {
const eventId = events[events.length - 1].getId();
// const path = utils.encodeUri("/rooms/$roomId/report", {
// $roomId: this.room.roomId,
// });
// this.$matrix.matrixClient.http.authedRequest("POST", path, undefined, { reason: this.reason }, { prefix: "/_matrix/client/v3"})
this.$matrix.matrixClient.reportEvent(this.room.roomId, eventId, -100, this.reason)
.then(() => {
this.showDialog = false;
})
.catch((err) => {
console.log("Error reporting", err);
});
}
},
},
};
</script>
<style lang="scss">
@import "@/assets/css/chat.scss";
</style>

View file

@ -179,6 +179,22 @@
</v-card-text>
</v-card>
<v-card class="account ma-3" flat>
<v-card-title class="h2">{{ $t("room_info.report") }}</v-card-title>
<v-card-text class="">
<div class="with-right-label" style="align-items:center;height:36px">
<div class="option-title">{{ $t('room_info.report_info') }}</div>
<v-btn
color="black"
depressed
small
class="outlined-button"
@click.stop="report"
>{{ $t("room_info.report") }}</v-btn>
</div>
</v-card-text>
</v-card>
<v-card class="members ma-3" flat>
<v-card-title class="h2"
>{{ $t("room_info.members") }}<v-spacer></v-spacer>
@ -277,6 +293,12 @@
v-on:message-retention-update="onMessageRetention"
/>
<ReportRoomDialog
:show="showReportDialog"
:room="room"
@close="showReportDialog = false"
/>
<RoomExport :room="room" v-if="exporting" v-on:close="exporting = false" />
</div>
</template>
@ -285,6 +307,7 @@
import LeaveRoomDialog from "../components/LeaveRoomDialog";
import PurgeRoomDialog from "../components/PurgeRoomDialog";
import MessageRetentionDialog from "../components/MessageRetentionDialog";
import ReportRoomDialog from "../components/ReportRoomDialog";
import RoomExport from "../components/RoomExport";
import RoomAvatarPicker from "../components/RoomAvatarPicker";
import CopyLink from "../components/CopyLink.vue"
@ -300,6 +323,7 @@ export default {
LeaveRoomDialog,
PurgeRoomDialog,
MessageRetentionDialog,
ReportRoomDialog,
UserProfileDialog,
RoomExport,
RoomAvatarPicker,
@ -314,6 +338,7 @@ export default {
showLeaveConfirmation: false,
showPurgeConfirmation: false,
showMessageRetentionDialog: false,
showReportDialog: false,
buildVersion: "",
updatingJoinRule: false, // Flag if we are processing update curerntly
joinRules: [
@ -519,6 +544,9 @@ export default {
this.exporting = true;
}
},
report() {
this.showReportDialog = true;
},
/**
* Return true if we can change power levels in the room, i.e. make read only room
*/

View file

@ -39,7 +39,7 @@ export default {
createRoom() {
this.close();
this.$navigation.push({ name: "CreateRoom" });
this.$navigation.push({ name: "Create" });
}
}
};

View file

@ -187,7 +187,7 @@ export default {
},
roomChange(room) {
if (room == null || room == undefined) {
if (room == null || room == undefined || room == 0) {
// Ignore, this is caused by "new room" etc.
return;
}

View file

@ -1,5 +1,5 @@
<template>
<div class="d-flex flex-row-reverse">
<div>
<v-chip
@click="viewProfile"
class="ma-2 white-space-pre"

View file

@ -0,0 +1,78 @@
<template>
<div class="create-room-avatar" color="#ededed">
<v-img class="create-room-avatar__icon clickable" @click.stop="showRoomAvatarPicker" v-if="modelValue && modelValue.image" :src="modelValue.image" />
<v-icon class="create-room-avatar__icon default" v-else>$vuetify.icons.room_avatar_placeholder</v-icon>
<v-icon class="create-room-avatar__camera clickable" v-if="!modelValue || !modelValue.image" @click.stop="showRoomAvatarPicker">$vuetify.icons.ic_camera</v-icon>
<input id="room-avatar-picker" ref="roomAvatar" type="file" name="roomAvatar"
@change="handlePickedRoomAvatar($event)" accept="image/*" class="d-none" />
</div>
</template>
<script>
export default {
name: "CreateRoomAvatar",
model: {
prop: "modelValue",
event: "update:modelValue",
},
props: {
modelValue: {
type: Object,
default: function () {
return null;
},
},
},
methods: {
/**
* Show picker to select room avatar file
*/
showRoomAvatarPicker() {
this.$refs.roomAvatar.click();
},
/**
* Handle picked avatar
*/
handlePickedRoomAvatar(event) {
if (event.target.files && event.target.files[0]) {
var reader = new FileReader();
reader.onload = (e) => {
this.$emit('update:modelValue', {
image: e.target.result,
file: event.target.files[0]
});
};
reader.readAsDataURL(event.target.files[0]);
}
},
}
};
</script>
<style lang="scss">
@import "@/assets/css/create.scss";
.create-room-avatar {
width: 72px;
height: 72px;
position: relative;
margin-left: auto;
margin-right: auto;
.create-room-avatar__icon {
width: 100%;
height: 100%;
border-radius: 14px;
&.default {
background: $light-purple;
}
}
.create-room-avatar__camera {
position: absolute;
right: 0;
bottom: 0;
}
}
</style>

View file

@ -0,0 +1,155 @@
import util from "../plugins/utils";
export default {
data() {
return {
creatingRoom: false,
creatingRoomStatus: null,
creatingRoomProgress: null,
formIsValid: false,
roomName: "",
roomTopic: "",
roomAvatar: null,
roomNameRules: [
(v) => !!v || this.$t("create.field_required"),
(v) => !v || v.length <= 50 || this.$t("new_room.room_name_limit_error_msg"),
(v) => !v || !v.includes(":") || this.$t("new_room.colon_not_allowed"),
],
roomTopicRules: [(v) => !v || v.length <= 500 || this.$t("create.topic_too_long")],
errorMessage: null,
};
},
computed: {
roomNameInputFields() {
return {
color: "black",
rules: this.roomNameRules,
counter: "50",
maxLength: "50",
"background-color": "white",
disabled: this.creatingRoom,
//autofocus: true,
solo: true,
required: true,
};
},
roomNameInputListeners() {
return {
keydown: () => (this.errorMessage = null),
keyup: (e) => {
if (e.keyCode === 13 && this.$refs.topic) {
this.$refs.topic.focus();
}
},
};
},
roomTopicInputFields() {
return {
ref: "topic",
color: "black",
rules: this.roomTopicRules,
maxLength: "500",
disabled: this.creatingRoom,
filled: true,
rounded: true,
"hide-details": true,
};
},
roomTopicInputListeners() {
return {
keydown: () => (this.errorMessage = null),
keyup: (e) => {
if (e.keyCode === 13 && this.$refs.create) {
this.$refs.create.focus();
}
},
};
},
loggedIn() {
return this.$store.state.auth.status.loggedIn;
},
},
methods: {
goBack() {
if (!this.creatingRoom) {
this.$navigation.pop();
}
},
createRoom(creatingMessageString, errorMessageString) {
// Reset errors
this.errorMessage = null;
this.creatingRoom = true;
this.creatingRoomStatus = creatingMessageString;
this.creatingRoomProgress = null;
let roomId = "";
this.preCreateRoom()
.then(() => {
let createRoomOptions = this.getRoomCreationOptions();
if (this.generateAliasForRoom()) {
// Promise to get a unique alias and use it in room creation options.
//
return util
.getUniqueAliasForRoomName(
this.$matrix.matrixClient,
this.roomName,
this.$matrix.currentUserMXDomain
)
.then((alias) => {
createRoomOptions.room_alias_name = alias;
return createRoomOptions;
});
} else {
return Promise.resolve(createRoomOptions);
}
})
.then((createRoomOptions) => {
return this.$matrix.matrixClient
.createRoom(createRoomOptions)
.then(({ room_id }) => {
roomId = util.sanitizeRoomId(room_id);
if (createRoomOptions.room_alias_name) {
const parts = roomId.split(":");
if (parts && parts.length == 2) {
roomId = "#" + createRoomOptions.room_alias_name + ":" + parts[1];
}
}
if (!this.roomAvatar || !this.roomAvatar.file) {
return true;
}
return util.setRoomAvatar(
this.$matrix.matrixClient,
room_id,
this.roomAvatar.file,
(p) => {
this.creatingRoomStatus = this.$t("new_room.status_avatar");
if (p.total) {
this.creatingRoomProgress = (100 * p.loaded) / p.total;
} else {
this.creatingRoomProgress = null;
}
}
);
});
})
.then(() => {
this.creatingRoomProgress = null;
this.$navigation.push(
{
name: "Chat",
params: { roomId: roomId },
},
-1
);
})
.catch((error) => {
this.errorMessage = errorMessageString || (error.data && error.data.error) || error.message || error.toString();
})
.finally(() => {
this.creatingRoom = false;
this.creatingRoomStatus = null;
});
},
},
};

View file

@ -192,10 +192,7 @@ export default {
this.currentItemIndex = 0;
},
close() {
this.$matrix.leaveRoomAndNavigate(this.room.roomId)
.catch((err) => {
console.log("Error leaving", err);
});
this.$emit('close');
},
sendAll() {
this.status = this.mainStatuses.SENDING;

View file

@ -39,7 +39,7 @@
<script>
import MessageIncoming from "./MessageIncoming.vue";
import messageMixin from "./messageMixin";
import util, { ROOM_TYPE_CHANNEL } from "../../plugins/utils";
import util, { ROOM_TYPE_CHANNEL, ROOM_TYPE_FILE_MODE } from "../../plugins/utils";
import GalleryItemsView from '../file_mode/GalleryItemsView.vue';
import ThumbnailView from '../file_mode/ThumbnailView.vue';
import SwipeableThumbnailsView from "./channel/SwipeableThumbnailsView.vue";
@ -66,7 +66,7 @@ export default {
},
computed: {
forceMultiview() {
return this.room.displayType == ROOM_TYPE_CHANNEL && this.items.length == 1 && util.isFileTypePDF(this.items[0].event);
return this.room.displayType == ROOM_TYPE_FILE_MODE || (this.room.displayType == ROOM_TYPE_CHANNEL && this.items.length == 1 && util.isFileTypePDF(this.items[0].event));
}
},
methods: {

View file

@ -12,7 +12,7 @@
</template>
<span>{{ $t("global.click_to_remove") }}</span>
</v-tooltip>
<v-icon v-else class="ma-1 ml-0 clickable" @click="onClickEmoji(name)">$vuetify.icons.ic_like</v-icon> {{ value.length }}
<v-icon v-else class="ma-1 ml-0 clickable" @click="onClickEmoji(name)">$vuetify.icons.ic_like</v-icon> {{ $i18n.toLocalNumbers(value.length.toFixed()) }}
</div>
</div>
</template>
@ -45,6 +45,7 @@ export default {
}
},
mounted() {
console.log("I18", this.$i18n.toLocalNumbers);
this.reactions = this.timelineSet.relations.getChildEventsForEvent(this.event.getId(), 'm.annotation', 'm.reaction');
this.event.on("Event.relationsCreated", this.onRelationsCreated);
},

View file

@ -1,8 +1,9 @@
import utils, { ROOM_TYPE_CHANNEL } from "../plugins/utils";
import roomTypeMixin from "./roomTypeMixin";
import roomRetentionMixin from "./roomRetentionMixin";
export default {
mixins: [roomTypeMixin],
mixins: [roomTypeMixin, roomRetentionMixin],
data() {
return {
roomJoinRule: null,
@ -15,36 +16,6 @@ export default {
isRoomTopicEditMode: false,
roomTopicErrorMessage: null,
messageRetentionDisplay: "",
retentionPeriods: [
{
text: this.$t("room_info.message_retention_none"),
value: 0
},
{
text: this.$t("room_info.message_retention_4_week"),
value: 3600 * 24 * 28 * 1000
},
{
text: this.$t("room_info.message_retention_2_week"),
value: 3600 * 24 * 14 * 1000
},
{
text: this.$t("room_info.message_retention_1_week"),
value: 3600 * 24 * 7 * 1000
},
{
text: this.$t("room_info.message_retention_1_day"),
value: 3600 * 24 * 1000
},
{
text: this.$t("room_info.message_retention_8_hours"),
value: 3600 * 8 * 1000
},
{
text: this.$t("room_info.message_retention_1_hour"),
value: 3600 * 1000
}
]
};
},
mounted() {

View file

@ -0,0 +1,48 @@
export default {
data() {
return {
retentionPeriods: [
{
text: this.$t("room_info.message_retention_none"),
value: 0
},
{
text: this.$t("room_info.message_retention_4_week"),
value: 3600 * 24 * 28 * 1000
},
{
text: this.$t("room_info.message_retention_2_week"),
value: 3600 * 24 * 14 * 1000
},
{
text: this.$t("room_info.message_retention_1_week"),
value: 3600 * 24 * 7 * 1000
},
{
text: this.$t("room_info.message_retention_1_day"),
value: 3600 * 24 * 1000
},
{
text: this.$t("room_info.message_retention_8_hours"),
value: 3600 * 8 * 1000
},
{
text: this.$t("room_info.message_retention_1_hour"),
value: 3600 * 1000
}
]
};
},
methods: {
/**
* Get a string describing current room retention setting.
* Can be "None", "1 week", "1 hour" etc...
*/
roomMessageRetentionDisplayString(milliseconds) {
const retentionPeriodFound = this.retentionPeriods.find(rp=>rp.value===milliseconds)
if(retentionPeriodFound) {
return retentionPeriodFound.text
}
},
},
};

View file

@ -123,7 +123,6 @@ export default {
})
.then(() => {
this.sendingStatus = this.sendStatuses.SENT;
this.sendingAttachments = [];
this.sendingRootEventId = null;
})
.catch((err) => {

View file

@ -15,8 +15,7 @@ function importAll(r) {
}
importAll(require.context('@/assets/translations/', true, /\.json$/));
export default new VueI18n({
const vue18n = new VueI18n({
locale: 'en',
fallbackLocale: 'en',
silentFallbackWarn: true,
@ -51,4 +50,37 @@ export default new VueI18n({
return (choicesLength < 4) ? 2 : 3;
}
}
})
})
vue18n.toLocalNumbers = (str) => {
if (vue18n.locale == "my") {
// Translate to burmese numerals
var result = "";
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c >= 48 && c <= 57) {
result += String.fromCharCode(c + 0x1040 - 48);
} else {
result += String.fromCharCode(c);
}
}
return result;
} else if (vue18n.locale == "bo") {
// Translate to tibetan numerals
result = "";
for (i = 0; i < str.length; i++) {
c = str.charCodeAt(i);
if (c >= 48 && c <= 57) {
result += String.fromCharCode(c + 0x0f20 - 48);
} else {
result += String.fromCharCode(c);
}
}
return result;
}
return str;
};
export default vue18n;

View file

@ -7,7 +7,9 @@ import Login from '../components/Login.vue'
import Profile from '../components/Profile.vue'
import CreateRoom from '../components/CreateRoom.vue'
import GetLink from '../components/GetLink.vue'
import Create from '../components/Create.vue'
import CreateChannel from '../components/CreateChannel.vue'
import CreateFileDrop from '../components/CreateFileDrop.vue'
import User from '../models/user'
import util from '../plugins/utils'
@ -61,11 +63,21 @@ const routes = [
name: 'GetLink',
component: GetLink,
},
{
path: '/create',
name: 'Create',
component: Create,
},
{
path: '/createchannel',
name: 'CreateChannel',
component: CreateChannel,
},
{
path: '/createfiledrop',
name: 'CreateFileDrop',
component: CreateFileDrop,
},
{
path: '/login',
name: 'Login',
@ -103,7 +115,7 @@ const router = new VueRouter({
});
router.beforeEach((to, from, next) => {
const publicPages = ['/login', '/createroom', '/getlink', '/createchannel'];
const publicPages = ['/login', '/createroom', '/getlink', '/create', '/createchannel', '/createfiledrop'];
var authRequired = !publicPages.includes(to.path);
const loggedIn = router.app.$store.state.auth.user;
@ -126,10 +138,15 @@ router.beforeEach((to, from, next) => {
}
const roomId = util.sanitizeRoomId(to.params.roomId);
router.app.$matrix.setCurrentRoomId(roomId);
if (roomId && roomId.startsWith('#')) {
if (roomId) {
//Invite to public room
authRequired = false;
}
} else if (to.name == 'Home') {
if (to.params.roomId !== undefined) {
const roomId = to.params.roomId ? util.sanitizeRoomId(to.params.roomId) : null;
router.app.$matrix.setCurrentRoomId(roomId);
}
} else if (to.name == 'User') {
if (to.params.userId) {
let roomId = util.sanitizeUserId(to.params.userId);
@ -153,7 +170,23 @@ router.beforeEach((to, from, next) => {
}
} else if (to.name == 'CreateRoom') {
return router.app.$config.promise.then((config) => {
if (config.hide_add_room_on_home) {
if (config.hide_add_room_on_home || !config.roomTypes.includes("group_chat")) {
next('/');
} else {
next();
}
}).catch(err => { console.error(err); next('/'); });
} else if (to.name == 'CreateChannel') {
return router.app.$config.promise.then((config) => {
if (!config.roomTypes.includes("channel")) {
next('/');
} else {
next();
}
}).catch(err => { console.error(err); next('/'); });
} else if (to.name == 'CreateFileDrop') {
return router.app.$config.promise.then((config) => {
if (!config.roomTypes.includes("file_drop")) {
next('/');
} else {
next();

View file

@ -29,6 +29,14 @@ export default {
if (!json.maxSizeAutoDownloads) {
Vue.set(config, "maxSizeAutoDownloads", 10 * 1024 * 1024);
}
if (!json.roomTypes) {
let roomTypes = ["group_chat", "channel"];
const fileDropEnabled = (json.experimental_file_mode === undefined) ? true : !!json.experimental_file_mode;
if (fileDropEnabled) {
roomTypes.push("file_drop");
}
Vue.set(config, "roomTypes", roomTypes);
}
Vue.set(config, "loaded", true);
// Tell callback we are done loading runtime config

View file

@ -21,7 +21,7 @@ export default {
let script = `
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['trackPageView', ' ', window.location.hostname]);
_paq.push(['enableLinkTracking']);
(function() {
var u="${server}";

View file

@ -417,6 +417,14 @@ export default {
}
break;
case "m.room.canonical_alias":
{
if (this.currentRoomId && this.currentRoomId.startsWith("#") && !this.currentRoom) {
this.currentRoom = this.getRoom(this.currentRoomId);
}
}
break;
case STATE_EVENT_ROOM_DELETED:
{
const room = this.matrixClient.getRoom(event.getRoomId());