merge master and improvements
This commit is contained in:
commit
7444891b6d
21 changed files with 748 additions and 208 deletions
|
|
@ -7,15 +7,15 @@
|
|||
<link rel="icon" id="favicon" href="<%= BASE_URL %>favicon.ico" />
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<link rel="apple-touch-icon" href="<%= BASE_URL %>icons/icon-72x72.png" sizes="72x72" />
|
||||
<link rel="apple-touch-icon" href="<%= BASE_URL %>icons/icon-96x96.png" sizes="96x96" />
|
||||
<link rel="apple-touch-icon" href="<%= BASE_URL %>icons/icon-128x128.png" sizes="128x128" />
|
||||
<link rel="apple-touch-icon" href="<%= BASE_URL %>icons/icon-144x144.png" sizes="144x144" />
|
||||
<link rel="apple-touch-icon" href="<%= BASE_URL %>icons/icon-152x152.png" sizes="152x152" />
|
||||
<link rel="apple-touch-icon" href="<%= BASE_URL %>icons/icon-192x192.png" sizes="192x192" />
|
||||
<link rel="apple-touch-icon" href="<%= BASE_URL %>icons/icon-384x384.png" sizes="384x384" />
|
||||
<link rel="apple-touch-icon" href="<%= BASE_URL %>icons/icon-512x512.png" sizes="512x512" />
|
||||
<link rel="manifest" href="<%= BASE_URL %>manifest.json" />
|
||||
<link rel="apple-touch-icon" href="./icons/icon-72x72.png" sizes="72x72" />
|
||||
<link rel="apple-touch-icon" href="./icons/icon-96x96.png" sizes="96x96" />
|
||||
<link rel="apple-touch-icon" href="./icons/icon-128x128.png" sizes="128x128" />
|
||||
<link rel="apple-touch-icon" href="./icons/icon-144x144.png" sizes="144x144" />
|
||||
<link rel="apple-touch-icon" href="./icons/icon-152x152.png" sizes="152x152" />
|
||||
<link rel="apple-touch-icon" href="./icons/icon-192x192.png" sizes="192x192" />
|
||||
<link rel="apple-touch-icon" href="./icons/icon-384x384.png" sizes="384x384" />
|
||||
<link rel="apple-touch-icon" href="./icons/icon-512x512.png" sizes="512x512" />
|
||||
<link rel="manifest" href="./manifest.json" />
|
||||
<script src="./lottie-player.js"></script>
|
||||
<style>
|
||||
#loader {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"id": "/",
|
||||
"start_url": "/",
|
||||
"start_url": ".",
|
||||
"name": "Convene - Chat for everyone ",
|
||||
"short_name": "Convene",
|
||||
"theme_color": "#FFFFFF",
|
||||
|
|
@ -8,42 +7,42 @@
|
|||
"display": "standalone",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon-72x72.png",
|
||||
"src": "./icons/icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-96x96.png",
|
||||
"src": "./icons/icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-128x128.png",
|
||||
"src": "./icons/icon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-144x144.png",
|
||||
"src": "./icons/icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-152x152.png",
|
||||
"src": "./icons/icon-152x152.png",
|
||||
"sizes": "152x152",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-192x192.png",
|
||||
"src": "./icons/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-384x384.png",
|
||||
"src": "./icons/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-512x512.png",
|
||||
"src": "./icons/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
// Notification click event listener
|
||||
self.addEventListener("notificationclick", (e) => {
|
||||
// Close the notification popout
|
||||
e.notification.close();
|
||||
// Get all the Window clients
|
||||
e.waitUntil(
|
||||
clients.matchAll({ type: "window" }).then((clientsArr) => {
|
||||
// If a Window tab matching the targeted URL already exists, focus that;
|
||||
|
|
@ -26,9 +24,10 @@ async function checkNewMessages() {
|
|||
// Todo...
|
||||
}
|
||||
|
||||
// Install PWA in mobile or web to test if periodicSync notification works
|
||||
// see browser compatibility: https://developer.mozilla.org/en-US/docs/Web/API/Web_Periodic_Background_Synchronization_API#browser_compatibility
|
||||
self.addEventListener('periodicsync', (event) => {
|
||||
if (event.tag === 'check-new-messages') {
|
||||
// Test if periodicSync notification triggers in Mobile app(created via add to homescreen)
|
||||
self.registration.showNotification("Notification via periodicSync");
|
||||
|
||||
event.waitUntil(checkNewMessages());
|
||||
|
|
|
|||
|
|
@ -994,21 +994,12 @@ body {
|
|||
margin-left: 38px;
|
||||
}
|
||||
|
||||
.member::after {
|
||||
content: " ";
|
||||
display: block;
|
||||
margin: 10px 0px;
|
||||
bottom: 0px;
|
||||
height: 1px;
|
||||
background-color: #e1e1e1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.show-all {
|
||||
color: black;
|
||||
font-size: 14 * $chat-text-size;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
padding: 10px;
|
||||
[dir="rtl"] & {
|
||||
margin-left: initial;
|
||||
margin-right: 10px;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,16 @@
|
|||
"voice_mode": "སྐད་སྒྲའི་རྣམ་པ།",
|
||||
"voice_mode_info": "ཁ་བརྡའི་འབྲེལ་མཐུད་དེ་ཉན་པ་དང་སྒྲ་ཕབ་ཀྱི་རྣམ་པའི་ནང་དུ་བསྒྱུར།",
|
||||
"download_chat": "ཁ་བརྡ་ཕབ་ལེན།",
|
||||
"read_only_room": "ཁ་བརྡ་ཁང་དུ་ཀློག་མ་གཏོགས་མི་ཆོག"
|
||||
"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": "ཡོངས་ཁྱབ་བཟོས།"
|
||||
},
|
||||
"invite": {
|
||||
"done": "ལེགས་འགྲུབ།",
|
||||
|
|
@ -60,7 +69,10 @@
|
|||
"send_verification": "ར་སྤྲོད་ཡིག་ཟམ་ཐོངས།",
|
||||
"no_supported_flow": "བཀོལ་ཆས་འདི་སྤྲད་ཡོད་པའི་དྲ་བའི་ཞབས་ཞུ་ཆས་ནང་དུ་འཛུལ་ཐུབ་ཀྱི་མི་འདུག",
|
||||
"sent_verification": "{email}ཡིག་ཟམ་ཁ་བྱང་འདིའི་ཐོག་ཏུ་འཕྲིན་པ་ཞིག་བཏང་ཡོད། ཡིག་ཟམ་ནང་འཛུལ་ཏེ་སོ་སོའི་ཡིག་ཟམ་ཁ་བྱང་ར་སྤྲོད་བྱེད་རོགས།",
|
||||
"email_not_valid": "ཡིག་ཟམ་ཁ་བྱང་བེད་མེད་རེད་འདུག"
|
||||
"email_not_valid": "ཡིག་ཟམ་ཁ་བྱང་བེད་མེད་རེད་འདུག",
|
||||
"registration_token": "ཐོ་འགོད་མཚོན་རྟགས་གཏགས་རོགས།",
|
||||
"token_not_valid": "མཚོན་རྟགས་རྩིས་མེད།",
|
||||
"send_token": "མཚོན་རྟགས་ཐོངས།"
|
||||
},
|
||||
"new_room": {
|
||||
"next": "རྗེས་མ།",
|
||||
|
|
@ -140,7 +152,10 @@
|
|||
"got_it": "ཧ་གོ་སོང་།",
|
||||
"room_history_joined": "ཚོགས་མི་ཁག་ཁ་བརྡ་ཁང་དུ་ཞུགས་པའི་རྗེས་སུ། ད་གཟོད་དེའི་ནང་དུ་བཏང་ཡོད་པའི་འཕྲིན་ཐུང་ཁག་མཐོང་ཐུབ།",
|
||||
"room_history_is": "ཁ་བརྡ་ཁང་གི་ཟིན་ཐོ་ཁག {type}.",
|
||||
"encrypted": "འཕྲིན་ཐུང་ཁག་ལ་སྣེ་གཉིས་བར་གྱི་གསང་སྡོམ་བྱས་ཡོད།"
|
||||
"encrypted": "འཕྲིན་ཐུང་ཁག་ལ་སྣེ་གཉིས་བར་གྱི་གསང་སྡོམ་བྱས་ཡོད།",
|
||||
"no_past_messages": "དགའ་བསུ་ཞུ། ཁྱེད་ཀྱི་བདེ་འཇགས་ཀྱི་ཆེད་དུ། སྔོན་གྱི་ཆ་འཕྲིན་ཁག་ལྟ་ཀློག་མི་ཐུབ།",
|
||||
"direct_info": "བཀྲ་ཤིས་བདེ་ལེགས། {you} ཁྱེད་རང་{user}་དང་སྒེར་གྱི་ཁ་བརྡ་བྱེད་བཞིན་ཡོད།",
|
||||
"direct_private_chat": "ཐད་ཀའི་འཕྲིན་ཐུང་།"
|
||||
},
|
||||
"room": {
|
||||
"leave": "ཕྱིར་ཐོན།",
|
||||
|
|
@ -148,7 +163,7 @@
|
|||
"room_list_rooms": "ཁ་བརྡ་ཁང་།",
|
||||
"room_list_invites": "གདན་ཞུ་ཁག",
|
||||
"purge_failed": "ཁ་བརྡ་ཁང་བཤིག་ཐུབ་མ་སོང་།",
|
||||
"purge_removing_members": "ཚོགས་མི་ཁག་ཕྱིར་འདོན། ({total}་ཀྱི་({count})",
|
||||
"purge_removing_members": "ཚོགས་མི་ཁག་ཕྱིར་འདོན། {total})་ཀྱི་({members}",
|
||||
"purge_redacting_events": "ཁ་བརྡ་གཙང་གསུབ། {total})་ཀྱི་({count}",
|
||||
"purge_set_room_state": "ཁ་བརྡ་ཁང་གི་རྣམ་པ་སྒྲིག་འགོད།",
|
||||
"room_list_new_messages": "{count} ཆ་འཕྲིན་གསར་པ།",
|
||||
|
|
@ -201,14 +216,24 @@
|
|||
"incoming_message_deleted_text": "ཆ་འཕྲིན་འདི་བསུབས་ཟིན།",
|
||||
"reply_poll": "བསམ་ཚུལ་བསྡུ་ལེན།",
|
||||
"not_allowed_to_send": "དོ་དམ་པ་དང་གཙོ་སྐྱོང་བ་ཁོ་ནས་མ་གཏོགས་ཁ་བརྡ་ཁང་དུ་གཏོང་མི་ཆོག",
|
||||
"user_was_kicked_by_you": "ཁྱེད་ཀྱིས་{user}་འདི་ཁ་བརྡའི་ཁོངས་ནས་སྒོར་ཕུད་སོང་།",
|
||||
"user_was_kicked": "{user} ་འདི་ཁ་བརྡའི་ཁོངས་ནས་སྒོར་ཕུད་ཟིན།",
|
||||
"user_was_kicked_by_you": "ཁྱེད་ཀྱིས་{སྤྱོད་མཁན}་འདི་ཁ་བརྡའི་ཁོངས་ནས་སྒོར་ཕུད་སོང་།",
|
||||
"user_was_kicked": "{སྤྱོད་མཁན} ་འདི་ཁ་བརྡའི་ཁོངས་ནས་སྒོར་ཕུད་ཟིན།",
|
||||
"user_was_kicked_you": "ཁྱེད་རང་ཁ་བརྡའི་ཁོངས་ནས་སྒོར་ཕུད་སོང་།",
|
||||
"user_was_banned": "{user}་འདི་ཁ་བརྡའི་ཁོངས་ནས་བཀག་སྡོམ་བྱས་ཏེ་སྒོར་ཕུད་ཟིན།",
|
||||
"user_was_banned_by_you": "ཁྱེད་ཀྱིས་{user} ་འདི་ཁ་བརྡའི་ཁོངས་ནས་བཀག་སྡོམ་བྱས་ཏེ་སྒོར་ཕུད་ཟིན།",
|
||||
"time_ago": "དེ་རིང་། | ཁ་སང་། | ཉིན་གྲངས་{count} གོང་།",
|
||||
"outgoing_message_deleted_text": "ཁྱེད་ཀྱིས་ཆ་འཕྲིན་འདི་བསུབས་སོང་།",
|
||||
"reaction_count_more": "{reactionCount} མང་བ།"
|
||||
"reaction_count_more": "{reactionCount} མང་བ།",
|
||||
"images": "པར་རིས།",
|
||||
"send_attachements_dialog_title": "ཁྱེད་ཀྱིས་གཤམ་གྱི་ཟུར་སྣོན་འདི་དག་གཏོང་འདོད་དམ།",
|
||||
"preparing_to_upload": "ཡར་འཇུག་བྱེད་པར་གྲ་སྒྲིག་བྱེད་བཞིན་པ...",
|
||||
"seen_by": "མཐོང་མཁན།",
|
||||
"seen_by_count": "ཚོགས་མི་སུས་ཀྱང་མཐོང་མི་འདུག| ཚོགས་མི་གཅིག་གིས་མཐོང་འདུག ཚོགས་མི་{count}་མཐོང་འདུག",
|
||||
"files": "ཡིག་ཆ་ཁག",
|
||||
"download_all": "ཆ་ཚང་ཕབ་ལེན།",
|
||||
"file": "ཡིག་ཆ།",
|
||||
"upload_file_too_large": "ཡར་འཇུག་བྱ་འདོད་ཀྱི་ཡིག་ཆ་ཆེ་དྲགས་འདུག",
|
||||
"upload_exceeded_file_limit": "({configFormattedUploadSize}) ་ཡི་ཡིག་ཆ་ཆེ་ཤོས་ཀྱི་ཆེ་ཆུང་གི་ཚད་གཞི་ལས་བརྒལ་འདུག "
|
||||
},
|
||||
"power_level": {
|
||||
"moderator": "མདོ་འཛིན་པ།",
|
||||
|
|
@ -261,7 +286,8 @@
|
|||
"join_user": "ཁ་བརྡ་འགོ་རྩོམ།",
|
||||
"enter_room_user": "ཁ་བརྡ་འགོ་རྩོམ།",
|
||||
"choose_name": "མིང་ཞིག་འདེམས་ཏེ་བཀོལ།",
|
||||
"title_user": "དགའ་བསུ་ཞུ། ཁྱེད་རང་ཁ་བརྡ་བྱེད་པར་གདན་ཞུ་གནང་སོང་།"
|
||||
"title_user": "དགའ་བསུ་ཞུ། ཁྱེད་རང་ཁ་བརྡ་བྱེད་པར་གདན་ཞུ་གནང་སོང་།",
|
||||
"you_have_been_banned": "ཁྱེད་རང་ཚོགས་པ་འདིའི་ནང་ནས་བཀག་འདུག"
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"powered_by": "ཁ་བརྡ་ཁང་འདི་{product} ནུས་ཤུགས་བསྩལ་ཡོད། {productLink} ནས་དེ་ལས་མང་བ་སྦྱོང་ཆོག་ལ། མདུན་དུ་བསྐྱོད་དེ་ཁ་བརྡ་ཁང་གཞན་ཞིག་བསྐྲུན་ཆོག",
|
||||
|
|
@ -282,11 +308,19 @@
|
|||
"global": {
|
||||
"save": "ཉར་ཚགས།",
|
||||
"password_didnot_match": "གསང་ཚིག་མཐུན་གྱི་མི་འདུག",
|
||||
"password_hint": "ཉུང་མཐར་ཡང་ཡིག་འབྲུ་༡༢་དགོས་ལ། དེའི་ནང་དུ་ཨང་གྲངས་གཅིག་དང་། ཡིག་ཆེན་གཅིག ཡིག་ཆུང་གཅིག་ངེས་པར་དུ་ཚང་དགོས།",
|
||||
"password_hint": "ཉུང་མཐར་ཡང་ཡིག་འབྲུ་༡༢་ཀྱི་ཁོངས་སུ། ཨང་གྲངས་གཅིག་དང་། ཡིག་ཆེན་གཅིག ཡིག་ཆུང་གཅིག་ངེས་པར་དུ་ཚང་དགོས།",
|
||||
"add_reaction": "ཡ་ལན་ཁ་སྣོན།",
|
||||
"click_to_remove": "བསྣུན་ཏེ་མེད་པར་བཟོས།",
|
||||
"show_less": "ཉུང་བ་སྟོན།",
|
||||
"show_more": "མང་བ་སྟོན།"
|
||||
"show_more": "མང་བ་སྟོན།",
|
||||
"time": {
|
||||
"hours": "ཆུ་ཚོད་གཅིག་གི་སྔོན། | ཆུ་ཚོད {n} སྔོན་ལ།",
|
||||
"minutes": "སྐར་མ་གཅིག་གི་སྔོན། | སྐར་མ {n} སྔོན་ལ།",
|
||||
"days": "ཉིན་གཅིག་གི་གོང་། | ཉིན{n}གོང་ལ།",
|
||||
"recently": "ད་ལྟ་རང་།"
|
||||
},
|
||||
"notify": "བརྡ་ཁྱབ་གཏོང་བ།",
|
||||
"close": "སྒོ་རྒྱོབ།"
|
||||
},
|
||||
"logout": {
|
||||
"confirm_text": "ཁྱེད་རང་ཁ་བརྡ་ཁང་ནས་ཕྱི་རུ་ཐོན་རྒྱུ་ཡིན་ནམ།"
|
||||
|
|
@ -321,6 +355,57 @@
|
|||
"export": {
|
||||
"exported_date": "{date} ཉིན་ལ་ཕྱིར་འདྲེན་བྱས།",
|
||||
"export_filename": "{date} ཉིན་ཕྱིར་འདྲེན་བྱས་པའི་ཁ་བརྡ།",
|
||||
"processed_n_of_total_events": "བྱུང་བ་{total}ཁོངས་ནས་ལས་སྣོན་བྱས་ཟིན་པའི་སྨྱན་སྦྱོར་གྱི་གྲངས {count}"
|
||||
"processed_n_of_total_events": "བྱུང་བ་{total}ཁོངས་ནས་ལས་སྣོན་བྱས་ཟིན་པའི་སྨྱན་སྦྱོར་གྱི་གྲངས {count}",
|
||||
"fetched_n_of_total_events": "གནས་ཚུལ་{total} ནང་ནས་{count} བཏོན་གནང་།",
|
||||
"fetched_n_events": "བཏོན་གནང་བའི་གནས་ཚུལ{count}"
|
||||
},
|
||||
"file_mode": {
|
||||
"add_a_message": "འཕྲིན་ཐུང་ཞིག་ཁ་སྣོན་བྱོས།",
|
||||
"send_more_files": "ཡིག་ཆ་དེ་ལས་མང་བ་ཐོངས།",
|
||||
"choose_files": "ཡིག་ཆ་འདེམས།",
|
||||
"secure_file_send": "བདེ་འཇགས་ཡིག་ཆ་བརྒྱུད་གཏོང་།",
|
||||
"sending": "གཏོང་བཞིན་པ།",
|
||||
"files": "ཡིག་ཆ་ཁག",
|
||||
"sending_progress": "གཏོང་བཞིན་པ...",
|
||||
"any_file_format_accepted": "ཡིག་ཆའི་རྣམ་བཞག་ཆ་ཚང་ངོས་ལེན་བྱེད།",
|
||||
"files_sent_with_note": "མཆན་ཞིག་དང་མཉམ་དུ་ཡིག་ཆ་1་བཏང་ཟིན། མཆན་ཞིག་དང་མཉམ་དུ་ཡིག་ཆ {count} བཏང་ཟིན།",
|
||||
"close": "སྒོ་རྒྱོབ།",
|
||||
"files_sent": "ཡིག་ཆ1 བཏང་ཟིན། |ཡིག་ཆ{count}བཏང་ཟིན།"
|
||||
},
|
||||
"notification": {
|
||||
"dialog": {
|
||||
"body": "འཕྲིན་ཐུང་དང་ཁ་བརྡ་གལ་ཆེན་གཅིག་ཀྱང་མ་ཤོར་བ་བྱོས། མི་ཞིག་གིས་ཁྱེད་ལ་འཕྲིན་ཐུང་གཏོང་སྐབས་སམ། ཁྱེད་ཀྱི་ཁ་བརྡ་ལ་ལན་འདེབས་གནང་སྐབས་བརྡ་ཁྱབ་གཏོང་ངེས།",
|
||||
"title": "གླེང་མོལ་བརྡ་ཁྱབ་ལ་འབྲེལ་མཐུད་རྒྱུན་འཁྱོངས་བྱོས།",
|
||||
"enable": "ནུས་ཡོད་བཟོ་བ།"
|
||||
},
|
||||
"title": "འཕྲིན་ཐུང་གསར་པ་འབྱོར་སོང་།",
|
||||
"blocked_message": "བརྡ་ཁྱབ་བཀག་འདུག ཆོག་མཆན་ཁག་བསྐྱར་སྒྲིག་བྱེད་རོགས།"
|
||||
},
|
||||
"emoji": {
|
||||
"categories": {
|
||||
"nature": "རང་བྱུང་།",
|
||||
"places": "ས་ཆ།",
|
||||
"activity": "བྱེད་སྒོ།",
|
||||
"flags": "དར་ཆ་ཁག",
|
||||
"foods": "ཞལ་ཟས།",
|
||||
"objects": "བྱ་དངོས།",
|
||||
"peoples": "མི་མང་།",
|
||||
"symbols": "མཚོན་རྟགས།",
|
||||
"frequently": "རྒྱུན་དུ་སྤྱོད་བཞིན་པ།"
|
||||
},
|
||||
"search": "འཚོལ..."
|
||||
},
|
||||
"getlink": {
|
||||
"title": "ཐད་ཀར་འབྲེལ་ཐག་ཐོབ་པར་བྱོས།",
|
||||
"info": "ཐད་ཀར་ཁ་བརྡ་བྱེད་སྤྱད་ཀྱི་འབྲེལ་ཐག་གིས་ཁྱེད་དང་མི་གཞན་གྱི་བར་ལ་བདེ་འཇགས་ཀྱི་འཕྲིན་གཏོང་སྐུད་ལམ་ཞིག་མཁོ་སྤྲོད་བྱེད། འགོ་འཛུགས་བྱེད་པར། མི་ཚོས་ཁྱེད་ལ་ཁ་བརྡ་བྱེད་པར་སླེབས་དུས། འཆར་མིང་ཞིག་འདེམས་ཏེ་སྟོན།",
|
||||
"share_qr": "བཤེར་རིས་བརྒྱུད་སྐུར་བྱོས།",
|
||||
"next": "རྗེས་མ།",
|
||||
"scan_title": "བཤེར་རིས་འདི་བཤེར་ཏེ་ཐད་ཀར་ཁ་བརྡ་ཞིག་འགོ་རྩོམ།",
|
||||
"continue": "མུ་མཐུད།",
|
||||
"ready_to_share": "བརྒྱུད་སྐུར་བྱེད་པར་གྲ་སྒྲིག་ཡིན། མི་ཞིག་གིས་འབྲེལ་ཐག་དེ་ཁ་འབྱེད་ཐེངས་རེར། ཐད་ཀར་གླེང་མོལ་བྱ་སའི་ཁ་བརྡ་ཁང་ཞིག་ཁ་འབྱེད་ངེས།",
|
||||
"qr_image_copied": "འབྲེག་པང་གི་སྟེང་དུ་པར་རིས་འདྲ་བཤུས་བྱས་ཚར།",
|
||||
"hello": "ཁམས་བཟང་། {user},\nའདི་ཐད་ཀར་ཁ་བརྡ་བྱེད་སའི་ཁྱེད་ཀྱི་འབྲེལ་ཐག་ཡིན།",
|
||||
"username": "འཆར་མིང་ཞིག་གཏགས། (དཔེར་ན།: waku)",
|
||||
"different_link": "འབྲེལ་ཐག་གཞན་པ་ཞིག་རག་པར་བྱོས།"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -262,5 +262,12 @@
|
|||
"processed_n_of_total_events": "Medien für {count} von {total} Ereignissen verarbeitet",
|
||||
"fetched_n_events": "{count} Ereignisse geladen",
|
||||
"export_filename": "Chat exportiert: {date}"
|
||||
},
|
||||
"global": {
|
||||
"save": "Speichern",
|
||||
"show_more": "Mehr anzeigen",
|
||||
"password_didnot_match": "Passwort stimmt nicht überein",
|
||||
"add_reaction": "Reaktion hinzufügen",
|
||||
"show_less": "Weniger anzeigen"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
},
|
||||
"menu": {
|
||||
"start_private_chat": "Direct Message with this user",
|
||||
"actions": "actions",
|
||||
"reply": "Reply",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
|
|
@ -313,6 +314,20 @@
|
|||
"download_chat": "Download chat",
|
||||
"read_only_room": "Read only room",
|
||||
"read_only_room_info": "Only admins and moderators are allowed to send to the room",
|
||||
"message_retention": "Disappearing messages",
|
||||
"message_retention_info": "You can set a timeout for automatically removing messages from the room.\nNote: the server must support this feature.",
|
||||
"message_retention_none": "Off",
|
||||
"message_retention_1_hour": "1 Hour",
|
||||
"message_retention_1_day": "1 Day",
|
||||
"message_retention_1_week": "1 Week",
|
||||
"message_retention_30_days": "30 Days",
|
||||
"message_retention_365_days": "365 Days",
|
||||
"message_retention_other": "Other",
|
||||
"message_retention_other_seconds": "{count} Seconds",
|
||||
"message_retention_other_seconds_unit": "Seconds",
|
||||
"message_retention_other_required": "Value can not be empty",
|
||||
"message_retention_other_too_small": "Value must be at least {count} seconds",
|
||||
"message_retention_other_too_large": "Value can be at most {count} seconds",
|
||||
"make_public": "Make Public",
|
||||
"make_public_warning": "warning: Full message history will be visible to new participants",
|
||||
"direct_link": "My Direct Link",
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
"info": "Todos los miembros y mensajes serán eliminados. Esta acción no se puede deshacer.",
|
||||
"title": "¿Borrar la sala?",
|
||||
"n_seconds": "{seconds} segundos",
|
||||
"self_destruct": "La habitación se autodestruirá en segundos.",
|
||||
"self_destruct": "Tu sala se autodestruirá en segundos.",
|
||||
"deleting": "Borrando la sala:",
|
||||
"notified": "Hemos avisado a los miembros.",
|
||||
"room_deletion_notice": "¡Es hora de decir adiós! Esta sala ha sido eliminada por {user}. Se autodestruirá en segundos."
|
||||
|
|
@ -98,7 +98,8 @@
|
|||
"language_description": "Convine esta disponible en varios Idiomas.",
|
||||
"dont_see_yours": "¿No ves el tuyo?",
|
||||
"tell_us": "Dinos.",
|
||||
"display_name_required": "El nombre para mostrar es obligatorio"
|
||||
"display_name_required": "El nombre para mostrar es obligatorio",
|
||||
"notification_label": "Notificación"
|
||||
},
|
||||
"login": {
|
||||
"login": "Iniciar sesión",
|
||||
|
|
@ -148,7 +149,8 @@
|
|||
"create": "Crear",
|
||||
"room_topic": "Añade una descripción si quieres",
|
||||
"options": "Opciones",
|
||||
"room_name_limit_error_msg": "50 caracteres como máximo"
|
||||
"room_name_limit_error_msg": "50 caracteres como máximo",
|
||||
"colon_not_allowed": "Colon no está permitido"
|
||||
},
|
||||
"room_welcome": {
|
||||
"join_public": "Cualquiera puede unirse abriendo este vínculo: {link}.",
|
||||
|
|
@ -239,7 +241,9 @@
|
|||
"user_was_kicked_by_you": "Has expulsado a {user} del chat.",
|
||||
"upload_file_too_large": "¡El archivo es demasiado grande para subirlo!",
|
||||
"time_ago": "Hoy | Ayer | Hace {count} días",
|
||||
"upload_exceeded_file_limit": "Se ha superado el tamaño máximo para el archivo ({configFormattedUploadSize}). "
|
||||
"upload_exceeded_file_limit": "Se ha superado el tamaño máximo para el archivo ({configFormattedUploadSize}). ",
|
||||
"someone": "Alguien",
|
||||
"sent_media": "Enviados {count} elementos multimedia."
|
||||
},
|
||||
"menu": {
|
||||
"login": "Iniciar sesión",
|
||||
|
|
@ -263,7 +267,8 @@
|
|||
"user_kick_and_ban": "Expulsar y banear a este usuario",
|
||||
"user_make_moderator": "Hacer moderador",
|
||||
"user_make_admin": "Hacer administrador",
|
||||
"user_revoke_moderator": "Revocar al moderador"
|
||||
"user_revoke_moderator": "Revocar al moderador",
|
||||
"delete_now": "Elimina ahora"
|
||||
},
|
||||
"fallbacks": {
|
||||
"download_name": "Descargar",
|
||||
|
|
@ -395,7 +400,8 @@
|
|||
"title": "¡Manténgase conectado con las notificaciones para el chat!"
|
||||
},
|
||||
"title": "Nuevo mensaje recibido",
|
||||
"blocked_message": "Notificaciones bloqueadas. Por favor, restablezca los permisos"
|
||||
"blocked_message": "La notificación está bloqueada. Vaya a la configuración de su dispositivo o navegador para habilitar la Notificación",
|
||||
"not_supported": "La notificación aún no es compatible con dispositivos móviles"
|
||||
},
|
||||
"export": {
|
||||
"fetched_n_of_total_events": "{count} de {total} eventos recuperados",
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@
|
|||
"user_make_moderator": "Tornar moderador",
|
||||
"user_kick_and_ban": "Expulsar e banir este usuário",
|
||||
"user_make_admin": "Tornar administrador",
|
||||
"user_revoke_moderator": "Revogar moderador"
|
||||
"user_revoke_moderator": "Revogar moderador",
|
||||
"delete_now": "Excluir agora"
|
||||
},
|
||||
"message": {
|
||||
"you": "Você",
|
||||
|
|
@ -119,7 +120,9 @@
|
|||
"files": "Arquivos",
|
||||
"download_all": "Baixar tudo",
|
||||
"upload_exceeded_file_limit": "O tamanho máximo do arquivo ({configFormattedUploadSize}) foi excedido. ",
|
||||
"preparing_to_upload": "Preparando para enviar..."
|
||||
"preparing_to_upload": "Preparando para enviar...",
|
||||
"someone": "Alguém",
|
||||
"sent_media": "Enviou {count} itens de mídia."
|
||||
},
|
||||
"room": {
|
||||
"members": "sem membros | 1 membro | {count} membros",
|
||||
|
|
@ -215,7 +218,8 @@
|
|||
"password_new": "Nova senha",
|
||||
"password_repeat": "Repita a nova senha",
|
||||
"display_name": "Nome de exibição",
|
||||
"display_name_required": "O nome de exibição é obrigatório"
|
||||
"display_name_required": "O nome de exibição é obrigatório",
|
||||
"notification_label": "Notificação"
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"you_are": "Você é",
|
||||
|
|
@ -261,7 +265,7 @@
|
|||
"info": "Todos os membros e as mensagens serão excluídos. Essa ação não pode ser desfeita.",
|
||||
"button": "Excluir",
|
||||
"n_seconds": "{seconds} segundos",
|
||||
"self_destruct": "A sala se autodestruirá em segundos.",
|
||||
"self_destruct": "A sua sala se autodestruirá em segundos.",
|
||||
"deleting": "Excluindo a sala:",
|
||||
"notified": "Nós notificamos os membros.",
|
||||
"room_deletion_notice": "Hora de dizer adeus! Esta sala foi excluída pelo {user}. Ela se autodestruirá em segundos."
|
||||
|
|
@ -393,7 +397,8 @@
|
|||
"title": "Manter-se conectado com as notificações do chat!",
|
||||
"enable": "Ativar"
|
||||
},
|
||||
"blocked_message": "Notificações bloqueadas. Redefina as permissões"
|
||||
"blocked_message": "A notificação está bloqueada. Vá para as configurações do dispositivo ou do navegador para ativar a notificação",
|
||||
"not_supported": "A notificação ainda não é suportada no mobile"
|
||||
},
|
||||
"getlink": {
|
||||
"title": "Obter um link direto",
|
||||
|
|
|
|||
|
|
@ -34,7 +34,16 @@
|
|||
"read_only_room_info": "只允许管理员和版主发送到聊天室",
|
||||
"export_room": "导出聊天",
|
||||
"user_moderator": "版主",
|
||||
"experimental_features": "实验功能"
|
||||
"experimental_features": "实验功能",
|
||||
"direct_link": "我的直接链接",
|
||||
"make_public_warning": "警告:新参与者将可见完整的信息历史记录",
|
||||
"room_type": "聊天室种类",
|
||||
"file_mode_info": "将聊天界面切换到“文件投送”模式",
|
||||
"room_type_default": "默认",
|
||||
"file_mode": "文件模式",
|
||||
"direct_link_desc": "已准备好分享了! 每次有人打开链接时,会打开一个新的直接聊天室。",
|
||||
"copy_link": "复制链接",
|
||||
"make_public": "公开"
|
||||
},
|
||||
"leave": {
|
||||
"leave": "离开",
|
||||
|
|
@ -63,7 +72,10 @@
|
|||
"email_not_valid": "电子邮件地址无效",
|
||||
"terms": "主服务器要求您查看并接受以下政策:",
|
||||
"accept_terms": "接受",
|
||||
"email": "您需要验证您的电子邮件地址"
|
||||
"email": "您需要验证您的电子邮件地址",
|
||||
"registration_token": "请输入注册令牌",
|
||||
"token_not_valid": "令牌无效",
|
||||
"send_token": "发送令牌"
|
||||
},
|
||||
"device_list": {
|
||||
"title": "设备",
|
||||
|
|
@ -137,7 +149,17 @@
|
|||
"outgoing_message_deleted_text": "你删除了这条信息。",
|
||||
"incoming_message_deleted_text": "这条信息已删除。",
|
||||
"not_allowed_to_send": "只允许管理员和版主发送到聊天室",
|
||||
"reaction_count_more": "{reactionCount} 更多"
|
||||
"reaction_count_more": "{reactionCount} 更多",
|
||||
"images": "图片",
|
||||
"send_attachements_dialog_title": "您想发送以下附件吗?",
|
||||
"preparing_to_upload": "准备上传...",
|
||||
"seen_by": "查看者",
|
||||
"seen_by_count": "没有被群成员看到 |已被 1 位成员查看 | 已被 {count} 位成员查看",
|
||||
"files": "文件",
|
||||
"download_all": "全部下载",
|
||||
"file": "文件",
|
||||
"upload_file_too_large": "文件太大, 无法上传!",
|
||||
"upload_exceeded_file_limit": "超出 ({configFormattedUploadSize}) 的最大文件大小。 "
|
||||
},
|
||||
"menu": {
|
||||
"login": "登录",
|
||||
|
|
@ -211,7 +233,8 @@
|
|||
"title_user": "欢迎您被邀请聊天",
|
||||
"join_user": "开始聊天",
|
||||
"enter_room_user": "开始聊天",
|
||||
"choose_name": "选择要使用的名称"
|
||||
"choose_name": "选择要使用的名称",
|
||||
"you_have_been_banned": "您已被禁止进入该聊天室。"
|
||||
},
|
||||
"profile": {
|
||||
"display_name": "显示名称",
|
||||
|
|
@ -261,7 +284,10 @@
|
|||
"join_public": "任何人都可以加入通过打开此链接: {link}。",
|
||||
"room_history_joined": "只有加入后,人们才可以看到发送的信息。",
|
||||
"room_history_is": "聊天室纪录是{type}.",
|
||||
"encrypted": "信息是端到端加密的。"
|
||||
"encrypted": "信息是端到端加密的。",
|
||||
"no_past_messages": "欢迎! 为了您的安全,过去的信息不提供。",
|
||||
"direct_info": "你好,{you}.正在与 {user} 进行私人聊天。",
|
||||
"direct_private_chat": "私信"
|
||||
},
|
||||
"profile_info_popup": {
|
||||
"new_room": "新的聊天室",
|
||||
|
|
@ -282,15 +308,19 @@
|
|||
"global": {
|
||||
"save": "保存",
|
||||
"password_didnot_match": "密码不匹配",
|
||||
"password_hint": "至少 12 个字符,包含至少一个数字、一个大写字母和一个小写字母",
|
||||
"password_hint": "至少 12 个字符,包含一个数字、一个大写字母和一个小写字母",
|
||||
"show_less": "显示较少",
|
||||
"show_more": "展示更多",
|
||||
"add_reaction": "添加反应",
|
||||
"click_to_remove": "点击删除",
|
||||
"time": {
|
||||
"recently": "刚才"
|
||||
"recently": "刚才",
|
||||
"hours": "1小时前| {n}小时前",
|
||||
"minutes": "1 分钟前 | {n} 分钟前",
|
||||
"days": "1天前| {n}天前"
|
||||
},
|
||||
"close": "关闭"
|
||||
"close": "关闭",
|
||||
"notify": "通知"
|
||||
},
|
||||
"logout": {
|
||||
"confirm_text": "您确定要注销吗?"
|
||||
|
|
@ -328,5 +358,54 @@
|
|||
"fetched_n_of_total_events": "已获取 {count} 个事件,共 {total} 个事件",
|
||||
"processed_n_of_total_events": "已处理 {count} 个事件的媒体,共 {total} 个事件",
|
||||
"export_filename": "导出的聊天 {date}"
|
||||
},
|
||||
"file_mode": {
|
||||
"add_a_message": "添加留言",
|
||||
"send_more_files": "发送更多文件",
|
||||
"choose_files": "地方",
|
||||
"secure_file_send": "安全文件发送",
|
||||
"sending": "发送中",
|
||||
"files": "文件",
|
||||
"sending_progress": "正在发送...",
|
||||
"any_file_format_accepted": "接受任何文件格式",
|
||||
"files_sent_with_note": "发送 1 个文件并附有备注! | 已发送 {count} 个文件并附有备注!",
|
||||
"close": "关闭",
|
||||
"files_sent": "已发送 1 个文件! | 已发送 {count} 个文件!"
|
||||
},
|
||||
"notification": {
|
||||
"dialog": {
|
||||
"body": "再也不要错过任何信息或重要对话! 每当有人向您发送信息或回复您的聊天时,您会收到通知。",
|
||||
"title": "通过聊天通知保持联系!",
|
||||
"enable": "启用"
|
||||
},
|
||||
"title": "收到新信息",
|
||||
"blocked_message": "通知被屏蔽。 请重置权限"
|
||||
},
|
||||
"emoji": {
|
||||
"categories": {
|
||||
"nature": "自然",
|
||||
"places": "地方",
|
||||
"activity": "活动",
|
||||
"flags": "旗帜",
|
||||
"foods": "食品",
|
||||
"objects": "物体",
|
||||
"peoples": "人民",
|
||||
"symbols": "符号",
|
||||
"frequently": "常用的"
|
||||
},
|
||||
"search": "搜索..."
|
||||
},
|
||||
"getlink": {
|
||||
"title": "获取直接链接",
|
||||
"info": "直接链接为人们与您提供安全的沟通渠道。首先,选择当人们与您聊天时显示的屏幕名称。",
|
||||
"share_qr": "扫描二维码",
|
||||
"next": "下一步",
|
||||
"scan_title": "扫描此二维码即可直接聊天",
|
||||
"continue": "继续",
|
||||
"ready_to_share": "已准备好分享了! 每次有人打开链接时,会打开一个新的直接聊天室。",
|
||||
"qr_image_copied": "图片已复制到剪贴板",
|
||||
"hello": "你好{user},\n这是您的直接链接",
|
||||
"username": "输入屏幕名称(例如:waku)",
|
||||
"different_link": "获取其它链接"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@
|
|||
v-on:download="download(selectedEvent)" v-on:more="
|
||||
isEmojiQuickReaction= true
|
||||
showMoreMessageOperations({event: selectedEvent, anchor: $event.anchor})
|
||||
" :originalEvent="selectedEvent" :timelineSet="timelineSet" />
|
||||
" :originalEvent="selectedEvent" :timelineSet="timelineSet"
|
||||
:readOnlyRoom="$matrix.currentRoomIsReadOnlyForUser"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div ref="avatarOperationsStrut" class="avatar-operations-strut">
|
||||
|
|
@ -483,6 +485,11 @@ export default {
|
|||
Places: this.$t("emoji.categories.places")
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A timer to handle message retention/auto deletion
|
||||
*/
|
||||
retentionTimer: null
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -511,6 +518,10 @@ export default {
|
|||
this.$root.$off('audio-playback-ended', this.audioPlaybackEnded);
|
||||
this.$audioPlayer.pause();
|
||||
this.stopRRTimer();
|
||||
if (this.retentionTimer) {
|
||||
clearInterval(this.retentionTimer);
|
||||
this.retentionTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
|
|
@ -774,6 +785,12 @@ export default {
|
|||
this.events.filter(event => (event.threadRootId && !event.parentThread)).forEach(event => this.setParentThread(event));
|
||||
this.events.filter(event => (event.replyEventId && !event.replyEvent)).forEach(event => this.setReplyToEvent(event));
|
||||
console.log("Loading finished!");
|
||||
this.updateRetentionTimer();
|
||||
} else if (!value) {
|
||||
if (this.retentionTimer) {
|
||||
clearInterval(this.retentionTimer);
|
||||
this.retentionTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -871,6 +888,52 @@ export default {
|
|||
this.initialLoadDone = true;
|
||||
console.log("Loading finished!");
|
||||
},
|
||||
|
||||
/**
|
||||
* Set events to display. At the same time, filter out messages that are past rentention period etc.
|
||||
*/
|
||||
setEvents(events) {
|
||||
this.events = this.removeTimedOutEvents(events);
|
||||
},
|
||||
|
||||
updateRetentionTimer(maybeEvent) {
|
||||
const retentionEvent = maybeEvent || (this.room && this.room.currentState.getStateEvents("m.room.retention", ""));
|
||||
if (retentionEvent) {
|
||||
const maxLifetime = parseInt(retentionEvent.getContent().max_lifetime);
|
||||
if (maxLifetime) {
|
||||
if (!this.retentionTimer) {
|
||||
this.retentionTimer = setInterval(this.onRetentionTimer, 60000);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.retentionTimer) {
|
||||
clearInterval(this.retentionTimer);
|
||||
this.retentionTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
removeTimedOutEvents(events) {
|
||||
const retentionEvent = this.room && this.room.currentState.getStateEvents("m.room.retention", "");
|
||||
let maxLifetime = 0;
|
||||
if (retentionEvent) {
|
||||
maxLifetime = parseInt(retentionEvent.getContent().max_lifetime);
|
||||
}
|
||||
return events.filter((e) => {
|
||||
if (maxLifetime > 0 && !e.isState()) { // Keep all state events
|
||||
return e.getLocalAge() < maxLifetime;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
onRetentionTimer() {
|
||||
const events = this.removeTimedOutEvents(this.events);
|
||||
if (events.length != this.events.length) {
|
||||
this.events = events; // Changed
|
||||
}
|
||||
},
|
||||
|
||||
onRoomJoined(initialEventId) {
|
||||
// Listen to events
|
||||
this.$matrix.on("Room.timeline", this.onEvent);
|
||||
|
|
@ -885,7 +948,7 @@ export default {
|
|||
this.timelineWindow
|
||||
.load(initialEventId, 20)
|
||||
.then(() => {
|
||||
self.events = self.timelineWindow.getEvents();
|
||||
self.setEvents(self.timelineWindow.getEvents());
|
||||
|
||||
const getMoreIfNeeded = function _getMoreIfNeeded() {
|
||||
const container = self.$refs.chatContainer;
|
||||
|
|
@ -896,7 +959,7 @@ export default {
|
|||
self.timelineWindow.canPaginate(EventTimeline.BACKWARDS)
|
||||
) {
|
||||
return self.timelineWindow.paginate(EventTimeline.BACKWARDS, 10, true, 5).then((success) => {
|
||||
self.events = self.timelineWindow.getEvents();
|
||||
self.setEvents(self.timelineWindow.getEvents());
|
||||
if (success) {
|
||||
return _getMoreIfNeeded.call(self);
|
||||
} else {
|
||||
|
|
@ -943,7 +1006,7 @@ export default {
|
|||
this.onRoomJoined(null);
|
||||
} else {
|
||||
// Error. Done loading.
|
||||
this.events = this.timelineWindow.getEvents();
|
||||
this.setEvents(this.timelineWindow.getEvents());
|
||||
this.setInitialLoadDone();
|
||||
}
|
||||
})
|
||||
|
|
@ -976,7 +1039,7 @@ export default {
|
|||
.then(() => {
|
||||
self.timelineSet = timelineSet;
|
||||
self.timelineWindow = timelineWindow;
|
||||
self.events = self.timelineWindow.getEvents();
|
||||
self.setEvents(self.timelineWindow.getEvents());
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
|
|
@ -1090,7 +1153,7 @@ export default {
|
|||
if (tl) {
|
||||
const parentEvent = tl.getEvents().find((e) => e.getId() === event.threadRootId);
|
||||
if (parentEvent) {
|
||||
this.events = this.timelineWindow.getEvents();
|
||||
this.setEvents(this.timelineWindow.getEvents());
|
||||
const fn = () => {
|
||||
Vue.set(parentEvent, "isMxThread", true);
|
||||
Vue.set(event, "parentThread", parentEvent);
|
||||
|
|
@ -1123,7 +1186,7 @@ export default {
|
|||
if (tl) {
|
||||
const parentEvent = tl.getEvents().find((e) => e.getId() === event.replyEventId);
|
||||
if (parentEvent) {
|
||||
this.events = this.timelineWindow.getEvents();
|
||||
this.setEvents(this.timelineWindow.getEvents());
|
||||
const fn = () => {Vue.set(event, "replyEvent", parentEvent);};
|
||||
if (this.initialLoadDone) {
|
||||
const sel = "[eventId=\"" + parentEvent.getId() + "\"]";
|
||||
|
|
@ -1162,7 +1225,7 @@ export default {
|
|||
this.paginateBackIfNeeded();
|
||||
}
|
||||
|
||||
if (loadingDone && event.forwardLooking && (!event.isRelation() || event.isMxThread || event.threadRootId || event.parentThread )) {
|
||||
if (loadingDone && event.forwardLooking && (!event.isRelation() || event.isMxThread || event.threadRootId || event.parentThread)) {
|
||||
// If we are at bottom, scroll to see new events...
|
||||
var scrollToSeeNew = event.getSender() == this.$matrix.currentUserId; // When we sent, scroll
|
||||
const container = this.chatContainer;
|
||||
|
|
@ -1179,13 +1242,17 @@ export default {
|
|||
(event.getPrevContent() || {}).membership == "join" &&
|
||||
(
|
||||
(event.getContent().membership == "leave" && event.getSender() != this.currentUserId) ||
|
||||
(event.getContent().membership == "ban" ))
|
||||
) {
|
||||
this.$store.commit("setCurrentRoomId", null);
|
||||
const wasPurged = event.getContent().reason == "Room Deleted";
|
||||
this.$navigation.push({ name: "Goodbye", params: { roomWasPurged: wasPurged } }, -1);
|
||||
}
|
||||
(event.getContent().membership == "ban"))
|
||||
) {
|
||||
this.$store.commit("setCurrentRoomId", null);
|
||||
const wasPurged = event.getContent().reason == "Room Deleted";
|
||||
this.$navigation.push({ name: "Goodbye", params: { roomWasPurged: wasPurged } }, -1);
|
||||
}
|
||||
|
||||
else if (event.getType() === "m.room.retention") {
|
||||
this.updateRetentionTimer(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onUserTyping(event, member) {
|
||||
|
|
@ -1404,7 +1471,7 @@ export default {
|
|||
.then((success) => {
|
||||
if (success && this.scrollPosition) {
|
||||
this.scrollPosition.prepareFor("up");
|
||||
this.events = this.timelineWindow.getEvents();
|
||||
this.setEvents(this.timelineWindow.getEvents());
|
||||
this.$nextTick(() => {
|
||||
// restore scroll position!
|
||||
console.log("Restore scroll!");
|
||||
|
|
@ -1429,7 +1496,7 @@ export default {
|
|||
.paginate(EventTimeline.FORWARDS, 10, true)
|
||||
.then((success) => {
|
||||
if (success) {
|
||||
this.events = this.timelineWindow.getEvents();
|
||||
this.setEvents(this.timelineWindow.getEvents());
|
||||
if (!this.useVoiceMode && this.scrollPosition) {
|
||||
this.scrollPosition.prepareFor("down");
|
||||
this.$nextTick(() => {
|
||||
|
|
|
|||
|
|
@ -477,14 +477,25 @@ export default {
|
|||
// 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: {
|
||||
users: powerLevels,
|
||||
events_default: this.readOnlyRoom ? 50 : 0
|
||||
}
|
||||
content: powerLevelContent
|
||||
});
|
||||
|
||||
return this.$matrix.matrixClient
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<v-list dense @click.native.stop="nullEvent">
|
||||
<v-subheader>{{$t('device_list.title')}}</v-subheader>
|
||||
<v-list-item-group color="primary">
|
||||
<v-list-item
|
||||
v-for="device in devices"
|
||||
|
|
|
|||
219
src/components/MessageRetentionDialog.vue
Normal file
219
src/components/MessageRetentionDialog.vue
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
<template>
|
||||
<v-dialog persistent 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 class="dialog-title">{{ $t("room_info.message_retention") }}</h2>
|
||||
<div class="dialog-text">
|
||||
{{ $t("room_info.message_retention_info") }}
|
||||
</div>
|
||||
</template>
|
||||
<v-container fluid>
|
||||
<v-row cols="12">
|
||||
<v-col cols="12">
|
||||
<v-form v-model="isValid">
|
||||
<v-radio-group v-model="retention" :value-comparator="compareValue">
|
||||
<v-radio active-class="radio-active" v-for="p in retentionPeriods" :key="p.text" :label="p.text"
|
||||
:value="p.value">
|
||||
<template v-slot:label v-if="p.value === ''">
|
||||
<div :class="{ 'other': true, 'visible': selectedOther }"><span>{{ p.text }}</span>
|
||||
<v-text-field :show="selectedOther" ref="otherInput" background-color="transparent" height="20px"
|
||||
dense outlined x-hide-details autofocus v-model="retentionOther" single-line type="number"
|
||||
hide-spin-buttons :rules="otherInputRules"
|
||||
:suffix="$t('room_info.message_retention_other_seconds_unit')"></v-text-field>
|
||||
</div>
|
||||
</template>
|
||||
</v-radio>
|
||||
</v-radio-group>
|
||||
</v-form>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row cols="12">
|
||||
<v-col cols="6">
|
||||
<v-btn depressed text block class="text-button" @click="showDialog = false">{{
|
||||
$t("menu.cancel") }}</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="6" align="center">
|
||||
<v-btn color="primary" :disabled="!isValid" depressed block class="filled-button"
|
||||
@click.stop="setRetention()">{{
|
||||
$t("menu.ok") }}</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</v-dialog>
|
||||
</template>
|
||||
<script>
|
||||
import roomInfoMixin from "./roomInfoMixin";
|
||||
|
||||
export default {
|
||||
name: "MessageRetentionDialog",
|
||||
mixins: [roomInfoMixin],
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: function () {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showDialog: false,
|
||||
isValid: true,
|
||||
selectedOther: false,
|
||||
retention: 0,
|
||||
retentionOther: 60, // Set same as min below
|
||||
retentionMinValue: 60,
|
||||
retentionMaxValue: 60 * 60 * 24 * 500,
|
||||
retentionPeriods: [
|
||||
{
|
||||
text: this.$t("room_info.message_retention_none"),
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_1_hour"),
|
||||
value: 3600 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_1_day"),
|
||||
value: 3600 * 24 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_30_days"),
|
||||
value: 3600 * 24 * 30 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_365_days"),
|
||||
value: 3600 * 24 * 365 * 1000
|
||||
},
|
||||
{
|
||||
text: this.$t("room_info.message_retention_other"),
|
||||
value: ''
|
||||
},
|
||||
],
|
||||
otherInputRules: [
|
||||
v => !!v || this.$t("room_info.message_retention_other_required"),
|
||||
v => (v && v >= this.retentionMinValue) || this.$t("room_info.message_retention_other_too_small", { count: this.retentionMinValue }),
|
||||
v => (v && v <= this.retentionMaxValue) || this.$t("room_info.message_retention_other_too_large", { count: this.retentionMaxValue }),
|
||||
]
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
show: {
|
||||
handler(newVal, ignoredOldVal) {
|
||||
this.showDialog = newVal;
|
||||
},
|
||||
},
|
||||
showDialog(val, oldVal) {
|
||||
if (!val && oldVal) {
|
||||
this.$emit("close");
|
||||
} else if (val && !oldVal) {
|
||||
// Showing, reset
|
||||
this.setInitialValue();
|
||||
}
|
||||
},
|
||||
retention(val) {
|
||||
if (val === '') {
|
||||
this.selectedOther = true;
|
||||
this.retentionOther = this.retentionMaxValue;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.otherInput[0].focus();
|
||||
});
|
||||
} else {
|
||||
// If one of the other, it's not "other"
|
||||
for (var i = 0; i < this.retentionPeriods.length; i++) {
|
||||
if (this.retentionPeriods[i].value === val) {
|
||||
this.selectedOther = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setInitialValue();
|
||||
},
|
||||
methods: {
|
||||
setInitialValue() {
|
||||
this.isValid = true;
|
||||
this.retention = this.roomMessageRetention();
|
||||
let found = false;
|
||||
for (var i = 0; i < this.retentionPeriods.length; i++) {
|
||||
if (this.retentionPeriods[i].value !== '' && this.retentionPeriods[i].value === this.retention) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
this.selectedOther = true;
|
||||
}
|
||||
},
|
||||
compareValue(a, b) {
|
||||
if (this.selectedOther) {
|
||||
if (b === '') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (a === b) return true;
|
||||
if (b === '') {
|
||||
// If none of the other, it's "other"
|
||||
for (var i = 0; i < this.retentionPeriods.length; i++) {
|
||||
if (this.retentionPeriods[i].value === a) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
setRetention() {
|
||||
let ms = this.retention;
|
||||
if (this.selectedOther) {
|
||||
ms = this.retentionOther * 1000;
|
||||
}
|
||||
if (ms === 0 || !ms) {
|
||||
// No expiry
|
||||
this.$matrix.matrixClient.sendStateEvent(
|
||||
this.room.roomId,
|
||||
"m.room.retention",
|
||||
{ max_lifetime: 0 }
|
||||
);
|
||||
this.showDialog = false;
|
||||
} else if (ms >= 1000 * this.retentionMinValue && ms <= 1000 * this.retentionMaxValue) {
|
||||
this.$matrix.matrixClient.sendStateEvent(
|
||||
this.room.roomId,
|
||||
"m.room.retention",
|
||||
{ max_lifetime: ms }
|
||||
);
|
||||
this.showDialog = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@/assets/css/chat.scss";
|
||||
|
||||
.v-radio:last-of-type {
|
||||
align-items: start !important;
|
||||
}
|
||||
|
||||
.v-radio .other {
|
||||
display: flex;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.v-radio .v-text-field {
|
||||
display: none;
|
||||
margin-left: 8px;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.v-radio.radio-active {
|
||||
.v-text-field {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -138,7 +138,7 @@
|
|||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-card class="account ma-3" flat v-if="(iAmAdmin() && availableRoomTypes.length > 1) || canChangeReadOnly()">
|
||||
<v-card class="account ma-3" flat v-if="(iAmAdmin() && availableRoomTypes.length > 1) || canChangeReadOnly() || canViewRetentionPolicy">
|
||||
<v-card-title class="h2 with-right-label"><div>{{ $t("room_info.experimental_features") }}</div><div></div></v-card-title>
|
||||
<v-card-text v-if="iAmAdmin() && availableRoomTypes.length > 1">
|
||||
<div class="d-flex flex-wrap">
|
||||
|
|
@ -159,6 +159,14 @@
|
|||
v-model="readOnlyRoom"
|
||||
></v-switch>
|
||||
</v-card-text>
|
||||
<v-card-text class="with-right-label" style="align-items:center" v-if="canViewRetentionPolicy">
|
||||
<div>
|
||||
<div class="option-title">{{ $t('room_info.message_retention') }}</div>
|
||||
<div class="option-text">{{ $t('room_info.message_retention_info') }}</div>
|
||||
</div>
|
||||
<div class="text-right">{{ messageRetention }}</div>
|
||||
<v-btn v-on:click="showMessageRetentionDialog = true" v-if="canChangeRetentionPolicy" icon size="x-small"><v-icon color="black">edit</v-icon></v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-card class="members ma-3" flat>
|
||||
|
|
@ -166,58 +174,72 @@
|
|||
>{{ $t("room_info.members") }}<v-spacer></v-spacer>
|
||||
<div>{{ members.length }}</div></v-card-title
|
||||
>
|
||||
<v-card-text>
|
||||
<div
|
||||
class="member ma-2"
|
||||
v-for="(member, index) in members"
|
||||
:key="member.userId"
|
||||
v-show="showAllMembers || index < SHOW_MEMBER_LIMIT"
|
||||
@click="toggleMemberExpanded(member)"
|
||||
>
|
||||
<v-avatar class="avatar" size="32" color="grey">
|
||||
<img v-if="memberAvatar(member)" :src="memberAvatar(member)" />
|
||||
<span v-else class="white--text headline">{{
|
||||
member.name.substring(0, 1).toUpperCase()
|
||||
}}</span>
|
||||
</v-avatar>
|
||||
<span class="user-name">
|
||||
{{
|
||||
member.userId == $matrix.currentUserId
|
||||
? $t("room_info.user_you", {
|
||||
user: member.user ? member.user.displayName : member.name,
|
||||
})
|
||||
: $t("room_info.user", {
|
||||
user: member.user ? member.user.displayName : member.name,
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
<span v-if="isAdmin(member)" class="user-power">
|
||||
{{ $t("room_info.user_admin") }}
|
||||
</span>
|
||||
<span v-else-if="isModerator(member)" class="user-power">
|
||||
{{ $t("room_info.user_moderator") }}
|
||||
</span>
|
||||
<div v-if="member.userId != $matrix.currentUserId && !$matrix.isDirectRoomWith(room, member.userId) && expandedMembers.includes(member)" class="start-private-chat clickable" @click="startPrivateChat(member.userId)">{{ $t("menu.start_private_chat") }}</div>
|
||||
<div v-if="canKickUser(member) || canBanUser(member)">
|
||||
<div v-if="member.userId != $matrix.currentUserId && expandedMembers.includes(member)" class="mt-2">{{ String.fromCharCode(160) }}</div>
|
||||
<div v-if="member.userId != $matrix.currentUserId && expandedMembers.includes(member) && canKickUser(member)" class="start-private-chat clickable" @click="kickUser(member)">{{ $t("menu.user_kick") }}</div>
|
||||
<div v-if="member.userId != $matrix.currentUserId && expandedMembers.includes(member) && canBanUser(member)" class="start-private-chat clickable" @click="banUser(member)">{{ $t("menu.user_kick_and_ban") }}</div>
|
||||
</div>
|
||||
<div v-if="member.userId != $matrix.currentUserId && expandedMembers.includes(member)" class="mt-2">{{ String.fromCharCode(160) }}</div>
|
||||
<div v-if="member.userId != $matrix.currentUserId && expandedMembers.includes(member) && !isAdmin(member) && canMakeAdmin(member)" class="start-private-chat clickable" @click="makeAdmin(member)">{{ $t("menu.user_make_admin") }}</div>
|
||||
<div v-if="member.userId != $matrix.currentUserId && expandedMembers.includes(member) && !isModerator(member) && !isAdmin(member) && canMakeModerator(member)" class="start-private-chat clickable" @click="makeModerator(member)">{{ $t("menu.user_make_moderator") }}</div>
|
||||
<div v-if="member.userId != $matrix.currentUserId && expandedMembers.includes(member) && isModerator(member) && canRevokeModerator(member)" class="start-private-chat clickable" @click="revokeModerator(member)">{{ $t("menu.user_revoke_moderator") }}</div>
|
||||
<DeviceList
|
||||
v-if="expandedMembers.includes(member)"
|
||||
:member="member"
|
||||
/>
|
||||
</div>
|
||||
<div class="show-all" @click="showAllMembers = !showAllMembers" v-if="members.length > SHOW_MEMBER_LIMIT">
|
||||
{{
|
||||
showAllMembers ? $t("room_info.hide_all") : $t("room_info.show_all")
|
||||
}}
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-expansion-panels>
|
||||
<v-expansion-panel v-for="(member, index) in members" :key="member.userId">
|
||||
<v-expansion-panel-header
|
||||
class="member"
|
||||
v-show="showAllMembers || index < SHOW_MEMBER_LIMIT"
|
||||
>
|
||||
<div>
|
||||
<v-avatar class="avatar" size="32" color="grey">
|
||||
<img v-if="memberAvatar(member)" :src="memberAvatar(member)" />
|
||||
<span v-else class="white--text headline">{{
|
||||
member.name.substring(0, 1).toUpperCase()
|
||||
}}</span>
|
||||
</v-avatar>
|
||||
<span class="user-name">
|
||||
{{
|
||||
member.userId == $matrix.currentUserId
|
||||
? $t("room_info.user_you", {
|
||||
user: member.user ? member.user.displayName : member.name,
|
||||
})
|
||||
: $t("room_info.user", {
|
||||
user: member.user ? member.user.displayName : member.name,
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
<span v-if="isAdmin(member)" class="user-power">
|
||||
{{ $t("room_info.user_admin") }}
|
||||
</span>
|
||||
<span v-else-if="isModerator(member)" class="user-power">
|
||||
{{ $t("room_info.user_moderator") }}
|
||||
</span>
|
||||
</div>
|
||||
</v-expansion-panel-header>
|
||||
<v-expansion-panel-content>
|
||||
<v-tabs>
|
||||
<v-tab v-if="member.userId != $matrix.currentUserId">{{ $t("menu.actions") }}</v-tab>
|
||||
<v-tab>{{$t('device_list.title')}}</v-tab>
|
||||
<!-- TODO: other rooms in common
|
||||
<v-tab v-if="member.userId != $matrix.currentUserId">{{ $t("menu.common_room") }}</v-tab>
|
||||
-->
|
||||
<v-tab-item v-if="member.userId != $matrix.currentUserId">
|
||||
<div class="py-3">
|
||||
<v-btn text v-if="member.userId != $matrix.currentUserId && !$matrix.isDirectRoomWith(room, member.userId)" class="start-private-chat clickable d-block text-none" @click="startPrivateChat(member.userId)">{{ $t("menu.start_private_chat") }}</v-btn>
|
||||
<div v-if="canKickUser(member) || canBanUser(member)">
|
||||
<div v-if="member.userId != $matrix.currentUserId" class="mt-2">{{ String.fromCharCode(160) }}</div>
|
||||
<v-btn text v-if="member.userId != $matrix.currentUserId && canKickUser(member)" class="start-private-chat clickable d-block text-none" @click="kickUser(member)">{{ $t("menu.user_kick") }}</v-btn>
|
||||
<v-btn text v-if="member.userId != $matrix.currentUserId && canBanUser(member)" class="start-private-chat clickable d-block text-none" @click="banUser(member)">{{ $t("menu.user_kick_and_ban") }}</v-btn>
|
||||
</div>
|
||||
<div v-if="member.userId != $matrix.currentUserId" class="mt-2">{{ String.fromCharCode(160) }}</div>
|
||||
<v-btn text v-if="member.userId != $matrix.currentUserId && !isAdmin(member) && canMakeAdmin(member)" class="start-private-chat clickable d-block text-none" @click="makeAdmin(member)">{{ $t("menu.user_make_admin") }}</v-btn>
|
||||
<v-btn text v-if="member.userId != $matrix.currentUserId && !isModerator(member) && !isAdmin(member) && canMakeModerator(member)" class="start-private-chat clickable d-block text-none" @click="makeModerator(member)">{{ $t("menu.user_make_moderator") }}</v-btn>
|
||||
<v-btn text v-if="member.userId != $matrix.currentUserId && isModerator(member) && canRevokeModerator(member)" class="start-private-chat clickable d-block text-none" @click="revokeModerator(member)">{{ $t("menu.user_revoke_moderator") }}</v-btn>
|
||||
</div>
|
||||
</v-tab-item>
|
||||
<v-tab-item>
|
||||
<DeviceList :member="member" />
|
||||
</v-tab-item>
|
||||
<!-- <v-tab-item v-if="member.userId != $matrix.currentUserId">
|
||||
{{ "TODO" }}
|
||||
</v-tab-item> -->
|
||||
</v-tabs>
|
||||
</v-expansion-panel-content>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
<div class="show-all p-2" @click="showAllMembers = !showAllMembers" v-if="members.length > SHOW_MEMBER_LIMIT">
|
||||
{{ showAllMembers ? $t("room_info.hide_all") : $t("room_info.show_all") }}
|
||||
</div>
|
||||
</v-card>
|
||||
|
||||
<!-- EXPORT CHAT -->
|
||||
|
|
@ -247,6 +269,12 @@
|
|||
@close="showPurgeConfirmation = false"
|
||||
/>
|
||||
|
||||
<MessageRetentionDialog
|
||||
:show="showMessageRetentionDialog"
|
||||
:room="room"
|
||||
@close="showMessageRetentionDialog = false"
|
||||
/>
|
||||
|
||||
<RoomExport :room="room" v-if="exporting" v-on:close="exporting = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -254,6 +282,7 @@
|
|||
<script>
|
||||
import LeaveRoomDialog from "../components/LeaveRoomDialog";
|
||||
import PurgeRoomDialog from "../components/PurgeRoomDialog";
|
||||
import MessageRetentionDialog from "../components/MessageRetentionDialog";
|
||||
import DeviceList from "../components/DeviceList";
|
||||
import RoomExport from "../components/RoomExport";
|
||||
import RoomAvatarPicker from "../components/RoomAvatarPicker";
|
||||
|
|
@ -269,6 +298,7 @@ export default {
|
|||
components: {
|
||||
LeaveRoomDialog,
|
||||
PurgeRoomDialog,
|
||||
MessageRetentionDialog,
|
||||
DeviceList,
|
||||
RoomExport,
|
||||
RoomAvatarPicker,
|
||||
|
|
@ -282,7 +312,8 @@ export default {
|
|||
showAllMembers: false,
|
||||
showLeaveConfirmation: false,
|
||||
showPurgeConfirmation: false,
|
||||
expandedMembers: [],
|
||||
showMessageRetentionDialog: false,
|
||||
messageRetention: "",
|
||||
buildVersion: "",
|
||||
updatingJoinRule: false, // Flag if we are processing update curerntly
|
||||
joinRules: [
|
||||
|
|
@ -298,12 +329,13 @@ export default {
|
|||
},
|
||||
],
|
||||
SHOW_MEMBER_LIMIT: 5,
|
||||
exporting: false,
|
||||
};
|
||||
exporting: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$matrix.on("Room.timeline", this.onEvent);
|
||||
this.updateMembers();
|
||||
this.updateMessageRetention();
|
||||
this.user = this.$matrix.matrixClient.getUser(this.$matrix.currentUserId);
|
||||
|
||||
// Display build version
|
||||
|
|
@ -382,6 +414,7 @@ export default {
|
|||
handler() {
|
||||
console.log("RoomInfo: Current room changed");
|
||||
this.updateMembers();
|
||||
this.updateMessageRetention();
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -391,11 +424,17 @@ export default {
|
|||
if (this.room && this.room.roomId == event.getRoomId()) {
|
||||
// For this room
|
||||
if (event.getType() == "m.room.member") {
|
||||
this.updateMembers();
|
||||
}
|
||||
this.updateMembers();
|
||||
} else if (event.getType() == "m.room.retention") {
|
||||
this.updateMessageRetention(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updateMessageRetention(event) {
|
||||
this.messageRetention = this.roomMessageRetentionDisplay(event);
|
||||
},
|
||||
|
||||
updateMembers() {
|
||||
if (this.room) {
|
||||
const myUserId = this.$matrix.currentUserId;
|
||||
|
|
@ -441,15 +480,6 @@ export default {
|
|||
this.$navigation.push({ name: "Profile" }, 1);
|
||||
},
|
||||
|
||||
toggleMemberExpanded(member) {
|
||||
const index = this.expandedMembers.indexOf(member);
|
||||
if (index > -1) {
|
||||
this.expandedMembers.splice(index, 1);
|
||||
} else {
|
||||
this.expandedMembers.push(member);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set room join rule.
|
||||
* @param joinRule One of "invite" or "public". Currently always disables guest access.
|
||||
|
|
|
|||
|
|
@ -90,7 +90,12 @@ export default {
|
|||
return this.sortItemsOnName(this.$matrix.invites);
|
||||
},
|
||||
joinedRooms() {
|
||||
return this.sortItemsOnName(this.$matrix.joinedRooms);
|
||||
// show room with notification on top, followed by room decending order by active Timestamp
|
||||
return [...this.$matrix.joinedRooms].sort((a, b) => {
|
||||
if (this.notificationCount(a)) return -1;
|
||||
if (this.notificationCount(b)) return 1;
|
||||
return b.getLastActiveTimestamp() - a.getLastActiveTimestamp()
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
</v-avatar>
|
||||
<!-- SLOT FOR CONTENT -->
|
||||
<slot></slot>
|
||||
<div class="op-button" ref="opbutton" v-if="!event.isRedacted() && !$matrix.currentRoomIsReadOnlyForUser">
|
||||
<div class="op-button" ref="opbutton" v-if="!event.isRedacted()">
|
||||
<v-btn id="btn-more" icon @click.stop="showContextMenu($refs.opbutton)">
|
||||
<v-icon>more_vert</v-icon>
|
||||
</v-btn>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<v-btn id="btn-more" icon @click.stop="more" class="ma-0 pa-0">
|
||||
<v-icon small> $vuetify.icons.addReaction </v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-if="incoming" id="btn-reply" icon @click.stop="addReply" class="ma-0 pa-0">
|
||||
<v-btn v-if="incoming && !readOnlyRoom" id="btn-reply" icon @click.stop="addReply" class="ma-0 pa-0">
|
||||
<v-icon>reply</v-icon>
|
||||
</v-btn>
|
||||
<v-btn id="btn-edit" icon @click.stop="edit" class="ma-0 pa-0" v-if="isEditable">
|
||||
|
|
@ -40,6 +40,12 @@ export default {
|
|||
default: function () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
readOnlyRoom: {
|
||||
type: Boolean,
|
||||
default: function () {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export default {
|
|||
editedRoomTopic: "",
|
||||
isRoomTopicEditMode: false,
|
||||
roomTopicErrorMessage: null,
|
||||
}
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$matrix.on("Room.timeline", this.roomInfoMixinOnEvent);
|
||||
|
|
@ -61,7 +61,10 @@ export default {
|
|||
publicRoomLink() {
|
||||
if (this.room && this.roomJoinRule == "public") {
|
||||
return this.$router.getRoomLink(
|
||||
this.room.getCanonicalAlias(), this.room.roomId, this.room.name, utils.roomDisplayTypeToQueryParam(this.room, this.roomDisplayType)
|
||||
this.room.getCanonicalAlias(),
|
||||
this.room.roomId,
|
||||
this.room.name,
|
||||
utils.roomDisplayTypeToQueryParam(this.room, this.roomDisplayType)
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
|
@ -69,7 +72,7 @@ export default {
|
|||
|
||||
roomHistory() {
|
||||
if (this.room) {
|
||||
return this.room.shouldEncryptForInvitedMembers()
|
||||
return this.room.shouldEncryptForInvitedMembers();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
|
@ -92,13 +95,24 @@ export default {
|
|||
*/
|
||||
privateParty() {
|
||||
if (this.isPrivate) {
|
||||
const membersButMe = this.room.getMembers().filter(m => m.userId != this.$matrix.currentUserId);
|
||||
const membersButMe = this.room.getMembers().filter((m) => m.userId != this.$matrix.currentUserId);
|
||||
if (membersButMe.length == 1) {
|
||||
return membersButMe[0];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
canViewRetentionPolicy() {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true if we can set message retention policy in the room.
|
||||
*/
|
||||
canChangeRetentionPolicy() {
|
||||
return this.userCanExportChat;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
room: {
|
||||
|
|
@ -115,14 +129,48 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Get a string describing current room retention setting.
|
||||
* Can be "None", "1 week", "1 hour" etc...
|
||||
*/
|
||||
roomMessageRetentionDisplay(maybeEvent) {
|
||||
const retention = this.roomMessageRetention(maybeEvent);
|
||||
if (retention == 0) {
|
||||
return this.$t("room_info.message_retention_none");
|
||||
} else if (retention == 60 * 60 * 1000) {
|
||||
return this.$t("room_info.message_retention_1_hour");
|
||||
} else if (retention == 24 * 60 * 60 * 1000) {
|
||||
return this.$t("room_info.message_retention_1_day");
|
||||
} else if (retention == 7 * 24 * 60 * 60 * 1000) {
|
||||
return this.$t("room_info.message_retention_1_week");
|
||||
} else if (retention == 30 * 24 * 60 * 60 * 1000) {
|
||||
return this.$t("room_info.message_retention_30_days");
|
||||
} else if (retention == 365 * 24 * 60 * 60 * 1000) {
|
||||
return this.$t("room_info.message_retention_365_days");
|
||||
}
|
||||
return this.$t("room_info.message_retention_other_seconds", { count: retention / 1000 });
|
||||
},
|
||||
|
||||
roomMessageRetention(maybeEvent) {
|
||||
const retentionEvent = maybeEvent || (this.room && this.room.currentState.getStateEvents("m.room.retention", ""));
|
||||
if (retentionEvent) {
|
||||
console.log("Retention event found", JSON.stringify(retentionEvent));
|
||||
const maxLifetime = parseInt(retentionEvent.getContent().max_lifetime);
|
||||
if (maxLifetime) {
|
||||
return maxLifetime;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
onRoomNameClicked() {
|
||||
if(this.userCanPurgeRoom) {
|
||||
if (this.userCanPurgeRoom) {
|
||||
this.isRoomNameEditMode = !this.isRoomNameEditMode;
|
||||
this.editedRoomName = this.roomName;
|
||||
}
|
||||
},
|
||||
updateRoomName() {
|
||||
if(this.editedRoomName) {
|
||||
if (this.editedRoomName) {
|
||||
this.$matrix.matrixClient.setRoomName(this.room.roomId, this.editedRoomName);
|
||||
this.isRoomNameEditMode = !this.isRoomNameEditMode;
|
||||
} else {
|
||||
|
|
@ -130,13 +178,13 @@ export default {
|
|||
}
|
||||
},
|
||||
onRoomTopicClicked() {
|
||||
if(this.userCanPurgeRoom) {
|
||||
if (this.userCanPurgeRoom) {
|
||||
this.isRoomTopicEditMode = !this.isRoomTopicEditMode;
|
||||
this.editedRoomTopic = this.roomTopic;
|
||||
}
|
||||
},
|
||||
updateRoomTopic() {
|
||||
if(this.editedRoomTopic) {
|
||||
if (this.editedRoomTopic) {
|
||||
this.$matrix.matrixClient.setRoomTopic(this.room.roomId, this.editedRoomTopic);
|
||||
this.isRoomTopicEditMode = !this.isRoomTopicEditMode;
|
||||
} else {
|
||||
|
|
@ -154,14 +202,8 @@ export default {
|
|||
if (this.room) {
|
||||
this.roomJoinRule = this.getRoomJoinRule();
|
||||
const canChangeAccess =
|
||||
this.room.currentState.mayClientSendStateEvent(
|
||||
"m.room.join_rules",
|
||||
this.$matrix.matrixClient
|
||||
) &&
|
||||
this.room.currentState.mayClientSendStateEvent(
|
||||
"m.room.guest_access",
|
||||
this.$matrix.matrixClient
|
||||
);
|
||||
this.room.currentState.mayClientSendStateEvent("m.room.join_rules", this.$matrix.matrixClient) &&
|
||||
this.room.currentState.mayClientSendStateEvent("m.room.guest_access", this.$matrix.matrixClient);
|
||||
this.userCanChangeJoinRule = canChangeAccess;
|
||||
this.userCanPurgeRoom = canChangeAccess; //TODO - need different permissions here?
|
||||
} else {
|
||||
|
|
@ -175,10 +217,7 @@ export default {
|
|||
if (event.getRoomId() !== this.roomId) {
|
||||
return; // Not for this room
|
||||
}
|
||||
if (
|
||||
event.getType() == "m.room.join_rules" ||
|
||||
event.getType() == "m.room.guest_access"
|
||||
) {
|
||||
if (event.getType() == "m.room.join_rules" || event.getType() == "m.room.guest_access") {
|
||||
this.updatePermissions();
|
||||
}
|
||||
},
|
||||
|
|
@ -186,15 +225,9 @@ export default {
|
|||
privatePartyAvatar(size) {
|
||||
const other = this.privateParty;
|
||||
if (other) {
|
||||
return other.getAvatarUrl(
|
||||
this.$matrix.matrixClient.getHomeserverUrl(),
|
||||
size,
|
||||
size,
|
||||
"scale",
|
||||
true
|
||||
);
|
||||
return other.getAvatarUrl(this.$matrix.matrixClient.getHomeserverUrl(), size, size, "scale", true);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
@ -1,17 +1,13 @@
|
|||
import { isMobileOrTabletBrowser } from './utils'
|
||||
|
||||
// Installing your PWA is required for periodic syncs to work
|
||||
const registerPeriodicBackgroundSync = async (registration) => {
|
||||
// Check if periodicSync is supported
|
||||
if ('periodicSync' in registration) {
|
||||
// Request permission
|
||||
const status = await navigator.permissions.query({
|
||||
name: 'periodic-background-sync',
|
||||
});
|
||||
if (status.state === 'granted') {
|
||||
console.log('PBS registered and granted')
|
||||
console.log('Periodic background sync registered and granted')
|
||||
|
||||
try {
|
||||
// Register the periodic background sync.
|
||||
await registration.periodicSync.register('check-new-messages', {
|
||||
// minInterval is one day
|
||||
minInterval: 24 * 60 * 60 * 1000,
|
||||
|
|
@ -19,11 +15,10 @@ const registerPeriodicBackgroundSync = async (registration) => {
|
|||
console.log('Periodic background sync registered!');
|
||||
console.log(registration.periodicSync.getTags())
|
||||
|
||||
// List registered periodic background sync tags.
|
||||
const tags = await registration.periodicSync.getTags();
|
||||
if (tags.length) {
|
||||
tags.forEach((tag) => {
|
||||
console.log('tag')
|
||||
console.log('tag name')
|
||||
console.log(tag)
|
||||
});
|
||||
}
|
||||
|
|
@ -39,15 +34,15 @@ const registerPeriodicBackgroundSync = async (registration) => {
|
|||
}
|
||||
|
||||
export function registerServiceWorker() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', async () => {
|
||||
const registration = await navigator.serviceWorker.register("/sw.js");
|
||||
console.log('Service worker registered for scope', registration.scope);
|
||||
|
||||
if(isMobileOrTabletBrowser) {
|
||||
if("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.register("./sw.js")
|
||||
.then(async registration => {
|
||||
console.log('Service Worker registered with scope:', registration.scope);
|
||||
await registerPeriodicBackgroundSync(registration);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Service Worker registration failed:', error);
|
||||
});
|
||||
} else {
|
||||
console.log("No Service Worker support!");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -924,17 +924,6 @@ class Util {
|
|||
});
|
||||
}
|
||||
|
||||
isMobileOrTabletBrowser() {
|
||||
// Regular expression to match common mobile and tablet browser user agent strings
|
||||
const mobileTabletPattern = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Tablet|Mobile|CriOS/i;
|
||||
|
||||
// Get the user agent string
|
||||
const userAgent = navigator.userAgent;
|
||||
|
||||
// Check if the user agent matches the pattern for mobile or tablet browsers
|
||||
return mobileTabletPattern.test(userAgent);
|
||||
}
|
||||
|
||||
getMatrixBaseUrl(user, config) {
|
||||
if (user) {
|
||||
const domain = User.domainPart(user.user_id);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue