179 lines
6.0 KiB
TypeScript
179 lines
6.0 KiB
TypeScript
"use client";
|
||
|
||
import { useCallback, useMemo, useState } from "react";
|
||
import { useSession } from "../session/useSession";
|
||
import { getClientTimezone } from "@/shared/utils/locales";
|
||
import { ICreateAuthorizeRequest } from "@/entities/user/types";
|
||
import { filterNullKeysOfObject } from "@/shared/utils/filter-object";
|
||
import { createAuthorization } from "@/entities/user/actions";
|
||
import { setAuthTokenToCookie } from "@/entities/user/serverActions";
|
||
import analyticsService, { AnalyticsEvent, AnalyticsPlatform } from "@/services/analytics/analyticsService";
|
||
import { metricService } from "@/services/analytics/metricService";
|
||
import { ApiError } from "@/shared/api/httpClient";
|
||
|
||
// TODO
|
||
const locale = "en";
|
||
|
||
interface IUseAuthProps {
|
||
funnelId: string;
|
||
googleAnalyticsId?: string;
|
||
/**
|
||
* Дополнительные данные для регистрации пользователя.
|
||
* Будут объединены с базовым payload при авторизации.
|
||
*/
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
registrationData?: Record<string, any>;
|
||
}
|
||
|
||
export const useAuth = ({ funnelId, googleAnalyticsId, registrationData }: IUseAuthProps) => {
|
||
const { updateSession } = useSession({ funnelId, googleAnalyticsId });
|
||
|
||
const [isLoading, setIsLoading] = useState(false);
|
||
const [error, setError] = useState<string | null>(null);
|
||
const [suggestedEmail, setSuggestedEmail] = useState<string | null>(null);
|
||
|
||
const getAllCookies = useCallback(() => {
|
||
// Токены которые не должны передаваться на backend
|
||
const EXCLUDED_COOKIES = ["accessToken", "activeSessionId"];
|
||
|
||
const cookies: Record<string, string> = {};
|
||
document.cookie.split(";").forEach((cookie) => {
|
||
const [name, value] = cookie.trim().split("=");
|
||
if (name && value && !EXCLUDED_COOKIES.includes(name)) {
|
||
cookies[name] = decodeURIComponent(value);
|
||
}
|
||
});
|
||
return cookies;
|
||
}, []);
|
||
|
||
const getAuthorizationPayload = useCallback(
|
||
(email: string): ICreateAuthorizeRequest => {
|
||
const timezone = getClientTimezone();
|
||
const basePayload = {
|
||
timezone,
|
||
locale,
|
||
email,
|
||
source: funnelId,
|
||
sign: true,
|
||
signDate: new Date().toISOString(),
|
||
feature: "stripe"
|
||
};
|
||
|
||
// Объединяем базовый payload с данными регистрации из воронки
|
||
const mergedPayload = registrationData
|
||
? { ...basePayload, ...registrationData }
|
||
: basePayload;
|
||
|
||
return filterNullKeysOfObject<ICreateAuthorizeRequest>(mergedPayload);
|
||
},
|
||
[funnelId, registrationData]
|
||
);
|
||
|
||
const authorization = useCallback(
|
||
async (email: string) => {
|
||
try {
|
||
setIsLoading(true);
|
||
setError(null);
|
||
|
||
// Обновляем сессию с куки перед авторизацией
|
||
try {
|
||
const cookies = getAllCookies();
|
||
await updateSession({ cookies });
|
||
console.log(
|
||
"Session updated with cookies before authorization:",
|
||
cookies
|
||
);
|
||
} catch (sessionError) {
|
||
console.warn("Failed to update session with cookies:", sessionError);
|
||
// Продолжаем авторизацию даже если обновление сессии не удалось
|
||
}
|
||
|
||
const payload = getAuthorizationPayload(email);
|
||
const { token, userId } = await createAuthorization(payload);
|
||
|
||
// Track registration events in analytics
|
||
// Send EnteredEmail to Yandex Metrika and Google Analytics
|
||
analyticsService.trackEvent(
|
||
AnalyticsEvent.ENTERED_EMAIL,
|
||
[AnalyticsPlatform.YANDEX_METRIKA, AnalyticsPlatform.GOOGLE_ANALYTICS]
|
||
);
|
||
|
||
// Send Lead to Facebook Pixel
|
||
analyticsService.trackEvent(
|
||
AnalyticsEvent.LEAD,
|
||
[AnalyticsPlatform.FACEBOOK]
|
||
);
|
||
|
||
// Set user ID and properties in analytics
|
||
if (userId) {
|
||
analyticsService.setUserId(userId);
|
||
analyticsService.setUserProperties({
|
||
email,
|
||
source: funnelId,
|
||
UserID: userId,
|
||
});
|
||
|
||
// ✅ Отправляем UserID и email в userParams (параметры посетителя)
|
||
metricService.setUserID(userId);
|
||
metricService.userParams({
|
||
UserID: userId,
|
||
email,
|
||
});
|
||
|
||
// ✅ Отправляем email и userId в params (параметры визита)
|
||
metricService.sendVisitContext({
|
||
email,
|
||
userId,
|
||
});
|
||
}
|
||
|
||
await setAuthTokenToCookie(token);
|
||
return token;
|
||
} catch (err) {
|
||
// Extract error message and suggestion from API error
|
||
if (err instanceof ApiError && err.data) {
|
||
const errorData = err.data as { errors?: Array<{ msg: string; path: string }> };
|
||
const emailError = errorData.errors?.find(e => e.path === 'email');
|
||
|
||
if (emailError) {
|
||
setError(emailError.msg);
|
||
|
||
// Extract suggested email from "Did you mean user@gmail.com?" message
|
||
const suggestionMatch = emailError.msg.match(/Did you mean (.+)\?/);
|
||
if (suggestionMatch) {
|
||
setSuggestedEmail(suggestionMatch[1]);
|
||
} else {
|
||
setSuggestedEmail(null);
|
||
}
|
||
} else {
|
||
setError('Email validation failed');
|
||
setSuggestedEmail(null);
|
||
}
|
||
} else {
|
||
setError((err as Error).message);
|
||
setSuggestedEmail(null);
|
||
}
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
},
|
||
[getAllCookies, getAuthorizationPayload, updateSession, funnelId]
|
||
);
|
||
|
||
const clearError = useCallback(() => {
|
||
setError(null);
|
||
setSuggestedEmail(null);
|
||
}, []);
|
||
|
||
return useMemo(
|
||
() => ({
|
||
authorization,
|
||
isLoading,
|
||
error,
|
||
suggestedEmail,
|
||
clearError,
|
||
}),
|
||
[authorization, isLoading, error, suggestedEmail, clearError]
|
||
);
|
||
};
|