archiving kind of works but buggy

This commit is contained in:
or-else
2019-02-03 12:48:47 +03:00
parent 7e30c2acc6
commit fc96d200ed
15 changed files with 308 additions and 130 deletions
+3 -2
View File
@@ -777,8 +777,9 @@ i.search {
.contact-box .action-text { .contact-box .action-text {
text-align: center; text-align: center;
font-size: 1.05rem; font-size: 0.95rem;
margin: 0.5rem auto 0.5rem auto; line-height: 3.5rem;
margin: 0 auto 0 auto;
cursor: pointer; cursor: pointer;
} }
+1 -1
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -1,5 +1,6 @@
{ {
"archived_contacts": "Archived contacts ({count})", "archived_contacts": "Archived contacts ({count})",
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯",
"full_name_prompt": "Full name, e.g. John Doe", "full_name_prompt": "Full name, e.g. John Doe",
"email_prompt": "Email, e.g. jdoe@example.com", "email_prompt": "Email, e.g. jdoe@example.com",
"button_sign_up": "Sign up", "button_sign_up": "Sign up",
+2 -2
View File
@@ -1,5 +1,5 @@
{ {
"contacts_not_found": ":!![OBSOLETE]!!:Чатов нет<br />¯∖_(ツ)_/¯", "contacts_not_found": "Чатов нет<br />¯∖_(ツ)_/¯",
"full_name_prompt": "Полное имя, напр. Иван Петров", "full_name_prompt": "Полное имя, напр. Иван Петров",
"email_prompt": "Email, напр. ivan@example.com", "email_prompt": "Email, напр. ivan@example.com",
"button_sign_up": "Создать счет", "button_sign_up": "Создать счет",
@@ -125,5 +125,5 @@
"save_attachment": "сохранить", "save_attachment": "сохранить",
"menu_item_archive_topic": "В архив", "menu_item_archive_topic": "В архив",
"archived_contacts": "Чаты в архиве ({count})", "archived_contacts": "Чаты в архиве ({count})",
"sidepanel_title_archive": "Архив" "sidepanel_title_archive": "Архив чатов"
} }
+2
View File
@@ -1,6 +1,7 @@
{ {
"en": { "en": {
"archived_contacts": "Archived contacts ({count})", "archived_contacts": "Archived contacts ({count})",
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯",
"full_name_prompt": "Full name, e.g. John Doe", "full_name_prompt": "Full name, e.g. John Doe",
"email_prompt": "Email, e.g. jdoe@example.com", "email_prompt": "Email, e.g. jdoe@example.com",
"button_sign_up": "Sign up", "button_sign_up": "Sign up",
@@ -128,6 +129,7 @@
"title_manage_tags": "Manage tags" "title_manage_tags": "Manage tags"
}, },
"ru": { "ru": {
"contacts_not_found": "Чатов нет<br />¯∖_(ツ)_/¯",
"full_name_prompt": "Полное имя, напр. Иван Петров", "full_name_prompt": "Полное имя, напр. Иван Петров",
"email_prompt": "Email, напр. ivan@example.com", "email_prompt": "Email, напр. ivan@example.com",
"button_sign_up": "Создать счет", "button_sign_up": "Создать счет",
+48 -28
View File
@@ -1,62 +1,82 @@
/* ContactsView holds all contacts-related stuff */ /* ContactsView holds all contacts-related stuff */
import React from 'react'; import React from 'react';
import { FormattedHTMLMessage } from 'react-intl'; import { FormattedHTMLMessage, defineMessages } from 'react-intl';
import ContactList from '../widgets/contact-list.jsx'; import ContactList from '../widgets/contact-list.jsx';
import { updateFavicon } from '../lib/utils.js'; import { updateFavicon } from '../lib/utils.js';
const messages = defineMessages({
archived_contacts_title: {
id: "archived_contacts",
defaultMessage: "Archived contacts ({count})",
description: "Label for archived chats"
}
});
export default class ContactsView extends React.Component { export default class ContactsView extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.handleAction = this.handleAction.bind(this);
this.state = ContactsView.getDerivedStateFromProps(props, {}); this.state = ContactsView.getDerivedStateFromProps(props, {});
} }
static getDerivedStateFromProps(nextProps, prevState) { static getDerivedStateFromProps(nextProps, prevState) {
console.log(nextProps.archive ? "show archive" : "show active"); const contacts = [];
let contacts = [];
let unreadThreads = 0; let unreadThreads = 0;
let archivedCount = 0; let archivedCount = 0;
nextProps.chatList.map((c) => { nextProps.chatList.map((c) => {
if (c.private && c.private.arch) { if (c.private && c.private.arch) {
console.log("Archived topic", c); if (nextProps.archive) {
archivedCount ++; contacts.push(c);
} else {
archivedCount ++;
}
} else if (!nextProps.archive) {
contacts.push(c);
unreadThreads += c.unread > 0 ? 1 : 0;
} }
unreadThreads += c.unread > 0 ? 1 : 0;
contacts.push(c);
}); });
contacts.sort(function(a, b){ contacts.sort(function(a, b) {
return (b.touched || 0) - (a.touched || 0); return (b.touched || 0) - (a.touched || 0);
}); });
if (archivedCount > 0) {
contacts.push({
action: 'archive',
title: messages.archived_contacts_title,
values: {count: archivedCount}
});
}
updateFavicon(unreadThreads); updateFavicon(unreadThreads);
return {contactList: contacts, archivedCount: archivedCount}; return {contactList: contacts, archivedCount: archivedCount};
} }
handleAction(action) {
this.props.onShowArchive();
}
render() { render() {
const archivedCount = this.state.archivedCount;
return ( return (
<React.Fragment> <FormattedHTMLMessage id="contacts_not_found"
<FormattedHTMLMessage id="contacts_not_found" defaultMessage="You have no chats<br />¯∖_(ツ)_/¯"
defaultMesage="You have no chats<br />¯∖_(ツ)_/¯" description="HTML message shown in ContactList when no contacts are found">{
description="HTML message shown in ContactList when no contacts are found">{ (no_contacts) => <ContactList
(no_contacts) => <ContactList connected={this.props.connected}
connected={this.props.connected} contacts={this.state.contactList}
contacts={this.state.contactList} emptyListMessage={no_contacts}
emptyListMessage={no_contacts} topicSelected={this.props.topicSelected}
topicSelected={this.props.topicSelected} myUserId={this.props.myUserId}
myUserId={this.props.myUserId} showOnline={true}
showOnline={true} showUnread={true}
showUnread={true} onTopicSelected={this.props.onTopicSelected}
onTopicSelected={this.props.onTopicSelected} showContextMenu={this.props.showContextMenu}
showContextMenu={this.props.showContextMenu} /> onAction={this.handleAction} />
}</FormattedHTMLMessage> }</FormattedHTMLMessage>
{archivedCount > 0 ? <FormattedMessage id="archived_contacts"
defaultMessage="Archived contacts ({count})"
description="Label for archived chats" values={{count: archivedCount}}/> : null}
</React.Fragment>
); );
} }
}; };
+4 -4
View File
@@ -98,7 +98,7 @@ class InfoView extends React.Component {
// No need to separately handle component mount. // No need to separately handle component mount.
componentDidUpdate(props) { componentDidUpdate(props) {
let topic = this.props.tinode.getTopic(props.topic); const topic = this.props.tinode.getTopic(props.topic);
if (!topic) { if (!topic) {
return; return;
} }
@@ -126,7 +126,7 @@ class InfoView extends React.Component {
} }
componentWillUnmount() { componentWillUnmount() {
var topic = this.props.tinode.getTopic(this.props.topic); const topic = this.props.tinode.getTopic(this.props.topic);
if (!topic) { if (!topic) {
return; return;
} }
@@ -185,7 +185,7 @@ class InfoView extends React.Component {
} }
onMetaDesc(desc) { onMetaDesc(desc) {
var topic = this.props.tinode.getTopic(this.props.topic); const topic = this.props.tinode.getTopic(this.props.topic);
if (!topic) { if (!topic) {
return; return;
} }
@@ -197,7 +197,7 @@ class InfoView extends React.Component {
} }
onSubsUpdated() { onSubsUpdated() {
var topic = this.props.tinode.getTopic(this.props.topic); const topic = this.props.tinode.getTopic(this.props.topic);
if (!topic) { if (!topic) {
return; return;
} }
+3 -2
View File
@@ -53,7 +53,7 @@ const messages = defineMessages({
'archive': { 'archive': {
id: 'sidepanel_title_archive', id: 'sidepanel_title_archive',
description: 'Sidepanel title for ContactsView-Archive.', description: 'Sidepanel title for ContactsView-Archive.',
defaultMessage: 'Archived' defaultMessage: 'Archived Chats'
} }
}); });
@@ -148,7 +148,8 @@ class SidepanelView extends React.Component {
archive={view == 'archive'} archive={view == 'archive'}
chatList={this.props.chatList} chatList={this.props.chatList}
showContextMenu={this.props.showContextMenu} showContextMenu={this.props.showContextMenu}
onTopicSelected={this.props.onTopicSelected} /> : onTopicSelected={this.props.onTopicSelected}
onShowArchive={this.props.onShowArchive} /> :
view === 'newtpk' ? view === 'newtpk' ?
<NewTopicView <NewTopicView
+17 -10
View File
@@ -80,6 +80,7 @@ class TinodeWeb extends React.Component {
this.handleUpdateAccountTagsRequest = this.handleUpdateAccountTagsRequest.bind(this); this.handleUpdateAccountTagsRequest = this.handleUpdateAccountTagsRequest.bind(this);
this.handleSettings = this.handleSettings.bind(this); this.handleSettings = this.handleSettings.bind(this);
this.handleGlobalSettings = this.handleGlobalSettings.bind(this); this.handleGlobalSettings = this.handleGlobalSettings.bind(this);
this.handleShowArchive = this.handleShowArchive.bind(this);
this.handleToggleMessageSounds = this.handleToggleMessageSounds.bind(this); this.handleToggleMessageSounds = this.handleToggleMessageSounds.bind(this);
this.initDesktopAlerts = this.initDesktopAlerts.bind(this); this.initDesktopAlerts = this.initDesktopAlerts.bind(this);
this.togglePushToken = this.togglePushToken.bind(this); this.togglePushToken = this.togglePushToken.bind(this);
@@ -106,7 +107,7 @@ class TinodeWeb extends React.Component {
} }
getBlankState() { getBlankState() {
let settings = LocalStorageUtil.getObject("settings") || {}; const settings = LocalStorageUtil.getObject("settings") || {};
return { return {
connected: false, connected: false,
@@ -206,10 +207,10 @@ class TinodeWeb extends React.Component {
} }
} }
let token = LocalStorageUtil.getObject('keep-logged-in') ? const token = LocalStorageUtil.getObject('keep-logged-in') ?
LocalStorageUtil.getObject('auth-token') : undefined; LocalStorageUtil.getObject('auth-token') : undefined;
let parsedNav = HashNavigation.parseUrlHash(window.location.hash); const parsedNav = HashNavigation.parseUrlHash(window.location.hash);
if (token) { if (token) {
// When reading from storage, date is returned as string. // When reading from storage, date is returned as string.
token.expires = new Date(token.expires); token.expires = new Date(token.expires);
@@ -793,6 +794,12 @@ class TinodeWeb extends React.Component {
HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, '')); HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, ''));
} }
// User chose 'Archived chats'.
handleShowArchive() {
HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,
this.state.myUserId ? 'archive' : ''));
}
// Initialize desktop alerts = push notifications. // Initialize desktop alerts = push notifications.
initDesktopAlerts() { initDesktopAlerts() {
// Google could not be bothered to mention that // Google could not be bothered to mention that
@@ -859,7 +866,7 @@ class TinodeWeb extends React.Component {
// User clicked Cancel button in Setting or Sign Up panel. // User clicked Cancel button in Setting or Sign Up panel.
handleSidepanelCancel() { handleSidepanelCancel() {
var parsed = HashNavigation.parseUrlHash(window.location.hash); const parsed = HashNavigation.parseUrlHash(window.location.hash);
parsed.path[0] = this.state.myUserId ? 'contacts' : ''; parsed.path[0] = this.state.myUserId ? 'contacts' : '';
if (parsed.params) { if (parsed.params) {
delete parsed.params.code; delete parsed.params.code;
@@ -877,7 +884,7 @@ class TinodeWeb extends React.Component {
// Request to start a new topic. New P2P topic requires peer's name. // Request to start a new topic. New P2P topic requires peer's name.
handleNewTopicRequest(peerName, pub, priv, tags) { handleNewTopicRequest(peerName, pub, priv, tags) {
var topicName = peerName || this.tinode.newGroupTopicName(); const topicName = peerName || this.tinode.newGroupTopicName();
if (!peerName) { if (!peerName) {
this.setState( this.setState(
{newGroupTopicParams: {desc: {public: pub, private: {comment: priv}}, tags: tags}}, {newGroupTopicParams: {desc: {public: pub, private: {comment: priv}}, tags: tags}},
@@ -938,9 +945,8 @@ class TinodeWeb extends React.Component {
} }
handleTagsUpdated(topicName, tags) { handleTagsUpdated(topicName, tags) {
var topic = this.tinode.getTopic(topicName); const topic = this.tinode.getTopic(topicName);
if (topic) { if (topic) {
var instance = this;
topic.setMeta({tags: tags}).catch((err) => { topic.setMeta({tags: tags}).catch((err) => {
this.handleError(err.message, 'err'); this.handleError(err.message, 'err');
}); });
@@ -962,7 +968,7 @@ class TinodeWeb extends React.Component {
} }
handleLeaveUnsubRequest(topicName) { handleLeaveUnsubRequest(topicName) {
var topic = this.tinode.getTopic(topicName); const topic = this.tinode.getTopic(topicName);
if (!topic) { if (!topic) {
return; return;
} }
@@ -998,7 +1004,7 @@ class TinodeWeb extends React.Component {
if (topic.isSubscribed()) { if (topic.isSubscribed()) {
subscribed = true; subscribed = true;
let acs = topic.getAccessMode(); const acs = topic.getAccessMode();
muted = acs && acs.isMuted(); muted = acs && acs.isMuted();
blocked = acs && !acs.isJoiner(); blocked = acs && !acs.isJoiner();
deleter = acs && acs.isDeleter(); deleter = acs && acs.isDeleter();
@@ -1041,7 +1047,7 @@ class TinodeWeb extends React.Component {
return; return;
} }
var topic = this.tinode.getTopic(topicName); const topic = this.tinode.getTopic(topicName);
if (!topic) { if (!topic) {
return; return;
} }
@@ -1159,6 +1165,7 @@ class TinodeWeb extends React.Component {
onValidateCredentials={this.handleValidateCredentialsRequest} onValidateCredentials={this.handleValidateCredentialsRequest}
onPasswordResetRequest={this.handlePasswordResetRequest} onPasswordResetRequest={this.handlePasswordResetRequest}
onResetPassword={this.handleResetPassword} onResetPassword={this.handleResetPassword}
onShowArchive={this.handleShowArchive}
onInitFind={this.tnInitFind} onInitFind={this.tnInitFind}
searchResults={this.state.searchResults} searchResults={this.state.searchResults}
+28
View File
@@ -0,0 +1,28 @@
// A panel included into a list of contacts with an action text.
import React from 'react';
import { injectIntl } from 'react-intl';
class ContactAction extends React.PureComponent {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
e.preventDefault();
e.stopPropagation();
this.props.onAction(this.props.action);
}
render() {
const {formatMessage} = this.props.intl;
return (
<li onClick={this.handleClick}>
<div className="action-text">{formatMessage(this.props.title, this.props.values)}</div>
</li>
);
}
};
export default injectIntl(ContactAction);
+6 -1
View File
@@ -31,7 +31,12 @@ class ContactList extends React.Component {
this.props.contacts.map((c) => { this.props.contacts.map((c) => {
if (c.action) { if (c.action) {
// Action item // Action item
contactNodes.push(<ContactAction title={c.title} action={c.action} />); contactNodes.push(
<ContactAction
title={c.title} action={c.action} values={c.values}
key={c.action}
onAction={this.props.onAction}
/>);
} else { } else {
// Normal contact // Normal contact
const key = c.topic ? c.topic : c.user; const key = c.topic ? c.topic : c.user;
+190 -77
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