diff --git a/apps/web/src/utils/helpers.ts b/apps/web/src/utils/helpers.ts index 648bc081..23cee68d 100644 --- a/apps/web/src/utils/helpers.ts +++ b/apps/web/src/utils/helpers.ts @@ -1,3 +1,5 @@ +import { env } from "next-runtime-env"; + export const formatToArray = ( value: string | string[] | undefined, ): string[] => { @@ -50,5 +52,21 @@ export const getAvatarUrl = (imageOrKey: string | null) => { return imageOrKey; } + // Construct URL from S3 key + const useVirtualHosted = env("NEXT_PUBLIC_USE_VIRTUAL_HOSTED_URLS") === "true"; + const storageDomain = env("NEXT_PUBLIC_STORAGE_DOMAIN"); + const storageUrl = env("NEXT_PUBLIC_STORAGE_URL"); + const bucket = env("NEXT_PUBLIC_AVATAR_BUCKET_NAME"); + + if (useVirtualHosted && storageDomain && bucket) { + // Virtual-hosted style: https://{bucket}.{domain}/{key} + return `https://${bucket}.${storageDomain}/${imageOrKey}`; + } + + if (storageUrl && bucket) { + // Path-style: {storageUrl}/{bucket}/{key} + return `${storageUrl}/${bucket}/${imageOrKey}`; + } + return ""; }; diff --git a/apps/web/src/views/settings/components/Avatar.tsx b/apps/web/src/views/settings/components/Avatar.tsx index 4e068eb8..d1e24e0b 100644 --- a/apps/web/src/views/settings/components/Avatar.tsx +++ b/apps/web/src/views/settings/components/Avatar.tsx @@ -110,18 +110,17 @@ export default function Avatar({ const cropYpx = (crop.y / 100) * image.naturalHeight; const cropWpx = (crop.width / 100) * image.naturalWidth; const cropHpx = (crop.height / 100) * image.naturalHeight; - canvas.width = Math.max(1, Math.floor(cropWpx)); - canvas.height = Math.max(1, Math.floor(cropHpx)); + + // Cap output at 512x512 — avatars display at 64x64, so higher res is wasteful + // and causes "File too large" errors on HiDPI screens + const maxSize = 512; + const scale = Math.min(maxSize / cropWpx, maxSize / cropHpx, 1); + canvas.width = Math.max(1, Math.floor(cropWpx * scale)); + canvas.height = Math.max(1, Math.floor(cropHpx * scale)); const ctx = canvas.getContext("2d"); if (!ctx) throw new Error("Canvas not supported"); - // For better quality on HiDPI screens - const pixelRatio = window.devicePixelRatio || 1; - canvas.width = canvas.width * pixelRatio; - canvas.height = canvas.height * pixelRatio; - ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); ctx.imageSmoothingQuality = "high"; - ctx.drawImage( image, cropXpx, @@ -130,8 +129,8 @@ export default function Avatar({ cropHpx, 0, 0, - canvas.width / pixelRatio, - canvas.height / pixelRatio, + canvas.width, + canvas.height, ); const mime = selectedFile?.type ?? "image/jpeg"; @@ -139,6 +138,7 @@ export default function Avatar({ canvas.toBlob( (b) => (b ? resolve(b) : reject(new Error("toBlob failed"))), mime, + 0.85, ); }); return blob;