From f8582e1fe94119028ef5dd733ef2d04caddb451f Mon Sep 17 00:00:00 2001 From: jude Date: Sat, 4 Nov 2023 17:45:54 +0000 Subject: [PATCH] Add delete functionality --- README.md | 17 ++ src/api.ts | 11 +- .../Reminder/ButtonRow/DeleteButton.tsx | 68 +++++ .../Reminder/ButtonRow/EditButtonRow.tsx | 68 +++++ src/components/Reminder/EditReminder.tsx | 250 ++---------------- src/components/Reminder/Message.tsx | 54 ++++ src/components/Reminder/ReminderContext.tsx | 9 + src/components/Reminder/Settings.tsx | 116 ++++++++ 8 files changed, 371 insertions(+), 222 deletions(-) create mode 100644 README.md create mode 100644 src/components/Reminder/ButtonRow/DeleteButton.tsx create mode 100644 src/components/Reminder/ButtonRow/EditButtonRow.tsx create mode 100644 src/components/Reminder/Message.tsx create mode 100644 src/components/Reminder/ReminderContext.tsx create mode 100644 src/components/Reminder/Settings.tsx diff --git a/README.md b/README.md new file mode 100644 index 0000000..195ca58 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# reminder-dashboard + +The re-re-rewrite of the dashboard. + +## Why + +The existing beta variant of the dashboard is written using vanilla JavaScript. This is fine, +but annoying to update. This would've been okay if I was more dedicated to updating the vanilla +JavaScript too, but I want to experiment with "new" things. + +This also allows me to expand my frontend skills, which is relevant to part of my job. + +## Developing + +1. Download the parent repo: https://gitea.jellypro.xyz/jude/reminder-bot +2. Initialise the submodules +3. Run both `npm start` and `cargo run` \ No newline at end of file diff --git a/src/api.ts b/src/api.ts index 1b1c12d..7666998 100644 --- a/src/api.ts +++ b/src/api.ts @@ -129,7 +129,7 @@ export const fetchGuildReminders = (guild: string) => ({ staleTime: OTHER_STALE_TIME, }); -export const mutateGuildReminder = (guild: string) => ({ +export const patchGuildReminder = (guild: string) => ({ mutationFn: (reminder: Reminder) => axios.patch(`/dashboard/api/guild/${guild}/reminders`, { ...reminder, @@ -137,6 +137,15 @@ export const mutateGuildReminder = (guild: string) => ({ }), }); +export const deleteGuildReminder = (guild: string) => ({ + mutationFn: (reminder: Reminder) => + axios.delete(`/dashboard/api/guild/${guild}/reminders`, { + data: { + uid: reminder.uid, + }, + }), +}); + export const fetchGuildTemplates = (guild: string) => ({ queryKey: ["GUILD_TEMPLATES", guild], queryFn: () => diff --git a/src/components/Reminder/ButtonRow/DeleteButton.tsx b/src/components/Reminder/ButtonRow/DeleteButton.tsx new file mode 100644 index 0000000..4db109c --- /dev/null +++ b/src/components/Reminder/ButtonRow/DeleteButton.tsx @@ -0,0 +1,68 @@ +import { useState } from "preact/hooks"; +import { Modal } from "../../Modal"; +import { useMutation, useQueryClient } from "react-query"; +import { useReminder } from "../ReminderContext"; +import { deleteGuildReminder } from "../../../api"; +import { useParams } from "wouter"; + +export const DeleteButton = () => { + const [modalOpen, setModalOpen] = useState(false); + + return ( + <> + + {modalOpen && } + + ); +}; + +const DeleteModal = ({ setModalOpen }) => { + const [reminder] = useReminder(); + const { guild } = useParams(); + + const queryClient = useQueryClient(); + const mutation = useMutation({ + ...deleteGuildReminder(guild), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["GUILD_REMINDERS", guild], + }); + setModalOpen(false); + }, + }); + + return ( + + <> +

This reminder will be permanently deleted. Are you sure?

+

+
+ + +
+ +
+ ); +}; diff --git a/src/components/Reminder/ButtonRow/EditButtonRow.tsx b/src/components/Reminder/ButtonRow/EditButtonRow.tsx new file mode 100644 index 0000000..8522964 --- /dev/null +++ b/src/components/Reminder/ButtonRow/EditButtonRow.tsx @@ -0,0 +1,68 @@ +import { useState } from "preact/hooks"; +import { useMutation, useQueryClient } from "react-query"; +import { patchGuildReminder } from "../../../api"; +import { useParams } from "wouter"; +import { ICON_FLASH_TIME } from "../../../consts"; +import { DeleteButton } from "./DeleteButton"; +import { useReminder } from "../ReminderContext"; + +export const EditButtonRow = () => { + const { guild } = useParams(); + const [reminder, setReminder] = useReminder(); + + const [recentlySaved, setRecentlySaved] = useState(false); + const queryClient = useQueryClient(); + + const mutation = useMutation({ + ...patchGuildReminder(guild), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["GUILD_REMINDERS", guild], + }); + setRecentlySaved(true); + setTimeout(() => { + setRecentlySaved(false); + }, ICON_FLASH_TIME); + }, + }); + + return ( +
+ + + +
+ ); +}; diff --git a/src/components/Reminder/EditReminder.tsx b/src/components/Reminder/EditReminder.tsx index 5d1e9f7..6324504 100644 --- a/src/components/Reminder/EditReminder.tsx +++ b/src/components/Reminder/EditReminder.tsx @@ -1,15 +1,12 @@ -import { fetchGuildChannels, fetchUserInfo, mutateGuildReminder, Reminder } from "../../api"; -import { useMutation, useQueries, useQueryClient } from "react-query"; +import { fetchGuildChannels, Reminder } from "../../api"; +import { useQuery } from "react-query"; import { useParams } from "wouter"; import { Name } from "./Name"; -import { Username } from "./Username"; -import { Content } from "./Content"; -import { ChannelSelector } from "./ChannelSelector"; import { useState } from "preact/hooks"; -import { IntervalSelector } from "./IntervalSelector"; -import { Embed } from "./Embed"; -import { DateTime } from "luxon"; -import { ICON_FLASH_TIME } from "../../consts"; +import { EditButtonRow } from "./ButtonRow/EditButtonRow"; +import { Message } from "./Message"; +import { Settings } from "./Settings"; +import { ReminderContext } from "./ReminderContext"; type Props = { reminder: Reminder; @@ -19,229 +16,40 @@ export const EditReminder = ({ reminder: initialReminder }: Props) => { const { guild } = useParams(); const [reminder, setReminder] = useState(initialReminder); - const queryClient = useQueryClient(); - const mutation = useMutation({ - ...mutateGuildReminder(guild), - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: ["GUILD_REMINDERS", guild], - }); - setRecentlySaved(true); - setTimeout(() => { - setRecentlySaved(false); - }, ICON_FLASH_TIME); - }, - }); - - const [ - { isSuccess: channelsFetched, data: guildChannels }, - { isSuccess: userFetched, data: userInfo }, - ] = useQueries([fetchGuildChannels(guild), fetchUserInfo()]); + const { isSuccess: channelsFetched, data: guildChannels } = useQuery(fetchGuildChannels(guild)); const [collapsed, setCollapsed] = useState(false); - const [recentlySaved, setRecentlySaved] = useState(false); - if (!channelsFetched || !userFetched) { - // todo + if (!channelsFetched) { return <>; } const channelInfo = guildChannels.find((c) => c.id === reminder.channel); return ( -
-
-
#{channelInfo.name}
- -
- -
-
-
-
-
-
-

- - Image for discord avatar - -

-
-
-
- { - setReminder((reminder) => ({ - ...reminder, - username, - })); - }} - > - { - setReminder((reminder) => ({ - ...reminder, - content, - })); - }} - > - -
-
-
-
-
-
-
- -
- -
- -
-
- -
-
- -
-
-
-
- Intervals available on{" "} - Patreon or{" "} - - self-hosting - -
-
- - -
- -
-
- -
-
-
- -
-
-
- -
-
-
-
- -
-
-
-
+ +
+
+
#{channelInfo.name}
+ +
+
+
+ + +
+
-
- - - -
-
+ ); }; diff --git a/src/components/Reminder/Message.tsx b/src/components/Reminder/Message.tsx new file mode 100644 index 0000000..54b222e --- /dev/null +++ b/src/components/Reminder/Message.tsx @@ -0,0 +1,54 @@ +import { ImagePicker } from "./ImagePicker"; +import { Username } from "./Username"; +import { Content } from "./Content"; +import { Embed } from "./Embed"; +import { useReminder } from "./ReminderContext"; + +export const Message = () => { + const [reminder, setReminder] = useReminder(); + + return ( +
+
+
+

+ { + setReminder((reminder) => ({ + ...reminder, + avatar: url, + })); + }} + > +

+
+
+
+ { + setReminder((reminder) => ({ + ...reminder, + username, + })); + }} + > + { + setReminder((reminder) => ({ + ...reminder, + content, + })); + }} + > + +
+
+
+
+ ); +}; diff --git a/src/components/Reminder/ReminderContext.tsx b/src/components/Reminder/ReminderContext.tsx new file mode 100644 index 0000000..e4257eb --- /dev/null +++ b/src/components/Reminder/ReminderContext.tsx @@ -0,0 +1,9 @@ +import { createContext } from "preact"; +import { useContext } from "preact/compat"; +import { Reminder } from "../../api"; + +export const ReminderContext = createContext< + [Reminder, (r: (reminder: Reminder) => Reminder) => void] +>([null, () => {}]); + +export const useReminder = () => useContext(ReminderContext); diff --git a/src/components/Reminder/Settings.tsx b/src/components/Reminder/Settings.tsx new file mode 100644 index 0000000..fa62685 --- /dev/null +++ b/src/components/Reminder/Settings.tsx @@ -0,0 +1,116 @@ +import { ChannelSelector } from "./ChannelSelector"; +import { DateTime } from "luxon"; +import { IntervalSelector } from "./IntervalSelector"; +import { useQuery } from "react-query"; +import { fetchUserInfo } from "../../api"; +import { useReminder } from "./ReminderContext"; + +export const Settings = () => { + const { isSuccess: userFetched, data: userInfo } = useQuery(fetchUserInfo()); + + const [reminder, setReminder] = useReminder(); + + if (!userFetched) { + return <>; + } + + return ( +
+
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+
+ Intervals available on Patreon{" "} + or{" "} + self-hosting +
+
+ + +
+ +
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+ ); +};