2014-11-28 20:49:41 -05:00
CocoView = require ' views/core/CocoView '
2014-11-25 13:19:38 -05:00
template = require ' templates/account/account-settings-view '
2014-11-28 20:49:41 -05:00
{ me } = require ' core/auth '
forms = require ' core/forms '
2014-06-30 22:16:26 -04:00
User = require ' models/User '
2015-02-19 16:14:34 -05:00
ConfirmModal = require ' views/editor/modal/ConfirmModal '
{ logoutUser , me } = require ( ' core/auth ' )
2014-01-03 13:32:13 -05:00
2014-11-25 15:43:17 -05:00
module.exports = class AccountSettingsView extends CocoView
2014-01-03 17:28:00 -05:00
id: ' account-settings-view '
2014-01-03 13:32:13 -05:00
template: template
2014-11-25 15:43:17 -05:00
className: ' countainer-fluid '
2014-01-03 13:32:13 -05:00
events:
2015-10-21 17:02:04 -04:00
' change .panel input ' : ' onChangePanelInput '
' change # name-input ' : ' onChangeNameInput '
' click # toggle-all-btn ' : ' onClickToggleAllButton '
' click # profile-photo-panel-body ' : ' onClickProfilePhotoPanelBody '
' click # delete-account-btn ' : ' onClickDeleteAccountButton '
2015-11-12 18:27:28 -05:00
' click # reset-progress-btn ' : ' onClickResetProgressButton '
2016-05-11 17:39:26 -04:00
' click .resend-verification-email ' : ' onClickResendVerificationEmail '
2015-02-27 19:07:41 -05:00
2014-01-03 13:32:13 -05:00
constructor: (options) ->
super options
2014-11-28 20:49:41 -05:00
require ( ' core/services/filepicker ' ) ( ) unless window . application . isIPadApp # Initialize if needed
2014-11-25 13:19:38 -05:00
@uploadFilePath = " db/user/ #{ me . id } "
2014-07-10 14:50:16 -04:00
2015-10-21 20:09:49 -04:00
getEmailSubsDict: ->
subs = { }
return subs unless me
subs [ sub ] = 1 for sub in me . getEnabledEmails ( )
return subs
2015-02-27 19:07:41 -05:00
2014-11-25 13:19:38 -05:00
#- Form input callbacks
2015-10-21 17:02:04 -04:00
onChangePanelInput: (e) ->
2015-11-19 18:37:37 -05:00
return if $ ( e . target ) . closest ( ' .form ' ) . attr ( ' id ' ) in [ ' reset-progress-form ' , ' delete-account-form ' ]
2014-11-25 13:19:38 -05:00
$ ( e . target ) . addClass ' changed '
2015-11-19 18:37:37 -05:00
@ trigger ' input-changed '
2014-07-10 14:50:16 -04:00
2015-10-21 17:02:04 -04:00
onClickToggleAllButton: ->
2014-11-25 13:19:38 -05:00
subs = @ getSubscriptions ( )
$ ( ' # email-panel input[type= " checkbox " ] ' , @ $el ) . prop ( ' checked ' , not _ . any ( _ . values ( subs ) ) ) . addClass ( ' changed ' )
2015-02-24 06:55:18 -05:00
@ trigger ' input-changed '
2014-07-10 14:50:16 -04:00
2015-10-21 17:02:04 -04:00
onChangeNameInput: ->
name = $ ( ' # name-input ' , @ $el ) . val ( )
2014-07-10 14:50:16 -04:00
return if name is me . get ' name '
User . getUnconflictedName name , (newName) =>
forms . clearFormAlerts ( @ $el )
if name is newName
@suggestedName = undefined
else
@suggestedName = newName
forms . setErrorToProperty @ $el , ' name ' , " That name is taken! How about #{ newName } ? " , true
2014-01-03 13:32:13 -05:00
2014-11-25 13:19:38 -05:00
onPictureChanged: (e) =>
@ trigger ' inputChanged ' , e
@ $el . find ( ' .gravatar-fallback ' ) . toggle not me . get ' photoURL '
2014-01-03 13:32:13 -05:00
2015-11-12 18:27:28 -05:00
onClickDeleteAccountButton: (e) ->
@ validateCredentialsForDestruction @ $el . find ( ' # delete-account-form ' ) , =>
renderData =
2016-03-14 18:35:33 -04:00
title: ' Are you really sure? '
body: ' This will completely delete your account. This action CANNOT be undone. Are you entirely sure? '
decline: ' Cancel '
confirm: ' DELETE Your Account '
2015-11-12 18:27:28 -05:00
confirmModal = new ConfirmModal renderData
confirmModal . on ' confirm ' , @ deleteAccount
@ openModalView confirmModal
2015-02-27 19:07:41 -05:00
2015-11-12 18:27:28 -05:00
onClickResetProgressButton: ->
@ validateCredentialsForDestruction @ $el . find ( ' # reset-progress-form ' ) , =>
renderData =
2016-03-14 18:35:33 -04:00
title: ' Are you really sure? '
body: ' This will completely erase your progress: code, levels, achievements, earned gems, and course work. This action CANNOT be undone. Are you entirely sure? '
decline: ' Cancel '
confirm: ' Erase ALL Progress '
2015-11-12 18:27:28 -05:00
confirmModal = new ConfirmModal renderData
confirmModal . on ' confirm ' , @ resetProgress
@ openModalView confirmModal
2016-05-11 17:39:26 -04:00
onClickResendVerificationEmail: (e) ->
$ . post me . getRequestVerificationEmailURL ( ) , ->
link = $ ( e . currentTarget )
link . find ( ' .resend-text ' ) . addClass ( ' hide ' )
link . find ( ' .sent-text ' ) . removeClass ( ' hide ' )
2015-11-12 18:27:28 -05:00
validateCredentialsForDestruction: ($form, onSuccess) ->
forms . clearFormAlerts ( $form )
2016-07-13 19:50:03 -04:00
enteredEmailOrUsername = $form . find ( ' input[name= " emailOrUsername " ] ' ) . val ( )
enteredPassword = $form . find ( ' input[name= " password " ] ' ) . val ( )
if enteredEmailOrUsername and enteredEmailOrUsername in [ me . get ( ' email ' ) , me . get ( ' name ' ) ]
2015-06-20 10:03:37 -04:00
isPasswordCorrect = false
toBeDelayed = true
$ . ajax
url: ' /auth/login '
type: ' POST '
data:
2016-07-13 19:50:03 -04:00
username: enteredEmailOrUsername
2015-11-12 18:27:28 -05:00
password: enteredPassword
2015-06-20 10:03:37 -04:00
parse: true
error: (error) ->
toBeDelayed = false
' Bad Error. Can \' t connect to server or something. ' + error
success: (response, textStatus, jqXHR) ->
toBeDelayed = false
2015-11-12 18:27:28 -05:00
return unless jqXHR . status is 200
2015-06-20 10:03:37 -04:00
isPasswordCorrect = true
2015-06-20 13:09:44 -04:00
callback = =>
2015-06-20 10:03:37 -04:00
if toBeDelayed
2015-06-20 13:09:44 -04:00
setTimeout callback , 100
2015-06-20 10:03:37 -04:00
else
if isPasswordCorrect
2015-11-12 18:27:28 -05:00
onSuccess ( )
2015-06-20 10:03:37 -04:00
else
message = $ . i18n . t ( ' account_settings.wrong_password ' , defaultValue: ' Wrong Password. ' )
2015-11-12 18:27:28 -05:00
err = [ message: message , property: ' password ' , formatted: true ]
forms . applyErrorsToForm ( $form , err )
2015-10-16 18:02:33 -04:00
$ ( ' .nano ' ) . nanoScroller ( { scrollTo: @ $el . find ( ' .has-error ' ) } )
2015-06-20 13:09:44 -04:00
setTimeout callback , 100
2015-02-19 16:14:34 -05:00
else
message = $ . i18n . t ( ' account_settings.wrong_email ' , defaultValue: ' Wrong Email. ' )
2015-11-12 18:27:28 -05:00
err = [ message: message , property: ' email ' , formatted: true ]
forms . applyErrorsToForm ( $form , err )
2015-02-19 16:14:34 -05:00
$ ( ' .nano ' ) . nanoScroller ( { scrollTo: @ $el . find ( ' .has-error ' ) } )
deleteAccount: ->
$ . ajax
type: ' DELETE '
success: ->
noty
timeout: 5000
text: ' Your account is gone. '
type: ' success '
layout: ' topCenter '
_ . delay ->
2015-10-16 18:02:33 -04:00
window ? . webkit ? . messageHandlers ? . notification ? . postMessage ( name: " signOut " ) if window . application . isIPadApp
2015-02-19 16:14:34 -05:00
Backbone . Mediator . publish ( " auth:logging-out " , { } )
2015-02-27 19:07:41 -05:00
window . tracker ? . trackEvent ' Log Out ' , category : ' Homepage ' if @ id is ' home-view '
2015-02-19 16:14:34 -05:00
logoutUser ( $ ( ' # login-email ' ) . val ( ) )
, 500
error: (jqXHR, status, error) ->
console . error jqXHR
2015-11-12 18:27:28 -05:00
noty
timeout: 5000
text: " Deleting account failed with error code #{ jqXHR . status } "
type: ' error '
layout: ' topCenter '
url: " /db/user/ #{ me . id } "
resetProgress: ->
$ . ajax
type: ' POST '
success: ->
noty
timeout: 5000
text: ' Your progress is gone. '
type: ' success '
layout: ' topCenter '
localStorage . clear ( )
me . fetch cache: false
_ . delay ( -> window . location . reload ( ) ) , 1000
error: (jqXHR, status, error) ->
console . error jqXHR
noty
timeout: 5000
text: " Resetting progress failed with error code #{ jqXHR . status } "
type: ' error '
layout: ' topCenter '
url: " /db/user/ #{ me . id } /reset_progress "
2015-02-19 16:14:34 -05:00
2015-10-21 17:02:04 -04:00
onClickProfilePhotoPanelBody: (e) ->
2014-11-25 13:19:38 -05:00
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 )
2015-02-05 11:56:51 -05:00
@ $el . find ( ' # photoURL ' ) . trigger ( ' change ' ) # cause for some reason editing the value doesn't trigger the jquery event
2014-11-25 13:19:38 -05:00
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
2015-02-27 19:07:41 -05:00
2014-11-25 13:19:38 -05:00
#- Misc
2014-01-03 13:32:13 -05:00
getSubscriptions: ->
2014-11-25 13:19:38 -05:00
inputs = ( $ ( i ) for i in $ ( ' # email-panel input[type= " checkbox " ].changed ' , @ $el ) )
2014-04-21 19:15:23 -04:00
emailNames = ( i . attr ( ' name ' ) . replace ( ' email_ ' , ' ' ) for i in inputs )
enableds = ( i . prop ( ' checked ' ) for i in inputs )
_ . zipObject emailNames , enableds
2014-01-03 13:32:13 -05:00
2015-02-27 19:07:41 -05:00
2014-11-25 13:19:38 -05:00
#- Saving changes
2015-02-27 19:07:41 -05:00
2014-11-25 15:43:17 -05:00
save: ->
2014-07-16 08:39:48 -04:00
$ ( ' # settings-tabs input ' ) . removeClass ' changed '
2014-01-03 13:32:13 -05:00
forms . clearFormAlerts ( @ $el )
@ grabData ( )
res = me . validate ( )
if res ?
2014-06-30 22:16:26 -04:00
console . error ' Couldn \' t save because of validation errors: ' , res
2014-01-03 13:32:13 -05:00
forms . applyErrorsToForm ( @ $el , res )
2014-11-25 13:19:38 -05:00
$ ( ' .nano ' ) . nanoScroller ( { scrollTo: @ $el . find ( ' .has-error ' ) } )
2014-01-03 13:32:13 -05:00
return
2014-03-10 16:20:00 -04:00
2014-02-27 15:02:08 -05:00
return unless me . hasLocalChanges ( )
2014-01-03 13:32:13 -05:00
2014-06-10 23:43:25 -04:00
res = me . patch ( )
2014-01-03 13:32:13 -05:00
return unless res
2014-11-25 15:43:17 -05:00
res . error =>
2016-07-13 19:50:03 -04:00
if res . responseJSON ? . property
errors = res . responseJSON
forms . applyErrorsToForm ( @ $el , errors )
$ ( ' .nano ' ) . nanoScroller ( { scrollTo: @ $el . find ( ' .has-error ' ) } )
else
noty
2016-07-25 19:33:40 -04:00
text: res . responseJSON ? . message or res . responseText
2016-07-13 19:50:03 -04:00
type: ' error '
layout: ' topCenter '
timeout: 5000
2014-11-25 15:43:17 -05:00
@ trigger ' save-user-error '
2014-07-10 14:50:16 -04:00
res . success (model, response, options) =>
2014-11-25 15:43:17 -05:00
@ trigger ' save-user-success '
@ trigger ' save-user-began '
2014-01-03 13:32:13 -05:00
grabData: ->
@ grabPasswordData ( )
@ grabOtherData ( )
grabPasswordData: ->
password1 = $ ( ' # password ' , @ $el ) . val ( )
password2 = $ ( ' # password2 ' , @ $el ) . val ( )
bothThere = Boolean ( password1 ) and Boolean ( password2 )
if bothThere and password1 isnt password2
message = $ . i18n . t ( ' account_settings.password_mismatch ' , defaultValue: ' Password does not match. ' )
2014-06-30 22:16:26 -04:00
err = [ message: message , property: ' password2 ' , formatted: true ]
2014-01-03 13:32:13 -05:00
forms . applyErrorsToForm ( @ $el , err )
2014-11-25 13:19:38 -05:00
$ ( ' .nano ' ) . nanoScroller ( { scrollTo: @ $el . find ( ' .has-error ' ) } )
2014-01-03 13:32:13 -05:00
return
if bothThere
me . set ( ' password ' , password1 )
2014-07-16 08:39:48 -04:00
else if password1
message = $ . i18n . t ( ' account_settings.password_repeat ' , defaultValue: ' Please repeat your password. ' )
err = [ message: message , property: ' password2 ' , formatted: true ]
forms . applyErrorsToForm ( @ $el , err )
2014-11-25 13:19:38 -05:00
$ ( ' .nano ' ) . nanoScroller ( { scrollTo: @ $el . find ( ' .has-error ' ) } )
2014-01-03 13:32:13 -05:00
grabOtherData: ->
2015-10-21 17:02:04 -04:00
@ $el . find ( ' # name-input ' ) . val @ suggestedName if @ suggestedName
me . set ' name ' , @ $el . find ( ' # name-input ' ) . val ( )
2014-11-25 13:19:38 -05:00
me . set ' email ' , @ $el . find ( ' # email ' ) . val ( )
2014-04-21 19:15:23 -04:00
for emailName , enabled of @ getSubscriptions ( )
2014-05-31 01:12:44 -04:00
me . setEmailSubscription emailName , enabled
2014-11-25 13:19:38 -05:00
me . set ( ' photoURL ' , @ $el . find ( ' # photoURL ' ) . val ( ) )
2014-01-03 17:28:00 -05:00
2015-02-12 14:42:05 -05:00
permissions = [ ]
2016-03-11 18:00:55 -05:00
unless application . isProduction ( )
adminCheckbox = @ $el . find ( ' # admin ' )
if adminCheckbox . length
permissions . push ' admin ' if adminCheckbox . prop ( ' checked ' )
godmodeCheckbox = @ $el . find ( ' # godmode ' )
if godmodeCheckbox . length
permissions . push ' godmode ' if godmodeCheckbox . prop ( ' checked ' )
me . set ( ' permissions ' , permissions )