"use client"; import { useCallback, useEffect, useRef, type RefCallback } from "react"; import { olayAnahtari, olayBirKez, type OlayAdi, type Ozellikler, } from "@/lib/analitik"; /** * Bir elemanın viewport'a ilk girişinde analitik olayı atan ref hook'u. * Gösterim olayları (hazir_panel_goruntulendi, kilit_goruntulendi vb.) * oturum başına en fazla 1 kez sayılmalı; tekilleştirme sessionStorage * anahtarıyla yapılır (bkz. olayBirKez). IntersectionObserver yoksa olay hiç * atılmaz — "görüntülendi" ölçümünde görünürlüğü doğrulayamadığımız cihazı * saymak veriyi şişirir. * * Callback ref döner (RefObject değil): gözlem effect'te değil, DOM düğümü * bağlandığı anda kurulur. Eleman ilk commit'te yoksa (kaydet-banner: mount * sonrası açılır, üstelik portal içinde) ya da sökülüp yeni bir düğümle geri * geliyorsa (tadımlık satırı: tip uymayınca gizlenip fetch sonrası yeniden * takılır) useRef + effect deseni gözlemi hiç kurmuyordu. */ export function useGorunumOlayi( ad: OlayAdi, ozellikler?: Ozellikler, /** Aynı olayın farklı yüzeyleri için tekilleştirme eki ("satir", "kart"). */ anahtarEki?: string, ): RefCallback { // Özellikler her render sonrası tazelenir; callback ref yalnızca ad/anahtar // değişiminde yeniden kurulur (deps'e obje koymamak için ref). const ozelliklerRef = useRef(ozellikler); useEffect(() => { ozelliklerRef.current = ozellikler; }); return useCallback( (el: T | null) => { if (!el || typeof IntersectionObserver === "undefined") return; try { if (window.sessionStorage.getItem(olayAnahtari(ad, anahtarEki))) return; } catch { // Storage engelliyse tekilleştirmeden vazgeç, olay yine atılabilir } const io = new IntersectionObserver((entries) => { if (!entries.some((e) => e.isIntersecting)) return; io.disconnect(); olayBirKez(ad, ozelliklerRef.current, anahtarEki); }); io.observe(el); // React 19 ref temizliği: düğüm sökülünce (ya da ad/anahtar değişince) // gözlemci bırakılır; yeni düğüm bağlanınca callback baştan çalışır. return () => io.disconnect(); }, [ad, anahtarEki], ); }