Compare commits
37
Commits
ebe47b345d
...
2fe35f9ded
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fe35f9ded | ||
|
|
852a1a7c4e | ||
|
|
c15092e850 | ||
|
|
7ca827559b | ||
|
|
f7beabacf9 | ||
|
|
848bb19f76 | ||
|
|
c17118d157 | ||
|
|
95e9891825 | ||
|
|
0984c73c01 | ||
|
|
f9e7a71920 | ||
|
|
ba72fe7602 | ||
|
|
6da591beb1 | ||
|
|
16c9e2bb23 | ||
|
|
3689b5d527 | ||
|
|
c277c16a53 | ||
|
|
1bc3e15da3 | ||
|
|
3ac895ec66 | ||
|
|
08bdc7bc08 | ||
|
|
03a137da4a | ||
|
|
8df6413705 | ||
|
|
5d56c17128 | ||
|
|
87c74f9396 | ||
|
|
2b7c80ef2d | ||
|
|
823215aea6 | ||
|
|
5ad734cdea | ||
|
|
48405bcb94 | ||
|
|
0f9b88e64e | ||
|
|
10a827504b | ||
|
|
1d24e9135b | ||
|
|
a5c190f6db | ||
|
|
f61f68d2ac | ||
|
|
2c5705c3ae | ||
|
|
c4547bbeb4 | ||
|
|
e7296d5d14 | ||
|
|
364cd2c488 | ||
|
|
855f871041 | ||
|
|
8dd32a341b |
+8
-5
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Generated
+414
-394
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
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
-34
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,9 @@ export default class HashNavigation {
|
||||
const args = [];
|
||||
for (const key in params) {
|
||||
if (params.hasOwnProperty(key)) {
|
||||
args.push(key + '=' + encodeURIComponent(params[key]));
|
||||
if (params[key] !== undefined) {
|
||||
args.push(key + '=' + encodeURIComponent(params[key]));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args.length > 0) {
|
||||
|
||||
@@ -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>
|
||||
<span> {cred.done ? null : <i className="material-icons">pending</i>}
|
||||
{!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) {
|
||||
|
||||
+67
-28
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll last message into view on component update e.g. on message received
|
||||
// or vertical shrinking.
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
// Scroll last message into view on component update e.g. on message received
|
||||
// or vertical shrinking.
|
||||
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,20 +272,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);
|
||||
}
|
||||
|
||||
if (topic && ((this.state.topic != prevState.topic) || !prevProps.ready)) {
|
||||
// Don't immediately subscribe to a new p2p topic, wait for the first message.
|
||||
if (topic._new && topic.isP2PType()) {
|
||||
topic.getMeta(topic.startMetaQuery().withDesc().build());
|
||||
} else {
|
||||
this.subscribe(topic);
|
||||
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.
|
||||
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 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,12 +583,16 @@ 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()) {
|
||||
this.setState({fetchingMessages: true}, _ => {
|
||||
topic.getMessagesPage(MESSAGES_PAGE)
|
||||
.catch(err => this.props.onError(err.message, 'err'))
|
||||
.finally(_ => this.setState({fetchingMessages: false}));
|
||||
});
|
||||
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, gaps, min, max)
|
||||
.catch(err => this.props.onError(err.message, 'err'))
|
||||
.finally(_ => this.setState({fetchingMessages: false}));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
this.audioInput.disconnect();
|
||||
if (this.audioInput) {
|
||||
this.audioInput.disconnect();
|
||||
}
|
||||
this.stream.getTracks().forEach(track => track.stop());
|
||||
}
|
||||
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -39,7 +39,7 @@ export default class ErrorPanel extends React.PureComponent {
|
||||
{this.props.text}
|
||||
{this.props.action ?
|
||||
<>
|
||||
 <a href="#"
|
||||
 <a href="#"
|
||||
style={{ whiteSpace: 'nowrap' }}
|
||||
onClick={(e) => {e.preventDefault(); this.props.action();}}>
|
||||
{this.props.actionText}
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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
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
+1
-1
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
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
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
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
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
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
+1
-1
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
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
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
File diff suppressed because one or more lines are too long
+103
-44
@@ -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,7 +3241,9 @@ class HashNavigation {
|
||||
const args = [];
|
||||
for (const key in params) {
|
||||
if (params.hasOwnProperty(key)) {
|
||||
args.push(key + '=' + encodeURIComponent(params[key]));
|
||||
if (params[key] !== undefined) {
|
||||
args.push(key + '=' + encodeURIComponent(params[key]));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args.length > 0) {
|
||||
@@ -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,16 +5324,16 @@ 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()) {
|
||||
topic.getMeta(topic.startMetaQuery().withDesc().build());
|
||||
} else {
|
||||
this.subscribe(topic);
|
||||
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 if (this.props.myUserId) {
|
||||
this.subscribe(topic);
|
||||
}
|
||||
} else if (topic.isSubscribed() && this.state.isReader && !prevState.isReader) {
|
||||
topic.getMeta(topic.startMetaQuery().withLaterData().build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,14 +5593,21 @@ 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()) {
|
||||
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({
|
||||
fetchingMessages: false
|
||||
}));
|
||||
});
|
||||
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, gaps, min, max).catch(err => this.props.onError(err.message, 'err')).finally(_ => this.setState({
|
||||
fetchingMessages: false
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
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
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
@@ -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
@@ -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
@@ -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;
|
||||
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;
|
||||
}
|
||||
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() {
|
||||
this.audioInput.disconnect();
|
||||
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
+1577
-1422
File diff suppressed because it is too large
Load Diff
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
+255
-13
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user