Skip to content

Commit

Permalink
Merge pull request #7 from Lostovayne/auth
Browse files Browse the repository at this point in the history
Auth
  • Loading branch information
Lostovayne authored Dec 21, 2024
2 parents 4e67f73 + fd19134 commit 676acb6
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 31 deletions.
22 changes: 13 additions & 9 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@

# Deployment used by `npx convex dev`
CONVEX_DEPLOYMENT=.......... # team: lostovayne, project: notion-clone-7002e
NEXT_PUBLIC_CONVEX_URL=https:.......

# Clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=...........
CLERK_SECRET_KEY=......

# Edge Store
CONVEX_DEPLOYMENT=
NEXT_PUBLIC_CONVEX_URL=
EDGE_STORE_ACCESS_KEY=
EDGE_STORE_SECRET_KEY=

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=

NEXT_PUBLIC_EDGE_STORE_ACCESS_KEY=
NEXT_PUBLIC_EDGE_STORE_SECRET_KEY=


NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL=/
NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL=/

NEXT_PUBLIC_AFTER_SIGN_IN_URL=/
# Create Wrangler.toml


Expand Down
9 changes: 6 additions & 3 deletions app/(main)/(routes)/documents/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ import { PlusCircle } from "lucide-react";
import { useMutation } from "convex/react";
import { api } from "@/convex/_generated/api";
import { toast } from "sonner";
import { useRouter } from "next/navigation";

const DocumentsPage = () => {
const { user } = useUser();
const router = useRouter();

const create = useMutation(api.documents.create);
const onCreate = async () => {
const promise = create({ title: "Untitled" });
const promise = create({ title: "Untitled" }).then((documentId) => {
router.push(`/documents/${documentId}`);
});

toast.promise(promise, {
loading: "Creating a new note...",
Expand All @@ -24,8 +29,6 @@ const DocumentsPage = () => {

return (
<div className="h-full flex flex-col items-center justify-center space-y-4">
{/* Squeleton de carga mientras se carga la imagen */}

<Image
src="/empty.png"
height={230}
Expand Down
10 changes: 4 additions & 6 deletions app/(main)/_components/item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const Item = ({
const onArchive = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
event.stopPropagation();
if (!id) return;
const promise = archive({ id });
const promise = archive({ id }).then(() => router.push("/documents"));

toast.promise(promise, {
loading: "Moving to trash...",
Expand All @@ -57,22 +57,20 @@ export const Item = ({
});
};

// HandleExpand
const handleExpand = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
event.stopPropagation();
onExpand?.();
};

// onCreate
const onCreate = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
event.stopPropagation();
if (!id) return;

if (!id) return;
const promise = create({ title: "Untitled", parentDocument: id }).then((documentId) => {
if (!expanded) {
onExpand?.();
}
// router.push(`/documents/${documentId}`);
router.push(`/documents/${documentId}`);
});

toast.promise(promise, {
Expand Down Expand Up @@ -104,7 +102,7 @@ export const Item = ({

{documentIcon ?
<div className="shrink-0 mr-2 text-[18px]">{documentIcon}</div>
: <Icon className="shrink-0 h-[18px] mr-2 text-muted-foreground" />}
: <Icon className="shrink-0 h-[18px] w-[18px] mr-2 text-muted-foreground" />}

<span className="truncate">{label}</span>
{isSearch && (
Expand Down
30 changes: 17 additions & 13 deletions app/(main)/_components/navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { api } from '@/convex/_generated/api';
import type { Id } from '@/convex/_generated/dataModel';
import { useQuery } from 'convex/react';
import { MenuIcon } from 'lucide-react';
import { useParams } from 'next/navigation';
import { ReactElement } from 'react';
import Banner from './banner';
import Menu from './menu';
import Title from './title';
import { api } from "@/convex/_generated/api";
import type { Id } from "@/convex/_generated/dataModel";
import { useQuery } from "convex/react";
import { MenuIcon } from "lucide-react";
import { useParams } from "next/navigation";
import { ReactElement } from "react";
import Banner from "./banner";
import Menu from "./menu";
import Title from "./title";
import Publish from "./publish";

interface NavbarProps {
isCollapsed: boolean;
Expand All @@ -16,14 +17,14 @@ interface NavbarProps {
const Navbar = ({ isCollapsed, onResetWidth }: NavbarProps): ReactElement => {
const params = useParams();
const document = useQuery(api.documents.getById, {
documentId: params.documentId as Id<'documents'>
documentId: params.documentId as Id<"documents">
});

if (document === undefined)
return (
<nav className="bg-background dark:bg-[#1f1f1f] px-3 py-2 w-full flex items-center justify-between">
<Title.Skeleton />
<div className={'flex items-center gap-x-2'}>
<div className={"flex items-center gap-x-2"}>
<Menu.Skeleton />
</div>
</nav>
Expand All @@ -33,11 +34,14 @@ const Navbar = ({ isCollapsed, onResetWidth }: NavbarProps): ReactElement => {
return (
<>
<nav className="bg-background dark:bg-[#1f1f1f] px-3 py-2 w-full flex items-center gap-x-4">
{isCollapsed && <MenuIcon role="button" className="text-muted-foreground size-6" onClick={onResetWidth} />}
{isCollapsed && (
<MenuIcon role="button" className="text-muted-foreground size-6" onClick={onResetWidth} />
)}

<div className="flex items-center justify-between w-full">
<Title initialData={document} />
<div className={'flex items-center gap-x-2'}>
<div className={"flex items-center gap-x-2"}>
<Publish initialData={document} />
<Menu documentId={document._id} />
</div>
</div>
Expand Down
118 changes: 118 additions & 0 deletions app/(main)/_components/publish.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"use client";

import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { api } from "@/convex/_generated/api";
import type { Doc } from "@/convex/_generated/dataModel";

import { useOrigin } from "@/hooks/use-origin";
import { useMutation } from "convex/react";
import { Check, CheckIcon, CopyIcon, GlobeIcon } from "lucide-react";
import { FC, ReactElement, useState } from "react";
import { toast } from "sonner";

interface PublishProps {
initialData: Doc<"documents">;
}

const Publish: FC<PublishProps> = ({ initialData }): ReactElement => {
const origin = useOrigin();
const update = useMutation(api.documents.update);

const [copied, setCopied] = useState<boolean>(false);
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);

const url = `${origin}/preview/${initialData._id}`;

const onPublish = async () => {
setIsSubmitting(true);
const promise = update({
id: initialData._id,
isPublished: true
}).finally(() => setIsSubmitting(false));

toast.promise(promise, {
loading: "Publishing...",
success: "Note published",
error: "Could not publish note"
});
};

const onUnPublish = async () => {
setIsSubmitting(true);
const promise = update({
id: initialData._id,
isPublished: false
}).finally(() => setIsSubmitting(false));

toast.promise(promise, {
loading: "Unpublishing...",
success: "Note unpublished",
error: "Could not unpublish note"
});
};

const onCopy = () => {
navigator.clipboard.writeText(url);
setCopied(true);
setTimeout(() => setCopied(false), 1000);
};

return (
<Popover>
<PopoverTrigger asChild>
<Button size={"sm"} variant={"ghost"}>
Publish
{initialData.isPublished && <GlobeIcon className=" text-sky-500 ml-2 h-4 w-4" />}
</Button>
</PopoverTrigger>
<PopoverContent className={"w-72"} align={"end"} alignOffset={8} forceMount>
{initialData.isPublished ?
<div className={"space-y-2"}>
<div className={"flex items-center gap-x-2"}>
<GlobeIcon className={"text-sky-500 h-4 w-4"} />
<p>This note is live on the internet</p>
</div>
<div className={"flex items-center"}>
<Input
value={url}
readOnly
className={
"flex-1 px-2 text-xs border rounded-l-md h-8 bg-muted-foreground truncate"
}
disabled
/>
<Button onClick={onCopy} disabled={copied} className={"h-8 rounded-l-none"}>
{copied ?
<CheckIcon className={" h-4 w-4"} />
: <CopyIcon className={" h-4 w-4"} />}
</Button>
</div>
<Button
onClick={onUnPublish}
size={"sm"}
className={"w-full text-xs"}
disabled={isSubmitting}>
Unpublish
</Button>
</div>
: <div className={"flex flex-col items-center justify-center"}>
<GlobeIcon className={"size-8 text-muted-foreground mb-2"} />
<p className={"text-sm font-medium mb-2"}>Publish this note</p>
<span>Share your work with the world</span>
<Button
disabled={isSubmitting}
onClick={onPublish}
className={"w-full text-xs"}
size={"sm"}>
Publish
</Button>
</div>
}
</PopoverContent>
</Popover>
);
};

export default Publish;
54 changes: 54 additions & 0 deletions app/(public)/(routes)/preview/[documentId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"use client";
import Cover from "@/components/cover";
import { Editor } from "@/components/dynamic-editor";
import Toolbar from "@/components/toolbar";
import { Skeleton } from "@/components/ui/skeleton";
import { api } from "@/convex/_generated/api";
import type { Id } from "@/convex/_generated/dataModel";
import { useMutation, useQuery } from "convex/react";
import { ReactElement } from "react";

interface DocumentIdPageProps {
params: { documentId: Id<"documents"> };
}

const DocumentIdPage = ({ params }: DocumentIdPageProps): ReactElement => {
const documentId = params.documentId;
const document = useQuery(api.documents.getById, {
documentId: documentId
});

const update = useMutation(api.documents.update);

const onChange = (content: string) => {
update({ id: documentId, content });
};

if (document === undefined)
return (
<div>
<Cover.Skeleton />
<div className={"md:max-w-3xl lg:max-w-4xl mx-auto mt-10"}>
<div className={"space-y-4 pl-8 pt-4"}>
<Skeleton className={"h-14 w-[50%] "} />
<Skeleton className={"h-4 w-[80%] "} />
<Skeleton className={"h-4 w-[40%] "} />
<Skeleton className={"h-4 w-[60%] "} />
</div>
</div>
</div>
);
if (document === null) return <div>Document not found</div>;

return (
<div className={"pb-40"}>
<Cover preview url={document.coverImage} />
<div className={"md:max-w-4xl lg:max-w-5xl xl:max-w-6xl 2xl:max-w-7xl mx-auto"}>
<Toolbar preview initialData={document} />
<Editor onChange={onChange} initialContent={document.content} editable={false} />
</div>
</div>
);
};

export default DocumentIdPage;
41 changes: 41 additions & 0 deletions app/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"use client";

import Image from "next/image";
import Link from "next/link";
import { Button } from "@/components/ui/button";

const Error = () => {
return (
<div className={"h-full flex flex-col items-center justify-center space-y-4"}>
<Image
src={"/error.png"}
height={230}
width={300}
alt="error"
className={"dark:hidden object-cover"}
priority
layout="fixed"
/>
<Image
src={"/error-dark.png"}
height={230}
width={300}
layout="fixed"
alt="error"
className={"hidden dark:block object-cover"}
priority
loading="eager"
/>
<h2 className={"text-lg font-medium"}>Oops! Something went wrong</h2>
<p className={"text-sm text-muted-foreground"}>
We&apos;re sorry, but something went wrong. Please try again later.
</p>

<Button asChild>
<Link href={"/documents"}>Go back home</Link>
</Button>
</div>
);
};

export default Error;
22 changes: 22 additions & 0 deletions hooks/use-origin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useEffect, useState } from "react";

export const useOrigin = () => {
const [mounted, setMounted] = useState<boolean>(false);
/**
* Determina el origen de la ventana actual.
* Si la ventana no está definida, devuelve una cadena vacía.
* evita el error de windows no definido en el entorno del server
*
* @returns {string} El origen de la ventana actual o una cadena vacía si no está disponible.
*/
const origin =
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";

useEffect(() => {
setMounted(true);
}, []);

if (!mounted) return "";

return origin;
};

0 comments on commit 676acb6

Please sign in to comment.