Add preferences to interface

This commit is contained in:
jude 2024-08-04 15:05:28 +01:00
parent febd04c374
commit ef76611d33
6 changed files with 133 additions and 4 deletions

View File

@ -17,7 +17,7 @@ type UserInfo = {
}; };
}; };
type UpdateUserInfo = { export type UpdateUserInfo = {
timezone?: string; timezone?: string;
reset_inputs_on_create?: boolean; reset_inputs_on_create?: boolean;
dashboard_color_scheme?: ColorScheme; dashboard_color_scheme?: ColorScheme;

View File

@ -51,7 +51,7 @@ export const CreateButtonRow = () => {
queryKey: ["USER_REMINDERS"], queryKey: ["USER_REMINDERS"],
}); });
} }
if (userInfo.reset_inputs_on_create) { if (userInfo.preferences.reset_inputs_on_create) {
setReminder(() => defaultReminder()); setReminder(() => defaultReminder());
} }
setRecentlyCreated(true); setRecentlyCreated(true);

View File

@ -12,7 +12,6 @@ export const EditButtonRow = () => {
const [reminder, setReminder] = useReminder(); const [reminder, setReminder] = useReminder();
const [recentlySaved, setRecentlySaved] = useState(false); const [recentlySaved, setRecentlySaved] = useState(false);
const iconFlashTimeout = useRef(0); const iconFlashTimeout = useRef(0);
const flash = useFlash(); const flash = useFlash();

View File

@ -8,6 +8,7 @@ import { fetchUserGuilds, fetchUserInfo, GuildInfo } from "../../api";
import { TimezonePicker } from "../TimezonePicker"; import { TimezonePicker } from "../TimezonePicker";
import "./styles.scss"; import "./styles.scss";
import { Link, useLocation } from "wouter"; import { Link, useLocation } from "wouter";
import { UserPreferences } from "../UserPreferences";
type ContentProps = { type ContentProps = {
guilds: GuildInfo[]; guilds: GuildInfo[];
@ -51,6 +52,7 @@ const SidebarContent = ({ guilds }: ContentProps) => {
<li> <li>
<div id="bottom-sidebar"></div> <div id="bottom-sidebar"></div>
<TimezonePicker /> <TimezonePicker />
<UserPreferences />
<a href="/login/discord/logout"> <a href="/login/discord/logout">
<span class="icon"> <span class="icon">
<i class="fas fa-sign-out"></i> <i class="fas fa-sign-out"></i>

View File

@ -54,7 +54,7 @@ aside.menu {
color: #fff !important; color: #fff !important;
} }
.menu a:hover { .menu a:hover:not(.is-active) {
color: #424242 !important; color: #424242 !important;
} }

View File

@ -0,0 +1,128 @@
import { Modal } from "../Modal";
import { fetchUserInfo, patchUserInfo, UpdateUserInfo } from "../../api";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { useRef, useState } from "preact/hooks";
import { ICON_FLASH_TIME } from "../../consts";
import { useFlash } from "../App/FlashContext";
export const UserPreferences = () => {
const [modalOpen, setModalOpen] = useState(false);
return (
<>
<a
role={"button"}
onClick={() => {
setModalOpen(true);
}}
>
<span class="icon">
<i class="fas fa-cog"></i>
</span>{" "}
Preferences
</a>
{modalOpen && <PreferencesModal setModalOpen={setModalOpen} />}
</>
);
};
const PreferencesModal = ({ setModalOpen }) => {
const flash = useFlash();
const queryClient = useQueryClient();
const { isLoading, isSuccess, isError, data } = useQuery({ ...fetchUserInfo() });
const userInfoMutation = useMutation({
...patchUserInfo(),
onError: (error) => {
flash({
message: `An error occurred: ${error}`,
type: "error",
});
},
onSuccess: () => {
if (iconFlashTimeout.current !== null) {
clearTimeout(iconFlashTimeout.current);
}
setRecentlySaved(true);
iconFlashTimeout.current = setTimeout(() => {
setRecentlySaved(false);
}, ICON_FLASH_TIME);
queryClient.invalidateQueries(["USER_INFO"]).then(() => setUpdatedSettings({}));
},
});
const resetInputsRef = useRef(null as HTMLInputElement | null);
const [recentlySaved, setRecentlySaved] = useState(false);
const iconFlashTimeout = useRef(0);
const [updatedSettings, setUpdatedSettings] = useState({} as UpdateUserInfo);
if (isError) {
return (
<Modal setModalOpen={setModalOpen} title={"Preferences"}>
<span>An error occurred loading user preferences</span>
</Modal>
);
}
return (
<Modal title={"Preferences"} setModalOpen={setModalOpen}>
<div style={{ display: "flex", flexDirection: "row", alignContent: "center" }}>
<label>
<div class={"is-inline-block"}>
{isLoading && <i class={"fa fa-spinner"} />}
{isSuccess && (
<input
type={"checkbox"}
checked={
updatedSettings.reset_inputs_on_create === undefined
? data.preferences.reset_inputs_on_create
: updatedSettings.reset_inputs_on_create
}
ref={resetInputsRef}
onInput={() =>
setUpdatedSettings((s) => ({
...s,
reset_inputs_on_create: resetInputsRef.current.checked,
}))
}
/>
)}
</div>
<div class={"is-inline-block"} style={{ marginLeft: "6px" }}>
Reset reminder inputs when creating a reminder
</div>
</label>
</div>
<br></br>
<div class="has-text-centered">
<button
class="button is-success is-outlined"
style={{
margin: "2px",
}}
onClick={() => {
userInfoMutation.mutate({ ...updatedSettings });
}}
disabled={userInfoMutation.isLoading}
>
<span>Save</span>
{userInfoMutation.isLoading ? (
<span class="icon">
<i class="fas fa-spin fa-cog"></i>
</span>
) : recentlySaved ? (
<span class="icon">
<i class="fas fa-check"></i>
</span>
) : (
<span class="icon">
<i class="fas fa-save"></i>
</span>
)}
</button>
</div>
</Modal>
);
};