import type { CSSProperties } from 'react';
import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/bluesky';
import {
DisplayContext,
getDisplayRestrictions,
moderatePost,
type ModerationDecision,
} from '@atcute/bluesky-moderation';
import { parseCanonicalResourceUri } from '@atcute/lexicons/syntax';
import { useQueryClient } from '@tanstack/react-query';
import { clsx } from 'clsx';
import { getPostRecord } from '#/lib/api/record-casts';
import type { Richtext } from '#/lib/rich-text';
import { postUriToTarget } from '#/lib/routes/targets';
import { POST_TOMBSTONE, type Shadow, usePostShadow } from '#/state/cache/post-shadow';
import { useModerationOpts } from '#/state/moderation/moderation-opts';
import { unstableCacheProfileView } from '#/state/queries/profile';
import { useOpenComposer } from '#/features/composer/open-composer';
import { BlockLink } from '#/components/BlockLink';
import { GalleryBleed, maybeApplyGalleryOffsetStyles } from '#/components/images/Gallery';
import { LabelsOnMyPost } from '#/components/moderation/LabelsOnMe';
import { PostRepliedTo } from '#/components/Post/PostRepliedTo';
import { PostContent } from '#/components/PostContent';
import { PostControls } from '#/components/PostControls';
import { PostOverflowMenuButton } from '#/components/PostControls/PostOverflowMenuButton';
import * as PostLayout from '#/components/PostLayout';
import { PostMeta } from '#/components/PostMeta';
import { PreviewableUserAvatar } from '#/components/PreviewableUserAvatar';
import * as css from './Post.css';
export function Post({
post,
hideTopBorder,
style,
}: {
post: AppBskyFeedDefs.PostView;
hideTopBorder?: boolean;
/** Chrome override merged onto the row (e.g. the unread-notification highlight). */
style?: CSSProperties;
}) {
const moderationOpts = useModerationOpts();
const record = getPostRecord(post);
const postShadowed = usePostShadow(post);
const richText = record
? {
text: record.text,
facets: record.facets,
}
: undefined;
const moderation = moderationOpts ? moderatePost(post, moderationOpts) : undefined;
if (postShadowed === POST_TOMBSTONE) {
return null;
}
if (record && richText && moderation) {
return (
<PostInner
post={postShadowed}
record={record}
richText={richText}
moderation={moderation}
hideTopBorder={hideTopBorder}
style={style}
/>
);
}
return null;
}
function PostInner({
post,
record,
richText,
moderation,
hideTopBorder,
style,
}: {
post: Shadow<AppBskyFeedDefs.PostView>;
record: AppBskyFeedPost.Main;
richText: Richtext;
moderation: ModerationDecision;
hideTopBorder?: boolean;
style?: CSSProperties;
}) {
const queryClient = useQueryClient();
const { openComposer } = useOpenComposer();
const itemTarget = postUriToTarget(post.uri);
const replyAuthorDid = record.reply
? parseCanonicalResourceUri(record.reply.parent?.uri || record.reply.root.uri).repo
: undefined;
const onPressReply = () => {
openComposer({
replyTo: {
uri: post.uri,
cid: post.cid,
text: record.text,
author: post.author,
embed: post.embed,
moderation,
langs: record.langs,
},
});
};
const onBeforePress = () => {
unstableCacheProfileView(queryClient, post.author);
};
const galleryOffsetStyles = maybeApplyGalleryOffsetStyles({
additionalCauses: [],
moderation,
post,
});
return (
<GalleryBleed>
<BlockLink to={itemTarget} onBeforePress={onBeforePress}>
<article className={clsx(css.outer, !hideTopBorder && css.outerBorder)} style={style}>
<PostLayout.Row>
<PostLayout.AvatarColumn>
<PreviewableUserAvatar
size={36}
profile={post.author}
moderation={getDisplayRestrictions(moderation, DisplayContext.ProfileMedia)}
type={post.author.associated?.labeler ? 'labeler' : 'user'}
tabIndex={-1}
/>
</PostLayout.AvatarColumn>
<PostLayout.ContentColumn style={galleryOffsetStyles?.meta}>
<div className={css.metaSpacing}>
<PostMeta
author={post.author}
moderation={moderation}
timestamp={post.indexedAt}
postTarget={itemTarget}
/>
<PostOverflowMenuButton post={post} record={record} richText={richText} />
</div>
{replyAuthorDid && <PostRepliedTo parentAuthor={replyAuthorDid} className={css.repliedTo} />}
<LabelsOnMyPost post={post} />
<PostContent
displayContext="view"
moderation={moderation}
post={post}
richText={richText}
embedStyle={galleryOffsetStyles?.embed}
/>
<PostControls post={post} onPressReply={onPressReply} />
</PostLayout.ContentColumn>
</PostLayout.Row>
</article>
</BlockLink>
</GalleryBleed>
);
}