Skip to content

Commit 8124f52

Browse files
author
welcome-dev
committed
feat(core): add notification feedback with angular-ui-notification (#1532)
Added visual notification for user/article updates angular-ui-notification config added to core client config Notification idea from #369
1 parent 8349192 commit 8124f52

26 files changed

+122
-137
lines changed

bower.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"angular-messages": "~1.5.0",
1111
"angular-mocks": "~1.5.0",
1212
"angular-resource": "~1.5.0",
13+
"angular-ui-notification": "~0.2.0",
1314
"angular-ui-router": "~0.2.18",
1415
"bootstrap": "~3.3.6",
1516
"ng-file-upload": "^12.1.0",

config/assets/default.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ module.exports = {
99
// bower:css
1010
'public/lib/bootstrap/dist/css/bootstrap.css',
1111
'public/lib/bootstrap/dist/css/bootstrap-theme.css',
12-
'public/lib/ng-img-crop/compile/unminified/ng-img-crop.css'
12+
'public/lib/ng-img-crop/compile/unminified/ng-img-crop.css',
13+
'public/lib/angular-ui-notification/dist/angular-ui-notification.css'
1314
// endbower
1415
],
1516
js: [
@@ -22,6 +23,7 @@ module.exports = {
2223
'public/lib/angular-messages/angular-messages.js',
2324
'public/lib/angular-mocks/angular-mocks.js',
2425
'public/lib/angular-resource/angular-resource.js',
26+
'public/lib/angular-ui-notification/dist/angular-ui-notification.js',
2527
'public/lib/angular-ui-router/release/angular-ui-router.js',
2628
'public/lib/owasp-password-strength-test/owasp-password-strength-test.js',
2729
// endbower

config/assets/production.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
'public/lib/bootstrap/dist/css/bootstrap.min.css',
1111
'public/lib/bootstrap/dist/css/bootstrap-theme.min.css',
1212
'public/lib/ng-img-crop/compile/minified/ng-img-crop.css',
13+
'public/lib/angular-ui-notification/dist/angular-ui-notification.min.css'
1314
// endbower
1415
],
1516
js: [
@@ -20,6 +21,7 @@ module.exports = {
2021
'public/lib/angular-messages/angular-messages.min.js',
2122
'public/lib/angular-mocks/angular-mocks.js',
2223
'public/lib/angular-resource/angular-resource.min.js',
24+
'public/lib/angular-ui-notification/dist/angular-ui-notification.min.js',
2325
'public/lib/angular-ui-router/release/angular-ui-router.min.js',
2426
'public/lib/ng-file-upload/ng-file-upload.min.js',
2527
'public/lib/ng-img-crop/compile/minified/ng-img-crop.js',

modules/articles/client/controllers/admin/article.client.controller.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
.module('articles.admin')
66
.controller('ArticlesAdminController', ArticlesAdminController);
77

8-
ArticlesAdminController.$inject = ['$scope', '$state', '$window', 'articleResolve', 'Authentication'];
8+
ArticlesAdminController.$inject = ['$scope', '$state', '$window', 'articleResolve', 'Authentication', 'Notification'];
99

10-
function ArticlesAdminController($scope, $state, $window, article, Authentication) {
10+
function ArticlesAdminController($scope, $state, $window, article, Authentication, Notification) {
1111
var vm = this;
1212

1313
vm.article = article;
1414
vm.authentication = Authentication;
15-
vm.error = null;
1615
vm.form = {};
1716
vm.remove = remove;
1817
vm.save = save;
@@ -22,6 +21,7 @@
2221
if ($window.confirm('Are you sure you want to delete?')) {
2322
vm.article.$remove(function() {
2423
$state.go('admin.articles.list');
24+
Notification.success({ message: '<i class="glyphicon glyphicon-ok"></i> Article deleted successfully!' });
2525
});
2626
}
2727
}
@@ -40,10 +40,11 @@
4040

4141
function successCallback(res) {
4242
$state.go('admin.articles.list'); // should we send the User to the list or the updated Article's view?
43+
Notification.success({ message: '<i class="glyphicon glyphicon-ok"></i> Article saved successfully!' });
4344
}
4445

4546
function errorCallback(res) {
46-
vm.error = res.data.message;
47+
Notification.error({ message: res.data.message, title: '<i class="glyphicon glyphicon-remove"></i> Article save error!' });
4748
}
4849
}
4950
}

modules/articles/client/controllers/articles.client.controller.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
vm.article = article;
1414
vm.authentication = Authentication;
15-
vm.error = null;
1615

1716
}
1817
}());

modules/articles/client/views/admin/form-article.client.view.html

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ <h1>{{vm.article._id ? 'Edit Article' : 'New Article'}}</h1>
2424
<div class="form-group">
2525
<button type="submit" class="btn btn-default">{{vm.article._id ? 'Update' : 'Create'}}</button>
2626
</div>
27-
<div ng-show="vm.error" class="text-danger">
28-
<strong ng-bind="vm.error"></strong>
29-
</div>
3027
</fieldset>
3128
</form>
3229
</div>

modules/articles/tests/client/admin.articles.client.controller.tests.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
$state,
1010
Authentication,
1111
ArticlesService,
12-
mockArticle;
12+
mockArticle,
13+
Notification;
1314

1415
// The $resource service augments the response object with methods for updating and deleting the resource.
1516
// If we were to use the standard toEqual matcher, our tests would fail because the test values would not match
@@ -36,7 +37,7 @@
3637
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
3738
// This allows us to inject a service but then attach it to a variable
3839
// with the same name as the service.
39-
beforeEach(inject(function ($controller, $rootScope, _$state_, _$httpBackend_, _Authentication_, _ArticlesService_) {
40+
beforeEach(inject(function ($controller, $rootScope, _$state_, _$httpBackend_, _Authentication_, _ArticlesService_, _Notification_) {
4041
// Set a new global scope
4142
$scope = $rootScope.$new();
4243

@@ -45,6 +46,7 @@
4546
$state = _$state_;
4647
Authentication = _Authentication_;
4748
ArticlesService = _ArticlesService_;
49+
Notification = _Notification_;
4850

4951
// create mock article
5052
mockArticle = new ArticlesService({
@@ -66,6 +68,8 @@
6668

6769
// Spy on state go
6870
spyOn($state, 'go');
71+
spyOn(Notification, 'error');
72+
spyOn(Notification, 'success');
6973
}));
7074

7175
describe('vm.save() as create', function () {
@@ -89,11 +93,13 @@
8993
$scope.vm.save(true);
9094
$httpBackend.flush();
9195

96+
// Test Notification success was called
97+
expect(Notification.success).toHaveBeenCalledWith({ message: '<i class="glyphicon glyphicon-ok"></i> Article saved successfully!' });
9298
// Test URL redirection after the article was created
9399
expect($state.go).toHaveBeenCalledWith('admin.articles.list');
94100
}));
95101

96-
it('should set $scope.vm.error if error', function () {
102+
it('should call Notification.error if error', function () {
97103
var errorMessage = 'this is an error message';
98104
$httpBackend.expectPOST('api/articles', sampleArticlePostData).respond(400, {
99105
message: errorMessage
@@ -102,7 +108,7 @@
102108
$scope.vm.save(true);
103109
$httpBackend.flush();
104110

105-
expect($scope.vm.error).toBe(errorMessage);
111+
expect(Notification.error).toHaveBeenCalledWith({ message: errorMessage, title: '<i class="glyphicon glyphicon-remove"></i> Article save error!' });
106112
});
107113
});
108114

@@ -120,11 +126,13 @@
120126
$scope.vm.save(true);
121127
$httpBackend.flush();
122128

129+
// Test Notification success was called
130+
expect(Notification.success).toHaveBeenCalledWith({ message: '<i class="glyphicon glyphicon-ok"></i> Article saved successfully!' });
123131
// Test URL location to new object
124132
expect($state.go).toHaveBeenCalledWith('admin.articles.list');
125133
}));
126134

127-
it('should set $scope.vm.error if error', inject(function (ArticlesService) {
135+
it('should call Notification.error if error', inject(function (ArticlesService) {
128136
var errorMessage = 'error';
129137
$httpBackend.expectPUT(/api\/articles\/([0-9a-fA-F]{24})$/).respond(400, {
130138
message: errorMessage
@@ -133,7 +141,7 @@
133141
$scope.vm.save(true);
134142
$httpBackend.flush();
135143

136-
expect($scope.vm.error).toBe(errorMessage);
144+
expect(Notification.error).toHaveBeenCalledWith({ message: errorMessage, title: '<i class="glyphicon glyphicon-remove"></i> Article save error!' });
137145
}));
138146
});
139147

@@ -152,6 +160,7 @@
152160
$scope.vm.remove();
153161
$httpBackend.flush();
154162

163+
expect(Notification.success).toHaveBeenCalledWith({ message: '<i class="glyphicon glyphicon-ok"></i> Article deleted successfully!' });
155164
expect($state.go).toHaveBeenCalledWith('admin.articles.list');
156165
});
157166

modules/core/client/app/config.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
var service = {
77
applicationEnvironment: window.env,
88
applicationModuleName: applicationModuleName,
9-
applicationModuleVendorDependencies: ['ngResource', 'ngAnimate', 'ngMessages', 'ui.router', 'ui.bootstrap', 'ngFileUpload', 'ngImgCrop'],
9+
applicationModuleVendorDependencies: ['ngResource', 'ngAnimate', 'ngMessages', 'ui.router', 'ui.bootstrap', 'ngFileUpload', 'ngImgCrop', 'ui-notification'],
1010
registerModule: registerModule
1111
};
1212

@@ -20,4 +20,17 @@
2020
// Add the module to the AngularJS configuration file
2121
angular.module(applicationModuleName).requires.push(moduleName);
2222
}
23+
24+
// Angular-ui-notification configuration
25+
angular.module('ui-notification').config(function(NotificationProvider) {
26+
NotificationProvider.setOptions({
27+
delay: 2000,
28+
startTop: 20,
29+
startRight: 10,
30+
verticalSpacing: 20,
31+
horizontalSpacing: 20,
32+
positionX: 'right',
33+
positionY: 'bottom'
34+
});
35+
});
2336
}(window));

modules/users/client/controllers/admin/user.client.controller.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
.module('users.admin')
66
.controller('UserController', UserController);
77

8-
UserController.$inject = ['$scope', '$state', '$window', 'Authentication', 'userResolve'];
8+
UserController.$inject = ['$scope', '$state', '$window', 'Authentication', 'userResolve', 'Notification'];
99

10-
function UserController($scope, $state, $window, Authentication, user) {
10+
function UserController($scope, $state, $window, Authentication, user, Notification) {
1111
var vm = this;
1212

1313
vm.authentication = Authentication;
@@ -22,9 +22,11 @@
2222
user.$remove();
2323

2424
vm.users.splice(vm.users.indexOf(user), 1);
25+
Notification.success('User deleted successfully!');
2526
} else {
2627
vm.user.$remove(function () {
2728
$state.go('admin.users');
29+
Notification.success({ message: '<i class="glyphicon glyphicon-ok"></i> User deleted successfully!' });
2830
});
2931
}
3032
}
@@ -43,8 +45,9 @@
4345
$state.go('admin.user', {
4446
userId: user._id
4547
});
48+
Notification.success({ message: '<i class="glyphicon glyphicon-ok"></i> User saved successfully!' });
4649
}, function (errorResponse) {
47-
vm.error = errorResponse.data.message;
50+
Notification.error({ message: errorResponse.data.message, title: '<i class="glyphicon glyphicon-remove"></i> User update error!' });
4851
});
4952
}
5053

modules/users/client/controllers/authentication.client.controller.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
.module('users')
66
.controller('AuthenticationController', AuthenticationController);
77

8-
AuthenticationController.$inject = ['$scope', '$state', 'UsersService', '$location', '$window', 'Authentication', 'PasswordValidator'];
8+
AuthenticationController.$inject = ['$scope', '$state', 'UsersService', '$location', '$window', 'Authentication', 'PasswordValidator', 'Notification'];
99

10-
function AuthenticationController($scope, $state, UsersService, $location, $window, Authentication, PasswordValidator) {
10+
function AuthenticationController($scope, $state, UsersService, $location, $window, Authentication, PasswordValidator, Notification) {
1111
var vm = this;
1212

1313
vm.authentication = Authentication;
@@ -17,15 +17,16 @@
1717
vm.callOauthProvider = callOauthProvider;
1818

1919
// Get an eventual error defined in the URL query string:
20-
vm.error = $location.search().err;
20+
if ($location.search().err) {
21+
Notification.error({ message: $location.search().err });
22+
}
2123

2224
// If user is signed in then redirect back home
2325
if (vm.authentication.user) {
2426
$location.path('/');
2527
}
2628

2729
function signup(isValid) {
28-
vm.error = null;
2930

3031
if (!isValid) {
3132
$scope.$broadcast('show-errors-check-validity', 'vm.userForm');
@@ -39,7 +40,6 @@
3940
}
4041

4142
function signin(isValid) {
42-
vm.error = null;
4343

4444
if (!isValid) {
4545
$scope.$broadcast('show-errors-check-validity', 'vm.userForm');
@@ -67,25 +67,25 @@
6767
function onUserSignupSuccess(response) {
6868
// If successful we assign the response to the global user model
6969
vm.authentication.user = response;
70-
70+
Notification.success({ message: '<i class="glyphicon glyphicon-ok"></i> Signup successful!' });
7171
// And redirect to the previous or home page
7272
$state.go($state.previous.state.name || 'home', $state.previous.params);
7373
}
7474

7575
function onUserSignupError(response) {
76-
vm.error = response.data.message;
76+
Notification.error({ message: response.data.message, title: '<i class="glyphicon glyphicon-remove"></i> Signup Error!', delay: 6000 });
7777
}
7878

7979
function onUserSigninSuccess(response) {
8080
// If successful we assign the response to the global user model
8181
vm.authentication.user = response;
82-
82+
Notification.info({ message: 'Welcome ' + response.firstName });
8383
// And redirect to the previous or home page
8484
$state.go($state.previous.state.name || 'home', $state.previous.params);
8585
}
8686

8787
function onUserSigninError(response) {
88-
vm.error = response.data.message;
88+
Notification.error({ message: response.data.message, title: '<i class="glyphicon glyphicon-remove"></i> Signin Error!', delay: 6000 });
8989
}
9090
}
9191
}());

modules/users/client/controllers/password.client.controller.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
.module('users')
66
.controller('PasswordController', PasswordController);
77

8-
PasswordController.$inject = ['$scope', '$stateParams', 'UsersService', '$location', 'Authentication', 'PasswordValidator'];
8+
PasswordController.$inject = ['$scope', '$stateParams', 'UsersService', '$location', 'Authentication', 'PasswordValidator', 'Notification'];
99

10-
function PasswordController($scope, $stateParams, UsersService, $location, Authentication, PasswordValidator) {
10+
function PasswordController($scope, $stateParams, UsersService, $location, Authentication, PasswordValidator, Notification) {
1111
var vm = this;
1212

1313
vm.resetUserPassword = resetUserPassword;
@@ -22,7 +22,6 @@
2222

2323
// Submit forgotten password account id
2424
function askForPasswordReset(isValid) {
25-
vm.success = vm.error = null;
2625

2726
if (!isValid) {
2827
$scope.$broadcast('show-errors-check-validity', 'vm.forgotPasswordForm');
@@ -37,7 +36,6 @@
3736

3837
// Change user password
3938
function resetUserPassword(isValid) {
40-
vm.success = vm.error = null;
4139

4240
if (!isValid) {
4341
$scope.$broadcast('show-errors-check-validity', 'vm.resetPasswordForm');
@@ -55,13 +53,13 @@
5553
function onRequestPasswordResetSuccess(response) {
5654
// Show user success message and clear form
5755
vm.credentials = null;
58-
vm.success = response.message;
56+
Notification.success({ message: response.message, title: '<i class="glyphicon glyphicon-ok"></i> Password reset email sent successfully!' });
5957
}
6058

6159
function onRequestPasswordResetError(response) {
6260
// Show user error message and clear form
6361
vm.credentials = null;
64-
vm.error = response.data.message;
62+
Notification.error({ message: response.data.message, title: '<i class="glyphicon glyphicon-remove"></i> Failed to send password reset email!', delay: 4000 });
6563
}
6664

6765
function onResetPasswordSuccess(response) {
@@ -70,12 +68,13 @@
7068

7169
// Attach user profile
7270
Authentication.user = response;
71+
Notification.success({ message: '<i class="glyphicon glyphicon-ok"></i> Password reset successful!' });
7372
// And redirect to the index page
7473
$location.path('/password/reset/success');
7574
}
7675

7776
function onResetPasswordError(response) {
78-
vm.error = response.data.message;
77+
Notification.error({ message: response.data.message, title: '<i class="glyphicon glyphicon-remove"></i> Password reset failed!', delay: 4000 });
7978
}
8079
}
8180
}());

0 commit comments

Comments
 (0)