diff --git a/src/domain/session/SessionViewModel.js b/src/domain/session/SessionViewModel.js index 6aee0d8d..5c405a3f 100644 --- a/src/domain/session/SessionViewModel.js +++ b/src/domain/session/SessionViewModel.js @@ -39,7 +39,8 @@ export class SessionViewModel extends ViewModel { }))); this._leftPanelViewModel = this.track(new LeftPanelViewModel(this.childOptions({ invites: this._sessionContainer.session.invites, - rooms: this._sessionContainer.session.rooms + rooms: this._sessionContainer.session.rooms, + compareFn: this._sessionContainer.sync.compare.bind(this._sessionContainer.sync), }))); this._settingsViewModel = null; this._roomViewModelObservable = null; diff --git a/src/domain/session/leftpanel/LeftPanelViewModel.js b/src/domain/session/leftpanel/LeftPanelViewModel.js index f38471ac..036efb27 100644 --- a/src/domain/session/leftpanel/LeftPanelViewModel.js +++ b/src/domain/session/leftpanel/LeftPanelViewModel.js @@ -26,7 +26,7 @@ import { PlaceholderRoomTileViewModel } from "./PlaceholderRoomTileViewModel.js" export class LeftPanelViewModel extends ViewModel { constructor(options) { super(options); - const {rooms, invites} = options; + const {rooms, invites, compareFn} = options; this._tileViewModelsMap = this._mapTileViewModels(rooms, invites); this._tileViewModelsFilterMap = new ApplyMap(this._tileViewModelsMap); this._tileViewModels = this._tileViewModelsFilterMap.sortValues((a, b) => a.compare(b)); @@ -34,6 +34,7 @@ export class LeftPanelViewModel extends ViewModel { this._setupNavigation(); this._closeUrl = this.urlCreator.urlForSegment("session"); this._settingsUrl = this.urlCreator.urlForSegment("settings"); + this._compareFn = compareFn; } _mapTileViewModels(rooms, invites) { @@ -45,7 +46,11 @@ export class LeftPanelViewModel extends ViewModel { } else if (roomOrInvite.isPlaceholder) { vm = new PlaceholderRoomTileViewModel(this.childOptions({room: roomOrInvite, emitChange})); } else { - vm = new RoomTileViewModel(this.childOptions({room: roomOrInvite, emitChange})); + vm = new RoomTileViewModel(this.childOptions({ + room: roomOrInvite, + compareFn: this._compareFn, + emitChange, + })); } const isOpen = this.navigation.path.get("room")?.value === roomOrInvite.id; if (isOpen) { diff --git a/src/domain/session/leftpanel/RoomTileViewModel.js b/src/domain/session/leftpanel/RoomTileViewModel.js index db380f5d..f35619e2 100644 --- a/src/domain/session/leftpanel/RoomTileViewModel.js +++ b/src/domain/session/leftpanel/RoomTileViewModel.js @@ -20,9 +20,10 @@ import {BaseTileViewModel} from "./BaseTileViewModel.js"; export class RoomTileViewModel extends BaseTileViewModel { constructor(options) { super(options); - const {room} = options; + const {room, compareFn} = options; this._room = room; this._url = this.urlCreator.openRoomActionUrl(this._room.id); + this._compareFn = compareFn; } get kind() { @@ -34,6 +35,8 @@ export class RoomTileViewModel extends BaseTileViewModel { } compare(other) { + return this._compareFn(this._room.id, other._room.id); + const parentComparison = super.compare(other); if (parentComparison !== 0) { return parentComparison; diff --git a/src/matrix/Sync3.ts b/src/matrix/Sync3.ts index 4e6a90dd..21e3e9f7 100644 --- a/src/matrix/Sync3.ts +++ b/src/matrix/Sync3.ts @@ -97,8 +97,11 @@ interface RoomResponse { }; type IndexToRoomId = { - [index: string]: string; + [index: number]: string; }; +type RoomIdToIndex = { + [roomId: string]: number; +} export class Sync3 { private hsApi: HomeServerApi; @@ -110,6 +113,7 @@ export class Sync3 { // sync v3 specific: contains the sliding window ranges to request private ranges: number[][]; private roomIndexToRoomId: IndexToRoomId; + private roomIdToRoomIndex: RoomIdToIndex; public error?: any; public status: ObservableValue; @@ -126,6 +130,7 @@ export class Sync3 { // Hydrogen only has 1 list currently (no DM section) so we only need 1 range this.ranges = [[0, 99]]; this.roomIndexToRoomId = {}; + this.roomIdToRoomIndex = {}; console.log("session", session); console.log("storage", storage); } @@ -152,6 +157,21 @@ export class Sync3 { } } + compare(roomIdA: string, roomIdB: string) { + if (roomIdA === roomIdB) { + return 0; + } + const indexA = this.roomIdToRoomIndex[roomIdA]; + const indexB = this.roomIdToRoomIndex[roomIdB]; + if (indexA === undefined || indexB === undefined) { + console.error("sync3 cannot compare: missing indices for rooms", roomIdA, roomIdB, indexA, indexB); + } + if (indexA < indexB) { + return -1; + } + return 1; + } + // The purpose of this function is to do repeated /sync calls and call processResponse. It doesn't // know or care how to handle the response, it only cares about the position and retries. private async syncLoop(pos?: number) { @@ -298,6 +318,14 @@ export class Sync3 { } await syncTxn.complete(log); + // atomically move all the rooms to their new positions + this.roomIndexToRoomId = indexToRoom; + this.roomIdToRoomIndex = {}; + Object.keys(indexToRoom).forEach((indexStr) => { + const index = Number(indexStr); + this.roomIdToRoomIndex[indexToRoom[index]] = index; + }); + // update in-memory structs // this.session.afterSync(); // ??? @@ -317,10 +345,6 @@ export class Sync3 { } }), []); }); - - - // instantly move all the rooms to their new positions - this.roomIndexToRoomId = indexToRoom; } // The purpose of this function is to process the response `ops` array by modifying the current diff --git a/src/placeholder-rooms.html b/src/placeholder-rooms.html index b97f6031..8056df73 100644 --- a/src/placeholder-rooms.html +++ b/src/placeholder-rooms.html @@ -104,6 +104,7 @@ navigation: navigation, urlCreator: urlRouter, platform: platform, + compareFn: syncer.compare.bind(syncer), }); leftPanel.loadRoomRange = async (range) => { // pretend to load something