import { Fragment, memo, useEffect, useRef, useState } from "react"; import { BiDotsHorizontalRounded, BiExit, BiSolidUser, BiPencil, BiLink, BiWrench, } from "react-icons/bi"; import Logo from "../assets/logo.svg"; import LogoAlternative from "../assets/fully_alternative_logo.svg"; import { // AnimatedCanvas, LoadingSpinner, DatePicker, TextPreloader, // CoolMouseBg, } from "../trash/trash-components"; import { AdminReq, classUserFRequest, RaspGet } from "../trash/req_mgr"; import { AnimatePresence, LayoutGroup, motion } from "motion/react"; import { apiFormatDate, isTimeBeforeNow } from "../trash/dengi"; import { useAtom } from "jotai"; import { API_BASE_URL } from "../App"; import { copyDXLink, ifstyle } from "../trash/helpers"; import { useWebHaptics } from "web-haptics/react"; import { DengiCaching } from "../trash/dengiCashingSystem"; /** * @param {{classIDAtom,adminAtom,setsetRoute,routeAtom,pickmeModeAtom,dengi_cash: DengiCaching}} param0 */ export function DX4App({ classIDAtom, adminAtom, setsetRoute, routeAtom, pickmeModeAtom, dengi_cash, }) { const [classId, setUserId] = useAtom(classIDAtom); const [adminId, _setAdminId] = useAtom(adminAtom); const [openUserSettings, setOpenUserSettings] = useState(false); const setRoute = useAtom(routeAtom)[1]; const [pickmeMode, setPickmeMode] = useAtom(pickmeModeAtom); const [rasp, setRasp] = useState([false, {}, 0]); const [selectedDate, setSelectedDate] = useState(new Date()); const [managing, setManaging] = useState(false); useEffect(() => { let cancelled = false; setRasp([false, {}]); const cacheName = `rasp-${classId}-${selectedDate.toDateString()}`; if (!dengi_cash.wasCached(cacheName)) { RaspGet(API_BASE_URL, classId, selectedDate).then( (d) => { if (d[0] == 1) { try { if (!cancelled) setRasp([ true, d[1]["data"] && d[1]["data"][0] ? d[1]["data"] : -322, d[1]["type"] ? d[1]["type"] : -1, ]); //console.log(d, d[1] ? d[1] : false); dengi_cash.cache(cacheName, [ d[1]["data"] && d[1]["data"][0] ? d[1]["data"] : -322, d[1]["type"] ? d[1]["type"] : -1, ]); } catch (exception) { //console.error(exception); if (!cancelled) setRasp([true, exception.toString()]); } } else { if (!cancelled) setRasp([true, "ошибка: " + d[1].toString()]); //console.log(d[1]); } }, (err) => { if (cancelled) return; setRasp([true, "ошибка: " + err]); }, ); } else { if (cancelled) return; setRasp([true, ...dengi_cash.get(cacheName)]); } return () => { cancelled = true; }; //console.log(dengi_cash); }, [selectedDate]); const [userFrNames, setUserFrNames] = useState({ admin: false, class: false, }); const lessonsEndedTriggered = useRef(false); //TODO: Auto change day v2 useEffect(() => { if ( lessonsEndedTriggered.current || typeof rasp[1] != "object" || rasp[1].lenght <= 0 || apiFormatDate(selectedDate) !== apiFormatDate(new Date()) ) return; try { if (!rasp[1].at) return; const tEnd = rasp[1].at(-1)["t-end"]; if (!/\d{1,2}:\d{2}$/.test(tEnd)) return; // not time type if (isTimeBeforeNow(tEnd)) { //console.log(true); const d = new Date(selectedDate); d.setDate(d.getDate() + 1); setSelectedDate(d); lessonsEndedTriggered.current = true; console.log("LOG: Auto change day triggired"); } } catch (err) { console.error(err); return; } }, [rasp]); useEffect(() => { (async () => { let cancelled = false; const promises = []; if (classId !== "") promises.push( dengi_cash.withCacheAsync(`userfr-${classId}`, async () => classUserFRequest(API_BASE_URL, classId), ), ); else promises.push(Promise.resolve([0])); if (adminId !== "") promises.push( dengi_cash.withCacheAsync(`admindata-${adminId}`, async () => AdminReq(API_BASE_URL, adminId), ), ); else promises.push(Promise.resolve([0])); Promise.all(promises).then(([classRes, adminRes]) => { if (cancelled) return; const classF = classRes[0] === 1 ? classRes[1].userclassname : false; const adminF = adminRes[0] === 1 ? adminRes[1].adminName : false; setUserFrNames({ admin: adminF, class: classF }); if (adminRes[0] === 1) setManaging([-1, 2].includes(adminRes[1].adminPrems)); }); return () => { cancelled = true; }; })(); }, [classId, adminId]); /** * @param {KeyboardEvent} event */ const handleKeyPress = (event) => { switch (event.key) { case "a": case "ArrowLeft": setSelectedDate((prevDate) => { const newDate = new Date(prevDate); newDate.setDate(newDate.getDate() - 1); return newDate; }); break; case "d": case "ArrowRight": setSelectedDate((prevDate) => { const newDate = new Date(prevDate); newDate.setDate(newDate.getDate() + 1); return newDate; }); break; default: break; } }; useEffect(() => { window.addEventListener("keydown", handleKeyPress); if(window['DX_OPEN_DATE']){ setSelectedDate(window['DX_OPEN_DATE']) window['DX_OPEN_DATE'] = undefined } return () => { window.removeEventListener("keydown", handleKeyPress); }; }, []); if (classId === "") { setsetRoute(setRoute, "/"); } const { trigger } = useWebHaptics(); const raspDragStartX = useRef(0); return ( <> {!openUserSettings && ( {userFrNames["class"] ? userFrNames["class"] : classId} )} { setOpenUserSettings(!openUserSettings); trigger([{ duration: 10 }, { delay: 100, duration: 10 }], { intensity: 1, }); }} onContextMenu={(e) => { e.preventDefault(); setPickmeMode(!pickmeMode); }} className={ "rounded-full " + (pickmeMode ? "lg:hover:bg-pink-400" : "lg:hover:bg-white lg:hover:fill-black") + " lg:hover:scale-105 transition cursor-pointer" } /> {adminId != "" && managing && ( { // setsetRoute(setRoute, `/editor/${apiFormatDate(selectedDate)}`); // }} href={`#/classprofile`} className={`flex items-center justify-center border p-1 gap-2 px-2 cursor-pointer rounded-xl transition-colors ${ pickmeMode ? "border-pink-400/30 text-pink-300/80 lg:hover:text-white lg:hover:border-pink-500" : "border-zinc-700 text-zinc-400 lg:hover:text-white lg:hover:border-white" } `} > управление )} {adminId != "" && ( { // setsetRoute(setRoute, `/editor/${apiFormatDate(selectedDate)}`); // }} href={ userFrNames.admin ? `#/editor/${apiFormatDate(selectedDate)}` : undefined } className={`flex items-center justify-center p-1.5 cursor-pointer border transition *:transition-colors rounded-full ${ pickmeMode ? "border-pink-400/30 lg:hover:text-white hover:lg:bg-pink-500" : "border-zinc-700 text-white lg:hover:text-black lg:hover:bg-white" } `} > {userFrNames.admin ? ( ) : ( )} )} { raspDragStartX.current = ev.clientX; }} onDragEnd={(ev) => { if (raspDragStartX.current > ev.clientX + 150) { const d = new Date(selectedDate); d.setDate(d.getDate() + 1); setSelectedDate(d); trigger([{ duration: 10 }], { intensity: 1 }); } else if (raspDragStartX.current < ev.clientX - 150) { const d = new Date(selectedDate); d.setDate(d.getDate() - 1); setSelectedDate(d); trigger([{ duration: 10 }], { intensity: 1 }); } }} > {!rasp[0] && ( )} ); } const CoolMainBg = memo(({ pickmeMode }) => { return (
); }); export const RaspView = memo(({ rasp, pickmeMode, selectedDate }) => { return ( {rasp[0] && typeof rasp[1] == "object" && ( { ev.preventDefault(); itemRedo.action(); }} variants={{ hid: { opacity: 0 }, show: { opacity: 1 }, }} initial="hid" animate="show" exit="hid" > n урока предмет каб
{rasp[2] == 2 && ( )} время
{rasp[1].map((d, i) => ( ))}
)} {typeof rasp[1] == "number" && rasp[1] === -322 && ( нет уроков )} {typeof rasp[1] == "string" && rasp[1].startsWith("ошибка") && ( {rasp[1]} )}
); }); const UserSettings = memo( ({ openUserSettings, userFrNames, setUserId, setsetRoute, setRoute, adminId, classId, pickmeMode, }) => { const [classLinkCopied, setClassLinkCopied] = useState(false); return ( <> {openUserSettings && ( {[ // icon // text // onclick // condition [ , "выход", () => { setUserId(""); setsetRoute(setRoute, "/"); }, true, ], // [ // , // "пикми режим", // () => { // setPickmeMode(!pickmeMode); // }, // true, // ], [ , <> {`админ ${userFrNames["admin"] ? userFrNames["admin"] : ""}`} {!userFrNames["admin"] && } , () => { setsetRoute(setRoute, "/adminprofile"); }, adminId != "", ], [ , classLinkCopied ? "скопировано" : "коп. ссылку", () => { copyDXLink(classId).then((a) => { if (a) { setClassLinkCopied(true); setTimeout(() => { setClassLinkCopied(false); }, 2000); } }); }, classId != "", ], ].map( (item, i) => item[3] && (
{item[0]} {item[1]}
), )}
)} ); }, ); class GlobalListener { constructor() { this.listeners = []; } listen(fn, once = false) { const wrapper = (...args) => { try { fn(...args); } finally { if (once) this.listeners = this.listeners.filter((l) => l !== wrapper); } }; this.listeners.push(wrapper); return () => { this.listeners = this.listeners.filter((l) => l !== wrapper); }; } action(...args) { this.listeners.slice().forEach((fn) => { try { fn(...args); } catch (err) { console.error(err); } }); } } const itemRedo = new GlobalListener(); const TableItem = memo(({ d }) => { const [sel, setSel] = useState(false); useEffect(() => { itemRedo.listen(() => { setSel(false); }); }, []); return ( { setSel(!sel); }} > {d["num"]} {d.subject .toString() .split("\n") .map((line, i, array) => ( {line} {i < array.length - 1 &&
}
)) || d.subject} {d.cabinet .toString() .split("\n") .map((line, i, array) => ( {line} {i < array.length - 1 &&
}
)) || d.cabinet}
{d["t-begin"]}  - {d["t-end"]}
); });