implementing chat archiving (does not work yet)

This commit is contained in:
or-else
2019-02-02 21:31:58 +03:00
parent 6388fedd70
commit 541ead2136
14 changed files with 145 additions and 81 deletions
+7
View File
@@ -775,6 +775,13 @@ i.search {
justify-content: space-around; justify-content: space-around;
} }
.contact-box .action-text {
text-align: center;
font-size: 1.05rem;
margin: 0.5rem auto 0.5rem auto;
cursor: pointer;
}
/* Online-offline indicator positioned inside avatar-box */ /* Online-offline indicator positioned inside avatar-box */
.avatar-box .online, .offline { .avatar-box .online, .offline {
display: inline-block; display: inline-block;
-1
View File
@@ -69,7 +69,6 @@ self.addEventListener('notificationclick', function(event) {
// This is needed for 'Add to Home Screen'. // This is needed for 'Add to Home Screen'.
self.addEventListener('fetch', function(event) { self.addEventListener('fetch', function(event) {
console.log("Fetch event", event.request);
// Workaround for https://bugs.chromium.org/p/chromium/issues/detail?id=823392 // Workaround for https://bugs.chromium.org/p/chromium/issues/detail?id=823392
if (event.request.cache == 'only-if-cached' && event.request.mode != 'same-origin') { if (event.request.cache == 'only-if-cached' && event.request.mode != 'same-origin') {
return; return;
+2 -1
View File
@@ -1,5 +1,5 @@
{ {
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯", "archived_contacts": "Archived contacts ({count})",
"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",
@@ -63,6 +63,7 @@
"sidepanel_title_newtpk": "Start New Chat", "sidepanel_title_newtpk": "Start New Chat",
"sidepanel_title_cred": "Confirm Credentials", "sidepanel_title_cred": "Confirm Credentials",
"sidepanel_title_reset": "Reset Password", "sidepanel_title_reset": "Reset Password",
"sidepanel_title_archive": "Archived",
"update_available": "Update available. <a href=\"\">Reload</a>.", "update_available": "Update available. <a href=\"\">Reload</a>.",
"reconnect_countdown": "Disconnected. Reconnecting in {seconds}s…", "reconnect_countdown": "Disconnected. Reconnecting in {seconds}s…",
"reconnect_now": "Try now", "reconnect_now": "Try now",
+4 -2
View File
@@ -1,5 +1,5 @@
{ {
"contacts_not_found": "Чатов нет<br />¯∖_(ツ)_/¯", "contacts_not_found": ":!![OBSOLETE]!!:Чатов нет<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": "Создать счет",
@@ -123,5 +123,7 @@
"reconnect_countdown": "Нет связи. Подключение через {seconds}с…", "reconnect_countdown": "Нет связи. Подключение через {seconds}с…",
"reconnect_now": "Подключить сейчас.", "reconnect_now": "Подключить сейчас.",
"save_attachment": "сохранить", "save_attachment": "сохранить",
"menu_item_archive_topic": "В архив" "menu_item_archive_topic": "В архив",
"archived_contacts": "Чаты в архиве ({count})",
"sidepanel_title_archive": "Архив"
} }
+5 -3
View File
@@ -1,6 +1,6 @@
{ {
"en": { "en": {
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯", "archived_contacts": "Archived contacts ({count})",
"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",
@@ -64,6 +64,7 @@
"sidepanel_title_newtpk": "Start New Chat", "sidepanel_title_newtpk": "Start New Chat",
"sidepanel_title_cred": "Confirm Credentials", "sidepanel_title_cred": "Confirm Credentials",
"sidepanel_title_reset": "Reset Password", "sidepanel_title_reset": "Reset Password",
"sidepanel_title_archive": "Archived",
"update_available": "Update available. <a href=\"\">Reload</a>.", "update_available": "Update available. <a href=\"\">Reload</a>.",
"reconnect_countdown": "Disconnected. Reconnecting in {seconds}s…", "reconnect_countdown": "Disconnected. Reconnecting in {seconds}s…",
"reconnect_now": "Try now", "reconnect_now": "Try now",
@@ -127,7 +128,6 @@
"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": "Создать счет",
@@ -251,6 +251,8 @@
"reconnect_countdown": "Нет связи. Подключение через {seconds}с…", "reconnect_countdown": "Нет связи. Подключение через {seconds}с…",
"reconnect_now": "Подключить сейчас.", "reconnect_now": "Подключить сейчас.",
"save_attachment": "сохранить", "save_attachment": "сохранить",
"menu_item_archive_topic": "В архив" "menu_item_archive_topic": "В архив",
"archived_contacts": "Чаты в архиве ({count})",
"sidepanel_title_archive": "Архив"
} }
} }
+27 -15
View File
@@ -14,9 +14,15 @@ export default class ContactsView extends React.Component {
} }
static getDerivedStateFromProps(nextProps, prevState) { static getDerivedStateFromProps(nextProps, prevState) {
console.log(nextProps.archive ? "show archive" : "show active");
let contacts = []; let contacts = [];
let unreadThreads = 0; let unreadThreads = 0;
let archivedCount = 0;
nextProps.chatList.map((c) => { nextProps.chatList.map((c) => {
if (c.private && c.private.arch) {
console.log("Archived topic", c);
archivedCount ++;
}
unreadThreads += c.unread > 0 ? 1 : 0; unreadThreads += c.unread > 0 ? 1 : 0;
contacts.push(c); contacts.push(c);
}); });
@@ -26,25 +32,31 @@ export default class ContactsView extends React.Component {
updateFavicon(unreadThreads); updateFavicon(unreadThreads);
return {contactList: contacts}; return {contactList: contacts, archivedCount: archivedCount};
} }
render() { render() {
const archivedCount = this.state.archivedCount;
return ( return (
<FormattedHTMLMessage id="contacts_not_found" <React.Fragment>
defaultMesage="You have no chats<br />¯∖_(ツ)_/¯" <FormattedHTMLMessage id="contacts_not_found"
description="HTML message shown in ContactList when no contacts are found">{ defaultMesage="You have no chats<br />¯∖_(ツ)_/¯"
(no_contacts) => <ContactList description="HTML message shown in ContactList when no contacts are found">{
connected={this.props.connected} (no_contacts) => <ContactList
contacts={this.state.contactList} connected={this.props.connected}
emptyListMessage={no_contacts} contacts={this.state.contactList}
topicSelected={this.props.topicSelected} emptyListMessage={no_contacts}
myUserId={this.props.myUserId} topicSelected={this.props.topicSelected}
showOnline={true} myUserId={this.props.myUserId}
showUnread={true} showOnline={true}
onTopicSelected={this.props.onTopicSelected} showUnread={true}
showContextMenu={this.props.showContextMenu} /> onTopicSelected={this.props.onTopicSelected}
}</FormattedHTMLMessage> showContextMenu={this.props.showContextMenu} />
}</FormattedHTMLMessage>
{archivedCount > 0 ? <FormattedMessage id="archived_contacts"
defaultMessage="Archived contacts ({count})"
description="Label for archived chats" values={{count: archivedCount}}/> : null}
</React.Fragment>
); );
} }
}; };
+7 -1
View File
@@ -49,6 +49,11 @@ const messages = defineMessages({
id: 'sidepanel_title_reset', id: 'sidepanel_title_reset',
description: 'Sidepanel title for PasswordResetView.', description: 'Sidepanel title for PasswordResetView.',
defaultMessage: 'Reset Password' defaultMessage: 'Reset Password'
},
'archive': {
id: 'sidepanel_title_archive',
description: 'Sidepanel title for ContactsView-Archive.',
defaultMessage: 'Archived'
} }
}); });
@@ -134,12 +139,13 @@ class SidepanelView extends React.Component {
onCancel={this.props.onCancel} onCancel={this.props.onCancel}
onError={this.props.onError} /> : onError={this.props.onError} /> :
view === 'contacts' ? (view === 'contacts' || view == 'archive') ?
<ContactsView <ContactsView
tinode={this.props.tinode} tinode={this.props.tinode}
myUserId={this.props.myUserId} myUserId={this.props.myUserId}
connected={this.props.connected} connected={this.props.connected}
topicSelected={this.props.topicSelected} topicSelected={this.props.topicSelected}
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} /> :
+1 -1
View File
@@ -274,7 +274,7 @@ class TinodeWeb extends React.Component {
var hash = HashNavigation.parseUrlHash(window.location.hash); var hash = HashNavigation.parseUrlHash(window.location.hash);
if (hash.path && hash.path.length > 0) { if (hash.path && hash.path.length > 0) {
// Left-side panel selector. // Left-side panel selector.
if (['register','settings','edit','cred','reset','newtpk','contacts',''].includes(hash.path[0])) { if (['register','settings','edit','cred','reset','newtpk','archive','contacts',''].includes(hash.path[0])) {
this.setState({sidePanelSelected: hash.path[0]}); this.setState({sidePanelSelected: hash.path[0]});
} else { } else {
console.log("Unknown sidepanel view", hash.path[0]); console.log("Unknown sidepanel view", hash.path[0]);
+62 -47
View File
@@ -4,6 +4,7 @@ import React from 'react';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
import Contact from './contact.jsx'; import Contact from './contact.jsx';
import ContactAction from './contact-action.jsx';
import { makeImageUrl } from '../lib/blob-helpers.js'; import { makeImageUrl } from '../lib/blob-helpers.js';
@@ -25,69 +26,83 @@ class ContactList extends React.Component {
const {formatMessage} = this.props.intl; const {formatMessage} = this.props.intl;
const showCheckmark = Array.isArray(this.props.topicSelected); const showCheckmark = Array.isArray(this.props.topicSelected);
const contactNodes = []; const contactNodes = [];
let contactsCount = 0;
if (this.props.contacts && this.props.contacts.length > 0) { if (this.props.contacts && this.props.contacts.length > 0) {
this.props.contacts.map((c) => { this.props.contacts.map((c) => {
let key = c.topic ? c.topic : c.user; if (c.action) {
// If filter function is provided, filter out the items // Action item
// which don't satisfy the condition. contactNodes.push(<ContactAction title={c.title} action={c.action} />);
if (this.props.filterFunc && this.props.filter) { } else {
let content = [key]; // Normal contact
if (c.private && c.private.comment) { const key = c.topic ? c.topic : c.user;
content.push(('' + c.private.comment).toLowerCase()); // If filter function is provided, filter out the items
// which don't satisfy the condition.
if (this.props.filterFunc && this.props.filter) {
let content = [key];
if (c.private && c.private.comment) {
content.push(('' + c.private.comment).toLowerCase());
}
if (c.public && c.public.fn) {
content.push(('' + c.public.fn).toLowerCase());
}
if (!this.props.filterFunc(this.props.filter, content)) {
return;
}
} }
if (c.public && c.public.fn) {
content.push(('' + c.public.fn).toLowerCase());
}
if (!this.props.filterFunc(this.props.filter, content)) {
return;
}
}
let selected = showCheckmark ? const selected = showCheckmark ?
(this.props.topicSelected.indexOf(key) > -1) : (this.props.topicSelected.indexOf(key) > -1) :
(this.props.topicSelected === key); (this.props.topicSelected === key);
let badges = []; const badges = [];
if (this.props.showMode) { if (this.props.showMode) {
if (key === this.props.myUserId) { if (key === this.props.myUserId) {
badges.push({name: formatMessage(messages.badge_you), color: 'green'}); badges.push({name: formatMessage(messages.badge_you), color: 'green'});
}
if (c.acs && c.acs.isOwner()) {
badges.push({name: formatMessage(messages.badge_owner), color: 'blue'});
}
} }
if (c.acs && c.acs.isOwner()) { const comment = Array.isArray(c.private) ?
badges.push({name: formatMessage(messages.badge_owner), color: 'blue'}); c.private.join(',') : (c.private ? c.private.comment : null);
}
}
let comment = Array.isArray(c.private) ?
c.private.join(',') : (c.private ? c.private.comment : null);
contactNodes.push( contactNodes.push(
<Contact <Contact
title={c.public ? c.public.fn : null} title={c.public ? c.public.fn : null}
avatar={makeImageUrl(c.public ? c.public.photo : null)} avatar={makeImageUrl(c.public ? c.public.photo : null)}
comment={comment} comment={comment}
unread={this.props.showUnread ? c.unread : 0} unread={this.props.showUnread ? c.unread : 0}
now={c.online && this.props.connected} now={c.online && this.props.connected}
acs={c.acs} acs={c.acs}
showMode={this.props.showMode} showMode={this.props.showMode}
badges={badges} badges={badges}
showCheckmark={showCheckmark} showCheckmark={showCheckmark}
selected={selected} selected={selected}
showOnline={this.props.showOnline} showOnline={this.props.showOnline}
onSelected={this.props.onTopicSelected} onSelected={this.props.onTopicSelected}
showContextMenu={this.props.showContextMenu} showContextMenu={this.props.showContextMenu}
item={key} item={key}
index={contactNodes.length} index={contactNodes.length}
key={key} /> key={key} />
); );
contactsCount ++;
}
}, this); }, this);
} }
return ( return (
<div className={this.props.noScroll ? null : "scrollable-panel"}> <div className={this.props.noScroll ? null : "scrollable-panel"}>
{contactsCount == 0 ?
<div className="center-medium-text"
dangerouslySetInnerHTML={{__html: this.props.emptyListMessage}} />
:
null
}
{contactNodes.length > 0 ? {contactNodes.length > 0 ?
<ul className="contact-box"> <ul className="contact-box">
{contactNodes} {contactNodes}
</ul> </ul>
: :
<div className="center-medium-text" dangerouslySetInnerHTML={{__html: this.props.emptyListMessage}} /> null
} }
</div> </div>
); );
+1 -1
View File
@@ -69,7 +69,7 @@ export default class Contact extends React.Component {
<i className="material-icons">expand_more</i> <i className="material-icons">expand_more</i>
</a> </a>
</span> : null} </span> : null}
</li> </li>
); );
} }
}; };
+26 -6
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