The weeb for the next gen discord boat - Wamellow wamellow.com
bot discord
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

create custom lists hook

Luna 625311d9 e2ab730e

+127 -80
+37 -80
app/dashboard/[guildId]/notifications/page.tsx
··· 1 1 "use client"; 2 2 3 3 import Image from "next/image"; 4 - import { useParams, usePathname, useRouter, useSearchParams } from "next/navigation"; 5 - import { useCallback } from "react"; 4 + import { useParams } from "next/navigation"; 6 5 import { HiChat, HiViewGridAdd } from "react-icons/hi"; 7 - import { useQuery, useQueryClient } from "react-query"; 8 6 9 7 import { guildStore } from "@/common/guilds"; 10 8 import Fetch from "@/components/button-fetch"; 11 9 import { CreateSplash } from "@/components/dashboard/lists/create-splash"; 10 + import { useList } from "@/components/dashboard/lists/hook"; 12 11 import { Navigation } from "@/components/dashboard/lists/navigation"; 13 12 import { ItemSelector } from "@/components/dashboard/lists/selector"; 14 13 import MessageCreatorEmbed from "@/components/embed-creator"; 15 14 import SelectMenu from "@/components/inputs/select-menu"; 16 15 import { ScreenMessage } from "@/components/screen-message"; 17 - import { cacheOptions, getData } from "@/lib/api"; 18 16 import SadWumpusPic from "@/public/sad-wumpus.gif"; 19 17 import { ApiV1GuildsModulesNotificationsGetResponse } from "@/typings"; 20 18 ··· 23 21 24 22 export default function Home() { 25 23 const guild = guildStore((g) => g); 26 - const pathname = usePathname(); 27 - const search = useSearchParams(); 28 - const router = useRouter(); 29 24 const params = useParams(); 30 - const queryClient = useQueryClient(); 31 25 32 26 const url = `/guilds/${params.guildId}/modules/notifications` as const; 33 - 34 - const { data, isLoading, error } = useQuery( 35 - url, 36 - () => getData<ApiV1GuildsModulesNotificationsGetResponse[]>(url), 37 - { 38 - enabled: !!params.guildId, 39 - ...cacheOptions 40 - } 41 - ); 27 + const { 28 + item, 29 + items, 30 + setItemId, 31 + editItem, 32 + addItem, 33 + removeItem, 34 + isLoading, 35 + error 36 + } = useList<ApiV1GuildsModulesNotificationsGetResponse>({ url }); 42 37 43 - const id = search.get("id"); 44 - const notification = (Array.isArray(data) ? data : []).find((t) => t.id === id); 45 - 46 - const createQueryString = useCallback((name: string, value: string) => { 47 - const params = new URLSearchParams(search); 48 - params.set(name, value); 49 - 50 - return params.toString(); 51 - }, [search]); 52 - 53 - if (error || (data && "message" in data)) { 38 + if (error) { 54 39 return ( 55 40 <ScreenMessage 56 41 top="20vh" 57 42 title="Something went wrong on this page.." 58 - description={ 59 - (data && "message" in data ? data.message : `${error}`) 60 - || "An unknown error occurred."} 43 + description={error} 61 44 href={`/dashboard/${guild?.id}`} 62 45 button="Go back to overview" 63 46 icon={<HiViewGridAdd />} ··· 67 50 ); 68 51 } 69 52 70 - if (isLoading || !data) return <></>; 71 - 72 - const setNotificationId = (id: string) => { 73 - router.push(pathname + "?" + createQueryString("id", id)); 74 - }; 75 - 76 - const editNotification = <T extends keyof ApiV1GuildsModulesNotificationsGetResponse>(k: keyof ApiV1GuildsModulesNotificationsGetResponse, value: ApiV1GuildsModulesNotificationsGetResponse[T]) => { 77 - if (!notification) return; 53 + if (isLoading || !items) return <></>; 78 54 79 - queryClient.setQueryData<ApiV1GuildsModulesNotificationsGetResponse[]>(url, () => [ 80 - ...(data?.filter((t) => t.id !== notification.id) || []), 81 - { ...notification, [k]: value } 82 - ]); 83 - }; 84 - 85 - const addNotification = (notification: ApiV1GuildsModulesNotificationsGetResponse) => { 86 - queryClient.setQueryData<ApiV1GuildsModulesNotificationsGetResponse[]>(url, () => [ 87 - ...(data || []), 88 - notification 89 - ]); 90 - }; 91 - 92 - const removeNotification = (id: string) => { 93 - queryClient.setQueryData<ApiV1GuildsModulesNotificationsGetResponse[]>(url, () => 94 - data?.filter((t) => t.id !== id) || [] 95 - ); 96 - }; 97 - 98 - if (!notification) { 55 + if (!item) { 99 56 return ( 100 57 <ItemSelector<ApiV1GuildsModulesNotificationsGetResponse> 101 - items={(data || []) as ApiV1GuildsModulesNotificationsGetResponse[]} 58 + items={items} 102 59 103 - set={setNotificationId} 60 + set={setItemId} 104 61 sort={(a, b) => a.creator.username.localeCompare(b.creator.username)} 105 62 name={(item) => item.creator.username} 106 63 ··· 109 66 createButton={(options) => ( 110 67 <CreateNotification 111 68 style={options.style} 112 - add={addNotification} 113 - set={setNotificationId} 69 + add={addItem} 70 + set={setItemId} 114 71 /> 115 72 )} 116 73 ··· 118 75 <DeleteNotification 119 76 id={options.id} 120 77 name={options.name} 121 - remove={removeNotification} 78 + remove={removeItem} 122 79 /> 123 80 )} 124 81 ··· 152 109 > 153 110 <CreateNotification 154 111 style={Style.Big} 155 - add={addNotification} 156 - set={setNotificationId} 112 + add={addItem} 113 + set={setItemId} 157 114 /> 158 115 </CreateSplash> 159 116 </ItemSelector> ··· 167 124 168 125 icon={ 169 126 <Image 170 - alt={`${notification?.creator.username}'s avatar`} 127 + alt={`${item?.creator.username}'s avatar`} 171 128 className="rounded-full size-5.5" 172 - src={notification?.creator.avatarUrl || ""} 129 + src={item?.creator.avatarUrl || ""} 173 130 width={24} 174 131 height={24} 175 132 /> 176 133 } 177 - name={notification.creator.username} 134 + name={item.creator.username} 178 135 /> 179 136 180 137 <div className="flex md:gap-4 gap-2"> 181 138 <SelectMenu 182 139 name="Channel" 183 - url={url + "/" + notification.id} 140 + url={url + "/" + item.id} 184 141 dataName="channelId" 185 142 items={guild?.channels?.sort((a, b) => a.name.localeCompare(b.name)).map((c) => ({ name: `#${c.name}`, value: c.id, error: c.missingPermissions.join(", ") }))} 186 143 description="Select a channel where notifications should be send into." 187 - defaultState={notification.channelId} 188 - onSave={(o) => editNotification("channelId", o.value as string)} 144 + defaultState={item.channelId} 145 + onSave={(o) => editItem("channelId", o.value as string)} 189 146 /> 190 147 191 148 <Fetch 192 149 className="w-1/3 md:w-1/6 relative top-8" 193 - url={url + "/" + notification.id + "/test"} 150 + url={url + "/" + item.id + "/test"} 194 151 icon={<HiChat className="min-h-4 min-w-4" />} 195 152 label="Test Message" 196 153 method="POST" ··· 201 158 <SelectMenu 202 159 className="md:w-1/2 w-full" 203 160 name="Ping role" 204 - url={url + "/" + notification.id} 161 + url={url + "/" + item.id} 205 162 dataName="roleId" 206 163 items={[ 207 164 { name: "@everyone (everyone in server)", value: "everyone" }, ··· 209 166 ...guild?.roles?.sort((a, b) => a.name.localeCompare(b.name)).map((c) => ({ name: `@${c.name}`, value: c.id, color: c.color })) || [] 210 167 ]} 211 168 description="Select a role which should get pinged on uploads." 212 - defaultState={notification.roleId} 213 - onSave={(o) => editNotification("roleId", o.value as string)} 169 + defaultState={item.roleId} 170 + onSave={(o) => editItem("roleId", o.value as string)} 214 171 showClear 215 172 /> 216 173 217 174 <MessageCreatorEmbed 218 - key={notification.id} 175 + key={item.id} 219 176 name="Message" 220 - url={url + "/" + notification.id} 177 + url={url + "/" + item.id} 221 178 dataName="message" 222 - defaultMessage={notification.message} 223 - onSave={(value) => editNotification("message", value as string)} 179 + defaultMessage={item.message} 180 + onSave={(value) => editItem("message", { content: value.content ?? null, embed: value.embed })} 224 181 /> 225 182 </>); 226 183 }
+90
components/dashboard/lists/hook.ts
··· 1 + import { useParams, usePathname, useRouter, useSearchParams } from "next/navigation"; 2 + import { useCallback } from "react"; 3 + import { useQuery, useQueryClient } from "react-query"; 4 + 5 + import { cacheOptions, getData } from "@/lib/api"; 6 + 7 + interface UseDataQueryOptions { 8 + url: string; 9 + } 10 + 11 + export function useList<T extends { id: string; }>({ url }: UseDataQueryOptions) { 12 + const router = useRouter(); 13 + const params = useParams(); 14 + const search = useSearchParams(); 15 + const pathname = usePathname(); 16 + 17 + const queryClient = useQueryClient(); 18 + 19 + const { data, isLoading, error } = useQuery( 20 + url, 21 + () => getData<T[]>(url), 22 + { 23 + enabled: !!params.guildId, 24 + ...cacheOptions 25 + } 26 + ); 27 + 28 + const itemId = search.get("id") as string; 29 + const item = (Array.isArray(data) ? data : []).find((i) => i.id === itemId); 30 + 31 + const createQueryString = useCallback( 32 + (name: string, value: string) => { 33 + const params = new URLSearchParams(search); 34 + params.set(name, value); 35 + 36 + return params.toString(); 37 + }, 38 + [search] 39 + ); 40 + 41 + const setItemId = (id: string) => { 42 + router.push(`${pathname}?${createQueryString("id", id)}`); 43 + }; 44 + 45 + const editItem = useCallback( 46 + <K extends keyof T>(key: K, value: T[K]) => { 47 + if (!item || !Array.isArray(data)) return; 48 + 49 + queryClient.setQueryData<T[]>(url, () => [ 50 + ...(data?.filter((t) => t.id !== item.id) || []), 51 + { ...item, [key]: value } 52 + ]); 53 + }, 54 + [item, data, url, queryClient] 55 + ); 56 + 57 + const addItem = useCallback( 58 + (newItem: T) => { 59 + if (!Array.isArray(data)) return; 60 + 61 + queryClient.setQueryData<T[]>(url, () => [ 62 + ...(data || []), 63 + newItem 64 + ]); 65 + }, 66 + [data, url, queryClient] 67 + ); 68 + 69 + const removeItem = useCallback( 70 + (id: string) => { 71 + if (!Array.isArray(data)) return; 72 + 73 + queryClient.setQueryData<T[]>(url, () => 74 + data?.filter((t) => t.id !== id) || [] 75 + ); 76 + }, 77 + [data, url, queryClient] 78 + ); 79 + 80 + return { 81 + item, 82 + items: Array.isArray(data) ? data : [], 83 + setItemId, 84 + editItem, 85 + addItem, 86 + removeItem, 87 + isLoading, 88 + error: error as string || (data && "message" in data ? JSON.stringify(data.message) : (error ? `${error}` : undefined)) 89 + }; 90 + }