Reworked the MainAccountView, AccountSettingsView, and MainUserView to try and simplify things a bit.

This commit is contained in:
Scott Erickson 2014-11-25 10:19:38 -08:00
parent 6b8a9593ef
commit bb766e9610
19 changed files with 375 additions and 742 deletions

View file

@ -99,9 +99,7 @@ module.exports = class CocoRouter extends Backbone.Router
'test(/*subpath)': go('TestView')
'user/:slugOrID': go('user/MainUserView')
'user/:slugOrID/stats': go('user/AchievementsView')
'user/:slugOrID/profile': go('user/JobProfileView')
#'user/:slugOrID/code': go('user/CodeView')
'*name': 'showNotFoundView'

View file

@ -791,6 +791,7 @@
account:
recently_played: "Recently Played"
no_recent_games: "No games played during the past two weeks."
payments: "Payments"
loading_error:
could_not_load: "Error loading from server"

View file

@ -0,0 +1,46 @@
#account-settings-view
//- Fixed save button
#site-content-area
padding-bottom: 44px
#save-button-container
position: fixed
bottom: 0
left: 0
right: 0
z-index: 10
background: gray
padding: 5px
#save-button
width: 100%
&.btn-info, &.btn-danger
opacity: 1.0
//- Panels
.panel-heading
font-family: Open Sans Condensed
font-weight: bold
.panel-title
font-size: 20px
//- Panel specific stuff
.profile-photo
max-width: 100%
max-height: 200px
display: block
margin-bottom: 10px
#email-panel
#specific-notification-settings
padding-left: 20px
margin-left: 20px
border-left: 1px solid gray

View file

@ -1,32 +0,0 @@
@import "app/styles/bootstrap/variables"
@import "app/styles/mixins"
#account-home
dl
margin-bottom: 0px
img#picture
max-width: 100%
.panel
margin-bottom: 10px
h2
margin-bottom: 0px
a
font-size: 28px
margin-left: 5px
.panel-title > a
margin-left: 5px
color: rgb(11, 99, 188)
.panel-me
td
padding-left: 15px
.panel-emails
h4
font-family: $font-family-base

View file

@ -0,0 +1,9 @@
@import "app/styles/bootstrap/variables"
@import "app/styles/mixins"
#main-account-view
#account-links
width: 300px
#account-links .btn
width: 100%

View file

@ -1,91 +0,0 @@
#account-settings-view
.nav
margin-bottom: 10px
.tab-content
border: 1px solid #aaa
padding: 20px
background: #eee
border-radius: 5px
#save-button-container
position: fixed
top: 100px
width: 1000px
z-index: 10
#save-button
float: right
&.btn-info, &.btn-danger
opacity: 1.0
.gravatar-fallback
margin-top: 10px
input.range
position: relative
top: 4px
div.range-color
position: relative
top: 6px
height: 16px
width: 16px
display: inline-block
margin-left: 10px
.help-inline
position: relative
top: 3px
left: 10px
font-size: 12px
.form
max-width: 600px
#email-pane
#specific-notification-settings
padding-left: 20px
margin-left: 20px
border-left: 1px solid gray
#job-profile-view
.profile-preview-button
&.bottom-preview
margin: 15px 0 0 0
.sample-profile-thumbnail
margin-top: -60px
.profile-completion-progress
width: 100%
display: inline-block
height: 33px
.progress-bar
line-height: 33px
.progress-next-item
margin-top: -20px
margin-bottom: 15px
#job-profile-treema
background-color: white
input
width: 790px
.treema-description
font-size: 14px
line-height: 22px
opacity: 1
.treema-row
padding-top: 6px
.treema-image-file
img
display: block
clear: both
max-width: 300px

View file

@ -66,37 +66,6 @@ $user-achievements-scale: 0.8
overflow: hidden
text-overflow: ellipsis
// Specific to the user stats page
#user-achievements-view
.achievement-body
width: 335px
height: 120px
margin: 10px 0px
.achievement-icon
width: $overall-scale * $icon-size * $user-achievements-scale
height: $overall-scale * $icon-size * $user-achievements-scale
top: -5px
.achievement-image
img
width: $overall-scale * $user-achievements-scale * $icon-image-size
.achievement-content
margin-left: 60px
margin-right: 5px
width: 260px
height: 100px
padding: 15px 10px 20px 60px
.achievement-title
font-size: 20px
.achievement-description
font-size: 12px
line-height: 1.3em
max-height: 2.6em
.achievement-popup
padding: $overall-scale * 20px 0px
position: relative

View file

@ -46,6 +46,9 @@
color: #555555
font-size: 15px
margin-left: 5px
.panel-footer
text-align: right
.contributor-categories
list-style: none

View file

@ -0,0 +1,164 @@
extends /templates/base
block content
ol.breadcrumb
li
a(href="/")
span.glyphicon.glyphicon-home
li
a(href="/account")(data-i18n="nav.account")
li.active(data-i18n="account_settings.title")
if me.get('anonymous')
.alert.alert-danger(data-i18n="account_settings.not_logged_in") Log in or create an account to change your settings.
else
#save-button-container
button#save-button.btn-lg.btn.disabled(data-i18n="general.save" disabled="true") No Changes
.row
.col-md-6
.panel.panel-default
.panel-heading
.panel-title(data-i18n="account_settings.me_tab")
.panel-body
.form
- var name = me.get('name') || '';
- var email = me.get('email');
- var admin = me.get('permissions', true).indexOf('admin') != -1;
.form-group
label.control-label(for="name", data-i18n="general.name") Name
input#name.form-control(name="name", type="text", value="#{name}")
.form-group
label.control-label(for="email", data-i18n="general.email") Email
input#email.form-control(name="email", type="text", value="#{email}")
if !isProduction
.form-group.checkbox
label(for="admin", data-i18n="account_settings.admin") Admin
input#admin(name="admin", type="checkbox", checked=admin)
.panel.panel-default
.panel-heading
.panel-title(data-i18n="account_settings.picture_tab")
.panel-body
img.profile-photo(src=me.getPhotoURL(230), draggable="false")
input#photoURL(type="hidden", value=me.get('photoURL')||'')
button#upload-photo-button.btn.form-control.btn-primary(data-i18n="account_settings.upload_picture")
.panel.panel-default
.panel-heading
.panel-title(data-i18n="account_settings.password_tab")
.panel-body
.form
.form-group
label.control-label(for="password", data-i18n="account_settings.new_password") New Password
input#password.form-control(name="password", type="password")
.form-group
label.control-label(for="password2", data-i18n="account_settings.new_password_verify") Verify
input#password2.form-control(name="password2", type="password")
.col-md-6
#email-panel.panel.panel-default
.panel-heading
.panel-title(data-i18n="account_settings.emails_tab")
.panel-body
.form
.form-group.checkbox
label.control-label(for="email_generalNews", data-i18n="account_settings.email_announcements") Announcements
input#email_generalNews(name="email_generalNews", type="checkbox", checked=subs.generalNews)
span.help-block(data-i18n="account_settings.email_announcements_description") Get emails on the latest news and developments at CodeCombat.
hr
h4(data-i18n="account_settings.email_notifications") Notifications
span(data-i18n="account_settings.email_notifications_summary") Controls for personalized, automatic email notifications related to your CodeCombat activity.
.form
.form-group.checkbox
label.control-label(for="email_anyNotes", data-i18n="account_settings.email_any_notes") Any Notifications
input#email_anyNotes(name="email_anyNotes", type="checkbox", checked=subs.anyNotes)
span.help-block(data-i18n="account_settings.email_any_notes_description") Disable to stop all activity notification emails.
fieldset#specific-notification-settings
.form-group.checkbox
label.control-label(for="email_recruitNotes", data-i18n="account_settings.email_recruit_notes") Job Opportunities
input#email_recruitNotes(name="email_recruitNotes", type="checkbox", checked=subs.recruitNotes)
span.help-block(data-i18n="account_settings.email_recruit_notes_description") If you play really well, we may contact you about getting you a (better) job.
hr
h4(data-i18n="account_settings.contributor_emails") Contributor Class Emails
span(data-i18n="account_settings.contribute_prefix") We're looking for people to join our party! Check out the
a(href="/contribute", data-i18n="account_settings.contribute_page") contribute page
span(data-i18n="account_settings.contribute_suffix") to find out more.
.form
.form-group.checkbox
label.control-label(for="email_archmageNews")
span(data-i18n="classes.archmage_title")
| Archmage
|
span(data-i18n="classes.archmage_title_description")
| (Coder)
input#email_archmageNews(name="email_archmageNews", type="checkbox", checked=subs.archmageNews)
span(data-i18n="contribute.archmage_subscribe_desc").help-block Get emails about general news and announcements about CodeCombat.
.form-group.checkbox
label.control-label(for="email_artisanNews")
span(data-i18n="classes.artisan_title")
| Artisan
|
span(data-i18n="classes.artisan_title_description")
| (Level Builder)
input#email_artisanNews(name="email_artisanNews", type="checkbox", checked=subs.artisanNews)
span(data-i18n="contribute.artisan_subscribe_desc").help-block Get emails on level editor updates and announcements.
.form-group.checkbox
label.control-label(for="email_adventurerNews")
span(data-i18n="classes.adventurer_title")
| Adventurer
|
span(data-i18n="classes.adventurer_title_description")
| (Level Playtester)
input#email_adventurerNews(name="email_adventurerNews", type="checkbox", checked=subs.adventurerNews)
span(data-i18n="contribute.adventurer_subscribe_desc").help-block Get emails when there are new levels to test.
.form-group.checkbox
label.control-label(for="email_scribeNews")
span(data-i18n="classes.scribe_title")
| Scribe
|
span(data-i18n="classes.scribe_title_description")
| (Article Editor)
input#email_scribeNews(name="email_scribeNews", type="checkbox", checked=subs.scribeNews)
span(data-i18n="contribute.scribe_subscribe_desc").help-block Get emails about article writing announcements.
.form-group.checkbox
label.control-label(for="email_diplomatNews")
span(data-i18n="classes.diplomat_title")
| Diplomat
|
span(data-i18n="classes.diplomat_title_description")
| (Translator)
input#email_diplomatNews(name="email_diplomatNews", type="checkbox", checked=subs.diplomatNews)
span(data-i18n="contribute.diplomat_subscribe_desc").help-block Get emails about i18n developments and, eventually, levels to translate.
.form-group.checkbox
label.control-label(for="email_ambassadorNews")
span(data-i18n="classes.ambassador_title")
| Ambassador
|
span(data-i18n="classes.ambassador_title_description")
| (Support)
input#email_ambassadorNews(name="email_ambassadorNews", type="checkbox", checked=subs.ambassadorNews)
span(data-i18n="contribute.ambassador_subscribe_desc").help-block Get emails on support updates and multiplayer developments.
button#toggle-all-button.btn.btn-primary.form-control(data-i18n="account_settings.email_toggle") Toggle All
.clearfix
block footer

View file

@ -1,141 +0,0 @@
extends /templates/base
block content
if !me.isAnonymous()
.clearfix
.col-sm-6.clearfix
h2
span(data-i18n="account_settings.title") Account Settings
a.spl(href="/account/settings")
i.glyphicon.glyphicon-cog
hr
.row
.col-xs-6
.panel.panel-default
.panel-heading
h3.panel-title
i.glyphicon.glyphicon-picture
a(href="account/settings#picture" data-i18n="account_settings.picture_tab") Picture
.panel-body.text-center
img#picture(src="#{me.getPhotoURL(150)}" alt="Picture")
.col-xs-6
.panel.panel-default
.panel-heading
h3.panel-title
i.glyphicon.glyphicon-user
a(href="account/settings#wizard" data-i18n="account_settings.wizard_tab") Wizard
if (wizardSource)
.panel-body.text-center
img(src="#{wizardSource}")
.panel.panel-default.panel-me
.panel-heading
h3.panel-title
i.glyphicon.glyphicon-user
a(href="account/settings#me" data-i18n="account_settings.me_tab") Me
.panel-body
table
tr
th(data-i18n="general.name") Name
td=me.displayName()
tr
th(data-i18n="general.email") Email
td=me.get('email')
.panel.panel-default.panel-emails
.panel-heading
h3.panel-title
i.glyphicon.glyphicon-envelope
a(href="account/settings#emails" data-i18n="account_settings.emails_tab") Emails
.panel-body
if !hasEmailNotes && !hasEmailNews && !hasGeneralNews
p(data-i18n="account_settings.email_subscriptions_none") No email subscriptions.
if hasGeneralNews
h4(data-i18n="account_settings.email_news") News
ul
li(data-i18n="account_settings.email_announcements") Announcements
if hasEmailNotes
h4(data-i18n="account_settings.email_notifications") Notifications
ul
if subs.anyNotes
li(data-i18n="account_settings.email_any_notes") Any Notifications
if subs.recruitNotes
li(data-i18n="account_settings.email_recruit_notes") Job Opportunities
if hasEmailNews
h4(data-i18n="account_settings.contributor_emails") Contributor Emails
ul
if (subs.archmageNews)
li
span(data-i18n="classes.archmage_title")
| Archmage
span(data-i18n="classes.archmage_title_description")
| (Coder)
if (subs.artisanNews)
li
span.spr(data-i18n="classes.artisan_title")
| Artisan
span(data-i18n="classes.artisan_title_description")
| (Level Builder)
if (subs.adventurerNews)
li
span.spr(data-i18n="classes.adventurer_title")
| Adventurer
span(data-i18n="classes.adventurer_title_description")
| (Level Playtester)
if (subs.scribeNews)
li
span.spr(data-i18n="classes.scribe_title")
| Scribe
span(data-i18n="classes.scribe_title_description")
| (Article Editor)
if (subs.diplomatNews)
li
span.spr(data-i18n="classes.diplomat_title")
| Diplomat
span(data-i18n="classes.diplomat_title_description")
| (Translator)
if (subs.ambassadorNews)
li
span.spr(data-i18n="classes.ambassador_title")
| Ambassador
span(data-i18n="classes.ambassador_title_description")
| (Support)
.panel.panel-default
.panel-heading
h3.panel-title
i.glyphicon.glyphicon-wrench
a(href="account/settings#password" data-i18n="general.password") Password
//.panel.panel-default
// .panel-heading
// h3.panel-title
// i.glyphicon.glyphicon-briefcase
// a(href="account/settings#job-profile" data-i18n="account_settings.job_profile") Job Profile
.col-sm-6
h2(data-i18n="user.recently_played") Recently Played
hr
if !recentlyPlayed
div(data-i18n="common.loading") Loading...
else if recentlyPlayed.length
table.table
tr
th(data-i18n="resources.level") Level
th(data-i18n="user.last_played") Last Played
th(data-i18n="user.status") Status
each session in recentlyPlayed
if session.get('levelName')
tr
td
- var posturl = ''
- if (session.get('team')) posturl = '?team=' + session.get('team')
a(href="/play/level/#{session.get('levelID') + posturl}")= session.get('levelName') + (session.get('team') ? ' (' + session.get('team') + ')' : '')
td= moment(session.get('changed')).fromNow()
if session.get('state').complete === true
td(data-i18n="user.status_completed") Completed
else if ! session.isMultiplayer()
td(data-i18n="user.status_unfinished") Unfinished
else
td
else
.panel.panel-default
.panel-body
div(data-i18n="account.no_recent_games") No games played during the past two weeks.

View file

@ -0,0 +1,21 @@
extends /templates/base
block content
if me.get('anonymous')
p(data-i18n="account_settings.not_logged_in") Log in or create an account to change your settings.
else
ol.breadcrumb
li
a(href="/")
span.glyphicon.glyphicon-home
li.active(data-i18n="nav.account")
#account-links.panel.panel-default
.panel-heading(data-i18n="nav.account")
ul.list-group
li.list-group-item
a.btn.btn-lg.btn-primary(href="/account/settings", data-i18n="play.settings")
li.list-group-item
a.btn.btn-lg.btn-primary(href="/account/payments", data-i18n="account.payments")

View file

@ -1,165 +0,0 @@
extends /templates/base
block content
h2(data-i18n="account_settings.title") Account Settings
if me.get('anonymous')
p(data-i18n="account_settings.not_logged_in") Log in or create an account to change your settings.
else
#save-button-container
button.btn#save-button.disabled(data-i18n="general.save" disabled="true") No Changes
ul.nav.nav-pills#settings-tabs
li
a(href="#general-pane", data-toggle="tab", data-i18n="account_settings.me_tab") Me
li
a(href="#picture-pane", data-toggle="tab", data-i18n="account_settings.picture_tab") Picture
li
a(href="#wizard-pane", data-toggle="tab", data-i18n="account_settings.wizard_tab") Wizard
li
a(href="#password-pane", data-toggle="tab", data-i18n="account_settings.password_tab") Password
li
a(href="#email-pane", data-toggle="tab", data-i18n="account_settings.emails_tab") Emails
if showsJobProfileTab
li
a(href="#job-profile-pane", data-toggle="tab", data-i18n="account_settings.job_profile_tab") Job Profile
.tab-content#settings-panes
#general-pane.tab-pane
p
.form
- var name = me.get('name') || '';
- var email = me.get('email');
- var admin = me.get('permissions', true).indexOf('admin') != -1;
.form-group
label.control-label(for="name", data-i18n="general.name") Name
input#name.form-control(name="name", type="text", value="#{name}")
.form-group
label.control-label(for="email", data-i18n="general.email") Email
input#email.form-control(name="email", type="text", value="#{email}")
if !isProduction
.form-group.checkbox
label(for="admin", data-i18n="account_settings.admin") Admin
input#admin(name="admin", type="checkbox", checked=admin)
#picture-pane.tab-pane
h3(data-i18n="account_settings.upload_picture") Upload a picture
#picture-treema
.gravatar-fallback
img(src=me.getPhotoURL(256), alt="Gravatar", title="Gravatar fallback image")
#wizard-pane.tab-pane
#wizard-settings-view
#password-pane.tab-pane
p
.form
.form-group
label.control-label(for="password", data-i18n="account_settings.new_password") New Password
input#password.form-control(name="password", type="password")
.form-group
label.control-label(for="password2", data-i18n="account_settings.new_password_verify") Verify
input#password2.form-control(name="password2", type="password")
#email-pane.tab-pane
h3(data-i18n="account_settings.email_subscriptions") Email Subscriptions
p
.form
.form-group.checkbox
label.control-label(for="email_generalNews", data-i18n="account_settings.email_announcements") Announcements
input#email_generalNews(name="email_generalNews", type="checkbox", checked=subs.generalNews)
span.help-block(data-i18n="account_settings.email_announcements_description") Get emails on the latest news and developments at CodeCombat.
hr
h4(data-i18n="account_settings.email_notifications") Notifications
span(data-i18n="account_settings.email_notifications_summary") Controls for personalized, automatic email notifications related to your CodeCombat activity.
.form
.form-group.checkbox
label.control-label(for="email_anyNotes", data-i18n="account_settings.email_any_notes") Any Notifications
input#email_anyNotes(name="email_anyNotes", type="checkbox", checked=subs.anyNotes)
span.help-block(data-i18n="account_settings.email_any_notes_description") Disable to stop all activity notification emails.
fieldset#specific-notification-settings
.form-group.checkbox
label.control-label(for="email_recruitNotes", data-i18n="account_settings.email_recruit_notes") Job Opportunities
input#email_recruitNotes(name="email_recruitNotes", type="checkbox", checked=subs.recruitNotes)
span.help-block(data-i18n="account_settings.email_recruit_notes_description") If you play really well, we may contact you about getting you a (better) job.
hr
h4(data-i18n="account_settings.contributor_emails") Contributor Class Emails
span(data-i18n="account_settings.contribute_prefix") We're looking for people to join our party! Check out the
a(href="/contribute", data-i18n="account_settings.contribute_page") contribute page
span(data-i18n="account_settings.contribute_suffix") to find out more.
.form
.form-group.checkbox
label.control-label(for="email_archmageNews")
span(data-i18n="classes.archmage_title")
| Archmage
|
span(data-i18n="classes.archmage_title_description")
| (Coder)
input#email_archmageNews(name="email_archmageNews", type="checkbox", checked=subs.archmageNews)
span(data-i18n="contribute.archmage_subscribe_desc").help-block Get emails about general news and announcements about CodeCombat.
.form-group.checkbox
label.control-label(for="email_artisanNews")
span(data-i18n="classes.artisan_title")
| Artisan
|
span(data-i18n="classes.artisan_title_description")
| (Level Builder)
input#email_artisanNews(name="email_artisanNews", type="checkbox", checked=subs.artisanNews)
span(data-i18n="contribute.artisan_subscribe_desc").help-block Get emails on level editor updates and announcements.
.form-group.checkbox
label.control-label(for="email_adventurerNews")
span(data-i18n="classes.adventurer_title")
| Adventurer
|
span(data-i18n="classes.adventurer_title_description")
| (Level Playtester)
input#email_adventurerNews(name="email_adventurerNews", type="checkbox", checked=subs.adventurerNews)
span(data-i18n="contribute.adventurer_subscribe_desc").help-block Get emails when there are new levels to test.
.form-group.checkbox
label.control-label(for="email_scribeNews")
span(data-i18n="classes.scribe_title")
| Scribe
|
span(data-i18n="classes.scribe_title_description")
| (Article Editor)
input#email_scribeNews(name="email_scribeNews", type="checkbox", checked=subs.scribeNews)
span(data-i18n="contribute.scribe_subscribe_desc").help-block Get emails about article writing announcements.
.form-group.checkbox
label.control-label(for="email_diplomatNews")
span(data-i18n="classes.diplomat_title")
| Diplomat
|
span(data-i18n="classes.diplomat_title_description")
| (Translator)
input#email_diplomatNews(name="email_diplomatNews", type="checkbox", checked=subs.diplomatNews)
span(data-i18n="contribute.diplomat_subscribe_desc").help-block Get emails about i18n developments and, eventually, levels to translate.
.form-group.checkbox
label.control-label(for="email_ambassadorNews")
span(data-i18n="classes.ambassador_title")
| Ambassador
|
span(data-i18n="classes.ambassador_title_description")
| (Support)
input#email_ambassadorNews(name="email_ambassadorNews", type="checkbox", checked=subs.ambassadorNews)
span(data-i18n="contribute.ambassador_subscribe_desc").help-block Get emails on support updates and multiplayer developments.
button.btn#toggle-all-button(data-i18n="account_settings.email_toggle") Toggle All
#job-profile-pane.tab-pane
#job-profile-view

View file

@ -1,54 +0,0 @@
extends /templates/kinds/user
block append content
.btn-group.pull-right
button#grid-layout-button.btn.btn-default(data-layout='grid', class=activeLayout==='grid' ? 'active' : '')
i.glyphicon.glyphicon-th
button#table-layout-button.btn.btn-default(data-layout='table', class=activeLayout==='table' ? 'active' : '')
i.glyphicon.glyphicon-th-list
if achievementsByCategory
if activeLayout === 'grid'
.grid-layout
each achievements, category in achievementsByCategory
.row
h2.achievement-category-title(data-i18n="achievements.category_#{category}")=category
each achievement, index in achievements
- var title = achievement.i18nName();
- var description = achievement.i18nDescription();
- var locked = ! achievement.get('unlocked');
- var style = achievement.getStyle()
- var imgURL = achievement.getImageURL();
if locked
- var imgURL = achievement.getLockedImageURL();
else
- var imgURL = achievement.getImageURL();
.col-lg-4.col-xs-12
include ../achievements/achievement-popup
else if activeLayout === 'table'
.table-layout
if earnedAchievements.length
table.table
tr
th(data-i18n="general.name") Name
th(data-i18n="general.description") Description
th(data-i18n="general.date") Date
th(data-i18n="achievements.amount_achieved") Amount
th XP
each earnedAchievement in earnedAchievements.models
- var achievement = earnedAchievement.get('achievement');
if achievement.get('category')
// No level-specific achievements in here.
tr
td= achievement.i18nName()
td= achievement.i18nDescription()
td= moment().format("MMMM Do YYYY", earnedAchievement.get('changed'))
if achievement.isRepeatable()
td= earnedAchievement.get('achievedAmount')
else
td
td= earnedAchievement.get('earnedPoints')
else
.panel#no-achievements
.panel-body(data-i18n="user.no_achievements") No achievements earned yet.
else
div How did you even do that?

View file

@ -14,17 +14,8 @@ block append content
span(data-i18n="user.favorite_prefix") Favorite language is
strong.favorite-language= favoriteLanguage
span(data-i18n="user.favorite_postfix") .
.btn-group-vertical.profile-menu
a.btn.btn-default(href="/user/#{user.getSlugOrID()}/profile")
i.glyphicon.glyphicon-briefcase
span(data-i18n="account_settings.job_profile") Job Profile
a.btn.btn-default(href="/user/#{user.getSlugOrID()}/stats")
i.glyphicon.glyphicon-certificate
span(data-i18n="user.stats") Stats
a.btn.btn-default.disabled(href="#")
i.glyphicon.glyphicon-pencil
span(data-i18n="general.code") Code
- var emails = user.get('emails')
- var emails = user.getEnabledEmails()
// TODO: fix this, use some other method for finding contributor classes other than email settings, since they're private... Maybe achievements?
if emails
ul.contributor-categories
//li.contributor-category
@ -69,9 +60,11 @@ block append content
th.col-xs-4(data-i18n="resources.level") Level
th.col-xs-4(data-i18n="user.last_played") Last Played
th.col-xs-4(data-i18n="user.status") Status
each session in singlePlayerSessions
- var count = 0
each session, index in singlePlayerSessions
if session.get('levelName')
tr
tr(class=count > 4 ? 'hide' : '')
- count++;
td
a(href="/play/level/#{session.get('levelID')}")= session.get('levelName')
td= moment(session.get('changed')).fromNow()
@ -79,6 +72,9 @@ block append content
td(data-i18n="user.status_completed") Completed
else
td(data-i18n="user.status_unfinished") Unfinished
if count > 4
.panel-footer
button.btn.btn-info.btn-sm.more-button(data-i18n="editor.more")
else
.panel-body
p(data-i18n="user.no_singleplayer") No Singleplayer games played yet.
@ -94,17 +90,20 @@ block append content
th.col-xs-4(data-i18n="resources.level") Level
th.col-xs-4(data-i18n="user.last_played") Last Played
th.col-xs-4(data-i18n="general.score") Score
each session in multiPlayerSessions
tr
each session, index in multiPlayerSessions
tr(class=index > 4 ? 'hide' : '')
td
- var posturl = ''
- if (session.get('team')) posturl = '?team=' + session.get('team')
a(href="/play/level/#{session.get('levelID') + posturl}")= session.get('levelName') + (session.get('team') ? ' (' + session.get('team') + ')' : '')
td= moment(session.get('changed')).fromNow()
if session.get('totalScore')
td= session.get('totalScore') * 100
td= parseInt(session.get('totalScore') * 100)
else
td(data-i18n="user.status_unfinished") Unfinished
if multiPlayerSessions.length > 4
.panel-footer
button.btn.btn-info.btn-sm.more-button(data-i18n="editor.more")
else
.panel-body
p(data-i18n="user.no_multiplayer") No Multiplayer games played yet.
@ -123,11 +122,15 @@ block append content
th.col-xs-4(data-i18n="achievements.achievement") Achievement
th.col-xs-4(data-i18n="achievements.last_earned") Last Earned
th.col-xs-4(data-i18n="achievements.amount_achieved") Amount
each achievement in earnedAchievements.models
tr
each achievement, index in earnedAchievements.models
tr(class=index > 4 ? 'hide' : '')
td= achievement.get('achievementName')
td= moment().format("MMMM Do YYYY", achievement.get('changed'))
if achievement.get('achievedAmount')
td= achievement.get('achievedAmount')
else
td
if earnedAchievements.length > 4
.panel-footer
button.btn.btn-info.btn-sm.more-button(data-i18n="editor.more")

View file

@ -1,73 +1,54 @@
RootView = require 'views/kinds/RootView'
template = require 'templates/account/settings'
template = require 'templates/account/account-settings-view'
{me} = require 'lib/auth'
forms = require 'lib/forms'
User = require 'models/User'
AuthModal = require 'views/modal/AuthModal'
WizardSettingsView = require './WizardSettingsView'
JobProfileTreemaView = require './JobProfileTreemaView'
module.exports = class AccountSettingsView extends RootView
id: 'account-settings-view'
template: template
changedFields: [] # DOM input fields
events:
'click #save-button': 'save'
'change #settings-panes input:checkbox': (e) -> @trigger 'checkboxToggled', e
'keyup #settings-panes input:text, #settings-panes input:password': (e) -> @trigger 'inputChanged', e
'keyup #name': 'onNameChange'
'change .panel input': 'onInputChanged'
'change #name': 'checkNameExists'
'click #toggle-all-button': 'toggleEmailSubscriptions'
'keypress #settings-panes': 'onKeyPress'
'click .profile-photo': 'onEditProfilePhoto'
'click #save-button': 'save'
'click #upload-photo-button': 'onEditProfilePhoto'
shortcuts:
'enter': 'save'
constructor: (options) ->
@save = _.debounce(@save, 200)
@onNameChange = _.debounce @checkNameExists, 500
super options
return unless me
require('lib/services/filepicker')() unless window.application.isIPadApp # Initialize if needed
@uploadFilePath = "db/user/#{me.id}"
@listenTo(me, 'invalid', (errors) -> forms.applyErrorsToForm(@$el, me.validationError))
@on 'checkboxToggled', @onToggle
@on 'checkboxToggled', @onInputChanged
@on 'inputChanged', @onInputChanged
@on 'enterPressed', @onEnter
afterInsert: ->
super()
@openModalView new AuthModal() if me.get('anonymous')
getRenderData: ->
c = super()
return c unless me
c.subs = {}
c.subs[sub] = 1 for sub in me.getEnabledEmails()
c
#- Form input callbacks
onInputChanged: (e) ->
return @enableSaveButton() unless e?.currentTarget
that = e.currentTarget
$that = $(that)
savedValue = $that.data 'saved-value'
currentValue = $that.val()
if savedValue isnt currentValue
@changedFields.push that unless that in @changedFields
@enableSaveButton()
else
_.pull @changedFields, that
@disableSaveButton() if _.isEmpty @changedFields
$(e.target).addClass 'changed'
return @enableSaveButton()
onToggle: (e) ->
$that = $(e.currentTarget)
$that.val $that[0].checked
onEnter: ->
toggleEmailSubscriptions: =>
subs = @getSubscriptions()
$('#email-panel input[type="checkbox"]', @$el).prop('checked', not _.any(_.values(subs))).addClass('changed')
@save()
onKeyPress: (e) ->
@trigger 'enterPressed', e if e.which is 13
enableSaveButton: ->
$('#save-button', @$el).removeClass 'disabled'
$('#save-button', @$el).removeClass 'btn-danger'
$('#save-button', @$el).removeAttr 'disabled'
$('#save-button', @$el).text 'Save'
disableSaveButton: ->
$('#save-button', @$el).addClass 'disabled'
$('#save-button', @$el).removeClass 'btn-danger'
$('#save-button', @$el).attr 'disabled', "true"
$('#save-button', @$el).text 'No Changes'
checkNameExists: =>
name = $('#name', @$el).val()
return if name is me.get 'name'
@ -79,87 +60,69 @@ module.exports = class AccountSettingsView extends RootView
@suggestedName = newName
forms.setErrorToProperty @$el, 'name', "That name is taken! How about #{newName}?", true
afterRender: ->
super()
$('#settings-tabs a', @$el).click((e) =>
e.preventDefault()
$(e.target).tab('show')
# make sure errors show up in the general pane, but keep the password pane clean
$('#password-pane input').val('')
#@save() unless $(e.target).attr('href') is '#password-pane'
forms.clearFormAlerts($('#password-pane', @$el))
)
@chooseTab(location.hash.replace('#', ''))
wizardSettingsView = new WizardSettingsView()
@listenTo wizardSettingsView, 'change', @enableSaveButton
@insertSubView wizardSettingsView
@jobProfileTreemaView = new JobProfileTreemaView()
@listenTo @jobProfileTreemaView, 'change', @enableSaveButton
@insertSubView @jobProfileTreemaView
_.defer => @buildPictureTreema() # Not sure why, but the Treemas don't fully build without this if you reload the page.
afterInsert: ->
super()
$('#email-pane input[type="checkbox"]').on 'change', ->
$(@).addClass 'changed'
if me.get('anonymous')
@openModalView new AuthModal()
@updateSavedValues()
chooseTab: (category) ->
id = "##{category}-pane"
pane = $(id, @$el)
return @chooseTab('general') unless pane.length or category is 'general'
loc = "a[href=#{id}]"
$(loc, @$el).tab('show')
$('.tab-pane').removeClass('active')
pane.addClass('active')
@currentTab = category
getRenderData: ->
c = super()
return c unless me
c.subs = {}
c.subs[sub] = 1 for sub in c.me.getEnabledEmails()
c.showsJobProfileTab = me.isAdmin() or me.get('jobProfile') or location.hash.search('job-profile-') isnt -1
c
getSubscriptions: ->
inputs = ($(i) for i in $('#email-pane input[type="checkbox"].changed', @$el))
emailNames = (i.attr('name').replace('email_', '') for i in inputs)
enableds = (i.prop('checked') for i in inputs)
_.zipObject emailNames, enableds
toggleEmailSubscriptions: =>
subs = @getSubscriptions()
$('#email-pane input[type="checkbox"]', @$el).prop('checked', not _.any(_.values(subs))).addClass('changed')
@save()
buildPictureTreema: ->
data = photoURL: me.get('photoURL')
data.photoURL = null if data.photoURL?.search('gravatar') isnt -1 # Old style
schema = $.extend true, {}, me.schema()
schema.properties = _.pick me.schema().properties, 'photoURL'
schema.required = ['photoURL']
treemaOptions =
filePath: "db/user/#{me.id}"
schema: schema
data: data
callbacks: {change: @onPictureChanged}
@pictureTreema = @$el.find('#picture-treema').treema treemaOptions
@pictureTreema?.build()
@pictureTreema?.open()
@$el.find('.gravatar-fallback').toggle not me.get 'photoURL'
onPictureChanged: (e) =>
@trigger 'inputChanged', e
@$el.find('.gravatar-fallback').toggle not me.get 'photoURL'
#- Just copied from OptionsView, TODO refactor
onEditProfilePhoto: (e) ->
return if window.application.isIPadApp # TODO: have an iPad-native way of uploading a photo, since we don't want to load FilePicker on iPad (memory)
photoContainer = @$el.find('.profile-photo')
onSaving = =>
photoContainer.addClass('saving')
onSaved = (uploadingPath) =>
@$el.find('#photoURL').val(uploadingPath)
@onInputChanged() # cause for some reason editing the value doesn't trigger the jquery event
me.set('photoURL', uploadingPath)
photoContainer.removeClass('saving').attr('src', me.getPhotoURL(photoContainer.width()))
filepicker.pick {mimetypes: 'image/*'}, @onImageChosen(onSaving, onSaved)
formatImagePostData: (inkBlob) ->
url: inkBlob.url, filename: inkBlob.filename, mimetype: inkBlob.mimetype, path: @uploadFilePath, force: true
onImageChosen: (onSaving, onSaved) ->
(inkBlob) =>
onSaving()
uploadingPath = [@uploadFilePath, inkBlob.filename].join('/')
data = @formatImagePostData(inkBlob)
success = @onImageUploaded(onSaved, uploadingPath)
$.ajax '/file', type: 'POST', data: data, success: success
onImageUploaded: (onSaved, uploadingPath) ->
(e) =>
onSaved uploadingPath
#- Save button enable/disable
enableSaveButton: ->
$('#save-button', @$el)
.addClass 'btn-info'
.removeClass 'disabled btn-danger'
.removeAttr 'disabled'
.text 'Save'
disableSaveButton: ->
$('#save-button', @$el)
.addClass 'disabled'
.removeClass 'btn-danger btn-info'
.attr 'disabled', "true"
.text 'No Changes'
#- Misc
getSubscriptions: ->
inputs = ($(i) for i in $('#email-panel input[type="checkbox"].changed', @$el))
emailNames = (i.attr('name').replace('email_', '') for i in inputs)
enableds = (i.prop('checked') for i in inputs)
_.zipObject emailNames, enableds
#- Saving changes
save: (e) ->
$('#settings-tabs input').removeClass 'changed'
forms.clearFormAlerts(@$el)
@ -168,6 +131,7 @@ module.exports = class AccountSettingsView extends RootView
if res?
console.error 'Couldn\'t save because of validation errors:', res
forms.applyErrorsToForm(@$el, res)
$('.nano').nanoScroller({scrollTo: @$el.find('.has-error')})
return
return unless me.hasLocalChanges()
@ -180,11 +144,11 @@ module.exports = class AccountSettingsView extends RootView
res.error ->
errors = JSON.parse(res.responseText)
forms.applyErrorsToForm(@$el, errors)
$('.nano').nanoScroller({scrollTo: @$el.find('.has-error')})
save.text($.i18n.t('account_settings.error_saving', defaultValue: 'Error Saving')).removeClass('btn-success').addClass('btn-danger', 500)
res.success (model, response, options) =>
@changedFields = []
@updateSavedValues()
save.text($.i18n.t('account_settings.saved', defaultValue: 'Changes Saved')).removeClass('btn-success', 500).attr('disabled', 'true')
save.text($.i18n.t('account_settings.saved', defaultValue: 'Changes Saved')).removeClass('btn-success btn-info', 1000).attr('disabled', 'true')
grabData: ->
@grabPasswordData()
@ -198,6 +162,7 @@ module.exports = class AccountSettingsView extends RootView
message = $.i18n.t('account_settings.password_mismatch', defaultValue: 'Password does not match.')
err = [message: message, property: 'password2', formatted: true]
forms.applyErrorsToForm(@$el, err)
$('.nano').nanoScroller({scrollTo: @$el.find('.has-error')})
return
if bothThere
me.set('password', password1)
@ -205,36 +170,19 @@ module.exports = class AccountSettingsView extends RootView
message = $.i18n.t('account_settings.password_repeat', defaultValue: 'Please repeat your password.')
err = [message: message, property: 'password2', formatted: true]
forms.applyErrorsToForm(@$el, err)
$('.nano').nanoScroller({scrollTo: @$el.find('.has-error')})
grabOtherData: ->
$('#name', @$el).val @suggestedName if @suggestedName
me.set 'name', $('#name', @$el).val()
me.set 'email', $('#email', @$el).val()
@$el.find('#name').val @suggestedName if @suggestedName
me.set 'name', @$el.find('#name').val()
me.set 'email', @$el.find('#email').val()
for emailName, enabled of @getSubscriptions()
me.setEmailSubscription emailName, enabled
me.set 'photoURL', @pictureTreema.get('/photoURL')
me.set('photoURL', @$el.find('#photoURL').val())
adminCheckbox = @$el.find('#admin')
if adminCheckbox.length
permissions = []
permissions.push 'admin' if adminCheckbox.prop('checked')
me.set('permissions', permissions)
jobProfile = me.get('jobProfile') ? {}
updated = false
for key, val of @jobProfileTreemaView.getData()
updated = updated or not _.isEqual jobProfile[key], val
jobProfile[key] = val
if updated
jobProfile.updated = (new Date()).toISOString()
me.set 'jobProfile', jobProfile
updateSavedValues: ->
$('#settings-panes input:text').each ->
$(@).data 'saved-value', $(@).val()
$('#settings-panes input:checkbox').each ->
$(@).data 'saved-value', JSON.stringify $(@)[0].checked
destroy: ->
@pictureTreema?.destroy()
super()

View file

@ -1,5 +1,5 @@
View = require 'views/kinds/RootView'
template = require 'templates/account/account_home'
template = require 'templates/account/main-account-view'
{me} = require 'lib/auth'
User = require 'models/User'
AuthModalView = require 'views/modal/AuthModal'
@ -7,7 +7,7 @@ RecentlyPlayedCollection = require 'collections/RecentlyPlayedCollection'
ThangType = require 'models/ThangType'
module.exports = class MainAccountView extends View
id: 'account-home'
id: 'main-account-view'
template: template
constructor: (options) ->

View file

@ -25,6 +25,7 @@ module.exports = class AuthModal extends ModalView
'auth:logging-in-with-facebook': 'onLoggingInWithFacebook'
constructor: (options) ->
options ?= {}
@onNameChange = _.debounce @checkNameExists, 500
super options
@mode = options.mode if options.mode

View file

@ -1,55 +0,0 @@
UserView = require 'views/kinds/UserView'
template = require 'templates/user/achievements'
{me} = require 'lib/auth'
Achievement = require 'models/Achievement'
EarnedAchievement = require 'models/EarnedAchievement'
AchievementCollection = require 'collections/AchievementCollection'
EarnedAchievementCollection = require 'collections/EarnedAchievementCollection'
module.exports = class AchievementsView extends UserView
id: 'user-achievements-view'
template: template
viewName: 'Stats'
activeLayout: 'grid'
events:
'click #grid-layout-button': 'layoutChanged'
'click #table-layout-button': 'layoutChanged'
constructor: (userID, options) ->
super options, userID
onLoaded: ->
unless @achievements or @earnedAchievements
@supermodel.resetProgress()
@achievements = new AchievementCollection
@earnedAchievements = new EarnedAchievementCollection @user.getSlugOrID()
@supermodel.loadCollection @achievements, 'achievements'
@supermodel.loadCollection @earnedAchievements, 'earnedAchievements'
else
for earned in @earnedAchievements.models
return unless relatedAchievement = _.find @achievements.models, (achievement) ->
achievement.get('_id') is earned.get 'achievement'
relatedAchievement.set 'unlocked', true
earned.set 'achievement', relatedAchievement
deferredImages = (achievement.cacheLockedImage() for achievement in @achievements.models when not achievement.get 'unlocked')
whenever = $.when deferredImages...
whenever.done => @render()
super()
layoutChanged: (e) ->
@activeLayout = $(e.currentTarget).data 'layout'
@render()
getRenderData: ->
context = super()
context.activeLayout = @activeLayout
# After user is loaded
if @user and not @user.isAnonymous()
context.earnedAchievements = @earnedAchievements
context.achievementsByCategory = {}
for achievement in @achievements.models when achievement.get('category')
context.achievementsByCategory[achievement.get('category')] ?= []
context.achievementsByCategory[achievement.get('category')].push achievement
context

View file

@ -1,7 +1,7 @@
UserView = require 'views/kinds/UserView'
CocoCollection = require 'collections/CocoCollection'
LevelSession = require 'models/LevelSession'
template = require 'templates/user/user_home'
template = require 'templates/user/main-user-view'
{me} = require 'lib/auth'
EarnedAchievementCollection = require 'collections/EarnedAchievementCollection'
@ -15,6 +15,9 @@ class LevelSessionsCollection extends CocoCollection
module.exports = class MainUserView extends UserView
id: 'user-home'
template: template
events:
'click .more-button': 'onClickMoreButton'
constructor: (userID, options) ->
super options
@ -54,3 +57,8 @@ module.exports = class MainUserView extends UserView
@supermodel.loadCollection @levelSessions, 'levelSessions'
@supermodel.loadCollection @earnedAchievements, 'earnedAchievements'
super()
onClickMoreButton: (e) ->
panel = $(e.target).closest('.panel')
panel.find('tr.hide').removeClass('hide')
panel.find('.panel-footer').remove()