Skip to content

Commit 1d7f608

Browse files
authored
Merge pull request #68 from FrostCord/FROS-Leave-Server
Implement leave/delete server
2 parents a4a5b19 + 31f0b0c commit 1d7f608

4 files changed

Lines changed: 108 additions & 34 deletions

File tree

components/home/MessageHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export function MessageHeader({
7474
<UserIcon user={server_user_profile} />
7575
<div className="flex-grow flex items-center">
7676
<div
77-
className="text-xl font-semibold tracking-wider mr-2"
77+
className="text-xl font-semibold tracking-wider mr-2 hover:cursor-pointer"
7878
data-tooltip-id={server_user_profile.id}
7979
style={{
8080
// Check for the first role that has a non-null color and use that

components/home/Server.tsx

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
2424
import { ServerPermissions } from '@/types/permissions';
2525
import PlusIcon from '@/components/icons/PlusIcon';
2626
import GearIcon from '@/components/icons/GearIcon';
27+
import { deleteServer, leaveServer } from '@/services/server.service';
28+
import TrashIcon from '@/components/icons/TrashIcon';
29+
import { LeaveIcon } from '@/components/icons/LeaveIcon';
2730

2831
export default function Server({
2932
server,
@@ -57,6 +60,15 @@ export default function Server({
5760
handleAsync();
5861
}, [server, supabase]);
5962

63+
const showServerSettingsOption = (
64+
(serverPermissions & ServerPermissions.MANAGE_INVITES) > 0
65+
|| (serverPermissions & ServerPermissions.MANAGE_ROLES) > 0
66+
|| (serverPermissions & ServerPermissions.MANAGE_USERS) > 0
67+
|| (serverPermissions & ServerPermissions.MANAGE_SERVER) > 0
68+
);
69+
70+
const showAddChannelOption = (serverPermissions & ServerPermissions.MANAGE_CHANNELS) > 0;
71+
6072
if (expand) {
6173
return (
6274
<div className="relative overflow-x-visible">
@@ -86,14 +98,7 @@ export default function Server({
8698
</div>
8799
</div>
88100
<DropdownMenu.Root>
89-
<DropdownMenu.Trigger
90-
asChild
91-
disabled={
92-
!((serverPermissions & ServerPermissions.MANAGE_MESSAGES) > 0 ||
93-
(serverPermissions & ServerPermissions.OWNER) > 0 ||
94-
(serverPermissions & ServerPermissions.MANAGE_SERVER) > 0)
95-
}
96-
>
101+
<DropdownMenu.Trigger asChild>
97102
<div
98103
onMouseEnter={() => setIsSettingsHovered(true)}
99104
onMouseLeave={() => setIsSettingsHovered(false)}
@@ -104,29 +109,62 @@ export default function Server({
104109
</DropdownMenu.Trigger>
105110
<DropdownMenu.Portal>
106111
<DropdownMenu.Content className="ContextMenuContent" side='right'>
107-
<DropdownMenu.Item asChild
108-
className="flex justify-center items-center hover:text-grey-300 cursor-pointer"
109-
onClick={() => {
110-
setShowAddChannelModal(true);
111-
}}
112-
>
113-
<div className="flex flex-row w-full">
114-
<PlusIcon width={5} height={5}/>
115-
<span className="ml-2 w-full">New channel</span>
116-
</div>
117-
</DropdownMenu.Item>
118-
<DropdownMenu.Item asChild
119-
className="flex justify-center items-center hover:text-grey-300 cursor-pointer"
120-
onClick={() => {
121-
setShowServerSettingsModal(true);
122-
}}
123-
hidden={(serverPermissions & ServerPermissions.MANAGE_SERVER) === 0}
124-
>
125-
<div className="flex flex-row w-full">
126-
<GearIcon width={5} height={5} />
127-
<span className="ml-2 w-full">Server Settings</span>
128-
</div>
129-
</DropdownMenu.Item>
112+
{ showAddChannelOption && (
113+
<DropdownMenu.Item asChild
114+
className="flex justify-center items-center hover:text-grey-300 cursor-pointer"
115+
onClick={() => {
116+
setShowAddChannelModal(true);
117+
}}
118+
>
119+
<div className="flex flex-row w-full">
120+
<PlusIcon width={5} height={5}/>
121+
<span className="ml-2 w-full">New channel</span>
122+
</div>
123+
</DropdownMenu.Item>
124+
)}
125+
{showServerSettingsOption && (
126+
<DropdownMenu.Item asChild
127+
className="flex justify-center items-center hover:text-grey-300 cursor-pointer"
128+
onClick={() => {
129+
setShowServerSettingsModal(true);
130+
}}
131+
>
132+
<div className="flex flex-row w-full">
133+
<GearIcon width={5} height={5} />
134+
<span className="ml-2 w-full">Server Settings</span>
135+
</div>
136+
</DropdownMenu.Item>
137+
)}
138+
139+
{(showAddChannelOption || showServerSettingsOption) && <DropdownMenu.Separator className="ContextMenuSeparator" />}
140+
{(serverPermissions & 1) === 0 && (
141+
<DropdownMenu.Item asChild
142+
className="flex justify-center items-center text-red-500 hover:text-grey-300 cursor-pointer"
143+
onClick={async () => {
144+
// TODO: Add confirmation modal
145+
await leaveServer(supabase, user!.id, server.id);
146+
}}
147+
>
148+
<div className="flex flex-row w-full">
149+
<LeaveIcon className='!w-5 !h-5' />
150+
<span className="ml-2 w-full">Leave Server</span>
151+
</div>
152+
</DropdownMenu.Item>
153+
)}
154+
{(serverPermissions & 1) === 1 && (
155+
<DropdownMenu.Item asChild
156+
className="flex justify-center items-center text-red-500 hover:text-grey-300 cursor-pointer"
157+
onClick={async () => {
158+
// TODO: Add confirmation modal
159+
await deleteServer(supabase, server.id);
160+
}}
161+
>
162+
<div className="flex flex-row w-full">
163+
<TrashIcon />
164+
<span className="ml-2 w-full">Delete Server</span>
165+
</div>
166+
</DropdownMenu.Item>
167+
)}
130168
</DropdownMenu.Content>
131169
</DropdownMenu.Portal>
132170
</DropdownMenu.Root>

components/icons/LeaveIcon.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export function LeaveIcon({ className }: { className?: string }) {
2+
return (
3+
<svg
4+
xmlns="http://www.w3.org/2000/svg"
5+
fill="none"
6+
viewBox="0 0 24 24"
7+
stroke-width="1.5"
8+
stroke="currentColor"
9+
className={`w-6 h-6 ${className}`}
10+
>
11+
<path
12+
stroke-linecap="round"
13+
stroke-linejoin="round"
14+
d="M9 15L3 9m0 0l6-6M3 9h12a6 6 0 010 12h-3"
15+
/>
16+
</svg>
17+
);
18+
}

services/server.service.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,37 @@ export type UpdateServerResponseError = UpdateServerResponse['error'];
6565

6666
export async function deleteServer(
6767
supabase: SupabaseClient<Database>,
68-
user_id: string,
6968
server_id: number
7069
) {
7170
// NOTE: only the owner should be able to delete a server. This is enforced by RLS
7271
// NOTE: Supabase has been set up to cascade delete all items related to a server (roles/invites/channels/messages)
7372
// So we can delete the server itself
74-
return await supabase.from('servers').delete().eq('id', server_id);
73+
return await supabase
74+
.from('servers')
75+
.delete()
76+
.eq('id', server_id);
7577
}
7678

7779
type DeleteServerResponse = Awaited<ReturnType<typeof deleteServer>>;
7880
export type DeleteServerResponseSuccess = DeleteServerResponse['data'];
7981
export type DeleteServerResponseError = DeleteServerResponse['error'];
8082

83+
export async function leaveServer(
84+
supabase: SupabaseClient<Database>,
85+
user_id: string,
86+
server_id: number
87+
) {
88+
return await supabase
89+
.from('server_users')
90+
.delete()
91+
.eq('profile_id', user_id)
92+
.eq('server_id', server_id);
93+
}
94+
95+
type LeaveServerResponse = Awaited<ReturnType<typeof leaveServer>>;
96+
export type LeaveServerResponseSuccess = LeaveServerResponse['data'];
97+
export type LeaveServerResponseError = LeaveServerResponse['error'];
98+
8199
export async function getServersForUser(
82100
supabase: SupabaseClient<Database>,
83101
user_id: string

0 commit comments

Comments
 (0)