User can sign out
This commit is contained in:
@ -1,6 +1,23 @@
|
||||
import {
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
MenuPopover,
|
||||
MenuTrigger,
|
||||
makeStyles,
|
||||
typographyStyles,
|
||||
} from "@fluentui/react-components";
|
||||
import { ServerApi } from "./api/ServerApi";
|
||||
import { AuthRouteWidget } from "./routes/AuthRouteWidget";
|
||||
import { AsyncWidget } from "./widgets/AsyncWidget";
|
||||
import { AuthApi } from "./api/AuthApi";
|
||||
import { useAlert } from "./hooks/providers/AlertDialogProvider";
|
||||
import { useConfirm } from "./hooks/providers/ConfirmDialogProvider";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
title: typographyStyles.title2,
|
||||
});
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
@ -15,7 +32,45 @@ export function App() {
|
||||
}
|
||||
|
||||
function AppInner(): React.ReactElement {
|
||||
const alert = useAlert();
|
||||
const confirm = useConfirm();
|
||||
const styles = useStyles();
|
||||
|
||||
const signOut = async () => {
|
||||
try {
|
||||
if (!(await confirm("Do you really want to sign out?"))) return;
|
||||
await AuthApi.SignOut();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert("Failed to perform sign out!");
|
||||
}
|
||||
};
|
||||
|
||||
if (!ServerApi.Config.authenticated && !ServerApi.Config.disable_auth)
|
||||
return <AuthRouteWidget />;
|
||||
return <>todo authenticated</>;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
maxWidth: "1000px",
|
||||
margin: "50px auto",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<span className={styles.title}>VirtWebRemote</span>
|
||||
<Menu>
|
||||
<MenuTrigger disableButtonEnhancement>
|
||||
<MenuButton>Account</MenuButton>
|
||||
</MenuTrigger>
|
||||
|
||||
<MenuPopover>
|
||||
<MenuList>
|
||||
<MenuItem onClick={signOut}>Sign out</MenuItem>
|
||||
</MenuList>
|
||||
</MenuPopover>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -25,4 +25,16 @@ export class AuthApi {
|
||||
|
||||
window.location.href = "/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign out
|
||||
*/
|
||||
static async SignOut(): Promise<void> {
|
||||
await APIClient.exec({
|
||||
uri: "/auth/sign_out",
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
window.location.href = "/";
|
||||
}
|
||||
}
|
||||
|
69
remote_frontend/src/hooks/providers/AlertDialogProvider.tsx
Normal file
69
remote_frontend/src/hooks/providers/AlertDialogProvider.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogBody,
|
||||
DialogContent,
|
||||
DialogSurface,
|
||||
DialogTitle,
|
||||
} from "@fluentui/react-components";
|
||||
import React, { PropsWithChildren } from "react";
|
||||
|
||||
type AlertContext = (message: string, title?: string) => Promise<void>;
|
||||
|
||||
const AlertContextK = React.createContext<AlertContext | null>(null);
|
||||
|
||||
export function AlertDialogProvider(p: PropsWithChildren): React.ReactElement {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const [title, setTitle] = React.useState<string | undefined>(undefined);
|
||||
const [message, setMessage] = React.useState("");
|
||||
|
||||
const cb = React.useRef<null | (() => void)>(null);
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
|
||||
if (cb.current !== null) cb.current();
|
||||
cb.current = null;
|
||||
};
|
||||
|
||||
const hook: AlertContext = (message, title) => {
|
||||
setTitle(title);
|
||||
setMessage(message);
|
||||
setOpen(true);
|
||||
|
||||
return new Promise((res) => {
|
||||
cb.current = res;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<AlertContextK.Provider value={hook}>{p.children}</AlertContextK.Provider>
|
||||
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(_event, data) => {
|
||||
if (!data.open) setOpen(data.open);
|
||||
}}
|
||||
>
|
||||
<DialogSurface>
|
||||
<DialogBody>
|
||||
{title && <DialogTitle>{title}</DialogTitle>}
|
||||
<DialogContent>{message}</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} autoFocus appearance="secondary">
|
||||
Ok
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</DialogBody>
|
||||
</DialogSurface>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function useAlert(): AlertContext {
|
||||
return React.useContext(AlertContextK)!;
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogBody,
|
||||
DialogContent,
|
||||
DialogSurface,
|
||||
DialogTitle,
|
||||
} from "@fluentui/react-components";
|
||||
import React, { PropsWithChildren } from "react";
|
||||
|
||||
type ConfirmContext = (
|
||||
message: string,
|
||||
title?: string,
|
||||
confirmButton?: string,
|
||||
cancelButton?: string
|
||||
) => Promise<boolean>;
|
||||
|
||||
const ConfirmContextK = React.createContext<ConfirmContext | null>(null);
|
||||
|
||||
export function ConfirmDialogProvider(
|
||||
p: PropsWithChildren
|
||||
): React.ReactElement {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const [title, setTitle] = React.useState<string | undefined>(undefined);
|
||||
const [message, setMessage] = React.useState("");
|
||||
const [confirmButton, setConfirmButton] = React.useState<string | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [cancelButton, setCancelButton] = React.useState<string | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const cb = React.useRef<null | ((a: boolean) => void)>(null);
|
||||
|
||||
const handleClose = (confirm: boolean) => {
|
||||
setOpen(false);
|
||||
|
||||
if (cb.current !== null) cb.current(confirm);
|
||||
cb.current = null;
|
||||
};
|
||||
|
||||
const hook: ConfirmContext = (
|
||||
message,
|
||||
title,
|
||||
confirmButton,
|
||||
cancelButton
|
||||
) => {
|
||||
setTitle(title);
|
||||
setMessage(message);
|
||||
setConfirmButton(confirmButton);
|
||||
setCancelButton(cancelButton);
|
||||
setOpen(true);
|
||||
|
||||
return new Promise((res) => {
|
||||
cb.current = res;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfirmContextK.Provider value={hook}>
|
||||
{p.children}
|
||||
</ConfirmContextK.Provider>
|
||||
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(_event, data) => {
|
||||
if (!data.open) setOpen(false);
|
||||
}}
|
||||
>
|
||||
<DialogSurface>
|
||||
<DialogBody>
|
||||
{title && <DialogTitle>{title}</DialogTitle>}
|
||||
<DialogContent>{message}</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={() => handleClose(false)}
|
||||
autoFocus
|
||||
appearance="secondary"
|
||||
>
|
||||
{cancelButton ?? "Cancel"}
|
||||
</Button>
|
||||
<Button onClick={() => handleClose(true)} appearance="primary">
|
||||
{confirmButton ?? "Confirm"}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</DialogBody>
|
||||
</DialogSurface>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function useConfirm(): ConfirmContext {
|
||||
return React.useContext(ConfirmContextK)!;
|
||||
}
|
@ -6,6 +6,8 @@ import {
|
||||
FluentProvider,
|
||||
teamsHighContrastTheme,
|
||||
} from "@fluentui/react-components";
|
||||
import { AlertDialogProvider } from "./hooks/providers/AlertDialogProvider";
|
||||
import { ConfirmDialogProvider } from "./hooks/providers/ConfirmDialogProvider";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
@ -13,7 +15,11 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
theme={teamsHighContrastTheme}
|
||||
style={{ display: "flex", flex: 1 }}
|
||||
>
|
||||
<App />
|
||||
<AlertDialogProvider>
|
||||
<ConfirmDialogProvider>
|
||||
<App />
|
||||
</ConfirmDialogProvider>
|
||||
</AlertDialogProvider>
|
||||
</FluentProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
Reference in New Issue
Block a user