mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-12 19:02:53 +08:00
feat: setup supabase
This commit is contained in:
parent
23b3eefad2
commit
ef82c68eeb
@ -11,11 +11,6 @@
|
|||||||
"linebreak-style": ["error", "unix"],
|
"linebreak-style": ["error", "unix"],
|
||||||
"quotes": ["error", "double", { "avoidEscape": true }],
|
"quotes": ["error", "double", { "avoidEscape": true }],
|
||||||
"semi": ["error", "always"],
|
"semi": ["error", "always"],
|
||||||
"padding-line-between-statements": [
|
|
||||||
"error",
|
|
||||||
{ "blankLine": "always", "prev": ["const", "let", "var"], "next": "*" },
|
|
||||||
{ "blankLine": "any", "prev": ["const", "let", "var"], "next": ["const", "let", "var"] }
|
|
||||||
],
|
|
||||||
"space-before-function-paren": [
|
"space-before-function-paren": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
"@mantine/prism": "^6.0.17",
|
"@mantine/prism": "^6.0.17",
|
||||||
"@monaco-editor/react": "^4.5.1",
|
"@monaco-editor/react": "^4.5.1",
|
||||||
"@sentry/nextjs": "^7.60.0",
|
"@sentry/nextjs": "^7.60.0",
|
||||||
|
"@supabase/auth-helpers-nextjs": "^0.7.3",
|
||||||
|
"@supabase/auth-helpers-react": "^0.4.1",
|
||||||
|
"@supabase/supabase-js": "^2.31.0",
|
||||||
"@tanstack/react-query": "^4.32.0",
|
"@tanstack/react-query": "^4.32.0",
|
||||||
"allotment": "^1.19.2",
|
"allotment": "^1.19.2",
|
||||||
"altogic": "^2.3.9",
|
"altogic": "^2.3.9",
|
||||||
|
@ -30,6 +30,7 @@ const StyledBottomBar = styled.div`
|
|||||||
max-height: 27px;
|
max-height: 27px;
|
||||||
height: 27px;
|
height: 27px;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
|
z-index: 35;
|
||||||
|
|
||||||
@media screen and (max-width: 320px) {
|
@media screen and (max-width: 320px) {
|
||||||
display: none;
|
display: none;
|
||||||
@ -73,7 +74,7 @@ const StyledBottomBarItem = styled.button<{ $error?: boolean }>`
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
opacity: 0.4;
|
opacity: 0.6;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -116,19 +117,20 @@ export const BottomBar = () => {
|
|||||||
try {
|
try {
|
||||||
setIsUpdating(true);
|
setIsUpdating(true);
|
||||||
toast.loading("Saving document...", { id: "fileSave" });
|
toast.loading("Saving document...", { id: "fileSave" });
|
||||||
const res = await saveToCloud(query?.json ?? null, getContents(), getFormat());
|
|
||||||
|
|
||||||
if (res.errors && res.errors.items.length > 0) throw res.errors;
|
const { data, error } = await saveToCloud({
|
||||||
if (res.data._id) replace({ query: { json: res.data._id } });
|
id: query?.json,
|
||||||
|
contents: getContents(),
|
||||||
|
format: getFormat(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
if (data[0].id) replace({ query: { json: data[0].id } });
|
||||||
|
|
||||||
toast.success("Document saved to cloud", { id: "fileSave" });
|
toast.success("Document saved to cloud", { id: "fileSave" });
|
||||||
setHasChanges(false);
|
setHasChanges(false);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error?.items?.length > 0) {
|
toast.error(error.message, { id: "fileSave" });
|
||||||
return toast.error(error.items[0].message, { id: "fileSave", duration: 5000 });
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.error("Failed to save document!", { id: "fileSave" });
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsUpdating(false);
|
setIsUpdating(false);
|
||||||
}
|
}
|
||||||
@ -155,14 +157,18 @@ export const BottomBar = () => {
|
|||||||
if (!query.json) return handleSaveJson();
|
if (!query.json) return handleSaveJson();
|
||||||
setIsUpdating(true);
|
setIsUpdating(true);
|
||||||
|
|
||||||
const res = await updateJson(query.json as string, { private: !isPrivate });
|
const { data: updatedJsonData, error } = await updateJson(query.json as string, {
|
||||||
|
private: !isPrivate,
|
||||||
|
});
|
||||||
|
|
||||||
if (!res.errors?.items.length) {
|
if (error) return toast.error(error.message);
|
||||||
setIsPrivate(res.data.private);
|
|
||||||
|
if (updatedJsonData[0]) {
|
||||||
|
setIsPrivate(updatedJsonData[0].private);
|
||||||
toast.success(`Document set to ${isPrivate ? "public" : "private"}.`);
|
toast.success(`Document set to ${isPrivate ? "public" : "private"}.`);
|
||||||
} else throw res.errors;
|
} else throw error;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("An error occurred while updating document!");
|
console.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsUpdating(false);
|
setIsUpdating(false);
|
||||||
}
|
}
|
||||||
@ -178,7 +184,7 @@ export const BottomBar = () => {
|
|||||||
<StyledLeft>
|
<StyledLeft>
|
||||||
<StyledBottomBarItem onClick={handleLoginClick}>
|
<StyledBottomBarItem onClick={handleLoginClick}>
|
||||||
<VscAccount />
|
<VscAccount />
|
||||||
{user ? user.name : "Login"}
|
{user?.user_metadata.name ?? "Login"}
|
||||||
{premium && (
|
{premium && (
|
||||||
<Badge size="sm" color="orange" radius="sm" fw="bold">
|
<Badge size="sm" color="orange" radius="sm" fw="bold">
|
||||||
PREMIUM
|
PREMIUM
|
||||||
@ -211,24 +217,25 @@ export const BottomBar = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
</StyledBottomBarItem>
|
</StyledBottomBarItem>
|
||||||
<StyledBottomBarItem onClick={handleSaveJson} disabled={isUpdating}>
|
{(data?.owner_id === user?.id || (!data && user)) && (
|
||||||
{hasChanges ? <AiOutlineCloudUpload /> : <AiOutlineCloudSync />}
|
<StyledBottomBarItem onClick={handleSaveJson} disabled={isUpdating}>
|
||||||
{hasChanges ? (query?.json ? "Unsaved Changes" : "Create Document") : "Saved"}
|
{hasChanges ? <AiOutlineCloudUpload /> : <AiOutlineCloudSync />}
|
||||||
</StyledBottomBarItem>
|
{hasChanges ? (query?.json ? "Unsaved Changes" : "Create Document") : "Saved"}
|
||||||
{data && (
|
</StyledBottomBarItem>
|
||||||
<>
|
|
||||||
{typeof data.private !== "undefined" && (
|
|
||||||
<StyledBottomBarItem onClick={setPrivate} disabled={isUpdating}>
|
|
||||||
{isPrivate ? <AiOutlineLock /> : <AiOutlineUnlock />}
|
|
||||||
{isPrivate ? "Private" : "Public"}
|
|
||||||
</StyledBottomBarItem>
|
|
||||||
)}
|
|
||||||
<StyledBottomBarItem onClick={() => setVisible("share")(true)} disabled={isPrivate}>
|
|
||||||
<AiOutlineLink />
|
|
||||||
Share
|
|
||||||
</StyledBottomBarItem>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
{data?.owner_id === user?.id && (
|
||||||
|
<StyledBottomBarItem onClick={setPrivate} disabled={isUpdating}>
|
||||||
|
{isPrivate ? <AiOutlineLock /> : <AiOutlineUnlock />}
|
||||||
|
{isPrivate ? "Private" : "Public"}
|
||||||
|
</StyledBottomBarItem>
|
||||||
|
)}
|
||||||
|
<StyledBottomBarItem
|
||||||
|
onClick={() => setVisible("share")(true)}
|
||||||
|
disabled={isPrivate || !data}
|
||||||
|
>
|
||||||
|
<AiOutlineLink />
|
||||||
|
Share
|
||||||
|
</StyledBottomBarItem>
|
||||||
{liveTransform ? (
|
{liveTransform ? (
|
||||||
<StyledBottomBarItem onClick={() => toggleLiveTransform(false)}>
|
<StyledBottomBarItem onClick={() => toggleLiveTransform(false)}>
|
||||||
<VscSync />
|
<VscSync />
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { Modal, Group, Button, Badge, Avatar, Grid, Divider, ModalProps } from "@mantine/core";
|
import { Modal, Group, Button, Badge, Avatar, Grid, Divider, ModalProps } from "@mantine/core";
|
||||||
|
import { useUser as useSupaUser } from "@supabase/auth-helpers-react";
|
||||||
|
import dayjs from "dayjs";
|
||||||
import { IoRocketSharp } from "react-icons/io5";
|
import { IoRocketSharp } from "react-icons/io5";
|
||||||
import useModal from "src/store/useModal";
|
import useModal from "src/store/useModal";
|
||||||
import useUser from "src/store/useUser";
|
import useUser from "src/store/useUser";
|
||||||
@ -43,7 +45,7 @@ const StyledContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||||
const user = useUser(state => state.user);
|
const user = useSupaUser();
|
||||||
const isPremium = useUser(state => state.premium);
|
const isPremium = useUser(state => state.premium);
|
||||||
const premiumCancelled = useUser(state => state.premiumCancelled);
|
const premiumCancelled = useUser(state => state.premiumCancelled);
|
||||||
const setVisible = useModal(state => state.setVisible);
|
const setVisible = useModal(state => state.setVisible);
|
||||||
@ -51,16 +53,21 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal title="Account" opened={opened} onClose={onClose} centered>
|
<Modal title="Account" opened={opened} onClose={onClose} centered>
|
||||||
<StyledTitle>Hello, {user?.name}!</StyledTitle>
|
<StyledTitle>Hello, {user?.user_metadata.name}!</StyledTitle>
|
||||||
<Group py="sm">
|
<Group py="sm">
|
||||||
<Grid gutter="xs">
|
<Grid gutter="xs">
|
||||||
<Grid.Col span={2}>
|
<Grid.Col span={2}>
|
||||||
<Avatar size="lg" radius="lg" src={user?.profilePicture} alt={user?.name} />
|
<Avatar
|
||||||
|
size="lg"
|
||||||
|
radius="lg"
|
||||||
|
src={user?.user_metadata.avatar_url}
|
||||||
|
alt={user?.user_metadata.name}
|
||||||
|
/>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={4}>
|
<Grid.Col span={4}>
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
USERNAME
|
USERNAME
|
||||||
<div>{user?.name}</div>
|
<div>{user?.user_metadata.name}</div>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={6}>
|
<Grid.Col span={6}>
|
||||||
@ -82,15 +89,17 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
<Grid.Col span={6}>
|
<Grid.Col span={6}>
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
EMAIL
|
EMAIL
|
||||||
<div>{user?.email}</div>
|
<div>{user?.user_metadata.email}</div>
|
||||||
</StyledContainer>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={4}>
|
|
||||||
<StyledContainer>
|
|
||||||
REGISTRATION
|
|
||||||
<div>{user?.signUpAt && new Date(user.signUpAt).toDateString()}</div>
|
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
|
{user?.created_at && (
|
||||||
|
<Grid.Col span={4}>
|
||||||
|
<StyledContainer>
|
||||||
|
REGISTRATION
|
||||||
|
<div>{dayjs(user.created_at).format("DD MMMM YYYY")}</div>
|
||||||
|
</StyledContainer>
|
||||||
|
</Grid.Col>
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Group>
|
</Group>
|
||||||
<Divider py="xs" />
|
<Divider py="xs" />
|
||||||
|
@ -1,252 +1,242 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import styled from "styled-components";
|
|
||||||
import {
|
import {
|
||||||
Modal,
|
Modal,
|
||||||
Group,
|
|
||||||
Button,
|
|
||||||
Text,
|
Text,
|
||||||
Stack,
|
|
||||||
Loader,
|
|
||||||
Center,
|
|
||||||
Divider,
|
Divider,
|
||||||
ScrollArea,
|
ScrollArea,
|
||||||
ModalProps,
|
ModalProps,
|
||||||
|
Table,
|
||||||
|
ActionIcon,
|
||||||
|
Badge,
|
||||||
Paper,
|
Paper,
|
||||||
|
Flex,
|
||||||
|
DefaultMantineColor,
|
||||||
|
Input,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Stack,
|
||||||
|
RingProgress,
|
||||||
|
UnstyledButton,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
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 toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { AiOutlineEdit, AiOutlineLock, AiOutlinePlus, AiOutlineUnlock } from "react-icons/ai";
|
import { AiOutlineLink } from "react-icons/ai";
|
||||||
import { FaTrash } from "react-icons/fa";
|
import { FaTrash } from "react-icons/fa";
|
||||||
import { IoRocketSharp } from "react-icons/io5";
|
import { MdFileOpen } from "react-icons/md";
|
||||||
|
import { VscAdd, VscEdit } from "react-icons/vsc";
|
||||||
import { deleteJson, getAllJson, saveToCloud, updateJson } from "src/services/json";
|
import { deleteJson, getAllJson, saveToCloud, updateJson } from "src/services/json";
|
||||||
import useFile, { File } from "src/store/useFile";
|
import useFile, { File } from "src/store/useFile";
|
||||||
import useModal from "src/store/useModal";
|
|
||||||
import useUser from "src/store/useUser";
|
import useUser from "src/store/useUser";
|
||||||
|
import { FileFormat } from "src/types/models";
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
const StyledJsonCard = styled.a<{ active?: boolean; create?: boolean }>`
|
const colorByFormat: Record<FileFormat, DefaultMantineColor> = {
|
||||||
display: ${({ create }) => (create ? "block" : "flex")};
|
json: "orange",
|
||||||
align-items: center;
|
yaml: "blue",
|
||||||
justify-content: space-between;
|
xml: "red",
|
||||||
line-height: 20px;
|
toml: "dark",
|
||||||
padding: 6px;
|
csv: "grape",
|
||||||
border-radius: 3px;
|
};
|
||||||
overflow: hidden;
|
|
||||||
flex: 1;
|
|
||||||
height: 60px;
|
|
||||||
background: ${({ active }) => active && "rgb(48, 98, 197)"};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledInfo = styled.div``;
|
|
||||||
|
|
||||||
const StyledTitle = styled.div`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
width: fit-content;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
span {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledDetils = styled.div`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 12px;
|
|
||||||
gap: 4px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledCreateWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
gap: 6px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
opacity: 0.6;
|
|
||||||
height: 30px;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledNameInput = styled.input`
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
width: 90%;
|
|
||||||
color: ${({ theme }) => theme.SEAGREEN};
|
|
||||||
font-weight: 600;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const GraphCard: React.FC<{ data: File; refetch: () => void; active: boolean }> = ({
|
|
||||||
data,
|
|
||||||
refetch,
|
|
||||||
active,
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
const [editMode, setEditMode] = React.useState(false);
|
|
||||||
const [name, setName] = React.useState(data.name);
|
|
||||||
|
|
||||||
|
const UpdateNameModal: React.FC<{
|
||||||
|
file: File | null;
|
||||||
|
onClose: () => void;
|
||||||
|
refetch: () => void;
|
||||||
|
}> = ({ file, onClose, refetch }) => {
|
||||||
|
const [name, setName] = React.useState("");
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
|
if (!file) return;
|
||||||
toast
|
toast
|
||||||
.promise(updateJson(data._id, { name }), {
|
.promise(updateJson(file.id, { name }), {
|
||||||
loading: "Updating document...",
|
loading: "Updating document...",
|
||||||
error: "Error occurred while updating document!",
|
error: "Error occurred while updating document!",
|
||||||
success: `Renamed document to ${name}`,
|
success: `Renamed document to ${name}`,
|
||||||
})
|
})
|
||||||
.then(refetch);
|
.then(() => refetch());
|
||||||
|
|
||||||
setEditMode(false);
|
onClose();
|
||||||
};
|
|
||||||
|
|
||||||
const onDeleteClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
toast
|
|
||||||
.promise(deleteJson(data._id), {
|
|
||||||
loading: "Deleting file...",
|
|
||||||
error: "An error occurred while deleting the file!",
|
|
||||||
success: `Deleted ${name}!`,
|
|
||||||
})
|
|
||||||
.then(refetch);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper withBorder w="100%">
|
<Modal title="Update Document name" opened={!!file} onClose={onClose} centered>
|
||||||
<StyledJsonCard
|
<Stack>
|
||||||
href={`?json=${data._id}`}
|
<Input
|
||||||
as={editMode ? "div" : "a"}
|
value={name}
|
||||||
active={active}
|
placeholder={file?.name}
|
||||||
{...props}
|
onChange={e => setName(e.currentTarget.value)}
|
||||||
>
|
/>
|
||||||
<StyledInfo>
|
<Group position="right">
|
||||||
{editMode ? (
|
<Button variant="outline" onClick={onClose}>
|
||||||
<form onSubmit={onSubmit}>
|
Cancel
|
||||||
<StyledNameInput
|
</Button>
|
||||||
value={name}
|
<Button onClick={onSubmit}>Update</Button>
|
||||||
onChange={e => setName(e.currentTarget.value)}
|
</Group>
|
||||||
onClick={e => e.preventDefault()}
|
</Stack>
|
||||||
autoFocus
|
</Modal>
|
||||||
/>
|
|
||||||
<input type="submit" hidden />
|
|
||||||
</form>
|
|
||||||
) : (
|
|
||||||
<StyledTitle
|
|
||||||
onClick={e => {
|
|
||||||
e.preventDefault();
|
|
||||||
setEditMode(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>{name}</span>
|
|
||||||
<AiOutlineEdit />
|
|
||||||
</StyledTitle>
|
|
||||||
)}
|
|
||||||
<StyledDetils>
|
|
||||||
{data.private ? <AiOutlineLock /> : <AiOutlineUnlock />}
|
|
||||||
Last modified {dayjs(data.updatedAt).fromNow()}
|
|
||||||
</StyledDetils>
|
|
||||||
</StyledInfo>
|
|
||||||
<Button variant="subtle" color="red" onClick={onDeleteClick}>
|
|
||||||
<FaTrash />
|
|
||||||
</Button>
|
|
||||||
</StyledJsonCard>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const CreateCard: React.FC<{ reachedLimit: boolean }> = ({ reachedLimit }) => {
|
|
||||||
const { replace } = useRouter();
|
|
||||||
const isPremium = useUser(state => state.premium);
|
|
||||||
const getContents = useFile(state => state.getContents);
|
|
||||||
const setHasChanges = useFile(state => state.setHasChanges);
|
|
||||||
const setVisible = useModal(state => state.setVisible);
|
|
||||||
const getFormat = useFile(state => state.getFormat);
|
|
||||||
|
|
||||||
const onCreate = async () => {
|
|
||||||
try {
|
|
||||||
toast.loading("Saving document...", { id: "fileSave" });
|
|
||||||
const res = await saveToCloud(null, getContents(), getFormat());
|
|
||||||
|
|
||||||
if (res.errors && res.errors.items.length > 0) throw res.errors;
|
|
||||||
|
|
||||||
toast.success("Document saved to cloud", { id: "fileSave" });
|
|
||||||
setHasChanges(false);
|
|
||||||
replace({ query: { json: res.data._id } });
|
|
||||||
} catch (error: any) {
|
|
||||||
if (error?.items?.length > 0) {
|
|
||||||
return toast.error(error.items[0].message, { id: "fileSave", duration: 7000 });
|
|
||||||
}
|
|
||||||
toast.error("Failed to save document!", { id: "fileSave" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!isPremium && reachedLimit)
|
|
||||||
return (
|
|
||||||
<StyledJsonCard onClick={() => setVisible("premium")(true)}>
|
|
||||||
<StyledCreateWrapper>
|
|
||||||
<IoRocketSharp size="18" />
|
|
||||||
<Text fw="bold">You reached max limit, upgrade to premium for more!</Text>
|
|
||||||
</StyledCreateWrapper>
|
|
||||||
</StyledJsonCard>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledJsonCard onClick={onCreate} create>
|
|
||||||
<StyledCreateWrapper>
|
|
||||||
<AiOutlinePlus size="20" />
|
|
||||||
<Text fw="bold">Create New File</Text>
|
|
||||||
</StyledCreateWrapper>
|
|
||||||
</StyledJsonCard>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||||
const { isReady, query } = useRouter();
|
const totalQuota = useUser(state => (state.premium ? 200 : 25));
|
||||||
|
const getContents = useFile(state => state.getContents);
|
||||||
const { data, isFetching, refetch } = useQuery(["allJson", query], () => getAllJson(), {
|
const setHasChanges = useFile(state => state.setHasChanges);
|
||||||
|
const getFormat = useFile(state => state.getFormat);
|
||||||
|
const [currentFile, setCurrentFile] = React.useState<File | null>(null);
|
||||||
|
const { isReady, query, replace } = useRouter();
|
||||||
|
const { data, refetch } = useQuery(["allJson", query], () => getAllJson(), {
|
||||||
enabled: isReady && opened,
|
enabled: isReady && opened,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
const onCreate = async () => {
|
||||||
<Modal title="Saved On The Cloud" opened={opened} onClose={onClose} centered>
|
try {
|
||||||
<ScrollArea h={360}>
|
toast.loading("Saving document...", { id: "fileSave" });
|
||||||
<Stack py="sm">
|
const { data, error } = await saveToCloud({ contents: getContents(), format: getFormat() });
|
||||||
{isFetching ? (
|
|
||||||
<Center>
|
|
||||||
<Loader />
|
|
||||||
</Center>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<CreateCard reachedLimit={data ? data?.data.result.length > 15 : false} />
|
|
||||||
{data?.data?.result?.map(json => (
|
|
||||||
<GraphCard
|
|
||||||
data={json}
|
|
||||||
key={json._id}
|
|
||||||
refetch={refetch}
|
|
||||||
active={query?.json === json._id}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</ScrollArea>
|
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
|
||||||
|
toast.success("Document saved to cloud", { id: "fileSave" });
|
||||||
|
setHasChanges(false);
|
||||||
|
replace({ query: { json: data[0].id } });
|
||||||
|
onClose();
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.error("Failed to save document!", { id: "fileSave" });
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDeleteClick = React.useCallback(
|
||||||
|
(file: File) => {
|
||||||
|
toast
|
||||||
|
.promise(deleteJson(file.id), {
|
||||||
|
loading: "Deleting file...",
|
||||||
|
error: "An error occurred while deleting the file!",
|
||||||
|
success: `Deleted ${file.name}!`,
|
||||||
|
})
|
||||||
|
.then(() => refetch());
|
||||||
|
},
|
||||||
|
[refetch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const copyShareLink = React.useCallback((fileId: string) => {
|
||||||
|
const shareLink = `${window.location.origin}/?json=${fileId}`;
|
||||||
|
navigator.clipboard.writeText(shareLink);
|
||||||
|
toast.success("Copied share link to clipboard!");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const rows = React.useMemo(
|
||||||
|
() =>
|
||||||
|
data?.map(element => (
|
||||||
|
<tr key={element.id}>
|
||||||
|
<td>{element.id}</td>
|
||||||
|
<td>
|
||||||
|
<Flex align="center" justify="space-between">
|
||||||
|
{element.name}
|
||||||
|
<ActionIcon color="cyan" onClick={() => setCurrentFile(element)}>
|
||||||
|
<VscEdit />
|
||||||
|
</ActionIcon>
|
||||||
|
</Flex>
|
||||||
|
</td>
|
||||||
|
<td>{dayjs(element.created_at).format("DD.MM.YYYY")}</td>
|
||||||
|
<td>
|
||||||
|
<Badge variant="light" color={colorByFormat[element.format]} size="sm">
|
||||||
|
{element.format.toUpperCase()}
|
||||||
|
</Badge>
|
||||||
|
</td>
|
||||||
|
<td>{element.views.toLocaleString("en-US")}</td>
|
||||||
|
<td>
|
||||||
|
<Flex gap="xs">
|
||||||
|
<Link href={`?json=${element.id}`} prefetch={false}>
|
||||||
|
<ActionIcon color="blue" onClick={onClose}>
|
||||||
|
<MdFileOpen size="18" />
|
||||||
|
</ActionIcon>
|
||||||
|
</Link>
|
||||||
|
<ActionIcon color="red" onClick={() => onDeleteClick(element)}>
|
||||||
|
<FaTrash size="18" />
|
||||||
|
</ActionIcon>
|
||||||
|
<ActionIcon color="green" onClick={() => copyShareLink(element.id)}>
|
||||||
|
<AiOutlineLink />
|
||||||
|
</ActionIcon>
|
||||||
|
</Flex>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)),
|
||||||
|
[data, copyShareLink, onClose, onDeleteClick]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="Saved On The Cloud"
|
||||||
|
opened={opened}
|
||||||
|
size="xl"
|
||||||
|
onClose={onClose}
|
||||||
|
centered
|
||||||
|
hidden={!!currentFile}
|
||||||
|
>
|
||||||
|
<Paper>
|
||||||
|
<ScrollArea h={360} offsetScrollbars>
|
||||||
|
<Table fontSize="xs" verticalSpacing="xs" striped>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Create Date</th>
|
||||||
|
<th>Format</th>
|
||||||
|
<th>Views</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{rows}</tbody>
|
||||||
|
</Table>
|
||||||
|
</ScrollArea>
|
||||||
|
</Paper>
|
||||||
|
{data && (
|
||||||
|
<Flex gap="md">
|
||||||
|
<Paper my="lg" withBorder radius="md" p="xs" w="100%">
|
||||||
|
<Group>
|
||||||
|
<RingProgress
|
||||||
|
size={60}
|
||||||
|
roundCaps
|
||||||
|
thickness={8}
|
||||||
|
sections={[
|
||||||
|
{
|
||||||
|
value: (data.length * 100) / totalQuota,
|
||||||
|
color: data.length > totalQuota / 1.5 ? "red" : "blue",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<Text color="dimmed" size="xs" transform="uppercase" weight={700}>
|
||||||
|
Total Quota
|
||||||
|
</Text>
|
||||||
|
<Text weight={700} size="xl">
|
||||||
|
{data.length} / {totalQuota}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
<Paper my="lg" withBorder radius="md" p="xs" w={250}>
|
||||||
|
<UnstyledButton fw="bold" w="100%" h="100%" onClick={onCreate}>
|
||||||
|
<Text fz="md" align="center" color="blue">
|
||||||
|
<Flex align="center" justify="center">
|
||||||
|
<VscAdd size="24" />
|
||||||
|
Create New Document
|
||||||
|
</Flex>
|
||||||
|
</Text>
|
||||||
|
</UnstyledButton>
|
||||||
|
</Paper>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
<Divider py="xs" />
|
<Divider py="xs" />
|
||||||
<Group position="right">
|
<Text fz="xs">
|
||||||
<Text fz="xs">
|
The Cloud Save feature is primarily designed for convenient access and is not advisable for
|
||||||
Cloud Save feature is for ease-of-access only and not recommended to store sensitive data,
|
storing sensitive data.
|
||||||
we don't guarantee protection of your data.
|
</Text>
|
||||||
</Text>
|
<UpdateNameModal file={currentFile} onClose={() => setCurrentFile(null)} refetch={refetch} />
|
||||||
</Group>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,6 @@ import {
|
|||||||
Badge,
|
Badge,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { BsCheck } from "react-icons/bs";
|
import { BsCheck } from "react-icons/bs";
|
||||||
import { paymentURL } from "src/constants/data";
|
|
||||||
|
|
||||||
export const PremiumModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
export const PremiumModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||||
return (
|
return (
|
||||||
@ -53,7 +52,7 @@ export const PremiumModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
</Title>
|
</Title>
|
||||||
<Button
|
<Button
|
||||||
component="a"
|
component="a"
|
||||||
href={paymentURL()}
|
href="/pricing"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
color="teal"
|
color="teal"
|
||||||
size="md"
|
size="md"
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
ModalProps,
|
ModalProps,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { FiExternalLink } from "react-icons/fi";
|
||||||
import { MdCheck, MdCopyAll } from "react-icons/md";
|
import { MdCheck, MdCopyAll } from "react-icons/md";
|
||||||
|
|
||||||
export const ShareModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
export const ShareModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||||
@ -42,7 +43,14 @@ export const ShareModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
<Text fz="sm" fw={700}>
|
<Text fz="sm" fw={700}>
|
||||||
Embed into your website
|
Embed into your website
|
||||||
</Text>
|
</Text>
|
||||||
<Button component="a" color="green" target="_blank" href="/docs" fullWidth>
|
<Button
|
||||||
|
component="a"
|
||||||
|
color="green"
|
||||||
|
target="_blank"
|
||||||
|
href="/docs"
|
||||||
|
leftIcon={<FiExternalLink />}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
Learn How to Embed
|
Learn How to Embed
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -3,6 +3,7 @@ import Link from "next/link";
|
|||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { Button } from "@mantine/core";
|
import { Button } from "@mantine/core";
|
||||||
import { FaStar } from "react-icons/fa";
|
import { FaStar } from "react-icons/fa";
|
||||||
|
import useUser from "src/store/useUser";
|
||||||
import { JSONCrackLogo } from "../JsonCrackLogo";
|
import { JSONCrackLogo } from "../JsonCrackLogo";
|
||||||
|
|
||||||
const StyledNavbarWrapper = styled.div`
|
const StyledNavbarWrapper = styled.div`
|
||||||
@ -43,6 +44,8 @@ const Right = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const Navbar = () => {
|
export const Navbar = () => {
|
||||||
|
const isAuthenticated = useUser(state => state.isAuthenticated);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledNavbarWrapper>
|
<StyledNavbarWrapper>
|
||||||
<StyledNavbar>
|
<StyledNavbar>
|
||||||
@ -86,11 +89,13 @@ export const Navbar = () => {
|
|||||||
>
|
>
|
||||||
Star us on GitHub
|
Star us on GitHub
|
||||||
</Button>
|
</Button>
|
||||||
<Link href="/sign-in" prefetch={false}>
|
{!isAuthenticated && (
|
||||||
<Button variant="light" radius="md" className="hide-mobile">
|
<Link href="/sign-in" prefetch={false}>
|
||||||
Sign In
|
<Button variant="light" radius="md" className="hide-mobile">
|
||||||
</Button>
|
Sign In
|
||||||
</Link>
|
</Button>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
<Link href="/editor" prefetch={false}>
|
<Link href="/editor" prefetch={false}>
|
||||||
<Button color="pink" radius="md">
|
<Button color="pink" radius="md">
|
||||||
Editor
|
Editor
|
||||||
|
10
src/lib/api/supabase.ts
Normal file
10
src/lib/api/supabase.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { createPagesBrowserClient } from "@supabase/auth-helpers-nextjs";
|
||||||
|
|
||||||
|
// Create a single supabase client for interacting with your database
|
||||||
|
const supabase = createPagesBrowserClient({
|
||||||
|
supabaseUrl: "https://bxkgqurwqjmvrqekcbws.supabase.co",
|
||||||
|
supabaseKey:
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJ4a2dxdXJ3cWptdnJxZWtjYndzIiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTA2NDU0MjUsImV4cCI6MjAwNjIyMTQyNX0.3nZ0yhuFjnI3yHbAL8S9UtK-Ny-6F5AylNHgo1tymTU",
|
||||||
|
});
|
||||||
|
|
||||||
|
export { supabase };
|
@ -4,10 +4,13 @@ import dynamic from "next/dynamic";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { StyleSheetManager, ThemeProvider } from "styled-components";
|
import { StyleSheetManager, ThemeProvider } from "styled-components";
|
||||||
import { MantineProvider, MantineThemeOverride } from "@mantine/core";
|
import { MantineProvider, MantineThemeOverride } from "@mantine/core";
|
||||||
|
import { SessionContextProvider, Session } from "@supabase/auth-helpers-react";
|
||||||
import { pageview } from "react-ga";
|
import { pageview } from "react-ga";
|
||||||
import { monaSans } from "src/constants/fonts";
|
import { monaSans } from "src/constants/fonts";
|
||||||
import GlobalStyle from "src/constants/globalStyle";
|
import GlobalStyle from "src/constants/globalStyle";
|
||||||
import { lightTheme } from "src/constants/theme";
|
import { lightTheme } from "src/constants/theme";
|
||||||
|
import { supabase } from "src/lib/api/supabase";
|
||||||
|
import useUser from "src/store/useUser";
|
||||||
|
|
||||||
const Toaster = dynamic(() => import("react-hot-toast").then(c => c.Toaster));
|
const Toaster = dynamic(() => import("react-hot-toast").then(c => c.Toaster));
|
||||||
const ExternalMode = dynamic(() => import("src/layout/ExternalMode"));
|
const ExternalMode = dynamic(() => import("src/layout/ExternalMode"));
|
||||||
@ -21,8 +24,20 @@ const mantineTheme: MantineThemeOverride = {
|
|||||||
primaryShade: 8,
|
primaryShade: 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
function JsonCrack({ Component, pageProps }: AppProps) {
|
function JsonCrack({
|
||||||
|
Component,
|
||||||
|
pageProps,
|
||||||
|
}: AppProps<{
|
||||||
|
initialSession: Session;
|
||||||
|
}>) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const setSession = useUser(state => state.setSession);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
supabase.auth.getSession().then(({ data: { session } }) => {
|
||||||
|
if (session) setSession(session);
|
||||||
|
});
|
||||||
|
}, [setSession]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const handleRouteChange = (url: string) => {
|
const handleRouteChange = (url: string) => {
|
||||||
@ -37,7 +52,7 @@ function JsonCrack({ Component, pageProps }: AppProps) {
|
|||||||
}, [router.events]);
|
}, [router.events]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<SessionContextProvider supabaseClient={supabase}>
|
||||||
<GoogleAnalytics />
|
<GoogleAnalytics />
|
||||||
<StyleSheetManager>
|
<StyleSheetManager>
|
||||||
<ThemeProvider theme={lightTheme}>
|
<ThemeProvider theme={lightTheme}>
|
||||||
@ -63,7 +78,7 @@ function JsonCrack({ Component, pageProps }: AppProps) {
|
|||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</StyleSheetManager>
|
</StyleSheetManager>
|
||||||
</>
|
</SessionContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,17 +39,17 @@ const StyledContentBody = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledHighlight = styled.span<{ link?: boolean; alert?: boolean }>`
|
const StyledHighlight = styled.span<{ $link?: boolean; $alert?: boolean }>`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
color: ${({ theme, link, alert }) =>
|
color: ${({ theme, $link, $alert }) =>
|
||||||
alert ? theme.DANGER : link ? theme.BLURPLE : theme.TEXT_POSITIVE};
|
$alert ? theme.DANGER : $link ? theme.BLURPLE : theme.TEXT_POSITIVE};
|
||||||
background: ${({ theme }) => theme.BACKGROUND_TERTIARY};
|
background: ${({ theme }) => theme.BACKGROUND_TERTIARY};
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin: ${({ alert }) => (alert ? "8px 0" : "1px")};
|
margin: ${({ $alert }) => ($alert ? "8px 0" : "1px")};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Docs = () => {
|
const Docs = () => {
|
||||||
@ -74,7 +74,7 @@ const Docs = () => {
|
|||||||
<StyledHighlight
|
<StyledHighlight
|
||||||
as="a"
|
as="a"
|
||||||
href="https://jsoncrack.com/editor?json=https://catfact.ninja/fact"
|
href="https://jsoncrack.com/editor?json=https://catfact.ninja/fact"
|
||||||
link
|
$link
|
||||||
>
|
>
|
||||||
https://jsoncrack.com/editor?json=https://catfact.ninja/fact
|
https://jsoncrack.com/editor?json=https://catfact.ninja/fact
|
||||||
</StyledHighlight>
|
</StyledHighlight>
|
||||||
@ -103,7 +103,6 @@ const Docs = () => {
|
|||||||
<StyledHighlight>?json=639b65c5a82efc29a24b2de2</StyledHighlight>
|
<StyledHighlight>?json=639b65c5a82efc29a24b2de2</StyledHighlight>
|
||||||
</StyledDescription>
|
</StyledDescription>
|
||||||
<StyledFrame
|
<StyledFrame
|
||||||
scrolling="no"
|
|
||||||
title="Untitled"
|
title="Untitled"
|
||||||
src="https://codepen.io/AykutSarac/embed/vYaORgM?default-tab=html%2Cresult"
|
src="https://codepen.io/AykutSarac/embed/vYaORgM?default-tab=html%2Cresult"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@ -125,7 +124,7 @@ const Docs = () => {
|
|||||||
<StyledHighlight
|
<StyledHighlight
|
||||||
as="a"
|
as="a"
|
||||||
href="https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage"
|
href="https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/postMessage"
|
||||||
link
|
$link
|
||||||
>
|
>
|
||||||
MessagePort
|
MessagePort
|
||||||
</StyledHighlight>
|
</StyledHighlight>
|
||||||
|
@ -9,7 +9,6 @@ import { EditorWrapper } from "src/layout/EditorWrapper";
|
|||||||
import { Loading } from "src/layout/Loading";
|
import { Loading } from "src/layout/Loading";
|
||||||
import useFile from "src/store/useFile";
|
import useFile from "src/store/useFile";
|
||||||
import useJson from "src/store/useJson";
|
import useJson from "src/store/useJson";
|
||||||
import useUser from "src/store/useUser";
|
|
||||||
|
|
||||||
const Panes = dynamic(() => import("src/containers/Editor/Panes"));
|
const Panes = dynamic(() => import("src/containers/Editor/Panes"));
|
||||||
|
|
||||||
@ -29,15 +28,13 @@ export const StyledEditorWrapper = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const EditorPage: React.FC = () => {
|
const EditorPage: React.FC = () => {
|
||||||
const { query } = useRouter();
|
const { query, isReady } = useRouter();
|
||||||
const checkSession = useUser(state => state.checkSession);
|
|
||||||
const checkEditorSession = useFile(state => state.checkEditorSession);
|
const checkEditorSession = useFile(state => state.checkEditorSession);
|
||||||
const loading = useJson(state => state.loading);
|
const loading = useJson(state => state.loading);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
checkSession();
|
if (isReady) checkEditorSession({ url: query?.url, json: query?.json });
|
||||||
checkEditorSession({ url: query?.url, json: query?.json });
|
}, [checkEditorSession, isReady, query]);
|
||||||
}, [checkEditorSession, checkSession, query]);
|
|
||||||
|
|
||||||
if (loading) return <Loading message="Fetching JSON from cloud..." />;
|
if (loading) return <Loading message="Fetching JSON from cloud..." />;
|
||||||
|
|
||||||
|
@ -3,12 +3,12 @@ import Head from "next/head";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { PaperProps, Center, Button, Group, Paper, Stack, TextInput, Text } from "@mantine/core";
|
import { PaperProps, Center, Button, Group, Paper, Stack, TextInput, Text } from "@mantine/core";
|
||||||
|
import { useSession } from "@supabase/auth-helpers-react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { Footer } from "src/layout/Footer";
|
import { Footer } from "src/layout/Footer";
|
||||||
import { JSONCrackLogo } from "src/layout/JsonCrackLogo";
|
import { JSONCrackLogo } from "src/layout/JsonCrackLogo";
|
||||||
import { Navbar } from "src/layout/Navbar";
|
import { Navbar } from "src/layout/Navbar";
|
||||||
import { altogic } from "src/lib/api/altogic";
|
import { supabase } from "src/lib/api/supabase";
|
||||||
import useUser from "src/store/useUser";
|
|
||||||
|
|
||||||
const StyledPageWrapper = styled.div`
|
const StyledPageWrapper = styled.div`
|
||||||
height: calc(100vh - 27px);
|
height: calc(100vh - 27px);
|
||||||
@ -29,12 +29,11 @@ export function AuthenticationForm(props: PaperProps) {
|
|||||||
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
altogic.auth
|
supabase.auth
|
||||||
.sendResetPwdEmail(email)
|
.resetPasswordForEmail(email, { redirectTo: "/reset-password" })
|
||||||
.then(res => {
|
.then(({ error }) => {
|
||||||
if (res.errors) {
|
if (error) return toast.error(error.message);
|
||||||
toast.error(res.errors.items[0].message);
|
setSuccess(true);
|
||||||
} else setSuccess(true);
|
|
||||||
})
|
})
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
};
|
};
|
||||||
@ -79,14 +78,12 @@ const StyledHeroSection = styled.section`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const SignIn = () => {
|
const SignIn = () => {
|
||||||
const { isReady, replace } = useRouter();
|
const { isReady, push } = useRouter();
|
||||||
const checkSession = useUser(state => state.checkSession);
|
const session = useSession();
|
||||||
const isAuthenticated = useUser(state => state.isAuthenticated);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!isReady) checkSession();
|
if (session) push("/editor");
|
||||||
if (isAuthenticated) replace("/");
|
}, [isReady, session, push]);
|
||||||
}, [isReady, isAuthenticated, replace, checkSession]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -20,16 +20,18 @@ import {
|
|||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useForm } from "@mantine/form";
|
import { useForm } from "@mantine/form";
|
||||||
import { useToggle, upperFirst } from "@mantine/hooks";
|
import { useToggle, upperFirst } from "@mantine/hooks";
|
||||||
|
import { useSession } from "@supabase/auth-helpers-react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { AiOutlineGithub, AiOutlineGoogle } from "react-icons/ai";
|
import { AiOutlineGithub, AiOutlineGoogle } from "react-icons/ai";
|
||||||
import { Footer } from "src/layout/Footer";
|
import { Footer } from "src/layout/Footer";
|
||||||
import { JSONCrackLogo } from "src/layout/JsonCrackLogo";
|
import { JSONCrackLogo } from "src/layout/JsonCrackLogo";
|
||||||
import { Navbar } from "src/layout/Navbar";
|
import { Navbar } from "src/layout/Navbar";
|
||||||
import { altogic } from "src/lib/api/altogic";
|
import { altogic } from "src/lib/api/altogic";
|
||||||
|
import { supabase } from "src/lib/api/supabase";
|
||||||
import useUser from "src/store/useUser";
|
import useUser from "src/store/useUser";
|
||||||
|
|
||||||
export function AuthenticationForm(props: PaperProps) {
|
export function AuthenticationForm(props: PaperProps) {
|
||||||
const login = useUser(state => state.login);
|
const setSession = useUser(state => state.setSession);
|
||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
const [type, toggle] = useToggle<"login" | "register">(["login", "register"]);
|
const [type, toggle] = useToggle<"login" | "register">(["login", "register"]);
|
||||||
const [done, setDone] = React.useState(false);
|
const [done, setDone] = React.useState(false);
|
||||||
@ -59,29 +61,34 @@ export function AuthenticationForm(props: PaperProps) {
|
|||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
if (type === "login") {
|
if (type === "login") {
|
||||||
altogic.auth
|
supabase.auth
|
||||||
.signInWithEmail(form.values.email, form.values.password)
|
.signInWithPassword({
|
||||||
.then(({ user, errors }) => {
|
email: form.values.email,
|
||||||
if (errors?.items.length || !user) return toast.error("Incorrect email or password!");
|
password: form.values.password,
|
||||||
login(user as any);
|
})
|
||||||
|
.then(({ data, error }) => {
|
||||||
|
if (error) return toast.error(error.message);
|
||||||
|
setSession(data.session);
|
||||||
})
|
})
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
} else {
|
} else {
|
||||||
altogic.auth
|
supabase.auth
|
||||||
.signUpWithEmail(form.values.email, form.values.password, form.values.name)
|
.signInWithPassword({
|
||||||
.then(({ errors }) => {
|
email: form.values.email,
|
||||||
if (errors?.items.length) {
|
password: form.values.password,
|
||||||
return errors.items.forEach(e => toast.error(e.message));
|
})
|
||||||
}
|
.then(({ data, error }) => {
|
||||||
|
if (error) return toast.error(error.message);
|
||||||
toast.success("Registration successful!");
|
toast.success("Registration successful!");
|
||||||
setDone(true);
|
setDone(true);
|
||||||
|
setSession(data.session);
|
||||||
})
|
})
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLoginClick = (provider: "github" | "google") => {
|
const handleLoginClick = (provider: "github" | "google") => {
|
||||||
altogic.auth.signInWithProvider(provider);
|
supabase.auth.signInWithOAuth({ provider });
|
||||||
};
|
};
|
||||||
|
|
||||||
if (done) {
|
if (done) {
|
||||||
@ -225,6 +232,10 @@ function ResetPassword(props: PaperProps) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (query?.access_token && typeof query?.access_token === "string") {
|
if (query?.access_token && typeof query?.access_token === "string") {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
// TODO: solve here
|
||||||
|
supabase.auth.updateUser({
|
||||||
|
password,
|
||||||
|
});
|
||||||
altogic.auth
|
altogic.auth
|
||||||
.resetPwdWithToken(query?.access_token, password)
|
.resetPwdWithToken(query?.access_token, password)
|
||||||
.then(({ errors }) => {
|
.then(({ errors }) => {
|
||||||
@ -275,14 +286,12 @@ function ResetPassword(props: PaperProps) {
|
|||||||
|
|
||||||
const SignIn = () => {
|
const SignIn = () => {
|
||||||
const { isReady, push, query } = useRouter();
|
const { isReady, push, query } = useRouter();
|
||||||
const isAuthenticated = useUser(state => state.isAuthenticated);
|
const session = useSession();
|
||||||
const checkSession = useUser(state => state.checkSession);
|
|
||||||
const isPasswordReset = query?.action === "reset-pwd" && !query?.error;
|
const isPasswordReset = query?.action === "reset-pwd" && !query?.error;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isAuthenticated) push("/editor");
|
if (session) push("/editor");
|
||||||
else checkSession();
|
}, [isReady, session, push]);
|
||||||
}, [isReady, isAuthenticated, push, checkSession]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -1,42 +1,50 @@
|
|||||||
import dayjs from "dayjs";
|
import { PostgrestSingleResponse } from "@supabase/supabase-js";
|
||||||
import { decompressFromBase64 } from "lz-string";
|
import toast from "react-hot-toast";
|
||||||
import { altogic, AltogicResponse } from "src/lib/api/altogic";
|
import { supabase } from "src/lib/api/supabase";
|
||||||
import { File } from "src/store/useFile";
|
import { File } from "src/store/useFile";
|
||||||
|
import useUser from "src/store/useUser";
|
||||||
import { FileFormat } from "src/types/models";
|
import { FileFormat } from "src/types/models";
|
||||||
|
|
||||||
const saveToCloud = async (
|
type CloudSave = {
|
||||||
id: string | null,
|
id?: string;
|
||||||
contents: string,
|
contents: string;
|
||||||
format = FileFormat.JSON
|
format: FileFormat;
|
||||||
): Promise<AltogicResponse<{ _id: string }>> => {
|
|
||||||
if (id) return await altogic.endpoint.put(`json/${id}`, { json: contents, format });
|
|
||||||
return await altogic.endpoint.post("json", { json: contents, format });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFromCloud = async (id: string) => {
|
const saveToCloud = async ({
|
||||||
const { data, errors } = await altogic.endpoint.get(`json/${id}`, undefined, {
|
id,
|
||||||
userid: altogic.auth.getUser()?._id,
|
contents,
|
||||||
});
|
format = FileFormat.JSON,
|
||||||
|
}: CloudSave): Promise<PostgrestSingleResponse<{ id: string }[]>> => {
|
||||||
|
return await supabase.from("document").upsert({ id, content: contents, format }).select("id");
|
||||||
|
};
|
||||||
|
|
||||||
if (errors) throw errors;
|
const getFromCloud = async (doc_id: string): Promise<PostgrestSingleResponse<File[]>> => {
|
||||||
|
return await supabase.rpc("get_document_by_id", { doc_id });
|
||||||
|
};
|
||||||
|
|
||||||
const isCompressed = dayjs("2023-04-20T07:04:25.255Z").isAfter(data?.updatedAt);
|
const getAllJson = async (): Promise<File[]> => {
|
||||||
|
const userId = useUser.getState().user?.id;
|
||||||
|
if (!userId) return [];
|
||||||
|
|
||||||
if (isCompressed) {
|
const { data, error } = await supabase
|
||||||
return { ...data, json: decompressFromBase64(data.json) };
|
.from("document")
|
||||||
|
.select()
|
||||||
|
.eq("owner_id", userId)
|
||||||
|
.order("created_at", { ascending: false });
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
toast.error(error.message);
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAllJson = async (): Promise<AltogicResponse<{ result: File[] }>> =>
|
const updateJson = async (id: string, data: object) => {
|
||||||
await altogic.endpoint.get("json");
|
return await supabase.from("document").update(data).eq("id", id).select("private");
|
||||||
|
};
|
||||||
|
|
||||||
const updateJson = async (id: string, data: object) =>
|
const deleteJson = async (id: string) => await supabase.from("document").delete().eq("id", id);
|
||||||
await altogic.endpoint.put(`json/${id}`, {
|
|
||||||
...data,
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteJson = async (id: string) => await altogic.endpoint.delete(`json/${id}`);
|
|
||||||
|
|
||||||
export { saveToCloud, getFromCloud, getAllJson, updateJson, deleteJson };
|
export { saveToCloud, getFromCloud, getAllJson, updateJson, deleteJson };
|
||||||
|
@ -7,7 +7,7 @@ import { create } from "zustand";
|
|||||||
import { defaultJson } from "src/constants/data";
|
import { defaultJson } from "src/constants/data";
|
||||||
import { contentToJson, jsonToContent } from "src/lib/utils/json/jsonAdapter";
|
import { contentToJson, jsonToContent } from "src/lib/utils/json/jsonAdapter";
|
||||||
import { isIframe } from "src/lib/utils/widget";
|
import { isIframe } from "src/lib/utils/widget";
|
||||||
import { getFromCloud, saveToCloud } from "src/services/json";
|
import { getFromCloud } from "src/services/json";
|
||||||
import { FileFormat } from "src/types/models";
|
import { FileFormat } from "src/types/models";
|
||||||
import useGraph from "./useGraph";
|
import useGraph from "./useGraph";
|
||||||
import useJson from "./useJson";
|
import useJson from "./useJson";
|
||||||
@ -29,7 +29,6 @@ interface JsonActions {
|
|||||||
setError: (error: object | null) => void;
|
setError: (error: object | null) => void;
|
||||||
setHasChanges: (hasChanges: boolean) => void;
|
setHasChanges: (hasChanges: boolean) => void;
|
||||||
setContents: (data: SetContents) => void;
|
setContents: (data: SetContents) => void;
|
||||||
saveToCloud: (isNew?: boolean) => void;
|
|
||||||
fetchFile: (fileId: string) => void;
|
fetchFile: (fileId: string) => void;
|
||||||
fetchUrl: (url: string) => void;
|
fetchUrl: (url: string) => void;
|
||||||
editContents: (path: string, value: string, callback?: () => void) => void;
|
editContents: (path: string, value: string, callback?: () => void) => void;
|
||||||
@ -41,13 +40,15 @@ interface JsonActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type File = {
|
export type File = {
|
||||||
_id: string;
|
id: string;
|
||||||
|
views: number;
|
||||||
|
owner_id: string;
|
||||||
name: string;
|
name: string;
|
||||||
json: string;
|
content: string;
|
||||||
private: boolean;
|
private: boolean;
|
||||||
format?: FileFormat;
|
format: FileFormat;
|
||||||
createdAt: string;
|
created_at: string;
|
||||||
updatedAt: string;
|
updated_at: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialStates = {
|
const initialStates = {
|
||||||
@ -96,7 +97,7 @@ const useFile = create<FileStates & JsonActions>()((set, get) => ({
|
|||||||
},
|
},
|
||||||
setFile: fileData => {
|
setFile: fileData => {
|
||||||
set({ fileData, format: fileData.format || FileFormat.JSON });
|
set({ fileData, format: fileData.format || FileFormat.JSON });
|
||||||
get().setContents({ contents: fileData.json, hasChanges: false });
|
get().setContents({ contents: fileData.content, hasChanges: false });
|
||||||
},
|
},
|
||||||
getContents: () => get().contents,
|
getContents: () => get().contents,
|
||||||
getFormat: () => get().format,
|
getFormat: () => get().format,
|
||||||
@ -109,7 +110,7 @@ const useFile = create<FileStates & JsonActions>()((set, get) => ({
|
|||||||
const contentJson = await contentToJson(get().contents, prevFormat);
|
const contentJson = await contentToJson(get().contents, prevFormat);
|
||||||
const jsonContent = await jsonToContent(JSON.stringify(contentJson, null, 2), format);
|
const jsonContent = await jsonToContent(JSON.stringify(contentJson, null, 2), format);
|
||||||
|
|
||||||
get().setContents({ contents: jsonContent, hasChanges: false });
|
get().setContents({ contents: jsonContent });
|
||||||
|
|
||||||
event({ action: "change_data_format", category: "User" });
|
event({ action: "change_data_format", category: "User" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -121,11 +122,12 @@ const useFile = create<FileStates & JsonActions>()((set, get) => ({
|
|||||||
try {
|
try {
|
||||||
set({ ...(contents && { contents }), error: null, hasChanges });
|
set({ ...(contents && { contents }), error: null, hasChanges });
|
||||||
|
|
||||||
|
const isFetchURL = window.location.href.includes("?");
|
||||||
const json = await contentToJson(get().contents, get().format);
|
const json = await contentToJson(get().contents, get().format);
|
||||||
|
|
||||||
if (!useStored.getState().liveTransform && skipUpdate) return;
|
if (!useStored.getState().liveTransform && skipUpdate) return;
|
||||||
|
|
||||||
if (contents && contents.length < 80_000 && !isIframe()) {
|
if (contents && contents.length < 80_000 && !isIframe() && !isFetchURL) {
|
||||||
sessionStorage.setItem("content", contents);
|
sessionStorage.setItem("content", contents);
|
||||||
sessionStorage.setItem("format", get().format);
|
sessionStorage.setItem("format", get().format);
|
||||||
}
|
}
|
||||||
@ -140,30 +142,6 @@ const useFile = create<FileStates & JsonActions>()((set, get) => ({
|
|||||||
},
|
},
|
||||||
setError: error => set({ error }),
|
setError: error => set({ error }),
|
||||||
setHasChanges: hasChanges => set({ hasChanges }),
|
setHasChanges: hasChanges => set({ hasChanges }),
|
||||||
saveToCloud: async (isNew = true) => {
|
|
||||||
try {
|
|
||||||
const url = new URL(window.location.href);
|
|
||||||
const params = new URLSearchParams(url.search);
|
|
||||||
const jsonQuery = params.get("doc");
|
|
||||||
|
|
||||||
toast.loading("Saving File...", { id: "fileSave" });
|
|
||||||
const res = await saveToCloud(isNew ? null : jsonQuery, get().contents, get().format);
|
|
||||||
|
|
||||||
if (res.errors && res.errors.items.length > 0) throw res.errors;
|
|
||||||
|
|
||||||
toast.success("File saved to cloud", { id: "fileSave" });
|
|
||||||
set({ hasChanges: false });
|
|
||||||
return res.data._id;
|
|
||||||
} catch (error: any) {
|
|
||||||
if (error?.items?.length > 0) {
|
|
||||||
toast.error(error.items[0].message, { id: "fileSave", duration: 5000 });
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.error("Failed to save File!", { id: "fileSave" });
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fetchUrl: async url => {
|
fetchUrl: async url => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
@ -178,7 +156,7 @@ const useFile = create<FileStates & JsonActions>()((set, get) => ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
checkEditorSession: ({ url, json }) => {
|
checkEditorSession: ({ url, json }) => {
|
||||||
if (typeof url === "string") return get().fetchUrl(url);
|
if (typeof url === "string" && isURL(url)) return get().fetchUrl(url);
|
||||||
if (typeof json === "string") return get().fetchFile(json);
|
if (typeof json === "string") return get().fetchFile(json);
|
||||||
|
|
||||||
const sessionContent = sessionStorage.getItem("content") as string | null;
|
const sessionContent = sessionStorage.getItem("content") as string | null;
|
||||||
@ -190,15 +168,14 @@ const useFile = create<FileStates & JsonActions>()((set, get) => ({
|
|||||||
},
|
},
|
||||||
fetchFile: async id => {
|
fetchFile: async id => {
|
||||||
try {
|
try {
|
||||||
if (isURL(id)) return get().fetchUrl(id);
|
const { data, error } = await getFromCloud(id);
|
||||||
|
if (error) throw error;
|
||||||
|
|
||||||
const file = await getFromCloud(id);
|
if (data?.length) get().setFile(data[0]);
|
||||||
|
if (data?.length === 0) throw new Error("Document not found");
|
||||||
get().setFile(file);
|
} catch (error: any) {
|
||||||
} catch (error) {
|
if (error?.message) toast.error(error?.message);
|
||||||
useJson.setState({ loading: false });
|
get().setContents({ contents: defaultJson, hasChanges: false });
|
||||||
useGraph.setState({ loading: false });
|
|
||||||
console.error(error);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editContents: async (path, value, callback) => {
|
editContents: async (path, value, callback) => {
|
||||||
|
@ -27,7 +27,7 @@ const initialStates: ModalState = {
|
|||||||
cancelPremium: false,
|
cancelPremium: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const authModals: Modal[] = ["cloud", "share", "account", "schema"];
|
const authModals: Modal[] = ["cloud", "account", "schema"];
|
||||||
const premiumModals: Modal[] = ["schema"];
|
const premiumModals: Modal[] = ["schema"];
|
||||||
|
|
||||||
const useModal = create<ModalState & ModalActions>()(set => ({
|
const useModal = create<ModalState & ModalActions>()(set => ({
|
||||||
|
@ -1,31 +1,25 @@
|
|||||||
import { setUser } from "@sentry/nextjs";
|
import { Session, User } from "@supabase/supabase-js";
|
||||||
import { set as gaSet } from "react-ga";
|
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { altogic } from "src/lib/api/altogic";
|
import { supabase } from "src/lib/api/supabase";
|
||||||
import { AltogicAuth, User } from "src/types/altogic";
|
|
||||||
import useModal from "./useModal";
|
|
||||||
|
|
||||||
const isDevelopment = process.env.NODE_ENV === "development";
|
|
||||||
|
|
||||||
interface UserActions {
|
interface UserActions {
|
||||||
login: (response: User | AltogicAuth) => void;
|
|
||||||
logout: () => void;
|
logout: () => void;
|
||||||
setUser: (key: keyof typeof initialStates, value: any) => void;
|
setSession: (session: Session) => void;
|
||||||
checkSession: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const devUser = {
|
interface Premium {
|
||||||
_id: "0",
|
id: 101;
|
||||||
provider: "google",
|
created_at: "2023-07-29T17:33:10.403228+00:00";
|
||||||
providerUserId: "115637229829349229857",
|
user: null;
|
||||||
email: "test@jsoncrack.com",
|
status: "active";
|
||||||
name: "JSON Crack",
|
ends_at: null;
|
||||||
profilePicture: "",
|
subscription_id: null;
|
||||||
signUpAt: "2022-12-04T11:07:32.000Z",
|
variant_id: null;
|
||||||
lastLoginAt: "2023-05-13T09:56:02.915Z",
|
renews_at: null;
|
||||||
updatedAt: "2023-05-06T16:19:47.486Z",
|
cancelled: null;
|
||||||
} as User;
|
email: "aykutsarac0@gmail.com";
|
||||||
|
}
|
||||||
|
|
||||||
interface UserStates {
|
interface UserStates {
|
||||||
user: User | null;
|
user: User | null;
|
||||||
@ -43,68 +37,24 @@ const initialStates: UserStates = {
|
|||||||
|
|
||||||
const useUser = create<UserStates & UserActions>()(set => ({
|
const useUser = create<UserStates & UserActions>()(set => ({
|
||||||
...initialStates,
|
...initialStates,
|
||||||
setUser: (key, value) => set({ [key]: value }),
|
setSession: async session => {
|
||||||
|
const {
|
||||||
|
data: { user },
|
||||||
|
error,
|
||||||
|
} = await supabase.auth.getUser();
|
||||||
|
|
||||||
|
if (error) return toast.error(error.message);
|
||||||
|
|
||||||
|
supabase.rpc("get_subscription", { email_arg: session.user.email }).then(({ data }) => {
|
||||||
|
if (data) set({ premium: data[0].status === "active" });
|
||||||
|
set({ user, isAuthenticated: true });
|
||||||
|
});
|
||||||
|
},
|
||||||
logout: async () => {
|
logout: async () => {
|
||||||
const session = altogic.auth.getSession();
|
toast.loading("Logging out...", { id: "logout" });
|
||||||
|
await supabase.auth.signOut();
|
||||||
if (!session) return;
|
|
||||||
|
|
||||||
const { errors } = await altogic.auth.signOut(session.token);
|
|
||||||
|
|
||||||
if (errors?.items) return console.error(errors);
|
|
||||||
|
|
||||||
set(initialStates);
|
set(initialStates);
|
||||||
altogic.auth.clearLocalData();
|
toast.success("Logged out.", { id: "logout" });
|
||||||
toast.success("Logged out.");
|
|
||||||
useModal.setState({ account: false });
|
|
||||||
},
|
|
||||||
login: user => {
|
|
||||||
set({ user: user as unknown as User, isAuthenticated: true });
|
|
||||||
gaSet({ userId: (user as User)._id });
|
|
||||||
},
|
|
||||||
checkSession: async () => {
|
|
||||||
if (isDevelopment) {
|
|
||||||
return set({ user: devUser as User, isAuthenticated: true, premium: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentSession = altogic.auth.getSession();
|
|
||||||
|
|
||||||
if (currentSession) {
|
|
||||||
const { user, errors } = await altogic.auth.getUserFromDB();
|
|
||||||
|
|
||||||
if (errors?.items || !user) {
|
|
||||||
altogic.auth.clearLocalData();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
altogic.auth.setUser(user);
|
|
||||||
altogic.auth.setSession(currentSession);
|
|
||||||
const { data: premiumData } = await altogic.endpoint.get("/isPremium");
|
|
||||||
|
|
||||||
setUser({ id: user._id, email: user.email, username: user.name });
|
|
||||||
set({
|
|
||||||
user: user as User,
|
|
||||||
isAuthenticated: true,
|
|
||||||
premium: premiumData.premium,
|
|
||||||
premiumCancelled: premiumData?.status === "cancelled" || false,
|
|
||||||
});
|
|
||||||
gaSet({ userId: user._id });
|
|
||||||
} else if (new URLSearchParams(window.location.search).get("access_token")) {
|
|
||||||
const { errors, user } = await altogic.auth.getAuthGrant();
|
|
||||||
|
|
||||||
if (errors?.items) {
|
|
||||||
toast.error(errors.items[0].message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
const { data: premiumData } = await altogic.endpoint.get("/isPremium");
|
|
||||||
|
|
||||||
setUser({ id: user._id, email: user.email, username: user.name });
|
|
||||||
set({ user: user as User, isAuthenticated: true, premium: premiumData.premium });
|
|
||||||
gaSet({ userId: user._id });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
221
yarn.lock
221
yarn.lock
@ -1955,6 +1955,75 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
|
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
|
||||||
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
|
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
|
||||||
|
|
||||||
|
"@supabase/auth-helpers-nextjs@^0.7.3":
|
||||||
|
version "0.7.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@supabase/auth-helpers-nextjs/-/auth-helpers-nextjs-0.7.3.tgz#516ddda811c5f81328e50fb635fb11a437930d61"
|
||||||
|
integrity sha512-ikuBGHFnvyfneToj0Y2EfiqUnEr1bdqR6JZiFydPnzACQ2Q7TkcOhkoUSc9/sMEeG7EKX5PhH8riAz42oPdKRg==
|
||||||
|
dependencies:
|
||||||
|
"@supabase/auth-helpers-shared" "0.4.1"
|
||||||
|
set-cookie-parser "^2.6.0"
|
||||||
|
|
||||||
|
"@supabase/auth-helpers-react@^0.4.1":
|
||||||
|
version "0.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@supabase/auth-helpers-react/-/auth-helpers-react-0.4.1.tgz#9198cfe1ceae9935d04b2fdec84a7d806a8c711e"
|
||||||
|
integrity sha512-Y9ZWzhpsOX93rokmywDdclzTDPbHjybU7o4/FmXlAqta75msFxHV9WVTR6//SZf2Bca2Cf65Oa/j+fYtMWraDQ==
|
||||||
|
|
||||||
|
"@supabase/auth-helpers-shared@0.4.1":
|
||||||
|
version "0.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@supabase/auth-helpers-shared/-/auth-helpers-shared-0.4.1.tgz#46f53f7ac1e80f6bcb69b5bc95e15b237a80d819"
|
||||||
|
integrity sha512-IEDX9JzWkIjQiLUaP4Qy5YDiG0jFQatWfS+jw8cCQs6QfbNdEPd2Y3qonwGHnM90CZom9SvjuylBv2pFVAL7Lw==
|
||||||
|
dependencies:
|
||||||
|
jose "^4.14.3"
|
||||||
|
|
||||||
|
"@supabase/functions-js@^2.1.0":
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@supabase/functions-js/-/functions-js-2.1.2.tgz#340a8d3845ef2014338b13a6d33cfa90eb745b14"
|
||||||
|
integrity sha512-QCR6pwJs9exCl37bmpMisUd6mf+0SUBJ6mUpiAjEkSJ/+xW8TCuO14bvkWHADd5hElJK9MxNlMQXxSA4DRz9nQ==
|
||||||
|
dependencies:
|
||||||
|
cross-fetch "^3.1.5"
|
||||||
|
|
||||||
|
"@supabase/gotrue-js@^2.46.1":
|
||||||
|
version "2.46.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@supabase/gotrue-js/-/gotrue-js-2.46.1.tgz#c4ac5056a02498db6d9edccab4e4d6db4fe7e3e1"
|
||||||
|
integrity sha512-tebFX3XvPqEJKHOVgkXTN20g9iUhLx6tebIYQvTggYTrqOT2af8oTpSBdgYzbwJ291G6P6CSpR6KY0cT9ade5A==
|
||||||
|
dependencies:
|
||||||
|
cross-fetch "^3.1.5"
|
||||||
|
|
||||||
|
"@supabase/postgrest-js@^1.7.0":
|
||||||
|
version "1.7.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@supabase/postgrest-js/-/postgrest-js-1.7.2.tgz#800814b98bb8cec840b65f0c545a3474f23c343a"
|
||||||
|
integrity sha512-GK80JpRq8l6Qll85erICypAfQCied8tdlXfsDN14W844HqXCSOisk8AaE01DAwGJanieaoN5fuqhzA2yKxDvEQ==
|
||||||
|
dependencies:
|
||||||
|
cross-fetch "^3.1.5"
|
||||||
|
|
||||||
|
"@supabase/realtime-js@^2.7.3":
|
||||||
|
version "2.7.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@supabase/realtime-js/-/realtime-js-2.7.3.tgz#cbcb84181add681ab99c87032bfe88101c6863b3"
|
||||||
|
integrity sha512-c7TzL81sx2kqyxsxcDduJcHL9KJdCOoKimGP6lQSqiZKX42ATlBZpWbyy9KFGFBjAP4nyopMf5JhPi2ZH9jyNw==
|
||||||
|
dependencies:
|
||||||
|
"@types/phoenix" "^1.5.4"
|
||||||
|
"@types/websocket" "^1.0.3"
|
||||||
|
websocket "^1.0.34"
|
||||||
|
|
||||||
|
"@supabase/storage-js@^2.5.1":
|
||||||
|
version "2.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@supabase/storage-js/-/storage-js-2.5.1.tgz#16c4c088996e0395034717836e626f14df63a349"
|
||||||
|
integrity sha512-nkR0fQA9ScAtIKA3vNoPEqbZv1k5B5HVRYEvRWdlP6mUpFphM9TwPL2jZ/ztNGMTG5xT6SrHr+H7Ykz8qzbhjw==
|
||||||
|
dependencies:
|
||||||
|
cross-fetch "^3.1.5"
|
||||||
|
|
||||||
|
"@supabase/supabase-js@^2.31.0":
|
||||||
|
version "2.31.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@supabase/supabase-js/-/supabase-js-2.31.0.tgz#49fe09652fa72b1173efe4ac086592e31c662cef"
|
||||||
|
integrity sha512-W9/4s+KnSUX67wJKBn/3yLq+ieycnMzVjK3nNTLX5Wko3ypNT/081l2iFYrf+nsLQ1CiT4mA92I3dxCy6CmxTg==
|
||||||
|
dependencies:
|
||||||
|
"@supabase/functions-js" "^2.1.0"
|
||||||
|
"@supabase/gotrue-js" "^2.46.1"
|
||||||
|
"@supabase/postgrest-js" "^1.7.0"
|
||||||
|
"@supabase/realtime-js" "^2.7.3"
|
||||||
|
"@supabase/storage-js" "^2.5.1"
|
||||||
|
cross-fetch "^3.1.5"
|
||||||
|
|
||||||
"@swc/helpers@0.4.14":
|
"@swc/helpers@0.4.14":
|
||||||
version "0.4.14"
|
version "0.4.14"
|
||||||
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74"
|
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74"
|
||||||
@ -2112,6 +2181,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||||
|
|
||||||
|
"@types/phoenix@^1.5.4":
|
||||||
|
version "1.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/phoenix/-/phoenix-1.6.0.tgz#eb7536259ee695646e75c4c7b0c9a857ea174781"
|
||||||
|
integrity sha512-qwfpsHmFuhAS/dVd4uBIraMxRd56vwBUYQGZ6GpXnFuM2XMRFJbIyruFKKlW2daQliuYZwe0qfn/UjFCDKic5g==
|
||||||
|
|
||||||
"@types/prop-types@*":
|
"@types/prop-types@*":
|
||||||
version "15.7.5"
|
version "15.7.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
||||||
@ -2179,6 +2253,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
|
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
|
||||||
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
|
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
|
||||||
|
|
||||||
|
"@types/websocket@^1.0.3":
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.5.tgz#3fb80ed8e07f88e51961211cd3682a3a4a81569c"
|
||||||
|
integrity sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@typescript-eslint/parser@^5.42.0":
|
"@typescript-eslint/parser@^5.42.0":
|
||||||
version "5.54.1"
|
version "5.54.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.54.1.tgz#05761d7f777ef1c37c971d3af6631715099b084c"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.54.1.tgz#05761d7f777ef1c37c971d3af6631715099b084c"
|
||||||
@ -2509,6 +2590,13 @@ buffer-from@~0.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-0.1.2.tgz#15f4b9bcef012044df31142c14333caf6e0260d0"
|
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-0.1.2.tgz#15f4b9bcef012044df31142c14333caf6e0260d0"
|
||||||
integrity sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==
|
integrity sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==
|
||||||
|
|
||||||
|
bufferutil@^4.0.1:
|
||||||
|
version "4.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad"
|
||||||
|
integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==
|
||||||
|
dependencies:
|
||||||
|
node-gyp-build "^4.3.0"
|
||||||
|
|
||||||
busboy@1.6.0:
|
busboy@1.6.0:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
|
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
|
||||||
@ -2720,6 +2808,13 @@ cross-fetch@^3.1.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
node-fetch "2.6.7"
|
node-fetch "2.6.7"
|
||||||
|
|
||||||
|
cross-fetch@^3.1.5:
|
||||||
|
version "3.1.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82"
|
||||||
|
integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==
|
||||||
|
dependencies:
|
||||||
|
node-fetch "^2.6.12"
|
||||||
|
|
||||||
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||||
@ -2770,6 +2865,14 @@ d3-shape@^3.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
d3-path "^3.1.0"
|
d3-path "^3.1.0"
|
||||||
|
|
||||||
|
d@1, d@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
|
||||||
|
integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
|
||||||
|
dependencies:
|
||||||
|
es5-ext "^0.10.50"
|
||||||
|
type "^1.0.1"
|
||||||
|
|
||||||
damerau-levenshtein@^1.0.8:
|
damerau-levenshtein@^1.0.8:
|
||||||
version "1.0.8"
|
version "1.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
|
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
|
||||||
@ -2787,6 +2890,13 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, d
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "2.1.2"
|
ms "2.1.2"
|
||||||
|
|
||||||
|
debug@^2.2.0:
|
||||||
|
version "2.6.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
|
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||||
|
dependencies:
|
||||||
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@^3.2.7:
|
debug@^3.2.7:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
||||||
@ -3113,6 +3223,32 @@ es-to-primitive@^1.2.1:
|
|||||||
is-date-object "^1.0.1"
|
is-date-object "^1.0.1"
|
||||||
is-symbol "^1.0.2"
|
is-symbol "^1.0.2"
|
||||||
|
|
||||||
|
es5-ext@^0.10.35, es5-ext@^0.10.50:
|
||||||
|
version "0.10.62"
|
||||||
|
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
|
||||||
|
integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
|
||||||
|
dependencies:
|
||||||
|
es6-iterator "^2.0.3"
|
||||||
|
es6-symbol "^3.1.3"
|
||||||
|
next-tick "^1.1.0"
|
||||||
|
|
||||||
|
es6-iterator@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
|
||||||
|
integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==
|
||||||
|
dependencies:
|
||||||
|
d "1"
|
||||||
|
es5-ext "^0.10.35"
|
||||||
|
es6-symbol "^3.1.1"
|
||||||
|
|
||||||
|
es6-symbol@^3.1.1, es6-symbol@^3.1.3:
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
|
||||||
|
integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
|
||||||
|
dependencies:
|
||||||
|
d "^1.0.1"
|
||||||
|
ext "^1.1.2"
|
||||||
|
|
||||||
escalade@^3.1.1:
|
escalade@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||||
@ -3370,6 +3506,13 @@ eventemitter3@^5.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.0.tgz#084eb7f5b5388df1451e63f4c2aafd71b217ccb3"
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.0.tgz#084eb7f5b5388df1451e63f4c2aafd71b217ccb3"
|
||||||
integrity sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg==
|
integrity sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg==
|
||||||
|
|
||||||
|
ext@^1.1.2:
|
||||||
|
version "1.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f"
|
||||||
|
integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==
|
||||||
|
dependencies:
|
||||||
|
type "^2.7.2"
|
||||||
|
|
||||||
fast-deep-equal@^1.0.0:
|
fast-deep-equal@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
|
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
|
||||||
@ -4075,6 +4218,11 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.9:
|
|||||||
gopd "^1.0.1"
|
gopd "^1.0.1"
|
||||||
has-tostringtag "^1.0.0"
|
has-tostringtag "^1.0.0"
|
||||||
|
|
||||||
|
is-typedarray@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||||
|
integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
|
||||||
|
|
||||||
is-weakmap@^2.0.1:
|
is-weakmap@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2"
|
resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2"
|
||||||
@ -4127,6 +4275,11 @@ javascript-natural-sort@0.7.1:
|
|||||||
resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59"
|
resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59"
|
||||||
integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==
|
integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==
|
||||||
|
|
||||||
|
jose@^4.14.3:
|
||||||
|
version "4.14.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.4.tgz#59e09204e2670c3164ee24cbfe7115c6f8bff9ca"
|
||||||
|
integrity sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==
|
||||||
|
|
||||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
@ -4471,6 +4624,11 @@ mrmime@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27"
|
resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27"
|
||||||
integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==
|
integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==
|
||||||
|
|
||||||
|
ms@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
|
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
|
||||||
|
|
||||||
ms@2.1.2:
|
ms@2.1.2:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
@ -4504,6 +4662,11 @@ natural-compare@^1.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||||
|
|
||||||
|
next-tick@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
|
||||||
|
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
|
||||||
|
|
||||||
next@13.3.0:
|
next@13.3.0:
|
||||||
version "13.3.0"
|
version "13.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1"
|
resolved "https://registry.yarnpkg.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1"
|
||||||
@ -4533,6 +4696,13 @@ node-fetch@2.6.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
whatwg-url "^5.0.0"
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
|
node-fetch@^2.6.12:
|
||||||
|
version "2.6.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba"
|
||||||
|
integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==
|
||||||
|
dependencies:
|
||||||
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
node-fetch@^2.6.7:
|
node-fetch@^2.6.7:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6"
|
||||||
@ -4540,6 +4710,11 @@ node-fetch@^2.6.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
whatwg-url "^5.0.0"
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
|
node-gyp-build@^4.3.0:
|
||||||
|
version "4.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055"
|
||||||
|
integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==
|
||||||
|
|
||||||
node-releases@^2.0.12:
|
node-releases@^2.0.12:
|
||||||
version "2.0.13"
|
version "2.0.13"
|
||||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d"
|
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d"
|
||||||
@ -5248,6 +5423,11 @@ semver@^7.3.8:
|
|||||||
dependencies:
|
dependencies:
|
||||||
lru-cache "^6.0.0"
|
lru-cache "^6.0.0"
|
||||||
|
|
||||||
|
set-cookie-parser@^2.6.0:
|
||||||
|
version "2.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz#131921e50f62ff1a66a461d7d62d7b21d5d15a51"
|
||||||
|
integrity sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==
|
||||||
|
|
||||||
shallowequal@^1.1.0:
|
shallowequal@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
|
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
|
||||||
@ -5670,6 +5850,16 @@ type-fest@^0.7.1:
|
|||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"
|
||||||
integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==
|
integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==
|
||||||
|
|
||||||
|
type@^1.0.1:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
|
||||||
|
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
|
||||||
|
|
||||||
|
type@^2.7.2:
|
||||||
|
version "2.7.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0"
|
||||||
|
integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==
|
||||||
|
|
||||||
typed-array-length@^1.0.4:
|
typed-array-length@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
|
resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
|
||||||
@ -5679,6 +5869,13 @@ typed-array-length@^1.0.4:
|
|||||||
for-each "^0.3.3"
|
for-each "^0.3.3"
|
||||||
is-typed-array "^1.1.9"
|
is-typed-array "^1.1.9"
|
||||||
|
|
||||||
|
typedarray-to-buffer@^3.1.5:
|
||||||
|
version "3.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
|
||||||
|
integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
|
||||||
|
dependencies:
|
||||||
|
is-typedarray "^1.0.0"
|
||||||
|
|
||||||
typescript@5.1.6:
|
typescript@5.1.6:
|
||||||
version "5.1.6"
|
version "5.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
|
||||||
@ -5792,6 +5989,13 @@ use-sync-external-store@1.2.0, use-sync-external-store@^1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
||||||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
||||||
|
|
||||||
|
utf-8-validate@^5.0.2:
|
||||||
|
version "5.0.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2"
|
||||||
|
integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==
|
||||||
|
dependencies:
|
||||||
|
node-gyp-build "^4.3.0"
|
||||||
|
|
||||||
util-deprecate@~1.0.1:
|
util-deprecate@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
@ -5827,6 +6031,18 @@ webpack-bundle-analyzer@4.7.0:
|
|||||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||||
|
|
||||||
|
websocket@^1.0.34:
|
||||||
|
version "1.0.34"
|
||||||
|
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111"
|
||||||
|
integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==
|
||||||
|
dependencies:
|
||||||
|
bufferutil "^4.0.1"
|
||||||
|
debug "^2.2.0"
|
||||||
|
es5-ext "^0.10.50"
|
||||||
|
typedarray-to-buffer "^3.1.5"
|
||||||
|
utf-8-validate "^5.0.2"
|
||||||
|
yaeti "^0.0.6"
|
||||||
|
|
||||||
whatwg-url@^5.0.0:
|
whatwg-url@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||||
@ -5912,6 +6128,11 @@ xtend@~2.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
object-keys "~0.4.0"
|
object-keys "~0.4.0"
|
||||||
|
|
||||||
|
yaeti@^0.0.6:
|
||||||
|
version "0.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
|
||||||
|
integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==
|
||||||
|
|
||||||
yallist@^3.0.2:
|
yallist@^3.0.2:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user