FIX: Let staged users choose their username (#13678)

FIX: Let staged users choose their username (#13678)

When a staged user tried to redeem an invite, a different username was suggested and manually typing the staged username failed because the username was not available.

diff --git a/app/assets/javascripts/discourse/app/controllers/invites-show.js b/app/assets/javascripts/discourse/app/controllers/invites-show.js
index 49f9f79..278b6c0 100644
--- a/app/assets/javascripts/discourse/app/controllers/invites-show.js
+++ b/app/assets/javascripts/discourse/app/controllers/invites-show.js
@@ -28,6 +28,7 @@ export default Controller.extend(
 
     invitedBy: readOnly("model.invited_by"),
     email: alias("model.email"),
+    accountEmail: alias("email"),
     hiddenEmail: alias("model.hidden_email"),
     emailVerifiedByLink: alias("model.email_verified_by_link"),
     accountUsername: alias("model.username"),
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index 5d84267..f883e4c 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -74,6 +74,7 @@ class InvitesController < ApplicationController
       }
 
       if staged_user = User.where(staged: true).with_email(invite.email).first
+        info[:username] = staged_user.username
         info[:user_fields] = staged_user.user_fields
       end
 
diff --git a/app/models/invite_redeemer.rb b/app/models/invite_redeemer.rb
index a6353d1..9dfc743 100644
--- a/app/models/invite_redeemer.rb
+++ b/app/models/invite_redeemer.rb
@@ -13,17 +13,16 @@ InviteRedeemer = Struct.new(:invite, :email, :username, :name, :password, :user_
 
   # extracted from User cause it is very specific to invites
   def self.create_user_from_invite(email:, invite:, username: nil, name: nil, password: nil, user_custom_fields: nil, ip_address: nil, session: nil, email_token: nil)
-    user = User.where(staged: true).with_email(email.strip.downcase).first
-    user.unstage! if user
-
-    user ||= User.new
-
-    if username && UsernameValidator.new(username).valid_format? && User.username_available?(username)
+    if username && UsernameValidator.new(username).valid_format? && User.username_available?(username, email)
       available_username = username
     else
       available_username = UserNameSuggester.suggest(email)
     end
 
+    user = User.where(staged: true).with_email(email.strip.downcase).first
+    user.unstage! if user
+    user ||= User.new
+
     user.attributes = {
       email: email,
       username: available_username,
diff --git a/spec/requests/invites_controller_spec.rb b/spec/requests/invites_controller_spec.rb
index 972715b..4c8efeb 100644
--- a/spec/requests/invites_controller_spec.rb
+++ b/spec/requests/invites_controller_spec.rb
@@ -37,6 +37,7 @@ describe InvitesController do
       expect(response.body).to have_tag("div#data-preloaded") do |element|
         json = JSON.parse(element.current_scope.attribute('data-preloaded').value)
         invite_info = JSON.parse(json['invite_info'])
+        expect(invite_info['username']).to eq(staged_user.username)
         expect(invite_info['user_fields'][user_field.id.to_s]).to eq('some value')
       end
     end
@@ -757,6 +758,39 @@ describe InvitesController do
         expect(response.parsed_body['redirect_to']).to eq("/")
       end
     end
+
+    context 'staged user' do
+      fab!(:invite) { Fabricate(:invite) }
+      fab!(:staged_user) { Fabricate(:user, staged: true, email: invite.email) }
+
+      it 'can keep the old username' do
+        old_username = staged_user.username
+
+        put "/invites/show/#{invite.invite_key}.json", params: {
+          username: staged_user.username,
+          password: "Password123456",
+          email_token: invite.email_token,
+        }
+
+        expect(response.status).to eq(200)
+        expect(invite.reload.redeemed?).to be_truthy
+        user = invite.invited_users.first.user
+        expect(user.username).to eq(old_username)
+      end
+
+      it 'can change the username' do
+        put "/invites/show/#{invite.invite_key}.json", params: {
+          username: "new_username",
+          password: "Password123456",
+          email_token: invite.email_token,
+        }
+
+        expect(response.status).to eq(200)
+        expect(invite.reload.redeemed?).to be_truthy
+        user = invite.invited_users.first.user
+        expect(user.username).to eq("new_username")
+      end
+    end
   end
 
   context '#destroy_all_expired' do

GitHub sha: 2d904ade6dfb260c85e2437247eafe86c7ade3b9

This commit appears in #13678 which was approved by lis2. It was merged by lis2.