<commit_before>kd = require 'kd'
KDButtonView = kd.ButtonView
KDCustomHTMLView = kd.CustomHTMLView
KDFormViewWithFields = kd.FormViewWithFields
KDHeaderView = kd.HeaderView
KDInputView = kd.InputView
KDNotificationView = kd.NotificationView
remote = require('app/remote').getInstance()
s3upload = require 'app/util/s3upload'
whoami = require 'app/util/whoami'
isKoding = require 'app/util/isKoding'
showError = require 'app/util/showError'
JView = require 'app/jview'
AvatarStaticView = require 'app/commonviews/avatarviews/avatarstaticview'
Encoder = require 'htmlencode'
VerifyPINModal = require 'app/commonviews/verifypinmodal'
VerifyPasswordModal = require 'app/commonviews/verifypasswordmodal'
sinkrow = require 'sinkrow'
KodingSwitch = require 'app/commonviews/kodingswitch'


module.exports = class AccountEditUsername extends JView

  notify = (msg, duration = 2000) ->
    new KDNotificationView
      title    : msg
      duration : duration


  constructor: (options = {}, data) ->

    super options, data

    @initViews()


  initViews: ->

    @account = whoami()

    @avatar = new AvatarStaticView @getAvatarOptions(), @account

    @uploadAvatarBtn  = new KDButtonView
      style           : 'solid small green'
      cssClass        : 'upload-avatar'
      title           : 'Upload Image'
      loader          : yes

    @uploadAvatarInput = new KDInputView
      type            : 'file'
      cssClass        : 'AppModal--account-avatarArea-buttons-upload'
      change          : @bound 'uploadInputChange'
      attributes      :
        accept        : 'image/*'

    @useGravatarBtn  = new KDButtonView
      style          : 'solid small gray'
      cssClass       : 'use-gravatar'
      title          : 'Use Gravatar'
      loader         : yes
      callback       : =>
        @account.modify 'profile.avatar' : '', (err) =>
          console.warn err  if err
          @useGravatarBtn.hideLoader()

    @userProfileFormFields =
      firstName          :
        cssClass         : 'Formline--half'
        placeholder      : "firstname"
        name             : "firstName"
        label            : 'Name'
      lastName           :
        cssClass         : 'Formline--half'
        placeholder      : "lastname"
        name             : "lastName"
        label            : 'Last name'
      email              :
        cssClass         : 'Formline--half'
        placeholder      : "<EMAIL>"
        name             : "email"
        testPath         : "account-email-input"
        label            : 'Email'
      username           :
        cssClass         : 'Formline--half'
        placeholder      : "username"
        name             : "username"
        label            : 'Username'
        attributes       :
          readonly       : "#{not /^guest-/.test @account.profile.nickname}"
        testPath         : "account-username-input"
      verifyEmail        :
        itemClass        : KDCustomHTMLView
        tagName          : "a"
        partial          : "You didn't verify your email yet <span>Verify now</span>"
        cssClass         : "hidden action-link verify-email"
        testPath         : "account-email-edit"
        click            : @bound 'verifyUserEmail'
      passwordHeader     :
        itemClass        : KDCustomHTMLView
        partial          : 'CHANGE PASSWORD'
        cssClass         : 'AppModal-sectionHeader'
      password           :
        cssClass         : 'Formline--half'
        placeholder      : "password"
        name             : "password"
        type             : "password"
        label            : 'Password'
      confirm            :
        cssClass         : 'Formline--half'
        placeholder      : "confirm password"
        name             : "confirmPassword"
        type             : "password"
        label            : 'Password (again)'

    if isKoding()

      @userProfileFormFields.locationHeader =
        itemClass        : KDCustomHTMLView
        partial          : 'LOCATION SERVICES'
        cssClass         : 'AppModal-sectionHeader'

      @userProfileFormFields.shareLocationLabel =
        itemClass        : KDCustomHTMLView
        partial          : 'Share my location when I post on a #channel'
        tagName          : 'ul'
        cssClass         : 'AppModal--account-switchList left-aligned'
        name             : 'shareLocationLabel'
        nextElement      :
          shareLocation      :
            defaultValue     : whoami().shareLocation
            itemClass        : KodingSwitch
            #cssClass         : 'AppModal--account-switchList'
            name             : 'shareLocation'

    @userProfileForm = new KDFormViewWithFields
      cssClass             : 'AppModal-form'
      fields               : @userProfileFormFields
      buttons              :
        Save               :
          title            : 'Save Changes'
          type             : 'submit'
          style            : 'solid green small'
          loader           : yes
      callback             : @bound 'update'


  uploadInputChange: ->

    @uploadAvatarBtn.showLoader()

    file = @uploadAvatarInput.getElement().files[0]

    return @uploadAvatarBtn.hideLoader()  unless file

    mimeType      = file.type
    reader        = new FileReader
    reader.onload = (event) =>
      dataURL     = event.target.result
      [_, base64] = dataURL.split ","

      @uploadAvatar
        mimeType : mimeType
        content  : file
      , =>
        @uploadAvatarBtn.hideLoader()

    reader.readAsDataURL file


  uploadAvatar: (avatar, callback) ->

    {mimeType, content} = avatar

    s3upload
      name    : "avatar-#{Date.now()}"
      content : content
      mimeType: mimeType
      timeout : 30000
    , (err, url) =>

      @uploadAvatarBtn.hideLoader()

      if err is "EntityTooLarge"
        return showError "The image you tried to upload is too big."
      else
        return showError err

      @account.modify "profile.avatar": "#{url}"


  update: (formData) ->

    {JUser} = remote.api
    {email, password, confirmPassword, firstName, lastName, username, shareLocation} = formData
    profileUpdated = yes
    skipPasswordConfirmation = no
    queue = [
      =>
        # update firstname and lastname
        me = whoami()

        me.modify {
          "profile.firstName": firstName,
          "profile.lastName" : lastName
          "shareLocation"    : formData.shareLocation
        }, (err)->
          return notify err.message  if err
          me.shareLocation = formData.shareLocation
          queue.next()
      =>
        # secondly change user email address
        JUser.changeEmail {email}, (err, result)=>
          # here we are simply discarding the email is same error
          # but do not forget to warn users about other errors
          if err
            return queue.next() if err.message is "EmailIsSameError"
            @hideSaveButtonLoader()
            return notify err.message

          options = {skipPasswordConfirmation, email}
          @confirmCurrentPassword options, (err) =>
            if err
              notify err
              profileUpdated = false
              return queue.next()
            skipPasswordConfirmation = true
            modal = new VerifyPINModal 'Update E-Mail', (pin)->
              remote.api.JUser.changeEmail {email, pin}, (err)->
                if err
                  notify err.message
                  profileUpdated = false
                queue.next()
            modal.once 'ModalCancelled', @bound 'hideSaveButtonLoader'
      =>
        # on third turn update password
        # check for password confirmation
        if password isnt confirmPassword
          notify "Passwords did not match"
          @hideSaveButtonLoader()
          return
        #check passworg lenght
        if password isnt "" and password.length < 8
          notify "Passwords should be at least 8 characters"
          @hideSaveButtonLoader()
          return
        # if password is empty than discard operation
        if password is ""
          {token} = kd.utils.parseQuery()
          if token
            profileUpdated = no
            notify "You should set your password"
          return queue.next()

        JUser.fetchUser (err, user)=>
          if err
            notify "An error occurred"
            return queue.next()

          skipPasswordConfirmation = true  if user.passwordStatus isnt "valid"
          @confirmCurrentPassword {skipPasswordConfirmation}, (err) =>
            if err
              notify err
              profileUpdated = false
              return queue.next()
            JUser.changePassword password, (err,docs)=>
              @userProfileForm.inputs.password.setValue ""
              @userProfileForm.inputs.confirm.setValue ""
              if err
                return queue.next()  if err.message is "PasswordIsSame"
                return notify err.message
              return queue.next()
      =>
        # if everything is OK or didnt change, show profile updated modal
        notify "Your account information is updated." if profileUpdated
        @hideSaveButtonLoader()
    ]
    sinkrow.daisy queue

  verifyUserEmail: ->

    {email} = @userInfo
    {nickname, firstName, lastName} = @account.profile

    notify = (message)->
      new KDNotificationView
        title    : message
        duration : 3500

    remote.api.JUser.verifyByPin resendIfExists: yes, (err) =>

      @userProfileForm.fields.verifyEmail.hide()
      @userProfileForm.inputs.verifyEmail.hide()
      return showError err if err

      notify "We've sent you a confirmation mail.", 3500

  confirmCurrentPassword: (opts, callback) ->

    {skipPasswordConfirmation, email} = opts

    return callback null  if skipPasswordConfirmation

    modal = new VerifyPasswordModal 'Confirm', (currentPassword) ->
      options = {password: <PASSWORD>, email}
      remote.api.JUser.verifyPassword options, (err, confirmed) ->

        return callback err.message  if err
        return callback 'Current password cannot be confirmed'  unless confirmed

        callback null

    @hideSaveButtonLoader()


  viewAppended: ->

    {JPasswordRecovery, JUser} = remote.api
    {token} = kd.utils.parseQuery()
    if token
      JPasswordRecovery.validate token, (err, isValid)=>
        if err and err.short isnt 'redeemed_token'
          notify err.message
        else if isValid
          notify "Thanks for confirming your email address"

    kd.singletons.mainController.ready =>
      whoami().fetchEmailAndStatus (err, userInfo) =>

        return kd.warn err  if err

        @userInfo = userInfo

        super

        @putDefaults()


  putDefaults: ->

    {email} = @userInfo
    {nickname, firstName, lastName} = @account.profile

    @userProfileForm.inputs.email.setDefaultValue Encoder.htmlDecode email
    @userProfileForm.inputs.username.setDefaultValue Encoder.htmlDecode nickname
    @userProfileForm.inputs.firstName.setDefaultValue Encoder.htmlDecode firstName
    @userProfileForm.inputs.lastName.setDefaultValue Encoder.htmlDecode lastName

    if isKoding()
      @userProfileForm.inputs.shareLocation.setDefaultValue whoami().shareLocation

    {focus} = kd.utils.parseQuery()
    @userProfileForm.inputs[focus]?.setFocus()  if focus

    notify = (message)->
      new KDNotificationView
        title    : message
        duration : 3500

    if @userInfo.status is "unconfirmed"
      @userProfileForm.fields.verifyEmail.show()
      @userProfileForm.inputs.verifyEmail.show()

    @userProfileForm.inputs.firstName.setFocus()


  getAvatarOptions: ->
    tagName       : 'figure'
    cssClass      : 'AppModal--account-avatar'
    size          :
      width       : 95
      height      : 95


  hideSaveButtonLoader: -> @userProfileForm.buttons.Save.hideLoader()


  pistachio: ->
    """
    <div class='AppModal--account-avatarArea clearfix'>
      {{> @avatar}}
      <div class="AppModal--account-avatarArea-buttons">
        {{> @uploadAvatarBtn}}
        {{> @uploadAvatarInput}}
        {{> @useGravatarBtn}}
      </div>
    </div>
    {{> @userProfileForm}}
    """
<commit_msg>Added specific error if avatar file is too big<commit_after>kd = require 'kd'
KDButtonView = kd.ButtonView
KDCustomHTMLView = kd.CustomHTMLView
KDFormViewWithFields = kd.FormViewWithFields
KDHeaderView = kd.HeaderView
KDInputView = kd.InputView
KDNotificationView = kd.NotificationView
remote = require('app/remote').getInstance()
s3upload = require 'app/util/s3upload'
whoami = require 'app/util/whoami'
isKoding = require 'app/util/isKoding'
showError = require 'app/util/showError'
JView = require 'app/jview'
AvatarStaticView = require 'app/commonviews/avatarviews/avatarstaticview'
Encoder = require 'htmlencode'
VerifyPINModal = require 'app/commonviews/verifypinmodal'
VerifyPasswordModal = require 'app/commonviews/verifypasswordmodal'
sinkrow = require 'sinkrow'
KodingSwitch = require 'app/commonviews/kodingswitch'


module.exports = class AccountEditUsername extends JView

  notify = (msg, duration = 2000) ->
    new KDNotificationView
      title    : msg
      duration : duration


  constructor: (options = {}, data) ->

    super options, data

    @initViews()


  initViews: ->

    @account = whoami()

    @avatar = new AvatarStaticView @getAvatarOptions(), @account

    @uploadAvatarBtn  = new KDButtonView
      style           : 'solid small green'
      cssClass        : 'upload-avatar'
      title           : 'Upload Image'
      loader          : yes

    @uploadAvatarInput = new KDInputView
      type            : 'file'
      cssClass        : 'AppModal--account-avatarArea-buttons-upload'
      change          : @bound 'uploadInputChange'
      attributes      :
        accept        : 'image/*'

    @useGravatarBtn  = new KDButtonView
      style          : 'solid small gray'
      cssClass       : 'use-gravatar'
      title          : 'Use Gravatar'
      loader         : yes
      callback       : =>
        @account.modify 'profile.avatar' : '', (err) =>
          console.warn err  if err
          @useGravatarBtn.hideLoader()

    @userProfileFormFields =
      firstName          :
        cssClass         : 'Formline--half'
        placeholder      : "firstname"
        name             : "firstName"
        label            : 'Name'
      lastName           :
        cssClass         : 'Formline--half'
        placeholder      : "lastname"
        name             : "lastName"
        label            : 'Last name'
      email              :
        cssClass         : 'Formline--half'
        placeholder      : "<EMAIL>"
        name             : "email"
        testPath         : "account-email-input"
        label            : 'Email'
      username           :
        cssClass         : 'Formline--half'
        placeholder      : "username"
        name             : "username"
        label            : 'Username'
        attributes       :
          readonly       : "#{not /^guest-/.test @account.profile.nickname}"
        testPath         : "account-username-input"
      verifyEmail        :
        itemClass        : KDCustomHTMLView
        tagName          : "a"
        partial          : "You didn't verify your email yet <span>Verify now</span>"
        cssClass         : "hidden action-link verify-email"
        testPath         : "account-email-edit"
        click            : @bound 'verifyUserEmail'
      passwordHeader     :
        itemClass        : KDCustomHTMLView
        partial          : 'CHANGE PASSWORD'
        cssClass         : 'AppModal-sectionHeader'
      password           :
        cssClass         : 'Formline--half'
        placeholder      : "password"
        name             : "password"
        type             : "password"
        label            : 'Password'
      confirm            :
        cssClass         : 'Formline--half'
        placeholder      : "<PASSWORD> password"
        name             : "<PASSWORD>Password"
        type             : "password"
        label            : 'Password (again)'

    if isKoding()

      @userProfileFormFields.locationHeader =
        itemClass        : KDCustomHTMLView
        partial          : 'LOCATION SERVICES'
        cssClass         : 'AppModal-sectionHeader'

      @userProfileFormFields.shareLocationLabel =
        itemClass        : KDCustomHTMLView
        partial          : 'Share my location when I post on a #channel'
        tagName          : 'ul'
        cssClass         : 'AppModal--account-switchList left-aligned'
        name             : 'shareLocationLabel'
        nextElement      :
          shareLocation      :
            defaultValue     : whoami().shareLocation
            itemClass        : KodingSwitch
            #cssClass         : 'AppModal--account-switchList'
            name             : 'shareLocation'

    @userProfileForm = new KDFormViewWithFields
      cssClass             : 'AppModal-form'
      fields               : @userProfileFormFields
      buttons              :
        Save               :
          title            : 'Save Changes'
          type             : 'submit'
          style            : 'solid green small'
          loader           : yes
      callback             : @bound 'update'


  uploadInputChange: ->

    @uploadAvatarBtn.showLoader()

    file = @uploadAvatarInput.getElement().files[0]

    return @uploadAvatarBtn.hideLoader()  unless file

    mimeType      = file.type
    reader        = new FileReader
    reader.onload = (event) =>
      dataURL     = event.target.result
      [_, base64] = dataURL.split ","

      @uploadAvatar
        mimeType : mimeType
        content  : file
      , =>
        @uploadAvatarBtn.hideLoader()

    reader.readAsDataURL file


  uploadAvatar: (avatar, callback) ->

    {mimeType, content} = avatar

    s3upload
      name    : "avatar-#{Date.now()}"
      content : content
      mimeType: mimeType
      timeout : 30000
    , (err, url) =>

      @uploadAvatarBtn.hideLoader()

      if err.message is 'EntityTooLarge'
        return notify "The image you tried to upload is too big."
      else
        return notify err

      @account.modify "profile.avatar": "#{url}"


  update: (formData) ->

    {JUser} = remote.api
    {email, password, confirmPassword, firstName, lastName, username, shareLocation} = formData
    profileUpdated = yes
    skipPasswordConfirmation = no
    queue = [
      =>
        # update firstname and lastname
        me = whoami()

        me.modify {
          "profile.firstName": firstName,
          "profile.lastName" : lastName
          "shareLocation"    : formData.shareLocation
        }, (err)->
          return notify err.message  if err
          me.shareLocation = formData.shareLocation
          queue.next()
      =>
        # secondly change user email address
        JUser.changeEmail {email}, (err, result)=>
          # here we are simply discarding the email is same error
          # but do not forget to warn users about other errors
          if err
            return queue.next() if err.message is "EmailIsSameError"
            @hideSaveButtonLoader()
            return notify err.message

          options = {skipPasswordConfirmation, email}
          @confirmCurrentPassword options, (err) =>
            if err
              notify err
              profileUpdated = false
              return queue.next()
            skipPasswordConfirmation = true
            modal = new VerifyPINModal 'Update E-Mail', (pin)->
              remote.api.JUser.changeEmail {email, pin}, (err)->
                if err
                  notify err.message
                  profileUpdated = false
                queue.next()
            modal.once 'ModalCancelled', @bound 'hideSaveButtonLoader'
      =>
        # on third turn update password
        # check for password confirmation
        if password isnt confirmPassword
          notify "Passwords did not match"
          @hideSaveButtonLoader()
          return
        #check passworg lenght
        if password isnt "" and password.length < 8
          notify "Passwords should be at least 8 characters"
          @hideSaveButtonLoader()
          return
        # if password is empty than discard operation
        if password is ""
          {token} = kd.utils.parseQuery()
          if token
            profileUpdated = no
            notify "You should set your password"
          return queue.next()

        JUser.fetchUser (err, user)=>
          if err
            notify "An error occurred"
            return queue.next()

          skipPasswordConfirmation = true  if user.passwordStatus isnt "valid"
          @confirmCurrentPassword {skipPasswordConfirmation}, (err) =>
            if err
              notify err
              profileUpdated = false
              return queue.next()
            JUser.changePassword password, (err,docs)=>
              @userProfileForm.inputs.password.setValue ""
              @userProfileForm.inputs.confirm.setValue ""
              if err
                return queue.next()  if err.message is "PasswordIsSame"
                return notify err.message
              return queue.next()
      =>
        # if everything is OK or didnt change, show profile updated modal
        notify "Your account information is updated." if profileUpdated
        @hideSaveButtonLoader()
    ]
    sinkrow.daisy queue

  verifyUserEmail: ->

    {email} = @userInfo
    {nickname, firstName, lastName} = @account.profile

    notify = (message)->
      new KDNotificationView
        title    : message
        duration : 3500

    remote.api.JUser.verifyByPin resendIfExists: yes, (err) =>

      @userProfileForm.fields.verifyEmail.hide()
      @userProfileForm.inputs.verifyEmail.hide()
      return showError err if err

      notify "We've sent you a confirmation mail.", 3500

  confirmCurrentPassword: (opts, callback) ->

    {skipPasswordConfirmation, email} = opts

    return callback null  if skipPasswordConfirmation

    modal = new VerifyPasswordModal 'Confirm', (currentPassword) ->
      options = {password: <PASSWORD>, email}
      remote.api.JUser.verifyPassword options, (err, confirmed) ->

        return callback err.message  if err
        return callback 'Current password cannot be confirmed'  unless confirmed

        callback null

    @hideSaveButtonLoader()


  viewAppended: ->

    {JPasswordRecovery, JUser} = remote.api
    {token} = kd.utils.parseQuery()
    if token
      JPasswordRecovery.validate token, (err, isValid)=>
        if err and err.short isnt 'redeemed_token'
          notify err.message
        else if isValid
          notify "Thanks for confirming your email address"

    kd.singletons.mainController.ready =>
      whoami().fetchEmailAndStatus (err, userInfo) =>

        return kd.warn err  if err

        @userInfo = userInfo

        super

        @putDefaults()


  putDefaults: ->

    {email} = @userInfo
    {nickname, firstName, lastName} = @account.profile

    @userProfileForm.inputs.email.setDefaultValue Encoder.htmlDecode email
    @userProfileForm.inputs.username.setDefaultValue Encoder.htmlDecode nickname
    @userProfileForm.inputs.firstName.setDefaultValue Encoder.htmlDecode firstName
    @userProfileForm.inputs.lastName.setDefaultValue Encoder.htmlDecode lastName

    if isKoding()
      @userProfileForm.inputs.shareLocation.setDefaultValue whoami().shareLocation

    {focus} = kd.utils.parseQuery()
    @userProfileForm.inputs[focus]?.setFocus()  if focus

    notify = (message)->
      new KDNotificationView
        title    : message
        duration : 3500

    if @userInfo.status is "unconfirmed"
      @userProfileForm.fields.verifyEmail.show()
      @userProfileForm.inputs.verifyEmail.show()

    @userProfileForm.inputs.firstName.setFocus()


  getAvatarOptions: ->
    tagName       : 'figure'
    cssClass      : 'AppModal--account-avatar'
    size          :
      width       : 95
      height      : 95


  hideSaveButtonLoader: -> @userProfileForm.buttons.Save.hideLoader()


  pistachio: ->
    """
    <div class='AppModal--account-avatarArea clearfix'>
      {{> @avatar}}
      <div class="AppModal--account-avatarArea-buttons">
        {{> @uploadAvatarBtn}}
        {{> @uploadAvatarInput}}
        {{> @useGravatarBtn}}
      </div>
    </div>
    {{> @userProfileForm}}
    """

{'fraction_non_alphanumeric': 0.08684113828440779, 'fraction_numerical': 0.00269464745029192, 'mean_word_length': 1.8461538461538463, 'pattern_counts': {'":': 4, '<': 23, '<?xml version=': 0, '>': 107, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1557135', 'n_tokens_mistral': 6620, 'n_tokens_neox': 6346, 'n_words': 1602}