Merge tag '0.8.1' into amp.chat

Change-Id: Ice2d3474ef9284bcfea81a4e0043d798edcfaa03
This commit is contained in:
Manuel Stahl 2021-05-25 16:00:00 +01:00
commit 364234f4f4
18 changed files with 3334 additions and 2919 deletions

5
.env Normal file
View File

@ -0,0 +1,5 @@
# This setting allows to fix the homeserver.
# If you set this setting, the user will not be able to select
# the server and have to use synapse-admin with this server.
#REACT_APP_SERVER=https://yourmatrixserver.example.com

View File

@ -1,5 +1,5 @@
language: node_js language: node_js
node_js: node_js:
- 13 - lts/*
cache: yarn cache: yarn

View File

@ -4,7 +4,7 @@
This project is built using [react-admin](https://marmelab.com/react-admin/). This project is built using [react-admin](https://marmelab.com/react-admin/).
It needs at least Synapse v1.27.0 for all functions to work as expected! It needs at least Synapse v1.32.0 for all functions to work as expected!
You get your server version with the request `/_synapse/admin/v1/server_version`. You get your server version with the request `/_synapse/admin/v1/server_version`.
See also [Synapse version API](https://github.com/matrix-org/synapse/blob/develop/docs/admin_api/version_api.rst). See also [Synapse version API](https://github.com/matrix-org/synapse/blob/develop/docs/admin_api/version_api.rst).
@ -33,6 +33,11 @@ Steps for 1):
- download dependencies: `yarn install` - download dependencies: `yarn install`
- start web server: `yarn start` - start web server: `yarn start`
You can fix the homeserver, so that the user can no longer define it himself.
Either you define it at startup (e.g. `REACT_APP_SERVER=https://yourmatrixserver.example.com yarn start`)
or by editing it in the [.env](.env) file. See also the
[documentation](https://create-react-app.dev/docs/adding-custom-environment-variables/).
Steps for 2): Steps for 2):
- run the Docker container from the public docker registry: `docker run -p 8080:80 awesometechnologies/synapse-admin` or use the (docker-compose.yml)[docker-compose.yml]: `docker-compose up -d` - run the Docker container from the public docker registry: `docker run -p 8080:80 awesometechnologies/synapse-admin` or use the (docker-compose.yml)[docker-compose.yml]: `docker-compose up -d`

View File

@ -11,16 +11,14 @@
}, },
"devDependencies": { "devDependencies": {
"@testing-library/jest-dom": "^5.1.1", "@testing-library/jest-dom": "^5.1.1",
"@testing-library/react": "^10.0.2", "@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^12.0.11", "@testing-library/user-event": "^13.1.8",
"enzyme": "^3.11.0", "eslint": "^7.25.0",
"enzyme-adapter-react-16": "^1.15.2", "eslint-config-prettier": "^8.3.0",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.1",
"eslint-plugin-prettier": "^3.1.2", "eslint-plugin-prettier": "^3.1.2",
"jest-fetch-mock": "^3.0.3", "jest-fetch-mock": "^3.0.3",
"prettier": "^2.0.0", "prettier": "^2.2.0",
"ra-test": "^3.14.0" "ra-test": "^3.15.0"
}, },
"dependencies": { "dependencies": {
"@progress/kendo-drawing": "^1.6.0", "@progress/kendo-drawing": "^1.6.0",
@ -30,11 +28,11 @@
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"qrcode.react": "^1.0.0", "qrcode.react": "^1.0.0",
"ra-language-chinese": "^2.0.10", "ra-language-chinese": "^2.0.10",
"ra-language-german": "^2.1.2", "ra-language-german": "^3.13.4",
"react": "^17.0.0", "react": "^17.0.0",
"react-admin": "^3.14.0", "react-admin": "^3.15.0",
"react-dom": "^16.14.0", "react-dom": "^17.0.2",
"react-scripts": "^3.4.4" "react-scripts": "^4.0.0"
}, },
"scripts": { "scripts": {
"start": "REACT_APP_VERSION=$(git describe --tags) react-scripts start", "start": "REACT_APP_VERSION=$(git describe --tags) react-scripts start",

View File

@ -6,6 +6,7 @@ import dataProvider from "./synapse/dataProvider";
import { UserList, UserCreate, UserEdit } from "./components/users"; import { UserList, UserCreate, UserEdit } from "./components/users";
import { RoomList, RoomCreate, RoomShow, RoomEdit } from "./components/rooms"; import { RoomList, RoomCreate, RoomShow, RoomEdit } from "./components/rooms";
import { ReportList, ReportShow } from "./components/EventReports"; import { ReportList, ReportShow } from "./components/EventReports";
import ImportFeature from "./components/ImportFeature";
import LoginPage from "./components/LoginPage"; import LoginPage from "./components/LoginPage";
import UserIcon from "@material-ui/icons/Group"; import UserIcon from "@material-ui/icons/Group";
import EqualizerIcon from "@material-ui/icons/Equalizer"; import EqualizerIcon from "@material-ui/icons/Equalizer";
@ -81,6 +82,7 @@ const App = () => (
<Resource name="joined_rooms" /> <Resource name="joined_rooms" />
<Resource name="pushers" /> <Resource name="pushers" />
<Resource name="servernotices" /> <Resource name="servernotices" />
<Resource name="forward_extremities" />
<Resource name="room_state" /> <Resource name="room_state" />
</Admin> </Admin>
); );

View File

@ -1,14 +1,9 @@
import React from "react"; import React from "react";
import { TestContext } from "ra-test"; import { render } from "@testing-library/react";
import { shallow } from "enzyme";
import App from "./App"; import App from "./App";
describe("App", () => { describe("App", () => {
it("renders", () => { it("renders", () => {
shallow( render(<App />);
<TestContext>
<App />
</TestContext>
);
}); });
}); });

View File

@ -82,6 +82,7 @@ const LoginPage = ({ theme }) => {
const setLocale = useSetLocale(); const setLocale = useSetLocale();
const translate = useTranslate(); const translate = useTranslate();
const base_url = localStorage.getItem("base_url"); const base_url = localStorage.getItem("base_url");
const cfg_base_url = process.env.REACT_APP_SERVER;
const renderInput = ({ const renderInput = ({
meta: { touched, error } = {}, meta: { touched, error } = {},
@ -111,7 +112,9 @@ const LoginPage = ({ theme }) => {
if (!values.base_url.match(/^(http|https):\/\//)) { if (!values.base_url.match(/^(http|https):\/\//)) {
errors.base_url = translate("synapseadmin.auth.protocol_error"); errors.base_url = translate("synapseadmin.auth.protocol_error");
} else if ( } else if (
!values.base_url.match(/^(http|https):\/\/[a-zA-Z0-9\-.]+(:\d{1,5})?$/) !values.base_url.match(
/^(http|https):\/\/[a-zA-Z0-9\-.]+(:\d{1,5})?[^?&\s]*$/
)
) { ) {
errors.base_url = translate("synapseadmin.auth.url_error"); errors.base_url = translate("synapseadmin.auth.url_error");
} }
@ -147,7 +150,7 @@ const LoginPage = ({ theme }) => {
const [serverVersion, setServerVersion] = useState(""); const [serverVersion, setServerVersion] = useState("");
const handleUsernameChange = _ => { const handleUsernameChange = _ => {
if (formData.base_url) return; if (formData.base_url || cfg_base_url) return;
// check if username is a full qualified userId then set base_url accordially // check if username is a full qualified userId then set base_url accordially
const home_server = extractHomeServer(formData.username); const home_server = extractHomeServer(formData.username);
const wellKnownUrl = `https://${home_server}/.well-known/matrix/client`; const wellKnownUrl = `https://${home_server}/.well-known/matrix/client`;
@ -199,6 +202,7 @@ const LoginPage = ({ theme }) => {
label={translate("ra.auth.username")} label={translate("ra.auth.username")}
disabled={loading} disabled={loading}
onBlur={handleUsernameChange} onBlur={handleUsernameChange}
resettable
fullWidth fullWidth
/> />
</div> </div>
@ -209,6 +213,7 @@ const LoginPage = ({ theme }) => {
label={translate("ra.auth.password")} label={translate("ra.auth.password")}
type="password" type="password"
disabled={loading} disabled={loading}
resettable
fullWidth fullWidth
/> />
</div> </div>
@ -217,7 +222,8 @@ const LoginPage = ({ theme }) => {
name="base_url" name="base_url"
component={renderInput} component={renderInput}
label={translate("synapseadmin.auth.base_url")} label={translate("synapseadmin.auth.base_url")}
disabled={loading} disabled={cfg_base_url || loading}
resettable
fullWidth fullWidth
/> />
</div> </div>
@ -228,7 +234,7 @@ const LoginPage = ({ theme }) => {
return ( return (
<Form <Form
initialValues={{ base_url: base_url }} initialValues={{ base_url: cfg_base_url || base_url }}
onSubmit={handleSubmit} onSubmit={handleSubmit}
validate={validate} validate={validate}
render={({ handleSubmit }) => ( render={({ handleSubmit }) => (

View File

@ -1,11 +1,11 @@
import React from "react"; import React from "react";
import { render } from "@testing-library/react";
import { TestContext } from "ra-test"; import { TestContext } from "ra-test";
import { shallow } from "enzyme";
import LoginPage from "./LoginPage"; import LoginPage from "./LoginPage";
describe("LoginForm", () => { describe("LoginForm", () => {
it("renders", () => { it("renders", () => {
shallow( render(
<TestContext> <TestContext>
<LoginPage /> <LoginPage />
</TestContext> </TestContext>

View File

@ -171,7 +171,7 @@ const RoomDirectoryFilter = ({ ...props }) => {
); );
}; };
export const FilterableRoomDirectoryList = ({ ...props }) => { export const FilterableRoomDirectoryList = ({ dispatch, ...props }) => {
const classes = useStyles(); const classes = useStyles();
const translate = useTranslate(); const translate = useTranslate();
const filter = props.roomDirectoryFilters; const filter = props.roomDirectoryFilters;
@ -242,7 +242,7 @@ export const FilterableRoomDirectoryList = ({ ...props }) => {
function mapStateToProps(state) { function mapStateToProps(state) {
return { return {
roomDirectoryFilters: roomdirectoryfilters:
state.admin.resources.room_directory.list.params.displayedFilters, state.admin.resources.room_directory.list.params.displayedFilters,
}; };
} }

View File

@ -16,6 +16,7 @@ import {
Filter, Filter,
FormTab, FormTab,
List, List,
NumberField,
Pagination, Pagination,
ReferenceArrayInput, ReferenceArrayInput,
ReferenceField, ReferenceField,
@ -33,10 +34,13 @@ import {
Toolbar, Toolbar,
TopToolbar, TopToolbar,
useDataProvider, useDataProvider,
useRecordContext,
useRefresh, useRefresh,
useTranslate, useTranslate,
} from "react-admin"; } from "react-admin";
import get from "lodash/get"; import get from "lodash/get";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import { import {
Tooltip, Tooltip,
Typography, Typography,
@ -47,6 +51,7 @@ import {
Select, Select,
MenuItem, MenuItem,
} from "@material-ui/core"; } from "@material-ui/core";
import FastForwardIcon from "@material-ui/icons/FastForward";
import HttpsIcon from "@material-ui/icons/Https"; import HttpsIcon from "@material-ui/icons/Https";
import NoEncryptionIcon from "@material-ui/icons/NoEncryption"; import NoEncryptionIcon from "@material-ui/icons/NoEncryption";
import PageviewIcon from "@material-ui/icons/Pageview"; import PageviewIcon from "@material-ui/icons/Pageview";
@ -62,6 +67,13 @@ import {
RoomDirectorySaveButton, RoomDirectorySaveButton,
} from "./RoomDirectory"; } from "./RoomDirectory";
const useStyles = makeStyles(theme => ({
helper_forward_extremities: {
fontFamily: "Roboto, Helvetica, Arial, sans-serif",
margin: "0.5em",
},
}));
const RoomPagination = props => ( const RoomPagination = props => (
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} /> <Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
); );
@ -481,6 +493,7 @@ const RoomShowActions = ({ basePath, data, resource }) => {
}; };
export const RoomShow = props => { export const RoomShow = props => {
const classes = useStyles({ props });
const translate = useTranslate(); const translate = useTranslate();
return ( return (
<Show {...props} actions={<RoomShowActions />} title={<RoomTitle />}> <Show {...props} actions={<RoomShowActions />} title={<RoomTitle />}>
@ -592,6 +605,7 @@ export const RoomShow = props => {
]} ]}
/> />
</Tab> </Tab>
<Tab <Tab
label={translate("resources.room_state.name", { smart_count: 2 })} label={translate("resources.room_state.name", { smart_count: 2 })}
icon={<EventIcon />} icon={<EventIcon />}
@ -628,6 +642,40 @@ export const RoomShow = props => {
</Datagrid> </Datagrid>
</ReferenceManyField> </ReferenceManyField>
</Tab> </Tab>
<Tab
label="resources.forward_extremities.name"
icon={<FastForwardIcon />}
path="forward_extremities"
>
<div className={classes.helper_forward_extremities}>
{translate("resources.rooms.helper.forward_extremities")}
</div>
<ReferenceManyField
reference="forward_extremities"
target="room_id"
addLabel={false}
>
<Datagrid style={{ width: "100%" }}>
<TextField source="id" sortable={false} />
<DateField
source="received_ts"
showTime
options={{
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}}
sortable={false}
/>
<NumberField source="depth" sortable={false} />
<TextField source="state_group" sortable={false} />
</Datagrid>
</ReferenceManyField>
</Tab>
</TabbedShowLayout> </TabbedShowLayout>
</Show> </Show>
); );
@ -679,8 +727,22 @@ const RoomFilter = ({ ...props }) => {
); );
}; };
const FilterableRoomList = ({ ...props }) => { const RoomNameField = props => {
const filter = props.roomFilters; const { source } = props;
const record = useRecordContext(props);
return (
<span>{record[source] || record["canonical_alias"] || record["id"]}</span>
);
};
RoomNameField.propTypes = {
label: PropTypes.string,
record: PropTypes.object,
source: PropTypes.string.isRequired,
};
const FilterableRoomList = ({ roomFilters, dispatch, ...props }) => {
const filter = roomFilters;
const localMembersFilter = const localMembersFilter =
filter && filter.joined_local_members ? true : false; filter && filter.joined_local_members ? true : false;
const stateEventsFilter = filter && filter.state_events ? true : false; const stateEventsFilter = filter && filter.state_events ? true : false;
@ -701,7 +763,7 @@ const FilterableRoomList = ({ ...props }) => {
sortBy="encryption" sortBy="encryption"
label={<HttpsIcon />} label={<HttpsIcon />}
/> />
<TextField source="name" /> <RoomNameField source="name" />
<TextField source="joined_members" /> <TextField source="joined_members" />
{localMembersFilter && <TextField source="joined_local_members" />} {localMembersFilter && <TextField source="joined_local_members" />}
{stateEventsFilter && <TextField source="state_events" />} {stateEventsFilter && <TextField source="state_events" />}

View File

@ -158,6 +158,7 @@ export const UserList = props => {
{...props} {...props}
filters={<UserFilter />} filters={<UserFilter />}
filterDefaultValues={{ guests: true, deactivated: false }} filterDefaultValues={{ guests: true, deactivated: false }}
sort={{ field: "name", order: "ASC" }}
actions={<UserListActions maxResults={10000} />} actions={<UserListActions maxResults={10000} />}
bulkActionButtons={<UserBulkActionButtons />} bulkActionButtons={<UserBulkActionButtons />}
pagination={<UserPagination />} pagination={<UserPagination />}
@ -165,13 +166,13 @@ export const UserList = props => {
<Datagrid rowClick="edit"> <Datagrid rowClick="edit">
<AvatarField <AvatarField
source="avatar_src" source="avatar_src"
sortable={false}
className={classes.small} className={classes.small}
sortBy="avatar_url"
/> />
<TextField source="id" sortable={false} /> <TextField source="id" sortBy="name" />
<TextField source="displayname" /> <TextField source="displayname" />
<BooleanField source="is_guest" sortable={false} /> <BooleanField source="is_guest" />
<BooleanField source="admin" sortable={false} /> <BooleanField source="admin" />
<SelectField <SelectField
source="user_type" source="user_type"
choices={[ choices={[
@ -180,7 +181,7 @@ export const UserList = props => {
{ id: "limited", name: "resources.users.type.limited" }, { id: "limited", name: "resources.users.type.limited" },
]} ]}
/> />
<BooleanField source="deactivated" sortable={false} /> <BooleanField source="deactivated" />
</Datagrid> </Datagrid>
</List> </List>
); );
@ -480,6 +481,7 @@ export const UserEdit = props => {
addLabel={false} addLabel={false}
pagination={<UserPagination />} pagination={<UserPagination />}
perPage={50} perPage={50}
sort={{ field: "created_ts", order: "DESC" }}
> >
<Datagrid style={{ width: "100%" }}> <Datagrid style={{ width: "100%" }}>
<DateField <DateField
@ -493,7 +495,6 @@ export const UserEdit = props => {
minute: "2-digit", minute: "2-digit",
second: "2-digit", second: "2-digit",
}} }}
sortable={false}
/> />
<DateField <DateField
source="last_access_ts" source="last_access_ts"
@ -506,14 +507,13 @@ export const UserEdit = props => {
minute: "2-digit", minute: "2-digit",
second: "2-digit", second: "2-digit",
}} }}
sortable={false}
/> />
<TextField source="media_id" sortable={false} /> <TextField source="media_id" />
<NumberField source="media_length" sortable={false} /> <NumberField source="media_length" />
<TextField source="media_type" sortable={false} /> <TextField source="media_type" />
<TextField source="upload_name" sortable={false} /> <TextField source="upload_name" />
<TextField source="quarantined_by" sortable={false} /> <TextField source="quarantined_by" />
<BooleanField source="safe_from_quarantine" sortable={false} /> <BooleanField source="safe_from_quarantine" />
<DeleteButton mutationMode="pessimistic" redirect={false} /> <DeleteButton mutationMode="pessimistic" redirect={false} />
</Datagrid> </Datagrid>
</ReferenceManyField> </ReferenceManyField>

View File

@ -1,6 +1,6 @@
import germanMessages from "ra-language-german"; import germanMessages from "ra-language-german";
export default { const de = {
...germanMessages, ...germanMessages,
synapseadmin: { synapseadmin: {
auth: { auth: {
@ -186,6 +186,10 @@ export default {
topic: "Thema", topic: "Thema",
avatar: "Avatar", avatar: "Avatar",
}, },
helper: {
forward_extremities:
"Forward extremities are the leaf events at the end of a Directed acyclic graph (DAG) in a room, aka events that have no children. The more exist in a room, the more state resolution that Synapse needs to perform (hint: it's an expensive operation). While Synapse has code to prevent too many of these existing at one time in a room, bugs can sometimes make them crop up again. If a room has >10 forward extremities, it's worth checking which room is the culprit and potentially removing them using the SQL queries mentioned in #1760.",
},
enums: { enums: {
join_rules: { join_rules: {
public: "Öffentlich", public: "Öffentlich",
@ -329,6 +333,15 @@ export default {
media_length: "Größe der Dateien", media_length: "Größe der Dateien",
}, },
}, },
forward_extremities: {
name: "Vorderextremitäten",
fields: {
id: "Event-ID",
received_ts: "Zeitstempel",
depth: "Tiefe",
state_group: "Zustandsgruppe",
},
},
room_state: { room_state: {
name: "Zustandsereignisse", name: "Zustandsereignisse",
fields: { fields: {
@ -383,5 +396,10 @@ export default {
empty: "Keine Einträge vorhanden", empty: "Keine Einträge vorhanden",
invite: "", invite: "",
}, },
navigation: {
...germanMessages.ra.navigation,
skip_nav: "Zum Inhalt springen",
},
}, },
}; };
export default de;

View File

@ -1,6 +1,6 @@
import englishMessages from "ra-language-english"; import englishMessages from "ra-language-english";
export default { const en = {
...englishMessages, ...englishMessages,
synapseadmin: { synapseadmin: {
auth: { auth: {
@ -184,6 +184,10 @@ export default {
topic: "Topic", topic: "Topic",
avatar: "Avatar", avatar: "Avatar",
}, },
helper: {
forward_extremities:
"Forward extremities are the leaf events at the end of a Directed acyclic graph (DAG) in a room, aka events that have no children. The more exist in a room, the more state resolution that Synapse needs to perform (hint: it's an expensive operation). While Synapse has code to prevent too many of these existing at one time in a room, bugs can sometimes make them crop up again. If a room has >10 forward extremities, it's worth checking which room is the culprit and potentially removing them using the SQL queries mentioned in #1760.",
},
enums: { enums: {
join_rules: { join_rules: {
public: "Public", public: "Public",
@ -262,7 +266,7 @@ export default {
name: "Media", name: "Media",
fields: { fields: {
media_id: "Media ID", media_id: "Media ID",
media_length: "Lenght", media_length: "File Size (in Bytes)",
media_type: "Type", media_type: "Type",
upload_name: "File name", upload_name: "File name",
quarantined_by: "Quarantined by", quarantined_by: "Quarantined by",
@ -325,6 +329,15 @@ export default {
media_length: "Media length", media_length: "Media length",
}, },
}, },
forward_extremities: {
name: "Forward Extremities",
fields: {
id: "Event ID",
received_ts: "Timestamp",
depth: "Depth",
state_group: "State group",
},
},
room_state: { room_state: {
name: "State events", name: "State events",
fields: { fields: {
@ -353,3 +366,4 @@ export default {
}, },
}, },
}; };
export default en;

View File

@ -1,6 +1,6 @@
import chineseMessages from "ra-language-chinese"; import chineseMessages from "ra-language-chinese";
export default { const zh = {
...chineseMessages, ...chineseMessages,
synapseadmin: { synapseadmin: {
auth: { auth: {
@ -288,3 +288,4 @@ export default {
}, },
}, },
}; };
export default zh;

View File

@ -1,6 +1,3 @@
import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import fetchMock from "jest-fetch-mock"; import fetchMock from "jest-fetch-mock";
configure({ adapter: new Adapter() });
fetchMock.enableMocks(); fetchMock.enableMocks();

View File

@ -3,6 +3,9 @@ import { fetchUtils } from "react-admin";
const authProvider = { const authProvider = {
// called when the user attempts to log in // called when the user attempts to log in
login: ({ base_url, username, password }) => { login: ({ base_url, username, password }) => {
// force homeserver for protection in case the form is manipulated
base_url = process.env.REACT_APP_SERVER || base_url;
console.log("login "); console.log("login ");
const options = { const options = {
method: "POST", method: "POST",
@ -10,6 +13,7 @@ const authProvider = {
type: "m.login.password", type: "m.login.password",
user: username, user: username,
password: password, password: password,
device_id: localStorage.getItem("device_id"),
initial_device_display_name: "Synapse Admin", initial_device_display_name: "Synapse Admin",
}), }),
}; };
@ -17,6 +21,7 @@ const authProvider = {
// use the base_url from login instead of the well_known entry from the // use the base_url from login instead of the well_known entry from the
// server, since the admin might want to access the admin API via some // server, since the admin might want to access the admin API via some
// private address // private address
base_url = base_url.replace(/\/+$/g, "");
localStorage.setItem("base_url", base_url); localStorage.setItem("base_url", base_url);
const decoded_base_url = window.decodeURIComponent(base_url); const decoded_base_url = window.decodeURIComponent(base_url);
@ -48,7 +53,6 @@ const authProvider = {
if (typeof access_token === "string") { if (typeof access_token === "string") {
fetchUtils.fetchJson(logout_api_url, options).then(({ json }) => { fetchUtils.fetchJson(logout_api_url, options).then(({ json }) => {
localStorage.removeItem("access_token"); localStorage.removeItem("access_token");
localStorage.removeItem("device_id");
}); });
} }
return Promise.resolve(); return Promise.resolve();

View File

@ -248,6 +248,22 @@ const resourceMap = {
return json.total; return json.total;
}, },
}, },
forward_extremities: {
map: fe => ({
...fe,
id: fe.event_id,
}),
reference: id => ({
endpoint: `/_synapse/admin/v1/rooms/${id}/forward_extremities`,
}),
data: "results",
total: json => {
return json.count;
},
delete: params => ({
endpoint: `/_synapse/admin/v1/rooms/${params.id}/forward_extremities`,
}),
},
room_directory: { room_directory: {
path: "/_matrix/client/r0/publicRooms", path: "/_matrix/client/r0/publicRooms",
map: rd => ({ map: rd => ({
@ -354,10 +370,13 @@ const dataProvider = {
getManyReference: (resource, params) => { getManyReference: (resource, params) => {
console.log("getManyReference " + resource); console.log("getManyReference " + resource);
const { page, perPage } = params.pagination; const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const from = (page - 1) * perPage; const from = (page - 1) * perPage;
const query = { const query = {
from: from, from: from,
limit: perPage, limit: perPage,
order_by: field,
dir: getSearchOrder(order),
}; };
const homeserver = localStorage.getItem("base_url"); const homeserver = localStorage.getItem("base_url");

6021
yarn.lock

File diff suppressed because it is too large Load Diff