prasi-bun/app/web/src/nova/ed/panel/popup/code/code.tsx

445 lines
15 KiB
TypeScript

import { useEffect } from "react";
import { useGlobal, useLocal } from "web-utils";
import { isLocalhost } from "../../../../../utils/ui/is-localhost";
import { Loading } from "../../../../../utils/ui/loading";
import { Modal } from "../../../../../utils/ui/modal";
import { Popover } from "../../../../../utils/ui/popover";
import { Tooltip } from "../../../../../utils/ui/tooltip";
import { EDGlobal } from "../../../logic/ed-global";
import {
iconChevronDown,
iconDownload,
iconNewTab,
iconScrollOff,
iconScrollOn,
iconUpload,
} from "./icons";
import { CodeNameItem, CodeNameList } from "./name-list";
export const code = {
mode: "" as "" | "old" | "new",
};
export const EdPopCode = () => {
const p = useGlobal(EDGlobal, "EDITOR");
useEffect(() => {
(async () => {
if (code.mode === "new") {
p.ui.popup.code.init = true;
}
})();
}, [p.ui.popup.code.open]);
useEffect(() => {
if (code.mode === "" && p.site.id) {
_db.code.findFirst({ where: { id_site: p.site.id } }).then((e) => {
code.mode = e ? "new" : "old";
if (localStorage.vsc_opened === "yes") {
localStorage.removeItem("vsc_opened");
p.ui.popup.code.open = true;
}
p.render();
});
}
}, [p.site.id]);
if (p.ui.popup.code.startup_status === "init" && p.sync?.code.action) {
p.ui.popup.code.startup_status = "loading";
p.sync.code
.action({ type: "startup-check", site_id: p.site.id })
.then((res) => {
if (res) {
if (res.type === "startup-check") {
p.ui.popup.code.startup_status = res.status;
p.render();
}
}
});
}
return (
<Modal
fade={false}
open={p.ui.popup.code.open}
onOpenChange={(open) => {
localStorage.removeItem("vsc_opened");
if (!open) {
p.ui.popup.code.startup_status = "init";
p.ui.popup.code.open = false;
p.render();
}
}}
>
<div
className={cx("bg-white select-none fixed inset-[50px] bottom-0 flex")}
>
{!code.mode && <Loading note="checking-version" backdrop={false} />}
{code.mode === "new" && <CodeBody />}
{code.mode === "old" && (
<div className="flex items-center justify-center flex-col flex-1">
<div>This site still using old code</div>
<div
onClick={() => {
if (
confirm(
"Old code will not load, are you sure want to upgrade ?"
)
) {
code.mode = "new";
_db.code.create({
data: { id_site: p.site.id, name: "site" },
});
p.ui.popup.code.open = false;
p.render();
}
}}
className="border border-blue-500 cursor-pointer bg-blue-100 p-2 hover:bg-blue-200"
>
Upgrade to New Code Project
</div>
<div className="text-xs py-2">
Warning: old code will not load once upgraded.
</div>
</div>
)}
</div>
</Modal>
);
};
const CodeBody = () => {
const p = useGlobal(EDGlobal, "EDITOR");
const local = useLocal({
namePicker: false,
codeAssign: false,
});
const vscode_url = isLocalhost()
? "http://localhost:3000?"
: "https://prasi-vsc.avolut.com/?tkn=prasi&";
const code_mode = p.site.code.mode;
return (
<div className="relative w-full h-full flex flex-col">
<div className="border-b flex justify-between h-[40px] items-stretch">
<div className="flex items-stretch">
{/* <Popover
placement="bottom"
offset={0}
arrow={false}
backdrop={false}
content={
<CodeNameList
onPick={async (e) => {
local.namePicker = false;
p.ui.popup.code.name = e.name;
p.render();
}}
/>
}
popoverClassName="bg-white shadow-md"
className={cx(
"flex items-center px-2 w-[200px] hover:bg-blue-50 space-x-1",
"cursor-pointer justify-between border-r "
)}
open={local.namePicker}
onOpenChange={(open) => {
local.namePicker = open;
local.render();
}}
>
<div className="capitalize overflow-ellipsis flex-1 flex items-center space-x-2">
<CodeNameItem name={p.ui.popup.code.name} />
</div>
<div
dangerouslySetInnerHTML={{
__html: iconChevronDown,
}}
></div>
</Popover> */}
{/* <Tooltip
content="STDOUT Log"
delay={0}
placement="bottom"
className={cx(
"flex items-stretch relative border-l",
p.ui.popup.code.error && "bg-red-500 text-white"
)}
onClick={() => {
p.ui.popup.code.show_log = !p.ui.popup.code.show_log;
p.render();
}}
>
{p.ui.popup.code.show_log && (
<div className="absolute bottom-[-4px] left-0 right-[1px] h-[5px] bg-white"></div>
)}
<div
className={cx(
"border-r flex text-center items-center hover:bg-blue-50 cursor-pointer px-2 transition-all",
p.ui.popup.code.loading
? "border-b-2 border-b-orange-400"
: "border-b-2 border-b-transparent"
)}
dangerouslySetInnerHTML={{
__html: p.ui.popup.code.loading ? iconLog : iconLoading,
}}
></div>
</Tooltip> */}
{p.ui.popup.code.startup_status !== "disabled" && (
<Tooltip
content={`Startup Script: ${p.ui.popup.code.startup_status}`}
className={cx("flex items-stretch relative border-r ")}
delay={0}
placement="bottom"
>
{["loading", "init"].includes(p.ui.popup.code.startup_status) ? (
<div
className={cx(
"flex text-center items-center hover:bg-blue-50 cursor-pointer px-2 transition-all"
)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className="lucide lucide-hourglass"
>
<path d="M5 22h14" />
<path d="M5 2h14" />
<path d="M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22" />
<path d="M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2" />
</svg>
</div>
) : (
<div
className={cx(
"flex text-center items-center hover:bg-blue-50 cursor-pointer px-2 transition-all",
p.ui.popup.code.startup_status === "running"
? "border-b-2 border-b-green-700 bg-green-50"
: "border-b-2 border-b-transparent"
)}
dangerouslySetInnerHTML={{
__html: p.ui.popup.code.startup_status
? iconScrollOn
: iconScrollOff,
}}
onClick={() => {
if (
p.ui.popup.code.startup_status === "stopped" &&
p.sync
) {
p.ui.popup.code.startup_status = "loading";
p.render();
p.sync.code
.action({ type: "startup-run", site_id: p.site.id })
.then(() => {
p.sync?.code
.action({
type: "startup-check",
site_id: p.site.id,
})
.then((res) => {
if (res) {
if (res.type === "startup-check") {
p.ui.popup.code.startup_status = res.status;
p.render();
}
}
});
});
} else {
p.ui.popup.code.startup_status = "loading";
p.render();
p.sync?.code
.action({ type: "startup-stop", site_id: p.site.id })
.then(() => {
p.sync?.code
.action({
type: "startup-check",
site_id: p.site.id,
})
.then((res) => {
if (res) {
if (res.type === "startup-check") {
p.ui.popup.code.startup_status = res.status;
p.render();
}
}
});
});
}
}}
></div>
)}
</Tooltip>
)}
<Tooltip
content={`Upload zip, will overwrite files.`}
className={cx(
"flex items-stretch relative cursor-pointer hover:bg-blue-50 "
)}
delay={0}
placement="bottom"
>
<input
type="file"
className="w-full h-full absolute inset-0 opacity-0 cursor-pointer text-[0px]"
></input>
<div
className={cx(
"border-r flex text-center items-center cursor-pointer px-2 transition-all pointer-events-none"
)}
dangerouslySetInnerHTML={{
__html: iconUpload,
}}
></div>
</Tooltip>
<Tooltip
content={`Download zip, excluding node_modules.`}
className={cx(
"flex items-stretch relative cursor-pointer hover:bg-blue-50 "
)}
delay={0}
placement="bottom"
>
<div
className={cx(
"border-r flex text-center items-center cursor-pointer px-2 transition-all cursor-pointer"
)}
dangerouslySetInnerHTML={{
__html: iconDownload,
}}
></div>
</Tooltip>
<Tooltip
content="Open in new tab"
delay={0}
placement="bottom"
className={cx("flex items-stretch relative")}
onClick={() => {
window.open(`${vscode_url}folder=/site/${p.site.id}/site/src`);
}}
>
<div
className={cx(
"border-r flex text-center items-center hover:bg-blue-50 cursor-pointer px-2 transition-all",
"border-b-2 border-b-transparent"
)}
dangerouslySetInnerHTML={{ __html: iconNewTab }}
></div>
</Tooltip>
</div>
<div className="flex items-center">
{code_mode === "vsc" && (
<div
className="flex items-center p-[2px] px-2 mr-2 cursor-pointer text-[11px] space-x-1 hover:bg-blue-100 hover:border-slate-200 border border-transparent transition-all"
onClick={async () => {
if (
confirm(
"Are you sure want to turn off VSCode?\nThis will enable old npm module"
)
) {
localStorage.vsc_opened = "yes";
await _db.site.update({
where: { id: p.site.id },
data: { code_mode: "old" },
});
location.reload();
}
}}
>
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.5 4C8.567 4 7 5.567 7 7.5C7 9.433 8.567 11 10.5 11C12.433 11 14 9.433 14 7.5C14 5.567 12.433 4 10.5 4ZM7.67133 11C6.65183 10.175 6 8.91363 6 7.5C6 6.08637 6.65183 4.82498 7.67133 4H4.5C2.567 4 1 5.567 1 7.5C1 9.433 2.567 11 4.5 11H7.67133ZM0 7.5C0 5.01472 2.01472 3 4.5 3H10.5C12.9853 3 15 5.01472 15 7.5C15 9.98528 12.9853 12 10.5 12H4.5C2.01472 12 0 9.98528 0 7.5Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
></path>
</svg>
<div>Turn off VSCode</div>
</div>
)}
</div>
</div>
{p.ui.popup.code.show_log && (
<div className="h-[150px] overflow-auto font-mono p-2 text-xs whitespace-pre-wrap border-b">
<div>{p.ui.popup.code.log || "stdout is empty..."}</div>
</div>
)}
{code_mode === "vsc" ? (
<div className="flex flex-1 relative">
{!p.ui.popup.code.open ? (
<Loading backdrop={false} />
) : (
<>
<iframe
className="flex flex-1 absolute inset-0 w-full h-full z-10"
src={`${vscode_url}folder=/site/${p.site.id}/site/src`}
></iframe>
<div className="flex flex-1 absolute inset-0 z-0 items-center justify-center">
Loading VSCode...
</div>
</>
)}
</div>
) : (
<div className="flex flex-col flex-1 relative items-center justify-center space-y-2">
<div className="text-xs">VSCode is turned off</div>
<div
className="flex items-center p-2 cursor-pointer text-xs font-mono space-x-1 bg-green-700 text-white hover:opacity-40 transition-all"
onClick={async () => {
if (
confirm(
"Are you sure want to turn on VSCode?\nThis will disable old npm module (you can enable it again later)."
)
) {
localStorage.vsc_opened = "yes";
await _db.site.update({
where: { id: p.site.id },
data: { code_mode: "vsc" },
});
location.reload();
}
}}
>
<div>Turn on VSCode</div>
</div>
</div>
)}
{(local.namePicker || local.codeAssign) && (
<div
className="fixed inset-0 z-50"
onClick={() => {
local.namePicker = false;
local.codeAssign = false;
local.render();
}}
></div>
)}
</div>
);
};