Skip to content

Commit

Permalink
fix: no video avaliable placeholder
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <[email protected]>
  • Loading branch information
Innei committed Jul 25, 2024
1 parent 17d1b55 commit 5b29417
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 133 deletions.
237 changes: 132 additions & 105 deletions src/renderer/src/components/ui/media.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,34 @@ import { getProxyUrl } from "@renderer/lib/img-proxy"
import { showNativeMenu } from "@renderer/lib/native-menu"
import { cn } from "@renderer/lib/utils"
import type { FC, ImgHTMLAttributes, VideoHTMLAttributes } from "react"
import { memo, useState } from "react"
import { memo, useMemo, useState } from "react"
import { toast } from "sonner"
import { useEventCallback } from "usehooks-ts"

import { usePreviewMedia } from "./media/hooks"

const failedList = new Set<string | undefined>()
export type ImageProps = (ImgHTMLAttributes<HTMLImageElement> & {
proxy?: {
width: number
height: number
}
disableContextMenu?: boolean
popper?: boolean
type: "photo"
previewImageUrl?: string
}) | (VideoHTMLAttributes<HTMLVideoElement> & {
proxy?: {
width: number
height: number
}
disableContextMenu?: boolean
popper?: boolean
type: "video"
previewImageUrl?: string
})
export type ImageProps =
| (ImgHTMLAttributes<HTMLImageElement> & {
proxy?: {
width: number
height: number
}
disableContextMenu?: boolean
popper?: boolean
type: "photo"
previewImageUrl?: string
})
| (VideoHTMLAttributes<HTMLVideoElement> & {
proxy?: {
width: number
height: number
}
disableContextMenu?: boolean
popper?: boolean
type: "video"
previewImageUrl?: string
})
const MediaImpl: FC<ImageProps> = ({
className,
proxy,
Expand Down Expand Up @@ -59,95 +61,120 @@ const MediaImpl: FC<ImageProps> = ({
}
})
const previewMedia = usePreviewMedia()
const handleClick = useEventCallback(
(e: React.MouseEvent) => {
if (popper && src) {
e.stopPropagation()
previewMedia([{
url: src,
type,
}], 0)
}
props.onClick?.(e as any)
},
)
const handleClick = useEventCallback((e: React.MouseEvent) => {
if (popper && src) {
e.stopPropagation()
previewMedia(
[
{
url: src,
type,
},
],
0,
)
}
props.onClick?.(e as any)
})

return (
<div className={cn("overflow-hidden rounded", className)} style={style}>
{(type === "photo") && (
<img
{...rest as ImgHTMLAttributes<HTMLImageElement>}
onError={errorHandle}
className={cn(
hidden && "hidden",
!(props.width || props.height) && "size-full",
"bg-stone-100 object-cover",
popper && "cursor-zoom-in",
)}
src={imgSrc}
onClick={handleClick}
{...(!disableContextMenu ?
{
onContextMenu: (e) => {
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")
}
const InnerContent = useMemo(() => {
switch (type) {
case "photo": {
return (
<img
{...(rest as ImgHTMLAttributes<HTMLImageElement>)}
onError={errorHandle}
className={cn(
hidden && "hidden",
!(props.width || props.height) && "size-full",
"bg-stone-100 object-cover",
popper && "cursor-zoom-in",
)}
src={imgSrc}
onClick={handleClick}
{...(!disableContextMenu ?
{
onContextMenu: (e) => {
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,
})
}
{
type: "text",
label: "Copy Image Address",
click: () => {
if (props.src) {
navigator.clipboard.writeText(props.src)
toast("Address copied to clipboard.", {
duration: 1000,
})
}
},
},
},
],
e,
)
},
} :
{})}
/>
)}
{(type === "video") && (
<div
className={cn(
hidden && "hidden",
!(props.width || props.height) && "size-full",
"relative bg-stone-100 object-cover",
)}
onClick={handleClick}
>
{previewImageUrl ? (
<img
src={previewImageUrl}
className="size-full object-cover"
/>
) : (
<video
src={src}
muted
className="relative size-full object-cover"
/>
)}
<div className="absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 rounded-full bg-black/50 p-2 text-3xl text-white/80">
<i className="i-mgc-play-cute-fi" />
],
e,
)
},
} :
{})}
/>
)
}
case "video": {
return (
<div
className={cn(
hidden && "hidden",
!(props.width || props.height) && "size-full",
"relative bg-stone-100 object-cover",
)}
onClick={handleClick}
>
{previewImageUrl ? (
<img src={previewImageUrl} className="size-full object-cover" />
) : (
<video
src={src}
muted
className="relative size-full object-cover"
/>
)}
<div className="absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 rounded-full bg-black/50 p-2 text-3xl text-white/80">
<i className="i-mgc-play-cute-fi" />
</div>
</div>
</div>
)}
)
}
default: {
throw new Error("Invalid type")
}
}
}, [
disableContextMenu,
errorHandle,
handleClick,
hidden,
imgSrc,
popper,
previewImageUrl,
props,
rest,
src,
type,
])

return (
<div className={cn("overflow-hidden rounded", className)} style={style}>
{InnerContent}
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export const SocialMediaItem: EntryListItemFC = ({
const previewMedia = usePreviewMedia()
const asRead = useAsRead(entry)
const feed = useFeedById(entry?.feedId)

// NOTE: prevent 0 height element, react virtuoso will not stop render any more
if (!entry || !feed) return <ReactVirtuosoItemPlaceholder />

Expand Down
81 changes: 54 additions & 27 deletions src/renderer/src/modules/entry-column/video-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,24 @@ import type { UniversalItemProps } from "./types"

const ViewTag = window.electron ? "webview" : "iframe"

export function VideoItem({ entryId, entryPreview, translation }: UniversalItemProps) {
export function VideoItem({
entryId,
entryPreview,
translation,
}: UniversalItemProps) {
const entry = useEntry(entryId) || entryPreview

const isActive = useRouteParamsSelector(({ entryId }) => entryId === entry?.entries.id)
const isActive = useRouteParamsSelector(
({ entryId }) => entryId === entry?.entries.id,
)

const [miniIframeSrc, iframeSrc] = useMemo(() => [urlToIframe(entry?.entries.url, true), urlToIframe(entry?.entries.url)], [entry?.entries.url])
const [miniIframeSrc, iframeSrc] = useMemo(
() => [
urlToIframe(entry?.entries.url, true),
urlToIframe(entry?.entries.url),
],
[entry?.entries.url],
)
const modalStack = useModalStack()

const ref = useRef<HTMLDivElement>(null)
Expand All @@ -36,7 +48,11 @@ export function VideoItem({ entryId, entryPreview, translation }: UniversalItemP

if (!entry) return <ReactVirtuosoItemPlaceholder />
return (
<GridItem entryId={entryId} entryPreview={entryPreview} translation={translation}>
<GridItem
entryId={entryId}
entryPreview={entryPreview}
translation={translation}
>
<div
className="w-full"
onClick={(e) => {
Expand All @@ -45,11 +61,12 @@ export function VideoItem({ entryId, entryPreview, translation }: UniversalItemP
modalStack.present({
title: "",
content: ({ dismiss }) => (
<m.div exit={{ scale: 0.94, opacity: 0 }} className="size-full p-12" onClick={() => dismiss()}>
<ViewTag
src={iframeSrc}
className="size-full"
/>
<m.div
exit={{ scale: 0.94, opacity: 0 }}
className="size-full p-12"
onClick={() => dismiss()}
>
<ViewTag src={iframeSrc} className="size-full" />
</m.div>
),
clickOutsideToDismiss: true,
Expand All @@ -63,25 +80,35 @@ export function VideoItem({ entryId, entryPreview, translation }: UniversalItemP
{miniIframeSrc && hovered ? (
<ViewTag
src={miniIframeSrc}
className={cn("pointer-events-none aspect-video w-full shrink-0 rounded-md bg-black object-cover", isActive && "rounded-b-none")}
className={cn(
"pointer-events-none aspect-video w-full shrink-0 rounded-md bg-black object-cover",
isActive && "rounded-b-none",
)}
/>
) : (
entry.entries.media && (
<Media
key={entry.entries.media?.[0].url}
src={entry.entries.media?.[0].url}
type={entry.entries.media?.[0].type}
previewImageUrl={entry.entries.media?.[0].preview_image_url}
className={cn("aspect-video w-full shrink-0 rounded-md object-cover", isActive && "rounded-b-none")}
loading="lazy"
proxy={{
width: 640,
height: 360,
}}
disableContextMenu
/>
)
)}
) : entry.entries.media ?
(
<Media
key={entry.entries.media?.[0].url}
src={entry.entries.media?.[0].url}
type={entry.entries.media?.[0].type}
previewImageUrl={entry.entries.media?.[0].preview_image_url}
className={cn(
"aspect-video w-full shrink-0 rounded-md object-cover",
isActive && "rounded-b-none",
)}
loading="lazy"
proxy={{
width: 640,
height: 360,
}}
disableContextMenu
/>
) :
(
<div className="center aspect-video w-full bg-muted text-sm text-muted-foreground">
No video available
</div>
)}
</div>
</div>
</GridItem>
Expand Down

0 comments on commit 5b29417

Please sign in to comment.