summary refs log tree commit diff
path: root/webclient/components
diff options
context:
space:
mode:
Diffstat (limited to 'webclient/components')
-rw-r--r--webclient/components/fileInput/file-input-directive.js22
-rw-r--r--webclient/components/matrix/event-handler-service.js118
-rw-r--r--webclient/components/matrix/event-stream-service.js2
-rw-r--r--webclient/components/matrix/matrix-call.js5
-rw-r--r--webclient/components/matrix/matrix-filter.js112
5 files changed, 199 insertions, 60 deletions
diff --git a/webclient/components/fileInput/file-input-directive.js b/webclient/components/fileInput/file-input-directive.js
index 14e2f772f7..9c849a140f 100644
--- a/webclient/components/fileInput/file-input-directive.js
+++ b/webclient/components/fileInput/file-input-directive.js
@@ -31,13 +31,23 @@ angular.module('mFileInput', [])
         },
 
         link: function(scope, element, attrs, ctrl) {
-            element.bind("click", function() {
-                element.find("input")[0].click();
-                element.find("input").bind("change", function(e) {
-                    scope.selectedFile = this.files[0];
-                    scope.$apply();
+            
+            // Check if HTML5 file selection is supported
+            if (window.FileList) {
+                element.bind("click", function() {
+                    element.find("input")[0].click();
+                    element.find("input").bind("change", function(e) {
+                        scope.selectedFile = this.files[0];
+                        scope.$apply();
+                    });
                 });
-            });
+            }
+            else {
+                setTimeout(function() {
+                    element.attr("disabled", true);
+                    element.attr("title", "The app uses the HTML5 File API to send files. Your browser does not support it.");
+                }, 1);
+            }
 
             // Change the mouse icon on mouseover on this element
             element.css("cursor", "pointer");
diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js
index fc5a81617c..5e95f34f4e 100644
--- a/webclient/components/matrix/event-handler-service.js
+++ b/webclient/components/matrix/event-handler-service.js
@@ -27,7 +27,8 @@ Typically, this service will store events or broadcast them to any listeners
 if typically all the $on method would do is update its own $scope.
 */
 angular.module('eventHandlerService', [])
-.factory('eventHandlerService', ['matrixService', '$rootScope', '$q', function(matrixService, $rootScope, $q) {
+.factory('eventHandlerService', ['matrixService', '$rootScope', '$q', '$timeout', 'mPresence', 
+function(matrixService, $rootScope, $q, $timeout, mPresence) {
     var ROOM_CREATE_EVENT = "ROOM_CREATE_EVENT";
     var MSG_EVENT = "MSG_EVENT";
     var MEMBER_EVENT = "MEMBER_EVENT";
@@ -44,6 +45,44 @@ angular.module('eventHandlerService', [])
     var eventMap = {};
 
     $rootScope.presence = {};
+    
+    // TODO: This is attached to the rootScope so .html can just go containsBingWord
+    // for determining classes so it is easy to highlight bing messages. It seems a
+    // bit strange to put the impl in this service though, but I can't think of a better
+    // file to put it in.
+    $rootScope.containsBingWord = function(content) {
+        if (!content || $.type(content) != "string") {
+            return false;
+        }
+        var bingWords = matrixService.config().bingWords;
+        var shouldBing = false;
+        
+        // case-insensitive name check for user_id OR display_name if they exist
+        var myUserId = matrixService.config().user_id;
+        if (myUserId) {
+            myUserId = myUserId.toLocaleLowerCase();
+        }
+        var myDisplayName = matrixService.config().display_name;
+        if (myDisplayName) {
+            myDisplayName = myDisplayName.toLocaleLowerCase();
+        }
+        if ( (myDisplayName && content.toLocaleLowerCase().indexOf(myDisplayName) != -1) ||
+             (myUserId && content.toLocaleLowerCase().indexOf(myUserId) != -1) ) {
+            shouldBing = true;
+        }
+        
+        // bing word list check
+        if (bingWords && !shouldBing) {
+            for (var i=0; i<bingWords.length; i++) {
+                var re = RegExp(bingWords[i]);
+                if (content.search(re) != -1) {
+                    shouldBing = true;
+                    break;
+                }
+            }
+        }
+        return shouldBing;
+    };
 
     var initialSyncDeferred;
 
@@ -60,7 +99,7 @@ angular.module('eventHandlerService', [])
     };
     reset();
 
-    var initRoom = function(room_id) {
+    var initRoom = function(room_id, room) {
         if (!(room_id in $rootScope.events.rooms)) {
             console.log("Creating new handler entry for " + room_id);
             $rootScope.events.rooms[room_id] = {
@@ -73,6 +112,16 @@ angular.module('eventHandlerService', [])
                 }
             };
         }
+
+        if (room) {
+            // Report all other metadata of the room object (membership, inviter, visibility, ...)
+            for (var field in room) {
+                if (-1 === ["room_id", "messages", "state"].indexOf(field)) {
+                    $rootScope.events.rooms[room_id][field] = room[field];
+                }
+            }
+            $rootScope.events.rooms[room_id].membership = room.membership;
+        }
     };
 
     var resetRoomMessages = function(room_id) {
@@ -137,6 +186,48 @@ angular.module('eventHandlerService', [])
             else {
                 $rootScope.events.rooms[event.room_id].messages.push(event);
             }
+            
+            if (window.Notification && event.user_id != matrixService.config().user_id) {
+                var shouldBing = $rootScope.containsBingWord(event.content.body);
+            
+                // TODO: Binging every message when idle doesn't make much sense. Can we use this more sensibly?
+                // Unfortunately document.hidden = false on ubuntu chrome if chrome is minimised / does not have focus;
+                // true when you swap tabs though. However, for the case where the chat screen is OPEN and there is
+                // another window on top, we want to be notifying for those events. This DOES mean that there will be
+                // notifications when currently viewing the chat screen though, but that is preferable to the alternative imo.
+                var isIdle = (document.hidden || matrixService.presence.unavailable === mPresence.getState());
+                
+                // always bing if there are 0 bing words... apparently.
+                var bingWords = matrixService.config().bingWords;
+                if (bingWords === undefined || bingWords.length === 0) {
+                    shouldBing = true;
+                }
+                
+                if (shouldBing) {
+                    console.log("Displaying notification for "+JSON.stringify(event));
+                    var member = $rootScope.events.rooms[event.room_id].members[event.user_id];
+                    var displayname = undefined;
+                    if (member) {
+                        displayname = member.displayname;
+                    }
+
+                    var message = event.content.body;
+                    if (event.content.msgtype === "m.emote") {
+                        message = "* " + displayname + " " + message;
+                    }
+
+                    var notification = new window.Notification(
+                        (displayname || event.user_id) +
+                        " (" + (matrixService.getRoomIdToAliasMapping(event.room_id) || event.room_id) + ")", // FIXME: don't leak room_ids here
+                    {
+                        "body": message,
+                        "icon": member ? member.avatar_url : undefined
+                    });
+                    $timeout(function() {
+                        notification.close();
+                    }, 5 * 1000);
+                }
+            }
         }
         else {
             $rootScope.events.rooms[event.room_id].messages.unshift(event);
@@ -151,13 +242,6 @@ angular.module('eventHandlerService', [])
     };
     
     var handleRoomMember = function(event, isLiveEvent, isStateEvent) {
-        // if the server is stupidly re-relaying a no-op join, discard it.
-        if (event.prev_content && 
-            event.content.membership === "join" &&
-            event.content.membership === event.prev_content.membership)
-        {
-            return;
-        }
         
         // add membership changes as if they were a room message if something interesting changed
         // Exception: Do not do this if the event is a room state event because such events already come
@@ -253,6 +337,10 @@ angular.module('eventHandlerService', [])
             reset();
             $rootScope.$broadcast(RESET_EVENT);
         },
+        
+        initRoom: function(room) {
+            initRoom(room.room_id, room);
+        },
     
         handleEvent: function(event, isLiveEvent, isStateEvent) {
 
@@ -430,6 +518,18 @@ angular.module('eventHandlerService', [])
                 member = room.members[user_id];
             }
             return member;
+        },
+        
+        setRoomVisibility: function(room_id, visible) {
+            if (!visible) {
+                return;
+            }
+            initRoom(room_id);
+            
+            var room = $rootScope.events.rooms[room_id];
+            if (room) {
+                room.visibility = visible;
+            }
         }
     };
 }]);
diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js
index 6f92332246..05469a3ded 100644
--- a/webclient/components/matrix/event-stream-service.js
+++ b/webclient/components/matrix/event-stream-service.js
@@ -112,6 +112,8 @@ angular.module('eventStreamService', [])
                 var rooms = response.data.rooms;
                 for (var i = 0; i < rooms.length; ++i) {
                     var room = rooms[i];
+                    
+                    eventHandlerService.initRoom(room);
 
                     if ("messages" in room) {
                         eventHandlerService.handleRoomMessages(room.room_id, room.messages, false);
diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js
index 5ba782bac8..fc02e1f62f 100644
--- a/webclient/components/matrix/matrix-call.js
+++ b/webclient/components/matrix/matrix-call.js
@@ -40,6 +40,11 @@ window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConne
 window.RTCSessionDescription = window.RTCSessionDescription || window.webkitRTCSessionDescription || window.mozRTCSessionDescription;
 window.RTCIceCandidate = window.RTCIceCandidate || window.webkitRTCIceCandidate || window.mozRTCIceCandidate;
 
+// Returns true if the browser supports all required features to make WebRTC call
+var isWebRTCSupported = function () {
+    return (navigator.getUserMedia || window.RTCPeerConnection || window.RTCSessionDescription || window.RTCIceCandidate);
+};
+
 angular.module('MatrixCall', [])
 .factory('MatrixCall', ['matrixService', 'matrixPhoneService', '$rootScope', '$timeout', function MatrixCallFactory(matrixService, matrixPhoneService, $rootScope, $timeout) {
     var MatrixCall = function(room_id) {
diff --git a/webclient/components/matrix/matrix-filter.js b/webclient/components/matrix/matrix-filter.js
index 015a88bcad..c99c435b92 100644
--- a/webclient/components/matrix/matrix-filter.js
+++ b/webclient/components/matrix/matrix-filter.js
@@ -26,75 +26,97 @@ angular.module('matrixFilter', [])
         // If there is an alias, use it
         // TODO: only one alias is managed for now
         var alias = matrixService.getRoomIdToAliasMapping(room_id);
-        if (alias) {
-            roomName = alias;
-        }
 
-        if (undefined === roomName) {
+        var room = $rootScope.events.rooms[room_id];
+        if (room) {
+            // Get name from room state date
+            var room_name_event = room["m.room.name"];
+            if (room_name_event) {
+                roomName = room_name_event.content.name;
+            }
+            else if (alias) {
+                roomName = alias;
+            }
+            else if (room.members) {
 
-            var room = $rootScope.events.rooms[room_id];
-            if (room) {
-                // Get name from room state date
-                var room_name_event = room["m.room.name"];
-                if (room_name_event) {
-                    roomName = room_name_event.content.name;
-                }
-                else if (room.members) {
-                    // Else, build the name from its users
-                    // FIXME: Is it still required?
-                    // Limit the room renaming to 1:1 room
-                    if (2 === Object.keys(room.members).length) {
-                        for (var i in room.members) {
-                            var member = room.members[i];
-                            if (member.state_key !== matrixService.config().user_id) {
-
-                                if (member.state_key in $rootScope.presence) {
-                                    // If the user is available in presence, use the displayname there
-                                    // as it is the most uptodate
-                                    roomName = $rootScope.presence[member.state_key].content.displayname;
-                                }
-                                else if (member.content.displayname) {
-                                    roomName = member.content.displayname;
-                                }
-                                else {
-                                    roomName = member.state_key;
-                                }
+                var user_id = matrixService.config().user_id;
+
+                // Else, build the name from its users
+                // Limit the room renaming to 1:1 room
+                if (2 === Object.keys(room.members).length) {
+                    for (var i in room.members) {
+                        var member = room.members[i];
+                        if (member.state_key !== user_id) {
+
+                            if (member.state_key in $rootScope.presence) {
+                                // If the user is available in presence, use the displayname there
+                                // as it is the most uptodate
+                                roomName = $rootScope.presence[member.state_key].content.displayname;
+                            }
+                            else if (member.content.displayname) {
+                                roomName = member.content.displayname;
+                            }
+                            else {
+                                roomName = member.state_key;
                             }
                         }
                     }
-                    else if (1 === Object.keys(room.members).length) {
+                }
+                else if (1 === Object.keys(room.members).length) {
+                    var otherUserId;
+
+                    if (Object.keys(room.members)[0] !== user_id) {
+                        otherUserId = Object.keys(room.members)[0];
+                    }
+                    else {
                         // The other member may be in the invite list, get all invited users
                         var invitedUserIDs = [];
                         for (var i in room.messages) {
                             var message = room.messages[i];
                             if ("m.room.member" === message.type && "invite" === message.membership) {
-                                // Make sure there is no duplicate user
-                                if (-1 === invitedUserIDs.indexOf(message.state_key)) {
-                                    invitedUserIDs.push(message.state_key);
+                                // Filter out the current user
+                                var member_id = message.state_key;
+                                if (member_id === user_id) {
+                                    member_id = message.user_id;
+                                }
+                                if (member_id !== user_id) {
+                                    // Make sure there is no duplicate user
+                                    if (-1 === invitedUserIDs.indexOf(member_id)) {
+                                        invitedUserIDs.push(member_id);
+                                    }
                                 }
                             } 
                         }
-                        
+
                         // For now, only 1:1 room needs to be renamed. It means only 1 invited user
                         if (1 === invitedUserIDs.length) {
-                            var userID = invitedUserIDs[0];
-
-                            // Try to resolve his displayname in presence global data
-                            if (userID in $rootScope.presence) {
-                                roomName = $rootScope.presence[userID].content.displayname;
-                            }
-                            else {
-                                roomName = userID;
-                            }
+                            otherUserId = invitedUserIDs[0];
                         }
                     }
+
+                    // Try to resolve his displayname in presence global data
+                    if (otherUserId in $rootScope.presence) {
+                        roomName = $rootScope.presence[otherUserId].content.displayname;
+                    }
+                    else {
+                        roomName = otherUserId;
+                    }
                 }
             }
         }
 
+        // Always show the alias in the room displayed name
+        if (roomName && alias && alias !== roomName) {
+            roomName += " (" + alias + ")";
+        }
+
         if (undefined === roomName) {
             // By default, use the room ID
             roomName = room_id;
+
+            // Log some information that lead to this leak
+            console.log("Room ID leak for " + room_id);
+            console.log("room object: " + JSON.stringify(room, undefined, 4));   
         }
 
         return roomName;