Add support for the room header (RoomView)
This commit is contained in:
parent
2378ed72c7
commit
c0a2a65c2f
|
@ -0,0 +1,8 @@
|
||||||
|
.room-layout {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-panel {
|
||||||
|
width: 20%;
|
||||||
|
background-color: #f2f5f8;
|
||||||
|
}
|
|
@ -1,45 +1,10 @@
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const fetch = require('node-fetch');
|
|
||||||
|
|
||||||
const urlJoin = require('./lib/url-join');
|
const urlJoin = require('./lib/url-join');
|
||||||
|
const fetchEndpoint = require('./lib/fetch-endpoint');
|
||||||
|
|
||||||
const { matrixServerUrl } = require('../config.json');
|
const { matrixServerUrl } = require('../config.json');
|
||||||
const secrets = require('../secrets.json');
|
assert(matrixServerUrl);
|
||||||
|
|
||||||
const matrixAccessToken = secrets.matrixAccessToken;
|
|
||||||
assert(matrixAccessToken);
|
|
||||||
|
|
||||||
class HTTPResponseError extends Error {
|
|
||||||
constructor(response, responseText, ...args) {
|
|
||||||
super(
|
|
||||||
`HTTP Error Response: ${response.status} ${response.statusText}: ${responseText}\n URL=${response.url}`,
|
|
||||||
...args
|
|
||||||
);
|
|
||||||
this.response = response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkStatus = async (response) => {
|
|
||||||
if (response.ok) {
|
|
||||||
// response.status >= 200 && response.status < 300
|
|
||||||
return response;
|
|
||||||
} else {
|
|
||||||
const responseText = await response.text();
|
|
||||||
throw new HTTPResponseError(response, responseText);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function fetchEndpoint(endpoint) {
|
|
||||||
const res = await fetch(endpoint, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Authorization: `Bearer ${matrixAccessToken}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await checkStatus(res);
|
|
||||||
const data = await res.json();
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchEventsForTimestamp(roomId, ts) {
|
async function fetchEventsForTimestamp(roomId, ts) {
|
||||||
assert(roomId);
|
assert(roomId);
|
||||||
|
@ -50,8 +15,6 @@ async function fetchEventsForTimestamp(roomId, ts) {
|
||||||
`_matrix/client/unstable/org.matrix.msc3030/rooms/${roomId}/timestamp_to_event?ts=${ts}&dir=f`
|
`_matrix/client/unstable/org.matrix.msc3030/rooms/${roomId}/timestamp_to_event?ts=${ts}&dir=f`
|
||||||
);
|
);
|
||||||
const timestampToEventResData = await fetchEndpoint(timestampToEventEndpoint);
|
const timestampToEventResData = await fetchEndpoint(timestampToEventEndpoint);
|
||||||
//console.log('timestampToEventResData', timestampToEventResData);
|
|
||||||
|
|
||||||
const eventIdForTimestamp = timestampToEventResData.event_id;
|
const eventIdForTimestamp = timestampToEventResData.event_id;
|
||||||
assert(eventIdForTimestamp);
|
assert(eventIdForTimestamp);
|
||||||
|
|
||||||
|
@ -67,11 +30,10 @@ async function fetchEventsForTimestamp(roomId, ts) {
|
||||||
`_matrix/client/r0/rooms/${roomId}/messages?from=${contextResData.start}&limit=50&filter={"lazy_load_members":true,"include_redundant_members":true}`
|
`_matrix/client/r0/rooms/${roomId}/messages?from=${contextResData.start}&limit=50&filter={"lazy_load_members":true,"include_redundant_members":true}`
|
||||||
);
|
);
|
||||||
const messageResData = await fetchEndpoint(messagesEndpoint);
|
const messageResData = await fetchEndpoint(messagesEndpoint);
|
||||||
//console.log('messageResData', messageResData);
|
|
||||||
|
|
||||||
//console.log('messageResData.state', messageResData.state);
|
//console.log('messageResData.state', messageResData.state);
|
||||||
const stateEventMap = {};
|
const stateEventMap = {};
|
||||||
for (const stateEvent of messageResData.state) {
|
for (const stateEvent of messageResData.state || []) {
|
||||||
if (stateEvent.type === 'm.room.member') {
|
if (stateEvent.type === 'm.room.member') {
|
||||||
stateEventMap[stateEvent.state_key] = stateEvent;
|
stateEventMap[stateEvent.state_key] = stateEvent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const urlJoin = require('./lib/url-join');
|
||||||
|
const fetchEndpoint = require('./lib/fetch-endpoint');
|
||||||
|
|
||||||
|
const { matrixServerUrl } = require('../config.json');
|
||||||
|
assert(matrixServerUrl);
|
||||||
|
|
||||||
|
async function fetchRoomData(roomId) {
|
||||||
|
const stateNameEndpoint = urlJoin(
|
||||||
|
matrixServerUrl,
|
||||||
|
`_matrix/client/r0/rooms/${roomId}/state/m.room.name`
|
||||||
|
);
|
||||||
|
const stateAvatarEndpoint = urlJoin(
|
||||||
|
matrixServerUrl,
|
||||||
|
`_matrix/client/r0/rooms/${roomId}/state/m.room.avatar`
|
||||||
|
);
|
||||||
|
|
||||||
|
const [stateNameResDataOutcome, stateAvatarResDataOutcome] = await Promise.allSettled([
|
||||||
|
fetchEndpoint(stateNameEndpoint),
|
||||||
|
fetchEndpoint(stateAvatarEndpoint),
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('stateAvatarResDataOutcome', stateAvatarResDataOutcome);
|
||||||
|
|
||||||
|
let name;
|
||||||
|
if (stateNameResDataOutcome.reason === undefined) {
|
||||||
|
name = stateNameResDataOutcome.value.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
let avatarUrl;
|
||||||
|
if (stateAvatarResDataOutcome.reason === undefined) {
|
||||||
|
avatarUrl = stateAvatarResDataOutcome.value.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: roomId,
|
||||||
|
name,
|
||||||
|
avatarUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = fetchRoomData;
|
|
@ -0,0 +1,39 @@
|
||||||
|
const assert = require('assert');
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
|
const { matrixAccessToken } = require('../../secrets.json');
|
||||||
|
assert(matrixAccessToken);
|
||||||
|
|
||||||
|
class HTTPResponseError extends Error {
|
||||||
|
constructor(response, responseText, ...args) {
|
||||||
|
super(
|
||||||
|
`HTTP Error Response: ${response.status} ${response.statusText}: ${responseText}\n URL=${response.url}`,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
this.response = response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkResponseStatus = async (response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
// response.status >= 200 && response.status < 300
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
const responseText = await response.text();
|
||||||
|
throw new HTTPResponseError(response, responseText);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function fetchEndpoint(endpoint) {
|
||||||
|
const res = await fetch(endpoint, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${matrixAccessToken}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await checkResponseStatus(res);
|
||||||
|
const data = await res.json();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = fetchEndpoint;
|
|
@ -7,7 +7,8 @@ const { parseHTML } = require('linkedom');
|
||||||
|
|
||||||
const config = require('../config.json');
|
const config = require('../config.json');
|
||||||
|
|
||||||
async function renderToString(events, stateEventMap) {
|
async function renderToString(roomData, events, stateEventMap) {
|
||||||
|
assert(roomData);
|
||||||
assert(events);
|
assert(events);
|
||||||
assert(stateEventMap);
|
assert(stateEventMap);
|
||||||
|
|
||||||
|
@ -40,12 +41,13 @@ async function renderToString(events, stateEventMap) {
|
||||||
// So we can see logs from the underlying vm
|
// So we can see logs from the underlying vm
|
||||||
vmContext.global.console = console;
|
vmContext.global.console = console;
|
||||||
|
|
||||||
|
vmContext.global.INPUT_ROOM_DATA = roomData;
|
||||||
vmContext.global.INPUT_EVENTS = events;
|
vmContext.global.INPUT_EVENTS = events;
|
||||||
vmContext.global.INPUT_STATE_EVENT_MAP = stateEventMap;
|
vmContext.global.INPUT_STATE_EVENT_MAP = stateEventMap;
|
||||||
vmContext.global.INPUT_CONFIG = config;
|
vmContext.global.INPUT_CONFIG = config;
|
||||||
|
|
||||||
const hydrogenRenderScriptCode = await readFile(
|
const hydrogenRenderScriptCode = await readFile(
|
||||||
path.resolve(__dirname, './hydrogen-vm-render-script.js'),
|
path.resolve(__dirname, '../shared/hydrogen-vm-render-script.js'),
|
||||||
'utf8'
|
'utf8'
|
||||||
);
|
);
|
||||||
const hydrogenRenderScript = new vm.Script(hydrogenRenderScriptCode, {
|
const hydrogenRenderScript = new vm.Script(hydrogenRenderScriptCode, {
|
||||||
|
|
|
@ -3,6 +3,7 @@ const express = require('express');
|
||||||
const asyncHandler = require('./lib/express-async-handler');
|
const asyncHandler = require('./lib/express-async-handler');
|
||||||
const urlJoin = require('./lib/url-join');
|
const urlJoin = require('./lib/url-join');
|
||||||
|
|
||||||
|
const fetchRoomData = require('./fetch-room-data');
|
||||||
const fetchEventsForTimestamp = require('./fetch-events-for-timestamp');
|
const fetchEventsForTimestamp = require('./fetch-events-for-timestamp');
|
||||||
const renderHydrogenToString = require('./render-hydrogen-to-string');
|
const renderHydrogenToString = require('./render-hydrogen-to-string');
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ assert(basePath);
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
app.get('/style.css', async function (req, res) {
|
app.get('/hydrogen-styles.css', async function (req, res) {
|
||||||
res.set('Content-Type', 'text/css');
|
res.set('Content-Type', 'text/css');
|
||||||
res.sendFile(require.resolve('hydrogen-view-sdk/style.css'));
|
res.sendFile(require.resolve('hydrogen-view-sdk/style.css'));
|
||||||
});
|
});
|
||||||
|
@ -24,6 +25,8 @@ app.get(
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Based off of the Gitter archive routes,
|
||||||
|
// https://gitlab.com/gitterHQ/webapp/-/blob/14954e05c905e8c7cb675efebb89116c07cfaab5/server/handlers/app/archive.js#L190-297
|
||||||
app.get(
|
app.get(
|
||||||
'/:roomIdOrAlias/date/:yyyy(\\d{4})/:mm(\\d{2})/:dd(\\d{2})/:hourRange(\\d\\d?-\\d\\d?)?',
|
'/:roomIdOrAlias/date/:yyyy(\\d{4})/:mm(\\d{2})/:dd(\\d{2})/:hourRange(\\d\\d?-\\d\\d?)?',
|
||||||
asyncHandler(async function (req, res) {
|
asyncHandler(async function (req, res) {
|
||||||
|
@ -77,19 +80,30 @@ app.get(
|
||||||
toTimestamp = Date.UTC(yyyy, mm, dd, toHour);
|
toTimestamp = Date.UTC(yyyy, mm, dd, toHour);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { events, stateEventMap } = await fetchEventsForTimestamp(roomIdOrAlias, fromTimestamp);
|
const [roomData, { events, stateEventMap }] = await Promise.all([
|
||||||
|
fetchRoomData(roomIdOrAlias),
|
||||||
|
fetchEventsForTimestamp(roomIdOrAlias, fromTimestamp),
|
||||||
|
]);
|
||||||
|
|
||||||
const hydrogenHtmlOutput = await renderHydrogenToString(events, stateEventMap);
|
const hydrogenHtmlOutput = await renderHydrogenToString(roomData, events, stateEventMap);
|
||||||
|
|
||||||
const styleUrl = urlJoin(basePath, 'style.css');
|
const hydrogenStylesUrl = urlJoin(basePath, 'hydrogen-styles.css');
|
||||||
|
const stylesUrl = urlJoin(basePath, 'styles.css');
|
||||||
const pageHtml = `
|
const pageHtml = `
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<link href="${styleUrl}" rel="stylesheet">
|
<link href="${hydrogenStylesUrl}" rel="stylesheet">
|
||||||
|
<link href="${stylesUrl}" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div class="room-layout">
|
||||||
${hydrogenHtmlOutput}
|
${hydrogenHtmlOutput}
|
||||||
|
|
||||||
|
<aside class="right-panel">
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
const {
|
||||||
|
TemplateView,
|
||||||
|
RoomView,
|
||||||
|
RightPanelView
|
||||||
|
} = require('hydrogen-view-sdk');
|
||||||
|
|
||||||
|
export class ArchiveView extends TemplateView {
|
||||||
|
render(t, vm) {
|
||||||
|
return t.div({
|
||||||
|
className: {
|
||||||
|
"ArchiveView": true,
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,8 +11,12 @@ const {
|
||||||
encodeEventIdKey,
|
encodeEventIdKey,
|
||||||
Timeline,
|
Timeline,
|
||||||
TimelineView,
|
TimelineView,
|
||||||
|
RoomView,
|
||||||
|
RoomViewModel,
|
||||||
} = require('hydrogen-view-sdk');
|
} = require('hydrogen-view-sdk');
|
||||||
|
|
||||||
|
const roomData = global.INPUT_ROOM_DATA;
|
||||||
|
assert(roomData);
|
||||||
const events = global.INPUT_EVENTS;
|
const events = global.INPUT_EVENTS;
|
||||||
assert(events);
|
assert(events);
|
||||||
const stateEventMap = global.INPUT_STATE_EVENT_MAP;
|
const stateEventMap = global.INPUT_STATE_EVENT_MAP;
|
||||||
|
@ -25,7 +29,6 @@ let eventIndexCounter = 0;
|
||||||
const fragmentIdComparer = new FragmentIdComparer([]);
|
const fragmentIdComparer = new FragmentIdComparer([]);
|
||||||
function makeEventEntryFromEventJson(eventJson, memberEvent) {
|
function makeEventEntryFromEventJson(eventJson, memberEvent) {
|
||||||
assert(eventJson);
|
assert(eventJson);
|
||||||
assert(memberEvent);
|
|
||||||
|
|
||||||
const eventIndex = eventIndexCounter;
|
const eventIndex = eventIndexCounter;
|
||||||
const eventEntry = new EventEntry(
|
const eventEntry = new EventEntry(
|
||||||
|
@ -34,8 +37,8 @@ function makeEventEntryFromEventJson(eventJson, memberEvent) {
|
||||||
eventIndex: eventIndex, // TODO: What should this be?
|
eventIndex: eventIndex, // TODO: What should this be?
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
event: eventJson,
|
event: eventJson,
|
||||||
displayName: memberEvent.content && memberEvent.content.displayname,
|
displayName: memberEvent && memberEvent.content && memberEvent.content.displayname,
|
||||||
avatarUrl: memberEvent.content && memberEvent.content.avatar_url,
|
avatarUrl: memberEvent && memberEvent.content && memberEvent.content.avatar_url,
|
||||||
key: encodeKey(roomId, 0, eventIndex),
|
key: encodeKey(roomId, 0, eventIndex),
|
||||||
eventIdKey: encodeEventIdKey(roomId, eventJson.event_id),
|
eventIdKey: encodeEventIdKey(roomId, eventJson.event_id),
|
||||||
},
|
},
|
||||||
|
@ -64,29 +67,35 @@ async function mountHydrogen() {
|
||||||
//hsApi: this._hsApi
|
//hsApi: this._hsApi
|
||||||
});
|
});
|
||||||
|
|
||||||
const tilesCreator = makeTilesCreator({
|
const mediaRepository = new MediaRepository({
|
||||||
platform,
|
|
||||||
roomVM: {
|
|
||||||
room: {
|
|
||||||
mediaRepository: new MediaRepository({
|
|
||||||
homeserver: config.matrixServerUrl,
|
homeserver: config.matrixServerUrl,
|
||||||
}),
|
});
|
||||||
},
|
|
||||||
},
|
const urlCreator = {
|
||||||
timeline,
|
|
||||||
urlCreator: {
|
|
||||||
urlUntilSegment: () => {
|
urlUntilSegment: () => {
|
||||||
return 'todo';
|
return 'todo';
|
||||||
},
|
},
|
||||||
urlForSegments: () => {
|
urlForSegments: () => {
|
||||||
return 'todo';
|
return 'todo';
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
navigation: {
|
|
||||||
|
const navigation = {
|
||||||
segment: () => {
|
segment: () => {
|
||||||
return 'todo';
|
return 'todo';
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const tilesCreator = makeTilesCreator({
|
||||||
|
platform,
|
||||||
|
roomVM: {
|
||||||
|
room: {
|
||||||
|
mediaRepository,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
timeline,
|
||||||
|
urlCreator,
|
||||||
|
navigation,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Something we can modify with new state updates as we see them
|
// Something we can modify with new state updates as we see them
|
||||||
|
@ -129,7 +138,45 @@ async function mountHydrogen() {
|
||||||
tiles: tiles,
|
tiles: tiles,
|
||||||
};
|
};
|
||||||
|
|
||||||
const view = new TimelineView(timelineViewModel);
|
// const view = new TimelineView(timelineViewModel);
|
||||||
|
|
||||||
|
// const roomViewModel = {
|
||||||
|
// kind: 'room',
|
||||||
|
// timelineViewModel,
|
||||||
|
// composerViewModel: {
|
||||||
|
// kind: 'none',
|
||||||
|
// },
|
||||||
|
// i18n: RoomViewModel.prototype.i18n,
|
||||||
|
|
||||||
|
// id: roomData.id,
|
||||||
|
// name: roomData.name,
|
||||||
|
// avatarUrl(size) {
|
||||||
|
// return getAvatarHttpUrl(roomData.avatarUrl, size, platform, mediaRepository);
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
|
const room = {
|
||||||
|
name: roomData.name,
|
||||||
|
id: roomData.id,
|
||||||
|
avatarUrl: roomData.avatarUrl,
|
||||||
|
avatarColorId: roomData.id,
|
||||||
|
mediaRepository: mediaRepository,
|
||||||
|
};
|
||||||
|
|
||||||
|
const roomViewModel = new RoomViewModel({
|
||||||
|
room,
|
||||||
|
ownUserId: 'xxx',
|
||||||
|
platform,
|
||||||
|
urlCreator,
|
||||||
|
navigation,
|
||||||
|
});
|
||||||
|
|
||||||
|
roomViewModel._timelineVM = timelineViewModel;
|
||||||
|
roomViewModel._composerVM = {
|
||||||
|
kind: 'none',
|
||||||
|
};
|
||||||
|
|
||||||
|
const view = new RoomView(roomViewModel);
|
||||||
|
|
||||||
//console.log('view.mount()', view.mount());
|
//console.log('view.mount()', view.mount());
|
||||||
app.appendChild(view.mount());
|
app.appendChild(view.mount());
|
Loading…
Reference in New Issue