a tool for shared writing and social publishing
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

further tweaks to url previews! Save em in supabase, and shrink

+60 -26
+48 -5
actions/addLinkCard.ts
··· 1 1 "use server"; 2 2 import * as z from "zod"; 3 + import { createClient } from "@supabase/supabase-js"; 4 + import { Database } from "supabase/database.types"; 5 + let supabase = createClient<Database>( 6 + process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, 7 + process.env.SUPABASE_SERVICE_ROLE_KEY as string, 8 + ); 3 9 4 10 export async function addLinkCard(args: { link: string }) { 5 11 console.log("addLinkCard"); ··· 32 38 }); 33 39 34 40 export const get_url_preview_data = async (url: string) => { 35 - let response = await fetch(`https://pro.microlink.io/?url=${url}`, { 36 - headers: { 37 - "x-api-key": process.env.MICROLINK_API_KEY!, 38 - }, 41 + let [response, image] = await Promise.all([ 42 + fetch(`https://pro.microlink.io/?url=${url}`, { 43 + headers: { 44 + "x-api-key": process.env.MICROLINK_API_KEY!, 45 + }, 46 + }), 47 + fetch( 48 + `https://pro.microlink.io/?url=${url}&screenshot&embed=screenshot.url`, 49 + { 50 + headers: { 51 + "x-api-key": process.env.MICROLINK_API_KEY!, 52 + }, 53 + }, 54 + ), 55 + ]); 56 + 57 + let key = await hash(url); 58 + supabase.storage.from("url-previews").upload(key, await image.arrayBuffer(), { 59 + contentType: image.headers.get("content-type") || undefined, 39 60 }); 40 61 41 62 let result = expectedAPIResponse.safeParse(await response.json()); 42 - return result; 63 + return { 64 + ...result, 65 + screenshot: { 66 + url: supabase.storage 67 + .from("url-previews") 68 + .getPublicUrl(key, { transform: { width: 240, height: 208 } }).data 69 + .publicUrl, 70 + height: 208, 71 + width: 240, 72 + }, 73 + }; 74 + }; 75 + 76 + const hash = async (str: string) => { 77 + let hashBuffer = await crypto.subtle.digest( 78 + "SHA-256", 79 + new TextEncoder().encode(str), 80 + ); 81 + const hashArray = Array.from(new Uint8Array(hashBuffer)); 82 + const hashHex = hashArray 83 + .map((byte) => byte.toString(16).padStart(2, "0")) 84 + .join(""); 85 + return hashHex; 43 86 };
+1 -1
components/Blocks/ExternalLinkBlock.tsx
··· 51 51 <div 52 52 className={`linkBlockPreview w-[120px] m-2 -mb-2 bg-cover shrink-0 rounded-t-md border border-border rotate-[4deg] origin-center`} 53 53 style={{ 54 - backgroundImage: `url(${`/api/link-preview-proxy?url=${url?.data.value}`})`, 54 + backgroundImage: `url(${previewImage?.data.src})`, 55 55 backgroundPosition: "center", 56 56 }} 57 57 />
+11
src/utils/addLinkBlock.ts
··· 25 25 if (data.success) { 26 26 await rep?.mutate.assertFact({ 27 27 entity: entityID, 28 + attribute: "link/preview", 29 + data: { 30 + fallback: "", 31 + type: "image", 32 + src: data.screenshot.url, 33 + width: data.screenshot.width, 34 + height: data.screenshot.height, 35 + }, 36 + }); 37 + await rep?.mutate.assertFact({ 38 + entity: entityID, 28 39 attribute: "link/title", 29 40 data: { 30 41 type: "text",