From fc3b4d9cc9d13aa9265003819329d3903f98e4dc Mon Sep 17 00:00:00 2001 From: Echo Date: Thu, 4 Sep 2025 12:09:27 +0200 Subject: [PATCH] [Glitch] Adds DisplayName component Port 42be0ca0eb72278b4c904a1ce67ad2dfceb8377e to glitch-soc Signed-off-by: Claire --- .../glitch/components/display_name/index.tsx | 122 ++++++++++++++++++ .../glitch/features/emoji/emoji_html.tsx | 28 ++-- .../flavours/glitch/features/emoji/hooks.ts | 31 ++++- .../flavours/glitch/reducers/index.ts | 4 +- 4 files changed, 158 insertions(+), 27 deletions(-) create mode 100644 app/javascript/flavours/glitch/components/display_name/index.tsx diff --git a/app/javascript/flavours/glitch/components/display_name/index.tsx b/app/javascript/flavours/glitch/components/display_name/index.tsx new file mode 100644 index 0000000000..5572ddf907 --- /dev/null +++ b/app/javascript/flavours/glitch/components/display_name/index.tsx @@ -0,0 +1,122 @@ +import type { ComponentPropsWithoutRef, FC } from 'react'; +import { useMemo } from 'react'; + +import classNames from 'classnames'; +import type { LinkProps } from 'react-router-dom'; +import { Link } from 'react-router-dom'; + +import { EmojiHTML } from '@/flavours/glitch/features/emoji/emoji_html'; +import type { Account } from '@/flavours/glitch/models/account'; +import { isModernEmojiEnabled } from '@/flavours/glitch/utils/environment'; + +import { Skeleton } from '../skeleton'; + +interface Props { + account?: Account; + localDomain?: string; + simple?: boolean; + noDomain?: boolean; +} + +export const DisplayName: FC> = ({ + account, + localDomain, + simple = false, + noDomain = false, + className, + ...props +}) => { + const username = useMemo(() => { + if (!account || noDomain) { + return null; + } + let acct = account.get('acct'); + + if (!acct.includes('@') && localDomain) { + acct = `${acct}@${localDomain}`; + } + return `@${acct}`; + }, [account, localDomain, noDomain]); + + if (!account) { + if (simple) { + return null; + } + return ( + + + + + + + {!noDomain && ( + +   + + + )} + + ); + } + const accountName = isModernEmojiEnabled() + ? account.get('display_name') + : account.get('display_name_html'); + if (simple) { + return ( + + + + ); + } + + return ( + + + + + {username && ( +  {username} + )} + + ); +}; + +export const LinkedDisplayName: FC< + Props & { asProps?: ComponentPropsWithoutRef<'span'> } & Partial +> = ({ + account, + asProps = {}, + className, + localDomain, + simple, + noDomain, + ...linkProps +}) => { + const displayProps = { + account, + className, + localDomain, + simple, + noDomain, + ...asProps, + }; + if (!account) { + return ; + } + + return ( + + + + ); +}; diff --git a/app/javascript/flavours/glitch/features/emoji/emoji_html.tsx b/app/javascript/flavours/glitch/features/emoji/emoji_html.tsx index 7527fea044..0bd1000922 100644 --- a/app/javascript/flavours/glitch/features/emoji/emoji_html.tsx +++ b/app/javascript/flavours/glitch/features/emoji/emoji_html.tsx @@ -1,7 +1,5 @@ import type { ComponentPropsWithoutRef, ElementType } from 'react'; -import { isModernEmojiEnabled } from '@/flavours/glitch/utils/environment'; - import { useEmojify } from './hooks'; import type { CustomEmojiMapArg } from './types'; @@ -12,16 +10,21 @@ type EmojiHTMLProps = Omit< htmlString: string; extraEmojis?: CustomEmojiMapArg; as?: Element; + shallow?: boolean; }; -export const ModernEmojiHTML = ({ +export const EmojiHTML = ({ extraEmojis, htmlString, - as: asElement, // Rename for syntax highlighting + as: Wrapper = 'div', // Rename for syntax highlighting + shallow, ...props -}: EmojiHTMLProps) => { - const Wrapper = asElement ?? 'div'; - const emojifiedHtml = useEmojify(htmlString, extraEmojis); +}: EmojiHTMLProps) => { + const emojifiedHtml = useEmojify({ + text: htmlString, + extraEmojis, + deep: !shallow, + }); if (emojifiedHtml === null) { return null; @@ -31,14 +34,3 @@ export const ModernEmojiHTML = ({ ); }; - -export const EmojiHTML = ( - props: EmojiHTMLProps, -) => { - if (isModernEmojiEnabled()) { - return ; - } - const { as: asElement, htmlString, extraEmojis, ...rest } = props; - const Wrapper = asElement ?? 'div'; - return ; -}; diff --git a/app/javascript/flavours/glitch/features/emoji/hooks.ts b/app/javascript/flavours/glitch/features/emoji/hooks.ts index a985c3e97f..9c9eeb7d17 100644 --- a/app/javascript/flavours/glitch/features/emoji/hooks.ts +++ b/app/javascript/flavours/glitch/features/emoji/hooks.ts @@ -8,6 +8,7 @@ import { isModernEmojiEnabled } from '@/flavours/glitch/utils/environment'; import { toSupportedLocale } from './locale'; import { determineEmojiMode } from './mode'; +import { emojifyElement, emojifyText } from './render'; import type { CustomEmojiMapArg, EmojiAppState, @@ -15,7 +16,17 @@ import type { } from './types'; import { stringHasAnyEmoji } from './utils'; -export function useEmojify(text: string, extraEmojis?: CustomEmojiMapArg) { +interface UseEmojifyOptions { + text: string; + extraEmojis?: CustomEmojiMapArg; + deep?: boolean; +} + +export function useEmojify({ + text, + extraEmojis, + deep = true, +}: UseEmojifyOptions) { const [emojifiedText, setEmojifiedText] = useState(null); const appState = useEmojiAppState(); @@ -36,17 +47,23 @@ export function useEmojify(text: string, extraEmojis?: CustomEmojiMapArg) { const emojify = useCallback( async (input: string) => { - const wrapper = document.createElement('div'); - wrapper.innerHTML = input; - const { emojifyElement } = await import('./render'); - const result = await emojifyElement(wrapper, appState, extra); + let result: string | null = null; + if (deep) { + const wrapper = document.createElement('div'); + wrapper.innerHTML = input; + if (await emojifyElement(wrapper, appState, extra)) { + result = wrapper.innerHTML; + } + } else { + result = await emojifyText(text, appState, extra); + } if (result) { - setEmojifiedText(result.innerHTML); + setEmojifiedText(result); } else { setEmojifiedText(input); } }, - [appState, extra], + [appState, deep, extra, text], ); useLayoutEffect(() => { if (isModernEmojiEnabled() && !!text.trim() && stringHasAnyEmoji(text)) { diff --git a/app/javascript/flavours/glitch/reducers/index.ts b/app/javascript/flavours/glitch/reducers/index.ts index 9594ff3448..8b3d27b059 100644 --- a/app/javascript/flavours/glitch/reducers/index.ts +++ b/app/javascript/flavours/glitch/reducers/index.ts @@ -103,9 +103,9 @@ const RootStateRecord = ImmutableRecord(initialRootState, 'RootState'); export const rootReducer = combineReducers(reducers, RootStateRecord); export function reducerWithInitialState( - stateOverrides: Record = {}, + ...stateOverrides: Record[] ) { - const initialStateRecord = mergeDeep(initialRootState, stateOverrides); + const initialStateRecord = mergeDeep(initialRootState, ...stateOverrides); const PatchedRootStateRecord = ImmutableRecord( initialStateRecord, 'RootState',