Files
illusory-iotam/apps/main/src/lib/domains/account/account.vm.svelte.ts
2026-03-27 20:06:38 +02:00

154 lines
5.3 KiB
TypeScript

import { ResultAsync, errAsync, okAsync } from "neverthrow";
import type { User } from "@pkg/logic/domains/user/data";
import { user as userStore } from "$lib/global.stores";
import { rotatePasswordSC } from "./account.remote";
import { authClient } from "$lib/auth.client";
import type { Err } from "@pkg/result";
import { toast } from "svelte-sonner";
import { get } from "svelte/store";
class AccountViewModel {
loading = $state(false);
passwordLoading = $state(false);
errorMessage = $state<string | null>(null);
async updateProfilePicture(imagePath: string): Promise<boolean> {
const result = await ResultAsync.fromPromise(
authClient.updateUser({ image: imagePath }),
(error): Err => ({
code: "NETWORK_ERROR",
message: "Failed to update profile picture",
description: "Network request failed",
detail: error instanceof Error ? error.message : String(error),
}),
).andThen((response) => {
if (response.error) {
return errAsync({
code: "API_ERROR",
message:
response.error.message ??
"Failed to update profile picture",
description:
response.error.statusText ?? "Please try again later",
detail: response.error.statusText ?? "Unknown error",
});
}
return okAsync(response.data);
});
return result.match(
() => {
toast.success("Profile picture updated");
return true;
},
(error) => {
this.errorMessage =
error.message ?? "Failed to update profile picture";
toast.error(this.errorMessage, {
description: error.description,
});
return false;
},
);
}
async updateProfile(userData: {
name: string;
username: string;
}): Promise<User | null> {
this.loading = true;
this.errorMessage = null;
const result = await ResultAsync.fromPromise(
authClient.updateUser({
displayUsername: userData.username,
username: userData.username,
name: userData.name,
}),
(error): Err => ({
code: "NETWORK_ERROR",
message: "Failed to update profile",
description: "Network request failed",
detail: error instanceof Error ? error.message : String(error),
}),
).andThen((response) => {
if (response.error) {
return errAsync({
code: "API_ERROR",
message:
response.error.message ?? "Failed to update profile",
description:
response.error.statusText ?? "Please try again later",
detail: response.error.statusText ?? "Unknown error",
});
}
return okAsync(response.data);
});
const user = result.match(
(data) => {
toast.success("Profile updated successfully");
window.location.reload();
return (data as any)?.user as User | null;
},
(error) => {
this.errorMessage = error.message ?? "Failed to update profile";
toast.error(this.errorMessage, {
description: error.description,
});
return null;
},
);
this.loading = false;
return user;
}
async changePassword(password: string): Promise<boolean> {
this.passwordLoading = true;
this.errorMessage = null;
const currentUser = get(userStore);
if (!currentUser?.id) {
this.passwordLoading = false;
this.errorMessage = "User not found";
toast.error(this.errorMessage);
return false;
}
const result = await ResultAsync.fromPromise(
rotatePasswordSC({ userId: currentUser.id, password }),
(error): Err => ({
code: "NETWORK_ERROR",
message: "Failed to change password",
description: "Network request failed",
detail: error instanceof Error ? error.message : String(error),
}),
).andThen((apiResult: any) => {
if (apiResult?.error) {
return errAsync(apiResult.error);
}
return okAsync(apiResult?.data);
});
const success = result.match(
() => {
toast.success("Password updated successfully");
return true;
},
(error) => {
this.errorMessage =
(error.message as string) ?? "Failed to change password";
toast.error(this.errorMessage, {
description: error.description,
});
return false;
},
);
this.passwordLoading = false;
return success;
}
}
export const accountVM = new AccountViewModel();