Improve "replied to a post" component (#8602)
* unify component * change bottom padding from 2px to 4px
This commit is contained in:
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" d="M15.793 10.293a1 1 0 0 1 1.338-.068l.076.068 3.293 3.293a2 2 0 0 1 .138 2.677l-.138.151-3.293 3.293a1 1 0 1 1-1.414-1.414L18.086 16H8a5 5 0 0 1-5-5V5a1 1 0 0 1 2 0v6a3 3 0 0 0 3 3h10.086l-2.293-2.293-.068-.076a1 1 0 0 1 .068-1.338Z"/></svg>
|
||||
|
After Width: | Height: | Size: 334 B |
@@ -0,0 +1,63 @@
|
||||
import {View} from 'react-native'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
import {useSession} from '#/state/session'
|
||||
import {UserInfoText} from '#/view/com/util/UserInfoText'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {ArrowCornerDownRight_Stroke2_Corner2_Rounded as ArrowCornerDownRightIcon} from '#/components/icons/ArrowCornerDownRight'
|
||||
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
|
||||
export function PostRepliedTo({
|
||||
parentAuthor,
|
||||
isParentBlocked,
|
||||
isParentNotFound,
|
||||
}: {
|
||||
parentAuthor: string | bsky.profile.AnyProfileView | undefined
|
||||
isParentBlocked?: boolean
|
||||
isParentNotFound?: boolean
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
const textStyle = [a.text_sm, t.atoms.text_contrast_medium, a.leading_snug]
|
||||
|
||||
let label
|
||||
if (isParentBlocked) {
|
||||
label = <Trans context="description">Replied to a blocked post</Trans>
|
||||
} else if (isParentNotFound) {
|
||||
label = <Trans context="description">Replied to a post</Trans>
|
||||
} else if (parentAuthor) {
|
||||
const did =
|
||||
typeof parentAuthor === 'string' ? parentAuthor : parentAuthor.did
|
||||
const isMe = currentAccount?.did === did
|
||||
if (isMe) {
|
||||
label = <Trans context="description">Replied to you</Trans>
|
||||
} else {
|
||||
label = (
|
||||
<Trans context="description">
|
||||
Replied to{' '}
|
||||
<ProfileHoverCard did={did}>
|
||||
<UserInfoText did={did} attr="displayName" style={textStyle} />
|
||||
</ProfileHoverCard>
|
||||
</Trans>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (!label) {
|
||||
// Should not happen.
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[a.flex_row, a.align_center, a.pb_xs, a.gap_xs]}>
|
||||
<ArrowCornerDownRightIcon
|
||||
size="xs"
|
||||
style={[t.atoms.text_contrast_medium, {top: -1}]}
|
||||
/>
|
||||
<Text style={textStyle}>{label}</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
export const ArrowCornerDownRight_Stroke2_Corner2_Rounded = createSinglePathSVG(
|
||||
{
|
||||
path: 'M15.793 10.293a1 1 0 0 1 1.338-.068l.076.068 3.293 3.293a2 2 0 0 1 .138 2.677l-.138.151-3.293 3.293a1 1 0 1 1-1.414-1.414L18.086 16H8a5 5 0 0 1-5-5V5a1 1 0 0 1 2 0v6a3 3 0 0 0 3 3h10.086l-2.293-2.293-.068-.076a1 1 0 0 1 .068-1.338Z',
|
||||
},
|
||||
)
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
type ModerationDecision,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {Trans} from '@lingui/macro'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {MAX_POST_LINES} from '#/lib/constants'
|
||||
@@ -17,28 +15,25 @@ import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {countLines} from '#/lib/strings/helpers'
|
||||
import {colors, s} from '#/lib/styles'
|
||||
import {colors} from '#/lib/styles'
|
||||
import {
|
||||
POST_TOMBSTONE,
|
||||
type Shadow,
|
||||
usePostShadow,
|
||||
} from '#/state/cache/post-shadow'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {precacheProfile} from '#/state/queries/profile'
|
||||
import {useSession} from '#/state/session'
|
||||
import {unstableCacheProfileView} from '#/state/queries/profile'
|
||||
import {Link} from '#/view/com/util/Link'
|
||||
import {PostMeta} from '#/view/com/util/PostMeta'
|
||||
import {Text} from '#/view/com/util/text/Text'
|
||||
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
|
||||
import {UserInfoText} from '#/view/com/util/UserInfoText'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {ContentHider} from '#/components/moderation/ContentHider'
|
||||
import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe'
|
||||
import {PostAlerts} from '#/components/moderation/PostAlerts'
|
||||
import {Embed, PostEmbedViewContext} from '#/components/Post/Embed'
|
||||
import {PostRepliedTo} from '#/components/Post/PostRepliedTo'
|
||||
import {ShowMoreTextButton} from '#/components/Post/ShowMoreTextButton'
|
||||
import {PostControls} from '#/components/PostControls'
|
||||
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
||||
import {RichText} from '#/components/RichText'
|
||||
import {SubtleWebHover} from '#/components/SubtleWebHover'
|
||||
import * as bsky from '#/types/bsky'
|
||||
@@ -145,12 +140,9 @@ function PostInner({
|
||||
}, [setLimitLines])
|
||||
|
||||
const onBeforePress = useCallback(() => {
|
||||
precacheProfile(queryClient, post.author)
|
||||
unstableCacheProfileView(queryClient, post.author)
|
||||
}, [queryClient, post.author])
|
||||
|
||||
const {currentAccount} = useSession()
|
||||
const isMe = replyAuthorDid === currentAccount?.did
|
||||
|
||||
const [hover, setHover] = useState(false)
|
||||
return (
|
||||
<Link
|
||||
@@ -187,34 +179,7 @@ function PostInner({
|
||||
postHref={itemHref}
|
||||
/>
|
||||
{replyAuthorDid !== '' && (
|
||||
<View style={[s.flexRow, s.mb2, s.alignCenter]}>
|
||||
<FontAwesomeIcon
|
||||
icon="reply"
|
||||
size={9}
|
||||
style={[pal.textLight, s.mr5]}
|
||||
/>
|
||||
<Text
|
||||
type="sm"
|
||||
style={[pal.textLight, s.mr2]}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}>
|
||||
{isMe ? (
|
||||
<Trans context="description">Reply to you</Trans>
|
||||
) : (
|
||||
<Trans context="description">
|
||||
Reply to{' '}
|
||||
<ProfileHoverCard did={replyAuthorDid}>
|
||||
<UserInfoText
|
||||
type="sm"
|
||||
did={replyAuthorDid}
|
||||
attr="displayName"
|
||||
style={[pal.textLight]}
|
||||
/>
|
||||
</ProfileHoverCard>
|
||||
</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
<PostRepliedTo parentAuthor={replyAuthorDid} />
|
||||
)}
|
||||
<LabelsOnMyPost post={post} />
|
||||
<ContentHider
|
||||
|
||||
@@ -9,10 +9,6 @@ import {
|
||||
type ModerationDecision,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {
|
||||
FontAwesomeIcon,
|
||||
type FontAwesomeIconStyle,
|
||||
} from '@fortawesome/react-native-fontawesome'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
@@ -26,7 +22,6 @@ import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {countLines} from '#/lib/strings/helpers'
|
||||
import {s} from '#/lib/styles'
|
||||
import {
|
||||
POST_TOMBSTONE,
|
||||
type Shadow,
|
||||
@@ -54,6 +49,7 @@ import {PostAlerts} from '#/components/moderation/PostAlerts'
|
||||
import {type AppModerationCause} from '#/components/Pills'
|
||||
import {Embed} from '#/components/Post/Embed'
|
||||
import {PostEmbedViewContext} from '#/components/Post/Embed/types'
|
||||
import {PostRepliedTo} from '#/components/Post/PostRepliedTo'
|
||||
import {ShowMoreTextButton} from '#/components/Post/ShowMoreTextButton'
|
||||
import {PostControls} from '#/components/PostControls'
|
||||
import {DiscoverDebug} from '#/components/PostControls/DiscoverDebug'
|
||||
@@ -448,10 +444,10 @@ let FeedItemInner = ({
|
||||
/>
|
||||
{showReplyTo &&
|
||||
(parentAuthor || isParentBlocked || isParentNotFound) && (
|
||||
<ReplyToLabel
|
||||
blocked={isParentBlocked}
|
||||
notFound={isParentNotFound}
|
||||
profile={parentAuthor}
|
||||
<PostRepliedTo
|
||||
parentAuthor={parentAuthor}
|
||||
isParentBlocked={isParentBlocked}
|
||||
isParentNotFound={isParentNotFound}
|
||||
/>
|
||||
)}
|
||||
<LabelsOnMyPost post={post} />
|
||||
@@ -576,80 +572,10 @@ let PostContent = ({
|
||||
}
|
||||
PostContent = memo(PostContent)
|
||||
|
||||
function ReplyToLabel({
|
||||
profile,
|
||||
blocked,
|
||||
notFound,
|
||||
}: {
|
||||
profile: AppBskyActorDefs.ProfileViewBasic | undefined
|
||||
blocked?: boolean
|
||||
notFound?: boolean
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
let label
|
||||
if (blocked) {
|
||||
label = <Trans context="description">Reply to a blocked post</Trans>
|
||||
} else if (notFound) {
|
||||
label = <Trans context="description">Reply to a post</Trans>
|
||||
} else if (profile != null) {
|
||||
const isMe = profile.did === currentAccount?.did
|
||||
if (isMe) {
|
||||
label = <Trans context="description">Reply to you</Trans>
|
||||
} else {
|
||||
label = (
|
||||
<Trans context="description">
|
||||
Reply to{' '}
|
||||
<ProfileHoverCard did={profile.did}>
|
||||
<TextLinkOnWebOnly
|
||||
type="md"
|
||||
style={pal.textLight}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}
|
||||
href={makeProfileLink(profile)}
|
||||
text={
|
||||
<Text emoji type="md" style={pal.textLight} lineHeight={1.2}>
|
||||
{profile.displayName
|
||||
? sanitizeDisplayName(profile.displayName)
|
||||
: sanitizeHandle(profile.handle)}
|
||||
</Text>
|
||||
}
|
||||
/>
|
||||
</ProfileHoverCard>
|
||||
</Trans>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (!label) {
|
||||
// Should not happen.
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[s.flexRow, s.mb2, s.alignCenter]}>
|
||||
<FontAwesomeIcon
|
||||
icon="reply"
|
||||
size={9}
|
||||
style={[{color: pal.colors.textLight} as FontAwesomeIconStyle, s.mr5]}
|
||||
/>
|
||||
<Text
|
||||
type="md"
|
||||
style={[pal.textLight, s.mr2]}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}>
|
||||
{label}
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
outer: {
|
||||
paddingLeft: 10,
|
||||
paddingRight: 15,
|
||||
// @ts-ignore web only -prf
|
||||
cursor: 'pointer',
|
||||
},
|
||||
replyLine: {
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
import {StyleProp, StyleSheet, TextStyle} from 'react-native'
|
||||
import {AppBskyActorGetProfile as GetProfile} from '@atproto/api'
|
||||
import {type StyleProp, type TextStyle} from 'react-native'
|
||||
import {type AppBskyActorGetProfile} from '@atproto/api'
|
||||
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {TypographyVariant} from '#/lib/ThemeContext'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {useProfileQuery} from '#/state/queries/profile'
|
||||
import {TextLinkOnWebOnly} from './Link'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {LoadingPlaceholder} from './LoadingPlaceholder'
|
||||
import {Text} from './text/Text'
|
||||
|
||||
export function UserInfoText({
|
||||
type = 'md',
|
||||
did,
|
||||
attr,
|
||||
failed,
|
||||
prefix,
|
||||
style,
|
||||
}: {
|
||||
type?: TypographyVariant
|
||||
did: string
|
||||
attr?: keyof GetProfile.OutputSchema
|
||||
attr?: keyof AppBskyActorGetProfile.OutputSchema
|
||||
loading?: string
|
||||
failed?: string
|
||||
prefix?: string
|
||||
@@ -35,45 +33,37 @@ export function UserInfoText({
|
||||
staleTime: STALE.INFINITY,
|
||||
})
|
||||
|
||||
let inner
|
||||
if (isError) {
|
||||
inner = (
|
||||
<Text type={type} style={style} numberOfLines={1}>
|
||||
return (
|
||||
<Text style={style} numberOfLines={1}>
|
||||
{failed}
|
||||
</Text>
|
||||
)
|
||||
} else if (profile) {
|
||||
inner = (
|
||||
<TextLinkOnWebOnly
|
||||
type={type}
|
||||
const text = `${prefix || ''}${sanitizeDisplayName(
|
||||
typeof profile[attr] === 'string' && profile[attr]
|
||||
? (profile[attr] as string)
|
||||
: sanitizeHandle(profile.handle),
|
||||
)}`
|
||||
return (
|
||||
<InlineLinkText
|
||||
label={text}
|
||||
style={style}
|
||||
lineHeight={1.2}
|
||||
numberOfLines={1}
|
||||
href={makeProfileLink(profile)}
|
||||
text={
|
||||
<Text emoji type={type} style={style} lineHeight={1.2}>
|
||||
{`${prefix || ''}${sanitizeDisplayName(
|
||||
typeof profile[attr] === 'string' && profile[attr]
|
||||
? (profile[attr] as string)
|
||||
: sanitizeHandle(profile.handle),
|
||||
)}`}
|
||||
</Text>
|
||||
}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
inner = (
|
||||
<LoadingPlaceholder
|
||||
width={80}
|
||||
height={8}
|
||||
style={styles.loadingPlaceholder}
|
||||
/>
|
||||
to={makeProfileLink(profile)}>
|
||||
<Text emoji style={style}>
|
||||
{text}
|
||||
</Text>
|
||||
</InlineLinkText>
|
||||
)
|
||||
}
|
||||
|
||||
return inner
|
||||
// eslint-disable-next-line bsky-internal/avoid-unwrapped-text
|
||||
return (
|
||||
<LoadingPlaceholder
|
||||
width={80}
|
||||
height={8}
|
||||
style={[a.relative, {top: 1, left: 2}]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
loadingPlaceholder: {position: 'relative', top: 1, left: 2},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user