mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-12 19:02:53 +08:00
improve parsing ui
This commit is contained in:
parent
5da20fa5be
commit
8225bc2986
@ -1,3 +1,3 @@
|
||||
NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
||||
NEXT_ALTOGIC_ENV_URL=https://815e-4e5a.c5-na.altogic.com/
|
||||
NEXT_ALTOGIC_CLIENT_KEY=f1e92022789f4ccf91273a345ab2bdf8
|
||||
NEXT_PUBLIC_ALTOGIC_ENV_URL=https://815e-4e5a.c5-na.altogic.com/
|
||||
NEXT_PUBLIC_ALTOGIC_CLIENT_KEY=f1e92022789f4ccf91273a345ab2bdf8
|
@ -1 +1,3 @@
|
||||
NEXT_PUBLIC_BASE_URL=https://jsoncrack.com
|
||||
NEXT_PUBLIC_BASE_URL=https://jsoncrack.com
|
||||
NEXT_PUBLIC_ALTOGIC_ENV_URL=https://815e-4e5a.c5-na.altogic.com/
|
||||
NEXT_PUBLIC_ALTOGIC_CLIENT_KEY=f1e92022789f4ccf91273a345ab2bdf8
|
@ -20,7 +20,6 @@
|
||||
"allotment": "^1.17.0",
|
||||
"altogic": "^2.3.8",
|
||||
"axios": "^1.1.3",
|
||||
"compress-json": "^2.1.2",
|
||||
"dayjs": "^1.11.6",
|
||||
"html-to-image": "^1.10.8",
|
||||
"jsonc-parser": "^3.2.0",
|
||||
|
8
src/api/altogic.ts
Normal file
8
src/api/altogic.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { createClient } from "altogic";
|
||||
|
||||
let envUrl = process.env.NEXT_PUBLIC_ALTOGIC_ENV_URL as string;
|
||||
let clientKey = process.env.NEXT_PUBLIC_ALTOGIC_CLIENT_KEY as string;
|
||||
|
||||
const altogic = createClient(envUrl, clientKey);
|
||||
|
||||
export { altogic };
|
@ -35,7 +35,7 @@ const StyledButton = styled.button<{
|
||||
background: ${({ status, theme }) => getButtonStatus(status, theme)};
|
||||
color: #ffffff;
|
||||
padding: 8px 16px;
|
||||
min-width: 60px;
|
||||
min-width: 70px;
|
||||
min-height: 32px;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
|
@ -54,14 +54,16 @@ export const MonacoEditor = () => {
|
||||
const debouncedSetJson = React.useMemo(
|
||||
() =>
|
||||
debounce(value => {
|
||||
if (!value) return;
|
||||
if (!value || hasError) return;
|
||||
setJson(value);
|
||||
}, 1200),
|
||||
[setJson]
|
||||
[hasError, setJson]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!hasError) debouncedSetJson(value);
|
||||
|
||||
return () => debouncedSetJson.cancel();
|
||||
}, [debouncedSetJson, hasError, value]);
|
||||
|
||||
return (
|
||||
@ -73,8 +75,8 @@ export const MonacoEditor = () => {
|
||||
theme={lightmode}
|
||||
options={editorOptions}
|
||||
onChange={val => {
|
||||
if (json) setConfig("hasChanges", true);
|
||||
setValue(val);
|
||||
if (json) setConfig("hasChanges", true);
|
||||
}}
|
||||
loading={<Loading message="Loading Editor..." />}
|
||||
beforeMount={handleEditorWillMount}
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
AiOutlineUnlock,
|
||||
} from "react-icons/ai";
|
||||
import { VscAccount } from "react-icons/vsc";
|
||||
import { altogic } from "src/services/altogic";
|
||||
import { altogic } from "src/api/altogic";
|
||||
import { getJson, saveJson, updateJson } from "src/services/db/json";
|
||||
import useConfig from "src/store/useConfig";
|
||||
import useGraph from "src/store/useGraph";
|
||||
@ -125,10 +125,12 @@ export const BottomBar = () => {
|
||||
{isPrivate ? <AiOutlineLock /> : <AiOutlineUnlock />}
|
||||
{isPrivate ? "Private" : "Public"}
|
||||
</StyledBottomBarItem>
|
||||
<StyledBottomBarItem onClick={() => setVisible("share")(true)}>
|
||||
<AiOutlineLink />
|
||||
Share
|
||||
</StyledBottomBarItem>
|
||||
{query.json && (
|
||||
<StyledBottomBarItem onClick={() => setVisible("share")(true)}>
|
||||
<AiOutlineLink />
|
||||
Share
|
||||
</StyledBottomBarItem>
|
||||
)}
|
||||
</StyledLeft>
|
||||
<StyledRight>
|
||||
<StyledBottomBarItem>
|
||||
|
@ -1,20 +1,28 @@
|
||||
import React from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { Button } from "src/components/Button";
|
||||
import { Modal, ModalProps } from "src/components/Modal";
|
||||
import { deleteJson } from "src/services/db/json";
|
||||
import useGraph from "src/store/useGraph";
|
||||
|
||||
export const ClearModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
|
||||
const setJson = useGraph(state => state.setJson);
|
||||
const { query, replace } = useRouter();
|
||||
|
||||
const handleClear = () => {
|
||||
setJson("{}");
|
||||
setVisible(false);
|
||||
|
||||
if (typeof query.json === "string") {
|
||||
deleteJson(query.json);
|
||||
replace("/editor");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal visible={visible} setVisible={setVisible}>
|
||||
<Modal.Header>Clear JSON</Modal.Header>
|
||||
<Modal.Content>Are you sure you want to clear JSON?</Modal.Content>
|
||||
<Modal.Header>Delete JSON</Modal.Header>
|
||||
<Modal.Content>Are you sure you want to delete JSON?</Modal.Content>
|
||||
<Modal.Controls setVisible={setVisible}>
|
||||
<Button status="DANGER" onClick={handleClear}>
|
||||
Confirm
|
||||
|
@ -6,6 +6,7 @@ import { AiOutlinePlus } from "react-icons/ai";
|
||||
import { Modal, ModalProps } from "src/components/Modal";
|
||||
import { getAllJson } from "src/services/db/json";
|
||||
import styled from "styled-components";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
@ -64,7 +65,7 @@ const GraphCard: React.FC<{ data: GraphCardProsp }> = ({
|
||||
...props
|
||||
}) => (
|
||||
<StyledJsonCard href={`?json=${id}`} {...props}>
|
||||
<StyledImg width="200" height="100" src={preview}></StyledImg>
|
||||
<StyledImg width="200" height="100" src={preview} />
|
||||
<StyledInfo>
|
||||
<StyledTitle>{title}</StyledTitle>
|
||||
<StyledDetils>{details}</StyledDetils>
|
||||
@ -90,7 +91,8 @@ const CreateCard: React.FC = () => (
|
||||
);
|
||||
|
||||
export const CloudModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
|
||||
const { data, isLoading } = useQuery(["allJson"], () => getAllJson());
|
||||
const { query } = useRouter();
|
||||
const { data, isLoading } = useQuery(["allJson", query], () => getAllJson());
|
||||
|
||||
if (isLoading) return <div>loading</div>;
|
||||
return (
|
||||
|
@ -1,26 +1,11 @@
|
||||
import React from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { compress } from "compress-json";
|
||||
import toast from "react-hot-toast";
|
||||
import { BiErrorAlt } from "react-icons/bi";
|
||||
import { Button } from "src/components/Button";
|
||||
import { Input } from "src/components/Input";
|
||||
import { Modal, ModalProps } from "src/components/Modal";
|
||||
import { baseURL } from "src/constants/data";
|
||||
import useConfig from "src/store/useConfig";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledWarning = styled.p``;
|
||||
|
||||
const StyledErrorWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: ${({ theme }) => theme.TEXT_DANGER};
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
const StyledFlex = styled.div`
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
@ -45,21 +30,8 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const ShareModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
|
||||
const json = useConfig(state => state.json);
|
||||
const [encodedJson, setEncodedJson] = React.useState("");
|
||||
const navigate = useRouter();
|
||||
|
||||
const embedText = `<iframe id="jsoncrackEmbed" src="${baseURL}/widget></iframe>`;
|
||||
const shareURL = `${baseURL}/editor?json=${encodedJson}`;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (visible) {
|
||||
const jsonEncode = compress(JSON.parse(json));
|
||||
const jsonString = JSON.stringify(jsonEncode);
|
||||
|
||||
setEncodedJson(encodeURIComponent(jsonString));
|
||||
}
|
||||
}, [json, visible]);
|
||||
const { push, query } = useRouter();
|
||||
const shareURL = `https://jsoncrack.com/editor?json=${query.json}`;
|
||||
|
||||
const handleShare = (value: string) => {
|
||||
navigator.clipboard.writeText(value);
|
||||
@ -71,39 +43,23 @@ export const ShareModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
|
||||
<Modal visible={visible} setVisible={setVisible}>
|
||||
<Modal.Header>Create a Share Link</Modal.Header>
|
||||
<Modal.Content>
|
||||
{encodedJson.length > 5000 ? (
|
||||
<StyledErrorWrapper>
|
||||
<BiErrorAlt size={60} />
|
||||
<StyledWarning>
|
||||
Link size exceeds 5000 characters, unable to generate link for file of
|
||||
this size!
|
||||
</StyledWarning>
|
||||
</StyledErrorWrapper>
|
||||
) : (
|
||||
<>
|
||||
<StyledContainer>
|
||||
Share Link
|
||||
<StyledFlex>
|
||||
<Input value={shareURL} type="url" readOnly />
|
||||
<Button status="SECONDARY" onClick={() => handleShare(shareURL)}>
|
||||
Copy
|
||||
</Button>
|
||||
</StyledFlex>
|
||||
</StyledContainer>
|
||||
<StyledContainer>
|
||||
Embed into your website
|
||||
<StyledFlex>
|
||||
<Button
|
||||
status="SUCCESS"
|
||||
onClick={() => navigate.push("/embed")}
|
||||
block
|
||||
>
|
||||
Learn How to Embed
|
||||
</Button>
|
||||
</StyledFlex>
|
||||
</StyledContainer>
|
||||
</>
|
||||
)}
|
||||
<StyledContainer>
|
||||
Share Link
|
||||
<StyledFlex>
|
||||
<Input value={shareURL} type="url" readOnly />
|
||||
<Button status="SECONDARY" onClick={() => handleShare(shareURL)}>
|
||||
Copy
|
||||
</Button>
|
||||
</StyledFlex>
|
||||
</StyledContainer>
|
||||
<StyledContainer>
|
||||
Embed into your website
|
||||
<StyledFlex>
|
||||
<Button status="SUCCESS" onClick={() => push("/embed")} block>
|
||||
Learn How to Embed
|
||||
</Button>
|
||||
</StyledFlex>
|
||||
</StyledContainer>
|
||||
</Modal.Content>
|
||||
<Modal.Controls setVisible={setVisible}></Modal.Controls>
|
||||
</Modal>
|
||||
|
@ -34,7 +34,7 @@ export const StyledEditorWrapper = styled.div`
|
||||
const EditorPage: React.FC = () => {
|
||||
const { query, isReady } = useRouter();
|
||||
const checkSession = useUser(state => state.checkSession);
|
||||
const { data, isLoading } = useQuery(
|
||||
const { data, isLoading, status } = useQuery(
|
||||
["dbJson", query.json],
|
||||
() => getJson(query.json as string),
|
||||
{
|
||||
@ -52,12 +52,15 @@ const EditorPage: React.FC = () => {
|
||||
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]);
|
||||
}, [data, status, isLoading, isReady, query.json, setJson, setLoading]);
|
||||
|
||||
return (
|
||||
<StyledEditorWrapper>
|
||||
|
@ -1,14 +0,0 @@
|
||||
import { createClient } from "altogic";
|
||||
|
||||
let envUrl = process.env.NEXT_ALTOGIC_ENV_URL as string;
|
||||
let clientKey = process.env.NEXT_ALTOGIC_CLIENT_KEY as string;
|
||||
|
||||
const altogic = createClient(
|
||||
"https://815e-4e5a.c5-na.altogic.com/",
|
||||
"f1e92022789f4ccf91273a345ab2bdf8",
|
||||
{
|
||||
signInRedirect: "/signin",
|
||||
}
|
||||
);
|
||||
|
||||
export { altogic };
|
@ -1,5 +1,5 @@
|
||||
import { compressToBase64 } from "lz-string";
|
||||
import { altogic } from "../altogic";
|
||||
import { altogic } from "src/api/altogic";
|
||||
|
||||
type JSON = {
|
||||
_id: string;
|
||||
@ -35,4 +35,6 @@ const updateJson = async (id: string, data: object) =>
|
||||
...data,
|
||||
});
|
||||
|
||||
export { saveJson, getJson, getAllJson, updateJson };
|
||||
const deleteJson = async (id: string) => await altogic.endpoint.delete(`json/${id}`);
|
||||
|
||||
export { saveJson, getJson, getAllJson, updateJson, deleteJson };
|
||||
|
@ -1,9 +0,0 @@
|
||||
import axios from "axios";
|
||||
|
||||
const validateToken = async <T>(token: string) => {
|
||||
return await axios.get<T>(
|
||||
`https://oauth2.googleapis.com/tokeninfo?id_token=${token}`
|
||||
);
|
||||
};
|
||||
|
||||
export { validateToken };
|
@ -1,5 +1,5 @@
|
||||
import toast from "react-hot-toast";
|
||||
import { altogic } from "src/services/altogic";
|
||||
import { altogic } from "src/api/altogic";
|
||||
import { AltogicAuth } from "src/typings/altogic";
|
||||
import create from "zustand";
|
||||
|
||||
@ -31,12 +31,12 @@ const useUser = create<UserStates & UserActions>()(set => ({
|
||||
...initialStates,
|
||||
setUser: (key, value) => set({ [key]: value }),
|
||||
logout: () => {
|
||||
localStorage.removeItem("auth_token");
|
||||
altogic.auth.signOut();
|
||||
|
||||
toast.success("Logged out.");
|
||||
set(initialStates);
|
||||
},
|
||||
login: response => {
|
||||
// localStorage.setItem("auth_token", response.session.token);
|
||||
set({ user: response.user, isAuthenticated: true });
|
||||
},
|
||||
checkSession: () => {
|
||||
@ -44,6 +44,8 @@ const useUser = create<UserStates & UserActions>()(set => ({
|
||||
const currentUser = altogic.auth.getUser();
|
||||
|
||||
if (currentSession) {
|
||||
alert("has");
|
||||
|
||||
altogic.auth.setSession(currentSession);
|
||||
set({ user: currentUser as any, isAuthenticated: true });
|
||||
}
|
||||
|
@ -2194,11 +2194,6 @@ commondir@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
|
||||
|
||||
compress-json@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/compress-json/-/compress-json-2.1.2.tgz#37e0e7c7480c572fad9ad387fca5a2f36fee6f83"
|
||||
integrity sha512-91247RD8bKQXzRmXUS4zGT250mhw86+J9X8w2L2SGtRE7g0CvzjOETFaFmsDdaXPWv8T7L9iiM7kdcnnH3BH7w==
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
|
Loading…
x
Reference in New Issue
Block a user