diff --git a/src/@types/api/pokerogue-account-api.ts b/src/@types/api/pokerogue-account-api.ts index 7bcdc766664..779592483fb 100644 --- a/src/@types/api/pokerogue-account-api.ts +++ b/src/@types/api/pokerogue-account-api.ts @@ -15,3 +15,10 @@ export interface AccountRegisterRequest { username: string; password: string; } + +export interface AccountChangePwRequest { + password: string; +} +export interface AccountChangePwResponse { + success: boolean; +} diff --git a/src/enums/ui-mode.ts b/src/enums/ui-mode.ts index dcf6bd2a238..bc93e747be2 100644 --- a/src/enums/ui-mode.ts +++ b/src/enums/ui-mode.ts @@ -43,5 +43,6 @@ export enum UiMode { TEST_DIALOGUE, AUTO_COMPLETE, ADMIN, - MYSTERY_ENCOUNTER + MYSTERY_ENCOUNTER, + CHANGE_PASSWORD_FORM, } diff --git a/src/plugins/api/pokerogue-account-api.ts b/src/plugins/api/pokerogue-account-api.ts index 03f522e8dac..22f86413618 100644 --- a/src/plugins/api/pokerogue-account-api.ts +++ b/src/plugins/api/pokerogue-account-api.ts @@ -1,6 +1,7 @@ import { ApiBase } from "#api/api-base"; import { SESSION_ID_COOKIE_NAME } from "#app/constants"; import type { + AccountChangePwRequest, AccountInfoResponse, AccountLoginRequest, AccountLoginResponse, @@ -95,4 +96,19 @@ export class PokerogueAccountApi extends ApiBase { removeCookie(SESSION_ID_COOKIE_NAME); // we are always clearing the cookie. } + + public async changePassword(changePwData: AccountChangePwRequest) { + try { + const response = await this.doPost("/account/changepw", changePwData, "form-urlencoded"); + if (response.ok) { + return null; + } + console.warn("Change password failed!", response.status, response.statusText); + return response.text(); + } catch (err) { + console.warn("Change password failed!", err); + } + + return "Unknown error!"; + } } diff --git a/src/ui/change-password-form-ui-handler.ts b/src/ui/change-password-form-ui-handler.ts new file mode 100644 index 00000000000..dc0fd55d98d --- /dev/null +++ b/src/ui/change-password-form-ui-handler.ts @@ -0,0 +1,115 @@ +import { globalScene } from "#app/global-scene"; +// import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; +import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; +import { UiMode } from "#enums/ui-mode"; +import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; +import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; +import type { ModalConfig } from "#ui/modal-ui-handler"; +import i18next from "i18next"; + +export class ChangePasswordFormUiHandler extends FormModalUiHandler { + private readonly ERR_PASSWORD: string = "invalid password"; + private readonly ERR_ACCOUNT_EXIST: string = "account doesn't exist"; + private readonly ERR_PASSWORD_MISMATCH: string = "password doesn't match"; + + constructor(mode: UiMode | null = null) { + super(mode); + } + + setup(): void { + super.setup(); + } + + override getModalTitle(_config?: ModalConfig): string { + return i18next.t("menu:changePassword"); + } + + override getWidth(_config?: ModalConfig): number { + return 160; + } + + override getMargin(_config?: ModalConfig): [number, number, number, number] { + return [0, 0, 48, 0]; + } + + override getButtonLabels(_config?: ModalConfig): string[] { + return [i18next.t("settings:buttonSubmit"), i18next.t("menu:cancel")]; + } + + override getReadableErrorMessage(error: string): string { + const colonIndex = error?.indexOf(":"); + if (colonIndex > 0) { + error = error.slice(0, colonIndex); + } + switch (error) { + case this.ERR_PASSWORD: + return i18next.t("menu:invalidRegisterPassword"); + case this.ERR_ACCOUNT_EXIST: + return i18next.t("menu:accountNonExistent"); + case this.ERR_PASSWORD_MISMATCH: + return i18next.t("menu:passwordNotMatchingConfirmPassword"); + } + + return super.getReadableErrorMessage(error); + } + + override getInputFieldConfigs(): InputFieldConfig[] { + const inputFieldConfigs: InputFieldConfig[] = []; + inputFieldConfigs.push({ + label: i18next.t("menu:password"), + isPassword: true, + }); + inputFieldConfigs.push({ + label: i18next.t("menu:confirmPassword"), + isPassword: true, + }); + return inputFieldConfigs; + } + + override show(args: any[]): boolean { + console.log("=========================1======================="); + if (super.show(args)) { + // Forces the modal to show above the others + // this.modalContainer.parentContainer?.bringToTop(this.modalContainer); + console.log("========================2========================"); + const config = args[0] as ModalConfig; + const originalSubmitAction = this.submitAction; + this.submitAction = () => { + if (globalScene.tweens.getTweensOf(this.modalContainer).length === 0) { + // Prevent overlapping overrides on action modification + this.submitAction = originalSubmitAction; + this.sanitizeInputs(); + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); + const onFail = (error: string | null) => { + globalScene.ui.setMode(UiMode.CHANGE_PASSWORD_FORM, Object.assign(config, { errorMessage: error?.trim() })); + globalScene.ui.playError(); + }; + const [passwordInput, confirmPasswordInput] = this.inputs; + if (!passwordInput?.text) { + return onFail(this.getReadableErrorMessage("invalid password")); + } + if (passwordInput.text !== confirmPasswordInput.text) { + return onFail(this.ERR_PASSWORD_MISMATCH); + } + + pokerogueApi.account.changePassword({ password: passwordInput.text }).then(error => { + if (!error && originalSubmitAction) { + originalSubmitAction(); + } else { + onFail(error); + } + }); + } + }; + + return true; + } + + return false; + } + + override clear() { + super.clear(); + this.setMouseCursorStyle("default"); //reset cursor + } +} diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index 4e45dfedcb3..ff48da01ef7 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -310,6 +310,17 @@ export class MenuUiHandler extends MessageUiHandler { }, keepOpen: true, }, + { + // Note: i18n key is under `menu`, not `menuUiHandler` to avoid duplication + label: i18next.t("menu:changePassword"), + handler: () => { + ui.setOverlayMode(UiMode.CHANGE_PASSWORD_FORM, { + buttonActions: [() => ui.revertMode(), () => ui.revertMode()], + }); + return true; + }, + keepOpen: true, + }, { label: i18next.t("menuUiHandler:consentPreferences"), handler: () => { diff --git a/src/ui/ui.ts b/src/ui/ui.ts index e9798e6350d..16a271e908c 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -12,6 +12,7 @@ import { BallUiHandler } from "#ui/ball-ui-handler"; import { BattleMessageUiHandler } from "#ui/battle-message-ui-handler"; import type { BgmBar } from "#ui/bgm-bar"; import { GameChallengesUiHandler } from "#ui/challenges-select-ui-handler"; +import { ChangePasswordFormUiHandler } from "#ui/change-password-form-ui-handler"; import { CommandUiHandler } from "#ui/command-ui-handler"; import { ConfirmUiHandler } from "#ui/confirm-ui-handler"; import { EggGachaUiHandler } from "#ui/egg-gacha-ui-handler"; @@ -101,6 +102,7 @@ const noTransitionModes = [ UiMode.ADMIN, UiMode.MYSTERY_ENCOUNTER, UiMode.RUN_INFO, + UiMode.CHANGE_PASSWORD_FORM, ]; export class UI extends Phaser.GameObjects.Container { @@ -171,6 +173,7 @@ export class UI extends Phaser.GameObjects.Container { new AutoCompleteUiHandler(), new AdminUiHandler(), new MysteryEncounterUiHandler(), + new ChangePasswordFormUiHandler(), ]; }