164 lines
3.8 KiB
TypeScript
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
|
|
})
|
|
})
|
|
}
|
|
}
|