Ana içeriğe geç
Ahmet Faruk UzunkayaYaratıcı geliştirici
← Projelere dön
MobilTamamlandıGüncellendi · 5 Ekim 2026

Dialoq

Mesleğine göre biriyle eşleşip 45 saniyelik bir sesli sohbete girdiğin React Native uygulaması.

Meslek filtresiyle biriyle eşleşiyorsun; ikiniz de 10 saniye içinde onay verirseniz WebRTC üzerinden 45 saniyelik bir sesli görüşme başlıyor. Sohbet iyi giderse süreyi uzatıp arkadaş olabiliyorsunuz. Bir de kürsüsü ve dinleyicileri olan canlı ses odaları var.

React NativeExpoWebRTCSocket.IONode.js
Dialoq ekran görüntüsü
Durum
Tamamlandı
Kategori
Mobil
Güncelleme
5 Ekim 2026
Yığın
React NativeExpoWebRTCSocket.IONode.js
Dialoq

Dialoq, farklı mesleklerden insanları kısa sesli görüşmelerde karşılaştıran bir uygulama. Açınca bir meslek filtresi seçip kuyruğa giriyorsun. Karşı tarafın filtresi de seninkiyle uyuşunca ikinize aynı anda bir eşleşme önerisi geliyor; ikiniz de 10 saniye içinde onaylarsanız 45 saniyelik bir sesli görüşme başlıyor. Süre yetmezse görüşmeyi 30 saniye uzatabiliyorsunuz, ikiniz de isterseniz sonunda arkadaş da oluyorsunuz. Reddettiğin biriyle de kısa bir süre tekrar eşleşmiyorsun.

Bire bir görüşmelerin yanında canlı ses odaları da var. Odada 8 kişilik bir kürsü ve dinleyiciler oluyor; konuşmak isteyen el kaldırıyor, moderatör de onaylıyor. Uygulamayı Expo SDK 56 ve React Native 0.85 ile TypeScript'te yazdım; aynı kod tarayıcıda da çalışıyor. Sunucu tarafında Node.js üzerinde Express ve Socket.IO var.

Telefonda WebRTC: görünmez bir tarayıcı

React Native'de yerleşik WebRTC yok. Bu yüzden ses motorunu görünmez bir WebView'un içine koydum: AudioBridge bileşeni orada tarayıcının kendi RTCPeerConnection'ını ve mikrofon erişimini kullanıyor, uygulamayla da mesajlaşarak haberleşiyor. Web sürümünde aynı sayfa gizli bir iframe içinde çalışıyor. Bire bir görüşme ve ses odası için iki ayrı WebRTC sayfası yazdım. Sayfa henüz hazır değilken gelen sinyaller kaybolmasın diye bir kuyrukta bekliyor.

Sesin kendisi sunucudan geçmiyor. Sunucu eşleştirmeyi ve odaları yönetiyor, WebRTC teklif, yanıt ve ICE sinyallerini de yalnızca ilgili kişiye iletiyor. Ses telefonlar arasında doğrudan, STUN ve isteğe bağlı bir TURN sunucusu üzerinden akıyor. Sunucu bütün durumu bellekte tutuyor. İstemci tarafında ise socket bağlantısını, eşleşme, görüşme ve oda durumunu ve cihazda saklanan profili (rumuz, meslek, avatar, kısa bio) tek bir AppContext'te topladım.

Eşleşmeden görüşmeye

Akışın tamamı birkaç Socket.IO olayından ibaret. İki kullanıcı joinQueue ile profilini ve meslek filtresini gönderiyor; filtreler uyuşunca sunucu ikisine de matchProposed yolluyor ve geri sayım başlıyor. Sen onayladığında bu, karşı tarafın ekranında anında görünüyor.

Kullanıcı A                  Sunucu                   Kullanıcı B
│                               │                               │
│────────── joinQueue ─────────►│◄───────── joinQueue ──────────│
│◄─── matchProposed (10 sn) ────│──── matchProposed (10 sn) ───►│
│──────── approveMatch ────────►│                               │
│                               │─────── partnerApproved ──────►│
│                               │◄─────── approveMatch ─────────│
│◄─────── matchApproved ────────│──────── matchApproved ───────►│
│─────── WebRTC offer / answer / ICE (sunucu üzerinden) ───────►│
│◄══════════════════════════ P2P ses ══════════════════════════►│

matchApproved görüşmenin oda kimliğini ve teklifi hangi tarafın başlatacağını taşıyor. Süre dolarsa öneri timeout ile düşüyor; biri reddederse ikisine de haber veriliyor. Görüşmenin 45 saniyesi, ses gerçekten bağlandığı anda işlemeye başlıyor. 25 saniye içinde bağlantı kurulamazsa da sana nedenini anlatan bir mesaj çıkıyor. Görüşme sırasında mikrofonunu ve hoparlörünü yönetebilir, karşı tarafın kendini sessize alıp almadığını görebilirsin.

Arkadaşlık isteğini de sunucuda doğruluyorum: iki taraf da isteyince iki telefona aynı anda friendRequestAccepted gidiyor. Bu yüzden kimin önce bastığı, hatta ikisinin aynı anda basması bile sonucu değiştirmiyor. Arkadaş listesi cihazında duruyor; bir kişiyi basılı tutarak silebilirsin.

Kürsü, dinleyici ve moderatör

Ses odasına girdiğinde dinleyici olarak başlıyorsun; mikrofonun yalnızca kürsüye çıkınca açılıyor. Kürsü boşsa ilk isteyen doğrudan çıkıp moderatör oluyor. Boş değilse el kaldırıp moderatörün "Kürsüye Al" onayını bekliyorsun. Moderatör ayrılırsa sıradaki konuşmacı onun yerine geçiyor. Kullanıcıların açtığı odalar boşalınca kapanıyor, hazır odalar ise hep açık kalıyor.

Odadaki ses de sunucuya uğramıyor. Konuşmacılar arasında tam örgü (mesh) kurdum; her dinleyici ise her konuşmacıya yalnızca alıcı bir bağlantı açıyor. İki tarafın aynı anda teklif gönderip çakışmasını önlemek için teklifi kimin göndereceğini her çift için sabitledim: dinleyiciyle konuşmacı arasında dinleyici, iki konuşmacı arasında da socket kimliği büyük olan taraf. Biri kürsüye çıktığında ya da indiğinde bağlantı kapatılıp yeni bir oturum kimliğiyle yeniden kuruluyor ve eski oturumdan gelen sinyaller yok sayılıyor. Kopan bağlantılar da kendiliğinden yenileniyor.

Bozuk mesajlar, kopan bağlantılar

Sunucuya gelen her mesajı doğruluyorum, bu yüzden bozuk ya da kötü niyetli bir mesaj sunucuyu çökertemiyor. Profil alanları da kırpılıyor ve uzunlukları sınırlanıyor. Görüşme ve oda olaylarını yalnızca o görüşmenin ya da odanın üyelerinden kabul ediyorum; yani dışarıdan biri bir görüşmeyi bitiremiyor, süresini uzatamıyor ya da sinyallerini dinleyemiyor. Kimlikleri crypto.randomUUID() ile ürettiğim için aynı milisaniyede oluşan eşleşmeler bile çakışmıyor.

Bağlantın koparsa istemci görüşmeyi düzgünce kapatıyor; yeniden bağlandığında da seni kuyruk aramasına ya da odaya kendiliğinden geri götürüyor. Android'in geri tuşu dahil ekrandan her çıkış, görüşmeyi ya da oda üyeliğini sunucu tarafında da sonlandırıyor.

Eksikleri de var tabii. Sunucu bellekte çalıştığı için yeniden başlarsa kuyruk, görüşmeler ve kullanıcı odaları sıfırlanıyor. Hesap sistemi yok; profil ve arkadaş listesi yalnızca cihazda duruyor, arkadaşlar arasında mesajlaşma da henüz yok. TURN varsayılan olarak tanımlı gelmediği için bazı NAT arkasındaki ağlarda ses kurulamayabiliyor. Mesh yapısı yüzünden kürsü 8 konuşmacıyla sınırlı; çok daha büyük odalar için LiveKit ya da mediasoup gibi bir SFU gerekecek. Hız sınırlama, raporlama ve engelleme de henüz yok.