diff --git a/matrixgw_backend/src/controllers/auth_controller.rs b/matrixgw_backend/src/controllers/auth_controller.rs index ab14ace..6cbab4c 100644 --- a/matrixgw_backend/src/controllers/auth_controller.rs +++ b/matrixgw_backend/src/controllers/auth_controller.rs @@ -2,7 +2,7 @@ use crate::app_config::AppConfig; use crate::controllers::{HttpFailure, HttpResult}; use crate::extractors::auth_extractor::{AuthExtractor, AuthenticatedMethod}; use crate::extractors::session_extractor::MatrixGWSession; -use crate::users::{User, UserEmail}; +use crate::users::{ExtendedUserInfo, User, UserEmail}; use actix_remote_ip::RemoteIP; use actix_web::{HttpResponse, web}; use light_openid::primitives::OpenIDConfig; @@ -108,7 +108,7 @@ pub async fn finish_oidc( /// Get current user information pub async fn auth_info(auth: AuthExtractor) -> HttpResult { - Ok(HttpResponse::Ok().json(auth.user)) + Ok(HttpResponse::Ok().json(ExtendedUserInfo::from_user(auth.user).await?)) } /// Sign out user diff --git a/matrixgw_backend/src/users.rs b/matrixgw_backend/src/users.rs index 2cb1e0a..aab18ed 100644 --- a/matrixgw_backend/src/users.rs +++ b/matrixgw_backend/src/users.rs @@ -166,3 +166,19 @@ impl APIToken { (self.last_used + self.max_inactivity) < time_secs() } } + +#[derive(serde::Serialize, Debug, Clone)] +pub struct ExtendedUserInfo { + #[serde(flatten)] + pub user: User, + pub matrix_user_id: Option, +} + +impl ExtendedUserInfo { + pub async fn from_user(user: User) -> anyhow::Result { + Ok(Self { + user, + matrix_user_id: None, // TODO + }) + } +} diff --git a/matrixgw_frontend/src/App.tsx b/matrixgw_frontend/src/App.tsx index 22acab2..9988388 100644 --- a/matrixgw_frontend/src/App.tsx +++ b/matrixgw_frontend/src/App.tsx @@ -10,6 +10,7 @@ import { ServerApi } from "./api/ServerApi"; import { LoginRoute } from "./routes/auth/LoginRoute"; import { OIDCCbRoute } from "./routes/auth/OIDCCbRoute"; import { HomeRoute } from "./routes/HomeRoute"; +import { MatrixLinkRoute } from "./routes/MatrixLinkRoute"; import { NotFoundRoute } from "./routes/NotFoundRoute"; import { BaseLoginPage } from "./widgets/auth/BaseLoginPage"; import BaseAuthenticatedPage from "./widgets/dashboard/BaseAuthenticatedPage"; @@ -37,6 +38,7 @@ export function App(): React.ReactElement { signedIn || ServerApi.Config.auth_disabled ? ( }> } /> + } /> } /> ) : ( diff --git a/matrixgw_frontend/src/api/AuthApi.ts b/matrixgw_frontend/src/api/AuthApi.ts index d3f60eb..c0fc286 100644 --- a/matrixgw_frontend/src/api/AuthApi.ts +++ b/matrixgw_frontend/src/api/AuthApi.ts @@ -6,6 +6,7 @@ export interface UserInfo { time_update: number; name: string; email: string; + matrix_user_id?: string; } const TokenStateKey = "auth-state"; diff --git a/matrixgw_frontend/src/api/MatrixLinkApi.ts b/matrixgw_frontend/src/api/MatrixLinkApi.ts new file mode 100644 index 0000000..6a05a3c --- /dev/null +++ b/matrixgw_frontend/src/api/MatrixLinkApi.ts @@ -0,0 +1,15 @@ +import { APIClient } from "./ApiClient"; + +export class MatrixLinkApi { + /** + * Start Matrix Account login + */ + static async StartAuth(): Promise<{ url: string }> { + return ( + await APIClient.exec({ + uri: "/matrix_link/start_auth", + method: "POST", + }) + ).data; + } +} diff --git a/matrixgw_frontend/src/routes/HomeRoute.tsx b/matrixgw_frontend/src/routes/HomeRoute.tsx index b93b24f..ee0ddf1 100644 --- a/matrixgw_frontend/src/routes/HomeRoute.tsx +++ b/matrixgw_frontend/src/routes/HomeRoute.tsx @@ -1,3 +1,10 @@ +import { useUserInfo } from "../widgets/dashboard/BaseAuthenticatedPage"; +import { NotLinkedAccountMessage } from "../widgets/NotLinkedAccountMessage"; + export function HomeRoute(): React.ReactElement { + const user = useUserInfo(); + + if (!user.info.matrix_user_id) return ; + return

Todo home route

; } diff --git a/matrixgw_frontend/src/routes/MatrixLinkRoute.tsx b/matrixgw_frontend/src/routes/MatrixLinkRoute.tsx new file mode 100644 index 0000000..7a33ed7 --- /dev/null +++ b/matrixgw_frontend/src/routes/MatrixLinkRoute.tsx @@ -0,0 +1,98 @@ +import LinkIcon from "@mui/icons-material/Link"; +import LinkOffIcon from "@mui/icons-material/LinkOff"; +import { + Button, + Card, + CardActions, + CardContent, + Typography, +} from "@mui/material"; +import { MatrixLinkApi } from "../api/MatrixLinkApi"; +import { useAlert } from "../hooks/contexts_provider/AlertDialogProvider"; +import { useLoadingMessage } from "../hooks/contexts_provider/LoadingMessageProvider"; +import { useUserInfo } from "../widgets/dashboard/BaseAuthenticatedPage"; +import { MatrixGWRouteContainer } from "../widgets/MatrixGWRouteContainer"; + +export function MatrixLinkRoute(): React.ReactElement { + const user = useUserInfo(); + return ( + + {user.info.matrix_user_id === null ? : } + + ); +} + +function ConnectCard(): React.ReactElement { + const alert = useAlert(); + const loadingMessage = useLoadingMessage(); + + const startMatrixConnection = async () => { + try { + loadingMessage.show("Initiating Matrix link..."); + + const res = await MatrixLinkApi.StartAuth(); + + window.location.href = res.url; + } catch (e) { + console.error(`Failed to connect to Matrix account! ${e}`); + alert(`Failed to connect to Matrix account! ${e}`); + } finally { + loadingMessage.hide(); + } + }; + + return ( + + + + Disconnected from your Matrix account + + + + You need to connect MatrixGW to your Matrix account to let it access + your messages. + + + + + + + ); +} + +function ConnectedCard(): React.ReactElement { + const user = useUserInfo(); + + return ( + + + + Connected to your Matrix account + + + +

+ MatrixGW is currently connected to your account with ID{" "} + {user.info.matrix_user_id}. +

+

+ If you encounter issues with your Matrix account you can try to + disconnect and connect back again. +

+
+
+ + + +
+ ); +} diff --git a/matrixgw_frontend/src/widgets/MatrixGWRouteContainer.tsx b/matrixgw_frontend/src/widgets/MatrixGWRouteContainer.tsx new file mode 100644 index 0000000..26c6ba9 --- /dev/null +++ b/matrixgw_frontend/src/widgets/MatrixGWRouteContainer.tsx @@ -0,0 +1,37 @@ +import { Typography } from "@mui/material"; +import React, { type PropsWithChildren } from "react"; + +export function MatrixGWRouteContainer( + p: { + label: string | React.ReactElement; + actions?: React.ReactElement; + } & PropsWithChildren +): React.ReactElement { + return ( +
+
+ {p.label} + {p.actions ?? <>} +
+ + {p.children} +
+ ); +} diff --git a/matrixgw_frontend/src/widgets/NotLinkedAccountMessage.tsx b/matrixgw_frontend/src/widgets/NotLinkedAccountMessage.tsx new file mode 100644 index 0000000..65412b3 --- /dev/null +++ b/matrixgw_frontend/src/widgets/NotLinkedAccountMessage.tsx @@ -0,0 +1,14 @@ +export function NotLinkedAccountMessage(): React.ReactElement { + return ( +
+ Your Matrix account is not linked yet! +
+ ); +} diff --git a/matrixgw_frontend/src/widgets/dashboard/BaseAuthenticatedPage.tsx b/matrixgw_frontend/src/widgets/dashboard/BaseAuthenticatedPage.tsx index 47e8be3..0382979 100644 --- a/matrixgw_frontend/src/widgets/dashboard/BaseAuthenticatedPage.tsx +++ b/matrixgw_frontend/src/widgets/dashboard/BaseAuthenticatedPage.tsx @@ -124,7 +124,6 @@ export default function BaseAuthenticatedPage(): React.ReactElement { flexDirection: "column", flex: 1, overflow: "auto", - padding: "50px", }} > @@ -137,6 +136,6 @@ export default function BaseAuthenticatedPage(): React.ReactElement { ); } -export function userUserInfo(): UserInfoContext { +export function useUserInfo(): UserInfoContext { return React.use(UserInfoContextK)!; } diff --git a/matrixgw_frontend/src/widgets/dashboard/DashboardHeader.tsx b/matrixgw_frontend/src/widgets/dashboard/DashboardHeader.tsx index 0f76b97..6beaa76 100644 --- a/matrixgw_frontend/src/widgets/dashboard/DashboardHeader.tsx +++ b/matrixgw_frontend/src/widgets/dashboard/DashboardHeader.tsx @@ -14,7 +14,7 @@ import Tooltip from "@mui/material/Tooltip"; import Typography from "@mui/material/Typography"; import * as React from "react"; import { RouterLink } from "../RouterLink"; -import { userUserInfo as useUserInfo } from "./BaseAuthenticatedPage"; +import { useUserInfo } from "./BaseAuthenticatedPage"; import ThemeSwitcher from "./ThemeSwitcher"; const AppBar = styled(MuiAppBar)(({ theme }) => ({ diff --git a/matrixgw_frontend/src/widgets/dashboard/DashboardSidebar.tsx b/matrixgw_frontend/src/widgets/dashboard/DashboardSidebar.tsx index d1584a6..a2c7f3d 100644 --- a/matrixgw_frontend/src/widgets/dashboard/DashboardSidebar.tsx +++ b/matrixgw_frontend/src/widgets/dashboard/DashboardSidebar.tsx @@ -107,7 +107,7 @@ export default function DashboardSidebar({ } - href="/matrixlink" + href="/matrix_link" />