import React from "react"; import Head from "next/head"; import { useRouter } from "next/router"; import { Flex, Popover, Text } from "@mantine/core"; import styled from "styled-components"; import toast from "react-hot-toast"; import { AiOutlineCloudSync, AiOutlineCloudUpload, AiOutlineLink, AiOutlineLock, AiOutlineUnlock, } from "react-icons/ai"; import { BiSolidDockLeft } from "react-icons/bi"; import { MdOutlineCheckCircleOutline } from "react-icons/md"; import { TbTransform } from "react-icons/tb"; import { VscError, VscFeedback, VscSourceControl, VscSync, VscSyncIgnored } from "react-icons/vsc"; import { documentSvc } from "src/services/document.service"; import useConfig from "src/store/useConfig"; import useFile from "src/store/useFile"; import useGraph from "src/store/useGraph"; import useModal from "src/store/useModal"; import useUser from "src/store/useUser"; const StyledBottomBar = styled.div` position: relative; display: flex; align-items: center; justify-content: space-between; border-top: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT}; background: ${({ theme }) => theme.TOOLBAR_BG}; max-height: 27px; height: 27px; z-index: 35; padding-right: 6px; @media screen and (max-width: 320px) { display: none; } `; const StyledLeft = styled.div` display: flex; align-items: center; justify-content: left; gap: 4px; padding-left: 8px; @media screen and (max-width: 480px) { display: none; } `; const StyledRight = styled.div` display: flex; align-items: center; justify-content: right; gap: 4px; `; const StyledBottomBarItem = styled.button<{ $bg?: string }>` display: flex; align-items: center; gap: 4px; width: fit-content; margin: 0; height: 28px; padding: 4px; font-size: 12px; font-weight: 400; color: ${({ theme }) => theme.INTERACTIVE_NORMAL}; background: ${({ $bg }) => $bg}; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; &:hover:not(&:disabled) { background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); color: ${({ theme }) => theme.INTERACTIVE_HOVER}; } &:disabled { opacity: 0.6; cursor: default; } `; export const BottomBar = () => { const { query, replace } = useRouter(); const data = useFile(state => state.fileData); const user = useUser(state => state.user); const toggleLiveTransform = useConfig(state => state.toggleLiveTransform); const liveTransformEnabled = useConfig(state => state.liveTransformEnabled); const hasChanges = useFile(state => state.hasChanges); const error = useFile(state => state.error); const getContents = useFile(state => state.getContents); const setContents = useFile(state => state.setContents); const nodeCount = useGraph(state => state.nodes.length); const fileName = useFile(state => state.fileData?.name); const toggleFullscreen = useGraph(state => state.toggleFullscreen); const fullscreen = useGraph(state => state.fullscreen); const setVisible = useModal(state => state.setVisible); const setHasChanges = useFile(state => state.setHasChanges); const getFormat = useFile(state => state.getFormat); const [isPrivate, setIsPrivate] = React.useState(false); const [isUpdating, setIsUpdating] = React.useState(false); const toggleEditor = () => toggleFullscreen(!fullscreen); React.useEffect(() => { setIsPrivate(data?.private ?? true); }, [data]); const handleSaveJson = React.useCallback(async () => { if (!user) return setVisible("login")(true); if ( hasChanges && !error && (typeof query.json === "string" || typeof query.json === "undefined") ) { try { setIsUpdating(true); toast.loading("Saving document...", { id: "fileSave" }); const { data, error } = await documentSvc.upsert({ id: query?.json, contents: getContents(), format: getFormat(), }); if (error) throw error; if (data) replace({ query: { json: data } }); toast.success("Document saved to cloud", { id: "fileSave" }); setHasChanges(false); } catch (error: any) { toast.error(error.message, { id: "fileSave" }); } finally { setIsUpdating(false); } } }, [ error, getContents, getFormat, hasChanges, query.json, replace, setHasChanges, setVisible, user, ]); const setPrivate = async () => { try { if (!query.json) return handleSaveJson(); setIsUpdating(true); const { data: updatedJsonData, error } = await documentSvc.update(query.json as string, { private: !isPrivate, }); if (error) return toast.error(error.message); if (updatedJsonData[0]) { setIsPrivate(updatedJsonData[0].private); toast.success(`Document set to ${isPrivate ? "public" : "private"}.`); } else throw error; } catch (error) { console.error(error); } finally { setIsUpdating(false); } }; return ( {data?.name && ( {data.name} | JSON Crack )} {fileName && ( setVisible("cloud")(true)}> {fileName} )} {error ? ( Invalid {error} ) : ( Valid )} {(data?.owner_email === user?.email || (!data && user)) && ( {hasChanges || !user ? : } {hasChanges || !user ? (query?.json ? "Unsaved Changes" : "Save to Cloud") : "Saved"} )} {data?.owner_email === user?.email && ( {isPrivate ? : } {isPrivate ? "Private" : "Public"} )} setVisible("share")(true)} disabled={isPrivate || !data} > Share {liveTransformEnabled ? ( toggleLiveTransform(false)}> Live Transform ) : ( toggleLiveTransform(true)}> Manual Transform )} {!liveTransformEnabled && ( setContents({})}> Transform )} Nodes: {nodeCount} setVisible("review")(true)}> Feedback ); };