diff options
Diffstat (limited to 'webclient/components/matrix/model-service.js')
-rw-r--r-- | webclient/components/matrix/model-service.js | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/webclient/components/matrix/model-service.js b/webclient/components/matrix/model-service.js new file mode 100644 index 0000000000..8b2ee877b1 --- /dev/null +++ b/webclient/components/matrix/model-service.js @@ -0,0 +1,170 @@ +/* +Copyright 2014 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +/* +This service serves as the entry point for all models in the app. If access to +underlying data in a room is required, then this service should be used as the +dependency. +*/ +// NB: This is more explicit than linking top-level models to $rootScope +// in that by adding this service as a dep you are clearly saying "this X +// needs access to the underlying data store", rather than polluting the +// $rootScope. +angular.module('modelService', []) +.factory('modelService', ['matrixService', function(matrixService) { + + /***** Room Object *****/ + var Room = function Room(room_id) { + this.room_id = room_id; + this.old_room_state = new RoomState(); + this.current_room_state = new RoomState(); + this.events = []; // events which can be displayed on the UI. TODO move? + }; + Room.prototype = { + addMessageEvents: function addMessageEvents(events, toFront) { + for (var i=0; i<events.length; i++) { + this.addMessageEvent(events[i], toFront); + } + }, + + addMessageEvent: function addMessageEvent(event, toFront) { + // every message must reference the RoomMember which made it *at + // that time* so things like display names display correctly. + var stateAtTheTime = toFront ? this.old_room_state : this.current_room_state; + event.__room_member = stateAtTheTime.getStateEvent("m.room.member", event.user_id); + if (event.type === "m.room.member" && event.content.membership === "invite") { + // give information on both the inviter and invitee + event.__target_room_member = stateAtTheTime.getStateEvent("m.room.member", event.state_key); + } + + if (toFront) { + this.events.unshift(event); + } + else { + this.events.push(event); + } + }, + + addOrReplaceMessageEvent: function addOrReplaceMessageEvent(event, toFront) { + // Start looking from the tail since the first goal of this function + // is to find a message among the latest ones + for (var i = this.events.length - 1; i >= 0; i--) { + var storedEvent = this.events[i]; + if (storedEvent.event_id === event.event_id) { + // It's clobbering time! + this.events[i] = event; + return; + } + } + this.addMessageEvent(event, toFront); + }, + + leave: function leave() { + return matrixService.leave(this.room_id); + } + }; + + /***** Room State Object *****/ + var RoomState = function RoomState() { + // list of RoomMember + this.members = {}; + // state events, the key is a compound of event type + state_key + this.state_events = {}; + this.pagination_token = ""; + }; + RoomState.prototype = { + // get a state event for this room from this.state_events. State events + // are unique per type+state_key tuple, with a lot of events using 0-len + // state keys. To make it not Really Annoying to access, this method is + // provided which can just be given the type and it will return the + // 0-len event by default. + state: function state(type, state_key) { + if (!type) { + return undefined; // event type MUST be specified + } + if (!state_key) { + return this.state_events[type]; // treat as 0-len state key + } + return this.state_events[type + state_key]; + }, + + storeStateEvent: function storeState(event) { + this.state_events[event.type + event.state_key] = event; + if (event.type === "m.room.member") { + this.members[event.state_key] = event; + } + }, + + storeStateEvents: function storeState(events) { + if (!events) { + return; + } + for (var i=0; i<events.length; i++) { + this.storeStateEvent(events[i]); + } + }, + + getStateEvent: function getStateEvent(event_type, state_key) { + return this.state_events[event_type + state_key]; + } + }; + + /***** Room Member Object *****/ + var RoomMember = function RoomMember() { + this.event = {}; // the m.room.member event representing the RoomMember. + this.user = undefined; // the User + }; + + /***** User Object *****/ + var User = function User() { + this.event = {}; // the m.presence event representing the User. + }; + + // rooms are stored here when they come in. + var rooms = { + // roomid: <Room> + }; + + console.log("Models inited."); + + return { + + getRoom: function(roomId) { + if(!rooms[roomId]) { + rooms[roomId] = new Room(roomId); + } + return rooms[roomId]; + }, + + getRooms: function() { + return rooms; + }, + + /** + * Get the member object of a room member + * @param {String} room_id the room id + * @param {String} user_id the id of the user + * @returns {undefined | Object} the member object of this user in this room if he is part of the room + */ + getMember: function(room_id, user_id) { + var room = this.getRoom(room_id); + return room.current_room_state.members[user_id]; + } + + }; +}]); |