| 91 | } |
| 92 | |
| 93 | function FileThumbnail({ fsPath, refreshVersion }: { fsPath: string; refreshVersion: number }) { |
| 94 | const [thumbnailUrl, setThumbnailUrl] = React.useState<string | null>(null); |
| 95 | const [loaded, setLoaded] = React.useState(false); |
| 96 | |
| 97 | useEffect(() => { |
| 98 | let cancelled = false; |
| 99 | setLoaded(false); |
| 100 | setThumbnailUrl(null); |
| 101 | |
| 102 | const cached = thumbnailCache.get(fsPath); |
| 103 | if (cached) { |
| 104 | setThumbnailUrl(cached); |
| 105 | setLoaded(true); |
| 106 | return; |
| 107 | } |
| 108 | |
| 109 | const readThumbnail = isMac ? readCachedPrgThumbnail : readPrgThumbnailBlob; |
| 110 | readThumbnail(fsPath) |
| 111 | .then((blob) => { |
| 112 | if (cancelled) return; |
| 113 | if (blob) { |
| 114 | const url = URL.createObjectURL(blob); |
| 115 | thumbnailCache.set(fsPath, url); |
| 116 | if (!cancelled) setThumbnailUrl(url); |
| 117 | } |
| 118 | if (!cancelled) setLoaded(true); |
| 119 | }) |
| 120 | .catch(() => { |
| 121 | if (!cancelled) setLoaded(true); |
| 122 | }); |
| 123 | |
| 124 | return () => { |
| 125 | cancelled = true; |
| 126 | }; |
| 127 | }, [fsPath, refreshVersion]); |
| 128 | |
| 129 | if (!loaded || !thumbnailUrl) { |
| 130 | return <FileImage className="text-muted-foreground/40 h-10 w-10" />; |
| 131 | } |
| 132 | |
| 133 | return <img src={thumbnailUrl} alt="" className="h-12 w-auto max-w-40 rounded object-contain" />; |
| 134 | } |
| 135 | |
| 136 | /** |
| 137 | * 最近文件面板按钮 |