Adding create reminder API
This commit is contained in:
		
							
								
								
									
										15
									
								
								src/api.ts
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/api.ts
									
									
									
									
									
								
							@@ -122,8 +122,11 @@ export const fetchGuildReminders = (guild: string) => ({
 | 
			
		||||
            .then((value) =>
 | 
			
		||||
                value.map((reminder) => ({
 | 
			
		||||
                    ...reminder,
 | 
			
		||||
                    utc_time: DateTime.fromISO(reminder.utc_time),
 | 
			
		||||
                    expires: reminder.expires === null ? null : DateTime.fromISO(reminder.expires),
 | 
			
		||||
                    utc_time: DateTime.fromISO(reminder.utc_time, { zone: "UTC" }),
 | 
			
		||||
                    expires:
 | 
			
		||||
                        reminder.expires === null
 | 
			
		||||
                            ? null
 | 
			
		||||
                            : DateTime.fromISO(reminder.expires, { zone: "UTC" }),
 | 
			
		||||
                })),
 | 
			
		||||
            ) as Promise<Reminder[]>,
 | 
			
		||||
    staleTime: OTHER_STALE_TIME,
 | 
			
		||||
@@ -137,6 +140,14 @@ export const patchGuildReminder = (guild: string) => ({
 | 
			
		||||
        }),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const postGuildReminder = (guild: string) => ({
 | 
			
		||||
    mutationFn: (reminder: Reminder) =>
 | 
			
		||||
        axios.post(`/dashboard/api/guild/${guild}/reminders`, {
 | 
			
		||||
            ...reminder,
 | 
			
		||||
            utc_time: reminder.utc_time.toFormat("yyyy-LL-dd'T'HH:mm:ss"),
 | 
			
		||||
        }),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const deleteGuildReminder = (guild: string) => ({
 | 
			
		||||
    mutationFn: (reminder: Reminder) =>
 | 
			
		||||
        axios.delete(`/dashboard/api/guild/${guild}/reminders`, {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										69
									
								
								src/components/Reminder/ButtonRow/CreateButtonRow.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/components/Reminder/ButtonRow/CreateButtonRow.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
import { LoadTemplate } from "../LoadTemplate";
 | 
			
		||||
import { useReminder } from "../ReminderContext";
 | 
			
		||||
import { useMutation, useQueryClient } from "react-query";
 | 
			
		||||
import { postGuildReminder } from "../../../api";
 | 
			
		||||
import { useParams } from "wouter";
 | 
			
		||||
import { useState } from "preact/hooks";
 | 
			
		||||
import { ICON_FLASH_TIME } from "../../../consts";
 | 
			
		||||
 | 
			
		||||
export const CreateButtonRow = () => {
 | 
			
		||||
    const { guild } = useParams();
 | 
			
		||||
    const [reminder] = useReminder();
 | 
			
		||||
 | 
			
		||||
    const [recentlyCreated, setRecentlyCreated] = useState(false);
 | 
			
		||||
 | 
			
		||||
    const queryClient = useQueryClient();
 | 
			
		||||
    const mutation = useMutation({
 | 
			
		||||
        ...postGuildReminder(guild),
 | 
			
		||||
        onSuccess: () => {
 | 
			
		||||
            queryClient.invalidateQueries({
 | 
			
		||||
                queryKey: ["GUILD_REMINDERS", guild],
 | 
			
		||||
            });
 | 
			
		||||
            setRecentlyCreated(true);
 | 
			
		||||
            setTimeout(() => {
 | 
			
		||||
                setRecentlyCreated(false);
 | 
			
		||||
            }, ICON_FLASH_TIME);
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <div class="button-row">
 | 
			
		||||
            <div class="button-row-reminder">
 | 
			
		||||
                <button
 | 
			
		||||
                    class="button is-success"
 | 
			
		||||
                    onClick={() => {
 | 
			
		||||
                        mutation.mutate(reminder);
 | 
			
		||||
                    }}
 | 
			
		||||
                >
 | 
			
		||||
                    <span>Create Reminder</span>{" "}
 | 
			
		||||
                    {mutation.isLoading ? (
 | 
			
		||||
                        <span class="icon">
 | 
			
		||||
                            <i class="fas fa-spin fa-cog"></i>
 | 
			
		||||
                        </span>
 | 
			
		||||
                    ) : recentlyCreated ? (
 | 
			
		||||
                        <span class="icon">
 | 
			
		||||
                            <i class="fas fa-check"></i>
 | 
			
		||||
                        </span>
 | 
			
		||||
                    ) : (
 | 
			
		||||
                        <span class="icon">
 | 
			
		||||
                            <i class="fas fa-sparkles"></i>
 | 
			
		||||
                        </span>
 | 
			
		||||
                    )}
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="button-row-template">
 | 
			
		||||
                <div>
 | 
			
		||||
                    <button class="button is-success is-outlined">
 | 
			
		||||
                        <span>Create Template</span>{" "}
 | 
			
		||||
                        <span class="icon">
 | 
			
		||||
                            <i class="fas fa-file-spreadsheet"></i>
 | 
			
		||||
                        </span>
 | 
			
		||||
                    </button>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div>
 | 
			
		||||
                    <LoadTemplate />
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@@ -62,7 +62,7 @@ export const EditButtonRow = () => {
 | 
			
		||||
            >
 | 
			
		||||
                {reminder.enabled ? "Disable" : "Enable"}
 | 
			
		||||
            </button>
 | 
			
		||||
            <DeleteButton></DeleteButton>
 | 
			
		||||
            <DeleteButton />
 | 
			
		||||
        </div>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,12 @@ import { useParams } from "wouter";
 | 
			
		||||
import { useState } from "preact/hooks";
 | 
			
		||||
import { useQuery } from "react-query";
 | 
			
		||||
import { fetchUserInfo, Reminder } from "../../api";
 | 
			
		||||
import { Name } from "./Name";
 | 
			
		||||
import { Username } from "./Username";
 | 
			
		||||
import { Content } from "./Content";
 | 
			
		||||
import { Embed } from "./Embed";
 | 
			
		||||
import { ChannelSelector } from "./ChannelSelector";
 | 
			
		||||
import { DateTime } from "luxon";
 | 
			
		||||
import { IntervalSelector } from "./IntervalSelector";
 | 
			
		||||
import { LoadTemplate } from "./LoadTemplate";
 | 
			
		||||
import { ImagePicker } from "./ImagePicker";
 | 
			
		||||
import { CreateButtonRow } from "./ButtonRow/CreateButtonRow";
 | 
			
		||||
import { TopBar } from "./TopBar";
 | 
			
		||||
import { Message } from "./Message";
 | 
			
		||||
import { Settings } from "./Settings";
 | 
			
		||||
import { ReminderContext } from "./ReminderContext";
 | 
			
		||||
 | 
			
		||||
function defaultReminder(): Reminder {
 | 
			
		||||
    return {
 | 
			
		||||
@@ -44,201 +41,23 @@ function defaultReminder(): Reminder {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const CreateReminder = () => {
 | 
			
		||||
    const { guild } = useParams();
 | 
			
		||||
    const [reminder, setReminder] = useState(defaultReminder);
 | 
			
		||||
 | 
			
		||||
    const { isSuccess: userFetched, data: userInfo } = useQuery(fetchUserInfo());
 | 
			
		||||
 | 
			
		||||
    const [collapsed, setCollapsed] = useState(false);
 | 
			
		||||
 | 
			
		||||
    if (!userFetched) {
 | 
			
		||||
        // todo
 | 
			
		||||
        return <></>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <div class={collapsed ? "reminderContent is-collapsed" : "reminderContent"}>
 | 
			
		||||
            <div class="columns is-mobile column reminder-topbar">
 | 
			
		||||
                <Name value={""}></Name>
 | 
			
		||||
                <div class="hide-button-bar">
 | 
			
		||||
                    <button
 | 
			
		||||
                        class="button hide-box"
 | 
			
		||||
                        onClick={() => {
 | 
			
		||||
                            setCollapsed(!collapsed);
 | 
			
		||||
                        }}
 | 
			
		||||
                    >
 | 
			
		||||
                        <span class="is-sr-only">Hide reminder</span>
 | 
			
		||||
                        <i class="fas fa-chevron-down"></i>
 | 
			
		||||
                    </button>
 | 
			
		||||
        <ReminderContext.Provider value={[reminder, setReminder]}>
 | 
			
		||||
            <div class={collapsed ? "reminderContent is-collapsed" : "reminderContent"}>
 | 
			
		||||
                <TopBar
 | 
			
		||||
                    toggleCollapsed={() => {
 | 
			
		||||
                        setCollapsed(!collapsed);
 | 
			
		||||
                    }}
 | 
			
		||||
                />
 | 
			
		||||
                <div class="columns reminder-settings">
 | 
			
		||||
                    <Message />
 | 
			
		||||
                    <Settings />
 | 
			
		||||
                </div>
 | 
			
		||||
                <CreateButtonRow />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="columns reminder-settings">
 | 
			
		||||
                <div class="column discord-frame">
 | 
			
		||||
                    <article class="media">
 | 
			
		||||
                        <figure class="media-left">
 | 
			
		||||
                            <p class="image is-32x32 customizable">
 | 
			
		||||
                                <ImagePicker
 | 
			
		||||
                                    class="is-rounded avatar"
 | 
			
		||||
                                    alt="Image for discord avatar"
 | 
			
		||||
                                    setImage={() => {}}
 | 
			
		||||
                                ></ImagePicker>
 | 
			
		||||
                            </p>
 | 
			
		||||
                        </figure>
 | 
			
		||||
                        <div class="media-content">
 | 
			
		||||
                            <div class="content">
 | 
			
		||||
                                <Username
 | 
			
		||||
                                    value={""}
 | 
			
		||||
                                    onChange={(username: string) => {
 | 
			
		||||
                                        setReminder((reminder) => ({
 | 
			
		||||
                                            ...reminder,
 | 
			
		||||
                                            username,
 | 
			
		||||
                                        }));
 | 
			
		||||
                                    }}
 | 
			
		||||
                                ></Username>
 | 
			
		||||
                                <Content
 | 
			
		||||
                                    value={""}
 | 
			
		||||
                                    onChange={(content: string) => {
 | 
			
		||||
                                        setReminder((reminder) => ({
 | 
			
		||||
                                            ...reminder,
 | 
			
		||||
                                            content,
 | 
			
		||||
                                        }));
 | 
			
		||||
                                    }}
 | 
			
		||||
                                ></Content>
 | 
			
		||||
                                <Embed reminder={{}} setReminder={setReminder}></Embed>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </article>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="column settings">
 | 
			
		||||
                    <div class="field channel-field">
 | 
			
		||||
                        <div class="collapses">
 | 
			
		||||
                            <label class="label" for="channelOption">
 | 
			
		||||
                                Channel*
 | 
			
		||||
                            </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <ChannelSelector channel={reminder.channel}></ChannelSelector>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
                    <div class="field">
 | 
			
		||||
                        <div class="control">
 | 
			
		||||
                            <label class="label collapses">
 | 
			
		||||
                                Time*
 | 
			
		||||
                                <input
 | 
			
		||||
                                    class="input"
 | 
			
		||||
                                    type="datetime-local"
 | 
			
		||||
                                    step="1"
 | 
			
		||||
                                    name="time"
 | 
			
		||||
                                    value={DateTime.now().toFormat("yyyy-LL-dd'T'HH:mm:ss")}
 | 
			
		||||
                                    onChange={(ev) => {
 | 
			
		||||
                                        setReminder((reminder) => ({
 | 
			
		||||
                                            ...reminder,
 | 
			
		||||
                                            utc_time: DateTime.fromISO(
 | 
			
		||||
                                                ev.currentTarget.value,
 | 
			
		||||
                                            ).toUTC(),
 | 
			
		||||
                                        }));
 | 
			
		||||
                                    }}
 | 
			
		||||
                                ></input>
 | 
			
		||||
                            </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
                    <div class="collapses split-controls">
 | 
			
		||||
                        <div>
 | 
			
		||||
                            <div
 | 
			
		||||
                                class={userInfo.patreon ? "patreon-only" : "patreon-only is-locked"}
 | 
			
		||||
                            >
 | 
			
		||||
                                <div class="patreon-invert foreground">
 | 
			
		||||
                                    Intervals available on{" "}
 | 
			
		||||
                                    <a href="https://patreon.com/jellywx">Patreon</a> or{" "}
 | 
			
		||||
                                    <a href="https://gitea.jellypro.xyz/jude/reminder-bot">
 | 
			
		||||
                                        self-hosting
 | 
			
		||||
                                    </a>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="field">
 | 
			
		||||
                                    <label class="label">
 | 
			
		||||
                                        Interval{" "}
 | 
			
		||||
                                        <a class="foreground" href="/help/intervals">
 | 
			
		||||
                                            <i class="fas fa-question-circle"></i>
 | 
			
		||||
                                        </a>
 | 
			
		||||
                                    </label>
 | 
			
		||||
                                    <IntervalSelector
 | 
			
		||||
                                        months={0}
 | 
			
		||||
                                        days={0}
 | 
			
		||||
                                        seconds={0}
 | 
			
		||||
                                    ></IntervalSelector>
 | 
			
		||||
                                </div>
 | 
			
		||||
 | 
			
		||||
                                <div class="field">
 | 
			
		||||
                                    <div class="control">
 | 
			
		||||
                                        <label class="label">
 | 
			
		||||
                                            Expiration
 | 
			
		||||
                                            <input
 | 
			
		||||
                                                class="input"
 | 
			
		||||
                                                type="datetime-local"
 | 
			
		||||
                                                step="1"
 | 
			
		||||
                                                name="expiration"
 | 
			
		||||
                                                value={DateTime.now().toFormat(
 | 
			
		||||
                                                    "yyyy-LL-dd'T'HH:mm:ss",
 | 
			
		||||
                                                )}
 | 
			
		||||
                                            ></input>
 | 
			
		||||
                                        </label>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div class="columns is-mobile tts-row">
 | 
			
		||||
                                <div class="column has-text-centered">
 | 
			
		||||
                                    <div class="is-boxed">
 | 
			
		||||
                                        <label class="label">
 | 
			
		||||
                                            Enable TTS <input type="checkbox" name="tts"></input>
 | 
			
		||||
                                        </label>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="column has-text-centered">
 | 
			
		||||
                                    <div class="file is-small is-boxed">
 | 
			
		||||
                                        <label class="file-label">
 | 
			
		||||
                                            <input
 | 
			
		||||
                                                class="file-input"
 | 
			
		||||
                                                type="file"
 | 
			
		||||
                                                name="attachment"
 | 
			
		||||
                                            ></input>
 | 
			
		||||
                                            <span class="file-cta">
 | 
			
		||||
                                                <span class="file-label">Add Attachment</span>
 | 
			
		||||
                                                <span class="file-icon">
 | 
			
		||||
                                                    <i class="fas fa-upload"></i>
 | 
			
		||||
                                                </span>
 | 
			
		||||
                                            </span>
 | 
			
		||||
                                        </label>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="button-row">
 | 
			
		||||
                <div class="button-row-reminder">
 | 
			
		||||
                    <button class="button is-success">
 | 
			
		||||
                        <span>Create Reminder</span>{" "}
 | 
			
		||||
                        <span class="icon">
 | 
			
		||||
                            <i class="fas fa-sparkles"></i>
 | 
			
		||||
                        </span>
 | 
			
		||||
                    </button>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="button-row-template">
 | 
			
		||||
                    <div>
 | 
			
		||||
                        <button class="button is-success is-outlined">
 | 
			
		||||
                            <span>Create Template</span>{" "}
 | 
			
		||||
                            <span class="icon">
 | 
			
		||||
                                <i class="fas fa-file-spreadsheet"></i>
 | 
			
		||||
                            </span>
 | 
			
		||||
                        </button>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div>
 | 
			
		||||
                        <LoadTemplate setReminder={setReminder}></LoadTemplate>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        </ReminderContext.Provider>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,49 +1,28 @@
 | 
			
		||||
import { fetchGuildChannels, Reminder } from "../../api";
 | 
			
		||||
import { useQuery } from "react-query";
 | 
			
		||||
import { useParams } from "wouter";
 | 
			
		||||
import { Name } from "./Name";
 | 
			
		||||
import { Reminder } from "../../api";
 | 
			
		||||
import { useState } from "preact/hooks";
 | 
			
		||||
import { EditButtonRow } from "./ButtonRow/EditButtonRow";
 | 
			
		||||
import { Message } from "./Message";
 | 
			
		||||
import { Settings } from "./Settings";
 | 
			
		||||
import { ReminderContext } from "./ReminderContext";
 | 
			
		||||
import { TopBar } from "./TopBar";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
    reminder: Reminder;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const EditReminder = ({ reminder: initialReminder }: Props) => {
 | 
			
		||||
    const { guild } = useParams();
 | 
			
		||||
    const [reminder, setReminder] = useState(initialReminder);
 | 
			
		||||
 | 
			
		||||
    const { isSuccess: channelsFetched, data: guildChannels } = useQuery(fetchGuildChannels(guild));
 | 
			
		||||
 | 
			
		||||
    const [collapsed, setCollapsed] = useState(false);
 | 
			
		||||
 | 
			
		||||
    if (!channelsFetched) {
 | 
			
		||||
        return <></>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const channelInfo = guildChannels.find((c) => c.id === reminder.channel);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <ReminderContext.Provider value={[reminder, setReminder]}>
 | 
			
		||||
            <div class={collapsed ? "reminderContent is-collapsed" : "reminderContent"}>
 | 
			
		||||
                <div class="columns is-mobile column reminder-topbar">
 | 
			
		||||
                    <div class="invert-collapses channel-bar">#{channelInfo.name}</div>
 | 
			
		||||
                    <Name value={reminder.name}></Name>
 | 
			
		||||
                    <div class="hide-button-bar">
 | 
			
		||||
                        <button
 | 
			
		||||
                            class="button hide-box"
 | 
			
		||||
                            onClick={() => {
 | 
			
		||||
                                setCollapsed(!collapsed);
 | 
			
		||||
                            }}
 | 
			
		||||
                        >
 | 
			
		||||
                            <span class="is-sr-only">Hide reminder</span>
 | 
			
		||||
                            <i class="fas fa-chevron-down"></i>
 | 
			
		||||
                        </button>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <TopBar
 | 
			
		||||
                    toggleCollapsed={() => {
 | 
			
		||||
                        setCollapsed(!collapsed);
 | 
			
		||||
                    }}
 | 
			
		||||
                />
 | 
			
		||||
                <div class="columns reminder-settings">
 | 
			
		||||
                    <Message />
 | 
			
		||||
                    <Settings />
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,9 @@ import { Modal } from "../Modal";
 | 
			
		||||
import { useQuery } from "react-query";
 | 
			
		||||
import { fetchGuildTemplates } from "../../api";
 | 
			
		||||
import { useParams } from "wouter";
 | 
			
		||||
import { useReminder } from "./ReminderContext";
 | 
			
		||||
 | 
			
		||||
export const LoadTemplate = ({ setReminder }) => {
 | 
			
		||||
export const LoadTemplate = () => {
 | 
			
		||||
    const [modalOpen, setModalOpen] = useState(false);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								src/components/Reminder/TopBar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/components/Reminder/TopBar.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
import { useReminder } from "./ReminderContext";
 | 
			
		||||
import { Name } from "./Name";
 | 
			
		||||
import { fetchGuildChannels, Reminder } from "../../api";
 | 
			
		||||
import { useQuery } from "react-query";
 | 
			
		||||
import { useParams } from "wouter";
 | 
			
		||||
 | 
			
		||||
export const TopBar = ({ toggleCollapsed }) => {
 | 
			
		||||
    const { guild } = useParams();
 | 
			
		||||
    const [reminder] = useReminder();
 | 
			
		||||
 | 
			
		||||
    const { isSuccess, data: guildChannels } = useQuery(fetchGuildChannels(guild));
 | 
			
		||||
 | 
			
		||||
    const channelName = (reminder: Reminder) =>
 | 
			
		||||
        guildChannels.find((c) => c.id === reminder.channel);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <div class="columns is-mobile column reminder-topbar">
 | 
			
		||||
            {isSuccess && <div class="invert-collapses channel-bar">#{channelName(reminder)}</div>}
 | 
			
		||||
            <Name value={reminder.name}></Name>
 | 
			
		||||
            <div class="hide-button-bar">
 | 
			
		||||
                <button class="button hide-box" onClick={toggleCollapsed}>
 | 
			
		||||
                    <span class="is-sr-only">Hide reminder</span>
 | 
			
		||||
                    <i class="fas fa-chevron-down"></i>
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user