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 })} > ); }