styling improvements

This commit is contained in:
AykutSarac 2022-12-11 14:27:07 +03:00
parent 8225bc2986
commit 4ed3411f86
20 changed files with 218 additions and 123 deletions

View File

@ -9,7 +9,7 @@ const withPWA = require("next-pwa")({
* @type {import('next').NextConfig} * @type {import('next').NextConfig}
*/ */
const nextConfig = { const nextConfig = {
reactStrictMode: true, reactStrictMode: false,
}; };
module.exports = withPWA(nextConfig); module.exports = withPWA(nextConfig);

BIN
public/assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -150,7 +150,7 @@ export const Sidebar: React.FC = () => {
const graphCollapsed = useGraph(state => state.graphCollapsed); const graphCollapsed = useGraph(state => state.graphCollapsed);
const direction = useGraph(state => state.direction); const direction = useGraph(state => state.direction);
const foldNodes = useConfig(state => state.foldNodes); const foldNodes = useConfig(state => state.foldNodes);
const hideEditor = useConfig(state => state.hideEditor); const fullscreen = useConfig(state => state.fullscreen);
const handleSave = () => { const handleSave = () => {
const a = document.createElement("a"); const a = document.createElement("a");
@ -189,7 +189,7 @@ export const Sidebar: React.FC = () => {
</Link> </Link>
<Tooltip className="mobile" title="Edit JSON"> <Tooltip className="mobile" title="Edit JSON">
<StyledElement onClick={() => setConfig("hideEditor", !hideEditor)}> <StyledElement onClick={() => setConfig("fullscreen", !fullscreen)}>
<AiOutlineEdit /> <AiOutlineEdit />
</StyledElement> </StyledElement>
</Tooltip> </Tooltip>

View File

@ -29,6 +29,7 @@ const StyledSupportButton = styled.a`
0 4px 8px rgba(0, 0, 0, 0.07), 0 8px 16px rgba(0, 0, 0, 0.07), 0 4px 8px rgba(0, 0, 0, 0.07), 0 8px 16px rgba(0, 0, 0, 0.07),
0 16px 32px rgba(0, 0, 0, 0.07), 0 32px 64px rgba(0, 0, 0, 0.07); 0 16px 32px rgba(0, 0, 0, 0.07), 0 32px 64px rgba(0, 0, 0, 0.07);
opacity: 0.7; opacity: 0.7;
box-sizing: content-box !important;
&:hover { &:hover {
width: 180px; width: 180px;
@ -43,8 +44,6 @@ const StyledSupportButton = styled.a`
`; `;
export const SupportButton = () => { export const SupportButton = () => {
if (location.pathname.includes("widget")) return null;
return ( return (
<StyledSupportButton <StyledSupportButton
href="https://github.com/sponsors/AykutSarac" href="https://github.com/sponsors/AykutSarac"

View File

@ -22,7 +22,6 @@ const GlobalStyle = createGlobalStyle`
font-family: 'Mona Sans'; font-family: 'Mona Sans';
font-weight: 400; font-weight: 400;
font-size: 16px; font-size: 16px;
scroll-behavior: smooth;
height: 100%; height: 100%;
background-color: #000000; background-color: #000000;
@ -32,14 +31,15 @@ const GlobalStyle = createGlobalStyle`
@media only screen and (min-width: 768px) { @media only screen and (min-width: 768px) {
background-color: #000000; background-color: #000000;
opacity: 1; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 800 800'%3E%3Cg fill-opacity='0.22'%3E%3Ccircle fill='%23000000' cx='400' cy='400' r='600'/%3E%3Ccircle fill='%23110718' cx='400' cy='400' r='500'/%3E%3Ccircle fill='%23220e30' cx='400' cy='400' r='400'/%3E%3Ccircle fill='%23331447' cx='400' cy='400' r='300'/%3E%3Ccircle fill='%23441b5f' cx='400' cy='400' r='200'/%3E%3Ccircle fill='%23552277' cx='400' cy='400' r='100'/%3E%3C/g%3E%3C/svg%3E");
background-image: radial-gradient(#414141 0.5px, #000000 0.5px); background-attachment: fixed;
background-size: 15px 15px; background-size: cover;
} }
} }
* { * {
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
scroll-behavior: smooth;
} }
.hide { .hide {

View File

@ -1,6 +1,5 @@
import React from "react"; import React from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useQuery } from "@tanstack/react-query";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { import {
AiOutlineCloudSync, AiOutlineCloudSync,
@ -10,11 +9,12 @@ import {
AiOutlineUnlock, AiOutlineUnlock,
} from "react-icons/ai"; } from "react-icons/ai";
import { VscAccount } from "react-icons/vsc"; import { VscAccount } from "react-icons/vsc";
import { altogic } from "src/api/altogic"; import { useJson } from "src/hooks/useFetchedJson";
import { getJson, saveJson, updateJson } from "src/services/db/json"; import { saveJson, updateJson } from "src/services/db/json";
import useConfig from "src/store/useConfig"; import useConfig from "src/store/useConfig";
import useGraph from "src/store/useGraph"; import useGraph from "src/store/useGraph";
import useModal from "src/store/useModal"; import useModal from "src/store/useModal";
import useStored from "src/store/useStored";
import useUser from "src/store/useUser"; import useUser from "src/store/useUser";
import styled from "styled-components"; import styled from "styled-components";
@ -61,29 +61,30 @@ const StyledBottomBarItem = styled.button`
} }
`; `;
export const BottomBar = () => { const StyledImg = styled.img<{ light: boolean }>`
const { isReady, replace, query } = useRouter(); filter: ${({ light }) => light && "invert(100%)"};
`;
const { data } = useQuery( export const BottomBar = () => {
["dbJson", query.json], const { replace, query } = useRouter();
() => getJson(query.json as string), const { data } = useJson();
{
enabled: isReady && !!query.json,
}
);
const user = useUser(state => state.user); const user = useUser(state => state.user);
const setVisible = useModal(state => state.setVisible); const setVisible = useModal(state => state.setVisible);
const getJsonState = useGraph(state => state.getJson); const getJsonState = useGraph(state => state.getJson);
const hasChanges = useConfig(state => state.hasChanges); const hasChanges = useConfig(state => state.hasChanges);
const setConfig = useConfig(state => state.setConfig); const setConfig = useConfig(state => state.setConfig);
const lightmode = useStored(state => state.lightmode);
const [isPrivate, setIsPrivate] = React.useState(true); const [isPrivate, setIsPrivate] = React.useState(true);
React.useEffect(() => { React.useEffect(() => {
if (data) setIsPrivate(data.data.private); console.log(data);
setIsPrivate(data?.data.private ?? true);
}, [data]); }, [data]);
const handleSaveJson = React.useCallback(() => { const handleSaveJson = React.useCallback(() => {
if (!user) return setVisible("login")(true);
if (hasChanges) { if (hasChanges) {
toast.promise( toast.promise(
saveJson({ id: query.json, data: getJsonState() }).then(res => { saveJson({ id: query.json, data: getJsonState() }).then(res => {
@ -97,17 +98,16 @@ export const BottomBar = () => {
} }
); );
} }
}, [getJsonState, hasChanges, query.json, replace, setConfig]); }, [getJsonState, hasChanges, query.json, replace, setConfig, setVisible, user]);
const handleLoginClick = () => { const handleLoginClick = () => {
if (user) return setVisible("account")(true); if (user) return setVisible("account")(true);
altogic.auth.signInWithProvider("google"); else setVisible("login")(true);
}; };
const setPrivate = () => { const setPrivate = () => {
if (!query.json) return handleSaveJson(); if (!query.json) return handleSaveJson();
updateJson(query.json as string, { private: !isPrivate }); updateJson(query.json as string, { private: !isPrivate });
setIsPrivate(!isPrivate);
}; };
return ( return (
@ -121,26 +121,35 @@ export const BottomBar = () => {
{hasChanges ? <AiOutlineCloudUpload /> : <AiOutlineCloudSync />} {hasChanges ? <AiOutlineCloudUpload /> : <AiOutlineCloudSync />}
{hasChanges ? "Unsaved Changes" : "Saved"} {hasChanges ? "Unsaved Changes" : "Saved"}
</StyledBottomBarItem> </StyledBottomBarItem>
{query.json && (
<>
<StyledBottomBarItem onClick={setPrivate}> <StyledBottomBarItem onClick={setPrivate}>
{isPrivate ? <AiOutlineLock /> : <AiOutlineUnlock />} {isPrivate ? <AiOutlineLock /> : <AiOutlineUnlock />}
{isPrivate ? "Private" : "Public"} {isPrivate ? "Private" : "Public"}
</StyledBottomBarItem> </StyledBottomBarItem>
{query.json && (
<StyledBottomBarItem onClick={() => setVisible("share")(true)}> <StyledBottomBarItem onClick={() => setVisible("share")(true)}>
<AiOutlineLink /> <AiOutlineLink />
Share Share
</StyledBottomBarItem> </StyledBottomBarItem>
</>
)} )}
</StyledLeft> </StyledLeft>
<StyledRight> <StyledRight>
<a
href="https://www.altogic.com/?utm_source=jsoncrack&utm_medium=referral&utm_campaign=sponsorship"
rel="sponsored noreferrer"
target="_blank"
>
<StyledBottomBarItem> <StyledBottomBarItem>
Powered by Powered by
<img <StyledImg
height="20" height="20"
src="https://regexlearn.com/altogic.svg" src="https://regexlearn.com/altogic.svg"
alt="powered by buildable" alt="powered by buildable"
light={lightmode}
/> />
</StyledBottomBarItem> </StyledBottomBarItem>
</a>
</StyledRight> </StyledRight>
</StyledBottomBar> </StyledBottomBar>
); );

View File

@ -17,25 +17,25 @@ const LiveEditor = dynamic(() => import("src/containers/Editor/LiveEditor"), {
}); });
const Panes: React.FC = () => { const Panes: React.FC = () => {
const hideEditor = useConfig(state => state.hideEditor); const fullscreen = useConfig(state => state.fullscreen);
const setConfig = useConfig(state => state.setConfig); const setConfig = useConfig(state => state.setConfig);
const isMobile = window.innerWidth <= 768; const isMobile = window.innerWidth <= 768;
React.useEffect(() => { React.useEffect(() => {
if (isMobile) setConfig("hideEditor", true); if (isMobile) setConfig("fullscreen", true);
}, [isMobile, setConfig]); }, [isMobile, setConfig]);
return ( return (
<StyledEditor proportionalLayout={false} vertical={isMobile}> <StyledEditor proportionalLayout={false} vertical={isMobile}>
<Allotment.Pane <Allotment.Pane
preferredSize={isMobile ? "100%" : 400} preferredSize={isMobile ? "100%" : 400}
minSize={hideEditor ? 0 : 300} minSize={fullscreen ? 0 : 300}
maxSize={isMobile ? Infinity : 800} maxSize={isMobile ? Infinity : 800}
visible={!hideEditor} visible={!fullscreen}
> >
<JsonEditor /> <JsonEditor />
</Allotment.Pane> </Allotment.Pane>
<Allotment.Pane minSize={0} maxSize={isMobile && !hideEditor ? 0 : Infinity}> <Allotment.Pane minSize={0} maxSize={isMobile && !fullscreen ? 0 : Infinity}>
<LiveEditor /> <LiveEditor />
</Allotment.Pane> </Allotment.Pane>
</StyledEditor> </StyledEditor>

View File

@ -48,13 +48,13 @@ const StyledToolElement = styled.button`
export const Tools: React.FC = () => { export const Tools: React.FC = () => {
const setVisible = useModal(state => state.setVisible); const setVisible = useModal(state => state.setVisible);
const hideEditor = useConfig(state => state.hideEditor); const fullscreen = useConfig(state => state.fullscreen);
const setConfig = useConfig(state => state.setConfig); const setConfig = useConfig(state => state.setConfig);
const zoomIn = useConfig(state => state.zoomIn); const zoomIn = useConfig(state => state.zoomIn);
const zoomOut = useConfig(state => state.zoomOut); const zoomOut = useConfig(state => state.zoomOut);
const centerView = useConfig(state => state.centerView); const centerView = useConfig(state => state.centerView);
const toggleEditor = () => setConfig("hideEditor", !hideEditor); const toggleEditor = () => setConfig("fullscreen", !fullscreen);
return ( return (
<> <>

View File

@ -2,6 +2,7 @@ import React from "react";
import Head from "next/head"; import Head from "next/head";
import Link from "next/link"; import Link from "next/link";
import Script from "next/script"; import Script from "next/script";
import { AiOutlineRight } from "react-icons/ai";
import { FaGithub, FaHeart, FaLinkedin, FaTwitter } from "react-icons/fa"; import { FaGithub, FaHeart, FaLinkedin, FaTwitter } from "react-icons/fa";
import { import {
HiCursorClick, HiCursorClick,
@ -51,10 +52,10 @@ const HeroSection = () => {
<Styles.StyledHighlightedText>instantly</Styles.StyledHighlightedText> into <Styles.StyledHighlightedText>instantly</Styles.StyledHighlightedText> into
graphs. graphs.
</Styles.StyledSubTitle> </Styles.StyledSubTitle>
<Styles.StyledMinorTitle>Paste - Import - Fetch!</Styles.StyledMinorTitle>
<Styles.StyledButton rel="prefetch" href="/editor" link> <Styles.StyledButton rel="prefetch" href="/editor" link>
GO TO EDITOR GO TO EDITOR
<AiOutlineRight strokeWidth="30px" />
</Styles.StyledButton> </Styles.StyledButton>
<Styles.StyledButtonWrapper> <Styles.StyledButtonWrapper>
@ -238,8 +239,8 @@ const EmbedSection = () => (
json: defaultJson, json: defaultJson,
options: { options: {
theme: "dark", theme: "dark",
direction: "DOWN" direction: "DOWN",
} },
}, },
"*" "*"
); );
@ -288,7 +289,8 @@ const SponsorSection = () => (
const Footer = () => ( const Footer = () => (
<Styles.StyledFooter> <Styles.StyledFooter>
<Styles.StyledFooterText> <Styles.StyledFooterText>
© 2022 JSON Crack - {pkg.version} © <img width="100" src="assets/icon.png" alt="icon" />
{new Date().getFullYear()} - {pkg.version}
</Styles.StyledFooterText> </Styles.StyledFooterText>
<Styles.StyledIconLinks> <Styles.StyledIconLinks>
<Styles.StyledNavLink <Styles.StyledNavLink
@ -335,8 +337,8 @@ const Home: React.FC = () => {
<EmbedSection /> <EmbedSection />
<SupportSection /> <SupportSection />
<SponsorSection /> <SponsorSection />
<Footer />
<SupportButton /> <SupportButton />
<Footer />
</Styles.StyledHome> </Styles.StyledHome>
); );
}; };

View File

@ -137,6 +137,7 @@ export const StyledSubTitle = styled.h2`
font-size: 2.5rem; font-size: 2.5rem;
max-width: 40rem; max-width: 40rem;
margin: 0; margin: 0;
margin-bottom: 25px;
@media only screen and (max-width: 768px) { @media only screen and (max-width: 768px) {
font-size: 1.5rem; font-size: 1.5rem;
@ -158,11 +159,12 @@ export const StyledMinorTitle = styled.p`
export const StyledButton = styled(Button)` export const StyledButton = styled(Button)`
background: ${({ status }) => !status && "#a13cc2"}; background: ${({ status }) => !status && "#a13cc2"};
padding: 12px 24px; padding: 12px 24px;
height: 46px;
div { div {
font-family: "Roboto", sans-serif; font-family: "Mona Sans";
font-weight: 700; font-weight: 700;
font-size: 16px; font-size: 1rem;
} }
`; `;
@ -196,11 +198,15 @@ export const StyledSponsorButton = styled(Button)<{ isBlue?: boolean }>`
`; `;
export const StyledFeaturesSection = styled.section` export const StyledFeaturesSection = styled.section`
display: flex; max-width: 60%;
max-width: 85%;
margin: 0 auto; margin: 0 auto;
gap: 2rem; padding: 0 3%;
padding: 50px 3%;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr);
grid-column-gap: 60px;
grid-row-gap: 60px;
@media only screen and (max-width: 768px) { @media only screen and (max-width: 768px) {
flex-direction: column; flex-direction: column;
@ -211,6 +217,16 @@ export const StyledFeaturesSection = styled.section`
export const StyledSectionCard = styled.div` export const StyledSectionCard = styled.div`
text-align: center; text-align: center;
flex: 1; flex: 1;
border: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
background: rgb(48, 0, 65);
background: linear-gradient(
138deg,
rgba(48, 0, 65, 0.8870141806722689) 0%,
rgba(72, 12, 84, 0.40802258403361347) 33%,
rgba(65, 8, 92, 0.6012998949579832) 100%
);
border-radius: 6px;
padding: 16px;
`; `;
export const StyledCardTitle = styled.div` export const StyledCardTitle = styled.div`
@ -350,6 +366,9 @@ export const StyledFooter = styled.footer`
`; `;
export const StyledFooterText = styled.p` export const StyledFooterText = styled.p`
display: flex;
align-items: center;
gap: 5px;
color: #b4b4b4; color: #b4b4b4;
`; `;

View File

@ -4,23 +4,48 @@ import { ClearModal } from "src/containers/Modals/ClearModal";
import { CloudModal } from "src/containers/Modals/CloudModal"; import { CloudModal } from "src/containers/Modals/CloudModal";
import { DownloadModal } from "src/containers/Modals/DownloadModal"; import { DownloadModal } from "src/containers/Modals/DownloadModal";
import { ImportModal } from "src/containers/Modals/ImportModal"; import { ImportModal } from "src/containers/Modals/ImportModal";
import { LoginModal } from "src/containers/Modals/LoginModal";
import { SettingsModal } from "src/containers/Modals/SettingsModal"; import { SettingsModal } from "src/containers/Modals/SettingsModal";
import { ShareModal } from "src/containers/Modals/ShareModal"; import { ShareModal } from "src/containers/Modals/ShareModal";
import useModal from "src/store/useModal"; import useModal from "src/store/useModal";
import shallow from "zustand/shallow";
export const ModalController = () => { export const ModalController = () => {
const setVisible = useModal(state => state.setVisible); const setVisible = useModal(state => state.setVisible);
const state = useModal(state => state);
const [
importModal,
clearModal,
downloadModal,
settingsModal,
cloudModal,
accountModal,
loginModal,
shareModal,
] = useModal(
state => [
state.import,
state.clear,
state.download,
state.settings,
state.cloud,
state.account,
state.login,
state.share,
],
shallow
);
return ( return (
<> <>
<ImportModal visible={state.import} setVisible={setVisible("import")} /> <ImportModal visible={importModal} setVisible={setVisible("import")} />
<ClearModal visible={state.clear} setVisible={setVisible("clear")} /> <ClearModal visible={clearModal} setVisible={setVisible("clear")} />
<DownloadModal visible={state.download} setVisible={setVisible("download")} /> <DownloadModal visible={downloadModal} setVisible={setVisible("download")} />
<SettingsModal visible={state.settings} setVisible={setVisible("settings")} /> <SettingsModal visible={settingsModal} setVisible={setVisible("settings")} />
<CloudModal visible={state.cloud} setVisible={setVisible("cloud")} /> <CloudModal visible={cloudModal} setVisible={setVisible("cloud")} />
<AccountModal visible={state.account} setVisible={setVisible("account")} /> <AccountModal visible={accountModal} setVisible={setVisible("account")} />
<ShareModal visible={state.share} setVisible={setVisible("share")} /> <LoginModal visible={loginModal} setVisible={setVisible("login")} />
<ShareModal visible={shareModal} setVisible={setVisible("share")} />
</> </>
); );
}; };

View File

@ -26,18 +26,6 @@ const StyledTitle = styled.p`
} }
`; `;
const StyledModalContent = styled.div`
margin-bottom: 20px;
`;
const StyledLoginWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
`;
const StyledButton = styled(Button)``;
const StyledAccountWrapper = styled.div` const StyledAccountWrapper = styled.div`
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;

View File

@ -1,12 +1,13 @@
import React from "react"; import React from "react";
import { useRouter } from "next/router";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import dayjs from "dayjs"; import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime"; import relativeTime from "dayjs/plugin/relativeTime";
import { AiOutlinePlus } from "react-icons/ai"; import { AiOutlinePlus } from "react-icons/ai";
import { Modal, ModalProps } from "src/components/Modal"; import { Modal, ModalProps } from "src/components/Modal";
import { getAllJson } from "src/services/db/json"; import { getAllJson } from "src/services/db/json";
import useUser from "src/store/useUser";
import styled from "styled-components"; import styled from "styled-components";
import { useRouter } from "next/router";
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
@ -91,8 +92,15 @@ const CreateCard: React.FC = () => (
); );
export const CloudModal: React.FC<ModalProps> = ({ visible, setVisible }) => { export const CloudModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
const { query } = useRouter(); const { isReady, query } = useRouter();
const { data, isLoading } = useQuery(["allJson", query], () => getAllJson()); const user = useUser(state => state.user);
const { data, isLoading } = useQuery(
["allJson", query, user],
() => getAllJson(),
{
enabled: isReady,
}
);
if (isLoading) return <div>loading</div>; if (isLoading) return <div>loading</div>;
return ( return (
@ -109,7 +117,7 @@ export const CloudModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
preview: preview:
"https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png", "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
}} }}
key={json.id} key={json._id}
/> />
))} ))}
<CreateCard /> <CreateCard />

View File

@ -0,0 +1,24 @@
import React from "react";
import { altogic } from "src/api/altogic";
import { Button } from "src/components/Button";
import { Modal, ModalProps } from "src/components/Modal";
export const LoginModal: React.FC<ModalProps> = ({ setVisible, visible }) => {
const handleLoginClick = () => {
altogic.auth.signInWithProvider("google");
};
return (
<Modal visible={visible} setVisible={setVisible}>
<Modal.Header>Login</Modal.Header>
<Modal.Content>
<h2>Welcome Back!</h2>
<p>Login to unlock full potential of JSON Crack!</p>
<Button onClick={handleLoginClick} status="SECONDARY" block>
Login
</Button>
</Modal.Content>
<Modal.Controls setVisible={setVisible} />
</Modal>
);
};

View File

@ -0,0 +1,35 @@
import React from "react";
import { useRouter } from "next/router";
import { useQuery } from "@tanstack/react-query";
import { decompressFromBase64 } from "lz-string";
import { defaultJson } from "src/constants/data";
import { getJson } from "src/services/db/json";
import useGraph from "src/store/useGraph";
export function useJson() {
const { query, isReady } = useRouter();
const setLoading = useGraph(state => state.setLoading);
const setJson = useGraph(state => state.setJson);
const { data, isLoading, status } = useQuery(
["dbJson", query.json],
() => getJson(query.json as string),
{
enabled: isReady && !!query.json,
}
);
React.useEffect(() => {
if (isReady) {
if (query.json) {
if (isLoading) return setLoading(true);
if (status || !data) setJson(defaultJson);
if (data?.data) setJson(decompressFromBase64(data.data.json) as string);
setLoading(false);
} else setJson(defaultJson);
}
}, [data, isLoading, isReady, query.json, setJson, setLoading, status]);
return { data };
}

View File

@ -1,14 +1,9 @@
import React from "react"; import React from "react";
import Head from "next/head"; import Head from "next/head";
import { useRouter } from "next/router";
import { useQuery } from "@tanstack/react-query";
import { decompressFromBase64 } from "lz-string";
import { Sidebar } from "src/components/Sidebar"; import { Sidebar } from "src/components/Sidebar";
import { defaultJson } from "src/constants/data";
import { BottomBar } from "src/containers/Editor/BottomBar"; import { BottomBar } from "src/containers/Editor/BottomBar";
import Panes from "src/containers/Editor/Panes"; import Panes from "src/containers/Editor/Panes";
import { getJson } from "src/services/db/json"; import { useJson } from "src/hooks/useFetchedJson";
import useGraph from "src/store/useGraph";
import useUser from "src/store/useUser"; import useUser from "src/store/useUser";
import styled from "styled-components"; import styled from "styled-components";
@ -32,36 +27,13 @@ export const StyledEditorWrapper = styled.div`
`; `;
const EditorPage: React.FC = () => { const EditorPage: React.FC = () => {
const { query, isReady } = useRouter();
const checkSession = useUser(state => state.checkSession); const checkSession = useUser(state => state.checkSession);
const { data, isLoading, status } = useQuery( useJson();
["dbJson", query.json],
() => getJson(query.json as string),
{
enabled: isReady && !!query.json,
}
);
const setJson = useGraph(state => state.setJson);
const setLoading = useGraph(state => state.setLoading);
React.useEffect(() => { React.useEffect(() => {
checkSession(); checkSession();
}, [checkSession]); }, [checkSession]);
React.useEffect(() => {
if (isReady) {
if (query.json) {
if (isLoading) return setLoading(true);
if (status || !data) setJson(defaultJson);
if (data?.data) setJson(decompressFromBase64(data.data.json) as string);
setLoading(false);
} else setJson(defaultJson);
}
}, [data, status, isLoading, isReady, query.json, setJson, setLoading]);
return ( return (
<StyledEditorWrapper> <StyledEditorWrapper>
<Head> <Head>

View File

@ -13,11 +13,11 @@ interface ConfigActions {
const initialStates = { const initialStates = {
foldNodes: false, foldNodes: false,
hideEditor: false, fullscreen: false,
performanceMode: true, performanceMode: true,
zoomPanPinch: undefined as ReactZoomPanPinchRef | undefined, zoomPanPinch: undefined as ReactZoomPanPinchRef | undefined,
hasChanges: false, hasChanges: false,
hasError: false hasError: false,
}; };
export type Config = typeof initialStates; export type Config = typeof initialStates;
@ -49,7 +49,14 @@ const useConfig = create<Config & ConfigActions>()((set, get) => ({
const canvas = document.querySelector(".jsoncrack-canvas") as HTMLElement; const canvas = document.querySelector(".jsoncrack-canvas") as HTMLElement;
if (zoomPanPinch && canvas) zoomPanPinch.zoomToElement(canvas); if (zoomPanPinch && canvas) zoomPanPinch.zoomToElement(canvas);
}, },
setConfig: (setting: keyof Config, value: unknown) => set({ [setting]: value }), setConfig: (setting, value) => {
if (setting === "fullscreen" && value) {
set({ fullscreen: true });
return get().centerView();
}
set({ [setting]: value });
},
})); }));
export default useConfig; export default useConfig;

View File

@ -9,7 +9,7 @@ import useConfig from "./useConfig";
const initialStates = { const initialStates = {
json: null as unknown as string, json: null as unknown as string,
loading: false, loading: true,
direction: "RIGHT" as CanvasDirection, direction: "RIGHT" as CanvasDirection,
graphCollapsed: false, graphCollapsed: false,
nodes: [] as NodeData[], nodes: [] as NodeData[],

View File

@ -15,11 +15,12 @@ const initialStates = {
node: false, node: false,
settings: false, settings: false,
share: false, share: false,
login: false
}; };
type ModalType = keyof typeof initialStates; type ModalType = keyof typeof initialStates;
const authModals: ModalType[] = ["cloud", "share"]; const authModals: ModalType[] = ["cloud", "share", "account"];
export type ModalStates = typeof initialStates; export type ModalStates = typeof initialStates;
@ -27,7 +28,7 @@ const useModal = create<ModalStates & ModalActions>()(set => ({
...initialStates, ...initialStates,
setVisible: modal => visible => { setVisible: modal => visible => {
if (authModals.includes(modal) && !useUser.getState().isAuthenticated) { if (authModals.includes(modal) && !useUser.getState().isAuthenticated) {
return set({ account: true }); return set({ login: true });
} }
set({ [modal]: visible }); set({ [modal]: visible });

View File

@ -2,6 +2,7 @@ import toast from "react-hot-toast";
import { altogic } from "src/api/altogic"; import { altogic } from "src/api/altogic";
import { AltogicAuth } from "src/typings/altogic"; import { AltogicAuth } from "src/typings/altogic";
import create from "zustand"; import create from "zustand";
import useModal from "./useModal";
type User = { type User = {
_id: string; _id: string;
@ -32,22 +33,27 @@ const useUser = create<UserStates & UserActions>()(set => ({
setUser: (key, value) => set({ [key]: value }), setUser: (key, value) => set({ [key]: value }),
logout: () => { logout: () => {
altogic.auth.signOut(); altogic.auth.signOut();
toast.success("Logged out."); toast.success("Logged out.");
useModal.setState({ account: false });
set(initialStates); set(initialStates);
}, },
login: response => { login: response => {
set({ user: response.user, isAuthenticated: true }); set({ user: response.user, isAuthenticated: true });
}, },
checkSession: () => { checkSession: async () => {
const currentSession = altogic.auth.getSession(); const currentSession = altogic.auth.getSession();
const currentUser = altogic.auth.getUser(); const currentUser = altogic.auth.getUser();
if (currentSession) { if (currentSession) {
alert("has");
altogic.auth.setSession(currentSession); altogic.auth.setSession(currentSession);
set({ user: currentUser as any, isAuthenticated: true }); set({ user: currentUser as any, isAuthenticated: true });
} else {
if (!new URLSearchParams(window.location.search).get("access_token")) return;
const data = await altogic.auth.getAuthGrant();
if (!data.errors?.items.length) {
set({ user: data.user as any, isAuthenticated: true });
}
} }
}, },
})); }));