diff --git a/public/styles/styles.css b/public/styles/styles.css index d7eede9..9bed638 100644 --- a/public/styles/styles.css +++ b/public/styles/styles.css @@ -29,9 +29,50 @@ body { min-width: 0; } +/* No need to open the right-panel when it's always visible at desktop widths */ +.room-header-change-dates-button { + display: none; + color: var(--icon-color--darker-20); +} +/* No need to close the right-panel when it's always visible at desktop widths */ +.RightPanelView_buttons .close { + display: none; +} + +@media screen and (max-width: 800px) { + /* Only the middle needs to be visible mobile by default */ + .ArchiveView { + grid-template: + 'status' auto + 'middle' 1fr / + 1fr; + } + /* Which also means hiding the right-panel by default on mobile */ + .ArchiveView:not(.right-shown) .RightPanelView { + display: none; + } + + /* When the user opens the right-panel, show it */ + .ArchiveView.right-shown { + grid-template: + 'status' auto + 'right' 1fr / + 1fr; + } + .ArchiveView.right-shown .middle { + display: none; + } + /* And show the button to open the right-panel on mobile */ + .room-header-change-dates-button { + display: block; + } + /* And show the button to close the right-panel on mobile */ + .RightPanelView_buttons .close { + display: block; + } +} + .CalendarView { - max-width: 280px; - font: 100% system-ui; } .CalendarView_header { @@ -132,7 +173,8 @@ body { .CalendarView_dayLink { display: inline-block; width: 100%; - padding: 2px 5px; + padding-top: 18%; + padding-bottom: 18%; text-decoration: none; } @@ -154,7 +196,6 @@ body { opacity: 0.5; } - /* Error pages */ .heading-sub-detail { @@ -162,7 +203,6 @@ body { } .tracing-span-list { - } .tracing-span-list-item { @@ -170,7 +210,6 @@ body { } .tracing-span-item-http-details { - } .tracing-span-item-sub-details { diff --git a/server/routes/install-routes.js b/server/routes/install-routes.js index 22fa237..92d122c 100644 --- a/server/routes/install-routes.js +++ b/server/routes/install-routes.js @@ -209,6 +209,7 @@ function installRoutes(app) { + ${sanitizeHtml(`${roomData.name} - Matrix Public Archive`)} diff --git a/server/routes/timeout-middleware.js b/server/routes/timeout-middleware.js index 4fb0fa9..4905252 100644 --- a/server/routes/timeout-middleware.js +++ b/server/routes/timeout-middleware.js @@ -55,6 +55,7 @@ async function timeoutMiddleware(req, res, next) { + Server timeout - Matrix Public Archive diff --git a/shared/4-hydrogen-vm-render-script.js b/shared/4-hydrogen-vm-render-script.js index e134081..ae188d0 100644 --- a/shared/4-hydrogen-vm-render-script.js +++ b/shared/4-hydrogen-vm-render-script.js @@ -234,6 +234,13 @@ async function mountHydrogen() { navigation, }); + roomViewModel.openRightPanel = function () { + let path = this.navigation.path.until('room'); + path = path.with(this.navigation.segment('right-panel', true)); + path = path.with(this.navigation.segment('change-dates', true)); + this.navigation.applyPath(path); + }; + // FIXME: We shouldn't have to dive into the internal fields to make this work roomViewModel._timelineVM = timelineViewModel; roomViewModel._composerVM = { @@ -292,6 +299,7 @@ async function mountHydrogen() { class ArchiveViewModel extends ViewModel { roomViewModel = roomViewModel; rightPanelModel = { + navigation, activeViewModel: { type: 'custom', customView: RightPanelContentView, @@ -302,15 +310,27 @@ async function mountHydrogen() { calendarDate: fromDate, }), }, + closePanel() { + const path = this.navigation.path.until('room'); + this.navigation.applyPath(path); + }, }; + get shouldShowRightPanel() { + return this._shouldShowRightPanel; + } + constructor(options) { super(options); this.#setupNavigation(); + this._updateRightPanel(); } #setupNavigation() { + const rightpanel = this.navigation.observe('right-panel'); + this.track(rightpanel.subscribe(() => this._updateRightPanel())); + setupLightboxNavigation(this, 'lightboxViewModel', (eventId) => { return { room, @@ -318,6 +338,11 @@ async function mountHydrogen() { }; }); } + + _updateRightPanel() { + this._shouldShowRightPanel = !!this.navigation.path.get('right-panel')?.value; + this.emitChange('shouldShowRightPanel'); + } } const archiveViewModel = new ArchiveViewModel({ diff --git a/shared/ArchiveView.js b/shared/ArchiveView.js index 343cafa..46c7590 100644 --- a/shared/ArchiveView.js +++ b/shared/ArchiveView.js @@ -2,22 +2,70 @@ const { TemplateView, + AvatarView, RoomView, RightPanelView, LightboxView, viewClassForTile, } = require('hydrogen-view-sdk'); +class RoomHeaderView extends TemplateView { + render(t, vm) { + return t.div({ className: 'RoomHeader middle-header' }, [ + t.view(new AvatarView(vm, 32)), + t.div({ className: 'room-description' }, [t.h2((vm) => vm.name)]), + t.button( + { + className: 'button-utility room-header-change-dates-button', + 'aria-label': vm.i18n`Change dates`, + onClick: (/*evt*/) => { + 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', + style: 'vertical-align: middle;', + }, + [ + 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 ArchiveView extends TemplateView { render(t, vm) { return t.div( { className: { ArchiveView: true, + 'right-shown': (vm) => vm.shouldShowRightPanel, }, }, [ - t.view(new RoomView(vm.roomViewModel, viewClassForTile)), + t.view( + new RoomView(vm.roomViewModel, viewClassForTile, { + RoomHeaderView, + }) + ), t.view(new RightPanelView(vm.rightPanelModel)), t.mapView( (vm) => vm.lightboxViewModel, diff --git a/shared/CalendarView.js b/shared/CalendarView.js index 5f0620a..98cc06f 100644 --- a/shared/CalendarView.js +++ b/shared/CalendarView.js @@ -66,6 +66,7 @@ class CalendarView extends TemplateView { month: 'long', timeZone: 'UTC', }), + // Dropdown arrow t.svg( { xmlns: 'http://www.w3.org/2000/svg', diff --git a/shared/lib/archive-history.js b/shared/lib/archive-history.js index 35d0a30..39ea817 100644 --- a/shared/lib/archive-history.js +++ b/shared/lib/archive-history.js @@ -27,10 +27,24 @@ class ArchiveHistory extends History { // downstream call of `urlRouter.attach()` which we do when bootstraping // everything. if (window.history) { - super.replaceUrlSilently(url); + let replacingUrl = url; + // This is a way to make sure the hash gets cleared out + if (url === '') { + replacingUrl = window.location.pathname; + } + super.replaceUrlSilently(replacingUrl); } } + pushUrlSilently(url) { + let replacingUrl = url; + // This is a way to make sure the hash gets cleared out + if (url === '') { + replacingUrl = window.location.pathname; + } + super.pushUrlSilently(replacingUrl); + } + // Make the URLs we use in the UI of the app relative to the room: // Before: #/session/123/room/!HBehERstyQBxyJDLfR:my.synapse.server/lightbox/$17cgP6YBP9ny9xuU1vBmpOYFhRG4zpOe9SOgWi2Wxsk // After: #/lightbox/$17cgP6YBP9ny9xuU1vBmpOYFhRG4zpOe9SOgWi2Wxsk