@nsupp/react-native-sdk
v0.1.0
Published
Expo Module bridge for the nsupp chat iOS and Android SDKs — native chat screen, identity, push.
Maintainers
Readme
@nsupp/react-native-sdk
nsupp destek sohbetini React Native / Expo uygulamanıza gömer. Sohbet arayüzü web widget'ının kendisidir: görünüm ve işlevin tamamı çalışma alanı ayarından, tek noktadan gelir.
Mimari: köprü + web widget'ının kendisi
Bu paket ne protokolü ne de arayüzü yeniden yazar. packages/ios-sdk (Swift) ve
packages/android-sdk (Kotlin) paketlerini saran ince bir Expo Module köprüsüdür; o paketler de
sohbeti bir WebView'da aynı widget.js ile gösterir — web sitenizdeki widget'ın ta kendisi.
Sonuç: ön-sohbet formu, Makaleler sekmesi, hamburger menüsü, powered-by, CSAT, kesinti bandı, hızlı yanıtlar ve dosya eki mobilde de birebir aynıdır, çünkü tek bir yerde yazılıdır. Panelde ayarı değiştirdiğinizde web ve mobil AYNI ANDA değişir.
Neden: saf-JS bir uygulama protokolün dördüncü kopyası olurdu (widget, iOS, Android + bu). Yakın
zamanda yapılan denetim tek bir kopyada bile dört ayrı hata buldu — yoklama imlecinin kayması,
open/mobile bayraklarının hiç gitmemesi, oturum jetonunun sorgu dizesine sızması ve "yeni konu"
sinyalinin hiçbir yere ulaşmaması. Bunları dördüncü kez yazmak, dördüncü kez üretmek demekti.
Sektör emsali de aynı yönde: bu alandaki yerleşik canlı-destek ürünlerinin resmi React Native paketleri de yerel SDK'ları saran Expo Module köprüleridir, JS yeniden uygulamaları değil.
Bedeli açıkça
Yerel kod içerdiği için Expo Go ile çalışmaz. expo prebuild + development build (ya da bare
workflow) gerekir. Yerel kod taşıyan her kütüphane için durum aynıdır.
Kurulum
npm install @nsupp/react-native-sdk
npx expo prebuildios/ tarafında CocoaPods otomatik bağlanır (expo-module.config.json); ek bir adım yok.
Yayın durumu: paket npm'e henüz yayınlanmadı (yayın anahtarları gerekiyor). Şu an depo içinden yol ile bağlanır. API yüzeyi her iki durumda da aynıdır — yalnız bağımlılık satırı değişecek.
Kullanım
import Nsupp from '@nsupp/react-native-sdk';
// Uygulamanın en üstünde, BİR KEZ:
Nsupp.configure('https://api.nsupp.com', 'pk_…');pk_… = panelde Settings → Setup & Integrations ekranındaki gömme kodunda geçen
data-public-key. Gizli değildir; tarayıcıda da açıkta durur. Çalışma alanında kayıtlı bir
uygulama varsa üçüncü argüman olarak uygulama anahtarı da gerekir (aşağıda).
Üç yol — hangisi olacağına siz karar verirsiniz
Üçü de aynı yüzeydir; nasıl göründüğü uygulamanızın kararıdır. Hiçbiri dayatılmaz.
Kip adları mobilde
balonveekran. Balon-olmayan kipin adı mobilde (iOS · Android · RN) ekrandır — tam ekran açılır. Masaüstünde (macOS · Electron) aynı yerde panel vardır ve o, köşede kayan bir penceredir. Ad platform sınıfına göre değişir çünkü davranış gerçekten farklıdır: telefonda "köşede kayan pencere" diye bir kip yok ve olmayacak, ekran buna elverişli değil. Aynı adı iki davranışa vermek yalan söylemek olurdu.balonbeş platformda da aynı anlamdadır.
① Köşede ikon (balon / launcher) — web'deki balonun karşılığı: ikon köşede durur, dokununca sohbet yüzeyi açılır/kapanır.
Nsupp.configure('https://api.nsupp.com', 'pk_…');
Nsupp.showLauncher(); // ekran açıkken, openChat()'ten ÖNCEİkon şeffaf tam-ekran bir katman değildir: yalnız kapladığı daire dokunma yakalar, geri kalan her yer sizin arayüzünüzdür ve tıklanabilir kalır. Panel açıkken de uygulamanız görünür durur.
② Sayfaya gömülü — sohbet kendi ekranınızın içinde yaşar (yan panel, sekme, "Destek" sayfası, bölünmüş görünümün yarısı):
import { NsuppChatView } from '@nsupp/react-native-sdk';
<NsuppChatView style={{ flex: 1 }} />③ Kendi düğmeniz + tam ekran (ekran kipi) — köşede hiçbir şey durmaz:
<Button title="Destek" onPress={() => Nsupp.openChat()} />Bilinmesi gerekenler:
- Kip bir kez seçilir.
showLauncher()ıconfigure()dan sonra, sohbet daha açılmadan çağırın: kip, yerel sunum kurulurken belirlenir. Sohbet AÇIKKEN çağırmak desteklenmiyor — ikonun o anda açık olan pencereyle ilişkisi platforma göre değişir, tek bir davranış söz veremeyiz.showLauncher()çağrıldıysaopenChat()de balonun kendi yüzeyini açar, ayrı bir tam ekran değil — aksi hâlde tek çağrı, uygulamanın kipine göre iki farklı yüzey gösterirdi. - Gömülü görünümün boyutunu siz verirsiniz. Yerel görünümün doğal boyutu yoktur;
flex: 1ya da açık bir yükseklik verin, yoksa 0 yükseklikte (görünmez) çizilir. - Görünüm panelden gelir.
<NsuppChatView />in renk/metin prop'u YOKTUR — olsaydı kabuk çalışma alanı ayarını geçersiz kılar ve platformlar birbirinden ayrışırdı. - Aynı anda tek yüzey. Gömülü görünümü iki yerde göstermeyin ve gömülüyken sohbeti ayrıca
açmayın. Kurallar:
reset()(çıkış) her yüzeye iner — hem gömülü görünüme hem hazır sunuma. Bu bir güvenlik sözüdür, "en son kurulan yüzey"e bırakılamaz: açık kalan bir panel, çıkış yapmış kullanıcının sohbetini göstermeye devam ederdi.- Bildirimden konuşma açma (
openChat(id)) en son kurulan yüzeye gider, öncekine gitmez. - Gömülü görünüm ile hazır sunum aynı anda canlıysa ikisi ayrı ziyaretçi oturumu açabilir (bölünmüş kimlik: aynı kişi sunucuda iki ziyaretçi). Yerel taraf bunu cihaz günlüğüne uyarı olarak yazar; sessizce bırakmıyoruz çünkü teşhis edilemeyen hata en pahalısıdır.
- Derin bağlantı
openChat(conversationId)ile verilir — gömülü görünümde de bu çağrı kullanılır. Modül düzeyindekiopenConversation()yerel oturumun imlecini oynatır, ekrandaki yüzeyi değiştirmez. - Gömülü görünüm yüklenemezse (ör.
configure()çağrılmadan yerleştirildiyse) sebebi cihaz günlüğüne yazar (logcat / Xcode konsolu). Bunu JS'e olay olarak taşımak için çekirdek SDK'ların gömülü görünümlerinde bir hata geri-çağrımı gerekiyor; şu an yok, bu yüzden söz de verilmiyor.
Kullanıcıyı tanıtma
// Kullanıcı GİRİŞ YAPTIĞI ANDA çağırın — sohbet ekranını beklemeyin.
Nsupp.identify('[email protected]', {
name: 'Ada Lovelace',
signature: imzaSunucudanGeldi, // HMAC-SHA256(email, identity_secret)
attributes: { plan: 'pro', segments: ['vip'] },
});Oturum henüz yoksa kimlik yerel tarafta bekletilir ve sohbet ilk açıldığında gönderilir. Kuyruk olmasaydı çağrı sessizce düşer, müşteri operatörde anonim görünür ve VIP/segment yönlendirmesi hiç çalışmazdı.
signature sunucunuzda üretilir. Uygulamaya gömülen bir sır doğrulamayı anlamsız kılar; JS
paketine gömülen sır ise ayrıca .js bundle'ından okunabilir.
Nsupp.setSessionData({ sonSiparis: '#1042' }); // önce identify gerekir
Nsupp.setSegments(['vip']); // attributes.segments'i DEĞİŞTİRİR
Nsupp.trackEvent('Checkout');
Nsupp.runTrigger('hosgeldin'); // mesaj tetikleyicisi / bot senaryosu
Nsupp.rate(5, 'çok hızlıydı'); // CSAT
Nsupp.startNewConversation(); // ayrı konu; öncekiler KAPANMAZ
Nsupp.openConversation(id); // oturum imleci; EKRANDAKİ yüzeyi değiştirmez
Nsupp.reset(); // ÇIKIŞTA çağırın
// Yardım merkezi (self-servis — sohbeti hiç açmadan çözülen sorular)
const makaleler = await Nsupp.loadArticles();
const sonuc = await Nsupp.searchArticles('kargo');
const makale = await Nsupp.getArticle('iade');Anlık bildirim
Panelde Settings → Push Notifications altına FCM/APNS kimlik bilgilerini girin (Mobile Push Notifications), sonra cihaz jetonunu bildirin:
// iOS: APNS jetonunun HEX metni · Android: FCM jetonu
Nsupp.registerPushToken(jeton);Bildirime dokunulduğunda ilgili konuşmayı açmak için:
Nsupp.openChat(bildirimYuku.conversationId);Bildirimin gösterimi uygulamanın işidir (kanal, ikon, ses, Android 13+ izin akışı). SDK bunları dayatmaz — dayatsaydı uygulamanızın bildirim düzenini bozardı.
Gizlilik bildirimi (PrivacyInfo.xcprivacy)
iOS tarafında pod kendi bildirimini TAŞIR — ios/PrivacyInfo.xcprivacy, podspec'te
resource_bundles ile bildirilir (NsuppSdkPrivacy.bundle). Beyan ettiği tek şey
NSPrivacyAccessedAPICategoryUserDefaults / CA92.1: ziyaretçi jetonunu UserDefaultsta
saklarız. İzleme yoktur (NSPrivacyTracking = false).
Bu olmasaydı sizin yüklemeniz ITMS-91053: Missing API declaration ile reddedilirdi.
Size düşen — toplanan veri türleri: e-posta/ad yalnız identify() çağırırsanız, destek
mesajları ve ekleri ziyaretçi gönderdiğinde, ziyaretçi jetonu her oturumda gider. Bunları kendi
App Store Connect gizlilik anketinizde ve Google Play Data safety formunuzda siz bildirirsiniz;
SDK sizin adınıza beyan edemez, çünkü ne göndereceğiniz sizin kullanımınıza bağlıdır.
Bilinmesi gerekenler
Çıkışta reset() çağırın. Ziyaretçi jetonu kimliğe değil cihaza bağlıdır; çağırmazsanız
paylaşılan bir cihazda sonraki kullanıcı öncekinin sohbet geçmişini açar.
setSessionData önce identify ister. Öznitelikler CRM'deki kişi kaydında yaşar ve kişi
e-posta ile doğar. Kimlik verilmemişse çağrı sessizce yutulmaz — yerel taraf hata durumuna yazar.
setSegments değiştirir, birleştirmez. İstemci mevcut segmentleri bilmez; birleştirme sözü
verseydik yalan olurdu.
Sohbet WebView'ı kendi origin'inde kalır. configure()a verdiğiniz adres bir origin
olmalıdır (https://api.nsupp.com, yol taşımadan): kabuk uygulama anahtarını ve ziyaretçi jetonunu
yalnız o origin'deki belgeye verir, başka bir adrese giden gezinme — bağlantı, JS yönlendirmesi,
form ya da sunucu 302'si — WebView'a yüklenmez. Ayrıştırılamayan bir adres verilirse sohbet
görünür şekilde açılmaz (sessizce her adrese izin vermek yerine).
Dış bağlantılarda şema süzgeci var. Sohbet/makale içeriğinden yalnız http, https, mailto
ve tel sistem tarayıcısına/uygulamasına devredilir; kendi myapp:// şemanızı sohbete koyarsanız
açılmaz (uzak içerik cihazdaki uygulamaların derin bağlantılarını tetikleyemesin diye).
Çalışma alanında kayıtlı bir uygulama varsa appKey ZORUNLUdur. Ölçüt alan adı kilidi
değil, KAYITtır. Alan adı kilidi bir tarayıcı kontrolüdür: web widget'ı Origin başlığı
gönderir, yerel uygulama göndermez — bu yüzden yerel yüzeyin kendi kimliği vardır. O kimlik bir
uygulama anahtarıdır: panelde Ayarlar → Uygulamalar'dan üretilir, bir kez gösterilir ve
configure()ın üçüncü argümanı olarak verilir:
Nsupp.configure('https://api.nsupp.com', 'pk_…', 'nsupp_app_…');- Panelden bir uygulama kaydettiğiniz andan itibaren, o çalışma alanına gelen anahtarsız yerel
istek
403 domain_lockedalır — alan adı kilidi kapalı olsa bile. Hiç kaydı olmayan çalışma alanları etkilenmez (geriye-uyum). - Kilit açık olması tek başına yetmez: kilidin dayanacağı bir alan adı yoksa (yalnız mobil uygulaması olan çalışma alanı) kilit hiç uygulanmaz ve anahtar da aranmaz. Kapının kayda bağlanmasının sebebi tam olarak budur — kilide bağlansaydı bu SDK'nın hedef müşterisi, anahtar üretip uygulamasına koyduktan sonra bile korumasız kalırdı.
- Kilit açık ve alan adı tanımlıysa yerel istek ayrıca o kilide de takılır (WebView'ın kendi origin'i izinli listede değildir), yani anahtar orada da gerekir.
- Anahtar sızarsa panelden döndürün; eskisi anında geçersizleşir.
- Eskiden muafiyetin dayanağı
x-nsupp-sdk-platformbaşlığıydı — o bir beyandı, curl da aynı başlığı yazabiliyordu. Artık dayanak doğrulanabilir: sunucu anahtarı kaydına çözer ve kaydın O çalışma alanına ait olmasını arar. - Dürüst sınır: anahtar uygulama paketinden çıkarılabilir (IPA/APK incelenebilir). Kişi-düzeyi
güvence
identifyimzasının işidir; bu anahtar onun yerine geçmez.
Geliştirme
npm run test:rn-sdk24 test koşar. Bu depoda Expo ve Android SDK'sı yok, yani ios/NsuppSdkModule.swift ile
android/.../NsuppSdkModule.kt burada derlenmez. Testler o boşluğun kapağıdır:
- TypeScript arayüzü ile Swift ve Kotlin köprülerinin fonksiyon adları birebir aynı mı
- Köprünün çağırdığı her yerel sembol (
Nsupp.x(),session.x()) çekirdekte gerçekten var mı - İsteğe bağlı argümanlar
undefineddeğilnullolarak geçiyor mu (Expo köprüsüundefinedı opsiyonel argüman saymaz; sayılmazsa çağrı sessizce düşer) - Gömülü görünüm sözleşmesi: istenen görünüm yöneticisinin adı = modül adı, iki yerel taraf da AYNI
ExpoViewsınıfını açıyor, TS'e eklenen her prop'un karşılığında yerelProp("…")var. (Görünüm sözleşmesi fonksiyonlardan daha sessiz kırılır: yanlış ad ya da eksikProphata bile vermez — bileşen boş çizilir, prop yok sayılır.) - Köşe ikonu ve gömülü görünüm çekirdeğin GERÇEK yüzeylerini kullanıyor mu (
NsuppWebChatView,NsuppChatPresenter(kip)) — kabuk kendi WebView'ını/balonunu kurarsa test kırılır reset()her yüzeye iniyor mu (oturum + gömülü görünüm + hazır sunum) ve gömülü görünüm ikinci bir denetleyici kurmuyor mu — bu testler YORUM-KÖRdür (kablolama sökülüp yalnız gerekçe yorumu kalırsa yeşil kalmazlar) ve iOS'un hizalandığı Android davranışını da ayrıca doğrularlar- README'deki uygulama-anahtarı iddiası gerçeğe uygun mu. Bu iddia İKİ KEZ yanlış yazıldı: önce
"kilit mobili etkilemez, başlıkla geçilir", sonra "kilit açıksa anahtar zorunlu". İkincisi de
yanlıştı ve ölçümle çürütüldü — kapı kilide değil KAYDA bağlı (
originAllowed,apps/server/src/widget-routes.ts). Test artık doğru ölçütü pinler; yanlış ölçütü yazan bir README'yi kırar
Derleyici yerine geçmezler (tip/imza denetimi yapmazlar) ama köprülerde en sık kırılan şeyi — ad
ayrışmasını — yakalarlar. Nitekim bu testler yazılırken gerçek bir hata bulundu: köprü
NsuppChatView(session:) çağırıyordu, o başlatıcı yoktu.
