Add reset progress feature in account settings

This commit is contained in:
Nick Winter 2015-11-12 15:27:28 -08:00
parent 62cd00e328
commit 9e61928080
5 changed files with 103 additions and 38 deletions

View file

@ -725,6 +725,8 @@
wrong_password: "Wrong Password"
upload_picture: "Upload a picture"
delete_this_account: "Delete this account permanently"
reset_progress_tab: "Reset All Progress"
reset_your_progress: "Clear all your progress and start over"
god_mode: "God Mode"
password_tab: "Password"
emails_tab: "Emails"
@ -732,6 +734,7 @@
new_password: "New Password"
new_password_verify: "Verify"
type_in_email: "Type in your email to confirm account deletion."
type_in_email_progress: "Type in your email to confirm deleting your progress."
type_in_password: "Also, type in your password."
email_subscriptions: "Email Subscriptions"
email_subscriptions_none: "No Email Subscriptions."

View file

@ -52,13 +52,13 @@ else
.panel-heading
.panel-title#delete-account-panel-title(data-i18n="account_settings.delete_account_tab")
.panel-body
.form
.form#delete-account-form
.form-group
label.control-label(for="email1", data-i18n="account_settings.type_in_email")
input#email1.form-control(name="email1", type="text")
.form-group
input#email1.form-control(name="email", type="email")
.form-group
label.control-label(for="password1", data-i18n="account_settings.type_in_password")
input#password1.form-control(name="password1", type="password")
input#password1.form-control(name="password", type="password")
button#delete-account-btn.btn.form-control.btn-primary(data-i18n="account_settings.delete_this_account")
.col-md-6
@ -96,7 +96,7 @@ else
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
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.
@ -163,4 +163,19 @@ else
button#toggle-all-btn.btn.btn-primary.form-control(data-i18n="account_settings.email_toggle") Toggle All
.panel.panel-default
.panel-heading
.panel-title#reset-progress-panel-title(data-i18n="account_settings.reset_progress_tab")
.panel-body
.form#reset-progress-form
.form-group
label.control-label(for="email-reset-progress", data-i18n="account_settings.type_in_email_progress")
input#email-reset-progress.form-control(name="email", type="email")
.form-group
label.control-label(for="password-reset-progress", data-i18n="account_settings.type_in_password")
input#password-reset-progress.form-control(name="password", type="password")
button#reset-progress-btn.btn.form-control.btn-primary(data-i18n="account_settings.reset_your_progress")
.clearfix

View file

@ -18,6 +18,7 @@ module.exports = class AccountSettingsView extends CocoView
'click #toggle-all-btn': 'onClickToggleAllButton'
'click #profile-photo-panel-body': 'onClickProfilePhotoPanelBody'
'click #delete-account-btn': 'onClickDeleteAccountButton'
'click #reset-progress-btn': 'onClickResetProgressButton'
constructor: (options) ->
super options
@ -62,61 +63,68 @@ module.exports = class AccountSettingsView extends CocoView
@trigger 'inputChanged', e
@$el.find('.gravatar-fallback').toggle not me.get 'photoURL'
onClickDeleteAccountButton: (e) ->
@validateCredentialsForDestruction @$el.find('#delete-account-form'), =>
renderData =
confirmTitle: 'Are you really sure?'
confirmBody: 'This will completely delete your account. This action CANNOT be undone. Are you entirely sure?'
confirmDecline: 'Not really'
confirmConfirm: 'Definitely'
confirmModal = new ConfirmModal renderData
confirmModal.on 'confirm', @deleteAccount
@openModalView confirmModal
#- Just copied from OptionsView, TODO refactor
onClickDeleteAccountButton: ->
forms.clearFormAlerts(@$el)
myEmail = me.get 'email'
email1 = document.getElementById('email1').value
password1 = document.getElementById('password1').value
if Boolean(email1) and email1 is myEmail
onClickResetProgressButton: ->
@validateCredentialsForDestruction @$el.find('#reset-progress-form'), =>
renderData =
confirmTitle: 'Are you really sure?'
confirmBody: 'This will completely erase your progress: code, levels, achievements, earned gems, etc. This action CANNOT be undone. Are you entirely sure?'
confirmDecline: 'Not really'
confirmConfirm: 'Definitely'
confirmModal = new ConfirmModal renderData
confirmModal.on 'confirm', @resetProgress
@openModalView confirmModal
validateCredentialsForDestruction: ($form, onSuccess) ->
forms.clearFormAlerts($form)
enteredEmail = $form.find('input[type="email"]').val()
enteredPassword = $form.find('input[type="password"]').val()
if enteredEmail and enteredEmail is me.get('email')
isPasswordCorrect = false
toBeDelayed = true
$.ajax
url: '/auth/login'
type: 'POST'
data:
{
username: email1,
password: password1
}
username: enteredEmail
password: enteredPassword
parse: true
error: (error) ->
toBeDelayed = false
'Bad Error. Can\'t connect to server or something. ' + error
success: (response, textStatus, jqXHR) ->
toBeDelayed = false
unless jqXHR.status is 200
return
return unless jqXHR.status is 200
isPasswordCorrect = true
callback = =>
if toBeDelayed
setTimeout callback, 100
else
if isPasswordCorrect
renderData =
'confirmTitle': 'Are you really sure?'
'confirmBody': 'This will completely delete your account. This action CANNOT be undone. Are you entirely sure?'
'confirmDecline': 'Not really'
'confirmConfirm': 'Definitely'
confirmModal = new ConfirmModal renderData
confirmModal.on 'confirm', @deleteAccount
@openModalView confirmModal
onSuccess()
else
message = $.i18n.t('account_settings.wrong_password', defaultValue: 'Wrong Password.')
err = [message: message, property: 'password1', formatted: true]
forms.applyErrorsToForm(@$el, err)
err = [message: message, property: 'password', formatted: true]
forms.applyErrorsToForm($form, err)
$('.nano').nanoScroller({scrollTo: @$el.find('.has-error')})
setTimeout callback, 100
else
message = $.i18n.t('account_settings.wrong_email', defaultValue: 'Wrong Email.')
err = [message: message, property: 'email1', formatted: true]
forms.applyErrorsToForm(@$el, err)
err = [message: message, property: 'email', formatted: true]
forms.applyErrorsToForm($form, err)
$('.nano').nanoScroller({scrollTo: @$el.find('.has-error')})
deleteAccount: ->
myID = me.id
$.ajax
type: 'DELETE'
success: ->
@ -133,11 +141,33 @@ module.exports = class AccountSettingsView extends CocoView
, 500
error: (jqXHR, status, error) ->
console.error jqXHR
timeout: 5000
text: "Deleting account failed with error code #{jqXHR.status}"
type: 'error'
layout: 'topCenter'
url: "/db/user/#{myID}"
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"
onClickProfilePhotoPanelBody: (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)

View file

@ -87,6 +87,7 @@ module.exports = class PollModal extends ModalView
randomNumber = reward.random
randomGems = Math.ceil 2 * randomNumber * reward.level
totalGems = if @previousReward then me.gems() else Math.round me.gems() + randomGems
playSound = @playSound
if @previousReward
utils.replaceText @$randomNumber.show(), commentStart + randomNumber.toFixed(7)
@ -103,7 +104,7 @@ module.exports = class PollModal extends ModalView
if Math.random() < randomGems / 40
gemTrigger = 'gem-' + (gemNoisesPlayed % 4) # 4 gem sounds
++gemNoisesPlayed
@playSound gemTrigger, (0.475 + i / 2000)
playSound gemTrigger, (0.475 + i / 2000)
@$randomNumber.delay 25
@$randomGems.delay(1100).queue ->
utils.replaceText $(@), commentStart + randomGems

View file

@ -24,6 +24,8 @@ UserRemark = require './remarks/UserRemark'
hipchat = require '../hipchat'
sendwithus = require '../sendwithus'
Prepaid = require '../prepaids/Prepaid'
UserPollsRecord = require '../polls/UserPollsRecord'
EarnedAchievement = require '../achievements/EarnedAchievement'
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset', 'lastIP']
candidateProperties = [
@ -326,6 +328,7 @@ UserHandler = class UserHandler extends Handler
return @getSubSponsor(req, res) if args[1] is 'sub_sponsor'
return @getSubSponsors(req, res) if args[1] is 'sub_sponsors'
return @sendOneTimeEmail(req, res, args[0]) if args[1] is 'send_one_time_email'
return @resetProgress(req, res, args[0]) if args[1] is 'reset_progress'
return @sendNotFoundError(res)
super(arguments...)
@ -697,6 +700,19 @@ UserHandler = class UserHandler extends Handler
return @sendDatabaseError res, err if err
@sendSuccess res, users
resetProgress: (req, res, userID) ->
return @sendMethodNotAllowed res unless req.method is 'POST'
return @sendForbiddenError res unless userID and userID is req.user?._id + '' # Only you can reset your own progress
return @sendForbiddenError res if req.user?.isAdmin() # Protect admins from resetting their progress
async.parallel [
(cb) -> LevelSession.remove {creator: req.user._id + ''}, cb
(cb) -> EarnedAchievement.remove {user: req.user._id + ''}, cb
(cb) -> UserPollsRecord.remove {user: req.user._id + ''}, cb
(cb) -> req.user.update {points: 0, 'stats.gamesCompleted': 0, 'stats.concepts': {}, 'earned.gems': 0, 'earned.levels': [], 'earned.items': [], 'earned.heroes': [], 'purchased.items': [], 'purchased.heroes': [], spent: 0}, cb
], (err, results) =>
return @sendDatabaseError res, err if err
@sendSuccess res, result: 'success'
countEdits = (model, done) ->
statKey = User.statsMapping.edits[model.modelName]