From 3d8269b6742a3b9134f1881c7f30451094d95c8c Mon Sep 17 00:00:00 2001 From: Kieran Date: Tue, 18 Apr 2023 12:47:01 +0100 Subject: [PATCH] chore: Link preview style changes --- packages/app/src/Element/Avatar.tsx | 5 ++-- packages/app/src/Element/HyperText.tsx | 9 +----- packages/app/src/Element/LinkPreview.css | 28 ++++++++++++++++++ packages/app/src/Element/LinkPreview.tsx | 36 +++++++++++++----------- packages/app/src/Element/Note.css | 23 --------------- packages/app/src/Element/ProxyImg.tsx | 5 ++-- packages/app/src/Hooks/useImgProxy.ts | 8 +++--- packages/app/src/Pages/Login.tsx | 3 +- packages/app/src/SnortApi.ts | 10 +++++++ packages/app/src/Util.ts | 8 ++---- 10 files changed, 70 insertions(+), 65 deletions(-) create mode 100644 packages/app/src/Element/LinkPreview.css diff --git a/packages/app/src/Element/Avatar.tsx b/packages/app/src/Element/Avatar.tsx index b656fcf3..54ff710d 100644 --- a/packages/app/src/Element/Avatar.tsx +++ b/packages/app/src/Element/Avatar.tsx @@ -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]); diff --git a/packages/app/src/Element/HyperText.tsx b/packages/app/src/Element/HyperText.tsx index 9c9549db..01d747eb 100644 --- a/packages/app/src/Element/HyperText.tsx +++ b/packages/app/src/Element/HyperText.tsx @@ -92,14 +92,7 @@ export default function HyperText({ link, creator }: { link: string; creator: st return ; } } else { - return ( - <> - e.stopPropagation()} target="_blank" rel="noreferrer" className="ext"> - {a} - - - - ); + return ; } } catch { // Ignore the error. diff --git a/packages/app/src/Element/LinkPreview.css b/packages/app/src/Element/LinkPreview.css new file mode 100644 index 00000000..ab4d37a7 --- /dev/null +++ b/packages/app/src/Element/LinkPreview.css @@ -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; +} diff --git a/packages/app/src/Element/LinkPreview.tsx b/packages/app/src/Element/LinkPreview.tsx index e17231a8..83512264 100644 --- a/packages/app/src/Element/LinkPreview.tsx +++ b/packages/app/src/Element/LinkPreview.tsx @@ -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(); + 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 ( + e.stopPropagation()} target="_blank" rel="noreferrer" className="ext"> + {url} + + ); + + const backgroundImage = preview?.image ? `url(${proxy(preview?.image)})` : ""; + const style = { "--img-url": backgroundImage } as CSSProperties; return (
{preview && ( e.stopPropagation()} target="_blank" rel="noreferrer" className="ext"> - {preview?.image && } + {preview?.image &&
}

{preview?.title} {preview?.description && ( <>
- {preview?.description} + {preview.description.slice(0, 160)} )}

diff --git a/packages/app/src/Element/Note.css b/packages/app/src/Element/Note.css index 2be8075e..61d60077 100644 --- a/packages/app/src/Element/Note.css +++ b/packages/app/src/Element/Note.css @@ -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; -} diff --git a/packages/app/src/Element/ProxyImg.tsx b/packages/app/src/Element/ProxyImg.tsx index d20074a2..84200fef 100644 --- a/packages/app/src/Element/ProxyImg.tsx +++ b/packages/app/src/Element/ProxyImg.tsx @@ -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]); diff --git a/packages/app/src/Hooks/useImgProxy.ts b/packages/app/src/Hooks/useImgProxy.ts index 901a1b96..ca41e386 100644 --- a/packages/app/src/Hooks/useImgProxy.ts +++ b/packages/app/src/Hooks/useImgProxy.ts @@ -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}`; }, }; diff --git a/packages/app/src/Pages/Login.tsx b/packages/app/src/Pages/Login.tsx index 0586586e..b2ff78d9 100644 --- a/packages/app/src/Pages/Login.tsx +++ b/packages/app/src/Pages/Login.tsx @@ -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() { diff --git a/packages/app/src/SnortApi.ts b/packages/app/src/SnortApi.ts index f7625796..404e7d78 100644 --- a/packages/app/src/SnortApi.ts +++ b/packages/app/src/SnortApi.ts @@ -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>("api/v1/subscription"); } + linkPreview(url: string) { + return this.#getJson(`api/v1/preview?url=${encodeURIComponent(url)}`); + } + async #getJsonAuthd( path: string, method?: "GET" | string, diff --git a/packages/app/src/Util.ts b/packages/app/src/Util.ts index 2aee55e6..59f0bb31 100644 --- a/packages/app/src/Util.ts +++ b/packages/app/src/Util.ts @@ -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) {