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:"
start_playing: "Start Playing!"
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_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
.modal-dialog
width: auto
@ -15,26 +13,3 @@
h4
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
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
flex-direction: column
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
when 'sso-confirm'
#single-sign-on-confirm-view
when 'extras'
#extras-view
when 'confirmation'
#confirmation-view
//- This is not yet implemented
//- when 'extras'
//- #extras-view
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")
block modal-body-content
.hero-list
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()
#hero-select-view
block modal-footer-content
.select-hero-btn.btn.btn-lg.btn-forest

View file

@ -6,6 +6,7 @@ CoppaDenyView = require './CoppaDenyView'
BasicInfoView = require './BasicInfoView'
SingleSignOnAlreadyExistsView = require './SingleSignOnAlreadyExistsView'
SingleSignOnConfirmView = require './SingleSignOnConfirmView'
ExtrasView = require './ExtrasView'
ConfirmationView = require './ConfirmationView'
State = require 'models/State'
template = require 'templates/core/create-account-modal/create-account-modal'
@ -63,6 +64,7 @@ module.exports = class CreateAccountModal extends ModalView
classCode
birthday: new Date('') # so that birthday.getTime() is NaN
authModalInitialValues: {}
accountCreated: false
}
{ startOnPath } = options
@ -92,15 +94,26 @@ module.exports = class CreateAccountModal extends ModalView
'sso-connect:already-in-use': -> @signupState.set { screen: 'sso-already-exists' }
'sso-connect:new-user': -> @signupState.set {screen: 'sso-confirm'}
'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 })),
'nav-back': -> @signupState.set { screen: 'basic-info' }
@listenTo @insertSubView(new SingleSignOnConfirmView({ @signupState })),
'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 }))
# TODO: Switch to promises and state, rather than using defer to hackily enable buttons after render
@ -108,7 +121,7 @@ module.exports = class CreateAccountModal extends ModalView
application.gplusHandler.loadAPI({ success: => @signupState.set { gplusEnabled: true } unless @destroyed })
@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
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'
HeroSelectView = require 'views/core/HeroSelectView'
template = require 'templates/courses/hero-select-modal'
Classroom = require 'models/Classroom'
ThangTypes = require 'collections/ThangTypes'
@ -9,34 +10,15 @@ User = require 'models/User'
module.exports = class HeroSelectModal extends ModalView
id: 'hero-select-modal'
template: template
retainSubviews: true
events:
'click .select-hero-btn': 'onClickSelectHeroButton'
'click .hero-option': 'onClickHeroOption'
initialize: ({ currentHeroID }) ->
@debouncedRender = _.debounce @render, 0
@state = new State({
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
initialize: ->
@listenTo @insertSubView(new HeroSelectView({ showCurrentHero: true })),
'hero-select:success', (hero) ->
@trigger('hero-select:success', hero)
onClickSelectHeroButton: () ->
@hide()

View file

@ -13,8 +13,9 @@ describe 'HeroSelectModal', ->
beforeEach (done) ->
window.me = user = factories.makeUser({ heroConfig: { thangType: hero1.get('original') } })
modal = new HeroSelectModal({ currentHeroID: hero1.id })
modal.heroes.fakeRequests[0].respondWith({ status: 200, responseText: heroesResponse })
modal = new HeroSelectModal()
subview = modal.subviews.hero_select_view
subview.heroes.fakeRequests[0].respondWith({ status: 200, responseText: heroesResponse })
jasmine.demoModal(modal)
_.defer ->
modal.render()
@ -24,10 +25,10 @@ describe 'HeroSelectModal', ->
modal.stopListening()
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) ->
modal.$(".hero-option[data-hero-id='#{hero2.id}']").click()
modal.$(".hero-option[data-hero-original='#{hero2.get('original')}']").click()
_.defer ->
expect(user.fakeRequests.length).toBe(1)
request = user.fakeRequests[0]