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._tiles = null;
this._entrySubscription = null; this._entrySubscription = null;
this._tileCreator = tileCreator; 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() { onSubscribeFirst() {
@ -21,7 +28,7 @@ export default class TilesCollection extends BaseObservableList {
let currentTile = null; let currentTile = null;
for (let entry of this._entries) { for (let entry of this._entries) {
if (!currentTile || !currentTile.tryIncludeEntry(entry)) { if (!currentTile || !currentTile.tryIncludeEntry(entry)) {
currentTile = this._tileCreator(entry); currentTile = this._tileCreator(entry, this._emitSpontanousUpdate);
if (currentTile) { if (currentTile) {
this._tiles.push(currentTile); this._tiles.push(currentTile);
} }
@ -86,7 +93,7 @@ export default class TilesCollection extends BaseObservableList {
return; return;
} }
const newTile = this._tileCreator(entry); const newTile = this._tileCreator(entry, this._emitSpontanousUpdate);
if (newTile) { if (newTile) {
prevTile && prevTile.updateNextSibling(newTile); prevTile && prevTile.updateNextSibling(newTile);
nextTile && nextTile.updatePreviousSibling(newTile); nextTile && nextTile.updatePreviousSibling(newTile);
@ -101,9 +108,12 @@ export default class TilesCollection extends BaseObservableList {
const tileIdx = this._findTileIdx(entry); const tileIdx = this._findTileIdx(entry);
const tile = this._findTileAtIdx(entry, tileIdx); const tile = this._findTileAtIdx(entry, tileIdx);
if (tile) { if (tile) {
const newParams = tile.updateEntry(entry, params); const action = tile.updateEntry(entry, params);
if (newParams) { if (action.shouldRemove) {
this.emitUpdate(tileIdx, tile, newParams); this._removeTile(tileIdx, tile);
}
if (action.shouldUpdate) {
this.emitUpdate(tileIdx, tile, action.updateParams);
} }
} }
// technically we should handle adding a tile here as well // 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 ... // 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 // would also be called when unloading a part of the timeline
onRemove(index, entry) { onRemove(index, entry) {
const tileIdx = this._findTileIdx(entry); const tileIdx = this._findTileIdx(entry);
@ -126,12 +145,7 @@ export default class TilesCollection extends BaseObservableList {
if (tile) { if (tile) {
const removeTile = tile.removeEntry(entry); const removeTile = tile.removeEntry(entry);
if (removeTile) { if (removeTile) {
const prevTile = this._getTileAtIdx(tileIdx - 1); this._removeTile(tileIdx, tile);
const nextTile = this._getTileAtIdx(tileIdx + 1);
this._tiles.splice(tileIdx, 1);
prevTile && prevTile.updateNextSibling(nextTile);
nextTile && nextTile.updatePreviousSibling(prevTile);
this.emitRemove(tileIdx, tile);
} else { } else {
this.emitUpdate(tileIdx, tile); this.emitUpdate(tileIdx, tile);
} }
@ -140,7 +154,8 @@ export default class TilesCollection extends BaseObservableList {
onMove(fromIdx, toIdx, value) { onMove(fromIdx, toIdx, value) {
// this ... cannot happen in the timeline? // 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]() { [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 SimpleTile from "./SimpleTile.js";
import UpdateAction from "../UpdateAction.js";
export default class GapTile extends SimpleTile { export default class GapTile extends SimpleTile {
constructor(options, timeline) { constructor(options, timeline) {
@ -12,20 +13,28 @@ export default class GapTile extends SimpleTile {
// prevent doing this twice // prevent doing this twice
if (!this._loading) { if (!this._loading) {
this._loading = true; this._loading = true;
// this._emitUpdate("isLoading"); this.emitUpdate("isLoading");
try { try {
await this._timeline.fillGap(this._entry, 10); await this._timeline.fillGap(this._entry, 10);
} catch (err) { } catch (err) {
console.error(`timeline.fillGap(): ${err.message}:\n${err.stack}`); console.error(`timeline.fillGap(): ${err.message}:\n${err.stack}`);
this._error = err; this._error = err;
// this._emitUpdate("error"); this.emitUpdate("error");
} finally { } finally {
this._loading = false; this._loading = false;
// this._emitUpdate("isLoading"); this.emitUpdate("isLoading");
} }
} }
} }
updateEntry(entry) {
if (!entry.isGap) {
return UpdateAction.Remove();
} else {
return UpdateAction.Nothing();
}
}
get shape() { get shape() {
return "gap"; return "gap";
} }

View file

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

View file

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