added invitation popup
This commit is contained in:
parent
2c29ae11e7
commit
e3075627a4
62
app/(invite)/(routes)/invite/[inviteCode]/page.tsx
Normal file
62
app/(invite)/(routes)/invite/[inviteCode]/page.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import { currentProfile } from "@/lib/current-profile";
|
||||
import { redirectToSignIn } from "@clerk/nextjs";
|
||||
import { redirect } from "next/navigation";
|
||||
import { db } from "@/lib/db";
|
||||
|
||||
interface InviteCodePageProps {
|
||||
params: {
|
||||
inviteCode: string;
|
||||
};
|
||||
};
|
||||
|
||||
const InviteCodePage = async ({
|
||||
params
|
||||
}: InviteCodePageProps) => {
|
||||
const profile = await currentProfile();
|
||||
|
||||
if (!profile) {
|
||||
return redirectToSignIn();
|
||||
}
|
||||
|
||||
if (!params?.inviteCode) {
|
||||
return redirect("/");
|
||||
}
|
||||
|
||||
const existingServer = await db.server.findFirst({
|
||||
where: {
|
||||
inviteCode: params.inviteCode,
|
||||
members: {
|
||||
some: {
|
||||
profileId: profile.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (existingServer) {
|
||||
return redirect(`/servers/${existingServer.id}`);
|
||||
}
|
||||
|
||||
const server= await db.server.update({
|
||||
where: {
|
||||
inviteCode: params.inviteCode,
|
||||
},
|
||||
data: {
|
||||
members: {
|
||||
create: [
|
||||
{
|
||||
profileId: profile.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if(server) {
|
||||
return redirect(`/servers/${server.id}`);
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export default InviteCodePage;
|
38
app/api/servers/[serverId]/invite-code/route.ts
Normal file
38
app/api/servers/[serverId]/invite-code/route.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { currentProfile } from "@/lib/current-profile";
|
||||
import { db } from "@/lib/db";
|
||||
|
||||
|
||||
export async function PATCH(
|
||||
req: Request,
|
||||
{ params }: { params: { serverId: string } }
|
||||
) {
|
||||
try {
|
||||
const profile = await currentProfile();
|
||||
|
||||
if (!profile) {
|
||||
return new NextResponse("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
if (!params.serverId) {
|
||||
return new NextResponse("Server ID Missing", { status: 400 });
|
||||
}
|
||||
|
||||
const server = await db.server.update({
|
||||
where: {
|
||||
id: params.serverId,
|
||||
profileId: profile.id,
|
||||
},
|
||||
data: {
|
||||
inviteCode: uuidv4(),
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json(server)
|
||||
} catch (error) {
|
||||
console.log("[SERVER_ID]", error);
|
||||
return new NextResponse("Internal Error", { status: 500});
|
||||
}
|
||||
}
|
90
components/modals/invite-modal.tsx
Normal file
90
components/modals/invite-modal.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
"use client";
|
||||
|
||||
import axios from "axios";
|
||||
import { useState } from "react";
|
||||
import { Check, Copy, RefreshCw } from "lucide-react";
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { useModal } from "@/hooks/use-modal-store";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useOrigin } from "@/hooks/user-origin";
|
||||
|
||||
|
||||
export const InviteModal = () => {
|
||||
const { onOpen, isOpen, onClose, type, data } = useModal();
|
||||
const origin = useOrigin();
|
||||
|
||||
const isModalOpen = isOpen && type === "invite";
|
||||
const { server } = data;
|
||||
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const inviteUrl = `${origin}/invite/${server?.inviteCode}`;
|
||||
|
||||
const onCopy = () => {
|
||||
navigator.clipboard.writeText(inviteUrl);
|
||||
setCopied(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const onNew = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await axios.patch(`/api/servers/${server?.id}/invite-code`);
|
||||
|
||||
onOpen("invite", {server: response.data});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally{
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={isModalOpen} onOpenChange={onClose}>
|
||||
<DialogContent className="bg-white text-black p-0 overflow-hidden">
|
||||
<DialogHeader className="pt-8 px-6">
|
||||
<DialogTitle className="text-2xl text-center font-bold">
|
||||
Invite Friends
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="p-6">
|
||||
<Label className="uppercase text-xs font-bold text-zinc-500 dark:text-secondary/70">
|
||||
Server invite link
|
||||
</Label>
|
||||
<div className="flex items-center mt-2 gap-x-2">
|
||||
<Input className="bg-zinc-300/50 border-0 focus-visible:ring-0 text-black focus-visible:ring-offset-0 "
|
||||
disabled={isLoading}
|
||||
value={inviteUrl}
|
||||
/>
|
||||
<Button disabled={isLoading} onClick={onCopy} size="icon">
|
||||
{copied ? <Check className="w-4 h-4"/> : <Copy className="w-4 h-4"/>}
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
onClick={onNew}
|
||||
disabled={isLoading}
|
||||
variant="link"
|
||||
size="sm"
|
||||
className="text-xs text-zinc-500 mt-4"
|
||||
>
|
||||
Generate a new link
|
||||
<RefreshCw className="w-4 h-4 ml-2"/>
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { CreateServerModal } from "@/components/modals/create-server-modal";
|
||||
import { InviteModal } from "@/components/modals/invite-modal";
|
||||
|
||||
export const ModalProvidor = () => {
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
@ -18,6 +19,7 @@ export const ModalProvidor = () => {
|
||||
return (
|
||||
<>
|
||||
<CreateServerModal />
|
||||
<InviteModal />
|
||||
</>
|
||||
)
|
||||
}
|
@ -3,7 +3,8 @@
|
||||
import { ServerWithMembersWithProfiles } from "@/types";
|
||||
import { MemberRole } from "@prisma/client";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
import { ChevronDown, LogOut, LogOutIcon, Plus, PlusCircle, Settings, Trash, Users } from "lucide-react";
|
||||
import { ChevronDown, LogOut, Plus, PlusCircle, Settings, Trash, Users } from "lucide-react";
|
||||
import { useModal } from "@/hooks/use-modal-store";
|
||||
|
||||
interface ServerHeaderProps {
|
||||
server: ServerWithMembersWithProfiles;
|
||||
@ -11,6 +12,7 @@ interface ServerHeaderProps {
|
||||
}
|
||||
|
||||
export const SeverHeader = ({server, role}: ServerHeaderProps) => {
|
||||
const { onOpen } = useModal();
|
||||
const isAdmin = role === MemberRole.ADMIN;
|
||||
const isModerator = isAdmin || role === MemberRole.MODERATOR;
|
||||
|
||||
@ -30,6 +32,7 @@ export const SeverHeader = ({server, role}: ServerHeaderProps) => {
|
||||
>
|
||||
{isModerator && (
|
||||
<DropdownMenuItem
|
||||
onClick={() => onOpen("invite", {server})}
|
||||
className="text-indigo-600 dark:text-indigo-400 px-3 py-2 text-sm cursor-pointer"
|
||||
>
|
||||
Invite People
|
||||
|
@ -1,17 +1,25 @@
|
||||
|
||||
import { Server } from "@prisma/client";
|
||||
import { create } from "zustand";
|
||||
|
||||
export type ModalType = "createServer";
|
||||
export type ModalType = "createServer" | "invite";
|
||||
|
||||
interface ModalData {
|
||||
server?: Server
|
||||
}
|
||||
|
||||
interface ModalStore {
|
||||
type: ModalType | null;
|
||||
data: ModalData;
|
||||
isOpen: boolean;
|
||||
onOpen: (type: ModalType) => void;
|
||||
onOpen: (type: ModalType, data?:ModalData) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const useModal = create<ModalStore>((set) => ({
|
||||
type: null,
|
||||
data: {},
|
||||
isOpen: false,
|
||||
onOpen: (type) => set({ isOpen: true, type }),
|
||||
onOpen: (type, data= {}) => set({ isOpen: true, type, data }),
|
||||
onClose: () => set({ type: null, isOpen: false }),
|
||||
}));
|
18
hooks/user-origin.ts
Normal file
18
hooks/user-origin.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export const useOrigin = () => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
|
||||
}, []);
|
||||
|
||||
const origin = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
|
||||
if (!mounted) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return origin;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user