VRFS-4050 - countdown timer

This commit is contained in:
Seth Call 2016-05-09 16:47:55 -05:00
parent 30aec92512
commit 6f9d39ef16
13 changed files with 128 additions and 45 deletions

View File

@ -419,7 +419,7 @@ SQL
end
connection.join_the_session(music_session, as_musician, tracks, user, audio_latency, video_sources)
JamRuby::MusicSessionUserHistory.join_music_session(user.id, music_session.id)
JamRuby::MusicSessionUserHistory.join_music_session(user.id, music_session.id, client_id)
# connection.music_session_id = music_session.id
# connection.as_musician = as_musician
# connection.joining_session = true

View File

@ -447,6 +447,10 @@ module JamRuby
music_session.creator
end
def student_id
music_session.creator.id
end
def self.index(user, params = {})
limit = params[:per_page]
limit ||= 100

View File

@ -69,7 +69,7 @@ module JamRuby
# spec: https://jamkazam.atlassian.net/wiki/display/PS/Product+Specification+-+JamClass#ProductSpecification-JamClass-TeacherReceives&RespondstoLessonBookingRequest
if !force && (music_session.session_removed_at.nil? && !((music_session.scheduled_start + (lesson_session.duration * 60)) < Time.now))
if !force && (!((music_session.scheduled_start + (lesson_session.duration * 60)) < Time.now))
reason = SESSION_ONGOING
bill = false
else
@ -98,8 +98,17 @@ module JamRuby
student = JOINED_LATE
bill = true
end
else
if teacher_analysis[:no_show]
teacher = NO_SHOW
elsif !teacher_analysis[:joined_on_time]
teacher = JOINED_LATE
elsif !teacher_analysis[:waited_correctly]
teacher = MINIMUM_TIME_NOT_MET
end
end
end
if reason.nil?

View File

@ -42,7 +42,7 @@ module JamRuby
def self.save(music_session_id, user_id, client_id, tracks)
return true if 0 < self.where(:music_session_id => music_session_id,
:user_id => user_id,
:client_id => client_id).count
:client_id => client_id).where('session_removed_at is NULL').count
session_user_history = MusicSessionUserHistory.new
session_user_history.music_session_id = music_session_id
session_user_history.user_id = user_id
@ -60,10 +60,12 @@ module JamRuby
(end_time - self.created_at) / 60.0
end
def self.join_music_session(user_id, session_id)
def self.join_music_session(user_id, session_id, client_id)
hist = self
.where(:user_id => user_id)
.where(:music_session_id => session_id)
.where(:client_id => client_id)
.where('session_removed_at IS NULL')
.limit(1)
.first
hist.start_history if hist

View File

@ -24,7 +24,7 @@ describe LessonSessionAnalyser do
Timecop.freeze((lesson.music_session.scheduled_start + lesson.duration * 60) + 1)
analysis = LessonSessionAnalyser.analyse(lesson)
analysis[:reason].should eql LessonSessionAnalyser::NEITHER_SHOW
analysis[:reason].should eql LessonSessionAnalyser::TEACHER_FAULT
analysis[:student].should eql nil
analysis[:bill].should be false
@ -47,8 +47,7 @@ describe LessonSessionAnalyser do
start = lesson.scheduled_start
end_time = lesson.scheduled_start + (60 * lesson.duration)
uh2 = FactoryGirl.create(:music_session_user_history, user: teacher, history: music_session, created_at: start, session_removed_at: end_time)
lesson.music_session.session_removed_at = end_time
lesson.music_session.save!
Timecop.travel(end_time + 1)
analysis = LessonSessionAnalyser.analyse(lesson)
@ -67,6 +66,36 @@ describe LessonSessionAnalyser do
together[:session_time].should eql 0
end
it "student joined 1 min before start time, is waiting for 12 minutes" do
lesson = testdrive_lesson(user, teacher)
music_session = lesson.music_session
# create some bogus, super-perfect teacher/student times
start = lesson.scheduled_start
end_time = lesson.scheduled_start + (60 * lesson.duration)
uh2 = FactoryGirl.create(:music_session_user_history, user: user, history: music_session, created_at: start - 60, session_removed_at: nil)
Timecop.freeze(start + 11 * 60)
analysis = LessonSessionAnalyser.analyse(lesson, true)
analysis[:reason].should eql LessonSessionAnalyser::TEACHER_FAULT
analysis[:teacher].should eql LessonSessionAnalyser::NO_SHOW
analysis[:student].should be_nil
analysis[:bill].should be false
student = analysis[:student_analysis]
student[:joined_on_time].should be true
student[:joined_late].should be false
student[:waited_correctly].should be true
student[:initial_waiting_pct].should eql 1.0
student[:potential_waiting_time].should eql 600.0
student[:session_time].should eql (11 * 60).to_f
together = analysis[:together_analysis]
together[:session_time].should eql 0
end
it "teacher joined on time, waited, student joined late" do
lesson = testdrive_lesson(user, teacher)
music_session = lesson.music_session
@ -78,8 +107,7 @@ describe LessonSessionAnalyser do
uh1 = FactoryGirl.create(:music_session_user_history, user: user, history: music_session, created_at: late_start, session_removed_at: late_start + 4 * 60)
uh2 = FactoryGirl.create(:music_session_user_history, user: teacher, history: music_session, created_at: start, session_removed_at: end_time)
lesson.music_session.session_removed_at = end_time
lesson.music_session.save!
Timecop.travel(end_time + 1)
analysis = LessonSessionAnalyser.analyse(lesson)
@ -117,8 +145,7 @@ describe LessonSessionAnalyser do
analysis = LessonSessionAnalyser.analyse(lesson)
analysis[:reason].should eql LessonSessionAnalyser::SESSION_ONGOING
lesson.music_session.session_removed_at = end_time
lesson.music_session.save!
Timecop.travel(end_time + 1)
analysis = LessonSessionAnalyser.analyse(lesson)
analysis[:reason].should eql LessonSessionAnalyser::SUCCESS

View File

@ -237,7 +237,7 @@
}, opts.animationDuration);
});
var broadcastWidth = findCardLayout.width + feedCardLayout.width;
var broadcastWidth = findCardLayout.width + feedCardLayout.width; //+ opts.gridPadding * 2;
layoutBroadcast(broadcastWidth, findCardLayout.left);
}

View File

@ -3,16 +3,38 @@ context = window
@InLessonBroadcast = React.createClass({
displayName: 'In Lesson Broadcast'
displayTime: (minuteOffset = 0) ->
untilTime = @props.lessonSession.until
getTimeRemaining: (t) ->
if t < 0
t = -t
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
};
displayTime: () ->
if @props.lessonSession.initialWindow
# offset time by 10 minutes to get the 'you need to wait message' in
untilTime = @getTimeRemaining(@props.lessonSession.until.total + (10 * 60 * 1000))
else
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, "
timeString += "#{untilTime.minutes} minutes, "
if untilTime.seconds != 0 || timeString.length > 0
timeString += "#{untilTime.seconds} seconds"
@ -39,14 +61,14 @@ context = window
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 className="time">{this.displayTime()}</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>
<p>Because your teacher was late, 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, and you will not be charged for this lesson.</p>
</div>`
else
content = `<div className="message">

View File

@ -22,20 +22,29 @@ BroadcastStore = Reflux.createStore(
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) );
t = new Date(endtime).getTime() - new Date().getTime()
originalT = t
if t < 0
seconds = Math.ceil( (t/1000) % 60 );
minutes = Math.ceil( (t/1000/60) % 60 );
hours = Math.ceil( (t/(1000*60*60)) % 24 );
days = Math.ceil( t/(1000*60*60*24) );
else
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
};
'total': originalT,
'days': days,
'hours': hours,
'minutes': minutes,
'seconds': seconds
};
lessonTick: () ->
@timeManagement()
@ -47,10 +56,10 @@ BroadcastStore = Reflux.createStore(
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
if lessonSession.until.total > -(10 * 60 * 1000) # 10 minutes
lessonSession.initialWindow = true
else
lessonSession.initialWindow = false
lessonSession.beforeSession = false
else
# we are before the due time
@ -90,7 +99,7 @@ BroadcastStore = Reflux.createStore(
@session = session
currentSession = session.session
if currentSession? && currentSession.lesson_session? && session.inSession()
if currentSession? && currentSession.lesson_session? && session.inSession() && currentSession.lesson_session.student_id == context.JK.currentUserId
@currentSession = currentSession
@ -106,6 +115,7 @@ BroadcastStore = Reflux.createStore(
@currentLesson = lessonSession
@timeManagement()
logger.debug("BroadcastStore: currentLesson until: ", @currentLesson.until, lessonSession.scheduled_start)
if !@currentLessonTimer?
@currentLessonTimer = setInterval((() => @lessonTick()), 1000)
@changed()
@ -135,7 +145,10 @@ BroadcastStore = Reflux.createStore(
if @currentLesson?
@currentLesson.teacherFault = @teacherFault
@currentLesson.teacherPresent = @session.findParticipantByUserId(@currentLesson.teacher_id)
this.trigger(@currentLesson)
if @currentLesson.teacherPresent?
this.trigger(null)
else
this.trigger(@currentLesson)
else
this.trigger(@broadcast)
}

View File

@ -1,13 +1,17 @@
@import 'client/common';
[data-react-class="BroadcastHolder"] {
#broadcast-notification-holder{
position:absolute;
min-height:60px;
top:62px;
bottom:0;
}
[data-react-class="BroadcastHolder"] {
position:relative;
min-height: 125px;
bottom: 60px;
@include border_box_sizing;
.broadcast-notification {
position:absolute;
border-width:1px;
border-color:$ColorScreenPrimary;
border-style:solid;
@ -17,7 +21,6 @@
left:0;
top:0;
overflow:hidden;
margin-left:60px;
@include border_box_sizing;

View File

@ -41,7 +41,8 @@ class ApiLessonSessionsController < ApiController
response = {message: 'not admin'}
render :json => response, :status => 422
else
time = Time.zone.local_to_utc(Time.now + params[:minutes].to_i * 60)
# time = Time.zone.local_to_utc(Time.now + params[:minutes].to_i * 60)
time = Time.current + params[:minutes].to_i * 60
@lesson_session.music_session.scheduled_start = time
@lesson_session.music_session.save!
render :json => {}, :status => 200

View File

@ -75,7 +75,7 @@ else
}
child(lesson_session: :lesson_session) {
attributes :id, :scheduled_start, :status, :teacher_id, :success, :duration
attributes :id, :scheduled_start, :status, :teacher_id, :success, :duration, :student_id
}
# only show join_requests if the current_user is in the session

View File

@ -9,8 +9,11 @@
<a layout-link="session-settings">SS</a>
-->
<%= render "users/user_dropdown" %>
<%= react_component 'BroadcastHolder', {} %>
<!-- Templates -->
<script type="text/template" id="template-search-section">

View File

@ -9,7 +9,6 @@
<div class="dialog-overlay op70" style="display:none; width:100%; height:100%; z-index:99;"></div>
<%= render "header" %>
<%= react_component 'BroadcastHolder', {} %>
<%= render "home" %>
<%= render "footer" %>
<%= render "paginator" %>