fix(analitik): kullaniciTanit script'ten önce sessiz no-op olmasın

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 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-21 09:22:10 +03:00
parent 67be93c394
commit 643e41cc98

View File

@@ -74,6 +74,10 @@ const DENEME_ARALIGI_MS = 500;
const DENEME_SINIRI = 20; const DENEME_SINIRI = 20;
let kuyruk: Array<[OlayAdi, Ozellikler | undefined]> = []; 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<string, string> | undefined] | null = null;
let zamanlayici: ReturnType<typeof setInterval> | null = null; let zamanlayici: ReturnType<typeof setInterval> | null = null;
let denemeSayisi = 0; let denemeSayisi = 0;
let kuyrukKapali = false; let kuyrukKapali = false;
@@ -83,11 +87,21 @@ function zamanlayiciyiDurdur(): void {
zamanlayici = null; 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 { function kuyruguBosalt(rybbit: RybbitApi): void {
const bekleyenler = kuyruk; const bekleyenler = kuyruk;
const kimlik = bekleyenKimlik;
kuyruk = []; kuyruk = [];
bekleyenKimlik = null;
zamanlayiciyiDurdur(); zamanlayiciyiDurdur();
if (kimlik) {
try {
rybbit.identify(kimlik[0], kimlik[1]);
} catch {
// yut
}
}
for (const [ad, ozellikler] of bekleyenler) { for (const [ad, ozellikler] of bekleyenler) {
try { try {
rybbit.event(ad, ozellikler); rybbit.event(ad, ozellikler);
@@ -97,23 +111,29 @@ function kuyruguBosalt(rybbit: RybbitApi): void {
} }
} }
function kuyrugaEkle(ad: OlayAdi, ozellikler?: Ozellikler): void { /** Script'i bekleyen yoklamayı başlatır (olay kuyruğu ve kimlik slotu ortak). */
if (kuyrukKapali || kuyruk.length >= KUYRUK_SINIRI) return; function zamanlayiciyiBaslat(): void {
kuyruk.push([ad, ozellikler]);
if (zamanlayici !== null) return; if (zamanlayici !== null) return;
zamanlayici = setInterval(() => { zamanlayici = setInterval(() => {
denemeSayisi += 1; denemeSayisi += 1;
if (window.rybbit) { if (window.rybbit) {
kuyruguBosalt(window.rybbit); kuyruguBosalt(window.rybbit);
} else if (denemeSayisi >= DENEME_SINIRI) { } 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 = []; kuyruk = [];
bekleyenKimlik = null;
kuyrukKapali = true; kuyrukKapali = true;
zamanlayiciyiDurdur(); zamanlayiciyiDurdur();
} }
}, DENEME_ARALIGI_MS); }, 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. */ /** Özel olay gönderir. Script henüz yoksa kısa süre kuyrukta bekletir. */
export function olay(ad: OlayAdi, ozellikler?: Ozellikler): void { export function olay(ad: OlayAdi, ozellikler?: Ozellikler): void {
if (typeof window === "undefined") return; if (typeof window === "undefined") return;
@@ -122,8 +142,8 @@ export function olay(ad: OlayAdi, ozellikler?: Ozellikler): void {
kuyrugaEkle(ad, ozellikler); kuyrugaEkle(ad, ozellikler);
return; return;
} }
// Sıra bozulmasın: bekleyen varsa önce onlar gider // Sıra bozulmasın: bekleyen (kimlik ya da olay) varsa önce onlar gider
if (kuyruk.length > 0) kuyruguBosalt(window.rybbit); if (kuyruk.length > 0 || bekleyenKimlik) kuyruguBosalt(window.rybbit);
window.rybbit.event(ad, ozellikler); window.rybbit.event(ad, ozellikler);
} catch { } catch {
// Analitik hiçbir koşulda kullanıcı akışını bozmaz // 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 * 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 * 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). * 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( export function kullaniciTanit(
kullaniciId: string, kullaniciId: string,
@@ -181,15 +203,25 @@ export function kullaniciTanit(
): void { ): void {
if (typeof window === "undefined") return; if (typeof window === "undefined") return;
try { 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 { } catch {
// yut // yut
} }
} }
/** Çıkışta kimliği temizler. */ /** Çıkışta kimliği temizler (bekleyen kimlik slotu dahil). */
export function kullaniciTemizle(): void { export function kullaniciTemizle(): void {
if (typeof window === "undefined") return; if (typeof window === "undefined") return;
bekleyenKimlik = null;
try { try {
window.rybbit?.clearUserId(); window.rybbit?.clearUserId();
} catch { } catch {