wip: out of band avatars

This commit is contained in:
or-else
2021-07-01 14:19:28 -07:00
parent c5c08aa4f9
commit e30868c072
15 changed files with 116 additions and 42 deletions
+6 -3
View File
@@ -45,9 +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 avatar image is uploaded, it's resized to
// a square of this size.
export const AVATAR_SIZE = 128;
// Size of the avatar image: when an image is uploaded, 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;
// Size of the broken_image shown in MessagesView
export const BROKEN_IMAGE_SIZE = 32;
+1
View File
@@ -146,6 +146,7 @@ export default class AccGeneralView extends React.Component {
}</FormattedMessage></div>
</div>
<AvatarUpload
tinode={this.props.tinode}
avatar={this.state.avatar}
uid={this.props.myUserId}
title={this.state.fullName}
+1
View File
@@ -92,6 +92,7 @@ export default class CreateAccountView extends React.PureComponent {
}</FormattedMessage>
</div>
<AvatarUpload
tinode={this.props.tinode}
onImageChanged={this.handleImageChanged}
onError={this.props.onError} />
</div>
+1
View File
@@ -36,6 +36,7 @@ export default class EditAccountView extends React.Component {
<BadgeList trustedBadges={this.props.trustedBadges} />
</div>
<AvatarUpload
tinode={this.props.tinode}
avatar={this.state.avatar}
readOnly={!this.state.owner}
uid={this.props.myUserId}
+1
View File
@@ -600,6 +600,7 @@ class InfoView extends React.Component {
<BadgeList trustedBadges={this.state.trustedBadges} />
</div>
<AvatarUpload
tinode={this.props.tinode}
avatar={this.state.avatar}
readOnly={!this.state.owner}
uid={this.props.topic}
+1
View File
@@ -150,6 +150,7 @@ class SidepanelView extends React.Component {
view === 'register' ?
<CreateAccountView
tinode={this.props.tinode}
onCreateAccount={this.props.onCreateAccount}
onCancel={this.props.onCancel}
onError={this.props.onError} /> :
+39 -16
View File
@@ -1,8 +1,10 @@
import React from 'react';
import LetterTile from './letter-tile.jsx';
import LoadSpinner from './load-spinner.jsx';
import { AVATAR_SIZE, MAX_EXTERN_ATTACHMENT_SIZE } from '../config.js';
import { AVATAR_SIZE, IMAGE_PREVIEW_DIM, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE,
MAX_INBAND_ATTACHMENT_SIZE } from '../config.js';
import { imageScaled, blobToBase64, makeImageDataUrl } from '../lib/blob-helpers.js';
export default class AvatarUpload extends React.Component {
@@ -10,7 +12,8 @@ export default class AvatarUpload extends React.Component {
super(props);
this.state = {
dataUrl: props.avatar
dataUrl: props.avatar,
uploading: false
};
this.handleFileUpload = this.handleFileUpload.bind(this);
@@ -18,25 +21,44 @@ export default class AvatarUpload extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.avatar != prevProps.avatar) {
this.setState({dataUrl: this.props.avatar})
this.setState({dataUrl: this.props.avatar});
}
}
handleFileUpload(e) {
imageScaled(e.target.files[0], AVATAR_SIZE, AVATAR_SIZE, MAX_EXTERN_ATTACHMENT_SIZE, true,
// Image successfully scaled and converted.
(mime, blob) => {
// Convert blob to base64-encoded bits.
blobToBase64(blob, (unused, base64bits) => {
const du = makeImageDataUrl({data: base64bits, type: mime});
this.setState({dataUrl: du});
this.props.onImageChanged(du);
const image = e.target.files[0];
if (image.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;
}
uploader.upload(image)
.then(() => {})
.catch((err) => {
this.props.onError(err, 'err');
})
.finally(() => {
this.setState({uploading: false});
});
},
// Failure
(err) => {
this.props.onError(err, 'err');
});
} else {
imageScaled(image, AVATAR_SIZE, AVATAR_SIZE, MAX_EXTERN_ATTACHMENT_SIZE, true,
// Image successfully scaled and converted.
(mime, blob) => {
// Convert blob to base64-encoded bits.
blobToBase64(blob, (unused, base64bits) => {
const du = makeImageDataUrl({data: base64bits, type: mime});
this.setState({dataUrl: du});
this.props.onImageChanged(du);
});
},
// Failure
(err) => {
this.props.onError(err, 'err');
});
}
// Clear the value so the same file can be uploaded again.
e.target.value = '';
}
@@ -71,6 +93,7 @@ export default class AvatarUpload extends React.Component {
<label htmlFor={randId} className="round">
<i className="material-icons">file_upload</i>
</label>}
<LoadSpinner show={this.state.uploading} large={true} clear={true} centered={true} />
</div>
);
}
+3 -1
View File
@@ -2,7 +2,9 @@ import React from 'react';
export default class LoadSpinner extends React.PureComponent {
render() {
const className = 'load-spinner-box' + (this.props.large ? ' large' : '')
+ (this.props.clear ? ' clear' : '') + (this.props.centered ? ' centered' : '');
return (this.props.show ?
<div className="load-spinner-box"><div className="loader-spinner"></div></div> : null);
<div className={className}><div className="loader-spinner"></div></div> : null);
}
}
+1
View File
@@ -87,6 +87,7 @@ export default class NewTopicGroup extends React.PureComponent {
}</FormattedMessage>
</div>
<AvatarUpload
tinode={this.props.tinode}
onError={this.props.onError}
onImageChanged={this.handleImageChanged} />
</div>