@ion299/sdk-react-native 0.1.0-beta.5 → 0.1.0-beta.7

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +69 -11
  3. package/android/src/main/java/com/chatplatform/sdk/ChatSdkAudioPlayerModule.kt +201 -0
  4. package/android/src/main/java/com/chatplatform/sdk/ChatSdkPackage.kt +1 -0
  5. package/ios/ChatSdkAudioPlayer.m +25 -0
  6. package/ios/ChatSdkAudioPlayer.swift +193 -0
  7. package/lib/commonjs/ChatSDK.js +16 -3
  8. package/lib/commonjs/ChatSDK.js.map +1 -1
  9. package/lib/commonjs/api.js +29 -0
  10. package/lib/commonjs/api.js.map +1 -1
  11. package/lib/commonjs/audio/audioController.js +145 -0
  12. package/lib/commonjs/audio/audioController.js.map +1 -0
  13. package/lib/commonjs/components/AudioMessage.js +198 -0
  14. package/lib/commonjs/components/AudioMessage.js.map +1 -0
  15. package/lib/commonjs/components/MessageBubble.js +9 -0
  16. package/lib/commonjs/components/MessageBubble.js.map +1 -1
  17. package/lib/commonjs/native/NativeChatSdkAudioPlayer.js +28 -0
  18. package/lib/commonjs/native/NativeChatSdkAudioPlayer.js.map +1 -0
  19. package/lib/module/ChatSDK.js +16 -3
  20. package/lib/module/ChatSDK.js.map +1 -1
  21. package/lib/module/api.js +29 -0
  22. package/lib/module/api.js.map +1 -1
  23. package/lib/module/audio/audioController.js +140 -0
  24. package/lib/module/audio/audioController.js.map +1 -0
  25. package/lib/module/components/AudioMessage.js +193 -0
  26. package/lib/module/components/AudioMessage.js.map +1 -0
  27. package/lib/module/components/MessageBubble.js +9 -0
  28. package/lib/module/components/MessageBubble.js.map +1 -1
  29. package/lib/module/native/NativeChatSdkAudioPlayer.js +23 -0
  30. package/lib/module/native/NativeChatSdkAudioPlayer.js.map +1 -0
  31. package/lib/typescript/commonjs/ChatSDK.d.ts +3 -0
  32. package/lib/typescript/commonjs/ChatSDK.d.ts.map +1 -1
  33. package/lib/typescript/commonjs/api.d.ts +3 -0
  34. package/lib/typescript/commonjs/api.d.ts.map +1 -1
  35. package/lib/typescript/commonjs/audio/audioController.d.ts +25 -0
  36. package/lib/typescript/commonjs/audio/audioController.d.ts.map +1 -0
  37. package/lib/typescript/commonjs/components/AudioMessage.d.ts +11 -0
  38. package/lib/typescript/commonjs/components/AudioMessage.d.ts.map +1 -0
  39. package/lib/typescript/commonjs/components/MessageBubble.d.ts.map +1 -1
  40. package/lib/typescript/commonjs/native/NativeChatSdkAudioPlayer.d.ts +20 -0
  41. package/lib/typescript/commonjs/native/NativeChatSdkAudioPlayer.d.ts.map +1 -0
  42. package/lib/typescript/module/ChatSDK.d.ts +3 -0
  43. package/lib/typescript/module/ChatSDK.d.ts.map +1 -1
  44. package/lib/typescript/module/api.d.ts +3 -0
  45. package/lib/typescript/module/api.d.ts.map +1 -1
  46. package/lib/typescript/module/audio/audioController.d.ts +25 -0
  47. package/lib/typescript/module/audio/audioController.d.ts.map +1 -0
  48. package/lib/typescript/module/components/AudioMessage.d.ts +11 -0
  49. package/lib/typescript/module/components/AudioMessage.d.ts.map +1 -0
  50. package/lib/typescript/module/components/MessageBubble.d.ts.map +1 -1
  51. package/lib/typescript/module/native/NativeChatSdkAudioPlayer.d.ts +20 -0
  52. package/lib/typescript/module/native/NativeChatSdkAudioPlayer.d.ts.map +1 -0
  53. package/package.json +1 -1
  54. package/src/ChatSDK.ts +18 -3
  55. package/src/api.ts +37 -0
  56. package/src/audio/audioController.ts +144 -0
  57. package/src/components/AudioMessage.tsx +198 -0
  58. package/src/components/MessageBubble.tsx +6 -0
  59. package/src/native/NativeChatSdkAudioPlayer.ts +54 -0
package/CHANGELOG.md CHANGED
@@ -6,6 +6,32 @@
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.0-beta.7]
10
+
11
+ ### Added
12
+ - Регистрация push-токена устройства для фоновых уведомлений:
13
+ `ChatSDK.registerPushToken(deviceToken, platform?)` и
14
+ `ChatSDK.unregisterPushToken(deviceToken?)`. Токен снимается автоматически
15
+ в `logout()`. WebSocket в фоне ОС гасит — события о новых сообщениях
16
+ оператора при свёрнутом/закрытом приложении доставляются через FCM/APNs:
17
+ ЧП шлёт push на зарегистрированные токены контакта (свой backend не нужен).
18
+ SDK провайдер-агностик: хост-приложение приносит нативный токен под свой стек.
19
+
20
+ ## [0.1.0-beta.6]
21
+
22
+ ### Added
23
+ - Аудио-вложения (голосовые сообщения) теперь проигрываются прямо в чате:
24
+ встроенный плеер с кнопкой play/pause, прогресс-баром и перемоткой по тапу.
25
+ Раньше аудио показывалось как обычный файл (`.webm`) только для скачивания.
26
+ - Собственный нативный модуль воспроизведения `ChatSdkAudioPlayer`
27
+ (Android `MediaPlayer`, iOS `AVPlayer`) — без сторонних JS-зависимостей.
28
+ Если модуль не собран, аудио откатывается на обычный файловый блок.
29
+
30
+ ### Notes
31
+ - iOS (`AVPlayer`) не декодирует контейнер WebM/Opus: голосовые, записанные
32
+ в браузере как `audio/webm`, на iOS воспроизвести нельзя — их нужно
33
+ транскодировать на бэкенде (например, в `m4a`/`aac`). Android их играет.
34
+
9
35
  ## [0.1.0-beta.5]
10
36
 
11
37
  ### Changed
package/README.md CHANGED
@@ -215,11 +215,56 @@ unsubMessagesUpdated(); unsubConnected(); unsubError()
215
215
 
216
216
  ## Push-уведомления
217
217
 
218
- ЧП **не** шлёт FCM/APNs напрямую. Вместо этого ЧП отправляет webhook на ваш
219
- backend при новом сообщении от оператора. Ваш backend сам решает, кому и как
220
- доставить push.
218
+ В фоне/закрытом приложении WebSocket гасит ОС события туда не приходят
219
+ (это ограничение iOS и Android, см. «Поведение в фоне»). Единственный
220
+ кроссплатформенный способ доставить уведомление о сообщении оператора —
221
+ **push (FCM / APNs)**. Есть два варианта интеграции.
221
222
 
222
- ### Webhook payload
223
+ ### Вариант A — `registerPushToken` (рекомендуется)
224
+
225
+ Хост-приложение получает **нативный** push-токен своим способом (под свой стек)
226
+ и отдаёт его SDK. ЧП сам шлёт FCM/APNs на зарегистрированные токены контакта —
227
+ свой backend не нужен. SDK **не зависит** от конкретного push-провайдера.
228
+
229
+ ```ts
230
+ await ChatSDK.login(/* ... */)
231
+ await ChatSDK.registerPushToken(deviceToken, platform) // platform: 'fcm' | 'apns'
232
+ ```
233
+
234
+ `platform`:
235
+ - `'fcm'` — FCM registration token (Android всегда; iOS — если используете Firebase и на iOS);
236
+ - `'apns'` — «сырой» APNs device-token (iOS без Firebase).
237
+
238
+ Токен снимается автоматически в `logout()` (или вручную `unregisterPushToken()`).
239
+
240
+ **Где взять токен — примеры под разные стеки:**
241
+
242
+ ```ts
243
+ // Expo — getDevicePushTokenAsync() отдаёт НАТИВНЫЙ FCM/APNs токен
244
+ import * as Notifications from 'expo-notifications'
245
+ import { Platform } from 'react-native'
246
+
247
+ await Notifications.requestPermissionsAsync()
248
+ const { data } = await Notifications.getDevicePushTokenAsync()
249
+ await ChatSDK.registerPushToken(String(data), Platform.OS === 'ios' ? 'apns' : 'fcm')
250
+ ```
251
+
252
+ ```ts
253
+ // Голый React Native — @react-native-firebase/messaging
254
+ import messaging from '@react-native-firebase/messaging'
255
+
256
+ await messaging().requestPermission()
257
+ const fcmToken = await messaging().getToken()
258
+ await ChatSDK.registerPushToken(fcmToken, 'fcm')
259
+ ```
260
+
261
+ > Нативные SDK (iOS/Android без RN) регистрируют токен тем же эндпоинтом —
262
+ > `registerPushToken` лишь обёртка над `POST /api/mobile/{token}/contact/{contactId}/push-token`.
263
+
264
+ ### Вариант B — webhook на свой backend
265
+
266
+ Если вы хотите управлять доставкой сами, ЧП может слать webhook
267
+ `message.created` на ваш backend, а push вы шлёте уже своей инфраструктурой.
223
268
 
224
269
  ```json
225
270
  {
@@ -238,12 +283,15 @@ backend при новом сообщении от оператора. Ваш bac
238
283
 
239
284
  Подпись: заголовок `X-Chat-Platform-Signature: sha256=HMAC_SHA256(body, webhook_secret)`.
240
285
 
241
- ### Открытие чата из тапа по push
286
+ ### Приём push и открытие чата
287
+
288
+ Сам push принимает **хост-приложение** (SDK не перехватывает уведомления).
289
+ Распознать «наш» push и открыть чат на тап:
242
290
 
243
291
  ```tsx
244
292
  import { ChatSDK } from '@chat-platform/sdk-react-native'
245
293
 
246
- // В обработчике нотификации
294
+ // В обработчике тапа по нотификации
247
295
  ChatSDK.handleNotification({
248
296
  token: data.cp_token,
249
297
  contactId: data.cp_contact_id,
@@ -251,7 +299,7 @@ ChatSDK.handleNotification({
251
299
  navigation.navigate('Chat')
252
300
  ```
253
301
 
254
- Рекомендуемые data-ключи в payload FCM/APNs: `cp_token`, `cp_contact_id`.
302
+ В data-payload push'а ЧП кладёт ключи `cp_token` и `cp_contact_id`.
255
303
 
256
304
  ---
257
305
 
@@ -279,7 +327,17 @@ navigation.navigate('Chat')
279
327
 
280
328
  ### `ChatSDK.logout()`
281
329
 
282
- Завершает сессию, отключает realtime.
330
+ Завершает сессию, отключает realtime, снимает зарегистрированный push-токен.
331
+
332
+ ### `ChatSDK.registerPushToken(deviceToken, platform?)`
333
+
334
+ Регистрирует push-токен устройства для фоновых уведомлений. Требует `login()`.
335
+ `platform`: `'fcm'` (по умолчанию) или `'apns'`. Токен запоминается и снимается
336
+ в `logout()`.
337
+
338
+ ### `ChatSDK.unregisterPushToken(deviceToken?)`
339
+
340
+ Снимает регистрацию push-токена (по умолчанию — последнего зарегистрированного).
283
341
 
284
342
  ### `ChatSDK.handleNotification(payload)`
285
343
 
@@ -304,9 +362,9 @@ navigation.navigate('Chat')
304
362
  - [ ] `ChatSDK.init(...)` в точке входа приложения
305
363
  - [ ] `ChatSDK.login(...)` после авторизации пользователя
306
364
  - [ ] `<ChatScreen />` в навигаторе
307
- - [ ] Webhook URL в настройках виджета в ЧП
308
- - [ ] FCM/APNs: данные `cp_token`, `cp_contact_id` в data payload
309
- - [ ] `ChatSDK.handleNotification(...)` в обработчике push на стороне приложения
365
+ - [ ] Push: получить нативный токен и вызвать `ChatSDK.registerPushToken(token, platform)` после `login()`
366
+ (либо webhook на свой backend вариант B)
367
+ - [ ] `ChatSDK.handleNotification(...)` в обработчике тапа по push (`cp_token`, `cp_contact_id` в data payload)
310
368
 
311
369
  ---
312
370
 
@@ -0,0 +1,201 @@
1
+ package com.chatplatform.sdk
2
+
3
+ import android.media.AudioAttributes
4
+ import android.media.MediaPlayer
5
+ import android.net.Uri
6
+ import android.os.Handler
7
+ import android.os.HandlerThread
8
+ import com.facebook.react.bridge.Arguments
9
+ import com.facebook.react.bridge.Promise
10
+ import com.facebook.react.bridge.ReactApplicationContext
11
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
12
+ import com.facebook.react.bridge.ReactMethod
13
+ import com.facebook.react.bridge.ReadableMap
14
+ import com.facebook.react.bridge.WritableMap
15
+ import com.facebook.react.modules.core.DeviceEventManagerModule
16
+
17
+ /**
18
+ * Проигрывает аудио-вложения (голосовые сообщения) прямо в чате.
19
+ * В каждый момент времени активен ровно один плеер — запуск нового
20
+ * освобождает предыдущий. Состояние отдаётся в JS событием
21
+ * "ChatSdkAudioState" { key, positionMillis, durationMillis, state }.
22
+ */
23
+ class ChatSdkAudioPlayerModule(reactContext: ReactApplicationContext) :
24
+ ReactContextBaseJavaModule(reactContext) {
25
+
26
+ override fun getName(): String = NAME
27
+
28
+ private val thread = HandlerThread("ChatSdkAudioPlayer").apply { start() }
29
+ private val handler = Handler(thread.looper)
30
+
31
+ private var player: MediaPlayer? = null
32
+ private var currentKey: String? = null
33
+ private var prepared = false
34
+
35
+ private val tick = object : Runnable {
36
+ override fun run() {
37
+ val p = player ?: return
38
+ try {
39
+ if (p.isPlaying) {
40
+ emit(currentKey, p.currentPosition.toLong(), safeDuration(p), "playing")
41
+ handler.postDelayed(this, 250)
42
+ }
43
+ } catch (_: Throwable) {
44
+ // плеер уже освобождён — просто перестаём тикать
45
+ }
46
+ }
47
+ }
48
+
49
+ @ReactMethod
50
+ fun addListener(eventName: String) {
51
+
52
+ }
53
+
54
+ @ReactMethod
55
+ fun removeListeners(count: Int) {
56
+
57
+ }
58
+
59
+ @ReactMethod
60
+ fun play(key: String, url: String, headers: ReadableMap?, promise: Promise) {
61
+ val hdr = HashMap<String, String>()
62
+ headers?.toHashMap()?.forEach { (k, v) -> if (v is String) hdr[k] = v }
63
+
64
+ handler.post {
65
+ try {
66
+ val existing = player
67
+ if (currentKey == key && existing != null && prepared) {
68
+ existing.start()
69
+ emit(key, existing.currentPosition.toLong(), safeDuration(existing), "playing")
70
+ handler.removeCallbacks(tick)
71
+ handler.post(tick)
72
+ promise.resolve(null)
73
+ return@post
74
+ }
75
+
76
+ releaseInternal()
77
+ currentKey = key
78
+ prepared = false
79
+ emit(key, 0, 0, "loading")
80
+
81
+ val mp = MediaPlayer()
82
+ mp.setAudioAttributes(
83
+ AudioAttributes.Builder()
84
+ .setUsage(AudioAttributes.USAGE_MEDIA)
85
+ .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
86
+ .build(),
87
+ )
88
+ if (hdr.isEmpty()) {
89
+ mp.setDataSource(reactApplicationContext, Uri.parse(url))
90
+ } else {
91
+ mp.setDataSource(reactApplicationContext, Uri.parse(url), hdr)
92
+ }
93
+ mp.setOnPreparedListener { p ->
94
+ if (currentKey != key) return@setOnPreparedListener
95
+ prepared = true
96
+ p.start()
97
+ emit(key, 0, safeDuration(p), "playing")
98
+ handler.removeCallbacks(tick)
99
+ handler.post(tick)
100
+ }
101
+ mp.setOnCompletionListener { p ->
102
+ handler.removeCallbacks(tick)
103
+ emit(key, safeDuration(p), safeDuration(p), "ended")
104
+ }
105
+ mp.setOnErrorListener { _, _, _ ->
106
+ handler.removeCallbacks(tick)
107
+ emit(key, 0, 0, "error")
108
+ releaseInternal()
109
+ true
110
+ }
111
+ player = mp
112
+ mp.prepareAsync()
113
+ promise.resolve(null)
114
+ } catch (e: Throwable) {
115
+ emit(key, 0, 0, "error")
116
+ releaseInternal()
117
+ promise.reject("AUDIO_PLAY_FAILED", e.message ?: "Не удалось воспроизвести аудио", e)
118
+ }
119
+ }
120
+ }
121
+
122
+ @ReactMethod
123
+ fun pause(key: String, promise: Promise) {
124
+ handler.post {
125
+ try {
126
+ val p = player
127
+ if (currentKey == key && p != null && prepared && p.isPlaying) {
128
+ p.pause()
129
+ handler.removeCallbacks(tick)
130
+ emit(key, p.currentPosition.toLong(), safeDuration(p), "paused")
131
+ }
132
+ promise.resolve(null)
133
+ } catch (e: Throwable) {
134
+ promise.reject("AUDIO_PAUSE_FAILED", e.message ?: "Ошибка паузы", e)
135
+ }
136
+ }
137
+ }
138
+
139
+ @ReactMethod
140
+ fun seek(key: String, positionMillis: Double, promise: Promise) {
141
+ handler.post {
142
+ try {
143
+ val p = player
144
+ if (currentKey == key && p != null && prepared) {
145
+ p.seekTo(positionMillis.toInt())
146
+ val state = if (p.isPlaying) "playing" else "paused"
147
+ emit(key, positionMillis.toLong(), safeDuration(p), state)
148
+ }
149
+ promise.resolve(null)
150
+ } catch (e: Throwable) {
151
+ promise.reject("AUDIO_SEEK_FAILED", e.message ?: "Ошибка перемотки", e)
152
+ }
153
+ }
154
+ }
155
+
156
+ @ReactMethod
157
+ fun stop(key: String, promise: Promise) {
158
+ handler.post {
159
+ try {
160
+ if (currentKey == key) {
161
+ handler.removeCallbacks(tick)
162
+ releaseInternal()
163
+ emit(key, 0, 0, "stopped")
164
+ }
165
+ promise.resolve(null)
166
+ } catch (e: Throwable) {
167
+ promise.reject("AUDIO_STOP_FAILED", e.message ?: "Ошибка остановки", e)
168
+ }
169
+ }
170
+ }
171
+
172
+ private fun releaseInternal() {
173
+ try { player?.reset() } catch (_: Throwable) {}
174
+ try { player?.release() } catch (_: Throwable) {}
175
+ player = null
176
+ prepared = false
177
+ }
178
+
179
+ private fun safeDuration(p: MediaPlayer): Long =
180
+ try {
181
+ val d = p.duration
182
+ if (d > 0) d.toLong() else 0L
183
+ } catch (_: Throwable) {
184
+ 0L
185
+ }
186
+
187
+ private fun emit(key: String?, position: Long, duration: Long, state: String) {
188
+ val map: WritableMap = Arguments.createMap()
189
+ map.putString("key", key ?: "")
190
+ map.putDouble("positionMillis", position.toDouble())
191
+ map.putDouble("durationMillis", duration.toDouble())
192
+ map.putString("state", state)
193
+ reactApplicationContext
194
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
195
+ .emit("ChatSdkAudioState", map)
196
+ }
197
+
198
+ companion object {
199
+ const val NAME = "ChatSdkAudioPlayer"
200
+ }
201
+ }
@@ -9,6 +9,7 @@ class ChatSdkPackage : ReactPackage {
9
9
  override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> = listOf(
10
10
  ChatSdkFilePickerModule(reactContext),
11
11
  ChatSdkDownloaderModule(reactContext),
12
+ ChatSdkAudioPlayerModule(reactContext),
12
13
  )
13
14
 
14
15
  override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> = emptyList()
@@ -0,0 +1,25 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
3
+
4
+ @interface RCT_EXTERN_MODULE(ChatSdkAudioPlayer, RCTEventEmitter)
5
+
6
+ RCT_EXTERN_METHOD(play:(NSString *)key
7
+ url:(NSString *)url
8
+ headers:(NSDictionary *)headers
9
+ resolver:(RCTPromiseResolveBlock)resolve
10
+ rejecter:(RCTPromiseRejectBlock)reject)
11
+
12
+ RCT_EXTERN_METHOD(pause:(NSString *)key
13
+ resolver:(RCTPromiseResolveBlock)resolve
14
+ rejecter:(RCTPromiseRejectBlock)reject)
15
+
16
+ RCT_EXTERN_METHOD(seek:(NSString *)key
17
+ positionMillis:(nonnull NSNumber *)positionMillis
18
+ resolver:(RCTPromiseResolveBlock)resolve
19
+ rejecter:(RCTPromiseRejectBlock)reject)
20
+
21
+ RCT_EXTERN_METHOD(stop:(NSString *)key
22
+ resolver:(RCTPromiseResolveBlock)resolve
23
+ rejecter:(RCTPromiseRejectBlock)reject)
24
+
25
+ @end
@@ -0,0 +1,193 @@
1
+ import Foundation
2
+ import AVFoundation
3
+ import React
4
+ @objc(ChatSdkAudioPlayer)
5
+ class ChatSdkAudioPlayer: RCTEventEmitter {
6
+
7
+ private var player: AVPlayer?
8
+ private var observedItem: AVPlayerItem?
9
+ private var timeObserver: Any?
10
+ private var endObserver: NSObjectProtocol?
11
+ private var currentKey: String?
12
+ private var durationMillis: Double = 0
13
+ private var listenerCount = 0
14
+
15
+ @objc override static func requiresMainQueueSetup() -> Bool { return false }
16
+ override func supportedEvents() -> [String]! { return ["ChatSdkAudioState"] }
17
+ override func startObserving() { listenerCount += 1 }
18
+ override func stopObserving() { listenerCount = max(0, listenerCount - 1) }
19
+
20
+ @objc(play:url:headers:resolver:rejecter:)
21
+ func play(_ key: String,
22
+ url urlString: String,
23
+ headers: NSDictionary?,
24
+ resolver resolve: @escaping RCTPromiseResolveBlock,
25
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
26
+ DispatchQueue.main.async {
27
+ if self.currentKey == key, let p = self.player {
28
+ self.activateSession()
29
+ p.play()
30
+ self.emit(key: key, position: self.currentPositionMillis(), duration: self.durationMillis, state: "playing")
31
+ resolve(nil)
32
+ return
33
+ }
34
+
35
+ guard let url = URL(string: urlString) else {
36
+ reject("INVALID_URL", "URL не задан", nil)
37
+ return
38
+ }
39
+
40
+ self.teardown()
41
+ self.currentKey = key
42
+ self.emit(key: key, position: 0, duration: 0, state: "loading")
43
+
44
+ var options: [String: Any] = [:]
45
+ if let h = headers as? [String: String], !h.isEmpty {
46
+ options["AVURLAssetHTTPHeaderFieldsKey"] = h
47
+ }
48
+ let asset = AVURLAsset(url: url, options: options)
49
+ let item = AVPlayerItem(asset: asset)
50
+ let p = AVPlayer(playerItem: item)
51
+ self.player = p
52
+ self.observedItem = item
53
+
54
+ self.activateSession()
55
+
56
+ item.addObserver(self, forKeyPath: "status", options: [.new], context: nil)
57
+
58
+ let interval = CMTime(seconds: 0.25, preferredTimescale: 600)
59
+ self.timeObserver = p.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in
60
+ guard let self = self, self.currentKey == key else { return }
61
+ if p.timeControlStatus == .playing {
62
+ let pos = CMTimeGetSeconds(time) * 1000
63
+ self.emit(key: key, position: pos.isFinite ? pos : 0, duration: self.durationMillis, state: "playing")
64
+ }
65
+ }
66
+
67
+ self.endObserver = NotificationCenter.default.addObserver(
68
+ forName: .AVPlayerItemDidPlayToEndTime,
69
+ object: item,
70
+ queue: .main,
71
+ ) { [weak self] _ in
72
+ guard let self = self, self.currentKey == key else { return }
73
+ self.emit(key: key, position: self.durationMillis, duration: self.durationMillis, state: "ended")
74
+ p.seek(to: .zero)
75
+ }
76
+
77
+ p.play()
78
+ resolve(nil)
79
+ }
80
+ }
81
+
82
+ @objc(pause:resolver:rejecter:)
83
+ func pause(_ key: String,
84
+ resolver resolve: @escaping RCTPromiseResolveBlock,
85
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
86
+ DispatchQueue.main.async {
87
+ if self.currentKey == key, let p = self.player {
88
+ p.pause()
89
+ self.emit(key: key, position: self.currentPositionMillis(), duration: self.durationMillis, state: "paused")
90
+ }
91
+ resolve(nil)
92
+ }
93
+ }
94
+
95
+ @objc(seek:positionMillis:resolver:rejecter:)
96
+ func seek(_ key: String,
97
+ positionMillis: NSNumber,
98
+ resolver resolve: @escaping RCTPromiseResolveBlock,
99
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
100
+ DispatchQueue.main.async {
101
+ guard self.currentKey == key, let p = self.player else {
102
+ resolve(nil)
103
+ return
104
+ }
105
+ let seconds = positionMillis.doubleValue / 1000
106
+ let target = CMTime(seconds: seconds, preferredTimescale: 600)
107
+ p.seek(to: target) { [weak self] _ in
108
+ guard let self = self, self.currentKey == key else { return }
109
+ let state = p.timeControlStatus == .playing ? "playing" : "paused"
110
+ self.emit(key: key, position: positionMillis.doubleValue, duration: self.durationMillis, state: state)
111
+ }
112
+ resolve(nil)
113
+ }
114
+ }
115
+
116
+ @objc(stop:resolver:rejecter:)
117
+ func stop(_ key: String,
118
+ resolver resolve: @escaping RCTPromiseResolveBlock,
119
+ rejecter reject: @escaping RCTPromiseRejectBlock) {
120
+ DispatchQueue.main.async {
121
+ if self.currentKey == key {
122
+ self.teardown()
123
+ self.deactivateSession()
124
+ self.emit(key: key, position: 0, duration: 0, state: "stopped")
125
+ }
126
+ resolve(nil)
127
+ }
128
+ }
129
+
130
+ override func observeValue(forKeyPath keyPath: String?,
131
+ of object: Any?,
132
+ change: [NSKeyValueChangeKey: Any]?,
133
+ context: UnsafeMutableRawPointer?) {
134
+ guard keyPath == "status", let item = object as? AVPlayerItem, item === observedItem else { return }
135
+ switch item.status {
136
+ case .readyToPlay:
137
+ let dur = CMTimeGetSeconds(item.duration) * 1000
138
+ durationMillis = (dur.isFinite && dur > 0) ? dur : 0
139
+ emit(key: currentKey, position: currentPositionMillis(), duration: durationMillis, state: "playing")
140
+ case .failed:
141
+ emit(key: currentKey, position: 0, duration: 0, state: "error")
142
+ teardown()
143
+ default:
144
+ break
145
+ }
146
+ }
147
+
148
+ private func currentPositionMillis() -> Double {
149
+ guard let p = player else { return 0 }
150
+ let s = CMTimeGetSeconds(p.currentTime())
151
+ return s.isFinite ? s * 1000 : 0
152
+ }
153
+
154
+ private func teardown() {
155
+ if let obs = timeObserver { player?.removeTimeObserver(obs); timeObserver = nil }
156
+ if let end = endObserver { NotificationCenter.default.removeObserver(end); endObserver = nil }
157
+ if let item = observedItem { item.removeObserver(self, forKeyPath: "status"); observedItem = nil }
158
+ player?.pause()
159
+ player = nil
160
+ durationMillis = 0
161
+ }
162
+
163
+ private func activateSession() {
164
+ do {
165
+ try AVAudioSession.sharedInstance().setCategory(.playback, mode: .spokenAudio, options: [])
166
+ try AVAudioSession.sharedInstance().setActive(true)
167
+ } catch {
168
+
169
+ }
170
+ }
171
+
172
+ private func deactivateSession() {
173
+ try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
174
+ }
175
+
176
+ private func emit(key: String?, position: Double, duration: Double, state: String) {
177
+ guard listenerCount > 0 else { return }
178
+ sendEvent(withName: "ChatSdkAudioState", body: [
179
+ "key": key ?? "",
180
+ "positionMillis": NSNumber(value: position),
181
+ "durationMillis": NSNumber(value: duration),
182
+ "state": state,
183
+ ])
184
+ }
185
+
186
+ override func invalidate() {
187
+ DispatchQueue.main.async {
188
+ self.teardown()
189
+ self.deactivateSession()
190
+ }
191
+ super.invalidate()
192
+ }
193
+ }
@@ -15,6 +15,7 @@ class ChatSDKSingleton {
15
15
  lastError = null;
16
16
  currentUser = null;
17
17
  listeners = new Map();
18
+ pushDeviceToken = null;
18
19
 
19
20
  // ─── Lifecycle ────────────────────────────────────────────────────────────
20
21
 
@@ -75,6 +76,7 @@ class ChatSDKSingleton {
75
76
  }
76
77
  }
77
78
  async logout() {
79
+ await this.unregisterPushToken().catch(() => {});
78
80
  this.currentUser = null;
79
81
  this.session?.destroy();
80
82
  this.session = null;
@@ -84,11 +86,22 @@ class ChatSDKSingleton {
84
86
 
85
87
  // ─── Push ─────────────────────────────────────────────────────────────────
86
88
 
89
+ async registerPushToken(deviceToken, platform = 'fcm') {
90
+ this.assertInitialized();
91
+ await this.api.registerPushToken(deviceToken, platform);
92
+ this.pushDeviceToken = deviceToken;
93
+ }
94
+ async unregisterPushToken(deviceToken) {
95
+ const target = deviceToken ?? this.pushDeviceToken;
96
+ if (!target || !this.api) return;
97
+ try {
98
+ await this.api.deletePushToken(target);
99
+ } finally {
100
+ if (target === this.pushDeviceToken) this.pushDeviceToken = null;
101
+ }
102
+ }
87
103
  handleNotification(_payload) {
88
- // Навигация обрабатывается в host app через onNotification callback.
89
- // SDK только проверяет, что payload относится к нашему токену.
90
104
  if (_payload.token !== this.config?.token) return;
91
- // В будущем: открыть ChatScreen если приложение foreground
92
105
  }
93
106
 
94
107
  // ─── Getters ──────────────────────────────────────────────────────────────
@@ -1 +1 @@
1
- {"version":3,"names":["_api","require","_session","ChatSDKSingleton","config","api","sdkConfig","session","state","lastError","currentUser","listeners","Map","init","raw","console","warn","resolveConfig","MobileApiClient","baseUrl","token","setState","getConfig","then","cfg","emit","catch","err","msg","Error","message","String","getLastError","login","user","device","assertInitialized","result","createSession","userId","name","surname","email","phone","setSession","sessionToken","contactId","setUserProfile","startSession","logout","destroy","clearSession","handleNotification","_payload","getApi","getSDKConfig","getBaseUrl","getState","isAuthenticated","getUser","getMessages","getOperator","isRealtimeConnected","isConnected","refreshMessages","Promise","resolve","on","event","handler","has","set","Set","handlers","get","add","delete","ChatSDKSession","apiBaseUrl","emitMessagesUpdated","messages","operator","emitConnectedChange","connected","emitOperatorChanged","payload","emitNewMessage","start","next","data","forEach","h","decoded","JSON","parse","atob","ChatSDK","exports"],"sourceRoot":"..\\..\\src","sources":["ChatSDK.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_api","require","_session","ChatSDKSingleton","config","api","sdkConfig","session","state","lastError","currentUser","listeners","Map","pushDeviceToken","init","raw","console","warn","resolveConfig","MobileApiClient","baseUrl","token","setState","getConfig","then","cfg","emit","catch","err","msg","Error","message","String","getLastError","login","user","device","assertInitialized","result","createSession","userId","name","surname","email","phone","setSession","sessionToken","contactId","setUserProfile","startSession","logout","unregisterPushToken","destroy","clearSession","registerPushToken","deviceToken","platform","target","deletePushToken","handleNotification","_payload","getApi","getSDKConfig","getBaseUrl","getState","isAuthenticated","getUser","getMessages","getOperator","isRealtimeConnected","isConnected","refreshMessages","Promise","resolve","on","event","handler","has","set","Set","handlers","get","add","delete","ChatSDKSession","apiBaseUrl","emitMessagesUpdated","messages","operator","emitConnectedChange","connected","emitOperatorChanged","payload","emitNewMessage","start","next","data","forEach","h","decoded","JSON","parse","atob","ChatSDK","exports"],"sourceRoot":"..\\..\\src","sources":["ChatSDK.ts"],"mappings":"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","ignoreList":[]}