Can set user recovery key from UI
This commit is contained in:
@@ -8,7 +8,7 @@ export interface UserInfo {
|
||||
email: string;
|
||||
matrix_user_id?: string;
|
||||
matrix_device_id?: string;
|
||||
matrix_recovery_state?: string;
|
||||
matrix_recovery_state?: "Enabled" | "Disabled" | "Unknown" | "Incomplete";
|
||||
}
|
||||
|
||||
const TokenStateKey = "auth-state";
|
||||
|
||||
@@ -33,4 +33,15 @@ export class MatrixLinkApi {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new user recovery key
|
||||
*/
|
||||
static async SetRecoveryKey(key: string): Promise<void> {
|
||||
await APIClient.exec({
|
||||
uri: "/matrix_link/set_recovery_key",
|
||||
method: "POST",
|
||||
jsonData: { key },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,18 +17,17 @@ const LoadingMessageContextK =
|
||||
export function LoadingMessageProvider(
|
||||
p: PropsWithChildren
|
||||
): React.ReactElement {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [open, setOpen] = React.useState(0);
|
||||
|
||||
const [message, setMessage] = React.useState("");
|
||||
|
||||
const hook: LoadingMessageContext = {
|
||||
show(message) {
|
||||
setMessage(message);
|
||||
setOpen(true);
|
||||
setOpen((v) => v + 1);
|
||||
},
|
||||
hide() {
|
||||
setMessage("");
|
||||
setOpen(false);
|
||||
setOpen((v) => v - 1);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -36,7 +35,7 @@ export function LoadingMessageProvider(
|
||||
<>
|
||||
<LoadingMessageContextK value={hook}>{p.children}</LoadingMessageContextK>
|
||||
|
||||
<Dialog open={open}>
|
||||
<Dialog open={open > 0}>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
<div
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import CheckIcon from "@mui/icons-material/Check";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import KeyIcon from "@mui/icons-material/Key";
|
||||
import LinkIcon from "@mui/icons-material/Link";
|
||||
import LinkOffIcon from "@mui/icons-material/LinkOff";
|
||||
import {
|
||||
@@ -5,8 +8,15 @@ import {
|
||||
Card,
|
||||
CardActions,
|
||||
CardContent,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
TextField,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import React from "react";
|
||||
import { MatrixLinkApi } from "../api/MatrixLinkApi";
|
||||
import { useAlert } from "../hooks/contexts_provider/AlertDialogProvider";
|
||||
import { useConfirm } from "../hooks/contexts_provider/ConfirmDialogProvider";
|
||||
@@ -19,7 +29,14 @@ export function MatrixLinkRoute(): React.ReactElement {
|
||||
const user = useUserInfo();
|
||||
return (
|
||||
<MatrixGWRouteContainer label={"Matrix account link"}>
|
||||
{user.info.matrix_user_id === null ? <ConnectCard /> : <ConnectedCard />}
|
||||
{user.info.matrix_user_id === null ? (
|
||||
<ConnectCard />
|
||||
) : (
|
||||
<>
|
||||
<ConnectedCard />
|
||||
<EncryptionKeyStatus />
|
||||
</>
|
||||
)}
|
||||
</MatrixGWRouteContainer>
|
||||
);
|
||||
}
|
||||
@@ -75,8 +92,6 @@ function ConnectedCard(): React.ReactElement {
|
||||
const alert = useAlert();
|
||||
const loadingMessage = useLoadingMessage();
|
||||
|
||||
const info = useUserInfo();
|
||||
|
||||
const user = useUserInfo();
|
||||
|
||||
const handleDisconnect = async () => {
|
||||
@@ -91,13 +106,13 @@ function ConnectedCard(): React.ReactElement {
|
||||
console.error(`Failed to unlink user account! ${e}`);
|
||||
alert(`Failed to unlink your account! ${e}`);
|
||||
} finally {
|
||||
info.reloadUserInfo();
|
||||
user.reloadUserInfo();
|
||||
loadingMessage.hide();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card style={{ marginBottom: "10px" }}>
|
||||
<CardContent>
|
||||
<Typography variant="h5" component="div" gutterBottom>
|
||||
<i>Connected to your Matrix account</i>
|
||||
@@ -135,3 +150,102 @@ function ConnectedCard(): React.ReactElement {
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function EncryptionKeyStatus(): React.ReactElement {
|
||||
const alert = useAlert();
|
||||
const snackbar = useSnackbar();
|
||||
const loadingMessage = useLoadingMessage();
|
||||
|
||||
const user = useUserInfo();
|
||||
|
||||
const [typeNewKey, setTypeNewKey] = React.useState(false);
|
||||
const [newKey, setNewKey] = React.useState("");
|
||||
|
||||
const handleSetKey = () => setTypeNewKey(true);
|
||||
const cancelSetKey = () => setTypeNewKey(false);
|
||||
const handleSubmitKey = async () => {
|
||||
try {
|
||||
loadingMessage.show("Updating recovery key...");
|
||||
|
||||
await MatrixLinkApi.SetRecoveryKey(newKey);
|
||||
setNewKey("");
|
||||
setTypeNewKey(false);
|
||||
|
||||
snackbar("Recovery key successfully updated!");
|
||||
user.reloadUserInfo();
|
||||
} catch (e) {
|
||||
console.error(`Failed to set new recovery key! ${e}`);
|
||||
alert(`Failed to set new recovery key! ${e}`);
|
||||
} finally {
|
||||
loadingMessage.hide();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography variant="h5" component="div" gutterBottom>
|
||||
Recovery keys
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
<p>
|
||||
Recovery key is used to verify MatrixGW connection and access
|
||||
message history in encrypted rooms.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Current encryption status:{" "}
|
||||
{user.info.matrix_recovery_state === "Enabled" ? (
|
||||
<CheckIcon
|
||||
style={{ display: "inline", verticalAlign: "middle" }}
|
||||
/>
|
||||
) : (
|
||||
<CloseIcon
|
||||
style={{ display: "inline", verticalAlign: "middle" }}
|
||||
/>
|
||||
)}{" "}
|
||||
{user.info.matrix_recovery_state}
|
||||
</p>
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button
|
||||
size="small"
|
||||
variant="outlined"
|
||||
startIcon={<KeyIcon />}
|
||||
onClick={handleSetKey}
|
||||
>
|
||||
Set new recovery key
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
|
||||
{/* Set new key dialog */}
|
||||
<Dialog open={typeNewKey} onClose={cancelSetKey}>
|
||||
<DialogTitle>Set new recovery key</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Enter below you recovery key to verify this session and gain access
|
||||
to old messages.
|
||||
</DialogContentText>
|
||||
<TextField
|
||||
label="Recovery key"
|
||||
type="text"
|
||||
variant="standard"
|
||||
autoComplete="off"
|
||||
value={newKey}
|
||||
onChange={(e) => setNewKey(e.target.value)}
|
||||
fullWidth
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={cancelSetKey}>Cancel</Button>
|
||||
<Button onClick={handleSubmitKey} disabled={newKey === ""} autoFocus>
|
||||
Submit
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { Button } from "@mui/material";
|
||||
import Box from "@mui/material/Box";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import * as React from "react";
|
||||
import { Outlet, useNavigate } from "react-router";
|
||||
import { AuthApi, type UserInfo } from "../../api/AuthApi";
|
||||
import { useAuth } from "../../App";
|
||||
import { useAlert } from "../../hooks/contexts_provider/AlertDialogProvider";
|
||||
import { useLoadingMessage } from "../../hooks/contexts_provider/LoadingMessageProvider";
|
||||
import { AsyncWidget } from "../AsyncWidget";
|
||||
import DashboardHeader from "./DashboardHeader";
|
||||
import DashboardSidebar from "./DashboardSidebar";
|
||||
import { AuthApi, type UserInfo } from "../../api/AuthApi";
|
||||
import { AsyncWidget } from "../AsyncWidget";
|
||||
import { Button } from "@mui/material";
|
||||
import { useAuth } from "../../App";
|
||||
|
||||
interface UserInfoContext {
|
||||
info: UserInfo;
|
||||
@@ -21,12 +23,25 @@ const UserInfoContextK = React.createContext<UserInfoContext | null>(null);
|
||||
|
||||
export default function BaseAuthenticatedPage(): React.ReactElement {
|
||||
const theme = useTheme();
|
||||
const alert = useAlert();
|
||||
const loadingMessage = useLoadingMessage();
|
||||
|
||||
const [userInfo, setuserInfo] = React.useState<null | UserInfo>(null);
|
||||
const loadUserInfo = async () => {
|
||||
setuserInfo(await AuthApi.GetUserInfo());
|
||||
};
|
||||
|
||||
const reloadUserInfo = async () => {
|
||||
try {
|
||||
loadingMessage.show("Refreshing user information...");
|
||||
} catch (e) {
|
||||
console.error(`Failed to load user information! ${e}`);
|
||||
alert(`Failed to load user information! ${e}`);
|
||||
} finally {
|
||||
loadingMessage.hide();
|
||||
}
|
||||
};
|
||||
|
||||
const auth = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -85,7 +100,7 @@ export default function BaseAuthenticatedPage(): React.ReactElement {
|
||||
<UserInfoContextK
|
||||
value={{
|
||||
info: userInfo!,
|
||||
reloadUserInfo: loadUserInfo,
|
||||
reloadUserInfo,
|
||||
signOut,
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user