diff --git a/README.md b/README.md index 903f4f1..5e5c84b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,28 @@ This project is built using [react-admin](https://marmelab.com/react-admin/). -Use `yarn install` after cloning this repo. +It needs at least Synapse v1.13.0 for all functions to work as expected! -Use `yarn start` to launch the webserver. +## Step-By-Step install: + +You have two options: + +1. Download the source code from github and run using nodejs +2. Run the Docker container + +Steps for 1): + +- make sure you have installed the following: git, yarn, nodejs +- download the source code: `git clone https://github.com/Awesome-Technologies/synapse-admin.git` +- change into downloaded directory: `cd synapse-admin` +- download dependencies: `yarn install` +- start web server: `yarn start` + +Steps for 2): + +- run the Docker container: `docker run -p 8080:80 awesometechnologies/synapse-admin` +- browse to http://localhost:8080 + +## Screenshots + +![Screenshots](./screenshots.jpg) diff --git a/screenshots.jpg b/screenshots.jpg new file mode 100644 index 0000000..9a08375 Binary files /dev/null and b/screenshots.jpg differ diff --git a/src/components/users.js b/src/components/users.js index 7ae40a1..a1f80f8 100644 --- a/src/components/users.js +++ b/src/components/users.js @@ -1,4 +1,5 @@ import React, { Fragment } from "react"; +import Avatar from "@material-ui/core/Avatar"; import PersonPinIcon from "@material-ui/icons/PersonPin"; import ContactMailIcon from "@material-ui/icons/ContactMail"; import DevicesIcon from "@material-ui/icons/Devices"; @@ -19,7 +20,6 @@ import { FormTab, BooleanField, BooleanInput, - ImageField, PasswordInput, TextField, TextInput, @@ -34,6 +34,19 @@ import { } from "react-admin"; import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices"; import { RemoveDeviceButton } from "./devices"; +import { makeStyles } from "@material-ui/core/styles"; + +const useStyles = makeStyles({ + small: { + height: "40px", + width: "40px", + }, + large: { + height: "120px", + width: "120px", + float: "right", + }, +}); const UserPagination = props => ( @@ -64,40 +77,36 @@ const UserBulkActionButtons = props => { ); }; -export const UserList = props => ( - } - filterDefaultValues={{ guests: true, deactivated: false }} - bulkActionButtons={} - pagination={} - > - - - - - - {/* Hack since the users endpoint does not give displaynames in the list*/} - - - - - - - - +const AvatarField = ({ source, className, record = {} }) => ( + ); +export const UserList = props => { + const classes = useStyles(); + return ( + } + filterDefaultValues={{ guests: true, deactivated: false }} + bulkActionButtons={} + pagination={} + > + + + + + + + + + + ); +}; + // https://matrix.org/docs/spec/appendices#user-identifiers const validateUser = regex( /^@[a-z0-9._=\-/]+:.*/, @@ -145,12 +154,15 @@ const UserTitle = ({ record }) => { const translate = useTranslate(); return ( - {translate("resources.users.name")}{" "} + {translate("resources.users.name", { + smart_count: 1, + })}{" "} {record ? `"${record.displayname}"` : ""} ); }; export const UserEdit = props => { + const classes = useStyles(); const translate = useTranslate(); return ( }> @@ -159,6 +171,11 @@ export const UserEdit = props => { label={translate("resources.users.name", { smart_count: 1 })} icon={} > + diff --git a/src/i18n/de.js b/src/i18n/de.js index 066bc64..98898b1 100644 --- a/src/i18n/de.js +++ b/src/i18n/de.js @@ -36,6 +36,7 @@ export default { displayname: "Anzeigename", password: "Passwort", avatar_url: "Avatar URL", + avatar_src: "Avatar", medium: "Medium", threepids: "3PIDs", address: "Adresse", diff --git a/src/i18n/en.js b/src/i18n/en.js index 88b4b29..f83efd3 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -35,6 +35,7 @@ export default { displayname: "Displayname", password: "Password", avatar_url: "Avatar URL", + avatar_src: "Avatar", medium: "Medium", threepids: "3PIDs", address: "Address", diff --git a/src/synapse/dataProvider.js b/src/synapse/dataProvider.js index d728d4f..6a2da69 100644 --- a/src/synapse/dataProvider.js +++ b/src/synapse/dataProvider.js @@ -14,24 +14,32 @@ const jsonClient = (url, options = {}) => { return fetchUtils.fetchJson(url, options); }; +const mxcUrlToHttp = mxcUrl => { + const homeserver = localStorage.getItem("base_url"); + const re = /^mxc:\/\/([^/]+)\/(\w+)/; + var ret = re.exec(mxcUrl); + console.log("mxcClient " + ret); + if (ret == null) return null; + const serverName = ret[1]; + const mediaId = ret[2]; + return `${homeserver}/_matrix/media/r0/thumbnail/${serverName}/${mediaId}?width=24&height=24&method=scale`; +}; + const resourceMap = { users: { path: "/_synapse/admin/v2/users", map: u => ({ ...u, id: u.name, + avatar_src: mxcUrlToHttp(u.avatar_url), is_guest: !!u.is_guest, admin: !!u.admin, deactivated: !!u.deactivated, // need timestamp in milliseconds creation_ts_ms: u.creation_ts * 1000, }), + total: json => json.total, data: "users", - total: (json, from, perPage) => { - return json.next_token - ? parseInt(json.next_token, 10) + perPage - : from + json.users.length; - }, getMany: id => ({ endpoint: `/_synapse/admin/v2/users/${id}`, }),