work on tile view models

This commit is contained in:
Bruno Windels 2019-03-09 00:40:03 +01:00
parent d6e357cc22
commit 153d54a285
9 changed files with 153 additions and 15 deletions

View file

@ -1,13 +1,42 @@
import SimpleTile from "./SimpleTile";
export default class GapTile extends SimpleTile {
constructor(entry, timeline) {
super(entry);
constructor(options, timeline) {
super(options);
this._timeline = timeline;
this._loading = false;
this._error = null;
}
// GapTile specific behaviour
fill() {
return this._timeline.fillGap(this._entry, 10);
async fill() {
// prevent doing this twice
if (!this._loading) {
this._loading = true;
this._emitUpdate("isLoading");
try {
return await this._timeline.fillGap(this._entry, 10);
} catch (err) {
this._loading = false;
this._error = err;
this._emitUpdate("isLoading");
this._emitUpdate("error");
}
}
}
get isLoading() {
return this._loading;
}
get direction() {
return this._entry.prev_batch ? "backward" : "forward";
}
get error() {
if (this._error) {
const dir = this._entry.prev_batch ? "previous" : "next";
return `Could not load ${dir} messages: ${this._error.message}`;
}
return null;
}
}

View file

@ -0,0 +1,22 @@
import MessageTile from "./MessageTile.js";
export default class ImageTile extends MessageTile {
constructor(options) {
super(options);
// we start loading the image here,
// and call this._emitUpdate once it's loaded?
// or maybe we have an becameVisible() callback on tiles where we start loading it?
}
get src() {
return "";
}
get width() {
return 200;
}
get height() {
return 200;
}
}

View file

@ -0,0 +1,20 @@
import MessageTile from "./MessageTile.js";
/*
map urls:
apple: https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html
android: https://developers.google.com/maps/documentation/urls/guide
wp: maps:49.275267 -122.988617
https://www.habaneroconsulting.com/stories/insights/2011/opening-native-map-apps-from-the-mobile-browser
*/
export default class LocationTile extends MessageTile {
get mapsLink() {
const geoUri = this._getContent().geo_uri;
const [lat, long] = geoUri.split(":")[1].split(",");
return `maps:${lat} ${long}`;
}
get label() {
return `${this.sender} sent their location, click to see it in maps.`;
}
}

View file

@ -0,0 +1,26 @@
import SimpleTile from "./SimpleTile.js";
export default class MessageTile extends SimpleTile {
constructor(options) {
super(options);
this._date = new Date(this._entry.event.origin_server_ts);
}
get sender() {
return this._entry.event.sender;
}
get date() {
return this._date.toLocaleDateString();
}
get time() {
return this._date.toLocaleTimeString();
}
_getContent() {
const event = this._entry.event;
return event && event.content;
}
}

View file

@ -0,0 +1,9 @@
import SimpleTile from "./SimpleTile.js";
export default class RoomNameTile extends SimpleTile {
get label() {
const event = this._entry.event;
const content = event.content;
return `${event.sender} changed membership to ${content.membership}`;
}
}

View file

@ -0,0 +1,9 @@
import SimpleTile from "./SimpleTile.js";
export default class RoomNameTile extends SimpleTile {
get label() {
const event = this._entry.event;
const content = event.content;
return `${event.sender} changed the room name to "${content.name}"`
}
}

View file

@ -1,6 +1,7 @@
export default class SimpleTile {
constructor(entry) {
constructor({entry, emitUpdate}) {
this._entry = entry;
this._emitUpdate = emitUpdate;
}
// view model props for all subclasses
// hmmm, could also do instanceof ... ?
@ -33,10 +34,17 @@ export default class SimpleTile {
// 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;
}
// simple entry can only contain 1 entry
// return whether the tile should be removed
// as SimpleTile only has one entry, the tile should be removed
removeEntry(entry) {
return true;
}
// SimpleTile can only contain 1 entry
tryIncludeEntry() {
return false;
}

View file

@ -0,0 +1,8 @@
import MessageTile from "./MessageTile.js";
export default class TextTile extends MessageTile {
get text() {
const content = this._getContent();
return content && content.body;
}
}

View file

@ -1,13 +1,15 @@
import GapTile from "./tiles/GapTile.js";
import TextTile from "./tiles/TextTile.js";
import ImageTile from "./tiles/ImageTile.js";
import LocationTile from "./tiles/LocationTile.js";
import RoomNameTile from "./tiles/RoomNameTile.js";
import RoomMemberTile from "./tiles/RoomMemberTile.js";
export default function ({timeline}) {
export default function ({timeline, emitUpdate}) {
return function tilesCreator(entry) {
const options = {entry, emitUpdate};
if (entry.gap) {
return new GapTile(entry, timeline);
return new GapTile(options, timeline);
} else if (entry.event) {
const event = entry.event;
switch (event.type) {
@ -16,18 +18,23 @@ export default function ({timeline}) {
const msgtype = content && content.msgtype;
switch (msgtype) {
case "m.text":
return new TextTile(entry);
case "m.notice":
return new TextTile(options);
case "m.image":
return new ImageTile(entry);
return new ImageTile(options);
case "m.location":
return new LocationTile(options);
default:
return null; // unknown tile types are not rendered?
// unknown msgtype not rendered
return null;
}
}
case "m.room.name":
return new RoomNameTile(entry);
return new RoomNameTile(options);
case "m.room.member":
return new RoomMemberTile(entry);
return new RoomMemberTile(options);
default:
// unknown type not rendered
return null;
}
}