Skip to content

Commit 410d39b

Browse files
authored
Merge pull request #49 from FrostCord/FROS-Create-Server/Channel-UI
Create channel UI
2 parents e7f7ed1 + b685b3b commit 410d39b

16 files changed

Lines changed: 667 additions & 139 deletions

components/forms/AddChannel.tsx

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import { Input } from './Styles';
2+
import styles from '@/styles/Components.module.css';
3+
4+
import { Dispatch, SetStateAction, useState } from 'react';
5+
import {
6+
Control,
7+
FieldErrorsImpl,
8+
UseFormRegister,
9+
Controller,
10+
} from 'react-hook-form';
11+
import ChannelMessageIcon from '@/components/icons/ChannelMessageIcon';
12+
import { ChannelMediaIcon } from '@/components/icons/ChannelMediaIcon';
13+
14+
export default function AddChannel({
15+
register,
16+
errors,
17+
control,
18+
serverError,
19+
showDesc,
20+
setShowDesc,
21+
channelType,
22+
setChannelType,
23+
}: {
24+
register: UseFormRegister<{
25+
description?: string | undefined;
26+
isMedia?: boolean | undefined;
27+
name: string;
28+
}>;
29+
errors: Partial<
30+
FieldErrorsImpl<{
31+
description: string;
32+
isMedia: NonNullable<boolean | undefined>;
33+
name: string;
34+
}>
35+
>;
36+
control: Control<
37+
{
38+
description?: string | undefined;
39+
isMedia?: boolean | undefined;
40+
name: string;
41+
},
42+
any
43+
>;
44+
serverError: string;
45+
showDesc: boolean;
46+
setShowDesc: Dispatch<SetStateAction<boolean>>;
47+
channelType: 'media' | 'text';
48+
setChannelType: Dispatch<SetStateAction<'media' | 'text'>>;
49+
}) {
50+
return (
51+
<form
52+
className="flex flex-col w-12 mb-1 mx-6"
53+
onSubmit={(e) => e.preventDefault()}
54+
>
55+
{serverError ? (
56+
<span className="my-2 text-red-700 text-sm font-bold">
57+
{serverError}
58+
</span>
59+
) : (
60+
''
61+
)}
62+
63+
<div className="flex flex-col mt-5">
64+
<div className="font-semibold tracking-wider">Channel Name</div>
65+
<input
66+
className={`${Input('bg-grey-700')} mt-2 ${styles.input}`}
67+
type="text"
68+
placeholder="Enter channel name"
69+
{...register('name')}
70+
/>
71+
{errors.name && (
72+
<p className="text-red-700 mt-2 text-sm font-bold">
73+
{errors.name.message}
74+
</p>
75+
)}
76+
<div className="font-semibold tracking-wider mt-4">Channel Type</div>
77+
<div className="flex-col flex">
78+
<Controller
79+
control={control}
80+
name="isMedia"
81+
render={({ field: { onChange, onBlur, value, ref } }) => (
82+
<>
83+
<div
84+
className={`flex ${
85+
channelType == 'text' ? 'bg-grey-600' : 'bg-grey-700'
86+
} rounded-2xl py-2 pl-6 items-center`}
87+
>
88+
<ChannelMessageIcon />
89+
<label className="ml-1 flex items-center w-full justify-between hover:cursor-pointer">
90+
Text{' '}
91+
<input
92+
className="mr-4"
93+
type="radio"
94+
onBlur={onBlur}
95+
onChange={() => onChange(false)}
96+
onClick={() => setChannelType('text')}
97+
checked={value === false}
98+
ref={ref}
99+
/>
100+
</label>
101+
</div>
102+
<div
103+
className={`flex ${
104+
channelType == 'media' ? 'bg-grey-600' : 'bg-grey-700'
105+
} rounded-2xl py-2 pl-6 mt-2 items-center`}
106+
>
107+
<ChannelMediaIcon />
108+
<label className="ml-1 flex items-center w-full justify-between hover:cursor-pointer">
109+
Voice{' '}
110+
<input
111+
className="mr-4 "
112+
type="radio"
113+
onBlur={onBlur}
114+
onChange={() => onChange(true)}
115+
onClick={() => setChannelType('media')}
116+
checked={value === true}
117+
ref={ref}
118+
/>
119+
</label>
120+
</div>
121+
</>
122+
)}
123+
/>
124+
</div>
125+
{showDesc ? (
126+
<div className="mt-4">
127+
<div className="font-semibold tracking-wider">Description</div>
128+
<input
129+
className={`${Input('bg-grey-700')} mt-2 ${styles.input}`}
130+
type="text"
131+
placeholder="Enter a description"
132+
{...register('description')}
133+
/>
134+
{errors.description && (
135+
<p className="text-red-700 mt-2 text-sm font-bold">
136+
{errors.description.message}
137+
</p>
138+
)}
139+
</div>
140+
) : (
141+
<div
142+
className={`${styles.description} mt-4 text-frost-600 font-bold tracking-wide hover:cursor-pointer hover:text-frost-500 underline underline-offset-2`}
143+
onClick={() => setShowDesc(true)}
144+
>
145+
Add a description{' '}
146+
</div>
147+
)}
148+
</div>
149+
</form>
150+
);
151+
}

components/forms/AddServer.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ import {
1010
useEffect,
1111
} from 'react';
1212
import Image from 'next/image';
13-
import { createServerSchema, CreateServerInput } from '@/types/client/server';
1413
import { FieldErrorsImpl, useForm, UseFormRegister } from 'react-hook-form';
15-
import { zodResolver } from '@hookform/resolvers/zod';
1614

1715
export default function AddServer({
1816
serverImage,
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import AddChannel from '@/components/forms/AddChannel';
2+
import Modal from '@/components/home/Modal';
3+
import {
4+
CreateChannelInput,
5+
createChannelSchema,
6+
} from '@/types/client/channel';
7+
import { zodResolver } from '@hookform/resolvers/zod';
8+
import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react';
9+
import { useForm } from 'react-hook-form';
10+
import { useSupabaseClient, useUser } from '@supabase/auth-helpers-react';
11+
import { createChannel } from '@/services/channels.service';
12+
import { PostgrestError } from '@supabase/supabase-js';
13+
import {
14+
useGetUserPermsForServer,
15+
useUserPerms,
16+
useUserServerPerms,
17+
} from '@/lib/store';
18+
19+
export default function AddChannelModal({
20+
showModal,
21+
setShowModal,
22+
serverId,
23+
}: {
24+
showModal: boolean;
25+
setShowModal: Dispatch<SetStateAction<boolean>>;
26+
serverId: number;
27+
}) {
28+
const addChannelRef = useRef<HTMLDialogElement>(null);
29+
30+
const [serverError, setServerError] = useState<string>('');
31+
const [showDesc, setSetShowDesc] = useState<boolean>(false);
32+
const [channelType, setChannelType] = useState<'media' | 'text'>('text');
33+
34+
const supabase = useSupabaseClient();
35+
36+
const user = useUser();
37+
38+
const getUserServerPerms = useGetUserPermsForServer();
39+
40+
const {
41+
register,
42+
handleSubmit,
43+
reset,
44+
control,
45+
formState: { errors },
46+
} = useForm<CreateChannelInput>({
47+
resolver: zodResolver(createChannelSchema),
48+
mode: 'onSubmit',
49+
defaultValues: {
50+
name: '',
51+
description: '',
52+
isMedia: false,
53+
},
54+
});
55+
56+
useEffect(() => {
57+
if (getUserServerPerms) {
58+
if (user) {
59+
getUserServerPerms(supabase, serverId, user.id);
60+
}
61+
}
62+
}, [user, getUserServerPerms, supabase, serverId]);
63+
64+
const onSubmit = async (formData: CreateChannelInput) => {
65+
const { data, error } = await createChannel(
66+
supabase,
67+
serverId,
68+
formData.name,
69+
formData.description,
70+
formData.isMedia
71+
);
72+
73+
if (error) {
74+
if ((error as PostgrestError).message) {
75+
setServerError((error as PostgrestError).message);
76+
}
77+
else {
78+
setServerError(error as string);
79+
}
80+
81+
setTimeout(() => {
82+
setServerError('');
83+
}, 7000);
84+
return;
85+
}
86+
else {
87+
addChannelRef.current?.close();
88+
setChannelType('text');
89+
setSetShowDesc(false);
90+
reset();
91+
setShowModal(false);
92+
}
93+
};
94+
95+
return (
96+
<Modal
97+
modalRef={addChannelRef}
98+
showModal={showModal}
99+
title={'Create a new Channel'}
100+
onKeyDown={(e) => {
101+
if (e.key === 'Enter') {
102+
handleSubmit(onSubmit)();
103+
}
104+
}}
105+
buttons={
106+
<>
107+
<div
108+
className="hover:underline hover:cursor-pointer"
109+
onClick={() => {
110+
setShowModal(false);
111+
setSetShowDesc(false);
112+
setChannelType('text');
113+
reset();
114+
addChannelRef.current?.close();
115+
}}
116+
>
117+
Cancel
118+
</div>
119+
<div
120+
className="bg-frost-500 py-2 px-5 rounded-lg hover:cursor-pointer hover:bg-frost-700"
121+
onClick={() => {
122+
handleSubmit(onSubmit)();
123+
}}
124+
>
125+
Submit
126+
</div>
127+
</>
128+
}
129+
>
130+
<AddChannel
131+
register={register}
132+
errors={errors}
133+
serverError={serverError}
134+
showDesc={showDesc}
135+
setShowDesc={setSetShowDesc}
136+
control={control}
137+
channelType={channelType}
138+
setChannelType={setChannelType}
139+
/>
140+
</Modal>
141+
);
142+
}

components/home/Chat.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ import { useRef, useEffect } from 'react';
33
import styles from '@/styles/Chat.module.css';
44
import { useSupabaseClient, useUser } from '@supabase/auth-helpers-react';
55
import MessageInput from './MessageInput';
6-
import type { MessageWithServerProfile, Message as MessageType } from '@/types/dbtypes';
7-
import { createMessage } from '@/services/message.service';
6+
import type {
7+
MessageWithServerProfile,
8+
Message as MessageType,
9+
} from '@/types/dbtypes';
10+
import { createMessage } from '@/services/message.service';
811
import Message from '@/components/home/Message';
912
import { useChannel, useMessages, useUserPerms } from '@/lib/store';
1013
import { Channel } from '@/types/dbtypes';
@@ -53,7 +56,8 @@ export default function Chat() {
5356
{messages &&
5457
messages.map((value, index: number, array) => {
5558
// Get the previous message, if the authors are the same, we don't need to repeat the header (profile picture, name, etc.)
56-
const previousMessage: MessageWithServerProfile | null = index > 0 ? array[index - 1] : null;
59+
const previousMessage: MessageWithServerProfile | null =
60+
index > 0 ? array[index - 1] : null;
5761

5862
return (
5963
<Message

components/home/RenderDesktopView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ export default function RenderDesktopView() {
1616
const [sideBarView, mainView] = renderContent(sideBarOption, channel);
1717

1818
return (
19-
<div className={`${styles.container}`}>
19+
<div className={`${styles.container} `}>
2020
<div className="col-start-1 col-end-2 bg-grey-950 flex-col justify-center ">
2121
<NavBar type="vertical" />
2222
</div>
23-
<div className="col-start-2 col-end-4 flex-col bg-grey-900 h-screen overflow-y-scroll overflow-x-hidden">
23+
<div className="col-start-2 col-end-4 flex-col bg-grey-900 ">
2424
{sideBarView}
2525
</div>
2626
<div className="col-start-4 col-end-13 flex flex-col h-screen">

0 commit comments

Comments
 (0)