From 4f2cd383445a6bc0c987fd57c81d574daa353092 Mon Sep 17 00:00:00 2001
From: Aine <97398200+aine-etke@users.noreply.github.com>
Date: Tue, 22 Oct 2024 12:18:55 +0300
Subject: [PATCH] Add user profile to the top menu (#80)

* Add user profile to the top menu

* update readme
---
 README.md                        |  1 +
 src/components/AdminLayout.tsx   |  6 ++++-
 src/synapse/authProvider.test.ts |  6 ++---
 src/synapse/authProvider.ts      | 42 ++++++++++++++++++++++++++++++--
 4 files changed, 49 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 5dda21d..01c9ec2 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,7 @@ The following changes are already implemented:
 * [Login with access token](https://github.com/etkecc/synapse-admin/pull/58)
 * [Fix footer causing vertical scrollbar](https://github.com/etkecc/synapse-admin/pull/60)
 * [Custom Menu Items](https://github.com/etkecc/synapse-admin/pull/79)
+* [Add user profile to the top menu](https://github.com/etkecc/synapse-admin/pull/80)
 
 _the list will be updated as new changes are added_
 
diff --git a/src/components/AdminLayout.tsx b/src/components/AdminLayout.tsx
index 2799693..c85c999 100644
--- a/src/components/AdminLayout.tsx
+++ b/src/components/AdminLayout.tsx
@@ -52,7 +52,11 @@ const AdminMenu = (props) => {
   useEffect(() => {
     const menuConfig = localStorage.getItem('menu');
     if (menuConfig) {
-      setMenu(JSON.parse(menuConfig));
+      try {
+        setMenu(JSON.parse(menuConfig));
+      } catch (e) {
+        console.error('Error parsing menu configuration', e);
+      }
     }
   }, []);
 
diff --git a/src/synapse/authProvider.test.ts b/src/synapse/authProvider.test.ts
index 16cf298..b2e89d6 100644
--- a/src/synapse/authProvider.test.ts
+++ b/src/synapse/authProvider.test.ts
@@ -30,7 +30,7 @@ describe("authProvider", () => {
       });
 
       expect(ret).toEqual({redirectTo: "/"});
-      expect(fetch).toHaveBeenCalledWith("http://example.com/_matrix/client/r0/login", {
+      expect(fetch).toHaveBeenCalledWith("http://example.com/_matrix/client/v3/login", {
         body: '{"device_id":null,"initial_device_display_name":"Synapse Admin","type":"m.login.password","identifier":{"type":"m.id.user","user":"@user:example.com"},"password":"secret"}',
         headers: new Headers({
           Accept: "application/json",
@@ -61,7 +61,7 @@ describe("authProvider", () => {
     });
 
     expect(ret).toEqual({redirectTo: "/"});
-    expect(fetch).toHaveBeenCalledWith("https://example.com/_matrix/client/r0/login", {
+    expect(fetch).toHaveBeenCalledWith("https://example.com/_matrix/client/v3/login", {
       body: '{"device_id":null,"initial_device_display_name":"Synapse Admin","type":"m.login.token","token":"login_token"}',
       headers: new Headers({
         Accept: "application/json",
@@ -83,7 +83,7 @@ describe("authProvider", () => {
 
       await authProvider.logout(null);
 
-      expect(fetch).toHaveBeenCalledWith("example.com/_matrix/client/r0/logout", {
+      expect(fetch).toHaveBeenCalledWith("example.com/_matrix/client/v3/logout", {
         headers: new Headers({
           Accept: "application/json",
           Authorization: "Bearer foo",
diff --git a/src/synapse/authProvider.ts b/src/synapse/authProvider.ts
index f1c673c..112a610 100644
--- a/src/synapse/authProvider.ts
+++ b/src/synapse/authProvider.ts
@@ -2,6 +2,7 @@ import { AuthProvider, HttpError, Options, fetchUtils } from "react-admin";
 
 import storage from "../storage";
 import { MatrixError, displayError } from "../components/error";
+import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
 
 const authProvider: AuthProvider = {
   // called when the user attempts to log in
@@ -57,7 +58,7 @@ const authProvider: AuthProvider = {
     storage.setItem("base_url", base_url);
 
     const decoded_base_url = window.decodeURIComponent(base_url);
-    let login_api_url = decoded_base_url + (accessToken ? "/_matrix/client/v3/account/whoami" : "/_matrix/client/r0/login");
+    let login_api_url = decoded_base_url + (accessToken ? "/_matrix/client/v3/account/whoami" : "/_matrix/client/v3/login");
 
     let response;
 
@@ -95,11 +96,48 @@ const authProvider: AuthProvider = {
       );
     }
   },
+  getIdentity: async () => {
+    const access_token = storage.getItem("access_token");
+    const user_id = storage.getItem("user_id");
+    const base_url = storage.getItem("base_url");
+
+    if (typeof access_token !== "string" || typeof user_id !== "string" || typeof base_url !== "string") {
+      return Promise.reject();
+    }
+
+    const options: Options = {
+      headers: new Headers({
+        Accept: "application/json",
+        Authorization: `Bearer ${access_token}`,
+      }),
+    };
+
+    const whoami_api_url = base_url + `/_matrix/client/v3/profile/${user_id}`;
+
+    try {
+      let avatar_url = "";
+      const response = await fetchUtils.fetchJson(whoami_api_url, options);
+      if (response.json.avatar_url) {
+        const mediaresp = await fetchAuthenticatedMedia(response.json.avatar_url, "thumbnail");
+        const blob = await mediaresp.blob();
+        avatar_url = URL.createObjectURL(blob);
+      }
+
+      return Promise.resolve({
+        id: user_id,
+        fullName: response.json.displayname,
+        avatar: avatar_url,
+      });
+    } catch (err) {
+      console.log("Error getting identity", err);
+      return Promise.reject();
+    }
+  },
   // called when the user clicks on the logout button
   logout: async () => {
     console.log("logout");
 
-    const logout_api_url = storage.getItem("base_url") + "/_matrix/client/r0/logout";
+    const logout_api_url = storage.getItem("base_url") + "/_matrix/client/v3/logout";
     const access_token = storage.getItem("access_token");
 
     const options: Options = {