signalbird 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -33
- package/dist/angular.d.mts +4 -4
- package/dist/angular.d.ts +4 -4
- package/dist/angular.js +56 -13
- package/dist/angular.js.map +1 -1
- package/dist/angular.mjs +56 -13
- package/dist/angular.mjs.map +1 -1
- package/dist/app.d.mts +37 -47
- package/dist/app.d.ts +37 -47
- package/dist/app.js +55 -12
- package/dist/app.js.map +1 -1
- package/dist/app.mjs +55 -12
- package/dist/app.mjs.map +1 -1
- package/dist/browser.d.mts +1 -13
- package/dist/browser.d.ts +1 -13
- package/dist/browser.js +25 -3
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +25 -3
- package/dist/browser.mjs.map +1 -1
- package/dist/embed.d.mts +6 -6
- package/dist/embed.d.ts +6 -6
- package/dist/embed.js +1 -1
- package/dist/embed.js.map +1 -1
- package/dist/embed.mjs +1 -1
- package/dist/embed.mjs.map +1 -1
- package/dist/index.d.mts +50 -50
- package/dist/index.d.ts +50 -50
- package/dist/index.js +48 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +48 -22
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +2 -2
- package/dist/react-native.d.ts +2 -2
- package/dist/react-native.js +55 -12
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +55 -12
- package/dist/react-native.mjs.map +1 -1
- package/dist/react.d.mts +4 -4
- package/dist/react.d.ts +4 -4
- package/dist/react.js +55 -12
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +55 -12
- package/dist/react.mjs.map +1 -1
- package/dist/signalbird.js +20 -20
- package/dist/vue.d.mts +3 -3
- package/dist/vue.d.ts +3 -3
- package/dist/vue.js +55 -12
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +55 -12
- package/dist/vue.mjs.map +1 -1
- package/package.json +2 -2
package/dist/app.d.mts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Bu yüzey MÜŞTERİNİN MÜŞTERİSİ içindir: ziyaretçi ya da uygulama kullanıcısı.
|
|
5
5
|
* Anahtarı açıktır (`sb_public_live_…`) ve istemciye gömülür; güvenliği gizlilikten
|
|
6
|
-
* değil kısıttan gelir
|
|
6
|
+
* değil kısıttan gelir - yalnız izinli kökenden çalışır ve yalnız ziyaretçinin
|
|
7
7
|
* KENDİ verisine dokunur.
|
|
8
8
|
*/
|
|
9
9
|
interface SbResult<T = unknown> {
|
|
@@ -33,21 +33,21 @@ interface AppConfig {
|
|
|
33
33
|
*/
|
|
34
34
|
platform?: 'ios' | 'android' | 'web';
|
|
35
35
|
/**
|
|
36
|
-
* Açık domain anahtarı (`sb_public_live_…`)
|
|
36
|
+
* Açık domain anahtarı (`sb_public_live_…`) - KİMLİĞİ doğrular.
|
|
37
37
|
*
|
|
38
38
|
* Panel → Alan adları → [alan adı] → Anahtarlar. Web anahtarı yalnız izinli
|
|
39
39
|
* kökenlerden, mobil anahtarı yalnız Origin taşımayan isteklerden çalışır.
|
|
40
40
|
*/
|
|
41
41
|
publicKey: string;
|
|
42
42
|
/**
|
|
43
|
-
* Sohbet kanalı
|
|
43
|
+
* Sohbet kanalı - `chat` modülünün anahtarı (`destek`). DAVRANIŞI seçer:
|
|
44
44
|
* hangi widget ayarı, hangi gelen kutusu.
|
|
45
45
|
*
|
|
46
46
|
* Anahtardan AYRIDIR ve bu bilinçlidir (1 Eyl 2026): domain anahtarını
|
|
47
47
|
* yenilediğinizde kanal adı değişmez, yani kodunuz aynı kalır.
|
|
48
48
|
*/
|
|
49
49
|
chatKey?: string;
|
|
50
|
-
/** Push kanalı
|
|
50
|
+
/** Push kanalı - `push` modülünün anahtarı. Cihaz kaydı için gerekir. */
|
|
51
51
|
pushKey?: string;
|
|
52
52
|
/** Varsayılan: https://live.signalbird.io/api */
|
|
53
53
|
baseUrl?: string;
|
|
@@ -73,7 +73,7 @@ interface TopicOption {
|
|
|
73
73
|
description?: string | null;
|
|
74
74
|
parent_id?: number | null;
|
|
75
75
|
}
|
|
76
|
-
/** `POST /v1/sdk/bootstrap` yanıtı
|
|
76
|
+
/** `POST /v1/sdk/bootstrap` yanıtı - widget çizilmeden önceki tek soru. */
|
|
77
77
|
interface BootstrapResult {
|
|
78
78
|
/**
|
|
79
79
|
* Sözleşme adı `channel` (1 Eyl 2026); `app` eski sunucular için okunur.
|
|
@@ -85,10 +85,10 @@ interface BootstrapResult {
|
|
|
85
85
|
app?: BootstrapChannel;
|
|
86
86
|
/** Boşsa konu adımı hiç gösterilmez. */
|
|
87
87
|
topics?: TopicOption[];
|
|
88
|
-
/** Mevcut açık konuşma (varsa)
|
|
88
|
+
/** Mevcut açık konuşma (varsa) - ChatSession ilk listelemeyi atlar. */
|
|
89
89
|
conversation?: Conversation | null;
|
|
90
90
|
/**
|
|
91
|
-
* Canlı bağlantı
|
|
91
|
+
* Canlı bağlantı - YALNIZ ADRES. Anahtar ya da sır taşımaz: bağlanan taraf
|
|
92
92
|
* hiçbir şey göremez, odaya girmek imza ister ve imzayı
|
|
93
93
|
* `POST /v1/sdk/chat/socket/auth` verir.
|
|
94
94
|
*
|
|
@@ -139,7 +139,7 @@ interface BootstrapChannel {
|
|
|
139
139
|
}
|
|
140
140
|
interface Visitor {
|
|
141
141
|
id: string;
|
|
142
|
-
/** Ziyaretçi sırrı
|
|
142
|
+
/** Ziyaretçi sırrı - YALNIZ oturum açılışında döner, sonra saklanır. */
|
|
143
143
|
secret?: string;
|
|
144
144
|
name?: string | null;
|
|
145
145
|
email?: string | null;
|
|
@@ -196,7 +196,7 @@ interface Message {
|
|
|
196
196
|
edited_at?: string | null;
|
|
197
197
|
created_at?: string;
|
|
198
198
|
/**
|
|
199
|
-
* Anlık çeviri
|
|
199
|
+
* Anlık çeviri - ziyaretçinin diline. Arayüz varsa bunu gösterir; orijinal
|
|
200
200
|
* `body`'de durmaya devam eder.
|
|
201
201
|
*/
|
|
202
202
|
translation?: {
|
|
@@ -211,7 +211,7 @@ interface Message {
|
|
|
211
211
|
*
|
|
212
212
|
* Tip listede yoktu ama oturum bu alanı yazıyordu: arayüz "gitmedi" hâlini
|
|
213
213
|
* çizmek istediğinde tipi zorlamak (`as`) zorunda kalıyordu. Bekleyen mesaj
|
|
214
|
-
* ayrıca işaretlenmez
|
|
214
|
+
* ayrıca işaretlenmez - sunucu yanıtı gelene kadar `id` ile `client_id`
|
|
215
215
|
* aynıdır ve bu, "henüz yolda" demenin en ucuz yoludur.
|
|
216
216
|
*/
|
|
217
217
|
failed?: boolean;
|
|
@@ -240,7 +240,7 @@ interface StartConversationInput {
|
|
|
240
240
|
attachments?: unknown[];
|
|
241
241
|
page_url?: string;
|
|
242
242
|
/**
|
|
243
|
-
* Destek konusu
|
|
243
|
+
* Destek konusu - id ya da slug. Seçim SUNUCUDA doğrulanır; geçersizse
|
|
244
244
|
* konuşma yine açılır, konu yok sayılır.
|
|
245
245
|
*/
|
|
246
246
|
topic?: string | number;
|
|
@@ -252,7 +252,7 @@ interface SendMessageInput {
|
|
|
252
252
|
attachments?: unknown[];
|
|
253
253
|
}
|
|
254
254
|
interface ConversationQuery {
|
|
255
|
-
/** `cm_…` imleci
|
|
255
|
+
/** `cm_…` imleci - yalnız bundan sonrakiler döner. */
|
|
256
256
|
after?: string;
|
|
257
257
|
limit?: number;
|
|
258
258
|
}
|
|
@@ -267,26 +267,6 @@ interface RegisterDeviceInput {
|
|
|
267
267
|
locale?: string;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
-
/**
|
|
271
|
-
* Uygulama istemcisi — son kullanıcı tarafı (sohbet + push kaydı).
|
|
272
|
-
*
|
|
273
|
-
* Tek bir sınıf; tarayıcı, React Native, Electron ve test aynı gövdeyi kullanır.
|
|
274
|
-
* Platform farkı iki noktada toplanmıştır ve ikisi de dışarıdan verilir:
|
|
275
|
-
* `storage` (ziyaretçi sırrı nerede durur) ve `fetchImpl`. Çatıya özel sarmalayıcı
|
|
276
|
-
* yazmak yerine bunu seçtik — React, Vue, Angular ve RN uyarlamaları bu sınıfın
|
|
277
|
-
* ÜSTÜNE oturur, kopyası değildir.
|
|
278
|
-
*
|
|
279
|
-
* Kimlik iki parçadır: açık domain anahtarı (`X-Signalbird-Key`) ve
|
|
280
|
-
* ziyaretçi sırrı (`X-Signalbird-Visitor`). Sır yalnız oturum açılışında döner;
|
|
281
|
-
* kaybolursa yeni oturum açılır ve geçmiş konuşmalar görünmez — bu yüzden
|
|
282
|
-
* saklama katmanı zorunludur, isteğe bağlı değil.
|
|
283
|
-
*
|
|
284
|
-
* Hiçbir metot istisna fırlatmaz: sohbet balonunun hatası müşterinin ödeme
|
|
285
|
-
* sayfasını çökertmemeli. Sonuç her zaman `{ok, status, …}` zarfıdır.
|
|
286
|
-
*
|
|
287
|
-
* Sözleşme: docs/CONTRACT.md § 11
|
|
288
|
-
*/
|
|
289
|
-
|
|
290
270
|
/** RFC 4122 uyumlu olmak zorunda değil; tek işi yerel kopyayı eşlemek. */
|
|
291
271
|
declare function clientId(): string;
|
|
292
272
|
declare class SignalbirdApp {
|
|
@@ -324,7 +304,7 @@ declare class SignalbirdApp {
|
|
|
324
304
|
identify(input: IdentifyInput): Promise<SbResult<{
|
|
325
305
|
visitor: Visitor;
|
|
326
306
|
}>>;
|
|
327
|
-
/** Saklanan ziyaretçi kimliği
|
|
307
|
+
/** Saklanan ziyaretçi kimliği - yoksa `null`. */
|
|
328
308
|
currentVisitor(): Promise<{
|
|
329
309
|
id: string;
|
|
330
310
|
name?: string | null;
|
|
@@ -343,7 +323,7 @@ declare class SignalbirdApp {
|
|
|
343
323
|
within_hours?: boolean;
|
|
344
324
|
}>>;
|
|
345
325
|
/**
|
|
346
|
-
* İlk mesajla konuşma açar. Kota burada harcanır
|
|
326
|
+
* İlk mesajla konuşma açar. Kota burada harcanır - konuşma başına sayılır,
|
|
347
327
|
* mesaj başına değil.
|
|
348
328
|
*/
|
|
349
329
|
startConversation(input: StartConversationInput): Promise<SbResult<{
|
|
@@ -378,21 +358,21 @@ declare class SignalbirdApp {
|
|
|
378
358
|
rateConversation(conversationId: string, rating: number, comment?: string): Promise<SbResult<unknown>>;
|
|
379
359
|
/**
|
|
380
360
|
* Cihaz token'ını kaydeder. Token'ı almak (FCM/APNs/Web Push izni) ev
|
|
381
|
-
* sahibinin işidir; SDK yalnız iletir
|
|
361
|
+
* sahibinin işidir; SDK yalnız iletir - izin diyaloğunu kimin, ne zaman
|
|
382
362
|
* göstereceği ürün kararıdır, kütüphane kararı değil.
|
|
383
363
|
*/
|
|
384
364
|
registerDevice(input: RegisterDeviceInput): Promise<SbResult<unknown>>;
|
|
385
365
|
/** Çıkışta çağrılır: kayıt silinmez, kapatılır (geçmiş korunur). */
|
|
386
366
|
unregisterDevice(token: string): Promise<SbResult<unknown>>;
|
|
387
367
|
/**
|
|
388
|
-
* Bildirime dokunuldu
|
|
368
|
+
* Bildirime dokunuldu - açılma damgası.
|
|
389
369
|
*
|
|
390
370
|
* Push'ta açılmayı YALNIZCA uygulama bilir: FCM/APNs "teslim ettim" der,
|
|
391
371
|
* "kullanıcı dokundu" demez. Bildirim yükündeki `data.sb_message_id`
|
|
392
372
|
* değerini buraya geri gönderin.
|
|
393
373
|
*
|
|
394
374
|
* ```ts
|
|
395
|
-
* // React Native / Expo
|
|
375
|
+
* // React Native / Expo - bildirime dokunma işleyicisinde
|
|
396
376
|
* const id = response.notification.request.content.data?.sb_message_id
|
|
397
377
|
* if (id) await sb.reportPushOpened(String(id))
|
|
398
378
|
* ```
|
|
@@ -407,12 +387,12 @@ declare class SignalbirdApp {
|
|
|
407
387
|
}
|
|
408
388
|
|
|
409
389
|
/**
|
|
410
|
-
* Sohbet oturumu
|
|
390
|
+
* Sohbet oturumu - çatısız durum yönetimi.
|
|
411
391
|
*
|
|
412
392
|
* `SignalbirdApp` ham uçları verir; burası bir sohbet ekranının gerçekten
|
|
413
393
|
* ihtiyaç duyduğu şeyi verir: mesaj listesi, okunmamış sayısı, yazıyor durumu,
|
|
414
394
|
* iyimser gönderim ve yoklama merdiveni. React/Vue/Angular/React Native
|
|
415
|
-
* uyarlamaları bu sınıfa abone olur
|
|
395
|
+
* uyarlamaları bu sınıfa abone olur - üçünde de aynı mantığı yeniden yazmak,
|
|
416
396
|
* üç ayrı hata takımı üretmek demekti.
|
|
417
397
|
*
|
|
418
398
|
* ── CANLI BAĞLANTI + YOKLAMA ──────────────────────────────────────────────
|
|
@@ -450,9 +430,9 @@ interface ChatState {
|
|
|
450
430
|
* Seçimi `setTopic()` taşır; ilk konuşma açılırken gönderilir.
|
|
451
431
|
*/
|
|
452
432
|
topics: TopicOption[];
|
|
453
|
-
/** Seçili konu (slug)
|
|
433
|
+
/** Seçili konu (slug) - ilk konuşmayla birlikte gider. */
|
|
454
434
|
topic: string | null;
|
|
455
|
-
/** Son hatanın kodu
|
|
435
|
+
/** Son hatanın kodu - arayüz isterse gösterir, göstermezse yutar. */
|
|
456
436
|
errorCode?: string;
|
|
457
437
|
/**
|
|
458
438
|
* Uygulamanın sohbet ayarları (renk, logo, tema, puanlama bağlantısı…).
|
|
@@ -464,7 +444,7 @@ interface ChatState {
|
|
|
464
444
|
}
|
|
465
445
|
type ChatListener = (state: ChatState) => void;
|
|
466
446
|
interface ChatSessionOptions {
|
|
467
|
-
/** Panel açık mı
|
|
447
|
+
/** Panel açık mı - yoklama hızını belirler. */
|
|
468
448
|
active?: boolean;
|
|
469
449
|
/** Arka plandayken tur atlanır; varsayılan: `document.visibilityState`. */
|
|
470
450
|
isVisible?: () => boolean;
|
|
@@ -491,22 +471,24 @@ declare class ChatSession {
|
|
|
491
471
|
* da düzenleme ekrana hiç yansımaz.
|
|
492
472
|
*/
|
|
493
473
|
private forceFull;
|
|
474
|
+
/** Son gönderilen `typing(true)` zamanı; 0 = açık sinyal yok. */
|
|
475
|
+
private lastTypingSent;
|
|
494
476
|
constructor(app: SignalbirdApp, options?: ChatSessionOptions);
|
|
495
477
|
subscribe(listener: ChatListener): () => void;
|
|
496
478
|
snapshot(): ChatState;
|
|
497
479
|
/** Bootstrap + varsa mevcut konuşmayı yükler, sonra yoklamayı başlatır. */
|
|
498
480
|
start(): Promise<void>;
|
|
499
|
-
/** Panel açıldı/kapandı
|
|
481
|
+
/** Panel açıldı/kapandı - yoklama hızı buna göre değişir. */
|
|
500
482
|
setActive(active: boolean): void;
|
|
501
483
|
/**
|
|
502
484
|
* Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar.
|
|
503
485
|
*
|
|
504
486
|
* Ekranın "yeni sohbet" düğmesi de bunu çağırır. Sunucuda hiçbir şey
|
|
505
|
-
* silinmez
|
|
487
|
+
* silinmez - yalnız bu oturumun neye baktığı değişir.
|
|
506
488
|
*/
|
|
507
489
|
reset(): void;
|
|
508
490
|
stop(): void;
|
|
509
|
-
/** Canlı bağlantı kurulu mu
|
|
491
|
+
/** Canlı bağlantı kurulu mu - arayüz isterse gösterir (zorunlu değil). */
|
|
510
492
|
get isLive(): boolean;
|
|
511
493
|
/** Ön-form gönderildiğinde ya da uygulama kullanıcıyı tanıdığında. */
|
|
512
494
|
openSession(input: SessionInput): Promise<SbResult<unknown>>;
|
|
@@ -520,11 +502,19 @@ declare class ChatSession {
|
|
|
520
502
|
send(body: string, attachments?: unknown[]): Promise<SbResult<unknown>>;
|
|
521
503
|
/**
|
|
522
504
|
* Ziyaretçinin konu seçimi. Konuşma AÇILDIKTAN sonra çağrılırsa etkisizdir:
|
|
523
|
-
* açılmış konuşmanın konusunu ajan panelden değiştirir
|
|
505
|
+
* açılmış konuşmanın konusunu ajan panelden değiştirir - ziyaretçiye kendi
|
|
524
506
|
* konuşmasını yeniden sınıflandırma yetkisi vermek, atamayı da bozardı.
|
|
525
507
|
*/
|
|
526
508
|
setTopic(slug: string | null): void;
|
|
527
|
-
/**
|
|
509
|
+
/**
|
|
510
|
+
* İlk tuşta `true`, 2.5 s hareketsizlikte `false` - çağıran zamanlar.
|
|
511
|
+
*
|
|
512
|
+
* SDK yine de kendini korur (18 Eyl 2026): `true` 4 saniyede bir kez gider,
|
|
513
|
+
* `false` yalnız açık bir `true` varsa. Bir entegrasyon her tuşta
|
|
514
|
+
* çağırdığında (Penyu mobil öyle yapıyordu) 40 harflik mesaj 40 istek
|
|
515
|
+
* oluyor ve sunucunun hız sınırı ikinci mesajı 429 ile düşürüyordu. Web
|
|
516
|
+
* widget'ındaki kuralın aynısı.
|
|
517
|
+
*/
|
|
528
518
|
typing(isTyping: boolean): void;
|
|
529
519
|
/** Görülen son mesaja kadar okundu işaretler. */
|
|
530
520
|
markRead(): Promise<void>;
|
package/dist/app.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Bu yüzey MÜŞTERİNİN MÜŞTERİSİ içindir: ziyaretçi ya da uygulama kullanıcısı.
|
|
5
5
|
* Anahtarı açıktır (`sb_public_live_…`) ve istemciye gömülür; güvenliği gizlilikten
|
|
6
|
-
* değil kısıttan gelir
|
|
6
|
+
* değil kısıttan gelir - yalnız izinli kökenden çalışır ve yalnız ziyaretçinin
|
|
7
7
|
* KENDİ verisine dokunur.
|
|
8
8
|
*/
|
|
9
9
|
interface SbResult<T = unknown> {
|
|
@@ -33,21 +33,21 @@ interface AppConfig {
|
|
|
33
33
|
*/
|
|
34
34
|
platform?: 'ios' | 'android' | 'web';
|
|
35
35
|
/**
|
|
36
|
-
* Açık domain anahtarı (`sb_public_live_…`)
|
|
36
|
+
* Açık domain anahtarı (`sb_public_live_…`) - KİMLİĞİ doğrular.
|
|
37
37
|
*
|
|
38
38
|
* Panel → Alan adları → [alan adı] → Anahtarlar. Web anahtarı yalnız izinli
|
|
39
39
|
* kökenlerden, mobil anahtarı yalnız Origin taşımayan isteklerden çalışır.
|
|
40
40
|
*/
|
|
41
41
|
publicKey: string;
|
|
42
42
|
/**
|
|
43
|
-
* Sohbet kanalı
|
|
43
|
+
* Sohbet kanalı - `chat` modülünün anahtarı (`destek`). DAVRANIŞI seçer:
|
|
44
44
|
* hangi widget ayarı, hangi gelen kutusu.
|
|
45
45
|
*
|
|
46
46
|
* Anahtardan AYRIDIR ve bu bilinçlidir (1 Eyl 2026): domain anahtarını
|
|
47
47
|
* yenilediğinizde kanal adı değişmez, yani kodunuz aynı kalır.
|
|
48
48
|
*/
|
|
49
49
|
chatKey?: string;
|
|
50
|
-
/** Push kanalı
|
|
50
|
+
/** Push kanalı - `push` modülünün anahtarı. Cihaz kaydı için gerekir. */
|
|
51
51
|
pushKey?: string;
|
|
52
52
|
/** Varsayılan: https://live.signalbird.io/api */
|
|
53
53
|
baseUrl?: string;
|
|
@@ -73,7 +73,7 @@ interface TopicOption {
|
|
|
73
73
|
description?: string | null;
|
|
74
74
|
parent_id?: number | null;
|
|
75
75
|
}
|
|
76
|
-
/** `POST /v1/sdk/bootstrap` yanıtı
|
|
76
|
+
/** `POST /v1/sdk/bootstrap` yanıtı - widget çizilmeden önceki tek soru. */
|
|
77
77
|
interface BootstrapResult {
|
|
78
78
|
/**
|
|
79
79
|
* Sözleşme adı `channel` (1 Eyl 2026); `app` eski sunucular için okunur.
|
|
@@ -85,10 +85,10 @@ interface BootstrapResult {
|
|
|
85
85
|
app?: BootstrapChannel;
|
|
86
86
|
/** Boşsa konu adımı hiç gösterilmez. */
|
|
87
87
|
topics?: TopicOption[];
|
|
88
|
-
/** Mevcut açık konuşma (varsa)
|
|
88
|
+
/** Mevcut açık konuşma (varsa) - ChatSession ilk listelemeyi atlar. */
|
|
89
89
|
conversation?: Conversation | null;
|
|
90
90
|
/**
|
|
91
|
-
* Canlı bağlantı
|
|
91
|
+
* Canlı bağlantı - YALNIZ ADRES. Anahtar ya da sır taşımaz: bağlanan taraf
|
|
92
92
|
* hiçbir şey göremez, odaya girmek imza ister ve imzayı
|
|
93
93
|
* `POST /v1/sdk/chat/socket/auth` verir.
|
|
94
94
|
*
|
|
@@ -139,7 +139,7 @@ interface BootstrapChannel {
|
|
|
139
139
|
}
|
|
140
140
|
interface Visitor {
|
|
141
141
|
id: string;
|
|
142
|
-
/** Ziyaretçi sırrı
|
|
142
|
+
/** Ziyaretçi sırrı - YALNIZ oturum açılışında döner, sonra saklanır. */
|
|
143
143
|
secret?: string;
|
|
144
144
|
name?: string | null;
|
|
145
145
|
email?: string | null;
|
|
@@ -196,7 +196,7 @@ interface Message {
|
|
|
196
196
|
edited_at?: string | null;
|
|
197
197
|
created_at?: string;
|
|
198
198
|
/**
|
|
199
|
-
* Anlık çeviri
|
|
199
|
+
* Anlık çeviri - ziyaretçinin diline. Arayüz varsa bunu gösterir; orijinal
|
|
200
200
|
* `body`'de durmaya devam eder.
|
|
201
201
|
*/
|
|
202
202
|
translation?: {
|
|
@@ -211,7 +211,7 @@ interface Message {
|
|
|
211
211
|
*
|
|
212
212
|
* Tip listede yoktu ama oturum bu alanı yazıyordu: arayüz "gitmedi" hâlini
|
|
213
213
|
* çizmek istediğinde tipi zorlamak (`as`) zorunda kalıyordu. Bekleyen mesaj
|
|
214
|
-
* ayrıca işaretlenmez
|
|
214
|
+
* ayrıca işaretlenmez - sunucu yanıtı gelene kadar `id` ile `client_id`
|
|
215
215
|
* aynıdır ve bu, "henüz yolda" demenin en ucuz yoludur.
|
|
216
216
|
*/
|
|
217
217
|
failed?: boolean;
|
|
@@ -240,7 +240,7 @@ interface StartConversationInput {
|
|
|
240
240
|
attachments?: unknown[];
|
|
241
241
|
page_url?: string;
|
|
242
242
|
/**
|
|
243
|
-
* Destek konusu
|
|
243
|
+
* Destek konusu - id ya da slug. Seçim SUNUCUDA doğrulanır; geçersizse
|
|
244
244
|
* konuşma yine açılır, konu yok sayılır.
|
|
245
245
|
*/
|
|
246
246
|
topic?: string | number;
|
|
@@ -252,7 +252,7 @@ interface SendMessageInput {
|
|
|
252
252
|
attachments?: unknown[];
|
|
253
253
|
}
|
|
254
254
|
interface ConversationQuery {
|
|
255
|
-
/** `cm_…` imleci
|
|
255
|
+
/** `cm_…` imleci - yalnız bundan sonrakiler döner. */
|
|
256
256
|
after?: string;
|
|
257
257
|
limit?: number;
|
|
258
258
|
}
|
|
@@ -267,26 +267,6 @@ interface RegisterDeviceInput {
|
|
|
267
267
|
locale?: string;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
-
/**
|
|
271
|
-
* Uygulama istemcisi — son kullanıcı tarafı (sohbet + push kaydı).
|
|
272
|
-
*
|
|
273
|
-
* Tek bir sınıf; tarayıcı, React Native, Electron ve test aynı gövdeyi kullanır.
|
|
274
|
-
* Platform farkı iki noktada toplanmıştır ve ikisi de dışarıdan verilir:
|
|
275
|
-
* `storage` (ziyaretçi sırrı nerede durur) ve `fetchImpl`. Çatıya özel sarmalayıcı
|
|
276
|
-
* yazmak yerine bunu seçtik — React, Vue, Angular ve RN uyarlamaları bu sınıfın
|
|
277
|
-
* ÜSTÜNE oturur, kopyası değildir.
|
|
278
|
-
*
|
|
279
|
-
* Kimlik iki parçadır: açık domain anahtarı (`X-Signalbird-Key`) ve
|
|
280
|
-
* ziyaretçi sırrı (`X-Signalbird-Visitor`). Sır yalnız oturum açılışında döner;
|
|
281
|
-
* kaybolursa yeni oturum açılır ve geçmiş konuşmalar görünmez — bu yüzden
|
|
282
|
-
* saklama katmanı zorunludur, isteğe bağlı değil.
|
|
283
|
-
*
|
|
284
|
-
* Hiçbir metot istisna fırlatmaz: sohbet balonunun hatası müşterinin ödeme
|
|
285
|
-
* sayfasını çökertmemeli. Sonuç her zaman `{ok, status, …}` zarfıdır.
|
|
286
|
-
*
|
|
287
|
-
* Sözleşme: docs/CONTRACT.md § 11
|
|
288
|
-
*/
|
|
289
|
-
|
|
290
270
|
/** RFC 4122 uyumlu olmak zorunda değil; tek işi yerel kopyayı eşlemek. */
|
|
291
271
|
declare function clientId(): string;
|
|
292
272
|
declare class SignalbirdApp {
|
|
@@ -324,7 +304,7 @@ declare class SignalbirdApp {
|
|
|
324
304
|
identify(input: IdentifyInput): Promise<SbResult<{
|
|
325
305
|
visitor: Visitor;
|
|
326
306
|
}>>;
|
|
327
|
-
/** Saklanan ziyaretçi kimliği
|
|
307
|
+
/** Saklanan ziyaretçi kimliği - yoksa `null`. */
|
|
328
308
|
currentVisitor(): Promise<{
|
|
329
309
|
id: string;
|
|
330
310
|
name?: string | null;
|
|
@@ -343,7 +323,7 @@ declare class SignalbirdApp {
|
|
|
343
323
|
within_hours?: boolean;
|
|
344
324
|
}>>;
|
|
345
325
|
/**
|
|
346
|
-
* İlk mesajla konuşma açar. Kota burada harcanır
|
|
326
|
+
* İlk mesajla konuşma açar. Kota burada harcanır - konuşma başına sayılır,
|
|
347
327
|
* mesaj başına değil.
|
|
348
328
|
*/
|
|
349
329
|
startConversation(input: StartConversationInput): Promise<SbResult<{
|
|
@@ -378,21 +358,21 @@ declare class SignalbirdApp {
|
|
|
378
358
|
rateConversation(conversationId: string, rating: number, comment?: string): Promise<SbResult<unknown>>;
|
|
379
359
|
/**
|
|
380
360
|
* Cihaz token'ını kaydeder. Token'ı almak (FCM/APNs/Web Push izni) ev
|
|
381
|
-
* sahibinin işidir; SDK yalnız iletir
|
|
361
|
+
* sahibinin işidir; SDK yalnız iletir - izin diyaloğunu kimin, ne zaman
|
|
382
362
|
* göstereceği ürün kararıdır, kütüphane kararı değil.
|
|
383
363
|
*/
|
|
384
364
|
registerDevice(input: RegisterDeviceInput): Promise<SbResult<unknown>>;
|
|
385
365
|
/** Çıkışta çağrılır: kayıt silinmez, kapatılır (geçmiş korunur). */
|
|
386
366
|
unregisterDevice(token: string): Promise<SbResult<unknown>>;
|
|
387
367
|
/**
|
|
388
|
-
* Bildirime dokunuldu
|
|
368
|
+
* Bildirime dokunuldu - açılma damgası.
|
|
389
369
|
*
|
|
390
370
|
* Push'ta açılmayı YALNIZCA uygulama bilir: FCM/APNs "teslim ettim" der,
|
|
391
371
|
* "kullanıcı dokundu" demez. Bildirim yükündeki `data.sb_message_id`
|
|
392
372
|
* değerini buraya geri gönderin.
|
|
393
373
|
*
|
|
394
374
|
* ```ts
|
|
395
|
-
* // React Native / Expo
|
|
375
|
+
* // React Native / Expo - bildirime dokunma işleyicisinde
|
|
396
376
|
* const id = response.notification.request.content.data?.sb_message_id
|
|
397
377
|
* if (id) await sb.reportPushOpened(String(id))
|
|
398
378
|
* ```
|
|
@@ -407,12 +387,12 @@ declare class SignalbirdApp {
|
|
|
407
387
|
}
|
|
408
388
|
|
|
409
389
|
/**
|
|
410
|
-
* Sohbet oturumu
|
|
390
|
+
* Sohbet oturumu - çatısız durum yönetimi.
|
|
411
391
|
*
|
|
412
392
|
* `SignalbirdApp` ham uçları verir; burası bir sohbet ekranının gerçekten
|
|
413
393
|
* ihtiyaç duyduğu şeyi verir: mesaj listesi, okunmamış sayısı, yazıyor durumu,
|
|
414
394
|
* iyimser gönderim ve yoklama merdiveni. React/Vue/Angular/React Native
|
|
415
|
-
* uyarlamaları bu sınıfa abone olur
|
|
395
|
+
* uyarlamaları bu sınıfa abone olur - üçünde de aynı mantığı yeniden yazmak,
|
|
416
396
|
* üç ayrı hata takımı üretmek demekti.
|
|
417
397
|
*
|
|
418
398
|
* ── CANLI BAĞLANTI + YOKLAMA ──────────────────────────────────────────────
|
|
@@ -450,9 +430,9 @@ interface ChatState {
|
|
|
450
430
|
* Seçimi `setTopic()` taşır; ilk konuşma açılırken gönderilir.
|
|
451
431
|
*/
|
|
452
432
|
topics: TopicOption[];
|
|
453
|
-
/** Seçili konu (slug)
|
|
433
|
+
/** Seçili konu (slug) - ilk konuşmayla birlikte gider. */
|
|
454
434
|
topic: string | null;
|
|
455
|
-
/** Son hatanın kodu
|
|
435
|
+
/** Son hatanın kodu - arayüz isterse gösterir, göstermezse yutar. */
|
|
456
436
|
errorCode?: string;
|
|
457
437
|
/**
|
|
458
438
|
* Uygulamanın sohbet ayarları (renk, logo, tema, puanlama bağlantısı…).
|
|
@@ -464,7 +444,7 @@ interface ChatState {
|
|
|
464
444
|
}
|
|
465
445
|
type ChatListener = (state: ChatState) => void;
|
|
466
446
|
interface ChatSessionOptions {
|
|
467
|
-
/** Panel açık mı
|
|
447
|
+
/** Panel açık mı - yoklama hızını belirler. */
|
|
468
448
|
active?: boolean;
|
|
469
449
|
/** Arka plandayken tur atlanır; varsayılan: `document.visibilityState`. */
|
|
470
450
|
isVisible?: () => boolean;
|
|
@@ -491,22 +471,24 @@ declare class ChatSession {
|
|
|
491
471
|
* da düzenleme ekrana hiç yansımaz.
|
|
492
472
|
*/
|
|
493
473
|
private forceFull;
|
|
474
|
+
/** Son gönderilen `typing(true)` zamanı; 0 = açık sinyal yok. */
|
|
475
|
+
private lastTypingSent;
|
|
494
476
|
constructor(app: SignalbirdApp, options?: ChatSessionOptions);
|
|
495
477
|
subscribe(listener: ChatListener): () => void;
|
|
496
478
|
snapshot(): ChatState;
|
|
497
479
|
/** Bootstrap + varsa mevcut konuşmayı yükler, sonra yoklamayı başlatır. */
|
|
498
480
|
start(): Promise<void>;
|
|
499
|
-
/** Panel açıldı/kapandı
|
|
481
|
+
/** Panel açıldı/kapandı - yoklama hızı buna göre değişir. */
|
|
500
482
|
setActive(active: boolean): void;
|
|
501
483
|
/**
|
|
502
484
|
* Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar.
|
|
503
485
|
*
|
|
504
486
|
* Ekranın "yeni sohbet" düğmesi de bunu çağırır. Sunucuda hiçbir şey
|
|
505
|
-
* silinmez
|
|
487
|
+
* silinmez - yalnız bu oturumun neye baktığı değişir.
|
|
506
488
|
*/
|
|
507
489
|
reset(): void;
|
|
508
490
|
stop(): void;
|
|
509
|
-
/** Canlı bağlantı kurulu mu
|
|
491
|
+
/** Canlı bağlantı kurulu mu - arayüz isterse gösterir (zorunlu değil). */
|
|
510
492
|
get isLive(): boolean;
|
|
511
493
|
/** Ön-form gönderildiğinde ya da uygulama kullanıcıyı tanıdığında. */
|
|
512
494
|
openSession(input: SessionInput): Promise<SbResult<unknown>>;
|
|
@@ -520,11 +502,19 @@ declare class ChatSession {
|
|
|
520
502
|
send(body: string, attachments?: unknown[]): Promise<SbResult<unknown>>;
|
|
521
503
|
/**
|
|
522
504
|
* Ziyaretçinin konu seçimi. Konuşma AÇILDIKTAN sonra çağrılırsa etkisizdir:
|
|
523
|
-
* açılmış konuşmanın konusunu ajan panelden değiştirir
|
|
505
|
+
* açılmış konuşmanın konusunu ajan panelden değiştirir - ziyaretçiye kendi
|
|
524
506
|
* konuşmasını yeniden sınıflandırma yetkisi vermek, atamayı da bozardı.
|
|
525
507
|
*/
|
|
526
508
|
setTopic(slug: string | null): void;
|
|
527
|
-
/**
|
|
509
|
+
/**
|
|
510
|
+
* İlk tuşta `true`, 2.5 s hareketsizlikte `false` - çağıran zamanlar.
|
|
511
|
+
*
|
|
512
|
+
* SDK yine de kendini korur (18 Eyl 2026): `true` 4 saniyede bir kez gider,
|
|
513
|
+
* `false` yalnız açık bir `true` varsa. Bir entegrasyon her tuşta
|
|
514
|
+
* çağırdığında (Penyu mobil öyle yapıyordu) 40 harflik mesaj 40 istek
|
|
515
|
+
* oluyor ve sunucunun hız sınırı ikinci mesajı 429 ile düşürüyordu. Web
|
|
516
|
+
* widget'ındaki kuralın aynısı.
|
|
517
|
+
*/
|
|
528
518
|
typing(isTyping: boolean): void;
|
|
529
519
|
/** Görülen son mesaja kadar okundu işaretler. */
|
|
530
520
|
markRead(): Promise<void>;
|
package/dist/app.js
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/shared/version.ts
|
|
4
|
+
var SDK_VERSION = "2.6.0" ;
|
|
5
|
+
var SDK_HEADER = "X-Signalbird-Sdk";
|
|
6
|
+
function sdkHeaderValue(platform) {
|
|
7
|
+
return `${platform}/${SDK_VERSION}`;
|
|
8
|
+
}
|
|
9
|
+
var warned = false;
|
|
10
|
+
function noteSdkStatus(headers) {
|
|
11
|
+
if (warned || !headers) return;
|
|
12
|
+
try {
|
|
13
|
+
const status = headers.get("Signalbird-Sdk-Status");
|
|
14
|
+
if (status !== "outdated" && status !== "unsupported") return;
|
|
15
|
+
warned = true;
|
|
16
|
+
const latest = headers.get("Signalbird-Sdk-Latest") ?? "?";
|
|
17
|
+
const message = status === "unsupported" ? `[signalbird] Bu SDK s\xFCr\xFCm\xFC (${SDK_VERSION}) art\u0131k desteklenmiyor. Son s\xFCr\xFCm: ${latest}. Paketi g\xFCncelleyin.` : `[signalbird] Yeni SDK s\xFCr\xFCm\xFC var: ${latest} (kurulu: ${SDK_VERSION}).`;
|
|
18
|
+
if (typeof console !== "undefined") console.warn(message);
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
3
23
|
// src/app/client.ts
|
|
4
24
|
var DEFAULT_BASE_URL = "https://live.signalbird.io/api";
|
|
5
25
|
var STORAGE_KEY = "sb_visitor";
|
|
@@ -87,7 +107,7 @@ var SignalbirdApp = class {
|
|
|
87
107
|
identify(input) {
|
|
88
108
|
return this.request("POST", "/v1/sdk/identify", input);
|
|
89
109
|
}
|
|
90
|
-
/** Saklanan ziyaretçi kimliği
|
|
110
|
+
/** Saklanan ziyaretçi kimliği - yoksa `null`. */
|
|
91
111
|
async currentVisitor() {
|
|
92
112
|
const stored = await this.loadVisitor();
|
|
93
113
|
return stored ? { id: stored.id, name: stored.name, email: stored.email } : null;
|
|
@@ -106,7 +126,7 @@ var SignalbirdApp = class {
|
|
|
106
126
|
return this.request("GET", `/v1/sdk/chat/conversations/${enc(id)}`, void 0, query);
|
|
107
127
|
}
|
|
108
128
|
/**
|
|
109
|
-
* İlk mesajla konuşma açar. Kota burada harcanır
|
|
129
|
+
* İlk mesajla konuşma açar. Kota burada harcanır - konuşma başına sayılır,
|
|
110
130
|
* mesaj başına değil.
|
|
111
131
|
*/
|
|
112
132
|
startConversation(input) {
|
|
@@ -180,7 +200,7 @@ var SignalbirdApp = class {
|
|
|
180
200
|
// ── Push ──────────────────────────────────────────────────────────────
|
|
181
201
|
/**
|
|
182
202
|
* Cihaz token'ını kaydeder. Token'ı almak (FCM/APNs/Web Push izni) ev
|
|
183
|
-
* sahibinin işidir; SDK yalnız iletir
|
|
203
|
+
* sahibinin işidir; SDK yalnız iletir - izin diyaloğunu kimin, ne zaman
|
|
184
204
|
* göstereceği ürün kararıdır, kütüphane kararı değil.
|
|
185
205
|
*/
|
|
186
206
|
registerDevice(input) {
|
|
@@ -191,14 +211,14 @@ var SignalbirdApp = class {
|
|
|
191
211
|
return this.request("DELETE", `/v1/sdk/devices/${enc(token)}`);
|
|
192
212
|
}
|
|
193
213
|
/**
|
|
194
|
-
* Bildirime dokunuldu
|
|
214
|
+
* Bildirime dokunuldu - açılma damgası.
|
|
195
215
|
*
|
|
196
216
|
* Push'ta açılmayı YALNIZCA uygulama bilir: FCM/APNs "teslim ettim" der,
|
|
197
217
|
* "kullanıcı dokundu" demez. Bildirim yükündeki `data.sb_message_id`
|
|
198
218
|
* değerini buraya geri gönderin.
|
|
199
219
|
*
|
|
200
220
|
* ```ts
|
|
201
|
-
* // React Native / Expo
|
|
221
|
+
* // React Native / Expo - bildirime dokunma işleyicisinde
|
|
202
222
|
* const id = response.notification.request.content.data?.sb_message_id
|
|
203
223
|
* if (id) await sb.reportPushOpened(String(id))
|
|
204
224
|
* ```
|
|
@@ -215,7 +235,8 @@ var SignalbirdApp = class {
|
|
|
215
235
|
const isForm = typeof FormData !== "undefined" && body instanceof FormData;
|
|
216
236
|
const headers = {
|
|
217
237
|
Accept: "application/json",
|
|
218
|
-
"X-Signalbird-Key": this.config.publicKey
|
|
238
|
+
"X-Signalbird-Key": this.config.publicKey,
|
|
239
|
+
[SDK_HEADER]: sdkHeaderValue(appPlatform())
|
|
219
240
|
};
|
|
220
241
|
const moduleKey = path.startsWith("/v1/sdk/devices") || path.startsWith("/v1/sdk/push") ? this.config.pushKey : this.config.chatKey;
|
|
221
242
|
if (moduleKey) headers["X-Signalbird-Module-Key"] = moduleKey;
|
|
@@ -231,6 +252,7 @@ var SignalbirdApp = class {
|
|
|
231
252
|
body: body === void 0 ? void 0 : isForm ? body : JSON.stringify(body),
|
|
232
253
|
signal: controller.signal
|
|
233
254
|
});
|
|
255
|
+
noteSdkStatus(response.headers);
|
|
234
256
|
const text = await response.text();
|
|
235
257
|
let data = null;
|
|
236
258
|
try {
|
|
@@ -306,6 +328,9 @@ function buildQuery(query) {
|
|
|
306
328
|
const encoded = params.toString();
|
|
307
329
|
return encoded ? `?${encoded}` : "";
|
|
308
330
|
}
|
|
331
|
+
function appPlatform() {
|
|
332
|
+
return typeof navigator !== "undefined" && navigator.product === "ReactNative" ? "react-native" : "app";
|
|
333
|
+
}
|
|
309
334
|
|
|
310
335
|
// src/shared/socket.ts
|
|
311
336
|
var BACKOFF = [1e3, 2e3, 5e3, 1e4, 3e4];
|
|
@@ -430,7 +455,7 @@ var Socket = class {
|
|
|
430
455
|
}
|
|
431
456
|
}
|
|
432
457
|
/**
|
|
433
|
-
* Olay yolla
|
|
458
|
+
* Olay yolla - ACK İSTEMEDEN.
|
|
434
459
|
*
|
|
435
460
|
* `subscribe`in sonucunu beklemek bir tur daha protokol yönetmek demekti;
|
|
436
461
|
* oysa sonucu zaten davranıştan görüyoruz: imza tutmadıysa yayın gelmez ve
|
|
@@ -495,6 +520,8 @@ var ChatSession = class {
|
|
|
495
520
|
* da düzenleme ekrana hiç yansımaz.
|
|
496
521
|
*/
|
|
497
522
|
this.forceFull = false;
|
|
523
|
+
/** Son gönderilen `typing(true)` zamanı; 0 = açık sinyal yok. */
|
|
524
|
+
this.lastTypingSent = 0;
|
|
498
525
|
this.active = options.active ?? false;
|
|
499
526
|
}
|
|
500
527
|
// ── Abonelik ──────────────────────────────────────────────────────────
|
|
@@ -529,7 +556,7 @@ var ChatSession = class {
|
|
|
529
556
|
this.patch({ loading: false });
|
|
530
557
|
this.schedule();
|
|
531
558
|
}
|
|
532
|
-
/** Panel açıldı/kapandı
|
|
559
|
+
/** Panel açıldı/kapandı - yoklama hızı buna göre değişir. */
|
|
533
560
|
setActive(active) {
|
|
534
561
|
if (this.active === active) return;
|
|
535
562
|
this.active = active;
|
|
@@ -544,7 +571,7 @@ var ChatSession = class {
|
|
|
544
571
|
* Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar.
|
|
545
572
|
*
|
|
546
573
|
* Ekranın "yeni sohbet" düğmesi de bunu çağırır. Sunucuda hiçbir şey
|
|
547
|
-
* silinmez
|
|
574
|
+
* silinmez - yalnız bu oturumun neye baktığı değişir.
|
|
548
575
|
*/
|
|
549
576
|
reset() {
|
|
550
577
|
this.patch({ conversation: null, messages: [], unread: 0, agentTyping: false, errorCode: void 0 });
|
|
@@ -558,7 +585,7 @@ var ChatSession = class {
|
|
|
558
585
|
this.socket = null;
|
|
559
586
|
this.live = false;
|
|
560
587
|
}
|
|
561
|
-
/** Canlı bağlantı kurulu mu
|
|
588
|
+
/** Canlı bağlantı kurulu mu - arayüz isterse gösterir (zorunlu değil). */
|
|
562
589
|
get isLive() {
|
|
563
590
|
return this.live;
|
|
564
591
|
}
|
|
@@ -613,16 +640,32 @@ var ChatSession = class {
|
|
|
613
640
|
}
|
|
614
641
|
/**
|
|
615
642
|
* Ziyaretçinin konu seçimi. Konuşma AÇILDIKTAN sonra çağrılırsa etkisizdir:
|
|
616
|
-
* açılmış konuşmanın konusunu ajan panelden değiştirir
|
|
643
|
+
* açılmış konuşmanın konusunu ajan panelden değiştirir - ziyaretçiye kendi
|
|
617
644
|
* konuşmasını yeniden sınıflandırma yetkisi vermek, atamayı da bozardı.
|
|
618
645
|
*/
|
|
619
646
|
setTopic(slug) {
|
|
620
647
|
this.patch({ topic: slug });
|
|
621
648
|
}
|
|
622
|
-
/**
|
|
649
|
+
/**
|
|
650
|
+
* İlk tuşta `true`, 2.5 s hareketsizlikte `false` - çağıran zamanlar.
|
|
651
|
+
*
|
|
652
|
+
* SDK yine de kendini korur (18 Eyl 2026): `true` 4 saniyede bir kez gider,
|
|
653
|
+
* `false` yalnız açık bir `true` varsa. Bir entegrasyon her tuşta
|
|
654
|
+
* çağırdığında (Penyu mobil öyle yapıyordu) 40 harflik mesaj 40 istek
|
|
655
|
+
* oluyor ve sunucunun hız sınırı ikinci mesajı 429 ile düşürüyordu. Web
|
|
656
|
+
* widget'ındaki kuralın aynısı.
|
|
657
|
+
*/
|
|
623
658
|
typing(isTyping) {
|
|
624
659
|
const conversation = this.state.conversation;
|
|
625
660
|
if (!conversation) return;
|
|
661
|
+
const now = Date.now();
|
|
662
|
+
if (isTyping) {
|
|
663
|
+
if (now - this.lastTypingSent < 4e3) return;
|
|
664
|
+
this.lastTypingSent = now;
|
|
665
|
+
} else {
|
|
666
|
+
if (this.lastTypingSent === 0) return;
|
|
667
|
+
this.lastTypingSent = 0;
|
|
668
|
+
}
|
|
626
669
|
void this.app.setTyping(conversation.id, isTyping);
|
|
627
670
|
}
|
|
628
671
|
/** Görülen son mesaja kadar okundu işaretler. */
|