forked from mystiq/hydrogen-web
work on tile view models
This commit is contained in:
parent
d6e357cc22
commit
153d54a285
9 changed files with 153 additions and 15 deletions
|
@ -1,13 +1,42 @@
|
||||||
import SimpleTile from "./SimpleTile";
|
import SimpleTile from "./SimpleTile";
|
||||||
|
|
||||||
export default class GapTile extends SimpleTile {
|
export default class GapTile extends SimpleTile {
|
||||||
constructor(entry, timeline) {
|
constructor(options, timeline) {
|
||||||
super(entry);
|
super(options);
|
||||||
this._timeline = timeline;
|
this._timeline = timeline;
|
||||||
|
this._loading = false;
|
||||||
|
this._error = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GapTile specific behaviour
|
async fill() {
|
||||||
fill() {
|
// prevent doing this twice
|
||||||
return this._timeline.fillGap(this._entry, 10);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
22
src/domain/session/room/timeline/tiles/ImageTile.js
Normal file
22
src/domain/session/room/timeline/tiles/ImageTile.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
20
src/domain/session/room/timeline/tiles/LocationTile.js
Normal file
20
src/domain/session/room/timeline/tiles/LocationTile.js
Normal 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.`;
|
||||||
|
}
|
||||||
|
}
|
26
src/domain/session/room/timeline/tiles/MessageTile.js
Normal file
26
src/domain/session/room/timeline/tiles/MessageTile.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
9
src/domain/session/room/timeline/tiles/RoomMemberTile.js
Normal file
9
src/domain/session/room/timeline/tiles/RoomMemberTile.js
Normal 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}`;
|
||||||
|
}
|
||||||
|
}
|
9
src/domain/session/room/timeline/tiles/RoomNameTile.js
Normal file
9
src/domain/session/room/timeline/tiles/RoomNameTile.js
Normal 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}"`
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
export default class SimpleTile {
|
export default class SimpleTile {
|
||||||
constructor(entry) {
|
constructor({entry, emitUpdate}) {
|
||||||
this._entry = entry;
|
this._entry = entry;
|
||||||
|
this._emitUpdate = emitUpdate;
|
||||||
}
|
}
|
||||||
// view model props for all subclasses
|
// view model props for all subclasses
|
||||||
// hmmm, could also do instanceof ... ?
|
// hmmm, could also do instanceof ... ?
|
||||||
|
@ -33,10 +34,17 @@ export default class SimpleTile {
|
||||||
|
|
||||||
// update received for already included (falls within sort keys) entry
|
// update received for already included (falls within sort keys) entry
|
||||||
updateEntry(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() {
|
tryIncludeEntry() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
8
src/domain/session/room/timeline/tiles/TextTile.js
Normal file
8
src/domain/session/room/timeline/tiles/TextTile.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,15 @@
|
||||||
import GapTile from "./tiles/GapTile.js";
|
import GapTile from "./tiles/GapTile.js";
|
||||||
import TextTile from "./tiles/TextTile.js";
|
import TextTile from "./tiles/TextTile.js";
|
||||||
import ImageTile from "./tiles/ImageTile.js";
|
import ImageTile from "./tiles/ImageTile.js";
|
||||||
|
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}) {
|
export default function ({timeline, emitUpdate}) {
|
||||||
return function tilesCreator(entry) {
|
return function tilesCreator(entry) {
|
||||||
|
const options = {entry, emitUpdate};
|
||||||
if (entry.gap) {
|
if (entry.gap) {
|
||||||
return new GapTile(entry, timeline);
|
return new GapTile(options, timeline);
|
||||||
} else if (entry.event) {
|
} else if (entry.event) {
|
||||||
const event = entry.event;
|
const event = entry.event;
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
|
@ -16,18 +18,23 @@ export default function ({timeline}) {
|
||||||
const msgtype = content && content.msgtype;
|
const msgtype = content && content.msgtype;
|
||||||
switch (msgtype) {
|
switch (msgtype) {
|
||||||
case "m.text":
|
case "m.text":
|
||||||
return new TextTile(entry);
|
case "m.notice":
|
||||||
|
return new TextTile(options);
|
||||||
case "m.image":
|
case "m.image":
|
||||||
return new ImageTile(entry);
|
return new ImageTile(options);
|
||||||
|
case "m.location":
|
||||||
|
return new LocationTile(options);
|
||||||
default:
|
default:
|
||||||
return null; // unknown tile types are not rendered?
|
// unknown msgtype not rendered
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "m.room.name":
|
case "m.room.name":
|
||||||
return new RoomNameTile(entry);
|
return new RoomNameTile(options);
|
||||||
case "m.room.member":
|
case "m.room.member":
|
||||||
return new RoomMemberTile(entry);
|
return new RoomMemberTile(options);
|
||||||
default:
|
default:
|
||||||
|
// unknown type not rendered
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue