merge next

This commit is contained in:
or-else
2021-08-19 16:22:33 -07:00
17 changed files with 788 additions and 774 deletions
+79 -54
View File
@@ -91,6 +91,7 @@ class InfoView extends React.Component {
owner: false,
admin: false,
sharer: false,
deleter: false,
muted: false,
address: null,
groupTopic: undefined,
@@ -115,6 +116,7 @@ class InfoView extends React.Component {
this.resetSubs = this.resetSubs.bind(this);
this.resetDesc = this.resetDesc.bind(this);
this.resetTags = this.resetTags.bind(this);
this.onMetaDesc = this.onMetaDesc.bind(this);
this.onSubsUpdated = this.onSubsUpdated.bind(this);
this.handleImageChanged = this.handleImageChanged.bind(this);
@@ -147,6 +149,7 @@ class InfoView extends React.Component {
this.setState({topic: props.topic});
this.resetDesc(topic, props);
this.resetSubs(topic, props);
this.resetTags(topic);
}
}
@@ -199,6 +202,7 @@ class InfoView extends React.Component {
owner: acs && acs.isOwner(),
admin: acs && acs.isAdmin(),
sharer: acs && acs.isSharer(),
deleter: acs && acs.isDeleter(),
muted: acs && acs.isMuted(),
fullName: _clip(topic.public ? topic.public.fn : undefined, MAX_TITLE_LENGTH),
@@ -215,8 +219,15 @@ class InfoView extends React.Component {
auth: defacs.auth,
anon: defacs.anon
});
}
if (topic.getType() == 'grp' && acs && acs.isOwner()) {
resetTags(topic) {
if (topic.getType() != 'grp') {
return;
}
const acs = topic.getAccessMode();
if (acs && acs.isOwner()) {
// Requesting tags: owner is editing the topic.
topic.getMeta(topic.startMetaQuery().withTags().build());
}
@@ -257,13 +268,12 @@ class InfoView extends React.Component {
}
handlePermissionsChanged(which, perm) {
console.log("handlePermissionsChanged", which, perm);
switch (which) {
case 'auth':
this.props.onTopicDescUpdate(this.props.topic, null, null, {auth: perm});
this.props.onTopicDescUpdateRequest(this.props.topic, null, null, {auth: perm});
break;
case 'anon':
this.props.onTopicDescUpdate(this.props.topic, null, null, {anon: perm});
this.props.onTopicDescUpdateRequest(this.props.topic, null, null, {anon: perm});
break;
case 'mode':
case 'want':
@@ -276,10 +286,11 @@ class InfoView extends React.Component {
this.props.onChangePermissions(this.props.topic, perm, this.state.userPermissionsEdited);
break;
}
this.handleBackNavigate();
}
handleLaunchPermissionsEditor(which, uid) {
console.log(which, uid);
const {formatMessage} = this.props.intl;
let toEdit, toCompare, toSkip, titleEdit, titleCompare, userTitle, userAvatar;
switch (which) {
@@ -339,7 +350,7 @@ class InfoView extends React.Component {
break;
}
default:
console.log("Unknown permission editing mode '" + which + "'");
console.error("Unknown permission editing mode '" + which + "'");
return;
}
this.setState({
@@ -396,14 +407,19 @@ class InfoView extends React.Component {
return;
}
const isMe = this.props.tinode.isMe(params.topicName);
const menuItems = [
{title: formatMessage(messages.edit_permissions), handler: () => {
this.handleLaunchPermissionsEditor('user', params.topicName);
}},
'member_delete',
user.acs.isMuted() ? 'member_unmute' : 'member_mute',
user.acs.isJoiner() ? 'member_block' : 'member_unblock'
this.handleLaunchPermissionsEditor(isMe ? 'want' : 'user', params.topicName);
}}
];
if (!isMe) {
menuItems.push('member_delete');
}
menuItems.push(user.acs.isMuted() ? 'member_unmute' : 'member_mute');
if (!isMe) {
menuItems.push(user.acs.isJoiner() ? 'member_block' : 'member_unblock');
}
this.props.showContextMenu({
topicName: this.props.topic,
x: params.x,
@@ -413,7 +429,6 @@ class InfoView extends React.Component {
render() {
const args = (this.props.panel || 'info').split('/');
console.log("args:", args);
const view = args[0];
args.shift();
@@ -464,17 +479,31 @@ class InfoView extends React.Component {
tinode={this.props.tinode}
topic={this.props.topic}
onCredAdd={this.props.onCredAdd}
onTopicTagsUpdate={this.props.onTopicTagsUpdate}
onTopicTagsUpdateRequest={this.props.onTopicTagsUpdateRequest}
onCredConfirm={this.props.onCredConfirm}
onCredDelete={this.props.onCredDelete}
onUpdateTopicDesc={this.props.onTopicDescUpdate}
onUpdateTags={this.props.onUpdateTags}
onUpdateTopicDesc={this.props.onTopicDescUpdateRequest}
onError={this.props.onError} />
:
view == 'security' ?
<TopicSecurity
tinode={this.props.tinode}
topic={this.props.topic}
owner={this.state.owner}
admin={this.state.admin}
sharer={this.state.sharer}
deleter={this.state.deleter}
muted={this.state.muted}
groupTopic={this.state.groupTopic}
channel={this.state.channel}
access={this.state.access}
modeGiven={this.state.modeGiven}
modeWant={this.state.modeWant}
modeGiven2={this.state.modeGiven2}
modeWant2={this.state.modeWant2}
auth={this.state.auth}
anon={this.state.anon}
onShowAlert={this.props.onShowAlert}
onDeleteMessages={this.props.onDeleteMessages}
onLeaveTopic={this.props.onLeaveTopic}
@@ -533,17 +562,15 @@ class InfoView extends React.Component {
</div> : null}
</div>
<div className="hr" />
<div className="panel-form-column">
<div className="panel-form-row">
<label>
<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} />
</div>
<div className="panel-form-row">
<label>
<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} />
</div>
<div className="hr" />
<div className="panel-form-column">
<div className="panel-form-row">
<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." />
@@ -552,36 +579,34 @@ class InfoView extends React.Component {
{this.state.groupTopic && this.state.sharer ?
<>
<div className="hr" />
<div className="panel-form-column">
<div className="panel-form-row">
<label className="small">
<FormattedMessage id="label_group_members" defaultMessage="Group members:"
description="Section title or label" />
</label>
</div>
<div className="panel-form-row">
<a href="#" className="flat-button" onClick={this.handleShowAddMembers}>
<i className="material-icons">person_add</i> &nbsp;<FormattedMessage id="button_add_members"
defaultMessage="Add members" description="Flat button [Add members] (to topic)" />
</a>
</div>
<FormattedMessage id="group_has_no_members" defaultMessage="No members"
description="Shown in place of group members">{
(no_members) => <ContactList
tinode={this.props.tinode}
contacts={this.state.contactList}
myUserId={this.props.myUserId}
emptyListMessage={no_members}
topicSelected={this.state.selectedContact}
showOnline={false}
showUnread={false}
showMode={true}
noScroll={true}
onTopicSelected={this.handleMemberSelected}
showContextMenu={this.state.admin ? this.handleContextMenu : false}
/>
}</FormattedMessage>
<div className="panel-form-row">
<label className="small">
<FormattedMessage id="label_group_members" defaultMessage="Group members:"
description="Section title or label" />
</label>
</div>
<div className="panel-form-row">
<a href="#" className="flat-button" onClick={this.handleShowAddMembers}>
<i className="material-icons">person_add</i> &nbsp;<FormattedMessage id="button_add_members"
defaultMessage="Add members" description="Flat button [Add members] (to topic)" />
</a>
</div>
<FormattedMessage id="group_has_no_members" defaultMessage="No members"
description="Shown in place of group members">{
(no_members) => <ContactList
tinode={this.props.tinode}
contacts={this.state.contactList}
myUserId={this.props.myUserId}
emptyListMessage={no_members}
topicSelected={this.state.selectedContact}
showOnline={false}
showUnread={false}
showMode={true}
noScroll={true}
onTopicSelected={this.handleMemberSelected}
showContextMenu={this.state.admin ? this.handleContextMenu : false}
/>
}</FormattedMessage>
</>
:
null
+44 -51
View File
@@ -98,7 +98,7 @@ class MessagesView extends React.Component {
this.handleScrollEvent = this.handleScrollEvent.bind(this);
this.handleDescChange = this.handleDescChange.bind(this);
this.handleSubsUpdated = this.handleSubsUpdated.bind(this);
this.handleNewMessage = this.handleNewMessage.bind(this);
this.handleMessageUpdate = this.handleMessageUpdate.bind(this);
this.handleAllMessagesReceived = this.handleAllMessagesReceived.bind(this);
this.handleInfoReceipt = this.handleInfoReceipt.bind(this);
this.handleImagePostview = this.handleImagePostview.bind(this);
@@ -144,7 +144,7 @@ class MessagesView extends React.Component {
// or vertical shrinking.
componentDidUpdate(prevProps, prevState) {
if (this.messagesScroller) {
if (prevState.topic != this.state.topic || prevState.messages.length != this.state.messages.length) {
if (prevState.topic != this.state.topic || prevState.latestMsgSeq != this.state.latestMsgSeq) {
// New message
this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;
} else if (prevProps.viewportHeight > this.props.viewportHeight) {
@@ -160,7 +160,7 @@ class MessagesView extends React.Component {
}
if (topic) {
topic.onData = this.handleNewMessage;
topic.onData = this.handleMessageUpdate;
topic.onAllMessagesReceived = this.handleAllMessagesReceived;
topic.onInfo = this.handleInfoReceipt;
topic.onMetaDesc = this.handleDescChange;
@@ -228,7 +228,8 @@ class MessagesView extends React.Component {
if (!nextProps.topic) {
// Default state: no topic.
nextState = {
messages: [],
latestMsgSeq: -1,
latestClearId: -1,
onlineSubs: [],
topic: null,
title: '',
@@ -261,7 +262,6 @@ class MessagesView extends React.Component {
if (topic) {
// Topic exists.
const msgs = [];
const subs = [];
if (nextProps.connected) {
@@ -272,14 +272,7 @@ class MessagesView extends React.Component {
});
}
topic.messages(function(msg) {
if (!msg.deleted) {
msgs.push(msg);
}
});
Object.assign(nextState, {
messages: msgs,
onlineSubs: subs
});
@@ -306,12 +299,15 @@ class MessagesView extends React.Component {
});
}
Object.assign(nextState, {
latestMsgSeq: topic.maxMsgSeq(),
latestClearId: topic.maxClearId(),
channel: topic.isChannelType()
});
} else {
// Invalid topic.
Object.assign(nextState, {
messages: [],
latestMsgSeq: -1,
latestClearId: -1,
onlineSubs: [],
title: '',
avatar: null,
@@ -401,10 +397,12 @@ class MessagesView extends React.Component {
const topic = this.props.tinode.getTopic(this.state.topic);
if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {
newState.fetchingMessages = true;
topic.getMessagesPage(MESSAGES_PAGE).catch((err) => {
this.setState({fetchingMessages: false});
this.props.onError(err.message, 'err');
});
topic.getMessagesPage(MESSAGES_PAGE)
.then(() => this.setState({fetchingMessages: false}))
.catch((err) => {
this.setState({fetchingMessages: false});
this.props.onError(err.message, 'err');
});
}
}
return newState;
@@ -521,33 +519,32 @@ class MessagesView extends React.Component {
}
}
handleNewMessage(msg) {
// Regenerate messages list
// The 'msg' could be false-ish if some message ranges were deleted.
handleMessageUpdate(msg) {
const topic = this.props.tinode.getTopic(this.state.topic);
const newState = {messages: []};
topic.messages((m) => {
if (!m.deleted) {
newState.messages.push(m);
}
});
// msg could be null if one or more messages were deleted.
if (msg && !msg.deleted) {
// If the message is added to the end of the message list,
// scroll to the bottom.
if (topic.isNewMessage(msg.seq)) {
newState.scrollPosition = 0;
}
// Aknowledge messages except own messages. They are
// automatically assumed to be read and recived.
const status = topic.msgStatus(msg, true);
if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {
this.postReadNotification(msg.seq);
}
this.props.onData(msg);
if (!msg) {
// msg could be null if one or more messages were deleted.
// Updating state to force redraw.
this.setState({latestClearId: topic.maxClearId()});
return;
}
this.setState(newState);
this.setState({latestMsgSeq: topic.maxMsgSeq()});
// If the message is added to the end of the message list,
// scroll to the bottom.
if (topic.isNewMessage(msg.seq)) {
this.setState({scrollPosition: 0});
}
// Aknowledge messages except own messages. They are
// automatically assumed to be read and recived.
const status = topic.msgStatus(msg, true);
if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {
this.postReadNotification(msg.seq);
}
// This will send "received" notifications right away.
this.props.onData(msg);
}
handleAllMessagesReceived(count) {
@@ -798,7 +795,8 @@ class MessagesView extends React.Component {
}
handleCancelUpload(seq, uploader) {
const found = this.state.messages.find(msg => msg.seq == seq);
const topic = this.props.tinode.getTopic(this.state.topic);
const found = topic.findMessage(seq);
if (found) {
found._cancelled = true;
}
@@ -1019,13 +1017,8 @@ class MessagesView extends React.Component {
let messageNodes = [];
let previousFrom = null;
let chatBoxClass = null;
for (let i=0; i<this.state.messages.length; i++) {
let msg = this.state.messages[i];
let nextFrom = null;
if (i + 1 < this.state.messages.length) {
nextFrom = this.state.messages[i+1].from || 'chan';
}
topic.messages((msg, prev, next, i) => {
let nextFrom = next ? (next.from || null) : 'chan';
let sequence = 'single';
let thisFrom = msg.from || 'chan';
@@ -1085,7 +1078,7 @@ class MessagesView extends React.Component {
key={msg.seq} />
);
}
});
let lastSeen = null;
if (isChannel) {
+10 -7
View File
@@ -121,7 +121,7 @@ class TinodeWeb extends React.Component {
this.handleNewTopicCreated = this.handleNewTopicCreated.bind(this);
this.handleTopicUpdateRequest = this.handleTopicUpdateRequest.bind(this);
this.handleChangePermissions = this.handleChangePermissions.bind(this);
this.handleTagsUpdated = this.handleTagsUpdated.bind(this);
this.handleTagsUpdateRequest = this.handleTagsUpdateRequest.bind(this);
this.handleLogout = this.handleLogout.bind(this);
this.handleDeleteAccount = this.handleDeleteAccount.bind(this);
this.handleDeleteTopicRequest = this.handleDeleteTopicRequest.bind(this);
@@ -225,7 +225,7 @@ class TinodeWeb extends React.Component {
serviceWorkerChannel.addEventListener('message', this.handlePushMessage);
} else {
// Safari is broken by design.
console.warn('Your browser does not support BroadcastChannel. Some features will not be available');
console.warn("Your browser does not support BroadcastChannel. Some features will not be available");
}
// Window/tab visible or invisible for pausing timers.
@@ -565,8 +565,9 @@ class TinodeWeb extends React.Component {
cred = Tinode.credential(cred);
// Try to login with login/password. If they are not available, try token. If no token, ask for login/password.
let promise = null;
const token = this.tinode.getAuthToken();
let token = this.tinode.getAuthToken();
if (login && password) {
token = null;
this.setState({password: null});
promise = this.tinode.loginBasic(login, password, cred);
} else if (token) {
@@ -594,7 +595,9 @@ class TinodeWeb extends React.Component {
autoLogin: false
});
this.handleError(err.message, 'err');
localStorage.removeItem('auth-token');
if (token) {
this.handleLogout();
}
HashNavigation.navigateTo('');
});
} else {
@@ -1275,7 +1278,7 @@ class TinodeWeb extends React.Component {
}
}
handleTagsUpdated(topicName, tags) {
handleTagsUpdateRequest(topicName, tags) {
const topic = this.tinode.getTopic(topicName);
if (topic) {
topic.setMeta({tags: tags}).catch((err) => {
@@ -1694,7 +1697,7 @@ class TinodeWeb extends React.Component {
errorActionText={this.state.errorActionText}
onNavigate={this.infoNavigator}
onTopicDescUpdate={this.handleTopicUpdateRequest}
onTopicDescUpdateRequest={this.handleTopicUpdateRequest}
onShowAlert={this.handleShowAlert}
onChangePermissions={this.handleChangePermissions}
onMemberUpdateRequest={this.handleMemberUpdateRequest}
@@ -1704,7 +1707,7 @@ class TinodeWeb extends React.Component {
onBlockTopic={this.handleBlockTopicRequest}
onReportTopic={this.handleReportTopic}
onAddMember={this.handleManageGroupMembers}
onTopicTagsUpdate={this.handleTagsUpdated}
onTopicTagsUpdateRequest={this.handleTagsUpdateRequest}
onInitFind={this.tnInitFind}
onError={this.handleError}