Files
bsky-social-app/src/screens/Profile/components/GermButton.tsx
T
Samuel Newman 320c6425f4 add todos
2026-02-11 12:22:56 +02:00

225 lines
5.9 KiB
TypeScript

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 <GermSelfButton did={currentAccount.did} />
}
if (germ.showButtonTo === 'usersIFollow' && !profile.viewer?.followedBy) {
return null
}
const url = constructGermUrl(germ, profile, currentAccount?.did)
if (!url) {
return null
}
return (
<Link
to={url}
label={_(msg`Open Germ DM`)}
style={[t.atoms.bg_contrast_50, a.rounded_full, a.gap_xs, {padding: 6}]}>
<GermLogo size="small" />
<Text style={[a.text_sm, a.font_medium]}>
<Trans>Germ DM</Trans>
</Text>
<ArrowTopRightIcon style={[t.atoms.text]} size="sm" />
</Link>
)
}
function GermLogo({size}: {size: 'small' | 'large'}) {
return (
<Image
source={require('../../../../assets/images/germ_logo.webp')}
accessibilityIgnoresInvertColors={false}
contentFit="cover"
style={[
size === 'large' ? {width: 32, height: 32} : {width: 16, height: 16},
]}
/>
)
}
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,
// TODO: update types
// showButtonTo: 'none',
},
},
)
// TODO: await appview and revalidate profile
},
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 (
<>
<Button
label={_(msg`Learn more about your Germ DM link`)}
onPress={selfExplanationDialogControl.open}
style={[
t.atoms.bg_contrast_50,
a.rounded_full,
a.gap_xs,
{padding: 6},
]}>
<GermLogo size="small" />
<Text style={[a.text_sm, a.font_medium]}>
<Trans>Germ DM</Trans>
</Text>
</Button>
<Dialog.Outer
control={selfExplanationDialogControl}
nativeOptions={{preventExpansion: true}}>
<Dialog.Handle />
<Dialog.ScrollableInner
label={_(msg`Germ DM Link`)}
style={web([{maxWidth: 400, borderRadius: 36}])}>
<View style={[a.flex_row, a.align_center, {gap: 6}]}>
<GermLogo size="large" />
<Text style={[a.text_2xl, a.font_bold]}>
<Trans>Germ DM Link</Trans>
</Text>
</View>
<Text style={[a.text_md, a.leading_snug, a.mt_sm]}>
<Trans>
This button lets others send you a message on the Germ DM app. You
can manage its visibility from the Germ DM app.
</Trans>
</Text>
<View style={[a.mt_2xl]}>
<Button
label={_(msg``)}
size="large"
color="primary"
onPress={() => selfExplanationDialogControl.close()}>
<ButtonText>
<Trans>Got it!</Trans>
</ButtonText>
</Button>
<Button
label={_(msg`Remove Germ DM link`)}
size="large"
color="primary"
onPress={() => mutate()}
disabled={isPending}>
{isPending && <ButtonIcon icon={Loader} />}
<ButtonText>
<Trans>Remove Germ DM link</Trans>
</ButtonText>
</Button>
</View>
</Dialog.ScrollableInner>
</Dialog.Outer>
</>
)
}
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'
}
}