import { AppBskyFeedDefs, AppBskyFeedPost } from "@atcute/bluesky";
import { Client, FetchHandler, ok } from "@atcute/client";
import { useQuery } from "@tanstack/react-query";
import { Pressable, Text, View } from "react-native";
import { useShareSheet } from "./ShareSheetProvider";
import { useOAuthSession } from "./SessionProvider";
import { useMaterial3Theme } from "@pchmn/expo-material3-theme";
import { useColorScheme } from "../hooks/useColorScheme";
import { Did } from "@atcute/lexicons";
import { Image } from "expo-image";
import { formatDateShort, formatNumberOrEmpty } from "../misc/formatting";
import EmbedView from "./EmbedView";
import { AnimatedMaterialSymbolsFilled, MaterialSymbols } from "./MaterialSymbols";
import { ZoomIn, ZoomOut } from "react-native-reanimated";
import { usePostInteractions } from "../hooks/usePostInteractions";
import { useFocusEffect } from "expo-router";
import { useRecyclingState } from "@legendapp/list";
export default function ThreadedReply(props: {
post: AppBskyFeedDefs.ThreadViewPost;
layer?: number;
hasSibling?: boolean;
}) {
const colorScheme = useColorScheme();
const { theme } = useMaterial3Theme({ sourceColor: "#f4983c" });
const MAX_LAYER = 4;
const layer = props.layer ?? 0;
const replies = props.post.replies ?? [];
if (layer >= MAX_LAYER) return <>>;
return (
{layer > 0 && (
{props.hasSibling && (
)}
)}
0 && props.layer !== MAX_LAYER - 1} />
{(props.post.replies ?? [])
.filter((val) => val.$type === "app.bsky.feed.defs#threadViewPost")
.map((val, i, arr) => (
))}
);
}
function Reply(props: { post: AppBskyFeedDefs.PostView; hasReply: boolean }) {
const colorScheme = useColorScheme();
const { theme } = useMaterial3Theme({ sourceColor: "#f4983c" });
const session = useOAuthSession();
const shareSheet = useShareSheet();
const { likeState, likeCount, repostedState, bookmarkedState, toggleBookmark, toggleLike, update } =
usePostInteractions(props.post);
useFocusEffect(() => {
update();
});
const labelersQuery = useQuery({
queryKey: ["labelers"],
queryFn: async ({ signal }) => {
const wrapper: FetchHandler = async (pathname, init) => {
return session.fetchHandler(pathname, init);
};
const client = new Client({
handler: wrapper,
proxy: { did: "did:web:api.bsky.app", serviceId: "#bsky_appview" },
});
const { preferences } = await ok(client.get("app.bsky.actor.getPreferences", { signal, params: {} }));
let labelersDids: Did[] = [];
for (const pref of preferences) {
switch (pref.$type) {
case "app.bsky.actor.defs#labelersPref":
labelersDids = pref.labelers.map((val) => val.did);
break;
}
}
return await ok(
client.get("app.bsky.labeler.getServices", { signal, params: { dids: labelersDids, detailed: true } })
);
},
});
return (
{props.hasReply && (
)}
{props.post.author.displayName}
{"@" + props.post.author.handle}
•
{formatDateShort(new Date((props.post.record as AppBskyFeedPost.Main).createdAt))}
{(props.post.record as AppBskyFeedPost.Main).text}
{props.post.embed && }
{formatNumberOrEmpty(props.post.replyCount ?? 0)}
{formatNumberOrEmpty(
(props.post.repostCount ?? 0) + (props.post.quoteCount ?? 0) + (repostedState ? 1 : 0)
)}
toggleLike()}
>
{likeState && (
)}
{formatNumberOrEmpty(likeCount)}
toggleBookmark()}
>
{bookmarkedState && (
)}
shareSheet.current.present({ uri: props.post.uri, did: props.post.author.did })}
>
);
}