adding support for out of band avatars
This commit is contained in:
@@ -6,10 +6,15 @@ 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 makeImageUrl(photo) {
|
||||
return (photo && photo.type && photo.data) ?
|
||||
'data:image/' + photo.type + ';base64,' + photo.data : null;
|
||||
// Make a data: URL from public.photo
|
||||
export function makeImageDataUrl(photo) {
|
||||
if (photo) {
|
||||
if (photo.data && photo.type) {
|
||||
return 'data:image/' + photo.type + ';base64,' + photo.data;
|
||||
}
|
||||
return photo.ref;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate linear dimensions for scaling image down to fit under a certain size.
|
||||
|
||||
+26
-14
@@ -19,23 +19,35 @@ export function updateFavicon(count) {
|
||||
document.title = (count > 0 ? '('+count+') ' : '') + 'Tinode';
|
||||
}
|
||||
|
||||
// Create theCard which represents topic 'public' info.
|
||||
export function theCard(fn, imageDataUrl) {
|
||||
// Create theCard which represents user's or topic's "public" info.
|
||||
export function theCard(fn, imageUrl, imageMimeType) {
|
||||
let card = null;
|
||||
fn = fn && fn.trim();
|
||||
|
||||
if ((fn && fn.trim()) || imageDataUrl) {
|
||||
card = {};
|
||||
if (fn) {
|
||||
card.fn = fn.trim();
|
||||
}
|
||||
if (imageDataUrl) {
|
||||
const dataStart = imageDataUrl.indexOf(',');
|
||||
card.photo = dataStart >= 0 ? {
|
||||
data: imageDataUrl.substring(dataStart+1),
|
||||
type: 'jpg'
|
||||
} : imageDataUrl;
|
||||
}
|
||||
if (fn) {
|
||||
card = {
|
||||
fn: fn
|
||||
};
|
||||
}
|
||||
|
||||
if (imageUrl) {
|
||||
card = card || {};
|
||||
let mimeType = imageMimeType;
|
||||
// Is this a data URL "data:[<mediatype>][;base64],<data>"?
|
||||
const matches = /^data:(image\/[-a-z0-9+.]+)(;base64)?,/i.exec(imageUrl);
|
||||
if (matches) {
|
||||
mimeType = matches[1];
|
||||
card.photo = {
|
||||
data: imageUrl.substring(imageUrl.indexOf(',') + 1)
|
||||
};
|
||||
} else {
|
||||
card.photo = {
|
||||
ref: imageUrl
|
||||
};
|
||||
}
|
||||
card.photo.type = (mimeType || 'image/jpeg').substring('image/'.length);
|
||||
}
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import InPlaceEdit from '../widgets/in-place-edit.jsx';
|
||||
import TagManager from '../widgets/tag-manager.jsx';
|
||||
|
||||
import { MAX_TITLE_LENGTH } from '../config.js';
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
import { arrayEqual, asEmail, asPhone, theCard } from '../lib/utils.js';
|
||||
|
||||
export default class AccGeneralView extends React.Component {
|
||||
@@ -19,7 +19,7 @@ export default class AccGeneralView extends React.Component {
|
||||
const me = this.props.tinode.getMeTopic();
|
||||
this.state = {
|
||||
fullName: me.public ? me.public.fn : undefined,
|
||||
avatar: makeImageUrl(me.public ? me.public.photo : null),
|
||||
avatar: makeImageDataUrl(me.public ? me.public.photo : null),
|
||||
tags: me.tags(),
|
||||
credentials: me.getCredentials() || [],
|
||||
addCredActive: false,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import AvatarUpload from '../widgets/avatar-upload.jsx';
|
||||
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
|
||||
export default class EditAccountView extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -13,7 +13,7 @@ export default class EditAccountView extends React.Component {
|
||||
const me = this.props.tinode.getMeTopic();
|
||||
this.state = {
|
||||
fullName: me.public ? me.public.fn : undefined,
|
||||
avatar: makeImageUrl(me.public ? me.public.photo : null)
|
||||
avatar: makeImageDataUrl(me.public ? me.public.photo : null)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import TagManager from '../widgets/tag-manager.jsx';
|
||||
|
||||
import { MAX_TITLE_LENGTH, NO_ACCESS_MODE } from '../config.js';
|
||||
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
import { arrayEqual, theCard } from '../lib/utils.js';
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -227,7 +227,7 @@ class InfoView extends React.Component {
|
||||
muted: acs && acs.isMuted(),
|
||||
|
||||
fullName: topic.public ? topic.public.fn : undefined,
|
||||
avatar: makeImageUrl(topic.public ? topic.public.photo : null),
|
||||
avatar: makeImageDataUrl(topic.public ? topic.public.photo : null),
|
||||
private: topic.private ? topic.private.comment : null,
|
||||
address: topic.name,
|
||||
groupTopic: topic.isGroupType(),
|
||||
|
||||
@@ -18,7 +18,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, makeImageUrl } from '../lib/blob-helpers.js';
|
||||
imageScaled, makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
import HashNavigation from '../lib/navigation.js';
|
||||
import { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js';
|
||||
|
||||
@@ -261,7 +261,7 @@ class MessagesView extends React.Component {
|
||||
if (topic.public) {
|
||||
Object.assign(nextState, {
|
||||
title: topic.public.fn,
|
||||
avatar: makeImageUrl(topic.public.photo)
|
||||
avatar: makeImageDataUrl(topic.public.photo)
|
||||
});
|
||||
} else {
|
||||
Object.assign(nextState, {
|
||||
@@ -391,7 +391,7 @@ class MessagesView extends React.Component {
|
||||
if (desc.public) {
|
||||
this.setState({
|
||||
title: desc.public.fn,
|
||||
avatar: makeImageUrl(desc.public.photo)
|
||||
avatar: makeImageDataUrl(desc.public.photo)
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
@@ -856,7 +856,7 @@ class MessagesView extends React.Component {
|
||||
const user = topic.userDesc(thisFrom);
|
||||
if (user && user.public) {
|
||||
userName = user.public.fn;
|
||||
userAvatar = makeImageUrl(user.public.photo);
|
||||
userAvatar = makeImageDataUrl(user.public.photo);
|
||||
}
|
||||
userFrom = thisFrom;
|
||||
chatBoxClass='chat-box group';
|
||||
|
||||
@@ -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, makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { base64ReEncode, makeImageDataUrl } 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';
|
||||
@@ -641,7 +641,7 @@ class TinodeWeb extends React.Component {
|
||||
if (desc.public) {
|
||||
this.setState({
|
||||
sidePanelTitle: desc.public.fn,
|
||||
sidePanelAvatar: makeImageUrl(desc.public.photo)
|
||||
sidePanelAvatar: makeImageDataUrl(desc.public.photo)
|
||||
});
|
||||
}
|
||||
if (desc.acs) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
import LetterTile from './letter-tile.jsx';
|
||||
|
||||
import { AVATAR_SIZE, MAX_EXTERN_ATTACHMENT_SIZE } from '../config.js';
|
||||
import { imageScaled, blobToBase64, makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { imageScaled, blobToBase64, makeImageDataUrl } from '../lib/blob-helpers.js';
|
||||
|
||||
export default class AvatarUpload extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -28,7 +28,7 @@ export default class AvatarUpload extends React.Component {
|
||||
(mime, blob) => {
|
||||
// Convert blob to base64-encoded bits.
|
||||
blobToBase64(blob, (unused, base64bits) => {
|
||||
const du = makeImageUrl({data: base64bits, type: mime});
|
||||
const du = makeImageDataUrl({data: base64bits, type: mime});
|
||||
this.setState({dataUrl: du});
|
||||
this.props.onImageChanged(du);
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
|
||||
import Chip from './chip.jsx';
|
||||
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageDataUrl } 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={makeImageUrl(item.public ? item.public.photo : null)}
|
||||
avatar={makeImageDataUrl(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 { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageDataUrl } 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={makeImageUrl(c.public ? c.public.photo : null)}
|
||||
avatar={makeImageDataUrl(c.public ? c.public.photo : null)}
|
||||
comment={comment}
|
||||
preview={preview}
|
||||
received={deliveryStatus}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl';
|
||||
import { MAX_ONLINE_IN_TOPIC } from '../config.js'
|
||||
|
||||
import LetterTile from './letter-tile.jsx';
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageDataUrl } 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={makeImageUrl(sub.public ? sub.public.photo : null) || true}
|
||||
avatar={makeImageDataUrl(sub.public ? sub.public.photo : null) || true}
|
||||
title={sub.public ? sub.public.fn : null} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||
import CheckBox from './checkbox.jsx';
|
||||
import Contact from './contact.jsx';
|
||||
|
||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||
import { makeImageDataUrl } 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={makeImageUrl(this.props.userAvatar ? this.props.userAvatar : null)} /></ul> : null}
|
||||
avatar={makeImageDataUrl(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">
|
||||
|
||||
Reference in New Issue
Block a user