add alert for destructable actions

This commit is contained in:
or-else
2019-09-24 17:12:38 +03:00
parent f3420dc66f
commit f9a29ed3ef
21 changed files with 723 additions and 301 deletions
+80 -38
View File
@@ -9,9 +9,9 @@
/* Basic elements defined */ /* Basic elements defined */
html, body { html, body {
height:100%; height: 100%;
width:100%; width: 100%;
font-size: 10pt; font-size: 10pt; /* 13.33 px/rem */
color: #666; color: #666;
overflow: hidden; overflow: hidden;
background-color: silver; background-color: silver;
@@ -82,9 +82,9 @@ img.avatar {
button { button {
display: inline-block; display: inline-block;
font-family: inherit; font-family: inherit;
font-size: 1.1rem; font-size: 1.05rem;
height: 2.2em; height: 2.2rem;
line-height: 2.2em; line-height: 2.2rem;
font-weight: normal; font-weight: normal;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
@@ -92,11 +92,13 @@ button {
cursor: pointer; cursor: pointer;
border: 1px solid transparent; border: 1px solid transparent;
white-space: nowrap; white-space: nowrap;
padding-left: 2rem; padding-left: 1.2rem;
padding-right: 2rem; padding-right: 1.2rem;
border-radius: 2px; border-radius: 4px;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.25); box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.25);
text-transform: uppercase; text-transform: uppercase;
min-width: 5rem;
margin: 0 0.35rem 0 0.35rem;
} }
button[disabled], button[disabled],
@@ -148,7 +150,7 @@ button.blue {
color: white; color: white;
} }
button.blue:focus, button.blue:hover, button.blue:active { button.blue:focus, button.blue:hover, button.blue:active {
background-color: #1976d2; background-color: #2962ff;
} }
button.white { button.white {
@@ -159,6 +161,16 @@ button.white:focus, button.white:hover, button.blue:active {
background-color: #e3f2fd; background-color: #e3f2fd;
} }
button.outline {
background-color: transparent;
color: #2196f3; /* blue 500 */
border: 1px solid silver;
box-shadow: none;
}
button.outline:focus, button.outline:hover, button.outline:active {
background-color: #e8eaf6; /* indigo 50 */
}
textarea, input { textarea, input {
font-family: inherit; font-family: inherit;
font-size: 1.1rem; font-size: 1.1rem;
@@ -303,6 +315,40 @@ li.right:hover .menuTrigger {
/* END popup/dropdown menu */ /* END popup/dropdown menu */
/* Begin Alert: modal window with message and buttons. */
div.alert-container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
position: absolute;
-x-backdrop-filter: blur(4px);
background: rgba(0,0,0,0.33);
z-index: 3;
}
div.alert {
background-color: white;
box-shadow: 0 1px 6px rgba(0, 0, 0, .45);
padding: 1rem;
border-radius: 4px;
margin: auto 2rem auto 2rem;
width: content;
min-width: 16rem;
max-width: 24rem;
}
div.alert .title {
color: #2196f3;
font-size: 1.2rem;
font-weight: 500;
width: 100%;
padding: 0 0 0.35rem 0;
}
div.alert .content {
padding: 0.5rem 0 0.5rem 0;
}
/* END Alert */
/* Generic text colors */ /* Generic text colors */
.blue { .blue {
color: #2196f3; /* blue 500 */ color: #2196f3; /* blue 500 */
@@ -354,7 +400,7 @@ li.right:hover .menuTrigger {
} }
/* Top-level application layout style: sidepanel + main view */ /* Top-level application layout style: sidepanel + main view */
#noscript-alertbox { #noscript-message {
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -659,57 +705,57 @@ i.search {
} }
/* Area which displays an error message */ /* Area which displays an error message */
.alert-box { .info-box {
position: relative; position: relative;
display: none; display: none;
overflow: hidden; overflow: hidden;
padding: 1rem 1.5rem 1rem 0.5rem; padding: 1rem 1.5rem 1rem 0.5rem;
} }
.alert-box .cancel { .info-box .cancel {
position: absolute; position: absolute;
right: 0.25rem; right: 0.25rem;
top: 0.5rem; top: 0.5rem;
} }
.alert-box.error { .info-box.error {
display: flex; display: flex;
background-color: #ffcdd2; background-color: #ffcdd2;
border-bottom: 1px solid #ffbfc8; border-bottom: 1px solid #ffbfc8;
color: #b71c1c; color: #b71c1c;
} }
.alert-box.warning { .info-box.warning {
display: flex; display: flex;
background-color: #fff9c4; background-color: #fff9c4;
border-bottom: 1px solid #fff59d; border-bottom: 1px solid #fff59d;
color: #6d4c41; color: #6d4c41;
} }
.alert-box.info { .info-box.info {
display: flex; display: flex;
background-color: #e1f5fe; background-color: #e1f5fe;
border-bottom: 1px solid #b3e5fc; border-bottom: 1px solid #b3e5fc;
color: #0288d1; color: #0288d1;
} }
.alert-box .cancel .material-icons { .info-box .cancel .material-icons {
font-size: 1rem; font-size: 1rem;
color: #757575; color: #757575;
} }
.alert-box.error .cancel .material-icons { .info-box.error .cancel .material-icons {
color: #b71c1c; color: #b71c1c;
} }
.alert-box .icon { .info-box .icon {
display: block; display: block;
margin-right: 0.5rem; margin-right: 0.5rem;
} }
.alert-box .icon .material-icons { .info-box .icon .material-icons {
font-size: 1.5rem; font-size: 1.5rem;
} }
.alert-box.warning .icon .material-icons { .info-box.warning .icon .material-icons {
color: #fbc02d; color: #fbc02d;
} }
.alert-box.error .icon .material-icons { .info-box.error .icon .material-icons {
color: #ef5350; color: #ef5350;
} }
.alert-box.info .icon .material-icons { .info-box.info .icon .material-icons {
color: #0288d1; color: #0288d1;
} }
/* List of contacts */ /* List of contacts */
@@ -876,7 +922,6 @@ i.search {
.panel-form-row { .panel-form-row {
display: flex; display: flex;
justify-content: flex-start;
align-items: center; align-items: center;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
line-height: 1.5; line-height: 1.5;
@@ -897,13 +942,12 @@ i.search {
the top level panel */ the top level panel */
.panel-form-row > .panel-form-column, .panel-form-row > .panel-form-column,
.panel-form-column > .panel-form-column { .panel-form-column > .panel-form-column {
padding: 0!important; padding: 0!important;
flex-grow: 1; flex-grow: 1;
} }
.panel-form-column > .panel-form-row, .panel-form-column > .panel-form-row,
.panel-form-row > .panel-form-row .panel-form-row > .panel-form-row {
{ padding: 0!important;
padding: 0!important;
} }
/* Styling of avatar upload control */ /* Styling of avatar upload control */
@@ -1560,15 +1604,13 @@ ul.tabbar li.active > a:after {
/* Badge with a label, like [you], [muted], etc */ /* Badge with a label, like [you], [muted], etc */
.badge { .badge {
font-family: 'Roboto Mono', Courier, monospace, sans-serif; font-family: 'Roboto Mono', Courier, monospace, sans-serif;
font-size: 0.75rem; font-size: 0.8rem;
border: 1px solid #666; border: 1px solid silver;
background-color: #eee; background-color: #e8e8e8;
color: #666; color: #666;
padding: 0 0.15rem 0 0.15rem; padding: 0.05rem 0.25rem 0.05rem 0.25rem;
margin-left: 0.1rem; margin: 0.05rem 0.1rem 0.05rem 0.1rem;
margin-right: 0.1rem; border-radius: 0.2rem;
margin-bottom: 0.1rem;
border-radius: 0.15rem;
display: inline-block; display: inline-block;
align-self: flex-start; align-self: flex-start;
} }
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -38,7 +38,7 @@
</head> </head>
<body> <body>
<noscript><div id="noscript-alertbox"><div id="noscript-title">Javascript support is required.</div> <noscript><div id="noscript-message"><div id="noscript-title">Javascript support is required.</div>
<div id="noscript-content">It appears JavaScript is turned off or is not supported by your browser. <div id="noscript-content">It appears JavaScript is turned off or is not supported by your browser.
TinodeWeb does not work without JavaScript. Please enable JavaScript by changing your browser options, TinodeWeb does not work without JavaScript. Please enable JavaScript by changing your browser options,
then <a href="">try again</a>. then <a href="">try again</a>.
+1 -1
View File
@@ -27,7 +27,7 @@
<script crossorigin="anonymous" src="https://unpkg.com/firebase@6.4.2/firebase-messaging.js"></script> <script crossorigin="anonymous" src="https://unpkg.com/firebase@6.4.2/firebase-messaging.js"></script>
</head> </head>
<body> <body>
<noscript><div id="noscript-alertbox"><div id="noscript-title">Javascript support is required.</div> <noscript><div id="noscript-message"><div id="noscript-title">Javascript support is required.</div>
<div id="noscript-content">It appears JavaScript is turned off or is not supported by your browser. <div id="noscript-content">It appears JavaScript is turned off or is not supported by your browser.
TinodeWeb does not work without JavaScript. Please enable JavaScript by changing your browser options, TinodeWeb does not work without JavaScript. Please enable JavaScript by changing your browser options,
then <a href="">try again</a>.</div></div></noscript> then <a href="">try again</a>.</div></div></noscript>
+8 -5
View File
@@ -24,6 +24,14 @@
"granted_permissions": "Granted", "granted_permissions": "Granted",
"menu_item_edit_permissions": "Edit permissions", "menu_item_edit_permissions": "Edit permissions",
"label_other_user": "Other", "label_other_user": "Other",
"action_delete_messages": "Delete Messages",
"delete_messages_warning": "Are you sure you want to delete all mesages? It cannot be undone.",
"action_leave_chat": "Leave Conversation",
"leave_chat_warning": "Are you sure you want to leave this conversation?",
"action_block_contact": "Block Contact",
"block_contact_warning": "Are you sure you want to block this contact?",
"action_report_chat": "Report Conversation",
"report_chat_warning": "Are you sure you want to block and report this conversation?",
"title_info": "Info", "title_info": "Info",
"label_topic_name": "Name", "label_topic_name": "Name",
"label_private": "Private comment", "label_private": "Private comment",
@@ -34,11 +42,6 @@
"label_permissions": "Permissions:", "label_permissions": "Permissions:",
"label_you": "You:", "label_you": "You:",
"label_default_access": "Default access mode:", "label_default_access": "Default access mode:",
"action_delete_messages": "Delete Messages",
"action_leave_chat": "Leave Conversation",
"action_report_group": "Report Group",
"action_block_contact": "Block Contact",
"action_report_contact": "Report Contact",
"label_group_members": "Group members:", "label_group_members": "Group members:",
"button_add_members": "Add members", "button_add_members": "Add members",
"group_has_no_members": "No members", "group_has_no_members": "No members",
+5 -2
View File
@@ -140,6 +140,9 @@
"link_privacy_policy": "Политика конфиденциальности", "link_privacy_policy": "Политика конфиденциальности",
"action_delete_messages": "Удалить сообщения", "action_delete_messages": "Удалить сообщения",
"action_block_contact": "Заблокировать контакт", "action_block_contact": "Заблокировать контакт",
"action_report_contact": "Сообщить о нарушении", "action_report_chat": "Сообщить о нарушении",
"action_report_group": "Сообщить о нарушении" "delete_messages_warning": "Вы действительно хотите удалить все сообщения?",
"leave_chat_warning": "Вы действительно хотите покинуть этот чат?",
"block_contact_warning": "Вы действительно заблокировать этот контакт?",
"report_chat_warning": "Вы действительно хотите сообщить о нарушении и заблокировать этот чат?"
} }
+5 -2
View File
@@ -140,6 +140,9 @@
"link_privacy_policy": "隐私政策", "link_privacy_policy": "隐私政策",
"action_delete_messages": "删除所有帖子", "action_delete_messages": "删除所有帖子",
"action_block_contact": "屏蔽联系人", "action_block_contact": "屏蔽联系人",
"action_report_contact": "检举垃圾邮件", "delete_messages_warning": ":!![MISSING]!!:Are you sure you want to delete all mesages? It cannot be undone.",
"action_report_group": "检举垃圾邮件" "leave_chat_warning": ":!![MISSING]!!:Are you sure you want to leave this conversation.",
"block_contact_warning": ":!![MISSING]!!:Are you sure you want to block this contact?",
"action_report_chat": "检举垃圾邮件",
"report_chat_warning": ":!![MISSING]!!:Are you sure you want to block and report this conversation?"
} }
+14 -9
View File
@@ -25,6 +25,14 @@
"granted_permissions": "Granted", "granted_permissions": "Granted",
"menu_item_edit_permissions": "Edit permissions", "menu_item_edit_permissions": "Edit permissions",
"label_other_user": "Other", "label_other_user": "Other",
"action_delete_messages": "Delete Messages",
"delete_messages_warning": "Are you sure you want to delete all mesages? It cannot be undone.",
"action_leave_chat": "Leave Conversation",
"leave_chat_warning": "Are you sure you want to leave this conversation?",
"action_block_contact": "Block Contact",
"block_contact_warning": "Are you sure you want to block this contact?",
"action_report_chat": "Report Conversation",
"report_chat_warning": "Are you sure you want to block and report this conversation?",
"title_info": "Info", "title_info": "Info",
"label_topic_name": "Name", "label_topic_name": "Name",
"label_private": "Private comment", "label_private": "Private comment",
@@ -35,11 +43,6 @@
"label_permissions": "Permissions:", "label_permissions": "Permissions:",
"label_you": "You:", "label_you": "You:",
"label_default_access": "Default access mode:", "label_default_access": "Default access mode:",
"action_delete_messages": "Delete Messages",
"action_leave_chat": "Leave Conversation",
"action_report_group": "Report Group",
"action_block_contact": "Block Contact",
"action_report_contact": "Report Contact",
"label_group_members": "Group members:", "label_group_members": "Group members:",
"button_add_members": "Add members", "button_add_members": "Add members",
"group_has_no_members": "No members", "group_has_no_members": "No members",
@@ -286,8 +289,11 @@
"link_privacy_policy": "Политика конфиденциальности", "link_privacy_policy": "Политика конфиденциальности",
"action_delete_messages": "Удалить сообщения", "action_delete_messages": "Удалить сообщения",
"action_block_contact": "Заблокировать контакт", "action_block_contact": "Заблокировать контакт",
"action_report_contact": "Сообщить о нарушении", "action_report_chat": "Сообщить о нарушении",
"action_report_group": "Сообщить о нарушении" "delete_messages_warning": "Вы действительно хотите удалить все сообщения?",
"leave_chat_warning": "Вы действительно хотите покинуть этот чат?",
"block_contact_warning": "Вы действительно заблокировать этот контакт?",
"report_chat_warning": "Вы действительно хотите сообщить о нарушении и заблокировать этот чат?"
}, },
"zh": { "zh": {
"archived_contacts": "已归档联系人 ({count})", "archived_contacts": "已归档联系人 ({count})",
@@ -431,7 +437,6 @@
"link_privacy_policy": "隐私政策", "link_privacy_policy": "隐私政策",
"action_delete_messages": "删除所有帖子", "action_delete_messages": "删除所有帖子",
"action_block_contact": "屏蔽联系人", "action_block_contact": "屏蔽联系人",
"action_report_contact": "检举垃圾邮件", "action_report_chat": "检举垃圾邮件"
"action_report_group": "检举垃圾邮件"
} }
} }
+1 -1
View File
@@ -291,7 +291,7 @@ export default class EditAccountView extends React.Component {
<div className="hr" /> <div className="hr" />
<div className="panel-form-column"> <div className="panel-form-column">
<a href="#" className="red flat-button" onClick={(e) => {e.preventDefault(); this.props.onLogout();}}> <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" <i className="material-icons">exit_to_app</i> &nbsp;<FormattedMessage id="button_logout"
defaultMessage="Logout" description="Button [Logout]" /> defaultMessage="Logout" description="Button [Logout]" />
</a> </a>
</div> </div>
+106 -21
View File
@@ -40,7 +40,47 @@ const messages = defineMessages({
id: 'label_other_user', id: 'label_other_user',
defaultMessage: 'Other', defaultMessage: 'Other',
description: 'Label for the other user when the user is unnamed' description: 'Label for the other user when the user is unnamed'
} },
delete_messages: {
id: "action_delete_messages",
defaultMessage: "Delete Messages",
description: "Flat button [Delete Messages]"
},
delete_messages_warning: {
id: "delete_messages_warning",
defaultMessage: "Are you sure you want to delete all mesages? It cannot be undone.",
description: "Alert dialog warning when deleting all messages."
},
leave_chat: {
id: "action_leave_chat",
defaultMessage: "Leave Conversation",
description: "Flat button [Leave Conversation]"
},
leave_chat_warning: {
id: "leave_chat_warning",
defaultMessage: "Are you sure you want to leave this conversation?",
description: "Alert dialog warning when unsubscribing from a chat."
},
block_contact: {
id: "action_block_contact",
defaultMessage: "Block Contact",
description: "Flat button [Block Contact]"
},
block_contact_warning: {
id: "block_contact_warning",
defaultMessage: "Are you sure you want to block this contact?",
description: "Alert dialog warning when blocking a contact."
},
report_chat: {
id: "action_report_chat",
defaultMessage: "Report Conversation",
description: "Flat button [Report Group]"
},
report_chat_warning: {
id: "report_chat_warning",
defaultMessage: "Are you sure you want to block and report this conversation?",
description: "Alert dialog warning when reporting a conversation for abuse"
},
}); });
class InfoView extends React.Component { class InfoView extends React.Component {
@@ -91,7 +131,10 @@ class InfoView extends React.Component {
this.handleShowAddMembers = this.handleShowAddMembers.bind(this); this.handleShowAddMembers = this.handleShowAddMembers.bind(this);
this.handleHideAddMembers = this.handleHideAddMembers.bind(this); this.handleHideAddMembers = this.handleHideAddMembers.bind(this);
this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this); this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);
this.handleDeleteMessages = this.handleDeleteMessages.bind(this);
this.handleLeave = this.handleLeave.bind(this); this.handleLeave = this.handleLeave.bind(this);
this.handleBlock = this.handleBlock.bind(this);
this.handleReport = this.handleReport.bind(this);
this.handleMemberSelected = this.handleMemberSelected.bind(this); this.handleMemberSelected = this.handleMemberSelected.bind(this);
this.handleMoreInfo = this.handleMoreInfo.bind(this); this.handleMoreInfo = this.handleMoreInfo.bind(this);
this.handleTagsUpdated = this.handleTagsUpdated.bind(this); this.handleTagsUpdated = this.handleTagsUpdated.bind(this);
@@ -354,9 +397,56 @@ class InfoView extends React.Component {
this.setState({showMemberPanel: false}); this.setState({showMemberPanel: false});
} }
handleDeleteMessages(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
this.props.onShowAlert(
formatMessage(messages.delete_messages), // title
formatMessage(messages.delete_messages_warning), // content
(() => { this.props.onDeleteMessages(this.props.topic); }), // onConfirm
null, // "OK"
true, // Show Reject button
null // "Cancel"
);
}
handleLeave(e) { handleLeave(e) {
e.preventDefault(); e.preventDefault();
this.props.onLeaveTopic(this.props.topic); const {formatMessage} = this.props.intl;
this.props.onShowAlert(
formatMessage(messages.leave_chat), // title
formatMessage(messages.leave_chat_warning), // content
(() => { this.props.onLeaveTopic(this.props.topic); }), // onConfirm
null, // "OK"
true, // Show Reject button
null // "Cancel"
);
}
handleBlock(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
this.props.onShowAlert(
formatMessage(messages.block_contact), // title
formatMessage(messages.block_contact_warning), // content
(() => { this.props.onBlockTopic(this.props.topic); }), // onConfirm
null, // "OK"
true, // Show Reject button
null // "Cancel"
);
}
handleReport(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
this.props.onShowAlert(
formatMessage(messages.report_chat), // title
formatMessage(messages.report_chat_warning), // content
(() => { this.props.onReportTopic(this.props.topic); }), // onConfirm
null, // "OK"
true, // Show Reject button
null // "Cancel"
);
} }
handleMemberSelected(uid) { handleMemberSelected(uid) {
@@ -591,29 +681,24 @@ class InfoView extends React.Component {
} }
<div className="panel-form-column"> <div className="panel-form-column">
<a href="#" className="flat-button" onClick={this.handleDeleteMessages}> <a href="#" className="flat-button" onClick={this.handleDeleteMessages}>
<i className="material-icons">delete_outline</i> &npbs;<FormattedMessage id="action_delete_messages" <i className="material-icons">delete_outline</i> &nbsp;{formatMessage(messages.delete_messages)}
defaultMessage="Delete Messages" description="Flat button [Delete Messages]" />
</a> </a>
<a href="#" className="red flat-button" onClick={this.handleLeave}> <a href="#" className="red flat-button" onClick={this.handleLeave}>
<i className="material-icons">exit_to_app</i> &npbs;<FormattedMessage id="action_leave_chat" <i className="material-icons">exit_to_app</i> &nbsp;{formatMessage(messages.leave_chat)}
defaultMessage="Leave Conversation" description="Flat button [Leave Conversation]" />
</a> </a>
{this.state.groupTopic ? {!this.state.groupTopic ?
<a href="#" className="red flat-button" onClick={this.handleReport}> <a href="#" className="red flat-button" onClick={this.handleBlock}>
<i className="material-icons">report</i> &npbs;<FormattedMessage id="action_report_group" <i className="material-icons">block</i> &nbsp;{formatMessage(messages.block_contact)}
defaultMessage="Report Group" description="Flat button [Report Group]" />
</a> </a>
: :
<> null
<a href="#" className="red flat-button" onClick={this.handleBlock}> }
<i className="material-icons">block</i> &npbs;<FormattedMessage id="action_block_contact" {!this.state.owner ?
defaultMessage="Block Contact" description="Flat button [Block Contact]" /> <a href="#" className="red flat-button" onClick={this.handleReport}>
</a> <i className="material-icons">report</i> &nbsp;{formatMessage(messages.report_chat)}
<a href="#" className="red flat-button" onClick={this.handleReport}> </a>
<i className="material-icons">report</i> &npbs;<FormattedMessage id="action_report_contact" :
defaultMessage="Report Contact" description="Flat button [Report Contact]" /> null
</a>
</>
} }
</div> </div>
{this.state.groupTopic ? {this.state.groupTopic ?
+82 -7
View File
@@ -8,6 +8,7 @@ import 'firebase/messaging';
import Tinode from 'tinode-sdk'; import Tinode from 'tinode-sdk';
import Alert from '../widgets/alert.jsx';
import ContextMenu from '../widgets/context-menu.jsx'; import ContextMenu from '../widgets/context-menu.jsx';
import InfoView from './info-view.jsx'; import InfoView from './info-view.jsx';
@@ -98,11 +99,14 @@ class TinodeWeb extends React.Component {
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.handleDeleteMessagesRequest = this.handleDeleteMessagesRequest.bind(this);
this.handleLeaveUnsubRequest = this.handleLeaveUnsubRequest.bind(this); this.handleLeaveUnsubRequest = this.handleLeaveUnsubRequest.bind(this);
this.handleDialogCancel = this.handleDialogCancel.bind(this); this.handleBlockTopicRequest = this.handleBlockTopicRequest.bind(this);
this.handleReportTopic = this.handleReportTopic.bind(this);
this.handleShowContextMenu = this.handleShowContextMenu.bind(this); this.handleShowContextMenu = this.handleShowContextMenu.bind(this);
this.defaultTopicContextMenu = this.defaultTopicContextMenu.bind(this); this.defaultTopicContextMenu = this.defaultTopicContextMenu.bind(this);
this.handleHideContextMenu = this.handleHideContextMenu.bind(this); this.handleHideContextMenu = this.handleHideContextMenu.bind(this);
this.handleShowAlert = this.handleShowAlert.bind(this);
this.handleShowInfoView = this.handleShowInfoView.bind(this); this.handleShowInfoView = this.handleShowInfoView.bind(this);
this.handleHideInfoView = this.handleHideInfoView.bind(this); this.handleHideInfoView = this.handleHideInfoView.bind(this);
this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this); this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);
@@ -138,8 +142,7 @@ class TinodeWeb extends React.Component {
sidePanelSelected: 'login', sidePanelSelected: 'login',
sidePanelTitle: null, sidePanelTitle: null,
sidePanelAvatar: null, sidePanelAvatar: null,
dialogSelected: null,
contextMenuVisible: false,
login: '', login: '',
password: '', password: '',
myUserId: null, myUserId: null,
@@ -159,6 +162,10 @@ class TinodeWeb extends React.Component {
contextMenuParams: null, contextMenuParams: null,
contextMenuItems: [], contextMenuItems: [],
// Modal alert dialog.
alertVisible: false,
alertParams: {},
// Chats as shown in the ContactsView // Chats as shown in the ContactsView
chatList: [], chatList: [],
// Contacts returned by a search query. // Contacts returned by a search query.
@@ -419,7 +426,6 @@ class TinodeWeb extends React.Component {
connected: false, connected: false,
ready: false, ready: false,
topicSelectedOnline: false, topicSelectedOnline: false,
dialogSelected: null,
errorText: err && err.message ? err.message : "Disconnected", errorText: err && err.message ? err.message : "Disconnected",
errorLevel: err && err.message ? 'err' : 'warn', errorLevel: err && err.message ? 'err' : 'warn',
loginDisabled: false, loginDisabled: false,
@@ -1080,6 +1086,18 @@ class TinodeWeb extends React.Component {
HashNavigation.navigateTo(''); HashNavigation.navigateTo('');
} }
handleDeleteMessagesRequest(topicName) {
const topic = this.tinode.getTopic(topicName);
if (!topic) {
return;
}
// Request hard-delete all messages.
topic.delMessagesAll(true).catch((err) => {
this.handleError(err.message, 'err');
});
}
handleLeaveUnsubRequest(topicName) { handleLeaveUnsubRequest(topicName) {
const topic = this.tinode.getTopic(topicName); const topic = this.tinode.getTopic(topicName);
if (!topic) { if (!topic) {
@@ -1094,8 +1112,39 @@ class TinodeWeb extends React.Component {
}); });
} }
handleDialogCancel() { handleBlockTopicRequest(topicName) {
this.setState({dialogSelected: null}); const topic = this.tinode.getTopic(topicName);
if (!topic) {
return;
}
topic.updateMode(null, '-JP').then((ctrl) => {
// Hide MessagesView and InfoView panels.
HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));
}).catch((err) => {
this.handleError(err.message, 'err');
});
}
handleReportTopic(topicName) {
const topic = this.tinode.getTopic(topicName);
if (!topic) {
return;
}
// Publish spam report.
this.tinode.publish(Tinode.TOPIC_SYS, Tinode.Drafty.attachJSON(null, {
'action': 'report',
'target': topicName
}));
// Remove J and P permissions.
topic.updateMode(null, '-JP').then((ctrl) => {
// Hide MessagesView and InfoView panels.
HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));
}).catch((err) => {
this.handleError(err.message, 'err');
});
} }
handleShowContextMenu(params, menuItems) { handleShowContextMenu(params, menuItems) {
@@ -1156,6 +1205,20 @@ class TinodeWeb extends React.Component {
} }
} }
handleShowAlert(title, content, onConfirm, confirm, onReject, reject) {
this.setState({
alertVisible: true,
alertParams: {
title: title,
content: content,
onConfirm: onConfirm,
confirm: confirm,
onReject: onReject,
reject: reject
}
});
}
handleShowInfoView() { handleShowInfoView() {
HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'info', true)); HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'info', true));
this.setState({showInfoPanel: true}); this.setState({showInfoPanel: true});
@@ -1258,7 +1321,15 @@ class TinodeWeb extends React.Component {
: :
null null
} }
<Alert
visible={this.state.alertVisible}
title={this.state.alertParams.title}
content={this.state.alertParams.content}
onReject={this.state.alertParams.onReject ? (() => { this.setState({alertVisible: false}); }) : null}
reject={this.state.alertParams.reject}
onConfirm={() => { this.setState({alertVisible: false}); this.state.alertParams.onConfirm(); }}
confirm={this.state.alertParams.confirm}
/>
<SidepanelView <SidepanelView
tinode={this.tinode} tinode={this.tinode}
connected={this.state.connected} connected={this.state.connected}
@@ -1365,9 +1436,13 @@ class TinodeWeb extends React.Component {
onTopicDescUpdate={this.handleTopicUpdateRequest} onTopicDescUpdate={this.handleTopicUpdateRequest}
onCancel={this.handleHideInfoView} onCancel={this.handleHideInfoView}
onShowAlert={this.handleShowAlert}
onChangePermissions={this.handleChangePermissions} onChangePermissions={this.handleChangePermissions}
onMemberUpdateRequest={this.handleMemberUpdateRequest} onMemberUpdateRequest={this.handleMemberUpdateRequest}
onDeleteMessages={this.handleDeleteMessagesRequest}
onLeaveTopic={this.handleLeaveUnsubRequest} onLeaveTopic={this.handleLeaveUnsubRequest}
onBlockTopic={this.handleBlockTopicRequest}
onReportTopic={this.handleReportTopic}
onAddMember={this.handleManageGroupMembers} onAddMember={this.handleManageGroupMembers}
onTopicTagsUpdate={this.handleTagsUpdated} onTopicTagsUpdate={this.handleTagsUpdated}
onInitFind={this.tnInitFind} onInitFind={this.tnInitFind}
+5 -4
View File
@@ -44,7 +44,7 @@ export default class ChipInput extends React.Component {
static indexChips(chips) { static indexChips(chips) {
const index = {}; const index = {};
let count = 0; let count = 0;
chips.map(function(item) { chips.map((item) => {
index[item.user] = count; index[item.user] = count;
count ++; count ++;
}); });
@@ -55,8 +55,8 @@ export default class ChipInput extends React.Component {
static sortChips(chips, keep) { static sortChips(chips, keep) {
const required = []; const required = [];
const normal = []; const normal = [];
chips.map(function(item) { chips.map((item) => {
if (keep.includes(item.user)) { if (keep && keep.includes(item.user)) {
required.push(item); required.push(item);
} else { } else {
normal.push(item); normal.push(item);
@@ -114,6 +114,7 @@ export default class ChipInput extends React.Component {
render() { render() {
const chips = []; const chips = [];
let count = 0; let count = 0;
const staticMembers = this.props.staticMembers || [];
this.state.sortedChips.map((item) => { this.state.sortedChips.map((item) => {
chips.push( chips.push(
<Chip <Chip
@@ -122,7 +123,7 @@ export default class ChipInput extends React.Component {
title={item.public ? item.public.fn : undefined} title={item.public ? item.public.fn : undefined}
noAvatar={this.props.avatarDisabled} noAvatar={this.props.avatarDisabled}
topic={item.user} topic={item.user}
required={this.props.staticMembers.includes(item.user)} required={staticMembers.includes(item.user)}
invalid={item.invalid} invalid={item.invalid}
index={count} index={count}
key={item.user} /> key={item.user} />
+2 -14
View File
@@ -277,7 +277,7 @@ class ContextMenu extends React.Component {
return; return;
} }
// Can't cancel. Delete instead. // Can't cancel. Delete instead.
let promise = all ? const promise = all ?
topic.delMessagesAll(hard) : topic.delMessagesAll(hard) :
topic.delMessagesList([params.seq], hard); topic.delMessagesList([params.seq], hard);
@@ -296,19 +296,7 @@ class ContextMenu extends React.Component {
return; return;
} }
let am, user; let result = topic.updateMode(params.user, mode);
if (params.user) {
user = topic.subscriber(params.user);
if (!user) {
console.log("Subscriber not found", params.topicName + "[" + params.user + "]");
return;
}
am = user.acs.updateGiven(mode).getGiven();
} else {
am = topic.getAccessMode().updateWant(mode).getWant();
}
let result = topic.setMeta({sub: {user: params.user, mode: am}});
if (errorHandler) { if (errorHandler) {
result = result.catch((err) => { result = result.catch((err) => {
errorHandler(err.message, 'err'); errorHandler(err.message, 'err');
+1 -1
View File
@@ -31,7 +31,7 @@ export default class ErrorPanel extends React.PureComponent {
render() { render() {
const icons = {err: 'error', warn: 'warning', info: 'info'} const icons = {err: 'error', warn: 'warning', info: 'info'}
const level = icons[this.props.level] || ''; const level = icons[this.props.level] || '';
const className = 'alert-box ' + level; const className = 'info-box ' + level;
return ( return (
<div className={className}> <div className={className}>
<div className="icon"><i className="material-icons">{level}</i></div> <div className="icon"><i className="material-icons">{level}</i></div>
+3 -3
View File
@@ -150,12 +150,12 @@ class PermissionsEditor extends React.Component {
</tbody></table> </tbody></table>
<br /> <br />
<div className="dialog-buttons"> <div className="dialog-buttons">
<button className="outline" onClick={this.handleCancel}>
<FormattedMessage id="button_cancel" />
</button>
<button className="blue" onClick={this.handleSubmit}> <button className="blue" onClick={this.handleSubmit}>
<FormattedMessage id="button_ok" /> <FormattedMessage id="button_ok" />
</button> </button>
<button className="white" onClick={this.handleCancel}>
<FormattedMessage id="button_cancel" />
</button>
</div> </div>
</div> </div>
); );
+4 -4
View File
@@ -121,13 +121,13 @@ export default class TagManager extends React.Component {
filterFunc={this.handleTagInput} /> filterFunc={this.handleTagInput} />
}</FormattedMessage> }</FormattedMessage>
{this.props.onSubmit || this.props.onCancel ? {this.props.onSubmit || this.props.onCancel ?
<div id="tag-manager-buttons" className="panel-form-row"> <div id="tag-manager-buttons" className="dialog-buttons panel-form-row">
<button className="outline" onClick={this.handleCancel}>
<FormattedMessage id="button_cancel" defautMessage="Cancel" description="Rejection button [Cancel]" />
</button>
<button className="blue" onClick={this.handleSubmit}> <button className="blue" onClick={this.handleSubmit}>
<FormattedMessage id="button_ok" defautMessage="OK" description="Confirmation button [OK]" /> <FormattedMessage id="button_ok" defautMessage="OK" description="Confirmation button [OK]" />
</button> </button>
<button className="white" onClick={this.handleCancel}>
<FormattedMessage id="button_cancel" defautMessage="Cancel" description="Rejection button [Cancel]" />
</button>
</div> </div>
: null} : null}
</div> </div>
+399 -182
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
+2 -2
View File
@@ -39,8 +39,8 @@ module.exports = (env, argv) => {
] ]
}, },
performance: { performance: {
maxEntrypointSize: 250000, maxEntrypointSize: 262144,
maxAssetSize: 250000 maxAssetSize: 262144
}, },
externals: { externals: {
'react': 'React', 'react': 'React',