mirror of
https://github.com/usememos/memos.git
synced 2024-12-26 15:13:10 +08:00
feat: implemented link previews (frontend files) (#3074)
* feat: implmented link previews (frontend files) * chore: updated frontend side for Link Previews * chore: updated frontend gen types with the renamed (server) service file * fix: passing errors * chore: switched to using generated type instead of separate fields * fix: passing linter error * chore: updated Link.tsx * chore: using `useResponsiveWidth` to render for different devices * chore: refactored Link.tsx
This commit is contained in:
parent
14479347d8
commit
9c1e2f8137
2 changed files with 85 additions and 8 deletions
|
@ -1,18 +1,92 @@
|
|||
import { Tooltip, Card, AspectRatio, Box } from "@mui/joy";
|
||||
import { Link as MLink } from "@mui/joy";
|
||||
import { useEffect, useState } from "react";
|
||||
import { linkServiceClient } from "@/grpcweb";
|
||||
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
||||
import { LinkMetadata } from "@/types/proto/api/v2/link_service";
|
||||
|
||||
interface Props {
|
||||
url: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
const Link: React.FC<Props> = ({ text, url }: Props) => {
|
||||
const [linkMetadata, setLinkMetadata] = useState<LinkMetadata | undefined>();
|
||||
const { md } = useResponsiveWidth();
|
||||
|
||||
const fetchUrlMetadata = async () => {
|
||||
try {
|
||||
const response = await linkServiceClient.getLinkMetadata({ link: url }, {});
|
||||
setLinkMetadata(response.metadata);
|
||||
} catch (error) {
|
||||
console.error("Error fetching URL metadata:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchUrlMetadata();
|
||||
}, [url]);
|
||||
|
||||
return (
|
||||
<a
|
||||
className="text-blue-600 dark:text-blue-400 cursor-pointer underline break-all hover:opacity-80 decoration-1"
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{text || url}
|
||||
</a>
|
||||
<>
|
||||
{md ? (
|
||||
<div>
|
||||
<Tooltip
|
||||
placement="top-end"
|
||||
variant="solid"
|
||||
sx={{}}
|
||||
title={
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
maxWidth: 450,
|
||||
maxHeight: 300,
|
||||
}}
|
||||
>
|
||||
{linkMetadata?.image ? (
|
||||
<a href={url} target="_blank" rel="noopener noreferrer" className="flex w-full">
|
||||
<Card variant="outlined" orientation="vertical" sx={{ width: "100%" }}>
|
||||
<AspectRatio ratio={"21/9"} objectFit="cover" variant="plain">
|
||||
<img src={linkMetadata?.image} alt={linkMetadata?.title} className="pointer-events-none" />
|
||||
</AspectRatio>
|
||||
<div className="flex-1 overflow-auto w-full">
|
||||
<div className="flex flex-col justify-between ">
|
||||
<div>
|
||||
<h3 className="text-2xl font-semibold tracking-tight truncate">{linkMetadata?.title}</h3>
|
||||
{linkMetadata?.description && <p className="text-sm truncate">{linkMetadata?.description}</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</a>
|
||||
) : (
|
||||
<Card variant="soft" orientation="vertical" sx={{ width: "100%" }}>
|
||||
<div className="flex-1 overflow-auto w-full">
|
||||
<div className="flex flex-col justify-between ">
|
||||
<div>
|
||||
<h3 className="text-2xl font-semibold tracking-tight">{linkMetadata?.title}</h3>
|
||||
<p className="text-sm text-black/50 dark:text-white/50">No Preview</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<MLink href={url} underline="none" sx={{ fontWeight: "lg" }}>
|
||||
{url || text}
|
||||
</MLink>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : (
|
||||
<MLink href={url} underline="none" sx={{ fontWeight: "lg" }}>
|
||||
{url || text}
|
||||
</MLink>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { createChannel, createClientFactory, FetchTransport } from "nice-grpc-we
|
|||
import { ActivityServiceDefinition } from "./types/proto/api/v2/activity_service";
|
||||
import { AuthServiceDefinition } from "./types/proto/api/v2/auth_service";
|
||||
import { InboxServiceDefinition } from "./types/proto/api/v2/inbox_service";
|
||||
import { LinkServiceDefinition } from "./types/proto/api/v2/link_service";
|
||||
import { MemoServiceDefinition } from "./types/proto/api/v2/memo_service";
|
||||
import { ResourceServiceDefinition } from "./types/proto/api/v2/resource_service";
|
||||
import { TagServiceDefinition } from "./types/proto/api/v2/tag_service";
|
||||
|
@ -38,3 +39,5 @@ export const inboxServiceClient = clientFactory.create(InboxServiceDefinition, c
|
|||
export const activityServiceClient = clientFactory.create(ActivityServiceDefinition, channel);
|
||||
|
||||
export const webhookServiceClient = clientFactory.create(WebhookServiceDefinition, channel);
|
||||
|
||||
export const linkServiceClient = clientFactory.create(LinkServiceDefinition, channel);
|
||||
|
|
Loading…
Reference in a new issue