Fix up loose docs with PubMetaRow
This commit is contained in:
@@ -1,33 +1,39 @@
|
||||
import {Fragment, type ReactNode} from 'react'
|
||||
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 {makeProfileLink} from '#/lib/routes/links'
|
||||
import {toNiceDomain} from '#/lib/strings/url-helpers'
|
||||
import {useProfileQuery} from '#/state/queries/profile'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {StandardSite} from '#/components/icons/community/StandardSite'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {matchStandardSitePublisher} from '#/components/Post/Embed/StandardSiteEmbed/publishers'
|
||||
import {isStandardSiteDocumentUri} from '#/components/Post/Embed/StandardSiteEmbed/utils'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export function PublicationMetaRow({
|
||||
view,
|
||||
author,
|
||||
onInteractWithin,
|
||||
onInteractWithout,
|
||||
}: {
|
||||
view: AppBskyEmbedExternal.ViewExternal
|
||||
author: {did: string | null | undefined}
|
||||
onInteractWithin: () => void
|
||||
onInteractWithout: () => void
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
const profileQuery = useProfileQuery({did: author.did ?? undefined})
|
||||
const handle = author.did ? profileQuery.data?.handle : undefined
|
||||
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 = [
|
||||
a.text_xs,
|
||||
a.leading_snug,
|
||||
@@ -36,18 +42,21 @@ export function PublicationMetaRow({
|
||||
|
||||
const items: {key: string; node: ReactNode}[] = []
|
||||
|
||||
if (!highlightedPublisher && view.source?.uri) {
|
||||
if (!highlightedPublisher) {
|
||||
items.push({
|
||||
key: 'domain',
|
||||
node: (
|
||||
<Text numberOfLines={1} style={metaTextStyle}>
|
||||
{toNiceDomain(view.source.uri)}
|
||||
</Text>
|
||||
<View style={[a.flex_row, a.align_center]}>
|
||||
<StandardSite size="sm" fill={t.atoms.text_contrast_medium.color} />
|
||||
<Text numberOfLines={1} style={metaTextStyle}>
|
||||
{articleDomain}
|
||||
</Text>
|
||||
</View>
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
if (author.did && handle) {
|
||||
if (authorProfile) {
|
||||
items.push({
|
||||
key: 'author',
|
||||
node: (
|
||||
@@ -55,8 +64,8 @@ export function PublicationMetaRow({
|
||||
<Trans>
|
||||
by{' '}
|
||||
<InlineLinkText
|
||||
label={l`View @${handle}'s profile`}
|
||||
to={makeProfileLink({did: author.did, handle})}
|
||||
label={l`View @${authorProfile.handle}'s profile`}
|
||||
to={makeProfileLink(authorProfile)}
|
||||
style={metaTextStyle}
|
||||
onPress={e => {
|
||||
// this link is nested, yes it's not ideal
|
||||
@@ -64,7 +73,7 @@ export function PublicationMetaRow({
|
||||
}}
|
||||
onMouseEnter={onInteractWithin}
|
||||
onMouseLeave={onInteractWithout}>
|
||||
@{handle}
|
||||
@{authorProfile.handle}
|
||||
</InlineLinkText>
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
@@ -14,8 +14,6 @@ import {ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {Divider} from '#/components/Divider'
|
||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||
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 {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
||||
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
|
||||
// refs are ever present, the byline silently uses the first one.
|
||||
const publicationUri = view.associatedRefs?.find(
|
||||
ref => new AtUri(ref.uri).collection === 'site.standard.publication',
|
||||
)?.uri
|
||||
const maybeAuthorDid = publicationUri ? new AtUri(publicationUri)?.did : null
|
||||
const {
|
||||
state: interactedWithin,
|
||||
onIn: onInteractWithin,
|
||||
onOut: onInteractWithout,
|
||||
} = useInteractionState()
|
||||
|
||||
const onPress = () => {
|
||||
playHaptic('Light')
|
||||
@@ -96,7 +93,6 @@ export const StandardSiteEmbed = ({
|
||||
if (isStandardPublication) {
|
||||
return (
|
||||
<PublicationCard
|
||||
author={{did: maybeAuthorDid}}
|
||||
hideSubscribe={hideSubscribe}
|
||||
view={view}
|
||||
onPress={onPress}
|
||||
@@ -124,138 +120,116 @@ export const StandardSiteEmbed = ({
|
||||
label={view.title || l`Open link to ${niceUrl}`}
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}>
|
||||
{({hovered}) => (
|
||||
<View style={[a.w_full]}>
|
||||
{imageUri ? (
|
||||
<Image
|
||||
style={[a.aspect_card]}
|
||||
source={{uri: imageUri}}
|
||||
accessibilityIgnoresInvertColors
|
||||
loading="lazy"
|
||||
/>
|
||||
) : undefined}
|
||||
{({hovered: maybeHovered}) => {
|
||||
const hovered = maybeHovered && !interactedWithin
|
||||
return (
|
||||
<View style={[a.w_full]}>
|
||||
{imageUri ? (
|
||||
<Image
|
||||
style={[a.aspect_card]}
|
||||
source={{uri: imageUri}}
|
||||
accessibilityIgnoresInvertColors
|
||||
loading="lazy"
|
||||
/>
|
||||
) : undefined}
|
||||
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.pt_sm,
|
||||
t.atoms.border_contrast_low,
|
||||
hasMedia && a.border_t,
|
||||
{gap: 3},
|
||||
isStandard && a.pt_md,
|
||||
]}>
|
||||
<View
|
||||
style={[
|
||||
a.pb_xs,
|
||||
a.px_md,
|
||||
a.flex_1,
|
||||
a.pt_sm,
|
||||
t.atoms.border_contrast_low,
|
||||
hasMedia && a.border_t,
|
||||
{gap: 3},
|
||||
isStandard && [{gap: 5}, a.pb_sm],
|
||||
isStandard && a.pt_md,
|
||||
]}>
|
||||
<Text
|
||||
emoji
|
||||
numberOfLines={3}
|
||||
<View
|
||||
style={[
|
||||
a.text_md,
|
||||
a.font_semi_bold,
|
||||
a.leading_snug,
|
||||
isStandard && [
|
||||
a.text_lg,
|
||||
a.font_bold,
|
||||
hovered && a.underline,
|
||||
],
|
||||
a.pb_xs,
|
||||
a.px_md,
|
||||
{gap: 3},
|
||||
isStandard && [{gap: 5}, a.pb_sm],
|
||||
]}>
|
||||
{view.title}
|
||||
</Text>
|
||||
{view.description ? (
|
||||
<Text
|
||||
emoji
|
||||
numberOfLines={view.thumb ? 2 : 4}
|
||||
style={[a.text_sm, a.leading_snug]}>
|
||||
{view.description}
|
||||
</Text>
|
||||
) : undefined}
|
||||
|
||||
{isStandard && (view.createdAt || view.readingTime) && (
|
||||
<View
|
||||
numberOfLines={3}
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.gap_md,
|
||||
{paddingTop: 2},
|
||||
a.text_md,
|
||||
a.font_semi_bold,
|
||||
a.leading_snug,
|
||||
isStandard && [
|
||||
a.text_lg,
|
||||
a.font_bold,
|
||||
hovered && a.underline,
|
||||
],
|
||||
]}>
|
||||
{view.createdAt && (
|
||||
<Text
|
||||
style={[
|
||||
a.text_xs,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_high,
|
||||
]}>
|
||||
{niceDate(i18n, view.createdAt, 'medium', 'none')}
|
||||
</Text>
|
||||
)}
|
||||
{view.readingTime && (
|
||||
<View style={[a.flex_row, a.align_center, a.gap_2xs]}>
|
||||
<Clock size="xs" style={t.atoms.text_contrast_high} />
|
||||
{view.title}
|
||||
</Text>
|
||||
{view.description ? (
|
||||
<Text
|
||||
emoji
|
||||
numberOfLines={view.thumb ? 2 : 4}
|
||||
style={[a.text_sm, a.leading_snug]}>
|
||||
{view.description}
|
||||
</Text>
|
||||
) : undefined}
|
||||
|
||||
{isStandard && (view.createdAt || view.readingTime) && (
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.gap_md,
|
||||
{paddingTop: 2},
|
||||
]}>
|
||||
{view.createdAt && (
|
||||
<Text
|
||||
style={[
|
||||
a.text_xs,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_high,
|
||||
]}>
|
||||
{l({
|
||||
message: plural(view.readingTime, {
|
||||
one: '#m',
|
||||
other: '#m',
|
||||
}),
|
||||
comment: `How long it takes to read an article, in minutes. Displayed in a short form, e.g. "5m" for 5 minutes.`,
|
||||
})}
|
||||
{niceDate(i18n, view.createdAt, 'medium', 'none')}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
)}
|
||||
{view.readingTime && (
|
||||
<View style={[a.flex_row, a.align_center, a.gap_2xs]}>
|
||||
<Clock size="xs" style={t.atoms.text_contrast_high} />
|
||||
<Text
|
||||
style={[
|
||||
a.text_xs,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_high,
|
||||
]}>
|
||||
{l({
|
||||
message: plural(view.readingTime, {
|
||||
one: '#m',
|
||||
other: '#m',
|
||||
}),
|
||||
comment: `How long it takes to read an article, in minutes. Displayed in a short form, e.g. "5m" for 5 minutes.`,
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{!view.source && (
|
||||
<View style={[a.px_md]}>
|
||||
<Divider />
|
||||
<View style={[a.py_sm]}>
|
||||
<PublicationMetaRow
|
||||
view={view}
|
||||
onInteractWithin={onInteractWithin}
|
||||
onInteractWithout={onInteractWithout}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{!view.source && (
|
||||
<View style={[a.px_md]}>
|
||||
<Divider />
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.gap_2xs,
|
||||
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>
|
||||
)}
|
||||
)
|
||||
}}
|
||||
</Link>
|
||||
|
||||
{view.source && (
|
||||
@@ -268,7 +242,6 @@ export const StandardSiteEmbed = ({
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
themeColors={themeColors}
|
||||
author={{did: maybeAuthorDid}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
@@ -283,7 +256,6 @@ export function PublicationCard({
|
||||
onLongPress,
|
||||
themeColors,
|
||||
style,
|
||||
author,
|
||||
}: {
|
||||
view: AppBskyEmbedExternal.ViewExternal
|
||||
hideSubscribe?: boolean
|
||||
@@ -291,7 +263,6 @@ export function PublicationCard({
|
||||
onLongPress?: () => void
|
||||
themeColors: ThemeColors
|
||||
style?: StyleProp<ViewStyle>
|
||||
author: {did: string | null | undefined}
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
@@ -361,7 +332,6 @@ export function PublicationCard({
|
||||
</Text>
|
||||
<PublicationMetaRow
|
||||
view={view}
|
||||
author={author}
|
||||
onInteractWithin={onInteractWithin}
|
||||
onInteractWithout={onInteractWithout}
|
||||
/>
|
||||
@@ -450,6 +420,7 @@ function PublicationIcon({
|
||||
themeColors: ThemeColors
|
||||
}) {
|
||||
const opacity = hovered ? 0.6 : 0.2
|
||||
if (!view.source) return null
|
||||
return view.source?.icon ? (
|
||||
<View>
|
||||
<UserAvatar
|
||||
@@ -481,7 +452,11 @@ function PublicationIcon({
|
||||
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
|
||||
style={[
|
||||
a.rounded_sm,
|
||||
@@ -501,14 +476,12 @@ export function PublicationFooter({
|
||||
onPress,
|
||||
onLongPress,
|
||||
themeColors,
|
||||
author,
|
||||
}: {
|
||||
view: AppBskyEmbedExternal.ViewExternal
|
||||
hideSubscribe?: boolean
|
||||
themeColors: ThemeColors
|
||||
onPress?: () => void
|
||||
onLongPress?: () => void
|
||||
author: {did: string | null | undefined}
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
@@ -573,7 +546,6 @@ export function PublicationFooter({
|
||||
</Text>
|
||||
<PublicationMetaRow
|
||||
view={view}
|
||||
author={author}
|
||||
onInteractWithin={onInteractWithin}
|
||||
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) {
|
||||
return view.associatedRefs?.some(ref =>
|
||||
new AtUri(ref.uri).collection.startsWith('site.standard.'),
|
||||
)
|
||||
return view.associatedRefs?.some(ref => isStandardSiteUri(ref))
|
||||
}
|
||||
|
||||
export function isStandardSitePublicationEmbed(
|
||||
|
||||
Reference in New Issue
Block a user