Only display rooms in the sliding window

Buggy: some rooms disappear entirely for some reason.
This commit is contained in:
Kegan Dougal 2021-11-30 18:07:08 +00:00
parent 4f7468a95a
commit 0b2d09b796
5 changed files with 60 additions and 5 deletions

View file

@ -41,6 +41,7 @@ export class SessionViewModel extends ViewModel {
invites: this._sessionContainer.session.invites, invites: this._sessionContainer.session.invites,
rooms: this._sessionContainer.session.rooms, rooms: this._sessionContainer.session.rooms,
compareFn: this._sessionContainer.sync.compare.bind(this._sessionContainer.sync), compareFn: this._sessionContainer.sync.compare.bind(this._sessionContainer.sync),
includeRoomFn: this._sessionContainer.sync.includeRoom.bind(this._sessionContainer.sync),
}))); })));
this._settingsViewModel = null; this._settingsViewModel = null;
this._roomViewModelObservable = null; this._roomViewModelObservable = null;

View file

@ -19,6 +19,7 @@ import {ViewModel} from "../../ViewModel.js";
import {RoomTileViewModel} from "./RoomTileViewModel.js"; import {RoomTileViewModel} from "./RoomTileViewModel.js";
import {InviteTileViewModel} from "./InviteTileViewModel.js"; import {InviteTileViewModel} from "./InviteTileViewModel.js";
import {RoomFilter} from "./RoomFilter.js"; import {RoomFilter} from "./RoomFilter.js";
import {FilteredMap} from "../../../observable/map/FilteredMap.js";
import {ApplyMap} from "../../../observable/map/ApplyMap.js"; import {ApplyMap} from "../../../observable/map/ApplyMap.js";
import {addPanelIfNeeded} from "../../navigation/index.js"; import {addPanelIfNeeded} from "../../navigation/index.js";
import { PlaceholderRoomTileViewModel } from "./PlaceholderRoomTileViewModel.js"; import { PlaceholderRoomTileViewModel } from "./PlaceholderRoomTileViewModel.js";
@ -26,9 +27,13 @@ import { PlaceholderRoomTileViewModel } from "./PlaceholderRoomTileViewModel.js"
export class LeftPanelViewModel extends ViewModel { export class LeftPanelViewModel extends ViewModel {
constructor(options) { constructor(options) {
super(options); super(options);
const {rooms, invites, compareFn} = options; const {rooms, invites, compareFn, includeRoomFn} = options;
this._tileViewModelsMap = this._mapTileViewModels(rooms, invites); this._tileViewModelsMap = this._mapTileViewModels(rooms, invites);
this._tileViewModelsFilterMap = new ApplyMap(this._tileViewModelsMap); this._tileViewModelsSlidingWindow = new FilteredMap(this._tileViewModelsMap, (r, roomId) => {
const include = includeRoomFn(roomId);
return include;
});
this._tileViewModelsFilterMap = new ApplyMap(this._tileViewModelsSlidingWindow);
this._tileViewModels = this._tileViewModelsFilterMap.sortValues((a, b) => a.compare(b)); this._tileViewModels = this._tileViewModelsFilterMap.sortValues((a, b) => a.compare(b));
this._currentTileVM = null; this._currentTileVM = null;
this._setupNavigation(); this._setupNavigation();

View file

@ -128,7 +128,7 @@ export class Sync3 {
this.status = new ObservableValue(SyncStatus.Stopped); this.status = new ObservableValue(SyncStatus.Stopped);
this.error = null; this.error = null;
// Hydrogen only has 1 list currently (no DM section) so we only need 1 range // Hydrogen only has 1 list currently (no DM section) so we only need 1 range
this.ranges = [[0, 49]]; this.ranges = [[0, 4]];
this.roomIndexToRoomId = {}; this.roomIndexToRoomId = {};
this.roomIdToRoomIndex = {}; this.roomIdToRoomIndex = {};
} }
@ -155,7 +155,11 @@ export class Sync3 {
} }
} }
compare(roomIdA: string, roomIdB: string) { includeRoom(roomId: string): boolean {
return this.roomIdToRoomIndex[roomId] !== undefined;
}
compare(roomIdA: string, roomIdB: string): number {
if (roomIdA === roomIdB) { if (roomIdA === roomIdB) {
return 0; return 0;
} }
@ -325,6 +329,9 @@ export class Sync3 {
await syncTxn.complete(log); await syncTxn.complete(log);
// Sync v3 specific // Sync v3 specific
// work out which rooms are no longer being tracked (they'll be deleted in indexToRoom but exist in this.roomIndexToRoomId)
// and then force update those rooms to force the FilteredMap to re-evalute to remove them from the left panel room list
const deletedRoomIDs = deletedElements(Object.values(this.roomIndexToRoomId), Object.values(indexToRoom));
// atomically move all the rooms to their new positions // atomically move all the rooms to their new positions
// We need to do this BEFORE calling afterSync as that causes the room list to be sorted // We need to do this BEFORE calling afterSync as that causes the room list to be sorted
// which eventually causes Sync3.compare to be called, so we need it to be using the latest // which eventually causes Sync3.compare to be called, so we need it to be using the latest
@ -335,6 +342,17 @@ export class Sync3 {
const index = Number(indexStr); const index = Number(indexStr);
this.roomIdToRoomIndex[indexToRoom[index]] = index; this.roomIdToRoomIndex[indexToRoom[index]] = index;
}); });
if (deletedRoomIDs.length > 0) {
console.log("DELETED ", deletedRoomIDs);
}
// now force update rooms which fell off the sliding window
deletedRoomIDs.forEach((roomId) => {
let room = this.session.rooms.get(roomId);
room.forceRefresh();
})
// END sync v3 specific
// update in-memory structs // update in-memory structs
// this.session.afterSync(); // ??? // this.session.afterSync(); // ???
@ -528,6 +546,19 @@ const indexInRange = (ranges: number[][], i: number) => {
return isInRange; return isInRange;
}; };
// returns the elements which exist in old but not in new
const deletedElements = (oldArr: string[], newArr: string[]): string[] => {
let set = {};
oldArr.forEach((k) => {
set[k] = true;
});
newArr.forEach((k) => {
delete set[k];
})
return Object.keys(set);
}
export function tests() { export function tests() {
return { return {
"processSyncOps": assert => { "processSyncOps": assert => {

View file

@ -76,6 +76,11 @@ export class BaseRoom extends EventEmitter {
return retryTimelineEntries; return retryTimelineEntries;
} }
// Forcibly update this room in collections
forceRefresh() {
this._emitUpdate();
}
/** /**
* Used for retrying decryption from other sources than sync, like key backup. * Used for retrying decryption from other sources than sync, like key backup.
* @internal * @internal

View file

@ -63,7 +63,20 @@ export class SortedMapList extends BaseObservableList {
onRemove(key, value) { onRemove(key, value) {
const pair = {key, value}; const pair = {key, value};
const idx = sortedIndex(this._sortedPairs, pair, this._comparator); // Don't call sortedIndex as it does a binary search for the removed item.
// Whilst that is faster than the O(n) search we're doing here, it's not valid to compare
// removed items as the system may have no ability to compare them at this point.
let idx = -1;
for (let i = 0; i < this._sortedPairs.length; i++) {
if (this._sortedPairs[i].key === key) {
idx = i;
break;
}
}
if (idx === -1) {
return;
}
console.log("removing ", key, idx, value);
// assert key === this._sortedPairs[idx].key; // assert key === this._sortedPairs[idx].key;
this._sortedPairs.splice(idx, 1); this._sortedPairs.splice(idx, 1);
this.emitRemove(idx, value); this.emitRemove(idx, value);