183183 >
184184 <div class =" flex flex-col gap-6" >
185185 <template v-if =" auth .user .has_totp && twoFactorStep === 0 " >
186- <label for = " two-factor-code " >
186+ <div class = " flex flex-col gap-2.5 " >
187187 <span class =" text-md font-semibold text-contrast" >{{
188188 formatMessage(messages.twoFactorEnterCodeLabel)
189189 }}</span >
190+ <StyledInput
191+ id="two-factor-code"
192+ v-model =" twoFactorCode "
193+ :maxlength =" 11 "
194+ :placeholder =" formatMessage (messages .twoFactorCodePlaceholder )"
195+ @keyup .enter =" removeTwoFactor ()"
196+ />
190197 <span class =" label__description" >{{
191198 formatMessage(messages.twoFactorEnterCodeDescription)
192199 }}</span >
193- </label >
194- <StyledInput
195- id="two-factor-code"
196- v-model =" twoFactorCode "
197- :maxlength =" 11 "
198- :placeholder =" formatMessage (messages .twoFactorCodePlaceholder )"
199- @keyup .enter =" removeTwoFactor ()"
200- />
201- <p v-if =" twoFactorIncorrect" class =" known-errors m-0" >
202- {{ formatMessage(messages.twoFactorIncorrectError) }}
203- </p >
200+ <p v-if =" twoFactorIncorrect" class =" known-errors m-0" >
201+ {{ formatMessage(messages.twoFactorIncorrectError) }}
202+ </p >
203+ </div >
204204 <div class =" flex justify-end gap-2.5" >
205205 <ButtonStyled >
206206 <button @click =" $refs.manageTwoFactorModal.hide()" >
222222 <p class =" m-0" >
223223 <IntlFormatted :message-id =" messages .twoFactorSetupScan " >
224224 <template #authy-link =" { children } " >
225- <a href =" https://authy.com/" target =" _blank" rel =" noreferrer" >
225+ <a class = " underline " href =" https://authy.com/" target =" _blank" rel =" noreferrer" >
226226 <component :is =" () => children" />
227227 </a >
228228 </template >
229229 <template #microsoft-authenticator-link =" { children } " >
230230 <a
231+ class =" underline"
231232 href =" https://www.microsoft.com/en-us/security/mobile-authenticator-app"
232233 target =" _blank"
233234 rel =" noreferrer"
252253 </p >
253254 </template >
254255 <template v-if =" twoFactorStep === 1 " >
255- <label for = " verify-code " >
256+ <div class = " flex flex-col gap-2.5 " >
256257 <span class =" text-md font-semibold text-contrast" >{{
257258 formatMessage(messages.twoFactorVerifyCodeLabel)
258259 }}</span >
260+ <TwoFactorAuthCodeInput
261+ ref="twoFactorCodeInput"
262+ v-model =" twoFactorCode "
263+ @keyup .enter =" verifyTwoFactorCode ()"
264+ />
259265 <span class =" label__description" >{{
260266 formatMessage(messages.twoFactorVerifyCodeDescription)
261267 }}</span >
262- </label >
263- <StyledInput
264- id="verify-code"
265- v-model =" twoFactorCode "
266- :maxlength =" 6 "
267- autocomplete="one-time-code"
268- :placeholder =" formatMessage (messages .twoFactorCodePlaceholder )"
269- @keyup .enter =" verifyTwoFactorCode ()"
270- />
271- <p v-if =" twoFactorIncorrect" class =" known-errors m-0" >
272- {{ formatMessage(messages.twoFactorIncorrectError) }}
273- </p >
268+ <p v-if =" twoFactorIncorrect" class =" known-errors m-0" >
269+ {{ formatMessage(messages.twoFactorIncorrectError) }}
270+ </p >
271+ </div >
274272 </template >
275273 <template v-if =" twoFactorStep === 2 " >
276274 <p class =" m-0" >{{ formatMessage(messages.twoFactorBackupCodesIntro) }}</p >
478476</template >
479477
480478<script setup>
479+ import { nextTick , watch } from ' vue'
481480import {
482481 CheckIcon ,
483482 DownloadIcon ,
@@ -503,6 +502,7 @@ import {
503502 NewModal ,
504503 StyledInput ,
505504 Table ,
505+ TwoFactorAuthCodeInput ,
506506 useVIntl ,
507507} from ' @modrinth/ui'
508508import KeyIcon from ' assets/icons/auth/key.svg'
@@ -878,9 +878,10 @@ const manageTwoFactorModal = ref()
878878const twoFactorSecret = ref (null )
879879const twoFactorFlow = ref (null )
880880const twoFactorStep = ref (0 )
881+ const twoFactorCodeInput = ref ()
881882async function showTwoFactorModal () {
882883 twoFactorStep .value = 0
883- twoFactorCode .value = null
884+ twoFactorCode .value = ' '
884885 twoFactorIncorrect .value = false
885886 if (auth .value .user .has_totp ) {
886887 manageTwoFactorModal .value .show ()
@@ -890,7 +891,6 @@ async function showTwoFactorModal() {
890891 twoFactorSecret .value = null
891892 twoFactorFlow .value = null
892893 backupCodes .value = []
893- manageTwoFactorModal .value .show ()
894894
895895 startLoading ()
896896 try {
@@ -908,11 +908,22 @@ async function showTwoFactorModal() {
908908 })
909909 }
910910 stopLoading ()
911+ manageTwoFactorModal .value .show ()
911912}
912913
913914const twoFactorIncorrect = ref (false )
914- const twoFactorCode = ref (null )
915+ const twoFactorCode = ref (' ' )
915916const backupCodes = ref ([])
917+
918+ watch (twoFactorStep, async (step ) => {
919+ if (step !== 1 ) {
920+ return
921+ }
922+
923+ await nextTick ()
924+ twoFactorCodeInput .value ? .focus ()
925+ })
926+
916927async function verifyTwoFactorCode () {
917928 startLoading ()
918929 try {
0 commit comments