218 lines
8.1 KiB
JavaScript
218 lines
8.1 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
TemplateView,
|
|
AvatarView,
|
|
TimelineView,
|
|
RightPanelView,
|
|
LightboxView,
|
|
} = require('hydrogen-view-sdk');
|
|
|
|
const {
|
|
customViewClassForTile,
|
|
} = require('matrix-public-archive-shared/lib/custom-tile-utilities');
|
|
|
|
const DeveloperOptionsContentView = require('matrix-public-archive-shared/views/DeveloperOptionsContentView');
|
|
const ModalView = require('matrix-public-archive-shared/views/ModalView');
|
|
|
|
class RoomHeaderView extends TemplateView {
|
|
render(t, vm) {
|
|
return t.div({ className: 'RoomHeader middle-header' }, [
|
|
t.a(
|
|
{
|
|
className: 'button-utility RoomHeader_actionButton',
|
|
href: vm.roomDirectoryUrl,
|
|
title: vm.i18n`Go back to the room directory`,
|
|
},
|
|
[
|
|
// Home icon from Element
|
|
t.svg(
|
|
{
|
|
xmlns: 'http://www.w3.org/2000/svg',
|
|
width: '16',
|
|
height: '16',
|
|
viewBox: '0 0 24 24',
|
|
fill: 'currentColor',
|
|
'aria-hidden': 'true',
|
|
},
|
|
[
|
|
t.path({
|
|
d: 'M20.2804 7.90031L13.2804 2.06697C12.5387 1.4489 11.4613 1.4489 10.7196 2.06698L3.71963 7.90031C3.26365 8.28029 3 8.84319 3 9.43675V20.5C3 21.6046 3.89543 22.5 5 22.5H7C8.10457 22.5 9 21.6046 9 20.5V16C9 14.8954 9.89543 14 11 14H13C14.1046 14 15 14.8954 15 16V20.5C15 21.6046 15.8954 22.5 17 22.5H19C20.1046 22.5 21 21.6046 21 20.5V9.43675C21 8.84319 20.7364 8.28029 20.2804 7.90031Z',
|
|
}),
|
|
]
|
|
),
|
|
]
|
|
),
|
|
t.view(new AvatarView(vm.roomAvatarViewModel, 32)),
|
|
t.div({ className: 'room-description' }, [t.h2((vm) => vm.roomName)]),
|
|
t.button(
|
|
{
|
|
className: 'button-utility RoomHeader_actionButton RoomHeader_changeDatesButton',
|
|
title: vm.i18n`Change dates`,
|
|
onClick: (/*event*/) => {
|
|
vm.openRightPanel();
|
|
},
|
|
},
|
|
[
|
|
// Calendar icon (via `calendar2-date` from Bootstrap)
|
|
t.svg(
|
|
{
|
|
xmlns: 'http://www.w3.org/2000/svg',
|
|
width: '16',
|
|
height: '16',
|
|
viewBox: '0 0 16 16',
|
|
fill: 'currentColor',
|
|
'aria-hidden': 'true',
|
|
},
|
|
[
|
|
t.path({
|
|
d: 'M6.445 12.688V7.354h-.633A12.6 12.6 0 0 0 4.5 8.16v.695c.375-.257.969-.62 1.258-.777h.012v4.61h.675zm1.188-1.305c.047.64.594 1.406 1.703 1.406 1.258 0 2-1.066 2-2.871 0-1.934-.781-2.668-1.953-2.668-.926 0-1.797.672-1.797 1.809 0 1.16.824 1.77 1.676 1.77.746 0 1.23-.376 1.383-.79h.027c-.004 1.316-.461 2.164-1.305 2.164-.664 0-1.008-.45-1.05-.82h-.684zm2.953-2.317c0 .696-.559 1.18-1.184 1.18-.601 0-1.144-.383-1.144-1.2 0-.823.582-1.21 1.168-1.21.633 0 1.16.398 1.16 1.23z',
|
|
}),
|
|
t.path({
|
|
d: 'M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM2 2a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H2z',
|
|
}),
|
|
t.path({
|
|
d: 'M2.5 4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5H3a.5.5 0 0 1-.5-.5V4z',
|
|
}),
|
|
]
|
|
),
|
|
]
|
|
),
|
|
]);
|
|
}
|
|
}
|
|
|
|
class DisabledComposerView extends TemplateView {
|
|
render(t /*, vm*/) {
|
|
return t.div({ className: 'DisabledComposerView' }, [
|
|
t.h3([
|
|
t.map(
|
|
(vm) => vm.currentTopPositionEventEntry,
|
|
(_currentTopPositionEventEntry, t, vm) => {
|
|
const activeDate = new Date(
|
|
// If the date from our `archiveRoomViewModel` is available, use that
|
|
vm?.currentTopPositionEventEntry?.timestamp ||
|
|
// Otherwise, use our initial `dayTimestampTo`
|
|
vm.dayTimestampTo
|
|
);
|
|
const dateString = activeDate.toISOString().split('T')[0];
|
|
return t.span(`You're viewing an archive of events from ${dateString}. Use a `);
|
|
}
|
|
),
|
|
t.a(
|
|
{
|
|
href: (vm) => vm.roomPermalink,
|
|
rel: 'noopener',
|
|
target: '_blank',
|
|
},
|
|
['Matrix client']
|
|
),
|
|
` to start chatting in this room.`,
|
|
]),
|
|
]);
|
|
}
|
|
}
|
|
|
|
class ArchiveRoomView extends TemplateView {
|
|
constructor(vm) {
|
|
super(vm);
|
|
|
|
// Keep track of the `IntersectionObserver` so we can disconnect it when necessary
|
|
this._interSectionObserverForUpdatedTopPositionEventEntry = null;
|
|
}
|
|
|
|
unmount() {
|
|
if (this._interSectionObserverForUpdatedTopPositionEventEntry) {
|
|
this._interSectionObserverForUpdatedTopPositionEventEntry.disconnect();
|
|
}
|
|
}
|
|
|
|
render(t, vm) {
|
|
const rootElement = t.div(
|
|
{
|
|
className: {
|
|
ArchiveRoomView: true,
|
|
'right-shown': (vm) => vm.shouldShowRightPanel,
|
|
},
|
|
},
|
|
[
|
|
// The red border and yellow background trail around the event that is
|
|
// driving the active date as you scroll around.
|
|
t.if(
|
|
(vm) => vm._developerOptionsContentViewModel?.debugActiveDateIntersectionObserver,
|
|
(t /*, vm*/) => {
|
|
return t.style({}, (vm) => {
|
|
return `
|
|
[data-event-id] {
|
|
transition: background-color 800ms;
|
|
}
|
|
[data-event-id="${vm.currentTopPositionEventEntry?.id}"] {
|
|
background-color: #ffff8a;
|
|
outline: 1px solid #f00;
|
|
outline-offset: -1px;
|
|
transition: background-color 0ms;
|
|
}
|
|
`;
|
|
});
|
|
}
|
|
),
|
|
t.main({ className: 'ArchiveRoomView_mainArea' }, [
|
|
t.view(new RoomHeaderView(vm)),
|
|
t.main({ className: 'ArchiveRoomView_mainBody' }, [
|
|
t.view(new TimelineView(vm.timelineViewModel, customViewClassForTile)),
|
|
t.view(new DisabledComposerView(vm)),
|
|
]),
|
|
]),
|
|
// We can't just conditionally render the right-panel with `t.ifView(...)` based
|
|
// on `shouldShowRightPanel` because the right-panel being "hidden" only applies
|
|
// to the mobile break points and is always shown on desktop.
|
|
t.view(new RightPanelView(vm.rightPanelModel)),
|
|
t.mapView(
|
|
(vm) => vm.lightboxViewModel,
|
|
(lightboxViewModel) => (lightboxViewModel ? new LightboxView(lightboxViewModel) : null)
|
|
),
|
|
t.view(new ModalView(DeveloperOptionsContentView, vm.developerOptionsModalViewModel)),
|
|
]
|
|
);
|
|
|
|
// Avoid an error when server-side rendering (SSR) because it doesn't have all the
|
|
// DOM API's available (and doesn't need it for this case)
|
|
if (typeof IntersectionObserver === 'function') {
|
|
const scrollRoot = rootElement.querySelector('.Timeline_scroller');
|
|
this._interSectionObserverForUpdatedTopPositionEventEntry = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting) {
|
|
const eventId = entry.target.getAttribute('data-event-id');
|
|
const eventEntry = vm.eventEntriesByEventId[eventId];
|
|
vm.setCurrentTopPositionEventEntry(eventEntry);
|
|
}
|
|
});
|
|
},
|
|
{
|
|
root: scrollRoot,
|
|
// Select the current active day from the top-edge of the scroll viewport.
|
|
//
|
|
// This is a trick that pushes the bottom margin up to the top of the
|
|
// root so there is just a 0px region at the top to detect
|
|
// intersections. This way we always recognize the element at the top.
|
|
// As mentioned in:
|
|
// - https://stackoverflow.com/a/54874286/796832
|
|
// - https://css-tricks.com/an-explanation-of-how-the-intersection-observer-watches/#aa-creating-a-position-sticky-event
|
|
//
|
|
// The format is the same as margin: top, left, bottom, right.
|
|
rootMargin: '0px 0px -100% 0px',
|
|
threshold: 0,
|
|
}
|
|
);
|
|
[...scrollRoot.querySelectorAll(`:scope > ul > [data-event-id]`)].forEach((el) => {
|
|
this._interSectionObserverForUpdatedTopPositionEventEntry.observe(el);
|
|
});
|
|
}
|
|
|
|
return rootElement;
|
|
}
|
|
}
|
|
|
|
module.exports = ArchiveRoomView;
|