diff --git a/background.html b/background.html index b4325ffe5..51a7ab2bf 100644 --- a/background.html +++ b/background.html @@ -132,15 +132,6 @@ - - - - - - - - diff --git a/ts/components/IdenticonSVG.stories.tsx b/ts/components/IdenticonSVG.stories.tsx new file mode 100644 index 000000000..6b9ec82d6 --- /dev/null +++ b/ts/components/IdenticonSVG.stories.tsx @@ -0,0 +1,21 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import React from 'react'; + +import { storiesOf } from '@storybook/react'; + +import { IdenticonSVG } from './IdenticonSVG'; +import { AvatarColorMap } from '../types/Colors'; + +const story = storiesOf('Components/IdenticonSVG', module); + +AvatarColorMap.forEach((value, key) => + story.add(key, () => ( + + )) +); diff --git a/ts/components/IdenticonSVG.tsx b/ts/components/IdenticonSVG.tsx new file mode 100644 index 000000000..e8173031b --- /dev/null +++ b/ts/components/IdenticonSVG.tsx @@ -0,0 +1,33 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import React from 'react'; + +export type PropsType = { + backgroundColor: string; + content: string; + foregroundColor: string; +}; + +export function IdenticonSVG({ + backgroundColor, + content, + foregroundColor, +}: PropsType): JSX.Element { + return ( + + + + {content} + + + ); +} diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index bdd860f29..fa0c7d162 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -85,6 +85,7 @@ import { isAnnouncementGroupReady } from '../util/isAnnouncementGroupReady'; import { getProfile } from '../util/getProfile'; import { SEALED_SENDER } from '../types/SealedSender'; import { getAvatarData } from '../util/getAvatarData'; +import { createIdenticon } from '../util/createIdenticon'; // TODO: remove once we move away from ArrayBuffers const FIXMEU8 = Uint8Array; @@ -4963,14 +4964,11 @@ export class ConversationModel extends window.Backbone return cached.url; } - const fresh = await new window.Whisper.IdenticonSVGView({ - color, - content, - }).getDataUrl(); + const url = await createIdenticon(color, content); - this.cachedIdenticon = { content, color, url: fresh }; + this.cachedIdenticon = { content, color, url }; - return fresh; + return url; } notifyTyping(options: { diff --git a/ts/util/createIdenticon.tsx b/ts/util/createIdenticon.tsx new file mode 100644 index 000000000..8cdf467a3 --- /dev/null +++ b/ts/util/createIdenticon.tsx @@ -0,0 +1,53 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import React from 'react'; +import loadImage from 'blueimp-load-image'; +import { renderToString } from 'react-dom/server'; +import { AvatarColorMap, AvatarColorType } from '../types/Colors'; +import { IdenticonSVG } from '../components/IdenticonSVG'; + +export function createIdenticon( + color: AvatarColorType, + content: string +): Promise { + const [defaultColorValue] = Array.from(AvatarColorMap.values()); + const avatarColor = AvatarColorMap.get(color); + const html = renderToString( + + ); + const svg = new Blob([html], { type: 'image/svg+xml;charset=utf-8' }); + const svgUrl = URL.createObjectURL(svg); + + return new Promise(resolve => { + const img = document.createElement('img'); + img.onload = () => { + const canvas = loadImage.scale(img, { + canvas: true, + maxWidth: 100, + maxHeight: 100, + }); + if (!(canvas instanceof HTMLCanvasElement)) { + resolve(''); + return; + } + + const ctx = canvas.getContext('2d'); + if (ctx) { + ctx.drawImage(img, 0, 0); + } + URL.revokeObjectURL(svgUrl); + resolve(canvas.toDataURL('image/png')); + }; + img.onerror = () => { + URL.revokeObjectURL(svgUrl); + resolve(''); + }; + + img.src = svgUrl; + }); +} diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index 96b45f0ad..e37546aa8 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -312,30 +312,6 @@ "updated": "2021-02-26T18:44:56.450Z", "reasonDetail": "Static selector, read-only access" }, - { - "rule": "jQuery-$(", - "path": "js/views/identicon_svg_view.js", - "line": " template: () => $('#identicon-svg').html(),", - "reasonCategory": "usageTrusted", - "updated": "2021-02-26T18:44:56.450Z", - "reasonDetail": "Static selector, read-only access" - }, - { - "rule": "jQuery-html(", - "path": "js/views/identicon_svg_view.js", - "line": " const html = this.render().$el.html();", - "reasonCategory": "usageTrusted", - "updated": "2018-09-15T00:38:04.183Z", - "reasonDetail": "Getting the value, not setting it" - }, - { - "rule": "jQuery-html(", - "path": "js/views/identicon_svg_view.js", - "line": " template: () => $('#identicon-svg').html(),", - "reasonCategory": "usageTrusted", - "updated": "2021-02-26T18:44:56.450Z", - "reasonDetail": "Static selector, read-only access" - }, { "rule": "jQuery-$(", "path": "js/views/inbox_view.js", diff --git a/ts/window.d.ts b/ts/window.d.ts index aaef99cf3..cabf1b796 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -633,8 +633,6 @@ export type WhisperType = { ) => void; }; - IdenticonSVGView: WhatIsThis; - ExpiringMessagesListener: { init: (events: Backbone.Events) => void; update: () => void;