From 41779880a4064d88b0d011ff0a086e1521f08e0e Mon Sep 17 00:00:00 2001 From: Evan Theurer Date: Fri, 19 Sep 2014 17:08:32 +0200 Subject: [PATCH 1/7] Add buddycondensed directive and css. --- html/main.html | 1 + src/styles/components/_buddycondensed.scss | 40 +++++++++ src/styles/main.scss | 1 + static/js/directives/buddycondensed.js | 96 ++++++++++++++++++++++ static/js/directives/directives.js | 6 +- static/partials/buddycondensed.html | 6 ++ 6 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 src/styles/components/_buddycondensed.scss create mode 100644 static/js/directives/buddycondensed.js create mode 100644 static/partials/buddycondensed.html diff --git a/html/main.html b/html/main.html index 3dccb6e3..e08d9c9f 100644 --- a/html/main.html +++ b/html/main.html @@ -18,6 +18,7 @@ + From f03514c5e7b1ea5594a610c09046411d889430c8 Mon Sep 17 00:00:00 2001 From: Evan Theurer Date: Wed, 24 Sep 2014 09:32:54 +0200 Subject: [PATCH 4/7] Add call and chat functionality. --- static/js/directives/buddycondensed.js | 19 ++++++++++++++++++- static/partials/buddycondensed.html | 8 ++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/static/js/directives/buddycondensed.js b/static/js/directives/buddycondensed.js index b794d7c9..fb5a9faf 100644 --- a/static/js/directives/buddycondensed.js +++ b/static/js/directives/buddycondensed.js @@ -23,8 +23,16 @@ define(['angular', 'text!partials/buddycondensed.html'], function(angular, templ // buddycondensed return [function() { - var controller = ['$scope', 'mediaStream', 'contacts', function($scope, mediaStream, contacts) { + var controller = ['$scope', 'mediaStream', 'contacts', 'buddyData', function($scope, mediaStream, contacts, buddyData) { var buddycondensed = []; + var getContactSessionId = function(userid) { + var session = null; + var scope = buddyData.lookup(userid, false, false); + if (scope) { + session = scope.session.get(); + } + return session && session.Id ? session.Id : null; + }; var empty = function(x) { return x === null || x === undefined || isNaN(x) || x === ""; }; @@ -77,6 +85,15 @@ define(['angular', 'text!partials/buddycondensed.html'], function(angular, templ } $scope.$apply(); }; + $scope.call = function(userid) { + mediaStream.webrtc.doCall(getContactSessionId(userid)); + }; + $scope.chat = function(userid) { + $scope.$emit("startchat", getContactSessionId(userid), { + autofocus: true, + restore: true + }); + }; $scope.listDefault = function() { if(buddycondensed.length >= $scope.maxBuddiesToShow) { return buddycondensed.slice(0, $scope.maxBuddiesToShow); diff --git a/static/partials/buddycondensed.html b/static/partials/buddycondensed.html index 846c72e1..2ae0ea02 100644 --- a/static/partials/buddycondensed.html +++ b/static/partials/buddycondensed.html @@ -9,8 +9,8 @@ {{buddy.Userid|displayName}}
- - + +
@@ -24,8 +24,8 @@ {{buddy.Userid|displayName}}
- - + +
From ba94188d85ef549d2ed606e954b07fc82df9a07d Mon Sep 17 00:00:00 2001 From: Evan Theurer Date: Wed, 24 Sep 2014 12:12:41 +0200 Subject: [PATCH 5/7] Improve hover experience with hoverIntent. --- src/styles/components/_buddycondensed.scss | 9 +- static/js/directives/buddycondensed.js | 36 +++++- static/js/libs/jquery/jquery.hoverIntent.js | 115 ++++++++++++++++++++ static/js/main.js | 5 + static/partials/buddycondensed.html | 2 +- 5 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 static/js/libs/jquery/jquery.hoverIntent.js diff --git a/src/styles/components/_buddycondensed.scss b/src/styles/components/_buddycondensed.scss index 8a003bca..f92a77d3 100644 --- a/src/styles/components/_buddycondensed.scss +++ b/src/styles/components/_buddycondensed.scss @@ -42,17 +42,14 @@ display: none; position: fixed; top: 49px; - &:hover { - display: block; - } } .desc { - display: inline-block; + display: none; font-weight: bold; padding-left: 10px; cursor: default; - &:hover ~ .overDefaultDisplayNum { - display: block; + &.overNum { + display: inline-block; } } .actions { diff --git a/static/js/directives/buddycondensed.js b/static/js/directives/buddycondensed.js index fb5a9faf..b2f7b0bc 100644 --- a/static/js/directives/buddycondensed.js +++ b/static/js/directives/buddycondensed.js @@ -18,7 +18,7 @@ * along with this program. If not, see . * */ -define(['angular', 'text!partials/buddycondensed.html'], function(angular, template) { +define(['angular', 'jquery', 'text!partials/buddycondensed.html', 'hoverIntent'], function(angular, $, template) { // buddycondensed return [function() { @@ -58,6 +58,7 @@ define(['angular', 'text!partials/buddycondensed.html'], function(angular, templ }; var joined = function(buddy) { buddycondensed.push(buddy); + buddycondensed.push(angular.extend({}, buddy)); }; var left = function(id) { for (var i in buddycondensed) { @@ -135,7 +136,38 @@ define(['angular', 'text!partials/buddycondensed.html'], function(angular, templ }); }]; - var link = function($scope, elem, attrs, ctrl) {}; + var link = function($scope, elem, attrs, ctrl) { + var overDefaultDisplayNum = elem.find(".overDefaultDisplayNum"); + var desc = elem.find(".desc"); + var aboveElem1 = false; + var aboveElem2 = false; + var out = function(event) { + console.log('out', event.currentTarget.className); + if (event.currentTarget === desc.get(0)) { + aboveElem1 = false; + } else if (event.currentTarget === overDefaultDisplayNum.get(0)) { + aboveElem2 = false; + } + if(!aboveElem1 && !aboveElem2) { + overDefaultDisplayNum.hide(); + } + }; + var over = function(event) { + console.log('out', event.currentTarget.className); + if (event.currentTarget === desc.get(0)) { + aboveElem1 = true; + } else if (event.currentTarget === overDefaultDisplayNum.get(0)) { + aboveElem2 = true; + } + overDefaultDisplayNum.show(); + }; + elem.hoverIntent({ + over: over, + out: out, + timeout: 1000, + selector: '.desc, .overDefaultDisplayNum' + }); + }; return { restrict: 'E', diff --git a/static/js/libs/jquery/jquery.hoverIntent.js b/static/js/libs/jquery/jquery.hoverIntent.js new file mode 100644 index 00000000..ac2f1192 --- /dev/null +++ b/static/js/libs/jquery/jquery.hoverIntent.js @@ -0,0 +1,115 @@ +/*! + * hoverIntent v1.8.1 // 2014.08.11 // jQuery v1.9.1+ + * http://cherne.net/brian/resources/jquery.hoverIntent.html + * + * You may use hoverIntent under the terms of the MIT license. Basically that + * means you are free to use hoverIntent as long as this header is left intact. + * Copyright 2007, 2014 Brian Cherne + */ + +/* hoverIntent is similar to jQuery's built-in "hover" method except that + * instead of firing the handlerIn function immediately, hoverIntent checks + * to see if the user's mouse has slowed down (beneath the sensitivity + * threshold) before firing the event. The handlerOut function is only + * called after a matching handlerIn. + * + * // basic usage ... just like .hover() + * .hoverIntent( handlerIn, handlerOut ) + * .hoverIntent( handlerInOut ) + * + * // basic usage ... with event delegation! + * .hoverIntent( handlerIn, handlerOut, selector ) + * .hoverIntent( handlerInOut, selector ) + * + * // using a basic configuration object + * .hoverIntent( config ) + * + * @param handlerIn function OR configuration object + * @param handlerOut function OR selector for delegation OR undefined + * @param selector selector OR undefined + * @author Brian Cherne + */ +(function($) { + $.fn.hoverIntent = function(handlerIn,handlerOut,selector) { + + // default configuration values + var cfg = { + interval: 100, + sensitivity: 6, + timeout: 0 + }; + + if ( typeof handlerIn === "object" ) { + cfg = $.extend(cfg, handlerIn ); + } else if ($.isFunction(handlerOut)) { + cfg = $.extend(cfg, { over: handlerIn, out: handlerOut, selector: selector } ); + } else { + cfg = $.extend(cfg, { over: handlerIn, out: handlerIn, selector: handlerOut } ); + } + + // instantiate variables + // cX, cY = current X and Y position of mouse, updated by mousemove event + // pX, pY = previous X and Y position of mouse, set by mouseover and polling interval + var cX, cY, pX, pY; + + // A private function for getting mouse position + var track = function(ev) { + cX = ev.pageX; + cY = ev.pageY; + }; + + // A private function for comparing current and previous mouse position + var compare = function(ev,ob) { + ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); + // compare mouse positions to see if they've crossed the threshold + if ( Math.sqrt( (pX-cX)*(pX-cX) + (pY-cY)*(pY-cY) ) < cfg.sensitivity ) { + $(ob).off("mousemove.hoverIntent",track); + // set hoverIntent state to true (so mouseOut can be called) + ob.hoverIntent_s = true; + return cfg.over.apply(ob,[ev]); + } else { + // set previous coordinates for next time + pX = cX; pY = cY; + // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs) + ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval ); + } + }; + + // A private function for delaying the mouseOut function + var delay = function(ev,ob) { + ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); + ob.hoverIntent_s = false; + return cfg.out.apply(ob,[ev]); + }; + + // A private function for handling mouse 'hovering' + var handleHover = function(e) { + // copy objects to be passed into t (required for event object to be passed in IE) + var ev = $.extend({},e); + var ob = this; + + // cancel hoverIntent timer if it exists + if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); } + + // if e.type === "mouseenter" + if (e.type === "mouseenter") { + // set "previous" X and Y position based on initial entry point + pX = ev.pageX; pY = ev.pageY; + // update "current" X and Y position based on mousemove + $(ob).on("mousemove.hoverIntent",track); + // start polling interval (self-calling timeout) to compare mouse coordinates over time + if (!ob.hoverIntent_s) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );} + + // else e.type == "mouseleave" + } else { + // unbind expensive mousemove event + $(ob).off("mousemove.hoverIntent",track); + // if hoverIntent state is true, then call the mouseOut function after the specified delay + if (ob.hoverIntent_s) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );} + } + }; + + // listen for mouseenter and mouseleave + return this.on({'mouseenter.hoverIntent':handleHover,'mouseleave.hoverIntent':handleHover}, cfg.selector); + }; +})(jQuery); diff --git a/static/js/main.js b/static/js/main.js index 91bbe9fb..7d42e65a 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -43,6 +43,7 @@ require.config({ 'avltree': 'libs/avltree', 'injectCSS': 'libs/jquery/jquery.injectCSS', 'mobile-events': 'libs/jquery/jquery.mobile-events', + 'hoverIntent': 'libs/jquery/jquery.hoverIntent', 'jed': 'libs/jed', 'audiocontext': 'libs/audiocontext', 'rAF': 'libs/rAF', @@ -112,6 +113,10 @@ require.config({ deps: ['jquery'], exports: '$' }, + 'hoverIntent': { + deps: ['jquery'], + exports: '$' + }, 'sjcl': { exports: 'sjcl' }, diff --git a/static/partials/buddycondensed.html b/static/partials/buddycondensed.html index 2ae0ea02..5ca3f45e 100644 --- a/static/partials/buddycondensed.html +++ b/static/partials/buddycondensed.html @@ -1,6 +1,6 @@
- + {{listOverDefault().length - maxBuddiesToShow}} {{_("more")}} + + {{listOverDefault().length}} {{_("more")}}
From 74dd5ca59090645cf40893b6a86e6f8cb085a2f9 Mon Sep 17 00:00:00 2001 From: Evan Theurer Date: Wed, 24 Sep 2014 12:33:20 +0200 Subject: [PATCH 6/7] Use hoverIntent for actions. --- src/styles/components/_buddycondensed.scss | 3 --- static/js/directives/buddycondensed.js | 26 +++++++++++++++++----- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/styles/components/_buddycondensed.scss b/src/styles/components/_buddycondensed.scss index f92a77d3..1712e3b2 100644 --- a/src/styles/components/_buddycondensed.scss +++ b/src/styles/components/_buddycondensed.scss @@ -84,8 +84,5 @@ .buddyPicture { cursor: pointer; overflow: visible; - &:hover .actions { - display: inline-block; - } } } diff --git a/static/js/directives/buddycondensed.js b/static/js/directives/buddycondensed.js index b2f7b0bc..5da426f5 100644 --- a/static/js/directives/buddycondensed.js +++ b/static/js/directives/buddycondensed.js @@ -141,8 +141,8 @@ define(['angular', 'jquery', 'text!partials/buddycondensed.html', 'hoverIntent'] var desc = elem.find(".desc"); var aboveElem1 = false; var aboveElem2 = false; - var out = function(event) { - console.log('out', event.currentTarget.className); + var outMoreBuddy = function(event) { + //console.log('out', event.currentTarget.className); if (event.currentTarget === desc.get(0)) { aboveElem1 = false; } else if (event.currentTarget === overDefaultDisplayNum.get(0)) { @@ -152,8 +152,8 @@ define(['angular', 'jquery', 'text!partials/buddycondensed.html', 'hoverIntent'] overDefaultDisplayNum.hide(); } }; - var over = function(event) { - console.log('out', event.currentTarget.className); + var overMoreBuddy = function(event) { + //console.log('out', event.currentTarget.className); if (event.currentTarget === desc.get(0)) { aboveElem1 = true; } else if (event.currentTarget === overDefaultDisplayNum.get(0)) { @@ -162,11 +162,25 @@ define(['angular', 'jquery', 'text!partials/buddycondensed.html', 'hoverIntent'] overDefaultDisplayNum.show(); }; elem.hoverIntent({ - over: over, - out: out, + over: overMoreBuddy, + out: outMoreBuddy, timeout: 1000, selector: '.desc, .overDefaultDisplayNum' }); + var overBuddyPicture = function(event) { + //console.log('overBuddyPicture', event.currentTarget.className); + $(event.currentTarget).find(".actions").css("display", "inline-block"); + }; + var outBuddyPicture = function(event) { + //console.log('outBuddyPicture', event.currentTarget.className); + $(event.currentTarget).find(".actions").css("display", "none"); + }; + elem.hoverIntent({ + over: overBuddyPicture, + out: outBuddyPicture, + timeout: 100, + selector: '.buddyPicture' + }); }; return { From e416c01b7f9f0bb18ee056449cfe6712e46fa590 Mon Sep 17 00:00:00 2001 From: Evan Theurer Date: Wed, 24 Sep 2014 14:13:44 +0200 Subject: [PATCH 7/7] Improve buddy sorting. Remove join create extra buddys. --- src/styles/components/_buddycondensed.scss | 41 ++++++++++++++++++++-- static/js/directives/buddycondensed.js | 21 +++++------ 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/styles/components/_buddycondensed.scss b/src/styles/components/_buddycondensed.scss index 1712e3b2..23dfc57f 100644 --- a/src/styles/components/_buddycondensed.scss +++ b/src/styles/components/_buddycondensed.scss @@ -22,6 +22,15 @@ display: inline-block; position: fixed; padding-left: 20px; + @media (max-width: 1125px) { + display: none; + } + @media (max-width: 480px) { + display: none; + padding-left: 0; + position: relative; + width: 80%; + } } .buddycondensed { &.buddy { @@ -36,20 +45,36 @@ } } .defaultDisplayNum { + @media (max-width: 480px) { + padding: 10px 0px; + } } .overDefaultDisplayNum { background: #f8f8f8; display: none; + max-width: 80%; position: fixed; top: 49px; + @media (max-width: 480px) { + overflow-y: auto; + max-height: 400px; + top: 109px; + } } .desc { display: none; font-weight: bold; padding-left: 10px; cursor: default; + @media (max-width: 480px) { + position: absolute; + top: 50px; + } &.overNum { display: inline-block; + @media (max-width: 480px) { + display: block; + } } } .actions { @@ -61,13 +86,21 @@ cursor: default; height: 51px; padding: 0 15px; - bottom: 10px; + bottom: 12px; position: relative; white-space: nowrap; z-index: 10; + @media (max-width: 480px) { + height: 60px; + padding: 0 5px; + bottom: 22px; + } .btn-group { margin-bottom: 5px; width: 55px; + @media (max-width: 480px) { + display: block; + } } .btn-primary { padding: 2px 4px; @@ -79,10 +112,14 @@ } .displayName { margin-right: 10px; + @media (max-width: 480px) { + line-height: 22px; + display: block; + } } } .buddyPicture { - cursor: pointer; + cursor: default; overflow: visible; } } diff --git a/static/js/directives/buddycondensed.js b/static/js/directives/buddycondensed.js index 5da426f5..0d97044e 100644 --- a/static/js/directives/buddycondensed.js +++ b/static/js/directives/buddycondensed.js @@ -34,15 +34,14 @@ define(['angular', 'jquery', 'text!partials/buddycondensed.html', 'hoverIntent'] return session && session.Id ? session.Id : null; }; var empty = function(x) { - return x === null || x === undefined || isNaN(x) || x === ""; + return x === null || x === undefined || x === ""; }; var sortCondensed = function() { - var unnamed = buddycondensed.length; buddycondensed.sort(function(current, next) { - if(!current.Status || current.Status && empty(current.Status.displayName)) { + if (!current.Status || current.Status && empty(current.Status.displayName)) { return 1; } else { - if(next.Status && !empty(next.Status.displayName)) { + if (next.Status && !empty(next.Status.displayName)) { if (current.Status.displayName < next.Status.displayName) { return -1; } else if (current.Status.displayName > next.Status.displayName) { @@ -58,11 +57,10 @@ define(['angular', 'jquery', 'text!partials/buddycondensed.html', 'hoverIntent'] }; var joined = function(buddy) { buddycondensed.push(buddy); - buddycondensed.push(angular.extend({}, buddy)); }; var left = function(id) { for (var i in buddycondensed) { - if(buddycondensed[i].Id === id) { + if (buddycondensed[i].Id === id) { buddycondensed.splice(i,1); break; } @@ -74,14 +72,13 @@ define(['angular', 'jquery', 'text!partials/buddycondensed.html', 'hoverIntent'] var hasSession = false; for (var i in buddycondensed) { // replace session data with contact data - if(buddycondensed[i].Userid === data.Userid) { + if (buddycondensed[i].Userid === data.Userid) { buddycondensed[i] = data; - //console.log('contactadded replaced', 'data', data.Status && data.Status.displayName, 'buddycondensed[i]', buddycondensed[i].Status && buddycondensed[i].Status.displayName); hasSession = true; break; } } - if(!hasSession) { + if (!hasSession) { joined(data); } $scope.$apply(); @@ -96,14 +93,14 @@ define(['angular', 'jquery', 'text!partials/buddycondensed.html', 'hoverIntent'] }); }; $scope.listDefault = function() { - if(buddycondensed.length >= $scope.maxBuddiesToShow) { + if (buddycondensed.length >= $scope.maxBuddiesToShow) { return buddycondensed.slice(0, $scope.maxBuddiesToShow); } else { return buddycondensed; } }; $scope.listOverDefault = function() { - if(buddycondensed.length >= $scope.maxBuddiesToShow) { + if (buddycondensed.length >= $scope.maxBuddiesToShow) { return buddycondensed.slice($scope.maxBuddiesToShow); } else { return []; @@ -148,7 +145,7 @@ define(['angular', 'jquery', 'text!partials/buddycondensed.html', 'hoverIntent'] } else if (event.currentTarget === overDefaultDisplayNum.get(0)) { aboveElem2 = false; } - if(!aboveElem1 && !aboveElem2) { + if (!aboveElem1 && !aboveElem2) { overDefaultDisplayNum.hide(); } };