Adding flash notifications

This commit is contained in:
Will Marshall
2014-04-17 15:08:01 +10:00
parent 3b440ed027
commit c6395a686a
11 changed files with 55 additions and 9 deletions

View File

@@ -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

View File

@@ -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'

View File

@@ -0,0 +1,15 @@
Darkswarm.directive "ofnFlash", (flash, $timeout)->
scope: {}
restrict: 'AE'
template: "<alert ng-repeat='flash in flashes' type='flash.type'>{{flash.message}}</alert>"
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)

View File

@@ -7,4 +7,3 @@ Darkswarm.directive "ofnFocus", ->
), true
return

View File

@@ -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 = {}

View File

@@ -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

View File

@@ -15,6 +15,7 @@
%body.off-canvas{"ng-app" => "Darkswarm"}
= render partial: "shared/menu"
= display_flash_messages
%ofn-flash
= render "shared/sidebar"

View File

@@ -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

View File

@@ -3,4 +3,4 @@
//= require angular-animate
//= require angular-mocks
//= require angular-cookies
//
//= require angular-flash.min.js

View File

@@ -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)

View File

@@ -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])}();