From 5db2cdeca470b1d8fd526b45c93bf15f2e287ec0 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Wed, 24 Mar 2021 18:47:16 +0530 Subject: [PATCH 1/8] fixing test driver related errors in notification_highlighter_spec --- .../features/notification_highlighter_spec.rb | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/web/spec/features/notification_highlighter_spec.rb b/web/spec/features/notification_highlighter_spec.rb index 5f11beaf4..9da87c8dd 100644 --- a/web/spec/features/notification_highlighter_spec.rb +++ b/web/spec/features/notification_highlighter_spec.rb @@ -11,15 +11,17 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f shared_examples_for :notification_badge do |options| it "in correct state" do - sign_in_poltergeist(user) unless page.has_selector?('h2', 'musicians') - badge = find("#{NOTIFICATION_PANEL} .badge", text:options[:count]) - badge['class'].include?('highlighted').should == options[:highlighted] + save_screenshot("notification_highlighter.png") + #sign_in_poltergeist(user) unless page.has_selector?('h2', 'musicians') + sign_in_poltergeist(user) unless page.has_selector?('h2', text: 'musicians') + badge = find("#{NOTIFICATION_PANEL} .badge", text: options[:count], visible: :all) + badge['class'].include?('highlighted').should == options[:highlighted] - if options[:action] == :click - badge.trigger(:click) - badge = find("#{NOTIFICATION_PANEL} .badge", text:0) - badge['class'].include?('highlighted').should == false - end + if options[:action] == :click + badge.click + badge = find("#{NOTIFICATION_PANEL} .badge", text:0, visible: :all) + badge['class'].include?('highlighted').should == false + end end end @@ -36,6 +38,7 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f user.reload end + it_behaves_like :notification_badge, highlighted: false, count:0 describe "sees notification" do @@ -53,13 +56,13 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f notification = Notification.send_text_message("text message", user2, user) notification.errors.any?.should be false end - + it_behaves_like :notification_badge, highlighted: true, count:1, action: :click end end - describe "and realtime notifications with sidebar open" do + describe "and realtime notifications with sidebar open" do before(:each) do # generate one message so that count = 1 to start notification = Notification.send_text_message("text message", user2, user) @@ -101,7 +104,7 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f end end end - end + end end describe "user with new notifications" do @@ -154,7 +157,7 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f in_client(user2) do sign_in_poltergeist(user2) find_musician(user) - find(".result-list-button-wrapper[data-musician-id='#{user.id}'] .search-m-friend").trigger(:click) + find(".result-list-button-wrapper[data-musician-id='#{user.id}'] .search-m-friend").click end in_client(user) do From f6074d75d86f172b2fa1596a571745dd179a0283 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Wed, 24 Mar 2021 19:23:33 +0530 Subject: [PATCH 2/8] more fixes to spec --- web/spec/support/client_interactions.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/spec/support/client_interactions.rb b/web/spec/support/client_interactions.rb index 2798f394a..817ee3caf 100644 --- a/web/spec/support/client_interactions.rb +++ b/web/spec/support/client_interactions.rb @@ -46,7 +46,7 @@ def initiate_text_dialog(user) site_search(user.first_name, expand: true) find("#search-results a[user-id=\"#{user.id}\"][hoveraction=\"musician\"]", text: user.name).hover_intent - find('#musician-hover #btnMessage').trigger(:click) + find('#musician-hover #btnMessage').click find('h1', text: 'conversation with ' + user.name) end @@ -57,12 +57,12 @@ def send_text_message(msg, options={}) within('#text-message-dialog form.text-message-box') do fill_in 'new-text-message', with: msg end - find('#text-message-dialog .btn-send-text-message').trigger(:click) + find('#text-message-dialog .btn-send-text-message').click find('#text-message-dialog .previous-message-text', text: msg) unless options[:should_fail] # close the dialog if caller specified close_on_send if options[:close_on_send] - find('#text-message-dialog .btn-close-dialog', text: 'CLOSE').trigger(:click) if options[:close_on_send] + find('#text-message-dialog .btn-close-dialog', text: 'CLOSE').click if options[:close_on_send] page.should have_no_selector('#text-message-dialog') end @@ -78,11 +78,11 @@ def send_chat_message(msg) within("[layout-id=\"panelChat\"] .chat-sender form.chat-message-form") do fill_in 'new-chat-message', with: msg end - find("[layout-id=\"panelChat\"] .chat-sender .btn-send-chat-message").trigger(:click) + find("[layout-id=\"panelChat\"] .chat-sender .btn-send-chat-message").click end def open_notifications - find("#{NOTIFICATION_PANEL} .panel-header").trigger(:click) + find("#{NOTIFICATION_PANEL} .panel-header").click end @@ -118,7 +118,7 @@ end # simulates focus event on window def window_focus - page.evaluate_script(%{window.jQuery(window).trigger('focus');}) + page.evaluate_script(%{window.jQuery(window).focus();}) end def close_websocket From 1f862baccf5039ccb8fbb77cab9f71d9256df4c9 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 24 Mar 2021 20:12:38 -0500 Subject: [PATCH 3/8] make tests go a little faster --- web/config/environments/test.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/config/environments/test.rb b/web/config/environments/test.rb index 4a501d851..25615788b 100644 --- a/web/config/environments/test.rb +++ b/web/config/environments/test.rb @@ -20,11 +20,12 @@ SampleApp::Application.configure do #config.assets.compress = true # Compress precompiled assets config.assets.compile = true # Refuse to compile assets on-the-fly config.assets.digest = true - #config.assets.debug = true + config.assets.debug = false + config.action_controller.perform_caching = true # Show full error reports and disable caching config.consider_all_requests_local = true - config.action_controller.perform_caching = false + #config.action_controller.perform_caching = false # Raise exceptions instead of rendering exception templates config.action_dispatch.show_exceptions = false From dcbf52a88bc67077b5a39940c11676c296808920 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 24 Mar 2021 20:18:18 -0500 Subject: [PATCH 4/8] ignore extra args when test env --- ruby/lib/jam_ruby/tasks/db/migrate.rake | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ruby/lib/jam_ruby/tasks/db/migrate.rake b/ruby/lib/jam_ruby/tasks/db/migrate.rake index ee1015ba9..7301e28cb 100644 --- a/ruby/lib/jam_ruby/tasks/db/migrate.rake +++ b/ruby/lib/jam_ruby/tasks/db/migrate.rake @@ -17,11 +17,14 @@ namespace :db do desc "Migrate the database" task :migrate do - version = ARGV[1] - if !version.nil? - version = version.to_i + version = nil + if ENV['RAILS_ENV'] != "test" + version = ARGV[1] + if !version.nil? + version = version.to_i + end end - + ActiveRecord::Base.establish_connection(db_config) migrate_dir = JamRuby::RakeUtil.migrations_path ActiveRecord::Migrator.migrate(migrate_dir, version) From cf4f5c89e08fadf9753b95d6c2c9ef9a6048add1 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 28 Mar 2021 17:34:32 -0500 Subject: [PATCH 5/8] session_video_spec --- ruby/lib/jam_ruby/tasks/db/migrate.rake | 2 +- .../SessionVideoBtn.js.jsx.coffee | 2 +- web/spec/features/session_video_spec.rb | 37 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 web/spec/features/session_video_spec.rb diff --git a/ruby/lib/jam_ruby/tasks/db/migrate.rake b/ruby/lib/jam_ruby/tasks/db/migrate.rake index 7301e28cb..e92cd1c34 100644 --- a/ruby/lib/jam_ruby/tasks/db/migrate.rake +++ b/ruby/lib/jam_ruby/tasks/db/migrate.rake @@ -24,7 +24,7 @@ namespace :db do version = version.to_i end end - + ActiveRecord::Base.establish_connection(db_config) migrate_dir = JamRuby::RakeUtil.migrations_path ActiveRecord::Migrator.migrate(migrate_dir, version) diff --git a/web/app/assets/javascripts/react-components/SessionVideoBtn.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionVideoBtn.js.jsx.coffee index 6ba078ad6..f81075152 100644 --- a/web/app/assets/javascripts/react-components/SessionVideoBtn.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionVideoBtn.js.jsx.coffee @@ -32,7 +32,7 @@ SessionActions = @SessionActions buttons: buttons}) render: () -> - ` + ` VIDEO ` diff --git a/web/spec/features/session_video_spec.rb b/web/spec/features/session_video_spec.rb new file mode 100644 index 000000000..c4092b5f5 --- /dev/null +++ b/web/spec/features/session_video_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' +describe "Music session video button", :js => true, :type => :feature, :capybara_feature => true do + subject { page } + let(:user) { FactoryGirl.create(:user, subscription_plan_code: 'jamsubplatinum') } + let(:connection) { FactoryGirl.create(:connection, :user => user, addr: "1.1.1.1") } + let(:music_session) { + ms = FactoryGirl.create(:active_music_session, :creator => user, :musician_access => true) + ms.save! + ms + } + before(:each) do + MusicSession.delete_all + ActiveMusicSession.delete_all + Rails.application.config.allow_force_native_client = true + @url = "/client#/session/#{music_session.id}" + + end + it "finds video link" do + fast_signin(user, @url) + # give the web page a little time to connect; sometimes in firefox this needs some time to connect + page.should_not have_selector('span.disconnected-msg', text: 'DISCONNECTED FROM SERVER') + #sleep 5 + find(".session-video-btn").click() + save_screenshot("video_button1.png") + + # this should open a new window/tab to a different site, as configured by the new + # Rails.configuration.video_conferencing_host parameter. + + # We only need to verify that secondary window opens up. We should probably also test that a new TempToken record + # was created as a result of clicking the video button .I wouldn't bother testing anything else in this particular + # test. + # + # It's true that this secondary window (in staging/production) will load a site that will then in turn call the + # new controller token auth method.. but we can test the correctness of the new controller method in a much + # simpler web/spec/controller spec + end +end \ No newline at end of file From 6f27f409629642bfcb157a55fbbc6c9bcc0340f2 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 28 Mar 2021 23:29:42 -0500 Subject: [PATCH 6/8] Fix the notification highlighter tests --- web/app/assets/javascripts/findMusician.js | 1 + web/app/assets/javascripts/jam_rest.js | 2 +- .../member_search_filter.js.coffee | 13 ++++++++---- .../assets/javascripts/notificationPanel.js | 7 +++++-- web/app/assets/javascripts/searchResults.js | 4 ++-- .../assets/stylesheets/client/content.scss | 2 +- .../features/notification_highlighter_spec.rb | 21 ++++++++----------- web/spec/spec_helper.rb | 8 +++---- web/spec/support/utilities.rb | 6 +++--- 9 files changed, 35 insertions(+), 29 deletions(-) diff --git a/web/app/assets/javascripts/findMusician.js b/web/app/assets/javascripts/findMusician.js index 8b0596154..a266cabc0 100644 --- a/web/app/assets/javascripts/findMusician.js +++ b/web/app/assets/javascripts/findMusician.js @@ -310,6 +310,7 @@ evt.stopPropagation(); var uid = $(this).parent().data('musician-id'); rest.sendFriendRequest(app, uid, friendRequestCallback); + return false; } function friendRequestCallback(user_id) { diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index cb17f10da..26a6fbfdf 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -1152,7 +1152,7 @@ processData: false, success: function (response) { if (callback) { - callback(userId); + callback(userId, app); } context.JK.GA.trackFriendConnect(context.JK.GA.FriendConnectTypes.request); }, diff --git a/web/app/assets/javascripts/member_search_filter.js.coffee b/web/app/assets/javascripts/member_search_filter.js.coffee index 357806ebc..1f96f9df9 100644 --- a/web/app/assets/javascripts/member_search_filter.js.coffee +++ b/web/app/assets/javascripts/member_search_filter.js.coffee @@ -445,16 +445,20 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter extends BaseSearchF _bindFriendMusician: () => objThis = this + callback = this.friendRequestCallback @screen.find('.search-m-friend').on 'click', (evt) -> # if the musician is already a friend, remove the button-orange class, and prevent the link from working + console.log("friend request connected") if 0 == $(this).closest('.button-orange').size() + console.log("friend request bail out") return false $(this).click (ee) -> ee.preventDefault() - return + return false evt.stopPropagation() uid = $(this).parent().data('musician-id') - objThis.rest.sendFriendRequest objThis.app, uid, this.friendRequestCallback + objThis.rest.sendFriendRequest(objThis.app, uid, callback) + return false _bindFollowMusician: () => objThis = this @@ -493,8 +497,9 @@ context.JK.MusicianSearchFilter = class MusicianSearchFilter extends BaseSearchF else 'Location Unavailable' - friendRequestCallback: (user_id)=> - # TODO: + friendRequestCallback: (user_id, app)=> + $('div[data-musician-id=' + user_id + '] .search-m-friend').removeClass('button-orange').addClass('button-grey'); + app.notify({title: 'Friend Request Sent', text: 'Your friend request has been sent! They have to approve to become friends.'}); paginate: () => super() diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js index 121caa43e..2cd252382 100644 --- a/web/app/assets/javascripts/notificationPanel.js +++ b/web/app/assets/javascripts/notificationPanel.js @@ -94,6 +94,11 @@ } function onNotificationOccurred(payload) { + + incrementNotificationCount(); + highlightCount(); + + if(userCanSeeNotifications(payload)) { app.updateNotificationSeen(payload.notification_id, payload.created_at); } @@ -105,8 +110,6 @@ } else { queueNotificationSeen(payload.notification_id, payload.created_at, payload.description); - highlightCount(); - incrementNotificationCount(); missedNotificationsWhileAway = true; } } diff --git a/web/app/assets/javascripts/searchResults.js b/web/app/assets/javascripts/searchResults.js index 28ed57fc0..bf9461622 100644 --- a/web/app/assets/javascripts/searchResults.js +++ b/web/app/assets/javascripts/searchResults.js @@ -215,7 +215,7 @@ if ($btn.is('.disabled')) { logger.debug("ignoring send friend request on disabled btn") - return; + return false; } var userId = $btn.parent().attr('user-id'); @@ -232,7 +232,7 @@ app.notify({title: 'Friend Request Sent', text: 'Your friend request has been sent'}) $btn.addClass('disabled') }) - + return false; } } diff --git a/web/app/assets/stylesheets/client/content.scss b/web/app/assets/stylesheets/client/content.scss index 010ca0cd9..4590d788e 100644 --- a/web/app/assets/stylesheets/client/content.scss +++ b/web/app/assets/stylesheets/client/content.scss @@ -181,7 +181,7 @@ > a.smallbutton { margin: 4px; &.button-grey { - display:none; // @FIXME VRFS-930 / VRFS-931 per comment from David - don't show. + //display:none; // @FIXME VRFS-930 / VRFS-931 per comment from David - don't show. } } } diff --git a/web/spec/features/notification_highlighter_spec.rb b/web/spec/features/notification_highlighter_spec.rb index 9da87c8dd..187c2a22b 100644 --- a/web/spec/features/notification_highlighter_spec.rb +++ b/web/spec/features/notification_highlighter_spec.rb @@ -47,7 +47,7 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f notification.errors.any?.should be false end - it_behaves_like :notification_badge, highlighted: false, count:0, action: :click + it_behaves_like :notification_badge, highlighted: true, count:1, action: :click end describe "document out of focus" do @@ -83,7 +83,7 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f find('#notification #btn-reply') # wait for notification to show, so that we know the sidebar had a chance to update end - it_behaves_like :notification_badge, highlighted: false, count:0 + it_behaves_like :notification_badge, highlighted: true, count:1 end describe "document out of focus" do @@ -150,9 +150,7 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f JamRuby::Score.connection.execute('delete from current_network_scores').check JamRuby::Score.createx(1, 'a', 1, 2, 'b', 2, 10) - in_client(user) do - sign_in_poltergeist(user) - end + sign_in_poltergeist(user) in_client(user2) do sign_in_poltergeist(user2) @@ -160,15 +158,14 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f find(".result-list-button-wrapper[data-musician-id='#{user.id}'] .search-m-friend").click end - in_client(user) do - badge = find("#{NOTIFICATION_PANEL} .badge", text: '1') - badge['class'].include?('highlighted').should == true - find('#notification #btn-accept', text: 'ACCEPT').trigger(:click) + badge = find("#{NOTIFICATION_PANEL} .badge", text: '1') + badge['class'].include?('highlighted').should == true - badge = find("#{NOTIFICATION_PANEL} .badge", text: '0') - badge['class'].include?('highlighted').should == false - end + find('#notification #btn-accept', text: 'ACCEPT').click + + badge = find("#{NOTIFICATION_PANEL} .badge", text: '0') + badge['class'].include?('highlighted').should == false end end end diff --git a/web/spec/spec_helper.rb b/web/spec/spec_helper.rb index b4a869629..43ccb3fc3 100644 --- a/web/spec/spec_helper.rb +++ b/web/spec/spec_helper.rb @@ -88,10 +88,10 @@ Thread.new do JamWebsockets::Server.new.run( :port => 6759, :emwebsocket_debug => false, - :connect_time_stale_client => 4, - :connect_time_expire_client => 6, - :connect_time_stale_browser => 4, - :connect_time_expire_browser => 6, + :connect_time_stale_client => 80, + :connect_time_expire_client => 120, + :connect_time_stale_browser => 80, + :connect_time_expire_browser => 120, :max_connections_per_user => 20, :rabbitmq_host => '127.0.0.1', :rabbitmq_port => 5672, diff --git a/web/spec/support/utilities.rb b/web/spec/support/utilities.rb index b0e1569e6..be5bdc431 100644 --- a/web/spec/support/utilities.rb +++ b/web/spec/support/utilities.rb @@ -46,12 +46,12 @@ end # in place of ever using Capybara.session_name directly, # this utility is used to handle the mapping of session names in a way across all tests runs -def in_client(name) +def in_client(name, &blk) session_name = name.class == JamRuby::User ? name.id : name - Capybara.session_name = mapped_session_name(session_name) + #Capybara.session_name = mapped_session_name(session_name) - yield + Capybara.using_session(session_name, &blk) end From 4db1c3b6865ba49cd0bd28a7c3bbc5905312e8ac Mon Sep 17 00:00:00 2001 From: Nuwan Date: Mon, 29 Mar 2021 14:44:02 +0530 Subject: [PATCH 7/8] for testing enable video capability of the client --- web/spec/features/session_video_spec.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/spec/features/session_video_spec.rb b/web/spec/features/session_video_spec.rb index c4092b5f5..ceb2bee83 100644 --- a/web/spec/features/session_video_spec.rb +++ b/web/spec/features/session_video_spec.rb @@ -13,15 +13,18 @@ describe "Music session video button", :js => true, :type => :feature, :capybara ActiveMusicSession.delete_all Rails.application.config.allow_force_native_client = true @url = "/client#/session/#{music_session.id}" - + end + it "finds video link" do fast_signin(user, @url) # give the web page a little time to connect; sometimes in firefox this needs some time to connect page.should_not have_selector('span.disconnected-msg', text: 'DISCONNECTED FROM SERVER') + execute_script("window.VideoActions.setVideoEnabled(true)") #sleep 5 find(".session-video-btn").click() - save_screenshot("video_button1.png") + sleep 5 + save_screenshot("video_button2.png") # this should open a new window/tab to a different site, as configured by the new # Rails.configuration.video_conferencing_host parameter. From 9711a9a666ff95ddf5dee2521aa38b96729feb96 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Sat, 10 Apr 2021 08:15:51 +0530 Subject: [PATCH 8/8] change to notification toast and sidebar * show 3 notification toasts instead of one * open notification tray automatically for for priority notifications --- web/app/assets/javascripts/layout.js | 380 ++++++++++++++++-- .../assets/javascripts/notificationPanel.js | 42 +- web/app/assets/stylesheets/client/notify.scss | 8 +- web/app/views/clients/_notify.html.erb | 25 +- .../features/notification_highlighter_spec.rb | 120 +++++- web/spec/features/notification_spec.rb | 33 +- 6 files changed, 548 insertions(+), 60 deletions(-) diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index aec9eecca..970307673 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -99,7 +99,9 @@ layoutScreens(width, height); layoutSidebar(width, height); layoutHeader(width, height); - layoutNotify(width, height); + layoutNotify1(width, height); + layoutNotify2(width, height); + layoutNotify3(width, height); layoutFooter(width, height); $(document).triggerHandler('layout_resized'); @@ -325,14 +327,41 @@ $('[layout="header"]').css(css); } - function layoutNotify(screenWidth, screenHeight) { - var $notify = $('[layout="notify"]'); - var nHeight = $notify.height(); + function layoutNotify1(screenWidth, screenHeight) { + var $notify1 = $('[layout="notify1"]'); + //var nHeight = $notify1.height(); var notifyStyle = { bottom: '0px', - position: 'fixed' + position: 'fixed', + padding: '20px' }; - $notify.css(notifyStyle); + $notify1.css(notifyStyle); + } + + function layoutNotify2(screenWidth, screenHeight) { + var $notify1 = $('[layout="notify1"]'); + var $notify2 = $('[layout="notify2"]'); + var nHeight = $notify1.height(); + var notifyStyle = { + bottom: (nHeight + 41).toString()+'px', + position: 'fixed', + padding: '20px' + }; + $notify2.css(notifyStyle); + } + + function layoutNotify3(screenWidth, screenHeight) { + var $notify1 = $('[layout="notify1"]'); + var $notify2 = $('[layout="notify2"]'); + var $notify3 = $('[layout="notify3"]'); + var nHeight1 = $notify1.height(); + var nHeight2 = $notify2.height(); + var notifyStyle = { + bottom: (nHeight1 + nHeight2 + 41 + 41).toString()+'px', + position: 'fixed', + padding: '20px' + }; + $notify3.css(notifyStyle); } function layoutFooter(screenWidth, screenHeight) { @@ -806,6 +835,16 @@ return false; } + function expandNotificationPanel(){ + if(!context.JK.currentUserId) { + showDialog('login-required-dialog'); + return false; + } + expandedPanel = 'panelNotifications'; + layout(); + return false; + } + function wizardLinkClicked(evt) { evt.preventDefault(); var targetStepId = $(evt.currentTarget).attr("layout-wizard-link"); @@ -915,8 +954,73 @@ // used for concurrent notifications var notifyQueue = []; - var firstNotification = false; - var notifyDetails; + var notify1Showing = false; + var notify2Showing = false; + var notify3Showing = false; + var isMouseOverNotify1 = false; + var isMouseOverNotify2 = false; + var isMouseOverNotify3 = false; + var notify1Elapsed = false; + var notify2Elapsed = false; + var notify3Elapsed = false; + + var $notify1 = $('[layout="notify1"]'); + var $notify2 = $('[layout="notify2"]'); + var $notify3 = $('[layout="notify3"]'); + + $notify1.on('mouseenter', mouseEnterNotification) + $notify1.on('mouseleave', mouseLeaveToNotification) + $notify2.on('mouseenter', mouseEnterNotification) + $notify2.on('mouseleave', mouseLeaveToNotification) + $notify3.on('mouseenter', mouseEnterNotification) + $notify3.on('mouseleave', mouseLeaveToNotification) + + function mouseEnterNotification(evt){ + var $notify = $(evt.target); + console.log("mouseEnter", $notify.prop("id")); + switch($notify.prop("id")){ + case 'notification1': + isMouseOverNotify1 = true; + break; + case 'notification2': + isMouseOverNotify2 = true; + break; + case 'notification3': + isMouseOverNotify3 = true; + break; + } + } + + function mouseLeaveToNotification(evt){ + var $notify = $(evt.target); + console.log("mouseLeave", $notify.prop("id")); + switch($notify.prop("id")){ + case 'notification1': + if(notify1Elapsed){ + $notify1.hide(0); + notify1Showing = false; + } + isMouseOverNotify1 = false; + break; + case 'notification2': + if(notify2Elapsed){ + $notify2.hide(0); + notify2Showing = false; + } + isMouseOverNotify2 = false; + break; + case 'notification3': + if(notify3Elapsed){ + $notify3.hide(0); + notify3Showing = false; + } + isMouseOverNotify3 = false; + break; + } + } + + //var firstNotification = false; + //var notifyDetails; var okButton = { id: "btn-okay", @@ -939,7 +1043,58 @@ cancelButton ]; + // this.notify = function (message, buttons, noCancel) { + // if (!buttons) { + // if (noCancel) { + // buttons = [okButton]; + // } + // else { + // buttons = defaultButtons; + // } + // } + + // // this allows clients to just specify the important action button without having to repeat the cancel descripton everywhere + // if (buttons.length === 1) { + // // jkolyer: changed default to remove cancel as this is used as an alert, not a confirmation (see ConfirmDialog) + // // buttons.push(cancelButton); + // } + + // var $notify = $('[layout="notify"]'); + + // if (notifyQueue.length === 0) { + // firstNotification = true; + // setNotificationInfo(message, buttons, $notify); + // } + + // notifyQueue.push({message: message, descriptor: buttons}); + // // JW - speeding up the in/out parts of notify. Extending non-moving time. + // $notify.hide(0) + // .show({ + // duration: 0, + // queue: true, + // complete: function () { + // notifyDetails = notifyQueue.shift(); + + // // shift 1 more time if this is first notification being displayed + // if (firstNotification) { + // notifyDetails = notifyQueue.shift(); + // firstNotification = false; + // } + + // if (notifyDetails !== undefined) { + // setNotificationInfo(notifyDetails.message, notifyDetails.descriptor, $notify); + // } + + // notifyDetails = {}; + // } + // }) + // .delay(8000) + // .hide(0) + // }; + this.notify = function (message, buttons, noCancel) { + console.log("call this.notify"); + if (!buttons) { if (noCancel) { buttons = [okButton]; @@ -949,46 +1104,182 @@ } } - // this allows clients to just specify the important action button without having to repeat the cancel descripton everywhere - if (buttons.length === 1) { - // jkolyer: changed default to remove cancel as this is used as an alert, not a confirmation (see ConfirmDialog) - // buttons.push(cancelButton); - } - - var $notify = $('[layout="notify"]'); - - if (notifyQueue.length === 0) { - firstNotification = true; - setNotificationInfo(message, buttons, $notify); - } - notifyQueue.push({message: message, descriptor: buttons}); - // JW - speeding up the in/out parts of notify. Extending non-moving time. - $notify.slideDown(250) - .delay(4000) - .slideUp({ - duration: 400, - queue: true, - complete: function () { - notifyDetails = notifyQueue.shift(); - // shift 1 more time if this is first notification being displayed - if (firstNotification) { - notifyDetails = notifyQueue.shift(); - firstNotification = false; + showNotificationToast(); + + } + + function showNotificationToast(){ + if(!notify1Showing){ + var notifyDetails1 = notifyQueue.shift(); + if (notifyDetails1 !== undefined) { + setNotificationInfo(notifyDetails1.message, notifyDetails1.descriptor, $notify1); + notify1Elapsed = false; + $notify1.hide(0) + .show({ + duration: 0, + //queue: true, + complete: function () { + console.log("Show notification1") + notify1Showing = true; + notifyDetails1 = {}; } - - if (notifyDetails !== undefined) { - setNotificationInfo(notifyDetails.message, notifyDetails.descriptor, $notify); + }) + //.delay(8000); + + setTimeout(function(){ + notify1Elapsed = true; + if(!isMouseOverNotify1){ + $notify1.hide(0, function(){ + notify1Showing = false; + console.log("Hide notification1") + }) } + }, 8000) + + } - notifyDetails = {}; - } - }); - }; + }else if(!notify2Showing){ + var notifyDetails2 = notifyQueue.shift(); + if (notifyDetails2 !== undefined) { + setNotificationInfo(notifyDetails2.message, notifyDetails2.descriptor, $notify2); + notify2Elapsed = false; + $notify2.hide(0) + .show({ + duration: 0, + //queue: true, + complete: function () { + console.log("Show notification2") + notify2Showing = true; + notifyDetails2 = {}; + } + }); + // .delay(8000) + // .hide(0, function(){ + // notify2Showing = false; + // console.log("Hide notification2") + // }) + setTimeout(function(){ + notify2Elapsed = true; + if(!isMouseOverNotify2){ + $notify2.hide(0, function(){ + notify2Showing = false; + console.log("Hide notification2") + }) + } + }, 8000); + } + + }else if(!notify3Showing){ + var notifyDetails3 = notifyQueue.shift(); + if (notifyDetails3 !== undefined) { + setNotificationInfo(notifyDetails3.message, notifyDetails3.descriptor, $notify3); + notify3Elapsed = false; + $notify3.hide(0) + .show({ + duration: 0, + //queue: true, + complete: function () { + console.log("Show notification3") + notify3Showing = true; + notifyDetails3 = {}; + } + }) + // .delay(8000) + // .hide(0, function(){ + // notify3Showing = false; + // console.log("Hide notification3") + // }) + setTimeout(function(){ + notify3Elapsed = true; + if(!isMouseOverNotify3){ + $notify3.hide(0, function(){ + notify3Showing = false; + console.log("Hide notification3") + }) + } + }, 8000); + } + + }else if(notify1Showing && notify2Showing && notify3Showing){ + //try again after 1 second + setTimeout(function(){ + showNotificationToast() + }, 1000) + } + } + + // function setNotificationInfo(message, buttons, notificationSelector) { + // var $notify = $('[layout="notify"]'); + // $('h2', $notify).text(message.title); + // $('p', $notify).empty(); + // if (message.text instanceof jQuery) { + // $('p', $notify).append(message.text) + // } + // else { + // $('p', $notify).html(message.text); + // } + + // if (message.icon_url) { + // $('#avatar', $notify).attr('src', message.icon_url); + // $('#notify-avatar', $notify).show(); + // } + // else { + // $('#notify-avatar', $notify).hide(); + // } + + // if (message.detail) { + // $('div.detail', $notify).html(message.detail).show(); + // } + // else { + // $('div.detail', $notify).hide(); + // } + + // var $buttonDiv = $('#buttons', $notify); + // $buttonDiv.empty(); + + // $.each(buttons, function(index, val) { + + // // build button HTML based on KV pairs + // var keys = Object.keys(val); + // var buttonHtml = '  '; + // $buttonDiv.append(buttonHtml); + + // // ensure it doesn't fire twice + // $('#' + val.id, $notify).unbind('click'); + + // $('#' + val.id, $notify).click(function() { + // if (val.callback !== undefined) { + // if (val.callback_args) { + // val.callback(val.callback_args); + // } + // else { + // val.callback(); + // } + // return false; + // } + // else { + // notificationSelector.hide(); + // return false; + // } + // }); + // }); + // } function setNotificationInfo(message, buttons, notificationSelector) { - var $notify = $('[layout="notify"]'); + var $notify = notificationSelector; $('h2', $notify).text(message.title); $('p', $notify).empty(); if (message.text instanceof jQuery) { @@ -1048,7 +1339,7 @@ return false; } else { - notificationSelector.hide(); + $notify.hide(); return false; } }); @@ -1093,6 +1384,7 @@ this.getCurrentScreen = function() { return currentScreen; // will be a string of the layout-id of the active screen } + this.close = function (evt) { close(evt); }; @@ -1156,6 +1448,10 @@ events(); }; + this.expandNotificationPanel = function(){ + return expandNotificationPanel() + } + return this; }; diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js index 2cd252382..937def4c6 100644 --- a/web/app/assets/javascripts/notificationPanel.js +++ b/web/app/assets/javascripts/notificationPanel.js @@ -119,7 +119,7 @@ if(isNotificationsPanelVisible()) { if(missedNotificationsWhileAway) { // catch user's eye, then put count to 0 - pulseToDark(); + // pulseToDark(); if(queuedNotificationCreatedAt) { app.updateNotificationSeen(queuedNotification, queuedNotificationCreatedAt); } @@ -147,9 +147,11 @@ } function events() { - $(context).on(EVENTS.DIALOG_CLOSED, function(e, data) {if(data.dialogCount == 0) userCameBack(); }); - $(window).focus(userCameBack); - $(window).blur(windowBlurred); + // $(context).on(EVENTS.DIALOG_CLOSED, function(e, data) { + // if(data.dialogCount == 0) userCameBack(); + // }); + //$(window).focus(userCameBack); + //$(window).blur(windowBlurred); app.user() .done(function(user) { setCount(user.new_notifications); @@ -160,6 +162,8 @@ $panel.on('open', opened); + $contents.on('click', opened); + // friend notifications registerFriendRequest(); registerFriendRequestAccepted(); @@ -582,6 +586,7 @@ } }] ); + context.JK.layout.expandNotificationPanel(); }); } @@ -635,6 +640,7 @@ } }] ); + context.JK.layout.expandNotificationPanel(); }); } @@ -652,6 +658,7 @@ "text": payload.msg, "icon_url": context.JK.resolveAvatarUrl(payload.photo_url) }); + context.JK.layout.expandNotificationPanel(); }); } @@ -692,7 +699,7 @@ logger.debug("Handling FRIEND_REQUEST msg " + JSON.stringify(payload)); handleNotification(payload, header.type); - + app.notify({ "title": "New Friend Request", "text": payload.msg, @@ -710,6 +717,8 @@ } }] ); + context.JK.layout.expandNotificationPanel(); + //opened() }); } @@ -790,6 +799,7 @@ } }] ); + context.JK.layout.expandNotificationPanel(); // THERE IS A RACE CONDITION THAT CAUSES AN ERROR WHEN INVOKING THE CODE BELOW // THE ACTIVEMUSICSESSION HAS NOT YET BEEN FULLY CREATED B/C THE CREATOR'S CLIENT IS @@ -1410,13 +1420,33 @@ $list.prepend(notificationHtml); - onNotificationOccurred(payload); + drawAttentionToNotification(payload, type); + + onNotificationOccurred(payload, type); initializeActions(payload, type); return true; } + function drawAttentionToNotification(payload, type){ + if (type === context.JK.MessageType.FRIEND_REQUEST || + type === context.JK.MessageType.FRIEND_REQUEST_ACCEPTED || + type === context.JK.MessageType.JOIN_REQUEST || + type === context.JK.MessageType.JOIN_REQUEST_APPROVED || + type === context.JK.MessageType.SESSION_INVITATION) { + var $newLi = $list.find('li[notification-id="' + payload.notification_id + '"]'); + $newLi.css("border", "solid 1px #01545f"); + $newLi.css("margin-bottom", "5px"); + $newLi.css("padding", "10px 0"); + $newLi.css("background-color", "#1099aa"); + } + } + + function notificationPanelClick(){ + opened() + } + function approveJoinRequest(args) { rest.updateJoinRequest(args.join_request_id, true) .done(function(response) { diff --git a/web/app/assets/stylesheets/client/notify.scss b/web/app/assets/stylesheets/client/notify.scss index 806f8f790..8891b7a84 100644 --- a/web/app/assets/stylesheets/client/notify.scss +++ b/web/app/assets/stylesheets/client/notify.scss @@ -1,6 +1,6 @@ @import "client/common.scss"; -#notification { +.notification { position:absolute; left:30%; padding:20px; @@ -14,7 +14,7 @@ box-shadow: 0px 0px 15px rgba(50, 50, 50, 1); } -#notification h2 { +.notification h2 { font-size: 1.5em; color:#fff; font-weight:200; @@ -23,7 +23,7 @@ margin-bottom:10px; } -#notification p { +.notification p { margin-bottom:20px; .error-text { @@ -31,7 +31,7 @@ } } -#notification p .text-message { +.notification p .text-message { white-space: pre-wrap; word-wrap: break-word; } diff --git a/web/app/views/clients/_notify.html.erb b/web/app/views/clients/_notify.html.erb index d058e9903..f4d8966d0 100644 --- a/web/app/views/clients/_notify.html.erb +++ b/web/app/views/clients/_notify.html.erb @@ -1,4 +1,4 @@ - + + + \ No newline at end of file diff --git a/web/spec/features/notification_highlighter_spec.rb b/web/spec/features/notification_highlighter_spec.rb index 187c2a22b..d6b262b97 100644 --- a/web/spec/features/notification_highlighter_spec.rb +++ b/web/spec/features/notification_highlighter_spec.rb @@ -11,7 +11,6 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f shared_examples_for :notification_badge do |options| it "in correct state" do - save_screenshot("notification_highlighter.png") #sign_in_poltergeist(user) unless page.has_selector?('h2', 'musicians') sign_in_poltergeist(user) unless page.has_selector?('h2', text: 'musicians') badge = find("#{NOTIFICATION_PANEL} .badge", text: options[:count], visible: :all) @@ -59,6 +58,7 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f it_behaves_like :notification_badge, highlighted: true, count:1, action: :click end + end @@ -80,7 +80,7 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f before(:each) do notification = Notification.send_text_message("text message", user2, user) notification.errors.any?.should be false - find('#notification #btn-reply') # wait for notification to show, so that we know the sidebar had a chance to update + find('.notification #btn-reply') # wait for notification to show, so that we know the sidebar had a chance to update end it_behaves_like :notification_badge, highlighted: true, count:1 @@ -91,7 +91,7 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f document_blur notification = Notification.send_text_message("text message 2", user2, user) notification.errors.any?.should be false - find('#notification #btn-reply') + find('.notification #btn-reply') end it_behaves_like :notification_badge, highlighted: true, count:1 @@ -104,6 +104,19 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f end end end + + + describe "click on notification panel", focus: true do + before(:each) do + notification = Notification.send_text_message("text message", user2, user) + notification.errors.any?.should be false + find('.notification #btn-reply') # wait for notification to show, so that we know the sidebar had a chance to update + find('#sidebar-notification-list').click + end + + it_behaves_like :notification_badge, highlighted: false, count: 0 + end + end end @@ -162,10 +175,109 @@ describe "Notification Highlighter", :js => true, :type => :feature, :capybara_f badge = find("#{NOTIFICATION_PANEL} .badge", text: '1') badge['class'].include?('highlighted').should == true - find('#notification #btn-accept', text: 'ACCEPT').click + find('.notification #btn-accept', text: 'ACCEPT').click badge = find("#{NOTIFICATION_PANEL} .badge", text: '0') badge['class'].include?('highlighted').should == false end end + + describe "priority notifications", focus: true do + let(:user1) { FactoryGirl.create(:user) } + let(:user2) { FactoryGirl.create(:user) } + + before(:each) do + Notification.destroy_all + Friendship.destroy_all + FriendRequest.destroy_all + JoinRequest.destroy_all + fast_signin(user2, "/client") + wait_until_curtain_gone + end + + describe "friend request" do + before do + req1 = FriendRequest.new + req1.user_id = user1.id + req1.friend_id = user2.id + req1.save! + req1.reload + Notification.send_friend_request(req1.id, user1.id, user2.id) + find('#btn-notification-action', text: 'ACCEPT') + expect(page).to have_selector("#{NOTIFICATION_PANEL} li.notification-entry", count: 1) + find('.note-text', text: "#{user1.name} has sent you a friend request.") + end + it "save_screenshot" do + save_screenshot("friend_request.png") + end + it_behaves_like :notification_badge, highlighted: false, count: 0 + end + + describe "friend request accepted" do + before do + Notification.send_friend_request_accepted(user2.id, user1.id) + expect(page).to have_selector("#{NOTIFICATION_PANEL} li.notification-entry", count: 1) + find('.note-text', text: "#{user1.name} has accepted your friend request.") + end + it "save_screenshot" do + save_screenshot("friend_request_accepted.png") + end + it_behaves_like :notification_badge, highlighted: false, count: 0 + end + + describe "music session join request" do + before do + ms = FactoryGirl.create(:music_session, creator: user2) + join_request = JoinRequest.new + join_request.music_session = ms + join_request.user = user1 + text = "#{user1.name} has requested to join your session." + join_request.text = text + join_request.save + + Notification.send_join_request(ms, join_request, text) + expect(page).to have_selector("#{NOTIFICATION_PANEL} li.notification-entry", count: 1) + find('.note-text', text: text) + end + it "save_screenshot" do + save_screenshot("music_session_join_request.png") + end + it_behaves_like :notification_badge, highlighted: false, count: 0 + end + + describe "music session join request approved" do + before do + ms = FactoryGirl.create(:music_session, creator: user1) + join_request = JoinRequest.new + join_request.music_session = ms + join_request.user = user2 + text = "#{user1.name} has approved your request to join the session." + join_request.text = text + join_request.save + + Notification.send_join_request_approved(ms, join_request) + expect(page).to have_selector("#{NOTIFICATION_PANEL} li.notification-entry", count: 1) + find('.note-text', text: text) + end + it "save_screenshot" do + save_screenshot("music_session_join_request_approved.png") + end + it_behaves_like :notification_badge, highlighted: false, count: 0 + end + + # describe "music session invitation" do + # before do + # ms = FactoryGirl.create(:music_session, creator: user1) + # text = "You have been invited to join a session by #{user1.name}." + # Notification.send_session_invitation(user2, user1, ms.id) + # expect(page).to have_selector("#{NOTIFICATION_PANEL} li.notification-entry", count: 1) + # find('.note-text', text: text) + # end + # it "save_screenshot" do + # save_screenshot("music_session_invitation.png") + # end + # it_behaves_like :notification_badge, highlighted: false, count: 0 + # end + + end end diff --git a/web/spec/features/notification_spec.rb b/web/spec/features/notification_spec.rb index baa9d2075..4df5dca55 100644 --- a/web/spec/features/notification_spec.rb +++ b/web/spec/features/notification_spec.rb @@ -4,11 +4,8 @@ describe "Notification Subpanel", :js => true, :type => :feature, :capybara_feat subject { page } - let(:user) { FactoryGirl.create(:user, last_jam_locidispid: 1) } - - describe "user with no notifications" do before(:each) do fast_signin(user, "/client") @@ -35,4 +32,34 @@ describe "Notification Subpanel", :js => true, :type => :feature, :capybara_feat expect(page).to have_selector("#{NOTIFICATION_PANEL} li.notification-entry", count: 1) end end + + +end + +describe "Notification Toast", js: true, type: :feature, capybara_feature: true do + let(:user) { FactoryGirl.create(:user) } + let(:user1) { FactoryGirl.create(:user) } + let(:user2) { FactoryGirl.create(:user) } + let(:user3) { FactoryGirl.create(:user) } + + before(:each) do + fast_signin(user, "/client") + end + + it "popup 3 toasts" do + wait_until_curtain_gone + create_friend_request(user1, user) + create_friend_request(user2, user) + create_friend_request(user3, user) + expect(page).to have_selector(".notification", count: 3) + end + + + def create_friend_request(source_user, targe_user) + req = FriendRequest.new + req.user_id = source_user.id + req.friend_id = targe_user.id + req.save! + Notification.send_friend_request(req.id, source_user.id, targe_user.id) + end end \ No newline at end of file