archiving works, refactored UNSAFE_componentWillReceiveProps in chips-input

This commit is contained in:
or-else
2019-02-06 20:08:50 +03:00
parent 160e2574d4
commit 87947b3de6
12 changed files with 313 additions and 142 deletions
+5 -1
View File
@@ -727,6 +727,10 @@ i.search {
.contact-box > li.selected { .contact-box > li.selected {
background-color: #cfd8dc; /* blue-gray 100 highlight */ background-color: #cfd8dc; /* blue-gray 100 highlight */
} }
.contact-box > li.action {
min-height: 2.5rem;
border-bottom: none;
}
.contact-box .text-box { .contact-box .text-box {
overflow-x: hidden; overflow-x: hidden;
@@ -778,7 +782,7 @@ i.search {
.contact-box .action-text { .contact-box .action-text {
text-align: center; text-align: center;
font-size: 0.95rem; font-size: 0.95rem;
line-height: 3.5rem; line-height: 2.5rem;
margin: 0 auto 0 auto; margin: 0 auto 0 auto;
cursor: pointer; cursor: pointer;
} }
+1 -1
View File
File diff suppressed because one or more lines are too long
+5 -2
View File
@@ -53,10 +53,13 @@ export default class ContactsView extends React.Component {
updateFavicon(unreadThreads); updateFavicon(unreadThreads);
return {contactList: contacts, archivedCount: archivedCount}; return {
contactList: contacts,
archivedCount: archivedCount
};
} }
handleAction(action) { handleAction(action_ignored) {
this.props.onShowArchive(); this.props.onShowArchive();
} }
+30 -24
View File
@@ -69,18 +69,6 @@ class MessagesView extends React.Component {
this.handleBackNavigation = this.handleBackNavigation.bind(this); this.handleBackNavigation = this.handleBackNavigation.bind(this);
} }
// Scroll last message into view on component update e.g. on message received
// or vertical shrinking.
componentDidUpdate(prevProps, prevState) {
if (this.messagesScroller) {
if (prevState.title != this.state.title || prevState.messages.length != this.state.messages.length) {
this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;
} else if (prevProps.viewportHeight > this.props.viewportHeight) {
this.messagesScroller.scrollTop += prevProps.viewportHeight - this.props.viewportHeight;
}
}
}
componentDidMount() { componentDidMount() {
this.propsChange(this.props); this.propsChange(this.props);
if (this.messagesScroller) { if (this.messagesScroller) {
@@ -95,13 +83,31 @@ class MessagesView extends React.Component {
} }
} }
UNSAFE_componentWillReceiveProps(nextProps) { // Scroll last message into view on component update e.g. on message received
this.propsChange(nextProps); // or vertical shrinking.
componentDidUpdate(prevProps, prevState) {
if (this.messagesScroller) {
if (prevState.title != this.state.title || prevState.messages.length != this.state.messages.length) {
this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;
} else if (prevProps.viewportHeight > this.props.viewportHeight) {
this.messagesScroller.scrollTop += prevProps.viewportHeight - this.props.viewportHeight;
}
}
} }
propsChange(props) { //static getDerivedStateFromProps(nextProps, prevState) {
//
//}
// Deprecated!
UNSAFE_componentWillReceiveProps(nextProps) {
this.propsChange(nextProps, this.state);
}
propsChange(props, state) {
const nextState = {};
if (!props || !props.topic) { if (!props || !props.topic) {
let oldTopic = this.state.topic; const oldTopic = state.topic;
this.setState({messages: [], onlineSubs: [], topic: null}, this.leave(oldTopic)); this.setState({messages: [], onlineSubs: [], topic: null}, this.leave(oldTopic));
return; return;
} }
@@ -112,15 +118,15 @@ class MessagesView extends React.Component {
return; return;
} }
let topic = this.props.tinode.getTopic(props.topic); const topic = props.tinode.getTopic(props.topic);
if (!topic) { if (!topic) {
return; return;
} }
let newGroupTopic = Tinode.isNewGroupTopicName(props.topic); let newGroupTopic = Tinode.isNewGroupTopicName(props.topic);
let tryToResubscribe = !this.props.connected && props.connected; let tryToResubscribe = !props.connected && props.connected;
if (props.topic != this.state.topic) { if (props.topic != state.topic) {
let msgs = []; let msgs = [];
let subs = []; let subs = [];
@@ -132,11 +138,11 @@ class MessagesView extends React.Component {
topic.onSubsUpdated = this.handleSubsUpdated; topic.onSubsUpdated = this.handleSubsUpdated;
topic.onPres = this.handleSubsUpdated; topic.onPres = this.handleSubsUpdated;
// Unbind the previous topic from this component. // Unbind the previous topic from this component.
this.leave(this.state.topic); this.leave(state.topic);
this.handleDescChange(topic); this.handleDescChange(topic);
topic.subscribers((sub) => { topic.subscribers((sub) => {
if (sub.online && sub.user != this.props.myUserId) { if (sub.online && sub.user != props.myUserId) {
subs.push(sub); subs.push(sub);
} }
}); });
@@ -180,7 +186,7 @@ class MessagesView extends React.Component {
topic.subscribe(getQuery.build(), setQuery) topic.subscribe(getQuery.build(), setQuery)
.then((ctrl) => { .then((ctrl) => {
this.setState({topic: ctrl.topic}); this.setState({topic: ctrl.topic});
this.props.onNewTopicCreated(props.topic, ctrl.topic); props.onNewTopicCreated(props.topic, ctrl.topic);
// If there are unsent messages, try sending them now. // If there are unsent messages, try sending them now.
topic.queuedMessages((pub) => { topic.queuedMessages((pub) => {
if (!pub._sending && topic.isSubscribed()) { if (!pub._sending && topic.isSubscribed()) {
@@ -189,8 +195,8 @@ class MessagesView extends React.Component {
}); });
}) })
.catch((err) => { .catch((err) => {
this.props.onError(err.message, 'err'); props.onError(err.message, 'err');
const {formatMessage} = this.props.intl; const {formatMessage} = props.intl;
this.setState({ this.setState({
title: formatMessage(messages.not_found), title: formatMessage(messages.not_found),
avatar: null, avatar: null,
+11
View File
@@ -104,6 +104,7 @@ class TinodeWeb extends React.Component {
this.handleValidateCredentialsRequest = this.handleValidateCredentialsRequest.bind(this); this.handleValidateCredentialsRequest = this.handleValidateCredentialsRequest.bind(this);
this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this); this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this);
this.handleResetPassword = this.handleResetPassword.bind(this); this.handleResetPassword = this.handleResetPassword.bind(this);
this.handleContextMenuAction = this.handleContextMenuAction.bind(this);
} }
getBlankState() { getBlankState() {
@@ -1049,6 +1050,16 @@ class TinodeWeb extends React.Component {
}); });
} }
handleContextMenuAction(action, promise, params) {
if (action == 'topic_archive') {
if (promise && params.topicName && params.topicName == this.state.topicSelected) {
promise.then(() => {
this.handleTopicSelected(null);
});
}
}
}
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});
+13 -13
View File
@@ -9,13 +9,7 @@ export default class ChipInput extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = ChipInput.getDerivedStateFromProps(props);
placeholder: props.chips ? '' : props.prompt,
sortedChips: ChipInput.sortChips(props.chips, props.required),
chipIndex: ChipInput.indexChips(props.chips),
input: '',
focused: false
};
this.handleTextInput = this.handleTextInput.bind(this); this.handleTextInput = this.handleTextInput.bind(this);
this.removeChipAt = this.removeChipAt.bind(this); this.removeChipAt = this.removeChipAt.bind(this);
@@ -25,15 +19,21 @@ export default class ChipInput extends React.Component {
this.handleKeyDown = this.handleKeyDown.bind(this); this.handleKeyDown = this.handleKeyDown.bind(this);
} }
UNSAFE_componentWillReceiveProps(nextProps) { static getDerivedStateFromProps(nextProps, prevState) {
this.setState({ const state = {
placeholder: nextProps.chips ? '' : nextProps.prompt,
sortedChips: ChipInput.sortChips(nextProps.chips, nextProps.required), sortedChips: ChipInput.sortChips(nextProps.chips, nextProps.required),
chipIndex: ChipInput.indexChips(nextProps.chips) chipIndex: ChipInput.indexChips(nextProps.chips),
}); input: '',
if (nextProps.chips.length > this.props.chips.length) { focused: prevState && prevState.focused
};
if (!prevState || nextProps.chips.length > prevState.sortedChips.length) {
// Chip added: clear input. // Chip added: clear input.
this.setState({input: ''}); state.input ='';
} }
return state;
} }
// Map chip index to user name // Map chip index to user name
+1 -1
View File
@@ -18,7 +18,7 @@ class ContactAction extends React.PureComponent {
render() { render() {
const {formatMessage} = this.props.intl; const {formatMessage} = this.props.intl;
return ( return (
<li onClick={this.handleClick}> <li onClick={this.handleClick} className="action">
<div className="action-text">{formatMessage(this.props.title, this.props.values)}</div> <div className="action-text">{formatMessage(this.props.title, this.props.values)}</div>
</li> </li>
); );
+133 -58
View File
@@ -80,66 +80,133 @@ class ContextMenu extends React.Component {
// Preconfigured menu items. // Preconfigured menu items.
this.MenuItems = { this.MenuItems = {
'topic_info': {title: formatMessage(messages.info), handler: null}, 'topic_info': {
id: 'topic_info',
'messages_clear': {title: formatMessage(messages.clear_messages), handler: (params, errorHandler) => { title: formatMessage(messages.info),
this.deleteMessages(true, false, params, errorHandler); handler: null
}}, },
'messages_clear_hard': {title: formatMessage(messages.clear_for_all), handler: (params, errorHandler) => { 'messages_clear': {
this.deleteMessages(true, true, params, errorHandler); id: 'messages_clear',
}}, title: formatMessage(messages.clear_messages),
'message_delete': {title: formatMessage(messages.delete), handler: (params, errorHandler) => { handler: (params, errorHandler) => {
this.deleteMessages(false, false, params, errorHandler); return this.deleteMessages(true, false, params, errorHandler);
}},
'message_delete_hard': {title: formatMessage(messages.delete_for_all), handler: (params, errorHandler) => {
this.deleteMessages(false, true, params, errorHandler);
}},
'topic_unmute': {title: formatMessage(messages.unmute), handler: this.topicPermissionSetter.bind(this, '+P')},
'topic_mute': {title: formatMessage(messages.mute), handler: this.topicPermissionSetter.bind(this, '-P')},
'topic_unblock': {title: formatMessage(messages.unblock), handler: this.topicPermissionSetter.bind(this, '+J')},
'topic_block': {title: formatMessage(messages.block), handler: this.topicPermissionSetter.bind(this, '-J')},
'topic_delete': {title: formatMessage(messages.topic_delete), handler: (params, errorHandler) => {
const topic = this.props.tinode.getTopic(params.topicName);
if (!topic) {
console.log("Topic not found: ", params.topicName);
return;
} }
topic.delTopic().catch((err) => { },
if (errorHandler) { 'messages_clear_hard': {
errorHandler(err.message, 'err'); id: 'messages_clear_hard',
} title: formatMessage(messages.clear_for_all),
}); handler: (params, errorHandler) => {
}}, return this.deleteMessages(true, true, params, errorHandler);
'topic_archive': {title: formatMessage(messages.archive), handler: (params, errorHandler) => {
const topic = this.props.tinode.getTopic(params.topicName);
if (!topic) {
console.log("Topic not found: ", params.topicName);
return;
} }
topic.archive(true).catch((err) => { },
if (errorHandler) { 'message_delete': {
errorHandler(err.message, 'err'); id: 'message_delete',
title: formatMessage(messages.delete),
handler: (params, errorHandler) => {
return this.deleteMessages(false, false, params, errorHandler);
}
},
'message_delete_hard': {
id: 'message_delete_hard',
title: formatMessage(messages.delete_for_all),
handler: (params, errorHandler) => {
return this.deleteMessages(false, true, params, errorHandler);
}
},
'topic_unmute': {
id: 'topic_unmute',
title: formatMessage(messages.unmute),
handler: this.topicPermissionSetter.bind(this, '+P')
},
'topic_mute': {
id: 'topic_mute',
title: formatMessage(messages.mute),
handler: this.topicPermissionSetter.bind(this, '-P')
},
'topic_unblock': {
id: 'topic_unblock',
title: formatMessage(messages.unblock),
handler: this.topicPermissionSetter.bind(this, '+J')
},
'topic_block': {
id: 'topic_block',
title: formatMessage(messages.block),
handler: this.topicPermissionSetter.bind(this, '-J')
},
'topic_delete': {
id: 'topic_delete',
title: formatMessage(messages.topic_delete),
handler: (params, errorHandler) => {
const topic = this.props.tinode.getTopic(params.topicName);
if (!topic) {
console.log("Topic not found: ", params.topicName);
return;
} }
}); return topic.delTopic().catch((err) => {
}}, if (errorHandler) {
errorHandler(err.message, 'err');
}
});
}
},
'topic_archive': {
id: 'topic_archive',
title: formatMessage(messages.archive),
handler: (params, errorHandler) => {
const topic = this.props.tinode.getTopic(params.topicName);
if (!topic) {
console.log("Topic not found: ", params.topicName);
return;
}
return topic.archive(true).catch((err) => {
if (errorHandler) {
errorHandler(err.message, 'err');
}
});
}
},
// menu_item_edit_permissions is defined elsewhere. // menu_item_edit_permissions is defined elsewhere.
'permissions': {title: formatMessage({id: 'menu_item_edit_permissions'}), handler: null}, 'permissions': {
'member_delete': {title: formatMessage(messages.member_delete), handler: (params, errorHandler) => { id: 'permissions',
const topic = this.props.tinode.getTopic(params.topicName); title: formatMessage({id: 'menu_item_edit_permissions'}),
if (!topic || !params.user) { handler: null
console.log("Topic or user not found: '" + params.topicName + "', '" + params.user + "'"); },
return; 'member_delete': {
} id: 'member_delete',
topic.delSubscription(params.user).catch((err) => { title: formatMessage(messages.member_delete),
if (errorHandler) { handler: (params, errorHandler) => {
errorHandler(err.message, 'err'); const topic = this.props.tinode.getTopic(params.topicName);
if (!topic || !params.user) {
console.log("Topic or user not found: '" + params.topicName + "', '" + params.user + "'");
return;
} }
}); return topic.delSubscription(params.user).catch((err) => {
}}, if (errorHandler) {
'member_mute': {title: formatMessage(messages.mute), handler: this.topicPermissionSetter.bind(this, '-P')}, errorHandler(err.message, 'err');
'member_unmute': {title: formatMessage(messages.unmute), handler: this.topicPermissionSetter.bind(this, '+P')}, }
'member_block': {title: formatMessage(messages.block), handler: this.topicPermissionSetter.bind(this, '-J')}, });
'member_unblock': {title: formatMessage(messages.unblock), handler: this.topicPermissionSetter.bind(this, '+J')}, }
},
'member_mute': {
id: 'member_mute',
title: formatMessage(messages.mute),
handler: this.topicPermissionSetter.bind(this, '-P')
},
'member_unmute': {
id: 'member_unmute',
title: formatMessage(messages.unmute),
handler: this.topicPermissionSetter.bind(this, '+P')
},
'member_block': {
id: 'member_block',
title: formatMessage(messages.block),
handler: this.topicPermissionSetter.bind(this, '-J')
},
'member_unblock': {
id: 'member_unblock',
title: formatMessage(messages.unblock),
handler: this.topicPermissionSetter.bind(this, '+J')
},
}; };
} }
@@ -176,7 +243,15 @@ class ContextMenu extends React.Component {
if (typeof item == 'string') { if (typeof item == 'string') {
item = this.MenuItems[item]; item = this.MenuItems[item];
} }
item.handler(this.props.params, this.props.onError);
if (!item) {
console.log("Invalid menu item ID", e.currentTarget.dataset.id);
} else {
this.props.onAction(
item.id,
item.handler(this.props.params, this.props.onError),
this.props.params);
}
} }
// Menu Actions // Menu Actions
@@ -200,7 +275,7 @@ class ContextMenu extends React.Component {
topic.delMessagesAll(hard) : topic.delMessagesAll(hard) :
topic.delMessagesList([params.seq], hard); topic.delMessagesList([params.seq], hard);
promise.catch((err) => { return promise.catch((err) => {
if (errorHandler) { if (errorHandler) {
errorHandler(err.message, 'err'); errorHandler(err.message, 'err');
} }
@@ -227,7 +302,7 @@ class ContextMenu extends React.Component {
am = topic.getAccessMode().updateWant(mode).getWant(); am = topic.getAccessMode().updateWant(mode).getWant();
} }
topic.setMeta({sub: {user: params.user, mode: am}}).catch((err) => { return topic.setMeta({sub: {user: params.user, mode: am}}).catch((err) => {
if (errorHandler) { if (errorHandler) {
errorHandler(err.message, 'err'); errorHandler(err.message, 'err');
} }
+111 -39
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