Add search to room directory landing page (#70)
Part of https://github.com/matrix-org/matrix-public-archive/issues/6
This commit is contained in:
parent
f73246768f
commit
92668996d7
|
@ -3,10 +3,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.RoomDirectoryView_header {
|
.RoomDirectoryView_header {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
padding-top: 80px;
|
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-bottom: 80px;
|
padding-bottom: 80px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
|
@ -14,6 +10,13 @@
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.RoomDirectoryView_headerForm {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
.RoomDirectoryView_matrixLogo {
|
.RoomDirectoryView_matrixLogo {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 148px;
|
max-width: 148px;
|
||||||
|
@ -50,6 +53,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
padding-left: 32px;
|
padding-left: 32px;
|
||||||
|
padding-right: 8px;
|
||||||
|
|
||||||
background: rgba(141, 151, 165, 0.15);
|
background: rgba(141, 151, 165, 0.15);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
@ -71,7 +75,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 80px;
|
margin-bottom: 160px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.RoomDirectoryView_paginationButtonCombo {
|
.RoomDirectoryView_paginationButtonCombo {
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
import mounted from 'matrix-public-archive-shared/hydrogen-vm-render-script';
|
import 'matrix-public-archive-shared/hydrogen-vm-render-script';
|
||||||
console.log('mounted', mounted);
|
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
import mounted from 'matrix-public-archive-shared/room-directory-vm-render-script';
|
import 'matrix-public-archive-shared/room-directory-vm-render-script';
|
||||||
console.log('mounted', mounted);
|
|
||||||
|
|
|
@ -10,19 +10,13 @@ const config = require('../config');
|
||||||
const matrixServerUrl = config.get('matrixServerUrl');
|
const matrixServerUrl = config.get('matrixServerUrl');
|
||||||
assert(matrixServerUrl);
|
assert(matrixServerUrl);
|
||||||
|
|
||||||
async function fetchPublicRooms(accessToken, { server, paginationToken, limit } = {}) {
|
async function fetchPublicRooms(accessToken, { server, searchTerm, paginationToken, limit } = {}) {
|
||||||
assert(accessToken);
|
assert(accessToken);
|
||||||
|
|
||||||
let qs = new URLSearchParams();
|
let qs = new URLSearchParams();
|
||||||
if (server) {
|
if (server) {
|
||||||
qs.append('server', server);
|
qs.append('server', server);
|
||||||
}
|
}
|
||||||
if (paginationToken) {
|
|
||||||
qs.append('since', paginationToken);
|
|
||||||
}
|
|
||||||
if (limit) {
|
|
||||||
qs.append('limit', limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
const publicRoomsEndpoint = urlJoin(
|
const publicRoomsEndpoint = urlJoin(
|
||||||
matrixServerUrl,
|
matrixServerUrl,
|
||||||
|
@ -30,6 +24,14 @@ async function fetchPublicRooms(accessToken, { server, paginationToken, limit }
|
||||||
);
|
);
|
||||||
|
|
||||||
const publicRoomsRes = await fetchEndpointAsJson(publicRoomsEndpoint, {
|
const publicRoomsRes = await fetchEndpointAsJson(publicRoomsEndpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
filter: {
|
||||||
|
generic_search_term: searchTerm,
|
||||||
|
},
|
||||||
|
since: paginationToken,
|
||||||
|
limit,
|
||||||
|
},
|
||||||
accessToken,
|
accessToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ function StatusError(status, inputMessage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.message = `${status} - ${message}`;
|
this.message = `${status} - ${message}`;
|
||||||
|
// This will be picked by the default Express error handler and assign the status code,
|
||||||
|
// https://expressjs.com/en/guide/error-handling.html#the-default-error-handler
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.name = 'StatusError';
|
this.name = 'StatusError';
|
||||||
Error.captureStackTrace(this, StatusError);
|
Error.captureStackTrace(this, StatusError);
|
||||||
|
|
|
@ -29,11 +29,13 @@ router.get(
|
||||||
'/',
|
'/',
|
||||||
asyncHandler(async function (req, res) {
|
asyncHandler(async function (req, res) {
|
||||||
const paginationToken = req.query.page;
|
const paginationToken = req.query.page;
|
||||||
|
const searchTerm = req.query.search;
|
||||||
|
|
||||||
const { rooms, nextPaginationToken, prevPaginationToken } = await fetchPublicRooms(
|
const { rooms, nextPaginationToken, prevPaginationToken } = await fetchPublicRooms(
|
||||||
matrixAccessToken,
|
matrixAccessToken,
|
||||||
{
|
{
|
||||||
//server: TODO,
|
//server: TODO,
|
||||||
|
searchTerm,
|
||||||
paginationToken,
|
paginationToken,
|
||||||
// It would be good to grab more rooms than we display in case we need
|
// It would be good to grab more rooms than we display in case we need
|
||||||
// to filter any out but then the pagination tokens with the homeserver
|
// to filter any out but then the pagination tokens with the homeserver
|
||||||
|
@ -54,7 +56,7 @@ router.get(
|
||||||
rooms,
|
rooms,
|
||||||
nextPaginationToken,
|
nextPaginationToken,
|
||||||
prevPaginationToken,
|
prevPaginationToken,
|
||||||
searchTerm: 'foobar (TODO)',
|
searchTerm,
|
||||||
config: {
|
config: {
|
||||||
basePath,
|
basePath,
|
||||||
matrixServerUrl,
|
matrixServerUrl,
|
||||||
|
|
|
@ -81,10 +81,14 @@ router.get(
|
||||||
'/',
|
'/',
|
||||||
asyncHandler(async function (req, res) {
|
asyncHandler(async function (req, res) {
|
||||||
const roomIdOrAlias = req.params.roomIdOrAlias;
|
const roomIdOrAlias = req.params.roomIdOrAlias;
|
||||||
assert(roomIdOrAlias.startsWith('!') || roomIdOrAlias.startsWith('#'));
|
const isValidAlias = roomIdOrAlias.startsWith('!') || roomIdOrAlias.startsWith('#');
|
||||||
|
if (!isValidAlias) {
|
||||||
|
throw new StatusError(404, `Invalid alias given: ${roomIdOrAlias}`);
|
||||||
|
}
|
||||||
|
|
||||||
// In case we're joining a new room for the first time,
|
// In case we're joining a new room for the first time,
|
||||||
// let's avoid redirecting to our join event
|
// let's avoid redirecting to our join event by getting
|
||||||
|
// the time before we join and looking backwards.
|
||||||
const dateBeforeJoin = Date.now();
|
const dateBeforeJoin = Date.now();
|
||||||
|
|
||||||
// We have to wait for the room join to happen first before we can fetch
|
// We have to wait for the room join to happen first before we can fetch
|
||||||
|
@ -99,7 +103,7 @@ router.get(
|
||||||
direction: 'b',
|
direction: 'b',
|
||||||
});
|
});
|
||||||
if (!originServerTs) {
|
if (!originServerTs) {
|
||||||
throw new StatusError(404, 'Unable to find day with an history');
|
throw new StatusError(404, 'Unable to find day with history');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to a day with messages
|
// Redirect to a day with messages
|
||||||
|
@ -127,7 +131,10 @@ router.get(
|
||||||
timeoutMiddleware,
|
timeoutMiddleware,
|
||||||
asyncHandler(async function (req, res) {
|
asyncHandler(async function (req, res) {
|
||||||
const roomIdOrAlias = req.params.roomIdOrAlias;
|
const roomIdOrAlias = req.params.roomIdOrAlias;
|
||||||
assert(roomIdOrAlias.startsWith('!') || roomIdOrAlias.startsWith('#'));
|
const isValidAlias = roomIdOrAlias.startsWith('!') || roomIdOrAlias.startsWith('#');
|
||||||
|
if (!isValidAlias) {
|
||||||
|
throw new StatusError(404, `Invalid alias given: ${roomIdOrAlias}`);
|
||||||
|
}
|
||||||
|
|
||||||
const { fromTimestamp, toTimestamp, hourRange, fromHour, toHour } =
|
const { fromTimestamp, toTimestamp, hourRange, fromHour, toHour } =
|
||||||
parseArchiveRangeFromReq(req);
|
parseArchiveRangeFromReq(req);
|
||||||
|
|
|
@ -123,6 +123,8 @@ function supressBlankAnchorsReloadingThePage() {
|
||||||
|
|
||||||
// eslint-disable-next-line max-statements
|
// eslint-disable-next-line max-statements
|
||||||
async function mountHydrogen() {
|
async function mountHydrogen() {
|
||||||
|
console.log('Mounting Hydrogen...');
|
||||||
|
console.time('Completed mounting Hydrogen');
|
||||||
const appElement = document.querySelector('#app');
|
const appElement = document.querySelector('#app');
|
||||||
|
|
||||||
const platformConfig = {};
|
const platformConfig = {};
|
||||||
|
@ -309,6 +311,8 @@ async function mountHydrogen() {
|
||||||
addSupportClasses();
|
addSupportClasses();
|
||||||
|
|
||||||
supressBlankAnchorsReloadingThePage();
|
supressBlankAnchorsReloadingThePage();
|
||||||
|
|
||||||
|
console.timeEnd('Completed mounting Hydrogen');
|
||||||
}
|
}
|
||||||
|
|
||||||
// N.B.: When we run this in a virtual machine (`vm`), it will return the last
|
// N.B.: When we run this in a virtual machine (`vm`), it will return the last
|
||||||
|
|
|
@ -17,8 +17,11 @@ class URLCreator {
|
||||||
this._basePath = basePath;
|
this._basePath = basePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
roomDirectoryUrl({ paginationToken } = {}) {
|
roomDirectoryUrl({ searchTerm, paginationToken } = {}) {
|
||||||
let qs = new URLSearchParams();
|
let qs = new URLSearchParams();
|
||||||
|
if (searchTerm) {
|
||||||
|
qs.append('search', searchTerm);
|
||||||
|
}
|
||||||
if (paginationToken) {
|
if (paginationToken) {
|
||||||
qs.append('page', paginationToken);
|
qs.append('page', paginationToken);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ assert(rooms);
|
||||||
const nextPaginationToken = window.matrixPublicArchiveContext.nextPaginationToken;
|
const nextPaginationToken = window.matrixPublicArchiveContext.nextPaginationToken;
|
||||||
const prevPaginationToken = window.matrixPublicArchiveContext.prevPaginationToken;
|
const prevPaginationToken = window.matrixPublicArchiveContext.prevPaginationToken;
|
||||||
const searchTerm = window.matrixPublicArchiveContext.searchTerm;
|
const searchTerm = window.matrixPublicArchiveContext.searchTerm;
|
||||||
assert(searchTerm);
|
|
||||||
const config = window.matrixPublicArchiveContext.config;
|
const config = window.matrixPublicArchiveContext.config;
|
||||||
assert(config);
|
assert(config);
|
||||||
assert(config.matrixServerUrl);
|
assert(config.matrixServerUrl);
|
||||||
|
@ -27,6 +26,8 @@ assert(config.basePath);
|
||||||
const matrixPublicArchiveURLCreator = new MatrixPublicArchiveURLCreator(config.basePath);
|
const matrixPublicArchiveURLCreator = new MatrixPublicArchiveURLCreator(config.basePath);
|
||||||
|
|
||||||
async function mountHydrogen() {
|
async function mountHydrogen() {
|
||||||
|
console.log('Mounting Hydrogen...');
|
||||||
|
console.time('Completed mounting Hydrogen');
|
||||||
const appElement = document.querySelector('#app');
|
const appElement = document.querySelector('#app');
|
||||||
|
|
||||||
const roomDirectoryViewModel = new RoomDirectoryViewModel({
|
const roomDirectoryViewModel = new RoomDirectoryViewModel({
|
||||||
|
@ -34,6 +35,7 @@ async function mountHydrogen() {
|
||||||
homeserverName: config.matrixServerName,
|
homeserverName: config.matrixServerName,
|
||||||
matrixPublicArchiveURLCreator,
|
matrixPublicArchiveURLCreator,
|
||||||
rooms,
|
rooms,
|
||||||
|
searchTerm,
|
||||||
nextPaginationToken,
|
nextPaginationToken,
|
||||||
prevPaginationToken,
|
prevPaginationToken,
|
||||||
});
|
});
|
||||||
|
@ -41,6 +43,7 @@ async function mountHydrogen() {
|
||||||
const view = new RoomDirectoryView(roomDirectoryViewModel);
|
const view = new RoomDirectoryView(roomDirectoryViewModel);
|
||||||
|
|
||||||
appElement.replaceChildren(view.mount());
|
appElement.replaceChildren(view.mount());
|
||||||
|
console.timeEnd('Completed mounting Hydrogen');
|
||||||
}
|
}
|
||||||
|
|
||||||
// N.B.: When we run this in a virtual machine (`vm`), it will return the last
|
// N.B.: When we run this in a virtual machine (`vm`), it will return the last
|
||||||
|
|
|
@ -14,6 +14,7 @@ class RoomDirectoryViewModel extends ViewModel {
|
||||||
homeserverName,
|
homeserverName,
|
||||||
matrixPublicArchiveURLCreator,
|
matrixPublicArchiveURLCreator,
|
||||||
rooms,
|
rooms,
|
||||||
|
searchTerm,
|
||||||
nextPaginationToken,
|
nextPaginationToken,
|
||||||
prevPaginationToken,
|
prevPaginationToken,
|
||||||
} = options;
|
} = options;
|
||||||
|
@ -39,6 +40,7 @@ class RoomDirectoryViewModel extends ViewModel {
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
this._searchTerm = searchTerm;
|
||||||
this._nextPaginationToken = nextPaginationToken;
|
this._nextPaginationToken = nextPaginationToken;
|
||||||
this._prevPaginationToken = prevPaginationToken;
|
this._prevPaginationToken = prevPaginationToken;
|
||||||
}
|
}
|
||||||
|
@ -51,9 +53,19 @@ class RoomDirectoryViewModel extends ViewModel {
|
||||||
return this._matrixPublicArchiveURLCreator.roomDirectoryUrl();
|
return this._matrixPublicArchiveURLCreator.roomDirectoryUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get searchTerm() {
|
||||||
|
return this._searchTerm || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
setSearchTerm(newSearchTerm) {
|
||||||
|
this._searchTerm = newSearchTerm;
|
||||||
|
this.emitChange('searchTerm');
|
||||||
|
}
|
||||||
|
|
||||||
get nextPageUrl() {
|
get nextPageUrl() {
|
||||||
if (this._nextPaginationToken) {
|
if (this._nextPaginationToken) {
|
||||||
return this._matrixPublicArchiveURLCreator.roomDirectoryUrl({
|
return this._matrixPublicArchiveURLCreator.roomDirectoryUrl({
|
||||||
|
searchTerm: this.searchTerm,
|
||||||
paginationToken: this._nextPaginationToken,
|
paginationToken: this._nextPaginationToken,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -64,6 +76,7 @@ class RoomDirectoryViewModel extends ViewModel {
|
||||||
get prevPageUrl() {
|
get prevPageUrl() {
|
||||||
if (this._prevPaginationToken) {
|
if (this._prevPaginationToken) {
|
||||||
return this._matrixPublicArchiveURLCreator.roomDirectoryUrl({
|
return this._matrixPublicArchiveURLCreator.roomDirectoryUrl({
|
||||||
|
searchTerm: this.searchTerm,
|
||||||
paginationToken: this._prevPaginationToken,
|
paginationToken: this._prevPaginationToken,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ class RoomCardView extends TemplateView {
|
||||||
className: {
|
className: {
|
||||||
RoomCardView: true,
|
RoomCardView: true,
|
||||||
},
|
},
|
||||||
|
'data-room-id': vm.roomId,
|
||||||
|
'data-testid': 'room-card',
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
t.a(
|
t.a(
|
||||||
|
|
|
@ -7,6 +7,14 @@ const RoomCardView = require('./RoomCardView');
|
||||||
|
|
||||||
class RoomDirectoryView extends TemplateView {
|
class RoomDirectoryView extends TemplateView {
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
|
// Make sure we don't overwrite the search input value if someone has typed
|
||||||
|
// before the JavaScript has loaded
|
||||||
|
const searchInputBeforeRendering = document.querySelector('.RoomDirectoryView_searchInput');
|
||||||
|
if (searchInputBeforeRendering) {
|
||||||
|
const searchInputValueBeforeRendering = searchInputBeforeRendering.value;
|
||||||
|
vm.setSearchTerm(searchInputValueBeforeRendering);
|
||||||
|
}
|
||||||
|
|
||||||
const roomList = new ListView(
|
const roomList = new ListView(
|
||||||
{
|
{
|
||||||
className: 'RoomDirectoryView_roomList',
|
className: 'RoomDirectoryView_roomList',
|
||||||
|
@ -30,6 +38,7 @@ class RoomDirectoryView extends TemplateView {
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
t.header({ className: 'RoomDirectoryView_header' }, [
|
t.header({ className: 'RoomDirectoryView_header' }, [
|
||||||
|
t.form({ className: 'RoomDirectoryView_headerForm', method: 'GET' }, [
|
||||||
t.a(
|
t.a(
|
||||||
{
|
{
|
||||||
className: 'RoomDirectoryView_matrixLogo',
|
className: 'RoomDirectoryView_matrixLogo',
|
||||||
|
@ -59,19 +68,34 @@ class RoomDirectoryView extends TemplateView {
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
t.input({
|
t.input({
|
||||||
|
type: 'search',
|
||||||
className: 'RoomDirectoryView_searchInput',
|
className: 'RoomDirectoryView_searchInput',
|
||||||
placeholder: 'Search rooms (disabled, not implemented yet)',
|
placeholder: 'Search rooms',
|
||||||
disabled: true,
|
name: 'search',
|
||||||
|
value: vm.searchTerm,
|
||||||
|
// Autocomplete is disabled because browsers share autocomplete
|
||||||
|
// suggestions across domains and this uses a very common
|
||||||
|
// `name="search"`. The name is important because it's what
|
||||||
|
// shows up in the query parameters when the `<form
|
||||||
|
// method="GET">` is submitted. I wish we could scope the
|
||||||
|
// autocomplete suggestions to the apps domain
|
||||||
|
// (https://github.com/whatwg/html/issues/8284). Trying some
|
||||||
|
// custom non-spec value here also doesn't seem to work (Chrome
|
||||||
|
// decides to autofill based on `name="search"`).
|
||||||
|
autocomplete: 'off',
|
||||||
|
autocapitalize: 'off',
|
||||||
|
'data-testid': 'room-directory-search-input',
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
t.div({ className: 'RoomDirectoryView_homeserverSelectSection' }, [
|
t.div({ className: 'RoomDirectoryView_homeserverSelectSection' }, [
|
||||||
t.div({}, 'Show: Matrix rooms on'),
|
t.div({}, 'Show: Matrix rooms on'),
|
||||||
t.select(
|
t.select(
|
||||||
{ className: 'RoomDirectoryView_homeserverSelector' },
|
{ className: 'RoomDirectoryView_homeserverSelector', name: 'homeserver' },
|
||||||
availableHomeserverOptionElements
|
availableHomeserverOptionElements
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
]),
|
||||||
t.main({ className: 'RoomDirectoryView_mainContent' }, [
|
t.main({ className: 'RoomDirectoryView_mainContent' }, [
|
||||||
t.view(roomList),
|
t.view(roomList),
|
||||||
t.div({ className: 'RoomDirectoryView_paginationButtonCombo' }, [
|
t.div({ className: 'RoomDirectoryView_paginationButtonCombo' }, [
|
||||||
|
|
|
@ -16,6 +16,19 @@ function getTxnId() {
|
||||||
return `${new Date().getTime()}--${txnCount}`;
|
return `${new Date().getTime()}--${txnCount}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Basic slugify function, plenty of edge cases and should not be used for
|
||||||
|
// production.
|
||||||
|
function slugify(inputText) {
|
||||||
|
return (
|
||||||
|
inputText
|
||||||
|
.toLowerCase()
|
||||||
|
// Replace whitespace with hyphens
|
||||||
|
.replace(/\s+/g, '-')
|
||||||
|
// Remove anything not alpha-numeric or hypen
|
||||||
|
.replace(/[^a-z0-9-]+/g, '')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function ensureUserRegistered({ matrixServerUrl, username }) {
|
async function ensureUserRegistered({ matrixServerUrl, username }) {
|
||||||
const registerResponse = await fetchEndpointAsJson(
|
const registerResponse = await fetchEndpointAsJson(
|
||||||
urlJoin(matrixServerUrl, '/_matrix/client/v3/register'),
|
urlJoin(matrixServerUrl, '/_matrix/client/v3/register'),
|
||||||
|
@ -73,19 +86,23 @@ async function getTestClientForHs(testMatrixServerUrl) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a public room to test in
|
// Create a public room to test in
|
||||||
async function createTestRoom(client, overrideCreateOptions) {
|
async function createTestRoom(client, overrideCreateOptions = {}) {
|
||||||
let qs = new URLSearchParams();
|
let qs = new URLSearchParams();
|
||||||
if (client.applicationServiceUserIdOverride) {
|
if (client.applicationServiceUserIdOverride) {
|
||||||
qs.append('user_id', client.applicationServiceUserIdOverride);
|
qs.append('user_id', client.applicationServiceUserIdOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const roomName = overrideCreateOptions.name || 'the hangout spot';
|
||||||
|
const roomAlias = slugify(roomName + getTxnId());
|
||||||
|
|
||||||
const createRoomResponse = await fetchEndpointAsJson(
|
const createRoomResponse = await fetchEndpointAsJson(
|
||||||
urlJoin(client.homeserverUrl, `/_matrix/client/v3/createRoom?${qs.toString()}`),
|
urlJoin(client.homeserverUrl, `/_matrix/client/v3/createRoom?${qs.toString()}`),
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: {
|
body: {
|
||||||
preset: 'public_chat',
|
preset: 'public_chat',
|
||||||
name: 'the hangout spot',
|
name: roomName,
|
||||||
|
room_alias_name: roomAlias,
|
||||||
initial_state: [
|
initial_state: [
|
||||||
{
|
{
|
||||||
type: 'm.room.history_visibility',
|
type: 'm.room.history_visibility',
|
||||||
|
@ -95,6 +112,7 @@ async function createTestRoom(client, overrideCreateOptions) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
visibility: 'public',
|
||||||
...overrideCreateOptions,
|
...overrideCreateOptions,
|
||||||
},
|
},
|
||||||
accessToken: client.accessToken,
|
accessToken: client.accessToken,
|
||||||
|
|
|
@ -507,6 +507,57 @@ describe('matrix-public-archive', () => {
|
||||||
`will render a room with a sparse amount of messages (a few per day) with no contamination between days`
|
`will render a room with a sparse amount of messages (a few per day) with no contamination between days`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
describe('Room directory', () => {
|
||||||
|
it('room search narrows down results', async () => {
|
||||||
|
const client = await getTestClientForHs(testMatrixServerUrl1);
|
||||||
|
// This is just an extra room to fill out the room directory and make sure
|
||||||
|
// that it does not appear when searching.
|
||||||
|
await createTestRoom(client);
|
||||||
|
|
||||||
|
// Then create two rooms we will find with search
|
||||||
|
const timeToken = Date.now();
|
||||||
|
const roomPlanetPrefix = `planet-${timeToken}`;
|
||||||
|
const roomSaturnId = await createTestRoom(client, {
|
||||||
|
name: `${roomPlanetPrefix}-saturn`,
|
||||||
|
});
|
||||||
|
const roomMarsId = await createTestRoom(client, {
|
||||||
|
name: `${roomPlanetPrefix}-mars`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Browse the room directory without search to see many rooms
|
||||||
|
archiveUrl = matrixPublicArchiveURLCreator.roomDirectoryUrl();
|
||||||
|
const roomDirectoryPageHtml = await fetchEndpointAsText(archiveUrl);
|
||||||
|
const dom = parseHTML(roomDirectoryPageHtml);
|
||||||
|
|
||||||
|
const roomsOnPageWithoutSearch = [
|
||||||
|
...dom.document.querySelectorAll(`[data-testid="room-card"]`),
|
||||||
|
].map((roomCardEl) => {
|
||||||
|
return roomCardEl.getAttribute('data-room-id');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then browse the room directory again, this time with the search
|
||||||
|
// narrowing down results.
|
||||||
|
archiveUrl = matrixPublicArchiveURLCreator.roomDirectoryUrl({
|
||||||
|
searchTerm: roomPlanetPrefix,
|
||||||
|
});
|
||||||
|
const roomDirectoryWithSearchPageHtml = await fetchEndpointAsText(archiveUrl);
|
||||||
|
const domWithSearch = parseHTML(roomDirectoryWithSearchPageHtml);
|
||||||
|
|
||||||
|
const roomsOnPageWithSearch = [
|
||||||
|
...domWithSearch.document.querySelectorAll(`[data-testid="room-card"]`),
|
||||||
|
].map((roomCardEl) => {
|
||||||
|
return roomCardEl.getAttribute('data-room-id');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert that the rooms we searched for are visible
|
||||||
|
assert.deepStrictEqual(roomsOnPageWithSearch.sort(), [roomSaturnId, roomMarsId].sort());
|
||||||
|
|
||||||
|
// Sanity check that search does something. Assert that it's not showing
|
||||||
|
// the same results as if we didn't make any search.
|
||||||
|
assert.notDeepStrictEqual(roomsOnPageWithSearch.sort(), roomsOnPageWithoutSearch.sort());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('access controls', () => {
|
describe('access controls', () => {
|
||||||
it('not allowed to view private room even when the archiver user is in the room', async () => {
|
it('not allowed to view private room even when the archiver user is in the room', async () => {
|
||||||
const client = await getTestClientForHs(testMatrixServerUrl1);
|
const client = await getTestClientForHs(testMatrixServerUrl1);
|
||||||
|
|
Loading…
Reference in New Issue