Add tests for featured thumbnail generation; apply Pint formatting and related edits
This commit is contained in:
@@ -36,6 +36,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
|
||||
$middleware->web(append: [
|
||||
\App\Http\Middleware\RedirectLegacyProfileSubdomain::class,
|
||||
\App\Http\Middleware\TrackOnlineVisitor::class,
|
||||
\App\Http\Middleware\UpdateLastVisit::class,
|
||||
\App\Http\Middleware\HandleInertiaRequests::class,
|
||||
// Runs on every web request; no-ops for guests, redirects authenticated
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
import { r as reactExports, a as reactDomExports, R as React } from "./vendor-tiptap-BUUKoc3C.js";
|
||||
import { S as ShareToast } from "../ssr.js";
|
||||
import "util";
|
||||
import "stream";
|
||||
import "path";
|
||||
import "http";
|
||||
import "https";
|
||||
import "url";
|
||||
import "fs";
|
||||
import "crypto";
|
||||
import "http2";
|
||||
import "assert";
|
||||
import "tty";
|
||||
import "os";
|
||||
import "zlib";
|
||||
import "events";
|
||||
import "node:process";
|
||||
import "node:path";
|
||||
import "node:url";
|
||||
import "./vendor-tooltip-CIQaDNlG.js";
|
||||
import "./vendor-realtime-BGlcW0gB.js";
|
||||
import "buffer";
|
||||
import "child_process";
|
||||
import "net";
|
||||
import "tls";
|
||||
import "./vendor-motion-Dg7DlHqj.js";
|
||||
import "process";
|
||||
import "async_hooks";
|
||||
const FeedShareArtworkModal = reactExports.lazy(() => import("../ssr.js").then((n) => n.a));
|
||||
function facebookUrl(url) {
|
||||
return `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`;
|
||||
}
|
||||
function twitterUrl(url, title) {
|
||||
return `https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}&text=${encodeURIComponent(title)}`;
|
||||
}
|
||||
function pinterestUrl(url, imageUrl, title) {
|
||||
return `https://pinterest.com/pin/create/button/?url=${encodeURIComponent(url)}&media=${encodeURIComponent(imageUrl)}&description=${encodeURIComponent(title)}`;
|
||||
}
|
||||
function emailUrl(url, title) {
|
||||
return `mailto:?subject=${encodeURIComponent(title)}&body=${encodeURIComponent(url)}`;
|
||||
}
|
||||
function CopyIcon() {
|
||||
return /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: "currentColor", className: "h-5 w-5" }, /* @__PURE__ */ React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75" }));
|
||||
}
|
||||
function CheckIcon() {
|
||||
return /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: "h-5 w-5 text-emerald-400" }, /* @__PURE__ */ React.createElement("path", { fillRule: "evenodd", d: "M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.05-.143Z", clipRule: "evenodd" }));
|
||||
}
|
||||
function FacebookIcon() {
|
||||
return /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: "h-5 w-5" }, /* @__PURE__ */ React.createElement("path", { d: "M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12Z" }));
|
||||
}
|
||||
function XTwitterIcon() {
|
||||
return /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: "h-5 w-5" }, /* @__PURE__ */ React.createElement("path", { d: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231 5.45-6.231Zm-1.161 17.52h1.833L7.084 4.126H5.117L17.083 19.77Z" }));
|
||||
}
|
||||
function PinterestIcon() {
|
||||
return /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: "h-5 w-5" }, /* @__PURE__ */ React.createElement("path", { d: "M12 2C6.477 2 2 6.477 2 12c0 4.236 2.636 7.855 6.356 9.312-.088-.791-.167-2.005.035-2.868.181-.78 1.172-4.97 1.172-4.97s-.299-.598-.299-1.482c0-1.388.806-2.425 1.808-2.425.853 0 1.265.64 1.265 1.408 0 .858-.546 2.14-.828 3.33-.236.995.5 1.807 1.482 1.807 1.778 0 3.144-1.874 3.144-4.58 0-2.393-1.72-4.068-4.177-4.068-2.845 0-4.515 2.135-4.515 4.34 0 .859.331 1.781.745 2.282a.3.3 0 0 1 .069.288l-.278 1.133c-.044.183-.145.222-.335.134-1.249-.581-2.03-2.407-2.03-3.874 0-3.154 2.292-6.052 6.608-6.052 3.469 0 6.165 2.472 6.165 5.776 0 3.447-2.173 6.22-5.19 6.22-1.013 0-1.965-.527-2.291-1.148l-.623 2.378c-.226.869-.835 1.958-1.244 2.621.937.29 1.931.446 2.962.446 5.523 0 10-4.477 10-10S17.523 2 12 2Z" }));
|
||||
}
|
||||
function EmailIcon() {
|
||||
return /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: "currentColor", className: "h-5 w-5" }, /* @__PURE__ */ React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75" }));
|
||||
}
|
||||
function EmbedIcon() {
|
||||
return /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: "currentColor", className: "h-5 w-5" }, /* @__PURE__ */ React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5" }));
|
||||
}
|
||||
function CloseIcon() {
|
||||
return /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", className: "h-5 w-5" }, /* @__PURE__ */ React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18 18 6M6 6l12 12" }));
|
||||
}
|
||||
function openShareWindow(url) {
|
||||
window.open(url, "_blank", "noopener,noreferrer,width=600,height=500");
|
||||
}
|
||||
function trackShare(artworkId, platform) {
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute("content");
|
||||
fetch(`/api/artworks/${artworkId}/share`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", "X-CSRF-TOKEN": csrfToken || "" },
|
||||
credentials: "same-origin",
|
||||
body: JSON.stringify({ platform })
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
function ArtworkShareModal({ open, onClose, artwork, shareUrl, isLoggedIn = false }) {
|
||||
const backdropRef = reactExports.useRef(null);
|
||||
const [linkCopied, setLinkCopied] = reactExports.useState(false);
|
||||
const [embedCopied, setEmbedCopied] = reactExports.useState(false);
|
||||
const [showEmbed, setShowEmbed] = reactExports.useState(false);
|
||||
const [toastVisible, setToastVisible] = reactExports.useState(false);
|
||||
const [toastMessage, setToastMessage] = reactExports.useState("");
|
||||
const [profileShareOpen, setProfileShareOpen] = reactExports.useState(false);
|
||||
const url = shareUrl || artwork?.canonical_url || (typeof window !== "undefined" ? window.location.href : "#");
|
||||
const title = artwork?.title || "Artwork";
|
||||
const imageUrl = artwork?.thumbs?.xl?.url || artwork?.thumbs?.lg?.url || artwork?.thumbs?.md?.url || "";
|
||||
const thumbMdUrl = artwork?.thumbs?.md?.url || imageUrl;
|
||||
const embedCode = `<a href="${url}">
|
||||
<img src="${thumbMdUrl}" alt="${title.replace(/"/g, """)}" />
|
||||
</a>`;
|
||||
reactExports.useEffect(() => {
|
||||
if (open) {
|
||||
document.body.style.overflow = "hidden";
|
||||
return () => {
|
||||
document.body.style.overflow = "";
|
||||
};
|
||||
}
|
||||
}, [open]);
|
||||
reactExports.useEffect(() => {
|
||||
if (!open) return;
|
||||
const handler = (e) => {
|
||||
if (e.key === "Escape") onClose();
|
||||
};
|
||||
window.addEventListener("keydown", handler);
|
||||
return () => window.removeEventListener("keydown", handler);
|
||||
}, [open, onClose]);
|
||||
reactExports.useEffect(() => {
|
||||
if (open) {
|
||||
setLinkCopied(false);
|
||||
setEmbedCopied(false);
|
||||
setShowEmbed(false);
|
||||
}
|
||||
}, [open]);
|
||||
const showToast = reactExports.useCallback((msg) => {
|
||||
setToastMessage(msg);
|
||||
setToastVisible(true);
|
||||
}, []);
|
||||
const handleCopyLink = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
setLinkCopied(true);
|
||||
showToast("Link copied!");
|
||||
trackShare(artwork?.id, "copy");
|
||||
setTimeout(() => setLinkCopied(false), 2500);
|
||||
} catch {
|
||||
}
|
||||
};
|
||||
const handleCopyEmbed = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(embedCode);
|
||||
setEmbedCopied(true);
|
||||
showToast("Embed code copied!");
|
||||
trackShare(artwork?.id, "embed");
|
||||
setTimeout(() => setEmbedCopied(false), 2500);
|
||||
} catch {
|
||||
}
|
||||
};
|
||||
const handlePlatformShare = (platform, shareLink) => {
|
||||
openShareWindow(shareLink);
|
||||
trackShare(artwork?.id, platform);
|
||||
onClose();
|
||||
};
|
||||
if (!open) return null;
|
||||
const SHARE_OPTIONS = [
|
||||
{
|
||||
label: linkCopied ? "Copied!" : "Copy Link",
|
||||
icon: linkCopied ? /* @__PURE__ */ React.createElement(CheckIcon, null) : /* @__PURE__ */ React.createElement(CopyIcon, null),
|
||||
onClick: handleCopyLink,
|
||||
className: linkCopied ? "border-emerald-500/40 bg-emerald-500/15 text-emerald-400" : "border-white/[0.08] bg-white/[0.04] text-white/70 hover:border-white/[0.15] hover:bg-white/[0.07] hover:text-white"
|
||||
},
|
||||
{
|
||||
label: "Facebook",
|
||||
icon: /* @__PURE__ */ React.createElement(FacebookIcon, null),
|
||||
onClick: () => handlePlatformShare("facebook", facebookUrl(url)),
|
||||
className: "border-white/[0.08] bg-white/[0.04] text-white/70 hover:border-[#1877F2]/40 hover:bg-[#1877F2]/15 hover:text-[#1877F2]"
|
||||
},
|
||||
{
|
||||
label: "X (Twitter)",
|
||||
icon: /* @__PURE__ */ React.createElement(XTwitterIcon, null),
|
||||
onClick: () => handlePlatformShare("twitter", twitterUrl(url, title)),
|
||||
className: "border-white/[0.08] bg-white/[0.04] text-white/70 hover:border-white/30 hover:bg-white/[0.10] hover:text-white"
|
||||
},
|
||||
{
|
||||
label: "Pinterest",
|
||||
icon: /* @__PURE__ */ React.createElement(PinterestIcon, null),
|
||||
onClick: () => handlePlatformShare("pinterest", pinterestUrl(url, imageUrl, title)),
|
||||
className: "border-white/[0.08] bg-white/[0.04] text-white/70 hover:border-[#E60023]/40 hover:bg-[#E60023]/15 hover:text-[#E60023]"
|
||||
},
|
||||
{
|
||||
label: "Email",
|
||||
icon: /* @__PURE__ */ React.createElement(EmailIcon, null),
|
||||
onClick: () => {
|
||||
window.location.href = emailUrl(url, title);
|
||||
trackShare(artwork?.id, "email");
|
||||
},
|
||||
className: "border-white/[0.08] bg-white/[0.04] text-white/70 hover:border-white/[0.15] hover:bg-white/[0.07] hover:text-white"
|
||||
},
|
||||
...isLoggedIn ? [{
|
||||
label: "My Profile",
|
||||
icon: /* @__PURE__ */ React.createElement("i", { className: "fa-solid fa-share-nodes h-5 w-5 text-[1.1rem]" }),
|
||||
onClick: () => setProfileShareOpen(true),
|
||||
className: "border-sky-500/30 bg-sky-500/10 text-sky-400 hover:border-sky-400/50 hover:bg-sky-500/20"
|
||||
}] : []
|
||||
];
|
||||
return reactDomExports.createPortal(
|
||||
/* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
||||
"div",
|
||||
{
|
||||
ref: backdropRef,
|
||||
onClick: (e) => {
|
||||
if (e.target === backdropRef.current) onClose();
|
||||
},
|
||||
className: "fixed inset-0 z-[9999] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4",
|
||||
role: "dialog",
|
||||
"aria-modal": "true",
|
||||
"aria-label": "Share this artwork"
|
||||
},
|
||||
/* @__PURE__ */ React.createElement("div", { className: "w-full max-w-md rounded-2xl border border-nova-700/50 bg-nova-900/80 shadow-2xl backdrop-blur-xl" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between border-b border-white/[0.06] px-6 py-4" }, /* @__PURE__ */ React.createElement("h3", { className: "text-base font-semibold text-white" }, "Share this artwork"), /* @__PURE__ */ React.createElement(
|
||||
"button",
|
||||
{
|
||||
type: "button",
|
||||
onClick: onClose,
|
||||
className: "rounded-lg p-1.5 text-white/40 transition hover:bg-white/[0.06] hover:text-white/70",
|
||||
"aria-label": "Close share dialog"
|
||||
},
|
||||
/* @__PURE__ */ React.createElement(CloseIcon, null)
|
||||
)), thumbMdUrl && /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 border-b border-white/[0.06] px-6 py-3" }, /* @__PURE__ */ React.createElement(
|
||||
"img",
|
||||
{
|
||||
src: thumbMdUrl,
|
||||
alt: title,
|
||||
className: "h-14 w-14 rounded-lg object-cover",
|
||||
loading: "lazy"
|
||||
}
|
||||
), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex-1" }, /* @__PURE__ */ React.createElement("p", { className: "truncate text-sm font-medium text-white" }, title), artwork?.user?.username && /* @__PURE__ */ React.createElement("p", { className: "truncate text-xs text-white/50" }, "by ", artwork.user.username))), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-3 gap-2.5 px-6 py-5 sm:grid-cols-5" }, SHARE_OPTIONS.map((opt) => /* @__PURE__ */ React.createElement(
|
||||
"button",
|
||||
{
|
||||
key: opt.label,
|
||||
type: "button",
|
||||
onClick: opt.onClick,
|
||||
className: [
|
||||
"flex flex-col items-center gap-1.5 rounded-xl border px-2 py-3 text-xs font-medium transition-all duration-200",
|
||||
opt.className
|
||||
].join(" ")
|
||||
},
|
||||
opt.icon,
|
||||
/* @__PURE__ */ React.createElement("span", { className: "truncate" }, opt.label)
|
||||
))), /* @__PURE__ */ React.createElement("div", { className: "border-t border-white/[0.06] px-6 py-4" }, /* @__PURE__ */ React.createElement(
|
||||
"button",
|
||||
{
|
||||
type: "button",
|
||||
onClick: () => setShowEmbed(!showEmbed),
|
||||
className: "flex items-center gap-2 text-sm font-medium text-white/60 transition hover:text-white/80"
|
||||
},
|
||||
/* @__PURE__ */ React.createElement(EmbedIcon, null),
|
||||
showEmbed ? "Hide Embed Code" : "Embed Code",
|
||||
/* @__PURE__ */ React.createElement(
|
||||
"svg",
|
||||
{
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
viewBox: "0 0 16 16",
|
||||
fill: "currentColor",
|
||||
className: `h-3.5 w-3.5 transition-transform duration-200 ${showEmbed ? "rotate-180" : ""}`
|
||||
},
|
||||
/* @__PURE__ */ React.createElement("path", { fillRule: "evenodd", d: "M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z", clipRule: "evenodd" })
|
||||
)
|
||||
), showEmbed && /* @__PURE__ */ React.createElement("div", { className: "mt-3 space-y-2" }, /* @__PURE__ */ React.createElement(
|
||||
"textarea",
|
||||
{
|
||||
readOnly: true,
|
||||
value: embedCode,
|
||||
rows: 3,
|
||||
className: "w-full resize-none rounded-xl border border-white/[0.08] bg-white/[0.03] px-4 py-3 font-mono text-xs text-white/70 outline-none focus:border-white/[0.15]",
|
||||
onClick: (e) => e.target.select()
|
||||
}
|
||||
), /* @__PURE__ */ React.createElement(
|
||||
"button",
|
||||
{
|
||||
type: "button",
|
||||
onClick: handleCopyEmbed,
|
||||
className: [
|
||||
"inline-flex items-center gap-1.5 rounded-full border px-4 py-1.5 text-xs font-medium transition-all duration-200",
|
||||
embedCopied ? "border-emerald-500/40 bg-emerald-500/15 text-emerald-400" : "border-white/[0.08] bg-white/[0.04] text-white/60 hover:border-white/[0.15] hover:text-white/80"
|
||||
].join(" ")
|
||||
},
|
||||
embedCopied ? /* @__PURE__ */ React.createElement(CheckIcon, null) : /* @__PURE__ */ React.createElement(CopyIcon, null),
|
||||
embedCopied ? "Copied!" : "Copy Embed"
|
||||
))))
|
||||
), /* @__PURE__ */ React.createElement(
|
||||
ShareToast,
|
||||
{
|
||||
message: toastMessage,
|
||||
visible: toastVisible,
|
||||
onHide: () => setToastVisible(false)
|
||||
}
|
||||
), profileShareOpen && /* @__PURE__ */ React.createElement(reactExports.Suspense, { fallback: null }, /* @__PURE__ */ React.createElement(
|
||||
FeedShareArtworkModal,
|
||||
{
|
||||
isOpen: profileShareOpen,
|
||||
onClose: () => setProfileShareOpen(false),
|
||||
preselectedArtwork: artwork?.id ? {
|
||||
id: artwork.id,
|
||||
title: artwork.title,
|
||||
thumb_url: artwork.thumbs?.md?.url ?? artwork.thumbs?.lg?.url ?? null,
|
||||
user: artwork.user ?? null
|
||||
} : null,
|
||||
onShared: () => {
|
||||
setProfileShareOpen(false);
|
||||
showToast("Shared to your profile!");
|
||||
}
|
||||
}
|
||||
))),
|
||||
document.body
|
||||
);
|
||||
}
|
||||
export {
|
||||
ArtworkShareModal as default
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
8110
bootstrap/ssr/ssr.js
8110
bootstrap/ssr/ssr.js
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user