Fix up loose docs with PubMetaRow

This commit is contained in:
Eric Bailey
2026-05-22 17:00:41 -05:00
parent 335bd5fe7d
commit f79453d87d
3 changed files with 143 additions and 146 deletions
@@ -1,33 +1,39 @@
import {Fragment, type ReactNode} from 'react' import {Fragment, type ReactNode} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyEmbedExternal} from '@atproto/api' import {type AppBskyEmbedExternal, AtUri} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {toNiceDomain} from '#/lib/strings/url-helpers' import {toNiceDomain} from '#/lib/strings/url-helpers'
import {useProfileQuery} from '#/state/queries/profile'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {StandardSite} from '#/components/icons/community/StandardSite'
import {InlineLinkText} from '#/components/Link' import {InlineLinkText} from '#/components/Link'
import {matchStandardSitePublisher} from '#/components/Post/Embed/StandardSiteEmbed/publishers' import {matchStandardSitePublisher} from '#/components/Post/Embed/StandardSiteEmbed/publishers'
import {isStandardSiteDocumentUri} from '#/components/Post/Embed/StandardSiteEmbed/utils'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
export function PublicationMetaRow({ export function PublicationMetaRow({
view, view,
author,
onInteractWithin, onInteractWithin,
onInteractWithout, onInteractWithout,
}: { }: {
view: AppBskyEmbedExternal.ViewExternal view: AppBskyEmbedExternal.ViewExternal
author: {did: string | null | undefined}
onInteractWithin: () => void onInteractWithin: () => void
onInteractWithout: () => void onInteractWithout: () => void
}) { }) {
const t = useTheme() const t = useTheme()
const {t: l} = useLingui() const {t: l} = useLingui()
const profileQuery = useProfileQuery({did: author.did ?? undefined})
const handle = author.did ? profileQuery.data?.handle : undefined
const highlightedPublisher = !!matchStandardSitePublisher(view) const highlightedPublisher = !!matchStandardSitePublisher(view)
const didsFromRecords =
view.associatedRefs
?.filter(isStandardSiteDocumentUri)
.map(ref => new AtUri(ref.uri).host) || []
// atm should only be one docment
const authorDid = didsFromRecords.at(0)
const authorProfile = authorDid
? view.associatedProfiles?.find(p => p.did === authorDid)
: undefined
const articleDomain = toNiceDomain(view.uri)
const metaTextStyle = [ const metaTextStyle = [
a.text_xs, a.text_xs,
a.leading_snug, a.leading_snug,
@@ -36,18 +42,21 @@ export function PublicationMetaRow({
const items: {key: string; node: ReactNode}[] = [] const items: {key: string; node: ReactNode}[] = []
if (!highlightedPublisher && view.source?.uri) { if (!highlightedPublisher) {
items.push({ items.push({
key: 'domain', key: 'domain',
node: ( node: (
<View style={[a.flex_row, a.align_center]}>
<StandardSite size="sm" fill={t.atoms.text_contrast_medium.color} />
<Text numberOfLines={1} style={metaTextStyle}> <Text numberOfLines={1} style={metaTextStyle}>
{toNiceDomain(view.source.uri)} {articleDomain}
</Text> </Text>
</View>
), ),
}) })
} }
if (author.did && handle) { if (authorProfile) {
items.push({ items.push({
key: 'author', key: 'author',
node: ( node: (
@@ -55,8 +64,8 @@ export function PublicationMetaRow({
<Trans> <Trans>
by{' '} by{' '}
<InlineLinkText <InlineLinkText
label={l`View @${handle}'s profile`} label={l`View @${authorProfile.handle}'s profile`}
to={makeProfileLink({did: author.did, handle})} to={makeProfileLink(authorProfile)}
style={metaTextStyle} style={metaTextStyle}
onPress={e => { onPress={e => {
// this link is nested, yes it's not ideal // this link is nested, yes it's not ideal
@@ -64,7 +73,7 @@ export function PublicationMetaRow({
}} }}
onMouseEnter={onInteractWithin} onMouseEnter={onInteractWithin}
onMouseLeave={onInteractWithout}> onMouseLeave={onInteractWithout}>
@{handle} @{authorProfile.handle}
</InlineLinkText> </InlineLinkText>
</Trans> </Trans>
</Text> </Text>
@@ -14,8 +14,6 @@ import {ButtonIcon, ButtonText} from '#/components/Button'
import {Divider} from '#/components/Divider' import {Divider} from '#/components/Divider'
import {useInteractionState} from '#/components/hooks/useInteractionState' import {useInteractionState} from '#/components/hooks/useInteractionState'
import {Clock_Stroke2_Corner0_Rounded as Clock} from '#/components/icons/Clock' import {Clock_Stroke2_Corner0_Rounded as Clock} from '#/components/icons/Clock'
import {StandardSite} from '#/components/icons/community/StandardSite'
import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe'
import {Link} from '#/components/Link' import {Link} from '#/components/Link'
import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import {PublicationMetaRow} from '#/components/Post/Embed/StandardSiteEmbed/PublicationMetaRow' import {PublicationMetaRow} from '#/components/Post/Embed/StandardSiteEmbed/PublicationMetaRow'
@@ -74,12 +72,11 @@ export const StandardSiteEmbed = ({
} }
} }
// Embeds are expected to associate with at most one publication; if multiple const {
// refs are ever present, the byline silently uses the first one. state: interactedWithin,
const publicationUri = view.associatedRefs?.find( onIn: onInteractWithin,
ref => new AtUri(ref.uri).collection === 'site.standard.publication', onOut: onInteractWithout,
)?.uri } = useInteractionState()
const maybeAuthorDid = publicationUri ? new AtUri(publicationUri)?.did : null
const onPress = () => { const onPress = () => {
playHaptic('Light') playHaptic('Light')
@@ -96,7 +93,6 @@ export const StandardSiteEmbed = ({
if (isStandardPublication) { if (isStandardPublication) {
return ( return (
<PublicationCard <PublicationCard
author={{did: maybeAuthorDid}}
hideSubscribe={hideSubscribe} hideSubscribe={hideSubscribe}
view={view} view={view}
onPress={onPress} onPress={onPress}
@@ -124,7 +120,9 @@ export const StandardSiteEmbed = ({
label={view.title || l`Open link to ${niceUrl}`} label={view.title || l`Open link to ${niceUrl}`}
onPress={onPress} onPress={onPress}
onLongPress={onLongPress}> onLongPress={onLongPress}>
{({hovered}) => ( {({hovered: maybeHovered}) => {
const hovered = maybeHovered && !interactedWithin
return (
<View style={[a.w_full]}> <View style={[a.w_full]}>
{imageUri ? ( {imageUri ? (
<Image <Image
@@ -219,43 +217,19 @@ export const StandardSiteEmbed = ({
{!view.source && ( {!view.source && (
<View style={[a.px_md]}> <View style={[a.px_md]}>
<Divider /> <Divider />
<View <View style={[a.py_sm]}>
style={[ <PublicationMetaRow
a.flex_row, view={view}
a.align_center, onInteractWithin={onInteractWithin}
a.gap_2xs, onInteractWithout={onInteractWithout}
a.pb_sm,
{
paddingTop: 6, // off menu
},
]}>
<Globe
size="xs"
style={[
a.transition_color,
hovered
? t.atoms.text_contrast_medium
: t.atoms.text_contrast_low,
]}
/> />
<Text
numberOfLines={1}
style={[
a.transition_color,
a.text_xs,
a.leading_snug,
hovered
? t.atoms.text_contrast_high
: t.atoms.text_contrast_medium,
]}>
{toNiceDomain(view.uri)}
</Text>
</View> </View>
</View> </View>
)} )}
</View> </View>
</View> </View>
)} )
}}
</Link> </Link>
{view.source && ( {view.source && (
@@ -268,7 +242,6 @@ export const StandardSiteEmbed = ({
onPress={onPress} onPress={onPress}
onLongPress={onLongPress} onLongPress={onLongPress}
themeColors={themeColors} themeColors={themeColors}
author={{did: maybeAuthorDid}}
/> />
</> </>
)} )}
@@ -283,7 +256,6 @@ export function PublicationCard({
onLongPress, onLongPress,
themeColors, themeColors,
style, style,
author,
}: { }: {
view: AppBskyEmbedExternal.ViewExternal view: AppBskyEmbedExternal.ViewExternal
hideSubscribe?: boolean hideSubscribe?: boolean
@@ -291,7 +263,6 @@ export function PublicationCard({
onLongPress?: () => void onLongPress?: () => void
themeColors: ThemeColors themeColors: ThemeColors
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
author: {did: string | null | undefined}
}) { }) {
const t = useTheme() const t = useTheme()
const {t: l} = useLingui() const {t: l} = useLingui()
@@ -361,7 +332,6 @@ export function PublicationCard({
</Text> </Text>
<PublicationMetaRow <PublicationMetaRow
view={view} view={view}
author={author}
onInteractWithin={onInteractWithin} onInteractWithin={onInteractWithin}
onInteractWithout={onInteractWithout} onInteractWithout={onInteractWithout}
/> />
@@ -450,6 +420,7 @@ function PublicationIcon({
themeColors: ThemeColors themeColors: ThemeColors
}) { }) {
const opacity = hovered ? 0.6 : 0.2 const opacity = hovered ? 0.6 : 0.2
if (!view.source) return null
return view.source?.icon ? ( return view.source?.icon ? (
<View> <View>
<UserAvatar <UserAvatar
@@ -481,7 +452,11 @@ function PublicationIcon({
backgroundColor: themeColors.accent, backgroundColor: themeColors.accent,
}, },
]}> ]}>
<StandardSite width={size * 0.8} fill={themeColors.accentForeground} /> <Text
emoji
style={[a.text_xl, a.font_bold, {color: themeColors.accentForeground}]}>
{[...view.source.title][0] ?? ''}
</Text>
<MediaInsetBorder <MediaInsetBorder
style={[ style={[
a.rounded_sm, a.rounded_sm,
@@ -501,14 +476,12 @@ export function PublicationFooter({
onPress, onPress,
onLongPress, onLongPress,
themeColors, themeColors,
author,
}: { }: {
view: AppBskyEmbedExternal.ViewExternal view: AppBskyEmbedExternal.ViewExternal
hideSubscribe?: boolean hideSubscribe?: boolean
themeColors: ThemeColors themeColors: ThemeColors
onPress?: () => void onPress?: () => void
onLongPress?: () => void onLongPress?: () => void
author: {did: string | null | undefined}
}) { }) {
const t = useTheme() const t = useTheme()
const {t: l} = useLingui() const {t: l} = useLingui()
@@ -573,7 +546,6 @@ export function PublicationFooter({
</Text> </Text>
<PublicationMetaRow <PublicationMetaRow
view={view} view={view}
author={author}
onInteractWithin={onInteractWithin} onInteractWithin={onInteractWithin}
onInteractWithout={onInteractWithout} onInteractWithout={onInteractWithout}
/> />
@@ -1,9 +1,25 @@
import {type AppBskyEmbedExternal, AtUri} from '@atproto/api' import {
type AppBskyEmbedExternal,
AtUri,
type ComAtprotoRepoStrongRef,
} from '@atproto/api'
export function isStandardSiteDocumentUri(ref: ComAtprotoRepoStrongRef.Main) {
return new AtUri(ref.uri).collection.startsWith('site.standard.document')
}
export function isStandardSitePublicationUri(
ref: ComAtprotoRepoStrongRef.Main,
) {
return new AtUri(ref.uri).collection.startsWith('site.standard.publication')
}
export function isStandardSiteUri(ref: ComAtprotoRepoStrongRef.Main) {
return new AtUri(ref.uri).collection.startsWith('site.standard.')
}
export function isStandardSiteEmbed(view: AppBskyEmbedExternal.ViewExternal) { export function isStandardSiteEmbed(view: AppBskyEmbedExternal.ViewExternal) {
return view.associatedRefs?.some(ref => return view.associatedRefs?.some(ref => isStandardSiteUri(ref))
new AtUri(ref.uri).collection.startsWith('site.standard.'),
)
} }
export function isStandardSitePublicationEmbed( export function isStandardSitePublicationEmbed(