fixing display of avatars by URL
This commit is contained in:
+18
-1
@@ -1101,13 +1101,29 @@ i.search {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-upload .preview {
|
||||
.avatar-upload img.preview {
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-upload img.preview::before {
|
||||
position: absolute;
|
||||
content: " ";
|
||||
display: flex;
|
||||
font-size: 0.9rem;
|
||||
font-weight: lighter;
|
||||
color: #666;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
background: center no-repeat url("../img/broken_image.png");
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-upload .inputfile.hidden {
|
||||
width: 0.1px;
|
||||
height: 0.1px;
|
||||
@@ -2177,6 +2193,7 @@ ul.tabbar li.active > a {
|
||||
border-radius: 0.25rem;
|
||||
border: solid 2px #1e88e5;
|
||||
box-shadow: 1px 1px 3px grey;
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
.cropper .preview {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
@@ -45,12 +45,12 @@ export const MEDIA_BREAKPOINT = 640;
|
||||
// Size of css 'rem' unit in pixels. Default 1rem = 10pt = 13px.
|
||||
export const REM_SIZE = 13;
|
||||
|
||||
// Size of the avatar image: when an image is uploaded, it's resized to
|
||||
// Size of the avatar image: when image dimensions are greater or the image is not square, it's resized to
|
||||
// a square of this size or less.
|
||||
export const AVATAR_SIZE = 384;
|
||||
|
||||
// Maximum size of an avatar in bytes for sending in-band. Bigger avatars will be sent out of band (as uploads).
|
||||
export const MAX_AVATAR_BYTES = 8192;
|
||||
export const MAX_AVATAR_BYTES = 4096;
|
||||
|
||||
// Size of the broken_image shown in MessagesView
|
||||
export const BROKEN_IMAGE_SIZE = 32;
|
||||
|
||||
@@ -6,8 +6,8 @@ import { bytesToHumanSize } from './strformat.js'
|
||||
export const SUPPORTED_IMAGE_FORMATS = ['image/jpeg', 'image/gif', 'image/png', 'image/svg', 'image/svg+xml'];
|
||||
export const MIME_EXTENSIONS = ['jpg', 'gif', 'png', 'svg', 'svg'];
|
||||
|
||||
// Make a data: URL from public.photo
|
||||
export function makeImageDataUrl(photo) {
|
||||
// Get an URL from a theCard photo: either create a data: URL or return reference URL.
|
||||
export function makeImageUrl(photo) {
|
||||
if (photo) {
|
||||
if (photo.data && photo.type) {
|
||||
const mime = photo.type.startsWith('image/') ? photo.type : ('image/' + photo.type);
|
||||
|
||||
+5
-5
@@ -38,10 +38,12 @@ export function theCard(fn, imageUrl, imageMimeType) {
|
||||
if (matches) {
|
||||
mimeType = matches[1];
|
||||
card.photo = {
|
||||
data: imageUrl.substring(imageUrl.indexOf(',') + 1)
|
||||
data: imageUrl.substring(imageUrl.indexOf(',') + 1),
|
||||
ref: Tinode.DEL_CHAR
|
||||
};
|
||||
} else {
|
||||
card.photo = {
|
||||
data: Tinode.DEL_CHAR,
|
||||
ref: imageUrl
|
||||
};
|
||||
}
|
||||
@@ -107,10 +109,9 @@ export function isUrlRelative(url) {
|
||||
|
||||
// Ensure URL does not present an XSS risk. Optional allowedSchemes may contain an array of
|
||||
// strings with permitted URL schemes, such as ['ftp', 'ftps']; otherwise accept http and https only.
|
||||
// If apikey and/or token is defined, it's appended to relative URL.
|
||||
export function sanitizeUrl(url, allowedSchemes) {
|
||||
if (!url) {
|
||||
return null;
|
||||
if (typeof url != 'string') {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Strip control characters and whitespace. They are not valid URL characters anyway.
|
||||
@@ -139,7 +140,6 @@ export function sanitizeUrl(url, allowedSchemes) {
|
||||
|
||||
// Ensure URL is suitable for <img src="url"> field: the URL must be a relative URL or
|
||||
// have http:, https:, blob: or data: scheme.
|
||||
// The relative URL is appended with the API key and/or auth token.
|
||||
// In case of data: scheme, the URL must start with a 'data:image/XXXX;base64,'.
|
||||
export function sanitizeImageUrl(url) {
|
||||
if (!url) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import TagManager from '../widgets/tag-manager.jsx';
|
||||
|
||||
import { AVATAR_SIZE, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE,
|
||||
MAX_INBAND_ATTACHMENT_SIZE, MAX_TITLE_LENGTH } from '../config.js';
|
||||
import { imageScaled, blobToBase64, makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
import { imageScaled, blobToBase64, makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { arrayEqual, asEmail, asPhone, theCard } from '../lib/utils.js';
|
||||
|
||||
export default class AccGeneralView extends React.Component {
|
||||
@@ -22,7 +22,7 @@ export default class AccGeneralView extends React.Component {
|
||||
const me = this.props.tinode.getMeTopic();
|
||||
this.state = {
|
||||
fullName: me.public ? me.public.fn : undefined,
|
||||
avatar: makeImageDataUrl(me.public ? me.public.photo : null),
|
||||
avatar: makeImageUrl(me.public ? me.public.photo : null),
|
||||
tags: me.tags(),
|
||||
credentials: me.getCredentials() || [],
|
||||
addCredActive: false,
|
||||
@@ -100,10 +100,6 @@ export default class AccGeneralView extends React.Component {
|
||||
if (blob.size > MAX_AVATAR_BYTES) {
|
||||
// Too large to send inband - uploading out of band and sending as a link.
|
||||
const uploader = this.props.tinode.getLargeFileHelper();
|
||||
if (!uploader) {
|
||||
this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({uploading: true});
|
||||
uploader.upload(blob)
|
||||
@@ -117,17 +113,19 @@ export default class AccGeneralView extends React.Component {
|
||||
this.setState({uploading: false});
|
||||
});
|
||||
} else {
|
||||
this.setState({uploading: true});
|
||||
// Convert blob to base64-encoded bits.
|
||||
blobToBase64(blob, (unused, base64bits) => {
|
||||
const du = makeImageDataUrl({data: base64bits, type: mime});
|
||||
const du = makeImageUrl({data: base64bits, type: mime});
|
||||
this.setState({source: du});
|
||||
this.props.onUpdateAccount(undefined, theCard(null, du));
|
||||
this.setState({uploading: false});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (width > AVATAR_SIZE || height > AVATAR_SIZE) {
|
||||
// Avatar is too large even after scaling. Shring it and make square.
|
||||
if (width > AVATAR_SIZE || height > AVATAR_SIZE || width != height) {
|
||||
// Avatar is not square or too large even after cropping. Shrink it and make square.
|
||||
imageScaled(blob, AVATAR_SIZE, AVATAR_SIZE, MAX_EXTERN_ATTACHMENT_SIZE, true,
|
||||
readyToUpload,
|
||||
(err) => {
|
||||
|
||||
@@ -55,9 +55,13 @@ export default class AvatarCropView extends React.PureComponent {
|
||||
onChange={this.handleChange} />
|
||||
</div>
|
||||
<div className="dialog-buttons">
|
||||
{this.props.onCancel ?
|
||||
<button className="secondary" onClick={this.props.onCancel}>
|
||||
<FormattedMessage id="button_cancel" defaultMessage="Cancel" description="Button [Cancel]" />
|
||||
</button>
|
||||
: null}
|
||||
<button className="primary" onClick={this.handleSubmit}>
|
||||
<FormattedMessage id="button_ok"
|
||||
defaultMessage="OK" description="Button [OK]" />
|
||||
<FormattedMessage id="button_ok" defaultMessage="OK" description="Button [OK]" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl';
|
||||
import AvatarUpload from '../widgets/avatar-upload.jsx';
|
||||
import BadgeList from '../widgets/badge-list.jsx';
|
||||
|
||||
import { makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
|
||||
export default class EditAccountView extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -14,7 +14,7 @@ export default class EditAccountView extends React.Component {
|
||||
const me = this.props.tinode.getMeTopic();
|
||||
this.state = {
|
||||
fullName: me.public ? me.public.fn : undefined,
|
||||
avatar: makeImageDataUrl(me.public ? me.public.photo : null)
|
||||
avatar: makeImageUrl(me.public ? me.public.photo : null)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import TagManager from '../widgets/tag-manager.jsx';
|
||||
|
||||
import { MAX_TITLE_LENGTH, NO_ACCESS_MODE } from '../config.js';
|
||||
|
||||
import { makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { arrayEqual, theCard } from '../lib/utils.js';
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -237,7 +237,7 @@ class InfoView extends React.Component {
|
||||
muted: acs && acs.isMuted(),
|
||||
|
||||
fullName: topic.public ? topic.public.fn : undefined,
|
||||
avatar: makeImageDataUrl(topic.public ? topic.public.photo : null),
|
||||
avatar: makeImageUrl(topic.public ? topic.public.photo : null),
|
||||
trustedBadges: badges,
|
||||
private: topic.private ? topic.private.comment : null,
|
||||
address: topic.name,
|
||||
|
||||
@@ -19,7 +19,7 @@ import SendMessage from '../widgets/send-message.jsx';
|
||||
import { DEFAULT_P2P_ACCESS_MODE, IMAGE_PREVIEW_DIM, KEYPRESS_DELAY, MESSAGES_PAGE,
|
||||
MAX_EXTERN_ATTACHMENT_SIZE, MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE, READ_DELAY } from '../config.js';
|
||||
import { SUPPORTED_IMAGE_FORMATS, blobToBase64, filePasted, fileToBase64,
|
||||
imageScaled, makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
imageScaled, makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import HashNavigation from '../lib/navigation.js';
|
||||
import { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js';
|
||||
|
||||
@@ -265,7 +265,7 @@ class MessagesView extends React.Component {
|
||||
if (topic.public) {
|
||||
Object.assign(nextState, {
|
||||
title: topic.public.fn,
|
||||
avatar: makeImageDataUrl(topic.public.photo)
|
||||
avatar: makeImageUrl(topic.public.photo)
|
||||
});
|
||||
} else {
|
||||
Object.assign(nextState, {
|
||||
@@ -395,7 +395,7 @@ class MessagesView extends React.Component {
|
||||
if (desc.public) {
|
||||
this.setState({
|
||||
title: desc.public.fn,
|
||||
avatar: makeImageDataUrl(desc.public.photo)
|
||||
avatar: makeImageUrl(desc.public.photo)
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
@@ -645,10 +645,6 @@ class MessagesView extends React.Component {
|
||||
if (file.size > maxInbandAttachmentSize) {
|
||||
// Too large to send inband - uploading out of band and sending as a link.
|
||||
const uploader = this.props.tinode.getLargeFileHelper();
|
||||
if (!uploader) {
|
||||
this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));
|
||||
return;
|
||||
}
|
||||
const uploadCompletionPromise = uploader.upload(file);
|
||||
const msg = Drafty.attachFile(null, {
|
||||
mime: file.type,
|
||||
@@ -873,7 +869,7 @@ class MessagesView extends React.Component {
|
||||
const user = topic.userDesc(thisFrom);
|
||||
if (user && user.public) {
|
||||
userName = user.public.fn;
|
||||
userAvatar = makeImageDataUrl(user.public.photo);
|
||||
userAvatar = makeImageUrl(user.public.photo);
|
||||
}
|
||||
userFrom = thisFrom;
|
||||
chatBoxClass='chat-box group';
|
||||
|
||||
@@ -118,6 +118,8 @@ class SidepanelView extends React.Component {
|
||||
badges = null;
|
||||
}
|
||||
|
||||
console.log("avatar URL=", avatar);
|
||||
|
||||
let onCancel;
|
||||
if (['login', 'contacts'].indexOf(view) == -1) {
|
||||
onCancel = this.props.onCancel;
|
||||
@@ -129,6 +131,7 @@ class SidepanelView extends React.Component {
|
||||
state={view}
|
||||
title={title}
|
||||
avatar={avatar}
|
||||
tinode={this.props.tinode}
|
||||
trustedBadges={badges}
|
||||
myUserId={this.props.myUserId}
|
||||
onSignUp={this.props.onSignUp}
|
||||
|
||||
+10
-12
@@ -18,7 +18,7 @@ import SidepanelView from './sidepanel-view.jsx';
|
||||
import { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, LOGGING_ENABLED,
|
||||
MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js';
|
||||
import { PACKAGE_VERSION } from '../version.js';
|
||||
import { base64ReEncode, makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
import { base64ReEncode, makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js';
|
||||
import LocalStorageUtil from '../lib/local-storage.js';
|
||||
import HashNavigation from '../lib/navigation.js';
|
||||
@@ -308,7 +308,7 @@ class TinodeWeb extends React.Component {
|
||||
initFCMessaging() {
|
||||
const {formatMessage, locale} = this.props.intl;
|
||||
const onError = (msg, err) => {
|
||||
console.log(msg, err);
|
||||
console.error(msg, err);
|
||||
this.handleError(formatMessage(messages.push_init_failed), 'err');
|
||||
this.setState({desktopAlertsEnabled: false});
|
||||
}
|
||||
@@ -384,7 +384,7 @@ class TinodeWeb extends React.Component {
|
||||
'cred','reset','newtpk','archive','blocked','contacts',''].includes(hash.path[0])) {
|
||||
this.setState({sidePanelSelected: hash.path[0]});
|
||||
} else {
|
||||
console.log("Unknown sidepanel view", hash.path[0]);
|
||||
console.info("Unknown sidepanel view", hash.path[0]);
|
||||
}
|
||||
|
||||
// Topic for MessagesView selector.
|
||||
@@ -464,13 +464,11 @@ class TinodeWeb extends React.Component {
|
||||
handlePersistenceChange(persist) {
|
||||
if (persist) {
|
||||
this.tinode.initStorage().then(() => {
|
||||
console.log("storage initialized");
|
||||
LocalStorageUtil.setObject('keep-logged-in', true);
|
||||
this.setState({persist: true});
|
||||
});
|
||||
} else {
|
||||
this.tinode.clearStorage().then(() => {
|
||||
console.log("storage cleared");
|
||||
LocalStorageUtil.setObject('keep-logged-in', false);
|
||||
this.setState({persist: false});
|
||||
});
|
||||
@@ -653,7 +651,7 @@ class TinodeWeb extends React.Component {
|
||||
if (desc.public) {
|
||||
this.setState({
|
||||
sidePanelTitle: desc.public.fn,
|
||||
sidePanelAvatar: makeImageDataUrl(desc.public.photo)
|
||||
sidePanelAvatar: makeImageUrl(desc.public.photo)
|
||||
});
|
||||
}
|
||||
if (desc.trusted) {
|
||||
@@ -721,7 +719,7 @@ class TinodeWeb extends React.Component {
|
||||
} else {
|
||||
// TODO(gene): handle other types of notifications:
|
||||
// * ua -- user agent changes (maybe display a pictogram for mobile/desktop).
|
||||
console.log("Unsupported (yet) presence update:" + what + " in: " + cont.topic);
|
||||
console.info("Unsupported (yet) presence update:" + what + " in: " + cont.topic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,7 +943,7 @@ class TinodeWeb extends React.Component {
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.log("Unknown invitation action", '"' + action + '""');
|
||||
console.info("Unknown invitation action", '"' + action + '""');
|
||||
}
|
||||
|
||||
if (response != null) {
|
||||
@@ -1077,7 +1075,7 @@ class TinodeWeb extends React.Component {
|
||||
}).then(() => {
|
||||
this.requestPushToken();
|
||||
}).catch((err) => {
|
||||
console.log("Failed to get notification permission.", err);
|
||||
console.error("Failed to get notification permission.", err);
|
||||
this.handleError(err.message, 'err');
|
||||
this.setState({desktopAlerts: false, firebaseToken: null});
|
||||
LocalStorageUtil.updateObject('settings', {desktopAlerts: false});
|
||||
@@ -1090,7 +1088,7 @@ class TinodeWeb extends React.Component {
|
||||
}
|
||||
} else if (this.state.firebaseToken && this.fbPush) {
|
||||
this.fbPush.deleteToken(this.state.firebaseToken).catch((err) => {
|
||||
console.log("Unable to delete token.", err);
|
||||
console.error("Unable to delete token.", err);
|
||||
}).finally(() => {
|
||||
LocalStorageUtil.updateObject('settings', {desktopAlerts: false});
|
||||
localStorage.removeItem('firebase-token');
|
||||
@@ -1119,7 +1117,7 @@ class TinodeWeb extends React.Component {
|
||||
}
|
||||
}).catch((err) => {
|
||||
this.handleError(err.message, 'err');
|
||||
console.log("Failed to retrieve firebase token", err);
|
||||
console.error("Failed to retrieve firebase token", err);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1154,7 +1152,7 @@ class TinodeWeb extends React.Component {
|
||||
let path = '';
|
||||
if (['security','support','general','notif'].includes(parsed.path[0])) {
|
||||
path = 'edit';
|
||||
} else if ('crop') {
|
||||
} else if ('crop' == parsed.path[0]) {
|
||||
path = 'general';
|
||||
} else if ('blocked' == parsed.path[0]) {
|
||||
path = 'security';
|
||||
|
||||
@@ -3,6 +3,8 @@ import React from 'react';
|
||||
import LetterTile from './letter-tile.jsx';
|
||||
import LoadSpinner from './load-spinner.jsx';
|
||||
|
||||
import { sanitizeImageUrl } from '../lib/utils.js';
|
||||
|
||||
import { AVATAR_SIZE } from '../config.js';
|
||||
|
||||
export default class AvatarUpload extends React.Component {
|
||||
@@ -42,7 +44,7 @@ export default class AvatarUpload extends React.Component {
|
||||
<i className="material-icons">clear</i>
|
||||
</a>}
|
||||
{this.state.source ?
|
||||
<img src={this.state.source} className="preview" /> :
|
||||
<img src={this.props.tinode.authorizeURL(sanitizeImageUrl(this.state.source))} className="preview" /> :
|
||||
this.props.readOnly && this.props.uid ?
|
||||
<div className="avatar-box">
|
||||
<LetterTile
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
|
||||
import Chip from './chip.jsx';
|
||||
|
||||
import { makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
|
||||
/* BEGIN ChipInput: group membership widget */
|
||||
export default class ChipInput extends React.Component {
|
||||
@@ -119,7 +119,7 @@ export default class ChipInput extends React.Component {
|
||||
chips.push(
|
||||
<Chip
|
||||
onCancel={this.handleChipCancel}
|
||||
avatar={makeImageDataUrl(item.public ? item.public.photo : null)}
|
||||
avatar={makeImageUrl(item.public ? item.public.photo : null)}
|
||||
title={item.public ? item.public.fn : undefined}
|
||||
noAvatar={this.props.avatarDisabled}
|
||||
topic={item.user}
|
||||
|
||||
@@ -9,7 +9,7 @@ import Tinode from 'tinode-sdk';
|
||||
import Contact from './contact.jsx';
|
||||
import ContactAction from './contact-action.jsx';
|
||||
|
||||
import { makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
|
||||
import { MESSAGE_PREVIEW_LENGTH } from '../config.js';
|
||||
|
||||
@@ -90,7 +90,7 @@ class ContactList extends React.Component {
|
||||
contactNodes.push(
|
||||
<Contact
|
||||
title={c.public ? c.public.fn : null}
|
||||
avatar={makeImageDataUrl(c.public ? c.public.photo : null)}
|
||||
avatar={makeImageUrl(c.public ? c.public.photo : null)}
|
||||
comment={comment}
|
||||
preview={preview}
|
||||
received={deliveryStatus}
|
||||
|
||||
@@ -129,10 +129,12 @@ export default class Cropper extends React.Component {
|
||||
});
|
||||
|
||||
// Initial zoom level fills the bounding box at the smallest image dimension and overflows the largest, i.e. "fill" not "fit".
|
||||
let zoom = Math.max((this.bBoxRect.width / imgRect.width), (this.bBoxRect.height / imgRect.height));
|
||||
const zoom = Math.max(this.bBoxRect.width / imgRect.width, this.bBoxRect.height / imgRect.height);
|
||||
// Converting from viewport coordinates to container, then panning.
|
||||
let panX = (this.cutoutRect.left - this.bBoxRect.left) - ((imgRect.width - this.cutoutRect.width) / 2);
|
||||
let panY = (this.cutoutRect.top - this.bBoxRect.top) - ((imgRect.height - this.cutoutRect.height) / 2);
|
||||
const panX = this.cutoutRect.left - this.bBoxRect.left -
|
||||
(imgRect.width - this.cutoutRect.width) / 2;
|
||||
const panY = this.cutoutRect.top - this.bBoxRect.top -
|
||||
(imgRect.height - this.cutoutRect.height) / 2;
|
||||
|
||||
this.positionAll(panX, panY, zoom);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl';
|
||||
import { MAX_ONLINE_IN_TOPIC } from '../config.js'
|
||||
|
||||
import LetterTile from './letter-tile.jsx';
|
||||
import { makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
|
||||
export default class GroupSubs extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -23,7 +23,7 @@ export default class GroupSubs extends React.Component {
|
||||
<div className="avatar-box" key={sub.user}>
|
||||
<LetterTile
|
||||
topic={sub.user}
|
||||
avatar={makeImageDataUrl(sub.public ? sub.public.photo : null) || true}
|
||||
avatar={makeImageUrl(sub.public ? sub.public.photo : null) || true}
|
||||
title={sub.public ? sub.public.fn : null} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -23,7 +23,7 @@ export default class LetterTile extends React.PureComponent {
|
||||
} else if (this.props.avatar) {
|
||||
// If avatar image is invalid, show a placeholder.
|
||||
avatar = <img className="avatar" alt="avatar" src={this.props.avatar}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||
import CheckBox from './checkbox.jsx';
|
||||
import Contact from './contact.jsx';
|
||||
|
||||
import { makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
|
||||
// Translatable permission names.
|
||||
const messages = defineMessages({
|
||||
@@ -135,7 +135,7 @@ class PermissionsEditor extends React.Component {
|
||||
<ul className="contact-box"><Contact
|
||||
item={this.props.item}
|
||||
title={this.props.userTitle}
|
||||
avatar={makeImageDataUrl(this.props.userAvatar ? this.props.userAvatar : null)} /></ul> : null}
|
||||
avatar={makeImageUrl(this.props.userAvatar ? this.props.userAvatar : null)} /></ul> : null}
|
||||
<label className="small"><FormattedMessage id="title_permissions"
|
||||
defaultMessage="Permissions" description="Section title"/></label>
|
||||
<table className="permission-editor">
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
|
||||
import LetterTile from './letter-tile.jsx';
|
||||
import ButtonBack from './button-back.jsx';
|
||||
import ContactBadges from './contact-badges.jsx';
|
||||
import MenuContacts from './menu-contacts.jsx';
|
||||
import MenuStart from './menu-start.jsx';
|
||||
|
||||
import { sanitizeImageUrl } from '../lib/utils.js';
|
||||
|
||||
export default class SideNavbar extends React.PureComponent {
|
||||
render() {
|
||||
const icon_badges = [];
|
||||
@@ -15,13 +16,17 @@ export default class SideNavbar extends React.PureComponent {
|
||||
icon_badges.push({icon: b, color: 'badge-inv'});
|
||||
});
|
||||
}
|
||||
let avatar = null;
|
||||
if (this.props.tinode) {
|
||||
avatar = this.props.tinode.authorizeURL(sanitizeImageUrl(this.props.avatar));
|
||||
}
|
||||
return (
|
||||
<div id="side-caption-panel" className="caption-panel">
|
||||
{this.props.onCancel ? <ButtonBack onBack={this.props.onCancel} /> : null}
|
||||
{this.props.avatar ?
|
||||
{avatar ?
|
||||
<div id="self-avatar" className="avatar-box">
|
||||
<LetterTile
|
||||
avatar={this.props.avatar}
|
||||
avatar={avatar}
|
||||
topic={this.props.myUserId}
|
||||
title={this.props.title} />
|
||||
</div>
|
||||
|
||||
+130
-100
@@ -65,7 +65,7 @@ const NO_ACCESS_MODE = 'N';
|
||||
const MEDIA_BREAKPOINT = 640;
|
||||
const REM_SIZE = 13;
|
||||
const AVATAR_SIZE = 384;
|
||||
const MAX_AVATAR_BYTES = 8192;
|
||||
const MAX_AVATAR_BYTES = 4096;
|
||||
const BROKEN_IMAGE_SIZE = 32;
|
||||
const MESSAGES_PAGE = 24;
|
||||
const MAX_INBAND_ATTACHMENT_SIZE = 262144;
|
||||
@@ -91,7 +91,7 @@ __webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "SUPPORTED_IMAGE_FORMATS": () => (/* binding */ SUPPORTED_IMAGE_FORMATS),
|
||||
/* harmony export */ "MIME_EXTENSIONS": () => (/* binding */ MIME_EXTENSIONS),
|
||||
/* harmony export */ "makeImageDataUrl": () => (/* binding */ makeImageDataUrl),
|
||||
/* harmony export */ "makeImageUrl": () => (/* binding */ makeImageUrl),
|
||||
/* harmony export */ "fitImageSize": () => (/* binding */ fitImageSize),
|
||||
/* harmony export */ "fileNameForMime": () => (/* binding */ fileNameForMime),
|
||||
/* harmony export */ "imageScaled": () => (/* binding */ imageScaled),
|
||||
@@ -110,7 +110,7 @@ __webpack_require__.r(__webpack_exports__);
|
||||
|
||||
const SUPPORTED_IMAGE_FORMATS = ['image/jpeg', 'image/gif', 'image/png', 'image/svg', 'image/svg+xml'];
|
||||
const MIME_EXTENSIONS = ['jpg', 'gif', 'png', 'svg', 'svg'];
|
||||
function makeImageDataUrl(photo) {
|
||||
function makeImageUrl(photo) {
|
||||
if (photo) {
|
||||
if (photo.data && photo.type) {
|
||||
const mime = photo.type.startsWith('image/') ? photo.type : 'image/' + photo.type;
|
||||
@@ -611,10 +611,12 @@ function theCard(fn, imageUrl, imageMimeType) {
|
||||
if (matches) {
|
||||
mimeType = matches[1];
|
||||
card.photo = {
|
||||
data: imageUrl.substring(imageUrl.indexOf(',') + 1)
|
||||
data: imageUrl.substring(imageUrl.indexOf(',') + 1),
|
||||
ref: (tinode_sdk__WEBPACK_IMPORTED_MODULE_0___default().DEL_CHAR)
|
||||
};
|
||||
} else {
|
||||
card.photo = {
|
||||
data: (tinode_sdk__WEBPACK_IMPORTED_MODULE_0___default().DEL_CHAR),
|
||||
ref: imageUrl
|
||||
};
|
||||
}
|
||||
@@ -670,8 +672,8 @@ function isUrlRelative(url) {
|
||||
return url && !/^\s*([a-z][a-z0-9+.-]*:|\/\/)/im.test(url);
|
||||
}
|
||||
function sanitizeUrl(url, allowedSchemes) {
|
||||
if (!url) {
|
||||
return null;
|
||||
if (typeof url != 'string') {
|
||||
return url;
|
||||
}
|
||||
|
||||
url = url.replace(/[^\x20-\x7E]/gmi, '').trim();
|
||||
@@ -798,7 +800,7 @@ class AccGeneralView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compo
|
||||
const me = this.props.tinode.getMeTopic();
|
||||
this.state = {
|
||||
fullName: me.public ? me.public.fn : undefined,
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_8__.makeImageDataUrl)(me.public ? me.public.photo : null),
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_8__.makeImageUrl)(me.public ? me.public.photo : null),
|
||||
tags: me.tags(),
|
||||
credentials: me.getCredentials() || [],
|
||||
addCredActive: false,
|
||||
@@ -811,9 +813,10 @@ class AccGeneralView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compo
|
||||
this.tnNewTags = this.tnNewTags.bind(this);
|
||||
this.tnCredsUpdated = this.tnCredsUpdated.bind(this);
|
||||
this.handleFullNameUpdate = this.handleFullNameUpdate.bind(this);
|
||||
this.handleImageUploaded = this.handleImageUploaded.bind(this);
|
||||
this.handleImageUpdated = this.handleImageUpdated.bind(this);
|
||||
this.handleAvatarCropped = this.handleAvatarCropped.bind(this);
|
||||
this.handleCancel = this.handleCancel.bind(this);
|
||||
this.uploadAvatar = this.uploadAvatar.bind(this);
|
||||
this.handleAvatarCropCancel = this.handleAvatarCropCancel.bind(this);
|
||||
this.handleCredChange = this.handleCredChange.bind(this);
|
||||
this.handleCredKeyDown = this.handleCredKeyDown.bind(this);
|
||||
this.handleCredEntered = this.handleCredEntered.bind(this);
|
||||
@@ -855,11 +858,15 @@ class AccGeneralView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compo
|
||||
}
|
||||
}
|
||||
|
||||
handleImageUploaded(mime, img) {
|
||||
handleImageUpdated(mime, img) {
|
||||
this.setState({
|
||||
newAvatar: img,
|
||||
newAvatarMime: mime
|
||||
});
|
||||
|
||||
if (!img) {
|
||||
this.props.onUpdateAccount(undefined, (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_9__.theCard)(null, (tinode_sdk__WEBPACK_IMPORTED_MODULE_2___default().DEL_CHAR)));
|
||||
}
|
||||
}
|
||||
|
||||
handleAvatarCropped(mime, blob, width, height) {
|
||||
@@ -869,9 +876,58 @@ class AccGeneralView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compo
|
||||
newAvatar: null,
|
||||
newAvatarMime: null
|
||||
});
|
||||
|
||||
if (blob) {
|
||||
this.uploadAvatar(mime, blob, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
handleCancel(img) {
|
||||
uploadAvatar(mime, blob, width, height) {
|
||||
const readyToUpload = (mime, blob, width, height) => {
|
||||
if (blob.size > _config_js__WEBPACK_IMPORTED_MODULE_7__.MAX_AVATAR_BYTES) {
|
||||
const uploader = this.props.tinode.getLargeFileHelper();
|
||||
this.setState({
|
||||
uploading: true
|
||||
});
|
||||
uploader.upload(blob).then(url => {
|
||||
this.props.onUpdateAccount(undefined, (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_9__.theCard)(null, url));
|
||||
}).catch(err => {
|
||||
this.props.onError(err, 'err');
|
||||
}).finally(() => {
|
||||
this.setState({
|
||||
uploading: false
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
uploading: true
|
||||
});
|
||||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_8__.blobToBase64)(blob, (unused, base64bits) => {
|
||||
const du = (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_8__.makeImageUrl)({
|
||||
data: base64bits,
|
||||
type: mime
|
||||
});
|
||||
this.setState({
|
||||
source: du
|
||||
});
|
||||
this.props.onUpdateAccount(undefined, (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_9__.theCard)(null, du));
|
||||
this.setState({
|
||||
uploading: false
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (width > _config_js__WEBPACK_IMPORTED_MODULE_7__.AVATAR_SIZE || height > _config_js__WEBPACK_IMPORTED_MODULE_7__.AVATAR_SIZE || width != height) {
|
||||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_8__.imageScaled)(blob, _config_js__WEBPACK_IMPORTED_MODULE_7__.AVATAR_SIZE, _config_js__WEBPACK_IMPORTED_MODULE_7__.AVATAR_SIZE, _config_js__WEBPACK_IMPORTED_MODULE_7__.MAX_EXTERN_ATTACHMENT_SIZE, true, readyToUpload, err => {
|
||||
this.props.onError(err, 'err');
|
||||
});
|
||||
} else {
|
||||
readyToUpload(mime, blob, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
handleAvatarCropCancel(img) {
|
||||
this.setState({
|
||||
newAvatar: null,
|
||||
newAvatarMime: null
|
||||
@@ -947,7 +1003,7 @@ class AccGeneralView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compo
|
||||
avatar: this.state.newAvatar,
|
||||
mime: this.state.newAvatarMime,
|
||||
onSubmit: this.handleAvatarCropped,
|
||||
onCancel: this.handleCancel,
|
||||
onCancel: this.handleAvatarCropCancel,
|
||||
onError: this.props.onError
|
||||
});
|
||||
}
|
||||
@@ -1007,8 +1063,7 @@ class AccGeneralView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compo
|
||||
avatar: this.state.avatar,
|
||||
uid: this.props.myUserId,
|
||||
title: this.state.fullName,
|
||||
onImageReceived: this.handleImageUploaded,
|
||||
onImageChanged: this.handleImageChanged,
|
||||
onImageUpdated: this.handleImageUpdated,
|
||||
onError: this.props.onError
|
||||
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "hr"
|
||||
@@ -1566,7 +1621,16 @@ class AvatarCropView extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
|
||||
onChange: this.handleChange
|
||||
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "dialog-buttons"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
||||
}, this.props.onCancel ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
||||
className: "secondary",
|
||||
onClick: this.props.onCancel
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
id: "button_cancel",
|
||||
defaultMessage: [{
|
||||
"type": 0,
|
||||
"value": "Cancel"
|
||||
}]
|
||||
})) : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
|
||||
className: "primary",
|
||||
onClick: this.handleSubmit
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
@@ -1951,7 +2015,7 @@ class EditAccountView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Comp
|
||||
const me = this.props.tinode.getMeTopic();
|
||||
this.state = {
|
||||
fullName: me.public ? me.public.fn : undefined,
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.makeImageDataUrl)(me.public ? me.public.photo : null)
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.makeImageUrl)(me.public ? me.public.photo : null)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2358,7 +2422,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
deleter: acs && acs.isDeleter(),
|
||||
muted: acs && acs.isMuted(),
|
||||
fullName: topic.public ? topic.public.fn : undefined,
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageDataUrl)(topic.public ? topic.public.photo : null),
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageUrl)(topic.public ? topic.public.photo : null),
|
||||
trustedBadges: badges,
|
||||
private: topic.private ? topic.private.comment : null,
|
||||
address: topic.name,
|
||||
@@ -2783,7 +2847,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
readOnly: !this.state.owner,
|
||||
uid: this.props.topic,
|
||||
title: this.state.fullName,
|
||||
onImageChanged: this.handleImageChanged,
|
||||
onImageUpdated: this.handleImageChanged,
|
||||
onError: this.props.onError
|
||||
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "hr"
|
||||
@@ -3463,7 +3527,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
if (topic.public) {
|
||||
Object.assign(nextState, {
|
||||
title: topic.public.fn,
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageDataUrl)(topic.public.photo)
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageUrl)(topic.public.photo)
|
||||
});
|
||||
} else {
|
||||
Object.assign(nextState, {
|
||||
@@ -3606,7 +3670,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
if (desc.public) {
|
||||
this.setState({
|
||||
title: desc.public.fn,
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageDataUrl)(desc.public.photo)
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageUrl)(desc.public.photo)
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
@@ -3879,12 +3943,6 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
|
||||
if (file.size > maxInbandAttachmentSize) {
|
||||
const uploader = this.props.tinode.getLargeFileHelper();
|
||||
|
||||
if (!uploader) {
|
||||
this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));
|
||||
return;
|
||||
}
|
||||
|
||||
const uploadCompletionPromise = uploader.upload(file);
|
||||
const msg = Drafty.attachFile(null, {
|
||||
mime: file.type,
|
||||
@@ -4110,7 +4168,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
|
||||
if (user && user.public) {
|
||||
userName = user.public.fn;
|
||||
userAvatar = (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageDataUrl)(user.public.photo);
|
||||
userAvatar = (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageUrl)(user.public.photo);
|
||||
}
|
||||
|
||||
userFrom = thisFrom;
|
||||
@@ -4871,6 +4929,7 @@ class SidepanelView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compon
|
||||
badges = null;
|
||||
}
|
||||
|
||||
console.log("avatar URL=", avatar);
|
||||
let onCancel;
|
||||
|
||||
if (['login', 'contacts'].indexOf(view) == -1) {
|
||||
@@ -4884,6 +4943,7 @@ class SidepanelView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compon
|
||||
state: view,
|
||||
title: title,
|
||||
avatar: avatar,
|
||||
tinode: this.props.tinode,
|
||||
trustedBadges: badges,
|
||||
myUserId: this.props.myUserId,
|
||||
onSignUp: this.props.onSignUp,
|
||||
@@ -5315,7 +5375,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
} = this.props.intl;
|
||||
|
||||
const onError = (msg, err) => {
|
||||
console.log(msg, err);
|
||||
console.error(msg, err);
|
||||
this.handleError(formatMessage(messages.push_init_failed), 'err');
|
||||
this.setState({
|
||||
desktopAlertsEnabled: false
|
||||
@@ -5399,7 +5459,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
sidePanelSelected: hash.path[0]
|
||||
});
|
||||
} else {
|
||||
console.log("Unknown sidepanel view", hash.path[0]);
|
||||
console.info("Unknown sidepanel view", hash.path[0]);
|
||||
}
|
||||
|
||||
const topicName = hash.path[1];
|
||||
@@ -5499,7 +5559,6 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
handlePersistenceChange(persist) {
|
||||
if (persist) {
|
||||
this.tinode.initStorage().then(() => {
|
||||
console.log("storage initialized");
|
||||
_lib_local_storage_js__WEBPACK_IMPORTED_MODULE_15__.default.setObject('keep-logged-in', true);
|
||||
this.setState({
|
||||
persist: true
|
||||
@@ -5507,7 +5566,6 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
});
|
||||
} else {
|
||||
this.tinode.clearStorage().then(() => {
|
||||
console.log("storage cleared");
|
||||
_lib_local_storage_js__WEBPACK_IMPORTED_MODULE_15__.default.setObject('keep-logged-in', false);
|
||||
this.setState({
|
||||
persist: false
|
||||
@@ -5684,7 +5742,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
if (desc.public) {
|
||||
this.setState({
|
||||
sidePanelTitle: desc.public.fn,
|
||||
sidePanelAvatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_13__.makeImageDataUrl)(desc.public.photo)
|
||||
sidePanelAvatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_13__.makeImageUrl)(desc.public.photo)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5745,7 +5803,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
});
|
||||
}
|
||||
} else if (what == 'del') {} else if (what == 'upd') {} else {
|
||||
console.log("Unsupported (yet) presence update:" + what + " in: " + cont.topic);
|
||||
console.info("Unsupported (yet) presence update:" + what + " in: " + cont.topic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5965,7 +6023,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log("Unknown invitation action", '"' + action + '""');
|
||||
console.info("Unknown invitation action", '"' + action + '""');
|
||||
}
|
||||
|
||||
if (response != null) {
|
||||
@@ -6094,7 +6152,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
}).then(() => {
|
||||
this.requestPushToken();
|
||||
}).catch(err => {
|
||||
console.log("Failed to get notification permission.", err);
|
||||
console.error("Failed to get notification permission.", err);
|
||||
this.handleError(err.message, 'err');
|
||||
this.setState({
|
||||
desktopAlerts: false,
|
||||
@@ -6117,7 +6175,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
}
|
||||
} else if (this.state.firebaseToken && this.fbPush) {
|
||||
this.fbPush.deleteToken(this.state.firebaseToken).catch(err => {
|
||||
console.log("Unable to delete token.", err);
|
||||
console.error("Unable to delete token.", err);
|
||||
}).finally(() => {
|
||||
_lib_local_storage_js__WEBPACK_IMPORTED_MODULE_15__.default.updateObject('settings', {
|
||||
desktopAlerts: false
|
||||
@@ -6164,7 +6222,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
}
|
||||
}).catch(err => {
|
||||
this.handleError(err.message, 'err');
|
||||
console.log("Failed to retrieve firebase token", err);
|
||||
console.error("Failed to retrieve firebase token", err);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6208,9 +6266,13 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
|
||||
if (['security', 'support', 'general', 'notif'].includes(parsed.path[0])) {
|
||||
path = 'edit';
|
||||
} else if (true) {
|
||||
} else if ('crop' == parsed.path[0]) {
|
||||
path = 'general';
|
||||
} else {}
|
||||
} else if ('blocked' == parsed.path[0]) {
|
||||
path = 'security';
|
||||
} else if (this.state.myUserId) {
|
||||
path = 'contacts';
|
||||
}
|
||||
|
||||
parsed.path[0] = path;
|
||||
|
||||
@@ -7102,8 +7164,8 @@ __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 _letter_tile_jsx__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./letter-tile.jsx */ "./src/widgets/letter-tile.jsx");
|
||||
/* harmony import */ var _load_spinner_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./load-spinner.jsx */ "./src/widgets/load-spinner.jsx");
|
||||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||||
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.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");
|
||||
|
||||
|
||||
|
||||
@@ -7113,10 +7175,9 @@ class AvatarUpload extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
source: props.avatar,
|
||||
uploading: false
|
||||
source: props.avatar
|
||||
};
|
||||
this.handleFileUpload = this.handleFileUpload.bind(this);
|
||||
this.handleFileReceived = this.handleFileReceived.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@@ -7127,48 +7188,9 @@ class AvatarUpload extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
}
|
||||
}
|
||||
|
||||
handleFileUpload(e) {
|
||||
handleFileReceived(e) {
|
||||
const image = e.target.files[0];
|
||||
|
||||
if (this.props.onImageReceived) {
|
||||
this.props.onImageReceived(image.type, URL.createObjectURL(image));
|
||||
return;
|
||||
}
|
||||
|
||||
if (image.size > _config_js__WEBPACK_IMPORTED_MODULE_3__.MAX_AVATAR_BYTES) {
|
||||
const uploader = this.props.tinode.getLargeFileHelper();
|
||||
|
||||
if (!uploader) {
|
||||
this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));
|
||||
return;
|
||||
}
|
||||
|
||||
uploader.upload(image).then(url => {
|
||||
this.props.onImageChanged(url);
|
||||
}).catch(err => {
|
||||
this.props.onError(err, 'err');
|
||||
}).finally(() => {
|
||||
this.setState({
|
||||
uploading: false
|
||||
});
|
||||
});
|
||||
} else {
|
||||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.imageScaled)(image, _config_js__WEBPACK_IMPORTED_MODULE_3__.AVATAR_SIZE, _config_js__WEBPACK_IMPORTED_MODULE_3__.AVATAR_SIZE, _config_js__WEBPACK_IMPORTED_MODULE_3__.MAX_EXTERN_ATTACHMENT_SIZE, true, (mime, blob) => {
|
||||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.blobToBase64)(blob, (unused, base64bits) => {
|
||||
const du = (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.makeImageDataUrl)({
|
||||
data: base64bits,
|
||||
type: mime
|
||||
});
|
||||
this.setState({
|
||||
source: du
|
||||
});
|
||||
this.props.onImageChanged(du);
|
||||
});
|
||||
}, err => {
|
||||
this.props.onError(err, 'err');
|
||||
});
|
||||
}
|
||||
|
||||
this.props.onImageUpdated(image.type, URL.createObjectURL(image), image.name);
|
||||
e.target.value = '';
|
||||
}
|
||||
|
||||
@@ -7182,12 +7204,12 @@ class AvatarUpload extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
className: "clear-avatar",
|
||||
onClick: e => {
|
||||
e.preventDefault();
|
||||
this.props.onImageChanged(null);
|
||||
this.props.onImageUpdated();
|
||||
}
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons"
|
||||
}, "clear")), this.state.source ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", {
|
||||
src: this.state.source,
|
||||
src: this.props.tinode.authorizeURL((0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_3__.sanitizeImageUrl)(this.state.source)),
|
||||
className: "preview"
|
||||
}) : this.props.readOnly && this.props.uid ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "avatar-box"
|
||||
@@ -7197,19 +7219,19 @@ class AvatarUpload extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
||||
title: this.props.title
|
||||
})) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
className: "blank"
|
||||
}, _config_js__WEBPACK_IMPORTED_MODULE_3__.AVATAR_SIZE, "\xD7", _config_js__WEBPACK_IMPORTED_MODULE_3__.AVATAR_SIZE), this.props.readOnly ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", {
|
||||
}, _config_js__WEBPACK_IMPORTED_MODULE_4__.AVATAR_SIZE, "\xD7", _config_js__WEBPACK_IMPORTED_MODULE_4__.AVATAR_SIZE), this.props.readOnly ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", {
|
||||
type: "file",
|
||||
id: randId,
|
||||
className: "inputfile hidden",
|
||||
accept: "image/*",
|
||||
onChange: this.handleFileUpload
|
||||
onChange: this.handleFileReceived
|
||||
}), this.props.readOnly ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
|
||||
htmlFor: randId,
|
||||
className: "round"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||
className: "material-icons"
|
||||
}, "file_upload")), react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_load_spinner_jsx__WEBPACK_IMPORTED_MODULE_2__.default, {
|
||||
show: this.state.uploading,
|
||||
show: this.props.uploading,
|
||||
large: true,
|
||||
clear: true,
|
||||
centered: true
|
||||
@@ -7788,7 +7810,7 @@ class ChipInput extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
this.state.sortedChips.map(item => {
|
||||
chips.push(react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_chip_jsx__WEBPACK_IMPORTED_MODULE_1__.default, {
|
||||
onCancel: this.handleChipCancel,
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_2__.makeImageDataUrl)(item.public ? item.public.photo : null),
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_2__.makeImageUrl)(item.public ? item.public.photo : null),
|
||||
title: item.public ? item.public.fn : undefined,
|
||||
noAvatar: this.props.avatarDisabled,
|
||||
topic: item.user,
|
||||
@@ -8087,7 +8109,7 @@ class ContactList extends (react__WEBPACK_IMPORTED_MODULE_0___default().Componen
|
||||
|
||||
contactNodes.push(react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_contact_jsx__WEBPACK_IMPORTED_MODULE_3__.default, {
|
||||
title: c.public ? c.public.fn : null,
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_5__.makeImageDataUrl)(c.public ? c.public.photo : null),
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_5__.makeImageUrl)(c.public ? c.public.photo : null),
|
||||
comment: comment,
|
||||
preview: preview,
|
||||
received: deliveryStatus,
|
||||
@@ -8989,9 +9011,9 @@ class Cropper extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
||||
minZoom: minZoom,
|
||||
maxZoom: Math.max(DEFAULT_MAX_ZOOM, minZoom + 1)
|
||||
});
|
||||
let zoom = Math.max(this.bBoxRect.width / imgRect.width, this.bBoxRect.height / imgRect.height);
|
||||
let panX = this.cutoutRect.left - this.bBoxRect.left - (imgRect.width - this.cutoutRect.width) / 2;
|
||||
let panY = this.cutoutRect.top - this.bBoxRect.top - (imgRect.height - this.cutoutRect.height) / 2;
|
||||
const zoom = Math.max(this.bBoxRect.width / imgRect.width, this.bBoxRect.height / imgRect.height);
|
||||
const panX = this.cutoutRect.left - this.bBoxRect.left - (imgRect.width - this.cutoutRect.width) / 2;
|
||||
const panY = this.cutoutRect.top - this.bBoxRect.top - (imgRect.height - this.cutoutRect.height) / 2;
|
||||
this.positionAll(panX, panY, zoom);
|
||||
}
|
||||
|
||||
@@ -9663,7 +9685,7 @@ class GroupSubs extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
||||
key: sub.user
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_3__.default, {
|
||||
topic: sub.user,
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.makeImageDataUrl)(sub.public ? sub.public.photo : null) || true,
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.makeImageUrl)(sub.public ? sub.public.photo : null) || true,
|
||||
title: sub.public ? sub.public.fn : null
|
||||
})));
|
||||
return usersOnline.length == countToShow;
|
||||
@@ -10181,7 +10203,7 @@ class LetterTile extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompo
|
||||
src: this.props.avatar,
|
||||
onError: e => {
|
||||
e.target.onerror = null;
|
||||
e.target.src = "/img/broken_image.png";
|
||||
e.target.src = "../img/broken_image.png";
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -10908,7 +10930,7 @@ class PermissionsEditor extends (react__WEBPACK_IMPORTED_MODULE_0___default().Co
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_contact_jsx__WEBPACK_IMPORTED_MODULE_3__.default, {
|
||||
item: this.props.item,
|
||||
title: this.props.userTitle,
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.makeImageDataUrl)(this.props.userAvatar ? this.props.userAvatar : null)
|
||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__.makeImageUrl)(this.props.userAvatar ? this.props.userAvatar : null)
|
||||
})) : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
|
||||
className: "small"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||
@@ -11403,6 +11425,8 @@ __webpack_require__.r(__webpack_exports__);
|
||||
/* harmony import */ var _contact_badges_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./contact-badges.jsx */ "./src/widgets/contact-badges.jsx");
|
||||
/* harmony import */ var _menu_contacts_jsx__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./menu-contacts.jsx */ "./src/widgets/menu-contacts.jsx");
|
||||
/* harmony import */ var _menu_start_jsx__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./menu-start.jsx */ "./src/widgets/menu-start.jsx");
|
||||
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11422,16 +11446,22 @@ class SideNavbar extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompo
|
||||
});
|
||||
}
|
||||
|
||||
let avatar = null;
|
||||
|
||||
if (this.props.tinode) {
|
||||
avatar = this.props.tinode.authorizeURL((0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_6__.sanitizeImageUrl)(this.props.avatar));
|
||||
}
|
||||
|
||||
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "side-caption-panel",
|
||||
className: "caption-panel"
|
||||
}, this.props.onCancel ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_button_back_jsx__WEBPACK_IMPORTED_MODULE_2__.default, {
|
||||
onBack: this.props.onCancel
|
||||
}) : null, this.props.avatar ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
}) : null, avatar ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
id: "self-avatar",
|
||||
className: "avatar-box"
|
||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_1__.default, {
|
||||
avatar: this.props.avatar,
|
||||
avatar: avatar,
|
||||
topic: this.props.myUserId,
|
||||
title: this.props.title
|
||||
})) : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||
|
||||
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