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
+4
View File
@@ -74,6 +74,10 @@ img.avatar {
border-radius: 50%;
}
img.avatar.self {
padding: 7%;
}
/* Input fields and buttons */
button {
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -72,7 +72,7 @@
"react": "^18.3.0",
"react-dom": "^18.3.0",
"react-intl": "^7.0.0",
"tinode-sdk": "^0.23.1-rc1",
"tinode-sdk": "^0.24.0-rc1",
"webm-duration-fix": "^1.0.4"
},
"devDependencies": {
+5 -1
View File
@@ -1,7 +1,7 @@
import { PACKAGE_VERSION } from './version.js';
// Name of this application, used in the User-Agent.
export const APP_NAME = 'TinodeWeb/' + (PACKAGE_VERSION || '0.21');
export const APP_NAME = 'TinodeWeb/' + (PACKAGE_VERSION || '0.24');
// API key. Use https://github.com/tinode/chat/tree/master/keygen to generate your own
export const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K';
@@ -140,4 +140,8 @@ export const WAKE_UP_TIMEOUT = 80000;
// Timer interval between checks if computer woke up from sleep (1 sec).
export const WAKE_UP_TICK = 1000;
// Swipe distance to register as a gesture, in pixels.
export const MIN_SWIPE_DISTANCE = REM_SIZE * 3;
// Self-topic avatar as an SVG icon encoded as data URI
export const SELF_AVATAR_URI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIwIDAgMTcgMTciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0zLjU2MiAxdjE1LjQ1OWw0LjY4Ni0zLjI3IDQuNzUyIDMuMjZ2LTE1LjQ0OWgtOS40Mzh6TTEyIDE0LjU1MWwtMy43NTYtMi41NzgtMy42ODEgMi41Njh2LTEyLjU0MWg3LjQzN3YxMi41NTF6IiBmaWxsPSIjMzMzMzMzIiAvPgo8L3N2Zz4=' // eslint-disable-line max-len
+1 -1
View File
File diff suppressed because one or more lines are too long
+10
View File
@@ -1661,5 +1661,15 @@
"description": "Title for scanning QR code",
"missing": false,
"obsolete": false
},
"self_topic_comment": {
"translation": "",
"missing": true,
"obsolete": false
},
"self_topic_name": {
"translation": "",
"missing": true,
"obsolete": false
}
}
+10
View File
@@ -1661,5 +1661,15 @@
"description": "Title for scanning QR code",
"missing": false,
"obsolete": false
},
"self_topic_comment": {
"translation": "Notes, messages, links, files saved for posterity",
"missing": false,
"obsolete": false
},
"self_topic_name": {
"translation": "Saved messages",
"missing": false,
"obsolete": false
}
}
+10
View File
@@ -1661,5 +1661,15 @@
"description": "Title for scanning QR code",
"missing": false,
"obsolete": false
},
"self_topic_comment": {
"translation": "",
"missing": true,
"obsolete": false
},
"self_topic_name": {
"translation": "",
"missing": true,
"obsolete": false
}
}
+4
View File
@@ -816,6 +816,10 @@
"defaultMessage": "List like alice@example.com, +17025550003...",
"description": "Placeholder in contacts search field"
},
"self_topic_comment": {
},
"self_topic_name": {
},
"sidepanel_title_acc_notifications": {
"defaultMessage": "Notifications",
"description": "Sidepanel title for AccNotificationsView."
+10
View File
@@ -1661,5 +1661,15 @@
"description": "Title for scanning QR code",
"missing": false,
"obsolete": false
},
"self_topic_comment": {
"translation": "",
"missing": true,
"obsolete": false
},
"self_topic_name": {
"translation": "",
"missing": true,
"obsolete": false
}
}
+10
View File
@@ -1661,5 +1661,15 @@
"description": "Title for scanning QR code",
"missing": false,
"obsolete": false
},
"self_topic_comment": {
"translation": "",
"missing": true,
"obsolete": false
},
"self_topic_name": {
"translation": "",
"missing": true,
"obsolete": false
}
}
+10
View File
@@ -1661,5 +1661,15 @@
"description": "Title for scanning QR code",
"missing": false,
"obsolete": false
},
"self_topic_comment": {
"translation": "",
"missing": true,
"obsolete": false
},
"self_topic_name": {
"translation": "",
"missing": true,
"obsolete": false
}
}
+10
View File
@@ -1661,5 +1661,15 @@
"description": "Title for scanning QR code",
"missing": false,
"obsolete": false
},
"self_topic_comment": {
"translation": "",
"missing": true,
"obsolete": false
},
"self_topic_name": {
"translation": "",
"missing": true,
"obsolete": false
}
}
+10
View File
@@ -1661,5 +1661,15 @@
"description": "Menu item [Unpin] for un-pinning the message.",
"missing": false,
"obsolete": false
},
"self_topic_comment": {
"translation": "",
"missing": true,
"obsolete": false
},
"self_topic_name": {
"translation": "",
"missing": true,
"obsolete": false
}
}
+10
View File
@@ -1661,5 +1661,15 @@
"description": "Menu item [Unpin] for un-pinning the message.",
"missing": false,
"obsolete": false
},
"self_topic_comment": {
"translation": "",
"missing": true,
"obsolete": false
},
"self_topic_name": {
"translation": "",
"missing": true,
"obsolete": false
}
}
+10
View File
@@ -1661,5 +1661,15 @@
"description": "Title for scanning QR code",
"missing": false,
"obsolete": false
},
"self_topic_comment": {
"translation": "",
"missing": true,
"obsolete": false
},
"self_topic_name": {
"translation": "",
"missing": true,
"obsolete": false
}
}
+10
View File
@@ -1661,5 +1661,15 @@
"description": "Title for scanning QR code",
"missing": false,
"obsolete": false
},
"self_topic_comment": {
"translation": "",
"missing": true,
"obsolete": false
},
"self_topic_name": {
"translation": "",
"missing": true,
"obsolete": false
}
}
+1 -1
View File
@@ -164,7 +164,7 @@ export function sanitizeUrlForMime(url, mimeMajor) {
}
// Is this a data: URL of the appropriate mime type?
const re = new RegExp(`data:${mimeMajor}\/[a-z0-9.-]+;base64,`, 'i');
const re = new RegExp(`data:${mimeMajor}\/[-+.a-z0-9]+;base64,`, 'i');
if (re.test(url.trim())) {
return url;
}
+60 -37
View File
@@ -97,6 +97,7 @@ class InfoView extends React.Component {
muted: false,
address: null,
groupTopic: undefined,
isSelf: false,
channel: undefined,
fullName: undefined,
description: undefined,
@@ -203,6 +204,8 @@ class InfoView extends React.Component {
}
}
const isSelf = topic.isSelfType();
this.setState({
owner: acs && acs.isOwner(),
admin: acs && acs.isAdmin(),
@@ -210,14 +213,19 @@ class InfoView extends React.Component {
deleter: acs && acs.isDeleter(),
muted: acs && acs.isMuted(),
fullName: clipStr(topic.public && topic.public.fn, MAX_TITLE_LENGTH),
description: clipStr(topic.public && topic.public.note, MAX_TOPIC_DESCRIPTION_LENGTH),
fullName: isSelf ?
props.intl.formatMessage({id: 'self_topic_name'}) :
clipStr(topic.public && topic.public.fn, MAX_TITLE_LENGTH),
description: isSelf ?
props.intl.formatMessage({id: 'self_topic_comment'}) :
clipStr(topic.public && topic.public.note, MAX_TOPIC_DESCRIPTION_LENGTH),
avatar: makeImageUrl(topic.public ? topic.public.photo : null),
trustedBadges: badges,
private: clipStr(topic.private && topic.private.comment, MAX_TITLE_LENGTH),
archived: topic.isArchived(),
address: topic.name,
groupTopic: topic.isGroupType(),
isSelf: isSelf,
channel: topic.isChannelType() || topic.chan,
access: acs ? acs.getMode() : undefined,
modeGiven: acs ? acs.getGiven() : undefined,
@@ -539,10 +547,13 @@ class InfoView extends React.Component {
:
<div id="info-view-content" className="scrollable-panel">
<div className="panel-form-column">
<a href="#" className="flat-button float-right" onClick={(e) => {e.preventDefault(); this.props.onNavigate('general');}}>
<i className="material-icons">edit</i>&nbsp;
<FormattedMessage id="button_edit" defaultMessage="Edit" description="Call to action [Edit]" />
</a>
{!this.state.isSelf ?
<a href="#" className="flat-button float-right" onClick={e => {e.preventDefault(); this.props.onNavigate('general');}}>
<i className="material-icons">edit</i>&nbsp;
<FormattedMessage id="button_edit" defaultMessage="Edit" description="Call to action [Edit]" />
</a>
: null
}
<center>
<AvatarUpload
tinode={this.props.tinode}
@@ -569,24 +580,30 @@ class InfoView extends React.Component {
</div>
: null
}
<div className="panel-form-row">
<div>
<label className="small"><FormattedMessage id="label_user_id" defaultMessage="ID:"
description="Label for user address (ID)" /></label>&nbsp;
<tt>{this.state.address}</tt>
{!this.state.isSelf ?
<div className="panel-form-row">
<div>
<label className="small"><FormattedMessage id="label_user_id" defaultMessage="ID:"
description="Label for user address (ID)" /></label>&nbsp;
<tt>{this.state.address}</tt>
</div>
<div style={{marginLeft: 'auto'}}>
&nbsp;<a href="#" onClick={this.handleCopyID}>
<i className="material-icons">content_copy</i>
</a>&nbsp;
&nbsp;<a href="#" onClick={this.handleShowQRCode}>
<i className="material-icons">qr_code</i>
</a>&nbsp;
</div>
</div>
<div style={{marginLeft: 'auto'}}>
&nbsp;<a href="#" onClick={this.handleCopyID}>
<i className="material-icons">content_copy</i>
</a>&nbsp;
&nbsp;<a href="#" onClick={this.handleShowQRCode}>
<i className="material-icons">qr_code</i>
</a>&nbsp;
: null
}
{this.state.trustedBadges.length > 0 ?
<div className="group">
<BadgeList trustedBadges={this.state.trustedBadges} />
</div>
</div>
<div className="group">
<BadgeList trustedBadges={this.state.trustedBadges} />
</div>
: null
}
{this.state.description ?
<div className="group">
<label className="small">
@@ -596,14 +613,17 @@ class InfoView extends React.Component {
<div>{this.state.description}</div>
</div> : null}
</div>
<div className="hr" />
<div className="panel-form-row">
<label>
<FormattedMessage id="label_muting_topic" defaultMessage="Muted:"
description="Label for Muting/unmuting the topic" />
</label>
<CheckBox name="P" checked={this.state.muted} onChange={this.handleMuted} />
</div>
{!this.state.isSelf ?
<><div className="hr" />
<div className="panel-form-row">
<label>
<FormattedMessage id="label_muting_topic" defaultMessage="Muted:"
description="Label for Muting/unmuting the topic" />
</label>
<CheckBox name="P" checked={this.state.muted} onChange={this.handleMuted} />
</div></>
: null
}
{this.state.archived ?
<div className="panel-form-row">
<label>
@@ -615,13 +635,16 @@ class InfoView extends React.Component {
:
null
}
<div className="hr" />
<div className="panel-form-row">
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onNavigate('security');}}>
<i className="material-icons">security</i>&nbsp;<FormattedMessage id="button_security"
defaultMessage="Security" description="Navigaton button for security panel." />
</a>
</div>
{!this.state.isSelf ?
<><div className="hr" />
<div className="panel-form-row">
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onNavigate('security');}}>
<i className="material-icons">security</i>&nbsp;<FormattedMessage id="button_security"
defaultMessage="Security" description="Navigaton button for security panel." />
</a>
</div></>
: null
}
{this.state.groupTopic && this.state.sharer ?
<>
<div className="hr" />
+18 -4
View File
@@ -76,7 +76,7 @@ const messages = defineMessages({
id: 'drag_file',
defaultMessage: 'Drag file here',
description: 'Prompt on the file drag-n-drop overlay banner'
}
},
});
// Checks if the access permissions are granted but not yet accepted.
@@ -377,7 +377,14 @@ class MessagesView extends React.Component {
onlineSubs: subs
});
if (topic.public) {
if (topic.isSelfType()) {
Object.assign(nextState, {
title: nextProps.intl.formatMessage({
id: 'self_topic_name'
}),
avatar: true
});
} else if (topic.public) {
Object.assign(nextState, {
title: topic.public.fn,
avatar: makeImageUrl(topic.public.photo)
@@ -627,7 +634,14 @@ class MessagesView extends React.Component {
}
handleDescChange(desc) {
if (desc.public) {
if (Tinode.isSelfTopicName(this.props.topic)) {
this.setState({
title: this.props.intl.formatMessage({
id: 'self_topic_name'
}),
avatar: true
});
} else if (desc.public) {
this.setState({
title: desc.public.fn,
avatar: makeImageUrl(desc.public.photo)
@@ -1601,7 +1615,7 @@ class MessagesView extends React.Component {
}
}
const avatar = this.state.avatar || true;
const online = this.state.deleted ? null :
const online = this.state.deleted || topic.isSelfType() ? null :
this.props.online ? 'online' + (this.state.typingIndicator ? ' typing' : '') : 'offline';
const titleClass = 'panel-title' + (this.state.deleted ? ' deleted' : '');
+1
View File
@@ -125,6 +125,7 @@ class NewTopicView extends React.Component {
contacts={this.props.searchResults}
myUserId={this.props.myUserId}
emptyListMessage={no_contacts_placeholder}
showSelfTopic={true}
showOnline={false}
showUnread={false}
showContextMenu={false}
+9 -7
View File
@@ -949,7 +949,7 @@ class TinodeWeb extends React.Component {
newState.ready = true;
}
this.tinode.getMeTopic().contacts((c) => {
this.tinode.getMeTopic().contacts(c => {
if (!c.topic && !c.user) {
// Contacts expect c.topic to be set.
c.topic = c.name;
@@ -1609,7 +1609,7 @@ class TinodeWeb extends React.Component {
}
let muted = false, blocked = false, self_blocked = false, subscribed = false, deleter = false,
archived = false, webrtc = false, writer = false;
archived = false, webrtc = false, writer = false, p2p = false, self = false;
if (topic) {
subscribed = topic.isSubscribed();
archived = topic.isArchived();
@@ -1625,24 +1625,26 @@ class TinodeWeb extends React.Component {
}
webrtc = writer && !!this.tinode.getServerParam('iceServers');
p2p = Tinode.isP2PTopicName(topicName);
self = Tinode.isSelfTopicName(topicName);
return [
subscribed ? {
title: this.props.intl.formatMessage(messages.menu_item_info),
handler: this.handleShowInfoView
} : null,
subscribed && Tinode.isP2PTopicName(topicName) && webrtc ? {
subscribed && p2p && webrtc ? {
title: this.props.intl.formatMessage(messages.menu_item_audio_call),
handler: this.handleStartAudioCall
} : null,
subscribed && Tinode.isP2PTopicName(topicName) && webrtc ? {
subscribed && p2p && webrtc ? {
title: this.props.intl.formatMessage(messages.menu_item_video_call),
handler: this.handleStartVideoCall
} : null,
subscribed ? 'messages_clear' : null,
subscribed && deleter ? 'messages_clear_hard' : null,
muted ? (blocked ? null : 'topic_unmute') : 'topic_mute',
self_blocked ? 'topic_unblock' : 'topic_block',
subscribed && deleter && !self ? 'messages_clear_hard' : null,
self ? null : (muted ? (blocked ? null : 'topic_unmute') : 'topic_mute'),
self ? null : (self_blocked ? 'topic_unblock' : 'topic_block'),
archived ? 'topic_restore' : 'topic_archive',
'topic_delete'
];
+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);
+1 -1
View File
File diff suppressed because one or more lines are too long
+162 -74
View File
@@ -2239,6 +2239,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ QUOTED_REPLY_LENGTH: function() { return /* binding */ QUOTED_REPLY_LENGTH; },
/* harmony export */ READ_DELAY: function() { return /* binding */ READ_DELAY; },
/* harmony export */ REM_SIZE: function() { return /* binding */ REM_SIZE; },
/* harmony export */ SELF_AVATAR_URI: function() { return /* binding */ SELF_AVATAR_URI; },
/* harmony export */ VIDEO_PREVIEW_DIM: function() { return /* binding */ VIDEO_PREVIEW_DIM; },
/* harmony export */ VIDEO_THUMBNAIL_WIDTH: function() { return /* binding */ VIDEO_THUMBNAIL_WIDTH; },
/* harmony export */ WAKE_UP_TICK: function() { return /* binding */ WAKE_UP_TICK; },
@@ -2246,7 +2247,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ });
/* harmony import */ var _version_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./version.js */ "./src/version.js");
const APP_NAME = 'TinodeWeb/' + (_version_js__WEBPACK_IMPORTED_MODULE_0__.PACKAGE_VERSION || '0.21');
const APP_NAME = 'TinodeWeb/' + (_version_js__WEBPACK_IMPORTED_MODULE_0__.PACKAGE_VERSION || '0.24');
const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K';
const KNOWN_HOSTS = {
hosted: 'web.tinode.co',
@@ -2296,6 +2297,7 @@ const QRCODE_SIZE = 128;
const WAKE_UP_TIMEOUT = 80000;
const WAKE_UP_TICK = 1000;
const MIN_SWIPE_DISTANCE = REM_SIZE * 3;
const SELF_AVATAR_URI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIwIDAgMTcgMTciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0zLjU2MiAxdjE1LjQ1OWw0LjY4Ni0zLjI3IDQuNzUyIDMuMjZ2LTE1LjQ0OWgtOS40Mzh6TTEyIDE0LjU1MWwtMy43NTYtMi41NzgtMy42ODEgMi41Njh2LTEyLjU0MWg3LjQzN3YxMi41NTF6IiBmaWxsPSIjMzMzMzMzIiAvPgo8L3N2Zz4=';
/***/ }),
@@ -3538,7 +3540,7 @@ function sanitizeUrlForMime(url, mimeMajor) {
if (sanitizedUrl) {
return sanitizedUrl;
}
const re = new RegExp(`data:${mimeMajor}\/[a-z0-9.-]+;base64,`, 'i');
const re = new RegExp(`data:${mimeMajor}\/[-+.a-z0-9]+;base64,`, 'i');
if (re.test(url.trim())) {
return url;
}
@@ -4310,6 +4312,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
muted: false,
address: null,
groupTopic: undefined,
isSelf: false,
channel: undefined,
fullName: undefined,
description: undefined,
@@ -4408,20 +4411,26 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
}
}
}
const isSelf = topic.isSelfType();
this.setState({
owner: acs && acs.isOwner(),
admin: acs && acs.isAdmin(),
sharer: acs && acs.isSharer(),
deleter: acs && acs.isDeleter(),
muted: acs && acs.isMuted(),
fullName: (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_16__.clipStr)(topic.public && topic.public.fn, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TITLE_LENGTH),
description: (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_16__.clipStr)(topic.public && topic.public.note, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TOPIC_DESCRIPTION_LENGTH),
fullName: isSelf ? props.intl.formatMessage({
id: "self_topic_name"
}) : (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_16__.clipStr)(topic.public && topic.public.fn, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TITLE_LENGTH),
description: isSelf ? props.intl.formatMessage({
id: "self_topic_comment"
}) : (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_16__.clipStr)(topic.public && topic.public.note, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TOPIC_DESCRIPTION_LENGTH),
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageUrl)(topic.public ? topic.public.photo : null),
trustedBadges: badges,
private: (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_16__.clipStr)(topic.private && topic.private.comment, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TITLE_LENGTH),
archived: topic.isArchived(),
address: topic.name,
groupTopic: topic.isGroupType(),
isSelf: isSelf,
channel: topic.isChannelType() || topic.chan,
access: acs ? acs.getMode() : undefined,
modeGiven: acs ? acs.getGiven() : undefined,
@@ -4725,7 +4734,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
className: "scrollable-panel"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "panel-form-column"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
}, !this.state.isSelf ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
href: "#",
className: "flat-button float-right",
onClick: e => {
@@ -4740,7 +4749,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
"type": 0,
"value": "Edit"
}]
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("center", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_avatar_upload_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
})) : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("center", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_avatar_upload_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
tinode: this.props.tinode,
avatar: this.state.avatar,
readOnly: true,
@@ -4774,7 +4783,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
}]
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "large ellipsized"
}, this.state.private)) : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
}, this.state.private)) : null, !this.state.isSelf ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "panel-form-row"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
className: "small"
@@ -4798,11 +4807,11 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
onClick: this.handleShowQRCode
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons"
}, "qr_code")), "\xA0")), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
}, "qr_code")), "\xA0")) : null, this.state.trustedBadges.length > 0 ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "group"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_badge_list_jsx__WEBPACK_IMPORTED_MODULE_4__["default"], {
trustedBadges: this.state.trustedBadges
})), this.state.description ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
})) : null, this.state.description ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "group"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
className: "small"
@@ -4812,7 +4821,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
"type": 0,
"value": "Description"
}]
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, this.state.description)) : null), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, this.state.description)) : null), !this.state.isSelf ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "hr"
}), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "panel-form-row"
@@ -4826,7 +4835,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
name: "P",
checked: this.state.muted,
onChange: this.handleMuted
})), this.state.archived ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
}))) : null, this.state.archived ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "panel-form-row"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
id: "label_unarchive_topic",
@@ -4838,7 +4847,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
name: "archived",
checked: true,
onChange: this.handleUnarchive
})) : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
})) : null, !this.state.isSelf ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "hr"
}), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "panel-form-row"
@@ -4857,7 +4866,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
"type": 0,
"value": "Security"
}]
}))), this.state.groupTopic && this.state.sharer ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
})))) : null, this.state.groupTopic && this.state.sharer ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "hr"
}), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "panel-form-row"
@@ -5474,7 +5483,14 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
Object.assign(nextState, {
onlineSubs: subs
});
if (topic.public) {
if (topic.isSelfType()) {
Object.assign(nextState, {
title: nextProps.intl.formatMessage({
id: "self_topic_name"
}),
avatar: true
});
} else if (topic.public) {
Object.assign(nextState, {
title: topic.public.fn,
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_20__.makeImageUrl)(topic.public.photo)
@@ -5697,7 +5713,14 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}
}
handleDescChange(desc) {
if (desc.public) {
if (tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.isSelfTopicName(this.props.topic)) {
this.setState({
title: this.props.intl.formatMessage({
id: "self_topic_name"
}),
avatar: true
});
} else if (desc.public) {
this.setState({
title: desc.public.fn,
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_20__.makeImageUrl)(desc.public.photo)
@@ -6559,7 +6582,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
}
}
const avatar = this.state.avatar || true;
const online = this.state.deleted ? null : this.props.online ? 'online' + (this.state.typingIndicator ? ' typing' : '') : 'offline';
const online = this.state.deleted || topic.isSelfType() ? null : this.props.online ? 'online' + (this.state.typingIndicator ? ' typing' : '') : 'offline';
const titleClass = 'panel-title' + (this.state.deleted ? ' deleted' : '');
let messagesComponent = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
id: "messages-container"
@@ -6878,6 +6901,7 @@ class NewTopicView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
contacts: this.props.searchResults,
myUserId: this.props.myUserId,
emptyListMessage: no_contacts_placeholder,
showSelfTopic: true,
showOnline: false,
showUnread: false,
showContextMenu: false,
@@ -8817,7 +8841,9 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
deleter = false,
archived = false,
webrtc = false,
writer = false;
writer = false,
p2p = false,
self = false;
if (topic) {
subscribed = topic.isSubscribed();
archived = topic.isArchived();
@@ -8831,16 +8857,18 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
}
}
webrtc = writer && !!this.tinode.getServerParam('iceServers');
p2p = tinode_sdk__WEBPACK_IMPORTED_MODULE_4__.Tinode.isP2PTopicName(topicName);
self = tinode_sdk__WEBPACK_IMPORTED_MODULE_4__.Tinode.isSelfTopicName(topicName);
return [subscribed ? {
title: this.props.intl.formatMessage(messages.menu_item_info),
handler: this.handleShowInfoView
} : null, subscribed && tinode_sdk__WEBPACK_IMPORTED_MODULE_4__.Tinode.isP2PTopicName(topicName) && webrtc ? {
} : null, subscribed && p2p && webrtc ? {
title: this.props.intl.formatMessage(messages.menu_item_audio_call),
handler: this.handleStartAudioCall
} : null, subscribed && tinode_sdk__WEBPACK_IMPORTED_MODULE_4__.Tinode.isP2PTopicName(topicName) && webrtc ? {
} : null, subscribed && p2p && webrtc ? {
title: this.props.intl.formatMessage(messages.menu_item_video_call),
handler: this.handleStartVideoCall
} : null, subscribed ? 'messages_clear' : null, subscribed && deleter ? 'messages_clear_hard' : null, muted ? blocked ? null : 'topic_unmute' : 'topic_mute', self_blocked ? 'topic_unblock' : 'topic_block', archived ? 'topic_restore' : 'topic_archive', 'topic_delete'];
} : null, subscribed ? 'messages_clear' : null, subscribed && deleter && !self ? 'messages_clear_hard' : null, self ? null : muted ? blocked ? null : 'topic_unmute' : 'topic_mute', self ? null : self_blocked ? 'topic_unblock' : 'topic_block', archived ? 'topic_restore' : 'topic_archive', 'topic_delete'];
}
handleHideContextMenu() {
this.setState({
@@ -11816,6 +11844,22 @@ class ContactList extends (react__WEBPACK_IMPORTED_MODULE_0___default().Componen
const showCheckmark = Array.isArray(this.props.topicSelected);
const contactNodes = [];
let contactsCount = 0;
if (this.props.showSelfTopic) {
const selected = showCheckmark ? this.props.topicSelected.indexOf('slf') > -1 : this.props.topicSelected === 'slf';
contactNodes.push(react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_contact_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
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) {
@@ -11837,6 +11881,7 @@ class ContactList extends (react__WEBPACK_IMPORTED_MODULE_0___default().Componen
filterOn.push(('' + c.public.fn).toLowerCase());
}
if (!this.props.filterFunc(this.props.filter, filterOn)) {
console.log('Filtered:', key);
return;
}
}
@@ -11935,11 +11980,11 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _contact_badges_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./contact-badges.jsx */ "./src/widgets/contact-badges.jsx");
/* harmony import */ var _letter_tile_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./letter-tile.jsx */ "./src/widgets/letter-tile.jsx");
/* harmony import */ var _unread_badge_jsx__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./unread-badge.jsx */ "./src/widgets/unread-badge.jsx");
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! tinode-sdk */ "tinode-sdk");
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_5__);
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! tinode-sdk */ "tinode-sdk");
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _contact_badges_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./contact-badges.jsx */ "./src/widgets/contact-badges.jsx");
/* harmony import */ var _letter_tile_jsx__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./letter-tile.jsx */ "./src/widgets/letter-tile.jsx");
/* harmony import */ var _unread_badge_jsx__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./unread-badge.jsx */ "./src/widgets/unread-badge.jsx");
/* harmony import */ var _lib_formatters_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../lib/formatters.js */ "./src/lib/formatters.js");
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
@@ -11950,6 +11995,23 @@ __webpack_require__.r(__webpack_exports__);
const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
self_topic_name: {
id: "self_topic_name",
defaultMessage: [{
"type": 0,
"value": "Saved messages"
}]
},
self_topic_comment: {
id: "self_topic_comment",
defaultMessage: [{
"type": 0,
"value": "Notes, messages, links, files saved for posterity"
}]
}
});
class Contact extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
constructor(props) {
super(props);
@@ -11974,14 +12036,19 @@ class Contact extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
}
render() {
let title = this.props.title;
const self = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.isSelfTopicName(this.props.item);
if (!title) {
title = react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
id: "unnamed_topic",
defaultMessage: [{
"type": 0,
"value": "Unnamed"
}]
}));
if (self) {
title = this.props.intl.formatMessage(messages.self_topic_name);
} else {
title = react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
id: "unnamed_topic",
defaultMessage: [{
"type": 0,
"value": "Unnamed"
}]
}));
}
} else if (title.length > 30) {
title = title.substring(0, 28) + '…';
}
@@ -12028,8 +12095,8 @@ class Contact extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
let preview;
if (typeof this.props.preview == 'string') {
preview = this.props.preview;
} else if (tinode_sdk__WEBPACK_IMPORTED_MODULE_5__.Drafty.isValid(this.props.preview)) {
preview = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, tinode_sdk__WEBPACK_IMPORTED_MODULE_5__.Drafty.format(this.props.preview, _lib_formatters_js__WEBPACK_IMPORTED_MODULE_6__.previewFormatter, {
} else if (tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.isValid(this.props.preview)) {
preview = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Drafty.format(this.props.preview, _lib_formatters_js__WEBPACK_IMPORTED_MODULE_6__.previewFormatter, {
formatMessage: this.props.intl.formatMessage,
previewIsResponse: this.props.previewIsResponse
}));
@@ -12051,12 +12118,13 @@ class Contact extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
className: 'material-icons small space-right' + (icon.color ? ' ' + icon.color : '')
}, icon.name) : 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 react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", {
className: !this.props.showCheckmark && this.props.selected ? 'selected' : null,
onClick: this.handleClick
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "avatar-box"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_4__["default"], {
tinode: this.props.tinode,
avatar: avatar,
title: this.props.title,
@@ -12064,7 +12132,7 @@ class Contact extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
deleted: this.props.deleted
}), this.props.deleted ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "deleted material-icons"
}, "cancel") : this.props.showOnline ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
}, "cancel") : !self && this.props.showOnline ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
className: online
}) : this.props.showCheckmark && this.props.selected ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "checkmark material-icons"
@@ -12078,15 +12146,15 @@ class Contact extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
src: "/img/channel.png",
className: "channel",
alt: "channel"
}) : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_contact_badges_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
}) : null, !self ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_contact_badges_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
badges: icon_badges
}), !this.props.deleted ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_unread_badge_jsx__WEBPACK_IMPORTED_MODULE_4__["default"], {
}) : null, !this.props.deleted ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_unread_badge_jsx__WEBPACK_IMPORTED_MODULE_5__["default"], {
count: this.props.unread
}) : null), this.props.showMode ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_contact_badges_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
}) : null), this.props.showMode ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_contact_badges_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
badges: badges
})) : this.props.small ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "contact-comment"
}, marker, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, preview || this.props.comment || '\u00A0'))), this.props.showContextMenu ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
}, marker, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, comment))), this.props.showContextMenu ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
className: "menuTrigger"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
href: "#",
@@ -12682,8 +12750,10 @@ class Cropper extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
this.cutoutRect = this.cutout.current.getBoundingClientRect();
}
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);
}
}
positionAll(panX, panY, zoom) {
this.setState({
@@ -14072,6 +14142,8 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
@@ -14080,21 +14152,29 @@ class LetterTile extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompo
render() {
let avatar;
if (this.props.avatar === true) {
const isGroup = tinode_sdk__WEBPACK_IMPORTED_MODULE_1__.Tinode.isGroupTopicName(this.props.topic);
const iconColor = (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_2__.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 = react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: className
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, letter));
if (tinode_sdk__WEBPACK_IMPORTED_MODULE_1__.Tinode.isSelfTopicName(this.props.topic)) {
avatar = react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
className: "avatar self",
alt: "avatar",
src: _config_js__WEBPACK_IMPORTED_MODULE_4__.SELF_AVATAR_URI
});
} else {
const className = 'material-icons ' + iconColor + (this.props.deleted ? ' disabled' : '');
avatar = isGroup ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: className
}, "group") : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: className
}, "person");
const isGroup = tinode_sdk__WEBPACK_IMPORTED_MODULE_1__.Tinode.isGroupTopicName(this.props.topic);
const iconColor = (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_2__.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 = react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: className
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, letter));
} else {
const className = 'material-icons ' + iconColor + (this.props.deleted ? ' disabled' : '');
avatar = isGroup ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: className
}, "group") : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: className
}, "person");
}
}
} else if (this.props.avatar) {
const url = this.props.tinode.authorizeURL((0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_3__.sanitizeUrlForMime)(this.props.avatar, 'image'));
@@ -14974,20 +15054,24 @@ class PinnedMessages extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
this.handleTouchCancel = this.handleTouchCancel.bind(this);
}
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() {
const list = this.props.messages || [];
@@ -15966,9 +16050,6 @@ class TagManager extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ TopicDescEdit; }
/* harmony export */ });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ "react-intl");
@@ -15997,12 +16078,18 @@ class TopicDescEdit extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compon
super(props);
const topic = this.props.tinode.getTopic(this.props.topic);
const acs = topic.getAccessMode();
const isSelf = tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.Tinode.isSelfTopicName(this.props.topic);
this.state = {
isMe: tinode_sdk__WEBPACK_IMPORTED_MODULE_2__.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: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_8__.makeImageUrl)(topic.public ? topic.public.photo : null),
tags: topic.tags() || [],
newAvatar: null,
@@ -16138,7 +16225,7 @@ class TopicDescEdit extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compon
onError: this.props.onError
});
}
const editable = this.state.isMe || this.state.owner;
const editable = this.state.isMe || this.state.owner && !this.state.isSelf;
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "panel-form-column"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("center", null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_avatar_upload_jsx__WEBPACK_IMPORTED_MODULE_4__["default"], {
@@ -16252,6 +16339,7 @@ class TopicDescEdit extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compon
}
}
;
/* harmony default export */ __webpack_exports__["default"] = ((0,react_intl__WEBPACK_IMPORTED_MODULE_1__.injectIntl)(TopicDescEdit));
/***/ }),
+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
File diff suppressed because one or more lines are too long
+16 -4
View File
@@ -403,6 +403,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ TOPIC_NEW: () => (/* binding */ TOPIC_NEW),
/* harmony export */ TOPIC_NEW_CHAN: () => (/* binding */ TOPIC_NEW_CHAN),
/* harmony export */ TOPIC_P2P: () => (/* binding */ TOPIC_P2P),
/* harmony export */ TOPIC_SLF: () => (/* binding */ TOPIC_SLF),
/* harmony export */ TOPIC_SYS: () => (/* binding */ TOPIC_SYS),
/* harmony export */ USER_NEW: () => (/* binding */ USER_NEW),
/* harmony export */ VERSION: () => (/* binding */ VERSION)
@@ -412,13 +413,14 @@ __webpack_require__.r(__webpack_exports__);
const PROTOCOL_VERSION = '0';
const VERSION = _version_js__WEBPACK_IMPORTED_MODULE_0__.PACKAGE_VERSION || '0.21';
const VERSION = _version_js__WEBPACK_IMPORTED_MODULE_0__.PACKAGE_VERSION || '0.24';
const LIBRARY = 'tinodejs/' + VERSION;
const TOPIC_NEW = 'new';
const TOPIC_NEW_CHAN = 'nch';
const TOPIC_ME = 'me';
const TOPIC_FND = 'fnd';
const TOPIC_SYS = 'sys';
const TOPIC_SLF = 'slf';
const TOPIC_CHAN = 'chn';
const TOPIC_GRP = 'grp';
const TOPIC_P2P = 'p2p';
@@ -3894,13 +3896,17 @@ class Topic {
'nch': _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_GRP,
'chn': _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_GRP,
'usr': _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_P2P,
'sys': _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_SYS
'sys': _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_SYS,
'slf': _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_SLF
};
return types[typeof name == 'string' ? name.substring(0, 3) : 'xxx'];
}
static isMeTopicName(name) {
return Topic.topicType(name) == _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_ME;
}
static isSelfTopicName(name) {
return Topic.topicType(name) == _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_SLF;
}
static isGroupTopicName(name) {
return Topic.topicType(name) == _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_GRP;
}
@@ -3908,7 +3914,7 @@ class Topic {
return Topic.topicType(name) == _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_P2P;
}
static isCommTopicName(name) {
return Topic.isP2PTopicName(name) || Topic.isGroupTopicName(name);
return Topic.isP2PTopicName(name) || Topic.isGroupTopicName(name) || Topic.isSelfTopicName(name);
}
static isNewGroupTopicName(name) {
return typeof name == 'string' && (name.substring(0, 3) == _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_NEW || name.substring(0, 3) == _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_NEW_CHAN);
@@ -4633,6 +4639,9 @@ class Topic {
isMeType() {
return Topic.isMeTopicName(this.name);
}
isSelfType() {
return Topic.isSelfTopicName(this.name);
}
isChannelType() {
return Topic.isChannelTopicName(this.name);
}
@@ -5263,7 +5272,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ PACKAGE_VERSION: () => (/* binding */ PACKAGE_VERSION)
/* harmony export */ });
const PACKAGE_VERSION = "0.23.1-rc1";
const PACKAGE_VERSION = "0.24.0-rc1";
/***/ })
@@ -6081,6 +6090,9 @@ class Tinode {
static isMeTopicName(name) {
return _topic_js__WEBPACK_IMPORTED_MODULE_8__["default"].isMeTopicName(name);
}
static isSelfTopicName(name) {
return _topic_js__WEBPACK_IMPORTED_MODULE_8__["default"].isSelfTopicName(name);
}
static isGroupTopicName(name) {
return _topic_js__WEBPACK_IMPORTED_MODULE_8__["default"].isGroupTopicName(name);
}
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