Fix up loose docs with PubMetaRow
This commit is contained in:
@@ -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: (
|
||||||
<Text numberOfLines={1} style={metaTextStyle}>
|
<View style={[a.flex_row, a.align_center]}>
|
||||||
{toNiceDomain(view.source.uri)}
|
<StandardSite size="sm" fill={t.atoms.text_contrast_medium.color} />
|
||||||
</Text>
|
<Text numberOfLines={1} style={metaTextStyle}>
|
||||||
|
{articleDomain}
|
||||||
|
</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,138 +120,116 @@ 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}) => {
|
||||||
<View style={[a.w_full]}>
|
const hovered = maybeHovered && !interactedWithin
|
||||||
{imageUri ? (
|
return (
|
||||||
<Image
|
<View style={[a.w_full]}>
|
||||||
style={[a.aspect_card]}
|
{imageUri ? (
|
||||||
source={{uri: imageUri}}
|
<Image
|
||||||
accessibilityIgnoresInvertColors
|
style={[a.aspect_card]}
|
||||||
loading="lazy"
|
source={{uri: imageUri}}
|
||||||
/>
|
accessibilityIgnoresInvertColors
|
||||||
) : undefined}
|
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
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.pb_xs,
|
a.flex_1,
|
||||||
a.px_md,
|
a.pt_sm,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
hasMedia && a.border_t,
|
||||||
{gap: 3},
|
{gap: 3},
|
||||||
isStandard && [{gap: 5}, a.pb_sm],
|
isStandard && a.pt_md,
|
||||||
]}>
|
]}>
|
||||||
<Text
|
<View
|
||||||
emoji
|
|
||||||
numberOfLines={3}
|
|
||||||
style={[
|
style={[
|
||||||
a.text_md,
|
a.pb_xs,
|
||||||
a.font_semi_bold,
|
a.px_md,
|
||||||
a.leading_snug,
|
{gap: 3},
|
||||||
isStandard && [
|
isStandard && [{gap: 5}, a.pb_sm],
|
||||||
a.text_lg,
|
|
||||||
a.font_bold,
|
|
||||||
hovered && a.underline,
|
|
||||||
],
|
|
||||||
]}>
|
]}>
|
||||||
{view.title}
|
|
||||||
</Text>
|
|
||||||
{view.description ? (
|
|
||||||
<Text
|
<Text
|
||||||
emoji
|
emoji
|
||||||
numberOfLines={view.thumb ? 2 : 4}
|
numberOfLines={3}
|
||||||
style={[a.text_sm, a.leading_snug]}>
|
|
||||||
{view.description}
|
|
||||||
</Text>
|
|
||||||
) : undefined}
|
|
||||||
|
|
||||||
{isStandard && (view.createdAt || view.readingTime) && (
|
|
||||||
<View
|
|
||||||
style={[
|
style={[
|
||||||
a.flex_row,
|
a.text_md,
|
||||||
a.align_center,
|
a.font_semi_bold,
|
||||||
a.gap_md,
|
a.leading_snug,
|
||||||
{paddingTop: 2},
|
isStandard && [
|
||||||
|
a.text_lg,
|
||||||
|
a.font_bold,
|
||||||
|
hovered && a.underline,
|
||||||
|
],
|
||||||
]}>
|
]}>
|
||||||
{view.createdAt && (
|
{view.title}
|
||||||
<Text
|
</Text>
|
||||||
style={[
|
{view.description ? (
|
||||||
a.text_xs,
|
<Text
|
||||||
a.leading_snug,
|
emoji
|
||||||
t.atoms.text_contrast_high,
|
numberOfLines={view.thumb ? 2 : 4}
|
||||||
]}>
|
style={[a.text_sm, a.leading_snug]}>
|
||||||
{niceDate(i18n, view.createdAt, 'medium', 'none')}
|
{view.description}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
) : undefined}
|
||||||
{view.readingTime && (
|
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_2xs]}>
|
{isStandard && (view.createdAt || view.readingTime) && (
|
||||||
<Clock size="xs" style={t.atoms.text_contrast_high} />
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_row,
|
||||||
|
a.align_center,
|
||||||
|
a.gap_md,
|
||||||
|
{paddingTop: 2},
|
||||||
|
]}>
|
||||||
|
{view.createdAt && (
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
a.text_xs,
|
a.text_xs,
|
||||||
a.leading_snug,
|
a.leading_snug,
|
||||||
t.atoms.text_contrast_high,
|
t.atoms.text_contrast_high,
|
||||||
]}>
|
]}>
|
||||||
{l({
|
{niceDate(i18n, view.createdAt, 'medium', 'none')}
|
||||||
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>
|
</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>
|
</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>
|
||||||
</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(
|
||||||
|
|||||||
Reference in New Issue
Block a user