Add hero selector to create account modal

Don't show grey border around Anya in signup modal

Refactor reload handling

Fix courses page updating to chosen hero
This commit is contained in:
phoenixeliot 2016-08-05 14:41:29 -07:00 committed by Phoenix Eliot
parent 6b49d4ccf6
commit 5b8d66cd1c
14 changed files with 157 additions and 75 deletions

View file

@ -318,6 +318,8 @@
write_this_down: "Write this down:" write_this_down: "Write this down:"
start_playing: "Start Playing!" start_playing: "Start Playing!"
sso_connected: "Successfully connected with:" sso_connected: "Successfully connected with:"
select_your_starting_hero: "Select Your Starting Hero:"
you_can_always_change_your_hero_later: "You can always change your hero later."
recover: recover:
recover_account_title: "Recover Account" recover_account_title: "Recover Account"

View file

@ -0,0 +1,25 @@
@import "app/styles/style-flat-variables"
#hero-select-view
.hero-list
display: flex
flex-wrap: wrap
justify-content: center
margin-bottom: -50px
.hero-option
display: flex
flex-direction: column
align-items: center
margin: 0 48px 50px
.hero-avatar
margin: 6px
background-color: #f8f8f8
box-shadow: 0 0 0 1px gray
.current .hero-avatar
box-shadow: 0 0 0 6px gray
.selected .hero-avatar
box-shadow: 0 0 0 6px $gold

View file

@ -1,5 +1,3 @@
@import "app/styles/style-flat-variables"
#hero-select-modal #hero-select-modal
.modal-dialog .modal-dialog
width: auto width: auto
@ -15,26 +13,3 @@
h4 h4
max-width: 500px max-width: 500px
.hero-list
display: flex
flex-wrap: wrap
justify-content: center
margin-bottom: -50px
.hero-option
display: flex
flex-direction: column
align-items: center
margin: 0 48px 50px
.hero-avatar
margin: 6px
background-color: #f8f8f8
box-shadow: 0 0 0 1px gray
.current .hero-avatar
box-shadow: 0 0 0 6px gray
.selected .hero-avatar
box-shadow: 0 0 0 6px $gold

View file

@ -69,7 +69,7 @@
a span a span
text-decoration: underline text-decoration: underline
#choose-account-type-view, #segment-check-view, #basic-info-view, #coppa-deny-view, #single-sign-on-already-exists-view, #single-sign-on-confirm-view, #confirmation-view #choose-account-type-view, #segment-check-view, #basic-info-view, #coppa-deny-view, #single-sign-on-already-exists-view, #single-sign-on-confirm-view, #extras-view, #confirmation-view
display: flex display: flex
flex-direction: column flex-direction: column
flex-grow: 1 flex-grow: 1

View file

@ -0,0 +1,8 @@
#extras-view
justify-content: center
#hero-select-view
margin-top: 20px
.hero-option
margin: 0 30px 50px

View file

@ -37,12 +37,10 @@ block modal-body
#single-sign-on-already-exists-view #single-sign-on-already-exists-view
when 'sso-confirm' when 'sso-confirm'
#single-sign-on-confirm-view #single-sign-on-confirm-view
when 'extras'
#extras-view
when 'confirmation' when 'confirmation'
#confirmation-view #confirmation-view
//- This is not yet implemented
//- when 'extras'
//- #extras-view
block modal-footer block modal-footer
//- //-

View file

@ -0,0 +1,11 @@
.modal-body
.modal-body-content
.text-center
h4(data-i18n="signup.select_your_starting_hero")
.small(data-i18n="signup.you_can_always_change_your_hero_later")
#hero-select-view
// In reverse order for tabbing purposes
.history-nav-buttons
button.next-button.btn.btn-lg.btn-navy(type='button')
span(data-i18n="about.next")

View file

@ -0,0 +1,20 @@
mixin heroOption(hero)
- var heroOriginal = hero.get('original')
- var selectedState
if state.get('selectedHeroOriginal') === heroOriginal
- selectedState = 'selected'
else if view.options.showCurrentHero && state.get('currentHeroOriginal') === heroOriginal
- selectedState = 'current'
else
- selectedState = ''
.hero-option(data-hero-original=heroOriginal, class=selectedState)
.hero-avatar
img(src=hero.getPortraitURL())
.text-h5.hero-name
span= hero.getHeroShortName()
.hero-list
if view.heroes.loaded
each hero in view.heroes.models
if hero.get('heroClass') === 'Warrior'
+heroOption(hero)

View file

@ -6,20 +6,7 @@ block modal-header-content
h4(data-i18n="courses.select_your_hero_description") h4(data-i18n="courses.select_your_hero_description")
block modal-body-content block modal-body-content
.hero-list #hero-select-view
if view.heroes.loaded
each hero in view.heroes.models
if hero.get('heroClass') === 'Warrior'
+heroOption(hero)
mixin heroOption(hero)
- var heroID = hero.id
- var selectedState = (state.get('selectedHeroID') === heroID ? 'selected' : (state.get('currentHeroID') === heroID ? 'current' : ''))
.hero-option(data-hero-id=heroID class=selectedState)
.hero-avatar
img(src=hero.getPortraitURL())
.text-h5.hero-name
span= hero.getHeroShortName()
block modal-footer-content block modal-footer-content
.select-hero-btn.btn.btn-lg.btn-forest .select-hero-btn.btn.btn-lg.btn-forest

View file

@ -6,6 +6,7 @@ CoppaDenyView = require './CoppaDenyView'
BasicInfoView = require './BasicInfoView' BasicInfoView = require './BasicInfoView'
SingleSignOnAlreadyExistsView = require './SingleSignOnAlreadyExistsView' SingleSignOnAlreadyExistsView = require './SingleSignOnAlreadyExistsView'
SingleSignOnConfirmView = require './SingleSignOnConfirmView' SingleSignOnConfirmView = require './SingleSignOnConfirmView'
ExtrasView = require './ExtrasView'
ConfirmationView = require './ConfirmationView' ConfirmationView = require './ConfirmationView'
State = require 'models/State' State = require 'models/State'
template = require 'templates/core/create-account-modal/create-account-modal' template = require 'templates/core/create-account-modal/create-account-modal'
@ -63,6 +64,7 @@ module.exports = class CreateAccountModal extends ModalView
classCode classCode
birthday: new Date('') # so that birthday.getTime() is NaN birthday: new Date('') # so that birthday.getTime() is NaN
authModalInitialValues: {} authModalInitialValues: {}
accountCreated: false
} }
{ startOnPath } = options { startOnPath } = options
@ -92,14 +94,25 @@ module.exports = class CreateAccountModal extends ModalView
'sso-connect:already-in-use': -> @signupState.set { screen: 'sso-already-exists' } 'sso-connect:already-in-use': -> @signupState.set { screen: 'sso-already-exists' }
'sso-connect:new-user': -> @signupState.set {screen: 'sso-confirm'} 'sso-connect:new-user': -> @signupState.set {screen: 'sso-confirm'}
'nav-back': -> @signupState.set { screen: 'segment-check' } 'nav-back': -> @signupState.set { screen: 'segment-check' }
'signup': -> @signupState.set { screen: 'confirmation' } 'signup': ->
if @signupState.get('path') is 'student'
@signupState.set { screen: 'extras', accountCreated: true }
else
@signupState.set { screen: 'confirmation', accountCreated: true }
@listenTo @insertSubView(new SingleSignOnAlreadyExistsView({ @signupState })), @listenTo @insertSubView(new SingleSignOnAlreadyExistsView({ @signupState })),
'nav-back': -> @signupState.set { screen: 'basic-info' } 'nav-back': -> @signupState.set { screen: 'basic-info' }
@listenTo @insertSubView(new SingleSignOnConfirmView({ @signupState })), @listenTo @insertSubView(new SingleSignOnConfirmView({ @signupState })),
'nav-back': -> @signupState.set { screen: 'basic-info' } 'nav-back': -> @signupState.set { screen: 'basic-info' }
'signup': -> @signupState.set { screen: 'confirmation' } 'signup': ->
if @signupState.get('path') is 'student'
@signupState.set { screen: 'extras', accountCreated: true }
else
@signupState.set { screen: 'confirmation', accountCreated: true }
@listenTo @insertSubView(new ExtrasView({ @signupState })),
'nav-forward': -> @signupState.set { screen: 'confirmation' }
@insertSubView(new ConfirmationView({ @signupState })) @insertSubView(new ConfirmationView({ @signupState }))
@ -108,7 +121,7 @@ module.exports = class CreateAccountModal extends ModalView
application.gplusHandler.loadAPI({ success: => @signupState.set { gplusEnabled: true } unless @destroyed }) application.gplusHandler.loadAPI({ success: => @signupState.set { gplusEnabled: true } unless @destroyed })
@once 'hidden', -> @once 'hidden', ->
if @signupState.get('screen') is 'confirmation' and not application.testing if @signupState.get('accountCreated') and not application.testing
# ensure logged in state propagates through the entire app # ensure logged in state propagates through the entire app
document.location.reload() document.location.reload()

View file

@ -0,0 +1,15 @@
CocoView = require 'views/core/CocoView'
HeroSelectView = require 'views/core/HeroSelectView'
template = require 'templates/core/create-account-modal/extras-view'
State = require 'models/State'
module.exports = class ExtrasView extends CocoView
id: 'extras-view'
template: template
retainSubviews: true
events:
'click .next-button': -> @trigger 'nav-forward'
initialize: ({ @signupState } = {}) ->
@insertSubView(new HeroSelectView({ showCurrentHero: false }))

View file

@ -0,0 +1,45 @@
CocoView = require 'views/core/CocoView'
template = require 'templates/core/hero-select-view'
Classroom = require 'models/Classroom'
ThangTypes = require 'collections/ThangTypes'
State = require 'models/State'
ThangType = require 'models/ThangType'
User = require 'models/User'
module.exports = class HeroSelectView extends CocoView
id: 'hero-select-view'
template: template
events:
'click .hero-option': 'onClickHeroOption'
initialize: (@options = {}) ->
defaultHeroOriginal = ThangType.heroes.captain
currentHeroOriginal = me.get('heroConfig')?.thangType or defaultHeroOriginal
@debouncedRender = _.debounce @render, 0
@state = new State({
currentHeroOriginal
selectedHeroOriginal: currentHeroOriginal
})
@heroes = new ThangTypes({}, { project: ['original', 'name', 'heroClass'] })
@supermodel.trackRequest @heroes.fetchHeroes()
@listenTo @state, 'all', -> @debouncedRender()
@listenTo @heroes, 'all', -> @debouncedRender()
onClickHeroOption: (e) ->
heroOriginal = $(e.currentTarget).data('hero-original')
@state.set selectedHeroOriginal: heroOriginal
@saveHeroSelection(heroOriginal)
saveHeroSelection: (heroOriginal) ->
me.set(heroConfig: {}) unless me.get('heroConfig')
heroConfig = _.assign me.get('heroConfig'), { thangType: heroOriginal }
me.set({ heroConfig })
hero = @heroes.findWhere({ original: heroOriginal })
me.save().then =>
@trigger 'hero-select:success', hero

View file

@ -1,4 +1,5 @@
ModalView = require 'views/core/ModalView' ModalView = require 'views/core/ModalView'
HeroSelectView = require 'views/core/HeroSelectView'
template = require 'templates/courses/hero-select-modal' template = require 'templates/courses/hero-select-modal'
Classroom = require 'models/Classroom' Classroom = require 'models/Classroom'
ThangTypes = require 'collections/ThangTypes' ThangTypes = require 'collections/ThangTypes'
@ -9,34 +10,15 @@ User = require 'models/User'
module.exports = class HeroSelectModal extends ModalView module.exports = class HeroSelectModal extends ModalView
id: 'hero-select-modal' id: 'hero-select-modal'
template: template template: template
retainSubviews: true
events: events:
'click .select-hero-btn': 'onClickSelectHeroButton' 'click .select-hero-btn': 'onClickSelectHeroButton'
'click .hero-option': 'onClickHeroOption'
initialize: ({ currentHeroID }) -> initialize: ->
@debouncedRender = _.debounce @render, 0 @listenTo @insertSubView(new HeroSelectView({ showCurrentHero: true })),
'hero-select:success', (hero) ->
@state = new State({ @trigger('hero-select:success', hero)
currentHeroID
selectedHeroID: currentHeroID
})
@heroes = new ThangTypes({}, { project: ['original', 'name', 'heroClass'] })
@supermodel.trackRequest @heroes.fetchHeroes()
@listenTo @state, 'all', -> @debouncedRender()
@listenTo @heroes, 'all', -> @debouncedRender()
onClickHeroOption: (e) ->
heroID = $(e.currentTarget).data('hero-id')
@state.set selectedHeroID: heroID
hero = @heroes.get(heroID)
me.set(heroConfig: {}) unless me.get('heroConfig')
heroConfig = _.assign me.get('heroConfig'), { thangType: hero.get('original') }
me.set({ heroConfig })
me.save().then =>
@trigger 'hero-select:success', hero
onClickSelectHeroButton: () -> onClickSelectHeroButton: () ->
@hide() @hide()

View file

@ -13,8 +13,9 @@ describe 'HeroSelectModal', ->
beforeEach (done) -> beforeEach (done) ->
window.me = user = factories.makeUser({ heroConfig: { thangType: hero1.get('original') } }) window.me = user = factories.makeUser({ heroConfig: { thangType: hero1.get('original') } })
modal = new HeroSelectModal({ currentHeroID: hero1.id }) modal = new HeroSelectModal()
modal.heroes.fakeRequests[0].respondWith({ status: 200, responseText: heroesResponse }) subview = modal.subviews.hero_select_view
subview.heroes.fakeRequests[0].respondWith({ status: 200, responseText: heroesResponse })
jasmine.demoModal(modal) jasmine.demoModal(modal)
_.defer -> _.defer ->
modal.render() modal.render()
@ -24,10 +25,10 @@ describe 'HeroSelectModal', ->
modal.stopListening() modal.stopListening()
it 'highlights the current hero', -> it 'highlights the current hero', ->
expect(modal.$(".hero-option[data-hero-id='#{hero1.id}']")[0].className.split(" ")).toContain('selected') expect(modal.$(".hero-option[data-hero-original='#{hero1.get('original')}']")[0].className.split(" ")).toContain('selected')
it 'saves when you change heroes', (done) -> it 'saves when you change heroes', (done) ->
modal.$(".hero-option[data-hero-id='#{hero2.id}']").click() modal.$(".hero-option[data-hero-original='#{hero2.get('original')}']").click()
_.defer -> _.defer ->
expect(user.fakeRequests.length).toBe(1) expect(user.fakeRequests.length).toBe(1)
request = user.fakeRequests[0] request = user.fakeRequests[0]