|
|
|
|
@@ -1,4 +1,4 @@
|
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
|
|
|
import React, { useState, useEffect, useCallback } from "react";
|
|
|
|
|
import {
|
|
|
|
|
Plus,
|
|
|
|
|
Share,
|
|
|
|
|
@@ -26,11 +26,7 @@ import NewWindow from "react-new-window";
|
|
|
|
|
import { signOut, useSession } from "next-auth/react";
|
|
|
|
|
import { useSnapshot } from "valtio";
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
createNewFile,
|
|
|
|
|
syncToGist,
|
|
|
|
|
updateEditorSettings,
|
|
|
|
|
} from "../state/actions";
|
|
|
|
|
import { createNewFile, syncToGist, updateEditorSettings } from "../state/actions";
|
|
|
|
|
import state from "../state";
|
|
|
|
|
import Box from "./Box";
|
|
|
|
|
import Button from "./Button";
|
|
|
|
|
@@ -46,6 +42,7 @@ import {
|
|
|
|
|
import Flex from "./Flex";
|
|
|
|
|
import Stack from "./Stack";
|
|
|
|
|
import Input from "./Input";
|
|
|
|
|
import Text from "./Text";
|
|
|
|
|
import toast from "react-hot-toast";
|
|
|
|
|
import {
|
|
|
|
|
AlertDialog,
|
|
|
|
|
@@ -55,11 +52,22 @@ import {
|
|
|
|
|
AlertDialogCancel,
|
|
|
|
|
AlertDialogAction,
|
|
|
|
|
} from "./AlertDialog";
|
|
|
|
|
import { styled } from "../stitches.config";
|
|
|
|
|
|
|
|
|
|
const DEFAULT_EXTENSION = ".c";
|
|
|
|
|
|
|
|
|
|
const ErrorText = styled(Text, {
|
|
|
|
|
color: "$red9",
|
|
|
|
|
mt: "$1",
|
|
|
|
|
display: "block",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
const snap = useSnapshot(state);
|
|
|
|
|
const [createNewAlertOpen, setCreateNewAlertOpen] = useState(false);
|
|
|
|
|
const [editorSettingsOpen, setEditorSettingsOpen] = useState(false);
|
|
|
|
|
const [isNewfileDialogOpen, setIsNewfileDialogOpen] = useState(false);
|
|
|
|
|
const [newfileError, setNewfileError] = useState<string | null>(null);
|
|
|
|
|
const [filename, setFilename] = useState("");
|
|
|
|
|
const { data: session, status } = useSession();
|
|
|
|
|
const [popup, setPopUp] = useState(false);
|
|
|
|
|
@@ -69,6 +77,36 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
setPopUp(false);
|
|
|
|
|
}
|
|
|
|
|
}, [session, popup]);
|
|
|
|
|
|
|
|
|
|
// when filename changes, reset error
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setNewfileError(null);
|
|
|
|
|
}, [filename, setNewfileError]);
|
|
|
|
|
|
|
|
|
|
const validateFilename = useCallback(
|
|
|
|
|
(filename: string): { error: string | null } => {
|
|
|
|
|
if (snap.files.find(file => file.name === filename)) {
|
|
|
|
|
return { error: "Filename already exists." };
|
|
|
|
|
}
|
|
|
|
|
// More checks in future
|
|
|
|
|
return { error: null };
|
|
|
|
|
},
|
|
|
|
|
[snap.files]
|
|
|
|
|
);
|
|
|
|
|
const handleConfirm = useCallback(() => {
|
|
|
|
|
// add default extension in case omitted
|
|
|
|
|
let _filename = filename.includes(".") ? filename : filename + DEFAULT_EXTENSION;
|
|
|
|
|
const chk = validateFilename(_filename);
|
|
|
|
|
if (chk.error) {
|
|
|
|
|
setNewfileError(`Error: ${chk.error}`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setIsNewfileDialogOpen(false);
|
|
|
|
|
createNewFile(_filename);
|
|
|
|
|
setFilename("");
|
|
|
|
|
}, [filename, setIsNewfileDialogOpen, setFilename, validateFilename]);
|
|
|
|
|
|
|
|
|
|
const files = snap.files;
|
|
|
|
|
return (
|
|
|
|
|
<Flex css={{ flexShrink: 0, gap: "$0" }}>
|
|
|
|
|
@@ -101,9 +139,7 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
return (
|
|
|
|
|
<Button
|
|
|
|
|
size="sm"
|
|
|
|
|
outline={
|
|
|
|
|
showWat ? snap.activeWat !== index : snap.active !== index
|
|
|
|
|
}
|
|
|
|
|
outline={showWat ? snap.activeWat !== index : snap.active !== index}
|
|
|
|
|
onClick={() => (state.active = index)}
|
|
|
|
|
key={file.name + index}
|
|
|
|
|
css={{
|
|
|
|
|
@@ -138,8 +174,7 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
// If deleted file is behind active tab
|
|
|
|
|
// we keep the current state otherwise
|
|
|
|
|
// select previous file on the list
|
|
|
|
|
state.active =
|
|
|
|
|
index > snap.active ? snap.active : snap.active - 1;
|
|
|
|
|
state.active = index > snap.active ? snap.active : snap.active - 1;
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<X size="9px" weight="bold" />
|
|
|
|
|
@@ -149,15 +184,10 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
{!showWat && (
|
|
|
|
|
<Dialog>
|
|
|
|
|
<Dialog open={isNewfileDialogOpen} onOpenChange={setIsNewfileDialogOpen}>
|
|
|
|
|
<DialogTrigger asChild>
|
|
|
|
|
<Button
|
|
|
|
|
ghost
|
|
|
|
|
size="sm"
|
|
|
|
|
css={{ alignItems: "center", px: "$2", mr: "$3" }}
|
|
|
|
|
>
|
|
|
|
|
<Plus size="16px" />{" "}
|
|
|
|
|
{snap.files.length === 0 && "Add new file"}
|
|
|
|
|
<Button ghost size="sm" css={{ alignItems: "center", px: "$2", mr: "$3" }}>
|
|
|
|
|
<Plus size="16px" /> {snap.files.length === 0 && "Add new file"}
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogTrigger>
|
|
|
|
|
<DialogContent>
|
|
|
|
|
@@ -166,8 +196,14 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
<label>Filename</label>
|
|
|
|
|
<Input
|
|
|
|
|
value={filename}
|
|
|
|
|
onChange={(e) => setFilename(e.target.value)}
|
|
|
|
|
onChange={e => setFilename(e.target.value)}
|
|
|
|
|
onKeyPress={e => {
|
|
|
|
|
if (e.key === "Enter") {
|
|
|
|
|
handleConfirm();
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<ErrorText>{newfileError}</ErrorText>
|
|
|
|
|
</DialogDescription>
|
|
|
|
|
|
|
|
|
|
<Flex
|
|
|
|
|
@@ -180,18 +216,12 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
<DialogClose asChild>
|
|
|
|
|
<Button outline>Cancel</Button>
|
|
|
|
|
</DialogClose>
|
|
|
|
|
<DialogClose asChild>
|
|
|
|
|
<Button
|
|
|
|
|
variant="primary"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
createNewFile(filename);
|
|
|
|
|
// reset
|
|
|
|
|
setFilename("");
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Create file
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogClose>
|
|
|
|
|
<Button
|
|
|
|
|
variant="primary"
|
|
|
|
|
onClick={handleConfirm}
|
|
|
|
|
>
|
|
|
|
|
Create file
|
|
|
|
|
</Button>
|
|
|
|
|
</Flex>
|
|
|
|
|
<DialogClose asChild>
|
|
|
|
|
<Box css={{ position: "absolute", top: "$3", right: "$3" }}>
|
|
|
|
|
@@ -211,9 +241,7 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
zIndex: 1,
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Container
|
|
|
|
|
css={{ width: "unset", display: "flex", alignItems: "center" }}
|
|
|
|
|
>
|
|
|
|
|
<Container css={{ width: "unset", display: "flex", alignItems: "center" }}>
|
|
|
|
|
{status === "authenticated" ? (
|
|
|
|
|
<DropdownMenu>
|
|
|
|
|
<DropdownMenuTrigger asChild>
|
|
|
|
|
@@ -246,15 +274,10 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
</DropdownMenuTrigger>
|
|
|
|
|
<DropdownMenuContent>
|
|
|
|
|
<DropdownMenuItem disabled onClick={() => signOut()}>
|
|
|
|
|
<User size="16px" /> {session?.user?.username} (
|
|
|
|
|
{session?.user.name})
|
|
|
|
|
<User size="16px" /> {session?.user?.username} ({session?.user.name})
|
|
|
|
|
</DropdownMenuItem>
|
|
|
|
|
<DropdownMenuItem
|
|
|
|
|
onClick={() =>
|
|
|
|
|
window.open(
|
|
|
|
|
`http://gist.github.com/${session?.user.username}`
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
onClick={() => window.open(`http://gist.github.com/${session?.user.username}`)}
|
|
|
|
|
>
|
|
|
|
|
<ArrowSquareOut size="16px" />
|
|
|
|
|
Go to your Gist
|
|
|
|
|
@@ -268,12 +291,7 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
</DropdownMenuContent>
|
|
|
|
|
</DropdownMenu>
|
|
|
|
|
) : (
|
|
|
|
|
<Button
|
|
|
|
|
outline
|
|
|
|
|
size="sm"
|
|
|
|
|
css={{ mr: "$3" }}
|
|
|
|
|
onClick={() => setPopUp(true)}
|
|
|
|
|
>
|
|
|
|
|
<Button outline size="sm" css={{ mr: "$3" }} onClick={() => setPopUp(true)}>
|
|
|
|
|
<GithubLogo size="16px" /> Login
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
@@ -320,9 +338,7 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
size="sm"
|
|
|
|
|
css={{ alignItems: "center" }}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
navigator.clipboard.writeText(
|
|
|
|
|
`${window.location.origin}/develop/${snap.gistId}`
|
|
|
|
|
);
|
|
|
|
|
navigator.clipboard.writeText(`${window.location.origin}/develop/${snap.gistId}`);
|
|
|
|
|
toast.success("Copied share link to clipboard!");
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
@@ -367,9 +383,7 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
Copy share link to clipboard
|
|
|
|
|
</DropdownMenuItem>
|
|
|
|
|
<DropdownMenuItem
|
|
|
|
|
disabled={
|
|
|
|
|
session?.user.username !== snap.gistOwner || !snap.gistId
|
|
|
|
|
}
|
|
|
|
|
disabled={session?.user.username !== snap.gistOwner || !snap.gistId}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
syncToGist(session);
|
|
|
|
|
}}
|
|
|
|
|
@@ -395,21 +409,15 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
</DropdownMenu>
|
|
|
|
|
</Stack>
|
|
|
|
|
|
|
|
|
|
{popup && !session ? (
|
|
|
|
|
<NewWindow center="parent" url="/sign-in" />
|
|
|
|
|
) : null}
|
|
|
|
|
{popup && !session ? <NewWindow center="parent" url="/sign-in" /> : null}
|
|
|
|
|
</Container>
|
|
|
|
|
</Flex>
|
|
|
|
|
<AlertDialog
|
|
|
|
|
open={createNewAlertOpen}
|
|
|
|
|
onOpenChange={(value) => setCreateNewAlertOpen(value)}
|
|
|
|
|
>
|
|
|
|
|
<AlertDialog open={createNewAlertOpen} onOpenChange={value => setCreateNewAlertOpen(value)}>
|
|
|
|
|
<AlertDialogContent>
|
|
|
|
|
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
|
|
|
|
<AlertDialogDescription>
|
|
|
|
|
This action will create new <strong>public</strong> Github Gist from
|
|
|
|
|
your current saved files. You can delete gist anytime from your
|
|
|
|
|
GitHub Gists page.
|
|
|
|
|
This action will create new <strong>public</strong> Github Gist from your current saved
|
|
|
|
|
files. You can delete gist anytime from your GitHub Gists page.
|
|
|
|
|
</AlertDialogDescription>
|
|
|
|
|
<Flex css={{ justifyContent: "flex-end", gap: "$3" }}>
|
|
|
|
|
<AlertDialogCancel asChild>
|
|
|
|
|
@@ -443,8 +451,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
type="number"
|
|
|
|
|
min="1"
|
|
|
|
|
value={editorSettings.tabSize}
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
setEditorSettings((curr) => ({
|
|
|
|
|
onChange={e =>
|
|
|
|
|
setEditorSettings(curr => ({
|
|
|
|
|
...curr,
|
|
|
|
|
tabSize: Number(e.target.value),
|
|
|
|
|
}))
|
|
|
|
|
@@ -454,18 +462,12 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|
|
|
|
|
|
|
|
|
<Flex css={{ marginTop: 25, justifyContent: "flex-end", gap: "$3" }}>
|
|
|
|
|
<DialogClose asChild>
|
|
|
|
|
<Button
|
|
|
|
|
outline
|
|
|
|
|
onClick={() => updateEditorSettings(editorSettings)}
|
|
|
|
|
>
|
|
|
|
|
<Button outline onClick={() => updateEditorSettings(editorSettings)}>
|
|
|
|
|
Cancel
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogClose>
|
|
|
|
|
<DialogClose asChild>
|
|
|
|
|
<Button
|
|
|
|
|
variant="primary"
|
|
|
|
|
onClick={() => updateEditorSettings(editorSettings)}
|
|
|
|
|
>
|
|
|
|
|
<Button variant="primary" onClick={() => updateEditorSettings(editorSettings)}>
|
|
|
|
|
Save changes
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogClose>
|
|
|
|
|
|