From 45504fd87171f31c9e802a71f1e7d281db2c718b Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Thu, 8 Aug 2024 08:02:25 -0400 Subject: [PATCH] fix: cross-platform emoji compatibility Signed-off-by: Adam Setch --- package.json | 1 + pnpm-lock.yaml | 27 +++++++ src/components/AllRead.tsx | 7 +- src/components/Oops.tsx | 7 +- .../AccountNotifications.test.tsx.snap | 68 +++++++++++----- .../__snapshots__/AllRead.test.tsx.snap | 34 +++++--- .../__snapshots__/Oops.test.tsx.snap | 34 +++++--- src/components/icons/Emoji.test.tsx | 12 +++ src/components/icons/Emoji.tsx | 17 ++++ .../icons/__snapshots__/Emoji.test.tsx.snap | 80 +++++++++++++++++++ src/electron/index.html | 11 +++ 11 files changed, 254 insertions(+), 44 deletions(-) create mode 100644 src/components/icons/Emoji.test.tsx create mode 100644 src/components/icons/Emoji.tsx create mode 100644 src/components/icons/__snapshots__/Emoji.test.tsx.snap diff --git a/package.json b/package.json index 646134de3..54091b623 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "dependencies": { "@electron/remote": "2.1.2", "@primer/octicons-react": "19.11.0", + "@twemoji/api": "15.1.0", "axios": "1.7.3", "class-variance-authority": "0.7.0", "clsx": "2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c49c8e16b..fd9d91455 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@primer/octicons-react': specifier: 19.11.0 version: 19.11.0(react@18.3.1) + '@twemoji/api': + specifier: ^15.1.0 + version: 15.1.0 axios: specifier: 1.7.3 version: 1.7.3 @@ -616,6 +619,12 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@twemoji/api@15.1.0': + resolution: {integrity: sha512-CySXR4/e6vY0z9lC2tW9EVjsH6zzz2/LQvLRMwwpHsDI4xcaweSIP+LcESf1Mq7w2/zcrrhG4ozniMvpSsEXag==} + + '@twemoji/parser@15.1.0': + resolution: {integrity: sha512-3HTiSxPvkWUJ4kZeCvwyKlIwkpTUfBOk6igpBBRQni58ceQMv5YK4smkc8vX/eqOlMMNER/9qobv+Q6Q8LVrqA==} + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -2097,6 +2106,9 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonfile@5.0.0: + resolution: {integrity: sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==} + jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -3853,6 +3865,15 @@ snapshots: '@tsconfig/node16@1.0.4': {} + '@twemoji/api@15.1.0': + dependencies: + '@twemoji/parser': 15.1.0 + fs-extra: 8.1.0 + jsonfile: 5.0.0 + universalify: 0.1.2 + + '@twemoji/parser@15.1.0': {} + '@types/aria-query@5.0.4': {} '@types/babel__core@7.20.5': @@ -5708,6 +5729,12 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonfile@5.0.0: + dependencies: + universalify: 0.1.2 + optionalDependencies: + graceful-fs: 4.2.11 + jsonfile@6.1.0: dependencies: universalify: 2.0.1 diff --git a/src/components/AllRead.tsx b/src/components/AllRead.tsx index b34c932b9..82a4506b9 100644 --- a/src/components/AllRead.tsx +++ b/src/components/AllRead.tsx @@ -1,5 +1,6 @@ import { type FC, useMemo } from 'react'; import { Constants } from '../utils/constants'; +import { Emoji } from './icons/Emoji'; export const AllRead: FC = () => { const emoji = useMemo( @@ -12,9 +13,11 @@ export const AllRead: FC = () => { return (
-

{emoji}

+
+ +
-

No new notifications.

+
No new notifications.
); }; diff --git a/src/components/Oops.tsx b/src/components/Oops.tsx index 7f7b21ea4..baf080779 100644 --- a/src/components/Oops.tsx +++ b/src/components/Oops.tsx @@ -1,5 +1,6 @@ import { type FC, useMemo } from 'react'; import type { GitifyError } from '../types'; +import { Emoji } from './icons/Emoji'; interface IOops { error: GitifyError; @@ -13,9 +14,11 @@ export const Oops: FC = ({ error }: IOops) => { return (
-

{emoji}

+
+ +
-

{error.title}

+
{error.title}
{error.descriptions.map((description, i) => { return ( // biome-ignore lint/suspicious/noArrayIndexKey: using index for key to keep the error constants clean diff --git a/src/components/__snapshots__/AccountNotifications.test.tsx.snap b/src/components/__snapshots__/AccountNotifications.test.tsx.snap index f37cb8bcd..382a382d5 100644 --- a/src/components/__snapshots__/AccountNotifications.test.tsx.snap +++ b/src/components/__snapshots__/AccountNotifications.test.tsx.snap @@ -71,16 +71,23 @@ exports[`components/AccountNotifications.tsx should render itself - account erro
-

- 🔥 -

-

+ 🔥 + +

+
Error title - +
@@ -156,16 +163,23 @@ exports[`components/AccountNotifications.tsx should render itself - account erro
-

- 🔥 -

-

+ 🔥 + +

+
Error title - +
@@ -1414,16 +1428,23 @@ exports[`components/AccountNotifications.tsx should render itself - no notificat
-

- 🎊 -

-

+ 🎊 + +

+
No new notifications. - +
, @@ -1494,16 +1515,23 @@ exports[`components/AccountNotifications.tsx should render itself - no notificat
-

- 🎊 -

-

+ 🎊 + +

+
No new notifications. - +
, "debug": [Function], diff --git a/src/components/__snapshots__/AllRead.test.tsx.snap b/src/components/__snapshots__/AllRead.test.tsx.snap index 484584b50..d8f2d1463 100644 --- a/src/components/__snapshots__/AllRead.test.tsx.snap +++ b/src/components/__snapshots__/AllRead.test.tsx.snap @@ -8,16 +8,23 @@ exports[`components/AllRead.tsx should render itself & its children 1`] = `
-

- 🎊 -

-

+ 🎊 + +

+
No new notifications. - +
, @@ -25,16 +32,23 @@ exports[`components/AllRead.tsx should render itself & its children 1`] = `
-

- 🎊 -

-

+ 🎊 + +

+
No new notifications. - +
, "debug": [Function], diff --git a/src/components/__snapshots__/Oops.test.tsx.snap b/src/components/__snapshots__/Oops.test.tsx.snap index 654158f4c..d9097a7e4 100644 --- a/src/components/__snapshots__/Oops.test.tsx.snap +++ b/src/components/__snapshots__/Oops.test.tsx.snap @@ -8,16 +8,23 @@ exports[`components/Oops.tsx should render itself & its children 1`] = `
-

- 🔥 -

-

+ 🔥 + +

+
Error title - +
@@ -30,16 +37,23 @@ exports[`components/Oops.tsx should render itself & its children 1`] = `
-

- 🔥 -

-

+ 🔥 + +

+
Error title - +
diff --git a/src/components/icons/Emoji.test.tsx b/src/components/icons/Emoji.test.tsx new file mode 100644 index 000000000..35e1a89a2 --- /dev/null +++ b/src/components/icons/Emoji.test.tsx @@ -0,0 +1,12 @@ +import { render } from '@testing-library/react'; +import { Emoji, type IEmoji } from './Emoji'; + +describe('components/icons/Emoji.tsx', () => { + it('should render', () => { + const props: IEmoji = { + emoji: '🍺', + }; + const tree = render(); + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/src/components/icons/Emoji.tsx b/src/components/icons/Emoji.tsx new file mode 100644 index 000000000..fc884ad46 --- /dev/null +++ b/src/components/icons/Emoji.tsx @@ -0,0 +1,17 @@ +import twemoji from '@twemoji/api'; +import type { FC } from 'react'; + +export interface IEmoji { + emoji: string; +} + +export const Emoji: FC = (props: IEmoji) => { + // Generate the HTML for the emoji using twemoji + const emojiHtml = twemoji.parse(props.emoji, { + folder: 'svg', + ext: '.svg', + }); + + // biome-ignore lint/security/noDangerouslySetInnerHtml: used for cross-platform emoji compatibility + return ; +}; diff --git a/src/components/icons/__snapshots__/Emoji.test.tsx.snap b/src/components/icons/__snapshots__/Emoji.test.tsx.snap new file mode 100644 index 000000000..f3ab321e8 --- /dev/null +++ b/src/components/icons/__snapshots__/Emoji.test.tsx.snap @@ -0,0 +1,80 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/icons/Emoji.tsx should render 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+ + 🍺 + +
+ , + "container":
+ + 🍺 + +
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/src/electron/index.html b/src/electron/index.html index 89b4b9064..99a1d2ef8 100644 --- a/src/electron/index.html +++ b/src/electron/index.html @@ -79,5 +79,16 @@ width: 100%; height: 2px; } + + /** + * Set emoji size according to surrounding text. + * Ref: https://github.com/jdecked/twemoji?tab=readme-ov-file#inline-styles + */ + img.emoji { + height: 1em; + width: 1em; + margin: 0 0.05em 0 0.1em; + vertical-align: -0.1em; + }