Fix easydropdown oopsies
This commit is contained in:
parent
abf34bcd5d
commit
5ce780f03b
|
|
@ -843,5 +843,9 @@ module JamRuby
|
|||
|
||||
stats
|
||||
end
|
||||
|
||||
def lesson_session
|
||||
music_session.lesson_session
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ module JamRuby
|
|||
has_many :chat_messages, :class_name => "JamRuby::ChatMessage", :foreign_key => "lesson_session_id"
|
||||
|
||||
|
||||
|
||||
validates :duration, presence: true, numericality: {only_integer: true}
|
||||
validates :lesson_booking, presence: true
|
||||
validates :lesson_type, inclusion: {in: LESSON_TYPES}
|
||||
|
|
@ -73,7 +72,7 @@ module JamRuby
|
|||
scope :completed, -> { where(status: STATUS_COMPLETED) }
|
||||
scope :missed, -> { where(status: STATUS_MISSED) }
|
||||
scope :upcoming, -> { joins(:music_session).where('music_sessions.scheduled_start > ?', Time.now) }
|
||||
scope :past_cancel_window, -> { joins(:music_session).where('music_sessions.scheduled_start > ?', 24.hours.from_now ) }
|
||||
scope :past_cancel_window, -> { joins(:music_session).where('music_sessions.scheduled_start > ?', 24.hours.from_now) }
|
||||
|
||||
def create_charge
|
||||
if !is_test_drive?
|
||||
|
|
@ -104,6 +103,7 @@ module JamRuby
|
|||
def music_session_id
|
||||
music_session.id
|
||||
end
|
||||
|
||||
def self.hourly_check
|
||||
analyse_sessions
|
||||
complete_sessions
|
||||
|
|
@ -141,7 +141,7 @@ module JamRuby
|
|||
now = Time.zone.local_to_utc(now)
|
||||
half_hour_from_now = Time.zone.local_to_utc(half_hour_from_now)
|
||||
end
|
||||
|
||||
|
||||
MusicSession.joins(lesson_session: [:lesson_booking]).where('lesson_sessions.status = ?', LessonSession::STATUS_APPROVED).where('sent_starting_notice = false').where('(scheduled_start > ? and scheduled_start < ?)', now, half_hour_from_now).each do |music_session|
|
||||
lession_session = music_session.lesson_session
|
||||
lession_session.send_starting_notice
|
||||
|
|
@ -184,7 +184,7 @@ module JamRuby
|
|||
lesson_payment_charge.amount_in_cents / 100.0
|
||||
end
|
||||
|
||||
def analysis_to_json(analysis)
|
||||
def self.analysis_to_json(analysis, preserve_object = false)
|
||||
json = {}
|
||||
|
||||
analysis.each do |k, v|
|
||||
|
|
@ -202,7 +202,11 @@ module JamRuby
|
|||
json[k] = v
|
||||
end
|
||||
end
|
||||
json.to_json
|
||||
if preserve_object
|
||||
json
|
||||
else
|
||||
json.to_json
|
||||
end
|
||||
end
|
||||
|
||||
def send_starting_notice
|
||||
|
|
@ -212,6 +216,7 @@ module JamRuby
|
|||
self.sent_starting_notice = true
|
||||
self.save(validate: false)
|
||||
end
|
||||
|
||||
def session_completed
|
||||
LessonSession.transaction do
|
||||
self.lock!
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ module JamRuby
|
|||
# reason: 'both_fault'
|
||||
|
||||
|
||||
def self.analyse(lesson_session)
|
||||
def self.analyse(lesson_session, force = false)
|
||||
reason = nil
|
||||
teacher = nil
|
||||
student = nil
|
||||
|
|
@ -69,7 +69,7 @@ module JamRuby
|
|||
|
||||
# spec: https://jamkazam.atlassian.net/wiki/display/PS/Product+Specification+-+JamClass#ProductSpecification-JamClass-TeacherReceives&RespondstoLessonBookingRequest
|
||||
|
||||
if music_session.session_removed_at.nil? && !((music_session.scheduled_start + (lesson_session.duration * 60)) < Time.now)
|
||||
if !force && (music_session.session_removed_at.nil? && !((music_session.scheduled_start + (lesson_session.duration * 60)) < Time.now))
|
||||
reason = SESSION_ONGOING
|
||||
bill = false
|
||||
else
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ group :test, :cucumber do
|
|||
gem 'simplecov', '~> 0.7.1'
|
||||
gem 'simplecov-rcov'
|
||||
gem 'capybara', '2.4.4'
|
||||
gem 'rails-assets-sinon', source: 'https://rails-assets.org'
|
||||
#if ENV['JAMWEB_QT5'] == '1'
|
||||
# # necessary on platforms such as arch linux, where pacman -S qt5-webkit is your easiet option
|
||||
# gem "capybara-webkit", :git => 'git://github.com/thoughtbot/capybara-webkit.git'
|
||||
|
|
|
|||
|
|
@ -369,7 +369,9 @@
|
|||
"JamTrackGroup": 15,
|
||||
"MetronomeGroup": 16,
|
||||
"MidiInputMusicGroup": 17,
|
||||
"PeerMidiInputMusicGroup": 18
|
||||
"PeerMidiInputMusicGroup": 18,
|
||||
"UsbInputMusicGroup": 19,
|
||||
"PeerUsbInputMusicGroup": 20
|
||||
};
|
||||
|
||||
context.JK.ChannelGroupLookup = {
|
||||
|
|
|
|||
|
|
@ -2231,6 +2231,16 @@
|
|||
});
|
||||
}
|
||||
|
||||
function getLessonAnalysis(options) {
|
||||
options = options || {}
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
url: "/api/lesson_sessions/" + options.id + "/analysis",
|
||||
dataType: "json",
|
||||
contentType: 'application/json'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function updateLessonSessionUnreadMessages(options) {
|
||||
return $.ajax({
|
||||
|
|
@ -2663,6 +2673,7 @@
|
|||
this.submitStripe = submitStripe;
|
||||
this.getLessonSessions = getLessonSessions;
|
||||
this.getLesson = getLesson;
|
||||
this.getLessonAnalysis = getLessonAnalysis;
|
||||
this.updateLessonSessionUnreadMessages = updateLessonSessionUnreadMessages;
|
||||
this.checkLessonCancel = checkLessonCancel;
|
||||
this.checkLessonReschedule = checkLessonReschedule;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
//= require ./react-components/stores/VideoStore
|
||||
//= require ./react-components/stores/SessionStore
|
||||
//= require ./react-components/stores/SessionStatsStore
|
||||
//= require ./react-components/stores/BroadcastStore
|
||||
//= require ./react-components/stores/ChatStore
|
||||
//= require ./react-components/stores/MixerStore
|
||||
//= require ./react-components/stores/ConfigureTracksStore
|
||||
|
|
|
|||
|
|
@ -11,8 +11,11 @@ BroadcastHolder = React.createClass(
|
|||
render: ->
|
||||
notification = []
|
||||
if this.state.notification
|
||||
notification.push(`<Broadcast key={this.state.notification.id} notification={this.state.notification}/>`)
|
||||
|
||||
if this.state.notification.isLesson
|
||||
notification.push(`<InLessonBroadcast key={'lesson'} lessonSession={this.state.notification}/>`)
|
||||
else
|
||||
notification.push(`<Broadcast key={this.state.notification.id} notification={this.state.notification}/>`)
|
||||
|
||||
`<div id="broadcast-notification-holder" className="broadcast-notification-holder" >
|
||||
<ReactCSSTransitionGroup transitionName="bn-slidedown">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
context = window
|
||||
|
||||
@InLessonBroadcast = React.createClass({
|
||||
displayName: 'In Lesson Broadcast'
|
||||
|
||||
displayTime: (minuteOffset = 0) ->
|
||||
untilTime = @props.lessonSession.until
|
||||
|
||||
timeString = ''
|
||||
if untilTime.days != 0
|
||||
timeString += "#{untilTime.days} days, "
|
||||
if untilTime.hours != 0 || timeString.length > 0
|
||||
timeString += "#{untilTime.hours} hours, "
|
||||
if untilTime.minutes != 0 || timeString.length > 0
|
||||
timeString += "#{untilTime.minutes + minuteOffset} minutes, "
|
||||
if untilTime.seconds != 0 || timeString.length > 0
|
||||
timeString += "#{untilTime.seconds} seconds"
|
||||
|
||||
if timeString == ''
|
||||
'now!'
|
||||
timeString
|
||||
|
||||
render: () ->
|
||||
console.log("@props.lessonSession", @props.lessonSession)
|
||||
if @props.lessonSession.completed
|
||||
if @props.lessonSession.success
|
||||
content = `<div className="message">
|
||||
<p>This lesson is over.</p>
|
||||
</div>`
|
||||
else
|
||||
content = `<div className="message">
|
||||
<p>This lesson is over, but will not be billed.</p>
|
||||
</div>`
|
||||
else if @props.lessonSession.beforeSession
|
||||
content = `<div className="message">
|
||||
<p>This lesson will start in:</p>
|
||||
<p className="time">{this.displayTime()}</p>
|
||||
</div>`
|
||||
else if @props.lessonSession.initialWindow
|
||||
content = `<div className="message">
|
||||
<p>You need to wait in this session for</p>
|
||||
<p className="time">{this.displayTime(10)}</p>
|
||||
<p>to allow time for your teacher to join you. If you leave before this timer reaches zero, and your teacher joins this session, you will be marked absent and charged for the lesson.</p>
|
||||
</div>`
|
||||
else if @props.lessonSession.teacherFault
|
||||
if @props.lessonSession.teacherPresent?
|
||||
content = `<div className="message">
|
||||
<p>You may now leave the session. However, if you choose to stay in the session with the teacher, after 5 minutes together the session will be considered a success, and you will be billed.</p>
|
||||
<p>If the two of you do not spend at least 5 minutes together in the session, your teacher will be marked absent and penalized for missing the lesson. You will not be charged for this lesson.</p>
|
||||
</div>`
|
||||
else
|
||||
content = `<div className="message">
|
||||
<p>You may now leave the session.</p>
|
||||
<p>Your teacher will be marked absent and penalized for missing the lesson. You will not be charged for this lesson.</p>
|
||||
<p>We apologize for your inconvenience, and we will work to remedy this situation.</p>
|
||||
</div>`
|
||||
|
||||
`<div className="broadcast-notification lesson">
|
||||
{content}
|
||||
</div>`
|
||||
|
||||
})
|
||||
|
|
@ -27,6 +27,16 @@ context = window
|
|||
|
||||
found
|
||||
|
||||
findParticipantByUserId: (userId) ->
|
||||
foundParticipant = null
|
||||
for participant in @participants()
|
||||
if participant.user.id == userId
|
||||
foundParticipant = participant
|
||||
break
|
||||
|
||||
foundParticipant
|
||||
|
||||
|
||||
otherParticipants: () ->
|
||||
others = []
|
||||
for participant in @participants()
|
||||
|
|
|
|||
|
|
@ -11,13 +11,116 @@ BroadcastStore = Reflux.createStore(
|
|||
{
|
||||
listenables: broadcastActions
|
||||
|
||||
currentSession: null
|
||||
currentLesson: null
|
||||
broadcast: null
|
||||
currentLessonTimer: null
|
||||
teacherFault: false
|
||||
init: ->
|
||||
this.listenTo(context.AppStore, this.onAppInit);
|
||||
this.listenTo(context.SessionStore, this.onSessionChange)
|
||||
|
||||
onAppInit: (@app) ->
|
||||
|
||||
|
||||
getTimeRemaining: (endtime) ->
|
||||
t = Date.parse(endtime) - new Date().getTime()
|
||||
seconds = Math.floor( (t/1000) % 60 );
|
||||
minutes = Math.floor( (t/1000/60) % 60 );
|
||||
hours = Math.floor( (t/(1000*60*60)) % 24 );
|
||||
days = Math.floor( t/(1000*60*60*24) );
|
||||
return {
|
||||
'total': t,
|
||||
'days': days,
|
||||
'hours': hours,
|
||||
'minutes': minutes,
|
||||
'seconds': seconds
|
||||
};
|
||||
|
||||
lessonTick: () ->
|
||||
@timeManagement()
|
||||
@changed()
|
||||
|
||||
timeManagement: () ->
|
||||
lastCheck = $.extend({}, @currentLesson)
|
||||
lessonSession = @currentLesson
|
||||
lessonSession.until = @getTimeRemaining(lessonSession.scheduled_start)
|
||||
if lessonSession.until.total < 0
|
||||
# we are past the start time
|
||||
if lessonSession.until.total < 10 * 60 * 1000 # 10 minutes
|
||||
lessonSession.initialWindow = false
|
||||
else
|
||||
lessonSession.initialWindow = true
|
||||
lessonSession.beforeSession = false
|
||||
else
|
||||
# we are before the due time
|
||||
lessonSession.initialWindow = false
|
||||
lessonSession.beforeSession = true
|
||||
|
||||
# if we've transitioned to a new window
|
||||
|
||||
if !lessonSession.beforeSession && ((lastCheck.initialWindow || !lastCheck.initialWindow?) && !lessonSession.initialWindow)
|
||||
logger.debug("BroadcastStore: lesson session 'initial window' transition")
|
||||
rest.getLessonAnalysis({id: lessonSession.id}).done((response) => @lessonAnalysisDone(response)).fail(@app.ajaxError)
|
||||
|
||||
lessonAnalysisDone: (@analysis) ->
|
||||
|
||||
if !@currentLesson?
|
||||
logger.debug("BroadcastStore: ignoring lessonAnalysisDone")
|
||||
if @analysis.status == 'completed'
|
||||
@currentLesson.completed = true
|
||||
@currentLesson.success = @analysis.success
|
||||
@changed()
|
||||
else if @analysis.analysis.reason != 'teacher_fault'
|
||||
@clearLesson()
|
||||
else
|
||||
@teacherFault = true
|
||||
@changed()
|
||||
|
||||
clearLesson: () ->
|
||||
if @currentLesson?
|
||||
@currentLesson = null
|
||||
if @currentLessonTimer?
|
||||
clearInterval(@currentLessonTimer)
|
||||
@currentLessonTimer = null
|
||||
@teacherFault = false
|
||||
@changed()
|
||||
|
||||
onSessionChange: (session) ->
|
||||
|
||||
@session = session
|
||||
currentSession = session.session
|
||||
if currentSession? && currentSession.lesson_session? && session.inSession()
|
||||
|
||||
@currentSession = currentSession
|
||||
|
||||
lessonSession = currentSession.lesson_session
|
||||
# so that receivers can check type of info coming at them via one-way events
|
||||
lessonSession.isLesson = true
|
||||
|
||||
if lessonSession.status == 'completed'
|
||||
lessonSession.completed = true
|
||||
lessonSession.success = lessonSession.success
|
||||
#else
|
||||
# rest.getLessonAnalysis({id: lessonSession.id}).done((response) => @lessonAnalysisDone(response)).fail(@app.ajaxError)
|
||||
|
||||
@currentLesson = lessonSession
|
||||
@timeManagement()
|
||||
if !@currentLessonTimer?
|
||||
@currentLessonTimer = setInterval((() => @lessonTick()), 1000)
|
||||
@changed()
|
||||
|
||||
else
|
||||
@clearLesson()
|
||||
|
||||
onLoad: () ->
|
||||
logger.debug("loading broadcast notification...")
|
||||
|
||||
onLoadCompleted: (response) ->
|
||||
if response.id?
|
||||
logger.debug("broadcast notification sync completed")
|
||||
this.trigger(response)
|
||||
@broadcast = response
|
||||
@changed()
|
||||
|
||||
|
||||
onLoadFailed: (jqXHR) ->
|
||||
|
|
@ -25,7 +128,16 @@ BroadcastStore = Reflux.createStore(
|
|||
logger.error("broadcast notification sync failed")
|
||||
|
||||
onHide: () ->
|
||||
this.trigger(null)
|
||||
@broadcast = null
|
||||
@changed()
|
||||
|
||||
changed: () ->
|
||||
if @currentLesson?
|
||||
@currentLesson.teacherFault = @teacherFault
|
||||
@currentLesson.teacherPresent = @session.findParticipantByUserId(@currentLesson.teacher_id)
|
||||
this.trigger(@currentLesson)
|
||||
else
|
||||
this.trigger(@broadcast)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -552,6 +552,15 @@ ConfigureTracksActions = @ConfigureTracksActions
|
|||
# TODO: find it via some REST API if not found?
|
||||
return $.Deferred().reject().promise();
|
||||
|
||||
findParticipantByUserId: (userId) ->
|
||||
foundParticipant = null
|
||||
for participant in @participants()
|
||||
if participant.user.id == userId
|
||||
foundParticipant = participant
|
||||
break
|
||||
|
||||
foundParticipant
|
||||
|
||||
displayWhoCreatedRecording: (clientId) ->
|
||||
if @app.clientId != clientId # don't show to creator
|
||||
@findUserBy({clientId: clientId})
|
||||
|
|
|
|||
|
|
@ -366,7 +366,7 @@
|
|||
padding-top: 3px;
|
||||
}
|
||||
|
||||
+ .easydropdown-wrapper {
|
||||
.easydropdown {
|
||||
float:left;
|
||||
margin-left:2px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,17 @@
|
|||
margin-left:60px;
|
||||
@include border_box_sizing;
|
||||
|
||||
|
||||
&.lesson {
|
||||
p {
|
||||
text-align:center;
|
||||
color:$ColorTextTypical;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
.message {
|
||||
width:100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.message {
|
||||
float:left;
|
||||
|
|
@ -27,6 +38,12 @@
|
|||
font-size:12px;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size:20px;
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.actions {
|
||||
float:right;
|
||||
text-align: right;
|
||||
|
|
|
|||
|
|
@ -20,9 +20,25 @@ class ApiLessonSessionsController < ApiController
|
|||
|
||||
end
|
||||
|
||||
def analysis
|
||||
if @lesson_session.analysed
|
||||
data = JSON.parse(@lesson_session.analysis)
|
||||
else
|
||||
data = LessonSession.analysis_to_json(LessonSessionAnalyser.analyse(@lesson_session), true)
|
||||
end
|
||||
|
||||
response = {
|
||||
analysis: data,
|
||||
status: @lesson_session.status,
|
||||
success: @lesson_session.success
|
||||
}
|
||||
|
||||
render :json => response, :status => 200
|
||||
end
|
||||
|
||||
def start_time
|
||||
if !current_user.admin
|
||||
response = { message: 'not admin' }
|
||||
response = {message: 'not admin'}
|
||||
render :json => response, :status => 422
|
||||
else
|
||||
time = Time.zone.local_to_utc(Time.now + params[:minutes].to_i * 60)
|
||||
|
|
@ -51,22 +67,22 @@ class ApiLessonSessionsController < ApiController
|
|||
|
||||
def reschedule_check
|
||||
|
||||
# check if within 24 hours
|
||||
# check if within 24 hours
|
||||
|
||||
if params[:update_all]
|
||||
# check if the next scheduled lesson is doable
|
||||
if 24.hours.from_now > @lesson_session.lesson_booking.next_lesson.music_session.scheduled_start
|
||||
response = { message: 'time_limit' }
|
||||
render :json => response, :status => 422
|
||||
return
|
||||
end
|
||||
else
|
||||
if 24.hours.from_now > @lesson_session.music_session.scheduled_start
|
||||
response = { message: 'time_limit' }
|
||||
render :json => response, :status => 422
|
||||
return
|
||||
end
|
||||
if params[:update_all]
|
||||
# check if the next scheduled lesson is doable
|
||||
if 24.hours.from_now > @lesson_session.lesson_booking.next_lesson.music_session.scheduled_start
|
||||
response = {message: 'time_limit'}
|
||||
render :json => response, :status => 422
|
||||
return
|
||||
end
|
||||
else
|
||||
if 24.hours.from_now > @lesson_session.music_session.scheduled_start
|
||||
response = {message: 'time_limit'}
|
||||
render :json => response, :status => 422
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
render :json => {}, :status => 200
|
||||
end
|
||||
|
|
@ -78,13 +94,13 @@ class ApiLessonSessionsController < ApiController
|
|||
if params[:update_all]
|
||||
# check if the next scheduled lesson is doable
|
||||
if 24.hours.from_now > @lesson_session.booking.next_lesson.music_session.scheduled_start
|
||||
response = { message: 'time_limit' }
|
||||
response = {message: 'time_limit'}
|
||||
render :json => response, :status => 422
|
||||
return
|
||||
end
|
||||
else
|
||||
if 24.hours.from_now > @lesson_session.music_session.scheduled_start
|
||||
response = { message: 'time_limit' }
|
||||
response = {message: 'time_limit'}
|
||||
render :json => response, :status => 422
|
||||
return
|
||||
end
|
||||
|
|
@ -112,4 +128,5 @@ class ApiLessonSessionsController < ApiController
|
|||
raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -74,6 +74,10 @@ else
|
|||
attributes :id, :sender_id, :receiver_id
|
||||
}
|
||||
|
||||
child(lesson_session: :lesson_session) {
|
||||
attributes :id, :scheduled_start, :status, :teacher_id, :success, :duration
|
||||
}
|
||||
|
||||
# only show join_requests if the current_user is in the session
|
||||
child({:join_requests => :join_requests}, :if => lambda { |music_session| music_session.users.exists?(current_user) } ) {
|
||||
attributes :id, :text
|
||||
|
|
|
|||
|
|
@ -696,6 +696,7 @@ SampleApp::Application.routes.draw do
|
|||
match '/lesson_sessions/:id/start_time' => 'api_lesson_sessions#start_time', :via => :post
|
||||
match '/lesson_sessions/:id/reschedule_check' => 'api_lesson_sessions#reschedule_check', :via => :post
|
||||
match '/lesson_sessions/:id/cancel_check' => 'api_lesson_sessions#cancel_check', :via => :post
|
||||
match '/lesson_sessions/:id/analysis' => 'api_lesson_sessions#analysis', :via => :get
|
||||
match '/lesson_bookings' => 'api_lesson_bookings#create', :via => :post
|
||||
match '/lesson_bookings/:id/accept' => 'api_lesson_bookings#accept', :via => :post
|
||||
match '/lesson_bookings/:id/counter' => 'api_lesson_bookings#counter', :via => :post
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# verifies the
|
||||
|
||||
describe "Lesson Session Broadcast", :js => true, :type => :feature, :capybara_feature => true do
|
||||
|
||||
subject { page }
|
||||
|
||||
let(:user) { FactoryGirl.create(:user) }
|
||||
let(:ams) { FactoryGirl.create(:active_music_session, creator: user) }
|
||||
|
||||
before(:each) do
|
||||
ActiveMusicSession.delete_all
|
||||
MusicSession.delete_all
|
||||
end
|
||||
|
||||
it "shows before message" do
|
||||
testdrive_lesson
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
Reference in New Issue