Wire admin studio SSR and search infrastructure
This commit is contained in:
@@ -3,12 +3,14 @@
|
||||
use App\Http\Middleware\ConditionalShareErrorsFromSession;
|
||||
use App\Http\Middleware\ConditionalStartSession;
|
||||
use App\Http\Middleware\ConditionalValidateCsrfToken;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Session\Middleware\StartSession;
|
||||
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
use Sentry\Laravel\Integration;
|
||||
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
@@ -28,6 +30,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
$middleware->validateCsrfTokens(except: [
|
||||
'chat_post',
|
||||
'chat_post/*',
|
||||
'api/art/*/view',
|
||||
]);
|
||||
|
||||
$middleware->web(append: [
|
||||
@@ -43,6 +46,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
$middleware->alias([
|
||||
'artwork.maturity.access' => \App\Http\Middleware\EnsureArtworkMaturityAccess::class,
|
||||
'admin.moderation' => \App\Http\Middleware\EnsureAdminOrModerator::class,
|
||||
'admin.access' => \App\Http\Middleware\EnsureStaffAccess::class,
|
||||
'creator.access' => \App\Http\Middleware\EnsureCreatorAccess::class,
|
||||
'ensure.email.login.upgrade'=> \App\Http\Middleware\EnsureEmailLoginUpgradeComplete::class,
|
||||
'ensure.onboarding.complete'=> \App\Http\Middleware\EnsureOnboardingComplete::class,
|
||||
@@ -56,8 +60,20 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
Integration::handles($exceptions);
|
||||
|
||||
// ── 404 / 410 / 403 — web HTML responses only ─────────────────────────
|
||||
$exceptions->render(function (
|
||||
AuthenticationException $e,
|
||||
\Illuminate\Http\Request $request
|
||||
) {
|
||||
if ($request->expectsJson()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return redirect()->to(route('login'));
|
||||
});
|
||||
|
||||
// ── HTML HTTP errors — use Nova-styled templates when possible ───────
|
||||
$exceptions->render(function (
|
||||
\Symfony\Component\HttpKernel\Exception\HttpException $e,
|
||||
\Illuminate\Http\Request $request
|
||||
@@ -68,24 +84,29 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
|
||||
$status = $e->getStatusCode();
|
||||
|
||||
// 403 and 401 use their generic Blade views — no extra data needed.
|
||||
if ($status === 403) {
|
||||
return response(view('errors.403', ['message' => $e->getMessage() ?: null]), 403);
|
||||
}
|
||||
if ($status === 401) {
|
||||
return response(view('errors.401'), 401);
|
||||
}
|
||||
if ($status === 410) {
|
||||
return response(view('errors.410'), 410);
|
||||
}
|
||||
|
||||
// Generic 404 — smart URL-pattern routing to contextual views.
|
||||
if ($status === 404) {
|
||||
return app(\App\Http\Controllers\Web\ErrorController::class)
|
||||
->handleNotFound($request);
|
||||
}
|
||||
|
||||
return null; // Fallback to Laravel's default.
|
||||
$view = view()->exists("errors.{$status}")
|
||||
? "errors.{$status}"
|
||||
: 'errors.http';
|
||||
|
||||
$data = $view === 'errors.http'
|
||||
? [
|
||||
'error_code' => $status,
|
||||
'error_title' => \Symfony\Component\HttpFoundation\Response::$statusTexts[$status] ?? 'Unexpected Error',
|
||||
'error_message' => $e->getMessage() ?: 'Something went wrong while loading this page.',
|
||||
]
|
||||
: [];
|
||||
|
||||
if ($status === 403) {
|
||||
$data['message'] = $e->getMessage() ?: null;
|
||||
}
|
||||
|
||||
return response()->view($view, $data, $status, $e->getHeaders());
|
||||
});
|
||||
|
||||
// ── ModelNotFoundException → contextual 404 on web ───────────────────
|
||||
|
||||
301
bootstrap/ssr/assets/ArtworkShareModal-DEVyX6r2.js
Normal file
301
bootstrap/ssr/assets/ArtworkShareModal-DEVyX6r2.js
Normal file
@@ -0,0 +1,301 @@
|
||||
import { r as reactExports, a as reactDomExports, R as React } from "./vendor-tiptap-DSw66HfW.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-cgmg5qQY.js";
|
||||
import "buffer";
|
||||
import "child_process";
|
||||
import "net";
|
||||
import "tls";
|
||||
import "./vendor-motion-yDK3iGlC.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
|
||||
};
|
||||
17
bootstrap/ssr/assets/emoji-data-4xGXbtDn.js
Normal file
17
bootstrap/ssr/assets/emoji-data-4xGXbtDn.js
Normal file
File diff suppressed because one or more lines are too long
2581
bootstrap/ssr/assets/emoji-ui-C_DZUNyP.js
Normal file
2581
bootstrap/ssr/assets/emoji-ui-C_DZUNyP.js
Normal file
File diff suppressed because one or more lines are too long
8068
bootstrap/ssr/assets/vendor-motion-yDK3iGlC.js
Normal file
8068
bootstrap/ssr/assets/vendor-motion-yDK3iGlC.js
Normal file
File diff suppressed because it is too large
Load Diff
9704
bootstrap/ssr/assets/vendor-realtime-cgmg5qQY.js
Normal file
9704
bootstrap/ssr/assets/vendor-realtime-cgmg5qQY.js
Normal file
File diff suppressed because it is too large
Load Diff
27177
bootstrap/ssr/assets/vendor-tiptap-DSw66HfW.js
Normal file
27177
bootstrap/ssr/assets/vendor-tiptap-DSw66HfW.js
Normal file
File diff suppressed because it is too large
Load Diff
2627
bootstrap/ssr/assets/vendor-tooltip-CIQaDNlG.js
Normal file
2627
bootstrap/ssr/assets/vendor-tooltip-CIQaDNlG.js
Normal file
File diff suppressed because it is too large
Load Diff
2406
bootstrap/ssr/ssr-manifest.json
Normal file
2406
bootstrap/ssr/ssr-manifest.json
Normal file
File diff suppressed because it is too large
Load Diff
128560
bootstrap/ssr/ssr.js
Normal file
128560
bootstrap/ssr/ssr.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user