sending messages to self works

This commit is contained in:
or-else
2025-02-16 22:06:02 +03:00
parent b2772924bd
commit 30bf77d4ba
38 changed files with 492 additions and 167 deletions
+22
View File
@@ -30,7 +30,28 @@ class ContactList extends React.Component {
const { formatMessage } = this.props.intl;
const showCheckmark = Array.isArray(this.props.topicSelected);
const contactNodes = [];
// Count of contacts, excluding action items.
let contactsCount = 0;
if (this.props.showSelfTopic) {
const selected = showCheckmark ?
(this.props.topicSelected.indexOf('slf') > -1) :
(this.props.topicSelected === 'slf');
contactNodes.push(
<Contact
tinode={this.props.tinode}
avatar={true}
showMode={this.props.showMode}
selected={selected}
showCheckmark={showCheckmark}
onSelected={this.props.onTopicSelected}
showContextMenu={this.props.showContextMenu}
index={contactNodes.length}
item='slf'
key='slf' />
);
contactsCount ++;
}
if (this.props.contacts && this.props.contacts.length > 0) {
this.props.contacts.forEach(c => {
if (c.action) {
@@ -55,6 +76,7 @@ class ContactList extends React.Component {
filterOn.push(('' + c.public.fn).toLowerCase());
}
if (!this.props.filterFunc(this.props.filter, filterOn)) {
console.log('Filtered:', key);
return;
}
}
+28 -6
View File
@@ -1,6 +1,8 @@
// A single topic or user.
import React from 'react';
import { FormattedMessage, injectIntl } from 'react-intl';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { Tinode } from 'tinode-sdk';
import ContactBadges from './contact-badges.jsx';
import LetterTile from './letter-tile.jsx';
@@ -11,6 +13,19 @@ import { Drafty } from 'tinode-sdk';
import { previewFormatter } from '../lib/formatters.js';
import { deliveryMarker } from '../lib/utils.js';
const messages = defineMessages({
self_topic_name: {
id: 'self_topic_name',
defaultMessage: 'Saved messages',
description: 'Name of self topic for UI'
},
self_topic_comment: {
id: 'self_topic_comment',
defaultMessage: 'Notes, messages, links, files saved for posterity',
description: 'Comment for self topic for UI'
}
});
class Contact extends React.Component {
constructor(props) {
super(props);
@@ -36,9 +51,14 @@ class Contact extends React.Component {
render() {
let title = this.props.title;
const self = Tinode.isSelfTopicName(this.props.item);
if (!title) {
title = <i><FormattedMessage id="unnamed_topic" defaultMessage="Unnamed"
description="Title shown when the topic has no name" /></i>;
if (self) {
title = this.props.intl.formatMessage(messages.self_topic_name);
} else {
title = <i><FormattedMessage id="unnamed_topic" defaultMessage="Unnamed"
description="Title shown when the topic has no name" /></i>;
}
} else if (title.length > 30) {
// FIXME: this is probably wrong for RTL languages.
title = title.substring(0, 28) + '…';
@@ -88,6 +108,8 @@ class Contact extends React.Component {
(icon.color ? ' ' + icon.color : '')}>{icon.name}</i> : null;
const titleClass = 'contact-title' + (this.props.deleted ? ' deleted' : '');
const comment = preview || this.props.comment ||
(self ? this.props.intl.formatMessage(messages.self_topic_comment) : '\u00A0');
return (
<li className={!this.props.showCheckmark && this.props.selected ? 'selected' : null} onClick={this.handleClick}>
<div className="avatar-box">
@@ -98,7 +120,7 @@ class Contact extends React.Component {
topic={this.props.item}
deleted={this.props.deleted} />
{this.props.deleted ? <i className="deleted material-icons">cancel</i> :
this.props.showOnline ? <span className={online} /> :
(!self && this.props.showOnline) ? <span className={online} /> :
(this.props.showCheckmark && this.props.selected) ?
<i className="checkmark material-icons">check_circle</i>
: null}
@@ -107,12 +129,12 @@ class Contact extends React.Component {
<div><span className={titleClass}>{title}</span>
{this.props.isGroup ? <i className="material-icons as-badge">group</i> : null}
{this.props.isChannel ? <img src="/img/channel.png" className="channel" alt="channel" /> : null}
<ContactBadges badges={icon_badges} />
{!self ? <ContactBadges badges={icon_badges} /> : null}
{!this.props.deleted ? <UnreadBadge count={this.props.unread} /> : null}
</div>
{this.props.showMode ?
<span><ContactBadges badges={badges} /></span> :
this.props.small ? null : <div className="contact-comment">{marker}<span>{preview || this.props.comment || '\u00A0'}</span></div>}
this.props.small ? null : <div className="contact-comment">{marker}<span>{comment}</span></div>}
</div>
{this.props.showContextMenu ?
<span className="menuTrigger">
+4 -2
View File
@@ -75,8 +75,10 @@ export default class Cropper extends React.Component {
}
componentWillUnmount() {
this.overlay.current.removeEventListener('mousedown', this.mouseDown);
this.overlay.current.removeEventListener('touchstart', this.mouseDown);
if (this.overlay.current) {
this.overlay.current.removeEventListener('mousedown', this.mouseDown);
this.overlay.current.removeEventListener('touchstart', this.mouseDown);
}
}
// Position all elements.
+16 -9
View File
@@ -5,26 +5,33 @@ import { Tinode } from 'tinode-sdk';
import { idToColorClass } from '../lib/strformat.js';
import { sanitizeUrlForMime } from '../lib/utils.js';
import { SELF_AVATAR_URI } from '../config.js';
export default class LetterTile extends React.PureComponent {
render() {
let avatar;
if (this.props.avatar === true) {
const isGroup = Tinode.isGroupTopicName(this.props.topic);
const iconColor = idToColorClass(this.props.topic, isGroup);
if (this.props.topic && this.props.title && this.props.title.trim()) {
const letter = this.props.title.trim().charAt(0);
const className = 'lettertile ' + iconColor + (this.props.deleted ? ' disabled' : '');
avatar = (<div className={className}><div>{letter}</div></div>)
if (Tinode.isSelfTopicName(this.props.topic)) {
avatar = <img className="avatar self" alt="avatar" src={SELF_AVATAR_URI} />
} else {
const className = 'material-icons ' + iconColor + (this.props.deleted ? ' disabled' : '');
avatar = isGroup ? <i className={className}>group</i> : <i className={className}>person</i>;
const isGroup = Tinode.isGroupTopicName(this.props.topic);
const iconColor = idToColorClass(this.props.topic, isGroup);
if (this.props.topic && this.props.title && this.props.title.trim()) {
const letter = this.props.title.trim().charAt(0);
const className = 'lettertile ' + iconColor + (this.props.deleted ? ' disabled' : '');
avatar = (<div className={className}><div>{letter}</div></div>)
} else {
const className = 'material-icons ' + iconColor + (this.props.deleted ? ' disabled' : '');
avatar = isGroup ? <i className={className}>group</i> : <i className={className}>person</i>;
}
}
} else if (this.props.avatar) {
const url = this.props.tinode.authorizeURL(sanitizeUrlForMime(this.props.avatar, 'image'));
// If avatar image is invalid, show a placeholder.
const className = 'avatar' + (this.props.deleted ? ' deleted' : '');
avatar = <img className={className} alt="avatar" src={url}
onError={(e)=>{e.target.onerror = null; e.target.src="../img/broken_image.png"}} />;
onError={e=>{e.target.onerror = null; e.target.src="../img/broken_image.png"}} />;
} else {
avatar = null;
}
+10 -6
View File
@@ -26,15 +26,19 @@ class PinnedMessages extends React.PureComponent {
}
componentDidMount() {
this.touchSurface.current.addEventListener('touchstart', this.handleTouchEventStart, {passive: true});
this.touchSurface.current.addEventListener('touchend', this.handleTouchEventEnd, {passive: true});
this.touchSurface.current.addEventListener('touchcancel', this.handleTouchCancel, {passive: true});
if (this.touchSurface.current) {
this.touchSurface.current.addEventListener('touchstart', this.handleTouchEventStart, {passive: true});
this.touchSurface.current.addEventListener('touchend', this.handleTouchEventEnd, {passive: true});
this.touchSurface.current.addEventListener('touchcancel', this.handleTouchCancel, {passive: true});
}
}
componentWillUnmount() {
this.touchSurface.current.removeEventListener('touchstart', this.handleTouchEventStart);
this.touchSurface.current.removeEventListener('touchend', this.handleTouchEventEnd);
this.touchSurface.current.removeEventListener('touchcancel', this.handleTouchCancel);
if (this.touchSurface.current) {
this.touchSurface.current.removeEventListener('touchstart', this.handleTouchEventStart);
this.touchSurface.current.removeEventListener('touchend', this.handleTouchEventEnd);
this.touchSurface.current.removeEventListener('touchcancel', this.handleTouchCancel);
}
}
getSelectedIndex() {
+11 -5
View File
@@ -1,7 +1,7 @@
// Widget for editing topic description.
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { FormattedMessage, injectIntl } from 'react-intl';
import { Tinode } from 'tinode-sdk';
@@ -15,18 +15,22 @@ import { AVATAR_SIZE, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE, MAX_TITLE_LE
import { imageScaled, blobToBase64, makeImageUrl } from '../lib/blob-helpers.js';
import { arrayEqual, theCard } from '../lib/utils.js';
export default class TopicDescEdit extends React.Component {
class TopicDescEdit extends React.Component {
constructor(props) {
super(props);
const topic = this.props.tinode.getTopic(this.props.topic);
const acs = topic.getAccessMode();
const isSelf = Tinode.isSelfTopicName(this.props.topic);
this.state = {
isMe: Tinode.isMeTopicName(this.props.topic),
isSelf: isSelf,
owner: acs && acs.isOwner(),
fullName: topic.public ? topic.public.fn : undefined,
fullName: topic.public ? topic.public.fn :
isSelf ? this.props.intl.formatMessage({id: 'self_topic_name'}) : null,
private: topic.private ? topic.private.comment : null,
description: topic.public ? topic.public.note : undefined,
description: topic.public ? topic.public.note :
isSelf ? this.props.intl.formatMessage({id: 'self_topic_comment'}) : null,
avatar: makeImageUrl(topic.public ? topic.public.photo : null),
tags: topic.tags() || [],
newAvatar: null,
@@ -161,7 +165,7 @@ export default class TopicDescEdit extends React.Component {
);
}
const editable = this.state.isMe || this.state.owner;
const editable = this.state.isMe || (this.state.owner && !this.state.isSelf);
return (
<>
@@ -272,3 +276,5 @@ export default class TopicDescEdit extends React.Component {
);
}
};
export default injectIntl(TopicDescEdit);