mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 20:35:06 -05:00
fix: biome, test mode selection
This commit is contained in:
parent
2078e5fba1
commit
bddf5f1528
@ -1,5 +1,5 @@
|
||||
import { ServerProvider } from "@/components/feat/server-page/server-provider";
|
||||
import { ServerResponse } from "@/lib/types/mh-server";
|
||||
import type { ServerResponse } from "@/lib/types/mh-server";
|
||||
import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
|
||||
@ -46,7 +46,6 @@ import {
|
||||
ArrowLeft,
|
||||
Braces,
|
||||
EllipsisVertical,
|
||||
File,
|
||||
FileCode,
|
||||
Pencil,
|
||||
Trash,
|
||||
@ -76,7 +75,7 @@ export default function ServerListModificationFrame() {
|
||||
{files.map((c, i) => (
|
||||
<Link
|
||||
href={`/servers/embedded/sl-modification-frame/file/${c.name}`}
|
||||
className="w-full py-1 px-2 rounded-xl flex items-center gap-1 justify-between hover:bg-slate-100"
|
||||
className="w-full py-1 px-2 rounded-xl flex items-center gap-1 justify-between hover:bg-slate-100 dark:hover:bg-zinc-700/30"
|
||||
key={c.name}
|
||||
>
|
||||
<span className="flex items-center gap-1">
|
||||
@ -88,7 +87,7 @@ export default function ServerListModificationFrame() {
|
||||
<DropdownMenuTrigger>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
className="flex items-center justify-center hover:bg-slate-200"
|
||||
className="flex items-center justify-center hover:bg-slate-200 dark:hover:bg-zinc-700/60"
|
||||
size="square-sm"
|
||||
>
|
||||
<EllipsisVertical
|
||||
|
||||
@ -225,6 +225,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loading-shimmer {
|
||||
0% {
|
||||
background-position: 125% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -150% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-shimmer {
|
||||
-webkit-text-fill-color: transparent;
|
||||
animation-duration: 2.5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-name: loading-shimmer;
|
||||
background: var(--color-muted-foreground)
|
||||
linear-gradient(
|
||||
90deg,
|
||||
var(--color-muted-foreground) 0%,
|
||||
var(--color-shadcn-primary) 50%,
|
||||
var(--color-muted-foreground) 100%
|
||||
);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 50% 200%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/*
|
||||
The default border color has changed to `currentColor` in Tailwind CSS v4,
|
||||
so we've added these compatibility styles to make sure everything still
|
||||
|
||||
@ -32,11 +32,11 @@ import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { ModificationFrame } from "./modification-frame";
|
||||
|
||||
export function ModificationButton() {
|
||||
export function ModificationButton({disabled}: {disabled?: boolean}) {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<Button>Filters & Sorting</Button>
|
||||
<Button disabled={disabled}>Filters & Sorting</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent className="p-0 h-[600px] w-[1000px] !max-w-[800px] overflow-x-hidden">
|
||||
|
||||
@ -36,13 +36,14 @@ import { Separator } from "@/components/ui/separator";
|
||||
import { Statistics } from "./statistics";
|
||||
import InfiniteScroll from "react-infinite-scroll-component";
|
||||
import { useInfiniteScrolling } from "@/lib/hooks/use-infinite-scrolling";
|
||||
import { useMHSFServer } from "@/lib/hooks/use-mhsf-multiple";
|
||||
import { ModificationButton } from "./modification/modification-button";
|
||||
import { useFilters } from "@/lib/hooks/use-filters";
|
||||
import { ServerTestModeSelector } from "./server-test-mode-selector";
|
||||
|
||||
export function ServerList() {
|
||||
const { servers, loading, serverCount, playerCount } = useServers();
|
||||
const { filteredData } = useFilters(servers);
|
||||
const { filteredData, testModeEnabled, testModeLoading, testModeStatus } =
|
||||
useFilters(servers);
|
||||
const { itemsLength, fetchMoreData, hasMoreData, data } =
|
||||
useInfiniteScrolling(filteredData);
|
||||
|
||||
@ -67,7 +68,14 @@ export function ServerList() {
|
||||
<h1 className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl">
|
||||
Servers
|
||||
</h1>
|
||||
<ModificationButton />
|
||||
<div className="flex items-center">
|
||||
<ModificationButton disabled={testModeEnabled} />
|
||||
<ServerTestModeSelector
|
||||
testModeStatus={testModeStatus}
|
||||
testModeEnabled={testModeEnabled}
|
||||
testModeLoading={testModeLoading}
|
||||
/>
|
||||
</div>
|
||||
<InfiniteScroll
|
||||
dataLength={itemsLength}
|
||||
next={fetchMoreData}
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
import { AnimatedText } from "@/components/ui/animated-text";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { useQueryState } from "nuqs";
|
||||
|
||||
export function ServerTestModeSelector({
|
||||
testModeStatus,
|
||||
testModeLoading,
|
||||
testModeEnabled,
|
||||
}: {
|
||||
testModeStatus: string;
|
||||
testModeLoading: boolean;
|
||||
testModeEnabled: boolean;
|
||||
}) {
|
||||
const [tm] = useQueryState("tm", { defaultValue: "" });
|
||||
|
||||
if (testModeEnabled || tm !== "")
|
||||
return (
|
||||
<div className="pl-5 flex items-center gap-1">
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<span className="relative flex size-2.5 pt-[1px] items-center cursor-pointer">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-orange-400 opacity-75" />
|
||||
<span className="relative inline-flex size-2.5 rounded-full bg-orange-500" />
|
||||
</span></TooltipTrigger>
|
||||
<TooltipContent>Test mode was enabled.</TooltipContent>
|
||||
</Tooltip>
|
||||
<AnimatedText
|
||||
className="text-muted-foreground top-[2.5px] left-[6px] min-w-[70vw]"
|
||||
text={testModeStatus}
|
||||
glimmer
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -6,9 +6,10 @@ import { cn } from "@/lib/utils";
|
||||
interface AnimatedTextProps {
|
||||
text: string;
|
||||
className?: string;
|
||||
glimmer?: boolean;
|
||||
}
|
||||
|
||||
export function AnimatedText({ text, className }: AnimatedTextProps) {
|
||||
export function AnimatedText({ text, className, glimmer }: AnimatedTextProps) {
|
||||
const [currentText, setCurrentText] = useState(text);
|
||||
|
||||
useEffect(() => {
|
||||
@ -24,7 +25,9 @@ export function AnimatedText({ text, className }: AnimatedTextProps) {
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
exit={{ y: -20, opacity: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className={cn("absolute left-0", className)}
|
||||
className={cn("absolute left-0", className, {
|
||||
"loading-shimmer": glimmer,
|
||||
})}
|
||||
>
|
||||
{currentText}
|
||||
</motion.span>
|
||||
|
||||
@ -39,6 +39,8 @@ export function useFilters(data: OnlineServer[]) {
|
||||
const [filteredData, setFilteredData] = useState<OnlineServer[]>(data);
|
||||
const [t] = useQueryState("tm");
|
||||
const [testModeEnabled, setTestModeEnabled] = useState(false);
|
||||
const [testModeStatus, setTestModeStatus] = useState("Haven't connected thread yet (if stuck, select the other tab, and come back)");
|
||||
const [testModeLoading, setTestModeLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (filteredData.length === 0) setFilteredData(data);
|
||||
@ -54,68 +56,91 @@ export function useFilters(data: OnlineServer[]) {
|
||||
setTestModeEnabled(true);
|
||||
const code = atob(t);
|
||||
(async () => {
|
||||
toast.info("Transpiling TypeScript...");
|
||||
setTestModeStatus("Transpiling TypeScript...");
|
||||
const startTime = Date.now();
|
||||
const { error, data: transpiledCode } = await tryCatch(
|
||||
(async () => transpileTypeScript(code))()
|
||||
);
|
||||
if (error) {
|
||||
toast.error(
|
||||
setTestModeStatus(
|
||||
"Failed to transpile TypeScript! Error: " + error.message
|
||||
);
|
||||
setTestModeLoading(false);
|
||||
return;
|
||||
}
|
||||
if (transpiledCode === null) {
|
||||
toast.error("Cannot continue.");
|
||||
setTestModeStatus("Cannot continue.");
|
||||
setTestModeLoading(false);
|
||||
return;
|
||||
}
|
||||
console.log(
|
||||
"[MHSF Filters] Transpiled TypeScript:",
|
||||
transpiledCode ?? ""
|
||||
);
|
||||
toast.info("Generating function...");
|
||||
const functionBody = transpiledCode.match(
|
||||
/function\s+filter\s*\([^)]*\)\s*\{([\s\S]*)\}/
|
||||
)?.[1];
|
||||
setTestModeStatus("Generating function...");
|
||||
if (
|
||||
!transpiledCode.includes("export default") &&
|
||||
!transpiledCode.includes("export")
|
||||
) {
|
||||
setTestModeStatus(
|
||||
"Transpiled code does not contain any export statements."
|
||||
);
|
||||
setTestModeLoading(false);
|
||||
return;
|
||||
}
|
||||
const functionBody = transpiledCode
|
||||
.replace(/export default(?!.*[;])/g, "") // Avoid replacing if followed by a semicolon
|
||||
.replace(/export(?!.*[;])/g, ""); // Avoid replacing if followed by a semicolon
|
||||
const { error: filterErr, data: filterFunc } = await tryCatch(
|
||||
(async () => new Function("server", functionBody as string))()
|
||||
(async () =>
|
||||
new Function(
|
||||
"server",
|
||||
`${functionBody}
|
||||
|
||||
return filter(server)`
|
||||
))()
|
||||
);
|
||||
if (filterErr) {
|
||||
toast.error(
|
||||
setTestModeStatus(
|
||||
`Failed to generate function! Error: ${filterErr.message}`
|
||||
);
|
||||
setTestModeLoading(false);
|
||||
return;
|
||||
}
|
||||
if (typeof filterFunc === "function") {
|
||||
toast.success("Compiled in " + (Date.now() - startTime) + "ms");
|
||||
setTestModeStatus(
|
||||
"Compiled in " + (Date.now() - startTime) + "ms"
|
||||
);
|
||||
toast.promise(
|
||||
async () => {
|
||||
let newServers = [];
|
||||
newServers = data.filter((c) => filterFunc(c));
|
||||
toast.info(
|
||||
setTestModeStatus(
|
||||
"Server count " + data.length + " -> " + newServers.length
|
||||
);
|
||||
setFilteredData(() => [...newServers]);
|
||||
setTestModeLoading(false);
|
||||
},
|
||||
{
|
||||
loading: "Manipulating data...",
|
||||
success: "Manipulated data; test mode finished!",
|
||||
error: (e) =>
|
||||
`Error while manipulating data; go back to your editor and run again. ${es}`,
|
||||
`Error while manipulating data; go back to your editor and run again. ${e}`,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
toast.error(
|
||||
setTestModeStatus(
|
||||
"Code doesn't have a 'filter' function. Cannot be tested."
|
||||
);
|
||||
toast.error(typeof filterFunc);
|
||||
setTestModeLoading(false);
|
||||
}
|
||||
})();
|
||||
}
|
||||
});
|
||||
}, [t, data]);
|
||||
|
||||
console.log(filteredData);
|
||||
console.log(filteredData, testModeStatus);
|
||||
|
||||
return { filteredData, testModeEnabled };
|
||||
return { filteredData, testModeEnabled, testModeLoading, testModeStatus };
|
||||
}
|
||||
|
||||
12
biome.json
Normal file
12
biome.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"linter": {
|
||||
"rules": {
|
||||
"style": {
|
||||
"useImportType": {
|
||||
"level": "warn"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user