Working to feature parity
This commit is contained in:
		| @@ -1,6 +1,5 @@ | ||||
| import axios from "axios"; | ||||
| import { DateTime } from "luxon"; | ||||
| import { QueryClient } from "react-query"; | ||||
|  | ||||
| type UserInfo = { | ||||
|     name: string; | ||||
| @@ -89,6 +88,10 @@ export const fetchUserInfo = () => ({ | ||||
|     staleTime: USER_INFO_STALE_TIME, | ||||
| }); | ||||
|  | ||||
| export const patchUserInfo = () => ({ | ||||
|     mutationFn: (user: UserInfo) => axios.patch(`/dashboard/api/user`, user), | ||||
| }); | ||||
|  | ||||
| export const fetchUserGuilds = () => ({ | ||||
|     queryKey: ["USER_GUILDS"], | ||||
|     queryFn: () => | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import { createContext } from "preact"; | ||||
| import { useContext } from "preact/compat"; | ||||
| import { Message } from "./FlashProvider"; | ||||
|  | ||||
| export const FlashContext = createContext(null); | ||||
| export const FlashContext = createContext(null as (message: Message) => void); | ||||
|  | ||||
| export const useFlash = () => useContext(FlashContext); | ||||
|   | ||||
| @@ -2,13 +2,18 @@ import { FlashContext } from "./FlashContext"; | ||||
| import { useState } from "preact/hooks"; | ||||
| import { MESSAGE_FLASH_TIME } from "../../consts"; | ||||
|  | ||||
| export type Message = { | ||||
|     message: string; | ||||
|     type: "error" | "success"; | ||||
| }; | ||||
|  | ||||
| export const FlashProvider = ({ children }) => { | ||||
|     const [messages, setMessages] = useState([] as string[]); | ||||
|     const [messages, setMessages] = useState([] as Message[]); | ||||
|  | ||||
|     return ( | ||||
|         <FlashContext.Provider | ||||
|             value={(message: string) => { | ||||
|                 setMessages((messages: string[]) => [...messages, message]); | ||||
|             value={(message: Message) => { | ||||
|                 setMessages((messages: Message[]) => [...messages, message]); | ||||
|                 setTimeout(() => { | ||||
|                     setMessages((messages) => [...messages].splice(1)); | ||||
|                 }, MESSAGE_FLASH_TIME); | ||||
| @@ -16,14 +21,22 @@ export const FlashProvider = ({ children }) => { | ||||
|         > | ||||
|             <> | ||||
|                 {children} | ||||
|                 {messages.map((message) => ( | ||||
|                     <div class="notification is-danger flash-message is-active"> | ||||
|                 <div class="flash-container"> | ||||
|                     {messages.map((message) => { | ||||
|                         const className = message.type === "error" ? "is-danger" : "is-success"; | ||||
|                         const icon = | ||||
|                             message.type === "error" ? "fa-exclamation-circle" : "fa-check"; | ||||
|  | ||||
|                         return ( | ||||
|                             <div class={`notification flash-message is-active ${className}`}> | ||||
|                                 <span class="icon"> | ||||
|                             <i class="far fa-exclamation-circle"></i> | ||||
|                                     <i class={`far ${icon}`}></i> | ||||
|                                 </span>{" "} | ||||
|                         <span class="error-message">{message}</span> | ||||
|                                 <span class="error-message">{message.message}</span> | ||||
|                             </div> | ||||
|                         ); | ||||
|                     })} | ||||
|                 </div> | ||||
|                 ))} | ||||
|             </> | ||||
|         </FlashContext.Provider> | ||||
|     ); | ||||
|   | ||||
| @@ -13,7 +13,7 @@ export function App() { | ||||
|             <QueryClientProvider client={queryClient}> | ||||
|                 <Router base={"/dashboard"}> | ||||
|                     <div class="columns is-gapless dashboard-frame"> | ||||
|                         <Sidebar></Sidebar> | ||||
|                         <Sidebar /> | ||||
|                         <div class="column is-main-content"> | ||||
|                             <Switch> | ||||
|                                 <Route path={"/:guild/reminders"} component={Guild}></Route> | ||||
|   | ||||
| @@ -19,8 +19,15 @@ export const CreateButtonRow = () => { | ||||
|         ...postGuildReminder(guild), | ||||
|         onSuccess: (data) => { | ||||
|             if (data.error) { | ||||
|                 flash(data.error); | ||||
|                 flash({ | ||||
|                     message: data.error, | ||||
|                     type: "error", | ||||
|                 }); | ||||
|             } else { | ||||
|                 flash({ | ||||
|                     message: "Reminder created", | ||||
|                     type: "success", | ||||
|                 }); | ||||
|                 queryClient.invalidateQueries({ | ||||
|                     queryKey: ["GUILD_REMINDERS", guild], | ||||
|                 }); | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { useMutation, useQueryClient } from "react-query"; | ||||
| import { useReminder } from "../ReminderContext"; | ||||
| import { deleteGuildReminder } from "../../../api"; | ||||
| import { useParams } from "wouter"; | ||||
| import { useFlash } from "../../App/FlashContext"; | ||||
|  | ||||
| export const DeleteButton = () => { | ||||
|     const [modalOpen, setModalOpen] = useState(false); | ||||
| @@ -27,10 +28,15 @@ const DeleteModal = ({ setModalOpen }) => { | ||||
|     const [reminder] = useReminder(); | ||||
|     const { guild } = useParams(); | ||||
|  | ||||
|     const flash = useFlash(); | ||||
|     const queryClient = useQueryClient(); | ||||
|     const mutation = useMutation({ | ||||
|         ...deleteGuildReminder(guild), | ||||
|         onSuccess: () => { | ||||
|             flash({ | ||||
|                 message: "Reminder deleted", | ||||
|                 type: "success", | ||||
|             }); | ||||
|             queryClient.invalidateQueries({ | ||||
|                 queryKey: ["GUILD_REMINDERS", guild], | ||||
|             }); | ||||
|   | ||||
| @@ -14,8 +14,6 @@ export const EditButtonRow = () => { | ||||
|     const [recentlySaved, setRecentlySaved] = useState(false); | ||||
|     const queryClient = useQueryClient(); | ||||
|  | ||||
|     const flash = useFlash(); | ||||
|  | ||||
|     const mutation = useMutation({ | ||||
|         ...patchGuildReminder(guild), | ||||
|         onSuccess: () => { | ||||
| @@ -27,9 +25,6 @@ export const EditButtonRow = () => { | ||||
|                 setRecentlySaved(false); | ||||
|             }, ICON_FLASH_TIME); | ||||
|         }, | ||||
|         onError: (error) => { | ||||
|             flash(error); | ||||
|         }, | ||||
|     }); | ||||
|  | ||||
|     return ( | ||||
|   | ||||
| @@ -25,14 +25,25 @@ export const LoadTemplate = () => { | ||||
|  | ||||
| const LoadTemplateModal = ({ setModalOpen }) => { | ||||
|     const { guild } = useParams(); | ||||
|     const { status, data: templates } = useQuery(fetchGuildTemplates(guild)); | ||||
|     const [reminder, setReminder] = useReminder(); | ||||
|     const { isSuccess, data: templates } = useQuery(fetchGuildTemplates(guild)); | ||||
|  | ||||
|     const [selected, setSelected] = useState(null); | ||||
|  | ||||
|     return ( | ||||
|         <Modal setModalOpen={setModalOpen} title={"Load Template"}> | ||||
|             <div class="control has-icons-left"> | ||||
|                 <div class="select is-fullwidth"> | ||||
|                     <select id="templateSelect"> | ||||
|                         {templates !== undefined && | ||||
|                     <select | ||||
|                         id="templateSelect" | ||||
|                         onChange={(ev) => { | ||||
|                             setSelected(ev.currentTarget.value); | ||||
|                         }} | ||||
|                     > | ||||
|                         <option disabled={true} selected={true}> | ||||
|                             Choose template... | ||||
|                         </option> | ||||
|                         {isSuccess && | ||||
|                             templates.map((template) => ( | ||||
|                                 <option value={template.id}>{template.name}</option> | ||||
|                             ))} | ||||
| @@ -44,7 +55,17 @@ const LoadTemplateModal = ({ setModalOpen }) => { | ||||
|             </div> | ||||
|             <br></br> | ||||
|             <div class="has-text-centered"> | ||||
|                 <button class="button is-success close-modal" id="load-template"> | ||||
|                 <button | ||||
|                     class="button is-success close-modal" | ||||
|                     id="load-template" | ||||
|                     onClick={() => { | ||||
|                         setReminder((reminder) => ({ | ||||
|                             ...reminder, | ||||
|                             // todo this probably needs to exclude some things | ||||
|                             ...templates.find((template) => template.id === selected), | ||||
|                         })); | ||||
|                     }} | ||||
|                 > | ||||
|                     Load Template | ||||
|                 </button> | ||||
|                 <button class="button is-danger" id="delete-template"> | ||||
|   | ||||
| @@ -5,6 +5,8 @@ import { Brand } from "./Brand"; | ||||
| import { Wave } from "./Wave"; | ||||
| import { GuildEntry } from "./GuildEntry"; | ||||
| import { fetchUserGuilds, GuildInfo } from "../../api"; | ||||
| import { useState } from "preact/hooks"; | ||||
| import { TimezonePicker } from "../TimezonePicker"; | ||||
|  | ||||
| type ContentProps = { | ||||
|     guilds: GuildInfo[]; | ||||
| @@ -32,12 +34,7 @@ const SidebarContent = ({ guilds }: ContentProps) => { | ||||
|                                 </span>{" "} | ||||
|                                 Import/Export | ||||
|                             </a> | ||||
|                             <a class="show-modal" data-modal="chooseTimezoneModal"> | ||||
|                                 <span class="icon"> | ||||
|                                     <i class="fas fa-map-marked"></i> | ||||
|                                 </span>{" "} | ||||
|                                 Timezone | ||||
|                             </a> | ||||
|                             <TimezonePicker /> | ||||
|                             <a href="/login/discord/logout"> | ||||
|                                 <span class="icon"> | ||||
|                                     <i class="fas fa-sign-out"></i> | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| import { DateTime } from "luxon"; | ||||
| import { useQuery } from "react-query"; | ||||
| import { QueryKeys } from "../../consts"; | ||||
| import { fetchUserInfo } from "../../api"; | ||||
| import { Modal } from "../Modal"; | ||||
| import { useState } from "preact/hooks"; | ||||
|  | ||||
| type DisplayProps = { | ||||
|     timezone: string; | ||||
| @@ -27,30 +28,35 @@ const TimezoneDisplay = ({ timezone }: DisplayProps) => { | ||||
|     ); | ||||
| }; | ||||
|  | ||||
| const TimezonePicker = () => { | ||||
|     const browserTimezone = DateTime.now().zone.name; | ||||
|  | ||||
|     const { isLoading, isError, data } = useQuery({ | ||||
|         queryKey: QueryKeys.USER_DATA, | ||||
|         queryFn: fetchUserInfo, | ||||
|     }); | ||||
| export const TimezonePicker = () => { | ||||
|     const [modalOpen, setModalOpen] = useState(false); | ||||
|  | ||||
|     return ( | ||||
|         <div class="modal" id="chooseTimezoneModal"> | ||||
|             <div class="modal-background"></div> | ||||
|             <div class="modal-card"> | ||||
|                 <header class="modal-card-head"> | ||||
|                     <label class="modal-card-title" for="urlInput"> | ||||
|                         Update Timezone{" "} | ||||
|                         <a href="/help/timezone"> | ||||
|                             <span> | ||||
|                                 <i class="fa fa-question-circle"></i> | ||||
|                             </span> | ||||
|         <> | ||||
|             <a | ||||
|                 class="show-modal" | ||||
|                 data-modal="chooseTimezoneModal" | ||||
|                 onClick={() => { | ||||
|                     setModalOpen(true); | ||||
|                 }} | ||||
|             > | ||||
|                 <span class="icon"> | ||||
|                     <i class="fas fa-map-marked"></i> | ||||
|                 </span>{" "} | ||||
|                 Timezone | ||||
|             </a> | ||||
|                     </label> | ||||
|                     <button class="delete close-modal" aria-label="close"></button> | ||||
|                 </header> | ||||
|                 <section class="modal-card-body"> | ||||
|             {modalOpen && <TimezoneModal setModalOpen={setModalOpen} />} | ||||
|         </> | ||||
|     ); | ||||
| }; | ||||
|  | ||||
| const TimezoneModal = ({ setModalOpen }) => { | ||||
|     const browserTimezone = DateTime.now().zone.name; | ||||
|  | ||||
|     const { isLoading, isError, data } = useQuery(fetchUserInfo()); | ||||
|  | ||||
|     return ( | ||||
|         <Modal title={"Timezone"} setModalOpen={setModalOpen}> | ||||
|             <p> | ||||
|                 Your configured timezone is:{" "} | ||||
|                 <TimezoneDisplay timezone={browserTimezone}></TimezoneDisplay> | ||||
| @@ -65,14 +71,11 @@ const TimezonePicker = () => { | ||||
|                         {isLoading ? ( | ||||
|                             <i className="fas fa-cog fa-spin"></i> | ||||
|                         ) : ( | ||||
|                                     <TimezoneDisplay | ||||
|                                         timezone={data.timezone || "UTC"} | ||||
|                                     ></TimezoneDisplay> | ||||
|                             <TimezoneDisplay timezone={data.timezone || "UTC"}></TimezoneDisplay> | ||||
|                         )} | ||||
|                     </> | ||||
|                 )} | ||||
|             </p> | ||||
|  | ||||
|             <br></br> | ||||
|             <div class="has-text-centered"> | ||||
|                 <button class="button is-success close-modal" id="set-browser-timezone"> | ||||
| @@ -91,9 +94,6 @@ const TimezonePicker = () => { | ||||
|                     Set Bot Timezone | ||||
|                 </button> | ||||
|             </div> | ||||
|                 </section> | ||||
|             </div> | ||||
|             <button class="modal-close is-large close-modal" aria-label="close"></button> | ||||
|         </div> | ||||
|         </Modal> | ||||
|     ); | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user