From 481a0fd479862517e19f21c2fc53f0f9b918827d Mon Sep 17 00:00:00 2001 From: David Witter Date: Thu, 22 Mar 2018 10:15:04 -0500 Subject: [PATCH 01/15] refactor: remove unecessary controller --- .../main/webapp/my-app/layout/controllers.js | 365 +++--------------- .../layout/list/partials/home-list-view.html | 2 +- .../my-app/layout/partials/remove-button.html | 2 +- 3 files changed, 61 insertions(+), 308 deletions(-) diff --git a/web/src/main/webapp/my-app/layout/controllers.js b/web/src/main/webapp/my-app/layout/controllers.js index ef98bbf4a..642a28a0c 100644 --- a/web/src/main/webapp/my-app/layout/controllers.js +++ b/web/src/main/webapp/my-app/layout/controllers.js @@ -36,16 +36,54 @@ define(['angular', 'jquery'], function(angular, $) { $location.path('/' + $localStorage.layoutMode); }]) + .controller('RemoveWidgetController', ['$scope', '$filter', '$sessionStorage', + '$mdToast', 'layoutService', function($scope, $filter, layoutService, + $mdToast, $sessionStorage) { + var vm = this; + /** + * Show toast message confirming widget removal + * @param fname + */ + vm.removeWidget = function(fname) { + + }; + + var confirmWidgetRemoval = function() { + // layoutService.removeFromHome(fname).success(function() { + // // Filter for fname match in layout + // var result = $filter('filter')($scope.$parent.layout, fname); + // var index = $scope.$parent.layout.indexOf(result[0]); + // + // // Remove from layout + // $scope.$apply($scope.$parent.layout.splice(index, 1)); + // + // // Clear marketplace flag + // if ($sessionStorage.marketplace != null) { + // // Filter for fname match in marketplace + // var marketplaceEntries = $filter('filter')( + // $sessionStorage.marketplace, result[0].fname + // ); + // if (marketplaceEntries.length > 0) { + // // Remove the entry flag + // marketplaceEntries[0].hasInLayout = false; + // } + // } + // }).error( + // function(request, text, error) { + // alert('Issue deleting ' + fname + + // ' from your list of favorites, try again later.'); + // }); + }; + }]) + /** - * Controller for the compact mode widget layout - * (layout/list/partials/home-list-view.html and - * layout/partials/default-card.html) + * Widget initialization and sorting for expanded mode widget layout + * (/widget/partials/home-widget-view.html and + * /widget/partials/widget-card.html) */ - .controller('LayoutController', - ['$localStorage', '$log', '$sessionStorage', - '$scope', '$rootScope', 'layoutService', - function($localStorage, $log, $sessionStorage, - $scope, $rootScope, layoutService) { + .controller('WidgetController', + ['$controller', '$log', '$scope', '$rootScope', 'layoutService', + function($controller, $log, $scope, $rootScope, layoutService) { var vm = this; $scope.selectedNodeId = ''; @@ -92,7 +130,7 @@ define(['angular', 'jquery'], function(angular, $) { var nextIndex = currentIndex + 1; // left or up - if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') { + if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') { // stop element from losing focus event.preventDefault(); // if currentIndex is already 0, do nothing @@ -168,27 +206,14 @@ define(['angular', 'jquery'], function(angular, $) { }; /** - * Set the href based on whether it's a static, exclusive, - * or basic widget (based on attributes from entity file) - * @param portlet - * @return {String} + * + * @param fname */ - vm.renderURL = function(portlet) { - if (portlet.staticContent != null && portlet.altMaxUrl == false) { - return 'static/' + portlet.fname; - } else if (portlet.altMaxUrl == false && - (portlet.renderOnWeb || $localStorage.webPortletRender)) { - return 'exclusive/' + portlet.fname; - } else { - return portlet.url; - } - }; - $rootScope.addPortletToHome = function(fname) { - layoutService.addToLayoutByFname(fname).success(function() { - layoutService.getUncachedLayout().then(function(data) { - $scope.$apply($scope.layout.unshift(data.layout[0])); - return true; + layoutService.addToLayoutByFname(fname).success(function() { + layoutService.getUncachedLayout().then(function(data) { + $scope.$apply($scope.layout.unshift(data.layout[0])); + return true; }).catch(function() { $log.warn('Could not getLayout while adding to home'); return false; @@ -199,288 +224,16 @@ define(['angular', 'jquery'], function(angular, $) { /** * Add widget to home layout */ - vm.addPortlet = function addPortletFunction(fname) { - $rootScope.addToLayoutByFname(fname).success(function() { - $scope.$apply(function() { - $sessionStorage.layout = $scope.layout; - }); - }).error( - function() { - $sessionStorage.layout = layoutService.getLayout(); - }); - }; - - /** - * Initialize LayoutController - */ - vm.init = function() { - if (angular.isUndefined($rootScope.layout) || - $rootScope.layout == null) { - $rootScope.layout = []; - $scope.layoutEmpty = false; - - // Get user's home layout - layoutService.getLayout().then(function(data) { - $rootScope.layout = data.layout; - $scope.layout = data.layout; - if (data.layout && data.layout.length == 0) { - $scope.layoutEmpty = true; - } - return data; - }).catch(function() { - $log.warn('Could not getLayout'); + vm.addPortlet = function addPortletFunction(fname) { + $rootScope.addToLayoutByFname(fname).success(function() { + $scope.$apply(function() { + $sessionStorage.layout = $scope.layout; }); - } - }; - - vm.init(); - }]) - - .controller('RemoveWidgetController', ['$scope', '$filter', 'layoutService', - '$sessionStorage', function($scope, $filter, layoutService, - $sessionStorage) { - var vm = this; - /** - * Remove widget from home layout - * @param fname - */ - vm.removePortlet = function removePortletFunction(fname) { - layoutService.removeFromHome(fname).success(function() { - // Filter for fname match in layout - var result = $filter('filter')($scope.$parent.layout, fname); - var index = $scope.$parent.layout.indexOf(result[0]); - - // Remove from layout - $scope.$apply($scope.$parent.layout.splice(index, 1)); - - // Clear marketplace flag - if ($sessionStorage.marketplace != null) { - // Filter for fname match in marketplace - var marketplaceEntries = $filter('filter')( - $sessionStorage.marketplace, result[0].fname - ); - if (marketplaceEntries.length > 0) { - // Remove the entry flag - marketplaceEntries[0].hasInLayout = false; - } - } }).error( - function(request, text, error) { - alert('Issue deleting ' + fname + - ' from your list of favorites, try again later.'); + function() { + $sessionStorage.layout = layoutService.getLayout(); }); }; - }]) - - /** - * Basic widget logic leveraged by WidgetController, - * expanded mode widget layout - * (/widget/partials/home-widget-view.html and - * /widget/partials/widget-card.html), - * and 'widget' component (/widget/directives.js) - */ - .controller('BaseWidgetFunctionsController', - ['$scope', '$sessionStorage', '$localStorage', - 'layoutService', 'childController', - function($scope, $sessionStorage, $localStorage, - layoutService, childController) { - /** - * Determine the type of widget to display - * @param portlet - * @return {*} - */ - childController.portletType = function portletType(portlet) { - // If portlet has a defined widgetType, - // check if it's one we have a defined template for - if (portlet.widgetType) { - if ('option-link' === portlet.widgetType) { - return 'OPTION_LINK'; - } else if ('weather' === portlet.widgetType) { - return 'WEATHER'; - } else if ('rss' === portlet.widgetType) { - return 'RSS'; - } else if ('list-of-links' === portlet.widgetType) { - if (portlet.widgetConfig.links.length === 1 && - portlet.altMaxUrl && - portlet.widgetConfig.links[0].href === portlet.url) { - // If list of links has only one link and - // if it is the same as the portlet URL, - // display the default widget view - return 'BASIC'; - } else { - return 'LOL'; - } - } else if ('search-with-links' === portlet.widgetType) { - return 'SWL'; - } else if ('generic' === portlet.widgetType) { - // DEPRECATED: Include 'generic' for the - // sake of backwards compatibility, - // but return what it really is: CUSTOM - return 'CUSTOM'; - } else if ('custom' === portlet.widgetType) { - return 'CUSTOM'; - } else { - return 'WIDGET'; - } - } else { - // Return "BASIC" widget type for anything - // else lacking an explicit widget - // type definition (default experience) - return 'BASIC'; - } - }; - - /** - * Sets href attribute for 'BASIC' type widget - * @param portlet - * @return {*} - */ - childController.renderURL = function renderURL(portlet) { - // Check if it's a static or exclusive portlet - if (portlet.staticContent != null && portlet.altMaxUrl == false) { - return 'static/' + portlet.fname; - } else if (portlet.altMaxUrl == false && (portlet.renderOnWeb || - $localStorage.webPortletRender)) { - return 'exclusive/' + portlet.fname; - } else { - return portlet.url; - } - }; - }, - ]) - - /** - * Widget initialization and sorting for expanded mode widget layout - * (/widget/partials/home-widget-view.html and - * /widget/partials/widget-card.html) - */ - .controller('WidgetController', - ['$controller', '$log', '$scope', '$rootScope', 'layoutService', - function($controller, $log, $scope, $rootScope, layoutService) { - var vm = this; - $scope.selectedNodeId = ''; - - // Inherit from BaseWidgetFunctionsController - $controller('BaseWidgetFunctionsController', - {$scope: $scope, childController: vm}); - - /** - * Set the selected widget in scope to track focus - * @param nodeId {string} The id of the selected widget - */ - $scope.selectNode = function(nodeId) { - $scope.selectedNodeId = nodeId; - }; - - /** - * Log whenever a widget is moved - * @param eventType {String} Kind of interaction that moved the widget - * @param fname {String} The moved widget's fname - * @param dropIndex {Number} Index where the widget landed - * @param startIndex {Number} (optional) Index before moving the widget - * @return - */ - $scope.logEvent = function(eventType, fname, dropIndex, startIndex) { - switch (eventType) { - case 'dragEnd': - $log.info('Dragged ' + fname + ' to index ' + dropIndex); - break; - case 'keyboardMove': - $log.info('Moved ' + fname + 'from index ' + startIndex + - ' to index ' + dropIndex); - break; - default: - return; - } - }; - - /** - * Respond to arrow key-presses when focusing a movable list element - * @param widget {Object} The widget trying to move - * @param event {Object} The event object - */ - $scope.moveWithKeyboard = function(widget, event) { - // get index independent of ng-repeat to avoid filter bugs - var currentIndex = - findLayoutIndex($scope.layout, 'nodeId', widget.nodeId); - var previousIndex = currentIndex - 1; - var nextIndex = currentIndex + 1; - - // left or up - if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') { - // stop element from losing focus - event.preventDefault(); - // if currentIndex is already 0, do nothing - if (currentIndex !== 0) { - // remove item from the list - $scope.layout.splice(currentIndex, 1); - // reinsert at new index - $scope.layout.splice(previousIndex, 0, widget); - // save new layout order - saveLayoutOrder(previousIndex, - $scope.layout.length, - widget.nodeId); - // log change - $scope.logEvent( 'keyboardMove', - widget.fname, - previousIndex, - currentIndex); - } - } - // right or down - if (event.key === 'ArrowRight' || event.key === 'ArrowDown') { - // stop screen from scrolling - event.preventDefault(); - // if currentIndex is end of the list, do nothing - if (currentIndex !== $scope.layout.length - 1) { - // remove item from the list - $scope.layout.splice(currentIndex, 1); - // reinsert at desired index - $scope.layout.splice(nextIndex, 0, widget); - // save new layout order - saveLayoutOrder(nextIndex, $scope.layout.length, widget.nodeId); - // log change - $scope.logEvent('keyboardMove', - widget.fname, - nextIndex, - currentIndex); - } - } - }; - - /** - * After a widget is moved, save the new layout using - * the given information - * @param dropIndex {Number} Where the widget ended up - * @param length {Number} Length of the layout array - * @param nodeId {String} ID of the moved widget - */ - var saveLayoutOrder = function(dropIndex, length, nodeId) { - // identify previous and next widgets - var previousNodeId = - dropIndex !== 0 ? $scope.layout[dropIndex - 1].nodeId : ''; - var nextNodeId = - dropIndex !== length - 1 ? $scope.layout[dropIndex + 1].nodeId : ''; - // call layout service to save - layoutService.moveStuff(dropIndex, - length, nodeId, previousNodeId, nextNodeId); - }; - - /** - * Find an array object with the given attribute/value pair - * @param array {Array} The array to iterate on - * @param attribute {String} The name of the attribute - * @param value {String} The value to match on - * @return {number} Index of the desired item or -1 - */ - var findLayoutIndex = function(array, attribute, value) { - for (var i = 0; i < array.length; i+= 1) { - if (array[i][attribute] === value) { - return i; - } - } - return -1; - }; /** * Initialize expanded mode widget layout diff --git a/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html b/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html index 5ede10d09..c784be7c6 100644 --- a/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html +++ b/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html @@ -20,7 +20,7 @@ --> -
+
diff --git a/web/src/main/webapp/my-app/layout/partials/remove-button.html b/web/src/main/webapp/my-app/layout/partials/remove-button.html index a01a635e7..bc1003bcc 100644 --- a/web/src/main/webapp/my-app/layout/partials/remove-button.html +++ b/web/src/main/webapp/my-app/layout/partials/remove-button.html @@ -20,7 +20,7 @@ --> close From 6373ad6f085f7ea8f505c0226fad1abc213c2806 Mon Sep 17 00:00:00 2001 From: David Witter Date: Fri, 23 Mar 2018 10:19:31 -0500 Subject: [PATCH 02/15] fix: add toast message --- .../main/webapp/my-app/layout/controllers.js | 81 ++++++++++++------- .../my-app/layout/partials/remove-button.html | 2 +- .../layout/partials/toast-widget-removal.html | 11 +++ 3 files changed, 66 insertions(+), 28 deletions(-) create mode 100644 web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html diff --git a/web/src/main/webapp/my-app/layout/controllers.js b/web/src/main/webapp/my-app/layout/controllers.js index 642a28a0c..abd4f3a39 100644 --- a/web/src/main/webapp/my-app/layout/controllers.js +++ b/web/src/main/webapp/my-app/layout/controllers.js @@ -41,38 +41,65 @@ define(['angular', 'jquery'], function(angular, $) { $mdToast, $sessionStorage) { var vm = this; /** - * Show toast message confirming widget removal + * Remove widget from visible layout, then trigger confirmation toast + * before persisting the layout change * @param fname */ - vm.removeWidget = function(fname) { + vm.removeWidget = function(title, fname) { + // Filter for fname match in layout + var result = $filter('filter')($scope.$parent.layout, fname); + var index = $scope.$parent.layout.indexOf(result[0]); + // Remove from layout + $scope.$parent.layout.splice(index, 1); + + // Trigger confirmation toast + confirmWidgetRemoval(title, fname); }; - var confirmWidgetRemoval = function() { - // layoutService.removeFromHome(fname).success(function() { - // // Filter for fname match in layout - // var result = $filter('filter')($scope.$parent.layout, fname); - // var index = $scope.$parent.layout.indexOf(result[0]); - // - // // Remove from layout - // $scope.$apply($scope.$parent.layout.splice(index, 1)); - // - // // Clear marketplace flag - // if ($sessionStorage.marketplace != null) { - // // Filter for fname match in marketplace - // var marketplaceEntries = $filter('filter')( - // $sessionStorage.marketplace, result[0].fname - // ); - // if (marketplaceEntries.length > 0) { - // // Remove the entry flag - // marketplaceEntries[0].hasInLayout = false; - // } - // } - // }).error( - // function(request, text, error) { - // alert('Issue deleting ' + fname + - // ' from your list of favorites, try again later.'); - // }); + + var confirmWidgetRemoval = function(title, fname) { + $mdToast.show({ + hideDelay: false, + parent: angular.element('.layout-list'), + position: 'top right', + scope: $scope, + templateUrl: + require.toUrl('my-app/layout/partials/toast-widget-removal.html'), + controller: function RemoveWidgetToastController( + $scope, + $sessionStorage, + layoutService + ) { + console.log(angular.element('.layout-list').scope()); + console.log(angular.element('.toast__widget-removal').scope()); + // If user clicked OK, persist change + // layoutService.removeFromHome(fname).success(function() { + // // Filter for fname match in layout + // var result = $filter('filter')($scope.$parent.layout, fname); + // var index = $scope.$parent.layout.indexOf(result[0]); + // + // // Remove from layout + // $scope.$apply($scope.$parent.layout.splice(index, 1)); + // + // // Clear marketplace flag + // if ($sessionStorage.marketplace != null) { + // // Filter for fname match in marketplace + // var marketplaceEntries = $filter('filter')( + // $sessionStorage.marketplace, result[0].fname + // ); + // if (marketplaceEntries.length > 0) { + // // Remove the entry flag + // marketplaceEntries[0].hasInLayout = false; + // } + // } + // }).error( + // function(request, text, error) { + // alert('Issue deleting ' + fname + + // ' from your list of favorites, try again later.'); + // }); + } + }); }; }]) diff --git a/web/src/main/webapp/my-app/layout/partials/remove-button.html b/web/src/main/webapp/my-app/layout/partials/remove-button.html index bc1003bcc..1b567a340 100644 --- a/web/src/main/webapp/my-app/layout/partials/remove-button.html +++ b/web/src/main/webapp/my-app/layout/partials/remove-button.html @@ -20,7 +20,7 @@ --> close diff --git a/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html b/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html new file mode 100644 index 000000000..f47f8d6ca --- /dev/null +++ b/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html @@ -0,0 +1,11 @@ + +
+ Removed {{ title }} + + Undo + + + OK + +
+
From e3be8fcbc88e83644360c7fdf508878f43d1fefb Mon Sep 17 00:00:00 2001 From: David Witter Date: Fri, 23 Mar 2018 12:53:22 -0500 Subject: [PATCH 03/15] feat: prompt confirm/undo of widget removal --- CHANGELOG.md | 4 +- .../main/webapp/my-app/layout/controllers.js | 190 ++++++++++++------ .../layout/list/partials/home-list-view.html | 1 + .../my-app/layout/partials/remove-button.html | 4 +- .../layout/partials/toast-widget-removal.html | 6 +- .../widget/partials/home-widget-view.html | 1 + 6 files changed, 136 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ebc0c90c..bfc697a1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,10 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - +* Removing widget from home screen now prompts for confirmation (allowing UNDO action) before saving changes (#805) ### Fixed - +* Fixed bug that sometimes caused the wrong widget to be removed from user's layout (#804) ### Dependency upgrades diff --git a/web/src/main/webapp/my-app/layout/controllers.js b/web/src/main/webapp/my-app/layout/controllers.js index abd4f3a39..03c5f747d 100644 --- a/web/src/main/webapp/my-app/layout/controllers.js +++ b/web/src/main/webapp/my-app/layout/controllers.js @@ -36,70 +36,26 @@ define(['angular', 'jquery'], function(angular, $) { $location.path('/' + $localStorage.layoutMode); }]) - .controller('RemoveWidgetController', ['$scope', '$filter', '$sessionStorage', - '$mdToast', 'layoutService', function($scope, $filter, layoutService, - $mdToast, $sessionStorage) { + .controller('RemoveWidgetController', ['$scope', '$filter', + function($scope, $filter) { var vm = this; /** - * Remove widget from visible layout, then trigger confirmation toast - * before persisting the layout change - * @param fname + * Capture information about removed widget, then + * pass it up the chain to the layout scope in + * WidgetController + * @param title {String} Human-readable widget title + * @param fname {String} Widget fname for matching in layout */ vm.removeWidget = function(title, fname) { - // Filter for fname match in layout + // Match layout entry with fname var result = $filter('filter')($scope.$parent.layout, fname); var index = $scope.$parent.layout.indexOf(result[0]); - - // Remove from layout - $scope.$parent.layout.splice(index, 1); - - // Trigger confirmation toast - confirmWidgetRemoval(title, fname); - }; - - - var confirmWidgetRemoval = function(title, fname) { - $mdToast.show({ - hideDelay: false, - parent: angular.element('.layout-list'), - position: 'top right', - scope: $scope, - templateUrl: - require.toUrl('my-app/layout/partials/toast-widget-removal.html'), - controller: function RemoveWidgetToastController( - $scope, - $sessionStorage, - layoutService - ) { - console.log(angular.element('.layout-list').scope()); - console.log(angular.element('.toast__widget-removal').scope()); - // If user clicked OK, persist change - // layoutService.removeFromHome(fname).success(function() { - // // Filter for fname match in layout - // var result = $filter('filter')($scope.$parent.layout, fname); - // var index = $scope.$parent.layout.indexOf(result[0]); - // - // // Remove from layout - // $scope.$apply($scope.$parent.layout.splice(index, 1)); - // - // // Clear marketplace flag - // if ($sessionStorage.marketplace != null) { - // // Filter for fname match in marketplace - // var marketplaceEntries = $filter('filter')( - // $sessionStorage.marketplace, result[0].fname - // ); - // if (marketplaceEntries.length > 0) { - // // Remove the entry flag - // marketplaceEntries[0].hasInLayout = false; - // } - // } - // }).error( - // function(request, text, error) { - // alert('Issue deleting ' + fname + - // ' from your list of favorites, try again later.'); - // }); - } - }); + var data = { + removedTitle: title, + removedName: fname, + removedIndex: index, + }; + $scope.$emit('REMOVE_WIDGET', data); }; }]) @@ -109,10 +65,14 @@ define(['angular', 'jquery'], function(angular, $) { * /widget/partials/widget-card.html) */ .controller('WidgetController', - ['$controller', '$log', '$scope', '$rootScope', 'layoutService', - function($controller, $log, $scope, $rootScope, layoutService) { + ['$controller', '$log', '$scope', '$rootScope', '$mdToast', + '$sessionStorage', '$filter', 'layoutService', + function($controller, $log, $scope, $rootScope, $mdToast, + $sessionStorage, $filter, layoutService) { var vm = this; $scope.selectedNodeId = ''; + $scope.hideWidgetIndex = null; + $scope.isToastDisplayed = false; /** * Set the selected widget in scope to track focus @@ -130,7 +90,7 @@ define(['angular', 'jquery'], function(angular, $) { * @param startIndex {Number} (optional) Index before moving the widget * @return */ - $scope.logEvent = function(eventType, fname, dropIndex, startIndex) { + $scope.logMoveEvent = function(eventType, fname, dropIndex, startIndex) { switch (eventType) { case 'dragEnd': $log.info('Dragged ' + fname + ' to index ' + dropIndex); @@ -171,7 +131,7 @@ define(['angular', 'jquery'], function(angular, $) { $scope.layout.length, widget.nodeId); // log change - $scope.logEvent( 'keyboardMove', + $scope.logMoveEvent( 'keyboardMove', widget.fname, previousIndex, currentIndex); @@ -190,7 +150,7 @@ define(['angular', 'jquery'], function(angular, $) { // save new layout order saveLayoutOrder(nextIndex, $scope.layout.length, widget.nodeId); // log change - $scope.logEvent('keyboardMove', + $scope.logMoveEvent('keyboardMove', widget.fname, nextIndex, currentIndex); @@ -198,6 +158,110 @@ define(['angular', 'jquery'], function(angular, $) { } }; + // Listen for removal event + $scope.$on('REMOVE_WIDGET', + /** + * Listen for widget removal event, then show confirmation toast + * if one is not already showing. + * @param event {Object} The angularjs event object + * @param data {Object} Data about the widget being removed + */ + function(event, data) { + // Make sure no toasts are already open + if (!$scope.isToastDisplayed) { + showConfirmationToast(data); + } + }); + + /** + * Show toast message allowing user to confirm or undo + * the removal of a widget from his/her layout. + * @param data {Object} Information about the removed widget + */ + var showConfirmationToast = function(data) { + $scope.removedTitle = data.removedTitle; + $scope.removedFname = data.removedName; + $scope.hideWidgetIndex = data.removedIndex; + + // Configure and show the toast message + $mdToast.show({ + hideDelay: false, + parent: angular.element(document).find('.layout-list')[0], + position: 'top right', + scope: $scope, + preserveScope: true, + templateUrl: + require.toUrl('my-app/layout/partials/toast-widget-removal.html'), + controller: function RemoveToastController( + $scope, + $sessionStorage, + $log, + $mdToast + ) { + $scope.isToastDisplayed = true; + /** + * Un-hide the widget with CSS + * and don't persist changes + */ + $scope.undoRemoveWidget = function() { + // Hide toast message + $mdToast.hide(); + // Reset CSS hiding + $scope.hideWidgetIndex = null; + // Reset toast displayed + $scope.isToastDisplayed = false; + }; + + /** + * Persist removal of the widget + */ + $scope.confirmRemoveWidget = function() { + // Hide toast message + $mdToast.hide(); + // Persist changes + saveLayoutRemoval($scope.removedFname, $scope.hideWidgetIndex); + // Reset toast displayed + $scope.isToastDisplayed = false; + }; + }, + }); + }; + + /** + * Call layout service to save the removal of the widget from the user's + * home layout. + * @param fname {String} The fname of the removed widget + * @param index {Number} The index of the removed widget in the layout + */ + var saveLayoutRemoval = function(fname, index) { + // Call layout service to persist change + layoutService.removeFromHome(fname) + .success(function() { + // Remove from layout in scope + $scope.layout.splice(index, 1); + + // Reset CSS hiding + $scope.hideWidgetIndex = null; + + // Clear marketplace flag + if ($sessionStorage.marketplace != null) { + // Filter for fname match in marketplace + var marketplaceEntries = $filter('filter')( + $sessionStorage.marketplace, fname + ); + if (marketplaceEntries.length > 0) { + // Remove the flag + marketplaceEntries[0].hasInLayout = false; + } + } + }) + .error(function(error) { + $log.debug('Problem deleting ' + fname + + 'from home screen. Try again later.'); + $log.debug(error); + }); + }; + /** * After a widget is moved, save the new layout using * the given information diff --git a/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html b/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html index c784be7c6..864565141 100644 --- a/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html +++ b/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html @@ -36,6 +36,7 @@ tabindex="0" aria-label="{{ widget.title }}" keep-focus + ng-class="{ 'hidden' : $index === hideWidgetIndex }" node-id="{{ widget.nodeId }}" selected-node-id="{{ selectedNodeId }}" index="{{ $index }}" diff --git a/web/src/main/webapp/my-app/layout/partials/remove-button.html b/web/src/main/webapp/my-app/layout/partials/remove-button.html index 1b567a340..7dec5d573 100644 --- a/web/src/main/webapp/my-app/layout/partials/remove-button.html +++ b/web/src/main/webapp/my-app/layout/partials/remove-button.html @@ -19,8 +19,8 @@ --> close diff --git a/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html b/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html index f47f8d6ca..831863b1d 100644 --- a/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html +++ b/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html @@ -1,10 +1,10 @@
- Removed {{ title }} - + Removed {{ removedTitle }} + Undo - + OK
diff --git a/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html b/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html index b64180290..8161e9188 100644 --- a/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html +++ b/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html @@ -34,6 +34,7 @@ tabindex="0" aria-label="{{ widget.title }}" keep-focus + ng-class="{ 'hidden' : $index === hideWidgetIndex }" node-id="{{ widget.nodeId }}" selected-node-id="{{ selectedNodeId }}" index="{{ $index }}" From 9f3b2534e5a78cae164faf83d5d42e8fefc1b7f9 Mon Sep 17 00:00:00 2001 From: David Witter Date: Fri, 23 Mar 2018 13:57:25 -0500 Subject: [PATCH 04/15] feat: adjust toast appearance to look like material notification --- web/src/main/webapp/css/my-app.less | 1 + web/src/main/webapp/css/toast.less | 68 +++++++++++++++++++ .../main/webapp/my-app/layout/controllers.js | 35 +++++++--- .../my-app/layout/partials/remove-button.html | 2 +- .../layout/partials/toast-widget-removal.html | 46 ++++++++++--- 5 files changed, 132 insertions(+), 20 deletions(-) create mode 100644 web/src/main/webapp/css/toast.less diff --git a/web/src/main/webapp/css/my-app.less b/web/src/main/webapp/css/my-app.less index 81938d3c2..8f6056cf5 100644 --- a/web/src/main/webapp/css/my-app.less +++ b/web/src/main/webapp/css/my-app.less @@ -24,6 +24,7 @@ @import "uw-portlet.less"; @import "marketplace.less"; @import "search-results.less"; + @import "toast.less"; } //Styles that need to be outside of .my-uw. e.g.: popups diff --git a/web/src/main/webapp/css/toast.less b/web/src/main/webapp/css/toast.less new file mode 100644 index 000000000..6b215ec73 --- /dev/null +++ b/web/src/main/webapp/css/toast.less @@ -0,0 +1,68 @@ +/** + * Licensed to Apereo under one or more contributor license + * agreements. See the NOTICE file distributed with this work + * for additional information regarding copyright ownership. + * Apereo licenses this file to you 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 the following location: + * + * 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. + */ +md-toast { + &.toast__widget-removal { + .md-toast-content { + background-color: @white; + color: @black; + padding: 0; + align-items: flex-start; + max-height: 126px; + max-width: 320px; + + &:before { + min-height: 0; + } + + .toast__message { + padding: 16px; + width: 100%; + + .toast__source { + font-size: 12px; + } + + .toast__icon-container { + display: flex; + height: 44px; + width: 44px; + max-width: 44px; + background: #e5e5e5; + border-radius: 50%; + justify-content: center; + align-items: center; + + .fa, + .material-icons { + font-size: 24px; + } + } + } + + .toast__actions { + background-color: @grayscale2; + width: 100%; + } + + .md-button { + min-width: 60px; + } + } + } +} diff --git a/web/src/main/webapp/my-app/layout/controllers.js b/web/src/main/webapp/my-app/layout/controllers.js index 03c5f747d..7ebba535e 100644 --- a/web/src/main/webapp/my-app/layout/controllers.js +++ b/web/src/main/webapp/my-app/layout/controllers.js @@ -43,17 +43,15 @@ define(['angular', 'jquery'], function(angular, $) { * Capture information about removed widget, then * pass it up the chain to the layout scope in * WidgetController - * @param title {String} Human-readable widget title - * @param fname {String} Widget fname for matching in layout + * @param widget {Object} The widget being removed */ - vm.removeWidget = function(title, fname) { + vm.removeWidget = function(widget) { // Match layout entry with fname - var result = $filter('filter')($scope.$parent.layout, fname); + var result = $filter('filter')($scope.$parent.layout, widget.fname); var index = $scope.$parent.layout.indexOf(result[0]); var data = { - removedTitle: title, - removedName: fname, removedIndex: index, + removedWidget: widget, }; $scope.$emit('REMOVE_WIDGET', data); }; @@ -66,9 +64,9 @@ define(['angular', 'jquery'], function(angular, $) { */ .controller('WidgetController', ['$controller', '$log', '$scope', '$rootScope', '$mdToast', - '$sessionStorage', '$filter', 'layoutService', + '$sessionStorage', '$filter', '$mdColors', 'layoutService', function($controller, $log, $scope, $rootScope, $mdToast, - $sessionStorage, $filter, layoutService) { + $sessionStorage, $filter, $mdColors, layoutService) { var vm = this; $scope.selectedNodeId = ''; $scope.hideWidgetIndex = null; @@ -179,14 +177,29 @@ define(['angular', 'jquery'], function(angular, $) { * @param data {Object} Information about the removed widget */ var showConfirmationToast = function(data) { - $scope.removedTitle = data.removedTitle; - $scope.removedFname = data.removedName; + // Add widget to scope for directive + $scope.widget = data.removedWidget; + + $scope.removedTitle = data.removedWidget.title; + $scope.removedFname = data.removedWidget.fname; $scope.hideWidgetIndex = data.removedIndex; + if ($sessionStorage.portal.theme) { + // theme already in session, use primary color from it + $scope.accentColorRgb = + $mdColors.getThemeColor($sessionStorage.portal.theme.name + + '-accent'); + } else { + // theme not yet in session, use primary color from zeroth theme + $scope.primaryColorRgb = + $mdColors.getThemeColor($rootScope.THEMES[0].name + + '-accent'); + } + // Configure and show the toast message $mdToast.show({ hideDelay: false, - parent: angular.element(document).find('.layout-list')[0], + parent: angular.element(document).find('.wrapper__frame-page')[0], position: 'top right', scope: $scope, preserveScope: true, diff --git a/web/src/main/webapp/my-app/layout/partials/remove-button.html b/web/src/main/webapp/my-app/layout/partials/remove-button.html index 8603b0293..f8685b941 100644 --- a/web/src/main/webapp/my-app/layout/partials/remove-button.html +++ b/web/src/main/webapp/my-app/layout/partials/remove-button.html @@ -20,7 +20,7 @@ --> close diff --git a/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html b/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html index 831863b1d..6e2e8251d 100644 --- a/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html +++ b/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html @@ -1,11 +1,41 @@ + -
- Removed {{ removedTitle }} - - Undo - - - OK - +
+
+
+ Layout changed + Removed {{ removedTitle }} +
+ + + +
+
+ + Undo + + + OK + +
From e6f8b71069a84cbb37af6075c8e8dde4a7bac3c4 Mon Sep 17 00:00:00 2001 From: David Witter Date: Fri, 23 Mar 2018 13:58:32 -0500 Subject: [PATCH 05/15] fix: resolve linting error --- web/src/main/webapp/css/toast.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/main/webapp/css/toast.less b/web/src/main/webapp/css/toast.less index 6b215ec73..f84d33547 100644 --- a/web/src/main/webapp/css/toast.less +++ b/web/src/main/webapp/css/toast.less @@ -26,7 +26,7 @@ md-toast { max-height: 126px; max-width: 320px; - &:before { + &::before { min-height: 0; } From 5174c672894c1d457b18d43c5409c93b08f03930 Mon Sep 17 00:00:00 2001 From: David Witter Date: Fri, 23 Mar 2018 14:00:12 -0500 Subject: [PATCH 06/15] fix: pad grid on smaller screens --- web/src/main/webapp/css/home.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/main/webapp/css/home.less b/web/src/main/webapp/css/home.less index a98da86b7..f13eb9a02 100644 --- a/web/src/main/webapp/css/home.less +++ b/web/src/main/webapp/css/home.less @@ -159,7 +159,7 @@ div.table-cell { .layout-list { list-style-type: none; - padding: 0; + padding: 0 12px; margin: auto; max-width: 1230px; From 3b815436415d7b47771cddab910087918d3f8dc4 Mon Sep 17 00:00:00 2001 From: David Witter Date: Fri, 23 Mar 2018 14:01:30 -0500 Subject: [PATCH 07/15] fix: pad grid on smaller screens --- web/src/main/webapp/css/home.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/main/webapp/css/home.less b/web/src/main/webapp/css/home.less index f13eb9a02..269a04229 100644 --- a/web/src/main/webapp/css/home.less +++ b/web/src/main/webapp/css/home.less @@ -161,7 +161,7 @@ div.table-cell { list-style-type: none; padding: 0 12px; margin: auto; - max-width: 1230px; + max-width: 1260px; /* Grid Fallback */ display: flex; From ce0838cd91dce0ef14c58cc200415afa7991e811 Mon Sep 17 00:00:00 2001 From: David Witter Date: Fri, 23 Mar 2018 14:27:50 -0500 Subject: [PATCH 08/15] fix: disable reordering when waiting confirmation --- web/src/main/webapp/my-app/layout/controllers.js | 6 +++++- .../webapp/my-app/layout/list/partials/home-list-view.html | 1 + .../webapp/my-app/layout/partials/toast-widget-removal.html | 2 +- .../my-app/layout/widget/partials/home-widget-view.html | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/web/src/main/webapp/my-app/layout/controllers.js b/web/src/main/webapp/my-app/layout/controllers.js index 7ebba535e..47c70987f 100644 --- a/web/src/main/webapp/my-app/layout/controllers.js +++ b/web/src/main/webapp/my-app/layout/controllers.js @@ -108,7 +108,11 @@ define(['angular', 'jquery'], function(angular, $) { * @param event {Object} The event object */ $scope.moveWithKeyboard = function(widget, event) { - // get index independent of ng-repeat to avoid filter bugs + if ($scope.isToastDisplayed) { + return; + } + + // Get index independent of ng-repeat to avoid filter bugs var currentIndex = findLayoutIndex($scope.layout, 'nodeId', widget.nodeId); var previousIndex = currentIndex - 1; diff --git a/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html b/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html index 864565141..86382b823 100644 --- a/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html +++ b/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html @@ -30,6 +30,7 @@
  • - Layout changed + Confirm change Removed {{ removedTitle }}
    diff --git a/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html b/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html index 8161e9188..162298042 100644 --- a/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html +++ b/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html @@ -28,6 +28,7 @@
    • Date: Fri, 23 Mar 2018 14:33:34 -0500 Subject: [PATCH 09/15] fix: adjust unit tests for widget controller --- web/src/main/webapp/my-app/layout/controllers.js | 7 ++++--- .../webapp/my-app/layout/spec/layout_controller_spec.js | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/web/src/main/webapp/my-app/layout/controllers.js b/web/src/main/webapp/my-app/layout/controllers.js index 47c70987f..0ba16f482 100644 --- a/web/src/main/webapp/my-app/layout/controllers.js +++ b/web/src/main/webapp/my-app/layout/controllers.js @@ -346,11 +346,12 @@ define(['angular', 'jquery'], function(angular, $) { /** * Initialize expanded mode widget layout */ - function init() { + vm.init = function() { if (angular.isUndefined($rootScope.layout) || $rootScope.layout == null) { $rootScope.layout = []; $scope.layoutEmpty = false; + // Get user's home layout layoutService.getLayout().then(function(data) { $rootScope.layout = data.layout; @@ -363,8 +364,8 @@ define(['angular', 'jquery'], function(angular, $) { $log.warn('Could not getLayout'); }); } - } + }; - init(); + vm.init(); }]); }); diff --git a/web/src/main/webapp/my-app/layout/spec/layout_controller_spec.js b/web/src/main/webapp/my-app/layout/spec/layout_controller_spec.js index 28c5cbcf8..7607190bd 100644 --- a/web/src/main/webapp/my-app/layout/spec/layout_controller_spec.js +++ b/web/src/main/webapp/my-app/layout/spec/layout_controller_spec.js @@ -20,7 +20,7 @@ /* eslint-env node, phantomjs, jasmine */ /* global inject */ define(['angular-mocks', 'portal', 'my-app'], function() { - describe('LayoutController', function() { + describe('WidgetController', function() { var scope; var controller; var $localStorage; @@ -67,7 +67,7 @@ define(['angular-mocks', 'portal', 'my-app'], function() { loginSilentURL = _SERVICE_LOC_.loginSilentURL; sharedPortletService = {}; - controller = $controller('LayoutController', { + controller = $controller('WidgetController', { '$localStorage': $localStorage, '$scope': scope, '$rootScope': rootScope, From 6fc48f5c5ca2ccaaa5ee4e68ea21fe6c3db11ecb Mon Sep 17 00:00:00 2001 From: David Witter Date: Tue, 27 Mar 2018 12:53:26 -0500 Subject: [PATCH 10/15] fix: change removal behavior and animate --- web/src/main/webapp/css/home.less | 67 ++++++++----- web/src/main/webapp/css/variables.less | 3 +- .../main/webapp/my-app/layout/controllers.js | 94 +++++++++---------- .../layout/partials/toast-widget-removal.html | 3 - 4 files changed, 90 insertions(+), 77 deletions(-) diff --git a/web/src/main/webapp/css/home.less b/web/src/main/webapp/css/home.less index 269a04229..acff9e8ca 100644 --- a/web/src/main/webapp/css/home.less +++ b/web/src/main/webapp/css/home.less @@ -161,19 +161,11 @@ div.table-cell { list-style-type: none; padding: 0 12px; margin: auto; - max-width: 1260px; - - /* Grid Fallback */ + max-width: @md; display: flex; flex-wrap: wrap; - - /* Supports Grid */ - /* stylelint-disable */ - display: grid; - /* stylelint-enable */ - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - grid-auto-rows: minmax(296px, 1fr); - grid-gap: 4px 8px; + flex-direction: row; + justify-content: flex-start; .dndPlaceholder { background: rgba(0, 0, 0, 0.12); @@ -184,25 +176,54 @@ div.table-cell { } .widget-container:not(.dndDraggingSource) { - padding-right: 0; - padding-left: 0; - display: flex; - align-items: center; - justify-content: center; - margin: 0; - flex: 1; + flex: 0 0 25%; overflow: hidden; + transition: flex 0.2s linear, transform 0.3s ease-out, opacity 0.3s linear; + padding: 8px; - widget { - display: flex; - flex: 1; + &.ng-enter.ng-enter-active, + &.ng-leave { + opacity: 1; + flex: 0 0 25%; + transform: scaleX(1); + + @media (max-width: @md) { + flex: 0 0 33%; + } + + @media (max-width: @sm) { + flex: 0 0 50%; + } + + @media (max-width: @widget-xs) { + flex: 0 0 100%; + } + } + &.ng-leave.ng-leave-active, + &.ng-enter { + opacity: 0; + flex: 0; + transform: scaleX(0); + } + + widget { .widget-frame { - display: flex; - flex: 1; margin: 0; } } + + @media (max-width: @md) { + flex: 0 0 33%; + } + + @media (max-width: @sm) { + flex: 0 0 50%; + } + + @media (max-width: @widget-xs) { + flex: 0 0 100%; + } } .dndDraggingSource { diff --git a/web/src/main/webapp/css/variables.less b/web/src/main/webapp/css/variables.less index 7226cd220..2eb6208c7 100644 --- a/web/src/main/webapp/css/variables.less +++ b/web/src/main/webapp/css/variables.less @@ -24,9 +24,10 @@ @home-view-1: 59; /* Media queries */ +@widget-xs: 680px; @xs: 600px; @sm: 960px; -@md: 1280px; +@md: 1260px; @lg: 1920px; // GRAYSCALE diff --git a/web/src/main/webapp/my-app/layout/controllers.js b/web/src/main/webapp/my-app/layout/controllers.js index 0ba16f482..11f331af9 100644 --- a/web/src/main/webapp/my-app/layout/controllers.js +++ b/web/src/main/webapp/my-app/layout/controllers.js @@ -69,8 +69,6 @@ define(['angular', 'jquery'], function(angular, $) { $sessionStorage, $filter, $mdColors, layoutService) { var vm = this; $scope.selectedNodeId = ''; - $scope.hideWidgetIndex = null; - $scope.isToastDisplayed = false; /** * Set the selected widget in scope to track focus @@ -108,10 +106,6 @@ define(['angular', 'jquery'], function(angular, $) { * @param event {Object} The event object */ $scope.moveWithKeyboard = function(widget, event) { - if ($scope.isToastDisplayed) { - return; - } - // Get index independent of ng-repeat to avoid filter bugs var currentIndex = findLayoutIndex($scope.layout, 'nodeId', widget.nodeId); @@ -163,16 +157,22 @@ define(['angular', 'jquery'], function(angular, $) { // Listen for removal event $scope.$on('REMOVE_WIDGET', /** - * Listen for widget removal event, then show confirmation toast + * Listen for widget removal event, then show undo toast * if one is not already showing. * @param event {Object} The angularjs event object * @param data {Object} Data about the widget being removed */ function(event, data) { - // Make sure no toasts are already open - if (!$scope.isToastDisplayed) { + // Remove the widget from layout in scope + $scope.layout.splice(data.removedIndex, 1); + + // Dismiss any open toasts (success), then show new one + // eslint-disable-next-line promise/always-return + $mdToast.hide().then(function() { showConfirmationToast(data); - } + }).catch(function(error) { + $log.error(error); + }); }); /** @@ -181,66 +181,60 @@ define(['angular', 'jquery'], function(angular, $) { * @param data {Object} Information about the removed widget */ var showConfirmationToast = function(data) { - // Add widget to scope for directive - $scope.widget = data.removedWidget; - - $scope.removedTitle = data.removedWidget.title; - $scope.removedFname = data.removedWidget.fname; - $scope.hideWidgetIndex = data.removedIndex; - if ($sessionStorage.portal.theme) { - // theme already in session, use primary color from it + // theme already in session, use accent color from it $scope.accentColorRgb = $mdColors.getThemeColor($sessionStorage.portal.theme.name + '-accent'); } else { - // theme not yet in session, use primary color from zeroth theme - $scope.primaryColorRgb = + // theme not yet in session, use accent color from first theme + $scope.accentColorRgb = $mdColors.getThemeColor($rootScope.THEMES[0].name + '-accent'); } // Configure and show the toast message + // Pass in widget for directive + // Pass in widget title for toast text display $mdToast.show({ - hideDelay: false, + hideDelay: 2000, parent: angular.element(document).find('.wrapper__frame-page')[0], position: 'top right', - scope: $scope, - preserveScope: true, + locals: { + widget: data.removedWidget, + removedTitle: data.removedWidget.title, + }, + bindToController: true, templateUrl: require.toUrl('my-app/layout/partials/toast-widget-removal.html'), - controller: function RemoveToastController( - $scope, - $sessionStorage, - $log, - $mdToast - ) { - $scope.isToastDisplayed = true; + controller: function RemoveToastController($scope, $mdToast, + widget, removedTitle) { + $scope.widget = widget; + $scope.removedTitle = removedTitle; /** - * Un-hide the widget with CSS - * and don't persist changes + * Resolve show() promise with 'undo' + * Note: a successful timeout or hide() without argument + * resolves with undefined) */ $scope.undoRemoveWidget = function() { // Hide toast message - $mdToast.hide(); - // Reset CSS hiding - $scope.hideWidgetIndex = null; - // Reset toast displayed - $scope.isToastDisplayed = false; - }; - - /** - * Persist removal of the widget - */ - $scope.confirmRemoveWidget = function() { - // Hide toast message - $mdToast.hide(); - // Persist changes - saveLayoutRemoval($scope.removedFname, $scope.hideWidgetIndex); - // Reset toast displayed - $scope.isToastDisplayed = false; + $mdToast.hide('undo'); }; }, + }) + // If user clicked undo, do not proceed + .then(function(response) { + if (response === 'undo') { + // Add the removed widget back to the layout + $scope.layout.splice(data.removedIndex, 0, data.removedWidget); + } else { + // Persist changes + saveLayoutRemoval(data.removedWidget.fname, data.removedIndex); + } + return response; + }) + .catch(function(error) { + $log.error(error); }); }; @@ -255,7 +249,7 @@ define(['angular', 'jquery'], function(angular, $) { layoutService.removeFromHome(fname) .success(function() { // Remove from layout in scope - $scope.layout.splice(index, 1); + // $scope.layout.splice(index, 1); // Reset CSS hiding $scope.hideWidgetIndex = null; diff --git a/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html b/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html index bf3267464..d344a9582 100644 --- a/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html +++ b/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html @@ -33,9 +33,6 @@ Undo - - OK -
From 39de17c0b7cfb8703314b28495d66e2e66272882 Mon Sep 17 00:00:00 2001 From: David Witter Date: Tue, 27 Mar 2018 12:58:17 -0500 Subject: [PATCH 11/15] fix: pass accent color to toast --- web/src/main/webapp/my-app/layout/controllers.js | 11 +++++++---- .../my-app/layout/partials/toast-widget-removal.html | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/web/src/main/webapp/my-app/layout/controllers.js b/web/src/main/webapp/my-app/layout/controllers.js index 11f331af9..cfb40e821 100644 --- a/web/src/main/webapp/my-app/layout/controllers.js +++ b/web/src/main/webapp/my-app/layout/controllers.js @@ -181,14 +181,15 @@ define(['angular', 'jquery'], function(angular, $) { * @param data {Object} Information about the removed widget */ var showConfirmationToast = function(data) { + var accentColor = ''; if ($sessionStorage.portal.theme) { // theme already in session, use accent color from it - $scope.accentColorRgb = + accentColor = $mdColors.getThemeColor($sessionStorage.portal.theme.name + '-accent'); } else { // theme not yet in session, use accent color from first theme - $scope.accentColorRgb = + accentColor = $mdColors.getThemeColor($rootScope.THEMES[0].name + '-accent'); } @@ -202,14 +203,16 @@ define(['angular', 'jquery'], function(angular, $) { position: 'top right', locals: { widget: data.removedWidget, + color: accentColor, removedTitle: data.removedWidget.title, }, bindToController: true, templateUrl: require.toUrl('my-app/layout/partials/toast-widget-removal.html'), - controller: function RemoveToastController($scope, $mdToast, - widget, removedTitle) { + controller: function RemoveToastController($scope, $mdToast, widget, + color, removedTitle) { $scope.widget = widget; + $scope.accentColorRgb = color; $scope.removedTitle = removedTitle; /** * Resolve show() promise with 'undo' diff --git a/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html b/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html index d344a9582..81e4ff21e 100644 --- a/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html +++ b/web/src/main/webapp/my-app/layout/partials/toast-widget-removal.html @@ -22,7 +22,7 @@
- Confirm change + Undo change? Removed {{ removedTitle }}
From 56a3431362a6bc64ff17082b20a2e04a6ef69ae6 Mon Sep 17 00:00:00 2001 From: David Witter Date: Tue, 27 Mar 2018 13:01:30 -0500 Subject: [PATCH 12/15] fix: remove unused ng-class --- .../main/webapp/my-app/layout/list/partials/home-list-view.html | 1 - .../webapp/my-app/layout/widget/partials/home-widget-view.html | 1 - 2 files changed, 2 deletions(-) diff --git a/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html b/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html index 86382b823..f7e2d7159 100644 --- a/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html +++ b/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html @@ -37,7 +37,6 @@ tabindex="0" aria-label="{{ widget.title }}" keep-focus - ng-class="{ 'hidden' : $index === hideWidgetIndex }" node-id="{{ widget.nodeId }}" selected-node-id="{{ selectedNodeId }}" index="{{ $index }}" diff --git a/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html b/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html index 162298042..341d1914c 100644 --- a/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html +++ b/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html @@ -35,7 +35,6 @@ tabindex="0" aria-label="{{ widget.title }}" keep-focus - ng-class="{ 'hidden' : $index === hideWidgetIndex }" node-id="{{ widget.nodeId }}" selected-node-id="{{ selectedNodeId }}" index="{{ $index }}" From 1b028796a034dabd0e759158aed60bd27fb3f2a4 Mon Sep 17 00:00:00 2001 From: David Witter Date: Tue, 27 Mar 2018 13:29:37 -0500 Subject: [PATCH 13/15] fix: fix toast appearance on small screens --- web/src/main/webapp/css/toast.less | 17 +++++++++++++++++ .../main/webapp/my-app/layout/controllers.js | 2 +- .../layout/list/partials/home-list-view.html | 3 +-- .../layout/partials/toast-widget-removal.html | 12 ++++++------ .../widget/partials/home-widget-view.html | 3 +-- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/web/src/main/webapp/css/toast.less b/web/src/main/webapp/css/toast.less index f84d33547..00d969b51 100644 --- a/web/src/main/webapp/css/toast.less +++ b/web/src/main/webapp/css/toast.less @@ -43,6 +43,7 @@ md-toast { height: 44px; width: 44px; max-width: 44px; + margin-right: 16px; background: #e5e5e5; border-radius: 50%; justify-content: center; @@ -64,5 +65,21 @@ md-toast { min-width: 60px; } } + + @media (max-width: @sm) { + position: fixed; + padding: 16px; + + .md-toast-content { + max-width: none; + + .toast__actions { + .md-button { + margin-left: 0; + margin-right: 12px; + } + } + } + } } } diff --git a/web/src/main/webapp/my-app/layout/controllers.js b/web/src/main/webapp/my-app/layout/controllers.js index cfb40e821..311383fab 100644 --- a/web/src/main/webapp/my-app/layout/controllers.js +++ b/web/src/main/webapp/my-app/layout/controllers.js @@ -198,7 +198,7 @@ define(['angular', 'jquery'], function(angular, $) { // Pass in widget for directive // Pass in widget title for toast text display $mdToast.show({ - hideDelay: 2000, + hideDelay: false, parent: angular.element(document).find('.wrapper__frame-page')[0], position: 'top right', locals: { diff --git a/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html b/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html index f7e2d7159..0f0bf6db1 100644 --- a/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html +++ b/web/src/main/webapp/my-app/layout/list/partials/home-list-view.html @@ -20,7 +20,7 @@ --> -
+
@@ -30,7 +30,6 @@
  • -
    -
    - Undo change? - Removed {{ removedTitle }} -
    +
    +
    + Undo change? + Removed {{ removedTitle }} +
    -
    +
    Undo diff --git a/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html b/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html index 341d1914c..8ef582193 100644 --- a/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html +++ b/web/src/main/webapp/my-app/layout/widget/partials/home-widget-view.html @@ -20,7 +20,7 @@ --> -
    +
    @@ -28,7 +28,6 @@
    • Date: Tue, 27 Mar 2018 13:30:32 -0500 Subject: [PATCH 14/15] fix: increase time to undo --- web/src/main/webapp/my-app/layout/controllers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/main/webapp/my-app/layout/controllers.js b/web/src/main/webapp/my-app/layout/controllers.js index 311383fab..99a8b6e0e 100644 --- a/web/src/main/webapp/my-app/layout/controllers.js +++ b/web/src/main/webapp/my-app/layout/controllers.js @@ -198,7 +198,7 @@ define(['angular', 'jquery'], function(angular, $) { // Pass in widget for directive // Pass in widget title for toast text display $mdToast.show({ - hideDelay: false, + hideDelay: 2500, parent: angular.element(document).find('.wrapper__frame-page')[0], position: 'top right', locals: { From 4ca097fc95d56ad5a399a5bfabe864b1e9ff568c Mon Sep 17 00:00:00 2001 From: David Witter Date: Tue, 27 Mar 2018 13:34:09 -0500 Subject: [PATCH 15/15] docs: update change log --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b769a87bd..c783115ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. uPortal's identity impersonation and attribute swapping features (#802) ### Changed -* Removing widget from home screen now prompts for confirmation (allowing UNDO action) before saving changes (#805) +* Removing widget from home screen now displays a toast allowing UNDO action before saving changes (#805) ### Fixed * Fixed bug that sometimes caused the wrong widget to be removed from user's layout (#804)