added channel list and members to server sidebar
This commit is contained in:
parent
f8f4f0f94a
commit
d3cdd24d80
@ -33,6 +33,7 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue
|
SelectValue
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
name: z.string().min(1, {
|
name: z.string().min(1, {
|
||||||
@ -47,20 +48,30 @@ const formSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const CreateChannelModal = () => {
|
export const CreateChannelModal = () => {
|
||||||
const { isOpen, onClose, type } = useModal();
|
const { isOpen, onClose, type, data } = useModal();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
|
||||||
const isModalOpen = isOpen && type === "createChannel";
|
const isModalOpen = isOpen && type === "createChannel";
|
||||||
|
const { channelType } = data;
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: "",
|
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 isLoading = form.formState.isSubmitting;
|
||||||
|
|
||||||
const onSubmit = async (values: z.infer<typeof formSchema>) => {
|
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;
|
role?: MemberRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SeverHeader = ({server, role}: ServerHeaderProps) => {
|
export const ServerHeader = ({server, role}: ServerHeaderProps) => {
|
||||||
const { onOpen } = useModal();
|
const { onOpen } = useModal();
|
||||||
const isAdmin = role === MemberRole.ADMIN;
|
const isAdmin = role === MemberRole.ADMIN;
|
||||||
const isModerator = isAdmin || role === MemberRole.MODERATOR;
|
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 { currentProfile } from "@/lib/current-profile";
|
||||||
import { db } from "@/lib/db";
|
import { db } from "@/lib/db";
|
||||||
|
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { ServerHeader } from "./server-header";
|
import { ServerHeader } from "./server-header";
|
||||||
import { ServerSearch } from "./server-search";
|
import { ServerSearch } from "./server-search";
|
||||||
|
import { ServerSection } from "./server-section";
|
||||||
|
import { ServerChannel } from "./server-channel";
|
||||||
|
import { ServerMember } from "./server-member";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface ServerSidebarProps {
|
interface ServerSidebarProps {
|
||||||
@ -116,6 +121,87 @@ export const ServerSidebar = async ({
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
|
|
||||||
import { Server } from "@prisma/client";
|
import { ChannelType, Server } from "@prisma/client";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
|
||||||
export type ModalType = "createServer" | "invite" | "editServer" | "members" | "createChannel" | "leaveServer" | "deleteServer";
|
export type ModalType = "createServer" | "invite" | "editServer" | "members" | "createChannel" | "leaveServer" | "deleteServer";
|
||||||
|
|
||||||
interface ModalData {
|
interface ModalData {
|
||||||
server?: Server
|
server?: Server;
|
||||||
|
channelType?: ChannelType;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ModalStore {
|
interface ModalStore {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user