-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
02e88ae
commit 5126840
Showing
5 changed files
with
264 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
"use client"; | ||
|
||
import { useState } from "react"; | ||
import { useMutation } from "convex/react"; | ||
import { toast } from "sonner"; | ||
import { Check, Copy, Globe } from "lucide-react"; | ||
|
||
import { Doc } from "@/convex/_generated/dataModel"; | ||
import { | ||
PopoverTrigger, | ||
Popover, | ||
PopoverContent | ||
} from "@/components/ui/popover" | ||
import { useOrigin } from "@/hooks/use-origin"; | ||
import { api } from "@/convex/_generated/api"; | ||
import { Button } from "@/components/ui/button"; | ||
|
||
interface PublishProps { | ||
initialData: Doc<"documents">; | ||
}; | ||
|
||
export const Publish = ({ | ||
initialData | ||
}: PublishProps) => { | ||
const origin = useOrigin(); | ||
const update = useMutation(api.documents.update); | ||
|
||
const [copied, setCopied] = useState(false); | ||
const [isSubmitting, setIsSubmitting] = useState(false); | ||
|
||
const url = `${origin}/preview/${initialData._id}`; | ||
|
||
const onPublish = () => { | ||
setIsSubmitting(true); | ||
|
||
const promise = update({ | ||
id: initialData._id, | ||
isPublished: true, | ||
}) | ||
.finally(() => setIsSubmitting(false)); | ||
|
||
toast.promise(promise, { | ||
loading: "Publishing...", | ||
success: "Note published", | ||
error: "Failed to publish note.", | ||
}); | ||
} | ||
|
||
const onUnpublish = () => { | ||
setIsSubmitting(true); | ||
|
||
const promise = update({ | ||
id: initialData._id, | ||
isPublished: false, | ||
}) | ||
.finally(() => setIsSubmitting(false)); | ||
|
||
toast.promise(promise, { | ||
loading: "Unpublishing...", | ||
success: "Note unpublished", | ||
error: "Failed to 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 && ( | ||
<Globe | ||
className="text-sky-500 w-4 h-4 ml-2" | ||
/> | ||
)} | ||
</Button> | ||
</PopoverTrigger> | ||
<PopoverContent | ||
className="w-72" | ||
align="end" | ||
alignOffset={8} | ||
forceMount | ||
> | ||
{initialData.isPublished ? ( | ||
<div className="space-y-4"> | ||
<div className="flex items-center gap-x-2"> | ||
<Globe className="text-sky-500 animate-pulse h-4 w-4" /> | ||
<p className="text-xs font-medium text-sky-500"> | ||
This note is live on web. | ||
</p> | ||
</div> | ||
<div className="flex items-center"> | ||
<input | ||
className="flex-1 px-2 text-xs border rounded-l-md h-8 bg-muted truncate" | ||
value={url} | ||
disabled | ||
/> | ||
<Button | ||
onClick={onCopy} | ||
disabled={copied} | ||
className="h-8 rounded-l-none" | ||
> | ||
{copied ? ( | ||
<Check className="h-4 w-4" /> | ||
) : ( | ||
<Copy className="h-4 w-4" /> | ||
)} | ||
</Button> | ||
</div> | ||
<Button | ||
size="sm" | ||
className="w-full text-xs" | ||
disabled={isSubmitting} | ||
onClick={onUnpublish} | ||
> | ||
Unpublish | ||
</Button> | ||
</div> | ||
) : ( | ||
<div className="flex flex-col items-center justify-center"> | ||
<Globe | ||
className="h-8 w-8 text-muted-foreground mb-2" | ||
/> | ||
<p className="text-sm font-medium mb-2"> | ||
Publish this note | ||
</p> | ||
<span className="text-xs text-muted-foreground mb-4"> | ||
Share your work with others. | ||
</span> | ||
<Button | ||
disabled={isSubmitting} | ||
onClick={onPublish} | ||
className="w-full text-xs" | ||
size="sm" | ||
> | ||
Publish | ||
</Button> | ||
</div> | ||
)} | ||
</PopoverContent> | ||
</Popover> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
"use client"; | ||
|
||
import { useMutation, useQuery } from "convex/react"; | ||
import dynamic from "next/dynamic"; | ||
import { useMemo } from "react"; | ||
|
||
import { api } from "@/convex/_generated/api"; | ||
import { Id } from "@/convex/_generated/dataModel"; | ||
import { Toolbar } from "@/components/toolbar"; | ||
import { Cover } from "@/components/cover"; | ||
import { Skeleton } from "@/components/ui/skeleton"; | ||
import { useParams } from "next/navigation"; | ||
|
||
// interface DocumentIdPageProps { | ||
// params: { | ||
// documentId: Id<"documents">; | ||
// }; | ||
// }; | ||
|
||
const DocumentIdPage = () => { | ||
const Editor = useMemo(() => dynamic(() => import("@/components/editor"), { ssr: false }) ,[]); | ||
const params = useParams(); | ||
|
||
const document = useQuery(api.documents.getById, { | ||
documentId: params.documentId as Id<"documents"> | ||
}); | ||
|
||
const update = useMutation(api.documents.update); | ||
|
||
const onChange = (content: string) => { | ||
update({ | ||
id: params.documentId as Id<"documents">, | ||
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>Not found</div> | ||
} | ||
|
||
return ( | ||
<div className="pb-40"> | ||
<Cover preview url={document.coverImage} /> | ||
<div className="md:max-w-3xl lg:max-w-4xl mx-auto"> | ||
<Toolbar preview initialData={document} /> | ||
<Editor | ||
editable={false} | ||
onChange={onChange} | ||
initialContent={document.content} | ||
/> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default DocumentIdPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
const PublicLayout = ({ | ||
children | ||
}: { | ||
children: React.ReactNode; | ||
}) => { | ||
return ( | ||
<div className="h-full dark:bg-[#1F1F1F]"> | ||
{children} | ||
</div> | ||
); | ||
} | ||
|
||
export default PublicLayout; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { url } from "inspector"; | ||
import { useEffect, useState } from "react"; | ||
|
||
// This hook is used to get the origin url of the current window location. | ||
export const useOrigin = () => { | ||
const [mounted, setMounted] = useState(false); | ||
const origin = typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; | ||
|
||
useEffect(() => { | ||
setMounted(true); | ||
}, []); | ||
|
||
if (!mounted) { | ||
return ""; | ||
} | ||
|
||
return origin; | ||
} |