import { ZodError } from "zod" import type { User, UserLoginData, UserRegisterData, } from "@/domain/entities/User" import type { AuthenticationUseCase } from "@/domain/use-cases/Authentication" import type { ErrorGlobal, FetchState } from "./_Presenter" import { Presenter } from "./_Presenter" import { getErrorsFieldsFromZodError } from "./utils/zod" export interface AuthenticationPresenterState { user: User | null /** * `true` if the initial authentication state has been loaded. */ hasLoaded: boolean register: { state: FetchState errors: { fields: Array global: ErrorGlobal } } login: { state: FetchState errors: { fields: Array global: ErrorGlobal } } logout: { state: FetchState } } export interface AuthenticationPresenterOptions { authenticationUseCase: AuthenticationUseCase } export class AuthenticationPresenter extends Presenter implements AuthenticationPresenterOptions { public authenticationUseCase: AuthenticationUseCase public constructor(options: AuthenticationPresenterOptions) { const { authenticationUseCase } = options super({ user: null, hasLoaded: true, register: { state: "idle", errors: { fields: [], global: null, }, }, login: { state: "idle", errors: { fields: [], global: null, }, }, logout: { state: "idle", }, }) this.authenticationUseCase = authenticationUseCase } public async register(data: unknown): Promise { try { this.setState((state) => { state.register.state = "loading" state.register.errors = { fields: [], global: null, } }) const user = await this.authenticationUseCase.register(data) this.setState((state) => { state.register.state = "success" state.user = user }) } catch (error) { this.setState((state) => { state.register.state = "error" if (error instanceof ZodError) { state.register.errors.fields = getErrorsFieldsFromZodError(error) } else { state.register.errors.global = "unknown" } }) } } public async login(data: unknown): Promise { try { this.setState((state) => { state.login.state = "loading" state.login.errors = { fields: [], global: null, } }) const user = await this.authenticationUseCase.login(data) this.setState((state) => { state.login.state = "success" state.user = user }) } catch (error) { this.setState((state) => { state.login.state = "error" if (error instanceof ZodError) { state.login.errors.fields = getErrorsFieldsFromZodError(error) } else { state.login.errors.global = "unknown" } }) } } public async logout(): Promise { try { this.setState((state) => { state.logout.state = "loading" }) await this.authenticationUseCase.logout() this.setState((state) => { state.logout.state = "success" state.user = null }) } catch (error) { this.setState((state) => { state.user = null state.logout.state = "error" }) } } public async initialAuthStateListener(): Promise { const user = await this.authenticationUseCase.getUser() this.setState((state) => { state.user = user state.hasLoaded = false }) this.authenticationUseCase.onUserStateChange((user) => { this.setState((state) => { state.user = user }) }) } }