adding credential editing

This commit is contained in:
or-else
2023-01-27 16:18:23 -08:00
parent 4f6d617829
commit aa5e9ef5c7
58 changed files with 4876 additions and 558 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+47 -5
View File
@@ -102,7 +102,7 @@
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": false
"obsolete": true
},
"button_add_members": {
"translation": "Mitglieder hinzufügen",
@@ -579,8 +579,8 @@
"obsolete": false
},
"label_user_contacts": {
"translation": "Kontakte:",
"defaultMessage": "Contacts:",
"translation": "Kontakte",
"defaultMessage": "Contacts",
"description": "Label for user contacts",
"missing": false,
"obsolete": false
@@ -1227,7 +1227,7 @@
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": false
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Beschreibung (optional)",
@@ -1436,7 +1436,7 @@
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": false
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Erfolgreich bestätigt",
@@ -1598,5 +1598,47 @@
"description": "Error message",
"missing": false,
"obsolete": false
},
"current_email": {
"translation": "",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"obsolete": false
},
"current_phone": {
"translation": "",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"obsolete": false
},
"new_email": {
"translation": "",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"obsolete": false
},
"change_email": {
"translation": "",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"obsolete": false
},
"change_phonel": {
"translation": "",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"obsolete": false
}
}
+47 -5
View File
@@ -102,7 +102,7 @@
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": false
"obsolete": true
},
"button_add_members": {
"translation": "Add members",
@@ -579,8 +579,8 @@
"obsolete": false
},
"label_user_contacts": {
"translation": "Contacts:",
"defaultMessage": "Contacts:",
"translation": "Contacts",
"defaultMessage": "Contacts",
"description": "Label for user contacts",
"missing": false,
"obsolete": false
@@ -1227,7 +1227,7 @@
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": false
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Optional description",
@@ -1436,7 +1436,7 @@
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": false
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Confirmed successfully",
@@ -1598,5 +1598,47 @@
"description": "Error message",
"missing": false,
"obsolete": false
},
"current_email": {
"translation": "Current email",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": false,
"obsolete": false
},
"current_phone": {
"translation": "Current phone number",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": false,
"obsolete": false
},
"new_email": {
"translation": "New email",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": false,
"obsolete": false
},
"new_phone_number": {
"translation": "New phone number",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": false,
"obsolete": false
},
"change_email": {
"translation": "Change email",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": false,
"obsolete": false
},
"change_phonel": {
"translation": "Change phone number",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": false,
"obsolete": false
}
}
+47 -5
View File
@@ -102,7 +102,7 @@
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": false
"obsolete": true
},
"button_add_members": {
"translation": "Añadir miembros",
@@ -579,8 +579,8 @@
"obsolete": false
},
"label_user_contacts": {
"translation": "Contactos:",
"defaultMessage": "Contacts:",
"translation": "Contactos",
"defaultMessage": "Contacts",
"description": "Label for user contacts",
"missing": false,
"obsolete": false
@@ -1220,7 +1220,7 @@
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": false
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Descripción (opcional)",
@@ -1436,7 +1436,7 @@
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": false
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Confirmado con éxito",
@@ -1598,5 +1598,47 @@
"description": "Error message",
"missing": false,
"obsolete": false
},
"current_email": {
"translation": "",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"obsolete": false
},
"current_phone": {
"translation": "",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"obsolete": false
},
"new_email": {
"translation": "",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"obsolete": false
},
"change_email": {
"translation": "",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"obsolete": false
},
"change_phonel": {
"translation": "",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"obsolete": false
}
}
+25 -13
View File
@@ -59,10 +59,6 @@
"defaultMessage": "Blocked contacts ({count})",
"description": "Blocked contacts link"
},
"button_add_another": {
"defaultMessage": "Add another",
"description": "Call to action [+ add another]"
},
"button_add_members": {
"defaultMessage": "Add members",
"description": "Flat button [Add members] (to topic)"
@@ -159,6 +155,14 @@
"defaultMessage": "Cannot initiate file upload.",
"description": "Generic error messagewhen attachment fails"
},
"change_email": {
"defaultMessage": "Change email",
"description": "Prompt to change current email"
},
"change_phonel": {
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number"
},
"channel": {
"defaultMessage": "channel",
"description": "Subtitle shown for channels in MessagesView instead of last seen"
@@ -207,6 +211,14 @@
"defaultMessage": "Your registration email",
"description": "Placeholder for entering email"
},
"current_email": {
"defaultMessage": "Current email",
"description": "Label for old email"
},
"current_phone": {
"defaultMessage": "Current phone number",
"description": "Label for old phone"
},
"delete_account": {
"defaultMessage": "Delete account",
"description": "Title for delete account warning"
@@ -453,7 +465,7 @@
"description": "Label for WS/WSS connection type in SettingsView"
},
"label_user_contacts": {
"defaultMessage": "Contacts:",
"defaultMessage": "Contacts",
"description": "Label for user contacts"
},
"label_user_id": {
@@ -616,6 +628,10 @@
"defaultMessage": "+{overflow} more",
"description": "Shown in MessagesView title bar when the number of online subscribers exceeds MAX_ONLINE_IN_TOPIC"
},
"new_email": {
"defaultMessage": "New email",
"description": "Prompt for entering a new email"
},
"new_message_prompt": {
"defaultMessage": "New message",
"description": "Prompt in send message field"
@@ -624,6 +640,10 @@
"defaultMessage": "Enter new password",
"description": "Placeholder for entering new password"
},
"new_phone_number": {
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number"
},
"no_connection": {
"defaultMessage": "No connection",
"description": "Warning that the user is offline."
@@ -732,10 +752,6 @@
"defaultMessage": "phone",
"description": "Dative case of 'phone', i.e. 'phone' in 'by phone'"
},
"phone_or_email_prompt": {
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email"
},
"private_editing_placeholder": {
"defaultMessage": "Visible to you only",
"description": "Placeholder for editing 'private'"
@@ -907,9 +923,5 @@
"user_not_found": {
"defaultMessage": "Not found",
"description": "In place of a user's full name when the user is not found."
},
"validate_credential_action": {
"defaultMessage": "confirm",
"description": "Validate credentail call to action"
}
}
+47 -5
View File
@@ -102,7 +102,7 @@
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": false
"obsolete": true
},
"button_add_members": {
"translation": "Ajouter des membres",
@@ -579,8 +579,8 @@
"obsolete": false
},
"label_user_contacts": {
"translation": "Contacts :",
"defaultMessage": "Contacts:",
"translation": "Contacts",
"defaultMessage": "Contacts",
"description": "Label for user contacts",
"missing": false,
"obsolete": false
@@ -1227,7 +1227,7 @@
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": false
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Description facultative",
@@ -1436,7 +1436,7 @@
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": false
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Confirmé avec succès",
@@ -1598,5 +1598,47 @@
"description": "Error message",
"missing": false,
"obsolete": false
},
"current_email": {
"translation": "",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"obsolete": false
},
"current_phone": {
"translation": "",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"obsolete": false
},
"new_email": {
"translation": "",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"obsolete": false
},
"change_email": {
"translation": "",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"obsolete": false
},
"change_phonel": {
"translation": "",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"obsolete": false
}
}
+47 -5
View File
@@ -102,7 +102,7 @@
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": false
"obsolete": true
},
"button_add_members": {
"translation": "회원추가",
@@ -579,8 +579,8 @@
"obsolete": false
},
"label_user_contacts": {
"translation": "연락처: ",
"defaultMessage": "Contacts:",
"translation": "연락처",
"defaultMessage": "Contacts",
"description": "Label for user contacts",
"missing": false,
"obsolete": false
@@ -1220,7 +1220,7 @@
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": false
"obsolete": true
},
"description_editing_placeholder": {
"translation": "설명(선택 사항)",
@@ -1436,7 +1436,7 @@
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": false
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "성공적으로 확인됨",
@@ -1598,5 +1598,47 @@
"description": "Error message",
"missing": false,
"obsolete": false
},
"current_email": {
"translation": "",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"obsolete": false
},
"current_phone": {
"translation": "",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"obsolete": false
},
"new_email": {
"translation": "",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"obsolete": false
},
"change_email": {
"translation": "",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"obsolete": false
},
"change_phonel": {
"translation": "",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"obsolete": false
}
}
+47 -5
View File
@@ -102,7 +102,7 @@
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": false
"obsolete": true
},
"button_add_members": {
"translation": "Adaugă persoane",
@@ -579,8 +579,8 @@
"obsolete": false
},
"label_user_contacts": {
"translation": "Contacte:",
"defaultMessage": "Contacts:",
"translation": "Contacte",
"defaultMessage": "Contacts",
"description": "Label for user contacts",
"missing": false,
"obsolete": false
@@ -1220,7 +1220,7 @@
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": false
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Descriere (opțional)",
@@ -1436,7 +1436,7 @@
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": false
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Confirmat cu succes",
@@ -1598,5 +1598,47 @@
"description": "Error message",
"missing": false,
"obsolete": false
},
"current_email": {
"translation": "",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"obsolete": false
},
"current_phone": {
"translation": "",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"obsolete": false
},
"new_email": {
"translation": "",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"obsolete": false
},
"change_email": {
"translation": "",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"obsolete": false
},
"change_phonel": {
"translation": "",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"obsolete": false
}
}
+47 -5
View File
@@ -102,7 +102,7 @@
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": false
"obsolete": true
},
"button_add_members": {
"translation": "Добавить",
@@ -579,8 +579,8 @@
"obsolete": false
},
"label_user_contacts": {
"translation": "Конакты:",
"defaultMessage": "Contacts:",
"translation": "Конакты",
"defaultMessage": "Contacts",
"description": "Label for user contacts",
"missing": false,
"obsolete": false
@@ -1220,7 +1220,7 @@
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": false
"obsolete": true
},
"description_editing_placeholder": {
"translation": "Описание (не обязательно)",
@@ -1436,7 +1436,7 @@
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": false
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "Подтверждено успешно",
@@ -1598,5 +1598,47 @@
"description": "Error message",
"missing": false,
"obsolete": false
},
"current_email": {
"translation": "",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"obsolete": false
},
"current_phone": {
"translation": "",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"obsolete": false
},
"new_email": {
"translation": "",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"obsolete": false
},
"change_email": {
"translation": "",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"obsolete": false
},
"change_phonel": {
"translation": "",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"obsolete": false
}
}
+49 -7
View File
@@ -102,7 +102,7 @@
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": false
"obsolete": true
},
"button_add_members": {
"translation": "新增成員",
@@ -579,8 +579,8 @@
"obsolete": false
},
"label_user_contacts": {
"translation": "聯絡人:",
"defaultMessage": "Contacts:",
"translation": "聯絡人",
"defaultMessage": "Contacts",
"description": "Label for user contacts",
"missing": false,
"obsolete": false
@@ -593,10 +593,10 @@
"obsolete": false
},
"label_wire_transport": {
"translation": "Wire transport:",
"translation": "",
"defaultMessage": "Wire transport:",
"description": "Label for wire transport selection in SettingsView",
"missing": false,
"missing": true,
"obsolete": false
},
"label_you": {
@@ -1220,7 +1220,7 @@
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": false
"obsolete": true
},
"description_editing_placeholder": {
"translation": "說明(可選)",
@@ -1436,7 +1436,7 @@
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": false
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "確認成功",
@@ -1598,5 +1598,47 @@
"description": "Error message",
"missing": false,
"obsolete": false
},
"current_email": {
"translation": "",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"obsolete": false
},
"current_phone": {
"translation": "",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"obsolete": false
},
"new_email": {
"translation": "",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"obsolete": false
},
"change_email": {
"translation": "",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"obsolete": false
},
"change_phonel": {
"translation": "",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"obsolete": false
}
}
+47 -5
View File
@@ -102,7 +102,7 @@
"defaultMessage": "Add another",
"description": "Call to action [+ add another]",
"missing": false,
"obsolete": false
"obsolete": true
},
"button_add_members": {
"translation": "添加成员",
@@ -579,8 +579,8 @@
"obsolete": false
},
"label_user_contacts": {
"translation": "往来:",
"defaultMessage": "Contacts:",
"translation": "往来",
"defaultMessage": "Contacts",
"description": "Label for user contacts",
"missing": false,
"obsolete": false
@@ -1220,7 +1220,7 @@
"defaultMessage": "confirm",
"description": "Validate credentail call to action",
"missing": false,
"obsolete": false
"obsolete": true
},
"description_editing_placeholder": {
"translation": "说明(可选)",
@@ -1436,7 +1436,7 @@
"defaultMessage": "Phone number or email",
"description": "Placeholer for phone or email",
"missing": false,
"obsolete": false
"obsolete": true
},
"cred_confirmed_successfully": {
"translation": "确认成功",
@@ -1598,5 +1598,47 @@
"description": "Error message",
"missing": false,
"obsolete": false
},
"current_email": {
"translation": "",
"defaultMessage": "Current email",
"description": "Label for old email",
"missing": true,
"obsolete": false
},
"current_phone": {
"translation": "",
"defaultMessage": "Current phone number",
"description": "Label for old phone",
"missing": true,
"obsolete": false
},
"new_email": {
"translation": "",
"defaultMessage": "New email",
"description": "Prompt for entering a new email",
"missing": true,
"obsolete": false
},
"new_phone_number": {
"translation": "",
"defaultMessage": "New phone number",
"description": "Prompt for entering a new telephone number",
"missing": true,
"obsolete": false
},
"change_email": {
"translation": "",
"defaultMessage": "Change email",
"description": "Prompt to change current email",
"missing": true,
"obsolete": false
},
"change_phonel": {
"translation": "",
"defaultMessage": "Change phone number",
"description": "Prompt to change current phone number",
"missing": true,
"obsolete": false
}
}
+51 -3
View File
@@ -1,12 +1,14 @@
// Edit account parameters.
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { parsePhoneNumber } from 'libphonenumber-js/mobile';
import AvatarUpload from '../widgets/avatar-upload.jsx';
import BadgeList from '../widgets/badge-list.jsx';
import { makeImageUrl } from '../lib/blob-helpers.js';
import { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';
import CredentialEdit from '../widgets/credential-edit.jsx';
function _clip(str, length) {
return str && str.substring(0, length);
@@ -20,15 +22,57 @@ export default class AccountSettingsView extends React.Component {
this.state = {
fullName: _clip(me.public ? me.public.fn : undefined, MAX_TITLE_LENGTH),
description: _clip(me.public ? me.public.note : undefined, MAX_TOPIC_DESCRIPTION_LENGTH),
avatar: makeImageUrl(me.public ? me.public.photo : null)
avatar: makeImageUrl(me.public ? me.public.photo : null),
credentials: me.getCredentials() || [],
credEdit: undefined
};
}
render() {
if (this.state.credEdit) {
return (
<CredentialEdit
method={this.state.credEdit.meth}
val={this.state.credEdit.val}
done={this.state.credEdit.done}
onShowCountrySelector={this.props.onShowCountrySelector}
onSubmit={cred => {this.setState({credEdit: undefined}); console.log("Credential editing done", cred)}}
onCancel={_ => this.setState({credEdit: undefined})} />
);
}
const credentials = [];
this.state.credentials.forEach((cred, idx) => {
if (!['email', 'tel'].includes(cred.meth)) {
// Skip unknown methods.
return ;
}
let val = cred.val
if (cred.meth == 'tel') {
const number = parsePhoneNumber(cred.val);
val = number ? number.formatInternational() : cred.val;
}
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>
</div>);
});
if (credentials.length > 0) {
// Add title as the first element.
credentials.unshift(
<label className="small" key={'title'}>
<FormattedMessage id="label_user_contacts" defaultMessage="Contacts"
description="Label for user contacts" />
</label>);
}
return (
<div className="scrollable-panel">
<div className="panel-form-column">
<a href="#" className="flat-button float-right" onClick={(e) => {e.preventDefault(); this.props.onNavigate('general');}}>
<a href="#" className="flat-button float-right" onClick={e => {e.preventDefault(); this.props.onNavigate('general');}}>
<i className="material-icons">edit</i>&nbsp;
<FormattedMessage id="button_edit" defaultMessage="Edit" description="Call to action [Edit]" />
</a>
@@ -61,10 +105,14 @@ export default class AccountSettingsView extends React.Component {
<FormattedMessage id="label_description" defaultMessage="Description"
description="Label for editing topic description" />
</label>
<div>{this.state.description}</div>
<div className="quoted">{this.state.description}</div>
</div> : null}
</div>
<div className="hr" />
<div className="panel-form-column">
<div className="group">{credentials}</div>
</div>
<div className="hr" />
<div className="panel-form-column">
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onNavigate('notif');}}>
<i className="material-icons">notifications</i>&nbsp;<FormattedMessage id="sidepanel_title_acc_notifications"
+2 -7
View File
@@ -30,7 +30,6 @@ export default class CreateAccountView extends React.PureComponent {
uploadUrl: null,
newAvatar: null,
newAvatarMime: null,
errorCleared: false,
buttonDisabled: false,
saveToken: LocalStorageUtil.getObject('keep-logged-in')
};
@@ -85,7 +84,6 @@ export default class CreateAccountView extends React.PureComponent {
handleSubmit(e) {
e.preventDefault();
this.setState({errorCleared: false});
this.props.onCreateAccount(
this.state.login.trim(),
this.state.password.trim(),
@@ -144,9 +142,6 @@ export default class CreateAccountView extends React.PureComponent {
}
render() {
// "reqCred":{"auth":["email"]}
const method = (this.props.tinode.getServerParam('reqCred', {}).auth || [])[0] || 'email';
if (this.state.newAvatar) {
return (
<AvatarCrop
@@ -192,7 +187,7 @@ export default class CreateAccountView extends React.PureComponent {
value={this.state.fn} onChange={this.handleFnChange} required/>
}</FormattedMessage>
</div>
{method == 'email' ?
{props.reqCredMethod == 'email' ?
<div className="panel-form-row">
<FormattedMessage id="email_prompt" defaultMessage="Email, e.g. jdoe@example.com"
description="Input placeholder for email entry">{
@@ -200,7 +195,7 @@ export default class CreateAccountView extends React.PureComponent {
value={this.state.email} onChange={this.handleEmailChange} required/>
}</FormattedMessage>
</div>
: method == 'tel' ?
: props.reqCredMethod == 'tel' ?
<><div className="panel-form-row">
<label className="small gray"><FormattedMessage id="mobile_phone_number" defaultMessage="Mobile phone number"
description="Prompt for entering a mobile phone number" /></label>
+3 -2
View File
@@ -12,7 +12,7 @@ import ErrorPanel from '../widgets/error-panel.jsx';
import GroupManager from '../widgets/group-manager.jsx';
import MenuCancel from '../widgets/menu-cancel.jsx';
import PermissionsEditor from '../widgets/permissions-editor.jsx';
import TopicCommon from '../widgets/topic-common.jsx';
import TopicCommonView from './topic-common-view.jsx';
import TopicSecurity from '../widgets/topic-security.jsx';
import { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH, NO_ACCESS_MODE } from '../config.js';
@@ -476,9 +476,10 @@ class InfoView extends React.Component {
onCancel={this.handleBackNavigate} />
:
view == 'general' ?
<TopicCommon
<TopicCommonView
tinode={this.props.tinode}
topic={this.props.topic}
reqCredMethod={this.props.reqCredMethod}
onCredAdd={this.props.onCredAdd}
onUpdateTagsRequest={this.props.onTopicTagsUpdateRequest}
onCredConfirm={this.props.onCredConfirm}
+7 -9
View File
@@ -46,19 +46,19 @@ export default class PasswordResetView extends React.PureComponent {
scheme: 'token',
token: this.state.token
});
} else if (this.state.code && this.method) {
} else if (this.state.code && this.props.reqCredMethod) {
// Reset using security code.
// The secret is structured as <code>:<cred_method>:<cred_value>, "123456:email:alice@example.com".
const cred = this.state.email.trim() || this.state.tel.trim();
this.props.onReset(this.state.password.trim(), {
scheme: 'code',
secret: `${this.state.code}:${this.method}:${cred}`
secret: `${this.state.code}:${this.props.reqCredMethod}:${cred}`
});
} else {
const email = this.state.email.trim();
const tel = this.state.tel.trim();
this.setState({email: email, tel: tel});
this.props.onRequest(this.method, email || tel).then(_ => this.setState({sent: true}));
this.props.onRequest(this.props.reqCredMethod, email || tel).then(_ => this.setState({sent: true}));
}
}
@@ -80,9 +80,6 @@ export default class PasswordResetView extends React.PureComponent {
}
render() {
// "reqCred":{"auth":["email"]}
this.method = (this.props.tinode.getServerParam('reqCred', {}).auth || [])[0] || 'email';
const showCredentialInput = !(this.state.token && this.state.scheme);
const showPasswordInput = !showCredentialInput || this.state.haveCode || this.state.sent;
@@ -151,12 +148,12 @@ export default class PasswordResetView extends React.PureComponent {
</>);
const sentNotification = (<>{
this.method == 'email' ?
this.props.reqCredMethod == 'email' ?
<FormattedMessage id="password_reset_email_sent"
defaultMessage="An email with security code has been sent to {email}. Enter the code below."
values={{email: <tt>{this.state.email}</tt>}}
description="Notification that the email with password reset instructions has been sent" />
: this.method == 'tel' ?
: this.props.reqCredMethod == 'tel' ?
<FormattedMessage id="password_reset_sms_sent"
defaultMessage="A text message with security code has been sent to {phone}. Enter the code below."
values={{phone: <tt>{this.state.tel}</tt>}}
@@ -183,7 +180,8 @@ export default class PasswordResetView extends React.PureComponent {
</div>
</>);
const credentialInput = this.method == 'email' ? emailInput : this.method == 'tel' ? phoneInput : null;
const credentialInput = this.props.reqCredMethod == 'email' ?
emailInput : this.props.reqCredMethod == 'tel' ? phoneInput : null;
return (
<form id="password-reset-form" onSubmit={this.handleSubmit}>
+6 -4
View File
@@ -4,7 +4,7 @@ import { defineMessages, injectIntl } from 'react-intl';
import ErrorPanel from '../widgets/error-panel.jsx';
import LoadSpinner from '../widgets/load-spinner.jsx';
import SideNavbar from '../widgets/side-navbar.jsx';
import TopicCommon from '../widgets/topic-common.jsx';
import TopicCommonView from './topic-common-view.jsx';
import AccountSettingsView from './account-settings-view.jsx';
import ContactsView from './contacts-view.jsx';
@@ -157,7 +157,7 @@ class SidepanelView extends React.PureComponent {
view === 'register' ?
<CreateAccountView
tinode={this.props.tinode}
serverVersion={this.props.serverVersion}
reqCredMethod={this.props.reqCredMethod}
onShowCountrySelector={this.props.onShowCountrySelector}
onCreateAccount={this.props.onCreateAccount}
onCancel={this.props.onCancel}
@@ -176,13 +176,15 @@ class SidepanelView extends React.PureComponent {
tinode={this.props.tinode}
myUserId={this.props.myUserId}
trustedBadges={this.props.trustedBadges}
onShowCountrySelector={this.props.onShowCountrySelector}
onNavigate={this.props.onNavigate} /> :
(view === 'general' || view === 'crop') ?
<TopicCommon
<TopicCommonView
topic="me"
tinode={this.props.tinode}
myUserId={this.props.myUserId}
reqCredMethod={this.props.reqCredMethod}
onUpdateTopicDesc={this.props.onUpdateAccountDesc}
onUpdateTagsRequest={this.props.onUpdateAccountTags}
onCredAdd={this.props.onCredAdd}
@@ -248,7 +250,7 @@ class SidepanelView extends React.PureComponent {
view === 'reset' ?
<PasswordResetView
tinode={this.props.tinode}
serverVersion={this.props.serverVersion}
reqCredMethod={this.props.reqCredMethod}
onShowCountrySelector={this.props.onShowCountrySelector}
onRequest={this.props.onPasswordResetRequest}
onReset={this.props.onResetPassword}
+5 -1
View File
@@ -266,6 +266,7 @@ class TinodeWeb extends React.Component {
// Merged results of a search query and p2p chats.
searchableContacts: [],
// Credential validation.
reqCredMethod: undefined,
credMethod: undefined,
credCode: undefined,
credToken: undefined,
@@ -615,7 +616,9 @@ class TinodeWeb extends React.Component {
const params = this.tinode.getServerInfo();
this.setState({
serverVersion: params.ver + ' ' + (params.build ? params.build : 'none')
serverVersion: params.ver + ' ' + (params.build ? params.build : 'none'),
// "reqCred":{"auth":["email"]}
reqCredMethod: ((params.reqCred || {}).auth || [])[0] || 'email'
});
if (this.state.autoLogin) {
@@ -2012,6 +2015,7 @@ class TinodeWeb extends React.Component {
serverAddress={this.state.serverAddress}
secureConnection={this.state.secureConnection}
serverVersion={this.state.serverVersion}
reqCredMethod={this.state.reqCredMethod}
onGlobalSettings={this.handleGlobalSettings}
onSignUp={this.handleNewAccount}
+79
View File
@@ -0,0 +1,79 @@
// View for editing topic parameters (including 'me' topic).
import React from 'react';
import TopicDescEdit from '../widgets/topic-desc-edit.jsx';
import { arrayEqual } from '../lib/utils.js';
export default class TopicCommonView extends React.Component {
constructor(props) {
super(props);
const topic = this.props.tinode.getTopic(this.props.topic);
const acs = topic.getAccessMode();
this.state = {
tags: []
};
this.previousTagsUpdated = undefined;
this.onTagsUpdated = this.onTagsUpdated.bind(this);
this.handleTagsUpdated = this.handleTagsUpdated.bind(this);
}
// No need to separately handle component mount.
componentDidUpdate(props) {
const topic = this.props.tinode.getTopic(props.topic);
if (!topic) {
return;
}
if (topic.onTagsUpdated != this.onTagsUpdated) {
if (topic.getType() == 'grp') {
this.previousTagsUpdated = topic.onTagsUpdated;
topic.onTagsUpdated = this.onTagsUpdated;
} else {
this.previousTagsUpdated = undefined;
}
}
if (this.state.topic != props.topic) {
this.setState({topic: props.topic});
}
}
componentWillUnmount() {
const topic = this.props.tinode.getTopic(this.props.topic);
topic.onTagsUpdated = this.previousTagsUpdated;
}
// Server informs that the tags have been updated.
onTagsUpdated(tags) {
this.setState({tags: tags});
if (this.previousTagsUpdated && this.previousTagsUpdated != this.onTagsUpdated) {
this.previousTagsUpdated(tags);
}
}
// Request server to change tags.
handleTagsUpdated(tags) {
if (!arrayEqual(this.state.tags.slice(0), tags.slice(0))) {
this.props.onUpdateTagsRequest(this.props.topic, tags);
}
}
render() {
return (
<div className="scrollable-panel">
<TopicDescEdit
tinode={this.props.tinode}
topic={this.props.topic}
onUpdateTopicDesc={this.props.onUpdateTopicDesc}
onUpdateTags={this.handleTagsUpdated}
onError={this.props.onError} />
</div>
);
}
};
+105
View File
@@ -0,0 +1,105 @@
// Enter one credential at a time.
import React from 'react';
import { FormattedMessage } from 'react-intl';
import PhoneEdit from './phone-edit.jsx';
import { parsePhoneNumber } from 'libphonenumber-js/mobile';
export default class CredentialEdit extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
tel: '',
email: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handlePhoneChange = this.handlePhoneChange.bind(this);
}
formatPhoneNumber(raw) {
const number = parsePhoneNumber(raw);
return number ? number.formatInternational() : raw;
}
handleEmailChange(e) {
this.setState({email: e.target.value});
}
handlePhoneChange(number) {
console.log("handlePhoneChange", number);
this.setState({tel: number});
}
handleSubmit(e) {
e.preventDefault();
const value = this.props.method == 'email' ? this.state.email : this.state.tel;
console.log("handleSubmit", value);
if (value) {
this.props.onSubmit(value);
}
}
render() {
return (<form className="panel-form-column" onSubmit={this.handleSubmit}>
{this.props.method == 'email' ?
<><div className="panel-form-row">
<label className="large"><FormattedMessage id="change_email" defaultMessage="Change email"
description="Prompt to change current email" /></label>
</div>
<div className="group">
<label className="small gray">
<FormattedMessage id="current_email" defaultMessage="Current email" description="Label for old email" />
</label>
</div></>
: this.props.method == 'tel' ?
<><div className="panel-form-row">
<label className="large"><FormattedMessage id="change_phonel" defaultMessage="Change phone number"
description="Prompt to change current phone number"/></label>
</div>
<div className="group">
<label className="small gray">
<FormattedMessage id="current_phone" defaultMessage="Current phone number" description="Label for old phone" />
</label>
</div></>
: null}
<div className="panel-form-row">
<tt className="quoted">{this.formatPhoneNumber(this.props.val)}</tt>
</div>
{this.props.method == 'email' ?
<><div className="group">
<label className="small gray"><FormattedMessage id="new_email" defaultMessage="New email"
description="Prompt for entering a new email" /></label>
</div>
<div className="group">
<FormattedMessage id="email_prompt" defaultMessage="Email, e.g. jdoe@example.com"
description="Input placeholder for email entry">{
(email_prompt) => <input type="email" placeholder={email_prompt} autoComplete="email"
value={this.state.email} onChange={this.handleEmailChange} required/>
}</FormattedMessage>
</div></>
: this.props.method == 'tel' ?
<><div className="group">
<label className="small gray"><FormattedMessage id="new_phone_number" defaultMessage="New phone number"
description="Prompt for entering a new telephone number" /></label>
</div>
<div className="panel-form-row">
<PhoneEdit
autoFocus={true}
onShowCountrySelector={this.props.onShowCountrySelector}
onSubmit={this.handlePhoneChange} />
</div></>
: null}
<div className="dialog-buttons">
<button className="secondary" onClick={this.props.onCancel}>
<FormattedMessage id="button_cancel" defaultMessage="Cancel" description="Button [Cancel]" />
</button>
<button className="primary" type="submit" >
<FormattedMessage id="button_ok" defaultMessage="OK" description="Button [OK]" />
</button>
</div>
</form>);
}
}
-182
View File
@@ -1,182 +0,0 @@
// View for editing topic (including 'me' topic) parameters.
import React from 'react';
import { FormattedMessage } from 'react-intl';
import TopicDescEdit from '../widgets/topic-desc-edit.jsx';
import { arrayEqual, asEmail, asPhone } from '../lib/utils.js';
export default class TopicCommon extends React.Component {
constructor(props) {
super(props);
const topic = this.props.tinode.getTopic(this.props.topic);
const acs = topic.getAccessMode();
this.state = {
isMe: this.props.topic == 'me',
owner: acs && acs.isOwner(),
credentials: (topic.getCredentials ? topic.getCredentials() : null) || [],
addCredActive: false,
addCredInvalid: false,
newCred: '',
tags: []
};
this.previousTagsUpdated = undefined;
this.onTagsUpdated = this.onTagsUpdated.bind(this);
this.handleTagsUpdated = this.handleTagsUpdated.bind(this);
this.tnCredsUpdated = this.tnCredsUpdated.bind(this);
this.handleCredChange = this.handleCredChange.bind(this);
this.handleCredKeyDown = this.handleCredKeyDown.bind(this);
this.handleCredEntered = this.handleCredEntered.bind(this);
}
// No need to separately handle component mount.
componentDidUpdate(props) {
const topic = this.props.tinode.getTopic(props.topic);
if (!topic) {
return;
}
topic.onCredsUpdated = this.tnCredsUpdated;
if (topic.onTagsUpdated != this.onTagsUpdated) {
if (topic.getType() == 'grp') {
this.previousTagsUpdated = topic.onTagsUpdated;
topic.onTagsUpdated = this.onTagsUpdated;
} else {
this.previousTagsUpdated = undefined;
}
}
if (this.state.topic != props.topic) {
this.setState({topic: props.topic});
}
}
componentWillUnmount() {
const topic = this.props.tinode.getTopic(this.props.topic);
topic.onCredsUpdated = undefined;
topic.onTagsUpdated = this.previousTagsUpdated;
}
tnCredsUpdated(creds) {
this.setState({credentials: creds || []});
}
handleCredChange(e) {
this.setState({newCred: e.target.value, addCredInvalid: false});
}
handleCredKeyDown(e) {
if (e.keyCode === 27) {
// Escape pressed
this.setState({newCred: '', addCredActive: false});
} else if (e.keyCode === 13) {
// Enter pressed
this.handleCredEntered(e);
}
}
handleCredEntered(e) {
const value = this.state.newCred.trim();
if (!value) {
this.setState({addCredActive: false, addCredInvalid: false});
return;
}
let val = asPhone(value);
let method;
if (val) {
method = 'tel';
} else {
val = asEmail(value);
if (val) {
method = 'email';
}
}
if (method) {
this.props.onCredAdd(method, val);
this.setState({addCredActive: false, newCred: ''});
} else {
this.setState({addCredInvalid: true});
}
}
// Server informs that the tags have been updated.
onTagsUpdated(tags) {
this.setState({tags: tags});
if (this.previousTagsUpdated && this.previousTagsUpdated != this.onTagsUpdated) {
this.previousTagsUpdated(tags);
}
}
// Request server to change tags.
handleTagsUpdated(tags) {
if (!arrayEqual(this.state.tags.slice(0), tags.slice(0))) {
this.props.onUpdateTagsRequest(this.props.topic, tags);
}
}
render() {
const credentials = [];
if (this.state.isMe) {
this.state.credentials.map((cred) => {
credentials.push(
<div key={cred.meth + ":" + cred.val + ":" + cred.done}>{cred.meth}: <tt>{cred.val}</tt>
<span> {!cred.done ?
<a href="#" onClick={(e) => {e.preventDefault(); this.props.onCredConfirm(cred.meth, cred.val);}}>
<FormattedMessage id="validate_credential_action" defaultMessage="confirm"
description="Validate credentail call to action" />
</a>
: null} <a href="#" onClick={(e) => {e.preventDefault(); this.props.onCredDelete(cred.meth, cred.val);}}><i
className="material-icons gray">delete_outline</i></a></span>
</div>
);
});
}
return (
<div className="scrollable-panel">
<TopicDescEdit
tinode={this.props.tinode}
topic={this.props.topic}
onUpdateTopicDesc={this.props.onUpdateTopicDesc}
onUpdateTags={this.handleTagsUpdated}
onError={this.props.onError} />
{this.state.isMe ?
<>
<div className="hr" />
<div className="panel-form-column">
<label className="small">
<FormattedMessage id="label_user_contacts" defaultMessage="Contacts:"
description="Label for user contacts" />
</label>
<div className="quoted">
{credentials}
{this.state.addCredActive ?
<FormattedMessage id="phone_or_email_prompt" defaultMessage="Phone number or email"
description="Placeholer for phone or email">{(phone_or_email_prompt) =>
<input type="text" value={this.state.value}
className={this.state.addCredInvalid ? 'invalid' : null}
placeholder={phone_or_email_prompt} required="required" autoFocus
onChange={this.handleCredChange} onKeyDown={this.handleCredKeyDown} onBlur={this.handleCredEntered} />
}</FormattedMessage>
: null}
<div>
<a href="#" onClick={(e) => {e.preventDefault(); this.setState({addCredActive: true});}}>
<i className="material-icons">add</i>
<FormattedMessage id="button_add_another" defaultMessage="Add another" description="Call to action [+ add another]" />
</a></div>
</div>
</div>
</>
: null
}
</div>
);
}
};