From c6395a686ad2b2f997af96e25f926fd43965ef3b Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 17 Apr 2014 15:08:01 +1000 Subject: [PATCH] Adding flash notifications --- app/assets/javascripts/darkswarm/all.js.coffee | 1 + .../javascripts/darkswarm/darkswarm.js.coffee | 2 +- .../darkswarm/directives/flash.js.coffee | 15 +++++++++++++++ .../darkswarm/directives/focus.js.coffee | 1 - .../darkswarm/services/order.js.coffee | 10 ++++++---- app/controllers/shop/checkout_controller.rb | 2 +- app/views/layouts/darkswarm.html.haml | 1 + spec/controllers/shop/checkout_controller_spec.rb | 9 ++++++++- spec/javascripts/application_spec.js | 2 +- .../unit/darkswarm/services/order_spec.js.coffee | 15 +++++++++++++++ vendor/assets/javascripts/angular-flash.min.js | 6 ++++++ 11 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/directives/flash.js.coffee create mode 100644 vendor/assets/javascripts/angular-flash.min.js diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee index 357e371974..65fa9107a9 100644 --- a/app/assets/javascripts/darkswarm/all.js.coffee +++ b/app/assets/javascripts/darkswarm/all.js.coffee @@ -10,6 +10,7 @@ #= require ../shared/bindonce.min.js #= require ../shared/ng-infinite-scroll.min.js #= require ../shared/angular-local-storage.js +#= require angular-flash.min.js # #= require ../shared/jquery.timeago #= require foundation diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee index ac9eefe0df..71d99be03b 100644 --- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee +++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee @@ -1,4 +1,4 @@ -window.Darkswarm = angular.module("Darkswarm", ["ngResource", "filters", 'mm.foundation', 'angularLocalStorage', 'pasvaz.bindonce', 'infinite-scroll']).config ($httpProvider, $tooltipProvider) -> +window.Darkswarm = angular.module("Darkswarm", ["ngResource", "filters", 'mm.foundation', 'angularLocalStorage', 'pasvaz.bindonce', 'infinite-scroll', 'angular-flash.service']).config ($httpProvider, $tooltipProvider) -> $httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content') $httpProvider.defaults.headers.put['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content') $httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest' diff --git a/app/assets/javascripts/darkswarm/directives/flash.js.coffee b/app/assets/javascripts/darkswarm/directives/flash.js.coffee new file mode 100644 index 0000000000..86eb2a05a2 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/flash.js.coffee @@ -0,0 +1,15 @@ +Darkswarm.directive "ofnFlash", (flash, $timeout)-> + scope: {} + restrict: 'AE' + template: "{{flash.message}}" + link: ($scope, element, attr) -> + $scope.flashes = [] + show = (message, type)-> + if message + $scope.flashes.push({message: message, type: type}) + $timeout($scope.delete, 5000) + + $scope.delete = -> + $scope.flashes.shift() + + flash.subscribe(show) diff --git a/app/assets/javascripts/darkswarm/directives/focus.js.coffee b/app/assets/javascripts/darkswarm/directives/focus.js.coffee index b9cae51e5e..c481702d6c 100644 --- a/app/assets/javascripts/darkswarm/directives/focus.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/focus.js.coffee @@ -7,4 +7,3 @@ Darkswarm.directive "ofnFocus", -> ), true return - diff --git a/app/assets/javascripts/darkswarm/services/order.js.coffee b/app/assets/javascripts/darkswarm/services/order.js.coffee index 450d14fe54..c3bebdf143 100644 --- a/app/assets/javascripts/darkswarm/services/order.js.coffee +++ b/app/assets/javascripts/darkswarm/services/order.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.factory 'Order', ($resource, Product, order, $http, CheckoutFormState)-> +Darkswarm.factory 'Order', ($resource, Product, order, $http, CheckoutFormState, flash)-> new class Order errors: {} @@ -13,9 +13,11 @@ Darkswarm.factory 'Order', ($resource, Product, order, $http, CheckoutFormState) submit: -> $http.put('/shop/checkout', {order: @preprocess()}).success (data, status)=> @navigate(data.path) - .error (errors, status)=> - @errors = errors - + .error (response, status)=> + @errors = response.errors + flash.error = response.flash?.error + flash.success = response.flash?.notice + # Rails wants our Spree::Address data to be provided with _attributes preprocess: -> munged_order = {} diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb index 0ffe90a91f..8f28645ab8 100644 --- a/app/controllers/shop/checkout_controller.rb +++ b/app/controllers/shop/checkout_controller.rb @@ -53,7 +53,7 @@ class Shop::CheckoutController < Spree::CheckoutController render :edit end format.js do - render json: @order.errors.to_json, status: 400 + render json: {errors: @order.errors, flash: flash.to_hash}.to_json, status: 400 end end end diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 51aaca837d..400d4a33c3 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -15,6 +15,7 @@ %body.off-canvas{"ng-app" => "Darkswarm"} = render partial: "shared/menu" = display_flash_messages + %ofn-flash = render "shared/sidebar" diff --git a/spec/controllers/shop/checkout_controller_spec.rb b/spec/controllers/shop/checkout_controller_spec.rb index d5c5ec60a5..9086fe0953 100644 --- a/spec/controllers/shop/checkout_controller_spec.rb +++ b/spec/controllers/shop/checkout_controller_spec.rb @@ -88,7 +88,14 @@ describe Shop::CheckoutController do it "returns errors" do xhr :post, :update, order: {}, use_route: :spree response.status.should == 400 - response.body.should == assigns[:order].errors.to_json + response.body.should == {errors: assigns[:order].errors, flash: []}.to_json + end + + it "returns flash" do + order.stub(:update_attributes).and_return true + order.stub(:next).and_return false + xhr :post, :update, order: {}, use_route: :spree + response.body.should == {errors: assigns[:order].errors, flash: {error: "Payment could not be processed, please check the details you entered"}}.to_json end it "returns order confirmation url on success" do diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js index cd070d79e9..615e29535e 100644 --- a/spec/javascripts/application_spec.js +++ b/spec/javascripts/application_spec.js @@ -3,4 +3,4 @@ //= require angular-animate //= require angular-mocks //= require angular-cookies -// +//= require angular-flash.min.js diff --git a/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee index 4d3fd47696..f78cec582b 100644 --- a/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/order_spec.js.coffee @@ -3,6 +3,7 @@ describe 'Order service', -> orderData = null $httpBackend = null CheckoutFormState = null + flash = null beforeEach -> orderData = { @@ -27,6 +28,7 @@ describe 'Order service', -> inject ($injector, _$httpBackend_)-> $httpBackend = _$httpBackend_ Order = $injector.get("Order") + flash = $injector.get("flash") CheckoutFormState = $injector.get("CheckoutFormState") spyOn(Order, "navigate") # Stubbing out writes to window.location @@ -58,6 +60,19 @@ describe 'Order service', -> Order.submit() $httpBackend.flush() + it "sends flash messages to the flash service", -> + $httpBackend.expectPUT("/shop/checkout").respond 400, {flash: {error: "frogs"}} + Order.submit() + $httpBackend.flush() + expect(flash.error).toEqual "frogs" + + it "puts errors into the scope", -> + $httpBackend.expectPUT("/shop/checkout").respond 400, {errors: {error: "frogs"}} + Order.submit() + $httpBackend.flush() + expect(Order.errors).toEqual {error: "frogs"} + + it "Munges the order attributes to add _attributes as Rails needs", -> expect(Order.preprocess().bill_address_attributes).not.toBe(undefined) expect(Order.preprocess().bill_address).toBe(undefined) diff --git a/vendor/assets/javascripts/angular-flash.min.js b/vendor/assets/javascripts/angular-flash.min.js new file mode 100644 index 0000000000..5e46b88c87 --- /dev/null +++ b/vendor/assets/javascripts/angular-flash.min.js @@ -0,0 +1,6 @@ +/**! + * @license angular-flash v0.1.13 + * Copyright (c) 2013 William L. Bunselmeyer. https://github.com/wmluke/angular-flash + * License: MIT + */ +!function(){"use strict";var a=0,b=function(c){function d(a,b){angular.forEach(j.subscribers,function(c){var d=!c.type||c.type===a,e=!j.id&&!c.id||c.id===j.id;d&&e&&c.cb(b,a)})}var e,f,g,h,i,j=angular.extend({id:null,subscribers:{},classnames:{error:[],warn:[],info:[],success:[]}},c),k=this;this.clean=function(){e=null,f=null,g=null,h=null,i=null},this.subscribe=function(b,c,d){return a+=1,j.subscribers[a]={cb:b,type:c,id:d},a},this.unsubscribe=function(a){delete j.subscribers[a]},this.to=function(a){var c=angular.copy(j);return c.id=a,new b(c)},Object.defineProperty(this,"success",{get:function(){return e},set:function(a){e=a,i="success",d(i,a)}}),Object.defineProperty(this,"info",{get:function(){return f},set:function(a){f=a,i="info",d(i,a)}}),Object.defineProperty(this,"warn",{get:function(){return g},set:function(a){g=a,i="warn",d(i,a)}}),Object.defineProperty(this,"error",{get:function(){return h},set:function(a){h=a,i="error",d(i,a)}}),Object.defineProperty(this,"type",{get:function(){return i}}),Object.defineProperty(this,"message",{get:function(){return i?k[i]:null}}),Object.defineProperty(this,"classnames",{get:function(){return j.classnames}}),Object.defineProperty(this,"id",{get:function(){return j.id}})};angular.module("angular-flash.service",[]).provider("flash",function(){var a=this;this.errorClassnames=["alert-error"],this.warnClassnames=["alert-warn"],this.infoClassnames=["alert-info"],this.successClassnames=["alert-success"],this.$get=function(){return new b({classnames:{error:a.errorClassnames,warn:a.warnClassnames,info:a.infoClassnames,success:a.successClassnames}})}})}(),function(){"use strict";function a(a){return(null===a||void 0===a)&&(a=""),/^\s*$/.test(a)}function b(b,c){return{scope:!0,link:function(d,e,f){function g(){var a=[].concat(b.classnames.error,b.classnames.warn,b.classnames.info,b.classnames.success);angular.forEach(a,function(a){e.removeClass(a)})}function h(h,j){if(i&&c.cancel(i),d.flash.type=j,d.flash.message=h,g(),angular.forEach(b.classnames[j],function(a){e.addClass(a)}),a(f.activeClass)||e.addClass(f.activeClass),!h)return void d.hide();var k=Number(f.duration||5e3);k>0&&(i=c(d.hide,k))}var i,j;d.flash={},d.hide=function(){g(),a(f.activeClass)||e.removeClass(f.activeClass)},d.$on("$destroy",function(){b.clean(),b.unsubscribe(j)}),j=b.subscribe(h,f.flashAlert,f.id),f.flashAlert&&b[f.flashAlert]&&h(b[f.flashAlert],f.flashAlert),!f.flashAlert&&b.message&&h(b.message,b.type)}}}angular.module("angular-flash.flash-alert-directive",["angular-flash.service"]).directive("flashAlert",["flash","$timeout",b])}(); \ No newline at end of file