Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Niu/prompt dialog #669

Merged
merged 10 commits into from
Dec 11, 2023
22 changes: 10 additions & 12 deletions app/alpha/new/[[...postIdArr]]/_client.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
"use client";

import { redirect, useParams } from "next/navigation";
import React, { Fragment } from "react";
import React, { useEffect, Fragment } from "react";
import { Controller } from "react-hook-form";
import { Disclosure, Transition } from "@headlessui/react";
import { ChevronUpIcon } from "@heroicons/react/20/solid";
// @TODO fix PromptDiaglog
// import { PromptDialog } from "@/components/PromptService/PromptService";
import Editor from "@/components/editor/editor";
import RenderPost from "@/components/editor/editor/RenderPost";
import useCreatePage from "@/hooks/useCreatePage";
import { usePrompt } from "@/components/PromptService";

const Create = () => {
const params = useParams();
Expand All @@ -23,7 +21,7 @@ const Create = () => {
savedTime,
open,
setOpen,
unsavedChanges,
hasUnsavedChanges,
handleSubmit,
register,
control,
Expand All @@ -42,6 +40,13 @@ const Create = () => {
saveStatus,
} = useCreatePage({ postId });

const { unsavedChanges: _unsaved, setUnsavedChanges: _setUnsaved } =
usePrompt();

useEffect(() => {
_setUnsaved(hasUnsavedChanges);
}, [hasUnsavedChanges, _setUnsaved]);

return (
<>
<button
Expand All @@ -51,13 +56,6 @@ const Create = () => {
>
Preview
</button>
{/* <PromptDialog
shouldConfirmLeave={unsavedChanges}
updateParent={handleOpenDialog}
title="Unsaved Changes"
subTitle="You have unsaved changes."
content="Changes that you have made will be lost."
/> */}
<form onSubmit={handleSubmit(onSubmit)}>
<Transition.Root show={open} as={Fragment}>
<div className="fixed bottom-0 left-0 top-0 z-50 h-screen w-full bg-black">
Expand Down
9 changes: 8 additions & 1 deletion app/create/[[...paramsArr]]/_client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { Disclosure, Transition } from "@headlessui/react";
import { ChevronUpIcon } from "@heroicons/react/20/solid";
import type { SavePostInput } from "../../../schema/post";
import { ConfirmPostSchema } from "../../../schema/post";
// @TODO fix PromptDialog
import { api } from "@/server/trpc/react";
import { removeMarkdown } from "../../../utils/removeMarkdown";
import { useDebounce } from "../../../hooks/useDebounce";
Expand All @@ -19,6 +18,7 @@ import { useMarkdownShortcuts } from "../../../markdoc/editor/shortcuts/shortcut
import { markdocComponents } from "../../../markdoc/components";
import { config } from "../../../markdoc/config";
import { redirect, useParams } from "next/navigation";
import { usePrompt } from "@/components/PromptService";

const Create = () => {
const params = useParams();
Expand All @@ -35,6 +35,13 @@ const Create = () => {
const [shouldRefetch, setShouldRefetch] = useState<boolean>(true);
const [unsavedChanges, setUnsavedChanges] = useState<boolean>(false);

const { unsavedChanges: _unsaved, setUnsavedChanges: _setUnsaved } =
usePrompt();

useEffect(() => {
_setUnsaved(unsavedChanges);
}, [unsavedChanges, _setUnsaved]);

const allowUpdate = unsavedChanges;
const textareaRef = useRef<HTMLTextAreaElement>(null);

Expand Down
19 changes: 9 additions & 10 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import Nav from "@/components/Nav/Nav";
import { getServerAuthSession } from "@/server/auth";
import AuthProvider from "@/context/AuthProvider";
import { Toaster } from "sonner";
import NextTopLoader from "nextjs-toploader";
import ProgressBar from "@/components/ProgressBar/ProgressBar";
import React from "react";
import A11yProvider from "@/components/A11yProvider/A11yProvider";
import { PromptProvider } from "@/components/PromptService";

// @TODO layout app in way that doesn't need to use client session check
export const metadata = {
Expand Down Expand Up @@ -55,18 +56,16 @@ export default async function RootLayout({
<Fathom />
<body className="h-full">
<A11yProvider>
<NextTopLoader
easing="linear"
showSpinner={false}
template='<div class="bar" role="bar"><div class="gradient"></div></div>'
/>
<ProgressBar />
<AuthProvider>
<ThemeProvider>
<TRPCReactProvider headers={headers()}>
<Toaster />
<Nav session={session} />
{children}
<Footer />
<PromptProvider>
<Toaster />
<Nav session={session} />
{children}
<Footer />
</PromptProvider>
</TRPCReactProvider>
</ThemeProvider>
</AuthProvider>
Expand Down
79 changes: 16 additions & 63 deletions app/my-posts/_client.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"use client";

import { useState, Fragment } from "react";
import { ExclamationCircleIcon, XMarkIcon } from "@heroicons/react/20/solid";
import { Dialog, Transition, Menu } from "@headlessui/react";
import { Transition, Menu } from "@headlessui/react";
import Link from "next/link";
import {
ChevronDownIcon,
Expand All @@ -11,8 +10,8 @@ import {
} from "@heroicons/react/20/solid";
import { useSearchParams } from "next/navigation";
import { api } from "@/server/trpc/react";
import { Modal } from "../../components/Modal/Modal";
import { Tabs } from "@/components/Tabs";
import { PromptDialog } from "@/components/PromptService";

function classNames(...classes: string[]) {
return classes.filter(Boolean).join(" ");
Expand Down Expand Up @@ -67,66 +66,20 @@ const MyPosts = () => {

return (
<>
<Modal
open={!!selectedArticleToDelete}
onClose={() => setSelectedArticleToDelete(undefined)}
>
<div className="absolute right-0 top-0 hidden pr-4 pt-4 sm:block">
<button
type="button"
className="bg-neutral-900 text-neutral-400 hover:text-neutral-500 focus:outline-none"
onClick={() => setSelectedArticleToDelete(undefined)}
>
<span className="sr-only">Close</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
<div className="sm:flex sm:items-start">
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-gradient-to-r from-orange-400 to-pink-600 sm:mx-0 sm:h-10 sm:w-10">
<ExclamationCircleIcon
className="text-white-600 h-6 w-6"
aria-hidden="true"
/>
</div>
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-white"
>
Delete article
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-neutral-500">
Are you sure you want to delete this article?
</p>
<p className="mt-2 text-sm text-neutral-500">
All of the data will be permanently removed from our servers
forever. This action cannot be undone.
</p>
</div>
</div>
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<button
type="button"
disabled={deleteStatus === "loading"}
className="primary-button ml-4"
onClick={() => {
if (selectedArticleToDelete)
mutate({ id: selectedArticleToDelete });
}}
>
{deleteStatus === "loading" ? "Deleting..." : "Delete"}
</button>
<button
type="button"
className="mt-3 inline-flex w-full justify-center rounded-md border border-neutral-300 bg-white px-4 py-2 text-base font-medium text-neutral-700 shadow-sm hover:text-neutral-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm"
onClick={() => setSelectedArticleToDelete(undefined)}
>
Cancel
</button>
</div>
</Modal>
{selectedArticleToDelete && (
<PromptDialog
confirm={() => {
if (selectedArticleToDelete)
mutate({ id: selectedArticleToDelete });
}}
cancel={() => setSelectedArticleToDelete(undefined)}
title="Delete article"
subTitle="Are you sure you want to delete this article?"
content="All of the data will be permanently removed from our servers forever. This action cannot be undone."
confirmText={deleteStatus === "loading" ? "Deleting..." : "Delete"}
cancelText="Cancel"
/>
)}
<div className="relative mx-4 max-w-2xl bg-neutral-100 dark:bg-black sm:mx-auto">
<div className="mb-4 mt-8">
<Tabs tabs={tabs} />
Expand Down
6 changes: 3 additions & 3 deletions components/Nav/MobileNav.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type UserNavigationItem } from "@/types/types";
import { Disclosure, Transition } from "@headlessui/react";
import { type Session } from "next-auth";
import Link from "next/link";
import { PromptLink as Link } from "../PromptService/PromptLink";
import { type FunctionComponent } from "react";
import { navigation, subNav, userSubNav } from "../../config/site_settings";

Expand Down Expand Up @@ -66,7 +66,7 @@ const MobileNav: FunctionComponent<MobileNavProps> = ({
{item.name}
</button>
) : (
<Link key={item.name} href={item.href}>
<Link key={item.name} to={item.href}>
<Disclosure.Button
as="div"
className="nav-button w-full font-medium"
Expand Down Expand Up @@ -133,7 +133,7 @@ const SubNav: FunctionComponent<SubNavProps> = ({ session }) => {
<Disclosure key={item.name}>
<Disclosure.Button
as={Link}
href={item.href}
to={item.href}
className={classNames(
item.fancy
? "block justify-center bg-gradient-to-r from-orange-400 to-pink-600 px-4 text-white shadow-sm hover:from-orange-300 hover:to-pink-500 focus:outline-none focus:ring-2 focus:ring-offset-2"
Expand Down
18 changes: 8 additions & 10 deletions components/Nav/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { api } from "@/server/trpc/react";
import { Disclosure, Menu, Transition } from "@headlessui/react";
import { BellIcon, PlusIcon } from "@heroicons/react/20/solid";
import { signIn, signOut } from "next-auth/react";
import Link from "next/link";
import { PromptLink as Link } from "../PromptService/PromptLink";
import { Fragment } from "react";
import { navigation } from "../../config/site_settings";
import { type Session } from "next-auth";
Expand Down Expand Up @@ -43,7 +43,7 @@ const Nav = ({ session }: { session: Session | null }) => {
<span className="sr-only">CodΓΊ</span>
<Link
className="-ml-2 flex w-auto items-baseline p-2 text-neutral-800 transition-colors ease-in-out hover:text-neutral-600 focus:text-neutral-600 dark:text-neutral-50 dark:hover:text-neutral-300 focus:dark:text-neutral-300"
href="/"
to="/"
>
<Logo className="h-5 lg:h-6" />
<span className="ml-1 text-xs font-medium text-neutral-600 dark:text-neutral-400">
Expand All @@ -68,7 +68,7 @@ const Nav = ({ session }: { session: Session | null }) => {
<Link
className="nav-button"
key={item.name}
href={item.href}
to={item.href}
>
{item.name}
</Link>
Expand All @@ -81,11 +81,11 @@ const Nav = ({ session }: { session: Session | null }) => {
<div className="flex items-center space-x-2 text-sm font-medium lg:text-base">
{session ? (
<>
<Link className="nav-button" href="/my-posts">
<Link className="nav-button" to="/my-posts">
Your Posts
</Link>

<Link className="primary-button px-4" href="/create">
<Link className="primary-button px-4" to="/create">
<PlusIcon className="-ml-2 mr-1 h-5 w-5 p-0 text-white" />
New Post
</Link>
Expand All @@ -111,8 +111,7 @@ const Nav = ({ session }: { session: Session | null }) => {
{session && (
<>
<Link
title="Notifications"
href="/notifications"
to="/notifications"
className="focus-style relative flex-shrink-0 rounded-md p-2 text-neutral-500 hover:bg-neutral-200 hover:text-neutral-600 dark:text-neutral-400 dark:hover:bg-neutral-900 dark:hover:text-white"
>
<span className="sr-only">View notifications</span>
Expand Down Expand Up @@ -162,7 +161,7 @@ const Nav = ({ session }: { session: Session | null }) => {
) : (
<Link
className="block rounded px-4 py-2 text-sm text-neutral-700 hover:bg-neutral-200"
href={item.href || ""}
to={item.href || ""}
>
{item.name}
</Link>
Expand All @@ -180,8 +179,7 @@ const Nav = ({ session }: { session: Session | null }) => {
<ThemeToggle />
{session && (
<Link
title="Notifications"
href="/notifications"
to="/notifications"
className="focus-style relative flex-shrink-0 rounded-md p-2 text-neutral-500 hover:bg-neutral-200 hover:text-neutral-600 dark:text-neutral-400 dark:hover:bg-neutral-900 dark:hover:text-white "
>
<span className="sr-only">View notifications</span>
Expand Down
24 changes: 24 additions & 0 deletions components/ProgressBar/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client";
import NextTopLoader from "nextjs-toploader";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";
import { done as _done } from "nprogress";

const ProgressBar = () => {
const pathname = usePathname();
const searchParams = useSearchParams();

useEffect(() => {
_done(true);
}, [pathname, searchParams]);

return (
<NextTopLoader
easing="linear"
showSpinner={false}
template='<div class="bar" role="bar"><div class="gradient"></div></div>'
/>
);
};

export default ProgressBar;
21 changes: 21 additions & 0 deletions components/PromptService/PromptContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use client";
import { createContext, useContext, useState } from "react";

const defaultContextValue = {
unsavedChanges: false,
setUnsavedChanges: () => {},
};

const PromptContext = createContext(defaultContextValue);

export const usePrompt = () => useContext(PromptContext);

export const PromptProvider = ({ children }) => {
const [unsavedChanges, setUnsavedChanges] = useState(false);

return (
<PromptContext.Provider value={{ unsavedChanges, setUnsavedChanges }}>
{children}
</PromptContext.Provider>
);
};
Loading