From 643e41cc98cc79fab41a00cac32dfc24cb6f3b40 Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Mon, 21 Sep 2026 09:22:10 +0300 Subject: [PATCH] =?UTF-8?q?fix(analitik):=20kullaniciTanit=20script'ten=20?= =?UTF-8?q?=C3=B6nce=20sessiz=20no-op=20olmas=C4=B1n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Erken olay kuyruğu yalnız olay() içindi; sert yüklemede (OAuth / e-posta linki dönüşü) RybbitIdentify effect'i de script'ten önce koşuyor, identify kayboluyor ve kuyruktan boşalan olaylar anonim profile yazılıyordu (08-kod-denetimi #6). - Tek "son değer" slotu (bekleyenKimlik): kuyruk değil, yalnız en son kimlik anlamlı. Yoklama zamanlayıcısı olay kuyruğuyla ortak (zamanlayiciyiBaslat); script 10 sn'de gelmezse slot da atılır. - kuyruguBosalt kimliği olaylardan ÖNCE uygular. - kullaniciTemizle slotu siler. rybbit-identify.tsx'e dokunulmadı. Co-Authored-By: Claude Fable 5.1 --- src/lib/analitik.ts | 50 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/src/lib/analitik.ts b/src/lib/analitik.ts index 4f9bb5b..186ff70 100644 --- a/src/lib/analitik.ts +++ b/src/lib/analitik.ts @@ -74,6 +74,10 @@ const DENEME_ARALIGI_MS = 500; const DENEME_SINIRI = 20; let kuyruk: Array<[OlayAdi, Ozellikler | undefined]> = []; +// Script'ten önce gelen identify için tek "son değer" slotu: sert yüklemede +// (OAuth / e-posta linki dönüşü) RybbitIdentify effect'i de script'ten önce +// koşar. Kuyruk değil slot — yalnız en son kimlik anlamlıdır. +let bekleyenKimlik: [string, Record | undefined] | null = null; let zamanlayici: ReturnType | null = null; let denemeSayisi = 0; let kuyrukKapali = false; @@ -83,11 +87,21 @@ function zamanlayiciyiDurdur(): void { zamanlayici = null; } -/** Bekleyen olayları geliş sırasıyla gönderir. */ +/** Önce bekleyen kimliği uygular, sonra olayları geliş sırasıyla gönderir + * (kuyruktaki olaylar anonim profile değil tanımlı kullanıcıya yazılsın). */ function kuyruguBosalt(rybbit: RybbitApi): void { const bekleyenler = kuyruk; + const kimlik = bekleyenKimlik; kuyruk = []; + bekleyenKimlik = null; zamanlayiciyiDurdur(); + if (kimlik) { + try { + rybbit.identify(kimlik[0], kimlik[1]); + } catch { + // yut + } + } for (const [ad, ozellikler] of bekleyenler) { try { rybbit.event(ad, ozellikler); @@ -97,23 +111,29 @@ function kuyruguBosalt(rybbit: RybbitApi): void { } } -function kuyrugaEkle(ad: OlayAdi, ozellikler?: Ozellikler): void { - if (kuyrukKapali || kuyruk.length >= KUYRUK_SINIRI) return; - kuyruk.push([ad, ozellikler]); +/** Script'i bekleyen yoklamayı başlatır (olay kuyruğu ve kimlik slotu ortak). */ +function zamanlayiciyiBaslat(): void { if (zamanlayici !== null) return; zamanlayici = setInterval(() => { denemeSayisi += 1; if (window.rybbit) { kuyruguBosalt(window.rybbit); } else if (denemeSayisi >= DENEME_SINIRI) { - // Script gelmedi: bekleyenleri at, sonraki olaylar eski no-op'a dönsün + // Script gelmedi: bekleyenleri at, sonraki çağrılar eski no-op'a dönsün kuyruk = []; + bekleyenKimlik = null; kuyrukKapali = true; zamanlayiciyiDurdur(); } }, DENEME_ARALIGI_MS); } +function kuyrugaEkle(ad: OlayAdi, ozellikler?: Ozellikler): void { + if (kuyrukKapali || kuyruk.length >= KUYRUK_SINIRI) return; + kuyruk.push([ad, ozellikler]); + zamanlayiciyiBaslat(); +} + /** Özel olay gönderir. Script henüz yoksa kısa süre kuyrukta bekletir. */ export function olay(ad: OlayAdi, ozellikler?: Ozellikler): void { if (typeof window === "undefined") return; @@ -122,8 +142,8 @@ export function olay(ad: OlayAdi, ozellikler?: Ozellikler): void { kuyrugaEkle(ad, ozellikler); return; } - // Sıra bozulmasın: bekleyen varsa önce onlar gider - if (kuyruk.length > 0) kuyruguBosalt(window.rybbit); + // Sıra bozulmasın: bekleyen (kimlik ya da olay) varsa önce onlar gider + if (kuyruk.length > 0 || bekleyenKimlik) kuyruguBosalt(window.rybbit); window.rybbit.event(ad, ozellikler); } catch { // Analitik hiçbir koşulda kullanıcı akışını bozmaz @@ -174,6 +194,8 @@ export function siraKovasi(sira: number): string { * Giriş yapan kullanıcıyı eşler. İlk çağrıda o cihazdaki önceki anonim * olaylar geriye dönük olarak bu kullanıcıya bağlanır (sihirbazı doldurup * sonra giriş yapan öğrencinin yolculuğu tek profilde birleşsin diye). + * Script henüz yoksa kimlik tek slotta bekler; kuyruk boşaltılırken + * olaylardan ÖNCE uygulanır. */ export function kullaniciTanit( kullaniciId: string, @@ -181,15 +203,25 @@ export function kullaniciTanit( ): void { if (typeof window === "undefined") return; try { - window.rybbit?.identify(kullaniciId, traits); + if (!window.rybbit) { + if (kuyrukKapali) return; + bekleyenKimlik = [kullaniciId, traits]; + zamanlayiciyiBaslat(); + return; + } + // Doğrudan uygulanır; kuyrukta olay bekliyorsa sonraki boşaltmada bu + // kimlikle gider. Eski slot değeri geçersiz kalmasın diye silinir. + bekleyenKimlik = null; + window.rybbit.identify(kullaniciId, traits); } catch { // yut } } -/** Çıkışta kimliği temizler. */ +/** Çıkışta kimliği temizler (bekleyen kimlik slotu dahil). */ export function kullaniciTemizle(): void { if (typeof window === "undefined") return; + bekleyenKimlik = null; try { window.rybbit?.clearUserId(); } catch {