implementing chat archiving (does not work yet)
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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} /> :
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user