Merge branch 'develop' of bitbucket.org:jamkazam/jam-cloud into develop

This commit is contained in:
Seth Call 2015-01-12 13:40:38 -06:00
commit 5f9986b30e
30 changed files with 598 additions and 65 deletions

View File

@ -241,3 +241,4 @@ jam_track_available.sql
active_jam_track.sql
bpms_on_tap_in.sql
jamtracks_job.sql
text_messages.sql

8
db/up/text_messages.sql Normal file
View File

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

View File

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

View File

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

View File

@ -0,0 +1,51 @@
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
def self.create(message, target_user_id, source_user_id)
sanitized_text = Sanitize.fragment(message, elements: HtmlSanitize::SAFE)
# create new message
tm = TextMessage.new
tm.message = sanitized_text
tm.target_user_id = target_user_id
tm.source_user_id = source_user_id
# send notification
@notification = Notification.send_text_message(sanitized_text, source_user_id, User.find_by_id(target_user_id))
tm
end
end
end

View File

@ -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"
@ -1017,6 +1020,8 @@ module JamRuby
user.save
user.errors.add("recaptcha", "verification failed") if recaptcha_failed
if user.errors.any?
raise ActiveRecord::Rollback
else
@ -1033,16 +1038,9 @@ module JamRuby
UserMailer.confirm_email(user, signup_confirm_url.nil? ? nil : (signup_confirm_url + "/" + user.signup_token) ).deliver
end
end
if recaptcha_failed
user.errors.add "recaptcha", "verification failed"
raise ActiveRecord::Rollback
end
end
return user
end
user
end # def signup
# this is intended to be development-mode or test-mode only; VRFS-149
# it creates or updates one user per developer, so that we aren't in the business

View File

@ -0,0 +1,46 @@
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
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 => 10})
messages.count.should == 1
end
it "should not allow empty message" do
expect { @msg.save! }.to raise_error(ActiveRecord::RecordInvalid)
end
end
end

View File

@ -339,7 +339,14 @@
if ("instrument_list" in pending_rsvp_request && pending_rsvp_request.instrument_list != null) {
$.each(pending_rsvp_request.instrument_list, function (index, instrument) {
var instrumentId = context.JK.getInstrumentId(instrument.id);
var instrumentId;
if (instrument) {
instrumentId = context.JK.getInstrumentId(instrument.id);
}
else {
instrumentId = 'other';
}
var inst = context.JK.getInstrumentIcon24(instrumentId);
instrumentLogoHtml += '<img title="' + instrumentId + '" hoveraction="instrument" data-instrument-id="' + instrumentId + '" src="' + inst + '" width="24" height="24" />&nbsp;';
instrumentDesc.push(instrumentId);
@ -378,7 +385,14 @@
$.each(sessionData.approved_rsvps, function(index, approved_rsvp) {
if ("instrument_list" in approved_rsvp) {
$.each(approved_rsvp.instrument_list, function(index, instrument) {
var instrumentId = context.JK.getInstrumentId(instrument.id);
var instrumentId;
if (instrument) {
instrumentId = context.JK.getInstrumentId(instrument.id);
}
else {
instrumentId = 'other';
}
var inst = context.JK.getInstrumentIcon24(instrumentId);
instrumentLogoHtml += '<img title="' + instrumentId + '" hoveraction="instrument" data-instrument-id="' + instrumentId + '" src="' + inst + '" width="24" height="24" />&nbsp;';
});

View File

@ -0,0 +1,130 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.SessionStartDialog = function(app, session) {
var logger = context.JK.logger;
var sessionUtils = context.JK.SessionUtils;
var $dialog = null;
var dialogId = 'session-start-dialog';
var $btnStartSession = null;
function beforeShow(data) {
}
function afterShow(data) {
}
function afterHide() {
}
function showDialog() {
return app.layout.showDialog(dialogId);
}
function events() {
$btnStartSession.unbind('click');
$btnStartSession.click(function(e) {
context.location = '/client#/session/' + session.id;
app.layout.closeDialog(dialogId);
});
}
function initializeSessionDetails() {
$dialog.find('#session-start-type-disp').html('Now!');
$dialog.find('#session-name-disp').html(session.name);
$dialog.find('#session-description-disp').html(session.description);
if (session.music_notations && session.music_notations.length > 0) {
$dialog.find('#session-notations-disp').html("Notations: " + session.music_notations.join(', '));
}
if (session.band) {
$dialog.find('#session-band-disp').html(band.name);
}
else {
$dialog.find('#session-band-disp').html('N/A');
}
$dialog.find('#session-language-disp').html(session.language_description);
var invitedFriends = session.invitations;
var sessionInvited = [];
$.each(invitedFriends, function(index, invitation) {
sessionInvited.push(invitation.receiver_name);
});
var sessionInvitedString = sessionInvited.join(', ');
if (session.musician_access && session.approval_required) {
if (session.open_rsvps) {
if (invitedFriends.length == 0)
sessionInvitedString = "Any interested JamKazam musicians that I approve";
else
sessionInvitedString += ", plus any interested JamKazam musicians that I approve";
}
else {
if (invitedFriends.length == 0) {
sessionInvitedString = "No open RSVPs";
}
else {
sessionInvitedString += " (No open RSVPs)";
}
}
}
else if (session.musician_access && !session.approval_required) {
if (invitedFriends.length == 0)
sessionInvitedString = "Any interested JamKazam musicians who want to join us";
else
sessionInvitedString += ", plus any interested JamKazam musicians who want to join us";
}
$dialog.find('#session-invited-disp').html(sessionInvitedString);
var instrumentsMe = [], instrumentsOthers = [];
$.each(session.approved_rsvps, function(index, rsvp) {
if (rsvp.id === context.JK.currentUserId) {
$.each(rsvp.instrument_list, function(index, instrument) {
instrumentsMe.push(instrument.desc);
});
}
else {
$.each(rsvp.instrument_list, function(index, instrument) {
instrumentsOthers.push(instrument.desc);
});
}
});
$dialog.find('#session-instruments-me-disp').html(instrumentsMe.join(', '));
$dialog.find('#session-instruments-rsvp-disp').html(instrumentsOthers.join(', '));
$dialog.find('#session-musician-access-disp').html('Musicians: ' + session.musician_access_description);
$dialog.find('#session-fans-access-disp').html('Fans: ' + session.fan_access_description);
$dialog.find('#session-policy-disp').html(session.legal_policy);
}
function initialize() {
var dialogBindings = {
'beforeShow' : beforeShow,
'afterShow' : afterShow,
'afterHide': afterHide
};
app.bindDialog(dialogId, dialogBindings);
$dialog = $('[layout-id="' + dialogId + '"]');
$btnStartSession = $dialog.find('.btnStartSession');
initializeSessionDetails();
events();
}
this.initialize = initialize;
this.showDialog = showDialog;
}
return this;
})(window,jQuery);

View File

@ -163,6 +163,7 @@
}
$sendTextMessage.click(sendMessage);
// TODO: PULL FROM NEW TABLE
rest.getNotifications(buildParams())
.done(function (response) {
context._.each(response, function (textMessage) {

View File

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

View File

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

View File

@ -200,14 +200,14 @@
createSessionSettings.startDate = createSessionSettings.startDate || (new Date().toDateString());
$("#session-start-date").val(createSessionSettings.startDate);
$screen.find("#session-start-date").val(createSessionSettings.startDate);
toggleDate(true);
toggleStartTime();
toggleStepStatus();
sessionUtils.defaultTimezone($timezoneList);
if(firstTimeShown) {
firstTimeShown = false;
$('#session-when-start-scheduled').iCheck('check');
$screen.find('#session-when-start-scheduled').iCheck('check');
}
}
@ -217,7 +217,7 @@
function beforeShowStep3() {
rest.getBands(context.JK.currentUserId)
.done(function(result) {
var options = $("#session-band-list");
var options = $screen.find("#session-band-list");
options.empty();
options.append($("<option />").val('').text('No'));
$.each(result, function(idx, item) {
@ -251,10 +251,10 @@
var sessionName = createSessionSettings.name;
sessionName += ' (' + createSessionSettings.genresValues[0] + ')';
$('#session-name-disp').html(sessionName);
$screen.find('#session-name-disp').html(sessionName);
var sessionDescription = createSessionSettings.description;
$('#session-description-disp').html(sessionDescription);
$screen.find('#session-description-disp').html(sessionDescription);
var sessionNotations = [];
for (var i = 0; i < createSessionSettings.notations.length; i++) {
@ -262,16 +262,16 @@
sessionNotations.push(name);
}
if(sessionNotations.length > 0) {
$('#session-notations-disp').html("Notations: " + sessionNotations.join(', '));
$screen.find('#session-notations-disp').html("Notations: " + sessionNotations.join(', '));
}
else {
$('#session-notations-disp').html('');
$screen.find('#session-notations-disp').html('');
}
$('#session-language-disp').html(createSessionSettings.language.label);
$('#session-band-disp').html(createSessionSettings.band.label);
$screen.find('#session-language-disp').html(createSessionSettings.language.label);
$screen.find('#session-band-disp').html(createSessionSettings.band.label);
var plusMusicians = $('#session-plus-musicians')[0].checked;
var plusMusicians = $screen.find('#session-plus-musicians')[0].checked;
var sessionInvited = [];
var invitedFriends = inviteMusiciansUtil.getInvitedFriendNames();
@ -302,7 +302,7 @@
else
sessionInvitedString += ", plus any interested JamKazam musicians who want to join us";
}
$('#session-invited-disp').html(sessionInvitedString);
$screen.find('#session-invited-disp').html(sessionInvitedString);
if (createSessionSettings.createType == '<%= MusicSession::CREATE_TYPE_START_SCHEDULED%>') {
var session = scheduledSessions[createSessionSettings.selectedSessionId];
@ -315,7 +315,7 @@
});
}
});
$('#session-instruments-me-disp').html(instruments_me.join(', '));
$screen.find('#session-instruments-me-disp').html(instruments_me.join(', '));
}
if (session.open_slots.length > 0) {
@ -334,7 +334,7 @@
$.map(instruments_rsvp_arr, function(val, i) {
instruments_str_arr.push(i + ' (' + val.count + ') (' + val.level + ')');
})
$('#session-instruments-rsvp-disp').html(instruments_str_arr.join(', '));
$screen.find('#session-instruments-rsvp-disp').html(instruments_str_arr.join(', '));
}
}
else {
@ -342,7 +342,7 @@
$.each(getCreatorInstruments(), function(index, instrument) {
instruments_me.push(instrument.name);
});
$('#session-instruments-me-disp').html(instruments_me.join(', '));
$screen.find('#session-instruments-me-disp').html(instruments_me.join(', '));
var instruments_rsvp = [];
var otherInstruments = instrumentRSVP.getSelectedInstruments();
@ -353,13 +353,13 @@
$.each(otherInstruments, function(index, instrument) {
instruments_rsvp.push(instrument.name + ' (' + instrument.count + ') (' + proficiencyDescriptionMap[instrument.level] + ')');
});
$('#session-instruments-rsvp-disp').html(instruments_rsvp.join(', '));
$screen.find('#session-instruments-rsvp-disp').html(instruments_rsvp.join(', '));
}
$('#session-musician-access-disp').html('Musicians: ' + createSessionSettings.musician_access.label);
$('#session-fans-access-disp').html('Fans: ' + createSessionSettings.fans_access.label);
$screen.find('#session-musician-access-disp').html('Musicians: ' + createSessionSettings.musician_access.label);
$screen.find('#session-fans-access-disp').html('Fans: ' + createSessionSettings.fans_access.label);
$('#session-policy-disp').html(createSessionSettings.session_policy);
$screen.find('#session-policy-disp').html(createSessionSettings.session_policy);
}
function beforeMoveStep1() {
@ -428,7 +428,7 @@
createSessionSettings.recurring_mode.value = 'once';
}
else {
createSessionSettings.startDate = $('#session-start-date').val();
createSessionSettings.startDate = $screen.find('#session-start-date').val();
createSessionSettings.startTime = $startTimeList.val();
createSessionSettings.endTime = $endTimeList.val();
createSessionSettings.notations = [];
@ -437,7 +437,7 @@
createSessionSettings.timezone.label = $timezoneList.get(0).options[$timezoneList.get(0).selectedIndex].text;
createSessionSettings.recurring_mode.label = $recurringModeList.get(0).options[$recurringModeList.get(0).selectedIndex].text;
createSessionSettings.recurring_mode.value = $recurringModeList.val();
createSessionSettings.open_rsvps = $('#session-plus-musicians')[0].checked;
createSessionSettings.open_rsvps = $screen.find('#session-plus-musicians')[0].checked;
}
return true;
@ -503,22 +503,22 @@
function beforeMoveStep2() {
var isValid = true;
var name = $('#session-name').val();
var name = $screen.find('#session-name').val();
if (!name) {
$('#divSessionName .error-text').remove();
$('#divSessionName').addClass("error");
$('#session-name').after("<ul class='error-text'><li>Name is required</li></ul>");
$screen.find('#session-name').after("<ul class='error-text'><li>Name is required</li></ul>");
isValid = false;
}
else {
$('#divSessionName').removeClass("error");
}
var description = $('#session-description').val();
var description = $screen.find('#session-description').val();
if (!description) {
$('#divSessionDescription .error-text').remove();
$('#divSessionDescription').addClass("error");
$('#session-description').after("<ul class='error-text'><li>Description is required</li></ul>");
$screen.find('#session-description').after("<ul class='error-text'><li>Description is required</li></ul>");
isValid = false;
}
else {
@ -558,13 +558,13 @@
else
createSessionSettings.band.label = $bandList.get(0).options[$bandList.get(0).selectedIndex].text;
createSessionSettings.open_rsvps = $('#session-plus-musicians')[0].checked;
createSessionSettings.open_rsvps = $screen.find('#session-plus-musicians')[0].checked;
return true;
}
function beforeMoveStep4() {
var isValid = true;
var sessionPolicyChecked = $('#session-policy-confirm').is(':checked');
var sessionPolicyChecked = $screen.find('#session-policy-confirm').is(':checked');
if (!sessionPolicyChecked) {
$('#divSessionPolicy .error-text').remove();
$('#divSessionPolicy').addClass("error");
@ -576,11 +576,11 @@
}
createSessionSettings.session_policy = $('input[name="session-policy-type"][checked="checked"]').attr('policy-id');
var $musicianAccess = $('#session-musician-access');
var $musicianAccess = $screen.find('#session-musician-access');
createSessionSettings.musician_access.value = $musicianAccess.val();
createSessionSettings.musician_access.label = $musicianAccess.get(0).options[$musicianAccess.get(0).selectedIndex].text;
var $fansAccess = $('#session-fans-access');
var $fansAccess = $screen.find('#session-fans-access');
createSessionSettings.fans_access.value = $fansAccess.val();
createSessionSettings.fans_access.label = $fansAccess.get(0).options[$fansAccess.get(0).selectedIndex].text;
@ -975,7 +975,7 @@
}
function toggleDate(dontRebuildDropdowns) {
var selectedDate = new Date($('#session-start-date').val());
var selectedDate = new Date($screen.find('#session-start-date').val());
var currentDate = new Date();
var startIndex = 0;
@ -1046,7 +1046,7 @@
radioClass: 'iradio_minimal',
inheritClass: true
});
$("#session-start-date").datepicker({
$screen.find("#session-start-date").datepicker({
dateFormat: "D d MM yy",
onSelect: function() { toggleDate(); }
}

View File

@ -19,7 +19,7 @@
var $musicianTemplate = $('#template-musician-info');
var showJoinLink = true;
var showListenLink = true;
var showRsvpLink = true;
var MAX_MINUTES_SHOW_START = 15;
// related to listen
function stateChange(e, data) {
@ -369,11 +369,39 @@
var showRsvpLink = true;
var noLinkText = '';
$('.rsvp-link-text', $parentRow).hide();
if (approvedRsvpId) {
function showStartSessionButton(scheduledStart) {
var now = new Date();
var scheduledDate = new Date(scheduledStart);
var minutesFromStart = (scheduledDate.getTime() - now.getTime()) / (1000 * 60);
return minutesFromStart <= MAX_MINUTES_SHOW_START;
};
if (session.creator.id === context.JK.currentUserId) {
showRsvpLink = false;
noLinkText = $('<span class="text">You have been confirmed for this session. <a href="#" style="color: #fc0">Cancel</a></span>');
noLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
noLinkText.find('a').click(function() {
ui.launchSessionStartDialog(session);
return false;
});
}
else if (approvedRsvpId) {
showRsvpLink = false;
if (session.scheduled_start && showStartSessionButton(session.scheduled_start)) {
noLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a>&nbsp;|&nbsp;<a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
noLinkText.find('a.start').click(function() {
ui.launchSessionStartDialog(session);
return false;
});
}
else {
noLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
}
// wire cancel link
noLinkText.find('a.cancel').click(function() {
ui.launchRsvpCancelDialog(session.id, approvedRsvpId)
.one(EVENTS.RSVP_CANCELED, function() {
rest.getSessionHistory(session.id)
@ -387,9 +415,20 @@
return false;
});
}
else if (hasInvitation) {
showRsvpLink = false;
if (session.scheduled_start && showStartSessionButton(session.scheduled_start)) {
noLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
noLinkText.find('a').click(function() {
ui.launchSessionStartDialog(session);
return false;
});
}
}
else if (pendingRsvpId) {
showRsvpLink = false;
noLinkText = $('<span class="text">You have RSVP\'ed to this session. <a href="#" style="color: #fc0">Cancel</a></span>');
noLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
noLinkText.find('a').click(function() {
ui.launchRsvpCancelDialog(session.id, pendingRsvpId)
.one(EVENTS.RSVP_CANCELED, function() {
@ -416,6 +455,7 @@
if (showRsvpLink) {
$('.rsvp-msg', $parentRow).hide();
$('.rsvp-link', $parentRow).show();
$('.rsvp-link-text', $parentRow).show();
$('.rsvp-link', $parentRow).click(function(evt) {
ui.launchRsvpSubmitDialog(session.id)

View File

@ -53,6 +53,12 @@
return rsvpDialog.showDialog();
}
function launchSessionStartDialog(session) {
var sessionStartDialog = new JK.SessionStartDialog(JK.app, session);
sessionStartDialog.initialize();
return sessionStartDialog.showDialog();
}
this.addSessionLike = addSessionLike;
this.addRecordingLike = addRecordingLike;
this.launchCommentDialog = launchCommentDialog;
@ -60,6 +66,7 @@
this.launchRsvpSubmitDialog = launchRsvpSubmitDialog;
this.launchRsvpCancelDialog = launchRsvpCancelDialog;
this.launchRsvpCreateSlotDialog = launchRsvpCreateSlotDialog;
this.launchSessionStartDialog = launchSessionStartDialog;
return this;
};

View File

@ -127,6 +127,10 @@ table.findsession-table, table.local-recordings, table.open-jam-tracks {
vertical-align:top;
}
.musician-groups td.nowrap {
white-space: nowrap;
}
a {
color:#fff;
text-decoration:none;

View File

@ -70,8 +70,8 @@
min-height: 600px;
}
.dialog-overlay-sm {
width: 600px;
.dialog-overlay-lg {
width: 800px;
height: auto;
position: fixed;
left: 50%;
@ -90,6 +90,18 @@
color: #aaa;
}
.dialog-overlay-sm {
width: 600px;
height: auto;
position: fixed;
left: 50%;
top: 20%;
margin-left: -300px;
background-color: #333;
border: 1px solid #ed3618;
z-index: 1000;
}
.dialog-overlay-sm .dialog-inner {
width: 550px;
height: auto;

View File

@ -0,0 +1,17 @@
.session-wrapper {
padding: 10px 35px 0 0px;
white-space: initial;
h3 {
font-weight: bold;
color:#dedede;
}
> div.session {
width: 50%;
&.right {
font-size: 13px;
}
}
}

View File

@ -0,0 +1,18 @@
require 'sanitize'
class ApiTextMessagesController < ApiController
before_filter :api_signed_in_user
before_filter :auth_user, :only => [:index, :create]
respond_to :json
def index
@text_messages = TextMessage.index(params[:target_user_id], @user.id, {:offset => params[:offset]})
respond_with @text_messages, responder: ApiResponder, :status => 200
end
def create
end
end

View File

@ -98,7 +98,9 @@ else
node do |invitation|
user_score(invitation.receiver.id).merge({
receiver_avatar_url: invitation.receiver.resolved_photo_url, audio_latency: last_jam_audio_latency(invitation.receiver)
receiver_name: invitation.receiver.name,
receiver_avatar_url: invitation.receiver.resolved_photo_url,
audio_latency: last_jam_audio_latency(invitation.receiver)
})
end
}

View File

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

View File

@ -196,7 +196,7 @@
<tr>
<td>
</td>
<td>
<td class="nowrap">
<span class="rsvp-msg" style="display:none;">You cannot RSVP to this session.</span>
<a class="rsvp-link">
<%= image_tag "content/rsvp-icon.jpg", :size => "40x40" %>

View File

@ -402,9 +402,9 @@
<h3 class="mt20">What instruments/parts do you need?</h3>
<div class="left ib w20 mt5">Me:</div>
<div class="left ib w75 mt5" id="session-instruments-me-disp" class="mt5"></div>
<div class="left ib w75 mt5" id="session-instruments-me-disp"></div>
<div class="clearall left ib w20">Others:</div>
<div class="left ib w75" id="session-instruments-rsvp-disp" class="mt5"></div><br clear="all">
<div class="left ib w75 mt5" id="session-instruments-rsvp-disp"></div><br clear="all">
<h3 class="mt20">What access policies are in effect?</h3>
<div id="session-musician-access-disp" class="mt5"></div>

View File

@ -18,6 +18,7 @@
= render 'dialogs/rsvpSubmitDialog'
= render 'dialogs/rsvpCancelDialog'
= render 'dialogs/rsvpCreateSlotDialog'
= render 'dialogs/sessionStartDialog'
= render 'dialogs/sessionCancelDialog'
= render 'dialogs/signinDialog'
= render 'dialogs/signupDialog'

View File

@ -0,0 +1,44 @@
.dialog.dialog-overlay-lg layout='dialog' layout-id='session-start-dialog' id='session-start-dialog'
.content-head
= image_tag "content/icon_add.png", {:width => 24, :height => 24, :class => 'content-icon' }
h1 Start Session
.dialog-inner
.session-wrapper
.left.w55
h3 When are you starting your session?
.mt5 id='session-start-type-disp'
h3.mb5.mt20 What are you playing?
em id='session-name-disp'
#session-description-disp.mt5
#session-notations-disp.mt5
h3.mt20 Which band is playing?
#session-band-disp.mt5
h3.mt20 What language will be spoken?
#session-language-disp.mt5
.right.w40
h3 Who is invited?
#session-invited-disp.mt5
h3.mt20 What instruments/parts do you need?
.left.ib.w20.mt5 Me:
#session-instruments-me-disp.left.ib.w75.mt5
.clearall.left.ib.w20.mt5 Others:
#session-instruments-rsvp-disp.left.ib.w75.mt5
br clear='all'
h3.mt20 What access policies are in effect?
#session-musician-access-disp.mt5
#session-fans-access-disp
h3.mt20 What legal policy is in effect?
#session-policy-disp.mt5
.clearall
br
br
.buttons
.right
a.button-grey class='btnCancel' layout-action='cancel' CANCEL
a.button-orange class='btnStartSession' START SESSION

View File

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

View File

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

View File

@ -666,6 +666,7 @@ describe UserManager do
before(:each) do
@old_recaptcha=Rails.application.config.recaptcha_enable
Rails.application.config.recaptcha_enable=true
UserMailer.deliveries.clear
end
after(:each) do
@ -687,6 +688,7 @@ describe UserManager do
signup_confirm_url: "http://localhost:3000/confirm")
user.errors.any?.should be_true
UserMailer.deliveries.should have(0).items
end
it "passes when facebook signup" do