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.

improve notification channel selector

Luna dc5f54a6 cb5c0a15

+271 -139
+160
app/dashboard/[guildId]/notifications/channel-selector.component.tsx
··· 1 + "use client"; 2 + 3 + import { Button } from "@nextui-org/react"; 4 + import Image from "next/image"; 5 + import Link from "next/link"; 6 + import { HiExternalLink, HiPencil } from "react-icons/hi"; 7 + 8 + import { guildStore } from "@/common/guilds"; 9 + import { ApiV1GuildsModulesNotificationsGetResponse } from "@/typings"; 10 + 11 + import CreateNotification, { Style } from "./create.component"; 12 + import DeleteNotification from "./delete.component"; 13 + 14 + interface Props { 15 + notifications: ApiV1GuildsModulesNotificationsGetResponse[]; 16 + 17 + addNotification: (notification: ApiV1GuildsModulesNotificationsGetResponse) => void; 18 + setNotificationId: (id: string) => void; 19 + removeNotification: (id: string) => void; 20 + } 21 + 22 + export function ChannelSelector({ 23 + notifications, 24 + 25 + addNotification, 26 + setNotificationId, 27 + removeNotification 28 + }: Props) { 29 + const guild = guildStore((g) => g); 30 + 31 + return (<> 32 + <div className="flex flex-col gap-2"> 33 + {notifications 34 + .sort((a, b) => a.creator.username.localeCompare(b.creator.username)) 35 + .map((notification) => ( 36 + <Channel 37 + key={"notification-" + notification.id} 38 + notification={notification} 39 + onClick={() => setNotificationId(notification.id)} 40 + > 41 + <Button 42 + color="secondary" 43 + variant="flat" 44 + onClick={() => setNotificationId(notification.id)} 45 + startContent={<HiPencil />} 46 + > 47 + Edit 48 + </Button> 49 + <DeleteNotification 50 + guildId={guild?.id as string} 51 + id={notification.id} 52 + name={notification?.creator.username} 53 + removeNotification={removeNotification} 54 + /> 55 + </Channel> 56 + )) 57 + } 58 + </div> 59 + 60 + <div className="flex items-start justify-between w-full mt-3"> 61 + <CreateNotification 62 + guildId={guild?.id as string} 63 + style={Style.Compact} 64 + addNotification={addNotification} 65 + setNotificationId={setNotificationId} 66 + /> 67 + 68 + <Button 69 + as={Link} 70 + className="w-full md:w-[unset]" 71 + href="/docs/notifications" 72 + target="_blank" 73 + endContent={<HiExternalLink />} 74 + > 75 + Read docs & view placeholders 76 + </Button> 77 + </div> 78 + 79 + {!notifications.length && 80 + <CreateNotificationBanner> 81 + <CreateNotification 82 + guildId={guild?.id as string} 83 + style={Style.Big} 84 + addNotification={addNotification} 85 + setNotificationId={setNotificationId} 86 + /> 87 + </CreateNotificationBanner> 88 + } 89 + </>); 90 + } 91 + 92 + function Channel({ 93 + notification, 94 + children, 95 + onClick 96 + }: { 97 + notification: ApiV1GuildsModulesNotificationsGetResponse; 98 + children: React.ReactNode; 99 + onClick: () => void; 100 + }) { 101 + const guild = guildStore((g) => g); 102 + const channel = guild?.channels?.find((channel) => channel.id === notification.channelId); 103 + 104 + return ( 105 + <button 106 + className="flex justify-between p-4 bg-wamellow rounded-xl w-full duration-100" 107 + onClick={onClick} 108 + > 109 + <div className="flex gap-3 items-center"> 110 + <Image 111 + alt={`${notification.creator.username}'s avatar`} 112 + className="rounded-full" 113 + src={notification.creator.avatarUrl} 114 + width={46} 115 + height={46} 116 + /> 117 + 118 + <div className="flex flex-col items-start"> 119 + <span className="text-neutral-100 text-lg font-medium -mb-[0.5]"> 120 + {notification.creator.username} 121 + </span> 122 + 123 + <div className="bg-blurple/50 text-neutral-100 px-1 rounded-md"> 124 + #{channel?.name || "unknown"} 125 + </div> 126 + </div> 127 + </div> 128 + 129 + <div className="space-x-2"> 130 + {children} 131 + </div> 132 + </button> 133 + ); 134 + } 135 + 136 + function CreateNotificationBanner({ 137 + children 138 + }: { 139 + children: React.ReactNode 140 + }) { 141 + return ( 142 + <div 143 + className="w-full flex flex-col items-center justify-center" 144 + style={{ marginTop: "20vh" }} 145 + > 146 + <div> 147 + 148 + <div className="mb-10 flex flex-col items-center text-center"> 149 + <span className="text-4xl dark:text-neutral-100 text-neutral-900 font-semibold">You dont have any notifications yet</span> <br /> 150 + <span className="text-lg dark:text-neutral-400 text-neutral-600 font-semibold">Notify your community when new videos are released</span> 151 + </div> 152 + 153 + <div className="w-full flex flex-col items-center"> 154 + {children} 155 + </div> 156 + 157 + </div> 158 + </div> 159 + ) 160 + }
+6 -2
app/dashboard/[guildId]/notifications/create.component.tsx
··· 31 31 setNotificationId: (id: string) => void; 32 32 } 33 33 34 - export default function CreateNotification({ guildId, style, addNotification, setNotificationId }: Props) { 34 + export default function CreateNotification({ 35 + guildId, 36 + style, 37 + addNotification, 38 + setNotificationId 39 + }: Props) { 35 40 const channels = guildStore((g) => g?.channels); 36 41 37 42 const [open, setOpen] = useState(false); ··· 55 60 <Chip 56 61 as={Button} 57 62 className="default" 58 - variant="faded" 59 63 onClick={() => setOpen(true)} 60 64 startContent={<HiPencil className="relative left-1 ml-1" />} 61 65 >
+4 -1
app/dashboard/[guildId]/notifications/delete.component.tsx
··· 32 32 <Button 33 33 isIconOnly 34 34 color="danger" 35 + variant="flat" 35 36 onClick={() => setOpen(true)} 36 37 isDisabled={!id} 37 38 > 38 - <HiTrash /> 39 + <span> 40 + <HiTrash /> 41 + </span> 39 42 <span className="sr-only">Delete selected notification</span> 40 43 </Button> 41 44 </Tooltip>
+95 -136
app/dashboard/[guildId]/notifications/page.tsx
··· 1 1 "use client"; 2 2 3 - import { Button, Chip } from "@nextui-org/react"; 4 3 import Image from "next/image"; 5 - import Link from "next/link"; 6 4 import { useParams, usePathname, useRouter, useSearchParams } from "next/navigation"; 7 - import { useCallback, useEffect } from "react"; 8 - import { BiLogoYoutube } from "react-icons/bi"; 9 - import { HiChat, HiExternalLink, HiViewGridAdd } from "react-icons/hi"; 5 + import { useCallback } from "react"; 6 + import { HiArrowLeft, HiChat, HiExternalLink, HiViewGridAdd } from "react-icons/hi"; 10 7 import { useQuery, useQueryClient } from "react-query"; 11 8 12 9 import { guildStore } from "@/common/guilds"; ··· 18 15 import SadWumpusPic from "@/public/sad-wumpus.gif"; 19 16 import { ApiV1GuildsModulesNotificationsGetResponse } from "@/typings"; 20 17 21 - import CreateNotification, { Style } from "./create.component"; 22 - import DeleteNotification from "./delete.component"; 18 + import { ChannelSelector } from "./channel-selector.component"; 19 + import { Button } from "@nextui-org/react"; 20 + import Link from "next/link"; 23 21 24 22 export default function Home() { 25 23 const guild = guildStore((g) => g); ··· 50 48 return params.toString(); 51 49 }, [search]); 52 50 53 - useEffect(() => { 54 - if (!Array.isArray(data)) return; 55 - if (data && !notification && data[0]) setNotificationId(data[0].id); 56 - }, [data]); 57 - 58 51 if (error || (data && "message" in data)) { 59 52 return ( 60 53 <ScreenMessage ··· 100 93 ); 101 94 }; 102 95 103 - return (<> 104 - 105 - <div className="flex flex-col-reverse md:flex-row gap-3 -mt-2 mb-5"> 106 - 107 - <div className="flex flex-wrap gap-2"> 108 - {data 109 - .sort((a, b) => a.creator.username.localeCompare(b.creator.username)) 110 - .map((notification) => ( 111 - <Chip 112 - key={"guildnotifications-" + notification.id} 113 - as={Button} 114 - className="default border-0" 115 - variant={id === notification.id ? "flat" : "faded"} 116 - color={id === notification.id ? "secondary" : undefined} 117 - startContent={ 118 - <span className="opacity-50 relative left-2"> 119 - <BiLogoYoutube /> 120 - </span> 121 - } 122 - onClick={() => setNotificationId(notification.id)} 123 - > 124 - {notification.creator.username + " "} 125 - </Chip> 126 - )) 127 - } 96 + const Head = () => ( 97 + <div className="flex items-center gap-2 relative bottom-2 mb-3"> 98 + <Button 99 + as={Link} 100 + href={`/dashboard/${guild?.id}/notifications`} 101 + startContent={<HiArrowLeft />} 102 + size="sm" 103 + > 104 + Back to channel list 105 + </Button> 128 106 129 - <CreateNotification 130 - guildId={guild?.id as string} 131 - style={Style.Compact} 132 - addNotification={addNotification} 133 - setNotificationId={setNotificationId} 107 + <div className="flex items-center gap-1.5"> 108 + <Image 109 + alt={`${notification?.creator.username}'s avatar`} 110 + className="rounded-full size-5.5" 111 + src={notification?.creator.avatarUrl || ""} 112 + width={24} 113 + height={24} 134 114 /> 135 - </div> 136 115 137 - <div className="ml-auto flex items-center gap-4 w-full md:w-[unset] mb-auto"> 138 - <Button 139 - as={Link} 140 - className="w-full md:w-[unset]" 141 - href="/docs/notifications" 142 - target="_blank" 143 - endContent={<HiExternalLink />} 144 - > 145 - Read docs & view placeholders 146 - </Button> 147 - 148 - <DeleteNotification 149 - guildId={guild?.id as string} 150 - id={id} 151 - name={notification?.creator.username} 152 - removeNotification={removeNotification} 153 - /> 116 + <div className="flex flex-col"> 117 + <span className="text-xxs -mb-1"> 118 + Editing: 119 + </span> 120 + <span className="text-neutral-100 font-medium"> 121 + {notification?.creator.username} 122 + </span> 123 + </div> 154 124 </div> 155 125 126 + <Button 127 + as={Link} 128 + className="ml-auto" 129 + href="/docs/notifications" 130 + target="_blank" 131 + endContent={<HiExternalLink />} 132 + size="sm" 133 + > 134 + Read docs & view placeholders 135 + </Button> 156 136 </div> 137 + ); 157 138 158 - {notification && 159 - <> 139 + if (!notification) { 140 + return ( 141 + <ChannelSelector 142 + notifications={data || []} 143 + addNotification={addNotification} 144 + setNotificationId={setNotificationId} 145 + removeNotification={removeNotification} 146 + /> 147 + ); 148 + } 160 149 161 - <div className="flex md:gap-4 gap-2"> 162 - <SelectMenu 163 - name="Channel" 164 - url={url + "/" + notification.id} 165 - dataName="channelId" 166 - items={guild?.channels?.sort((a, b) => a.name.localeCompare(b.name)).map((c) => ({ name: `#${c.name}`, value: c.id, error: c.missingPermissions.join(", ") }))} 167 - description="Select a channel where notifications should be send into." 168 - defaultState={notification.channelId} 169 - onSave={(o) => editNotification("channelId", o.value as string)} 170 - /> 150 + return (<> 151 + <Head /> 171 152 172 - <Fetch 173 - className="w-1/3 md:w-1/6 relative top-8" 174 - url={url + "/" + notification.id + "/test"} 175 - icon={<HiChat className="min-h-4 min-w-4" />} 176 - label="Test Message" 177 - method="POST" 178 - size="lg" 179 - /> 180 - </div> 153 + <div className="flex md:gap-4 gap-2"> 154 + <SelectMenu 155 + name="Channel" 156 + url={url + "/" + notification.id} 157 + dataName="channelId" 158 + items={guild?.channels?.sort((a, b) => a.name.localeCompare(b.name)).map((c) => ({ name: `#${c.name}`, value: c.id, error: c.missingPermissions.join(", ") }))} 159 + description="Select a channel where notifications should be send into." 160 + defaultState={notification.channelId} 161 + onSave={(o) => editNotification("channelId", o.value as string)} 162 + /> 181 163 182 - <SelectMenu 183 - className="md:w-1/2 w-full" 184 - name="Ping role" 185 - url={url + "/" + notification.id} 186 - dataName="roleId" 187 - items={[ 188 - { name: "@everyone (everyone in server)", value: "everyone" }, 189 - { name: "@here (everyone online)", value: "here" }, 190 - ...guild?.roles?.sort((a, b) => a.name.localeCompare(b.name)).map((c) => ({ name: `@${c.name}`, value: c.id, color: c.color })) || [] 191 - ]} 192 - description="Select a role which should get pinged on uploads." 193 - defaultState={notification.roleId} 194 - onSave={(o) => editNotification("roleId", o.value as string)} 195 - showClear 196 - /> 164 + <Fetch 165 + className="w-1/3 md:w-1/6 relative top-8" 166 + url={url + "/" + notification.id + "/test"} 167 + icon={<HiChat className="min-h-4 min-w-4" />} 168 + label="Test Message" 169 + method="POST" 170 + size="lg" 171 + /> 172 + </div> 197 173 198 - <MessageCreatorEmbed 199 - key={notification.id} 200 - name="Message" 201 - url={url + "/" + notification.id} 202 - dataName="message" 203 - defaultMessage={notification.message} 204 - onSave={(value) => editNotification("message", value as string)} 205 - /> 206 - </> 207 - } 174 + <SelectMenu 175 + className="md:w-1/2 w-full" 176 + name="Ping role" 177 + url={url + "/" + notification.id} 178 + dataName="roleId" 179 + items={[ 180 + { name: "@everyone (everyone in server)", value: "everyone" }, 181 + { name: "@here (everyone online)", value: "here" }, 182 + ...guild?.roles?.sort((a, b) => a.name.localeCompare(b.name)).map((c) => ({ name: `@${c.name}`, value: c.id, color: c.color })) || [] 183 + ]} 184 + description="Select a role which should get pinged on uploads." 185 + defaultState={notification.roleId} 186 + onSave={(o) => editNotification("roleId", o.value as string)} 187 + showClear 188 + /> 208 189 209 - {!data.length && 210 - <div 211 - className="w-full flex flex-col items-center justify-center" 212 - style={{ marginTop: "20vh" }} 213 - > 214 - <div> 215 - 216 - <div className="mb-10 flex flex-col items-center text-center"> 217 - <span className="text-4xl dark:text-neutral-100 text-neutral-900 font-semibold">You dont have any notifications yet</span> <br /> 218 - <span className="text-lg dark:text-neutral-400 text-neutral-600 font-semibold">Notify your community when new videos are released</span> 219 - </div> 220 - 221 - <div className="w-full flex flex-col items-center"> 222 - <CreateNotification 223 - guildId={guild?.id as string} 224 - style={Style.Big} 225 - addNotification={addNotification} 226 - setNotificationId={setNotificationId} 227 - /> 228 - </div> 229 - 230 - </div> 231 - </div> 232 - } 233 - 190 + <MessageCreatorEmbed 191 + key={notification.id} 192 + name="Message" 193 + url={url + "/" + notification.id} 194 + dataName="message" 195 + defaultMessage={notification.message} 196 + onSave={(value) => editNotification("message", value as string)} 197 + /> 234 198 </>); 235 - } 236 - 237 - function convertCamelCaseToSpaced(input: string): string { 238 - const spacedString = input.replace(/([A-Z])/g, " $1"); 239 - return spacedString.charAt(0).toUpperCase() + spacedString.slice(1); 240 199 }
+6
next.config.js
··· 35 35 hostname: "ai.local.wamellow.com", 36 36 port: "", 37 37 pathname: "/static/**" 38 + }, 39 + { 40 + protocol: "https", 41 + hostname: "yt3.ggpht.com", 42 + port: "", 43 + pathname: "/**" 38 44 } 39 45 ] 40 46 }