refactoring of account settings into multiple screens
This commit is contained in:
@@ -381,10 +381,19 @@ div.alert .content {
|
|||||||
color: #bbb;
|
color: #bbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.large {
|
||||||
|
font-size: 120%;
|
||||||
|
}
|
||||||
|
|
||||||
.small {
|
.small {
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.float-right {
|
||||||
|
margin-left: auto;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
.hr {
|
.hr {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -985,6 +994,10 @@ i.search {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.avatar-upload.read-only {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.avatar-upload .clear-avatar {
|
.avatar-upload .clear-avatar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+27
-17
@@ -1,26 +1,32 @@
|
|||||||
{
|
{
|
||||||
|
"validate_credential_action": "confirm",
|
||||||
|
"title_tag_manager": "Tags (user discovery)",
|
||||||
|
"label_user_contacts": "Contacts:",
|
||||||
|
"button_add_another": "Add another",
|
||||||
|
"label_message_sound": "Message sound:",
|
||||||
|
"label_push_notifications": "Notification alerts:",
|
||||||
|
"label_push_notifications_disabled": "Notification alerts (requires HTTPS):",
|
||||||
|
"label_incognito_mode": "Incognito mode:",
|
||||||
|
"delete_account": "Delete account",
|
||||||
|
"delete_account_arning": "Are you sure you want to delete your account? It cannot be undone.",
|
||||||
|
"label_password": "Password",
|
||||||
|
"password_unchanged_prompt": "Unchanged",
|
||||||
|
"button_logout": "Logout",
|
||||||
|
"button_delete_account": "Delete account",
|
||||||
|
"label_default_access_mode": "Default access mode:",
|
||||||
|
"link_contact_us": "Contact Us",
|
||||||
|
"link_terms_of_service": "Terms of Service",
|
||||||
|
"link_privacy_policy": "Privacy Policy",
|
||||||
|
"label_sdk": "SDK:",
|
||||||
|
"label_server_address": "Server address:",
|
||||||
"archived_contacts": "Archived contacts ({count})",
|
"archived_contacts": "Archived contacts ({count})",
|
||||||
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯",
|
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯",
|
||||||
"full_name_prompt": "Full name, e.g. John Doe",
|
"full_name_prompt": "Full name, e.g. John Doe",
|
||||||
"email_prompt": "Email, e.g. jdoe@example.com",
|
"email_prompt": "Email, e.g. jdoe@example.com",
|
||||||
"button_sign_up": "Sign up",
|
"button_sign_up": "Sign up",
|
||||||
"validate_credential_action": "confirm",
|
|
||||||
"label_your_name": "Your name",
|
"label_your_name": "Your name",
|
||||||
"label_password": "Password",
|
"label_user_id": "ID:",
|
||||||
"password_unchanged_prompt": "Unchanged",
|
"button_edit": "Edit",
|
||||||
"label_message_sound": "Message sound:",
|
|
||||||
"label_push_notifications": "Notification alerts:",
|
|
||||||
"label_push_notifications_disabled": "Notification alerts (requires HTTPS):",
|
|
||||||
"label_incognito_mode": "Incognito mode:",
|
|
||||||
"title_tag_manager": "Tags (user discovery)",
|
|
||||||
"label_user_contacts": "Contacts:",
|
|
||||||
"label_user_id": "Address:",
|
|
||||||
"label_default_access_mode": "Default access mode:",
|
|
||||||
"button_logout": "Logout",
|
|
||||||
"label_legal": "Legal",
|
|
||||||
"link_contact_us": "Contact Us",
|
|
||||||
"link_terms_of_service": "Terms of Service",
|
|
||||||
"link_privacy_policy": "Privacy Policy",
|
|
||||||
"requested_permissions": "Requested",
|
"requested_permissions": "Requested",
|
||||||
"granted_permissions": "Granted",
|
"granted_permissions": "Granted",
|
||||||
"menu_item_edit_permissions": "Edit permissions",
|
"menu_item_edit_permissions": "Edit permissions",
|
||||||
@@ -78,7 +84,11 @@
|
|||||||
"sidepanel_title_login": "Sign In",
|
"sidepanel_title_login": "Sign In",
|
||||||
"sidepanel_title_register": "Create Account",
|
"sidepanel_title_register": "Create Account",
|
||||||
"sidepanel_title_settings": "Settings",
|
"sidepanel_title_settings": "Settings",
|
||||||
"sidepanel_title_edit_account": "Edit Account",
|
"sidepanel_title_account_settings": "Account Settings",
|
||||||
|
"sidepanel_title_acc_general": "General",
|
||||||
|
"sidepanel_title_acc_security": "Security",
|
||||||
|
"sidepanel_title_acc_notifications": "Notifications",
|
||||||
|
"sidepanel_title_acc_support": "Support",
|
||||||
"sidepanel_title_newtpk": "Start New Chat",
|
"sidepanel_title_newtpk": "Start New Chat",
|
||||||
"sidepanel_title_cred": "Confirm Credentials",
|
"sidepanel_title_cred": "Confirm Credentials",
|
||||||
"sidepanel_title_reset": "Reset Password",
|
"sidepanel_title_reset": "Reset Password",
|
||||||
|
|||||||
+14
-4
@@ -6,7 +6,7 @@
|
|||||||
"label_your_name": "Ваше имя",
|
"label_your_name": "Ваше имя",
|
||||||
"label_password": "Пароль",
|
"label_password": "Пароль",
|
||||||
"password_unchanged_prompt": "Не изменен",
|
"password_unchanged_prompt": "Не изменен",
|
||||||
"label_user_id": "Адрес:",
|
"label_user_id": "ID:",
|
||||||
"label_default_access_mode": "Доступ по умолчанию:",
|
"label_default_access_mode": "Доступ по умолчанию:",
|
||||||
"label_message_sound": "Звук нового сообщения:",
|
"label_message_sound": "Звук нового сообщения:",
|
||||||
"label_push_notifications": "Уведомления:",
|
"label_push_notifications": "Уведомления:",
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
"sidepanel_title_login": "Авторизация",
|
"sidepanel_title_login": "Авторизация",
|
||||||
"sidepanel_title_register": "Зарегистрироваться",
|
"sidepanel_title_register": "Зарегистрироваться",
|
||||||
"sidepanel_title_settings": "Настройки",
|
"sidepanel_title_settings": "Настройки",
|
||||||
"sidepanel_title_edit_account": "Редактировать аккаунт",
|
"sidepanel_title_account_settings": "Настройки аккаунта",
|
||||||
"sidepanel_title_newtpk": "Новый чат",
|
"sidepanel_title_newtpk": "Новый чат",
|
||||||
"sidepanel_title_cred": "Подтвердить",
|
"sidepanel_title_cred": "Подтвердить",
|
||||||
"sidepanel_title_reset": "Сменить пароль",
|
"sidepanel_title_reset": "Сменить пароль",
|
||||||
@@ -135,7 +135,6 @@
|
|||||||
"more_online_members": "+еще {overflow}",
|
"more_online_members": "+еще {overflow}",
|
||||||
"label_user_contacts": "Конакты:",
|
"label_user_contacts": "Конакты:",
|
||||||
"validate_credential_action": "подтвердить",
|
"validate_credential_action": "подтвердить",
|
||||||
"label_legal": "Прочее",
|
|
||||||
"link_contact_us": "Связаться с нами",
|
"link_contact_us": "Связаться с нами",
|
||||||
"link_terms_of_service": "Условия сервиса",
|
"link_terms_of_service": "Условия сервиса",
|
||||||
"link_privacy_policy": "Политика конфиденциальности",
|
"link_privacy_policy": "Политика конфиденциальности",
|
||||||
@@ -153,5 +152,16 @@
|
|||||||
"search_no_results": "Ничего не найдено",
|
"search_no_results": "Ничего не найдено",
|
||||||
"deleted_content": "удалено",
|
"deleted_content": "удалено",
|
||||||
"image_caption_prompt": "Подпись к фото",
|
"image_caption_prompt": "Подпись к фото",
|
||||||
"label_incognito_mode": "Режим инкогнито:"
|
"label_incognito_mode": "Режим инкогнито:",
|
||||||
|
"delete_account": "Удалить аккаунт",
|
||||||
|
"delete_account_arning": "Вы уверены, что ходите удалить свой аккаунт? Его невозможно будет восстановить.",
|
||||||
|
"button_delete_account": "Удалить аккаунт",
|
||||||
|
"sidepanel_title_acc_security": "Безопасность",
|
||||||
|
"sidepanel_title_acc_notifications": "Уведомления",
|
||||||
|
"sidepanel_title_acc_support": "Поддержка",
|
||||||
|
"label_sdk": "SDK:",
|
||||||
|
"label_server_address": "Адрес сервера:",
|
||||||
|
"button_edit": "Редактировать",
|
||||||
|
"button_add_another": "Добавить",
|
||||||
|
"sidepanel_title_acc_general": "Общие настройки"
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-3
@@ -61,7 +61,7 @@
|
|||||||
"sidepanel_title_login": "登录",
|
"sidepanel_title_login": "登录",
|
||||||
"sidepanel_title_register": "创建账户",
|
"sidepanel_title_register": "创建账户",
|
||||||
"sidepanel_title_settings": "设置",
|
"sidepanel_title_settings": "设置",
|
||||||
"sidepanel_title_edit_account": "编辑账户",
|
"sidepanel_title_account_settings": "帐号设定",
|
||||||
"sidepanel_title_newtpk": "开始新会话",
|
"sidepanel_title_newtpk": "开始新会话",
|
||||||
"sidepanel_title_cred": "确认凭据",
|
"sidepanel_title_cred": "确认凭据",
|
||||||
"sidepanel_title_reset": "重置密码",
|
"sidepanel_title_reset": "重置密码",
|
||||||
@@ -134,7 +134,6 @@
|
|||||||
"more_online_members": "还有{overflow}个",
|
"more_online_members": "还有{overflow}个",
|
||||||
"label_user_contacts": "往来:",
|
"label_user_contacts": "往来:",
|
||||||
"validate_credential_action": "确认",
|
"validate_credential_action": "确认",
|
||||||
"label_legal": "法律东西",
|
|
||||||
"link_contact_us": "联系我们",
|
"link_contact_us": "联系我们",
|
||||||
"link_terms_of_service": "条款和条件",
|
"link_terms_of_service": "条款和条件",
|
||||||
"link_privacy_policy": "隐私政策",
|
"link_privacy_policy": "隐私政策",
|
||||||
@@ -153,5 +152,16 @@
|
|||||||
"deleted_content": "内容已删除",
|
"deleted_content": "内容已删除",
|
||||||
"image_caption_prompt": "图片标题",
|
"image_caption_prompt": "图片标题",
|
||||||
"menu_item_send_retry": "重试",
|
"menu_item_send_retry": "重试",
|
||||||
"label_incognito_mode": "无痕模式:"
|
"label_incognito_mode": "无痕模式:",
|
||||||
|
"delete_account": "删除帐户",
|
||||||
|
"delete_account_arning": "您确定要删除您的帐户吗?无法撤消。",
|
||||||
|
"button_delete_account": "删除帐户",
|
||||||
|
"sidepanel_title_acc_security": "安全",
|
||||||
|
"sidepanel_title_acc_notifications": "通知",
|
||||||
|
"sidepanel_title_acc_support": "支持",
|
||||||
|
"label_sdk": "开发包:",
|
||||||
|
"label_server_address": "服务器地址:",
|
||||||
|
"button_edit": "编辑",
|
||||||
|
"button_add_another": "加上另一个",
|
||||||
|
"sidepanel_title_acc_general": "常用设定"
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-24
@@ -1,27 +1,33 @@
|
|||||||
{
|
{
|
||||||
"en": {
|
"en": {
|
||||||
|
"validate_credential_action": "confirm",
|
||||||
|
"title_tag_manager": "Tags (user discovery)",
|
||||||
|
"label_user_contacts": "Contacts:",
|
||||||
|
"button_add_another": "Add another",
|
||||||
|
"label_message_sound": "Message sound:",
|
||||||
|
"label_push_notifications": "Notification alerts:",
|
||||||
|
"label_push_notifications_disabled": "Notification alerts (requires HTTPS):",
|
||||||
|
"label_incognito_mode": "Incognito mode:",
|
||||||
|
"delete_account": "Delete account",
|
||||||
|
"delete_account_arning": "Are you sure you want to delete your account? It cannot be undone.",
|
||||||
|
"label_password": "Password",
|
||||||
|
"password_unchanged_prompt": "Unchanged",
|
||||||
|
"button_logout": "Logout",
|
||||||
|
"button_delete_account": "Delete account",
|
||||||
|
"label_default_access_mode": "Default access mode:",
|
||||||
|
"link_contact_us": "Contact Us",
|
||||||
|
"link_terms_of_service": "Terms of Service",
|
||||||
|
"link_privacy_policy": "Privacy Policy",
|
||||||
|
"label_sdk": "SDK:",
|
||||||
|
"label_server_address": "Server address:",
|
||||||
"archived_contacts": "Archived contacts ({count})",
|
"archived_contacts": "Archived contacts ({count})",
|
||||||
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯",
|
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯",
|
||||||
"full_name_prompt": "Full name, e.g. John Doe",
|
"full_name_prompt": "Full name, e.g. John Doe",
|
||||||
"email_prompt": "Email, e.g. jdoe@example.com",
|
"email_prompt": "Email, e.g. jdoe@example.com",
|
||||||
"button_sign_up": "Sign up",
|
"button_sign_up": "Sign up",
|
||||||
"validate_credential_action": "confirm",
|
|
||||||
"label_your_name": "Your name",
|
"label_your_name": "Your name",
|
||||||
"label_password": "Password",
|
"label_user_id": "ID:",
|
||||||
"password_unchanged_prompt": "Unchanged",
|
"button_edit": "Edit",
|
||||||
"label_message_sound": "Message sound:",
|
|
||||||
"label_push_notifications": "Notification alerts:",
|
|
||||||
"label_push_notifications_disabled": "Notification alerts (requires HTTPS):",
|
|
||||||
"label_incognito_mode": "Incognito mode:",
|
|
||||||
"title_tag_manager": "Tags (user discovery)",
|
|
||||||
"label_user_contacts": "Contacts:",
|
|
||||||
"label_user_id": "Address:",
|
|
||||||
"label_default_access_mode": "Default access mode:",
|
|
||||||
"button_logout": "Logout",
|
|
||||||
"label_legal": "Legal",
|
|
||||||
"link_contact_us": "Contact Us",
|
|
||||||
"link_terms_of_service": "Terms of Service",
|
|
||||||
"link_privacy_policy": "Privacy Policy",
|
|
||||||
"requested_permissions": "Requested",
|
"requested_permissions": "Requested",
|
||||||
"granted_permissions": "Granted",
|
"granted_permissions": "Granted",
|
||||||
"menu_item_edit_permissions": "Edit permissions",
|
"menu_item_edit_permissions": "Edit permissions",
|
||||||
@@ -79,7 +85,11 @@
|
|||||||
"sidepanel_title_login": "Sign In",
|
"sidepanel_title_login": "Sign In",
|
||||||
"sidepanel_title_register": "Create Account",
|
"sidepanel_title_register": "Create Account",
|
||||||
"sidepanel_title_settings": "Settings",
|
"sidepanel_title_settings": "Settings",
|
||||||
"sidepanel_title_edit_account": "Edit Account",
|
"sidepanel_title_account_settings": "Account Settings",
|
||||||
|
"sidepanel_title_acc_general": "General",
|
||||||
|
"sidepanel_title_acc_security": "Security",
|
||||||
|
"sidepanel_title_acc_notifications": "Notifications",
|
||||||
|
"sidepanel_title_acc_support": "Support",
|
||||||
"sidepanel_title_newtpk": "Start New Chat",
|
"sidepanel_title_newtpk": "Start New Chat",
|
||||||
"sidepanel_title_cred": "Confirm Credentials",
|
"sidepanel_title_cred": "Confirm Credentials",
|
||||||
"sidepanel_title_reset": "Reset Password",
|
"sidepanel_title_reset": "Reset Password",
|
||||||
@@ -164,7 +174,7 @@
|
|||||||
"label_your_name": "Ваше имя",
|
"label_your_name": "Ваше имя",
|
||||||
"label_password": "Пароль",
|
"label_password": "Пароль",
|
||||||
"password_unchanged_prompt": "Не изменен",
|
"password_unchanged_prompt": "Не изменен",
|
||||||
"label_user_id": "Адрес:",
|
"label_user_id": "ID:",
|
||||||
"label_default_access_mode": "Доступ по умолчанию:",
|
"label_default_access_mode": "Доступ по умолчанию:",
|
||||||
"label_message_sound": "Звук нового сообщения:",
|
"label_message_sound": "Звук нового сообщения:",
|
||||||
"label_push_notifications": "Уведомления:",
|
"label_push_notifications": "Уведомления:",
|
||||||
@@ -192,7 +202,7 @@
|
|||||||
"sidepanel_title_login": "Авторизация",
|
"sidepanel_title_login": "Авторизация",
|
||||||
"sidepanel_title_register": "Зарегистрироваться",
|
"sidepanel_title_register": "Зарегистрироваться",
|
||||||
"sidepanel_title_settings": "Настройки",
|
"sidepanel_title_settings": "Настройки",
|
||||||
"sidepanel_title_edit_account": "Редактировать аккаунт",
|
"sidepanel_title_account_settings": "Настройки аккаунта",
|
||||||
"sidepanel_title_newtpk": "Новый чат",
|
"sidepanel_title_newtpk": "Новый чат",
|
||||||
"sidepanel_title_cred": "Подтвердить",
|
"sidepanel_title_cred": "Подтвердить",
|
||||||
"sidepanel_title_reset": "Сменить пароль",
|
"sidepanel_title_reset": "Сменить пароль",
|
||||||
@@ -293,7 +303,6 @@
|
|||||||
"more_online_members": "+еще {overflow}",
|
"more_online_members": "+еще {overflow}",
|
||||||
"label_user_contacts": "Конакты:",
|
"label_user_contacts": "Конакты:",
|
||||||
"validate_credential_action": "подтвердить",
|
"validate_credential_action": "подтвердить",
|
||||||
"label_legal": "Прочее",
|
|
||||||
"link_contact_us": "Связаться с нами",
|
"link_contact_us": "Связаться с нами",
|
||||||
"link_terms_of_service": "Условия сервиса",
|
"link_terms_of_service": "Условия сервиса",
|
||||||
"link_privacy_policy": "Политика конфиденциальности",
|
"link_privacy_policy": "Политика конфиденциальности",
|
||||||
@@ -311,7 +320,18 @@
|
|||||||
"search_no_results": "Ничего не найдено",
|
"search_no_results": "Ничего не найдено",
|
||||||
"deleted_content": "удалено",
|
"deleted_content": "удалено",
|
||||||
"image_caption_prompt": "Подпись к фото",
|
"image_caption_prompt": "Подпись к фото",
|
||||||
"label_incognito_mode": "Режим инкогнито:"
|
"label_incognito_mode": "Режим инкогнито:",
|
||||||
|
"delete_account": "Удалить аккаунт",
|
||||||
|
"delete_account_arning": "Вы уверены, что ходите удалить свой аккаунт? Его невозможно будет восстановить.",
|
||||||
|
"button_delete_account": "Удалить аккаунт",
|
||||||
|
"sidepanel_title_acc_security": "Безопасность",
|
||||||
|
"sidepanel_title_acc_notifications": "Уведомления",
|
||||||
|
"sidepanel_title_acc_support": "Поддержка",
|
||||||
|
"label_sdk": "SDK:",
|
||||||
|
"label_server_address": "Адрес сервера:",
|
||||||
|
"button_edit": "Редактировать",
|
||||||
|
"button_add_another": "Добавить",
|
||||||
|
"sidepanel_title_acc_general": "Общие настройки"
|
||||||
},
|
},
|
||||||
"zh": {
|
"zh": {
|
||||||
"archived_contacts": "已归档联系人 ({count})",
|
"archived_contacts": "已归档联系人 ({count})",
|
||||||
@@ -376,7 +396,7 @@
|
|||||||
"sidepanel_title_login": "登录",
|
"sidepanel_title_login": "登录",
|
||||||
"sidepanel_title_register": "创建账户",
|
"sidepanel_title_register": "创建账户",
|
||||||
"sidepanel_title_settings": "设置",
|
"sidepanel_title_settings": "设置",
|
||||||
"sidepanel_title_edit_account": "编辑账户",
|
"sidepanel_title_account_settings": "帐号设定",
|
||||||
"sidepanel_title_newtpk": "开始新会话",
|
"sidepanel_title_newtpk": "开始新会话",
|
||||||
"sidepanel_title_cred": "确认凭据",
|
"sidepanel_title_cred": "确认凭据",
|
||||||
"sidepanel_title_reset": "重置密码",
|
"sidepanel_title_reset": "重置密码",
|
||||||
@@ -449,7 +469,6 @@
|
|||||||
"more_online_members": "还有{overflow}个",
|
"more_online_members": "还有{overflow}个",
|
||||||
"label_user_contacts": "往来:",
|
"label_user_contacts": "往来:",
|
||||||
"validate_credential_action": "确认",
|
"validate_credential_action": "确认",
|
||||||
"label_legal": "法律东西",
|
|
||||||
"link_contact_us": "联系我们",
|
"link_contact_us": "联系我们",
|
||||||
"link_terms_of_service": "条款和条件",
|
"link_terms_of_service": "条款和条件",
|
||||||
"link_privacy_policy": "隐私政策",
|
"link_privacy_policy": "隐私政策",
|
||||||
@@ -468,6 +487,17 @@
|
|||||||
"deleted_content": "内容已删除",
|
"deleted_content": "内容已删除",
|
||||||
"image_caption_prompt": "图片标题",
|
"image_caption_prompt": "图片标题",
|
||||||
"menu_item_send_retry": "重试",
|
"menu_item_send_retry": "重试",
|
||||||
"label_incognito_mode": "无痕模式:"
|
"label_incognito_mode": "无痕模式:",
|
||||||
|
"delete_account": "删除帐户",
|
||||||
|
"delete_account_arning": "您确定要删除您的帐户吗?无法撤消。",
|
||||||
|
"button_delete_account": "删除帐户",
|
||||||
|
"sidepanel_title_acc_security": "安全",
|
||||||
|
"sidepanel_title_acc_notifications": "通知",
|
||||||
|
"sidepanel_title_acc_support": "支持",
|
||||||
|
"label_sdk": "开发包:",
|
||||||
|
"label_server_address": "服务器地址:",
|
||||||
|
"button_edit": "编辑",
|
||||||
|
"button_add_another": "加上另一个",
|
||||||
|
"sidepanel_title_acc_general": "常用设定"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
// Edit account parameters.
|
||||||
|
import React from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import Tinode from 'tinode-sdk';
|
||||||
|
|
||||||
|
import AvatarUpload from '../widgets/avatar-upload.jsx';
|
||||||
|
import InPlaceEdit from '../widgets/in-place-edit.jsx';
|
||||||
|
import TagManager from '../widgets/tag-manager.jsx';
|
||||||
|
|
||||||
|
import { MAX_TITLE_LENGTH } from '../config.js';
|
||||||
|
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||||
|
import { arrayEqual, asEmail, asPhone, vcard } from '../lib/utils.js';
|
||||||
|
|
||||||
|
export default class AccGeneralView extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const me = this.props.tinode.getMeTopic();
|
||||||
|
this.state = {
|
||||||
|
fullName: me.public ? me.public.fn : undefined,
|
||||||
|
avatar: makeImageUrl(me.public ? me.public.photo : null),
|
||||||
|
tags: me.tags(),
|
||||||
|
credentials: me.getCredentials() || [],
|
||||||
|
addCredActive: false,
|
||||||
|
addCredInvalid: false,
|
||||||
|
newCred: '',
|
||||||
|
previousOnTags: me.onTagsUpdated
|
||||||
|
};
|
||||||
|
|
||||||
|
this.tnNewTags = this.tnNewTags.bind(this);
|
||||||
|
this.tnCredsUpdated = this.tnCredsUpdated.bind(this);
|
||||||
|
this.handleFullNameUpdate = this.handleFullNameUpdate.bind(this);
|
||||||
|
this.handleImageChanged = this.handleImageChanged.bind(this);
|
||||||
|
this.handleCredChange = this.handleCredChange.bind(this);
|
||||||
|
this.handleCredKeyDown = this.handleCredKeyDown.bind(this);
|
||||||
|
this.handleCredEntered = this.handleCredEntered.bind(this);
|
||||||
|
this.handleTagsUpdated = this.handleTagsUpdated.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const me = this.props.tinode.getMeTopic();
|
||||||
|
me.onCredsUpdated = this.tnCredsUpdated;
|
||||||
|
me.onTagsUpdated = this.tnNewTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const me = this.props.tinode.getMeTopic();
|
||||||
|
me.onTagsUpdated = this.state.previousOnTags;
|
||||||
|
me.onCredsUpdated = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
tnNewTags(tags) {
|
||||||
|
this.setState({tags: tags});
|
||||||
|
}
|
||||||
|
|
||||||
|
tnCredsUpdated(creds) {
|
||||||
|
this.setState({credentials: creds || []});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFullNameUpdate(fn) {
|
||||||
|
fn = fn.trim().substring(0, MAX_TITLE_LENGTH);
|
||||||
|
if (fn) {
|
||||||
|
this.setState({fullName: fn});
|
||||||
|
this.props.onUpdateAccount(undefined, vcard(fn, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleImageChanged(img) {
|
||||||
|
this.setState({avatar: img});
|
||||||
|
this.props.onUpdateAccount(undefined, vcard(null, img || Tinode.DEL_CHAR));
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
let 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});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTagsUpdated(tags) {
|
||||||
|
// Check if tags have actually changed.
|
||||||
|
if (arrayEqual(this.state.tags.slice(0), tags.slice(0))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.props.onUpdateTags(tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const credentials = [];
|
||||||
|
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">
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<div className="panel-form-column">
|
||||||
|
<label className="small"><FormattedMessage id="label_your_name" /></label>
|
||||||
|
<div><FormattedMessage id="full_name_prompt">{
|
||||||
|
(full_name_placeholder) => <InPlaceEdit
|
||||||
|
placeholder={full_name_placeholder}
|
||||||
|
value={this.state.fullName}
|
||||||
|
onFinished={this.handleFullNameUpdate} />
|
||||||
|
}</FormattedMessage></div>
|
||||||
|
</div>
|
||||||
|
<AvatarUpload
|
||||||
|
avatar={this.state.avatar}
|
||||||
|
uid={this.props.myUserId}
|
||||||
|
title={this.state.fullName}
|
||||||
|
onImageChanged={this.handleImageChanged}
|
||||||
|
onError={this.props.onError} />
|
||||||
|
</div>
|
||||||
|
<div className="hr" />
|
||||||
|
<FormattedMessage id="title_tag_manager" defaultMessage="Tags (user discovery)"
|
||||||
|
description="Section title for TagManager">{
|
||||||
|
(title_tag_manager) => <TagManager
|
||||||
|
title={title_tag_manager}
|
||||||
|
activated={false}
|
||||||
|
tags={this.state.tags}
|
||||||
|
onSubmit={this.handleTagsUpdated} />
|
||||||
|
}</FormattedMessage>
|
||||||
|
<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 ?
|
||||||
|
<input type="text" value={this.state.value}
|
||||||
|
className={this.state.addCredInvalid ? 'invalid' : null}
|
||||||
|
placeholder="Phone number or email" required="required" autoFocus
|
||||||
|
onChange={this.handleCredChange} onKeyDown={this.handleCredKeyDown} onBlur={this.handleCredEntered} />
|
||||||
|
: 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>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// Edit account parameters.
|
||||||
|
import React from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import CheckBox from '../widgets/checkbox.jsx';
|
||||||
|
|
||||||
|
export default class EditAccountView extends React.PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.handleCheckboxClick = this.handleCheckboxClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCheckboxClick(what, checked) {
|
||||||
|
if (what == 'sound') {
|
||||||
|
this.props.onToggleMessageSounds(checked);
|
||||||
|
} else if (what == 'alert') {
|
||||||
|
this.props.onTogglePushNotifications(checked);
|
||||||
|
} else if (what == 'incognito') {
|
||||||
|
this.props.onToggleIncognitoMode(checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="scrollable-panel">
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<label htmlFor="message-sound">
|
||||||
|
<FormattedMessage id="label_message_sound" defaultMessage="Message sound:"
|
||||||
|
description="Label for message sounds toggle" />
|
||||||
|
</label>
|
||||||
|
<CheckBox name="sound" id="message-sound"
|
||||||
|
checked={this.props.messageSounds} onChange={this.handleCheckboxClick} />
|
||||||
|
</div>
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<label htmlFor="desktop-alerts">
|
||||||
|
{this.props.desktopAlertsEnabled ?
|
||||||
|
<FormattedMessage id="label_push_notifications" defaultMessage="Notification alerts:"
|
||||||
|
description="Label for push notifications switch" />
|
||||||
|
:
|
||||||
|
<FormattedMessage id="label_push_notifications_disabled"
|
||||||
|
defaultMessage="Notification alerts (requires HTTPS):"
|
||||||
|
description="Label for push notifications switch" />
|
||||||
|
}
|
||||||
|
</label>
|
||||||
|
<CheckBox name="alert" id="desktop-alerts"
|
||||||
|
checked={this.props.desktopAlerts}
|
||||||
|
onChange={this.props.desktopAlertsEnabled ? this.handleCheckboxClick : null} />
|
||||||
|
</div>
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<label htmlFor="incognito-mode">
|
||||||
|
<FormattedMessage id="label_incognito_mode" defaultMessage="Incognito mode:"
|
||||||
|
description="Label for incognito mode toggle" />
|
||||||
|
</label>
|
||||||
|
<CheckBox name="incognito" id="incognito-mode"
|
||||||
|
checked={this.props.incognitoMode} onChange={this.handleCheckboxClick} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
// Edit account parameters.
|
||||||
|
import React from 'react';
|
||||||
|
import { FormattedMessage, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
|
import InPlaceEdit from '../widgets/in-place-edit.jsx';
|
||||||
|
import PermissionsEditor from '../widgets/permissions-editor.jsx';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
delete_account: {
|
||||||
|
id: 'delete_account',
|
||||||
|
defaultMessage: 'Delete account',
|
||||||
|
description: 'Title for delete account warning'
|
||||||
|
},
|
||||||
|
delete_account_warning: {
|
||||||
|
id: 'delete_account_arning',
|
||||||
|
defaultMessage: 'Are you sure you want to delete your account? It cannot be undone.',
|
||||||
|
description: 'Warning message when deleting an account'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default class AccSecurityView extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const me = this.props.tinode.getMeTopic();
|
||||||
|
const defacs = me.getDefaultAccess();
|
||||||
|
this.state = {
|
||||||
|
auth: defacs ? defacs.auth : null,
|
||||||
|
anon: defacs ? defacs.anon : null,
|
||||||
|
showPermissionEditorFor: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handlePasswordUpdate = this.handlePasswordUpdate.bind(this);
|
||||||
|
this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);
|
||||||
|
this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this);
|
||||||
|
this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this);
|
||||||
|
this.handleDeleteAccount = this.handleDeleteAccount.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePasswordUpdate(pwd) {
|
||||||
|
this.setState({password: pwd});
|
||||||
|
this.props.onUpdateAccount(pwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLaunchPermissionsEditor(which) {
|
||||||
|
this.setState({
|
||||||
|
showPermissionEditorFor: which,
|
||||||
|
editedPermissions: this.state[which]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHidePermissionsEditor() {
|
||||||
|
this.setState({showPermissionEditorFor: undefined});
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePermissionsChanged(perm) {
|
||||||
|
let defacs = {};
|
||||||
|
defacs[this.state.showPermissionEditorFor] = perm;
|
||||||
|
this.props.onUpdateAccount(undefined, undefined, defacs);
|
||||||
|
|
||||||
|
let newState = {showPermissionEditorFor: undefined};
|
||||||
|
newState[this.state.showPermissionEditorFor] = perm;
|
||||||
|
this.setState(newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDeleteAccount(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const {formatMessage} = this.props.intl;
|
||||||
|
this.props.onShowAlert(
|
||||||
|
formatMessage(messages.delete_account), // title
|
||||||
|
formatMessage(messages.delete_account_warning), // content
|
||||||
|
(() => { this.props.onDeleteAccount(); }), // onConfirm
|
||||||
|
null, // "OK"
|
||||||
|
null, // Show Reject button
|
||||||
|
true // "Cancel"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>{this.state.showPermissionEditorFor ?
|
||||||
|
<PermissionsEditor
|
||||||
|
mode={this.state.editedPermissions}
|
||||||
|
skip="O"
|
||||||
|
onSubmit={this.handlePermissionsChanged}
|
||||||
|
onCancel={this.handleHidePermissionsEditor} />
|
||||||
|
:
|
||||||
|
<div className="scrollable-panel">
|
||||||
|
<div className="panel-form-column">
|
||||||
|
<label className="small">
|
||||||
|
<FormattedMessage id="label_password" defaultMessage="Password"
|
||||||
|
description="Label for password editing" />
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<FormattedMessage id="password_unchanged_prompt" defaultMessage="Unchanged"
|
||||||
|
description="Message in editor while password is unchanged">{
|
||||||
|
(password_unchanged) => <InPlaceEdit
|
||||||
|
placeholder={password_unchanged}
|
||||||
|
type="password"
|
||||||
|
onFinished={this.handlePasswordUpdate} />
|
||||||
|
}</FormattedMessage>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="hr" />
|
||||||
|
<div className="panel-form-column">
|
||||||
|
<a href="#" className="red flat-button" onClick={(e) => {e.preventDefault(); this.props.onLogout();}}>
|
||||||
|
<i className="material-icons">exit_to_app</i> <FormattedMessage id="button_logout"
|
||||||
|
defaultMessage="Logout" description="Button [Logout]" />
|
||||||
|
</a>
|
||||||
|
<a href="#" className="red flat-button" onClick={(e) => {this.handleDeleteAccount(e);}}>
|
||||||
|
<i className="material-icons">delete</i> <FormattedMessage id="button_delete_account"
|
||||||
|
defaultMessage="Delete account" description="Button [Delete account]" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="hr" />
|
||||||
|
<div className="panel-form-column">
|
||||||
|
<div>
|
||||||
|
<label className="small">
|
||||||
|
<FormattedMessage id="label_default_access_mode" defaultMessage="Default access mode:"
|
||||||
|
description="Label for default access mode" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="quoted">
|
||||||
|
<div>Auth: <tt className="clickable"
|
||||||
|
onClick={this.handleLaunchPermissionsEditor.bind(this, 'auth')}>{this.state.auth}</tt></div>
|
||||||
|
<div>Anon: <tt className="clickable"
|
||||||
|
onClick={this.handleLaunchPermissionsEditor.bind(this, 'anon')}>{this.state.anon}</tt></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Edit account parameters.
|
||||||
|
import React from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import Tinode from 'tinode-sdk';
|
||||||
|
|
||||||
|
import { APP_NAME, LINK_CONTACT_US, LINK_PRIVACY_POLICY, LINK_TERMS_OF_SERVICE } from '../config.js';
|
||||||
|
|
||||||
|
export default class AccSupportView extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="scrollable-panel">
|
||||||
|
<div className="panel-form-column">
|
||||||
|
<a href={LINK_CONTACT_US} className="flat-button" target="_blank">
|
||||||
|
<i className="material-icons">email</i> <FormattedMessage id="link_contact_us"
|
||||||
|
defaultMessage="Contact Us" description="Ancor text for contacting us by email" />
|
||||||
|
</a>
|
||||||
|
<a href={LINK_TERMS_OF_SERVICE} className="flat-button" target="_blank">
|
||||||
|
<i className="material-icons">description</i> <FormattedMessage id="link_terms_of_service"
|
||||||
|
defaultMessage="Terms of Service" description="Ancor text for terms of service link" />
|
||||||
|
</a>
|
||||||
|
<a href={LINK_PRIVACY_POLICY} className="flat-button" target="_blank">
|
||||||
|
<i className="material-icons">policy</i> <FormattedMessage id="link_privacy_policy"
|
||||||
|
defaultMessage="Privacy Policy" description="Ancor text for privacy policy link" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="hr" />
|
||||||
|
<div className="panel-form-column">
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<label className="small"><FormattedMessage id="label_client" /></label>
|
||||||
|
{APP_NAME}
|
||||||
|
</div>
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<label className="small"><FormattedMessage id="label_sdk" defaultMessage="SDK:" /></label>
|
||||||
|
{Tinode.getLibrary()}
|
||||||
|
</div>
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<label className="small"><FormattedMessage id="label_server" /></label>
|
||||||
|
{this.props.serverVersion}
|
||||||
|
</div>
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<label className="small"><FormattedMessage id="label_server_address" defaultMessage="Server address:" /></label>
|
||||||
|
{this.props.serverAddress}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
+34
-312
@@ -2,340 +2,62 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import Tinode from 'tinode-sdk';
|
|
||||||
|
|
||||||
import AvatarUpload from '../widgets/avatar-upload.jsx';
|
import AvatarUpload from '../widgets/avatar-upload.jsx';
|
||||||
import CheckBox from '../widgets/checkbox.jsx';
|
|
||||||
import InPlaceEdit from '../widgets/in-place-edit.jsx';
|
|
||||||
import PermissionsEditor from '../widgets/permissions-editor.jsx';
|
|
||||||
import TagManager from '../widgets/tag-manager.jsx';
|
|
||||||
|
|
||||||
import { LINK_CONTACT_US, LINK_PRIVACY_POLICY, LINK_TERMS_OF_SERVICE, MAX_TITLE_LENGTH } from '../config.js';
|
|
||||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||||
import { arrayEqual, asEmail, asPhone, vcard } from '../lib/utils.js';
|
|
||||||
|
|
||||||
export default class EditAccountView extends React.Component {
|
export default class EditAccountView extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const me = this.props.tinode.getMeTopic();
|
const me = this.props.tinode.getMeTopic();
|
||||||
const defacs = me.getDefaultAccess();
|
|
||||||
this.state = {
|
this.state = {
|
||||||
fullName: me.public ? me.public.fn : undefined,
|
fullName: me.public ? me.public.fn : undefined,
|
||||||
avatar: makeImageUrl(me.public ? me.public.photo : null),
|
avatar: makeImageUrl(me.public ? me.public.photo : null)
|
||||||
auth: defacs ? defacs.auth : null,
|
|
||||||
anon: defacs ? defacs.anon : null,
|
|
||||||
tags: me.tags(),
|
|
||||||
credentials: me.getCredentials() || [],
|
|
||||||
addCredActive: false,
|
|
||||||
addCredInvalid: false,
|
|
||||||
newCred: '',
|
|
||||||
showPermissionEditorFor: undefined,
|
|
||||||
previousOnTags: me.onTagsUpdated
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.tnNewTags = this.tnNewTags.bind(this);
|
|
||||||
this.tnCredsUpdated = this.tnCredsUpdated.bind(this);
|
|
||||||
this.handleFullNameUpdate = this.handleFullNameUpdate.bind(this);
|
|
||||||
this.handlePasswordUpdate = this.handlePasswordUpdate.bind(this);
|
|
||||||
this.handleImageChanged = this.handleImageChanged.bind(this);
|
|
||||||
this.handleCheckboxClick = this.handleCheckboxClick.bind(this);
|
|
||||||
this.handleCredChange = this.handleCredChange.bind(this);
|
|
||||||
this.handleCredKeyDown = this.handleCredKeyDown.bind(this);
|
|
||||||
this.handleCredEntered = this.handleCredEntered.bind(this);
|
|
||||||
this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);
|
|
||||||
this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this);
|
|
||||||
this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this);
|
|
||||||
this.handleTagsUpdated = this.handleTagsUpdated.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const me = this.props.tinode.getMeTopic();
|
|
||||||
me.onCredsUpdated = this.tnCredsUpdated;
|
|
||||||
me.onTagsUpdated = this.tnNewTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
const me = this.props.tinode.getMeTopic();
|
|
||||||
me.onTagsUpdated = this.state.previousOnTags;
|
|
||||||
me.onCredsUpdated = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
tnNewTags(tags) {
|
|
||||||
this.setState({tags: tags});
|
|
||||||
}
|
|
||||||
|
|
||||||
tnCredsUpdated(creds) {
|
|
||||||
this.setState({credentials: creds || []});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleFullNameUpdate(fn) {
|
|
||||||
fn = fn.trim().substring(0, MAX_TITLE_LENGTH);
|
|
||||||
if (fn) {
|
|
||||||
this.setState({fullName: fn});
|
|
||||||
this.props.onUpdateAccount(undefined, vcard(fn, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePasswordUpdate(pwd) {
|
|
||||||
this.setState({password: pwd});
|
|
||||||
this.props.onUpdateAccount(pwd);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleImageChanged(img) {
|
|
||||||
this.setState({avatar: img});
|
|
||||||
this.props.onUpdateAccount(undefined, vcard(null, img || Tinode.DEL_CHAR));
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCheckboxClick(what, checked) {
|
|
||||||
if (what == 'sound') {
|
|
||||||
this.props.onToggleMessageSounds(checked);
|
|
||||||
} else if (what == 'alert') {
|
|
||||||
this.props.onTogglePushNotifications(checked);
|
|
||||||
} else if (what == 'incognito') {
|
|
||||||
this.props.onToggleIncognitoMode(checked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
let 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});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleLaunchPermissionsEditor(which) {
|
|
||||||
this.setState({
|
|
||||||
showPermissionEditorFor: which,
|
|
||||||
editedPermissions: this.state[which]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleHidePermissionsEditor() {
|
|
||||||
this.setState({showPermissionEditorFor: undefined});
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePermissionsChanged(perm) {
|
|
||||||
let defacs = {};
|
|
||||||
defacs[this.state.showPermissionEditorFor] = perm;
|
|
||||||
this.props.onUpdateAccount(undefined, undefined, defacs);
|
|
||||||
|
|
||||||
let newState = {showPermissionEditorFor: undefined};
|
|
||||||
newState[this.state.showPermissionEditorFor] = perm;
|
|
||||||
this.setState(newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleTagsUpdated(tags) {
|
|
||||||
// Check if tags have actually changed.
|
|
||||||
if (arrayEqual(this.state.tags.slice(0), tags.slice(0))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.props.onUpdateTags(tags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let credentials = [];
|
|
||||||
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 (
|
return (
|
||||||
<React.Fragment>{this.state.showPermissionEditorFor ?
|
<div className="scrollable-panel">
|
||||||
<PermissionsEditor
|
<div className="panel-form-row">
|
||||||
mode={this.state.editedPermissions}
|
|
||||||
skip="O"
|
|
||||||
onSubmit={this.handlePermissionsChanged}
|
|
||||||
onCancel={this.handleHidePermissionsEditor} />
|
|
||||||
:
|
|
||||||
<div className="scrollable-panel">
|
|
||||||
<div className="panel-form-row">
|
|
||||||
<div className="panel-form-column">
|
|
||||||
<div><label className="small">
|
|
||||||
<FormattedMessage id="label_your_name" defaultMessage="Your name"
|
|
||||||
description="Label for full name editing" />
|
|
||||||
</label></div>
|
|
||||||
<div><FormattedMessage id="full_name_prompt">{
|
|
||||||
(full_name_placeholder) => <InPlaceEdit
|
|
||||||
placeholder={full_name_placeholder}
|
|
||||||
value={this.state.fullName}
|
|
||||||
onFinished={this.handleFullNameUpdate} />
|
|
||||||
}</FormattedMessage></div>
|
|
||||||
<div><label className="small">
|
|
||||||
<FormattedMessage id="label_password" defaultMessage="Password"
|
|
||||||
description="Label for password editing" />
|
|
||||||
</label></div>
|
|
||||||
<div>
|
|
||||||
<FormattedMessage id="password_unchanged_prompt" defaultMessage="Unchanged"
|
|
||||||
description="Message in editor while password is unchanged">{
|
|
||||||
(password_unchanged) => <InPlaceEdit
|
|
||||||
placeholder={password_unchanged}
|
|
||||||
type="password"
|
|
||||||
onFinished={this.handlePasswordUpdate} />
|
|
||||||
}</FormattedMessage>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<AvatarUpload
|
|
||||||
avatar={this.state.avatar}
|
|
||||||
uid={this.props.myUserId}
|
|
||||||
title={this.state.fullName}
|
|
||||||
onImageChanged={this.handleImageChanged}
|
|
||||||
onError={this.props.onError} />
|
|
||||||
</div>
|
|
||||||
<div className="hr" />
|
|
||||||
<div className="panel-form-row">
|
|
||||||
<label htmlFor="message-sound">
|
|
||||||
<FormattedMessage id="label_message_sound" defaultMessage="Message sound:"
|
|
||||||
description="Label for message sounds toggle" />
|
|
||||||
</label>
|
|
||||||
<CheckBox name="sound" id="message-sound"
|
|
||||||
checked={this.props.messageSounds} onChange={this.handleCheckboxClick} />
|
|
||||||
</div>
|
|
||||||
<div className="panel-form-row">
|
|
||||||
<label htmlFor="desktop-alerts">
|
|
||||||
{this.props.desktopAlertsEnabled ?
|
|
||||||
<FormattedMessage id="label_push_notifications" defaultMessage="Notification alerts:"
|
|
||||||
description="Label for push notifications switch" />
|
|
||||||
:
|
|
||||||
<FormattedMessage id="label_push_notifications_disabled"
|
|
||||||
defaultMessage="Notification alerts (requires HTTPS):"
|
|
||||||
description="Label for push notifications switch" />
|
|
||||||
}
|
|
||||||
</label>
|
|
||||||
<CheckBox name="alert" id="desktop-alerts"
|
|
||||||
checked={this.props.desktopAlerts}
|
|
||||||
onChange={this.props.desktopAlertsEnabled ? this.handleCheckboxClick : null} />
|
|
||||||
</div>
|
|
||||||
<div className="panel-form-row">
|
|
||||||
<label htmlFor="incognito-mode">
|
|
||||||
<FormattedMessage id="label_incognito_mode" defaultMessage="Incognito mode:"
|
|
||||||
description="Label for incognito mode toggle" />
|
|
||||||
</label>
|
|
||||||
<CheckBox name="incognito" id="incognito-mode"
|
|
||||||
checked={this.props.incognitoMode} onChange={this.handleCheckboxClick} />
|
|
||||||
</div>
|
|
||||||
<div className="hr" />
|
|
||||||
<FormattedMessage id="title_tag_manager" defaultMessage="Tags (user discovery)"
|
|
||||||
description="Section title for TagManager">{
|
|
||||||
(title_tag_manager) => <TagManager
|
|
||||||
title={title_tag_manager}
|
|
||||||
activated={false}
|
|
||||||
tags={this.state.tags}
|
|
||||||
onSubmit={this.handleTagsUpdated} />
|
|
||||||
}</FormattedMessage>
|
|
||||||
<div className="hr" />
|
|
||||||
<div className="panel-form-column">
|
<div className="panel-form-column">
|
||||||
|
<label className="small">
|
||||||
|
<FormattedMessage id="label_your_name" defaultMessage="Your name"
|
||||||
|
description="Label for full name editing" />
|
||||||
|
</label>
|
||||||
|
<div className="large">{this.state.fullName}</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="small">
|
<label className="small"><FormattedMessage id="label_user_id" defaultMessage="ID:"
|
||||||
<FormattedMessage id="label_user_contacts" defaultMessage="Contacts:"
|
description="Label for user address (ID)" /></label>
|
||||||
description="Label for user contacts" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="quoted">
|
|
||||||
{credentials}
|
|
||||||
{this.state.addCredActive ?
|
|
||||||
<input type="text" value={this.state.value}
|
|
||||||
className={this.state.addCredInvalid ? "invalid" : null}
|
|
||||||
placeholder="Phone number or email" required="required" autoFocus
|
|
||||||
onChange={this.handleCredChange} onKeyDown={this.handleCredKeyDown} onBlur={this.handleCredEntered} />
|
|
||||||
: null}
|
|
||||||
<div><a href="#" onClick={(e) => {e.preventDefault(); this.setState({addCredActive: true});}}>+ Add another</a></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="hr" />
|
|
||||||
<div className="panel-form-column">
|
|
||||||
<div className="panel-form-row">
|
|
||||||
<label className="small"><FormattedMessage id="label_user_id" defaultMessage="Address:"
|
|
||||||
description="Label for user address (ID)" /></label>
|
|
||||||
<tt>{this.props.myUserId}</tt>
|
<tt>{this.props.myUserId}</tt>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<label className="small">
|
|
||||||
<FormattedMessage id="label_default_access_mode" defaultMessage="Default access mode:"
|
|
||||||
description="Label for default access mode" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="quoted">
|
|
||||||
<div>Auth: <tt className="clickable"
|
|
||||||
onClick={this.handleLaunchPermissionsEditor.bind(this, 'auth')}>{this.state.auth}</tt></div>
|
|
||||||
<div>Anon: <tt className="clickable"
|
|
||||||
onClick={this.handleLaunchPermissionsEditor.bind(this, 'anon')}>{this.state.anon}</tt></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="hr" />
|
|
||||||
<div className="panel-form-column">
|
|
||||||
<a href="#" className="red flat-button" onClick={(e) => {e.preventDefault(); this.props.onLogout();}}>
|
|
||||||
<i className="material-icons">exit_to_app</i> <FormattedMessage id="button_logout"
|
|
||||||
defaultMessage="Logout" description="Button [Logout]" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="hr" />
|
|
||||||
<div className="panel-form-column">
|
|
||||||
<div>
|
|
||||||
<label className="small">
|
|
||||||
<FormattedMessage id="label_legal" defaultMessage="Legal"
|
|
||||||
description="Section label" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="quoted">
|
|
||||||
<a href={LINK_CONTACT_US} className="flat-button" target="_blank">
|
|
||||||
<i className="material-icons">email</i> <FormattedMessage id="link_contact_us"
|
|
||||||
defaultMessage="Contact Us" description="Ancor text for contacting us by email" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="quoted">
|
|
||||||
<a href={LINK_TERMS_OF_SERVICE} className="flat-button" target="_blank">
|
|
||||||
<i className="material-icons">description</i> <FormattedMessage id="link_terms_of_service"
|
|
||||||
defaultMessage="Terms of Service" description="Ancor text for terms of service link" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="quoted">
|
|
||||||
<a href={LINK_PRIVACY_POLICY} className="flat-button" target="_blank">
|
|
||||||
<i className="material-icons">policy</i> <FormattedMessage id="link_privacy_policy"
|
|
||||||
defaultMessage="Privacy Policy" description="Ancor text for privacy policy link" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<AvatarUpload
|
||||||
|
avatar={this.state.avatar}
|
||||||
|
readOnly={!this.state.owner}
|
||||||
|
uid={this.props.myUserId}
|
||||||
|
title={this.state.fullName} />
|
||||||
</div>
|
</div>
|
||||||
}</React.Fragment>
|
<div className="panel-form-row">
|
||||||
|
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('general');}}>
|
||||||
|
<i className="material-icons">edit</i>
|
||||||
|
<FormattedMessage id="button_edit" defaultMessage="Edit" description="Call to action [Edit]" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="hr" />
|
||||||
|
<div className="panel-form-column">
|
||||||
|
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('notif');}}>
|
||||||
|
<i className="material-icons">notifications</i> <FormattedMessage id="sidepanel_title_acc_notifications" />
|
||||||
|
</a>
|
||||||
|
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('security');}}>
|
||||||
|
<i className="material-icons">security</i> <FormattedMessage id="sidepanel_title_acc_security" />
|
||||||
|
</a>
|
||||||
|
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('support');}}>
|
||||||
|
<i className="material-icons">contact_support</i> <FormattedMessage id="sidepanel_title_acc_support" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import { APP_NAME } from '../config.js';
|
|||||||
|
|
||||||
export default class LogoView extends React.PureComponent {
|
export default class LogoView extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
var version = APP_NAME + ' (' + Tinode.getLibrary() + ')';
|
const version = APP_NAME + ' (' + Tinode.getLibrary() + ')';
|
||||||
return (
|
return (
|
||||||
<div id="dummy-view" className={this.props.hideSelf ? 'nodisplay' : null}>
|
<div id="dummy-view">
|
||||||
<div>
|
<div>
|
||||||
<a href="https://github.com/tinode/chat/">
|
<a href="https://github.com/tinode/chat/">
|
||||||
<img id="logo" alt="logo" src="img/logo.svg" />
|
<img id="logo" alt="logo" src="img/logo.svg" />
|
||||||
|
|||||||
@@ -692,7 +692,7 @@ class MessagesView extends React.Component {
|
|||||||
component = null;
|
component = null;
|
||||||
} else if (!this.state.topic) {
|
} else if (!this.state.topic) {
|
||||||
component = (
|
component = (
|
||||||
<LogoView hideSelf={this.props.hideSelf}
|
<LogoView
|
||||||
serverVersion={this.props.serverVersion}
|
serverVersion={this.props.serverVersion}
|
||||||
serverAddress={this.props.serverAddress} />
|
serverAddress={this.props.serverAddress} />
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import SideNavbar from '../widgets/side-navbar.jsx';
|
|||||||
import ContactsView from './contacts-view.jsx';
|
import ContactsView from './contacts-view.jsx';
|
||||||
import CreateAccountView from './create-account-view.jsx';
|
import CreateAccountView from './create-account-view.jsx';
|
||||||
import EditAccountView from './edit-account-view.jsx';
|
import EditAccountView from './edit-account-view.jsx';
|
||||||
|
import AccGeneralView from './acc-general-view.jsx';
|
||||||
|
import AccNotificationsView from './acc-notifications-view.jsx';
|
||||||
|
import AccSecurityView from './acc-security-view.jsx';
|
||||||
|
import AccSupportView from './acc-support-view.jsx';
|
||||||
import LoginView from './login-view.jsx';
|
import LoginView from './login-view.jsx';
|
||||||
import NewTopicView from './new-topic-view.jsx';
|
import NewTopicView from './new-topic-view.jsx';
|
||||||
import PasswordResetView from './password-reset-view.jsx';
|
import PasswordResetView from './password-reset-view.jsx';
|
||||||
@@ -32,9 +36,29 @@ const messages = defineMessages({
|
|||||||
defaultMessage: 'Settings'
|
defaultMessage: 'Settings'
|
||||||
},
|
},
|
||||||
'edit': {
|
'edit': {
|
||||||
id: 'sidepanel_title_edit_account',
|
id: 'sidepanel_title_account_settings',
|
||||||
description: 'Sidepanel title for EditAccountView.',
|
description: 'Sidepanel title for EditAccountView.',
|
||||||
defaultMessage: 'Edit Account'
|
defaultMessage: 'Account Settings'
|
||||||
|
},
|
||||||
|
'general': {
|
||||||
|
id: 'sidepanel_title_acc_general',
|
||||||
|
description: 'Sidepanel title for AccGeneralView.',
|
||||||
|
defaultMessage: 'General'
|
||||||
|
},
|
||||||
|
'security': {
|
||||||
|
id: 'sidepanel_title_acc_security',
|
||||||
|
description: 'Sidepanel title for AccSecurityView.',
|
||||||
|
defaultMessage: 'Security'
|
||||||
|
},
|
||||||
|
'notif': {
|
||||||
|
id: 'sidepanel_title_acc_notifications',
|
||||||
|
description: 'Sidepanel title for AccNotificationsView.',
|
||||||
|
defaultMessage: 'Notifications'
|
||||||
|
},
|
||||||
|
'support': {
|
||||||
|
id: 'sidepanel_title_acc_support',
|
||||||
|
description: 'Sidepanel title for AccSupportView.',
|
||||||
|
defaultMessage: 'Support'
|
||||||
},
|
},
|
||||||
'newtpk': {
|
'newtpk': {
|
||||||
id: 'sidepanel_title_newtpk',
|
id: 'sidepanel_title_newtpk',
|
||||||
@@ -63,12 +87,17 @@ class SidepanelView extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.handleLoginRequested = this.handleLoginRequested.bind(this);
|
this.handleLoginRequested = this.handleLoginRequested.bind(this);
|
||||||
|
this.handleNewTopic = this.handleNewTopic.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoginRequested(login, password) {
|
handleLoginRequested(login, password) {
|
||||||
this.props.onLoginRequest(login, password);
|
this.props.onLoginRequest(login, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleNewTopic() {
|
||||||
|
this.props.onBasicNavigate('newtpk');
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {formatMessage} = this.props.intl;
|
const {formatMessage} = this.props.intl;
|
||||||
const view = this.props.state || (this.props.myUserId ? 'contacts' : 'login');
|
const view = this.props.state || (this.props.myUserId ? 'contacts' : 'login');
|
||||||
@@ -96,7 +125,7 @@ class SidepanelView extends React.Component {
|
|||||||
myUserId={this.props.myUserId}
|
myUserId={this.props.myUserId}
|
||||||
onSignUp={this.props.onSignUp}
|
onSignUp={this.props.onSignUp}
|
||||||
onSettings={this.props.onSettings}
|
onSettings={this.props.onSettings}
|
||||||
onNewTopic={this.props.onNewTopic}
|
onNewTopic={this.handleNewTopic}
|
||||||
onCancel={onCancel} />
|
onCancel={onCancel} />
|
||||||
|
|
||||||
<ErrorPanel
|
<ErrorPanel
|
||||||
@@ -131,21 +160,43 @@ class SidepanelView extends React.Component {
|
|||||||
<EditAccountView
|
<EditAccountView
|
||||||
tinode={this.props.tinode}
|
tinode={this.props.tinode}
|
||||||
myUserId={this.props.myUserId}
|
myUserId={this.props.myUserId}
|
||||||
|
onBasicNavigate={this.props.onBasicNavigate} /> :
|
||||||
|
|
||||||
|
view === 'general' ?
|
||||||
|
<AccGeneralView
|
||||||
|
tinode={this.props.tinode}
|
||||||
|
myUserId={this.props.myUserId}
|
||||||
|
onUpdateAccount={this.props.onUpdateAccount}
|
||||||
|
onUpdateTags={this.props.onUpdateAccountTags}
|
||||||
|
onCredAdd={this.props.onCredAdd}
|
||||||
|
onCredDelete={this.props.onCredDelete}
|
||||||
|
onCredConfirm={this.props.onCredConfirm}
|
||||||
|
onBasicNavigate={this.props.onBasicNavigate}
|
||||||
|
onCancel={this.props.onCancel}
|
||||||
|
onError={this.props.onError} /> :
|
||||||
|
|
||||||
|
view === 'notif' ?
|
||||||
|
<AccNotificationsView
|
||||||
messageSounds={this.props.messageSounds}
|
messageSounds={this.props.messageSounds}
|
||||||
desktopAlerts={this.props.desktopAlerts}
|
desktopAlerts={this.props.desktopAlerts}
|
||||||
desktopAlertsEnabled={this.props.desktopAlertsEnabled}
|
desktopAlertsEnabled={this.props.desktopAlertsEnabled}
|
||||||
incognitoMode={this.props.incognitoMode}
|
incognitoMode={this.props.incognitoMode}
|
||||||
onUpdateAccount={this.props.onUpdateAccount}
|
|
||||||
onUpdateTags={this.props.onUpdateAccountTags}
|
|
||||||
onTogglePushNotifications={this.props.onTogglePushNotifications}
|
onTogglePushNotifications={this.props.onTogglePushNotifications}
|
||||||
onToggleMessageSounds={this.props.onToggleMessageSounds}
|
onToggleMessageSounds={this.props.onToggleMessageSounds}
|
||||||
onToggleIncognitoMode={this.props.onToggleIncognitoMode}
|
onToggleIncognitoMode={this.props.onToggleIncognitoMode} /> :
|
||||||
onCredAdd={this.props.onCredAdd}
|
|
||||||
onCredDelete={this.props.onCredDelete}
|
view === 'security' ?
|
||||||
onCredConfirm={this.props.onCredConfirm}
|
<AccSecurityView
|
||||||
|
tinode={this.props.tinode}
|
||||||
|
onUpdateAccount={this.props.onUpdateAccount}
|
||||||
onLogout={this.props.onLogout}
|
onLogout={this.props.onLogout}
|
||||||
onCancel={this.props.onCancel}
|
onDeleteAccount={this.props.onDeleteAccount}
|
||||||
onError={this.props.onError} /> :
|
onShowAlert={this.props.onShowAlert} /> :
|
||||||
|
|
||||||
|
view === 'support' ?
|
||||||
|
<AccSupportView
|
||||||
|
serverAddress={this.props.serverAddress}
|
||||||
|
serverVersion={this.props.serverVersion} /> :
|
||||||
|
|
||||||
(view === 'contacts' || view == 'archive') ?
|
(view === 'contacts' || view == 'archive') ?
|
||||||
<ContactsView
|
<ContactsView
|
||||||
|
|||||||
@@ -92,13 +92,13 @@ class TinodeWeb extends React.Component {
|
|||||||
this.togglePushToken = this.togglePushToken.bind(this);
|
this.togglePushToken = this.togglePushToken.bind(this);
|
||||||
this.requestPushToken = this.requestPushToken.bind(this);
|
this.requestPushToken = this.requestPushToken.bind(this);
|
||||||
this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);
|
this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);
|
||||||
this.handleNewTopic = this.handleNewTopic.bind(this);
|
|
||||||
this.handleNewTopicRequest = this.handleNewTopicRequest.bind(this);
|
this.handleNewTopicRequest = this.handleNewTopicRequest.bind(this);
|
||||||
this.handleNewTopicCreated = this.handleNewTopicCreated.bind(this);
|
this.handleNewTopicCreated = this.handleNewTopicCreated.bind(this);
|
||||||
this.handleTopicUpdateRequest = this.handleTopicUpdateRequest.bind(this);
|
this.handleTopicUpdateRequest = this.handleTopicUpdateRequest.bind(this);
|
||||||
this.handleChangePermissions = this.handleChangePermissions.bind(this);
|
this.handleChangePermissions = this.handleChangePermissions.bind(this);
|
||||||
this.handleTagsUpdated = this.handleTagsUpdated.bind(this);
|
this.handleTagsUpdated = this.handleTagsUpdated.bind(this);
|
||||||
this.handleLogout = this.handleLogout.bind(this);
|
this.handleLogout = this.handleLogout.bind(this);
|
||||||
|
this.handleDeleteAccount = this.handleDeleteAccount.bind(this);
|
||||||
this.handleDeleteMessagesRequest = this.handleDeleteMessagesRequest.bind(this);
|
this.handleDeleteMessagesRequest = this.handleDeleteMessagesRequest.bind(this);
|
||||||
this.handleLeaveUnsubRequest = this.handleLeaveUnsubRequest.bind(this);
|
this.handleLeaveUnsubRequest = this.handleLeaveUnsubRequest.bind(this);
|
||||||
this.handleBlockTopicRequest = this.handleBlockTopicRequest.bind(this);
|
this.handleBlockTopicRequest = this.handleBlockTopicRequest.bind(this);
|
||||||
@@ -300,7 +300,8 @@ class TinodeWeb extends React.Component {
|
|||||||
const hash = HashNavigation.parseUrlHash(window.location.hash);
|
const hash = HashNavigation.parseUrlHash(window.location.hash);
|
||||||
if (hash.path && hash.path.length > 0) {
|
if (hash.path && hash.path.length > 0) {
|
||||||
// Left-side panel selector.
|
// Left-side panel selector.
|
||||||
if (['register','settings','edit','cred','reset','newtpk','archive','contacts',''].includes(hash.path[0])) {
|
if (['register','settings','edit','notif','security','support','general',
|
||||||
|
'cred','reset','newtpk','archive','contacts',''].includes(hash.path[0])) {
|
||||||
this.setState({sidePanelSelected: hash.path[0]});
|
this.setState({sidePanelSelected: hash.path[0]});
|
||||||
} else {
|
} else {
|
||||||
console.log("Unknown sidepanel view", hash.path[0]);
|
console.log("Unknown sidepanel view", hash.path[0]);
|
||||||
@@ -1007,7 +1008,13 @@ class TinodeWeb extends React.Component {
|
|||||||
// User clicked Cancel button in Setting or Sign Up panel.
|
// User clicked Cancel button in Setting or Sign Up panel.
|
||||||
handleSidepanelCancel() {
|
handleSidepanelCancel() {
|
||||||
const parsed = HashNavigation.parseUrlHash(window.location.hash);
|
const parsed = HashNavigation.parseUrlHash(window.location.hash);
|
||||||
parsed.path[0] = this.state.myUserId ? 'contacts' : '';
|
let path = '';
|
||||||
|
if (['security','support','general','notif'].includes(parsed.path[0])) {
|
||||||
|
path = 'edit';
|
||||||
|
} else if (this.state.myUserId) {
|
||||||
|
path = 'contacts';
|
||||||
|
}
|
||||||
|
parsed.path[0] = path;
|
||||||
if (parsed.params) {
|
if (parsed.params) {
|
||||||
delete parsed.params.code;
|
delete parsed.params.code;
|
||||||
delete parsed.params.method;
|
delete parsed.params.method;
|
||||||
@@ -1017,9 +1024,9 @@ class TinodeWeb extends React.Component {
|
|||||||
this.setState({errorText: '', errorLevel: null});
|
this.setState({errorText: '', errorLevel: null});
|
||||||
}
|
}
|
||||||
|
|
||||||
// User clicked a (+) menu item.
|
// Basic nagigator by hash value. No need to bind to this.
|
||||||
handleNewTopic() {
|
basicNavigator(hash) {
|
||||||
HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'newtpk'));
|
HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request to start a new topic. New P2P topic requires peer's name.
|
// Request to start a new topic. New P2P topic requires peer's name.
|
||||||
@@ -1122,6 +1129,12 @@ class TinodeWeb extends React.Component {
|
|||||||
HashNavigation.navigateTo('');
|
HashNavigation.navigateTo('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDeleteAccount() {
|
||||||
|
this.tinode.delCurrentUser().then((ctrl) => {
|
||||||
|
this.handleLogout();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handleDeleteMessagesRequest(topicName) {
|
handleDeleteMessagesRequest(topicName) {
|
||||||
const topic = this.tinode.getTopic(topicName);
|
const topic = this.tinode.getTopic(topicName);
|
||||||
if (!topic) {
|
if (!topic) {
|
||||||
@@ -1396,10 +1409,12 @@ class TinodeWeb extends React.Component {
|
|||||||
desktopAlertsEnabled={this.state.desktopAlertsEnabled}
|
desktopAlertsEnabled={this.state.desktopAlertsEnabled}
|
||||||
incognitoMode={this.state.incognitoMode}
|
incognitoMode={this.state.incognitoMode}
|
||||||
serverAddress={this.state.serverAddress}
|
serverAddress={this.state.serverAddress}
|
||||||
onGlobalSettings={this.handleGlobalSettings}
|
serverVersion={this.state.serverVersion}
|
||||||
|
|
||||||
|
onGlobalSettings={this.handleGlobalSettings}
|
||||||
onSignUp={this.handleNewAccount}
|
onSignUp={this.handleNewAccount}
|
||||||
onSettings={this.handleSettings}
|
onSettings={this.handleSettings}
|
||||||
|
onBasicNavigate={this.basicNavigator}
|
||||||
onLoginRequest={this.handleLoginRequest}
|
onLoginRequest={this.handleLoginRequest}
|
||||||
onCreateAccount={this.handleNewAccountRequest}
|
onCreateAccount={this.handleNewAccountRequest}
|
||||||
onUpdateAccount={this.handleUpdateAccountRequest}
|
onUpdateAccount={this.handleUpdateAccountRequest}
|
||||||
@@ -1412,8 +1427,9 @@ class TinodeWeb extends React.Component {
|
|||||||
onCredConfirm={this.handleCredConfirm}
|
onCredConfirm={this.handleCredConfirm}
|
||||||
onTopicSelected={this.handleTopicSelected}
|
onTopicSelected={this.handleTopicSelected}
|
||||||
onCreateTopic={this.handleNewTopicRequest}
|
onCreateTopic={this.handleNewTopicRequest}
|
||||||
onNewTopic={this.handleNewTopic}
|
|
||||||
onLogout={this.handleLogout}
|
onLogout={this.handleLogout}
|
||||||
|
onDeleteAccount={this.handleDeleteAccount}
|
||||||
|
onShowAlert={this.handleShowAlert}
|
||||||
onCancel={this.handleSidepanelCancel}
|
onCancel={this.handleSidepanelCancel}
|
||||||
onError={this.handleError}
|
onError={this.handleError}
|
||||||
onValidateCredentials={this.handleValidateCredentialsRequest}
|
onValidateCredentials={this.handleValidateCredentialsRequest}
|
||||||
|
|||||||
@@ -41,9 +41,10 @@ export default class AvatarUpload extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
// Randomize id value in case more than one AvatarUpload is shown
|
// Randomize id value in case more than one AvatarUpload is shown
|
||||||
// at the same time.
|
// at the same time.
|
||||||
var randId = "file-input-avatar-" + (Math.random() + '').substr(2);
|
const randId = 'file-input-avatar-' + (Math.random() + '').substr(2);
|
||||||
|
const className = 'avatar-upload' + (this.props.readOnly ? ' read-only' : '');
|
||||||
return (
|
return (
|
||||||
<div className="avatar-upload">
|
<div className={className}>
|
||||||
{this.props.readOnly || !this.state.dataUrl ?
|
{this.props.readOnly || !this.state.dataUrl ?
|
||||||
null :
|
null :
|
||||||
<a href="#" className="clear-avatar" onClick={(e) => {e.preventDefault(); this.props.onImageChanged(null);}}>
|
<a href="#" className="clear-avatar" onClick={(e) => {e.preventDefault(); this.props.onImageChanged(null);}}>
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// The <- button to be displayed in title bars.
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default class ButtonBack extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<a href="#" onClick={(e) => {e.preventDefault(); this.props.onBack();}}>
|
||||||
|
<i className="material-icons">arrow_back</i>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,9 @@ import React from 'react';
|
|||||||
export default class MenuCancel extends React.PureComponent {
|
export default class MenuCancel extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<a href="#" onClick={(e) => {e.preventDefault(); this.props.onCancel();}}><i className="material-icons">close</i></a>
|
<a href="#" onClick={(e) => {e.preventDefault(); this.props.onCancel();}}>
|
||||||
|
<i className="material-icons">close</i>
|
||||||
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import LetterTile from './letter-tile.jsx';
|
import LetterTile from './letter-tile.jsx';
|
||||||
import MenuCancel from './menu-cancel.jsx';
|
import ButtonBack from './button-back.jsx';
|
||||||
import MenuContacts from './menu-contacts.jsx';
|
import MenuContacts from './menu-contacts.jsx';
|
||||||
import MenuStart from './menu-start.jsx';
|
import MenuStart from './menu-start.jsx';
|
||||||
|
|
||||||
@@ -9,20 +9,22 @@ export default class SideNavbar extends React.PureComponent {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div id="side-caption-panel" className="caption-panel">
|
<div id="side-caption-panel" className="caption-panel">
|
||||||
<div id="self-avatar" className="avatar-box">
|
{this.props.onCancel ? <ButtonBack onBack={this.props.onCancel} /> : null}
|
||||||
<LetterTile
|
{this.props.avatar ?
|
||||||
avatar={this.props.avatar}
|
<div id="self-avatar" className="avatar-box">
|
||||||
topic={this.props.myUserId}
|
<LetterTile
|
||||||
title={this.props.title} />
|
avatar={this.props.avatar}
|
||||||
</div>
|
topic={this.props.myUserId}
|
||||||
|
title={this.props.title} />
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
null}
|
||||||
<div id="sidepanel-title" className="panel-title">{this.props.title}</div>
|
<div id="sidepanel-title" className="panel-title">{this.props.title}</div>
|
||||||
{this.props.state === 'login' ?
|
{this.props.state === 'login' ?
|
||||||
<MenuStart onSignUp={this.props.onSignUp} onSettings={this.props.onSettings} /> :
|
<MenuStart onSignUp={this.props.onSignUp} onSettings={this.props.onSettings} /> :
|
||||||
this.props.state === 'contacts' ?
|
this.props.state === 'contacts' ?
|
||||||
<MenuContacts onNewTopic={this.props.onNewTopic} onSettings={this.props.onSettings} /> :
|
<MenuContacts onNewTopic={this.props.onNewTopic} onSettings={this.props.onSettings} /> :
|
||||||
null}
|
null}
|
||||||
{this.props.onCancel ?
|
|
||||||
<MenuCancel onCancel={this.props.onCancel} /> : null}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+772
-423
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+14
-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
Reference in New Issue
Block a user