diff --git a/frontend/src/components/Status/index.tsx b/frontend/src/components/Status/index.tsx index 9ebe75c..1ef3698 100644 --- a/frontend/src/components/Status/index.tsx +++ b/frontend/src/components/Status/index.tsx @@ -6,6 +6,7 @@ import type { Account, MastodonStatus } from '~/types' import styles from '../../utils/innerHtmlContent.scss?inline' import { MediaGallery } from '../MediaGallery.tsx' import { useAccountUrl } from '~/utils/useAccountUrl' +import { getDisplayNameElement } from '~/utils/getDisplayNameElement' type Props = { status: MastodonStatus @@ -33,7 +34,7 @@ export default component$((props: Props) => {
- {status.account.display_name} + {getDisplayNameElement(status.account)}
@{status.account.username}
@@ -75,7 +76,7 @@ export const RebloggerLink = component$(({ account }: { account: Account | null

- {account.display_name} + {getDisplayNameElement(account)}  boosted

diff --git a/frontend/src/routes/(frontend)/[accountId]/[statusId]/index.tsx b/frontend/src/routes/(frontend)/[accountId]/[statusId]/index.tsx index 5981033..cb68843 100644 --- a/frontend/src/routes/(frontend)/[accountId]/[statusId]/index.tsx +++ b/frontend/src/routes/(frontend)/[accountId]/[statusId]/index.tsx @@ -15,6 +15,7 @@ import styles from '../../../../utils/innerHtmlContent.scss?inline' import { getTextContent } from 'wildebeest/backend/src/activitypub/objects' import { getDocumentHead } from '~/utils/getDocumentHead' import { useAccountUrl } from '~/utils/useAccountUrl' +import { getDisplayNameElement } from '~/utils/getDisplayNameElement' export const statusLoader = loader$< { DATABASE: D1Database }, @@ -81,7 +82,7 @@ export const AccountCard = component$<{ status: MastodonStatus }>(({ status }) =
- {status.account.display_name} + {getDisplayNameElement(status.account)}
@{status.account.acct}
diff --git a/frontend/src/routes/(frontend)/[accountId]/index.tsx b/frontend/src/routes/(frontend)/[accountId]/index.tsx index 43dadc2..31ada58 100644 --- a/frontend/src/routes/(frontend)/[accountId]/index.tsx +++ b/frontend/src/routes/(frontend)/[accountId]/index.tsx @@ -9,10 +9,11 @@ import { getAccount } from 'wildebeest/backend/src/accounts/getAccount' import { getNotFoundHtml } from '~/utils/getNotFoundHtml/getNotFoundHtml' import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml' import { getDocumentHead } from '~/utils/getDocumentHead' -import type { MastodonStatus } from '~/types' +import type { Account, MastodonStatus } from '~/types' import { StatusesPanel } from '~/components/StatusesPanel/StatusesPanel' import { parseHandle } from 'wildebeest/backend/src/utils/parse' import { getLocalStatuses } from 'wildebeest/functions/api/v1/accounts/[id]/statuses' +import { getDisplayNameElement } from '~/utils/getDisplayNameElement' export const accountLoader = loader$< { DATABASE: D1Database }, @@ -93,7 +94,7 @@ export default component$(() => { />
-

{accountDetails.account.display_name}

+

{getDisplayNameElement(accountDetails.account as Account)}

{accountDetails.accountHandle}
diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index bd41489..9929e83 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -65,3 +65,14 @@ button { .pointer { cursor: pointer; } + +.custom-emoji { + display: inline-block; + font-size: inherit; + vertical-align: middle; + -o-object-fit: contain; + object-fit: contain; + margin: -.2ex 0.15em .2ex; + width: 1rem; + height: 1rem; +} diff --git a/frontend/src/utils/getDisplayNameElement.tsx b/frontend/src/utils/getDisplayNameElement.tsx new file mode 100644 index 0000000..5858d9b --- /dev/null +++ b/frontend/src/utils/getDisplayNameElement.tsx @@ -0,0 +1,21 @@ +import { type JSXNode } from '@builder.io/qwik' +import { type Account } from '~/types' + +export function getDisplayNameElement(account: Account): JSXNode { + return ( + <> + {account.display_name.split(/(:[^\s:]+:)/g).map((str) => { + const customEmojiMatch = str.match(/^:([^\s:]+):$/) + if (customEmojiMatch) { + const shortCode = customEmojiMatch[1] + const customEmojiInfo = account.emojis.find((emoji) => emoji.shortcode === shortCode) + if (customEmojiInfo) { + // eslint-disable-next-line qwik/single-jsx-root + return {`:${shortCode}:`} + } + } + return <>{str} + })} + + ) +} diff --git a/ui-e2e-tests/custom-emojis.spec.ts b/ui-e2e-tests/custom-emojis.spec.ts new file mode 100644 index 0000000..6e89afa --- /dev/null +++ b/ui-e2e-tests/custom-emojis.spec.ts @@ -0,0 +1,49 @@ +import { test, expect } from '@playwright/test' + +test('View of custom emojis in an toots author display name', async ({ page, browserName }) => { + // this page.route is a hack to mock the custom emojis since they haven't + // yet been implemented in the backend (this should be not needed and removed + // when those are implemented) + test.skip( + browserName !== 'chromium', + "Only chromium seem to test this well, I suspect it's because of the page.route" + ) + await page.route('http://127.0.0.1:8788/@george/*/q-data.json', async (route) => { + const response = await route.fetch() + let body = await response.text() + body = body.replace( + /"emojis":\[\]/g, + `"emojis": ${JSON.stringify([ + { + shortcode: 'verified', + url: 'https://files.mastodon.social/cache/custom_emojis/images/000/452/462/original/947cae7ac4dfdfa0.png', + static_url: + 'https://files.mastodon.social/cache/custom_emojis/images/000/452/462/static/947cae7ac4dfdfa0.png', + visible_in_picker: true, + }, + ])}` + ) + await route.fulfill({ + response, + body, + }) + }) + + await page.goto('http://127.0.0.1:8788/explore') + + await page + .locator('article') + .filter({ hasText: 'George' }) + .filter({ + hasText: 'We did it!', + }) + .locator('i.fa-globe + span') + .click() + + const customEmojiLocator = page.getByRole('link', { name: 'George :verified: 👍', exact: true }).getByRole('img') + await expect(customEmojiLocator).toBeVisible() + await expect(customEmojiLocator).toHaveAttribute( + 'src', + 'https://files.mastodon.social/cache/custom_emojis/images/000/452/462/original/947cae7ac4dfdfa0.png' + ) +}) diff --git a/ui-e2e-tests/infinite-scrolling.spec.ts b/ui-e2e-tests/infinite-scrolling.spec.ts index aae895b..d023db2 100644 --- a/ui-e2e-tests/infinite-scrolling.spec.ts +++ b/ui-e2e-tests/infinite-scrolling.spec.ts @@ -84,7 +84,7 @@ export function getMockStatusFn(): () => MastodonStatus { const paddedNum = `${numOfGeneratedMockStatuses}`.padStart(3, '0') const status = { content: `Mock Fetched Status #${paddedNum}`, - account: {} as Account, + account: { display_name: 'test', emojis: [] } as unknown as Account, media_attachments: [], } as unknown as MastodonStatus numOfGeneratedMockStatuses++