diff --git a/src/domain/session/room/timeline/TilesCollection.js b/src/domain/session/room/timeline/TilesCollection.js index 8af2879d..69268b41 100644 --- a/src/domain/session/room/timeline/TilesCollection.js +++ b/src/domain/session/room/timeline/TilesCollection.js @@ -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]() { diff --git a/src/domain/session/room/timeline/UpdateAction.js b/src/domain/session/room/timeline/UpdateAction.js new file mode 100644 index 00000000..1421cbd6 --- /dev/null +++ b/src/domain/session/room/timeline/UpdateAction.js @@ -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); + } +} diff --git a/src/domain/session/room/timeline/tiles/GapTile.js b/src/domain/session/room/timeline/tiles/GapTile.js index c063db25..f8804cf8 100644 --- a/src/domain/session/room/timeline/tiles/GapTile.js +++ b/src/domain/session/room/timeline/tiles/GapTile.js @@ -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"; } diff --git a/src/domain/session/room/timeline/tiles/SimpleTile.js b/src/domain/session/room/timeline/tiles/SimpleTile.js index 605fc1c1..304cbd87 100644 --- a/src/domain/session/room/timeline/tiles/SimpleTile.js +++ b/src/domain/session/room/timeline/tiles/SimpleTile.js @@ -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 diff --git a/src/domain/session/room/timeline/tilesCreator.js b/src/domain/session/room/timeline/tilesCreator.js index 96638aa0..60ce42ed 100644 --- a/src/domain/session/room/timeline/tilesCreator.js +++ b/src/domain/session/room/timeline/tilesCreator.js @@ -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); diff --git a/src/ui/web/TimelineTile.js b/src/ui/web/TimelineTile.js index 4c0ff45d..0e583a37 100644 --- a/src/ui/web/TimelineTile.js +++ b/src/ui/web/TimelineTile.js @@ -17,7 +17,8 @@ export default class TimelineTile { unmount() {} - update() {} + update(vm, paramName) { + } } function renderTile(tile) {