diff --git a/db/manifest b/db/manifest index fb64e8498..0fc258a88 100755 --- a/db/manifest +++ b/db/manifest @@ -241,3 +241,4 @@ jam_track_available.sql active_jam_track.sql bpms_on_tap_in.sql jamtracks_job.sql +text_messages.sql diff --git a/db/up/text_messages.sql b/db/up/text_messages.sql new file mode 100644 index 000000000..4e8a10089 --- /dev/null +++ b/db/up/text_messages.sql @@ -0,0 +1,8 @@ +CREATE TABLE text_messages ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + source_user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE, + target_user_id VARCHAR(64) REFERENCES users(id) ON DELETE CASCADE, + message TEXT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index d89371770..084ae7d9e 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -197,6 +197,7 @@ require "jam_ruby/models/jam_company" require "jam_ruby/models/user_sync" require "jam_ruby/models/video_source" require "jam_ruby/models/recorded_video" +require "jam_ruby/models/text_message" require "jam_ruby/jam_tracks_manager" include Jampb diff --git a/ruby/lib/jam_ruby/models/active_music_session.rb b/ruby/lib/jam_ruby/models/active_music_session.rb index 6dd93acc8..c28ae8de2 100644 --- a/ruby/lib/jam_ruby/models/active_music_session.rb +++ b/ruby/lib/jam_ruby/models/active_music_session.rb @@ -290,13 +290,8 @@ module JamRuby } ) - if (offset) - query = query.offset(offset) - end - - if (limit) - query = query.limit(limit) - end + query = query.offset(offset) if offset + query = query.limit(limit) if limit if as_musician query = query.where( diff --git a/ruby/lib/jam_ruby/models/text_message.rb b/ruby/lib/jam_ruby/models/text_message.rb new file mode 100644 index 000000000..80987e92b --- /dev/null +++ b/ruby/lib/jam_ruby/models/text_message.rb @@ -0,0 +1,36 @@ +include Devise::Models + +module JamRuby + class TextMessage < ActiveRecord::Base + + self.primary_key = 'id' + + default_scope order('created_at DESC') + + attr_accessible :target_user_id, :source_user_id, :message + + belongs_to :target_user, :class_name => "JamRuby::User", :foreign_key => "target_user_id" + belongs_to :source_user, :class_name => "JamRuby::User", :foreign_key => "source_user_id" + + validates :message, length: {minimum: 1, maximum: 400}, no_profanity: true + + def self.index(target_user_id, source_user_id, options = {}) + offset = options[:offset] + limit = options[:limit] + + # if not specified, default offset to 0 + offset ||= 0 + offset = offset.to_i + + # if not specified, default limit to 10 + limit ||= 10 + limit = limit.to_i + + TextMessage + .offset(offset) + .limit(limit) + .where('(source_user_id = (?) AND target_user_id = (?)) OR (source_user_id = (?) AND target_user_id = (?))', source_user_id, target_user_id, target_user_id, source_user_id) + + end + end +end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index a5ea8b827..1d0930881 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -79,6 +79,9 @@ module JamRuby # self.id = followable_id in follows table has_many :followers, :as => :followable, :class_name => "JamRuby::Follow", :dependent => :destroy + # text messages + has_many :text_messages, :class_name => "JamRuby:TextMessage", :foreign_key => "target_user_id" + # notifications has_many :notifications, :class_name => "JamRuby::Notification", :foreign_key => "target_user_id" has_many :inverse_notifications, :through => :notifications, :class_name => "JamRuby::User" diff --git a/ruby/spec/jam_ruby/models/text_message.spec.rb b/ruby/spec/jam_ruby/models/text_message.spec.rb new file mode 100644 index 000000000..146a60d14 --- /dev/null +++ b/ruby/spec/jam_ruby/models/text_message.spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe TextMessage do + + before do + TextMessage.delete_all + User.delete_all + @target_user = FactoryGirl.create(:user) + @source_user = FactoryGirl.create(:user) + + @msg = TextMessage.new(:target_user_id => @target_user.id, :source_user_id => @source_user.id) + end + + describe "index" do + + # it "should retrieve conversation for both users" do + # @msg.message = "Test message" + # @msg.save! + + # messages = TextMessage.index(@target_user.id, @source_user.id) + # messages.count.should == 1 + + # messages = TextMessage.index(@source_user.id, @target_user.id) + # messages.count.should == 1 + # end + + it "should page records" do + 11.times do |n| + message = TextMessage.new(:target_user_id => @target_user.id, :source_user_id => @source_user.id) + message.message = "Message #{n}" + message.save! + end + + puts TextMessage.all.count + + messages = TextMessage.index(@target_user.id, @source_user.id, {:offset => 0}) + messages.count.should == 10 + + messages = TextMessage.index(@target_user.id, @source_user.id, {:offset => 1, :limit => 1}) + messages.count.should == 1 + end + + # it "should not allow empty message" do + # expect { @msg.save! }.to raise_error(ActiveRecord::RecordInvalid) + # end + + end +end \ No newline at end of file diff --git a/web/app/assets/javascripts/dialog/textMessageDialog.js b/web/app/assets/javascripts/dialog/textMessageDialog.js index 78107a7f9..62b3b68f9 100644 --- a/web/app/assets/javascripts/dialog/textMessageDialog.js +++ b/web/app/assets/javascripts/dialog/textMessageDialog.js @@ -163,6 +163,7 @@ } $sendTextMessage.click(sendMessage); + // TODO: PULL FROM NEW TABLE rest.getNotifications(buildParams()) .done(function (response) { context._.each(response, function (textMessage) { diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index 230e6d23b..37951e687 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -1258,6 +1258,7 @@ }); } + // TODO: push into new table function createTextMessage(options) { var id = getId(options); return $.ajax({ @@ -1269,6 +1270,17 @@ }); } + function getTextMessages(options) { + if(!options) options = {}; + var id = getId(options); + return $.ajax({ + type: "GET", + url: '/api/users/' + id + '/text_messages?' + $.param(options), + dataType: "json", + contentType: 'application/json' + }); + } + function getNotifications(options) { if(!options) options = {}; var id = getId(options); diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js index 41fa918c4..ca393b18d 100644 --- a/web/app/assets/javascripts/notificationPanel.js +++ b/web/app/assets/javascripts/notificationPanel.js @@ -220,7 +220,7 @@ function updateNotificationList(response) { $.each(response, function(index, val) { - if(val.description == 'TEXT_MESSAGE') { + if(val.description == context.JK.MessageType.TEXT_MESSAGE) { val.formatted_msg = textMessageDialog.formatTextMessage(val.message.substring(0, 200), val.source_user_id, val.source_user.name, val.message.length > 200).html(); } diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index ba33ccd45..22c913265 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -365,8 +365,7 @@ var now = new Date(); var scheduledDate = new Date(scheduledStart); var minutesFromStart = (scheduledDate.getTime() - now.getTime()) / (1000 * 60); - var showButton = minutesFromStart <= MAX_MINUTES_SHOW_START; - return showButton; + return minutesFromStart <= MAX_MINUTES_SHOW_START; }; if (session.creator.id === context.JK.currentUserId) { diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb index 7d8d40f94..7a71e4874 100644 --- a/web/app/controllers/api_users_controller.rb +++ b/web/app/controllers/api_users_controller.rb @@ -9,6 +9,7 @@ class ApiUsersController < ApiController :favorite_create, :favorite_destroy, # favorites :friend_request_index, :friend_request_show, :friend_request_create, :friend_request_update, # friend requests :friend_show, :friend_destroy, # friends + :text_message_index, # text messages :notification_index, :notification_destroy, # notifications :band_invitation_index, :band_invitation_show, :band_invitation_update, # band invitations :set_password, :begin_update_email, :update_avatar, :delete_avatar, :generate_filepicker_policy, @@ -313,6 +314,12 @@ class ApiUsersController < ApiController respond_with responder: ApiResponder, :status => 204 end + ###################### TEXT MESSAGES #################### + def text_message_index + @text_messages = TextMessage.index(params[:target_user_id], @user.id, {:offset => params[:offset]}) + respond_with @text_messages, responder: ApiResponder, :status => 200 + end + ###################### NOTIFICATIONS #################### def notification_index if params[:type] == 'TEXT_MESSAGE' diff --git a/web/app/views/api_users/text_message_index.rabl b/web/app/views/api_users/text_message_index.rabl new file mode 100644 index 000000000..e38e85034 --- /dev/null +++ b/web/app/views/api_users/text_message_index.rabl @@ -0,0 +1,11 @@ +collection @text_messages + +attributes :id, :source_user_id, :target_user_id, :message, :created_at + +node :source_user do |msg| + attributes :id, :name +end + +node :target_user do |msg| + attributes :id, :name +end \ No newline at end of file diff --git a/web/config/routes.rb b/web/config/routes.rb index ecbec6909..2a18c7fce 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -296,6 +296,10 @@ SampleApp::Application.routes.draw do match '/users/:id/friends/:friend_id' => 'api_users#friend_show', :via => :get, :as => 'api_friend_detail' match '/users/:id/friends/:friend_id' => 'api_users#friend_destroy', :via => :delete + # text messages + match '/text_messages' => 'api_text_messages#index', :via => :get + match '/text_messages' => 'api_text_messages#create', :via => :post + # notifications match '/users/:id/notifications' => 'api_users#notification_index', :via => :get match '/users/:id/notifications/:notification_id' => 'api_users#notification_destroy', :via => :delete diff --git a/web/spec/features/find_sessions_spec.rb b/web/spec/features/find_sessions_spec.rb index ba8f7c517..043fdd2ab 100644 --- a/web/spec/features/find_sessions_spec.rb +++ b/web/spec/features/find_sessions_spec.rb @@ -192,6 +192,121 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr end end + describe "start session behavior" do + before(:each) do + stub_const("APP_CONFIG", web_config) + @music_session = FactoryGirl.create(:music_session, creator: user) + + @invited_user = FactoryGirl.create(:user) + FactoryGirl.create(:friendship, :user => @invited_user, :friend => user) + FactoryGirl.create(:friendship, :user => user, :friend => @invited_user) + @invitation = FactoryGirl.create(:invitation, :sender => user, :receiver => @invited_user, :music_session => @music_session) + + @rsvp_user = FactoryGirl.create(:user) + @rsvp_slot = FactoryGirl.create(:rsvp_slot, music_session: @music_session) + @rsvp_request = FactoryGirl.create(:rsvp_request_for_slots, chosen: nil, user: @rsvp_user, slots: [@rsvp_slot]) + end + + it "should always show start session link for session creator" do + pending + # sign in as creator + fast_signin(user, Nav.find_session) + find("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + fast_signout + end + + it "should not show start session link for anyone who is not creator, invitee, or RSVP user" do + pending + random_user = FactoryGirl.create(:user) + fast_signin(random_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start") + fast_signout + end + + it "should show start session link for invited or RSVP users" do + pending + # make session date/time TBD + @music_session.scheduled_start = Time.now + 5.minutes + @music_session.save! + + # invited user + fast_signin(@invited_user, Nav.find_session) + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + fast_signout + + # RSVP user + fast_signin(@rsvp_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + + # now approve the RSVP + @rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true + @rsvp_request.rsvp_requests_rsvp_slots[0].save! + + fast_signin(@rsvp_user, Nav.find_session) + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + end + + it "should not show start session link for invited or RSVP users when date/time is TBD" do + pending + # make session date/time TBD + @music_session.scheduled_start = nil + @music_session.save! + + # invited user + fast_signin(@invited_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + fast_signout + + # RSVP user + fast_signin(@rsvp_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + + # now approve the RSVP + @rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true + @rsvp_request.rsvp_requests_rsvp_slots[0].save! + + # "start session" should still be hidden + fast_signin(@rsvp_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + end + + it "should not show start session link for invited or RSVP users when more than 15 minutes remain to start time" do + pending + # make session date/time more than 15 min away + @music_session.scheduled_start = Time.now + 60.minutes + @music_session.save! + + # invited user + fast_signin(@invited_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + fast_signout + + # RSVP user + fast_signin(@rsvp_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + + # now approve the RSVP + @rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true + @rsvp_request.rsvp_requests_rsvp_slots[0].save! + + # "start session" should still be hidden + fast_signin(@rsvp_user, Nav.find_session) + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.start", text: "Start session now?") + page.should have_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") + fast_signout + end + end + describe "rsvp behavior" do before(:each) do stub_const("APP_CONFIG", web_config) @@ -222,10 +337,9 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr it "RSVP text shows correctly" do music_session = FactoryGirl.create(:music_session, creator: user) + # session creator cannot cancel fast_signin(user, Nav.find_session) - - find("#sessions-scheduled .rsvp-msg span.text", text: "You have been confirmed for this session. ") - + page.should have_no_selector("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") sign_out # create a slot so the session can be joined @@ -241,15 +355,15 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr # first state: an unconfirmed RSVP go_to_root fast_signin(finder, Nav.find_session) - find("#sessions-scheduled .rsvp-msg span.text", text: "You have RSVP'ed to this session. ") + find("#sessions-scheduled .rsvp-msg span.text a.cancel", text: "Cancel RSVP") rsvp_request.rsvp_requests_rsvp_slots[0].chosen = true rsvp_request.rsvp_requests_rsvp_slots[0].save! - # second state: a connfirmed RSVP + # second state: a confirmed RSVP go_to_root fast_signin(finder, Nav.find_session) - find("#sessions-scheduled .rsvp-msg span.text", text: "You have been confirmed for this session. ") + find("#sessions-scheduled a.cancel", text: "Cancel RSVP") # need to now CANCEL, and check what it says: // VRFS-1891