mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-27 09:35:39 -05:00
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:
parent
6b49d4ccf6
commit
5b8d66cd1c
14 changed files with 157 additions and 75 deletions
|
@ -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"
|
||||
|
|
25
app/styles/core/hero-select-view.sass
Normal file
25
app/styles/core/hero-select-view.sass
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
8
app/styles/modal/create-account-modal/extras-view.sass
Normal file
8
app/styles/modal/create-account-modal/extras-view.sass
Normal file
|
@ -0,0 +1,8 @@
|
|||
#extras-view
|
||||
justify-content: center
|
||||
|
||||
#hero-select-view
|
||||
margin-top: 20px
|
||||
|
||||
.hero-option
|
||||
margin: 0 30px 50px
|
|
@ -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
|
||||
//-
|
||||
|
|
11
app/templates/core/create-account-modal/extras-view.jade
Normal file
11
app/templates/core/create-account-modal/extras-view.jade
Normal 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")
|
20
app/templates/core/hero-select-view.jade
Normal file
20
app/templates/core/hero-select-view.jade
Normal 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)
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
15
app/views/core/CreateAccountModal/ExtrasView.coffee
Normal file
15
app/views/core/CreateAccountModal/ExtrasView.coffee
Normal 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 }))
|
45
app/views/core/HeroSelectView.coffee
Normal file
45
app/views/core/HeroSelectView.coffee
Normal 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
|
|
@ -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()
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in a new issue