From 91ba9b94c87180befbd3e618090c6ed441ac0577 Mon Sep 17 00:00:00 2001 From: Lea Date: Thu, 10 Aug 2023 23:05:53 +0200 Subject: [PATCH] feat: bulk delete invites --- components/pages/inspector/InviteList.tsx | 56 ++++++++++++++++++++--- lib/actions.ts | 14 +++++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/components/pages/inspector/InviteList.tsx b/components/pages/inspector/InviteList.tsx index 3dd9087..85b7511 100644 --- a/components/pages/inspector/InviteList.tsx +++ b/components/pages/inspector/InviteList.tsx @@ -1,10 +1,13 @@ "use client" import { InviteCard } from "@/components/cards/InviteCard"; +import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; import { Command, CommandItem } from "@/components/ui/command"; import { Input } from "@/components/ui/input"; import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover"; +import { toast } from "@/components/ui/use-toast"; +import { bulkDeleteInvites } from "@/lib/actions"; import { ChannelInvite } from "@/lib/db"; import { ChevronsUpDown } from "lucide-react"; import { useMemo, useState } from "react"; @@ -19,17 +22,19 @@ export default function ServerInviteList({ server, invites, channels, users }: { const [selectVanityOnly, setSelectVanityOnly] = useState(false); const [selectChannel, setSelectChannel] = useState(false); const [selectUser, setSelectUser] = useState(false); - const [vanityOnly, setVanityOnly] = useState(false); + const [vanityFilter, setVanityFilter] = useState(null); const [channelFilter, setChannelFilter] = useState(""); const [userFilter, setUserFilter] = useState(""); + const [deletedInvites, setDeletedInvites] = useState([]); const filteredInvites = useMemo(() => { return invites - ?.filter(invite => vanityOnly ? invite.vanity : true) + ?.filter(invite => !deletedInvites.includes(invite._id)) + ?.filter(invite => vanityFilter === true ? invite.vanity : vanityFilter === false ? !invite.vanity : true) ?.filter(invite => channelFilter ? invite.channel == channelFilter : true) ?.filter(invite => userFilter ? invite.creator == userFilter : true) ?.reverse(); - }, [vanityOnly, channelFilter, userFilter, invites]); + }, [vanityFilter, channelFilter, userFilter, invites, deletedInvites]); return (
@@ -42,21 +47,22 @@ export default function ServerInviteList({ server, invites, channels, users }: { aria-expanded={selectVanityOnly} className="flex-1 justify-between" > - {vanityOnly ? "Vanity" : "All"} + {vanityFilter === true ? "Vanity" : vanityFilter === false ? "Not vanity" : "All"} {[ - { value: false, label: "All" }, + { value: null, label: "All" }, { value: true, label: "Vanity" }, + { value: false, label: "Not vanity" }, ].map((option) => ( { setSelectVanityOnly(false); - setVanityOnly(option.value); + setVanityFilter(option.value); }} >{option.label} ))} @@ -73,7 +79,7 @@ export default function ServerInviteList({ server, invites, channels, users }: { className="flex-1 justify-between" > {channelFilter - ? (channels?.find(c => c._id == channelFilter) as any)?.name + ? '#' + (channels?.find(c => c._id == channelFilter) as any)?.name : "Select channel"} @@ -131,6 +137,42 @@ export default function ServerInviteList({ server, invites, channels, users }: { + + + + + + + + + Bulk delete invites + + + + This will delete all invites that match your filter options. +
+ {filteredInvites.length} invite{filteredInvites.length == 1 ? '' : 's'} will be deleted. +
+ + { + try { + await bulkDeleteInvites(filteredInvites.map(i => i._id)); + setDeletedInvites([...deletedInvites, ...filteredInvites.map(i => i._id)]); + toast({ title: "Selected invites have been deleted" }); + } catch(e) { + toast({ + title: "Failed to delete invite", + description: String(e), + variant: "destructive", + }); + } + }} + >Bulk delete + Cancel + +
+
{filteredInvites.map(invite => (("channel_invites") @@ -600,7 +602,8 @@ export async function deleteInvite(invite: string) { export async function editInvite(invite: string, newInvite: string) { await checkPermission("channels/update/invites", { invite, newInvite }); - if (!newInvite) throw new Error("invite is empty"); + if (!invite) throw new Error("invite is empty"); + if (!newInvite) throw new Error("new invite is empty"); const { value } = await mongo() .db("revolt") @@ -615,6 +618,15 @@ export async function editInvite(invite: string, newInvite: string) { .insertOne({ ...value, _id: newInvite }); } +export async function bulkDeleteInvites(invites: string[]) { + await checkPermission("channels/update/invites", invites); + + await mongo() + .db("revolt") + .collection("channel_invites") + .deleteMany({ _id: { $in: invites } }); +} + export async function updateBotDiscoverability(botId: string, state: boolean) { await checkPermission("bots/update/discoverability", botId, { state }); await mongo()