Working to feature parity
This commit is contained in:
parent
30dfaa17af
commit
8ba7a39ce5
@ -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">
|
||||
<span class="icon">
|
||||
<i class="far fa-exclamation-circle"></i>
|
||||
</span>{" "}
|
||||
<span class="error-message">{message}</span>
|
||||
</div>
|
||||
))}
|
||||
<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 ${icon}`}></i>
|
||||
</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,73 +28,72 @@ 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>
|
||||
</label>
|
||||
<button class="delete close-modal" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<p>
|
||||
Your configured timezone is:{" "}
|
||||
<TimezoneDisplay timezone={browserTimezone}></TimezoneDisplay>
|
||||
<br></br>
|
||||
<br></br>
|
||||
Your browser timezone is:{" "}
|
||||
<TimezoneDisplay timezone={browserTimezone}></TimezoneDisplay>
|
||||
<br></br>
|
||||
{!isError && (
|
||||
<>
|
||||
Your bot timezone is:{" "}
|
||||
{isLoading ? (
|
||||
<i className="fas fa-cog fa-spin"></i>
|
||||
) : (
|
||||
<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">
|
||||
<span>Use Browser Timezone</span>{" "}
|
||||
<span class="icon">
|
||||
<i class="fab fa-firefox-browser"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button class="button is-link close-modal" id="set-bot-timezone">
|
||||
<span>Use Bot Timezone</span>{" "}
|
||||
<span class="icon">
|
||||
<i class="fab fa-discord"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button class="button is-warning close-modal" id="update-bot-timezone">
|
||||
Set Bot Timezone
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<button class="modal-close is-large close-modal" aria-label="close"></button>
|
||||
</div>
|
||||
<>
|
||||
<a
|
||||
class="show-modal"
|
||||
data-modal="chooseTimezoneModal"
|
||||
onClick={() => {
|
||||
setModalOpen(true);
|
||||
}}
|
||||
>
|
||||
<span class="icon">
|
||||
<i class="fas fa-map-marked"></i>
|
||||
</span>{" "}
|
||||
Timezone
|
||||
</a>
|
||||
{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>
|
||||
<br></br>
|
||||
<br></br>
|
||||
Your browser timezone is:{" "}
|
||||
<TimezoneDisplay timezone={browserTimezone}></TimezoneDisplay>
|
||||
<br></br>
|
||||
{!isError && (
|
||||
<>
|
||||
Your bot timezone is:{" "}
|
||||
{isLoading ? (
|
||||
<i className="fas fa-cog fa-spin"></i>
|
||||
) : (
|
||||
<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">
|
||||
<span>Use Browser Timezone</span>{" "}
|
||||
<span class="icon">
|
||||
<i class="fab fa-firefox-browser"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button class="button is-link close-modal" id="set-bot-timezone">
|
||||
<span>Use Bot Timezone</span>{" "}
|
||||
<span class="icon">
|
||||
<i class="fab fa-discord"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button class="button is-warning close-modal" id="update-bot-timezone">
|
||||
Set Bot Timezone
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user