2015-09-13 01:01:59 -04:00
Campaign = require ' models/Campaign '
2015-09-03 14:04:40 -04:00
CocoCollection = require ' collections/CocoCollection '
Course = require ' models/Course '
CourseInstance = require ' models/CourseInstance '
2015-11-09 21:08:39 -05:00
Classroom = require ' models/Classroom '
2015-09-13 01:01:59 -04:00
LevelSession = require ' models/LevelSession '
RootView = require ' views/core/RootView '
template = require ' templates/courses/course-details '
User = require ' models/User '
2015-12-02 13:59:55 -05:00
storage = require ' core/storage '
2015-09-03 14:04:40 -04:00
module.exports = class CourseDetailsView extends RootView
id: ' course-details-view '
template: template
2015-12-02 12:52:52 -05:00
teacherMode: false
singlePlayerMode: false
memberSort: ' nameAsc '
2015-09-03 14:04:40 -04:00
2015-09-13 01:01:59 -04:00
events:
' click .btn-play-level ' : ' onClickPlayLevel '
2015-09-23 19:27:45 -04:00
' click .btn-select-instance ' : ' onClickSelectInstance '
2015-12-02 13:59:55 -05:00
' submit # school-form ' : ' onSubmitSchoolForm '
2015-09-13 01:01:59 -04:00
2015-09-24 20:12:18 -04:00
constructor: (options, @courseID, @courseInstanceID) ->
2015-09-03 14:04:40 -04:00
super options
2015-09-24 20:52:00 -04:00
@ courseID ? = options . courseID
@ courseInstanceID ? = options . courseInstanceID
2015-11-09 21:08:39 -05:00
@classroom = new Classroom ( )
2015-09-23 19:27:45 -04:00
@course = @ supermodel . getModel ( Course , @ courseID ) or new Course _id: @ courseID
@ listenTo @ course , ' sync ' , @ onCourseSync
if @ course . loaded
@ onCourseSync ( )
else
2016-02-18 11:44:40 -05:00
@ supermodel . loadModel @ course
2015-09-13 01:01:59 -04:00
getRenderData: ->
context = super ( )
context.campaign = @ campaign
context.course = @ course if @ course ? . loaded
context.courseInstance = @ courseInstance if @ courseInstance ? . loaded
2015-09-23 19:27:45 -04:00
context.courseInstances = @ courseInstances ? . models ? [ ]
2015-09-13 01:01:59 -04:00
context.levelConceptMap = @ levelConceptMap ? { }
2015-09-23 19:27:45 -04:00
context.noCourseInstance = @ noCourseInstance
context.noCourseInstanceSelected = @ noCourseInstanceSelected
2015-09-13 01:01:59 -04:00
context.userLevelStateMap = @ userLevelStateMap ? { }
2015-12-02 13:59:55 -05:00
context.promptForSchool = @ courseComplete and not me . isAnonymous ( ) and not me . get ( ' schoolName ' ) and not storage . load ( ' no-school ' )
2015-09-13 01:01:59 -04:00
context
2015-12-02 12:52:52 -05:00
afterRender: ->
super ( )
if @ supermodel . finished ( ) and @ courseComplete and me . isAnonymous ( ) and @ options . justBeatLevel
2016-02-25 18:24:16 -05:00
# TODO: Make an intermediate modal that tells them they've finished HoC and has some snazzy stuff for convincing players to sign up instead of just throwing up the bare CreateAccountModal
CreateAccountModal = require ' views/core/CreateAccountModal '
@ openModalView new CreateAccountModal showSignupRationale: true
2015-12-02 12:52:52 -05:00
2015-09-13 01:01:59 -04:00
onCourseSync: ->
2015-12-02 12:52:52 -05:00
return if @ destroyed
2015-09-13 01:01:59 -04:00
# console.log 'onCourseSync'
2015-11-11 12:07:16 -05:00
if me . isAnonymous ( ) and ( not me . get ( ' hourOfCode ' ) and not @ course . get ( ' hourOfCode ' ) )
2015-09-23 19:27:45 -04:00
@noCourseInstance = true
2015-12-02 12:52:52 -05:00
@ render ( )
2015-09-23 19:27:45 -04:00
return
2015-09-13 01:01:59 -04:00
return if @ campaign ?
2015-09-23 19:27:45 -04:00
campaignID = @ course . get ( ' campaignID ' )
@campaign = @ supermodel . getModel ( Campaign , campaignID ) or new Campaign _id: campaignID
2015-09-13 01:01:59 -04:00
@ listenTo @ campaign , ' sync ' , @ onCampaignSync
2015-09-23 19:27:45 -04:00
if @ campaign . loaded
@ onCampaignSync ( )
else
2016-02-18 11:44:40 -05:00
@ supermodel . loadModel @ campaign
2015-12-02 12:52:52 -05:00
@ render ( )
2015-09-13 01:01:59 -04:00
onCampaignSync: ->
2015-12-02 12:52:52 -05:00
return if @ destroyed
2015-09-13 01:01:59 -04:00
# console.log 'onCampaignSync'
2015-09-03 14:04:40 -04:00
if @ courseInstanceID
2015-09-13 01:01:59 -04:00
@ loadCourseInstance ( @ courseInstanceID )
2015-11-12 12:57:34 -05:00
else unless me . isAnonymous ( )
2015-12-02 12:52:52 -05:00
@ loadCourseInstances ( )
2015-09-13 01:01:59 -04:00
@levelConceptMap = { }
for levelID , level of @ campaign . get ( ' levels ' )
@ levelConceptMap [ levelID ] ? = { }
for concept in level . concepts
@ levelConceptMap [ levelID ] [ concept ] = true
2015-12-02 12:52:52 -05:00
if level . type is ' course-ladder '
@arenaLevel = level
@ render ( )
loadCourseInstances: ->
@courseInstances = new CocoCollection [ ] , { url: " /db/user/ #{ me . id } /course_instances " , model: CourseInstance , comparator: ' courseID ' }
@ listenToOnce @ courseInstances , ' sync ' , @ onCourseInstancesSync
@ supermodel . loadCollection @ courseInstances , ' course_instances '
loadAllCourses: ->
@allCourses = new CocoCollection [ ] , { url: " /db/course " , model: Course , comparator: ' _id ' }
@ listenToOnce @ allCourses , ' sync ' , @ onAllCoursesSync
@ supermodel . loadCollection @ allCourses , ' courses '
2015-09-03 14:04:40 -04:00
2015-09-13 01:01:59 -04:00
loadCourseInstance: (courseInstanceID) ->
2015-12-02 12:52:52 -05:00
return if @ destroyed
2015-09-13 01:01:59 -04:00
# console.log 'loadCourseInstance'
return if @ courseInstance ?
2015-09-24 20:12:18 -04:00
@courseInstanceID = courseInstanceID
@courseInstance = @ supermodel . getModel ( CourseInstance , @ courseInstanceID ) or new CourseInstance _id: @ courseInstanceID
2015-09-13 01:01:59 -04:00
@ listenTo @ courseInstance , ' sync ' , @ onCourseInstanceSync
2015-09-23 19:27:45 -04:00
if @ courseInstance . loaded
@ onCourseInstanceSync ( )
else
2016-02-18 11:44:40 -05:00
@courseInstance = @ supermodel . loadModel ( @ courseInstance ) . model
2015-09-03 14:04:40 -04:00
2015-09-13 01:01:59 -04:00
onCourseInstancesSync: ->
2015-12-02 12:52:52 -05:00
return if @ destroyed
2015-09-13 01:01:59 -04:00
# console.log 'onCourseInstancesSync'
2015-12-02 12:52:52 -05:00
@ findNextCourseInstance ( )
if not @ courseInstance
# We are loading these to find the one we want to display.
if @ courseInstances . models . length is 1
@ loadCourseInstance ( @ courseInstances . models [ 0 ] . id )
2015-09-23 19:27:45 -04:00
else
2015-12-02 12:52:52 -05:00
if @ courseInstances . models . length is 0
@noCourseInstance = true
else
@noCourseInstanceSelected = true
@ render ( )
2015-09-13 01:01:59 -04:00
onCourseInstanceSync: ->
2015-12-02 12:52:52 -05:00
return if @ destroyed
2015-09-23 19:27:45 -04:00
# console.log 'onCourseInstanceSync'
2015-11-09 21:08:39 -05:00
if @ courseInstance . get ( ' classroomID ' )
@classroom = new Classroom ( { _id: @ courseInstance . get ( ' classroomID ' ) } )
2016-02-18 11:44:40 -05:00
@ supermodel . loadModel @ classroom
2015-12-02 12:52:52 -05:00
@singlePlayerMode = @ courseInstance . get ( ' name ' ) is ' Single Player '
@teacherMode = @ courseInstance . get ( ' ownerID ' ) is me . id and not @ singlePlayerMode
@levelSessions = new CocoCollection ( [ ] , { url: " /db/course_instance/ #{ @ courseInstance . id } /level_sessions " , model: LevelSession , comparator: ' _id ' } )
2015-09-13 01:01:59 -04:00
@ listenToOnce @ levelSessions , ' sync ' , @ onLevelSessionsSync
@ supermodel . loadCollection @ levelSessions , ' level_sessions ' , cache: false
2015-10-27 20:47:48 -04:00
@owner = new User ( { _id: @ courseInstance . get ( ' ownerID ' ) } )
2016-02-18 11:44:40 -05:00
@ supermodel . loadModel @ owner
2015-12-02 12:52:52 -05:00
@ render ( )
2015-09-13 01:01:59 -04:00
onLevelSessionsSync: ->
2015-12-02 12:52:52 -05:00
return if @ destroyed
2015-09-13 01:01:59 -04:00
# console.log 'onLevelSessionsSync'
2015-09-24 10:28:43 -04:00
@memberStats = { }
2015-09-13 01:01:59 -04:00
@userConceptStateMap = { }
@userLevelStateMap = { }
for levelSession in @ levelSessions . models
2015-12-01 20:23:33 -05:00
continue if levelSession . skipMe # Don't track second arena session as another completed level
2015-09-13 01:01:59 -04:00
userID = levelSession . get ( ' creator ' )
levelID = levelSession . get ( ' level ' ) . original
state = if levelSession . get ( ' state ' ) ? . complete then ' complete ' else ' started '
2015-12-01 20:23:33 -05:00
playtime = parseInt ( levelSession . get ( ' playtime ' ) ? 0 , 10 )
do (userID, levelID) =>
secondSessionForLevel = _ . find ( @ levelSessions . models , ( (otherSession) ->
otherSession . get ( ' creator ' ) is userID and otherSession . get ( ' level ' ) . original is levelID and otherSession . id isnt levelSession . id
) )
if secondSessionForLevel
state = ' complete ' if secondSessionForLevel . get ( ' state ' ) ? . complete
playtime = playtime + parseInt ( secondSessionForLevel . get ( ' playtime ' ) ? 0 , 10 )
secondSessionForLevel.skipMe = true
2015-09-24 10:28:43 -04:00
@ memberStats [ userID ] ? = totalLevelsCompleted: 0 , totalPlayTime: 0
@ memberStats [ userID ] . totalLevelsCompleted ++ if state is ' complete '
2015-12-01 20:23:33 -05:00
@ memberStats [ userID ] . totalPlayTime += playtime
2015-09-24 10:28:43 -04:00
@ userConceptStateMap [ userID ] ? = { }
2015-09-13 01:01:59 -04:00
for concept of @ levelConceptMap [ levelID ]
@ userConceptStateMap [ userID ] [ concept ] = state
2015-09-24 10:28:43 -04:00
@ userLevelStateMap [ userID ] ? = { }
@ userLevelStateMap [ userID ] [ levelID ] = state
2015-09-13 01:01:59 -04:00
@conceptsCompleted = { }
for userID , conceptStateMap of @ userConceptStateMap
for concept , state of conceptStateMap
@ conceptsCompleted [ concept ] ? = 0
@ conceptsCompleted [ concept ] ++
2015-12-01 20:23:33 -05:00
2015-12-02 12:52:52 -05:00
if @ memberStats [ me . id ] ? . totalLevelsCompleted >= _ . size ( @ campaign . get ( ' levels ' ) ) - 1 # Don't need to complete arena
2015-12-01 20:23:33 -05:00
@courseComplete = true
2015-12-02 12:52:52 -05:00
@ loadCourseInstances ( ) unless @ courseInstances # Find the next course instance to do.
2015-12-01 20:23:33 -05:00
2015-12-02 12:52:52 -05:00
@ render ( )
2015-09-13 01:01:59 -04:00
2015-12-02 12:52:52 -05:00
onAllCoursesSync: ->
@ findNextCourseInstance ( )
findNextCourseInstance: ->
@nextCourseInstance = _ . find @ courseInstances . models , (ci) =>
# Sorted by courseID
ci . get ( ' classroomID ' ) is @ courseInstance . get ( ' classroomID ' ) and ci . id isnt @ courseInstance . id and ci . get ( ' courseID ' ) > @ course . id
if @ nextCourseInstance
nextCourseID = @ nextCourseInstance . get ( ' courseID ' )
@nextCourse = @ supermodel . getModel ( Course , nextCourseID ) or new Course _id: nextCourseID
2016-02-18 11:44:40 -05:00
@nextCourse = @ supermodel . loadModel ( @ nextCourse ) . model
2015-12-02 12:52:52 -05:00
else if @ allCourses ? . loaded
@nextCourse = _ . find @ allCourses . models , (course) => course . id > @ course . id
else
@ loadAllCourses ( )
2015-09-13 01:01:59 -04:00
onClickPlayLevel: (e) ->
2015-12-04 17:28:05 -05:00
levelSlug = $ ( e . target ) . closest ( ' .btn-play-level ' ) . data ( ' level-slug ' )
levelID = $ ( e . target ) . closest ( ' .btn-play-level ' ) . data ( ' level-id ' )
2015-11-18 17:02:45 -05:00
level = @ campaign . get ( ' levels ' ) [ levelID ]
if level . type is ' course-ladder '
2015-12-01 15:23:01 -05:00
viewClass = ' views/ladder/LadderView '
viewArgs = [ { supermodel: @ supermodel } , levelSlug ]
2015-11-19 16:20:21 -05:00
route = ' /play/ladder/ ' + levelSlug
2015-12-02 13:10:50 -05:00
unless @ singlePlayerMode # No league for solo courses
2015-12-01 15:23:01 -05:00
route += ' /course/ ' + @ courseInstance . id
viewArgs = viewArgs . concat [ ' course ' , @ courseInstance . id ]
2015-11-18 17:02:45 -05:00
else
2015-12-01 15:23:01 -05:00
route = @ getLevelURL levelSlug
viewClass = ' views/play/level/PlayLevelView '
viewArgs = [ { courseID: @ courseID , courseInstanceID: @ courseInstanceID , supermodel: @ supermodel } , levelSlug ]
Backbone . Mediator . publish ' router:navigate ' , route: route , viewClass: viewClass , viewArgs: viewArgs
2015-09-13 01:01:59 -04:00
2015-11-12 12:57:34 -05:00
getLevelURL: (levelSlug) ->
" /play/level/ #{ levelSlug } ?course= #{ @ courseID } &course-instance= #{ @ courseInstanceID } "
2015-09-23 19:27:45 -04:00
onClickSelectInstance: (e) ->
courseInstanceID = $ ( ' .select-instance ' ) . val ( )
@noCourseInstanceSelected = false
@ loadCourseInstance ( courseInstanceID )
2015-10-27 20:47:48 -04:00
getOwnerName: ->
2015-12-01 17:15:18 -05:00
return if @ owner . isNew ( )
2015-10-27 20:47:48 -04:00
if @ owner . get ( ' firstName ' ) and @ owner . get ( ' lastName ' )
return " #{ @ owner . get ( ' firstName ' ) } #{ @ owner . get ( ' lastName ' ) } "
2015-12-01 17:15:18 -05:00
@ owner . get ( ' name ' ) or @ owner . get ( ' email ' )
2015-12-02 13:59:55 -05:00
onSubmitSchoolForm: (e) ->
e . preventDefault ( )
schoolName = @ $el . find ( ' # course-complete-school-input ' ) . val ( ) . trim ( )
if schoolName and schoolName isnt me . get ( ' schoolName ' )
me . set ' schoolName ' , schoolName
me . patch ( )
else
storage . save ' no-school ' , true
@ $el . find ( ' # school-form ' ) . slideUp ( ' slow ' )
2015-12-11 15:59:53 -05:00
getLastLevelCompleted: ->
lastLevelCompleted = null
for levelID in _ . keys ( @ campaign . get ( ' levels ' ) )
if @ userLevelStateMap ? [ me . id ] ? [ levelID ] is ' complete '
lastLevelCompleted = levelID
return lastLevelCompleted