diff options
Diffstat (limited to 'src/view/com/post-thread')
-rw-r--r-- | src/view/com/post-thread/PostThread.tsx | 95 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThreadItem.tsx | 23 |
2 files changed, 87 insertions, 31 deletions
diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index 6cd1f3551..072ef7e33 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -36,11 +36,13 @@ import {Trans, msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import { UsePreferencesQueryResponse, + useModerationOpts, usePreferencesQuery, } from '#/state/queries/preferences' import {useSession} from '#/state/session' -import {isNative} from '#/platform/detection' +import {isAndroid, isNative} from '#/platform/detection' import {logger} from '#/logger' +import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped' const MAINTAIN_VISIBLE_CONTENT_POSITION = {minIndexForVisible: 2} @@ -79,14 +81,30 @@ export function PostThread({ data: thread, } = usePostThreadQuery(uri) const {data: preferences} = usePreferencesQuery() + const rootPost = thread?.type === 'post' ? thread.post : undefined const rootPostRecord = thread?.type === 'post' ? thread.record : undefined + const moderationOpts = useModerationOpts() + const isNoPwi = React.useMemo(() => { + const mod = + rootPost && moderationOpts + ? moderatePost(rootPost, moderationOpts) + : undefined + + const cause = mod?.content.cause + + return cause + ? cause.type === 'label' && cause.labelDef.id === '!no-unauthenticated' + : false + }, [rootPost, moderationOpts]) + useSetTitle( - rootPost && - `${sanitizeDisplayName( - rootPost.author.displayName || `@${rootPost.author.handle}`, - )}: "${rootPostRecord?.text}"`, + rootPost && !isNoPwi + ? `${sanitizeDisplayName( + rootPost.author.displayName || `@${rootPost.author.handle}`, + )}: "${rootPostRecord!.text}"` + : '', ) useEffect(() => { if (rootPost) { @@ -139,7 +157,7 @@ function PostThreadLoaded({ const {hasSession} = useSession() const {_} = useLingui() const pal = usePalette('default') - const {isTablet, isDesktop} = useWebMediaQueries() + const {isTablet, isDesktop, isTabletOrMobile} = useWebMediaQueries() const ref = useRef<ListMethods>(null) const highlightedPostRef = useRef<View | null>(null) const needsScrollAdjustment = useRef<boolean>( @@ -157,7 +175,11 @@ function PostThreadLoaded({ const posts = React.useMemo(() => { let arr = [TOP_COMPONENT].concat( Array.from( - flattenThreadSkeleton(sortThread(thread, threadViewPrefs), hasSession), + flattenThreadSkeleton( + sortThread(thread, threadViewPrefs), + hasSession, + treeView, + ), ), ) if (arr.length > maxVisible) { @@ -167,7 +189,7 @@ function PostThreadLoaded({ arr.push(BOTTOM_COMPONENT) } return arr - }, [thread, maxVisible, threadViewPrefs, hasSession]) + }, [thread, treeView, maxVisible, threadViewPrefs, hasSession]) /** * NOTE @@ -197,17 +219,35 @@ function PostThreadLoaded({ // wait for loading to finish if (thread.type === 'post' && !!thread.parent) { - highlightedPostRef.current?.measure( - (_x, _y, _width, _height, _pageX, pageY) => { - ref.current?.scrollToOffset({ - animated: false, - offset: pageY - (isDesktop ? 0 : 50), - }) - }, - ) + function onMeasure(pageY: number) { + let spinnerHeight = 0 + if (isDesktop) { + spinnerHeight = 40 + } else if (isTabletOrMobile) { + spinnerHeight = 82 + } + ref.current?.scrollToOffset({ + animated: false, + offset: pageY - spinnerHeight, + }) + } + if (isNative) { + highlightedPostRef.current?.measure( + (_x, _y, _width, _height, _pageX, pageY) => { + onMeasure(pageY) + }, + ) + } else { + // Measure synchronously to avoid a layout jump. + const domNode = highlightedPostRef.current + if (domNode) { + const pageY = (domNode as any as Element).getBoundingClientRect().top + onMeasure(pageY) + } + } needsScrollAdjustment.current = false } - }, [thread, isDesktop]) + }, [thread, isDesktop, isTabletOrMobile]) const onPTR = React.useCallback(async () => { setIsPTRing(true) @@ -222,7 +262,11 @@ function PostThreadLoaded({ const renderItem = React.useCallback( ({item, index}: {item: YieldedItem; index: number}) => { if (item === TOP_COMPONENT) { - return isTablet ? <ViewHeader title={_(msg`Post`)} /> : null + return isTablet ? ( + <ViewHeader + title={_(msg({message: `Post`, context: 'description'}))} + /> + ) : null } else if (item === PARENT_SPINNER) { return ( <View style={styles.parentSpinner}> @@ -276,8 +320,10 @@ function PostThreadLoaded({ // -prf return ( <View + // @ts-ignore web-only style={{ - height: 400, + // Leave enough space below that the scroll doesn't jump + height: isNative ? 400 : '100vh', borderTopWidth: 1, borderColor: pal.colors.border, }} @@ -354,6 +400,7 @@ function PostThreadLoaded({ style={s.hContentRegion} // @ts-ignore our .web version only -prf desktopFixedHeight + removeClippedSubviews={isAndroid ? false : undefined} /> ) } @@ -393,7 +440,7 @@ function PostThreadBlocked() { style={[pal.link as FontAwesomeIconStyle, s.mr5]} size={14} /> - Back + <Trans context="action">Back</Trans> </Text> </TouchableOpacity> </View> @@ -464,10 +511,11 @@ function isThreadPost(v: unknown): v is ThreadPost { function* flattenThreadSkeleton( node: ThreadNode, hasSession: boolean, + treeView: boolean, ): Generator<YieldedItem, void> { if (node.type === 'post') { if (node.parent) { - yield* flattenThreadSkeleton(node.parent, hasSession) + yield* flattenThreadSkeleton(node.parent, hasSession, treeView) } else if (node.ctx.isParentLoading) { yield PARENT_SPINNER } @@ -480,7 +528,10 @@ function* flattenThreadSkeleton( } if (node.replies?.length) { for (const reply of node.replies) { - yield* flattenThreadSkeleton(reply, hasSession) + yield* flattenThreadSkeleton(reply, hasSession, treeView) + if (!treeView && !node.ctx.isHighlightedPost) { + break + } } } else if (node.ctx.isChildLoading) { yield CHILD_SPINNER diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 986fd70b2..a27ee0a58 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -158,6 +158,7 @@ let PostThreadItemLoaded = ({ onPostReply: () => void }): React.ReactNode => { const pal = usePalette('default') + const {_} = useLingui() const langPrefs = useLanguagePrefs() const {openComposer} = useComposerControls() const {currentAccount} = useSession() @@ -172,7 +173,7 @@ let PostThreadItemLoaded = ({ const urip = new AtUri(post.uri) return makeProfileLink(post.author, 'post', urip.rkey) }, [post.uri, post.author]) - const itemTitle = `Post by ${post.author.handle}` + const itemTitle = _(msg`Post by ${post.author.handle}`) const authorHref = makeProfileLink(post.author) const authorTitle = post.author.handle const isAuthorMuted = post.author.viewer?.muted @@ -180,12 +181,12 @@ let PostThreadItemLoaded = ({ const urip = new AtUri(post.uri) return makeProfileLink(post.author, 'post', urip.rkey, 'liked-by') }, [post.uri, post.author]) - const likesTitle = 'Likes on this post' + const likesTitle = _(msg`Likes on this post`) const repostsHref = React.useMemo(() => { const urip = new AtUri(post.uri) return makeProfileLink(post.author, 'post', urip.rkey, 'reposted-by') }, [post.uri, post.author]) - const repostsTitle = 'Reposts of this post' + const repostsTitle = _(msg`Reposts of this post`) const isModeratedPost = moderation.decisions.post.cause?.type === 'label' && moderation.decisions.post.cause.label.src !== currentAccount?.did @@ -214,6 +215,7 @@ let PostThreadItemLoaded = ({ displayName: post.author.displayName, avatar: post.author.avatar, }, + embed: post.embed, }, onPost: onPostReply, }) @@ -224,7 +226,7 @@ let PostThreadItemLoaded = ({ }, [setLimitLines]) if (!record) { - return <ErrorMessage message="Invalid or unsupported post record" /> + return <ErrorMessage message={_(msg`Invalid or unsupported post record`)} /> } if (isHighlightedPost) { @@ -246,10 +248,9 @@ let PostThreadItemLoaded = ({ </View> )} - <Link + <View testID={`postThreadItem-by-${post.author.handle}`} style={[styles.outer, styles.outerHighlighted, pal.border, pal.view]} - noFeedback accessible={false}> <PostSandboxWarning /> <View style={styles.layout}> @@ -334,6 +335,7 @@ let PostThreadItemLoaded = ({ postCid={post.cid} postUri={post.uri} record={record} + richText={richText} showAppealLabelItem={ post.author.did === currentAccount?.did && isModeratedPost } @@ -367,6 +369,7 @@ let PostThreadItemLoaded = ({ richText={richText} lineHeight={1.3} style={s.flex1} + selectable /> </View> ) : undefined} @@ -437,11 +440,12 @@ let PostThreadItemLoaded = ({ big post={post} record={record} + richText={richText} onPressReply={onPressReply} /> </View> </View> - </Link> + </View> <WhoCanReply post={post} /> </> ) @@ -562,7 +566,7 @@ let PostThreadItemLoaded = ({ ) : undefined} {limitLines ? ( <TextLink - text="Show More" + text={_(msg`Show More`)} style={pal.link} onPress={onPressShowMore} href="#" @@ -585,6 +589,7 @@ let PostThreadItemLoaded = ({ <PostCtrls post={post} record={record} + richText={richText} onPressReply={onPressReply} /> </View> @@ -701,7 +706,7 @@ function ExpandedPostDetails({ <Text style={pal.textLight}>{niceDate(post.indexedAt)}</Text> {needsTranslation && ( <> - <Text style={[pal.textLight, s.ml5, s.mr5]}>•</Text> + <Text style={pal.textLight}> · </Text> <Link href={translatorUrl} title={_(msg`Translate`)}> <Text style={pal.link}> <Trans>Translate</Trans> |