diff --git a/background.html b/background.html
index b4325ffe5585..51a7ab2bf2af 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 000000000000..6b9ec82d6514
--- /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 000000000000..e8173031b364
--- /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 (
+
+ );
+}
diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts
index bdd860f29fb2..fa0c7d162fd5 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 000000000000..8cdf467a39da
--- /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 96b45f0ad666..e37546aa8aa2 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 aaef99cf377c..cabf1b7966f4 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;