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