diff --git a/public/css/styles.css b/public/css/styles.css index 4bacd4a..7181239 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -55,6 +55,15 @@ summary { min-width: 0; } +.ArchiveRoomView_mainArea { + grid-area: middle; + display: flex; + flex-direction: column; + width: 100%; + min-width: 0; + min-height: 0; +} + .RoomHeader_actionButton { display: inline-flex; justify-content: center; @@ -75,6 +84,19 @@ summary { display: none; } +.ArchiveRoomView_mainBody { + flex: 1 1; + display: flex; + flex-direction: column; + height: 100%; + min-height: 0; + min-width: 0; +} + +.ArchiveRoomView_mainBody > .Timeline { + flex: 1 0; +} + @media screen and (max-width: 800px) { /* Only the middle needs to be visible mobile by default */ .ArchiveRoomView { @@ -95,7 +117,7 @@ summary { 'right' 1fr / 1fr; } - .ArchiveRoomView.right-shown .middle { + .ArchiveRoomView.right-shown .ArchiveRoomView_mainArea { display: none; } /* And show the button to open the right-panel on mobile */ diff --git a/shared/hydrogen-vm-render-script.js b/shared/hydrogen-vm-render-script.js index d8a2180..2048431 100644 --- a/shared/hydrogen-vm-render-script.js +++ b/shared/hydrogen-vm-render-script.js @@ -11,7 +11,6 @@ const { MediaRepository, createNavigation, createRouter, - tag, RetainedObservableValue, PowerLevels, @@ -22,11 +21,8 @@ const { encodeKey, encodeEventIdKey, Timeline, - ViewModel, - RoomViewModel, } = require('hydrogen-view-sdk'); -const MatrixPublicArchiveURLCreator = require('matrix-public-archive-shared/lib/url-creator'); const ArchiveRoomView = require('matrix-public-archive-shared/views/ArchiveRoomView'); const ArchiveHistory = require('matrix-public-archive-shared/lib/archive-history'); const supressBlankAnchorsReloadingThePage = require('matrix-public-archive-shared/lib/supress-blank-anchors-reloading-the-page'); @@ -52,8 +48,6 @@ assert(config); assert(config.matrixServerUrl); assert(config.basePath); -const matrixPublicArchiveURLCreator = new MatrixPublicArchiveURLCreator(config.basePath); - let txnCount = 0; function getFakeEventId() { txnCount++; @@ -258,93 +252,22 @@ async function mountHydrogen() { tiles, }; - const roomViewModel = new RoomViewModel({ - room, - // This is an arbitrary string (doesn't need to match anything and it shouldn't) - ownUserId: 'xxx-ownUserId', - platform, - urlCreator: urlRouter, - 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); - }; - - roomViewModel.roomDirectoryUrl = matrixPublicArchiveURLCreator.roomDirectoryUrl(); - - Object.defineProperty(roomViewModel, 'timelineViewModel', { - get() { - return timelineViewModel; - }, - }); - const archiveRoomViewModel = new ArchiveRoomViewModel({ // Hydrogen options navigation: navigation, urlCreator: urlRouter, history: archiveHistory, // Our options - roomViewModel, + homeserverUrl: config.matrixServerUrl, + timelineViewModel, room, - fromDate: new Date(fromTimestamp), + // The timestamp from the URL that was originally visited + dayTimestamp: fromTimestamp, eventEntriesByEventId, shouldIndex, basePath: config.basePath, }); - // Create a custom disabled composer view that shows our archive message. - class DisabledArchiveComposerViewModel extends ViewModel { - constructor(options) { - super(options); - - // Whenever the `archiveRoomViewModel.currentTopPositionEventEntry` - // changes, re-render the composer view with the updated date. - archiveRoomViewModel.on('change', (changedProps) => { - if (changedProps === 'currentTopPositionEventEntry') { - this.emitChange(); - } - }); - } - - get kind() { - return 'disabled'; - } - - get description() { - return [ - (/*vm*/) => { - const activeDate = new Date( - // If the date from our `archiveRoomViewModel` is available, use that - archiveRoomViewModel?.currentTopPositionEventEntry?.timestamp || - // Otherwise, use our initial `fromTimestamp` - fromTimestamp - ); - const dateString = activeDate.toISOString().split('T')[0]; - return `You're viewing an archive of events from ${dateString}. Use a `; - }, - tag.a( - { - href: matrixPublicArchiveURLCreator.permalinkForRoom(roomData.id), - rel: 'noopener', - target: '_blank', - }, - ['Matrix client'] - ), - ` to start chatting in this room.`, - ]; - } - } - const disabledArchiveComposerViewModel = new DisabledArchiveComposerViewModel({}); - Object.defineProperty(roomViewModel, 'composerViewModel', { - get() { - return disabledArchiveComposerViewModel; - }, - }); - // --------------------------------------------------------------------- // --------------------------------------------------------------------- diff --git a/shared/viewmodels/ArchiveRoomViewModel.js b/shared/viewmodels/ArchiveRoomViewModel.js index 068be31..d2db2e5 100644 --- a/shared/viewmodels/ArchiveRoomViewModel.js +++ b/shared/viewmodels/ArchiveRoomViewModel.js @@ -9,18 +9,29 @@ const MatrixPublicArchiveURLCreator = require('matrix-public-archive-shared/lib/ const CalendarViewModel = require('matrix-public-archive-shared/viewmodels/CalendarViewModel'); const DeveloperOptionsContentViewModel = require('matrix-public-archive-shared/viewmodels/DeveloperOptionsContentViewModel'); const RightPanelContentView = require('matrix-public-archive-shared/views/RightPanelContentView'); +const AvatarViewModel = require('matrix-public-archive-shared/viewmodels/AvatarViewModel'); class ArchiveRoomViewModel extends ViewModel { constructor(options) { super(options); - const { roomViewModel, room, fromDate, eventEntriesByEventId, shouldIndex, basePath } = options; - assert(roomViewModel); + const { + homeserverUrl, + timelineViewModel, + room, + dayTimestamp, + eventEntriesByEventId, + shouldIndex, + basePath, + } = options; + assert(homeserverUrl); + assert(timelineViewModel); assert(room); - assert(fromDate); + assert(dayTimestamp); assert(shouldIndex !== undefined); assert(eventEntriesByEventId); this._room = room; + this._dayTimestamp = dayTimestamp; this._eventEntriesByEventId = eventEntriesByEventId; this._currentTopPositionEventEntry = null; this._matrixPublicArchiveURLCreator = new MatrixPublicArchiveURLCreator(basePath); @@ -28,11 +39,25 @@ class ArchiveRoomViewModel extends ViewModel { const navigation = this.navigation; const urlCreator = this.urlCreator; + this._roomAvatarViewModel = new AvatarViewModel({ + homeserverUrlToPullMediaFrom: homeserverUrl, + avatarUrl: this._room.avatarUrl, + avatarTitle: this._room.name || this._room.canonicalAlias || this._room.id, + avatarLetterString: + this._room.name || + // Strip the `#` off the alias + this._room.canonicalAlias?.[1] || + // Strip the `!` off the room_id + this._room.id?.[1], + entityId: this._room.id, + }); + + const initialDate = new Date(dayTimestamp); this._calendarViewModel = new CalendarViewModel({ // The day being shown in the archive - activeDate: fromDate, + activeDate: initialDate, // The month displayed in the calendar - calendarDate: fromDate, + calendarDate: initialDate, room, basePath, }); @@ -55,7 +80,7 @@ class ArchiveRoomViewModel extends ViewModel { }) ); - this.roomViewModel = roomViewModel; + this._timelineViewModel = timelineViewModel; // FIXME: Do we have to fake this? this.rightPanelModel = { navigation, @@ -136,6 +161,10 @@ class ArchiveRoomViewModel extends ViewModel { handleLightBoxNavigationChange(initialLightBoxEventId); } + get timelineViewModel() { + return this._timelineViewModel; + } + setShouldShowDeveloperOptions(shouldShowDeveloperOptions) { this._developerOptionsModalViewModel.setOpen(shouldShowDeveloperOptions); } @@ -175,6 +204,33 @@ class ArchiveRoomViewModel extends ViewModel { ) + window.location.hash ); } + + get dayTimestamp() { + return this._dayTimestamp; + } + + get roomDirectoryUrl() { + return this._matrixPublicArchiveURLCreator.roomDirectoryUrl(); + } + + get roomPermalink() { + return this._matrixPublicArchiveURLCreator.permalinkForRoom(this._room.id); + } + + get roomName() { + return this._room.name; + } + + get roomAvatarViewModel() { + return this._roomAvatarViewModel; + } + + openRightPanel() { + 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); + } } module.exports = ArchiveRoomViewModel; diff --git a/shared/views/ArchiveRoomView.js b/shared/views/ArchiveRoomView.js index daf981f..016eef2 100644 --- a/shared/views/ArchiveRoomView.js +++ b/shared/views/ArchiveRoomView.js @@ -3,7 +3,7 @@ const { TemplateView, AvatarView, - RoomView, + TimelineView, RightPanelView, LightboxView, } = require('hydrogen-view-sdk'); @@ -43,8 +43,8 @@ class RoomHeaderView extends TemplateView { ), ] ), - t.view(new AvatarView(vm, 32)), - t.div({ className: 'room-description' }, [t.h2((vm) => vm.name)]), + 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', @@ -82,6 +82,37 @@ class RoomHeaderView extends TemplateView { } } +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 `dayTimestamp` + vm.dayTimestamp + ); + 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 { render(t, vm) { const rootElement = t.div( @@ -112,11 +143,13 @@ class ArchiveRoomView extends TemplateView { }); } ), - t.view( - new RoomView(vm.roomViewModel, customViewClassForTile, { - RoomHeaderView, - }) - ), + 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)), + ]), + ]), t.view(new RightPanelView(vm.rightPanelModel)), t.mapView( (vm) => vm.lightboxViewModel,