diff --git a/package.json b/package.json index d8be18eb6..a7d99120e 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,6 @@ "tailwindcss-animate": "1.0.7", "tldts": "6.1.32", "unified": "11.0.5", - "unist-util-visit": "5.0.0", "usehooks-ts": "3.1.0", "vfile": "6.0.2", "zod": "3.23.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a82f3796d..9936f02a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -268,9 +268,6 @@ importers: unified: specifier: 11.0.5 version: 11.0.5 - unist-util-visit: - specifier: 5.0.0 - version: 5.0.0 usehooks-ts: specifier: 3.1.0 version: 3.1.0(react@18.3.1) diff --git a/src/main/tipc.ts b/src/main/tipc.ts index eb28248e1..34b763217 100644 --- a/src/main/tipc.ts +++ b/src/main/tipc.ts @@ -8,7 +8,7 @@ import { app, dialog, Menu, nativeTheme, ShareMenu } from "electron" import { downloadFile } from "./lib/download" import type { RendererHandlers } from "./renderer-handlers" import { quitAndInstall } from "./updater" -import { createSettingWindow, createWindow, getMainWindow } from "./window" +import { createSettingWindow, getMainWindow } from "./window" const t = tipc.create() @@ -91,7 +91,7 @@ export const router = { }), saveToEagle: t.procedure - .input<{ url: string, images: string[] }>() + .input<{ url: string, mediaUrls: string[] }>() .action(async ({ input }) => { try { const res = await fetch("http://localhost:41595/api/item/addFromURLs", { @@ -100,8 +100,8 @@ export const router = { "Content-Type": "application/json", }, body: JSON.stringify({ - items: input.images?.map((image) => ({ - url: image, + items: input.mediaUrls?.map((media) => ({ + url: media, website: input.url, headers: { referer: input.url, @@ -126,36 +126,6 @@ export const router = { handlers.invalidateQuery.send(input) }), - /** - * @deprecated - */ - previewImage: t.procedure - .input<{ - realUrl: string - url: string - width: number - height: number - }>() - .action(async ({ input }) => { - if ( - process.env["VITE_IMGPROXY_URL"] && - input.url.startsWith(process.env["VITE_IMGPROXY_URL"]) - ) { - const meta = await fetch( - `${process.env["VITE_IMGPROXY_URL"]}/unsafe/meta/${encodeURIComponent( - input.realUrl, - )}`, - ).then((res) => res.json()) - input.width = meta.thumbor.source.width - input.height = meta.thumbor.source.height - } - createWindow({ - extraPath: `/preview?url=${encodeURIComponent(input.realUrl)}`, - width: input.width, - height: input.height, - }) - }), - getLoginItemSettings: t.procedure .input() .action(async () => await app.getLoginItemSettings()), diff --git a/src/renderer/src/components/feed-icon.tsx b/src/renderer/src/components/feed-icon.tsx index 3712dba42..4c802691c 100644 --- a/src/renderer/src/components/feed-icon.tsx +++ b/src/renderer/src/components/feed-icon.tsx @@ -1,5 +1,5 @@ import { SiteIcon } from "@renderer/components/site-icon" -import { Image } from "@renderer/components/ui/image" +import { Media } from "@renderer/components/ui/media" import { cn } from "@renderer/lib/utils" import type { CombinedEntryModel, FeedModel } from "@renderer/models" @@ -19,8 +19,9 @@ export function FeedIcon({ const image = entry?.authorAvatar || feed.image if (image) { return ( - () -export type ImageProps = ImgHTMLAttributes & { - proxy?: { - width: number - height: number - } - disableContextMenu?: boolean - popper?: boolean -} -const ImageImpl: FC = ({ - className, - proxy, - disableContextMenu, - popper = false, - ...props -}) => { - const { src, style, ...rest } = props - const [hidden, setHidden] = useState(!src) - const [imgSrc, setImgSrc] = useState( - proxy && src && !failedList.has(src) ? - getProxyUrl({ - url: src, - width: proxy.width, - height: proxy.height, - }) : - src, - ) - - const errorHandle: React.ReactEventHandler = - useEventCallback((e) => { - if (imgSrc !== props.src) { - setImgSrc(props.src) - failedList.add(props.src) - } else { - setHidden(true) - props.onError?.(e) - } - }) - const previewImages = usePreviewImages() - const handleClick = useEventCallback( - (e: React.MouseEvent) => { - if (popper && src) { - e.stopPropagation() - previewImages([src], 0) - } - props.onClick?.(e) - }, - ) - - return ( -
- { - e.stopPropagation() - props.onContextMenu?.(e) - showNativeMenu( - [ - { - type: "text", - label: "Open Image in New Window", - click: () => { - if (props.src && imgSrc && tipcClient) { - window.open(props.src, "_blank") - } - }, - }, - { - type: "text", - label: "Copy Image Address", - click: () => { - if (props.src) { - navigator.clipboard.writeText(props.src) - toast("Address copied to clipboard.", { - duration: 1000, - }) - } - }, - }, - ], - e, - ) - }, - } : - {})} - /> -
- ) -} - -export const Image = memo(ImageImpl) diff --git a/src/renderer/src/components/ui/media.tsx b/src/renderer/src/components/ui/media.tsx new file mode 100644 index 000000000..bf92fa526 --- /dev/null +++ b/src/renderer/src/components/ui/media.tsx @@ -0,0 +1,120 @@ +import type { MediaModel } from "@renderer/hono" +import { tipcClient } from "@renderer/lib/client" +import { getProxyUrl } from "@renderer/lib/img-proxy" +import { showNativeMenu } from "@renderer/lib/native-menu" +import { cn } from "@renderer/lib/utils" +import type { FC, ImgHTMLAttributes } from "react" +import { memo, useState } from "react" +import { toast } from "sonner" +import { useEventCallback } from "usehooks-ts" + +import { usePreviewMedia } from "./media/hooks" + +const failedList = new Set() +export type ImageProps = ImgHTMLAttributes & { + proxy?: { + width: number + height: number + } + disableContextMenu?: boolean + popper?: boolean + type?: MediaModel["type"] +} +const MediaImpl: FC = ({ + className, + proxy, + disableContextMenu, + popper = false, + ...props +}) => { + const { src, style, type, ...rest } = props + const [hidden, setHidden] = useState(!src) + const [imgSrc, setImgSrc] = useState( + proxy && src && !failedList.has(src) ? + getProxyUrl({ + url: src, + width: proxy.width, + height: proxy.height, + }) : + src, + ) + + const errorHandle: React.ReactEventHandler = + useEventCallback((e) => { + if (imgSrc !== props.src) { + setImgSrc(props.src) + failedList.add(props.src) + } else { + setHidden(true) + props.onError?.(e) + } + }) + const previewMedia = usePreviewMedia() + const handleClick = useEventCallback( + (e: React.MouseEvent) => { + if (popper && src) { + e.stopPropagation() + previewMedia([{ + url: src, + type: "photo", + }], 0) + } + props.onClick?.(e) + }, + ) + + return ( +
+ {(!type || type === "photo") && ( + { + e.stopPropagation() + props.onContextMenu?.(e) + showNativeMenu( + [ + { + type: "text", + label: "Open Image in New Window", + click: () => { + if (props.src && imgSrc && tipcClient) { + window.open(props.src, "_blank") + } + }, + }, + { + type: "text", + label: "Copy Image Address", + click: () => { + if (props.src) { + navigator.clipboard.writeText(props.src) + toast("Address copied to clipboard.", { + duration: 1000, + }) + } + }, + }, + ], + e, + ) + }, + } : + {})} + /> + )} +
+ ) +} + +export const Media = memo(MediaImpl) diff --git a/src/renderer/src/components/ui/image/hooks.tsx b/src/renderer/src/components/ui/media/hooks.tsx similarity index 64% rename from src/renderer/src/components/ui/image/hooks.tsx rename to src/renderer/src/components/ui/media/hooks.tsx index caab145d7..f5c10ca43 100644 --- a/src/renderer/src/components/ui/image/hooks.tsx +++ b/src/renderer/src/components/ui/media/hooks.tsx @@ -1,17 +1,18 @@ +import type { MediaModel } from "@renderer/hono" import { useCallback } from "react" import { useModalStack } from "../modal/stacked/hooks" import { NoopChildren } from "../modal/stacked/utils" -import { PreviewImageContent } from "./preview-image" +import { PreviewMediaContent } from "./preview-media" -export const usePreviewImages = () => { +export const usePreviewMedia = () => { const { present } = useModalStack() return useCallback( - (images: string[], initialIndex = 0) => { + (media: MediaModel[], initialIndex = 0) => { present({ content: () => (
- +
), title: "Image", diff --git a/src/renderer/src/components/ui/image/index.module.css b/src/renderer/src/components/ui/media/index.module.css similarity index 100% rename from src/renderer/src/components/ui/image/index.module.css rename to src/renderer/src/components/ui/media/index.module.css diff --git a/src/renderer/src/components/ui/image/preview-image.tsx b/src/renderer/src/components/ui/media/preview-media.tsx similarity index 85% rename from src/renderer/src/components/ui/image/preview-image.tsx rename to src/renderer/src/components/ui/media/preview-media.tsx index 1df26d38e..889fdebb0 100644 --- a/src/renderer/src/components/ui/image/preview-image.tsx +++ b/src/renderer/src/components/ui/media/preview-media.tsx @@ -1,5 +1,6 @@ import { m } from "@renderer/components/common/Motion" import { COPY_MAP } from "@renderer/constants" +import type { MediaModel } from "@renderer/hono" import { tipcClient } from "@renderer/lib/client" import { stopPropagation } from "@renderer/lib/dom" import { showNativeMenu } from "@renderer/lib/native-menu" @@ -57,11 +58,11 @@ const Wrapper: Component<{ ) } -export const PreviewImageContent: FC<{ - images: string[] +export const PreviewMediaContent: FC<{ + media: MediaModel[] initialIndex?: number -}> = ({ images, initialIndex = 0 }) => { - const [currentSrc, setCurrentSrc] = useState(images[initialIndex]) +}> = ({ media, initialIndex = 0 }) => { + const [currentMedia, setCurrentMedia] = useState(media[initialIndex]) const handleContextMenu = useCallback( (image: string, e: React.MouseEvent) => { @@ -96,9 +97,9 @@ export const PreviewImageContent: FC<{ }, [], ) - if (images.length === 0) return null - if (images.length === 1) { - const src = images[0] + if (media.length === 0) return null + if (media.length === 1) { + const src = media[0].url return ( + { - setCurrentSrc(images[realIndex]) + setCurrentMedia(media[realIndex]) }} modules={[Scrollbar, Mousewheel, Virtual, Keyboard]} className="size-full" > - {images.map((image, index) => ( - + {media.map((med, index) => ( + handleContextMenu(image, e)} + onContextMenu={(e) => handleContextMenu(med.url, e)} className="size-full object-contain" alt="cover" - src={image} + src={med.url} loading="lazy" /> diff --git a/src/renderer/src/components/ui/image/swipe-images.tsx b/src/renderer/src/components/ui/media/swipe-media.tsx similarity index 80% rename from src/renderer/src/components/ui/image/swipe-images.tsx rename to src/renderer/src/components/ui/media/swipe-media.tsx index b36d6924a..f14df12ef 100644 --- a/src/renderer/src/components/ui/image/swipe-images.tsx +++ b/src/renderer/src/components/ui/media/swipe-media.tsx @@ -2,30 +2,31 @@ import "swiper/css" import "swiper/css/navigation" import "swiper/css/scrollbar" -import { Image } from "@renderer/components/ui/image" +import { Media } from "@renderer/components/ui/media" +import type { MediaModel } from "@renderer/hono" import { cn } from "@renderer/lib/utils" import { useHover } from "@use-gesture/react" -import { uniq } from "lodash-es" +import { uniqBy } from "lodash-es" import { useRef, useState } from "react" import { Mousewheel, Navigation, Scrollbar } from "swiper/modules" import { Swiper, SwiperSlide } from "swiper/react" import styles from "./index.module.css" -export function SwipeImages({ - images, +export function SwipeMedia({ + media, uniqueKey, className, imgClassName, onPreview, }: { - images?: string[] | null + media?: MediaModel[] | null uniqueKey?: string className?: string imgClassName?: string - onPreview?: (images: string[], index?: number) => void + onPreview?: (media: MediaModel[], index?: number) => void }) { - const uniqImages = uniq(images) + const uniqMedia = uniqBy(media, "url") const hoverRef = useRef(null) const [enableSwipe, setEnableSwipe] = useState(false) @@ -40,7 +41,7 @@ export function SwipeImages({ }, ) - if (!images) return null + if (!media) return null return (
- {enableSwipe && (uniqImages?.length || 0) > 1 ? ( + {enableSwipe && (uniqMedia?.length || 0) > 1 ? ( <> - {uniqImages?.slice(0, 5).map((image, i) => ( - - ( + + { - onPreview?.(images, i) + onPreview?.(uniqMedia, i) e.stopPropagation() }} /> @@ -102,16 +104,17 @@ export function SwipeImages({
- ) : uniqImages?.length >= 1 ? + ) : uniqMedia?.length >= 1 ? ( - { - onPreview?.(uniqImages) + onPreview?.(uniqMedia) e.stopPropagation() }} className="size-full rounded-none object-cover sm:transition-transform sm:duration-300 sm:ease-in-out sm:group-hover:scale-105" alt="cover" - src={uniqImages[0]} + src={uniqMedia[0].url} + type={uniqMedia[0].type} loading="lazy" proxy={{ width: 600, diff --git a/src/renderer/src/hono.ts b/src/renderer/src/hono.ts index 0d201a835..3dd7fa97f 100644 --- a/src/renderer/src/hono.ts +++ b/src/renderer/src/hono.ts @@ -450,6 +450,17 @@ declare const collectionsRelations: drizzle_orm.Relations<"collections", { feeds: drizzle_orm.One<"feeds", true>; }>; +type MediaModel = { + url: string; + type: "photo" | "video"; + preview_image_url?: string; +}; +type EnclosuresModel = { + url: string; + length?: number; + type?: string; + title?: string; +}; declare const entries: drizzle_orm_pg_core.PgTableWithColumns<{ name: "entries"; schema: undefined; @@ -646,32 +657,32 @@ declare const entries: drizzle_orm_pg_core.PgTableWithColumns<{ baseColumn: never; generated: undefined; }, {}, {}>; - images: drizzle_orm_pg_core.PgColumn<{ - name: "images"; + media: drizzle_orm_pg_core.PgColumn<{ + name: "media"; tableName: "entries"; dataType: "array"; columnType: "PgArray"; - data: string[]; - driverParam: string | string[]; + data: unknown[]; + driverParam: string | unknown[]; notNull: false; hasDefault: false; isPrimaryKey: false; isAutoincrement: false; hasRuntimeDefault: false; - enumValues: [string, ...string[]]; + enumValues: undefined; baseColumn: drizzle_orm.Column<{ - name: "images"; + name: "media"; tableName: "entries"; - dataType: "string"; - columnType: "PgText"; - data: string; - driverParam: string; + dataType: "json"; + columnType: "PgJsonb"; + data: unknown; + driverParam: unknown; notNull: false; hasDefault: false; isPrimaryKey: false; isAutoincrement: false; hasRuntimeDefault: false; - enumValues: [string, ...string[]]; + enumValues: undefined; baseColumn: never; generated: undefined; }, object, object>; @@ -755,14 +766,18 @@ declare const entriesOpenAPISchema: z.ZodObject; changedAt: z.ZodString; publishedAt: z.ZodString; - images: z.ZodNullable>; + media: z.ZodNullable, "many">>; categories: z.ZodNullable>; enclosures: z.ZodNullable, "many">>; -}, "enclosures">, { +}, "media" | "enclosures">, { enclosures: z.ZodNullable; @@ -779,6 +794,19 @@ declare const entriesOpenAPISchema: z.ZodObject, "many">>>; + media: z.ZodNullable; + preview_image_url: z.ZodOptional; + }, "strip", z.ZodTypeAny, { + type: "photo" | "video"; + url: string; + preview_image_url?: string | undefined; + }, { + type: "photo" | "video"; + url: string; + preview_image_url?: string | undefined; + }>, "many">>>; }>, "strip", z.ZodTypeAny, { description: string | null; title: string | null; @@ -792,8 +820,12 @@ declare const entriesOpenAPISchema: z.ZodObject; }>; type EntriesModel = InferInsertModel & { - enclosures?: { - url: string; - length?: number; - type?: string; - title?: string; - }[] | null; + enclosures?: EnclosuresModel[] | null; + media?: MediaModel[] | null; }; declare const entryReadHistories: drizzle_orm_pg_core.PgTableWithColumns<{ name: "entryReadHistories"; @@ -2997,8 +3029,12 @@ declare const _routes: hono_hono_base.HonoBase; type AppType = typeof _routes; -export { type ActionsModel, type AppType, type EntriesModel, type EntryReadHistoriesModel, type FeedModel, type SettingsModel, accounts, actions, actionsItemOpenAPISchema, actionsOpenAPISchema, actionsRelations, collections, collectionsOpenAPISchema, collectionsRelations, entries, entriesOpenAPISchema, entriesRelations, entryReadHistories, entryReadHistoriesOpenAPISchema, entryReadHistoriesRelations, feedPowerTokens, feedPowerTokensOpenAPISchema, feedPowerTokensRelations, feeds, feedsInputSchema, feedsOpenAPISchema, feedsRelations, invitations, languageSchema, sessions, subscriptions, subscriptionsOpenAPISchema, subscriptionsRelations, timeline, timelineOpenAPISchema, timelineRelations, transactionType, transactions, transactionsOpenAPISchema, transactionsRelations, users, usersOpenApiSchema, usersRelations, verificationTokens, wallets, walletsOpenAPISchema, walletsRelations }; +export { type ActionsModel, type AppType, type EnclosuresModel, type EntriesModel, type EntryReadHistoriesModel, type FeedModel, type MediaModel, type SettingsModel, accounts, actions, actionsItemOpenAPISchema, actionsOpenAPISchema, actionsRelations, collections, collectionsOpenAPISchema, collectionsRelations, entries, entriesOpenAPISchema, entriesRelations, entryReadHistories, entryReadHistoriesOpenAPISchema, entryReadHistoriesRelations, feedPowerTokens, feedPowerTokensOpenAPISchema, feedPowerTokensRelations, feeds, feedsInputSchema, feedsOpenAPISchema, feedsRelations, invitations, languageSchema, sessions, subscriptions, subscriptionsOpenAPISchema, subscriptionsRelations, timeline, timelineOpenAPISchema, timelineRelations, transactionType, transactions, transactionsOpenAPISchema, transactionsRelations, users, usersOpenApiSchema, usersRelations, verificationTokens, wallets, walletsOpenAPISchema, walletsRelations }; diff --git a/src/renderer/src/hooks/biz/useEntryActions.tsx b/src/renderer/src/hooks/biz/useEntryActions.tsx index 1df69acab..aa9e4cc41 100644 --- a/src/renderer/src/hooks/biz/useEntryActions.tsx +++ b/src/renderer/src/hooks/biz/useEntryActions.tsx @@ -149,21 +149,21 @@ export const useEntryActions = ({ }, }, { - name: "Save Images to Eagle", + name: "Save Media to Eagle", icon: "/eagle.svg", disabled: (checkEagle.isLoading ? true : !checkEagle.data) || - !populatedEntry.entries.images?.length, + !populatedEntry.entries.media?.length, onClick: async () => { if ( !populatedEntry.entries.url || - !populatedEntry.entries.images?.length + !populatedEntry.entries.media?.length ) { return } const response = await tipcClient?.saveToEagle({ url: populatedEntry.entries.url, - images: populatedEntry.entries.images, + mediaUrls: populatedEntry.entries.media.map((m) => m.url), }) if (response?.status === "success") { toast("Saved to Eagle.", { diff --git a/src/renderer/src/lib/parse-html.ts b/src/renderer/src/lib/parse-html.ts index f68878ac2..bed578fe9 100644 --- a/src/renderer/src/lib/parse-html.ts +++ b/src/renderer/src/lib/parse-html.ts @@ -1,7 +1,7 @@ import { Checkbox } from "@renderer/components/ui/checkbox" import { ShikiHighLighter } from "@renderer/components/ui/code-highlighter" -import { Image } from "@renderer/components/ui/image" import { LinkWithTooltip } from "@renderer/components/ui/link" +import { Media } from "@renderer/components/ui/media" import { toJsxRuntime } from "hast-util-to-jsx-runtime" import { createElement } from "react" import { Fragment, jsx, jsxs } from "react/jsx-runtime" @@ -11,7 +11,6 @@ import rehypeParse from "rehype-parse" import rehypeSanitize, { defaultSchema } from "rehype-sanitize" import rehypeStringify from "rehype-stringify" import { unified } from "unified" -import { visit } from "unist-util-visit" import { VFile } from "vfile" export const parseHtml = async ( @@ -42,27 +41,7 @@ export const parseHtml = async ( const hastTree = pipeline.runSync(tree, file) - const metadata: { - desctription: string - images: string[] - } = { - desctription: file.data.meta?.description || "", - images: [], - } - if (hastTree) { - visit(hastTree, (node) => { - if (node.type === "element") { - if (node.tagName === "img" && typeof node.properties.src === "string") { - metadata.images.push(node.properties.src) - } else if (node.tagName === "a") { - node.properties.target = "_blank" - } - } - }) - } - return { - metadata, content: toJsxRuntime(hastTree, { Fragment, ignoreInvalidStyle: true, @@ -79,7 +58,7 @@ export const parseHtml = async ( style: { maxWidth: "100%", display: "inline" }, }) } - return createElement(Image, { ...props, popper: true }) + return createElement(Media, { ...props, popper: true }) }, p: ({ node, ...props }) => { if (node?.children) { diff --git a/src/renderer/src/modules/discover/form.tsx b/src/renderer/src/modules/discover/form.tsx index 759ed086d..72bf0d3b4 100644 --- a/src/renderer/src/modules/discover/form.tsx +++ b/src/renderer/src/modules/discover/form.tsx @@ -15,8 +15,8 @@ import { FormLabel, FormMessage, } from "@renderer/components/ui/form" -import { Image } from "@renderer/components/ui/image" import { Input } from "@renderer/components/ui/input" +import { Media } from "@renderer/components/ui/media" import { useModalStack } from "@renderer/components/ui/modal/stacked/hooks" import { apiClient } from "@renderer/lib/api-fetch" import type { FeedViewType } from "@renderer/lib/enum" @@ -177,9 +177,9 @@ export function DiscoverForm({ type }: { type: string }) { className="flex min-w-0 flex-1 flex-col items-center gap-1" rel="noreferrer" > - {assertEntry.images?.[0] ? ( - ) : ( diff --git a/src/renderer/src/modules/entry-column/list-item-template.tsx b/src/renderer/src/modules/entry-column/list-item-template.tsx index 2f3d1cffe..57efd9f00 100644 --- a/src/renderer/src/modules/entry-column/list-item-template.tsx +++ b/src/renderer/src/modules/entry-column/list-item-template.tsx @@ -1,6 +1,6 @@ import { FeedIcon } from "@renderer/components/feed-icon" import { RelativeTime } from "@renderer/components/ui/datetime" -import { Image } from "@renderer/components/ui/image" +import { Media } from "@renderer/components/ui/media" import { FEED_COLLECTION_LIST } from "@renderer/constants" import { useAsRead } from "@renderer/hooks/biz/useAsRead" import { useRouteParamsSelector } from "@renderer/hooks/biz/useRouteParams" @@ -111,9 +111,10 @@ export function ListItem({ /> )} - {withDetails && entry.entries.images?.[0] && ( - entryId === entry?.entries.id, ) - const previewImage = usePreviewImages() + const previewMedia = usePreviewMedia() if (!entry) return return (
- { - previewImage(images, i) + onPreview={(media, i) => { + previewMedia(media, i) }} />
diff --git a/src/renderer/src/modules/entry-column/social-media-item.tsx b/src/renderer/src/modules/entry-column/social-media-item.tsx index ef713cfe2..e52a6efab 100644 --- a/src/renderer/src/modules/entry-column/social-media-item.tsx +++ b/src/renderer/src/modules/entry-column/social-media-item.tsx @@ -1,7 +1,7 @@ import { FeedIcon } from "@renderer/components/feed-icon" import { RelativeTime } from "@renderer/components/ui/datetime" -import { Image } from "@renderer/components/ui/image" -import { usePreviewImages } from "@renderer/components/ui/image/hooks" +import { Media } from "@renderer/components/ui/media" +import { usePreviewMedia } from "@renderer/components/ui/media/hooks" import { useAsRead } from "@renderer/hooks/biz/useAsRead" import { cn } from "@renderer/lib/utils" import { useEntry } from "@renderer/store/entry/hooks" @@ -19,7 +19,7 @@ export const SocialMediaItem: EntryListItemFC = ({ }) => { const entry = useEntry(entryId) || entryPreview - const previewImage = usePreviewImages() + const previewMedia = usePreviewMedia() const asRead = useAsRead(entry) const feed = useFeedById(entry?.feedId) @@ -68,10 +68,11 @@ export const SocialMediaItem: EntryListItemFC = ({
- {entry.entries.images?.map((image, i, images) => ( - ( + { - previewImage(images, i) + previewMedia(mediaList, i) e.stopPropagation() }} /> diff --git a/src/renderer/src/modules/entry-column/video-item.tsx b/src/renderer/src/modules/entry-column/video-item.tsx index c8bd5e582..9ab0414c3 100644 --- a/src/renderer/src/modules/entry-column/video-item.tsx +++ b/src/renderer/src/modules/entry-column/video-item.tsx @@ -1,5 +1,5 @@ import { m } from "@renderer/components/common/Motion" -import { Image } from "@renderer/components/ui/image" +import { Media } from "@renderer/components/ui/media" import { useModalStack } from "@renderer/components/ui/modal" import { NoopChildren } from "@renderer/components/ui/modal/stacked/utils" import { useRouteParamsSelector } from "@renderer/hooks/biz/useRouteParams" @@ -66,9 +66,9 @@ export function VideoItem({ entryId, entryPreview, translation }: UniversalItemP className={cn("pointer-events-none aspect-video w-full shrink-0 rounded-md bg-black object-cover", isActive && "rounded-b-none")} /> ) : ( -