Compare commits
43
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87c73869af | ||
|
|
763a525292 | ||
|
|
f3e54e46b5 | ||
|
|
e73a920154 | ||
|
|
9c4db41b76 | ||
|
|
7ba20c78b1 | ||
|
|
a6c29e3e5d | ||
|
|
1d0c7e1c73 | ||
|
|
36aa610b8e | ||
|
|
8822c2892e | ||
|
|
ddfbfdcbb3 | ||
|
|
faf644d9dd | ||
|
|
c34ba46563 | ||
|
|
b783ec2949 | ||
|
|
0840a79024 | ||
|
|
4399428fdf | ||
|
|
e0e25fc6d3 | ||
|
|
25e9c1a885 | ||
|
|
b59fd06381 | ||
|
|
b540ac44df | ||
|
|
6c739d9c43 | ||
|
|
b2daf6b58e | ||
|
|
063245e665 | ||
|
|
e3bdd5bb45 | ||
|
|
122fd018d0 | ||
|
|
febc75da17 | ||
|
|
eff8bbabc5 | ||
|
|
c7f4fa0165 | ||
|
|
a0312f8b52 | ||
|
|
f8879f7b68 | ||
|
|
39c8b9a4a1 | ||
|
|
812cdd970c | ||
|
|
a24ee01b37 | ||
|
|
6b8595750a | ||
|
|
aa451946a5 | ||
|
|
d1ea1d7658 | ||
|
|
626484d8cb | ||
|
|
864753942f | ||
|
|
66c2db86d9 | ||
|
|
bf9ba90ed3 | ||
|
|
07aa38e162 | ||
|
|
181161250a | ||
|
|
019b270242 |
@@ -28,16 +28,17 @@ This is NOT a standalone app, this is just a frontend, a client. It requires a [
|
||||
|
||||
The app is fully internationalized using [React-Intl](https://github.com/formatjs/react-intl). The UI language is selected automatically from the language specified by the browser. A specific language can be forced by adding `#?hl=XX` parameter to the URL when starting the app, e.g. https://web.tinode.co/#?hl=ru.
|
||||
|
||||
As of the time of this writing the following translations exist:
|
||||
The app is currently available in the following languages:
|
||||
* English
|
||||
* Chinese (simplified)
|
||||
* Chinese (traditional)
|
||||
* German
|
||||
* Korean
|
||||
* Romanian
|
||||
* Russian
|
||||
* Spanish
|
||||
|
||||
More translations are welcome. The source file with the original English strings and context explanations is located at [/src/i18n/ex/base-en.json](/src/i18n/ex/base-en.json). Only `defaultMessage` needs to be translated. Send a pull request with a json file with translated strings. Take a look at [Spanish](/src/i18n/es.json), [Russian](/src/i18n/ru.json), or [Simplified Chinese](/src/i18n/zh.json) translations for guidance.
|
||||
More translations are welcome. See [instructions](https://github.com/tinode/chat/blob/devel/docs/translations.md#webapp).
|
||||
|
||||
|
||||
## Not done yet
|
||||
|
||||
+9
-2
@@ -373,6 +373,10 @@ div.alert .content {
|
||||
color: #2196f3; /* blue 500 */
|
||||
}
|
||||
|
||||
.green {
|
||||
color: #388e3c; /* green 700 */
|
||||
}
|
||||
|
||||
.red {
|
||||
color: #D32F2F; /* red 700 */
|
||||
}
|
||||
@@ -462,7 +466,7 @@ div.alert .content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
max-width: 80rem;
|
||||
max-width: 84rem;
|
||||
overflow: hidden;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
@@ -1416,7 +1420,7 @@ i.search {
|
||||
.chat-box .bubble {
|
||||
position: relative;
|
||||
display: flex;
|
||||
max-width: 40rem;
|
||||
max-width: 36rem;
|
||||
min-width: 9rem;
|
||||
height: auto;
|
||||
padding: 0.75rem 0.5rem 0.75rem 0.5rem;
|
||||
@@ -1936,6 +1940,9 @@ ul.tabbar li.active > a {
|
||||
.material-icons.big {
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
.material-icons.huge {
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
#self-avatar .material-icons {
|
||||
font-size: 2.4rem;
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+5
-5
@@ -22,11 +22,11 @@
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,400i,500,700,700i" />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
|
||||
<link rel="stylesheet" href="css/base.min.css" />
|
||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react@17.0.1/umd/react.production.min.js"></script>
|
||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react-dom@17.0.1/umd/react-dom.production.min.js"></script>
|
||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react-intl@5.18.3/react-intl.iife.min.js"></script>
|
||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@8.6.7/firebase-app.js"></script>
|
||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@8.6.7/firebase-messaging.js"></script>
|
||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react@17.0.2/umd/react.production.min.js"></script>
|
||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
|
||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react-intl@5.20.7/react-intl.iife.min.js"></script>
|
||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@8.9.1/firebase-app.js"></script>
|
||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@8.9.1/firebase-messaging.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript><div id="noscript-message"><div id="noscript-title">Javascript support is required.</div>
|
||||
|
||||
Generated
+1398
-1793
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "tinode-webapp",
|
||||
"description": "Tinode messenger for the web",
|
||||
"version": "0.17.2",
|
||||
"version": "0.17.10",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tinode/example-react-js.git"
|
||||
@@ -52,7 +52,8 @@
|
||||
"ko",
|
||||
"ro",
|
||||
"ru",
|
||||
"zh"
|
||||
"zh",
|
||||
"zh-TW"
|
||||
],
|
||||
"main": "umd/index.prod.js",
|
||||
"bugs": {
|
||||
@@ -63,11 +64,11 @@
|
||||
"author": "Tinode Authors <info@tinode.co>",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"firebase": "^8.6.7",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"firebase": "^8.9.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-intl": "^5.20.2",
|
||||
"tinode-sdk": "^0.17.2"
|
||||
"tinode-sdk": "^0.17.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.5",
|
||||
|
||||
+12
-5
@@ -1,11 +1,12 @@
|
||||
// Must be located at the root.
|
||||
importScripts('https://cdn.jsdelivr.net/npm/firebase@8.6.7/firebase-app.js');
|
||||
importScripts('https://cdn.jsdelivr.net/npm/firebase@8.6.7/firebase-messaging.js');
|
||||
importScripts('https://cdn.jsdelivr.net/npm/firebase@8.9.1/firebase-app.js');
|
||||
importScripts('https://cdn.jsdelivr.net/npm/firebase@8.9.1/firebase-messaging.js');
|
||||
importScripts('firebase-init.js');
|
||||
importScripts('version.js');
|
||||
|
||||
// Channel to notify the webapp.
|
||||
const webAppChannel = new BroadcastChannel('tinode-sw');
|
||||
// Channel to notify the webapp. There is no BroadcastChannel in Safari.
|
||||
const webAppChannel = (typeof BroadcastChannel == 'function') ?
|
||||
new BroadcastChannel('tinode-sw') : null;
|
||||
|
||||
// Basic internationalization.
|
||||
const i18n = {
|
||||
@@ -36,6 +37,10 @@ const i18n = {
|
||||
'zh': {
|
||||
'new_message': "新讯息",
|
||||
'new_chat': "新聊天",
|
||||
},
|
||||
'zh-TW': {
|
||||
'new_message': "新訊息",
|
||||
'new_chat': "新聊天",
|
||||
}
|
||||
};
|
||||
self.i18nMessage = function(id) {
|
||||
@@ -56,7 +61,9 @@ fbMessaging.onBackgroundMessage((payload) => {
|
||||
}
|
||||
|
||||
// Notify webapp that a message was received.
|
||||
webAppChannel.postMessage(payload.data);
|
||||
if (webAppChannel) {
|
||||
webAppChannel.postMessage(payload.data);
|
||||
}
|
||||
|
||||
const pushType = payload.data.what || 'msg';
|
||||
const title = payload.data.title || self.i18nMessage(pushType == 'msg' ? 'new_message' : 'new_chat');
|
||||
|
||||
+4
-1
@@ -175,5 +175,8 @@
|
||||
"channel": "Sender",
|
||||
"drafty_attachment": "Dateianhang",
|
||||
"drafty_form": "Formular:",
|
||||
"drafty_image": "Bild"
|
||||
"drafty_image": "Bild",
|
||||
"topic_delete": "Chat löschen",
|
||||
"password_reset_email_sent": "Eine E-Mail wurde an {email} gesendet. Folgen Sie den Anweisungen in der E-Mail, um Ihr Passwort zurückzusetzen.",
|
||||
"menu_item_restore_topic": "Wiederherstellen"
|
||||
}
|
||||
|
||||
+4
-1
@@ -105,6 +105,7 @@
|
||||
"menu_item_info": "Info",
|
||||
"menu_item_member_delete": "Remove",
|
||||
"menu_item_mute": "Mute",
|
||||
"menu_item_restore_topic": "Restore",
|
||||
"menu_item_send_retry": "Retry",
|
||||
"menu_item_unblock": "Unblock",
|
||||
"menu_item_unmute": "Unmute",
|
||||
@@ -120,6 +121,7 @@
|
||||
"numeric_confirmation_code_prompt": "Numbers only",
|
||||
"online_now": "online now",
|
||||
"password_prompt": "Password",
|
||||
"password_reset_email_sent": "An email has been sent to {email}. Follow the directions in the email to reset your password.",
|
||||
"password_unchanged_prompt": "Unchanged",
|
||||
"peers_messaging_disabled": "Peer's messaging is disabled.",
|
||||
"permission_admin": "Approve ({val})",
|
||||
@@ -169,7 +171,8 @@
|
||||
"title_permissions": "Permissions",
|
||||
"title_tag_manager": "Tags (user discovery)",
|
||||
"topic_block_warning": "Are you sure you want to block this conversation?",
|
||||
"topic_delete_warning": "Are you sure you want to delete this conversation?",
|
||||
"topic_delete": "Delete Conversation",
|
||||
"topic_delete_warning": "Are you sure you want to delete this conversation? It cannot be undone.",
|
||||
"topic_name_editing_placeholder": "Freeform name of the group",
|
||||
"unnamed_topic": "Unnamed",
|
||||
"update_available": "Update available.",
|
||||
|
||||
+5
-2
@@ -162,7 +162,7 @@
|
||||
"title_permissions": "Permisos",
|
||||
"message_sending": "enviando...",
|
||||
"message_sending_failed": "no se pudo enviar el mensaje",
|
||||
"search_placeholder": "Ej. email:alice@example.com, tel:17025550003...",
|
||||
"search_placeholder": "Ej. email:alba@example.com, tel:17025550003...",
|
||||
"messaging_disabled_prompt": "El envío de mensajes está deshabilitado",
|
||||
"new_message_prompt": "Nuevo mensaje",
|
||||
"image_caption_prompt": "Añade un comentario",
|
||||
@@ -175,5 +175,8 @@
|
||||
"channel": "canal",
|
||||
"drafty_attachment": "Archivo",
|
||||
"drafty_form": "Formulario:",
|
||||
"drafty_image": "Imagen"
|
||||
"drafty_image": "Imagen",
|
||||
"topic_delete": "Eliminar chat",
|
||||
"password_reset_email_sent": "Se ha enviado un correo electrónico a {email}. Siga las instrucciones del correo electrónico para restablecer su contraseña.",
|
||||
"menu_item_restore_topic": "Restaurar"
|
||||
}
|
||||
|
||||
@@ -421,6 +421,10 @@
|
||||
"defaultMessage": "Mute",
|
||||
"description": "Turn off notifications"
|
||||
},
|
||||
"menu_item_restore_topic": {
|
||||
"defaultMessage": "Restore",
|
||||
"description": "Restore topic from archive"
|
||||
},
|
||||
"menu_item_send_retry": {
|
||||
"defaultMessage": "Retry",
|
||||
"description": "Retry sending message"
|
||||
@@ -481,6 +485,10 @@
|
||||
"defaultMessage": "Password",
|
||||
"description": "Placeholder/prompt for entering password"
|
||||
},
|
||||
"password_reset_email_sent": {
|
||||
"defaultMessage": "An email has been sent to {email}. Follow the directions in the email to reset your password.",
|
||||
"description": "Notification that the email with password reset instructions has been sent"
|
||||
},
|
||||
"password_unchanged_prompt": {
|
||||
"defaultMessage": "Unchanged",
|
||||
"description": "Message in editor while password is unchanged"
|
||||
@@ -677,8 +685,12 @@
|
||||
"defaultMessage": "Are you sure you want to block this conversation?",
|
||||
"description": "Alert warning when blocking a topic."
|
||||
},
|
||||
"topic_delete": {
|
||||
"defaultMessage": "Delete Conversation",
|
||||
"description": "Alert title when deleting the topic."
|
||||
},
|
||||
"topic_delete_warning": {
|
||||
"defaultMessage": "Are you sure you want to delete this conversation?",
|
||||
"defaultMessage": "Are you sure you want to delete this conversation? It cannot be undone.",
|
||||
"description": "Alert warning when deleting entire topic"
|
||||
},
|
||||
"topic_name_editing_placeholder": {
|
||||
|
||||
+5
-2
@@ -162,7 +162,7 @@
|
||||
"title_permissions": "권한",
|
||||
"message_sending": "보내기...",
|
||||
"message_sending_failed": "실패",
|
||||
"search_placeholder": "email:alice@example.com,tel:17025550003...와같은목록",
|
||||
"search_placeholder": "email:kim@example.com,tel:17025550003...와같은목록",
|
||||
"messaging_disabled_prompt": "메시지비활성화",
|
||||
"new_message_prompt": "새메시지",
|
||||
"image_caption_prompt": "이미지설명",
|
||||
@@ -178,5 +178,8 @@
|
||||
"channel": "채널",
|
||||
"drafty_attachment": "부착",
|
||||
"drafty_form": "형태:",
|
||||
"drafty_image": "이미지"
|
||||
"drafty_image": "이미지",
|
||||
"topic_delete": "채팅 삭제",
|
||||
"password_reset_email_sent": "이메일이 {email}(으)로 전송되었습니다. 이메일의 지침에 따라 비밀번호를 재설정하세요.",
|
||||
"menu_item_restore_topic": "복원"
|
||||
}
|
||||
|
||||
+4
-1
@@ -175,5 +175,8 @@
|
||||
"title_manage_tags": "Administrează",
|
||||
"drafty_attachment": "Atasament",
|
||||
"drafty_form": "Formă:",
|
||||
"drafty_image": "Imagine"
|
||||
"drafty_image": "Imagine",
|
||||
"topic_delete": "Ștergeți Chat",
|
||||
"password_reset_email_sent": "Un e-mail a fost trimis către {email}. Urmați instrucțiunile din e-mail pentru a vă reseta parola.",
|
||||
"menu_item_restore_topic": "Restabili"
|
||||
}
|
||||
|
||||
+4
-1
@@ -175,5 +175,8 @@
|
||||
"channel": "канал",
|
||||
"drafty_attachment": "Аттачмент",
|
||||
"drafty_form": "Форма:",
|
||||
"drafty_image": "Картинка"
|
||||
"drafty_image": "Картинка",
|
||||
"topic_delete": "Удалить чат",
|
||||
"password_reset_email_sent": "Сообщение было отправлено на адрес {email}. Следуйте инструкциям в сообщении, чтобы изменить пароль.",
|
||||
"menu_item_restore_topic": "Восстановить"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"action_block_contact": "封鎖聯絡人",
|
||||
"action_cancel": "取消",
|
||||
"action_clear_messages": "清除訊息",
|
||||
"action_delete_messages": "為所有人清除訊息",
|
||||
"action_leave_chat": "離開對話",
|
||||
"action_more": "更多",
|
||||
"action_report_chat": "檢舉對話",
|
||||
"archived_contacts": "已封存的聯絡人 ({count})",
|
||||
"badge_owner": "擁有者",
|
||||
"badge_you": "您",
|
||||
"block_contact_warning": "您確定要封鎖此聯絡人嗎?",
|
||||
"blocked_contacts_link": "已封鎖的聯絡人 ({count})",
|
||||
"button_add_another": "新增其他人",
|
||||
"button_add_members": "新增成員",
|
||||
"button_cancel": "取消",
|
||||
"button_confirm": "確認",
|
||||
"button_create": "建立",
|
||||
"button_delete_account": "刪除帳號",
|
||||
"button_edit": "編輯",
|
||||
"button_logout": "登出",
|
||||
"button_ok": "OK",
|
||||
"button_reset": "重設",
|
||||
"button_send_request": "傳送要求",
|
||||
"button_sign_in": "登入",
|
||||
"button_sign_up": "註冊",
|
||||
"button_subscribe": "訂閱",
|
||||
"button_update": "更新",
|
||||
"cannot_initiate_file_upload": "無法為檔案上傳初始化。",
|
||||
"channel": "頻道",
|
||||
"channel_prompt": "這是頻道",
|
||||
"chat_invitation": "您已被邀請開始新對話。您接下來要做什麼呢?",
|
||||
"chat_invitation_accept": "接受",
|
||||
"chat_invitation_block": "封鎖",
|
||||
"chat_invitation_ignore": "忽略",
|
||||
"clear_messages_warning": "您確定要清除所有訊息嗎?此操作無法復原。",
|
||||
"code_doesnot_match": "代碼不相符",
|
||||
"contacts_not_found": "您沒有任何對話<br />¯∖_(ツ)_/¯",
|
||||
"contacts_not_found_short": "沒有符合 ''{query}'' 的聯絡人",
|
||||
"credential_email_prompt": "您的註冊電子郵件地址",
|
||||
"delete_account": "刪除帳號",
|
||||
"delete_account_arning": "您確定要刪除您的帳號嗎?此操作無法復原。",
|
||||
"delete_messages_warning": "您確定要為所有人刪除所有訊息嗎?此操作無法復原。",
|
||||
"deleted_content": "內容已被刪除",
|
||||
"download_action": "下載",
|
||||
"drafty_attachment": "附加檔案",
|
||||
"drafty_form": "表單:",
|
||||
"drafty_image": "圖片",
|
||||
"email_dative": "電子郵件地址",
|
||||
"email_prompt": "電子郵件地址,例如:jdoe@example.com",
|
||||
"enable_peers_messaging": "啟用",
|
||||
"enter_confirmation_code_prompt": "輸入透過 {method} 傳送給您的確認碼:",
|
||||
"error_invalid_id": "ID 無效",
|
||||
"file_attachment_too_large": "檔案大小 {size} 超出 {limit} 限制。",
|
||||
"forgot_password_link": "忘記密碼?",
|
||||
"full_name_prompt": "全名,例如:王小明",
|
||||
"granted_permissions": "已授權",
|
||||
"group_has_no_members": "無成員",
|
||||
"group_user_id_prompt": "群組或使用者 ID",
|
||||
"image_caption_prompt": "圖片說明",
|
||||
"invalid_content": "內容無效",
|
||||
"invalid_security_token": "安全權杖無效",
|
||||
"label_client": "客戶端:",
|
||||
"label_content_type": "類型:",
|
||||
"label_default_access": "預設存取模式:",
|
||||
"label_default_access_mode": "預設存取模式:",
|
||||
"label_file_name": "檔名:",
|
||||
"label_group_members": "群組成員:",
|
||||
"label_incognito_mode": "無痕模式:",
|
||||
"label_message_sound": "訊息提示聲:",
|
||||
"label_muting_topic": "靜音:",
|
||||
"label_other_user": "其他",
|
||||
"label_password": "密碼",
|
||||
"label_permissions": "權限:",
|
||||
"label_private": "私人留言",
|
||||
"label_push_notifications": "通知:",
|
||||
"label_push_notifications_disabled": "通知 (需要 HTTPS):",
|
||||
"label_reset_password": "傳送重設密碼郵件:",
|
||||
"label_sdk": "SDK:",
|
||||
"label_server": "伺服器:",
|
||||
"label_server_address": "伺服器位址:",
|
||||
"label_server_to_use": "使用的伺服器:",
|
||||
"label_size": "大小:",
|
||||
"label_topic_name": "名稱",
|
||||
"label_user_contacts": "聯絡人:",
|
||||
"label_user_id": "ID:",
|
||||
"label_wire_transport": "Wire transport:",
|
||||
"label_you": "您:",
|
||||
"label_your_name": "您的名字",
|
||||
"label_your_permissions": "您的權限:",
|
||||
"last_seen_timestamp": "最後上線",
|
||||
"leave_chat_warning": "您確定要離開此對話嗎?",
|
||||
"link_contact_us": "聯絡我們",
|
||||
"link_privacy_policy": "隱私權政策",
|
||||
"link_terms_of_service": "服務條款",
|
||||
"login_prompt": "登入",
|
||||
"menu_item_archive_topic": "封存",
|
||||
"menu_item_block": "封鎖",
|
||||
"menu_item_clear_messages": "清除訊息",
|
||||
"menu_item_clear_messages_for_all": "為所有人清除訊息",
|
||||
"menu_item_delete": "刪除",
|
||||
"menu_item_delete_for_all": "為所有人刪除",
|
||||
"menu_item_delete_topic": "刪除",
|
||||
"menu_item_edit_permissions": "編輯權限",
|
||||
"menu_item_info": "Info",
|
||||
"menu_item_member_delete": "移除",
|
||||
"menu_item_mute": "靜音",
|
||||
"menu_item_send_retry": "重試",
|
||||
"menu_item_unblock": "解除封鎖",
|
||||
"menu_item_unmute": "解除靜音",
|
||||
"message_sending": "正在傳送...",
|
||||
"message_sending_failed": "失敗",
|
||||
"messages_not_readable": "沒有存取訊息的權限",
|
||||
"messaging_disabled_prompt": "停用訊息",
|
||||
"more_online_members": "+{overflow}",
|
||||
"new_message_prompt": "新訊息",
|
||||
"new_password_placeholder": "輸入新的密碼",
|
||||
"no_connection": "無連線",
|
||||
"no_contacts": "您沒有任何聯絡人 :-(",
|
||||
"numeric_confirmation_code_prompt": "僅數字",
|
||||
"online_now": "上線中",
|
||||
"password_prompt": "密碼",
|
||||
"password_unchanged_prompt": "未修改",
|
||||
"peers_messaging_disabled": "Peer 已停用訊息。",
|
||||
"permission_admin": "核可 ({val})",
|
||||
"permission_delete": "刪除 ({val})",
|
||||
"permission_join": "加入 ({val})",
|
||||
"permission_owner": "擁有者 ({val})",
|
||||
"permission_pres": "收到通知 ({val})",
|
||||
"permission_read": "讀取 ({val})",
|
||||
"permission_share": "分享 ({val})",
|
||||
"permission_write": "撰寫 ({val})",
|
||||
"phone_dative": "phone",
|
||||
"private_editing_placeholder": "僅您可見",
|
||||
"push_init_failed": "初始化通知失敗。",
|
||||
"reconnect_countdown": "已斷線。將在 {seconds} 秒後重新連線…",
|
||||
"reconnect_now": "現在重試",
|
||||
"reload_update": "重新載入",
|
||||
"report_chat_warning": "您確定要封鎖並檢舉此對話嗎?",
|
||||
"requested_permissions": "重新請求",
|
||||
"save_attachment": "儲存",
|
||||
"search_for_contacts": "使用搜尋來尋找聯絡人",
|
||||
"search_no_results": "搜尋無結果",
|
||||
"search_placeholder": "例如 email:alice@example.com, tel:17025550003... 的清單",
|
||||
"sidepanel_title_acc_general": "一般",
|
||||
"sidepanel_title_acc_notifications": "通知",
|
||||
"sidepanel_title_acc_security": "安全性",
|
||||
"sidepanel_title_acc_support": "支援",
|
||||
"sidepanel_title_account_settings": "帳號設定",
|
||||
"sidepanel_title_archive": "已封存的對話",
|
||||
"sidepanel_title_blocked": "已封鎖的對話",
|
||||
"sidepanel_title_cred": "確認認證",
|
||||
"sidepanel_title_login": "登入",
|
||||
"sidepanel_title_newtpk": "開始新對話",
|
||||
"sidepanel_title_register": "建立帳號",
|
||||
"sidepanel_title_reset": "重設密碼",
|
||||
"sidepanel_title_settings": "設定",
|
||||
"stay_logged_in": "保持登入",
|
||||
"tabtitle_find_user": "尋找",
|
||||
"tabtitle_group_by_id": "以 id",
|
||||
"tabtitle_new_group": "新群組",
|
||||
"tags_editor_no_tags": "新增一些標籤",
|
||||
"tags_not_found": "未定義任何標籤。請新增一些標籤。",
|
||||
"title_all_contacts": "所有聯絡人",
|
||||
"title_group_members": "群組成員",
|
||||
"title_info": "資訊",
|
||||
"title_manage_tags": "管理",
|
||||
"title_not_found": "找不到",
|
||||
"title_permissions": "權限",
|
||||
"title_tag_manager": "標籤 (使用者探索)",
|
||||
"topic_block_warning": "您確定要封鎖此對話嗎?",
|
||||
"topic_delete_warning": "您確定要刪除此對話嗎?",
|
||||
"topic_name_editing_placeholder": "群組的任意名稱",
|
||||
"unnamed_topic": "未命名",
|
||||
"update_available": "有可用的更新。",
|
||||
"upload_finishing": "正在完成...",
|
||||
"user_not_found": "找不到",
|
||||
"validate_credential_action": "確認",
|
||||
"topic_delete": "刪除聊天",
|
||||
"password_reset_email_sent": "一封電子郵件已發送至 {email}。按照電子郵件中的說明重置密碼。",
|
||||
"menu_item_restore_topic": "恢復"
|
||||
}
|
||||
+4
-1
@@ -175,5 +175,8 @@
|
||||
"channel": "频道",
|
||||
"drafty_attachment": "附件",
|
||||
"drafty_form": "形式:",
|
||||
"drafty_image": "图像"
|
||||
"drafty_image": "图像",
|
||||
"topic_delete": "删除聊天",
|
||||
"password_reset_email_sent": "一封电子邮件已发送至 {email}。按照电子邮件中的说明重置密码。",
|
||||
"menu_item_restore_topic": "从存档中恢复"
|
||||
}
|
||||
|
||||
+5
-3
@@ -29,8 +29,10 @@ const messages =
|
||||
allMessages.en;
|
||||
|
||||
ReactDOM.render(
|
||||
<IntlProvider locale={language} messages={messages} textComponent={React.Fragment}>
|
||||
<TinodeWeb />
|
||||
</IntlProvider>,
|
||||
<React.StrictMode>
|
||||
<IntlProvider locale={language} messages={messages} textComponent={React.Fragment}>
|
||||
<TinodeWeb />
|
||||
</IntlProvider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('mountPoint')
|
||||
);
|
||||
|
||||
+212
-9
@@ -176,7 +176,10 @@
|
||||
"channel": "Sender",
|
||||
"drafty_attachment": "Dateianhang",
|
||||
"drafty_form": "Formular:",
|
||||
"drafty_image": "Bild"
|
||||
"drafty_image": "Bild",
|
||||
"topic_delete": "Chat löschen",
|
||||
"password_reset_email_sent": "Eine E-Mail wurde an {email} gesendet. Folgen Sie den Anweisungen in der E-Mail, um Ihr Passwort zurückzusetzen.",
|
||||
"menu_item_restore_topic": "Wiederherstellen"
|
||||
},
|
||||
"en": {
|
||||
"action_block_contact": "Block Contact",
|
||||
@@ -285,6 +288,7 @@
|
||||
"menu_item_info": "Info",
|
||||
"menu_item_member_delete": "Remove",
|
||||
"menu_item_mute": "Mute",
|
||||
"menu_item_restore_topic": "Restore",
|
||||
"menu_item_send_retry": "Retry",
|
||||
"menu_item_unblock": "Unblock",
|
||||
"menu_item_unmute": "Unmute",
|
||||
@@ -300,6 +304,7 @@
|
||||
"numeric_confirmation_code_prompt": "Numbers only",
|
||||
"online_now": "online now",
|
||||
"password_prompt": "Password",
|
||||
"password_reset_email_sent": "An email has been sent to {email}. Follow the directions in the email to reset your password.",
|
||||
"password_unchanged_prompt": "Unchanged",
|
||||
"peers_messaging_disabled": "Peer's messaging is disabled.",
|
||||
"permission_admin": "Approve ({val})",
|
||||
@@ -349,7 +354,8 @@
|
||||
"title_permissions": "Permissions",
|
||||
"title_tag_manager": "Tags (user discovery)",
|
||||
"topic_block_warning": "Are you sure you want to block this conversation?",
|
||||
"topic_delete_warning": "Are you sure you want to delete this conversation?",
|
||||
"topic_delete": "Delete Conversation",
|
||||
"topic_delete_warning": "Are you sure you want to delete this conversation? It cannot be undone.",
|
||||
"topic_name_editing_placeholder": "Freeform name of the group",
|
||||
"unnamed_topic": "Unnamed",
|
||||
"update_available": "Update available.",
|
||||
@@ -521,7 +527,7 @@
|
||||
"title_permissions": "Permisos",
|
||||
"message_sending": "enviando...",
|
||||
"message_sending_failed": "no se pudo enviar el mensaje",
|
||||
"search_placeholder": "Ej. email:alice@example.com, tel:17025550003...",
|
||||
"search_placeholder": "Ej. email:alba@example.com, tel:17025550003...",
|
||||
"messaging_disabled_prompt": "El envío de mensajes está deshabilitado",
|
||||
"new_message_prompt": "Nuevo mensaje",
|
||||
"image_caption_prompt": "Añade un comentario",
|
||||
@@ -534,7 +540,10 @@
|
||||
"channel": "canal",
|
||||
"drafty_attachment": "Archivo",
|
||||
"drafty_form": "Formulario:",
|
||||
"drafty_image": "Imagen"
|
||||
"drafty_image": "Imagen",
|
||||
"topic_delete": "Eliminar chat",
|
||||
"password_reset_email_sent": "Se ha enviado un correo electrónico a {email}. Siga las instrucciones del correo electrónico para restablecer su contraseña.",
|
||||
"menu_item_restore_topic": "Restaurar"
|
||||
},
|
||||
"ko": {
|
||||
"validate_credential_action": "확인",
|
||||
@@ -697,7 +706,7 @@
|
||||
"title_permissions": "권한",
|
||||
"message_sending": "보내기...",
|
||||
"message_sending_failed": "실패",
|
||||
"search_placeholder": "email:alice@example.com,tel:17025550003...와같은목록",
|
||||
"search_placeholder": "email:kim@example.com,tel:17025550003...와같은목록",
|
||||
"messaging_disabled_prompt": "메시지비활성화",
|
||||
"new_message_prompt": "새메시지",
|
||||
"image_caption_prompt": "이미지설명",
|
||||
@@ -713,7 +722,10 @@
|
||||
"channel": "채널",
|
||||
"drafty_attachment": "부착",
|
||||
"drafty_form": "형태:",
|
||||
"drafty_image": "이미지"
|
||||
"drafty_image": "이미지",
|
||||
"topic_delete": "채팅 삭제",
|
||||
"password_reset_email_sent": "이메일이 {email}(으)로 전송되었습니다. 이메일의 지침에 따라 비밀번호를 재설정하세요.",
|
||||
"menu_item_restore_topic": "복원"
|
||||
},
|
||||
"ro": {
|
||||
"validate_credential_action": "confirmă",
|
||||
@@ -892,7 +904,10 @@
|
||||
"title_manage_tags": "Administrează",
|
||||
"drafty_attachment": "Atasament",
|
||||
"drafty_form": "Formă:",
|
||||
"drafty_image": "Imagine"
|
||||
"drafty_image": "Imagine",
|
||||
"topic_delete": "Ștergeți Chat",
|
||||
"password_reset_email_sent": "Un e-mail a fost trimis către {email}. Urmați instrucțiunile din e-mail pentru a vă reseta parola.",
|
||||
"menu_item_restore_topic": "Restabili"
|
||||
},
|
||||
"ru": {
|
||||
"contacts_not_found": "Чатов нет<br />¯∖_(ツ)_/¯",
|
||||
@@ -1071,7 +1086,192 @@
|
||||
"channel": "канал",
|
||||
"drafty_attachment": "Аттачмент",
|
||||
"drafty_form": "Форма:",
|
||||
"drafty_image": "Картинка"
|
||||
"drafty_image": "Картинка",
|
||||
"topic_delete": "Удалить чат",
|
||||
"password_reset_email_sent": "Сообщение было отправлено на адрес {email}. Следуйте инструкциям в сообщении, чтобы изменить пароль.",
|
||||
"menu_item_restore_topic": "Восстановить"
|
||||
},
|
||||
"zh-TW": {
|
||||
"action_block_contact": "封鎖聯絡人",
|
||||
"action_cancel": "取消",
|
||||
"action_clear_messages": "清除訊息",
|
||||
"action_delete_messages": "為所有人清除訊息",
|
||||
"action_leave_chat": "離開對話",
|
||||
"action_more": "更多",
|
||||
"action_report_chat": "檢舉對話",
|
||||
"archived_contacts": "已封存的聯絡人 ({count})",
|
||||
"badge_owner": "擁有者",
|
||||
"badge_you": "您",
|
||||
"block_contact_warning": "您確定要封鎖此聯絡人嗎?",
|
||||
"blocked_contacts_link": "已封鎖的聯絡人 ({count})",
|
||||
"button_add_another": "新增其他人",
|
||||
"button_add_members": "新增成員",
|
||||
"button_cancel": "取消",
|
||||
"button_confirm": "確認",
|
||||
"button_create": "建立",
|
||||
"button_delete_account": "刪除帳號",
|
||||
"button_edit": "編輯",
|
||||
"button_logout": "登出",
|
||||
"button_ok": "OK",
|
||||
"button_reset": "重設",
|
||||
"button_send_request": "傳送要求",
|
||||
"button_sign_in": "登入",
|
||||
"button_sign_up": "註冊",
|
||||
"button_subscribe": "訂閱",
|
||||
"button_update": "更新",
|
||||
"cannot_initiate_file_upload": "無法為檔案上傳初始化。",
|
||||
"channel": "頻道",
|
||||
"channel_prompt": "這是頻道",
|
||||
"chat_invitation": "您已被邀請開始新對話。您接下來要做什麼呢?",
|
||||
"chat_invitation_accept": "接受",
|
||||
"chat_invitation_block": "封鎖",
|
||||
"chat_invitation_ignore": "忽略",
|
||||
"clear_messages_warning": "您確定要清除所有訊息嗎?此操作無法復原。",
|
||||
"code_doesnot_match": "代碼不相符",
|
||||
"contacts_not_found": "您沒有任何對話<br />¯∖_(ツ)_/¯",
|
||||
"contacts_not_found_short": "沒有符合 ''{query}'' 的聯絡人",
|
||||
"credential_email_prompt": "您的註冊電子郵件地址",
|
||||
"delete_account": "刪除帳號",
|
||||
"delete_account_arning": "您確定要刪除您的帳號嗎?此操作無法復原。",
|
||||
"delete_messages_warning": "您確定要為所有人刪除所有訊息嗎?此操作無法復原。",
|
||||
"deleted_content": "內容已被刪除",
|
||||
"download_action": "下載",
|
||||
"drafty_attachment": "附加檔案",
|
||||
"drafty_form": "表單:",
|
||||
"drafty_image": "圖片",
|
||||
"email_dative": "電子郵件地址",
|
||||
"email_prompt": "電子郵件地址,例如:jdoe@example.com",
|
||||
"enable_peers_messaging": "啟用",
|
||||
"enter_confirmation_code_prompt": "輸入透過 {method} 傳送給您的確認碼:",
|
||||
"error_invalid_id": "ID 無效",
|
||||
"file_attachment_too_large": "檔案大小 {size} 超出 {limit} 限制。",
|
||||
"forgot_password_link": "忘記密碼?",
|
||||
"full_name_prompt": "全名,例如:王小明",
|
||||
"granted_permissions": "已授權",
|
||||
"group_has_no_members": "無成員",
|
||||
"group_user_id_prompt": "群組或使用者 ID",
|
||||
"image_caption_prompt": "圖片說明",
|
||||
"invalid_content": "內容無效",
|
||||
"invalid_security_token": "安全權杖無效",
|
||||
"label_client": "客戶端:",
|
||||
"label_content_type": "類型:",
|
||||
"label_default_access": "預設存取模式:",
|
||||
"label_default_access_mode": "預設存取模式:",
|
||||
"label_file_name": "檔名:",
|
||||
"label_group_members": "群組成員:",
|
||||
"label_incognito_mode": "無痕模式:",
|
||||
"label_message_sound": "訊息提示聲:",
|
||||
"label_muting_topic": "靜音:",
|
||||
"label_other_user": "其他",
|
||||
"label_password": "密碼",
|
||||
"label_permissions": "權限:",
|
||||
"label_private": "私人留言",
|
||||
"label_push_notifications": "通知:",
|
||||
"label_push_notifications_disabled": "通知 (需要 HTTPS):",
|
||||
"label_reset_password": "傳送重設密碼郵件:",
|
||||
"label_sdk": "SDK:",
|
||||
"label_server": "伺服器:",
|
||||
"label_server_address": "伺服器位址:",
|
||||
"label_server_to_use": "使用的伺服器:",
|
||||
"label_size": "大小:",
|
||||
"label_topic_name": "名稱",
|
||||
"label_user_contacts": "聯絡人:",
|
||||
"label_user_id": "ID:",
|
||||
"label_wire_transport": "Wire transport:",
|
||||
"label_you": "您:",
|
||||
"label_your_name": "您的名字",
|
||||
"label_your_permissions": "您的權限:",
|
||||
"last_seen_timestamp": "最後上線",
|
||||
"leave_chat_warning": "您確定要離開此對話嗎?",
|
||||
"link_contact_us": "聯絡我們",
|
||||
"link_privacy_policy": "隱私權政策",
|
||||
"link_terms_of_service": "服務條款",
|
||||
"login_prompt": "登入",
|
||||
"menu_item_archive_topic": "封存",
|
||||
"menu_item_block": "封鎖",
|
||||
"menu_item_clear_messages": "清除訊息",
|
||||
"menu_item_clear_messages_for_all": "為所有人清除訊息",
|
||||
"menu_item_delete": "刪除",
|
||||
"menu_item_delete_for_all": "為所有人刪除",
|
||||
"menu_item_delete_topic": "刪除",
|
||||
"menu_item_edit_permissions": "編輯權限",
|
||||
"menu_item_info": "Info",
|
||||
"menu_item_member_delete": "移除",
|
||||
"menu_item_mute": "靜音",
|
||||
"menu_item_send_retry": "重試",
|
||||
"menu_item_unblock": "解除封鎖",
|
||||
"menu_item_unmute": "解除靜音",
|
||||
"message_sending": "正在傳送...",
|
||||
"message_sending_failed": "失敗",
|
||||
"messages_not_readable": "沒有存取訊息的權限",
|
||||
"messaging_disabled_prompt": "停用訊息",
|
||||
"more_online_members": "+{overflow}",
|
||||
"new_message_prompt": "新訊息",
|
||||
"new_password_placeholder": "輸入新的密碼",
|
||||
"no_connection": "無連線",
|
||||
"no_contacts": "您沒有任何聯絡人 :-(",
|
||||
"numeric_confirmation_code_prompt": "僅數字",
|
||||
"online_now": "上線中",
|
||||
"password_prompt": "密碼",
|
||||
"password_unchanged_prompt": "未修改",
|
||||
"peers_messaging_disabled": "Peer 已停用訊息。",
|
||||
"permission_admin": "核可 ({val})",
|
||||
"permission_delete": "刪除 ({val})",
|
||||
"permission_join": "加入 ({val})",
|
||||
"permission_owner": "擁有者 ({val})",
|
||||
"permission_pres": "收到通知 ({val})",
|
||||
"permission_read": "讀取 ({val})",
|
||||
"permission_share": "分享 ({val})",
|
||||
"permission_write": "撰寫 ({val})",
|
||||
"phone_dative": "phone",
|
||||
"private_editing_placeholder": "僅您可見",
|
||||
"push_init_failed": "初始化通知失敗。",
|
||||
"reconnect_countdown": "已斷線。將在 {seconds} 秒後重新連線…",
|
||||
"reconnect_now": "現在重試",
|
||||
"reload_update": "重新載入",
|
||||
"report_chat_warning": "您確定要封鎖並檢舉此對話嗎?",
|
||||
"requested_permissions": "重新請求",
|
||||
"save_attachment": "儲存",
|
||||
"search_for_contacts": "使用搜尋來尋找聯絡人",
|
||||
"search_no_results": "搜尋無結果",
|
||||
"search_placeholder": "例如 email:alice@example.com, tel:17025550003... 的清單",
|
||||
"sidepanel_title_acc_general": "一般",
|
||||
"sidepanel_title_acc_notifications": "通知",
|
||||
"sidepanel_title_acc_security": "安全性",
|
||||
"sidepanel_title_acc_support": "支援",
|
||||
"sidepanel_title_account_settings": "帳號設定",
|
||||
"sidepanel_title_archive": "已封存的對話",
|
||||
"sidepanel_title_blocked": "已封鎖的對話",
|
||||
"sidepanel_title_cred": "確認認證",
|
||||
"sidepanel_title_login": "登入",
|
||||
"sidepanel_title_newtpk": "開始新對話",
|
||||
"sidepanel_title_register": "建立帳號",
|
||||
"sidepanel_title_reset": "重設密碼",
|
||||
"sidepanel_title_settings": "設定",
|
||||
"stay_logged_in": "保持登入",
|
||||
"tabtitle_find_user": "尋找",
|
||||
"tabtitle_group_by_id": "以 id",
|
||||
"tabtitle_new_group": "新群組",
|
||||
"tags_editor_no_tags": "新增一些標籤",
|
||||
"tags_not_found": "未定義任何標籤。請新增一些標籤。",
|
||||
"title_all_contacts": "所有聯絡人",
|
||||
"title_group_members": "群組成員",
|
||||
"title_info": "資訊",
|
||||
"title_manage_tags": "管理",
|
||||
"title_not_found": "找不到",
|
||||
"title_permissions": "權限",
|
||||
"title_tag_manager": "標籤 (使用者探索)",
|
||||
"topic_block_warning": "您確定要封鎖此對話嗎?",
|
||||
"topic_delete_warning": "您確定要刪除此對話嗎?",
|
||||
"topic_name_editing_placeholder": "群組的任意名稱",
|
||||
"unnamed_topic": "未命名",
|
||||
"update_available": "有可用的更新。",
|
||||
"upload_finishing": "正在完成...",
|
||||
"user_not_found": "找不到",
|
||||
"validate_credential_action": "確認",
|
||||
"topic_delete": "刪除聊天",
|
||||
"password_reset_email_sent": "一封電子郵件已發送至 {email}。按照電子郵件中的說明重置密碼。",
|
||||
"menu_item_restore_topic": "恢復"
|
||||
},
|
||||
"zh": {
|
||||
"archived_contacts": "已归档联系人 ({count})",
|
||||
@@ -1250,6 +1450,9 @@
|
||||
"channel": "频道",
|
||||
"drafty_attachment": "附件",
|
||||
"drafty_form": "形式:",
|
||||
"drafty_image": "图像"
|
||||
"drafty_image": "图像",
|
||||
"topic_delete": "删除聊天",
|
||||
"password_reset_email_sent": "一封电子邮件已发送至 {email}。按照电子邮件中的说明重置密码。",
|
||||
"menu_item_restore_topic": "从存档中恢复"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
// This is a generated file. Don't edit.
|
||||
|
||||
export const PACKAGE_VERSION = "0.17.2";
|
||||
export const PACKAGE_VERSION = "0.17.10";
|
||||
|
||||
+33
-3
@@ -61,6 +61,16 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Are you sure you want to delete all messages for everyone? It cannot be undone.',
|
||||
description: 'Alert dialog warning when hard-deleting all messages.'
|
||||
},
|
||||
topic_delete: {
|
||||
id: 'topic_delete',
|
||||
defaultMessage: 'Delete Conversation',
|
||||
description: 'Alert title when deleting the topic.'
|
||||
},
|
||||
topic_delete_warning: {
|
||||
id: 'topic_delete_warning',
|
||||
defaultMessage: 'Are you sure you want to delete this conversation? It cannot be undone.',
|
||||
description: 'Alert warning when deleting entire topic'
|
||||
},
|
||||
leave_chat: {
|
||||
id: 'action_leave_chat',
|
||||
defaultMessage: 'Leave Conversation',
|
||||
@@ -143,6 +153,7 @@ class InfoView extends React.Component {
|
||||
this.handleShowAddMembers = this.handleShowAddMembers.bind(this);
|
||||
this.handleHideAddMembers = this.handleHideAddMembers.bind(this);
|
||||
this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);
|
||||
this.handleDeleteTopic = this.handleDeleteTopic.bind(this);
|
||||
this.handleDeleteMessages = this.handleDeleteMessages.bind(this);
|
||||
this.handleLeave = this.handleLeave.bind(this);
|
||||
this.handleBlock = this.handleBlock.bind(this);
|
||||
@@ -422,6 +433,19 @@ class InfoView extends React.Component {
|
||||
this.setState({showMemberPanel: false});
|
||||
}
|
||||
|
||||
handleDeleteTopic(e) {
|
||||
e.preventDefault();
|
||||
const {formatMessage} = this.props.intl;
|
||||
this.props.onShowAlert(
|
||||
formatMessage(messages.topic_delete), // title
|
||||
formatMessage(messages.topic_delete_warning), // content
|
||||
(() => { this.props.onDeleteTopic(this.props.topic); }), // onConfirm
|
||||
null, // "OK"
|
||||
true, // Show Reject button
|
||||
null // "Cancel"
|
||||
);
|
||||
}
|
||||
|
||||
handleDeleteMessages(e) {
|
||||
e.preventDefault();
|
||||
const {formatMessage} = this.props.intl;
|
||||
@@ -717,9 +741,15 @@ class InfoView extends React.Component {
|
||||
:
|
||||
null
|
||||
}
|
||||
<a href="#" className="danger flat-button" onClick={this.handleLeave}>
|
||||
<i className="material-icons">exit_to_app</i> {formatMessage(messages.leave_chat)}
|
||||
</a>
|
||||
{this.state.owner ?
|
||||
<a href="#" className="danger flat-button" onClick={this.handleDeleteTopic}>
|
||||
<i className="material-icons">delete</i> {formatMessage(messages.topic_delete)}
|
||||
</a>
|
||||
:
|
||||
<a href="#" className="danger flat-button" onClick={this.handleLeave}>
|
||||
<i className="material-icons">exit_to_app</i> {formatMessage(messages.leave_chat)}
|
||||
</a>
|
||||
}
|
||||
{!this.state.groupTopic ?
|
||||
<a href="#" className="danger flat-button" onClick={this.handleBlock}>
|
||||
<i className="material-icons">block</i> {formatMessage(messages.block_contact)}
|
||||
|
||||
+52
-63
@@ -84,7 +84,7 @@ class MessagesView extends React.Component {
|
||||
this.handleScrollEvent = this.handleScrollEvent.bind(this);
|
||||
this.handleDescChange = this.handleDescChange.bind(this);
|
||||
this.handleSubsUpdated = this.handleSubsUpdated.bind(this);
|
||||
this.handleNewMessage = this.handleNewMessage.bind(this);
|
||||
this.handleMessageUpdate = this.handleMessageUpdate.bind(this);
|
||||
this.handleAllMessagesReceived = this.handleAllMessagesReceived.bind(this);
|
||||
this.handleInfoReceipt = this.handleInfoReceipt.bind(this);
|
||||
this.handleImagePostview = this.handleImagePostview.bind(this);
|
||||
@@ -124,7 +124,7 @@ class MessagesView extends React.Component {
|
||||
// or vertical shrinking.
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.messagesScroller) {
|
||||
if (prevState.topic != this.state.topic || prevState.messages.length != this.state.messages.length) {
|
||||
if (prevState.topic != this.state.topic || prevState.messageCount != this.state.messageCount) {
|
||||
// New message
|
||||
this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;
|
||||
} else if (prevProps.viewportHeight > this.props.viewportHeight) {
|
||||
@@ -140,7 +140,7 @@ class MessagesView extends React.Component {
|
||||
}
|
||||
|
||||
if (topic) {
|
||||
topic.onData = this.handleNewMessage;
|
||||
topic.onData = this.handleMessageUpdate;
|
||||
topic.onAllMessagesReceived = this.handleAllMessagesReceived;
|
||||
topic.onInfo = this.handleInfoReceipt;
|
||||
topic.onMetaDesc = this.handleDescChange;
|
||||
@@ -208,7 +208,8 @@ class MessagesView extends React.Component {
|
||||
if (!nextProps.topic) {
|
||||
// Default state: no topic.
|
||||
nextState = {
|
||||
messages: [],
|
||||
messageCount: 0,
|
||||
latestClearId: -1,
|
||||
onlineSubs: [],
|
||||
topic: null,
|
||||
title: '',
|
||||
@@ -236,7 +237,6 @@ class MessagesView extends React.Component {
|
||||
|
||||
if (topic) {
|
||||
// Topic exists.
|
||||
const msgs = [];
|
||||
const subs = [];
|
||||
|
||||
if (nextProps.connected) {
|
||||
@@ -247,14 +247,7 @@ class MessagesView extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
topic.messages(function(msg) {
|
||||
if (!msg.deleted) {
|
||||
msgs.push(msg);
|
||||
}
|
||||
});
|
||||
|
||||
Object.assign(nextState, {
|
||||
messages: msgs,
|
||||
onlineSubs: subs
|
||||
});
|
||||
|
||||
@@ -281,12 +274,15 @@ class MessagesView extends React.Component {
|
||||
});
|
||||
}
|
||||
Object.assign(nextState, {
|
||||
messageCount: topic.messageCount(),
|
||||
latestClearId: topic.maxClearId(),
|
||||
channel: topic.isChannelType()
|
||||
});
|
||||
} else {
|
||||
// Invalid topic.
|
||||
Object.assign(nextState, {
|
||||
messages: [],
|
||||
messageCount: 0,
|
||||
latestClearId: -1,
|
||||
onlineSubs: [],
|
||||
title: '',
|
||||
avatar: null,
|
||||
@@ -369,21 +365,19 @@ class MessagesView extends React.Component {
|
||||
// Get older messages
|
||||
handleScrollEvent(event) {
|
||||
this.setState({scrollPosition: event.target.scrollHeight - event.target.scrollTop});
|
||||
if (this.state.fetchingMessages) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.target.scrollTop <= 0) {
|
||||
this.setState((prevState, props) => {
|
||||
const newState = {};
|
||||
if (!prevState.fetchingMessages) {
|
||||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||||
if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {
|
||||
newState.fetchingMessages = true;
|
||||
topic.getMessagesPage(MESSAGES_PAGE).catch((err) => {
|
||||
this.setState({fetchingMessages: false});
|
||||
this.props.onError(err.message, 'err');
|
||||
});
|
||||
}
|
||||
}
|
||||
return newState;
|
||||
});
|
||||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||||
if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {
|
||||
this.setState({fetchingMessages: true}, () => {
|
||||
topic.getMessagesPage(MESSAGES_PAGE)
|
||||
.catch((err) => this.props.onError(err.message, 'err'))
|
||||
.finally(() => this.setState({fetchingMessages: false}));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,33 +490,32 @@ class MessagesView extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleNewMessage(msg) {
|
||||
// Regenerate messages list
|
||||
// The 'msg' could be false-ish if some message ranges were deleted.
|
||||
handleMessageUpdate(msg) {
|
||||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||||
const newState = {messages: []};
|
||||
topic.messages((m) => {
|
||||
if (!m.deleted) {
|
||||
newState.messages.push(m);
|
||||
}
|
||||
});
|
||||
|
||||
// msg could be null if one or more messages were deleted.
|
||||
if (msg && !msg.deleted) {
|
||||
// If the message is added to the end of the message list,
|
||||
// scroll to the bottom.
|
||||
if (topic.isNewMessage(msg.seq)) {
|
||||
newState.scrollPosition = 0;
|
||||
}
|
||||
|
||||
// Aknowledge messages except own messages. They are
|
||||
// automatically assumed to be read and recived.
|
||||
const status = topic.msgStatus(msg, true);
|
||||
if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {
|
||||
this.postReadNotification(msg.seq);
|
||||
}
|
||||
this.props.onData(msg);
|
||||
if (!msg) {
|
||||
// msg could be null if one or more messages were deleted.
|
||||
// Updating state to force redraw.
|
||||
this.setState({latestClearId: topic.maxClearId()});
|
||||
return;
|
||||
}
|
||||
this.setState(newState);
|
||||
|
||||
this.setState({messageCount: topic.messageCount()});
|
||||
|
||||
// Scroll to the bottom if the message is added to the end of the message list.
|
||||
// TODO: This should be replaced by showing a "scroll to bottom" button.
|
||||
if (topic.isNewMessage(msg.seq)) {
|
||||
this.setState({scrollPosition: 0});
|
||||
}
|
||||
|
||||
// Aknowledge messages except own messages. They are
|
||||
// automatically assumed to be read and recived.
|
||||
const status = topic.msgStatus(msg, true);
|
||||
if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {
|
||||
this.postReadNotification(msg.seq);
|
||||
}
|
||||
// This will send "received" notifications right away.
|
||||
this.props.onData(msg);
|
||||
}
|
||||
|
||||
handleAllMessagesReceived(count) {
|
||||
@@ -705,7 +698,7 @@ class MessagesView extends React.Component {
|
||||
|
||||
// Make small preview to show while uploading.
|
||||
imageScaled(blob, IMAGE_PREVIEW_DIM, IMAGE_PREVIEW_DIM, -1, false,
|
||||
(tinyBlob) => {
|
||||
(tinyMine, tinyBlob) => {
|
||||
// Convert tiny image into base64 for serialization and previewing.
|
||||
blobToBase64(tinyBlob, (blobMime, tinyBits64) => {
|
||||
let msg = Drafty.insertImage(null, 0, {
|
||||
@@ -758,7 +751,7 @@ class MessagesView extends React.Component {
|
||||
// Get image dimensions and size, optionally scale it down.
|
||||
imageScaled(file, MAX_IMAGE_DIM, MAX_IMAGE_DIM, maxExternAttachmentSize, false,
|
||||
// Success
|
||||
(blob, mime, width, height, fname) => {
|
||||
(mime, blob, width, height, fname) => {
|
||||
this.setState({imagePreview: {
|
||||
url: URL.createObjectURL(blob),
|
||||
blob: blob,
|
||||
@@ -777,7 +770,8 @@ class MessagesView extends React.Component {
|
||||
}
|
||||
|
||||
handleCancelUpload(seq, uploader) {
|
||||
const found = this.state.messages.find(msg => msg.seq == seq);
|
||||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||||
const found = topic.findMessage(seq);
|
||||
if (found) {
|
||||
found._cancelled = true;
|
||||
}
|
||||
@@ -828,13 +822,8 @@ class MessagesView extends React.Component {
|
||||
let messageNodes = [];
|
||||
let previousFrom = null;
|
||||
let chatBoxClass = null;
|
||||
for (let i=0; i<this.state.messages.length; i++) {
|
||||
let msg = this.state.messages[i];
|
||||
let nextFrom = null;
|
||||
|
||||
if (i + 1 < this.state.messages.length) {
|
||||
nextFrom = this.state.messages[i+1].from || 'chan';
|
||||
}
|
||||
topic.messages((msg, prev, next, i) => {
|
||||
let nextFrom = next ? (next.from || null) : 'chan';
|
||||
|
||||
let sequence = 'single';
|
||||
let thisFrom = msg.from || 'chan';
|
||||
@@ -888,7 +877,7 @@ class MessagesView extends React.Component {
|
||||
onCancelUpload={this.handleCancelUpload}
|
||||
key={msg.seq} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let lastSeen = null;
|
||||
if (isChannel) {
|
||||
|
||||
@@ -12,7 +12,8 @@ export default class PasswordResetView extends React.PureComponent {
|
||||
|
||||
this.state = {
|
||||
email: '',
|
||||
password: ''
|
||||
password: '',
|
||||
sent: false
|
||||
};
|
||||
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
@@ -21,7 +22,7 @@ export default class PasswordResetView extends React.PureComponent {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let parsed = HashNavigation.parseUrlHash(window.location.hash);
|
||||
const parsed = HashNavigation.parseUrlHash(window.location.hash);
|
||||
this.setState({token: parsed.params.token, scheme: parsed.params.scheme});
|
||||
}
|
||||
|
||||
@@ -29,8 +30,14 @@ export default class PasswordResetView extends React.PureComponent {
|
||||
e.preventDefault();
|
||||
if (this.state.token) {
|
||||
this.props.onReset(this.state.scheme, this.state.password.trim(), this.state.token);
|
||||
} else if (this.state.sent) {
|
||||
this.props.onCancel();
|
||||
} else {
|
||||
this.props.onRequest('email', this.state.email.trim());
|
||||
const email = this.state.email.trim();
|
||||
this.setState({email: email});
|
||||
this.props.onRequest('email', email).then(() => {
|
||||
this.setState({sent: true});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +50,9 @@ export default class PasswordResetView extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
let reset = (this.state.token && this.state.scheme);
|
||||
return (
|
||||
<form id="password-reset-form" onSubmit={this.handleSubmit}>
|
||||
{reset ?
|
||||
{(this.state.token && this.state.scheme) ?
|
||||
<FormattedMessage id="new_password_placeholder" defaultMessage="Enter new password"
|
||||
description="Placeholder for entering new password">{
|
||||
(placeholder) => <VisiblePassword
|
||||
@@ -56,7 +62,17 @@ export default class PasswordResetView extends React.PureComponent {
|
||||
required={true} autoFocus={true}
|
||||
onChange={this.handlePasswordChange} />
|
||||
}</FormattedMessage>
|
||||
:
|
||||
: this.state.sent ?
|
||||
<>
|
||||
<br/>
|
||||
<center><i className="material-icons huge green">task_alt</i></center>
|
||||
<br/>
|
||||
<center><FormattedMessage id="password_reset_email_sent"
|
||||
defaultMessage="An email has been sent to {email}. Follow the directions in the email to reset your password."
|
||||
values={{ email: <tt>{this.state.email}</tt> }}
|
||||
description="Notification that the email with password reset instructions has been sent" /></center>
|
||||
</>
|
||||
:
|
||||
<>
|
||||
<label htmlFor="inputEmail">
|
||||
<FormattedMessage id="label_reset_password"
|
||||
@@ -75,8 +91,13 @@ export default class PasswordResetView extends React.PureComponent {
|
||||
</>
|
||||
}
|
||||
<div className="dialog-buttons">
|
||||
<button className="primary" type="submit">{reset ?
|
||||
<FormattedMessage id="button_reset" defaultMessage="Reset" description="Button [Reset]" /> :
|
||||
<button className="primary" type="submit">{
|
||||
(this.state.token && this.state.scheme) ?
|
||||
<FormattedMessage id="button_reset" defaultMessage="Reset" description="Button [Reset]" />
|
||||
: this.state.sent ?
|
||||
<FormattedMessage id="button_ok" defaultMessage="OK"
|
||||
description="Button [OK]" />
|
||||
:
|
||||
<FormattedMessage id="button_send_request" defaultMessage="Send request"
|
||||
description="Button [Send request]" />
|
||||
}</button>
|
||||
|
||||
+57
-24
@@ -123,6 +123,7 @@ class TinodeWeb extends React.Component {
|
||||
this.handleTagsUpdated = this.handleTagsUpdated.bind(this);
|
||||
this.handleLogout = this.handleLogout.bind(this);
|
||||
this.handleDeleteAccount = this.handleDeleteAccount.bind(this);
|
||||
this.handleDeleteTopicRequest = this.handleDeleteTopicRequest.bind(this);
|
||||
this.handleDeleteMessagesRequest = this.handleDeleteMessagesRequest.bind(this);
|
||||
this.handleLeaveUnsubRequest = this.handleLeaveUnsubRequest.bind(this);
|
||||
this.handleBlockTopicRequest = this.handleBlockTopicRequest.bind(this);
|
||||
@@ -216,9 +217,15 @@ class TinodeWeb extends React.Component {
|
||||
window.addEventListener('online', (e) => { this.handleOnline(true); });
|
||||
window.addEventListener('offline', (e) => { this.handleOnline(false); });
|
||||
window.addEventListener('hashchange', this.handleHashRoute);
|
||||
|
||||
// Process background notifications from the service worker.
|
||||
const serviceWorkerChannel = new BroadcastChannel('tinode-sw');
|
||||
serviceWorkerChannel.addEventListener('message', this.handlePushMessage);
|
||||
if (typeof BroadcastChannel == 'function') {
|
||||
const serviceWorkerChannel = new BroadcastChannel('tinode-sw');
|
||||
serviceWorkerChannel.addEventListener('message', this.handlePushMessage);
|
||||
} else {
|
||||
// Safari is broken by design.
|
||||
console.warn('Your browser does not support BroadcastChannel. Some features will not be available');
|
||||
}
|
||||
|
||||
// Window/tab visible or invisible for pausing timers.
|
||||
document.addEventListener('visibilitychange', this.handleVisibilityEvent);
|
||||
@@ -307,7 +314,7 @@ class TinodeWeb extends React.Component {
|
||||
initFCMessaging() {
|
||||
const {formatMessage, locale} = this.props.intl;
|
||||
const onError = (msg, err) => {
|
||||
console.log(msg, err);
|
||||
console.error(msg, err);
|
||||
this.handleError(formatMessage(messages.push_init_failed), 'err');
|
||||
this.setState({desktopAlertsEnabled: false});
|
||||
}
|
||||
@@ -383,24 +390,28 @@ class TinodeWeb extends React.Component {
|
||||
'cred','reset','newtpk','archive','blocked','contacts',''].includes(hash.path[0])) {
|
||||
this.setState({sidePanelSelected: hash.path[0]});
|
||||
} else {
|
||||
console.log("Unknown sidepanel view", hash.path[0]);
|
||||
console.warn("Unknown sidepanel view", hash.path[0]);
|
||||
}
|
||||
|
||||
// Topic for MessagesView selector.
|
||||
const topicName = hash.path[1];
|
||||
if (Tinode.topicType(topicName) && topicName != this.state.topicSelected) {
|
||||
const newState = {
|
||||
topicSelected: topicName
|
||||
};
|
||||
const acs = this.tinode.getTopicAccessMode(topicName);
|
||||
if (acs) {
|
||||
newState.topicSelectedAcs = acs;
|
||||
}
|
||||
this.setState(newState);
|
||||
let topicName = hash.path[1] || null;
|
||||
if (topicName != this.state.topicSelected) {
|
||||
if (!Tinode.topicType(topicName)) {
|
||||
// Clear invalid topic name.
|
||||
topicName = null;
|
||||
}
|
||||
const newState = {
|
||||
topicSelected: topicName
|
||||
};
|
||||
const acs = this.tinode.getTopicAccessMode(topicName);
|
||||
if (acs) {
|
||||
newState.topicSelectedAcs = acs;
|
||||
}
|
||||
this.setState(newState);
|
||||
}
|
||||
} else {
|
||||
// Empty hashpath
|
||||
this.setState({sidePanelSelected: ''});
|
||||
this.setState({sidePanelSelected: '', topicSelected: null});
|
||||
}
|
||||
|
||||
// Save validation credentials, if available.
|
||||
@@ -463,13 +474,11 @@ class TinodeWeb extends React.Component {
|
||||
handlePersistenceChange(persist) {
|
||||
if (persist) {
|
||||
this.tinode.initStorage().then(() => {
|
||||
console.log("storage initialized");
|
||||
LocalStorageUtil.setObject('keep-logged-in', true);
|
||||
this.setState({persist: true});
|
||||
});
|
||||
} else {
|
||||
this.tinode.clearStorage().then(() => {
|
||||
console.log("storage cleared");
|
||||
LocalStorageUtil.setObject('keep-logged-in', false);
|
||||
this.setState({persist: false});
|
||||
});
|
||||
@@ -709,7 +718,7 @@ class TinodeWeb extends React.Component {
|
||||
} else {
|
||||
// TODO(gene): handle other types of notifications:
|
||||
// * ua -- user agent changes (maybe display a pictogram for mobile/desktop).
|
||||
console.log("Unsupported (yet) presence update:" + what + " in: " + cont.topic);
|
||||
console.info("Unsupported (yet) presence update:" + what + " in: " + cont.topic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -766,6 +775,12 @@ class TinodeWeb extends React.Component {
|
||||
newState.topicSelectedAcs = c.acs;
|
||||
}
|
||||
});
|
||||
|
||||
const past = new Date(0);
|
||||
newState.chatList.sort((a, b) => {
|
||||
return (a.touched || past).getTime() - (b.touched || past).getTime();
|
||||
});
|
||||
|
||||
// Merge search results and chat list.
|
||||
newState.searchableContacts = TinodeWeb.prepareSearchableContacts(newState.chatList, this.state.searchResults);
|
||||
this.setState(newState);
|
||||
@@ -932,7 +947,7 @@ class TinodeWeb extends React.Component {
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.log("Unknown invitation action", '"' + action + '""');
|
||||
console.warn("Unknown invitation action", '"' + action + '""');
|
||||
}
|
||||
|
||||
if (response != null) {
|
||||
@@ -1064,7 +1079,7 @@ class TinodeWeb extends React.Component {
|
||||
}).then(() => {
|
||||
this.requestPushToken();
|
||||
}).catch((err) => {
|
||||
console.log("Failed to get notification permission.", err);
|
||||
console.warn("Failed to get notification permission.", err);
|
||||
this.handleError(err.message, 'err');
|
||||
this.setState({desktopAlerts: false, firebaseToken: null});
|
||||
LocalStorageUtil.updateObject('settings', {desktopAlerts: false});
|
||||
@@ -1077,7 +1092,7 @@ class TinodeWeb extends React.Component {
|
||||
}
|
||||
} else if (this.state.firebaseToken && this.fbPush) {
|
||||
this.fbPush.deleteToken(this.state.firebaseToken).catch((err) => {
|
||||
console.log("Unable to delete token.", err);
|
||||
console.warn("Unable to delete token.", err);
|
||||
}).finally(() => {
|
||||
LocalStorageUtil.updateObject('settings', {desktopAlerts: false});
|
||||
localStorage.removeItem('firebase-token');
|
||||
@@ -1106,7 +1121,7 @@ class TinodeWeb extends React.Component {
|
||||
}
|
||||
}).catch((err) => {
|
||||
this.handleError(err.message, 'err');
|
||||
console.log("Failed to retrieve firebase token", err);
|
||||
console.warn("Failed to retrieve firebase token", err);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1253,6 +1268,8 @@ class TinodeWeb extends React.Component {
|
||||
this.fbPush.deleteToken(this.state.firebaseToken)
|
||||
}
|
||||
|
||||
clearInterval(this.reconnectCountdown);
|
||||
|
||||
if (this.tinode) {
|
||||
this.tinode.clearStorage();
|
||||
this.tinode.onDisconnect = undefined;
|
||||
@@ -1274,6 +1291,21 @@ class TinodeWeb extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
handleDeleteTopicRequest(topicName) {
|
||||
const topic = this.tinode.getTopic(topicName);
|
||||
if (!topic) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Request to hard-delete topic.
|
||||
topic.delTopic(true).then((ctrl) => {
|
||||
// Hide MessagesView and InfoView panels.
|
||||
HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));
|
||||
}).catch((err) => {
|
||||
this.handleError(err.message, 'err');
|
||||
});
|
||||
}
|
||||
|
||||
handleDeleteMessagesRequest(topicName) {
|
||||
const topic = this.tinode.getTopic(topicName);
|
||||
if (!topic) {
|
||||
@@ -1371,7 +1403,7 @@ class TinodeWeb extends React.Component {
|
||||
subscribed && deleter ? 'messages_clear_hard' : null,
|
||||
muted ? (blocked ? null : 'topic_unmute') : 'topic_mute',
|
||||
self_blocked ? 'topic_unblock' : 'topic_block',
|
||||
!archived ? 'topic_archive' : null,
|
||||
archived ? 'topic_restore' : 'topic_archive',
|
||||
'topic_delete'
|
||||
];
|
||||
}
|
||||
@@ -1464,7 +1496,7 @@ class TinodeWeb extends React.Component {
|
||||
|
||||
handlePasswordResetRequest(method, value) {
|
||||
// If already connected, connnect() will return a resolved promise.
|
||||
this.tinode.connect()
|
||||
return this.tinode.connect()
|
||||
.then(() => {
|
||||
return this.tinode.requestResetAuthSecret('basic', method, value);
|
||||
})
|
||||
@@ -1639,6 +1671,7 @@ class TinodeWeb extends React.Component {
|
||||
onShowAlert={this.handleShowAlert}
|
||||
onChangePermissions={this.handleChangePermissions}
|
||||
onMemberUpdateRequest={this.handleMemberUpdateRequest}
|
||||
onDeleteTopic={this.handleDeleteTopicRequest}
|
||||
onDeleteMessages={this.handleDeleteMessagesRequest}
|
||||
onLeaveTopic={this.handleLeaveUnsubRequest}
|
||||
onBlockTopic={this.handleBlockTopicRequest}
|
||||
|
||||
@@ -92,8 +92,10 @@ export default class ChatMessage extends React.Component {
|
||||
attachments.push(<Attachment
|
||||
tinode={this.props.tinode}
|
||||
downloadUrl={Drafty.getDownloadUrl(att)}
|
||||
filename={att.name} uploading={Drafty.isProcessing(att)}
|
||||
mimetype={att.mime} size={Drafty.getEntitySize(att)}
|
||||
filename={att.name}
|
||||
uploading={Drafty.isProcessing(att)}
|
||||
mimetype={att.mime}
|
||||
size={Drafty.getEntitySize(att)}
|
||||
progress={this.state.progress}
|
||||
onCancelUpload={this.handleCancelUpload}
|
||||
onError={this.props.onError}
|
||||
|
||||
@@ -54,7 +54,7 @@ const messages = defineMessages({
|
||||
},
|
||||
topic_delete_warning: {
|
||||
id: 'topic_delete_warning',
|
||||
defaultMessage: 'Are you sure you want to delete this conversation?',
|
||||
defaultMessage: 'Are you sure you want to delete this conversation? It cannot be undone.',
|
||||
description: 'Alert warning when deleting entire topic'
|
||||
},
|
||||
delete_messages_warning: {
|
||||
@@ -88,6 +88,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Archive',
|
||||
description: 'Move topic from the list of active chats to archive'
|
||||
},
|
||||
unarchive: {
|
||||
id: 'menu_item_restore_topic',
|
||||
defaultMessage: 'Restore',
|
||||
description: 'Restore topic from archive'
|
||||
},
|
||||
edit_permissions: {
|
||||
id: 'menu_item_edit_permissions',
|
||||
defaultMessage: 'Edit permissions',
|
||||
@@ -241,6 +246,22 @@ class ContextMenu extends React.Component {
|
||||
});
|
||||
}
|
||||
},
|
||||
'topic_restore': {
|
||||
id: 'topic_restore',
|
||||
title: formatMessage(messages.unarchive),
|
||||
handler: (params, errorHandler) => {
|
||||
const topic = this.props.tinode.getTopic(params.topicName);
|
||||
if (!topic) {
|
||||
console.log("Topic not found: ", params.topicName);
|
||||
return;
|
||||
}
|
||||
return topic.archive(false).catch((err) => {
|
||||
if (errorHandler) {
|
||||
errorHandler(err.message, 'err');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
// menu_item_edit_permissions is defined elsewhere.
|
||||
'permissions': {
|
||||
id: 'permissions',
|
||||
|
||||
+211
-104
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+329
-105
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,3 +1,3 @@
|
||||
// This is a generated file. Don't edit.
|
||||
|
||||
const PACKAGE_VERSION = "0.17.2";
|
||||
const PACKAGE_VERSION = "0.17.10";
|
||||
|
||||
Reference in New Issue
Block a user