push
This commit is contained in:
parent
41f622125b
commit
5b89cfd573
5
.npmrc
5
.npmrc
@ -1 +1,4 @@
|
||||
node-options=--experimental-vm-modules --no-warnings
|
||||
node-options=--experimental-vm-modules --no-warnings
|
||||
|
||||
@wit-lab-llc:registry=https://npm.pkg.github.com
|
||||
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
|
||||
54
package-lock.json
generated
54
package-lock.json
generated
@ -17,6 +17,7 @@
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"@smakss/react-scroll-direction": "^4.0.4",
|
||||
"@unleash/proxy-client-react": "^4.5.2",
|
||||
"@wit-lab-llc/frontend-shared": "^1.0.4",
|
||||
"apng-js": "^1.1.1",
|
||||
"core-js": "^3.37.1",
|
||||
"framer-motion": "^11.0.8",
|
||||
@ -66,6 +67,28 @@
|
||||
"vite-plugin-svgr": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"../wit-frontend-shared": {
|
||||
"name": "@wit-lab-llc/frontend-shared",
|
||||
"version": "1.0.0",
|
||||
"extraneous": true,
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@fingerprintjs/fingerprintjs": "^5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.0.2",
|
||||
"tsup": "^8.5.1",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
|
||||
@ -1028,6 +1051,12 @@
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fingerprintjs/fingerprintjs": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-5.0.1.tgz",
|
||||
"integrity": "sha512-KbaeE/rk2WL8MfpRP6jTI4lSr42SJPjvkyrjP3QU6uUDkOMWWYC2Ts1sNSYcegHC8avzOoYTHBj+2fTqvZWQBA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz",
|
||||
@ -2127,6 +2156,18 @@
|
||||
"vite": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@wit-lab-llc/frontend-shared": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://npm.pkg.github.com/download/@wit-lab-llc/frontend-shared/1.0.4/c342b071bc0716511d84bc3f3c9685aa7649ea5e",
|
||||
"integrity": "sha512-9EZEpjWCdz+IP4RsgkVTPiUEKhm5LqnbgD/S/GJ07eG0Y10ulj3qXjDVXc7N9gURyZULshriDt350w6nhN7Z3w==",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@fingerprintjs/fingerprintjs": "^5.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
@ -6238,6 +6279,11 @@
|
||||
"integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==",
|
||||
"dev": true
|
||||
},
|
||||
"@fingerprintjs/fingerprintjs": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-5.0.1.tgz",
|
||||
"integrity": "sha512-KbaeE/rk2WL8MfpRP6jTI4lSr42SJPjvkyrjP3QU6uUDkOMWWYC2Ts1sNSYcegHC8avzOoYTHBj+2fTqvZWQBA=="
|
||||
},
|
||||
"@floating-ui/core": {
|
||||
"version": "1.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz",
|
||||
@ -6903,6 +6949,14 @@
|
||||
"react-refresh": "^0.14.0"
|
||||
}
|
||||
},
|
||||
"@wit-lab-llc/frontend-shared": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://npm.pkg.github.com/download/@wit-lab-llc/frontend-shared/1.0.4/c342b071bc0716511d84bc3f3c9685aa7649ea5e",
|
||||
"integrity": "sha512-9EZEpjWCdz+IP4RsgkVTPiUEKhm5LqnbgD/S/GJ07eG0Y10ulj3qXjDVXc7N9gURyZULshriDt350w6nhN7Z3w==",
|
||||
"requires": {
|
||||
"@fingerprintjs/fingerprintjs": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
|
||||
@ -12,7 +12,9 @@
|
||||
"start:local": "vite --host --mode localhost",
|
||||
"start:prod": "vite --host --mode production",
|
||||
"build:dev": "tsc && vite build --mode develop",
|
||||
"build:prod": "tsc && vite build --mode production"
|
||||
"build:prod": "tsc && vite build --mode production",
|
||||
"link:shared": "npm link @wit-lab-llc/frontend-shared",
|
||||
"unlink:shared": "npm unlink @wit-lab-llc/frontend-shared && npm install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.4",
|
||||
@ -24,6 +26,7 @@
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"@smakss/react-scroll-direction": "^4.0.4",
|
||||
"@unleash/proxy-client-react": "^4.5.2",
|
||||
"@wit-lab-llc/frontend-shared": "^1.0.4",
|
||||
"apng-js": "^1.1.1",
|
||||
"core-js": "^3.37.1",
|
||||
"framer-motion": "^11.0.8",
|
||||
|
||||
@ -5,6 +5,7 @@ import { ICreateAuthorizeUser } from "./User";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import { Currency } from "@/components/PaymentTable/Price";
|
||||
import { PeriodType } from "@/hooks/translations";
|
||||
import type { SessionFingerprintData, SessionFacebookData } from "@wit-lab-llc/frontend-shared";
|
||||
|
||||
export interface PayloadCreate {
|
||||
feature: string, // Type: string
|
||||
@ -14,7 +15,13 @@ export interface PayloadCreate {
|
||||
sign: boolean, // Type: boolean
|
||||
signDate: string | undefined, // Type: string, ISO Date
|
||||
utm: IUTM, // Type: { [key: string]: string } - Optional
|
||||
domain: string // Type: string
|
||||
anonymousId?: string,
|
||||
query?: Record<string, string>,
|
||||
landingQuery?: Record<string, string>,
|
||||
lastActivityAt?: string,
|
||||
domain: string, // Type: string
|
||||
fingerprint?: SessionFingerprintData, // Fingerprint data from library
|
||||
facebookData?: SessionFacebookData // Facebook data for Conversions API
|
||||
}
|
||||
|
||||
export interface PayloadUpdate {
|
||||
@ -22,6 +29,10 @@ export interface PayloadUpdate {
|
||||
data: {
|
||||
feature: string, // Type: string
|
||||
|
||||
anonymousId?: string,
|
||||
query?: Record<string, string>,
|
||||
lastActivityAt?: string,
|
||||
|
||||
profile?: Partial<ICreateAuthorizeUser>;
|
||||
partner?: Partial<Exclude<ICreateAuthorizeUser, "relationship_status">>;
|
||||
answers?: Partial<IAnswersSessionPalmistry | IAnswersSessionChats | IAnswersSessionCompatibilityV2 | IAnswersSessionCompatibilityV3 | IAnswersSessionCompatibilityV4>;
|
||||
|
||||
@ -3,9 +3,33 @@ import { PayloadUpdate, ResponseCreate } from "@/api/resources/Session";
|
||||
import { ESourceAuthorization } from "@/api/resources/User";
|
||||
import { getClientTimezone, language } from "@/locales";
|
||||
import { actions, selectors } from "@/store";
|
||||
import { useCallback, useMemo, useState } from "react"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
FingerprintCollector,
|
||||
FacebookCollector,
|
||||
type SessionFingerprintData,
|
||||
type SessionFacebookData,
|
||||
getOrCreateAnonymousId,
|
||||
} from "@wit-lab-llc/frontend-shared";
|
||||
import { parseQueryParams } from "@/services/url";
|
||||
|
||||
let fingerprintCollectorInstance: FingerprintCollector | null = null;
|
||||
let facebookCollectorInstance: FacebookCollector | null = null;
|
||||
|
||||
function getFingerprintCollector(): FingerprintCollector {
|
||||
if (!fingerprintCollectorInstance) {
|
||||
fingerprintCollectorInstance = new FingerprintCollector();
|
||||
}
|
||||
return fingerprintCollectorInstance;
|
||||
}
|
||||
|
||||
function getFacebookCollector(): FacebookCollector {
|
||||
if (!facebookCollectorInstance) {
|
||||
facebookCollectorInstance = new FacebookCollector();
|
||||
}
|
||||
return facebookCollectorInstance;
|
||||
}
|
||||
|
||||
export const useSession = () => {
|
||||
const dispatch = useDispatch();
|
||||
@ -19,16 +43,119 @@ export const useSession = () => {
|
||||
const timezone = getClientTimezone();
|
||||
|
||||
const [isError, setIsError] = useState(false);
|
||||
const [fingerprintCollector] = useState(() => getFingerprintCollector());
|
||||
const [facebookCollector] = useState(() => getFacebookCollector());
|
||||
const dataCollectedRef = useRef(false);
|
||||
|
||||
const visitTtlMs = 30 * 60 * 1000;
|
||||
|
||||
const getVisitStorageKey = useCallback((source: ESourceAuthorization) => {
|
||||
return `${source}_visit`;
|
||||
}, []);
|
||||
|
||||
const readVisit = useCallback((source: ESourceAuthorization) => {
|
||||
try {
|
||||
const raw = localStorage.getItem(getVisitStorageKey(source));
|
||||
if (!raw) return null;
|
||||
return JSON.parse(raw) as {
|
||||
sessionId: string;
|
||||
startedAt: number;
|
||||
lastActivityAt: number;
|
||||
anonymousId?: string;
|
||||
landingQuery?: Record<string, string>;
|
||||
lastQuery?: Record<string, string>;
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}, [getVisitStorageKey]);
|
||||
|
||||
const writeVisit = useCallback((source: ESourceAuthorization, visit: {
|
||||
sessionId: string;
|
||||
startedAt: number;
|
||||
lastActivityAt: number;
|
||||
anonymousId?: string;
|
||||
landingQuery?: Record<string, string>;
|
||||
lastQuery?: Record<string, string>;
|
||||
}) => {
|
||||
localStorage.setItem(getVisitStorageKey(source), JSON.stringify(visit));
|
||||
localStorage.setItem(`${source}_sessionId`, visit.sessionId);
|
||||
}, [getVisitStorageKey]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined" || dataCollectedRef.current) return;
|
||||
dataCollectedRef.current = true;
|
||||
|
||||
Promise.all([
|
||||
fingerprintCollector.collect().catch(() => null),
|
||||
Promise.resolve(facebookCollector.collect()),
|
||||
]).then(() => {
|
||||
console.log("[useSession] Fingerprint and Facebook data collected");
|
||||
});
|
||||
}, [fingerprintCollector, facebookCollector]);
|
||||
|
||||
const createSession = useCallback(async (source: ESourceAuthorization): Promise<ResponseCreate> => {
|
||||
if (session[source]?.length) {
|
||||
localStorage.setItem(`${source}_sessionId`, session[source]);
|
||||
return {
|
||||
sessionId: session[source],
|
||||
status: "old"
|
||||
}
|
||||
const nowMs = Date.now();
|
||||
const existingVisit = readVisit(source);
|
||||
if (existingVisit && nowMs - existingVisit.lastActivityAt <= visitTtlMs) {
|
||||
const nextQuery = parseQueryParams();
|
||||
const nextAnonymousId = existingVisit.anonymousId ?? getOrCreateAnonymousId();
|
||||
writeVisit(source, {
|
||||
...existingVisit,
|
||||
lastActivityAt: nowMs,
|
||||
lastQuery: nextQuery,
|
||||
anonymousId: nextAnonymousId,
|
||||
});
|
||||
dispatch(actions.session.update({ session: existingVisit.sessionId, source }));
|
||||
localStorage.setItem(`${source}_sessionId`, existingVisit.sessionId);
|
||||
return { sessionId: existingVisit.sessionId, status: "old" };
|
||||
}
|
||||
|
||||
try {
|
||||
let fingerprint: SessionFingerprintData | undefined;
|
||||
try {
|
||||
const fpData = await fingerprintCollector.getOrCollect();
|
||||
if (fpData) {
|
||||
const payload = fingerprintCollector.toServerPayload();
|
||||
fingerprint = {
|
||||
visitorId: fpData.visitorId,
|
||||
confidence: fpData.confidence,
|
||||
collectedAt: fpData.collectedAt,
|
||||
...payload,
|
||||
} as SessionFingerprintData;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("[useSession] Failed to collect fingerprint:", e);
|
||||
}
|
||||
|
||||
let facebookData: SessionFacebookData | undefined;
|
||||
try {
|
||||
const fbData = facebookCollector.getData() || facebookCollector.collect();
|
||||
if (fbData) {
|
||||
facebookData = {
|
||||
fbp: fbData.fbp ?? undefined,
|
||||
fbc: fbData.fbc ?? undefined,
|
||||
fbclid: fbData.fbclid ?? undefined,
|
||||
externalId: fingerprint?.visitorId,
|
||||
landingPage: fbData.landingPage ?? undefined,
|
||||
referrer: fbData.referrer,
|
||||
clientUserAgent: fbData.userAgent,
|
||||
eventSourceUrl: fbData.currentUrl,
|
||||
browserLanguage: fbData.browserLanguage,
|
||||
screenResolution: fbData.screenResolution,
|
||||
viewportSize: fbData.viewportSize,
|
||||
colorDepth: fbData.colorDepth,
|
||||
devicePixelRatio: fbData.devicePixelRatio,
|
||||
touchSupport: fbData.touchSupport,
|
||||
cookiesEnabled: fbData.cookiesEnabled,
|
||||
doNotTrack: fbData.doNotTrack,
|
||||
collectedAt: fbData.collectedAt,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("[useSession] Failed to collect Facebook data:", e);
|
||||
}
|
||||
|
||||
const sessionParams = {
|
||||
feature,
|
||||
locale: language,
|
||||
@ -37,7 +164,13 @@ export const useSession = () => {
|
||||
sign: checked,
|
||||
signDate: dateOfCheck.length ? dateOfCheck : undefined,
|
||||
utm,
|
||||
domain: window.location.hostname
|
||||
anonymousId: getOrCreateAnonymousId(),
|
||||
query: parseQueryParams(),
|
||||
landingQuery: parseQueryParams(),
|
||||
lastActivityAt: new Date().toISOString(),
|
||||
domain: window.location.hostname,
|
||||
fingerprint,
|
||||
facebookData,
|
||||
};
|
||||
console.log('Creating session with parameters:', sessionParams);
|
||||
const sessionFromServer = await api.createSession(sessionParams);
|
||||
@ -48,6 +181,15 @@ export const useSession = () => {
|
||||
source
|
||||
}));
|
||||
localStorage.setItem(`${source}_sessionId`, sessionFromServer.sessionId);
|
||||
const q = parseQueryParams();
|
||||
writeVisit(source, {
|
||||
sessionId: sessionFromServer.sessionId,
|
||||
startedAt: Date.now(),
|
||||
lastActivityAt: Date.now(),
|
||||
anonymousId: sessionParams.anonymousId,
|
||||
landingQuery: q,
|
||||
lastQuery: q,
|
||||
});
|
||||
return sessionFromServer
|
||||
}
|
||||
console.error('Session creation failed - invalid response:', sessionFromServer);
|
||||
@ -64,7 +206,7 @@ export const useSession = () => {
|
||||
sessionId: ""
|
||||
}
|
||||
}
|
||||
}, [api, checked, dateOfCheck, dispatch, feature, session, timezone, utm])
|
||||
}, [api, checked, dateOfCheck, dispatch, feature, session, timezone, utm, fingerprintCollector, facebookCollector, readVisit, visitTtlMs, writeVisit])
|
||||
|
||||
const updateSession = useCallback(async (data: Omit<PayloadUpdate["data"], "feature">, source: ESourceAuthorization, sessionId?: string) => {
|
||||
try {
|
||||
@ -73,22 +215,39 @@ export const useSession = () => {
|
||||
sessionId: _sessionId,
|
||||
data: {
|
||||
feature,
|
||||
anonymousId: getOrCreateAnonymousId(),
|
||||
query: parseQueryParams(),
|
||||
lastActivityAt: new Date().toISOString(),
|
||||
...data
|
||||
}
|
||||
});
|
||||
|
||||
if (_sessionId) {
|
||||
const nowMs = Date.now();
|
||||
const existingVisit = readVisit(source);
|
||||
if (existingVisit && existingVisit.sessionId === _sessionId) {
|
||||
writeVisit(source, {
|
||||
...existingVisit,
|
||||
lastActivityAt: nowMs,
|
||||
lastQuery: parseQueryParams(),
|
||||
anonymousId: getOrCreateAnonymousId(),
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}, [api, feature, session])
|
||||
}, [api, feature, session, readVisit, writeVisit])
|
||||
|
||||
const deleteSession = useCallback(async (source: ESourceAuthorization) => {
|
||||
localStorage.removeItem(`${source}_sessionId`);
|
||||
localStorage.removeItem(getVisitStorageKey(source));
|
||||
dispatch(actions.session.update({
|
||||
session: "",
|
||||
source
|
||||
}))
|
||||
}, [dispatch])
|
||||
}, [dispatch, getVisitStorageKey])
|
||||
|
||||
return useMemo(() => ({
|
||||
session,
|
||||
|
||||
@ -26,12 +26,19 @@ import { InitializationProvider } from "./initialization";
|
||||
import { getSourceByPathname } from "./utils/source.utils";
|
||||
import { parseQueryParams } from "./services/url";
|
||||
import { actions } from "./store";
|
||||
import { initWitLib } from "@wit-lab-llc/frontend-shared";
|
||||
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.js`;
|
||||
|
||||
const environments = import.meta.env;
|
||||
|
||||
const init = async () => {
|
||||
// Initialize WIT shared library for fingerprint and facebook data collection
|
||||
initWitLib({
|
||||
baseUrl: environments.AURA_API_URL || '',
|
||||
debug: environments.MODE !== 'production',
|
||||
});
|
||||
|
||||
// Parse UTM parameters from URL at initialization
|
||||
const utm = parseQueryParams();
|
||||
console.log('UTM parameters parsed at init:', utm);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user