diff --git a/README.md b/README.md index 1c02c43..5dda21d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ This project is built using [react-admin](https://marmelab.com/react-admin/). * [Configuration](#configuration) * [Restricting available homeserver](#restricting-available-homeserver) * [Protecting appservice managed users](#protecting-appservice-managed-users) + * [Adding custom menu items](#adding-custom-menu-items) * [Providing support URL](#providing-support-url) * [Usage](#usage) * [Supported Synapse](#supported-synapse) @@ -67,6 +68,7 @@ The following changes are already implemented: * [Better media preview/download](https://github.com/etkecc/synapse-admin/pull/53) * [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) _the list will be updated as new changes are added_ @@ -129,9 +131,29 @@ Example for [mautrix-telegram](https://github.com/mautrix/telegram) } ``` +### Adding custom menu items + +You can add custom menu items to the main menu by providing a `menu` array in the `config.json`. + +```json +{ + "menu": [ + { + "label": "Contact support", + "icon": "SupportAgent", + "url": "https://github.com/etkecc/synapse-admin/issues" + } + ] +} +``` + +Where `icon` is one of the [preloaded icons](./src/components/icons.ts) + ### Providing support URL -Synapse-Admin provides a support link in the main menu - `Contact support`. By default, the link points to the GitHub issues page of the project. You can change this link by providing a `supportURL` in the `config.json`. +**Deprecated**: use `menu` config option described above. Automatically migrated to the `menu` if the `supportURL` is present. + +~~Synapse-Admin provides a support link in the main menu - `Contact support`. By default, the link points to the GitHub issues page of the project. You can change this link by providing a `supportURL` in the `config.json`.~~ ```json { diff --git a/src/App.test.tsx b/src/App.test.tsx index d9c6394..6e34780 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -9,4 +9,4 @@ describe("App", () => { render(); await screen.findAllByText("Welcome to Synapse-admin"); }); -}); \ No newline at end of file +}); diff --git a/src/AppContext.tsx b/src/AppContext.tsx index d7f2755..f005479 100644 --- a/src/AppContext.tsx +++ b/src/AppContext.tsx @@ -4,6 +4,13 @@ interface AppContextType { restrictBaseUrl: string | string[]; asManagedUsers: string[]; supportURL: string; + menu: MenuItem[]; +} + +interface MenuItem { + label: string; + icon: string; + url: string; } export const AppContext = createContext({}); diff --git a/src/components/AdminLayout.tsx b/src/components/AdminLayout.tsx index 72fb788..2799693 100644 --- a/src/components/AdminLayout.tsx +++ b/src/components/AdminLayout.tsx @@ -1,17 +1,7 @@ import { AppBar, Confirm, Layout, Logout, Menu, useLogout, UserMenu } from "react-admin"; -import LiveHelpIcon from "@mui/icons-material/LiveHelp"; import { LoginMethod } from "../pages/LoginPage"; -import { useState } from "react"; - -const DEFAULT_SUPPORT_LINK = "https://github.com/etkecc/synapse-admin/issues"; -const supportLink = (): string => { - try { - new URL(localStorage.getItem("support_url") || ""); // Check if the URL is valid - return localStorage.getItem("support_url") || DEFAULT_SUPPORT_LINK; - } catch (e) { - return DEFAULT_SUPPORT_LINK; - } -}; +import { useEffect, useState, Suspense } from "react"; +import { Icons, DefaultIcon } from "./icons"; const AdminUserMenu = () => { const [open, setOpen] = useState(false); @@ -56,12 +46,38 @@ const AdminUserMenu = () => { const AdminAppBar = () => } />; -const AdminMenu = () => ( - - - } /> - -); +const AdminMenu = (props) => { + const [menu, setMenu] = useState([]); + + useEffect(() => { + const menuConfig = localStorage.getItem('menu'); + if (menuConfig) { + setMenu(JSON.parse(menuConfig)); + } + }, []); + + return ( + + + {menu.map((item, index) => { + const { url, icon, label } = item; + const IconComponent = Icons[icon] as React.ComponentType | undefined; + + return ( + + : } + onClick={props.onMenuClick} + /> + + ); + })} + + ); +}; export const AdminLayout = ({ children }) => ( import('@mui/icons-material/Announcement')), + Engineering: lazy(() => import('@mui/icons-material/Engineering')), + HelpCenter: lazy(() => import('@mui/icons-material/HelpCenter')), + SupportAgent: lazy(() => import('@mui/icons-material/SupportAgent')), + Default: lazy(() => import('@mui/icons-material/OpenInNew')), + // Add more icons as needed +}; + +export const DefaultIcon = Icons.Default; diff --git a/src/index.tsx b/src/index.tsx index 770d1d9..efa762d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,7 +3,7 @@ import React from "react"; import { createRoot } from "react-dom/client"; import App from "./App"; -import { AppContext } from "./AppContext"; +import { AppContext, MenuItem } from "./AppContext"; import storage from "./storage"; fetch("config.json") @@ -12,9 +12,24 @@ fetch("config.json") if (props.asManagedUsers) { storage.setItem("as_managed_users", JSON.stringify(props.asManagedUsers)); } - if (props.supportURL) { - storage.setItem("support_url", props.supportURL); + + let menu: MenuItem[] = []; + if (props.menu) { + menu = props.menu; } + if (props.supportURL) { + const migratedSupportURL = { + label: "Contact support", + icon: "SupportAgent", + url: props.supportURL, + }; + console.warn("supportURL config option is deprecated. Please, use the menu option instead. Automatically migrated to the new menu option:", migratedSupportURL); + menu.push(migratedSupportURL as MenuItem); + } + if (menu.length > 0) { + storage.setItem("menu", JSON.stringify(menu)); + } + return createRoot(document.getElementById("root")).render(