1
1
mirror of https://github.com/theoludwig/p61-project.git synced 2024-07-17 07:00:12 +02:00
p61-project/presentation/presenters/Authentication.ts

164 lines
3.8 KiB
TypeScript

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<keyof UserRegisterData>
global: ErrorGlobal
}
}
login: {
state: FetchState
errors: {
fields: Array<keyof UserLoginData>
global: ErrorGlobal
}
}
logout: {
state: FetchState
}
}
export interface AuthenticationPresenterOptions {
authenticationUseCase: AuthenticationUseCase
}
export class AuthenticationPresenter
extends Presenter<AuthenticationPresenterState>
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<void> {
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<UserRegisterData>(error)
} else {
state.register.errors.global = "unknown"
}
})
}
}
public async login(data: unknown): Promise<void> {
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<UserLoginData>(error)
} else {
state.login.errors.global = "unknown"
}
})
}
}
public async logout(): Promise<void> {
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<void> {
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
})
})
}
}