From d835e7e1076d8ff0508b70574a50e828c961daf9 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 1 Sep 2026 12:52:35 +0300 Subject: [PATCH] fix newskie joined time copy --- .../NewskieDialog/index.tsx} | 36 +++------- .../dialogs/NewskieDialog/utils.test.ts | 72 +++++++++++++++++++ src/components/dialogs/NewskieDialog/utils.ts | 50 +++++++++++++ src/screens/Profile/Header/Handle.tsx | 2 +- 4 files changed, 133 insertions(+), 27 deletions(-) rename src/components/{NewskieDialog.tsx => dialogs/NewskieDialog/index.tsx} (85%) create mode 100644 src/components/dialogs/NewskieDialog/utils.test.ts create mode 100644 src/components/dialogs/NewskieDialog/utils.ts diff --git a/src/components/NewskieDialog.tsx b/src/components/dialogs/NewskieDialog/index.tsx similarity index 85% rename from src/components/NewskieDialog.tsx rename to src/components/dialogs/NewskieDialog/index.tsx index fc499a7fa0..27e85f8bf0 100644 --- a/src/components/NewskieDialog.tsx +++ b/src/components/dialogs/NewskieDialog/index.tsx @@ -7,7 +7,6 @@ import {Trans} from '@lingui/react/macro' import {differenceInSeconds} from 'date-fns' import {HITSLOP_10} from '#/lib/constants' -import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useSession} from '#/state/session' @@ -20,6 +19,7 @@ import * as StarterPackCard from '#/components/StarterPack/StarterPackCard' import {Text} from '#/components/Typography' import {IS_NATIVE} from '#/env' import {type app} from '#/lexicons' +import {getJoinMessage} from './utils' export function NewskieDialog({ profile, @@ -80,11 +80,10 @@ function DialogInner({ now: number }) { const control = Dialog.useDialogContext() - const {_} = useLingui() + const {_, i18n} = useLingui() const t = useTheme() const moderationOpts = useModerationOpts() const {currentAccount} = useSession() - const timeAgo = useGetTimeAgo() const isMe = profile.did === currentAccount?.did const profileName = useMemo(() => { @@ -96,28 +95,6 @@ function DialogInner({ ) }, [moderationOpts, profile]) - const getJoinMessage = () => { - const timeAgoString = timeAgo(createdAt, now, {format: 'long'}) - - if (isMe) { - if (profile.joinedViaStarterPack) { - return _( - msg`You joined Bluesky using a starter pack ${timeAgoString} ago`, - ) - } else { - return _(msg`You joined Bluesky ${timeAgoString} ago`) - } - } else { - if (profile.joinedViaStarterPack) { - return _( - msg`${profileName} joined Bluesky using a starter pack ${timeAgoString} ago`, - ) - } else { - return _(msg`${profileName} joined Bluesky ${timeAgoString} ago`) - } - } - } - return ( - {getJoinMessage()} + {getJoinMessage({ + i18n, + profileName, + isMe, + joinedViaStarterPack: Boolean(profile.joinedViaStarterPack), + createdAt, + now, + })} {profile.joinedViaStarterPack ? ( { + i18n.loadAndActivate({locale: 'en', messages: {}}) +}) + +const now = Date.parse('2026-09-01T12:00:00.000Z') + +describe('NewskieDialog getJoinMessage', () => { + it.each([ + { + isMe: true, + joinedViaStarterPack: false, + expected: 'You joined Bluesky just now', + }, + { + isMe: true, + joinedViaStarterPack: true, + expected: 'You joined Bluesky using a starter pack just now', + }, + { + isMe: false, + joinedViaStarterPack: false, + expected: 'Alice joined Bluesky just now', + }, + { + isMe: false, + joinedViaStarterPack: true, + expected: 'Alice joined Bluesky using a starter pack just now', + }, + ])('$expected', ({isMe, joinedViaStarterPack, expected}) => { + expect( + getJoinMessage({ + i18n, + profileName: 'Alice', + isMe, + joinedViaStarterPack, + createdAt: new Date(now - 4_000).toISOString(), + now, + }), + ).toBe(expected) + }) + + it('keeps the ago suffix for elapsed time', () => { + expect( + getJoinMessage({ + i18n, + profileName: 'Alice', + isMe: true, + joinedViaStarterPack: false, + createdAt: new Date(now - 5_000).toISOString(), + now, + }), + ).toBe('You joined Bluesky 5 seconds ago') + }) + + it('treats a future timestamp as just now', () => { + expect( + getJoinMessage({ + i18n, + profileName: 'Alice', + isMe: true, + joinedViaStarterPack: false, + createdAt: new Date(now + 60_000).toISOString(), + now, + }), + ).toBe('You joined Bluesky just now') + }) +}) diff --git a/src/components/dialogs/NewskieDialog/utils.ts b/src/components/dialogs/NewskieDialog/utils.ts new file mode 100644 index 0000000000..f3a1f1d306 --- /dev/null +++ b/src/components/dialogs/NewskieDialog/utils.ts @@ -0,0 +1,50 @@ +import {type I18n} from '@lingui/core' +import {msg} from '@lingui/core/macro' + +import {dateDiff, formatDateDiff} from '#/lib/hooks/useTimeAgo' + +export function getJoinMessage({ + i18n, + profileName, + isMe, + joinedViaStarterPack, + createdAt, + now, +}: { + i18n: I18n + profileName: string + isMe: boolean + joinedViaStarterPack: boolean + createdAt: string + now: number +}): string { + const diff = dateDiff(createdAt, now) + + if (diff.unit === 'now') { + if (isMe) { + return joinedViaStarterPack + ? i18n._(msg`You joined Bluesky using a starter pack just now`) + : i18n._(msg`You joined Bluesky just now`) + } + + return joinedViaStarterPack + ? i18n._(msg`${profileName} joined Bluesky using a starter pack just now`) + : i18n._(msg`${profileName} joined Bluesky just now`) + } + + const timeAgoString = formatDateDiff({diff, i18n, format: 'long'}) + + if (isMe) { + return joinedViaStarterPack + ? i18n._( + msg`You joined Bluesky using a starter pack ${timeAgoString} ago`, + ) + : i18n._(msg`You joined Bluesky ${timeAgoString} ago`) + } + + return joinedViaStarterPack + ? i18n._( + msg`${profileName} joined Bluesky using a starter pack ${timeAgoString} ago`, + ) + : i18n._(msg`${profileName} joined Bluesky ${timeAgoString} ago`) +} diff --git a/src/screens/Profile/Header/Handle.tsx b/src/screens/Profile/Header/Handle.tsx index 33e7cbcb8b..ecbb232afd 100644 --- a/src/screens/Profile/Header/Handle.tsx +++ b/src/screens/Profile/Header/Handle.tsx @@ -6,7 +6,7 @@ import {Trans} from '@lingui/react/macro' import {isInvalidHandle, sanitizeHandle} from '#/lib/strings/handles' import {type Shadow} from '#/state/cache/types' import {atoms as a, useTheme, web} from '#/alf' -import {NewskieDialog} from '#/components/NewskieDialog' +import {NewskieDialog} from '#/components/dialogs/NewskieDialog' import {Text} from '#/components/Typography' import {IS_IOS, IS_NATIVE} from '#/env' import {type app} from '#/lexicons'