refactor context menu
This commit is contained in:
+174
-168
@@ -427,7 +427,7 @@ function vcard(fn, imageDataUrl) {
|
|||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deep-shallow compare two arrays.
|
// Deep-shallow compare two arrays: shallow compare each element.
|
||||||
function arrayEqual(a, b) {
|
function arrayEqual(a, b) {
|
||||||
// Compare lengths first.
|
// Compare lengths first.
|
||||||
if (a.length != b.length) {
|
if (a.length != b.length) {
|
||||||
@@ -446,134 +446,75 @@ function arrayEqual(a, b) {
|
|||||||
|
|
||||||
/* BEGIN Context Menu: popup/dropdown menu */
|
/* BEGIN Context Menu: popup/dropdown menu */
|
||||||
|
|
||||||
// Function is used by context menu to set permissions.
|
|
||||||
function topicPermissionSetter(mode, params, errorHandler) {
|
|
||||||
var topic = Tinode.getTopic(params.topicName);
|
|
||||||
if (!topic) {
|
|
||||||
console.log("Topic not found", params.topicName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var am, user;
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
var instance = this;
|
|
||||||
topic.setMeta({sub: {user: params.user, mode: am}}).catch(function(err) {
|
|
||||||
if (errorHandler) {
|
|
||||||
errorHandler(err.message, "err");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteMessages(all, hard, params, errorHandler) {
|
|
||||||
var topic = Tinode.getTopic(params.topicName);
|
|
||||||
if (!topic) {
|
|
||||||
console.log("Topic not found: ", params.topicName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We don't know if the message is still pending (e.g. attachment is being uploaded),
|
|
||||||
// so try cancelling first. No harm if we can't cancel.
|
|
||||||
if (topic.cancelSend(params.seq)) {
|
|
||||||
return new Promise.resolve();
|
|
||||||
}
|
|
||||||
// Can't cancel. Delete instead.
|
|
||||||
var promise = all ?
|
|
||||||
topic.delMessagesAll(hard) :
|
|
||||||
topic.delMessagesList([params.seq], hard);
|
|
||||||
promise.catch((err) => {
|
|
||||||
if (errorHandler) {
|
|
||||||
errorHandler(err.message, "err");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Context menu items.
|
|
||||||
var ContextMenuItems = {
|
|
||||||
"topic_info": {title: "Info", handler: null},
|
|
||||||
|
|
||||||
"messages_clear": {title: "Clear messages", handler: function(params, errorHandler) {
|
|
||||||
deleteMessages(true, false, params, errorHandler);
|
|
||||||
}},
|
|
||||||
"messages_clear_hard": {title: "Clear for All", handler: function(params, errorHandler) {
|
|
||||||
deleteMessages(true, true, params, errorHandler);
|
|
||||||
}},
|
|
||||||
"message_delete": {title: "Delete", handler: function(params, errorHandler) {
|
|
||||||
deleteMessages(false, false, params, errorHandler);
|
|
||||||
}},
|
|
||||||
"message_delete_hard": {title: "Delete for All", handler: function(params, errorHandler) {
|
|
||||||
deleteMessages(false, true, params, errorHandler);
|
|
||||||
}},
|
|
||||||
"topic_unmute": {title: "Unmute", handler: topicPermissionSetter.bind(this, "+P")},
|
|
||||||
"topic_mute": {title: "Mute", handler: topicPermissionSetter.bind(this, "-P")},
|
|
||||||
"topic_unblock": {title: "Unblock", handler: topicPermissionSetter.bind(this, "+J")},
|
|
||||||
"topic_block": {title: "Block", handler: topicPermissionSetter.bind(this, "-J")},
|
|
||||||
"topic_delete": {title: "Delete", handler: function(params, errorHandler) {
|
|
||||||
var topic = Tinode.getTopic(params.topicName);
|
|
||||||
if (!topic) {
|
|
||||||
console.log("Topic not found: ", params.topicName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
topic.delTopic().catch(function(err) {
|
|
||||||
if (errorHandler) {
|
|
||||||
errorHandler(err.message, "err");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}},
|
|
||||||
|
|
||||||
"permissions": {title: "Edit permissions", handler: null},
|
|
||||||
"member_delete": {title: "Remove", handler: function(params, errorHandler) {
|
|
||||||
var topic = Tinode.getTopic(params.topicName);
|
|
||||||
if (!topic || !params.user) {
|
|
||||||
console.log("Topic or user not found: '" + params.topicName + "', '" + params.user + "'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
topic.delSubscription(params.user).catch(function(err) {
|
|
||||||
if (errorHandler) {
|
|
||||||
errorHandler(err.message, "err");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}},
|
|
||||||
"member_mute": {title: "Mute", handler: topicPermissionSetter.bind(this, "-P")},
|
|
||||||
"member_unmute": {title: "Unmute", handler: topicPermissionSetter.bind(this, "+P")},
|
|
||||||
"member_block": {title: "Block", handler: topicPermissionSetter.bind(this, "-J")},
|
|
||||||
"member_unblock": {title: "Unblock", handler: topicPermissionSetter.bind(this, "+J")},
|
|
||||||
};
|
|
||||||
|
|
||||||
class ContextMenu extends React.Component {
|
class ContextMenu extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.toggle = this.toggle.bind(this);
|
|
||||||
this.handlePageClick = this.handlePageClick.bind(this);
|
this.handlePageClick = this.handlePageClick.bind(this);
|
||||||
this.handleEscapeKey = this.handleEscapeKey.bind(this);
|
this.handleEscapeKey = this.handleEscapeKey.bind(this);
|
||||||
this.handleClick = this.handleClick.bind(this);
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
|
||||||
|
// Preconfigured menu items.
|
||||||
|
this.MenuItems = {
|
||||||
|
"topic_info": {title: "Info", handler: null},
|
||||||
|
|
||||||
|
"messages_clear": {title: "Clear messages", handler: (params, errorHandler) => {
|
||||||
|
this.deleteMessages(true, false, params, errorHandler);
|
||||||
|
}},
|
||||||
|
"messages_clear_hard": {title: "Clear for All", handler: (params, errorHandler) => {
|
||||||
|
this.deleteMessages(true, true, params, errorHandler);
|
||||||
|
}},
|
||||||
|
"message_delete": {title: "Delete", handler: (params, errorHandler) => {
|
||||||
|
this.deleteMessages(false, false, params, errorHandler);
|
||||||
|
}},
|
||||||
|
"message_delete_hard": {title: "Delete for All", handler: (params, errorHandler) => {
|
||||||
|
this.deleteMessages(false, true, params, errorHandler);
|
||||||
|
}},
|
||||||
|
"topic_unmute": {title: "Unmute", handler: this.topicPermissionSetter.bind(this, "+P")},
|
||||||
|
"topic_mute": {title: "Mute", handler: this.topicPermissionSetter.bind(this, "-P")},
|
||||||
|
"topic_unblock": {title: "Unblock", handler: this.topicPermissionSetter.bind(this, "+J")},
|
||||||
|
"topic_block": {title: "Block", handler: this.topicPermissionSetter.bind(this, "-J")},
|
||||||
|
"topic_delete": {title: "Delete", handler: (params, errorHandler) => {
|
||||||
|
var topic = Tinode.getTopic(params.topicName);
|
||||||
|
if (!topic) {
|
||||||
|
console.log("Topic not found: ", params.topicName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
topic.delTopic().catch(function(err) {
|
||||||
|
if (errorHandler) {
|
||||||
|
errorHandler(err.message, "err");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}},
|
||||||
|
|
||||||
|
"permissions": {title: "Edit permissions", handler: null},
|
||||||
|
"member_delete": {title: "Remove", handler: (params, errorHandler) => {
|
||||||
|
var topic = Tinode.getTopic(params.topicName);
|
||||||
|
if (!topic || !params.user) {
|
||||||
|
console.log("Topic or user not found: '" + params.topicName + "', '" + params.user + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
topic.delSubscription(params.user).catch((err) => {
|
||||||
|
if (errorHandler) {
|
||||||
|
errorHandler(err.message, "err");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}},
|
||||||
|
"member_mute": {title: "Mute", handler: this.topicPermissionSetter.bind(this, "-P")},
|
||||||
|
"member_unmute": {title: "Unmute", handler: this.topicPermissionSetter.bind(this, "+P")},
|
||||||
|
"member_block": {title: "Block", handler: this.topicPermissionSetter.bind(this, "-J")},
|
||||||
|
"member_unblock": {title: "Unblock", handler: this.topicPermissionSetter.bind(this, "+J")},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
document.addEventListener('mousedown', this.handlePageClick, false);
|
||||||
|
document.addEventListener('keyup', this.handleEscapeKey, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.toggle(false);
|
document.removeEventListener('mousedown', this.handlePageClick, false);
|
||||||
}
|
document.removeEventListener('keyup', this.handleEscapeKey, false);
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
||||||
this.toggle(nextProps.visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle(visible) {
|
|
||||||
if (visible) {
|
|
||||||
document.addEventListener('mousedown', this.handlePageClick, false);
|
|
||||||
document.addEventListener('keyup', this.handleEscapeKey, false);
|
|
||||||
} else {
|
|
||||||
document.removeEventListener('mousedown', this.handlePageClick, false);
|
|
||||||
document.removeEventListener('keyup', this.handleEscapeKey, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePageClick(e) {
|
handlePageClick(e) {
|
||||||
@@ -599,17 +540,67 @@ class ContextMenu extends React.Component {
|
|||||||
item.handler(this.props.params, this.props.onError);
|
item.handler(this.props.params, this.props.onError);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
// Menu Actions
|
||||||
if (!this.props.visible) {
|
|
||||||
return null;
|
deleteMessages(all, hard, params, errorHandler) {
|
||||||
|
var topic = this.props.tinode.getTopic(params.topicName);
|
||||||
|
if (!topic) {
|
||||||
|
console.log("Topic not found: ", params.topicName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We don't know if the message is still pending (e.g. attachment is being uploaded),
|
||||||
|
// so try cancelling first. No harm if we can't cancel.
|
||||||
|
if (topic.cancelSend(params.seq)) {
|
||||||
|
return new Promise.resolve();
|
||||||
|
}
|
||||||
|
// Can't cancel. Delete instead.
|
||||||
|
var promise = all ?
|
||||||
|
topic.delMessagesAll(hard) :
|
||||||
|
topic.delMessagesList([params.seq], hard);
|
||||||
|
promise.catch((err) => {
|
||||||
|
if (errorHandler) {
|
||||||
|
errorHandler(err.message, "err");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function is used by context menu to set permissions.
|
||||||
|
topicPermissionSetter(mode, params, errorHandler) {
|
||||||
|
var topic = this.props.tinode.getTopic(params.topicName);
|
||||||
|
if (!topic) {
|
||||||
|
console.log("Topic not found", params.topicName);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var am, user;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
topic.setMeta({sub: {user: params.user, mode: am}}).catch((err) => {
|
||||||
|
if (errorHandler) {
|
||||||
|
errorHandler(err.message, "err");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let menu = [];
|
let menu = [];
|
||||||
this.props.items.map((item) => {
|
this.props.items.map((item) => {
|
||||||
|
if (typeof item == 'string') {
|
||||||
|
item = this.MenuItems[item];
|
||||||
|
}
|
||||||
if (item && item.title) {
|
if (item && item.title) {
|
||||||
menu.push(
|
menu.push(
|
||||||
item.title === "-" ?
|
item.title == "-" ?
|
||||||
<li className="separator" key={count} />
|
<li className="separator" key={count} />
|
||||||
:
|
:
|
||||||
<li onClick={this.handleClick} data-id={count} key={count}>{item.title}</li>
|
<li onClick={this.handleClick} data-id={count} key={count}>{item.title}</li>
|
||||||
@@ -699,7 +690,7 @@ class LetterTile extends React.PureComponent {
|
|||||||
let color = "lettertile dark-color" + (Math.abs(stringHash(this.props.topic)) % 16);
|
let color = "lettertile dark-color" + (Math.abs(stringHash(this.props.topic)) % 16);
|
||||||
avatar = (<div className={color}><div>{letter}</div></div>)
|
avatar = (<div className={color}><div>{letter}</div></div>)
|
||||||
} else {
|
} else {
|
||||||
avatar = (Tinode.topicType(this.props.topic) === "grp") ?
|
avatar = (Tinode.topicType(this.props.topic) == "grp") ?
|
||||||
<i className="material-icons">group</i> :
|
<i className="material-icons">group</i> :
|
||||||
<i className="material-icons">person</i>;
|
<i className="material-icons">person</i>;
|
||||||
}
|
}
|
||||||
@@ -1089,18 +1080,16 @@ class ChipInput extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
var chips = [];
|
var chips = [];
|
||||||
|
|
||||||
var instance = this;
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
this.state.sortedChips.map(function(item) {
|
this.state.sortedChips.map((item) => {
|
||||||
chips.push(
|
chips.push(
|
||||||
<Chip
|
<Chip
|
||||||
onCancel={instance.handleChipCancel}
|
onCancel={instance.handleChipCancel}
|
||||||
avatar={makeImageUrl(item.public ? item.public.photo : null)}
|
avatar={makeImageUrl(item.public ? item.public.photo : null)}
|
||||||
title={item.public ? item.public.fn : undefined}
|
title={item.public ? item.public.fn : undefined}
|
||||||
noAvatar={instance.props.avatarDisabled}
|
noAvatar={this.props.avatarDisabled}
|
||||||
topic={item.user}
|
topic={item.user}
|
||||||
required={item.user === instance.props.required}
|
required={item.user === this.props.required}
|
||||||
index={count}
|
index={count}
|
||||||
key={item.user} />
|
key={item.user} />
|
||||||
);
|
);
|
||||||
@@ -1429,7 +1418,10 @@ class AvatarUpload extends React.Component {
|
|||||||
<img src={this.state.dataUrl} className="preview" /> :
|
<img src={this.state.dataUrl} className="preview" /> :
|
||||||
this.props.readOnly && this.props.uid ?
|
this.props.readOnly && this.props.uid ?
|
||||||
<div className="avatar-box">
|
<div className="avatar-box">
|
||||||
<LetterTile avatar={true} topic={this.props.uid} title={this.props.title} />
|
<LetterTile
|
||||||
|
avatar={true}
|
||||||
|
topic={this.props.uid}
|
||||||
|
title={this.props.title} />
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
<div className="blank">128×128</div>}
|
<div className="blank">128×128</div>}
|
||||||
@@ -1536,7 +1528,10 @@ class SideNavbar extends React.PureComponent {
|
|||||||
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">
|
<div id="self-avatar" className="avatar-box">
|
||||||
<LetterTile avatar={this.props.avatar} topic={this.props.myUserId} title={this.props.title} />
|
<LetterTile
|
||||||
|
avatar={this.props.avatar}
|
||||||
|
topic={this.props.myUserId}
|
||||||
|
title={this.props.title} />
|
||||||
</div>
|
</div>
|
||||||
<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' ?
|
||||||
@@ -2060,8 +2055,9 @@ class Contact extends React.Component {
|
|||||||
onClick={this.handleClick}>
|
onClick={this.handleClick}>
|
||||||
<div className="avatar-box">
|
<div className="avatar-box">
|
||||||
<LetterTile
|
<LetterTile
|
||||||
avatar={avatar} title={this.props.title} topic={this.props.item} />
|
avatar={avatar}
|
||||||
|
title={this.props.title}
|
||||||
|
topic={this.props.item} />
|
||||||
{this.props.showOnline ? <span className={online} /> :
|
{this.props.showOnline ? <span className={online} /> :
|
||||||
(this.props.showCheckmark && this.props.selected ?
|
(this.props.showCheckmark && this.props.selected ?
|
||||||
<i className="checkmark material-icons">check_circle</i>
|
<i className="checkmark material-icons">check_circle</i>
|
||||||
@@ -2254,14 +2250,13 @@ class ContactList extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
var me = Tinode.getCurrentUserID();
|
var me = Tinode.getCurrentUserID();
|
||||||
var contactNodes = [];
|
var contactNodes = [];
|
||||||
var instance = this;
|
|
||||||
var showCheckmark = Array.isArray(this.props.topicSelected);
|
var showCheckmark = Array.isArray(this.props.topicSelected);
|
||||||
if (this.props.contacts && this.props.contacts.length > 0) {
|
if (this.props.contacts && this.props.contacts.length > 0) {
|
||||||
this.props.contacts.map(function(c) {
|
this.props.contacts.map((c) => {
|
||||||
var key = c.topic ? c.topic : c.user;
|
var key = c.topic ? c.topic : c.user;
|
||||||
// If filter function is provided, filter out the items
|
// If filter function is provided, filter out the items
|
||||||
// which don't satisfy the condition.
|
// which don't satisfy the condition.
|
||||||
if (instance.props.filterFunc && instance.props.filter) {
|
if (this.props.filterFunc && this.props.filter) {
|
||||||
var content = [key];
|
var content = [key];
|
||||||
if (c.private && c.private.comment) {
|
if (c.private && c.private.comment) {
|
||||||
content.push(("" + c.private.comment).toLowerCase());
|
content.push(("" + c.private.comment).toLowerCase());
|
||||||
@@ -2269,7 +2264,7 @@ class ContactList extends React.Component {
|
|||||||
if (c.public && c.public.fn) {
|
if (c.public && c.public.fn) {
|
||||||
content.push(("" + c.public.fn).toLowerCase());
|
content.push(("" + c.public.fn).toLowerCase());
|
||||||
}
|
}
|
||||||
if (!instance.props.filterFunc(instance.props.filter, content)) {
|
if (!this.props.filterFunc(this.props.filter, content)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2905,7 +2900,7 @@ class InfoView extends React.Component {
|
|||||||
this.previousSubsUpdated = topic.onSubsUpdated;
|
this.previousSubsUpdated = topic.onSubsUpdated;
|
||||||
topic.onSubsUpdated = this.onSubsUpdated;
|
topic.onSubsUpdated = this.onSubsUpdated;
|
||||||
|
|
||||||
if (topic.getType() === "grp") {
|
if (topic.getType() == "grp") {
|
||||||
this.previousTagsUpdated = topic.onTagsUpdated;
|
this.previousTagsUpdated = topic.onTagsUpdated;
|
||||||
topic.onTagsUpdated = this.onTagsUpdated;
|
topic.onTagsUpdated = this.onTagsUpdated;
|
||||||
} else {
|
} else {
|
||||||
@@ -2933,7 +2928,7 @@ class InfoView extends React.Component {
|
|||||||
|
|
||||||
resetSubs(topic, props) {
|
resetSubs(topic, props) {
|
||||||
var newState = {contactList: []};
|
var newState = {contactList: []};
|
||||||
if (topic.getType() === "p2p") {
|
if (topic.getType() == "p2p") {
|
||||||
// Fetch the other party in the p2p conversation.
|
// Fetch the other party in the p2p conversation.
|
||||||
// Topic may not be ready yet, so check if user is found.
|
// Topic may not be ready yet, so check if user is found.
|
||||||
var user2 = topic.subscriber(props.topic);
|
var user2 = topic.subscriber(props.topic);
|
||||||
@@ -2963,7 +2958,7 @@ class InfoView extends React.Component {
|
|||||||
avatar: makeImageUrl(topic.public ? topic.public.photo : null),
|
avatar: makeImageUrl(topic.public ? topic.public.photo : null),
|
||||||
private: topic.private ? topic.private.comment : null,
|
private: topic.private ? topic.private.comment : null,
|
||||||
address: topic.name,
|
address: topic.name,
|
||||||
groupTopic: (topic.getType() === "grp"),
|
groupTopic: (topic.getType() == "grp"),
|
||||||
showMemberPanel: false,
|
showMemberPanel: false,
|
||||||
access: acs ? acs.getMode() : undefined,
|
access: acs ? acs.getMode() : undefined,
|
||||||
modeGiven: acs ? acs.getGiven() : undefined,
|
modeGiven: acs ? acs.getGiven() : undefined,
|
||||||
@@ -3179,9 +3174,9 @@ class InfoView extends React.Component {
|
|||||||
{title: "Edit permissions", handler: function() {
|
{title: "Edit permissions", handler: function() {
|
||||||
instance.handleLaunchPermissionsEditor("user", params.topicName);
|
instance.handleLaunchPermissionsEditor("user", params.topicName);
|
||||||
}},
|
}},
|
||||||
ContextMenuItems["member_delete"],
|
"member_delete",
|
||||||
user.acs.isMuted() ? ContextMenuItems["member_unmute"] : ContextMenuItems["member_mute"],
|
user.acs.isMuted() ? "member_unmute" : "member_mute",
|
||||||
user.acs.isJoiner() ? ContextMenuItems["member_block"] : ContextMenuItems["member_unblock"]
|
user.acs.isJoiner() ? "member_block" : "member_unblock"
|
||||||
];
|
];
|
||||||
this.props.showContextMenu({
|
this.props.showContextMenu({
|
||||||
topicName: this.props.topic,
|
topicName: this.props.topic,
|
||||||
@@ -3472,8 +3467,10 @@ class ChatMessage extends React.Component {
|
|||||||
{this.props.userFrom && this.props.response ?
|
{this.props.userFrom && this.props.response ?
|
||||||
<div className="avatar-box">
|
<div className="avatar-box">
|
||||||
{fullDisplay ?
|
{fullDisplay ?
|
||||||
<LetterTile topic={this.props.userFrom}
|
<LetterTile
|
||||||
title={this.props.userName} avatar={avatar} /> :
|
topic={this.props.userFrom}
|
||||||
|
title={this.props.userName}
|
||||||
|
avatar={avatar} /> :
|
||||||
null}
|
null}
|
||||||
</div> :
|
</div> :
|
||||||
null}
|
null}
|
||||||
@@ -3917,12 +3914,12 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
handleShowContextMenuMessage(params) {
|
handleShowContextMenuMessage(params) {
|
||||||
params.topicName = this.state.topic;
|
params.topicName = this.state.topic;
|
||||||
var menuItems = [ContextMenuItems["message_delete"]];
|
var menuItems = ["message_delete"];
|
||||||
var topic = Tinode.getTopic(params.topicName);
|
var topic = Tinode.getTopic(params.topicName);
|
||||||
if (topic) {
|
if (topic) {
|
||||||
var acs = topic.getAccessMode();
|
var acs = topic.getAccessMode();
|
||||||
if (acs && acs.isDeleter()) {
|
if (acs && acs.isDeleter()) {
|
||||||
menuItems.push(ContextMenuItems["message_delete_hard"]);
|
menuItems.push("message_delete_hard");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.props.showContextMenu(params, menuItems);
|
this.props.showContextMenu(params, menuItems);
|
||||||
@@ -3937,7 +3934,7 @@ class MessagesView extends React.Component {
|
|||||||
if (this.state.topic) {
|
if (this.state.topic) {
|
||||||
var messageNodes = [];
|
var messageNodes = [];
|
||||||
var topic = Tinode.getTopic(this.state.topic);
|
var topic = Tinode.getTopic(this.state.topic);
|
||||||
var groupTopic = topic.getType() === "grp";
|
var groupTopic = topic.getType() == "grp";
|
||||||
var previousFrom = null;
|
var previousFrom = null;
|
||||||
for (var i=0; i<this.state.messages.length; i++) {
|
for (var i=0; i<this.state.messages.length; i++) {
|
||||||
var msg = this.state.messages[i];
|
var msg = this.state.messages[i];
|
||||||
@@ -3988,9 +3985,10 @@ class MessagesView extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastSeen = null;
|
let lastSeen = null;
|
||||||
var cont = Tinode.getMeTopic().getContact(this.state.topic);
|
let topicType = Tinode.topicType(cont.topic);
|
||||||
if (cont && Tinode.topicType(cont.topic) === "p2p") {
|
let cont = this.props.tinode.getMeTopic().getContact(this.state.topic);
|
||||||
|
if (cont && topicType == "p2p") {
|
||||||
if (cont.online) {
|
if (cont.online) {
|
||||||
lastSeen = "online now";
|
lastSeen = "online now";
|
||||||
} else if (cont.seen) {
|
} else if (cont.seen) {
|
||||||
@@ -4011,8 +4009,10 @@ class MessagesView extends React.Component {
|
|||||||
:
|
:
|
||||||
null}
|
null}
|
||||||
<div className="avatar-box">
|
<div className="avatar-box">
|
||||||
<LetterTile avatar={avatar}
|
<LetterTile
|
||||||
topic={this.state.topic} title={this.state.title} />
|
avatar={avatar}
|
||||||
|
topic={this.state.topic}
|
||||||
|
title={this.state.title} />
|
||||||
<span className={online} />
|
<span className={online} />
|
||||||
</div>
|
</div>
|
||||||
<div id="topic-title-group">
|
<div id="topic-title-group">
|
||||||
@@ -4505,7 +4505,7 @@ class TinodeWeb extends React.Component {
|
|||||||
}
|
}
|
||||||
// Topic for MessagesView selector.
|
// Topic for MessagesView selector.
|
||||||
if (hash.path.length > 1 && hash.path[1] != this.state.topicSelected) {
|
if (hash.path.length > 1 && hash.path[1] != this.state.topicSelected) {
|
||||||
var tp = this.state.tinode.topicType(hash.path[1]);
|
var tp = Tinode.topicType(hash.path[1]);
|
||||||
if (tp) {
|
if (tp) {
|
||||||
this.setState({
|
this.setState({
|
||||||
topicSelected: hash.path[1],
|
topicSelected: hash.path[1],
|
||||||
@@ -5023,11 +5023,11 @@ class TinodeWeb extends React.Component {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
subscribed ? {title: "Info", handler: this.handleShowInfoView} : null,
|
subscribed ? {title: "Info", handler: this.handleShowInfoView} : null,
|
||||||
subscribed ? ContextMenuItems["messages_clear"] : null,
|
subscribed ? "messages_clear" : null,
|
||||||
subscribed && deleter ? ContextMenuItems["messages_clear_hard"] : null,
|
subscribed && deleter ? "messages_clear_hard" : null,
|
||||||
subscribed ? (muted ? ContextMenuItems["topic_unmute"] : ContextMenuItems["topic_mute"]) : null,
|
subscribed ? (muted ? "topic_unmute" : "topic_mute") : null,
|
||||||
subscribed ? (blocked ? ContextMenuItems["topic_unblock"] : ContextMenuItems["topic_block"]) : null,
|
subscribed ? (blocked ? "topic_unblock" : "topic_block") : null,
|
||||||
ContextMenuItems["topic_delete"]
|
"topic_delete"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5085,15 +5085,19 @@ class TinodeWeb extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div id="app-container">
|
<div id="app-container">
|
||||||
<ContextMenu
|
{this.state.contextMenuVisible ?
|
||||||
bounds={this.state.contextMenuBounds}
|
<ContextMenu
|
||||||
clickAt={this.state.contextMenuClickAt}
|
tinode={this.state.tinode}
|
||||||
visible={this.state.contextMenuVisible}
|
bounds={this.state.contextMenuBounds}
|
||||||
params={this.state.contextMenuParams}
|
clickAt={this.state.contextMenuClickAt}
|
||||||
items={this.state.contextMenuItems}
|
params={this.state.contextMenuParams}
|
||||||
hide={this.handleHideContextMenu}
|
items={this.state.contextMenuItems}
|
||||||
onAction={this.handleContextMenuAction}
|
hide={this.handleHideContextMenu}
|
||||||
onError={this.handleError} />
|
onAction={this.handleContextMenuAction}
|
||||||
|
onError={this.handleError} />
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
<SidepanelView
|
<SidepanelView
|
||||||
connected={this.state.connected}
|
connected={this.state.connected}
|
||||||
@@ -5142,6 +5146,7 @@ class TinodeWeb extends React.Component {
|
|||||||
showContextMenu={this.handleShowContextMenu} />
|
showContextMenu={this.handleShowContextMenu} />
|
||||||
|
|
||||||
<MessagesView
|
<MessagesView
|
||||||
|
tinode={this.state.tinode}
|
||||||
connected={this.state.connected}
|
connected={this.state.connected}
|
||||||
online={this.state.topicSelectedOnline}
|
online={this.state.topicSelectedOnline}
|
||||||
acs={this.state.topicSelectedAcs}
|
acs={this.state.topicSelectedAcs}
|
||||||
@@ -5165,6 +5170,7 @@ class TinodeWeb extends React.Component {
|
|||||||
|
|
||||||
{this.state.showInfoPanel ?
|
{this.state.showInfoPanel ?
|
||||||
<InfoView
|
<InfoView
|
||||||
|
tinode={this.state.tinode}
|
||||||
connected={this.state.connected}
|
connected={this.state.connected}
|
||||||
displayMobile={this.state.displayMobile}
|
displayMobile={this.state.displayMobile}
|
||||||
topic={this.state.topicSelected}
|
topic={this.state.topicSelected}
|
||||||
|
|||||||
Reference in New Issue
Block a user