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 fetch = require('node-fetch');
|
||||
|
||||
const urlJoin = require('./lib/url-join');
|
||||
const fetchEndpoint = require('./lib/fetch-endpoint');
|
||||
|
||||
const { matrixServerUrl } = require('../config.json');
|
||||
const secrets = require('../secrets.json');
|
||||
|
||||
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;
|
||||
}
|
||||
assert(matrixServerUrl);
|
||||
|
||||
async function fetchEventsForTimestamp(roomId, ts) {
|
||||
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`
|
||||
);
|
||||
const timestampToEventResData = await fetchEndpoint(timestampToEventEndpoint);
|
||||
//console.log('timestampToEventResData', timestampToEventResData);
|
||||
|
||||
const eventIdForTimestamp = timestampToEventResData.event_id;
|
||||
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}`
|
||||
);
|
||||
const messageResData = await fetchEndpoint(messagesEndpoint);
|
||||
//console.log('messageResData', messageResData);
|
||||
|
||||
//console.log('messageResData.state', messageResData.state);
|
||||
const stateEventMap = {};
|
||||
for (const stateEvent of messageResData.state) {
|
||||
for (const stateEvent of messageResData.state || []) {
|
||||
if (stateEvent.type === 'm.room.member') {
|
||||
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');
|
||||
|
||||
async function renderToString(events, stateEventMap) {
|
||||
async function renderToString(roomData, events, stateEventMap) {
|
||||
assert(roomData);
|
||||
assert(events);
|
||||
assert(stateEventMap);
|
||||
|
||||
|
@ -40,12 +41,13 @@ async function renderToString(events, stateEventMap) {
|
|||
// So we can see logs from the underlying vm
|
||||
vmContext.global.console = console;
|
||||
|
||||
vmContext.global.INPUT_ROOM_DATA = roomData;
|
||||
vmContext.global.INPUT_EVENTS = events;
|
||||
vmContext.global.INPUT_STATE_EVENT_MAP = stateEventMap;
|
||||
vmContext.global.INPUT_CONFIG = config;
|
||||
|
||||
const hydrogenRenderScriptCode = await readFile(
|
||||
path.resolve(__dirname, './hydrogen-vm-render-script.js'),
|
||||
path.resolve(__dirname, '../shared/hydrogen-vm-render-script.js'),
|
||||
'utf8'
|
||||
);
|
||||
const hydrogenRenderScript = new vm.Script(hydrogenRenderScriptCode, {
|
||||
|
|
|
@ -3,6 +3,7 @@ const express = require('express');
|
|||
const asyncHandler = require('./lib/express-async-handler');
|
||||
const urlJoin = require('./lib/url-join');
|
||||
|
||||
const fetchRoomData = require('./fetch-room-data');
|
||||
const fetchEventsForTimestamp = require('./fetch-events-for-timestamp');
|
||||
const renderHydrogenToString = require('./render-hydrogen-to-string');
|
||||
|
||||
|
@ -12,7 +13,7 @@ assert(basePath);
|
|||
|
||||
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.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(
|
||||
'/:roomIdOrAlias/date/:yyyy(\\d{4})/:mm(\\d{2})/:dd(\\d{2})/:hourRange(\\d\\d?-\\d\\d?)?',
|
||||
asyncHandler(async function (req, res) {
|
||||
|
@ -77,19 +80,30 @@ app.get(
|
|||
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 = `
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link href="${styleUrl}" rel="stylesheet">
|
||||
<link href="${hydrogenStylesUrl}" rel="stylesheet">
|
||||
<link href="${stylesUrl}" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
${hydrogenHtmlOutput}
|
||||
<div class="room-layout">
|
||||
${hydrogenHtmlOutput}
|
||||
|
||||
<aside class="right-panel">
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</body>
|
||||
</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,
|
||||
Timeline,
|
||||
TimelineView,
|
||||
RoomView,
|
||||
RoomViewModel,
|
||||
} = require('hydrogen-view-sdk');
|
||||
|
||||
const roomData = global.INPUT_ROOM_DATA;
|
||||
assert(roomData);
|
||||
const events = global.INPUT_EVENTS;
|
||||
assert(events);
|
||||
const stateEventMap = global.INPUT_STATE_EVENT_MAP;
|
||||
|
@ -25,7 +29,6 @@ let eventIndexCounter = 0;
|
|||
const fragmentIdComparer = new FragmentIdComparer([]);
|
||||
function makeEventEntryFromEventJson(eventJson, memberEvent) {
|
||||
assert(eventJson);
|
||||
assert(memberEvent);
|
||||
|
||||
const eventIndex = eventIndexCounter;
|
||||
const eventEntry = new EventEntry(
|
||||
|
@ -34,8 +37,8 @@ function makeEventEntryFromEventJson(eventJson, memberEvent) {
|
|||
eventIndex: eventIndex, // TODO: What should this be?
|
||||
roomId: roomId,
|
||||
event: eventJson,
|
||||
displayName: memberEvent.content && memberEvent.content.displayname,
|
||||
avatarUrl: memberEvent.content && memberEvent.content.avatar_url,
|
||||
displayName: memberEvent && memberEvent.content && memberEvent.content.displayname,
|
||||
avatarUrl: memberEvent && memberEvent.content && memberEvent.content.avatar_url,
|
||||
key: encodeKey(roomId, 0, eventIndex),
|
||||
eventIdKey: encodeEventIdKey(roomId, eventJson.event_id),
|
||||
},
|
||||
|
@ -64,29 +67,35 @@ async function mountHydrogen() {
|
|||
//hsApi: this._hsApi
|
||||
});
|
||||
|
||||
const mediaRepository = new MediaRepository({
|
||||
homeserver: config.matrixServerUrl,
|
||||
});
|
||||
|
||||
const urlCreator = {
|
||||
urlUntilSegment: () => {
|
||||
return 'todo';
|
||||
},
|
||||
urlForSegments: () => {
|
||||
return 'todo';
|
||||
},
|
||||
};
|
||||
|
||||
const navigation = {
|
||||
segment: () => {
|
||||
return 'todo';
|
||||
},
|
||||
};
|
||||
|
||||
const tilesCreator = makeTilesCreator({
|
||||
platform,
|
||||
roomVM: {
|
||||
room: {
|
||||
mediaRepository: new MediaRepository({
|
||||
homeserver: config.matrixServerUrl,
|
||||
}),
|
||||
mediaRepository,
|
||||
},
|
||||
},
|
||||
timeline,
|
||||
urlCreator: {
|
||||
urlUntilSegment: () => {
|
||||
return 'todo';
|
||||
},
|
||||
urlForSegments: () => {
|
||||
return 'todo';
|
||||
},
|
||||
},
|
||||
navigation: {
|
||||
segment: () => {
|
||||
return 'todo';
|
||||
},
|
||||
},
|
||||
urlCreator,
|
||||
navigation,
|
||||
});
|
||||
|
||||
// Something we can modify with new state updates as we see them
|
||||
|
@ -129,7 +138,45 @@ async function mountHydrogen() {
|
|||
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());
|
||||
app.appendChild(view.mount());
|
Loading…
Reference in New Issue