refactoring for better topic and account editing

This commit is contained in:
or-else
2021-08-05 16:32:13 -07:00
parent dafc3161c5
commit 642821db9f
24 changed files with 759 additions and 611 deletions
+32 -18
View File
@@ -20,6 +20,7 @@ html, body {
tt {
font-family: 'Roboto Mono', Courier, monospace;
white-space: nowrap;
}
a {
@@ -422,8 +423,8 @@ div.trusted-badge {
}
.float-right {
margin-left: auto;
float: right;
position: absolute;
right: 0.5rem;
}
.hr {
@@ -440,6 +441,12 @@ div.trusted-badge {
height: 0.015rem;
}
.ellipsized {
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/* Element which can take focus but shows no outline */
.group-focus {
outline: none;
@@ -617,7 +624,7 @@ div.trusted-badge {
display: block;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
overflow-x: hidden;
}
/* END of InPlaceEdit styles */
@@ -842,7 +849,7 @@ i.search {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
overflow-x: hidden;
}
.flex-column.narrow {
@@ -1021,12 +1028,14 @@ i.search {
/* Various forms inside a panel (signup form, new topic form) */
.panel-form {
display: flex;
position: relative;
flex-direction: column;
padding: 0;
}
.panel-form-row {
display: flex;
position: relative;
align-items: center;
padding: 0.5rem 0.75rem;
line-height: 1.5;
@@ -1036,6 +1045,7 @@ i.search {
.panel-form-column {
display: flex;
position: relative;
flex-direction: column;
flex-grow: 0;
padding: 0.5rem 0.75rem;
@@ -1054,15 +1064,19 @@ i.search {
padding: 0!important;
}
.panel-form-column > .group {
margin-top: 0.25rem;
margin-bottom: 0.25rem;
}
/* Styling of avatar upload control */
.avatar-upload {
position: relative;
display: flex;
flex-direction: column;
flex-shrink: 0;
width: 8rem;
height: 8.5rem;
margin-left: 0.5rem;
width: 10rem;
height: 10.5rem;
font-size: 4.5rem;
padding: 0;
}
@@ -1086,14 +1100,14 @@ i.search {
.avatar-upload .avatar-box {
position: relative;
width: 8rem;
height: 8rem;
width: 10rem;
height: 10rem;
}
.avatar-upload .avatar-box > .material-icons {
position: relative;
font-size: 4rem;
line-height: 8rem;
line-height: 10rem;
vertical-align: middle;
}
@@ -1112,17 +1126,17 @@ i.search {
border: 1px dashed #999;
font-size: 1rem;
flex-shrink: 0;
width: 8rem;
height: 8rem;
width: 10rem;
height: 10rem;
overflow: hidden;
color: #999;
line-height: 8rem;
line-height: 10rem;
border-radius: 50%;
}
.avatar-upload img.preview {
width: 8rem;
height: 8rem;
width: 10rem;
height: 10rem;
overflow: hidden;
border-radius: 50%;
}
@@ -1136,8 +1150,8 @@ i.search {
color: #666;
top: 0;
left: 0;
width: 8rem;
height: 8rem;
width: 10rem;
height: 10rem;
justify-content: center;
background: center no-repeat url("../img/broken_image.png");
border-radius: 50%;
@@ -1654,7 +1668,7 @@ img.inline-image::before {
img.image-preview::before {
content: " ";
background-size: 8rem 8rem;
background-size: 10rem 10rem;
}
/* Attachment styles */
+1 -1
View File
File diff suppressed because one or more lines are too long
+8 -1
View File
@@ -39,7 +39,7 @@
"defaultMessage": "More",
"description": "Action for showing more content",
"missing": false,
"obsolete": false
"obsolete": true
},
"action_report_chat": {
"translation": "Unterhaltung melden",
@@ -1284,5 +1284,12 @@
"description": "Label for editing topic description",
"missing": true,
"obsolete": false
},
"button_security": {
"translation": "Sicherheit",
"defaultMessage": "Security",
"description": "Navigaton button for security panel.",
"missing": true,
"obsolete": false
}
}
+8 -1
View File
@@ -39,7 +39,7 @@
"defaultMessage": "More",
"description": "Action for showing more content",
"missing": false,
"obsolete": false
"obsolete": true
},
"action_report_chat": {
"translation": "Report Conversation",
@@ -1284,5 +1284,12 @@
"description": "Label for editing topic description",
"missing": false,
"obsolete": false
},
"button_security": {
"translation": "Security",
"defaultMessage": "Security",
"description": "Navigaton button for security panel.",
"missing": false,
"obsolete": false
}
}
+8 -1
View File
@@ -39,7 +39,7 @@
"defaultMessage": "More",
"description": "Action for showing more content",
"missing": false,
"obsolete": false
"obsolete": true
},
"action_report_chat": {
"translation": "Reportar conversación",
@@ -1284,5 +1284,12 @@
"description": "Label for editing topic description",
"missing": true,
"obsolete": false
},
"button_security": {
"translation": "",
"defaultMessage": "Security",
"description": "Navigaton button for security panel.",
"missing": true,
"obsolete": false
}
}
+4 -4
View File
@@ -19,10 +19,6 @@
"defaultMessage": "Leave Conversation",
"description": "Flat button [Leave Conversation]"
},
"action_more": {
"defaultMessage": "More",
"description": "Action for showing more content"
},
"action_report_chat": {
"defaultMessage": "Report Conversation",
"description": "Flat button [Report Group]"
@@ -99,6 +95,10 @@
"defaultMessage": "Reset",
"description": "Button [Reset]"
},
"button_security": {
"defaultMessage": "Security",
"description": "Navigaton button for security panel."
},
"button_send_request": {
"defaultMessage": "Send request",
"description": "Button [Send request]"
+8 -1
View File
@@ -39,7 +39,7 @@
"defaultMessage": "More",
"description": "Action for showing more content",
"missing": false,
"obsolete": false
"obsolete": true
},
"action_report_chat": {
"translation": "대화기록",
@@ -1294,5 +1294,12 @@
"description": "Label for editing topic description",
"missing": true,
"obsolete": false
},
"button_security": {
"translation": "",
"defaultMessage": "Security",
"description": "Navigaton button for security panel.",
"missing": true,
"obsolete": false
}
}
+8 -1
View File
@@ -39,7 +39,7 @@
"defaultMessage": "More",
"description": "Action for showing more content",
"missing": false,
"obsolete": false
"obsolete": true
},
"action_report_chat": {
"translation": "Raportează conversația",
@@ -1284,5 +1284,12 @@
"description": "Label for editing topic description",
"missing": true,
"obsolete": false
},
"button_security": {
"translation": "",
"defaultMessage": "Security",
"description": "Navigaton button for security panel.",
"missing": true,
"obsolete": false
}
}
+8 -1
View File
@@ -39,7 +39,7 @@
"defaultMessage": "More",
"description": "Action for showing more content",
"missing": false,
"obsolete": false
"obsolete": true
},
"action_report_chat": {
"translation": "Сообщить о нарушении",
@@ -1284,5 +1284,12 @@
"description": "Label for editing topic description",
"missing": true,
"obsolete": false
},
"button_security": {
"translation": "",
"defaultMessage": "Security",
"description": "Navigaton button for security panel.",
"missing": true,
"obsolete": false
}
}
+8 -1
View File
@@ -39,7 +39,7 @@
"defaultMessage": "More",
"description": "Action for showing more content",
"missing": false,
"obsolete": false
"obsolete": true
},
"action_report_chat": {
"translation": "檢舉對話",
@@ -1283,5 +1283,12 @@
"description": "Label for editing topic description",
"missing": true,
"obsolete": false
},
"button_security": {
"translation": "",
"defaultMessage": "Security",
"description": "Navigaton button for security panel.",
"missing": true,
"obsolete": false
}
}
+8 -1
View File
@@ -39,7 +39,7 @@
"defaultMessage": "More",
"description": "Action for showing more content",
"missing": false,
"obsolete": false
"obsolete": true
},
"action_report_chat": {
"translation": "检举垃圾邮件",
@@ -1284,5 +1284,12 @@
"description": "Label for editing topic description",
"missing": true,
"obsolete": false
},
"button_security": {
"translation": "",
"defaultMessage": "Security",
"description": "Navigaton button for security panel.",
"missing": true,
"obsolete": false
}
}
+7 -1
View File
@@ -13,7 +13,7 @@ export default class HashNavigation {
}
if (parts[1]) {
parts[1].split('&').forEach(function(part) {
let item = part.split('=');
const item = part.split('=');
if (item[0]) {
params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]);
}
@@ -58,6 +58,12 @@ export default class HashNavigation {
return this.composeUrlHash(parsed.path, parsed.params);
}
static setUrlInfoPanel(hash, infopanel) {
const parsed = this.parseUrlHash(hash);
parsed.params.info = infopanel;
return this.composeUrlHash(parsed.path, parsed.params);
}
static setUrlTopic(hash, topic) {
const parsed = this.parseUrlHash(hash);
parsed.path[1] = topic;
+1 -1
View File
File diff suppressed because one or more lines are too long
+42 -26
View File
@@ -6,6 +6,11 @@ import AvatarUpload from '../widgets/avatar-upload.jsx';
import BadgeList from '../widgets/badge-list.jsx';
import { makeImageUrl } from '../lib/blob-helpers.js';
import { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';
function _clip(str, length) {
return str && str.substring(0, length);
}
export default class AccountSettingsView extends React.Component {
constructor(props) {
@@ -13,7 +18,8 @@ export default class AccountSettingsView extends React.Component {
const me = this.props.tinode.getMeTopic();
this.state = {
fullName: me.public ? me.public.fn : undefined,
fullName: _clip(me.public ? me.public.fn : undefined, MAX_TITLE_LENGTH),
description: _clip(me.public ? me.public.note : undefined, MAX_TOPIC_DESCRIPTION_LENGTH),
avatar: makeImageUrl(me.public ? me.public.photo : null)
};
}
@@ -21,44 +27,54 @@ export default class AccountSettingsView extends React.Component {
render() {
return (
<div className="scrollable-panel">
<div className="panel-form-row">
<div className="panel-form-column">
<div className="panel-form-column">
<a href="#" className="flat-button float-right" onClick={(e) => {e.preventDefault(); this.props.onNavigate('general');}}>
<i className="material-icons">edit</i>&nbsp;
<FormattedMessage id="button_edit" defaultMessage="Edit" description="Call to action [Edit]" />
</a>
<center>
<AvatarUpload
tinode={this.props.tinode}
avatar={this.state.avatar}
readOnly={true}
uid={this.props.myUserId}
title={this.state.fullName} />
</center>
<div className="group">
<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>
<label className="small"><FormattedMessage id="label_user_id" defaultMessage="ID:"
description="Label for user address (ID)" /></label>&nbsp;
<tt style={{whiteSpace: 'nowrap'}}>{this.props.myUserId}</tt>
</div>
<div className="large ellipsized">{this.state.fullName}</div>
</div>
<div className="group">
<label className="small"><FormattedMessage id="label_user_id" defaultMessage="ID:"
description="Label for user address (ID)" /></label>&nbsp;
<tt>{this.props.myUserId}</tt>
</div>
<div className="group">
<BadgeList trustedBadges={this.props.trustedBadges} />
</div>
<AvatarUpload
tinode={this.props.tinode}
avatar={this.state.avatar}
readOnly={true}
uid={this.props.myUserId}
title={this.state.fullName} />
</div>
<div className="panel-form-row">
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('general');}}>
<i className="material-icons">edit</i>&nbsp;
<FormattedMessage id="button_edit" defaultMessage="Edit" description="Call to action [Edit]" />
</a>
{this.state.description ?
<div className="group">
<label className="small">
<FormattedMessage id="label_description" defaultMessage="Description"
description="Label for editing topic description" />
</label>
<div>{this.state.description}</div>
</div> : null}
</div>
<div className="hr" />
<div className="panel-form-column">
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('notif');}}>
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onNavigate('notif');}}>
<i className="material-icons">notifications</i>&nbsp;<FormattedMessage id="sidepanel_title_acc_notifications"
defaultMessage="Notifications" description="Sidepanel title for AccNotificationsView." />
</a>
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('security');}}>
<i className="material-icons">security</i>&nbsp;<FormattedMessage id="sidepanel_title_acc_security"
defaultMessage="Security" description="Sidepanel title for AccSecurityView." />
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onNavigate('security');}}>
<i className="material-icons">security</i>&nbsp;<FormattedMessage id="button_security"
defaultMessage="Security" description="Navigaton button for security panel." />
</a>
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('support');}}>
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onNavigate('support');}}>
<i className="material-icons">contact_support</i>&nbsp;<FormattedMessage id="sidepanel_title_acc_support"
defaultMessage="Support" description="Sidepanel title for AccSupportView." />
</a>
+58 -181
View File
@@ -4,6 +4,8 @@ import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import Tinode from 'tinode-sdk';
import TopicSecurityView from './topic-security-view.jsx';
import AvatarUpload from '../widgets/avatar-upload.jsx';
import BadgeList from '../widgets/badge-list.jsx';
import CheckBox from '../widgets/checkbox.jsx';
@@ -16,7 +18,7 @@ import MoreButton from '../widgets/more-button.jsx';
import PermissionsEditor from '../widgets/permissions-editor.jsx';
import TagManager from '../widgets/tag-manager.jsx';
import { MAX_TITLE_LENGTH, NO_ACCESS_MODE } from '../config.js';
import { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH, NO_ACCESS_MODE } from '../config.js';
import { makeImageUrl } from '../lib/blob-helpers.js';
import { arrayEqual, theCard } from '../lib/utils.js';
@@ -42,58 +44,12 @@ const messages = defineMessages({
defaultMessage: 'Other',
description: 'Label for the other user when the user is unnamed'
},
clear_messages: {
id: 'action_clear_messages',
defaultMessage: 'Clear Messages',
description: 'Flat button [Clear Messages] (soft-delete messages)'
},
clear_messages_warning: {
id: 'clear_messages_warning',
defaultMessage: 'Are you sure you want to clear all messages? It cannot be undone.',
description: 'Alert dialog warning when deleting all messages.'
},
delete_messages: {
id: 'action_delete_messages',
defaultMessage: 'Clear Messages for All',
description: 'Flat button [Clear for All] (hard-delete all messages)'
},
delete_messages_warning: {
id: 'delete_messages_warning',
defaultMessage: 'Are you sure you want to delete all messages for everyone? It cannot be undone.',
description: 'Alert dialog warning when hard-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'
},
});
function _clip(str, length) {
return str && str.substring(0, length);
}
class InfoView extends React.Component {
constructor(props) {
super(props);
@@ -109,6 +65,7 @@ class InfoView extends React.Component {
groupTopic: undefined,
channel: undefined,
fullName: undefined,
description: undefined,
avatar: null,
private: null,
selectedContact: null,
@@ -124,7 +81,6 @@ class InfoView extends React.Component {
trustedBadges: [],
showMemberPanel: false,
showPermissionEditorFor: undefined,
moreInfoExpanded: false,
previousMetaDesc: undefined,
previousSubsUpdated: undefined,
previousTagsUpdated: undefined
@@ -145,12 +101,7 @@ class InfoView extends React.Component {
this.handleShowAddMembers = this.handleShowAddMembers.bind(this);
this.handleHideAddMembers = this.handleHideAddMembers.bind(this);
this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);
this.handleDeleteMessages = this.handleDeleteMessages.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.handleMoreInfo = this.handleMoreInfo.bind(this);
this.handleTagsUpdated = this.handleTagsUpdated.bind(this);
this.handleContextMenu = this.handleContextMenu.bind(this);
}
@@ -236,10 +187,11 @@ class InfoView extends React.Component {
deleter: acs && acs.isDeleter(),
muted: acs && acs.isMuted(),
fullName: topic.public ? topic.public.fn : undefined,
fullName: _clip(topic.public ? topic.public.fn : undefined, MAX_TITLE_LENGTH),
description: _clip(topic.public ? topic.public.note : undefined, MAX_TOPIC_DESCRIPTION_LENGTH),
avatar: makeImageUrl(topic.public ? topic.public.photo : null),
trustedBadges: badges,
private: topic.private ? topic.private.comment : null,
private: _clip(topic.private ? topic.private.comment : null, MAX_TITLE_LENGTH),
address: topic.name,
groupTopic: topic.isGroupType(),
channel: topic.isChannelType(),
@@ -433,66 +385,10 @@ class InfoView extends React.Component {
this.setState({showMemberPanel: false});
}
handleDeleteMessages(e) {
e.preventDefault();
const {formatMessage} = this.props.intl;
this.props.onShowAlert(
formatMessage(this.state.deleter ? messages.delete_messages : messages.clear_messages), // title
formatMessage(this.state.deleter ? messages.delete_messages_warning : messages.clear_messages_warning), // content
(() => { this.props.onDeleteMessages(this.props.topic); }), // onConfirm
null, // "OK"
true, // Show Reject button
null // "Cancel"
);
}
handleLeave(e) {
e.preventDefault();
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) {
this.setState({selectedContact: uid});
}
handleMoreInfo(open) {
this.setState({moreInfoExpanded: open});
}
handleTagsUpdated(tags) {
if (!arrayEqual(this.state.tags.slice(0), tags.slice(0))) {
this.props.onTopicTagsUpdate(this.props.topic, tags);
@@ -569,42 +465,52 @@ class InfoView extends React.Component {
/>
:
<div id="info-view-content" className="scrollable-panel">
<div className="panel-form-row">
<div className="panel-form-column">
<div className="panel-form-column">
<a href="#" className="flat-button float-right" onClick={(e) => {e.preventDefault(); this.props.onNavigate('general');}}>
<i className="material-icons">edit</i>&nbsp;
<FormattedMessage id="button_edit" defaultMessage="Edit" description="Call to action [Edit]" />
</a>
<center>
<AvatarUpload
tinode={this.props.tinode}
avatar={this.state.avatar}
readOnly={true}
uid={this.props.topic}
title={this.state.fullName} />
</center>
<div className="group">
<div><label className="small">
<FormattedMessage id="label_topic_name" defaultMessage="Name"
description="Label for editing topic name" />
</label></div>
<div className="large">{this.state.fullName}</div>
{this.state.private ?
<>
<div><label className="small">
<FormattedMessage id="label_private" defaultMessage="Private comment"
description="Label for editing 'private'" />
</label></div>
<div className="large">{this.state.private}</div>
</>
: null
}
<div>
<label className="small"><FormattedMessage id="label_user_id" defaultMessage="ID:"
description="Label for user address (ID)" /></label>&nbsp;
<tt>{this.props.myUserId}</tt>
<div className="large ellipsized">{this.state.fullName}</div>
</div>
{this.state.private ?
<div className="group">
<div><label className="small">
<FormattedMessage id="label_private" defaultMessage="Private comment"
description="Label for editing 'private'" />
</label></div>
<div className="large ellipsized">{this.state.private}</div>
</div>
: null
}
<div className="group">
<label className="small"><FormattedMessage id="label_user_id" defaultMessage="ID:"
description="Label for user address (ID)" /></label>&nbsp;
<tt>{this.state.address}</tt>
</div>
<div className="group">
<BadgeList trustedBadges={this.state.trustedBadges} />
</div>
<AvatarUpload
tinode={this.props.tinode}
avatar={this.state.avatar}
readOnly={true}
uid={this.props.topic}
title={this.state.fullName} />
</div>
<div className="panel-form-row">
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('general');}}>
<i className="material-icons">edit</i>&nbsp;
<FormattedMessage id="button_edit" defaultMessage="Edit" description="Call to action [Edit]" />
</a>
{this.state.description ?
<div className="group">
<label className="small">
<FormattedMessage id="label_description" defaultMessage="Description"
description="Label for editing topic description" />
</label>
<div>{this.state.description}</div>
</div> : null}
</div>
<div className="hr" />
<div className="panel-form-column">
@@ -613,16 +519,15 @@ class InfoView extends React.Component {
<FormattedMessage id="label_muting_topic" defaultMessage="Muted:"
description="Label for Muting/unmuting the topic" />
</label>
<CheckBox name="P" checked={this.state.muted}
onChange={this.handleMuted} />
<CheckBox name="P" checked={this.state.muted} onChange={this.handleMuted} />
</div>
<FormattedMessage id="action_more" defaultMessage="More"
description="Action for showing more content">{
(more) => <MoreButton
title={more}
open={this.state.moreInfoExpanded}
onToggle={this.handleMoreInfo} />
}</FormattedMessage>
</div>
<div className="hr" />
<div className="panel-form-column">
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onNavigate('security');}}>
<i className="material-icons">security</i>&nbsp;<FormattedMessage id="button_security"
defaultMessage="Security" description="Navigaton button for security panel." />
</a>
{this.state.moreInfoExpanded ?
<div className="panel-form-column">
{this.state.groupTopic ?
@@ -713,34 +618,6 @@ class InfoView extends React.Component {
:
null
}
<div className="panel-form-column">
{!this.state.channel ?
<a href="#" className="flat-button" onClick={this.handleDeleteMessages}>
<i className="material-icons">delete_outline</i> &nbsp;{
formatMessage(this.state.deleter ? messages.delete_messages : messages.clear_messages)
}
</a>
:
null
}
<a href="#" className="danger flat-button" onClick={this.handleLeave}>
<i className="material-icons">exit_to_app</i> &nbsp;{formatMessage(messages.leave_chat)}
</a>
{!this.state.groupTopic ?
<a href="#" className="danger flat-button" onClick={this.handleBlock}>
<i className="material-icons">block</i> &nbsp;{formatMessage(messages.block_contact)}
</a>
:
null
}
{!this.state.owner ?
<a href="#" className="danger flat-button" onClick={this.handleReport}>
<i className="material-icons">report</i> &nbsp;{formatMessage(messages.report_chat)}
</a>
:
null
}
</div>
{this.state.groupTopic && this.state.sharer ?
<>
<div className="hr" />
+2 -3
View File
@@ -100,7 +100,7 @@ class SidepanelView extends React.Component {
}
handleNewTopic() {
this.props.onBasicNavigate('newtpk');
this.props.onNavigate('newtpk');
}
render() {
@@ -173,14 +173,13 @@ class SidepanelView extends React.Component {
tinode={this.props.tinode}
myUserId={this.props.myUserId}
trustedBadges={this.props.trustedBadges}
onBasicNavigate={this.props.onBasicNavigate} /> :
onNavigate={this.props.onNavigate} /> :
(view === 'general' || view === 'crop') ?
<TopicCommonView
topic="me"
tinode={this.props.tinode}
myUserId={this.props.myUserId}
onBasicNavigate={this.props.onBasicNavigate}
onUpdateTopicDesc={this.props.onUpdateAccountDesc}
onUpdateTags={this.props.onUpdateAccountTags}
onCredAdd={this.props.onCredAdd}
+18 -11
View File
@@ -187,7 +187,7 @@ class TinodeWeb extends React.Component {
newTopicParams: null,
loginDisabled: false,
displayMobile: (window.innerWidth <= MEDIA_BREAKPOINT),
showInfoPanel: false,
infoPanel: undefined,
mobilePanel: 'sidepanel',
contextMenuVisible: false,
@@ -425,7 +425,7 @@ class TinodeWeb extends React.Component {
// Additional parameters of panels.
this.setState({
showInfoPanel: hash.params.info,
infoPanel: hash.params.info,
newTopicTabSelected: hash.params.tab
});
}
@@ -862,7 +862,7 @@ class TinodeWeb extends React.Component {
errorText: '',
errorLevel: null,
mobilePanel: 'topic-view',
showInfoPanel: false
infoPanel: undefined
});
// Different contact selected.
if (this.state.topicSelected != topicName) {
@@ -880,7 +880,7 @@ class TinodeWeb extends React.Component {
mobilePanel: 'sidepanel',
topicSelectedOnline: false,
topicSelectedAcs: null,
showInfoPanel: false
infoPanel: undefined
});
HashNavigation.navigateTo(HashNavigation.setUrlTopic('', null));
@@ -1164,11 +1164,16 @@ class TinodeWeb extends React.Component {
this.setState({errorText: '', errorLevel: null});
}
// Basic nagigator by hash value. No need to bind to this.
// Sidepanel navigator. No need to bind to 'this'.
basicNavigator(hash) {
HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, hash));
}
// Topic info navigator. No need to bind to 'this'.
infoNavigator(hash) {
HashNavigation.navigateTo(HashNavigation.setUrlInfoPanel(window.location.hash, hash));
}
// Request to start a topic, new or selected from search results, or "by ID".
handleStartTopicRequest(topicName, pub, priv, tags, isChannel) {
// Check if topic is indeed new. If not, launch it.
@@ -1435,13 +1440,13 @@ class TinodeWeb extends React.Component {
}
handleShowInfoView() {
HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'info', true));
this.setState({showInfoPanel: true});
HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'info', 'info'));
this.setState({infoPanel: 'info'});
}
handleHideInfoView() {
HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'info'));
this.setState({showInfoPanel: false});
this.setState({infoPanel: undefined});
}
handleMemberUpdateRequest(topicName, added, removed) {
@@ -1582,7 +1587,7 @@ class TinodeWeb extends React.Component {
onGlobalSettings={this.handleGlobalSettings}
onSignUp={this.handleNewAccount}
onSettings={this.handleSettings}
onBasicNavigate={this.basicNavigator}
onNavigate={this.basicNavigator}
onLoginRequest={this.handleLoginRequest}
onPersistenceChange={this.handlePersistenceChange}
onCreateAccount={this.handleNewAccountRequest}
@@ -1624,7 +1629,7 @@ class TinodeWeb extends React.Component {
viewportWidth={this.state.viewportWidth}
viewportHeight={this.state.viewportHeight}
hideSelf={this.state.displayMobile &&
(this.state.mobilePanel !== 'topic-view' || this.state.showInfoPanel)}
(this.state.mobilePanel !== 'topic-view' || this.state.infoPanel)}
topic={this.state.topicSelected}
myUserId={this.state.myUserId}
serverVersion={this.state.serverVersion}
@@ -1647,7 +1652,7 @@ class TinodeWeb extends React.Component {
onNewChat={this.handleNewChatInvitation}
sendMessage={this.handleSendMessage} />
{this.state.showInfoPanel ?
{this.state.infoPanel ?
<InfoView
tinode={this.tinode}
connected={this.state.connected}
@@ -1655,12 +1660,14 @@ class TinodeWeb extends React.Component {
topic={this.state.topicSelected}
searchableContacts={this.state.searchableContacts}
myUserId={this.state.myUserId}
panel={this.state.infoPanel}
errorText={this.state.errorText}
errorLevel={this.state.errorLevel}
errorAction={this.state.errorAction}
errorActionText={this.state.errorActionText}
onNavigate={this.infoNavigator}
onTopicDescUpdate={this.handleTopicUpdateRequest}
onCancel={this.handleHideInfoView}
onShowAlert={this.handleShowAlert}
+8 -5
View File
@@ -64,7 +64,7 @@ export default class TopicCommonView extends React.Component {
}
handleCredEntered(e) {
let value = this.state.newCred.trim();
const value = this.state.newCred.trim();
if (!value) {
this.setState({addCredActive: false, addCredInvalid: false});
return;
@@ -92,14 +92,17 @@ export default class TopicCommonView extends React.Component {
const credentials = [];
if (this.state.isMe) {
this.state.credentials.map((cred) => {
credentials.push(<div key={cred.meth + ":" + cred.val + ":" + cred.done}>{cred.meth}: <tt>{cred.val}</tt>
<span > {!cred.done ?
<a href="#" onClick={(e) => {e.preventDefault(); this.props.onCredConfirm(cred.meth, cred.val);}}>
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>);
className="material-icons gray">delete_outline</i></a></span>
</div>
);
});
}
-1
View File
@@ -3,7 +3,6 @@ import React from 'react';
import { injectIntl, defineMessages } from 'react-intl';
import { REM_SIZE } from '../config.js';
import HashNavigation from '../lib/navigation.js';
const messages = defineMessages({
info: {
+55 -42
View File
@@ -173,37 +173,57 @@ export default class TopicDescEdit extends React.Component {
return (
<>
<div className="panel-form-column">
<div className="panel-form-row">
<div className="panel-form-column">
{this.state.isMe ? <>
<label className="small">
<FormattedMessage id="label_your_name" defaultMessage="Your name"
description="Label for full name editing" />
</label>
<div><FormattedMessage id="full_name_prompt" defaultMessage="Full name, e.g. John Doe"
<div className="panel-form-column">
<center>
<AvatarUpload
tinode={this.props.tinode}
avatar={this.state.avatar}
readOnly={!editable}
uid={this.props.topic}
title={this.state.fullName}
onImageUpdated={this.handleImageUpdated}
onError={this.props.onError} />
</center>
{this.state.isMe ?
<div className="group">
<label className="small">
<FormattedMessage id="label_your_name" defaultMessage="Your name"
description="Label for full name editing" />
</label>
<div>
<FormattedMessage id="full_name_prompt" defaultMessage="Full name, e.g. John Doe"
description="Input placeholder for person's full name">{
(full_name_placeholder) => <InPlaceEdit
placeholder={full_name_placeholder}
value={this.state.fullName}
required={true}
onFinished={this.handleFullNameUpdate} />
}</FormattedMessage></div>
</> : <>
(full_name_placeholder) => <InPlaceEdit
placeholder={full_name_placeholder}
value={this.state.fullName}
required={true}
onFinished={this.handleFullNameUpdate} />
}</FormattedMessage>
</div>
</div>
:
<>
<div className="group">
<div><label className="small">
<FormattedMessage id="label_topic_name" defaultMessage="Name"
description="Label for editing topic name" />
</label></div>
<div><InPlaceEdit
<div>
<InPlaceEdit
placeholder={this.state.groupTopic ? "Group name" : <i>Unknown</i>}
readOnly={!this.state.owner}
value={this.state.fullName}
required={true}
onFinished={this.handleFullNameUpdate} /></div>
<div><label className="small">
<FormattedMessage id="label_private" defaultMessage="Private comment"
description="Label for editing 'private'" />
</label></div>
onFinished={this.handleFullNameUpdate} />
</div>
</div>
<div className="group">
<div>
<label className="small">
<FormattedMessage id="label_private" defaultMessage="Private comment"
description="Label for editing 'private'" />
</label>
</div>
<div>
<FormattedMessage id="private_editing_placeholder"
defaultMessage="Visible to you only"
@@ -214,18 +234,10 @@ export default class TopicDescEdit extends React.Component {
onFinished={this.handlePrivateUpdate} />
}</FormattedMessage>
</div>
</>
}
</div>
<AvatarUpload
tinode={this.props.tinode}
avatar={this.state.avatar}
readOnly={!editable}
uid={this.props.topic}
title={this.state.fullName}
onImageUpdated={this.handleImageUpdated}
onError={this.props.onError} />
</div>
</>
}
<div className="group">
<div><label className="small">
<FormattedMessage id="label_description" defaultMessage="Description"
description="Label for editing topic description" />
@@ -243,21 +255,22 @@ export default class TopicDescEdit extends React.Component {
}</FormattedMessage>
</div>
</div>
{editable ?
<>
<div className="hr" />
<FormattedMessage id="title_tag_manager" defaultMessage="Tags (user discovery)"
description="Section title for TagManager">{
</div>
{editable ?
<>
<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}
tinode={this.props.tinode}
onSubmit={this.handleTagsUpdated} />
}</FormattedMessage>
</>
: null
}
}</FormattedMessage>
</>
: null
}
</>
);
}
+464 -306
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