diff --git a/assets/images/germ_logo.webp b/assets/images/germ_logo.webp new file mode 100644 index 0000000000..a85048381a Binary files /dev/null and b/assets/images/germ_logo.webp differ diff --git a/src/components/dialogs/LinkWarning.tsx b/src/components/dialogs/LinkWarning.tsx index 74be78f9d3..aeb3e959ce 100644 --- a/src/components/dialogs/LinkWarning.tsx +++ b/src/components/dialogs/LinkWarning.tsx @@ -22,12 +22,12 @@ export function LinkWarningDialog() { webOptions={{alignCenter: true}} onClose={linkWarningDialogControl.clear}> - + ) } -function InAppBrowserConsentInner({ +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..70e02d1f7d --- /dev/null +++ b/src/screens/Profile/components/GermButton.tsx @@ -0,0 +1,221 @@ +import {Platform, View} from 'react-native' +import {Image} from 'expo-image' +import {type AppBskyActorDefs} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useMutation} from '@tanstack/react-query' + +import {isNetworkError} from '#/lib/strings/errors' +import {logger} from '#/logger' +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 {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 type * as bsky from '#/types/bsky' + +export function GermButton({ + germ, + profile, +}: { + germ: AppBskyActorDefs.ProfileAssociatedGerm + profile: bsky.profile.AnyProfileView +}) { + const {_} = useLingui() + const t = useTheme() + const {currentAccount} = useSession() + + if (germ.showButtonTo === 'none') { + 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 ( + + + + Germ DM + + + + ) +} + +function GermLogo({size}: {size: 'small' | 'large'}) { + return ( + + ) +} + +function GermSelfButton({did}: {did: string}) { + const {_} = useLingui() + const selfExplanationDialogControl = Dialog.useDialogControl() + const agent = useAgent() + const t = useTheme() + + const {mutate, isPending} = useMutation({ + onMutate: async () => { + const res = await agent.com.germnetwork.declaration.get({ + repo: did, + rkey: 'self', + }) + + if (!res?.value) { + throw new Error('Failed to get self declaration') + } + + await agent.com.germnetwork.declaration.put( + { + repo: did, + rkey: 'self', + }, + { + ...res.value, + messageMe: { + ...res.value.messageMe, + showButtonTo: 'none', + }, + }, + ) + }, + onSuccess: () => { + Toast.show(_(msg`Germ DM link removed`)) + }, + onError: error => { + Toast.show( + _(msg`Failed to remove Germ DM link. Error: ${error?.message}`), + ) + if (!isNetworkError(error)) { + logger.error('Failed to remove Germ DM link', {safeMessage: error}) + } + }, + }) + + return ( + <> + + + + + + + + + Germ DM Link + + + + + + This button lets others send you a message on the Germ DM app. You + can manage its visibility from the Germ DM app. + + + + + + + + + + ) +} + +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 platform() { + switch (Platform.OS) { + case 'ios': + return 'iOS' + case 'android': + return 'android' + default: + return 'web' + } +} 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 {