Get placeholder->room logic working

Feels somewhat hacky, but it mostly works with the caveats:
- Room names / avatars bleed between rooms when there are updates.
- Clicking on a room doesn't immediately highlight it on the list
This commit is contained in:
Kegan Dougal 2021-12-07 12:48:37 +00:00
parent cc69fc099d
commit 1b6d9db7cd
6 changed files with 52 additions and 11 deletions

View file

@ -33,8 +33,7 @@ export class LeftPanelViewModel extends ViewModel {
this._sync = sync; this._sync = sync;
const sync3List = new Sync3ObservableList(sync, rooms); const sync3List = new Sync3ObservableList(sync, rooms);
const list = new ConcatList(invites.sortValues((a,b) => a.compare(b)), sync3List); const list = new ConcatList(invites.sortValues((a,b) => a.compare(b)), sync3List);
this._tileViewModelsMap = this._mapTileViewModels(list); this._tileViewModels = this._mapTileViewModels(list);
this._tileViewModels = this._tileViewModelsMap;
this._currentTileVM = null; this._currentTileVM = null;
this._setupNavigation(); this._setupNavigation();
this._closeUrl = this.urlCreator.urlForSegment("session"); this._closeUrl = this.urlCreator.urlForSegment("session");
@ -106,8 +105,21 @@ export class LeftPanelViewModel extends ViewModel {
this._currentTileVM?.close(); this._currentTileVM?.close();
this._currentTileVM = null; this._currentTileVM = null;
if (roomId) { if (roomId) {
this._currentTileVM = this._tileViewModelsMap.get(roomId); // find the vm for the room. Previously we used a map to do this but sync3 only gives
this._currentTileVM?.open(); // us a list. We could've re-mapped things in the observable pipeline but we don't need
// these values to be kept up-to-date when a O(n) search on click isn't particularly
// expensive.
let targetVM;
for ( let vm of this._tileViewModels ) {
if (vm.id === roomId) {
targetVM = vm;
break;
}
}
if (targetVM) {
this._currentTileVM = targetVM;
this._currentTileVM?.open();
}
} }
} }

View file

@ -34,6 +34,10 @@ export class RoomTileViewModel extends BaseTileViewModel {
return this._url; return this._url;
} }
get id() {
return this._room.id;
}
compare(other) { compare(other) {
return this._compareFn(this._room.id, other._room.id); return this._compareFn(this._room.id, other._room.id);

View file

@ -129,7 +129,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, 4]]; this.ranges = [[0, 49]];
this.roomIndexToRoomId = {}; this.roomIndexToRoomId = {};
this.roomIdToRoomIndex = {}; this.roomIdToRoomIndex = {};
this.totalRooms = 0; this.totalRooms = 0;

View file

@ -19,7 +19,7 @@ import { BaseObservableList } from "./BaseObservableList";
import { findAndUpdateInArray } from "./common"; import { findAndUpdateInArray } from "./common";
export type Mapper<F, T> = (value: F) => T export type Mapper<F, T> = (value: F) => T
export type Updater<F, T> = (mappedValue: T, params: any, value: F) => void; export type Updater<F, T> = (mappedValue: T, params: any, value: F) => any;
export class BaseMappedList<F, T, R = T> extends BaseObservableList<T> { export class BaseMappedList<F, T, R = T> extends BaseObservableList<T> {
protected _sourceList: BaseObservableList<F>; protected _sourceList: BaseObservableList<F>;
@ -56,10 +56,21 @@ export function runAdd<F, T, R>(list: BaseMappedList<F, T, R>, index: number, ma
} }
export function runUpdate<F, T, R>(list: BaseMappedList<F, T, R>, index: number, value: F, params: any): void { export function runUpdate<F, T, R>(list: BaseMappedList<F, T, R>, index: number, value: F, params: any): void {
const mappedValue = list._mappedValues![index]; let mappedValue = list._mappedValues![index];
if (list._updater) { if (list._updater) {
list._updater(mappedValue, params, value); // allow updates to completely remap the underlying data type
// TODO: do we need to unsubscribe from anything here?
let newMappedValue = list._updater(mappedValue, params, value);
if (newMappedValue) {
if (!params) {
params = {};
}
params.oldValue = mappedValue;
mappedValue = newMappedValue;
list._mappedValues![index] = mappedValue;
}
} }
// pass the new updated value down the chain
list.emitUpdate(index, mappedValue, params); list.emitUpdate(index, mappedValue, params);
} }

View file

@ -23,6 +23,7 @@ import { IView } from "./types";
export interface IOptions<T, V> extends IParentOptions<T, V> { export interface IOptions<T, V> extends IParentOptions<T, V> {
itemHeight: number; itemHeight: number;
overflowItems?: number; overflowItems?: number;
shouldRecreateItem?: (value: any, oldValue: any) => boolean;
onRangeVisible?: (range: ListRange) => void; onRangeVisible?: (range: ListRange) => void;
} }
@ -33,15 +34,17 @@ export class LazyListView<T, V extends IView> extends ListView<T, V> {
private overflowItems: number; private overflowItems: number;
private scrollContainer?: HTMLElement; private scrollContainer?: HTMLElement;
private onRangeVisible?: (range: ListRange) => void; private onRangeVisible?: (range: ListRange) => void;
private shouldRecreateItem?: (value: any, oldValue: any) => boolean;
constructor( constructor(
{ itemHeight, onRangeVisible, overflowItems = 20, ...options }: IOptions<T, V>, { itemHeight, onRangeVisible, shouldRecreateItem, overflowItems = 20, ...options }: IOptions<T, V>,
childCreator: (value: T) => V childCreator: (value: T) => V
) { ) {
super(options, childCreator); super(options, childCreator);
this.itemHeight = itemHeight; this.itemHeight = itemHeight;
this.overflowItems = overflowItems; this.overflowItems = overflowItems;
this.onRangeVisible = onRangeVisible; // function(ItemRange) this.onRangeVisible = onRangeVisible;
this.shouldRecreateItem = shouldRecreateItem;
} }
handleEvent(e: Event) { handleEvent(e: Event) {
@ -190,7 +193,11 @@ export class LazyListView<T, V extends IView> extends ListView<T, V> {
onUpdate(i: number, value: T, params: any) { onUpdate(i: number, value: T, params: any) {
if (this.renderRange!.containsIndex(i)) { if (this.renderRange!.containsIndex(i)) {
this.updateChild(this.renderRange!.toLocalIndex(i), value, params); if (this.shouldRecreateItem && this.shouldRecreateItem(value, params?.oldValue)) {
super.recreateItem(this.renderRange!.toLocalIndex(i), value);
} else {
this.updateChild(this.renderRange!.toLocalIndex(i), value, params);
}
} }
} }

View file

@ -19,6 +19,8 @@ import {TemplateView} from "../../general/TemplateView";
import {RoomTileView} from "./RoomTileView.js"; import {RoomTileView} from "./RoomTileView.js";
import {InviteTileView} from "./InviteTileView.js"; import {InviteTileView} from "./InviteTileView.js";
import { PlaceholderRoomTileView } from "./PlaceholderRoomTileView"; import { PlaceholderRoomTileView } from "./PlaceholderRoomTileView";
import { PlaceholderRoomTileViewModel } from "../../../../../domain/session/leftpanel/PlaceholderRoomTileViewModel";
import { RoomTileViewModel } from "../../../../../domain/session/leftpanel/RoomTileViewModel";
class FilterField extends TemplateView { class FilterField extends TemplateView {
render(t, options) { render(t, options) {
@ -67,6 +69,11 @@ export class LeftPanelView extends TemplateView {
onRangeVisible: (range) => { onRangeVisible: (range) => {
vm.loadRoomRange(range); vm.loadRoomRange(range);
}, },
shouldRecreateItem: (value, oldValue) => {
const isOldRoom = oldValue instanceof RoomTileViewModel;
const isNewRoom = value instanceof RoomTileViewModel;
return isOldRoom != isNewRoom;
}
}, },
tileVM => { tileVM => {
if (tileVM.kind === "invite") { if (tileVM.kind === "invite") {