added channel list and members to server sidebar
This commit is contained in:
parent
f8f4f0f94a
commit
d3cdd24d80
@ -33,6 +33,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from "@/components/ui/select";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().min(1, {
|
||||
@ -47,20 +48,30 @@ const formSchema = z.object({
|
||||
});
|
||||
|
||||
export const CreateChannelModal = () => {
|
||||
const { isOpen, onClose, type } = useModal();
|
||||
const { isOpen, onClose, type, data } = useModal();
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
|
||||
const isModalOpen = isOpen && type === "createChannel";
|
||||
const { channelType } = data;
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
type: ChannelType.TEXT,
|
||||
type: channelType || ChannelType.TEXT,
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (channelType) {
|
||||
form.setValue("type", channelType);
|
||||
}
|
||||
else{
|
||||
form.setValue("type", ChannelType.TEXT);
|
||||
}
|
||||
}, [channelType, form])
|
||||
|
||||
const isLoading = form.formState.isSubmitting;
|
||||
|
||||
const onSubmit = async (values: z.infer<typeof formSchema>) => {
|
||||
|
64
components/server/server-channel.tsx
Normal file
64
components/server/server-channel.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
"use client"
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Channel, ChannelType, MemberRole, Server } from "@prisma/client";
|
||||
import { Edit, Hash, Lock, Mic, Trash, Video } from "lucide-react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { ActionTooltip } from "@/components/action-tooltip";
|
||||
|
||||
interface ServerChannelProps {
|
||||
channel: Channel;
|
||||
server: Server;
|
||||
role?: MemberRole;
|
||||
}
|
||||
|
||||
const iconMap = {
|
||||
[ChannelType.TEXT]: Hash,
|
||||
[ChannelType.AUDIO]: Mic,
|
||||
[ChannelType.VIDEO]: Video
|
||||
}
|
||||
|
||||
export const ServerChannel = ({
|
||||
channel,
|
||||
server,
|
||||
role
|
||||
}: ServerChannelProps) => {
|
||||
const params = useParams();
|
||||
const router = useRouter();
|
||||
|
||||
const Icon = iconMap[channel.type];
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => {}}
|
||||
className={cn(
|
||||
"group px-2 py-2 rounded-md flex items-center gap-x-2 w-full hover:bg-zinc-700/10 dark:hover:bg-zinc-700/50 transition mb-1",
|
||||
params?.channelId === channel.id && "bg-zinc-700/10 dark:bg-zinc-700"
|
||||
)}
|
||||
>
|
||||
<Icon className="flex-shrink-0 w-5 h-5 text-zinc-500 dark:text-zinc-400"/>
|
||||
<p className={cn(
|
||||
"link-clamp-1 font-semibold text-sm text-zinc-500 group-hover:text-zinc-600 dark:text-zinc-400 dark:group-hover:text-zinc-300 transition",
|
||||
params?.channelId === channel.id && "text-primary dark:text-zinc-200 dark:group-hover:text-white"
|
||||
)}>
|
||||
{channel.name}
|
||||
</p>
|
||||
{channel.name !== "general" && role !== MemberRole.GUEST && (
|
||||
<div className="ml-auto flex items-center gap-x-2">
|
||||
<ActionTooltip label="Edit">
|
||||
<Edit className="hidden group-hover:block w-4 h-4 text-zinc-500 hove:text-zinc-600 dark:text-zinc-400 dark:hove:text-zinc-300 transition"
|
||||
/>
|
||||
</ActionTooltip>
|
||||
<ActionTooltip label="Delete">
|
||||
<Trash className="hidden group-hover:block w-4 h-4 text-zinc-500 hove:text-zinc-600 dark:text-zinc-400 dark:hove:text-zinc-300 transition"
|
||||
/>
|
||||
</ActionTooltip>
|
||||
|
||||
</div>
|
||||
)}
|
||||
{channel.name === "general" && (
|
||||
<Lock className="ml-auto w-4 h-4 text-zinc-500 dark:text-zinc-400"/>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
@ -11,7 +11,7 @@ interface ServerHeaderProps {
|
||||
role?: MemberRole;
|
||||
}
|
||||
|
||||
export const SeverHeader = ({server, role}: ServerHeaderProps) => {
|
||||
export const ServerHeader = ({server, role}: ServerHeaderProps) => {
|
||||
const { onOpen } = useModal();
|
||||
const isAdmin = role === MemberRole.ADMIN;
|
||||
const isModerator = isAdmin || role === MemberRole.MODERATOR;
|
||||
|
49
components/server/server-member.tsx
Normal file
49
components/server/server-member.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
"use client"
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Member, MemberRole, Profile, Server } from "@prisma/client"
|
||||
import { ShieldAlert, ShieldCheck } from "lucide-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useRouter } from "next/router";
|
||||
import { UserAvatar } from "@/components/user-avatar";
|
||||
|
||||
interface ServerMemberProps {
|
||||
member: Member & {
|
||||
profile: Profile;
|
||||
};
|
||||
server: Server;
|
||||
}
|
||||
|
||||
const roleIconMap = {
|
||||
[MemberRole.GUEST]: null,
|
||||
[MemberRole.MODERATOR]: <ShieldCheck className="h-4 w-4 ml-2 text-indigo-500" />,
|
||||
[MemberRole.ADMIN]: <ShieldAlert className="w-4 h-4 ml-2 text-rose-500" />
|
||||
}
|
||||
|
||||
export const ServerMember = ({
|
||||
member,
|
||||
server
|
||||
}: ServerMemberProps) => {
|
||||
const params = useParams();
|
||||
//const router = useRouter();
|
||||
|
||||
const icon = roleIconMap[member.role];
|
||||
return (
|
||||
<button className={cn(
|
||||
"group px-2 py-2 rounded-md flex items-center gap-x-2 w-full hover:bg-zinc-700/10 dark:hover:bg-zinc-700/50 transition mb-1",
|
||||
params?.memberId === member.id && "bg-zinc-700/20 dark:bg-zinc-700"
|
||||
)}>
|
||||
<UserAvatar src={member.profile.imageUrl}
|
||||
className="h-8 w-8 md:h-8 md:w-8"
|
||||
/>
|
||||
<p
|
||||
className={cn(
|
||||
"font-semibold text-sm text-zinc-500 group-hover:text-zinc-600 dark:text-zinc-400 dark:group-hover:text-zinc-300 transition",
|
||||
)}
|
||||
>
|
||||
{member.profile.name}
|
||||
</p>
|
||||
{icon}
|
||||
</button>
|
||||
)
|
||||
}
|
47
components/server/server-section.tsx
Normal file
47
components/server/server-section.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
"use client"
|
||||
|
||||
import { ServerWithMembersWithProfiles } from "@/types";
|
||||
import { ChannelType, MemberRole } from "@prisma/client";
|
||||
import { ActionTooltip } from "../action-tooltip";
|
||||
import { Plus, Settings } from "lucide-react";
|
||||
import { useModal } from "@/hooks/use-modal-store";
|
||||
|
||||
interface ServerSectionProps {
|
||||
label: string;
|
||||
role?: MemberRole;
|
||||
sectionType: "channels" | "members";
|
||||
channelType?: ChannelType;
|
||||
server?: ServerWithMembersWithProfiles;
|
||||
}
|
||||
|
||||
export const ServerSection = ({
|
||||
label,
|
||||
role,
|
||||
sectionType,
|
||||
channelType,
|
||||
server
|
||||
}: ServerSectionProps)=> {
|
||||
const {onOpen } = useModal();
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between py-2">
|
||||
<p className="text-xs uppercase font-semibold text-zinc-500 dark:text-zinc-400">
|
||||
{label}
|
||||
</p>
|
||||
{role !== MemberRole.GUEST && sectionType === "channels" && (
|
||||
<ActionTooltip label="Create Channel" side="top">
|
||||
<button onClick={() => onOpen("createChannel", {channelType})} className="text-zinc-500 hove:text-zinc-600 dark:text-zinc-400 dark:hover:text-zinc-300 transition">
|
||||
<Plus className="h-4 w-4"/>
|
||||
</button>
|
||||
</ActionTooltip>
|
||||
)}
|
||||
{role === MemberRole.ADMIN && sectionType === "members" && (
|
||||
<ActionTooltip label="Manage Members" side="top">
|
||||
<button onClick={() => onOpen("members", {server})} className="text-zinc-500 hove:text-zinc-600 dark:text-zinc-400 dark:hover:text-zinc-300 transition">
|
||||
<Settings className="h-4 w-4"/>
|
||||
</button>
|
||||
</ActionTooltip>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
@ -5,9 +5,14 @@ import { Hash, Mic, ShieldAlert, ShieldCheck, Video } from "lucide-react";
|
||||
import { currentProfile } from "@/lib/current-profile";
|
||||
import { db } from "@/lib/db";
|
||||
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { ServerHeader } from "./server-header";
|
||||
import { ServerSearch } from "./server-search";
|
||||
import { ServerSection } from "./server-section";
|
||||
import { ServerChannel } from "./server-channel";
|
||||
import { ServerMember } from "./server-member";
|
||||
|
||||
|
||||
|
||||
interface ServerSidebarProps {
|
||||
@ -116,6 +121,87 @@ export const ServerSidebar = async ({
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<Separator className="bg-zinc-200 dark:bg-zinc-700 rounded-md my-2"/>
|
||||
{!!textChannels?.length && (
|
||||
<div className="mb-2">
|
||||
<ServerSection
|
||||
sectionType="channels"
|
||||
channelType={ChannelType.TEXT}
|
||||
role={role}
|
||||
label="Text Channels"
|
||||
/>
|
||||
<div className="space-y-[2px]">
|
||||
{textChannels?.map((channel) => (
|
||||
<ServerChannel
|
||||
key={channel.id}
|
||||
channel={channel}
|
||||
role={role}
|
||||
server={server}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!!audioChannels?.length && (
|
||||
<div className="mb-2">
|
||||
<ServerSection
|
||||
sectionType="channels"
|
||||
channelType={ChannelType.AUDIO}
|
||||
role={role}
|
||||
label="Voice Channels"
|
||||
/>
|
||||
<div className="space-y-[2px]">
|
||||
{audioChannels?.map((channel) => (
|
||||
<ServerChannel
|
||||
key={channel.id}
|
||||
channel={channel}
|
||||
role={role}
|
||||
server={server}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!!videoChannels?.length && (
|
||||
<div className="mb-2">
|
||||
<ServerSection
|
||||
sectionType="channels"
|
||||
channelType={ChannelType.VIDEO}
|
||||
role={role}
|
||||
label="Video Channels"
|
||||
/>
|
||||
<div className="space-y-[2px]">
|
||||
{videoChannels?.map((channel) => (
|
||||
<ServerChannel
|
||||
key={channel.id}
|
||||
channel={channel}
|
||||
role={role}
|
||||
server={server}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!!members?.length && (
|
||||
<div className="mb-2">
|
||||
<ServerSection
|
||||
sectionType="members"
|
||||
channelType={ChannelType.VIDEO}
|
||||
role={role}
|
||||
label="Members"
|
||||
server={server}
|
||||
/>
|
||||
<div className="space-y-[2px]">
|
||||
{members?.map((member) => (
|
||||
<ServerMember
|
||||
key={member.id}
|
||||
member={member}
|
||||
server={server}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</div>
|
||||
)
|
||||
|
@ -1,11 +1,12 @@
|
||||
|
||||
import { Server } from "@prisma/client";
|
||||
import { ChannelType, Server } from "@prisma/client";
|
||||
import { create } from "zustand";
|
||||
|
||||
export type ModalType = "createServer" | "invite" | "editServer" | "members" | "createChannel" | "leaveServer" | "deleteServer";
|
||||
|
||||
interface ModalData {
|
||||
server?: Server
|
||||
server?: Server;
|
||||
channelType?: ChannelType;
|
||||
}
|
||||
|
||||
interface ModalStore {
|
||||
|
Loading…
x
Reference in New Issue
Block a user