diff --git a/admin/app/admin/broadcast_notifications.rb b/admin/app/admin/broadcast_notifications.rb index 39a5ad9ce..b6b373218 100644 --- a/admin/app/admin/broadcast_notifications.rb +++ b/admin/app/admin/broadcast_notifications.rb @@ -16,7 +16,7 @@ ActiveAdmin.register JamRuby::BroadcastNotification, :as => 'BroadcastNotificati row :title row :message row :button_label - row :button_text + row :button_url row :frequency row :frequency_distribution end diff --git a/ruby/lib/jam_ruby/models/broadcast_notification.rb b/ruby/lib/jam_ruby/models/broadcast_notification.rb index 55ca8239d..7eeaf1de8 100644 --- a/ruby/lib/jam_ruby/models/broadcast_notification.rb +++ b/ruby/lib/jam_ruby/models/broadcast_notification.rb @@ -1,19 +1,22 @@ module JamRuby class BroadcastNotification < ActiveRecord::Base - attr_accessible :title, :message, :button_label, :frequency + attr_accessible :title, :message, :button_label, :frequency, :button_url, as: :admin has_many :user_views, class_name: 'JamRuby::BroadcastNotificationView', dependent: :destroy + validates :button_label, presence: true, length: {maximum: 14} + validates :message, presence: true, length: {maximum: 200} + validates :title, presence: true, length: {maximum: 50} + def self.next_broadcast(user) self.viewable_notifications(user).limit(1).first end def self.viewable_notifications(user) self.select('broadcast_notifications.*, bnv.updated_at AS bnv_updated_at') - .joins('INNER JOIN broadcast_notification_views AS bnv ON bnv.broadcast_notification_id = broadcast_notifications.id') - .where(['bnv.user_id = ?',user.id]) + .joins("LEFT OUTER JOIN broadcast_notification_views AS bnv ON bnv.broadcast_notification_id = broadcast_notifications.id AND (bnv.user_id IS NULL OR (bnv.user_id = '#{user.id}'))") .where(['broadcast_notifications.frequency > 0']) - .where(['broadcast_notifications.frequency > bnv.view_count']) + .where(['bnv.user_id IS NULL OR broadcast_notifications.frequency > bnv.view_count']) .order('bnv_updated_at') end diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 68e34d8df..4538efbd6 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -799,9 +799,9 @@ FactoryGirl.define do end factory :broadcast_notification, :class => JamRuby::BroadcastNotification do - title Faker::Lorem.sentence[0...64] - message Faker::Lorem.paragraph[0...256] - button_label Faker::Lorem.words(2).join(' ')[0...32] + title Faker::Lorem.sentence[0...50] + message Faker::Lorem.paragraph[0...200] + button_label Faker::Lorem.words(2).join(' ')[0...14] frequency 3 end diff --git a/ruby/spec/jam_ruby/models/broadcast_notification_spec.rb b/ruby/spec/jam_ruby/models/broadcast_notification_spec.rb index 8da92b339..083daa08f 100644 --- a/ruby/spec/jam_ruby/models/broadcast_notification_spec.rb +++ b/ruby/spec/jam_ruby/models/broadcast_notification_spec.rb @@ -27,13 +27,21 @@ describe BroadcastNotification do end it 'loads viewable broadcasts for a user' do + broadcast1.touch + broadcast2.touch + broadcast3.touch + broadcast4.touch + + bns = BroadcastNotification.viewable_notifications(user1) + bns.count.should eq(4) + broadcast2.frequency.times { |nn| broadcast2.did_view(user1) } broadcast3.did_view(user1) broadcast1.did_view(user1) broadcast4.did_view(user2) bns = BroadcastNotification.viewable_notifications(user1) - expect(bns.count).to be(2) + expect(bns.count).to be(3) expect(bns[0].id).to eq(broadcast3.id) expect(bns.detect {|bb| bb.id==broadcast2.id }).to be_nil expect(BroadcastNotification.next_broadcast(user1).id).to eq(broadcast3.id) diff --git a/web/app/assets/javascripts/client_init.js.coffee b/web/app/assets/javascripts/client_init.js.coffee index 460eb1aa6..da8e59c3a 100644 --- a/web/app/assets/javascripts/client_init.js.coffee +++ b/web/app/assets/javascripts/client_init.js.coffee @@ -19,7 +19,7 @@ context.JK.ClientInit = class ClientInit setTimeout(this.checkBroadcastNotification, 3000) checkBroadcastNotification: () => - if context.JK.userId + if context.JK.currentUserId broadcastActions.load.triggerPromise() diff --git a/web/build b/web/build index 18f6eebdf..f82cec874 100755 --- a/web/build +++ b/web/build @@ -137,6 +137,7 @@ EOF # cache all gems local, and tell bundle to use local gems only #bundle install --path vendor/bundle --local # prepare production acssets + RAILS_ENV=production bundle install --path vendor/bundle rm -rf $DIR/public/assets bundle exec rake assets:precompile RAILS_ENV=production diff --git a/web/vendor/assets/javascripts/bugsnag.js.erb b/web/vendor/assets/javascripts/bugsnag.js.erb index 979438309..5c97cc216 100644 --- a/web/vendor/assets/javascripts/bugsnag.js.erb +++ b/web/vendor/assets/javascripts/bugsnag.js.erb @@ -1,8 +1,12 @@ // 2.4.8 http://d2wy8f7a9ursnm.cloudfront.net/bugsnag-2.4.8.min.js +<% if Rails.application.config.bugsnag_notify_release_stages.include? Rails.env %> + // START COPY/PASTE FROM BUGSNAG CDN !function(a,b){function c(a,b){try{if("function"!=typeof a)return a;if(!a.bugsnag){var c=e();a.bugsnag=function(d){if(b&&b.eventHandler&&(u=d),v=c,!y){var e=a.apply(this,arguments);return v=null,e}try{return a.apply(this,arguments)}catch(f){throw l("autoNotify",!0)&&(x.notifyException(f,null,null,"error"),s()),f}finally{v=null}},a.bugsnag.bugsnag=a.bugsnag}return a.bugsnag}catch(d){return a}}function d(){B=!1}function e(){var a=document.currentScript||v;if(!a&&B){var b=document.scripts||document.getElementsByTagName("script");a=b[b.length-1]}return a}function f(a){var b=e();b&&(a.script={src:b.src,content:l("inlineScript",!0)?b.innerHTML:""})}function g(b){var c=l("disableLog"),d=a.console;void 0===d||void 0===d.log||c||d.log("[Bugsnag] "+b)}function h(b,c,d){if(d>=5)return encodeURIComponent(c)+"=[RECURSIVE]";d=d+1||1;try{if(a.Node&&b instanceof a.Node)return encodeURIComponent(c)+"="+encodeURIComponent(r(b));var e=[];for(var f in b)if(b.hasOwnProperty(f)&&null!=f&&null!=b[f]){var g=c?c+"["+f+"]":f,i=b[f];e.push("object"==typeof i?h(i,g,d):encodeURIComponent(g)+"="+encodeURIComponent(i))}return e.join("&")}catch(j){return encodeURIComponent(c)+"="+encodeURIComponent(""+j)}}function i(a,b){if(null==b)return a;a=a||{};for(var c in b)if(b.hasOwnProperty(c))try{a[c]=b[c].constructor===Object?i(a[c],b[c]):b[c]}catch(d){a[c]=b[c]}return a}function j(a,b){a+="?"+h(b)+"&ct=img&cb="+(new Date).getTime();var c=new Image;c.src=a}function k(a){var b={},c=/^data\-([\w\-]+)$/;if(a)for(var d=a.attributes,e=0;e\n";var f=[];try{for(var h=arguments.callee.caller.caller;h&&f.length"}return a.nodeName}}function s(){z+=1,a.setTimeout(function(){z-=1})}function t(a,b,c){var d=a[b],e=c(d);a[b]=e}var u,v,w,x={},y=!0,z=0,A=10;x.noConflict=function(){return a.Bugsnag=b,x},x.refresh=function(){A=10},x.notifyException=function(a,b,c,d){b&&"string"!=typeof b&&(c=b,b=void 0),c||(c={}),f(c),n({name:b||a.name,message:a.message||a.description,stacktrace:p(a)||o(),file:a.fileName||a.sourceURL,lineNumber:a.lineNumber||a.line,columnNumber:a.columnNumber?a.columnNumber+1:void 0,severity:d||"warning"},c)},x.notify=function(b,c,d,e){n({name:b,message:c,stacktrace:o(),file:a.location.toString(),lineNumber:1,severity:e||"warning"},d)};var B="complete"!==document.readyState;document.addEventListener?(document.addEventListener("DOMContentLoaded",d,!0),a.addEventListener("load",d,!0)):a.attachEvent("onload",d);var C,D=/^[0-9a-f]{32}$/i,E=/function\s*([\w\-$]+)?\s*\(/i,F="https://notify.bugsnag.com/",G=F+"js",H="2.4.8",I=document.getElementsByTagName("script"),J=I[I.length-1];if(a.atob){if(a.ErrorEvent)try{0===new a.ErrorEvent("test").colno&&(y=!1)}catch(K){}}else y=!1;if(l("autoNotify",!0)){t(a,"onerror",function(b){return function(c,d,e,g,h){var i=l("autoNotify",!0),j={};!g&&a.event&&(g=a.event.errorCharacter),f(j),v=null,i&&!z&&n({name:h&&h.name||"window.onerror",message:c,file:d,lineNumber:e,columnNumber:g,stacktrace:h&&p(h)||o(),severity:"error"},j),b&&b(c,d,e,g,h)}});var L=function(a){return function(b,d){if("function"==typeof b){b=c(b);var e=Array.prototype.slice.call(arguments,2);return a(function(){b.apply(this,e)},d)}return a(b,d)}};t(a,"setTimeout",L),t(a,"setInterval",L),a.requestAnimationFrame&&t(a,"requestAnimationFrame",function(a){return function(b){return a(c(b))}}),a.setImmediate&&t(a,"setImmediate",function(a){return function(){var b=Array.prototype.slice.call(arguments);return b[0]=c(b[0]),a.apply(this,b)}}),"EventTarget Window Node ApplicationCache AudioTrackList ChannelMergerNode CryptoOperation EventSource FileReader HTMLUnknownElement IDBDatabase IDBRequest IDBTransaction KeyOperation MediaController MessagePort ModalWindow Notification SVGElementInstance Screen TextTrack TextTrackCue TextTrackList WebSocket WebSocketWorker Worker XMLHttpRequest XMLHttpRequestEventTarget XMLHttpRequestUpload".replace(/\w+/g,function(b){var d=a[b]&&a[b].prototype;d&&d.hasOwnProperty&&d.hasOwnProperty("addEventListener")&&(t(d,"addEventListener",function(a){return function(b,d,e,f){try{d&&d.handleEvent&&(d.handleEvent=c(d.handleEvent,{eventHandler:!0}))}catch(h){g(h)}return a.call(this,b,c(d,{eventHandler:!0}),e,f)}}),t(d,"removeEventListener",function(a){return function(b,d,e,f){return a.call(this,b,d,e,f),a.call(this,b,c(d),e,f)}}))})}a.Bugsnag=x,"function"==typeof define&&define.amd?define([],function(){return x}):"object"==typeof module&&"object"==typeof module.exports&&(module.exports=x)}(window,window.Bugsnag); // END COPY/PASTE FROM BUGSNAG CDN // manual code: make sure Bugsnag has it's API KEY window.Bugsnag.apiKey = gon.global.bugsnag_key + +<% end %> \ No newline at end of file