diff --git a/apps/web/components/document-cards/tweet-preview.tsx b/apps/web/components/document-cards/tweet-preview.tsx index 984b469c..77e90971 100644 --- a/apps/web/components/document-cards/tweet-preview.tsx +++ b/apps/web/components/document-cards/tweet-preview.tsx @@ -1,6 +1,6 @@ "use client" -import { Suspense } from "react" +import { Suspense, useMemo } from "react" import type { Tweet } from "react-tweet/api" import { TweetBody, enrichTweet, TweetSkeleton } from "react-tweet" import { cn } from "@lib/utils" @@ -117,19 +117,43 @@ function CustomTweetMedia({ ) } +function isTweetLike(value: unknown): value is Tweet { + return ( + typeof value === "object" && + value !== null && + !Array.isArray(value) && + "user" in value + ) +} + function parseTweetData(data: Tweet | string): Tweet | null { if (!data) return null - if (typeof data !== "string") return data + if (typeof data !== "string") return isTweetLike(data) ? data : null try { - const parsed = JSON.parse(data) - return parsed && typeof parsed === "object" && !Array.isArray(parsed) - ? (parsed as Tweet) - : null - } catch { + const parsed: unknown = JSON.parse(data) + if (isTweetLike(parsed)) return parsed + console.warn("TweetPreview: parsed value did not match Tweet shape") + return null + } catch (error) { + console.warn("TweetPreview: failed to parse tweet data", error) return null } } +function TweetPreviewFallback({ noBgColor }: { noBgColor?: boolean }) { + return ( +