mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-12 19:02:53 +08:00
styling improvements
This commit is contained in:
parent
8225bc2986
commit
4ed3411f86
@ -9,7 +9,7 @@ const withPWA = require("next-pwa")({
|
||||
* @type {import('next').NextConfig}
|
||||
*/
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
reactStrictMode: false,
|
||||
};
|
||||
|
||||
module.exports = withPWA(nextConfig);
|
||||
|
BIN
public/assets/icon.png
Normal file
BIN
public/assets/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
@ -150,7 +150,7 @@ export const Sidebar: React.FC = () => {
|
||||
const graphCollapsed = useGraph(state => state.graphCollapsed);
|
||||
const direction = useGraph(state => state.direction);
|
||||
const foldNodes = useConfig(state => state.foldNodes);
|
||||
const hideEditor = useConfig(state => state.hideEditor);
|
||||
const fullscreen = useConfig(state => state.fullscreen);
|
||||
|
||||
const handleSave = () => {
|
||||
const a = document.createElement("a");
|
||||
@ -189,7 +189,7 @@ export const Sidebar: React.FC = () => {
|
||||
</Link>
|
||||
|
||||
<Tooltip className="mobile" title="Edit JSON">
|
||||
<StyledElement onClick={() => setConfig("hideEditor", !hideEditor)}>
|
||||
<StyledElement onClick={() => setConfig("fullscreen", !fullscreen)}>
|
||||
<AiOutlineEdit />
|
||||
</StyledElement>
|
||||
</Tooltip>
|
||||
|
@ -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 16px 32px rgba(0, 0, 0, 0.07), 0 32px 64px rgba(0, 0, 0, 0.07);
|
||||
opacity: 0.7;
|
||||
box-sizing: content-box !important;
|
||||
|
||||
&:hover {
|
||||
width: 180px;
|
||||
@ -43,8 +44,6 @@ const StyledSupportButton = styled.a`
|
||||
`;
|
||||
|
||||
export const SupportButton = () => {
|
||||
if (location.pathname.includes("widget")) return null;
|
||||
|
||||
return (
|
||||
<StyledSupportButton
|
||||
href="https://github.com/sponsors/AykutSarac"
|
||||
|
@ -22,7 +22,6 @@ const GlobalStyle = createGlobalStyle`
|
||||
font-family: 'Mona Sans';
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
scroll-behavior: smooth;
|
||||
height: 100%;
|
||||
|
||||
background-color: #000000;
|
||||
@ -32,14 +31,15 @@ const GlobalStyle = createGlobalStyle`
|
||||
|
||||
@media only screen and (min-width: 768px) {
|
||||
background-color: #000000;
|
||||
opacity: 1;
|
||||
background-image: radial-gradient(#414141 0.5px, #000000 0.5px);
|
||||
background-size: 15px 15px;
|
||||
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-attachment: fixed;
|
||||
background-size: cover;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.hide {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import toast from "react-hot-toast";
|
||||
import {
|
||||
AiOutlineCloudSync,
|
||||
@ -10,11 +9,12 @@ import {
|
||||
AiOutlineUnlock,
|
||||
} from "react-icons/ai";
|
||||
import { VscAccount } from "react-icons/vsc";
|
||||
import { altogic } from "src/api/altogic";
|
||||
import { getJson, saveJson, updateJson } from "src/services/db/json";
|
||||
import { useJson } from "src/hooks/useFetchedJson";
|
||||
import { saveJson, updateJson } from "src/services/db/json";
|
||||
import useConfig from "src/store/useConfig";
|
||||
import useGraph from "src/store/useGraph";
|
||||
import useModal from "src/store/useModal";
|
||||
import useStored from "src/store/useStored";
|
||||
import useUser from "src/store/useUser";
|
||||
import styled from "styled-components";
|
||||
|
||||
@ -61,29 +61,30 @@ const StyledBottomBarItem = styled.button`
|
||||
}
|
||||
`;
|
||||
|
||||
export const BottomBar = () => {
|
||||
const { isReady, replace, query } = useRouter();
|
||||
const StyledImg = styled.img<{ light: boolean }>`
|
||||
filter: ${({ light }) => light && "invert(100%)"};
|
||||
`;
|
||||
|
||||
const { data } = useQuery(
|
||||
["dbJson", query.json],
|
||||
() => getJson(query.json as string),
|
||||
{
|
||||
enabled: isReady && !!query.json,
|
||||
}
|
||||
);
|
||||
export const BottomBar = () => {
|
||||
const { replace, query } = useRouter();
|
||||
const { data } = useJson();
|
||||
|
||||
const user = useUser(state => state.user);
|
||||
const setVisible = useModal(state => state.setVisible);
|
||||
const getJsonState = useGraph(state => state.getJson);
|
||||
const hasChanges = useConfig(state => state.hasChanges);
|
||||
const setConfig = useConfig(state => state.setConfig);
|
||||
const lightmode = useStored(state => state.lightmode);
|
||||
const [isPrivate, setIsPrivate] = React.useState(true);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (data) setIsPrivate(data.data.private);
|
||||
console.log(data);
|
||||
|
||||
setIsPrivate(data?.data.private ?? true);
|
||||
}, [data]);
|
||||
|
||||
const handleSaveJson = React.useCallback(() => {
|
||||
if (!user) return setVisible("login")(true);
|
||||
if (hasChanges) {
|
||||
toast.promise(
|
||||
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 = () => {
|
||||
if (user) return setVisible("account")(true);
|
||||
altogic.auth.signInWithProvider("google");
|
||||
else setVisible("login")(true);
|
||||
};
|
||||
|
||||
const setPrivate = () => {
|
||||
if (!query.json) return handleSaveJson();
|
||||
updateJson(query.json as string, { private: !isPrivate });
|
||||
setIsPrivate(!isPrivate);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -121,26 +121,35 @@ export const BottomBar = () => {
|
||||
{hasChanges ? <AiOutlineCloudUpload /> : <AiOutlineCloudSync />}
|
||||
{hasChanges ? "Unsaved Changes" : "Saved"}
|
||||
</StyledBottomBarItem>
|
||||
{query.json && (
|
||||
<>
|
||||
<StyledBottomBarItem onClick={setPrivate}>
|
||||
{isPrivate ? <AiOutlineLock /> : <AiOutlineUnlock />}
|
||||
{isPrivate ? "Private" : "Public"}
|
||||
</StyledBottomBarItem>
|
||||
{query.json && (
|
||||
<StyledBottomBarItem onClick={() => setVisible("share")(true)}>
|
||||
<AiOutlineLink />
|
||||
Share
|
||||
</StyledBottomBarItem>
|
||||
</>
|
||||
)}
|
||||
</StyledLeft>
|
||||
<StyledRight>
|
||||
<a
|
||||
href="https://www.altogic.com/?utm_source=jsoncrack&utm_medium=referral&utm_campaign=sponsorship"
|
||||
rel="sponsored noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<StyledBottomBarItem>
|
||||
Powered by
|
||||
<img
|
||||
<StyledImg
|
||||
height="20"
|
||||
src="https://regexlearn.com/altogic.svg"
|
||||
alt="powered by buildable"
|
||||
light={lightmode}
|
||||
/>
|
||||
</StyledBottomBarItem>
|
||||
</a>
|
||||
</StyledRight>
|
||||
</StyledBottomBar>
|
||||
);
|
||||
|
@ -17,25 +17,25 @@ const LiveEditor = dynamic(() => import("src/containers/Editor/LiveEditor"), {
|
||||
});
|
||||
|
||||
const Panes: React.FC = () => {
|
||||
const hideEditor = useConfig(state => state.hideEditor);
|
||||
const fullscreen = useConfig(state => state.fullscreen);
|
||||
const setConfig = useConfig(state => state.setConfig);
|
||||
const isMobile = window.innerWidth <= 768;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isMobile) setConfig("hideEditor", true);
|
||||
if (isMobile) setConfig("fullscreen", true);
|
||||
}, [isMobile, setConfig]);
|
||||
|
||||
return (
|
||||
<StyledEditor proportionalLayout={false} vertical={isMobile}>
|
||||
<Allotment.Pane
|
||||
preferredSize={isMobile ? "100%" : 400}
|
||||
minSize={hideEditor ? 0 : 300}
|
||||
minSize={fullscreen ? 0 : 300}
|
||||
maxSize={isMobile ? Infinity : 800}
|
||||
visible={!hideEditor}
|
||||
visible={!fullscreen}
|
||||
>
|
||||
<JsonEditor />
|
||||
</Allotment.Pane>
|
||||
<Allotment.Pane minSize={0} maxSize={isMobile && !hideEditor ? 0 : Infinity}>
|
||||
<Allotment.Pane minSize={0} maxSize={isMobile && !fullscreen ? 0 : Infinity}>
|
||||
<LiveEditor />
|
||||
</Allotment.Pane>
|
||||
</StyledEditor>
|
||||
|
@ -48,13 +48,13 @@ const StyledToolElement = styled.button`
|
||||
export const Tools: React.FC = () => {
|
||||
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 zoomIn = useConfig(state => state.zoomIn);
|
||||
const zoomOut = useConfig(state => state.zoomOut);
|
||||
const centerView = useConfig(state => state.centerView);
|
||||
const toggleEditor = () => setConfig("hideEditor", !hideEditor);
|
||||
const toggleEditor = () => setConfig("fullscreen", !fullscreen);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -2,6 +2,7 @@ import React from "react";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import Script from "next/script";
|
||||
import { AiOutlineRight } from "react-icons/ai";
|
||||
import { FaGithub, FaHeart, FaLinkedin, FaTwitter } from "react-icons/fa";
|
||||
import {
|
||||
HiCursorClick,
|
||||
@ -51,10 +52,10 @@ const HeroSection = () => {
|
||||
<Styles.StyledHighlightedText>instantly</Styles.StyledHighlightedText> into
|
||||
graphs.
|
||||
</Styles.StyledSubTitle>
|
||||
<Styles.StyledMinorTitle>Paste - Import - Fetch!</Styles.StyledMinorTitle>
|
||||
|
||||
<Styles.StyledButton rel="prefetch" href="/editor" link>
|
||||
GO TO EDITOR
|
||||
<AiOutlineRight strokeWidth="30px" />
|
||||
</Styles.StyledButton>
|
||||
|
||||
<Styles.StyledButtonWrapper>
|
||||
@ -238,8 +239,8 @@ const EmbedSection = () => (
|
||||
json: defaultJson,
|
||||
options: {
|
||||
theme: "dark",
|
||||
direction: "DOWN"
|
||||
}
|
||||
direction: "DOWN",
|
||||
},
|
||||
},
|
||||
"*"
|
||||
);
|
||||
@ -288,7 +289,8 @@ const SponsorSection = () => (
|
||||
const Footer = () => (
|
||||
<Styles.StyledFooter>
|
||||
<Styles.StyledFooterText>
|
||||
© 2022 JSON Crack - {pkg.version}
|
||||
© <img width="100" src="assets/icon.png" alt="icon" />
|
||||
{new Date().getFullYear()} - {pkg.version}
|
||||
</Styles.StyledFooterText>
|
||||
<Styles.StyledIconLinks>
|
||||
<Styles.StyledNavLink
|
||||
@ -335,8 +337,8 @@ const Home: React.FC = () => {
|
||||
<EmbedSection />
|
||||
<SupportSection />
|
||||
<SponsorSection />
|
||||
<Footer />
|
||||
<SupportButton />
|
||||
<Footer />
|
||||
</Styles.StyledHome>
|
||||
);
|
||||
};
|
||||
|
@ -137,6 +137,7 @@ export const StyledSubTitle = styled.h2`
|
||||
font-size: 2.5rem;
|
||||
max-width: 40rem;
|
||||
margin: 0;
|
||||
margin-bottom: 25px;
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
font-size: 1.5rem;
|
||||
@ -158,11 +159,12 @@ export const StyledMinorTitle = styled.p`
|
||||
export const StyledButton = styled(Button)`
|
||||
background: ${({ status }) => !status && "#a13cc2"};
|
||||
padding: 12px 24px;
|
||||
height: 46px;
|
||||
|
||||
div {
|
||||
font-family: "Roboto", sans-serif;
|
||||
font-family: "Mona Sans";
|
||||
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`
|
||||
display: flex;
|
||||
max-width: 85%;
|
||||
max-width: 60%;
|
||||
margin: 0 auto;
|
||||
gap: 2rem;
|
||||
padding: 50px 3%;
|
||||
padding: 0 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) {
|
||||
flex-direction: column;
|
||||
@ -211,6 +217,16 @@ export const StyledFeaturesSection = styled.section`
|
||||
export const StyledSectionCard = styled.div`
|
||||
text-align: center;
|
||||
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`
|
||||
@ -350,6 +366,9 @@ export const StyledFooter = styled.footer`
|
||||
`;
|
||||
|
||||
export const StyledFooterText = styled.p`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
color: #b4b4b4;
|
||||
`;
|
||||
|
||||
|
@ -4,23 +4,48 @@ import { ClearModal } from "src/containers/Modals/ClearModal";
|
||||
import { CloudModal } from "src/containers/Modals/CloudModal";
|
||||
import { DownloadModal } from "src/containers/Modals/DownloadModal";
|
||||
import { ImportModal } from "src/containers/Modals/ImportModal";
|
||||
import { LoginModal } from "src/containers/Modals/LoginModal";
|
||||
import { SettingsModal } from "src/containers/Modals/SettingsModal";
|
||||
import { ShareModal } from "src/containers/Modals/ShareModal";
|
||||
import useModal from "src/store/useModal";
|
||||
import shallow from "zustand/shallow";
|
||||
|
||||
export const ModalController = () => {
|
||||
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 (
|
||||
<>
|
||||
<ImportModal visible={state.import} setVisible={setVisible("import")} />
|
||||
<ClearModal visible={state.clear} setVisible={setVisible("clear")} />
|
||||
<DownloadModal visible={state.download} setVisible={setVisible("download")} />
|
||||
<SettingsModal visible={state.settings} setVisible={setVisible("settings")} />
|
||||
<CloudModal visible={state.cloud} setVisible={setVisible("cloud")} />
|
||||
<AccountModal visible={state.account} setVisible={setVisible("account")} />
|
||||
<ShareModal visible={state.share} setVisible={setVisible("share")} />
|
||||
<ImportModal visible={importModal} setVisible={setVisible("import")} />
|
||||
<ClearModal visible={clearModal} setVisible={setVisible("clear")} />
|
||||
<DownloadModal visible={downloadModal} setVisible={setVisible("download")} />
|
||||
<SettingsModal visible={settingsModal} setVisible={setVisible("settings")} />
|
||||
<CloudModal visible={cloudModal} setVisible={setVisible("cloud")} />
|
||||
<AccountModal visible={accountModal} setVisible={setVisible("account")} />
|
||||
<LoginModal visible={loginModal} setVisible={setVisible("login")} />
|
||||
<ShareModal visible={shareModal} setVisible={setVisible("share")} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -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`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
@ -1,12 +1,13 @@
|
||||
import React from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import dayjs from "dayjs";
|
||||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import { AiOutlinePlus } from "react-icons/ai";
|
||||
import { Modal, ModalProps } from "src/components/Modal";
|
||||
import { getAllJson } from "src/services/db/json";
|
||||
import useUser from "src/store/useUser";
|
||||
import styled from "styled-components";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
@ -91,8 +92,15 @@ const CreateCard: React.FC = () => (
|
||||
);
|
||||
|
||||
export const CloudModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
|
||||
const { query } = useRouter();
|
||||
const { data, isLoading } = useQuery(["allJson", query], () => getAllJson());
|
||||
const { isReady, query } = useRouter();
|
||||
const user = useUser(state => state.user);
|
||||
const { data, isLoading } = useQuery(
|
||||
["allJson", query, user],
|
||||
() => getAllJson(),
|
||||
{
|
||||
enabled: isReady,
|
||||
}
|
||||
);
|
||||
|
||||
if (isLoading) return <div>loading</div>;
|
||||
return (
|
||||
@ -109,7 +117,7 @@ export const CloudModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
|
||||
preview:
|
||||
"https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
|
||||
}}
|
||||
key={json.id}
|
||||
key={json._id}
|
||||
/>
|
||||
))}
|
||||
<CreateCard />
|
||||
|
24
src/containers/Modals/LoginModal/index.tsx
Normal file
24
src/containers/Modals/LoginModal/index.tsx
Normal 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>
|
||||
);
|
||||
};
|
35
src/hooks/useFetchedJson.tsx
Normal file
35
src/hooks/useFetchedJson.tsx
Normal 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 };
|
||||
}
|
@ -1,14 +1,9 @@
|
||||
import React from "react";
|
||||
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 { defaultJson } from "src/constants/data";
|
||||
import { BottomBar } from "src/containers/Editor/BottomBar";
|
||||
import Panes from "src/containers/Editor/Panes";
|
||||
import { getJson } from "src/services/db/json";
|
||||
import useGraph from "src/store/useGraph";
|
||||
import { useJson } from "src/hooks/useFetchedJson";
|
||||
import useUser from "src/store/useUser";
|
||||
import styled from "styled-components";
|
||||
|
||||
@ -32,36 +27,13 @@ export const StyledEditorWrapper = styled.div`
|
||||
`;
|
||||
|
||||
const EditorPage: React.FC = () => {
|
||||
const { query, isReady } = useRouter();
|
||||
const checkSession = useUser(state => state.checkSession);
|
||||
const { data, isLoading, status } = useQuery(
|
||||
["dbJson", query.json],
|
||||
() => getJson(query.json as string),
|
||||
{
|
||||
enabled: isReady && !!query.json,
|
||||
}
|
||||
);
|
||||
|
||||
const setJson = useGraph(state => state.setJson);
|
||||
const setLoading = useGraph(state => state.setLoading);
|
||||
useJson();
|
||||
|
||||
React.useEffect(() => {
|
||||
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 (
|
||||
<StyledEditorWrapper>
|
||||
<Head>
|
||||
|
@ -13,11 +13,11 @@ interface ConfigActions {
|
||||
|
||||
const initialStates = {
|
||||
foldNodes: false,
|
||||
hideEditor: false,
|
||||
fullscreen: false,
|
||||
performanceMode: true,
|
||||
zoomPanPinch: undefined as ReactZoomPanPinchRef | undefined,
|
||||
hasChanges: false,
|
||||
hasError: false
|
||||
hasError: false,
|
||||
};
|
||||
|
||||
export type Config = typeof initialStates;
|
||||
@ -49,7 +49,14 @@ const useConfig = create<Config & ConfigActions>()((set, get) => ({
|
||||
const canvas = document.querySelector(".jsoncrack-canvas") as HTMLElement;
|
||||
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;
|
||||
|
@ -9,7 +9,7 @@ import useConfig from "./useConfig";
|
||||
|
||||
const initialStates = {
|
||||
json: null as unknown as string,
|
||||
loading: false,
|
||||
loading: true,
|
||||
direction: "RIGHT" as CanvasDirection,
|
||||
graphCollapsed: false,
|
||||
nodes: [] as NodeData[],
|
||||
|
@ -15,11 +15,12 @@ const initialStates = {
|
||||
node: false,
|
||||
settings: false,
|
||||
share: false,
|
||||
login: false
|
||||
};
|
||||
|
||||
type ModalType = keyof typeof initialStates;
|
||||
|
||||
const authModals: ModalType[] = ["cloud", "share"];
|
||||
const authModals: ModalType[] = ["cloud", "share", "account"];
|
||||
|
||||
export type ModalStates = typeof initialStates;
|
||||
|
||||
@ -27,7 +28,7 @@ const useModal = create<ModalStates & ModalActions>()(set => ({
|
||||
...initialStates,
|
||||
setVisible: modal => visible => {
|
||||
if (authModals.includes(modal) && !useUser.getState().isAuthenticated) {
|
||||
return set({ account: true });
|
||||
return set({ login: true });
|
||||
}
|
||||
|
||||
set({ [modal]: visible });
|
||||
|
@ -2,6 +2,7 @@ import toast from "react-hot-toast";
|
||||
import { altogic } from "src/api/altogic";
|
||||
import { AltogicAuth } from "src/typings/altogic";
|
||||
import create from "zustand";
|
||||
import useModal from "./useModal";
|
||||
|
||||
type User = {
|
||||
_id: string;
|
||||
@ -32,22 +33,27 @@ const useUser = create<UserStates & UserActions>()(set => ({
|
||||
setUser: (key, value) => set({ [key]: value }),
|
||||
logout: () => {
|
||||
altogic.auth.signOut();
|
||||
|
||||
toast.success("Logged out.");
|
||||
useModal.setState({ account: false });
|
||||
set(initialStates);
|
||||
},
|
||||
login: response => {
|
||||
set({ user: response.user, isAuthenticated: true });
|
||||
},
|
||||
checkSession: () => {
|
||||
checkSession: async () => {
|
||||
const currentSession = altogic.auth.getSession();
|
||||
const currentUser = altogic.auth.getUser();
|
||||
|
||||
if (currentSession) {
|
||||
alert("has");
|
||||
|
||||
altogic.auth.setSession(currentSession);
|
||||
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 });
|
||||
}
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
Loading…
x
Reference in New Issue
Block a user