diff --git a/apps/site/components/client/ChannelCard2.tsx b/apps/site/components/client/ChannelCard2.tsx
index a25bbec3..2a7d1900 100644
--- a/apps/site/components/client/ChannelCard2.tsx
+++ b/apps/site/components/client/ChannelCard2.tsx
@@ -5,7 +5,7 @@ import {
import { Flex, Stack, Typography, Public } from '@/design-system'
import type { Channel, ChannelRoles } from '@/gql'
import Image from 'next/image'
-import { GenericThumbnailSmall } from '@/server'
+import { ItemFallback } from '@/server'
import { w3sUrlFromCid } from '@/lib'
import { UsernameNoFetch } from '@/client'
import { truncateText } from '@/utils'
@@ -57,9 +57,9 @@ export function ChannelCard2({
) : (
{metadata?.length ? (
-
) : (
diff --git a/apps/site/components/server/ChannelCard.tsx b/apps/site/components/server/ChannelCard.tsx
index 1ebb6736..08db1570 100644
--- a/apps/site/components/server/ChannelCard.tsx
+++ b/apps/site/components/server/ChannelCard.tsx
@@ -9,7 +9,7 @@ import { truncateText } from '@/utils'
import { Flex, Stack, Typography, Public } from '@/design-system'
import type { Adds, Channel, ChannelRoles } from '@/gql'
import { type MediaAssetObject, w3sUrlFromCid } from '@/lib'
-import { GenericThumbnailLarge, Username } from '@/server'
+import { ItemFallback, Username } from '@/server'
import { kv } from '@vercel/kv'
import { muxClient } from '@/config/mux'
import { isVideo } from '@/lib'
@@ -113,11 +113,9 @@ export async function ChannelCard({
) : (
-
)}
{/* Channel name & creator */}
diff --git a/apps/site/components/server/ItemCard.tsx b/apps/site/components/server/ItemCard.tsx
index b4ecbc19..d1d915e0 100644
--- a/apps/site/components/server/ItemCard.tsx
+++ b/apps/site/components/server/ItemCard.tsx
@@ -5,7 +5,7 @@ import {
import { Flex, Stack, Typography, Public } from '@/design-system'
import type { Adds, ChannelRoles } from '@/gql'
import { type MediaAssetObject, w3sUrlFromCid, isVideo } from '@/lib'
-import { GenericThumbnailLarge, Username } from '@/server'
+import { Username, ItemFallback } from '@/server'
import { kv } from '@vercel/kv'
import Image from 'next/image'
import Link from 'next/link'
@@ -79,7 +79,7 @@ export async function ItemCard({
priority={true}
/>
) : (
-
+
)}
diff --git a/apps/site/components/server/ItemFallback.tsx b/apps/site/components/server/ItemFallback.tsx
new file mode 100644
index 00000000..4a2028c0
--- /dev/null
+++ b/apps/site/components/server/ItemFallback.tsx
@@ -0,0 +1,159 @@
+import { cn, Flex } from '@/design-system'
+import { P, match } from 'ts-pattern'
+import { isVideo, isAudio, isGlb, isPdf, isMarkdown } from '@/lib'
+
+export function ItemFallback({
+ contentType,
+ className,
+ size,
+}: {
+ contentType: string
+ className?: string
+ size?: 'xs' | 'sm' | 'md' | 'lg'
+}) {
+ return (
+
+ {match(contentType)
+ .with(
+ P.when((type: string) => isAudio({ mimeType: type })),
+ () => ,
+ )
+ .with(
+ P.when((type: string) => isVideo({ mimeType: type })),
+ () => ,
+ )
+ .with(
+ P.when((type: string) => isMarkdown({ mimeType: type })),
+ () => ,
+ )
+ .with(
+ P.when((type: string) => isGlb({ mimeType: type })),
+ () => ,
+ )
+ .with(
+ P.when((type: string) => isPdf({ mimeType: type })),
+ () => ,
+ )
+ .otherwise(() => (
+
+ ))}
+
+ )
+}
+
+export function AudioFallback({ size }: { size?: 'xs' | 'sm' | 'md' | 'lg' }) {
+ return (
+
+ )
+}
+
+export function VideoFallback() {
+ return (
+
+ )
+}
+
+export function FileFallback() {
+ return (
+
+ )
+}
+
+export function TextFallback({ size }: { size?: 'xs' | 'sm' | 'md' | 'lg' }) {
+ return (
+
+ )
+}
+
+export function ThreeDFallback({ size }: { size?: 'xs' | 'sm' | 'md' | 'lg' }) {
+ return (
+
+ )
+}
+
+export function Unsupported({ size }: { size?: 'xs' | 'sm' | 'md' | 'lg' }) {
+ return (
+
+ )
+}
diff --git a/apps/site/components/server/Thumbnails.tsx b/apps/site/components/server/Thumbnails.tsx
deleted file mode 100644
index 4e5567cc..00000000
--- a/apps/site/components/server/Thumbnails.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { Stack, Typography, cn } from '@/design-system'
-import { truncateText } from '@/utils'
-
-function extractFileExtension(mimeType: string): string {
- if (!mimeType) {
- return ''
- }
- const lastSlashIndex = mimeType.lastIndexOf('/')
- if (lastSlashIndex !== -1) {
- return `.${mimeType.substring(lastSlashIndex + 1)}`
- }
- return ''
-}
-
-export function GenericThumbnailLarge({
- text,
- className,
-}: { text: string; className?: string }) {
- return (
-
-
- {extractFileExtension(text)}
-
-
- )
-}
-
-export function GenericThumbnailSmall({
- text,
- className,
-}: { text: string; className?: string }) {
- return (
-
-
- {truncateText(extractFileExtension(text), 5, false)}
-
-
- )
-}
diff --git a/apps/site/components/server/channel/ThumbnailNameCreator.tsx b/apps/site/components/server/channel/ThumbnailNameCreator.tsx
index 5559bc7e..82a296bf 100644
--- a/apps/site/components/server/channel/ThumbnailNameCreator.tsx
+++ b/apps/site/components/server/channel/ThumbnailNameCreator.tsx
@@ -2,7 +2,7 @@ import { Stack, Typography } from '@/design-system'
import type { Item } from '@/gql'
import { ipfsUrlToCid, w3sUrlFromCid } from '@/lib'
import { VIDEO_THUMBNAIL_TYPES_TO_RENDER } from 'constants/thumbnails'
-import { GenericThumbnailSmall, Username } from '@/server'
+import { ItemFallback, Username } from '@/server'
import { truncateText } from '@/utils'
import Image from 'next/image'
import { muxClient } from '@/config/mux'
@@ -89,9 +89,10 @@ export async function ThumbnailNameCreator({
/>
) : (
-