Skip to content

Commit

Permalink
Merge pull request #16 from git-init-priyanshu/nextjs
Browse files Browse the repository at this point in the history
Collaboration Feature
  • Loading branch information
git-init-priyanshu authored Aug 18, 2024
2 parents aa922a5 + 53344c5 commit 6f12827
Show file tree
Hide file tree
Showing 21 changed files with 1,490 additions and 113 deletions.
18 changes: 2 additions & 16 deletions app/(auth)/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,12 @@

import { cookies } from "next/headers"
import { z } from 'zod';
import { JWTPayload, SignJWT, importJWK } from 'jose';
// @ts-ignore
import bcrypt from 'bcryptjs';

import prisma from "@/prisma/prismaClient";
import { signinSchema, signupSchema } from './zodSchema';

const generateJWT = async (payload: JWTPayload) => {
const secret = process.env.JWT_SECRET || 'secret';

const jwk = await importJWK({ k: secret, alg: 'HS256', kty: 'oct' });

const jwt = await new SignJWT(payload)
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('365d')
.sign(jwk);

return jwt;
};
import { generateJWT } from "@/helpers/generateJWT";

export const SignupAction = async (data: z.infer<typeof signupSchema>) => {
try {
Expand Down Expand Up @@ -51,7 +37,7 @@ export const SignupAction = async (data: z.infer<typeof signupSchema>) => {
await prisma.user.upsert({
where: { email: data.email },
update: {
password: hashedPassword
password: hashedPassword
},
create: {
name: data.name,
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions app/(auth)/signin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import Link from "next/link"

import LogInForm from "./components/LogInForm"
import SignInForm from "./components/SignInForm"
import GoogleAuthButton from "./components/GoogleAuthButton"

export default function Login() {
Expand All @@ -16,7 +16,7 @@ export default function Login() {
</p>
</div>
<div className="flex flex-col gap-2 mt-4 text-center text-sm">
<LogInForm />
<SignInForm />
<p className="divider">OR</p>
<GoogleAuthButton />
<p>
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions app/(auth)/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Link from "next/link"

import SignInForm from "./components/SignInForm"
import SignUpForm from "./components/SignUpForm"
import GoogleAuthButton from "./components/GoogleAuthButton"

export default function Signup() {
Expand All @@ -13,7 +13,7 @@ export default function Signup() {
</p>
</div>
<div className="flex flex-col gap-2 mt-4 text-center text-sm">
<SignInForm />
<SignUpForm />
<p className="divider">OR</p>
<GoogleAuthButton />
<p>
Expand Down
11 changes: 10 additions & 1 deletion app/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@ export const GetAllDocs = async (userId: string) => {
try {
const response = await prisma.document.findMany(
{
where: { userId },
where: {
users: {
some: {
user: {
id: userId
}
}
}
},
select: {
id: true,
thumbnail: true,
name: true,
updatedAt: true,
createdBy: true,
users: {
select: {
user: {
Expand Down
43 changes: 15 additions & 28 deletions app/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,47 @@

import { useRef, useState } from "react"
import { useRouter } from "next/navigation"
import { CircleCheck } from "lucide-react"

import {
Card,
CardContent,
CardFooter,
} from "@/components/ui/card"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Input } from "@/components/ui/input"
import type { User } from "@prisma/client"

import prettifyDate from '@/helpers/prettifyDates'
import CardOptions from "./components/Options"
import { RenameDocument } from "./actions"
import AvatarList from '@/components/AvatarList'
import CardOptions from "./components/Options"
import prettifyDate from '@/helpers/prettifyDates'
import useClientSession from "@/lib/customHooks/useClientSession"
import { CircleCheck } from "lucide-react"

type DocCardPropType = {
docId: string;
thumbnail: string | null;
title: string;
updatedAt: Date
users: {
user:
{
name: string,
picture: string | null
}
user: Pick<User, 'name' | 'picture'>
}[]
}
export default function DocCard({ docId, thumbnail, title, updatedAt, users }: DocCardPropType) {
export default function DocCard({
docId,
thumbnail,
title,
updatedAt,
users
}: DocCardPropType) {
const router = useRouter();

const session = useClientSession();
localStorage.setItem('name', session.name as string);

const inputRef = useRef<HTMLInputElement>(null);

const [name, setName] = useState(title)

const getInitials = (name: string) => {
let initials = name.split(" ");

if (initials.length > 2) return initials[0][0] + initials[1][0];
return initials[0][0];
}

return (
<Card className="hover:shadow-lg" >
<CardContent
Expand All @@ -72,17 +69,7 @@ export default function DocCard({ docId, thumbnail, title, updatedAt, users }: D
</div>
<div className="flex items-center w-full justify-between">
<div className="flex gap-2 items-center">
{users.map((e, index) => {
return (
<Avatar key={index} className="size-8">
{
e.user.picture
? <AvatarImage src={e.user.picture} />
: <AvatarFallback>{getInitials(e.user.name)}</AvatarFallback>
}
</Avatar>
)
})}
<AvatarList users={users} />
<p className="text-neutral-600 cursor-default">
{prettifyDate(String(updatedAt), {
year: "numeric",
Expand Down
22 changes: 17 additions & 5 deletions app/components/Card/components/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { Button } from "@/components/ui/button"
import { DeleteDocument } from "../actions"
import useClientSession from "@/lib/customHooks/useClientSession"
import LoaderButton from "@/components/LoaderButton"
import { Jua } from "next/font/google"

type CardOptionsPropType = {
docId: string,
Expand All @@ -40,12 +41,12 @@ export default function CardOptions({ docId, inputRef }: CardOptionsPropType) {
const docOptions = [
{ icon: Type, color: "#60b7c3", title: "Rename", onClick: () => renameDocument() },
{ icon: FilePenLine, color: "#48acf9", title: "Edit", onClick: () => editDocument() },
{ icon: Share2, color: "#48f983", title: "Share", onClick: () => console.log("edit") },
{ icon: Share2, color: "#48f983", title: "Share", onClick: () => shareDocument() },
{ icon: Trash2, color: "#f94848", title: "Delete", onClick: () => deleteDocument() },
]

const [isOptionsOpen, setIsOptionsOpen] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);

const renameDocument = () => {
Expand All @@ -58,9 +59,20 @@ export default function CardOptions({ docId, inputRef }: CardOptionsPropType) {
router.push(`/writer/${docId}`)
}

const shareDocument = () => {
navigator.clipboard.writeText(`${process.env.NEXT_PUBLIC_APP_URL}/writer/${docId}`).then(() => {
toast.success('Share link copied to clipboard');
}).catch(e => {
console.log(e)
toast.error(e);
}).finally(() => {
setIsOptionsOpen(false);
});
}

const deleteDocument = async () => {
setIsOptionsOpen(false);
setIsOpen(true);
setIsDeleteModalOpen(true);
}

const confirmDeleteDocument = async () => {
Expand Down Expand Up @@ -102,7 +114,7 @@ export default function CardOptions({ docId, inputRef }: CardOptionsPropType) {
</PopoverContent>
</Popover>

<Dialog open={isOpen}>
<Dialog open={isDeleteModalOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete Document?</DialogTitle>
Expand All @@ -111,7 +123,7 @@ export default function CardOptions({ docId, inputRef }: CardOptionsPropType) {
</DialogDescription>
</DialogHeader>
<div className="flex gap-4">
<Button variant="outline" onClick={() => setIsOpen(false)}>Cancel</Button>
<Button variant="outline" onClick={() => setIsDeleteModalOpen(false)}>Cancel</Button>
<LoaderButton
variant="outline"
onClickFunc={confirmDeleteDocument}
Expand Down
26 changes: 26 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,29 @@
margin-left: 0.25em;
}

/* Give a remote user a caret */
.collaboration-cursor__caret {
border-left: 1px solid #0d0d0d;
border-right: 1px solid #0d0d0d;
margin-left: -1px;
margin-right: -1px;
pointer-events: none;
position: relative;
word-break: normal;
}

/* Render the username above the caret */
.collaboration-cursor__label {
border-radius: 3px 3px 3px 0;
color: #0d0d0d;
font-size: 12px;
font-style: normal;
font-weight: 600;
left: -1px;
line-height: normal;
padding: 0.1rem 0.3rem;
position: absolute;
top: -1.4em;
user-select: none;
white-space: nowrap;
}
49 changes: 39 additions & 10 deletions app/writer/[id]/actions.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
"use server"

import getServerSession from "@/lib/customHooks/getServerSession"
import prisma from "@/prisma/prismaClient"
import { revalidatePath } from "next/cache"

export const GetDocDetails = async (id: any, userId: string) => {
export const GetDocDetails = async (id: any) => {
const session = await getServerSession();
if (!session.id) return {
success: false,
error: "User is not logged in",
}

try {
const doc = await prisma.document.findFirst({ where: { id, userId } })
const doc = await prisma.document.update({
where: { id },
data: {
users: {
upsert: {
where: {
userId_documentId: {
userId: session.id,
documentId: id,
},
},
update: {},
create: {
user: {
connect: {
id: session.id
}
}
}
},
},
}
})

if (!doc) return {
success: false,
error: "Document does not exist",
Expand All @@ -26,14 +56,13 @@ export const UpdateDocData = async (id: any, userId: string, data: string) => {
error: "Document does not exist",
}

await prisma.document.update(
{
where: { id, userId },
data: {
data: data,
updatedAt: new Date(),
}
})
await prisma.document.update({
where: { id, userId },
data: {
data: data,
updatedAt: new Date(),
}
})

return { success: true, data: "Saved" }
} catch (e) {
Expand Down
12 changes: 6 additions & 6 deletions app/writer/[id]/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,25 +56,25 @@ export default function Header({
: <div className='h-7 w-40 rounded hidden lg:block bg-neutral-100 animate-pulse mx-4'></div>}

<Button
onClick={() => editor?.chain().focus().undo().run()}
onClick={() => editor?.commands.undo()}
variant="ghost"
className='px-3 rounded-md'
disabled={!editor?.can().chain().focus().undo().run()}
// disabled={!editor?.can().chain().focus().undo().run()}
><Undo size={15} /></Button>
<Button
onClick={() => editor?.chain().focus().redo().run()}
onClick={() => editor?.commands.redo()}
variant="ghost"
className='px-3 rounded-md'
disabled={!editor?.can().chain().focus().redo().run()}
// disabled={!editor?.can().chain().focus().redo().run()}
><Redo size={15} /></Button>

<Drawer>
<DrawerTrigger asChild>
<Button variant="ghost" className='px-3 rounded-md'>
<Button variant="ghost" className='px-3 rounded-md block lg:hidden'>
<ALargeSmall size={15} />
</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerContent className='block lg:hidden'>
<DrawerHeader className='flex justify-between items-center sm:px-20'>
<DrawerTitle>Format</DrawerTitle>
<DrawerClose>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { useState } from "react";
import { useEffect, useRef, useState } from "react";
import { Editor } from "@tiptap/react";
import {
Baseline,
Expand Down
Loading

0 comments on commit 6f12827

Please sign in to comment.