@heyframe/composables
Version:
HeyFrame Frontends composables for Vue
330 lines (311 loc) • 8.94 kB
text/typescript
import { syncRefs } from "@vueuse/core";
import { computed, ref } from "vue";
import type { ComputedRef, Ref } from "vue";
import {
useCart,
useContext,
useInternationalization,
useSessionContext,
useHeyFrameContext,
} from "#imports";
import type { Schemas, operations } from "#heyframe";
export type UseUserReturn = {
/**
* Logs-in user with given credentials
* @param params - username and password
*
* @see https://github.com/heyframe/frontends/issues/112 if login fails due to missing context token
*/
login(params: { username: string; password: string }): Promise<void>;
/**
* Registers the user for given credentials
* @param params {@link CustomerRegistrationParams}
* @returns {@link Customer} object on success
*/
register(
params: Omit<
operations["register post /account/register"]["body"],
"storefrontUrl"
>,
): Promise<Schemas["Customer"]>;
/**
* Whole {@link Customer} object
*/
user: ComputedRef<Schemas["Customer"] | undefined>;
/**
* Indicates if the user is logged in
*/
isLoggedIn: ComputedRef<boolean>;
/**
* Indicates if the user is logged in as a customer (not a guest)
*/
isCustomerSession: ComputedRef<boolean>;
/**
* Indicates if the user is logged in as a guest
*/
isGuestSession: ComputedRef<boolean>;
/**
* {@link Country} of the user
*/
country: Ref<Schemas["Country"] | null>;
/**
* {@link Salutation} of the user
*/
salutation: Ref<Schemas["Salutation"] | null>;
/**
* Default billing address id
*/
defaultBillingAddressId: ComputedRef<string | null>;
/**
* Default shipping address id
*/
defaultShippingAddressId: ComputedRef<string | null>;
/**
* Fetches the user data from the API
*/
refreshUser(params?: Schemas["Criteria"]): Promise<Schemas["Customer"]>;
/**
* Logs out the user
*/
logout(): Promise<
operations["logoutCustomer post /account/logout"]["response"]
>;
/**
* Loads the {@link Country} of the user
*/
loadCountry(
countryId: string,
): Promise<operations["readCountry post /country"]["response"]>;
/**
* Loads the {@link Salutation} for given id
*/
loadSalutation(
salutationId: string,
): Promise<operations["readSalutation post /salutation"]["response"]>;
/**
* Updates the user profile data
* @param personals {@link RequestParameters<'changeProfile'>}
* @returns
*/
updatePersonalInfo(
personals: operations["changeProfile post /account/change-profile"]["body"],
): Promise<void>;
/**
* Updates the user email
* @param updateEmailData - {@link RequestParameters<'changeEmail'>}
* @returns
*/
updateEmail(
updateEmailData: operations["changeEmail post /account/change-email"]["body"],
): Promise<void>;
/**
* @deprecated API endpoint is removed in HeyFrame 6.7
*
* Sets the default payment method for given id
* @param paymentMethodId
* @returns
*/
setDefaultPaymentMethod(paymentMethodId: string): Promise<void>;
/**
* Default payment method for the user
*/
userDefaultPaymentMethod: ComputedRef<
Schemas["PaymentMethod"]["translated"] | null
>;
/**
* Default billing address for the user
*/
userDefaultBillingAddress: ComputedRef<Schemas["CustomerAddress"] | null>;
/**
* Default shipping address for the user
*/
userDefaultShippingAddress: ComputedRef<Schemas["CustomerAddress"] | null>;
};
/**
* Composable for user management.
* @public
* @category Customer & Account
*/
export function useUser(): UseUserReturn {
const { apiClient } = useHeyFrameContext();
const { userFromContext, refreshSessionContext } = useSessionContext();
const _user = useContext<Schemas["Customer"] | undefined>("customer");
syncRefs(userFromContext, _user, {
immediate: true,
});
const { getStorefrontUrl } = useInternationalization();
const { refreshCart } = useCart();
const userDefaultPaymentMethod = computed(
() =>
user.value?.lastPaymentMethod?.translated ||
// @ts-expect-error TODO: [MAJOR] Removed since 6.7
user.value?.defaultPaymentMethod?.translated ||
null,
);
const userDefaultBillingAddress = computed(
() => user.value?.defaultBillingAddress || null,
);
const userDefaultShippingAddress = computed(
() => user.value?.defaultShippingAddress || null,
);
const country: Ref<Schemas["Country"] | null> = ref(null);
const salutation: Ref<Schemas["Salutation"] | null> = ref(null);
const user = computed(() => _user.value);
async function login({
username,
password,
}: {
username: string;
password: string;
}): Promise<void> {
await apiClient.invoke("loginCustomer post /account/login", {
body: {
username,
password,
},
});
await refreshSessionContext();
refreshCart();
}
async function register(
params: Omit<
operations["register post /account/register"]["body"],
"storefrontUrl"
>,
): Promise<Schemas["Customer"]> {
const { data } = await apiClient.invoke("register post /account/register", {
body: {
...(params as operations["register post /account/register"]["body"]),
storefrontUrl: getStorefrontUrl(),
},
});
// Update the user data in the context if the user is active and not using double opt-in registration set in the HeyFrame Admin
if (data.active && !data.doubleOptInRegistration) {
_user.value = data;
}
await refreshSessionContext();
return data;
}
async function logout(): Promise<
operations["logoutCustomer post /account/logout"]["response"]
> {
const response = await apiClient.invoke(
"logoutCustomer post /account/logout",
);
await refreshSessionContext();
refreshCart();
return response.data;
}
async function refreshUser(
params: Schemas["Criteria"] = {},
): Promise<Schemas["Customer"]> {
try {
const response = await apiClient.invoke(
"readCustomer post /account/customer",
{ body: params },
);
_user.value = response.data;
return response.data;
} catch (e) {
_user.value = undefined;
throw e;
}
}
async function loadCountry(
countryId: string,
): Promise<operations["readCountry post /country"]["response"]> {
const countries = await apiClient.invoke("readCountry post /country", {
body: {
filter: [
{
field: "id",
type: "equals",
value: countryId,
},
],
},
});
country.value = countries.data.elements?.[0] ?? null;
return countries.data;
}
async function loadSalutation(
salutationId: string,
): Promise<operations["readSalutation post /salutation"]["response"]> {
const salutations = await apiClient.invoke(
"readSalutation post /salutation",
{
body: {
filter: [
{
field: "id",
type: "equals",
value: salutationId,
},
],
},
},
);
salutation.value = salutations.data.elements?.[0] ?? null;
return salutations.data;
}
async function updatePersonalInfo(
personals: operations["changeProfile post /account/change-profile"]["body"],
): Promise<void> {
await apiClient.invoke("changeProfile post /account/change-profile", {
body: personals,
});
}
async function updateEmail(
updateEmailData: operations["changeEmail post /account/change-email"]["body"],
): Promise<void> {
await apiClient.invoke("changeEmail post /account/change-email", {
body: updateEmailData,
});
}
async function setDefaultPaymentMethod(
paymentMethodId: string,
): Promise<void> {
await apiClient.invoke(
// @ts-expect-error TODO: [MAJOR] Removed since 6.7
"changePaymentMethod post /account/change-payment-method/{paymentMethodId}",
{
pathParams: { paymentMethodId },
},
);
}
const defaultBillingAddressId = computed(
() => user.value?.defaultBillingAddressId || null,
);
const defaultShippingAddressId = computed(
() => user.value?.defaultShippingAddressId || null,
);
const isLoggedIn = computed(
() => !!user.value?.id && !!user.value.active && !user.value.guest,
);
const isCustomerSession = computed(
() => !!user.value?.id && !user.value.guest,
);
const isGuestSession = computed(() => !!user.value?.guest);
return {
login,
register,
user,
isLoggedIn,
isCustomerSession,
isGuestSession,
refreshUser,
logout,
updateEmail,
updatePersonalInfo,
loadSalutation,
salutation,
loadCountry,
country,
defaultBillingAddressId,
defaultShippingAddressId,
userDefaultPaymentMethod,
userDefaultBillingAddress,
setDefaultPaymentMethod,
userDefaultShippingAddress,
};
}