Compare commits

...
37 Commits
Author SHA1 Message Date
or-else 2fe35f9ded merge devel 2023-05-10 14:43:49 -07:00
or-else 852a1a7c4e fix for https://github.com/tinode/chat/issues/858 2023-05-10 13:20:40 -07:00
or-else c15092e850 work in progress on disappearing deleted topic 2023-05-09 14:23:21 -07:00
or-else 7ca827559b provide default value for the field 2023-05-08 19:07:35 -07:00
or-else f7beabacf9 fix for https://github.com/tinode/chat/issues/861 2023-05-08 18:45:44 -07:00
or-else 848bb19f76 bump version 2023-05-08 17:19:32 -07:00
or-else c17118d157 fix a bunch of problems with credential validation 2023-05-08 14:08:21 -07:00
or-else 95e9891825 add extra spacing in error panel 2023-05-08 10:59:46 -07:00
or-else 0984c73c01 rebuilt with updated sdk 2023-05-06 10:42:52 -07:00
or-else f9e7a71920 fix display of pinned messages 2023-05-02 10:28:30 -07:00
or-else ba72fe7602 rebuilt with correct sdk 2023-04-28 17:18:28 -07:00
or-else 6da591beb1 remove unused missingRanges 2023-04-28 17:16:38 -07:00
or-else 16c9e2bb23 dependencies updated, rebuilt 2023-04-28 13:08:57 -07:00
or-else 3689b5d527 another fix for #124 2023-04-28 12:48:16 -07:00
or-else c277c16a53 mostly functional 2023-04-27 17:44:20 -07:00
or-else 1bc3e15da3 merge devel 2023-04-27 11:43:30 -07:00
or-else 3ac895ec66 rebuilt with correct sdk version 2023-04-27 11:41:11 -07:00
or-else 08bdc7bc08 allow automatic subscription to a new p2p topic 2023-04-27 11:00:22 -07:00
or-else 03a137da4a fractional fetching, wip 2023-04-27 09:41:49 -07:00
or-else 8df6413705 merge devel 2023-04-25 12:34:25 -07:00
or-else 5d56c17128 another fix for #124 2023-04-25 12:31:07 -07:00
or-else 87c74f9396 rebuilt with sdk 2023-04-25 10:18:44 -07:00
or-else 2b7c80ef2d removed unused files 2023-04-24 14:36:05 -07:00
or-else 823215aea6 merge devel 2023-04-24 14:34:25 -07:00
or-else 5ad734cdea some new files added 2023-04-24 14:32:21 -07:00
or-else 48405bcb94 fix for #124 2023-04-24 14:25:48 -07:00
or-else 0f9b88e64e sdk with fractional message fetching (wip) 2023-04-24 11:38:23 -07:00
or-else 10a827504b always load pinned messages 2023-04-18 17:07:38 -07:00
or-else 1d24e9135b merge devel, update dependencies, rebuild 2023-04-16 11:47:47 -07:00
or-else a5c190f6db sync version with the backend 0.22.6 2023-04-15 10:43:33 -07:00
or-else f61f68d2ac added another missing translation 2023-04-14 20:21:40 -07:00
or-else 2c5705c3ae translations updated 2023-04-14 20:12:26 -07:00
or-else c4547bbeb4 partial update of translations 2023-04-14 19:56:23 -07:00
or-else e7296d5d14 rebuilt 2023-04-14 19:38:21 -07:00
or-else 364cd2c488 merge devel 2023-04-14 19:34:27 -07:00
or-else 855f871041 rebuilt with SDK fix for downloads 2023-04-14 19:20:25 -07:00
or-else 8dd32a341b fix some issues with audio recording and playback in safari #121, #122 2023-04-14 17:47:35 -07:00
86 changed files with 2815 additions and 2331 deletions
+8 -5
View File
@@ -3043,17 +3043,20 @@ ul.tabbar li.active > a {
padding: 0.15rem 0.5rem 0.25rem 0.5rem;
background-color: #fafafa;
background-position: center;
line-height: 1.5em;
font-weight: lighter;
border-radius: 0 0.25rem 0.25rem 0;
color: #666;
height: 100%;
width: 100%;
min-width: 24rem;
width: 24rem;
min-width: 0;
cursor: pointer;
transition: background 0.4s;
}
.pinned > p {
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.pinned:hover {
@@ -3194,7 +3197,7 @@ ul.phone-country-selector span.country {
}
.pinned {
min-width: 12rem;
width: 16rem;
}
}
@@ -3236,6 +3239,6 @@ ul.phone-country-selector span.country {
}
.pinned {
min-width: unset;
width: unset;
}
}
+1 -1
View File
File diff suppressed because one or more lines are too long
+414 -394
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -122,3 +122,7 @@ export const LINK_PRIVACY_POLICY = 'https://tinode.co/privacy.html';
// Link to Terms of Service.
export const LINK_TERMS_OF_SERVICE = 'https://tinode.co/terms.html';
// When user starts a P2P topic, automatic subscription does not
// happen until the first message. Setting this option to true enables
// immediate subscription.
export const IMMEDIATE_P2P_SUBSCRIPTION = false;
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+20 -34
View File
@@ -97,13 +97,6 @@
"missing": false,
"obsolete": false
},
"button_add_another": {
"translation": "Hinzufügen",
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": true
},
"button_add_members": {
"translation": "Mitglieder hinzufügen",
"defaultMessage": "Add members",
@@ -1000,7 +993,7 @@
},
"search_placeholder": {
"translation": "Liste: email:lisa@beispiel.de, tel:17025550003...",
"defaultMessage": "List like email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like alice@example.com, +17025550003...",
"description": "Placeholder in contacts search field",
"missing": false,
"obsolete": false
@@ -1222,13 +1215,6 @@
"missing": false,
"obsolete": false
},
"validate_credential_action": {
"translation": "bestätigen",
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Beschreibung (optional)",
"defaultMessage": "Description (optional)",
@@ -1431,13 +1417,6 @@
"missing": false,
"obsolete": false
},
"phone_or_email_prompt": {
"translation": "Telefonnummer oder E-Mail",
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Erfolgreich bestätigt",
"defaultMessage": "Confirmed successfully",
@@ -1600,45 +1579,45 @@
"obsolete": false
},
"current_email": {
"translation": "",
"translation": "Aktuelle E-Mail",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"missing": false,
"obsolete": false
},
"current_phone": {
"translation": "",
"translation": "Aktuelle Telefonnummer",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"missing": false,
"obsolete": false
},
"new_email": {
"translation": "",
"translation": "Neue E-Mail",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"missing": false,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"translation": "Neue Telefonnummer",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"missing": false,
"obsolete": false
},
"change_email": {
"translation": "",
"translation": "E-Mail ändern",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"missing": false,
"obsolete": false
},
"change_phone": {
"translation": "",
"translation": "Telefonnummer ändern",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"missing": false,
"obsolete": false
},
"call_busy": {
@@ -1648,6 +1627,13 @@
"missing": false,
"obsolete": false
},
"failed_to_init_audio": {
"translation": "Audioaufnahme konnte nicht initialisiert werden",
"defaultMessage": "Failed to initialize audio recording",
"description": "Error message when audio is not available",
"missing": false,
"obsolete": false
},
"pin_message": {
"translation": "",
"defaultMessage": "Pin",
+8 -22
View File
@@ -97,13 +97,6 @@
"missing": false,
"obsolete": false
},
"button_add_another": {
"translation": "Add another",
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": true
},
"button_add_members": {
"translation": "Add members",
"defaultMessage": "Add members",
@@ -1007,7 +1000,7 @@
},
"search_placeholder": {
"translation": "List like email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like alice@example.com, +17025550003...",
"description": "Placeholder in contacts search field",
"missing": false,
"obsolete": false
@@ -1222,13 +1215,6 @@
"missing": false,
"obsolete": false
},
"validate_credential_action": {
"translation": "confirm",
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Optional description",
"defaultMessage": "Description (optional)",
@@ -1431,13 +1417,6 @@
"missing": false,
"obsolete": false
},
"phone_or_email_prompt": {
"translation": "Phone number or email",
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Confirmed successfully",
"defaultMessage": "Confirmed successfully",
@@ -1648,6 +1627,13 @@
"missing": false,
"obsolete": false
},
"failed_to_init_audio": {
"translation": "Failed to initialize audio recording",
"defaultMessage": "Failed to initialize audio recording",
"description": "Error message when audio is not available",
"missing": false,
"obsolete": false
},
"pin_message": {
"translation": "Pin",
"defaultMessage": "Pin",
+20 -34
View File
@@ -97,13 +97,6 @@
"missing": false,
"obsolete": false
},
"button_add_another": {
"translation": "Añadir contacto",
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": true
},
"button_add_members": {
"translation": "Añadir miembros",
"defaultMessage": "Add members",
@@ -1000,7 +993,7 @@
},
"search_placeholder": {
"translation": "Ej. email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like alice@example.com, +17025550003...",
"description": "Placeholder in contacts search field",
"missing": false,
"obsolete": false
@@ -1215,13 +1208,6 @@
"missing": false,
"obsolete": false
},
"validate_credential_action": {
"translation": "confirmar",
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Descripción (opcional)",
"defaultMessage": "Description (optional)",
@@ -1431,13 +1417,6 @@
"missing": false,
"obsolete": false
},
"phone_or_email_prompt": {
"translation": "Número de teléfono o correo electrónico",
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Confirmado con éxito",
"defaultMessage": "Confirmed successfully",
@@ -1600,45 +1579,45 @@
"obsolete": false
},
"current_email": {
"translation": "",
"translation": "Correo electrónico actual",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"missing": false,
"obsolete": false
},
"current_phone": {
"translation": "",
"translation": "Número de teléfono actual",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"missing": false,
"obsolete": false
},
"new_email": {
"translation": "",
"translation": "Nuevo correo electrónico",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"missing": false,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"translation": "Nuevo número de teléfono",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"missing": false,
"obsolete": false
},
"change_email": {
"translation": "",
"translation": "Cambiar correo electrónico",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"missing": false,
"obsolete": false
},
"change_phone": {
"translation": "",
"translation": "Cambiar número de teléfono",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"missing": false,
"obsolete": false
},
"call_busy": {
@@ -1661,5 +1640,12 @@
"description": "Menu item [Unpin] for un-pinning the message.",
"missing": true,
"obsolete": false
},
"failed_to_init_audio": {
"translation": "Error al inicializar la grabación de audio",
"defaultMessage": "Failed to initialize audio recording",
"description": "Error message when audio is not available",
"missing": false,
"obsolete": false
}
}
+5 -1
View File
@@ -291,6 +291,10 @@
"defaultMessage": "Invalid ID",
"description": "Error message"
},
"failed_to_init_audio": {
"defaultMessage": "Failed to initialize audio recording",
"description": "Error message when audio is not available"
},
"file_attachment_too_large": {
"defaultMessage": "The file size {size} exceeds the {limit} limit.",
"description": "Error message when attachment is too large"
@@ -801,7 +805,7 @@
"description": "Text shown in contacts view when query returned no results."
},
"search_placeholder": {
"defaultMessage": "List like email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like alice@example.com, +17025550003...",
"description": "Placeholder in contacts search field"
},
"sidepanel_title_acc_notifications": {
+20 -34
View File
@@ -97,13 +97,6 @@
"missing": false,
"obsolete": false
},
"button_add_another": {
"translation": "Ajouter un autre",
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": true
},
"button_add_members": {
"translation": "Ajouter des membres",
"defaultMessage": "Add members",
@@ -1007,7 +1000,7 @@
},
"search_placeholder": {
"translation": "Liste comme email:alice@exemple.com, tel:17025550003...",
"defaultMessage": "List like email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like alice@example.com, +17025550003...",
"description": "Placeholder in contacts search field",
"missing": false,
"obsolete": false
@@ -1222,13 +1215,6 @@
"missing": false,
"obsolete": false
},
"validate_credential_action": {
"translation": "confirmer",
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Description facultative",
"defaultMessage": "Description (optional)",
@@ -1431,13 +1417,6 @@
"missing": false,
"obsolete": false
},
"phone_or_email_prompt": {
"translation": "Numéro de téléphone ou email",
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Confirmé avec succès",
"defaultMessage": "Confirmed successfully",
@@ -1600,45 +1579,45 @@
"obsolete": false
},
"current_email": {
"translation": "",
"translation": "Courriel actuel",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"missing": false,
"obsolete": false
},
"current_phone": {
"translation": "",
"translation": "Numéro de téléphone actuel",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"missing": false,
"obsolete": false
},
"new_email": {
"translation": "",
"translation": "Nouvel e-mail",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"missing": false,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"translation": "Nouveau numéro de téléphone",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"missing": false,
"obsolete": false
},
"change_email": {
"translation": "",
"translation": "Modifier l'e-mail",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"missing": false,
"obsolete": false
},
"change_phone": {
"translation": "",
"translation": "Modifier le numéro de téléphone",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"missing": false,
"obsolete": false
},
"call_busy": {
@@ -1661,5 +1640,12 @@
"description": "Menu item [Unpin] for un-pinning the message.",
"missing": true,
"obsolete": false
},
"failed_to_init_audio": {
"translation": "Échec de l'initialisation de l'enregistrement audio",
"defaultMessage": "Failed to initialize audio recording",
"description": "Error message when audio is not available",
"missing": false,
"obsolete": false
}
}
+20 -34
View File
@@ -97,13 +97,6 @@
"missing": false,
"obsolete": false
},
"button_add_another": {
"translation": "다른항목추가",
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": true
},
"button_add_members": {
"translation": "회원추가",
"defaultMessage": "Add members",
@@ -1000,7 +993,7 @@
},
"search_placeholder": {
"translation": "email:alice@example.com,tel:17025550003...와같은목록",
"defaultMessage": "List like email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like alice@example.com, +17025550003...",
"description": "Placeholder in contacts search field",
"missing": false,
"obsolete": false
@@ -1215,13 +1208,6 @@
"missing": false,
"obsolete": false
},
"validate_credential_action": {
"translation": "확인",
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": true
},
"description_editing_placeholder": {
"translation": "설명(선택 사항)",
"defaultMessage": "Description (optional)",
@@ -1431,13 +1417,6 @@
"missing": false,
"obsolete": false
},
"phone_or_email_prompt": {
"translation": "전화번호 또는 이메일",
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "성공적으로 확인됨",
"defaultMessage": "Confirmed successfully",
@@ -1600,45 +1579,45 @@
"obsolete": false
},
"current_email": {
"translation": "",
"translation": "현재 이메일",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"missing": false,
"obsolete": false
},
"current_phone": {
"translation": "",
"translation": "현재 전화번호",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"missing": false,
"obsolete": false
},
"new_email": {
"translation": "",
"translation": "새 이메일 주소",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"missing": false,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"translation": "새 전화번호",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"missing": false,
"obsolete": false
},
"change_email": {
"translation": "",
"translation": "이메일 변경",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"missing": false,
"obsolete": false
},
"change_phone": {
"translation": "",
"translation": "전화번호 변경",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"missing": false,
"obsolete": false
},
"call_busy": {
@@ -1661,5 +1640,12 @@
"description": "Menu item [Unpin] for un-pinning the message.",
"missing": true,
"obsolete": false
},
"failed_to_init_audio": {
"translation": "오디오 녹음 초기화 실패",
"defaultMessage": "Failed to initialize audio recording",
"description": "Error message when audio is not available",
"missing": false,
"obsolete": false
}
}
+20 -34
View File
@@ -97,13 +97,6 @@
"missing": false,
"obsolete": false
},
"button_add_another": {
"translation": "Adaugă",
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": true
},
"button_add_members": {
"translation": "Adaugă persoane",
"defaultMessage": "Add members",
@@ -1000,7 +993,7 @@
},
"search_placeholder": {
"translation": "Caută după e-mail sau număr telefon...",
"defaultMessage": "List like email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like alice@example.com, +17025550003...",
"description": "Placeholder in contacts search field",
"missing": false,
"obsolete": false
@@ -1215,13 +1208,6 @@
"missing": false,
"obsolete": false
},
"validate_credential_action": {
"translation": "confirmă",
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Descriere (opțional)",
"defaultMessage": "Description (optional)",
@@ -1431,13 +1417,6 @@
"missing": false,
"obsolete": false
},
"phone_or_email_prompt": {
"translation": "Număr de telefon sau e-mail",
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Confirmat cu succes",
"defaultMessage": "Confirmed successfully",
@@ -1600,45 +1579,45 @@
"obsolete": false
},
"current_email": {
"translation": "",
"translation": "E-mail curent",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"missing": false,
"obsolete": false
},
"current_phone": {
"translation": "",
"translation": "Număr de telefon actual",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"missing": false,
"obsolete": false
},
"new_email": {
"translation": "",
"translation": "E-mail nou",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"missing": false,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"translation": "Număr de telefon nou",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"missing": false,
"obsolete": false
},
"change_email": {
"translation": "",
"translation": "Schimbați adresa de e-mail",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"missing": false,
"obsolete": false
},
"change_phone": {
"translation": "",
"translation": "Schimbați numărul de telefon",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"missing": false,
"obsolete": false
},
"call_busy": {
@@ -1661,5 +1640,12 @@
"description": "Menu item [Unpin] for un-pinning the message.",
"missing": true,
"obsolete": false
},
"failed_to_init_audio": {
"translation": "Inițializarea înregistrării audio a eșuat",
"defaultMessage": "Failed to initialize audio recording",
"description": "Error message when audio is not available",
"missing": false,
"obsolete": false
}
}
+20 -34
View File
@@ -97,13 +97,6 @@
"missing": false,
"obsolete": false
},
"button_add_another": {
"translation": "Добавить",
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": true
},
"button_add_members": {
"translation": "Добавить",
"defaultMessage": "Add members",
@@ -1000,7 +993,7 @@
},
"search_placeholder": {
"translation": "Список, напр. email:alice@example.com, tel:+17025550003...",
"defaultMessage": "List like email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like alice@example.com, +17025550003...",
"description": "Placeholder in contacts search field",
"missing": false,
"obsolete": false
@@ -1215,13 +1208,6 @@
"missing": false,
"obsolete": false
},
"validate_credential_action": {
"translation": "подтвердить",
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Описание (не обязательно)",
"defaultMessage": "Description (optional)",
@@ -1431,13 +1417,6 @@
"missing": false,
"obsolete": false
},
"phone_or_email_prompt": {
"translation": "Телефон или емейл",
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Подтверждено успешно",
"defaultMessage": "Confirmed successfully",
@@ -1600,45 +1579,45 @@
"obsolete": false
},
"current_email": {
"translation": "",
"translation": "Текущий e-mail",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"missing": false,
"obsolete": false
},
"current_phone": {
"translation": "",
"translation": "Текущий номер телефона",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"missing": false,
"obsolete": false
},
"new_email": {
"translation": "",
"translation": "Новый e-mail",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"missing": false,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"translation": "Новый номер телефона",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"missing": false,
"obsolete": false
},
"change_email": {
"translation": "",
"translation": "Изменить e-mail",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"missing": false,
"obsolete": false
},
"change_phone": {
"translation": "",
"translation": "Изменить номер телефона",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"missing": false,
"obsolete": false
},
"call_busy": {
@@ -1661,5 +1640,12 @@
"description": "Menu item [Unpin] for un-pinning the message.",
"missing": true,
"obsolete": false
},
"failed_to_init_audio": {
"translation": "Не удалось инициализировать аудиозапись",
"defaultMessage": "Failed to initialize audio recording",
"description": "Error message when audio is not available",
"missing": false,
"obsolete": false
}
}
+22 -36
View File
@@ -97,13 +97,6 @@
"missing": false,
"obsolete": false
},
"button_add_another": {
"translation": "新增其他人",
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": true
},
"button_add_members": {
"translation": "新增成員",
"defaultMessage": "Add members",
@@ -593,10 +586,10 @@
"obsolete": false
},
"label_wire_transport": {
"translation": "",
"translation": "線路傳輸:",
"defaultMessage": "Wire transport:",
"description": "Label for wire transport selection in SettingsView",
"missing": true,
"missing": false,
"obsolete": false
},
"label_you": {
@@ -1000,7 +993,7 @@
},
"search_placeholder": {
"translation": "例如 email:alice@example.com, tel:17025550003... 的清單",
"defaultMessage": "List like email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like alice@example.com, +17025550003...",
"description": "Placeholder in contacts search field",
"missing": false,
"obsolete": false
@@ -1215,13 +1208,6 @@
"missing": false,
"obsolete": false
},
"validate_credential_action": {
"translation": "確認",
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": true
},
"description_editing_placeholder": {
"translation": "說明(可選)",
"defaultMessage": "Description (optional)",
@@ -1431,13 +1417,6 @@
"missing": false,
"obsolete": false
},
"phone_or_email_prompt": {
"translation": "電話號碼或電子郵件",
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "確認成功",
"defaultMessage": "Confirmed successfully",
@@ -1600,45 +1579,45 @@
"obsolete": false
},
"current_email": {
"translation": "",
"translation": "當前郵箱",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"missing": false,
"obsolete": false
},
"current_phone": {
"translation": "",
"translation": "當前電話號碼",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"missing": false,
"obsolete": false
},
"new_email": {
"translation": "",
"translation": "新電子郵件地址",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"missing": false,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"translation": "新電話號碼",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"missing": false,
"obsolete": false
},
"change_email": {
"translation": "",
"translation": "更改郵箱",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"missing": false,
"obsolete": false
},
"change_phone": {
"translation": "",
"translation": "更改電話號碼",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"missing": false,
"obsolete": false
},
"call_busy": {
@@ -1661,5 +1640,12 @@
"description": "Menu item [Unpin] for un-pinning the message.",
"missing": true,
"obsolete": false
},
"failed_to_init_audio": {
"translation": "初始化錄音失敗",
"defaultMessage": "Failed to initialize audio recording",
"description": "Error message when audio is not available",
"missing": false,
"obsolete": false
}
}
+20 -34
View File
@@ -97,13 +97,6 @@
"missing": false,
"obsolete": false
},
"button_add_another": {
"translation": "加上另一个",
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": true
},
"button_add_members": {
"translation": "添加成员",
"defaultMessage": "Add members",
@@ -1000,7 +993,7 @@
},
"search_placeholder": {
"translation": "列表如 email:alice@example.com, tel:+17025550003...",
"defaultMessage": "List like email:alice@example.com, tel:17025550003...",
"defaultMessage": "List like alice@example.com, +17025550003...",
"description": "Placeholder in contacts search field",
"missing": false,
"obsolete": false
@@ -1215,13 +1208,6 @@
"missing": false,
"obsolete": false
},
"validate_credential_action": {
"translation": "确认",
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": true
},
"description_editing_placeholder": {
"translation": "说明(可选)",
"defaultMessage": "Description (optional)",
@@ -1431,13 +1417,6 @@
"missing": false,
"obsolete": false
},
"phone_or_email_prompt": {
"translation": "电话号码或电子邮件",
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "确认成功",
"defaultMessage": "Confirmed successfully",
@@ -1600,45 +1579,45 @@
"obsolete": false
},
"current_email": {
"translation": "",
"translation": "当前邮箱",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"missing": false,
"obsolete": false
},
"current_phone": {
"translation": "",
"translation": "当前电话号码",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"missing": false,
"obsolete": false
},
"new_email": {
"translation": "",
"translation": "新电子邮件地址",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"missing": false,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"translation": "新电话号码",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"missing": false,
"obsolete": false
},
"change_email": {
"translation": "",
"translation": "更改邮箱",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"missing": false,
"obsolete": false
},
"change_phone": {
"translation": "",
"translation": "更改电话号码",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"missing": false,
"obsolete": false
},
"call_busy": {
@@ -1661,5 +1640,12 @@
"description": "Menu item [Unpin] for un-pinning the message.",
"missing": true,
"obsolete": false
},
"failed_to_init_audio": {
"translation": "初始化录音失败",
"defaultMessage": "Failed to initialize audio recording",
"description": "Error message when audio is not available",
"missing": false,
"obsolete": false
}
}
+2
View File
@@ -32,9 +32,11 @@ export default class HashNavigation {
const args = [];
for (const key in params) {
if (params.hasOwnProperty(key)) {
if (params[key] !== undefined) {
args.push(key + '=' + encodeURIComponent(params[key]));
}
}
}
if (args.length > 0) {
url += '?' + args.join('&');
}
+15 -7
View File
@@ -46,12 +46,21 @@ export default class AccountSettingsView extends React.Component {
val={this.state.credEdit.val}
done={this.state.credEdit.done}
onShowCountrySelector={this.props.onShowCountrySelector}
onSubmit={this.props.onCredAdd}
onCredAdd={this.props.onCredAdd}
onCredConfirm={this.props.onCredConfirm}
onCancel={_ => this.setState({credEdit: undefined})}
onError={this.props.onError} />
);
}
// Count how many times each method is validated.
const validated = {};
this.state.credentials.forEach(cred => {
if (cred.done) {
validated[cred.meth] = (validated[cred.meth] || 0) + 1;
}
});
const credentials = [];
this.state.credentials.forEach((cred, idx) => {
if (!['email', 'tel'].includes(cred.meth)) {
@@ -68,15 +77,14 @@ export default class AccountSettingsView extends React.Component {
credentials.push(
<div className="group quoted" key={idx}>
<tt className="clickable" onClick={e => {e.preventDefault(); this.setState({credEdit: cred});}}>{val}</tt>
<span> {
cred.done ? null :
<>
<i className="material-icons">pending</i>&nbsp;
<span> {cred.done ? null : <i className="material-icons">pending</i>}&nbsp;
{!cred.done || validated[cred.meth] > 1 || this.props.reqCredMethod != cred.meth ?
<a href="#" onClick={e => {e.preventDefault(); this.props.onCredDelete(cred.meth, cred.val);}}>
<i className="material-icons">delete</i>
</a>
</>
}</span>
:
null}
</span>
</div>);
});
if (credentials.length > 0) {
+59 -20
View File
@@ -21,8 +21,8 @@ import PinnedMessages from '../widgets/pinned-messages.jsx';
import SendMessage from '../widgets/send-message.jsx';
import VideoPreview from '../widgets/video-preview.jsx';
import { DEFAULT_P2P_ACCESS_MODE, EDIT_PREVIEW_LENGTH, IMAGE_PREVIEW_DIM, KEYPRESS_DELAY,
MESSAGES_PAGE, MAX_EXTERN_ATTACHMENT_SIZE, MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE,
import { DEFAULT_P2P_ACCESS_MODE, EDIT_PREVIEW_LENGTH, IMAGE_PREVIEW_DIM, IMMEDIATE_P2P_SUBSCRIPTION,
KEYPRESS_DELAY, MESSAGES_PAGE, MAX_EXTERN_ATTACHMENT_SIZE, MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE,
READ_DELAY, QUOTED_REPLY_LENGTH, VIDEO_PREVIEW_DIM } from '../config.js';
import { CALL_STATE_OUTGOING_INITATED, CALL_STATE_IN_PROGRESS } from '../constants.js';
import { blobToBase64, fileToBase64, imageScaled, makeImageUrl } from '../lib/blob-helpers.js';
@@ -109,6 +109,7 @@ class MessagesView extends React.Component {
this.state = MessagesView.getDerivedStateFromProps(props, {});
this.componentSetup = this.componentSetup.bind(this);
this.leave = this.leave.bind(this);
this.sendMessage = this.sendMessage.bind(this);
this.retrySend = this.retrySend.bind(this);
@@ -156,8 +157,9 @@ class MessagesView extends React.Component {
this.handleDrag = this.handleDrag.bind(this);
this.handleDrop = this.handleDrop.bind(this);
this.chatMessageRefs = {};
this.chatMessageRefs = [];
this.getOrCreateMessageRef = this.getOrCreateMessageRef.bind(this);
this.getVisibleMessageRange = this.getVisibleMessageRange.bind(this);
// Keeps track of the drag event.
// Need a counter b/c the browser's 'drag' events may fire multiple times
@@ -173,7 +175,7 @@ class MessagesView extends React.Component {
}
getOrCreateMessageRef(seqId) {
if (this.chatMessageRefs.hasOwnProperty(seqId)) {
if (this.chatMessageRefs[seqId]) {
return this.chatMessageRefs[seqId];
}
const ref = React.createRef();
@@ -181,6 +183,21 @@ class MessagesView extends React.Component {
return ref;
}
getVisibleMessageRange(holderRect) {
let min = Number.MAX_SAFE_INTEGER, max = -1;
this.chatMessageRefs.forEach((ref, seq) => {
if (ref.current) {
const { top, bottom, height } = ref.current.getBoundingClientRect();
const visible = top <= holderRect.top ? holderRect.top - top <= height : bottom - holderRect.bottom <= height;
if (visible) {
min = Math.min(min, seq);
max = Math.max(max, seq);
}
}
});
return max >= min ? {min: min, max: max} : {min: 0, max: 0};
}
componentDidMount() {
if (this.messagesScroller) {
this.messagesScroller.addEventListener('scroll', this.handleScrollEvent);
@@ -188,6 +205,8 @@ class MessagesView extends React.Component {
// Drag and drop events
this.mountDnDEvents(this.dndRef);
this.componentSetup({}, {});
}
componentWillUnmount() {
@@ -208,9 +227,9 @@ class MessagesView extends React.Component {
}
}
componentDidUpdate(prevProps, prevState) {
// Scroll last message into view on component update e.g. on message received
// or vertical shrinking.
componentDidUpdate(prevProps, prevState) {
if (this.messagesScroller &&
(prevState.topic != this.state.topic || prevState.maxSeqId != this.state.maxSeqId ||
prevState.minSeqId != this.state.minSeqId)) {
@@ -221,6 +240,18 @@ class MessagesView extends React.Component {
}
}
if (!this.props.applicationVisible) {
// If application is not visible, flush all unsent 'read' notifications.
this.clearNotificationQueue();
} else {
// Otherwise assume there are unread messages.
this.postReadNotification(0);
}
this.componentSetup(prevProps, prevState);
}
componentSetup(prevProps, prevState) {
const topic = this.props.tinode ? this.props.tinode.getTopic(this.state.topic) : undefined;
if (this.state.topic != prevState.topic) {
if (prevState.topic && !Tinode.isNewGroupTopicName(prevState.topic)) {
@@ -241,21 +272,19 @@ class MessagesView extends React.Component {
}
}
if (!this.props.applicationVisible) {
// If application is not visible, flush all unsent 'read' notifications.
this.clearNotificationQueue();
} else {
// Otherwise assume there are unread messages.
this.postReadNotification(0);
}
if (topic && ((this.state.topic != prevState.topic) || !prevProps.ready)) {
if (topic) {
if ((this.state.topic != prevState.topic) || (this.props.myUserId && !prevProps.myUserId)) {
// Don't immediately subscribe to a new p2p topic, wait for the first message.
if (topic._new && topic.isP2PType()) {
const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic);
if (topic.isP2PType() && newTopic && !IMMEDIATE_P2P_SUBSCRIPTION) {
topic.getMeta(topic.startMetaQuery().withDesc().build());
} else {
} else if (this.props.myUserId) {
this.subscribe(topic);
}
} else if (topic.isSubscribed() && this.state.isReader && !prevState.isReader) {
// If reader status has changed and data became available.
topic.getMeta(topic.startMetaQuery().withLaterData().build());
}
}
}
@@ -448,7 +477,6 @@ class MessagesView extends React.Component {
// Is this a new topic?
const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic);
// Don't request the tags. They are useless unless the user
// is the owner and is editing the topic.
let getQuery = topic.startMetaQuery().withLaterDesc().withLaterSub().withAux();
@@ -473,6 +501,9 @@ class MessagesView extends React.Component {
if (this.state.topic != ctrl.topic) {
this.setState({topic: ctrl.topic});
}
if (this.state.deleted) {
this.setState({deleted: false});
}
this.props.onNewTopicCreated(this.props.topic, ctrl.topic);
// If there are unsent messages (except hard-failed and video call messages),
// try sending them now. Hard-failed and unsent video call messages will be dropped.
@@ -552,15 +583,19 @@ class MessagesView extends React.Component {
if (event.target.scrollTop <= FETCH_PAGE_TRIGGER) {
const topic = this.props.tinode.getTopic(this.state.topic);
if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {
if (topic && topic.isSubscribed()) {
const {min, max} = this.getVisibleMessageRange(event.target.getBoundingClientRect());
const gaps = topic.msgHasMoreMessages(min, max, false);
if (gaps.length > 0) {
this.setState({fetchingMessages: true}, _ => {
topic.getMessagesPage(MESSAGES_PAGE)
topic.getMessagesPage(MESSAGES_PAGE, gaps, min, max)
.catch(err => this.props.onError(err.message, 'err'))
.finally(_ => this.setState({fetchingMessages: false}));
});
}
}
}
}
/* Mount drag and drop events */
mountDnDEvents(dnd) {
@@ -737,6 +772,10 @@ class MessagesView extends React.Component {
// 0 means "latest".
this.postReadNotification(0);
}
// Load pinned message too (if any);
const topic = this.props.tinode.getTopic(this.state.topic);
topic.getPinnedMessages();
}
handleAuxUpdate(aux) {
@@ -1438,7 +1477,7 @@ class MessagesView extends React.Component {
}
const pinnedMessages = [];
this.state.pins.forEach(seq => pinnedMessages.push(topic.latestMsgVersion(seq)));
this.state.pins.forEach(seq => pinnedMessages.push(topic.latestMsgVersion(seq) || topic.findMessage(seq)));
const messageNodes = [];
let previousFrom = null;
+3 -3
View File
@@ -25,7 +25,7 @@ const messages = defineMessages({
},
search_placeholder: {
id: "search_placeholder",
defaultMessage: "List like email:alice@example.com, tel:17025550003...",
defaultMessage: "List like alice@example.com, +17025550003...",
description: "Placeholder in contacts search field"
},
});
@@ -61,10 +61,10 @@ class NewTopicView extends React.Component {
this.setState({searchQuery: Tinode.isNullValue(query) ? null : query});
}
handleSearchResultSelected(name) {
handleSearchResultSelected(topicName) {
if (this.state.tabSelected == 'find') {
HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));
this.props.onCreateTopic(name);
this.props.onCreateTopic(topicName);
}
}
+2 -2
View File
@@ -57,7 +57,7 @@ class PasswordResetView extends React.PureComponent {
// Reset using token.
this.props.onReset(this.state.password.trim(), {
scheme: 'token',
token: this.state.token
secret: this.state.token
});
} else if (this.state.code && this.props.reqCredMethod) {
// Reset using security code.
@@ -193,7 +193,7 @@ class PasswordResetView extends React.PureComponent {
{this.state.haveCode || this.state.sent ? codeInput : null}
{showPasswordInput ? passwordInput : null}
<div className="dialog-buttons">
{this.state.haveCode || this.state.sent ? null :
{this.state.haveCode || this.state.sent || this.state.token ? null :
<a href="#" onClick={this.handleShowCodeField} style={{marginRight: 'auto'}}>
<FormattedMessage id="password_i_have_code" defaultMessage="I have code"
description="Call to open field to enter password reset code" />
+1
View File
@@ -181,6 +181,7 @@ class SidepanelView extends React.PureComponent {
tinode={this.props.tinode}
myUserId={this.props.myUserId}
trustedBadges={this.props.trustedBadges}
reqCredMethod={this.props.reqCredMethod}
onShowCountrySelector={this.props.onShowCountrySelector}
onNavigate={this.props.onNavigate}
onCredAdd={this.props.onCredAdd}
+9 -5
View File
@@ -431,7 +431,7 @@ class TinodeWeb extends React.Component {
}).then(token => {
if (token) {
return token;
} else {
} else if (typeof Notification != 'undefined') {
// Try to request permissions.
return Notification.requestPermission().then(permission => {
if (permission === 'granted') {
@@ -450,6 +450,7 @@ class TinodeWeb extends React.Component {
}
});
}
throw new Error("Notifications are not supported");
});
}
@@ -761,6 +762,7 @@ class TinodeWeb extends React.Component {
parsed.path[0] = 'cred';
parsed.params['method'] = params.cred[0];
parsed.params['token'] = params.token;
parsed.params['code'] = params.code;
HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params));
}
@@ -846,7 +848,7 @@ class TinodeWeb extends React.Component {
}
} else if (what == 'read') {
this.resetContactList();
} else if (what == 'msg') {
} else if (what == 'msg' && cont) {
// Check if the topic is archived. If so, don't play a sound.
const topic = this.tinode.getTopic(cont.topic);
const archived = topic && topic.isArchived();
@@ -885,7 +887,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.info("Unsupported (yet) presence update:", what, "in", cont.topic);
console.info("Unsupported (yet) presence update:", what, "in", (cont || {}).topic);
}
}
@@ -1017,6 +1019,7 @@ class TinodeWeb extends React.Component {
} else {
// Currently selected contact deleted
this.setState({
topicSelected: null,
errorText: '',
errorLevel: null,
mobilePanel: 'sidepanel',
@@ -1285,7 +1288,7 @@ class TinodeWeb extends React.Component {
}
handleCredConfirm(method, response) {
TinodeWeb.navigateToCredentialsView({cred: [method]});
TinodeWeb.navigateToCredentialsView({cred: [method], code: response});
}
// User clicked Cancel button in Setting or Sign Up panel.
@@ -1333,7 +1336,7 @@ class TinodeWeb extends React.Component {
const params = {};
if (Tinode.isP2PTopicName(topicName)) {
// Because we are initialing the subscription, set 'want' to all permissions.
// Because we are initiating the subscription, set 'want' to all permissions.
params.sub = {mode: DEFAULT_P2P_ACCESS_MODE};
// Give the other user all permissions too.
params.desc = {defacs: {auth: DEFAULT_P2P_ACCESS_MODE}};
@@ -1704,6 +1707,7 @@ class TinodeWeb extends React.Component {
if (this.tinode.isAuthenticated()) {
// Adding new email or phone number in account setting.
this.tinode.getMeTopic().setMeta({cred: {meth: cred, resp: code}})
.then(_ => HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'contacts')))
.catch(err => this.handleError(err.message, 'err'));
} else {
// Credential validation on signup.
+1 -1
View File
@@ -100,7 +100,7 @@ class ValidationView extends React.PureComponent {
(numbers_only) => <input type="text" id="enter-confirmation-code"
placeholder={numbers_only}
value={this.state.code} onChange={this.handleCodeChange}
onKeyPress={this.handleKeyPress} required />
onKeyDown={this.handleKeyPress} required />
}</FormattedMessage>
</div>
<div className="dialog-buttons">
+6 -5
View File
@@ -70,7 +70,7 @@ class AudioPlayer extends React.PureComponent {
componentWillUnmount() {
if (this.audioPlayer) {
this.audioPlayer.oncanplay = null;
this.audioPlayer.onloadedmetadata = null;
this.audioPlayer.ontimeupdate = null;
this.audioPlayer.onended = null;
this.audioPlayer.pause();
@@ -94,7 +94,7 @@ class AudioPlayer extends React.PureComponent {
initAudio() {
this.audioPlayer = new Audio(this.props.src);
this.audioPlayer.oncanplay = _ => this.setState({canPlay: true});
this.audioPlayer.onloadedmetadata = _ => this.setState({canPlay: true});
this.audioPlayer.ontimeupdate = _ => this.setState({
currentTime: secondsToTime(this.audioPlayer.currentTime, this.state.longMin)
});
@@ -149,7 +149,7 @@ class AudioPlayer extends React.PureComponent {
this.canvasContext.strokeStyle = BAR_COLOR_DARK;
for (let i = 0; i < this.viewBuffer.length; i++) {
let x = 1 + i * (LINE_WIDTH + SPACING) + LINE_WIDTH * 0.5;
let y = this.viewBuffer[i] * height * 0.9;
let y = Math.max(this.viewBuffer[i] * height * 0.9, 1);
const color = x < thumbAt ? BAR_COLOR_DARK : BAR_COLOR;
if (this.canvasContext.strokeStyle != color) {
@@ -159,7 +159,7 @@ class AudioPlayer extends React.PureComponent {
}
this.canvasContext.moveTo(x, (height - y) * 0.5);
this.canvasContext.lineTo(x, height * 0.5 + y * 0.5);
this.canvasContext.lineTo(x, (height + y) * 0.5);
}
// Actually draw the bars on canvas.
this.canvasContext.stroke();
@@ -216,10 +216,11 @@ class AudioPlayer extends React.PureComponent {
if (!this.state.canPlay) {
return;
}
if (this.state.playing) {
this.audioPlayer.pause();
this.setState({playing: false});
} else {
} else if (this.audioPlayer.readyState >= 2) {
this.audioPlayer.play();
this.setState({playing: true}, this.visualize);
}
+39 -8
View File
@@ -31,8 +31,11 @@ const VISUALIZATION_BARS = 96;
// Maximum number of samples per bar.
const MAX_SAMPLES_PER_BAR = 10;
// Recording format.
const AUDIO_MIME_TYPE = 'audio/webm';
// Default recording format (FF, Chrome except on iOS).
const DEFAULT_AUDIO_MIME_TYPE = 'audio/webm';
// Safari supports only mp4 as audio recording format.
const SAFARI_AUDIO_MIME_TYPE = 'audio/mp4';
const AUDIO_MIME_TYPES = [DEFAULT_AUDIO_MIME_TYPE, SAFARI_AUDIO_MIME_TYPE, ''];
const messages = defineMessages({
icon_title_delete: {
@@ -55,6 +58,11 @@ const messages = defineMessages({
defaultMessage: 'Send message',
description: 'Icon tool tip for sending a message'
},
failed_to_init_audio: {
id: 'failed_to_init_audio',
defaultMessage: 'Failed to initialize audio recording',
description: 'Error message when audio is not available'
}
});
class AudioRecorder extends React.PureComponent {
@@ -188,10 +196,10 @@ class AudioRecorder extends React.PureComponent {
this.canvasContext.beginPath();
for (let i = 0; i < this.viewBuffer.length; i++) {
let x = i * (LINE_WIDTH + SPACING) - dx;
let y = Math.min(this.viewBuffer[i] / BAR_SCALE, 0.9) * height;
let y = Math.max(Math.min(this.viewBuffer[i] / BAR_SCALE, 0.9) * height, 1);
this.canvasContext.moveTo(x, (height - y) * 0.5);
this.canvasContext.lineTo(x, height * 0.5 + y * 0.5);
this.canvasContext.lineTo(x, (height + y) * 0.5);
}
// Actually draw the bars on canvas.
this.canvasContext.stroke();
@@ -261,11 +269,29 @@ class AudioRecorder extends React.PureComponent {
initMediaRecording(stream) {
this.stream = stream;
this.mediaRecorder = new MediaRecorder(stream, {mimeType: AUDIO_MIME_TYPE, audioBitsPerSecond: 24_000});
AUDIO_MIME_TYPES.some(mimeType => {
if (MediaRecorder.isTypeSupported(mimeType)) {
this.mediaRecorder = new MediaRecorder(stream, {mimeType: mimeType, audioBitsPerSecond: 24_000});
return true;
}
return false;
});
if (!this.mediaRecorder) {
console.warn('MediaRecorder failed to initialize: no supported audio formats');
this.props.onError(this.props.intl.formatMessage(messages.failed_to_init_audio));
return;
}
// The following code is needed for visualization.
this.audioContext = new AudioContext();
this.audioInput = this.audioContext.createMediaStreamSource(stream);
if (!this.audioInput) {
console.warn('createMediaStreamSource returned null: audio input unavailable');
this.props.onError(this.props.intl.formatMessage(messages.failed_to_init_audio));
return;
}
this.analyser = this.audioContext.createAnalyser();
this.analyser.fftSize = BUFFER_SIZE;
this.audioInput.connect(this.analyser);
@@ -305,11 +331,14 @@ class AudioRecorder extends React.PureComponent {
}
// Obtain data in a form sutable for sending or playing back.
// If duration is valid, apply fix for Chrome's WebM duration bug.
getRecording(mimeType) {
mimeType = mimeType || AUDIO_MIME_TYPE;
mimeType = mimeType || DEFAULT_AUDIO_MIME_TYPE;
let blob = new Blob(this.audioChunks, {type: mimeType});
return fixWebmDuration(blob, mimeType)
// If duration is valid, apply fix for Chrome's WebM duration bug.
const result = mimeType == DEFAULT_AUDIO_MIME_TYPE ?
fixWebmDuration(blob, mimeType) :
Promise.resolve(blob);
return result
.then(fixedBlob => { blob = fixedBlob; return fixedBlob.arrayBuffer(); })
.then(arrayBuff => this.audioContext.decodeAudioData(arrayBuff))
.then(decoded => this.createPreview(decoded))
@@ -347,7 +376,9 @@ class AudioRecorder extends React.PureComponent {
}
cleanUp() {
if (this.audioInput) {
this.audioInput.disconnect();
}
this.stream.getTracks().forEach(track => track.stop());
}
+18 -17
View File
@@ -58,8 +58,11 @@ class CredentialEdit extends React.PureComponent {
handleSubmit(e) {
e.preventDefault();
const value = this.props.method == 'email' ? this.state.email : this.state.tel;
if (value) {
this.props.onSubmit(this.props.method, value);
if (this.state.code) {
this.props.onError(null);
this.props.onCredConfirm(this.props.method, this.state.code);
} else if (value) {
this.props.onCredAdd(this.props.method, value);
this.setState({sent: true});
const msg = this.props.method == 'email' ? messages.password_reset_email_sent : messages.password_reset_sms_sent;
this.props.onError(this.props.intl.formatMessage(msg), 'info');
@@ -68,8 +71,8 @@ class CredentialEdit extends React.PureComponent {
render() {
const readyForCode = this.state.sent || !this.props.done;
const currentValue = CredentialEdit.formatPhoneNumber(readyForCode ?
(this.state.tel || this.state.email) : this.props.val);
const rawValue = readyForCode ? (this.state.tel || this.state.email) : this.props.val;
const formattedValue = this.props.method == 'tel' ? CredentialEdit.formatPhoneNumber(rawValue) : rawValue;
const changeEmail = (
<><div className="panel-form-row">
@@ -140,21 +143,19 @@ class CredentialEdit extends React.PureComponent {
);
return (<form className="panel-form-column" onSubmit={this.handleSubmit}>
{readyForCode ? null :
this.props.method == 'email' ?
changeEmail
: this.props.method == 'tel' ?
changePhone
: null}
{readyForCode ?
(this.props.method == 'email' ? newEmailLabel : this.props.method == 'tel' ? newPhoneLabel : null)
:
(this.props.method == 'email' ? changeEmail : this.props.method == 'tel' ? changePhone : null)
}
<div className="panel-form-row">
<tt className="quoted">{currentValue}</tt>
<tt className="quoted">{formattedValue}</tt>
</div>
{readyForCode ? null :
this.props.method == 'email' ?
newEmailInput
: this.props.method == 'tel' ?
newPhoneInput
: null}
{readyForCode ?
null
:
(this.props.method == 'email' ? newEmailInput : this.props.method == 'tel' ? newPhoneInput : null)
}
{readyForCode ? codeInput : null}
<div className="dialog-buttons">
<button className="secondary" onClick={this.props.onCancel}>
+1 -1
View File
@@ -39,7 +39,7 @@ export default class ErrorPanel extends React.PureComponent {
{this.props.text}
{this.props.action ?
<>
&#32;<a href="#"
&nbsp;&#32;<a href="#"
style={{ whiteSpace: 'nowrap' }}
onClick={(e) => {e.preventDefault(); this.props.action();}}>
{this.props.actionText}
+1 -1
View File
@@ -72,7 +72,7 @@ class PinnedMessages extends React.PureComponent {
<div><i className="material-icons gray">push_pin</i></div>
}
<div className="pinned-scroll">{dots}</div>
<div className="pinned" onClick={this.handleSelected}>{shown}</div>
<div className="pinned" onClick={this.handleSelected}><p>{shown}</p></div>
{selected > 0 ?
<span className="menuTrigger upper">
<a href="#" onClick={this.handleMovePrev}>
+3 -3
View File
@@ -8,7 +8,7 @@ export default class VisiblePassword extends React.PureComponent {
this.inputRef = React.createRef();
this.state = {
value: this.props.value,
value: this.props.value || '',
visible: false
};
@@ -39,7 +39,7 @@ export default class VisiblePassword extends React.PureComponent {
handleKeyDown(e) {
if (e.keyCode == 27) {
// Escape pressed
this.setState({value: this.props.value, visible: false});
this.setState({value: this.props.value || '', visible: false});
if (this.props.onFinished) {
this.props.onFinished();
}
@@ -68,7 +68,7 @@ export default class VisiblePassword extends React.PureComponent {
return (
<div tabIndex="-1" className="group-focus" onBlur={this.handleEditingFinished}>
<input className="with-visibility"
type={this.state.visible ? "text" : "password"}
type={this.state.visible ? 'text' : 'password'}
value={this.state.value}
placeholder={this.props.placeholder}
required={this.props.required ? 'required' : ''}
+1 -1
View File
File diff suppressed because one or more lines are too long
+2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
-2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+94 -35
View File
@@ -2212,6 +2212,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ "FORWARDED_PREVIEW_LENGTH": () => (/* binding */ FORWARDED_PREVIEW_LENGTH),
/* harmony export */ "IMAGE_PREVIEW_DIM": () => (/* binding */ IMAGE_PREVIEW_DIM),
/* harmony export */ "IMAGE_THUMBNAIL_DIM": () => (/* binding */ IMAGE_THUMBNAIL_DIM),
/* harmony export */ "IMMEDIATE_P2P_SUBSCRIPTION": () => (/* binding */ IMMEDIATE_P2P_SUBSCRIPTION),
/* harmony export */ "KEYPRESS_DELAY": () => (/* binding */ KEYPRESS_DELAY),
/* harmony export */ "KNOWN_HOSTS": () => (/* binding */ KNOWN_HOSTS),
/* harmony export */ "LINK_CONTACT_US": () => (/* binding */ LINK_CONTACT_US),
@@ -2289,6 +2290,7 @@ const MAX_DURATION = 600_000;
const LINK_CONTACT_US = 'mailto:support@tinode.co';
const LINK_PRIVACY_POLICY = 'https://tinode.co/privacy.html';
const LINK_TERMS_OF_SERVICE = 'https://tinode.co/terms.html';
const IMMEDIATE_P2P_SUBSCRIPTION = false;
/***/ }),
@@ -3239,9 +3241,11 @@ class HashNavigation {
const args = [];
for (const key in params) {
if (params.hasOwnProperty(key)) {
if (params[key] !== undefined) {
args.push(key + '=' + encodeURIComponent(params[key]));
}
}
}
if (args.length > 0) {
url += '?' + args.join('&');
}
@@ -5181,6 +5185,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
constructor(props) {
super(props);
this.state = MessagesView.getDerivedStateFromProps(props, {});
this.componentSetup = this.componentSetup.bind(this);
this.leave = this.leave.bind(this);
this.sendMessage = this.sendMessage.bind(this);
this.retrySend = this.retrySend.bind(this);
@@ -5225,8 +5230,9 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
this.handleDragOut = this.handleDragOut.bind(this);
this.handleDrag = this.handleDrag.bind(this);
this.handleDrop = this.handleDrop.bind(this);
this.chatMessageRefs = {};
this.chatMessageRefs = [];
this.getOrCreateMessageRef = this.getOrCreateMessageRef.bind(this);
this.getVisibleMessageRange = this.getVisibleMessageRange.bind(this);
this.dragCounter = 0;
this.dndRef = null;
this.readNotificationQueue = [];
@@ -5234,18 +5240,44 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
this.keyPressTimer = null;
}
getOrCreateMessageRef(seqId) {
if (this.chatMessageRefs.hasOwnProperty(seqId)) {
if (this.chatMessageRefs[seqId]) {
return this.chatMessageRefs[seqId];
}
const ref = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
this.chatMessageRefs[seqId] = ref;
return ref;
}
getVisibleMessageRange(holderRect) {
let min = Number.MAX_SAFE_INTEGER,
max = -1;
this.chatMessageRefs.forEach((ref, seq) => {
if (ref.current) {
const {
top,
bottom,
height
} = ref.current.getBoundingClientRect();
const visible = top <= holderRect.top ? holderRect.top - top <= height : bottom - holderRect.bottom <= height;
if (visible) {
min = Math.min(min, seq);
max = Math.max(max, seq);
}
}
});
return max >= min ? {
min: min,
max: max
} : {
min: 0,
max: 0
};
}
componentDidMount() {
if (this.messagesScroller) {
this.messagesScroller.addEventListener('scroll', this.handleScrollEvent);
}
this.mountDnDEvents(this.dndRef);
this.componentSetup({}, {});
}
componentWillUnmount() {
if (this.messagesScroller) {
@@ -5266,6 +5298,14 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition - this.messagesScroller.offsetHeight;
}
}
if (!this.props.applicationVisible) {
this.clearNotificationQueue();
} else {
this.postReadNotification(0);
}
this.componentSetup(prevProps, prevState);
}
componentSetup(prevProps, prevState) {
const topic = this.props.tinode ? this.props.tinode.getTopic(this.state.topic) : undefined;
if (this.state.topic != prevState.topic) {
if (prevState.topic && !tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.isNewGroupTopicName(prevState.topic)) {
@@ -5284,17 +5324,17 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
topic.onAuxUpdated = this.handleAuxUpdate;
}
}
if (!this.props.applicationVisible) {
this.clearNotificationQueue();
} else {
this.postReadNotification(0);
}
if (topic && (this.state.topic != prevState.topic || !prevProps.ready)) {
if (topic._new && topic.isP2PType()) {
if (topic) {
if (this.state.topic != prevState.topic || this.props.myUserId && !prevProps.myUserId) {
const newTopic = this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic;
if (topic.isP2PType() && newTopic && !_config_js__WEBPACK_IMPORTED_MODULE_18__.IMMEDIATE_P2P_SUBSCRIPTION) {
topic.getMeta(topic.startMetaQuery().withDesc().build());
} else {
} else if (this.props.myUserId) {
this.subscribe(topic);
}
} else if (topic.isSubscribed() && this.state.isReader && !prevState.isReader) {
topic.getMeta(topic.startMetaQuery().withLaterData().build());
}
}
}
static getDerivedStateFromProps(nextProps, prevState) {
@@ -5485,6 +5525,11 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
topic: ctrl.topic
});
}
if (this.state.deleted) {
this.setState({
deleted: false
});
}
this.props.onNewTopicCreated(this.props.topic, ctrl.topic);
let discard = [];
topic.queuedMessages(pub => {
@@ -5548,17 +5593,24 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}
if (event.target.scrollTop <= FETCH_PAGE_TRIGGER) {
const topic = this.props.tinode.getTopic(this.state.topic);
if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {
if (topic && topic.isSubscribed()) {
const {
min,
max
} = this.getVisibleMessageRange(event.target.getBoundingClientRect());
const gaps = topic.msgHasMoreMessages(min, max, false);
if (gaps.length > 0) {
this.setState({
fetchingMessages: true
}, _ => {
topic.getMessagesPage(_config_js__WEBPACK_IMPORTED_MODULE_18__.MESSAGES_PAGE).catch(err => this.props.onError(err.message, 'err')).finally(_ => this.setState({
topic.getMessagesPage(_config_js__WEBPACK_IMPORTED_MODULE_18__.MESSAGES_PAGE, gaps, min, max).catch(err => this.props.onError(err.message, 'err')).finally(_ => this.setState({
fetchingMessages: false
}));
});
}
}
}
}
mountDnDEvents(dnd) {
if (dnd) {
dnd.addEventListener('dragstart', this.handleDragStart);
@@ -5715,6 +5767,8 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
if (count > 0) {
this.postReadNotification(0);
}
const topic = this.props.tinode.getTopic(this.state.topic);
topic.getPinnedMessages();
}
handleAuxUpdate(aux) {
const pins = (aux['pins'] || []).slice();
@@ -6337,7 +6391,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}
}
const pinnedMessages = [];
this.state.pins.forEach(seq => pinnedMessages.push(topic.latestMsgVersion(seq)));
this.state.pins.forEach(seq => pinnedMessages.push(topic.latestMsgVersion(seq) || topic.findMessage(seq)));
const messageNodes = [];
let previousFrom = null;
let prevDate = null;
@@ -6640,7 +6694,7 @@ const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
id: "search_placeholder",
defaultMessage: [{
"type": 0,
"value": "List like email:alice@example.com, tel:17025550003..."
"value": "List like alice@example.com, +17025550003..."
}]
}
});
@@ -6673,10 +6727,10 @@ class NewTopicView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
searchQuery: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.isNullValue(query) ? null : query
});
}
handleSearchResultSelected(name) {
handleSearchResultSelected(topicName) {
if (this.state.tabSelected == 'find') {
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_7__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_7__["default"].removeUrlParam(window.location.hash, 'tab'));
this.props.onCreateTopic(name);
this.props.onCreateTopic(topicName);
}
}
handleNewGroupSubmit(name, description, dataUrl, priv, tags, isChannel) {
@@ -7144,6 +7198,7 @@ class SidepanelView extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
tinode: this.props.tinode,
myUserId: this.props.myUserId,
trustedBadges: this.props.trustedBadges,
reqCredMethod: this.props.reqCredMethod,
onShowCountrySelector: this.props.onShowCountrySelector,
onNavigate: this.props.onNavigate,
onCredAdd: this.props.onCredAdd,
@@ -7657,7 +7712,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
}).then(token => {
if (token) {
return token;
} else {
} else if (typeof Notification != 'undefined') {
return Notification.requestPermission().then(permission => {
if (permission === 'granted') {
return (0,firebase_messaging__WEBPACK_IMPORTED_MODULE_3__.getToken)(fcm, {
@@ -7675,6 +7730,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
}
});
}
throw new Error("Notifications are not supported");
});
}
handleResize() {
@@ -7957,6 +8013,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
parsed.path[0] = 'cred';
parsed.params['method'] = params.cred[0];
parsed.params['token'] = params.token;
parsed.params['code'] = params.code;
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_18__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_18__["default"].composeUrlHash(parsed.path, parsed.params));
}
handleLoginSuccessful() {
@@ -8030,7 +8087,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
}
} else if (what == 'read') {
this.resetContactList();
} else if (what == 'msg') {
} else if (what == 'msg' && cont) {
const topic = this.tinode.getTopic(cont.topic);
const archived = topic && topic.isArchived();
if (cont.unread > 0 && this.state.messageSounds && !archived) {
@@ -8051,7 +8108,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
});
}
} else if (what == 'del') {} else if (what == 'upd' || what == 'call') {} else {
console.info("Unsupported (yet) presence update:", what, "in", cont.topic);
console.info("Unsupported (yet) presence update:", what, "in", (cont || {}).topic);
}
}
tnMeSubsUpdated(unused) {
@@ -8153,6 +8210,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
}
} else {
this.setState({
topicSelected: null,
errorText: '',
errorLevel: null,
mobilePanel: 'sidepanel',
@@ -8400,7 +8458,8 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
}
handleCredConfirm(method, response) {
TinodeWeb.navigateToCredentialsView({
cred: [method]
cred: [method],
code: response
});
}
handleSidepanelCancel() {
@@ -8773,7 +8832,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
meth: cred,
resp: code
}
}).catch(err => this.handleError(err.message, 'err'));
}).then(_ => _lib_navigation_js__WEBPACK_IMPORTED_MODULE_18__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_18__["default"].setUrlSidePanel(window.location.hash, 'contacts'))).catch(err => this.handleError(err.message, 'err'));
} else {
this.setState({
credMethod: cred,
@@ -9371,7 +9430,7 @@ class ValidationView extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
placeholder: numbers_only,
value: this.state.code,
onChange: this.handleCodeChange,
onKeyPress: this.handleKeyPress,
onKeyDown: this.handleKeyPress,
required: true
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "dialog-buttons"
@@ -9637,7 +9696,7 @@ class AudioPlayer extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComp
}
componentWillUnmount() {
if (this.audioPlayer) {
this.audioPlayer.oncanplay = null;
this.audioPlayer.onloadedmetadata = null;
this.audioPlayer.ontimeupdate = null;
this.audioPlayer.onended = null;
this.audioPlayer.pause();
@@ -9660,7 +9719,7 @@ class AudioPlayer extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComp
}
initAudio() {
this.audioPlayer = new Audio(this.props.src);
this.audioPlayer.oncanplay = _ => this.setState({
this.audioPlayer.onloadedmetadata = _ => this.setState({
canPlay: true
});
this.audioPlayer.ontimeupdate = _ => this.setState({
@@ -9703,7 +9762,7 @@ class AudioPlayer extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComp
this.canvasContext.strokeStyle = BAR_COLOR_DARK;
for (let i = 0; i < this.viewBuffer.length; i++) {
let x = 1 + i * (LINE_WIDTH + SPACING) + LINE_WIDTH * 0.5;
let y = this.viewBuffer[i] * height * 0.9;
let y = Math.max(this.viewBuffer[i] * height * 0.9, 1);
const color = x < thumbAt ? BAR_COLOR_DARK : BAR_COLOR;
if (this.canvasContext.strokeStyle != color) {
this.canvasContext.stroke();
@@ -9711,7 +9770,7 @@ class AudioPlayer extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComp
this.canvasContext.strokeStyle = color;
}
this.canvasContext.moveTo(x, (height - y) * 0.5);
this.canvasContext.lineTo(x, height * 0.5 + y * 0.5);
this.canvasContext.lineTo(x, (height + y) * 0.5);
}
this.canvasContext.stroke();
if (this.props.duration) {
@@ -9762,7 +9821,7 @@ class AudioPlayer extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComp
this.setState({
playing: false
});
} else {
} else if (this.audioPlayer.readyState >= 2) {
this.audioPlayer.play();
this.setState({
playing: true
@@ -12879,7 +12938,7 @@ class ErrorPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompo
className: "icon"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons"
}, level)), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, this.props.text, this.props.action ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
}, level)), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, this.props.text, this.props.action ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, "\xA0 ", react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
href: "#",
style: {
whiteSpace: 'nowrap'
@@ -14884,7 +14943,7 @@ class PinnedMessages extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
}, dots), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "pinned",
onClick: this.handleSelected
}, shown), selected > 0 ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("p", null, shown)), selected > 0 ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
className: "menuTrigger upper"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
href: "#",
@@ -16505,7 +16564,7 @@ class VisiblePassword extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
super(props);
this.inputRef = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
this.state = {
value: this.props.value,
value: this.props.value || '',
visible: false
};
this.handleVisibility = this.handleVisibility.bind(this);
@@ -16535,7 +16594,7 @@ class VisiblePassword extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
handleKeyDown(e) {
if (e.keyCode == 27) {
this.setState({
value: this.props.value,
value: this.props.value || '',
visible: false
});
if (this.props.onFinished) {
@@ -16566,7 +16625,7 @@ class VisiblePassword extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
onBlur: this.handleEditingFinished
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", {
className: "with-visibility",
type: this.state.visible ? "text" : "password",
type: this.state.visible ? 'text' : 'password',
value: this.state.value,
placeholder: this.props.placeholder,
required: this.props.required ? 'required' : '',
@@ -16748,7 +16807,7 @@ function isVersionServiceProvider(provider) {
}
const name$o = "@firebase/app";
const version$1 = "0.9.5";
const version$1 = "0.9.7";
/**
* @license
@@ -16815,7 +16874,7 @@ const name$2 = "@firebase/firestore";
const name$1 = "@firebase/firestore-compat";
const name = "firebase";
const version = "9.18.0";
const version = "9.19.1";
/**
* @license
@@ -20752,7 +20811,7 @@ __webpack_require__.r(__webpack_exports__);
var name = "firebase";
var version = "9.18.0";
var version = "9.19.1";
/**
* @license
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+20 -9
View File
@@ -61,13 +61,20 @@ class AccountSettingsView extends (react__WEBPACK_IMPORTED_MODULE_0___default().
val: this.state.credEdit.val,
done: this.state.credEdit.done,
onShowCountrySelector: this.props.onShowCountrySelector,
onSubmit: this.props.onCredAdd,
onCredAdd: this.props.onCredAdd,
onCredConfirm: this.props.onCredConfirm,
onCancel: _ => this.setState({
credEdit: undefined
}),
onError: this.props.onError
});
}
const validated = {};
this.state.credentials.forEach(cred => {
if (cred.done) {
validated[cred.meth] = (validated[cred.meth] || 0) + 1;
}
});
const credentials = [];
this.state.credentials.forEach((cred, idx) => {
if (!['email', 'tel'].includes(cred.meth)) {
@@ -89,9 +96,9 @@ class AccountSettingsView extends (react__WEBPACK_IMPORTED_MODULE_0___default().
credEdit: cred
});
}
}, val), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, " ", cred.done ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
}, val), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, " ", cred.done ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons"
}, "pending"), "\xA0", react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
}, "pending"), "\xA0", !cred.done || validated[cred.meth] > 1 || this.props.reqCredMethod != cred.meth ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
href: "#",
onClick: e => {
e.preventDefault();
@@ -99,7 +106,7 @@ class AccountSettingsView extends (react__WEBPACK_IMPORTED_MODULE_0___default().
}
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons"
}, "delete"))))));
}, "delete")) : null)));
});
if (credentials.length > 0) {
credentials.unshift(react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
@@ -313,8 +320,11 @@ class CredentialEdit extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
handleSubmit(e) {
e.preventDefault();
const value = this.props.method == 'email' ? this.state.email : this.state.tel;
if (value) {
this.props.onSubmit(this.props.method, value);
if (this.state.code) {
this.props.onError(null);
this.props.onCredConfirm(this.props.method, this.state.code);
} else if (value) {
this.props.onCredAdd(this.props.method, value);
this.setState({
sent: true
});
@@ -324,7 +334,8 @@ class CredentialEdit extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
}
render() {
const readyForCode = this.state.sent || !this.props.done;
const currentValue = CredentialEdit.formatPhoneNumber(readyForCode ? this.state.tel || this.state.email : this.props.val);
const rawValue = readyForCode ? this.state.tel || this.state.email : this.props.val;
const formattedValue = this.props.method == 'tel' ? CredentialEdit.formatPhoneNumber(rawValue) : rawValue;
const changeEmail = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "panel-form-row"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
@@ -441,11 +452,11 @@ class CredentialEdit extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("form", {
className: "panel-form-column",
onSubmit: this.handleSubmit
}, readyForCode ? null : this.props.method == 'email' ? changeEmail : this.props.method == 'tel' ? changePhone : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
}, readyForCode ? this.props.method == 'email' ? newEmailLabel : this.props.method == 'tel' ? newPhoneLabel : null : this.props.method == 'email' ? changeEmail : this.props.method == 'tel' ? changePhone : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "panel-form-row"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tt", {
className: "quoted"
}, currentValue)), readyForCode ? null : this.props.method == 'email' ? newEmailInput : this.props.method == 'tel' ? newPhoneInput : null, readyForCode ? codeInput : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
}, formattedValue)), readyForCode ? null : this.props.method == 'email' ? newEmailInput : this.props.method == 'tel' ? newPhoneInput : null, readyForCode ? codeInput : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "dialog-buttons"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
className: "secondary",
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -71,7 +71,7 @@ class PasswordResetView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
if (this.state.token) {
this.props.onReset(this.state.password.trim(), {
scheme: 'token',
token: this.state.token
secret: this.state.token
});
} else if (this.state.code && this.props.reqCredMethod) {
const cred = this.state.email.trim() || this.state.tel.trim();
@@ -222,7 +222,7 @@ class PasswordResetView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
onSubmit: this.handleSubmit
}, !this.state.sent && showCredentialInput ? credentialInput : null, this.state.haveCode || this.state.sent ? codeInput : null, showPasswordInput ? passwordInput : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "dialog-buttons"
}, this.state.haveCode || this.state.sent ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
}, this.state.haveCode || this.state.sent || this.state.token ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
href: "#",
onClick: this.handleShowCodeField,
style: {
File diff suppressed because one or more lines are too long
+34 -6
View File
@@ -37,7 +37,9 @@ const BAR_COLOR = '#BBBD';
const BAR_SCALE = 64.0;
const VISUALIZATION_BARS = 96;
const MAX_SAMPLES_PER_BAR = 10;
const AUDIO_MIME_TYPE = 'audio/webm';
const DEFAULT_AUDIO_MIME_TYPE = 'audio/webm';
const SAFARI_AUDIO_MIME_TYPE = 'audio/mp4';
const AUDIO_MIME_TYPES = [DEFAULT_AUDIO_MIME_TYPE, SAFARI_AUDIO_MIME_TYPE, ''];
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
icon_title_delete: {
id: "icon_title_delete",
@@ -66,6 +68,13 @@ const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
"type": 0,
"value": "Send message"
}]
},
failed_to_init_audio: {
id: "failed_to_init_audio",
defaultMessage: [{
"type": 0,
"value": "Failed to initialize audio recording"
}]
}
});
class AudioRecorder extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) {
@@ -170,9 +179,9 @@ class AudioRecorder extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
this.canvasContext.beginPath();
for (let i = 0; i < this.viewBuffer.length; i++) {
let x = i * (LINE_WIDTH + SPACING) - dx;
let y = Math.min(this.viewBuffer[i] / BAR_SCALE, 0.9) * height;
let y = Math.max(Math.min(this.viewBuffer[i] / BAR_SCALE, 0.9) * height, 1);
this.canvasContext.moveTo(x, (height - y) * 0.5);
this.canvasContext.lineTo(x, height * 0.5 + y * 0.5);
this.canvasContext.lineTo(x, (height + y) * 0.5);
}
this.canvasContext.stroke();
const now = new Date().getTime();
@@ -237,12 +246,28 @@ class AudioRecorder extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
}
initMediaRecording(stream) {
this.stream = stream;
AUDIO_MIME_TYPES.some(mimeType => {
if (MediaRecorder.isTypeSupported(mimeType)) {
this.mediaRecorder = new MediaRecorder(stream, {
mimeType: AUDIO_MIME_TYPE,
mimeType: mimeType,
audioBitsPerSecond: 24_000
});
return true;
}
return false;
});
if (!this.mediaRecorder) {
console.warn('MediaRecorder failed to initialize: no supported audio formats');
this.props.onError(this.props.intl.formatMessage(messages.failed_to_init_audio));
return;
}
this.audioContext = new AudioContext();
this.audioInput = this.audioContext.createMediaStreamSource(stream);
if (!this.audioInput) {
console.warn('createMediaStreamSource returned null: audio input unavailable');
this.props.onError(this.props.intl.formatMessage(messages.failed_to_init_audio));
return;
}
this.analyser = this.audioContext.createAnalyser();
this.analyser.fftSize = BUFFER_SIZE;
this.audioInput.connect(this.analyser);
@@ -275,11 +300,12 @@ class AudioRecorder extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
this.recordingTimestamp = this.startedOn;
}
getRecording(mimeType) {
mimeType = mimeType || AUDIO_MIME_TYPE;
mimeType = mimeType || DEFAULT_AUDIO_MIME_TYPE;
let blob = new Blob(this.audioChunks, {
type: mimeType
});
return webm_duration_fix__WEBPACK_IMPORTED_MODULE_3___default()(blob, mimeType).then(fixedBlob => {
const result = mimeType == DEFAULT_AUDIO_MIME_TYPE ? webm_duration_fix__WEBPACK_IMPORTED_MODULE_3___default()(blob, mimeType) : Promise.resolve(blob);
return result.then(fixedBlob => {
blob = fixedBlob;
return fixedBlob.arrayBuffer();
}).then(arrayBuff => this.audioContext.decodeAudioData(arrayBuff)).then(decoded => this.createPreview(decoded)).then(preview => ({
@@ -311,7 +337,9 @@ class AudioRecorder extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
return buffer;
}
cleanUp() {
if (this.audioInput) {
this.audioInput.disconnect();
}
this.stream.getTracks().forEach(track => track.stop());
}
render() {
File diff suppressed because one or more lines are too long
+1523 -1368
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long