support updates originating from tile, and removing tile on update

This commit is contained in:
Bruno Windels 2019-06-12 21:57:13 +02:00
parent 422cca746b
commit 64f126ba68
6 changed files with 83 additions and 21 deletions

View file

@ -9,6 +9,13 @@ export default class TilesCollection extends BaseObservableList {
this._tiles = null;
this._entrySubscription = null;
this._tileCreator = tileCreator;
this._emitSpontanousUpdate = this._emitSpontanousUpdate.bind(this);
}
_emitSpontanousUpdate(tile, params) {
const entry = tile.lowerEntry;
const tileIdx = this._findTileIdx(entry);
this.emitUpdate(tileIdx, tile, params);
}
onSubscribeFirst() {
@ -21,7 +28,7 @@ export default class TilesCollection extends BaseObservableList {
let currentTile = null;
for (let entry of this._entries) {
if (!currentTile || !currentTile.tryIncludeEntry(entry)) {
currentTile = this._tileCreator(entry);
currentTile = this._tileCreator(entry, this._emitSpontanousUpdate);
if (currentTile) {
this._tiles.push(currentTile);
}
@ -86,7 +93,7 @@ export default class TilesCollection extends BaseObservableList {
return;
}
const newTile = this._tileCreator(entry);
const newTile = this._tileCreator(entry, this._emitSpontanousUpdate);
if (newTile) {
prevTile && prevTile.updateNextSibling(newTile);
nextTile && nextTile.updatePreviousSibling(newTile);
@ -101,9 +108,12 @@ export default class TilesCollection extends BaseObservableList {
const tileIdx = this._findTileIdx(entry);
const tile = this._findTileAtIdx(entry, tileIdx);
if (tile) {
const newParams = tile.updateEntry(entry, params);
if (newParams) {
this.emitUpdate(tileIdx, tile, newParams);
const action = tile.updateEntry(entry, params);
if (action.shouldRemove) {
this._removeTile(tileIdx, tile);
}
if (action.shouldUpdate) {
this.emitUpdate(tileIdx, tile, action.updateParams);
}
}
// technically we should handle adding a tile here as well
@ -119,6 +129,15 @@ export default class TilesCollection extends BaseObservableList {
// merge with neighbours? ... hard to imagine use case for this ...
}
_removeTile(tileIdx, tile) {
const prevTile = this._getTileAtIdx(tileIdx - 1);
const nextTile = this._getTileAtIdx(tileIdx + 1);
this._tiles.splice(tileIdx, 1);
prevTile && prevTile.updateNextSibling(nextTile);
nextTile && nextTile.updatePreviousSibling(prevTile);
this.emitRemove(tileIdx, tile);
}
// would also be called when unloading a part of the timeline
onRemove(index, entry) {
const tileIdx = this._findTileIdx(entry);
@ -126,12 +145,7 @@ export default class TilesCollection extends BaseObservableList {
if (tile) {
const removeTile = tile.removeEntry(entry);
if (removeTile) {
const prevTile = this._getTileAtIdx(tileIdx - 1);
const nextTile = this._getTileAtIdx(tileIdx + 1);
this._tiles.splice(tileIdx, 1);
prevTile && prevTile.updateNextSibling(nextTile);
nextTile && nextTile.updatePreviousSibling(prevTile);
this.emitRemove(tileIdx, tile);
this._removeTile(tileIdx, tile);
} else {
this.emitUpdate(tileIdx, tile);
}
@ -140,7 +154,8 @@ export default class TilesCollection extends BaseObservableList {
onMove(fromIdx, toIdx, value) {
// this ... cannot happen in the timeline?
// should be sorted by sortKey and sortKey is immutable
// perhaps we can use this event to support a local echo (in a different fragment)
// to be moved to the key of the remote echo, so we don't loose state ... ?
}
[Symbol.iterator]() {

View file

@ -0,0 +1,31 @@
export default class UpdateAction {
constructor(remove, update, updateParams) {
this._remove = remove;
this._update = update;
this._updateParams = updateParams;
}
get shouldRemove() {
return this._remove;
}
get shouldUpdate() {
return this._update;
}
get updateParams() {
return this._updateParams;
}
static Remove() {
return new UpdateAction(true, false, null);
}
static Update(newParams) {
return new UpdateAction(false, true, newParams);
}
static Nothing() {
return new UpdateAction(false, false, null);
}
}

View file

@ -1,4 +1,5 @@
import SimpleTile from "./SimpleTile.js";
import UpdateAction from "../UpdateAction.js";
export default class GapTile extends SimpleTile {
constructor(options, timeline) {
@ -12,20 +13,28 @@ export default class GapTile extends SimpleTile {
// prevent doing this twice
if (!this._loading) {
this._loading = true;
// this._emitUpdate("isLoading");
this.emitUpdate("isLoading");
try {
await this._timeline.fillGap(this._entry, 10);
} catch (err) {
console.error(`timeline.fillGap(): ${err.message}:\n${err.stack}`);
this._error = err;
// this._emitUpdate("error");
this.emitUpdate("error");
} finally {
this._loading = false;
// this._emitUpdate("isLoading");
this.emitUpdate("isLoading");
}
}
}
updateEntry(entry) {
if (!entry.isGap) {
return UpdateAction.Remove();
} else {
return UpdateAction.Nothing();
}
}
get shape() {
return "gap";
}

View file

@ -1,3 +1,5 @@
import UpdateAction from "../UpdateAction.js";
export default class SimpleTile {
constructor({entry, emitUpdate}) {
this._entry = entry;
@ -6,6 +8,7 @@ export default class SimpleTile {
// view model props for all subclasses
// hmmm, could also do instanceof ... ?
get shape() {
return null;
// "gap" | "message" | "image" | ... ?
}
@ -28,15 +31,18 @@ export default class SimpleTile {
return this._entry;
}
emitUpdate(paramName) {
this._emitUpdate(this, paramName);
}
// TilesCollection contract
compareEntry(entry) {
return this._entry.compare(entry);
}
// update received for already included (falls within sort keys) entry
updateEntry(entry) {
// return names of props updated, or true for all, or null for no changes caused
return true;
updateEntry() {
return UpdateAction.Nothing();
}
// return whether the tile should be removed

View file

@ -5,8 +5,8 @@ import LocationTile from "./tiles/LocationTile.js";
import RoomNameTile from "./tiles/RoomNameTile.js";
import RoomMemberTile from "./tiles/RoomMemberTile.js";
export default function ({timeline, emitUpdate}) {
return function tilesCreator(entry) {
export default function ({timeline}) {
return function tilesCreator(entry, emitUpdate) {
const options = {entry, emitUpdate};
if (entry.isGap) {
return new GapTile(options, timeline);

View file

@ -17,7 +17,8 @@ export default class TimelineTile {
unmount() {}
update() {}
update(vm, paramName) {
}
}
function renderTile(tile) {