chore: Link preview style changes

This commit is contained in:
Kieran 2023-04-18 12:47:01 +01:00
parent 664d4bf976
commit 3d8269b674
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
10 changed files with 70 additions and 65 deletions

View File

@ -12,9 +12,8 @@ const Avatar = ({ user, ...rest }: { user?: UserMetadata; onClick?: () => void }
useEffect(() => {
if (user?.picture) {
proxy(user.picture, 120)
.then(a => setUrl(a))
.catch(console.warn);
const url = proxy(user.picture, 120);
setUrl(url);
}
}, [user]);

View File

@ -92,14 +92,7 @@ export default function HyperText({ link, creator }: { link: string; creator: st
return <MagnetLink magnet={parsed} />;
}
} else {
return (
<>
<a href={a} onClick={e => e.stopPropagation()} target="_blank" rel="noreferrer" className="ext">
{a}
</a>
<LinkPreview url={a} />
</>
);
return <LinkPreview url={a} />;
}
} catch {
// Ignore the error.

View File

@ -0,0 +1,28 @@
.link-preview-container {
border: 1px solid var(--gray);
border-radius: 10px;
overflow: hidden;
}
.link-preview-container:hover {
cursor: pointer;
}
.link-preview-title {
padding: 0 10px 10px 10px;
}
.link-preview-title > small {
color: var(--font-secondary-color);
font-size: small;
}
.link-preview-image {
margin: 0 0 15px 0 !important;
border-radius: 0 !important;
background-image: var(--img-url);
min-height: 250px;
max-height: 500px;
background-size: cover;
background-position: center;
}

View File

@ -1,21 +1,14 @@
import { useEffect, useState } from "react";
import "./LinkPreview.css";
import { CSSProperties, useEffect, useState } from "react";
import { ApiHost } from "Const";
import Spinner from "Icons/Spinner";
import { ProxyImg } from "Element/ProxyImg";
interface LinkPreviewData {
title?: string;
description?: string;
image?: string;
}
import SnortApi, { LinkPreviewData } from "SnortApi";
import useImgProxy from "Hooks/useImgProxy";
async function fetchUrlPreviewInfo(url: string) {
const api = new SnortApi();
try {
const res = await fetch(`${ApiHost}/api/v1/preview?url=${encodeURIComponent(url)}`);
if (res.ok) {
return (await res.json()) as LinkPreviewData;
}
return await api.linkPreview(url);
} catch (e) {
console.warn(`Failed to load link preview`, url);
}
@ -23,11 +16,12 @@ async function fetchUrlPreviewInfo(url: string) {
const LinkPreview = ({ url }: { url: string }) => {
const [preview, setPreview] = useState<LinkPreviewData | null>();
const { proxy } = useImgProxy();
useEffect(() => {
(async () => {
const data = await fetchUrlPreviewInfo(url);
if (data && data.title) {
if (data && data.image) {
setPreview(data);
} else {
setPreview(null);
@ -35,19 +29,27 @@ const LinkPreview = ({ url }: { url: string }) => {
})();
}, [url]);
if (preview === null) return null;
if (preview === null)
return (
<a href={url} onClick={e => e.stopPropagation()} target="_blank" rel="noreferrer" className="ext">
{url}
</a>
);
const backgroundImage = preview?.image ? `url(${proxy(preview?.image)})` : "";
const style = { "--img-url": backgroundImage } as CSSProperties;
return (
<div className="link-preview-container">
{preview && (
<a href={url} onClick={e => e.stopPropagation()} target="_blank" rel="noreferrer" className="ext">
{preview?.image && <ProxyImg src={preview?.image} className="link-preview-image" />}
{preview?.image && <div className="link-preview-image" style={style} />}
<p className="link-preview-title">
{preview?.title}
{preview?.description && (
<>
<br />
<small>{preview?.description}</small>
<small>{preview.description.slice(0, 160)}</small>
</>
)}
</p>

View File

@ -263,26 +263,3 @@
.close-menu-container {
position: absolute;
}
.link-preview-container {
border: 1px solid var(--gray);
border-radius: 10px;
text-align: center;
}
.link-preview-container:hover {
cursor: pointer;
}
.link-preview-title {
padding: 0 10px 10px 10px;
}
.link-preview-title > small {
color: var(--font-secondary-color);
font-size: small;
}
.link-preview-image {
margin: 0 0 15px 0 !important;
}

View File

@ -12,9 +12,8 @@ export const ProxyImg = (props: ProxyImgProps) => {
useEffect(() => {
if (src) {
proxy(src, size)
.then(a => setUrl(a))
.catch(console.warn);
const url = proxy(src, size);
setUrl(url);
}
}, [src]);

View File

@ -17,8 +17,8 @@ export default function useImgProxy() {
return s.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
}
async function signUrl(u: string) {
const result = await hmacSha256(
function signUrl(u: string) {
const result = hmacSha256(
secp.utils.hexToBytes(unwrap(settings).key),
secp.utils.hexToBytes(unwrap(settings).salt),
te.encode(u)
@ -27,13 +27,13 @@ export default function useImgProxy() {
}
return {
proxy: async (url: string, resize?: number) => {
proxy: (url: string, resize?: number) => {
if (!settings) return url;
const opt = resize ? `rs:fit:${resize}:${resize}/dpr:${window.devicePixelRatio}` : "";
const urlBytes = te.encode(url);
const urlEncoded = urlSafe(base64.encode(urlBytes, 0, urlBytes.byteLength));
const path = `/${opt}/${urlEncoded}`;
const sig = await signUrl(path);
const sig = signUrl(path);
return `${new URL(settings.url).toString()}${sig}${path}`;
},
};

View File

@ -85,7 +85,8 @@ export default function LoginPage() {
useEffect(() => {
const ret = unwrap(Artwork.at(Artwork.length * Math.random()));
proxy(ret.link).then(a => setArt({ ...ret, link: a }));
const url = proxy(ret.link);
setArt({ ...ret, link: url });
}, []);
async function doLogin() {

View File

@ -41,6 +41,12 @@ export class SubscriptionError extends Error {
}
}
export interface LinkPreviewData {
title?: string;
description?: string;
image?: string;
}
export default class SnortApi {
#url: string;
#publisher?: EventPublisher;
@ -74,6 +80,10 @@ export default class SnortApi {
return this.#getJsonAuthd<Array<Subscription>>("api/v1/subscription");
}
linkPreview(url: string) {
return this.#getJson<LinkPreviewData>(`api/v1/preview?url=${encodeURIComponent(url)}`);
}
async #getJsonAuthd<T>(
path: string,
method?: "GET" | string,

View File

@ -473,12 +473,8 @@ export function findTag(e: TaggedRawEvent, tag: string) {
return maybeTag && maybeTag[1];
}
export async function hmacSha256(key: Uint8Array, ...messages: Uint8Array[]) {
if (window.crypto.subtle) {
return await secp.utils.hmacSha256(key, ...messages);
} else {
return hmac(hash, key, secp.utils.concatBytes(...messages));
}
export function hmacSha256(key: Uint8Array, ...messages: Uint8Array[]) {
return hmac(hash, key, secp.utils.concatBytes(...messages));
}
export function getRelayName(url: string) {