From 93d2f3a8dbd78a23dcbe3642897474d0f37168e2 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 11 Feb 2026 23:51:19 +0000 Subject: [PATCH] Germ DM button (#9848) * germ link (wip) * add todos * just yeet declaration for now * ensure not proxied * allow undo delete * tweak styles * ignore unknown values * tweak styles * fix boolean logic * skip IAB * fix mutationFn * add link warning interstitial * Logging and metrics * Little more error handling * Update copy --------- Co-authored-by: Eric Bailey --- assets/images/germ_logo.webp | Bin 0 -> 7840 bytes src/analytics/metrics/types.ts | 5 + src/components/dialogs/LinkWarning.tsx | 19 +- .../Profile/Header/ProfileHeaderStandard.tsx | 5 + src/screens/Profile/components/GermButton.tsx | 334 ++++++++++++++++++ src/state/session/index.tsx | 2 +- 6 files changed, 362 insertions(+), 3 deletions(-) create mode 100644 assets/images/germ_logo.webp create mode 100644 src/screens/Profile/components/GermButton.tsx diff --git a/assets/images/germ_logo.webp b/assets/images/germ_logo.webp new file mode 100644 index 0000000000000000000000000000000000000000..a85048381ac71eee1131022b862b807875f6ad85 GIT binary patch literal 7840 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 literal 0 HcmV?d00001 diff --git a/src/analytics/metrics/types.ts b/src/analytics/metrics/types.ts index 97b7fcfe09..65f2a5d39f 100644 --- a/src/analytics/metrics/types.ts +++ b/src/analytics/metrics/types.ts @@ -875,4 +875,9 @@ export type Events = { 'liveEvents:unhideAllFeedBanners': { context: LiveEventFeedMetricContext } + + 'profile:associated:germ:click-to-chat': {} + 'profile:associated:germ:click-self-info': {} + 'profile:associated:germ:self-disconnect': {} + 'profile:associated:germ:self-reconnect': {} } diff --git a/src/components/dialogs/LinkWarning.tsx b/src/components/dialogs/LinkWarning.tsx index 74be78f9d3..181d411ce4 100644 --- a/src/components/dialogs/LinkWarning.tsx +++ b/src/components/dialogs/LinkWarning.tsx @@ -22,12 +22,27 @@ export function LinkWarningDialog() { webOptions={{alignCenter: true}} onClose={linkWarningDialogControl.clear}> - + ) } -function InAppBrowserConsentInner({ +export function CustomLinkWarningDialog({ + control, + link, +}: { + control: Dialog.DialogControlProps + link?: {href: string; displayText: string; share?: boolean} +}) { + return ( + + + + + ) +} + +function LinkWarningDialogInner({ link, }: { link?: {href: string; displayText: string; share?: boolean} diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx index 6467756d4e..c60fcd032a 100644 --- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx +++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx @@ -39,6 +39,7 @@ import * as Toast from '#/components/Toast' import {Text} from '#/components/Typography' import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton' import {IS_IOS} from '#/env' +import {GermButton} from '../components/GermButton' import {EditProfileDialog} from './EditProfileDialog' import {ProfileHeaderHandle} from './Handle' import {ProfileHeaderMetrics} from './Metrics' @@ -163,6 +164,10 @@ let ProfileHeaderStandard = ({ ) : undefined} + {profile.associated?.germ && ( + + )} + {!isMe && !isBlockedUser && shouldShowKnownFollowers(profile.viewer?.knownFollowers) && ( diff --git a/src/screens/Profile/components/GermButton.tsx b/src/screens/Profile/components/GermButton.tsx new file mode 100644 index 0000000000..c5af7d536e --- /dev/null +++ b/src/screens/Profile/components/GermButton.tsx @@ -0,0 +1,334 @@ +import {Platform, View} from 'react-native' +import {Image} from 'expo-image' +import { + type AppBskyActorDefs, + type AppBskyActorGetProfile, + type AtpAgent, +} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useMutation, useQueryClient} from '@tanstack/react-query' + +import {until} from '#/lib/async/until' +import {isNetworkError} from '#/lib/strings/errors' +import {RQKEY} from '#/state/queries/profile' +import {useAgent, useSession} from '#/state/session' +import {atoms as a, useTheme, web} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import * as Dialog from '#/components/Dialog' +import {CustomLinkWarningDialog} from '#/components/dialogs/LinkWarning' +import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRightIcon} from '#/components/icons/Arrow' +import {Link} from '#/components/Link' +import {Loader} from '#/components/Loader' +import * as Toast from '#/components/Toast' +import {Text} from '#/components/Typography' +import {useAnalytics} from '#/analytics' +import type * as bsky from '#/types/bsky' + +export function GermButton({ + germ, + profile, +}: { + germ: AppBskyActorDefs.ProfileAssociatedGerm + profile: bsky.profile.AnyProfileView +}) { + const t = useTheme() + const ax = useAnalytics() + const {_} = useLingui() + const {currentAccount} = useSession() + const linkWarningControl = Dialog.useDialogControl() + + // exclude `none` and all unknown values + if ( + !(germ.showButtonTo === 'everyone' || germ.showButtonTo === 'usersIFollow') + ) { + return null + } + + if (currentAccount?.did === profile.did) { + return + } + + if (germ.showButtonTo === 'usersIFollow' && !profile.viewer?.followedBy) { + return null + } + + const url = constructGermUrl(germ, profile, currentAccount?.did) + + if (!url) { + return null + } + + return ( + <> + { + ax.metric('profile:associated:germ:click-to-chat', {}) + if (isCustomGermDomain(url)) { + evt.preventDefault() + linkWarningControl.open() + return false + } + }} + label={_(msg`Open Germ DM`)} + overridePresentation={false} + shouldProxy={false} + style={[ + t.atoms.bg_contrast_50, + a.rounded_full, + a.self_start, + {padding: 6}, + ]}> + + + Germ DM + + + + + + ) +} + +function GermLogo({size}: {size: 'small' | 'large'}) { + return ( + + ) +} + +function GermSelfButton({did}: {did: string}) { + const t = useTheme() + const ax = useAnalytics() + const {_} = useLingui() + const selfExplanationDialogControl = Dialog.useDialogControl() + const agent = useAgent() + const queryClient = useQueryClient() + + const {mutate: deleteDeclaration, isPending} = useMutation({ + mutationFn: async () => { + const previousRecord = await agent.com.germnetwork.declaration + .get({ + repo: did, + rkey: 'self', + }) + .then(res => res.value) + .catch(() => null) + + await agent.com.germnetwork.declaration.delete({ + repo: did, + rkey: 'self', + }) + + await whenAppViewReady(agent, did, res => !res.data.associated?.germ) + + return previousRecord + }, + onSuccess: previousRecord => { + ax.metric('profile:associated:germ:self-disconnect', {}) + + async function undo() { + if (!previousRecord) return + try { + await agent.com.germnetwork.declaration.put( + { + repo: did, + rkey: 'self', + }, + previousRecord, + ) + await whenAppViewReady(agent, did, res => !!res.data.associated?.germ) + await queryClient.refetchQueries({queryKey: RQKEY(did)}) + + Toast.show(_(msg`Germ DM reconnected`)) + ax.metric('profile:associated:germ:self-reconnect', {}) + } catch (e: any) { + Toast.show( + _(msg`Failed to reconnect Germ DM. Error: ${e?.message}`), + { + type: 'error', + }, + ) + if (!isNetworkError(e)) { + ax.logger.error('Failed to reconnect Germ DM link', { + safeMessage: e, + }) + } + } + } + + selfExplanationDialogControl.close(() => { + void queryClient.refetchQueries({queryKey: RQKEY(did)}) + Toast.show( + + + + Germ DM disconnected + + {previousRecord && ( + void undo()}> + Undo + + )} + , + ) + }) + }, + onError: error => { + Toast.show( + _(msg`Failed to disconnect Germ DM. Error: ${error?.message}`), + { + type: 'error', + }, + ) + if (!isNetworkError(error)) { + ax.logger.error('Failed to disconnect Germ DM link', { + safeMessage: error, + }) + } + }, + }) + + return ( + <> + + + + + + + + + Germ DM Link + + + + + + This button lets others open the Germ DM app to send you a + message. You can manage its visibility from the Germ DM app, or + you can disconnect your Bluesky account from Germ DM altogether by + clicking the button below. + + + + + + + + + + ) +} + +function constructGermUrl( + declaration: AppBskyActorDefs.ProfileAssociatedGerm, + profile: bsky.profile.AnyProfileView, + viewerDid?: string, +) { + try { + const urlp = new URL(declaration.messageMeUrl) + + if (urlp.pathname.endsWith('/')) { + urlp.pathname = urlp.pathname.slice(0, -1) + } + + urlp.pathname += `/${platform()}` + + if (viewerDid) { + urlp.hash = `#${profile.did}+${viewerDid}` + } else { + urlp.hash = `#${profile.did}` + } + + return urlp.toString() + } catch { + return null + } +} + +function isCustomGermDomain(url: string) { + try { + const urlp = new URL(url) + return urlp.hostname !== 'landing.ger.mx' + } catch { + return false + } +} + +function platform() { + switch (Platform.OS) { + case 'ios': + return 'iOS' + case 'android': + return 'android' + default: + return 'web' + } +} + +async function whenAppViewReady( + agent: AtpAgent, + actor: string, + fn: (res: AppBskyActorGetProfile.Response) => boolean, +) { + await until( + 5, // 5 tries + 1e3, // 1s delay between tries + fn, + () => agent.app.bsky.actor.getProfile({actor}), + ) +} diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx index 4a70fc4c09..e63e180a4e 100644 --- a/src/state/session/index.tsx +++ b/src/state/session/index.tsx @@ -430,7 +430,7 @@ export function useRequireAuth() { const {signinDialogControl} = useGlobalDialogsControlContext() return useCallback( - (fn: () => void) => { + (fn: () => unknown) => { if (hasSession) { fn() } else {