mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-29 01:41:22 +00:00
fix(tweet-preview): shape-check, fallback UI, memoize, and warn on bad data
Copilot review flagged three things: 1. Casting an arbitrary parsed object to Tweet without validating shape can still crash inside enrichTweet. Added an isTweetLike type guard that requires the 'user' field, which both CustomTweetHeader and the downstream enrichTweet expect. 2. Returning null on bad data silently hid the failure. Now render a small 'Tweet preview unavailable' fallback (matching the style of the existing fallback in document-modal/content/tweet.tsx) and emit a console.warn so failures show up during debugging. 3. JSON.parse was running on every render. Wrapped in useMemo keyed by the data prop so reparsing only happens when the input changes.
This commit is contained in:
parent
0944e5fb33
commit
3fe7eb83d1
1 changed files with 33 additions and 9 deletions
|
|
@ -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 (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full min-w-0 text-center text-[13px] text-[#737373]",
|
||||
noBgColor ? "bg-transparent py-4" : "bg-black rounded-[18px] p-4",
|
||||
dmSansClassName(),
|
||||
)}
|
||||
>
|
||||
Tweet preview unavailable
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function TweetPreview({
|
||||
data,
|
||||
noBgColor,
|
||||
|
|
@ -137,8 +161,8 @@ export function TweetPreview({
|
|||
data: Tweet | string
|
||||
noBgColor?: boolean
|
||||
}) {
|
||||
const parsedTweet = parseTweetData(data)
|
||||
if (!parsedTweet) return null
|
||||
const parsedTweet = useMemo(() => parseTweetData(data), [data])
|
||||
if (!parsedTweet) return <TweetPreviewFallback noBgColor={noBgColor} />
|
||||
const tweet = enrichTweet(parsedTweet)
|
||||
|
||||
return (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue