- Cache nextjs build
- Uncensor sentry(for diagnostics) - Refactor ICC - Completed proper event system for ICC - Fixed every pages being dynamic because of adminCheck on route layout - Fixed home page loading time - Make all states refresh everytime SSE reconnects - Fixed OG for artifact admin
This commit is contained in:
+9
-2
@@ -54,8 +54,15 @@ COPY --chown=1001 . .
|
|||||||
# Deployment versioning
|
# Deployment versioning
|
||||||
RUN bun -e "const id = Bun.randomUUIDv7(); Bun.write('.version', id); Bun.write('.env.deployment', 'NEXT_DEPLOYMENT_ID='+id)"
|
RUN bun -e "const id = Bun.randomUUIDv7(); Bun.write('.version', id); Bun.write('.env.deployment', 'NEXT_DEPLOYMENT_ID='+id)"
|
||||||
|
|
||||||
# Run patcher (background) + Next.js build (foreground), then kill patcher
|
# Bind mount recursively makes non-existent directories as root, instead of configured user
|
||||||
RUN bun run build
|
RUN mkdir -p .next
|
||||||
|
|
||||||
|
# Build nextjs
|
||||||
|
RUN --mount=type=cache,target=/home/container/.next/cache,uid=1001,gid=1001 \
|
||||||
|
bun run build
|
||||||
|
|
||||||
|
# Fix nextjs caching(bind mount removes it after build process)
|
||||||
|
RUN mkdir -p .next/cache
|
||||||
|
|
||||||
### Stage 3: runner ###
|
### Stage 3: runner ###
|
||||||
FROM oven/bun:canary-alpine AS runner
|
FROM oven/bun:canary-alpine AS runner
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { CdnChooser } from "@/components/chooser";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Spinner } from "@/components/ui/spinner";
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { comms } from "@/lib/comms";
|
import { shared } from "@/lib/comms";
|
||||||
import { syncAmber, toggleEnka } from "./api";
|
import { syncAmber, toggleEnka } from "./api";
|
||||||
import { Section } from "./page";
|
import { Section } from "./page";
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ export function SettingsServicesSection({
|
|||||||
const [syncing, setSyncing] = useState(false);
|
const [syncing, setSyncing] = useState(false);
|
||||||
const [syncResult, setSyncResult] = useState("");
|
const [syncResult, setSyncResult] = useState("");
|
||||||
const [enka, setEnka] = useState<boolean>();
|
const [enka, setEnka] = useState<boolean>();
|
||||||
const [debug] = comms.var("debug");
|
const [debug] = shared.state("debug");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof enka !== "boolean") return setEnka(enkaInitial);
|
if (typeof enka !== "boolean") return setEnka(enkaInitial);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { setLimit, toggleCheck, toggleLock } from "@/lib/api";
|
import { setLimit, toggleCheck, toggleLock } from "@/lib/api";
|
||||||
|
import { shared } from "@/lib/comms";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export function SidebarLink({
|
export function SidebarLink({
|
||||||
@@ -207,6 +208,9 @@ export function SubmissionList({
|
|||||||
export function Watcher() {
|
export function Watcher() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const count = useRef<number | undefined>(undefined);
|
const count = useRef<number | undefined>(undefined);
|
||||||
|
// Reload after connection restored
|
||||||
|
shared.signal("sync", () => router.refresh());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const es = new EventSource(`/api/artifact/ev`);
|
const es = new EventSource(`/api/artifact/ev`);
|
||||||
es.addEventListener("update", () => router.refresh());
|
es.addEventListener("update", () => router.refresh());
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ import { db } from "@/lib/db";
|
|||||||
import { submissions } from "@/lib/db/schema";
|
import { submissions } from "@/lib/db/schema";
|
||||||
import { LimitManager, SubmissionList, Watcher } from "./client";
|
import { LimitManager, SubmissionList, Watcher } from "./client";
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: "เสือกไอดีชาวบ้าน (แอดมิน)",
|
||||||
|
};
|
||||||
|
|
||||||
export default async function AdminLayout({
|
export default async function AdminLayout({
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
export const metadata = {
|
|
||||||
title: "เสือกไอดีชาวบ้าน (แอดมิน)",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function AdminPage() {
|
export default function AdminPage() {
|
||||||
return <div></div>;
|
return <div></div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||||
import { getCharacters } from "@/lib/api";
|
import { getCharacters } from "@/lib/api";
|
||||||
import { comms } from "@/lib/comms";
|
import { shared } from "@/lib/comms";
|
||||||
import { uidRegex } from "@/lib/const";
|
import { uidRegex } from "@/lib/const";
|
||||||
import type { characters } from "@/lib/db/schema";
|
import type { characters } from "@/lib/db/schema";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@@ -159,7 +159,7 @@ function Placeholder({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
|
|
||||||
export function Disclaimer() {
|
export function Disclaimer() {
|
||||||
const [agreed, setAgreed] = useState(false);
|
const [agreed, setAgreed] = useState(false);
|
||||||
const [updated] = comms.var("updated");
|
const [updated] = shared.state("updated");
|
||||||
|
|
||||||
if (agreed || updated) return;
|
if (agreed || updated) return;
|
||||||
return (
|
return (
|
||||||
@@ -181,9 +181,9 @@ export function ClearCookie() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function WarningDialog() {
|
export function WarningDialog() {
|
||||||
const [_manual, _setManual] = comms.var("manual");
|
const [_manual, _setManual] = shared.state("manual");
|
||||||
|
|
||||||
comms.event("beforeSubmit", () => {
|
shared.signal("beforeSubmit", () => {
|
||||||
_manual;
|
_manual;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -3,7 +3,6 @@ import { Toaster } from "sonner";
|
|||||||
import Background from "#/bg.jpg";
|
import Background from "#/bg.jpg";
|
||||||
import Image from "@/components/image";
|
import Image from "@/components/image";
|
||||||
import { SentryDevToolbar } from "@/components/sentry";
|
import { SentryDevToolbar } from "@/components/sentry";
|
||||||
import { adminCheck } from "@/lib/auth";
|
|
||||||
import Providers, { VersionCheck } from "../client";
|
import Providers, { VersionCheck } from "../client";
|
||||||
|
|
||||||
export default async function UiLayout({
|
export default async function UiLayout({
|
||||||
@@ -11,7 +10,6 @@ export default async function UiLayout({
|
|||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const isAdmin = await adminCheck();
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Image
|
<Image
|
||||||
@@ -22,7 +20,7 @@ export default async function UiLayout({
|
|||||||
<Providers>
|
<Providers>
|
||||||
<AnimatePresence mode="wait">{children}</AnimatePresence>
|
<AnimatePresence mode="wait">{children}</AnimatePresence>
|
||||||
<VersionCheck />
|
<VersionCheck />
|
||||||
{isAdmin && <SentryDevToolbar />}
|
<SentryDevToolbar />
|
||||||
</Providers>
|
</Providers>
|
||||||
<Toaster theme="dark" richColors closeButton />
|
<Toaster theme="dark" richColors closeButton />
|
||||||
</>
|
</>
|
||||||
|
|||||||
+6
-6
@@ -41,8 +41,8 @@ export default function Home() {
|
|||||||
src={ArtifactLogo}
|
src={ArtifactLogo}
|
||||||
alt="เสือกไอดีชาวบ้าน"
|
alt="เสือกไอดีชาวบ้าน"
|
||||||
className="w-22"
|
className="w-22"
|
||||||
width={500}
|
width={100}
|
||||||
height={500}
|
height={100}
|
||||||
/>
|
/>
|
||||||
</ItemMedia>
|
</ItemMedia>
|
||||||
<ItemContent>
|
<ItemContent>
|
||||||
@@ -66,8 +66,8 @@ export default function Home() {
|
|||||||
src={RubgramLogo}
|
src={RubgramLogo}
|
||||||
alt="รับกรรมแทนคนดู"
|
alt="รับกรรมแทนคนดู"
|
||||||
className="w-22"
|
className="w-22"
|
||||||
width={500}
|
width={100}
|
||||||
height={500}
|
height={100}
|
||||||
/>
|
/>
|
||||||
</ItemMedia>
|
</ItemMedia>
|
||||||
<ItemContent>
|
<ItemContent>
|
||||||
@@ -90,8 +90,8 @@ export default function Home() {
|
|||||||
src={TierlistLogo}
|
src={TierlistLogo}
|
||||||
alt="จัดเทียร์ลิสต์อบิส"
|
alt="จัดเทียร์ลิสต์อบิส"
|
||||||
className="w-22"
|
className="w-22"
|
||||||
width={500}
|
width={100}
|
||||||
height={500}
|
height={100}
|
||||||
/>
|
/>
|
||||||
</ItemMedia>
|
</ItemMedia>
|
||||||
<ItemContent>
|
<ItemContent>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import {
|
|||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { comms } from "@/lib/comms";
|
import { shared } from "@/lib/comms";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { setFree, setLimit, toggleCheck, toggleLock } from "../api";
|
import { setFree, setLimit, toggleCheck, toggleLock } from "../api";
|
||||||
|
|
||||||
@@ -244,7 +244,7 @@ export function SubmissionList({
|
|||||||
}[];
|
}[];
|
||||||
}) {
|
}) {
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [debug] = comms.var("debug");
|
const [debug] = shared.state("debug");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -275,6 +275,9 @@ export function SubmissionList({
|
|||||||
export function Watcher() {
|
export function Watcher() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const count = useRef<number | undefined>(undefined);
|
const count = useRef<number | undefined>(undefined);
|
||||||
|
// Reload after connection restored
|
||||||
|
shared.signal("sync", () => router.refresh());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const es = new EventSource(`/api/rubgram/ev`);
|
const es = new EventSource(`/api/rubgram/ev`);
|
||||||
es.addEventListener("update", () => router.refresh());
|
es.addEventListener("update", () => router.refresh());
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
MultiSelectValue,
|
MultiSelectValue,
|
||||||
} from "@/components/ui/multi-select";
|
} from "@/components/ui/multi-select";
|
||||||
import { Spinner } from "@/components/ui/spinner";
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
import { comms } from "@/lib/comms";
|
import { shared } from "@/lib/comms";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
calcPrice,
|
calcPrice,
|
||||||
@@ -33,7 +33,7 @@ export function WelcomeScreening({
|
|||||||
session: Awaited<ReturnType<typeof getDiscordSession>>;
|
session: Awaited<ReturnType<typeof getDiscordSession>>;
|
||||||
}) {
|
}) {
|
||||||
const [agreed, setAgreed] = useState(false);
|
const [agreed, setAgreed] = useState(false);
|
||||||
const [updated] = comms.var("updated");
|
const [updated] = shared.state("updated");
|
||||||
|
|
||||||
if (agreed || (updated && session)) return;
|
if (agreed || (updated && session)) return;
|
||||||
return (
|
return (
|
||||||
@@ -76,7 +76,7 @@ export function ServiceSelector({
|
|||||||
types,
|
types,
|
||||||
allDiscount,
|
allDiscount,
|
||||||
}: Awaited<ReturnType<typeof getEndgameConfig>>) {
|
}: Awaited<ReturnType<typeof getEndgameConfig>>) {
|
||||||
const [selected, setSelected] = comms.var("rubgram.services");
|
const [selected, setSelected] = shared.state("rubgram.services");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
@@ -119,7 +119,7 @@ export function ServiceSelector({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function PriceEstimation() {
|
export function PriceEstimation() {
|
||||||
const [selected] = comms.var("rubgram.services");
|
const [selected] = shared.state("rubgram.services");
|
||||||
const [label, setLabel] = useState("ทั้งหมด 0 บาท");
|
const [label, setLabel] = useState("ทั้งหมด 0 บาท");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -202,7 +202,7 @@ export function Countdown({ time }: { time: Date }) {
|
|||||||
|
|
||||||
export function CancelButton({ sid }: { sid: string }) {
|
export function CancelButton({ sid }: { sid: string }) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [, setServices] = comms.var("rubgram.services");
|
const [, setServices] = shared.state("rubgram.services");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
Fragment,
|
Fragment,
|
||||||
type ReactNode,
|
type ReactNode,
|
||||||
type RefObject,
|
type RefObject,
|
||||||
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
useLayoutEffect,
|
useLayoutEffect,
|
||||||
@@ -47,7 +48,7 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { tlPlacements, tlState } from "@/lib/api";
|
import { tlPlacements, tlState } from "@/lib/api";
|
||||||
import { comms } from "@/lib/comms";
|
import { shared } from "@/lib/comms";
|
||||||
import type {
|
import type {
|
||||||
characters,
|
characters,
|
||||||
tierlistBadges,
|
tierlistBadges,
|
||||||
@@ -122,7 +123,7 @@ export function TierList({
|
|||||||
download: boolean;
|
download: boolean;
|
||||||
ev: "unknown" | "connecting" | "ready";
|
ev: "unknown" | "connecting" | "ready";
|
||||||
}>({ upload: false, download: false, ev: "unknown" });
|
}>({ upload: false, download: false, ev: "unknown" });
|
||||||
const [updated] = comms.var("updated");
|
const [updated] = shared.state("updated");
|
||||||
|
|
||||||
//#region error handling
|
//#region error handling
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -136,17 +137,19 @@ export function TierList({
|
|||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region states fetching
|
//#region states fetching
|
||||||
useEffect(() => {
|
const fetchStates = useCallback(async () => {
|
||||||
async function fetchStates() {
|
setEvStatus((x) => ({ ...x, download: true }));
|
||||||
setEvStatus((x) => ({ ...x, download: true }));
|
setStates(
|
||||||
setStates(
|
await fetch(`/api/tl/${version.id}/states`)
|
||||||
await fetch(`/api/tl/${version.id}/states`)
|
.then((r) => r.json())
|
||||||
.then((r) => r.json())
|
.finally(() => setEvStatus((x) => ({ ...x, download: false }))),
|
||||||
.finally(() => setEvStatus((x) => ({ ...x, download: false }))),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
fetchStates();
|
|
||||||
}, [version.id]);
|
}, [version.id]);
|
||||||
|
// Reload after connection restored
|
||||||
|
shared.signal("sync", fetchStates);
|
||||||
|
useEffect(() => {
|
||||||
|
fetchStates();
|
||||||
|
}, [fetchStates]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setEvStatus((x) => ({ ...x, ev: "connecting" }));
|
setEvStatus((x) => ({ ...x, ev: "connecting" }));
|
||||||
|
|||||||
+11
-9
@@ -1,13 +1,13 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ProgressProvider } from "@bprogress/next/app";
|
import { ProgressProvider } from "@bprogress/next/app";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { use, useEffect, useRef, useState } from "react";
|
||||||
import { useKey } from "react-use";
|
import { useKey } from "react-use";
|
||||||
import ReconnectingEventSource from "reconnecting-eventsource";
|
import ReconnectingEventSource from "reconnecting-eventsource";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { stringify } from "yaml";
|
import { stringify } from "yaml";
|
||||||
import { CdnChooserProvider } from "@/components/chooser";
|
import { CdnChooserProvider } from "@/components/chooser";
|
||||||
import CommsProvider, { comms } from "@/lib/comms";
|
import IccProvider, { IccContext, shared } from "@/lib/comms";
|
||||||
|
|
||||||
export default function Providers({ children }: { children: React.ReactNode }) {
|
export default function Providers({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
@@ -18,7 +18,7 @@ export default function Providers({ children }: { children: React.ReactNode }) {
|
|||||||
shallowRouting
|
shallowRouting
|
||||||
>
|
>
|
||||||
<CdnChooserProvider>
|
<CdnChooserProvider>
|
||||||
<CommsProvider>{children}</CommsProvider>
|
<IccProvider>{children}</IccProvider>
|
||||||
</CdnChooserProvider>
|
</CdnChooserProvider>
|
||||||
</ProgressProvider>
|
</ProgressProvider>
|
||||||
);
|
);
|
||||||
@@ -26,10 +26,11 @@ export default function Providers({ children }: { children: React.ReactNode }) {
|
|||||||
|
|
||||||
export function VersionCheck({ headless = false }: { headless?: boolean }) {
|
export function VersionCheck({ headless = false }: { headless?: boolean }) {
|
||||||
const ver = useRef<string>("");
|
const ver = useRef<string>("");
|
||||||
const [debug, setDebug] = comms.var("debug");
|
const { emit } = use(IccContext);
|
||||||
const [, setConnected] = comms.var("connected");
|
const [debug, setDebug] = shared.state("debug");
|
||||||
const [, setUpdated] = comms.var("updated");
|
const [, setConnected] = shared.state("connected");
|
||||||
const [debugData] = comms.raw();
|
const [, setUpdated] = shared.state("updated");
|
||||||
|
const [debugData] = shared.raw();
|
||||||
const [readyForUpdate, setReadyForUpdate] = useState(false);
|
const [readyForUpdate, setReadyForUpdate] = useState(false);
|
||||||
|
|
||||||
useKey("F2", () => {
|
useKey("F2", () => {
|
||||||
@@ -40,7 +41,7 @@ export function VersionCheck({ headless = false }: { headless?: boolean }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!readyForUpdate && window.location.hash === "#update") {
|
if (!readyForUpdate && window.location.hash === "#update") {
|
||||||
setUpdated(true);
|
setUpdated(true);
|
||||||
toast.success("คุณอยู่ในเวอร์ชั่นล่าสุดแล้ว");
|
setTimeout(() => toast.success("คุณอยู่ในเวอร์ชั่นล่าสุดแล้ว"), 1000);
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
url.hash = "";
|
url.hash = "";
|
||||||
window.history.replaceState("", "", url);
|
window.history.replaceState("", "", url);
|
||||||
@@ -49,6 +50,7 @@ export function VersionCheck({ headless = false }: { headless?: boolean }) {
|
|||||||
const es = new ReconnectingEventSource(`/api/active`, {});
|
const es = new ReconnectingEventSource(`/api/active`, {});
|
||||||
es.addEventListener("version", (d) => {
|
es.addEventListener("version", (d) => {
|
||||||
setConnected(true);
|
setConnected(true);
|
||||||
|
emit("sync");
|
||||||
const newVersion = JSON.parse(d.data);
|
const newVersion = JSON.parse(d.data);
|
||||||
const version = ver.current;
|
const version = ver.current;
|
||||||
if (!version) console.log("Client version:", newVersion);
|
if (!version) console.log("Client version:", newVersion);
|
||||||
@@ -84,7 +86,7 @@ export function VersionCheck({ headless = false }: { headless?: boolean }) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
return () => es.close();
|
return () => es.close();
|
||||||
}, [headless, setConnected, setUpdated, readyForUpdate]);
|
}, [headless, setConnected, setUpdated, readyForUpdate, emit]);
|
||||||
|
|
||||||
return debug ? (
|
return debug ? (
|
||||||
<div className="absolute bottom-0 right-0 m-2 px-1 rounded flex flex-col gap-2 bg-card border active:pointer-events-none opacity-20 hover:opacity-100 transition-opacity">
|
<div className="absolute bottom-0 right-0 m-2 px-1 rounded flex flex-col gap-2 bg-card border active:pointer-events-none opacity-20 hover:opacity-100 transition-opacity">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Kanit } from "next/font/google";
|
import { Kanit } from "next/font/google";
|
||||||
import { Watcher } from "@/app/(ui)/artifact/admin/client";
|
import { Watcher } from "@/app/(ui)/rubgram/admin/client";
|
||||||
import { VersionCheck } from "@/app/client";
|
import { VersionCheck } from "@/app/client";
|
||||||
import AutoFitText from "@/components/fit";
|
import AutoFitText from "@/components/fit";
|
||||||
import { getArtifactConfig } from "@/lib/api";
|
import { getArtifactConfig } from "@/lib/api";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useSentryToolbar } from "@sentry/toolbar";
|
import { useSentryToolbar } from "@sentry/toolbar";
|
||||||
import { comms } from "@/lib/comms";
|
import { shared } from "@/lib/comms";
|
||||||
|
|
||||||
export function SentryDevToolbar() {
|
export function SentryDevToolbar() {
|
||||||
const [debug] = comms.var("debug");
|
const [debug] = shared.state("debug");
|
||||||
|
|
||||||
useSentryToolbar({
|
useSentryToolbar({
|
||||||
// Remember to conditionally enable the Toolbar.
|
// Remember to conditionally enable the Toolbar.
|
||||||
|
|||||||
@@ -8,7 +8,12 @@ Sentry.init({
|
|||||||
dsn: "https://9a918fb166b0abde6cbb698191ac61c9@o4510549315420160.ingest.us.sentry.io/4510549315616768",
|
dsn: "https://9a918fb166b0abde6cbb698191ac61c9@o4510549315420160.ingest.us.sentry.io/4510549315616768",
|
||||||
|
|
||||||
// Add optional integrations for additional features
|
// Add optional integrations for additional features
|
||||||
integrations: [Sentry.replayIntegration()],
|
integrations: [
|
||||||
|
Sentry.replayIntegration({
|
||||||
|
maskAllText: false,
|
||||||
|
blockAllMedia: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
|
||||||
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
|
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
|
||||||
tracesSampleRate: 1,
|
tracesSampleRate: 1,
|
||||||
|
|||||||
+50
-35
@@ -9,7 +9,7 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
type WebsiteComms = Partial<{
|
type SharedStates = Partial<{
|
||||||
manual: boolean;
|
manual: boolean;
|
||||||
debug: boolean;
|
debug: boolean;
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
@@ -18,24 +18,30 @@ type WebsiteComms = Partial<{
|
|||||||
_raw: Record<string, unknown>;
|
_raw: Record<string, unknown>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
type WebsiteSignals = { beforeSubmit?: undefined } & WebsiteComms;
|
type SharedSignals = {
|
||||||
|
beforeSubmit?: undefined;
|
||||||
|
sync?: undefined;
|
||||||
|
} & SharedStates;
|
||||||
|
|
||||||
type WebsiteCommsMutator = {
|
type IccMutator = {
|
||||||
get<K extends keyof WebsiteComms>(key: K): WebsiteComms[K];
|
get<K extends keyof SharedStates>(key: K): SharedStates[K];
|
||||||
set<K extends keyof WebsiteComms>(key: K, value: WebsiteComms[K]): void;
|
set<K extends keyof SharedStates>(key: K, value: SharedStates[K]): void;
|
||||||
on<K extends keyof WebsiteSignals>(
|
on<K extends keyof SharedSignals>(
|
||||||
key: K,
|
key: K,
|
||||||
listener: (value: WebsiteSignals[K]) => void,
|
listener: (value: SharedSignals[K]) => void,
|
||||||
): () => void;
|
): () => void;
|
||||||
off<K extends keyof WebsiteSignals>(
|
off<K extends keyof SharedSignals>(
|
||||||
key: K,
|
key: K,
|
||||||
listener: (value: WebsiteSignals[K]) => void,
|
listener: (value: SharedSignals[K]) => void,
|
||||||
|
): void;
|
||||||
|
emit<K extends keyof SharedSignals>(
|
||||||
|
key: K,
|
||||||
|
...args: SharedSignals[K] extends undefined ? [] : [value: SharedSignals[K]]
|
||||||
): void;
|
): void;
|
||||||
emit<K extends keyof WebsiteSignals>(key: K, value: WebsiteSignals[K]): void;
|
|
||||||
active: boolean;
|
active: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CommsContext = createContext<WebsiteCommsMutator>({
|
export const IccContext = createContext<IccMutator>({
|
||||||
get: () => {
|
get: () => {
|
||||||
console.error("ICC used outside of provider.");
|
console.error("ICC used outside of provider.");
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -47,18 +53,24 @@ export const CommsContext = createContext<WebsiteCommsMutator>({
|
|||||||
active: false,
|
active: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function CommsProvider({
|
export default function IccProvider({
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const storeRef = useRef<WebsiteComms>({});
|
const storeRef = useRef<SharedStates>({});
|
||||||
|
|
||||||
const listeners = useRef<{
|
const listeners = useRef<{
|
||||||
[K in keyof WebsiteSignals]?: Array<(value: WebsiteSignals[K]) => void>;
|
[K in keyof SharedSignals]?: Array<
|
||||||
|
(
|
||||||
|
...args: SharedSignals[K] extends undefined
|
||||||
|
? []
|
||||||
|
: [value: SharedSignals[K]]
|
||||||
|
) => void
|
||||||
|
>;
|
||||||
}>({});
|
}>({});
|
||||||
|
|
||||||
const contextValue = useRef<WebsiteCommsMutator>({
|
const contextValue = useRef<IccMutator>({
|
||||||
get: (k) => storeRef.current[k],
|
get: (k) => storeRef.current[k],
|
||||||
set(k, v) {
|
set(k, v) {
|
||||||
storeRef.current = { ...storeRef.current, [k]: v };
|
storeRef.current = { ...storeRef.current, [k]: v };
|
||||||
@@ -68,7 +80,9 @@ export default function CommsProvider({
|
|||||||
const { _raw, ...withoutRaw } = storeRef.current;
|
const { _raw, ...withoutRaw } = storeRef.current;
|
||||||
contextValue.current.set("_raw", withoutRaw);
|
contextValue.current.set("_raw", withoutRaw);
|
||||||
}
|
}
|
||||||
contextValue.current.emit(k, v);
|
contextValue.current.emit(
|
||||||
|
...([k, v] as unknown as Parameters<typeof contextValue.current.emit>),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
on(k, listener) {
|
on(k, listener) {
|
||||||
// @ts-expect-error typescript screams for some impossible static type conflict
|
// @ts-expect-error typescript screams for some impossible static type conflict
|
||||||
@@ -81,26 +95,27 @@ export default function CommsProvider({
|
|||||||
(l) => l !== listener,
|
(l) => l !== listener,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
emit: (k, value) => {
|
emit(k, value = undefined) {
|
||||||
// biome-ignore lint/suspicious/useIterableCallbackReturn: l returns void
|
(listeners.current[k] || []).map((l) =>
|
||||||
(listeners.current[k] || []).forEach((l) => l(value));
|
l(...([value] as Parameters<typeof l>)),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
active: true,
|
active: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
return <CommsContext value={contextValue.current}>{children}</CommsContext>;
|
return <IccContext value={contextValue.current}>{children}</IccContext>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const comms = {
|
export const shared = {
|
||||||
var<K extends keyof WebsiteComms>(
|
state<K extends keyof SharedStates>(
|
||||||
name: K,
|
name: K,
|
||||||
): [
|
): [
|
||||||
WebsiteComms[K],
|
SharedStates[K],
|
||||||
(
|
(
|
||||||
value: WebsiteComms[K] | ((prev: WebsiteComms[K]) => WebsiteComms[K]),
|
value: SharedStates[K] | ((prev: SharedStates[K]) => SharedStates[K]),
|
||||||
) => void,
|
) => void,
|
||||||
] {
|
] {
|
||||||
const comms = use(CommsContext);
|
const comms = use(IccContext);
|
||||||
if (!comms.active) {
|
if (!comms.active) {
|
||||||
console.warn("ICC used outside of provider. Using local state instead.");
|
console.warn("ICC used outside of provider. Using local state instead.");
|
||||||
}
|
}
|
||||||
@@ -108,45 +123,45 @@ export const comms = {
|
|||||||
comms.active ? comms.get(name) : undefined,
|
comms.active ? comms.get(name) : undefined,
|
||||||
);
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function listener(value: WebsiteComms[typeof name]) {
|
function listener(value: SharedStates[typeof name]) {
|
||||||
setState(value);
|
setState(value);
|
||||||
}
|
}
|
||||||
return comms.on(name, listener);
|
return comms.on(name, listener);
|
||||||
}, [name]);
|
}, [comms, name]);
|
||||||
|
|
||||||
const mutator = useCallback(
|
const mutator = useCallback(
|
||||||
(
|
(
|
||||||
value:
|
value:
|
||||||
| WebsiteComms[typeof name]
|
| SharedStates[typeof name]
|
||||||
| ((prev: WebsiteComms[typeof name]) => WebsiteComms[typeof name]),
|
| ((prev: SharedStates[typeof name]) => SharedStates[typeof name]),
|
||||||
) => {
|
) => {
|
||||||
const v = typeof value === "function" ? value(comms.get(name)) : value;
|
const v = typeof value === "function" ? value(comms.get(name)) : value;
|
||||||
if (comms.active) comms.set(name, v);
|
if (comms.active) comms.set(name, v);
|
||||||
else setState(v);
|
else setState(v);
|
||||||
},
|
},
|
||||||
[name],
|
[comms, name],
|
||||||
);
|
);
|
||||||
|
|
||||||
return [state, mutator] as const;
|
return [state, mutator] as const;
|
||||||
},
|
},
|
||||||
event<K extends keyof WebsiteSignals>(
|
signal<K extends keyof SharedSignals>(
|
||||||
key: K,
|
key: K,
|
||||||
listener: (ev: WebsiteSignals[K]) => void,
|
listener: (ev: SharedSignals[K]) => void,
|
||||||
) {
|
) {
|
||||||
const comms = use(CommsContext);
|
const comms = use(IccContext);
|
||||||
const listenerRef = useRef(listener);
|
const listenerRef = useRef(listener);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
listenerRef.current = listener;
|
listenerRef.current = listener;
|
||||||
}, [listener]);
|
}, [listener]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const stableListener = (value: WebsiteSignals[K]) => {
|
const stableListener = (value: SharedSignals[K]) => {
|
||||||
listenerRef.current(value);
|
listenerRef.current(value);
|
||||||
};
|
};
|
||||||
return comms.on(key, stableListener);
|
return comms.on(key, stableListener);
|
||||||
}, [comms, key]);
|
}, [comms, key]);
|
||||||
},
|
},
|
||||||
raw() {
|
raw() {
|
||||||
return [comms.var("_raw")[0]];
|
return [shared.state("_raw")[0]];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user