@capgo/capacitor-stream-call 0.0.51 → 0.0.56

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.
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
13
13
  s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
14
  s.ios.deployment_target = '14.0'
15
15
  s.dependency 'Capacitor'
16
- s.dependency 'StreamVideo', '1.20.0'
17
- s.dependency 'StreamVideoSwiftUI', '1.20.0'
16
+ s.dependency 'StreamVideo', '1.24.0'
17
+ s.dependency 'StreamVideoSwiftUI', '1.24.0'
18
18
  s.swift_version = '5.1'
19
19
  end
package/Package.swift CHANGED
@@ -11,7 +11,7 @@ let package = Package(
11
11
  ],
12
12
  dependencies: [
13
13
  .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0"),
14
- .package(url: "https://github.com/GetStream/stream-video-swift.git", exact: "1.20.0")
14
+ .package(url: "https://github.com/GetStream/stream-video-swift.git", exact: "1.24.0")
15
15
  ],
16
16
  targets: [
17
17
  .target(
package/README.md CHANGED
@@ -518,24 +518,34 @@ Get detailed information about an active call including caller details
518
518
 
519
519
  #### LoginOptions
520
520
 
521
- | Prop | Type | Description |
522
- | ---------------- | ------------------- | ------------------------------------------------------- |
523
- | **`token`** | <code>string</code> | Stream Video API token |
524
- | **`userId`** | <code>string</code> | User ID for the current user |
525
- | **`name`** | <code>string</code> | Display name for the current user |
526
- | **`imageURL`** | <code>string</code> | Optional avatar URL for the current user |
527
- | **`apiKey`** | <code>string</code> | Stream Video API key |
528
- | **`magicDivId`** | <code>string</code> | ID of the HTML element where the video will be rendered |
521
+ | Prop | Type | Description |
522
+ | ----------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------- |
523
+ | **`token`** | <code>string</code> | Stream Video API token |
524
+ | **`userId`** | <code>string</code> | User ID for the current user |
525
+ | **`name`** | <code>string</code> | Display name for the current user |
526
+ | **`imageURL`** | <code>string</code> | Optional avatar URL for the current user |
527
+ | **`apiKey`** | <code>string</code> | Stream Video API key |
528
+ | **`magicDivId`** | <code>string</code> | ID of the HTML element where the video will be rendered |
529
+ | **`pushNotificationsConfig`** | <code><a href="#pushnotificationsconfig">PushNotificationsConfig</a></code> | |
530
+
531
+
532
+ #### PushNotificationsConfig
533
+
534
+ | Prop | Type |
535
+ | ---------------------- | ------------------- |
536
+ | **`pushProviderName`** | <code>string</code> |
537
+ | **`voipProviderName`** | <code>string</code> |
529
538
 
530
539
 
531
540
  #### CallOptions
532
541
 
533
- | Prop | Type | Description |
534
- | ------------- | --------------------------------------------- | ------------------------------------------------ |
535
- | **`userIds`** | <code>string[]</code> | User ID of the person to call |
536
- | **`type`** | <code><a href="#calltype">CallType</a></code> | Type of call, defaults to 'default' |
537
- | **`ring`** | <code>boolean</code> | Whether to ring the other user, defaults to true |
538
- | **`team`** | <code>string</code> | Team name to call |
542
+ | Prop | Type | Description |
543
+ | ------------- | --------------------------------------------- | --------------------------------------------------------------- |
544
+ | **`userIds`** | <code>string[]</code> | User ID of the person to call |
545
+ | **`type`** | <code><a href="#calltype">CallType</a></code> | Type of call, defaults to 'default' |
546
+ | **`ring`** | <code>boolean</code> | Whether to ring the other user, defaults to true |
547
+ | **`team`** | <code>string</code> | Team name to call |
548
+ | **`video`** | <code>boolean</code> | Whether to start the call with video enabled, defaults to false |
539
549
 
540
550
 
541
551
  #### CallEvent
package/dist/docs.json CHANGED
@@ -546,6 +546,38 @@
546
546
  "docs": "ID of the HTML element where the video will be rendered",
547
547
  "complexTypes": [],
548
548
  "type": "string | undefined"
549
+ },
550
+ {
551
+ "name": "pushNotificationsConfig",
552
+ "tags": [],
553
+ "docs": "",
554
+ "complexTypes": [
555
+ "PushNotificationsConfig"
556
+ ],
557
+ "type": "PushNotificationsConfig"
558
+ }
559
+ ]
560
+ },
561
+ {
562
+ "name": "PushNotificationsConfig",
563
+ "slug": "pushnotificationsconfig",
564
+ "docs": "",
565
+ "tags": [],
566
+ "methods": [],
567
+ "properties": [
568
+ {
569
+ "name": "pushProviderName",
570
+ "tags": [],
571
+ "docs": "",
572
+ "complexTypes": [],
573
+ "type": "string"
574
+ },
575
+ {
576
+ "name": "voipProviderName",
577
+ "tags": [],
578
+ "docs": "",
579
+ "complexTypes": [],
580
+ "type": "string"
549
581
  }
550
582
  ]
551
583
  },
@@ -610,6 +642,13 @@
610
642
  "docs": "Team name to call",
611
643
  "complexTypes": [],
612
644
  "type": "string | undefined"
645
+ },
646
+ {
647
+ "name": "video",
648
+ "tags": [],
649
+ "docs": "Whether to start the call with video enabled, defaults to false",
650
+ "complexTypes": [],
651
+ "type": "boolean | undefined"
613
652
  }
614
653
  ]
615
654
  },
@@ -21,6 +21,11 @@ export interface LoginOptions {
21
21
  apiKey: string;
22
22
  /** ID of the HTML element where the video will be rendered */
23
23
  magicDivId?: string;
24
+ pushNotificationsConfig?: PushNotificationsConfig;
25
+ }
26
+ export interface PushNotificationsConfig {
27
+ pushProviderName: string;
28
+ voipProviderName: string;
24
29
  }
25
30
  /**
26
31
  * @typedef CallState
@@ -107,6 +112,8 @@ export interface CallOptions {
107
112
  ring?: boolean;
108
113
  /** Team name to call */
109
114
  team?: string;
115
+ /** Whether to start the call with video enabled, defaults to false */
116
+ video?: boolean;
110
117
  }
111
118
  /**
112
119
  * @interface StreamCallPlugin
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * @interface LoginOptions\n * @description Configuration options for logging into the Stream Video service\n * @property {string} token - Stream Video API token for authentication\n * @property {string} userId - Unique identifier for the current user\n * @property {string} name - Display name for the current user\n * @property {string} [imageURL] - Avatar URL for the current user\n * @property {string} apiKey - Stream Video API key for your application\n * @property {string} [magicDivId] - DOM element ID where video will be rendered\n */\nexport interface LoginOptions {\n /** Stream Video API token */\n token: string;\n /** User ID for the current user */\n userId: string;\n /** Display name for the current user */\n name: string;\n /** Optional avatar URL for the current user */\n imageURL?: string;\n /** Stream Video API key */\n apiKey: string;\n /** ID of the HTML element where the video will be rendered */\n magicDivId?: string;\n}\n\n/**\n * @typedef CallState\n * @description Represents all possible call states from API and UI\n */\nexport type CallState =\n // User-facing states\n | 'idle'\n | 'ringing'\n | 'joining'\n | 'reconnecting'\n | 'joined'\n | 'leaving'\n | 'left'\n // Event-specific states\n | 'created'\n | 'session_started'\n | 'rejected'\n | 'missed'\n | 'accepted'\n | 'ended'\n | 'unknown';\n\n/**\n * @typedef CallType\n * @description Represents the pre-defined types of a call.\n * - `default`: Simple 1-1 or group video calling with sensible defaults. Video/audio enabled, backstage disabled. Admins/hosts have elevated permissions.\n * - `audio_room`: For audio-only spaces (like Clubhouse). Backstage enabled (requires `goLive`), pre-configured permissions for requesting to speak.\n * - `livestream`: For one-to-many streaming. Backstage enabled (requires `goLive`), access granted to all authenticated users.\n * - `development`: For testing ONLY. All permissions enabled, backstage disabled. **Not recommended for production.**\n */\nexport type CallType = 'default' | 'audio_room' | 'livestream' | 'development';\n\n/**\n * @interface CallMember\n * @description Information about a call member/participant\n * @property {string} userId - User ID of the member\n * @property {string} [name] - Display name of the user\n * @property {string} [imageURL] - Profile image URL of the user\n * @property {string} [role] - Role of the user in the call\n */\nexport interface CallMember {\n /** User ID of the member */\n userId: string;\n /** Display name of the user */\n name?: string;\n /** Profile image URL of the user */\n imageURL?: string;\n /** Role of the user in the call */\n role?: string;\n}\n\n/**\n * @interface CallEvent\n * @description Event emitted when call state changes\n * @property {string} callId - Unique identifier of the call\n * @property {CallState} state - Current state of the call\n * @property {string} [userId] - User ID of the participant who triggered the event\n * @property {string} [reason] - Reason for the call state change\n * @property {CallMember} [caller] - Information about the caller (for incoming calls)\n * @property {CallMember[]} [members] - List of call members\n */\nexport interface CallEvent {\n /** ID of the call */\n callId: string;\n /** Current state of the call */\n state: CallState;\n /** User ID of the participant in the call who triggered the event */\n userId?: string;\n /** Reason for the call state change, if applicable */\n reason?: string;\n /** Information about the caller (for incoming calls) */\n caller?: CallMember;\n /** List of call members */\n members?: CallMember[];\n}\n\nexport interface CameraEnabledResponse {\n enabled: boolean;\n}\n\n/**\n * @interface SuccessResponse\n * @description Standard response indicating operation success/failure\n * @property {boolean} success - Whether the operation succeeded\n */\nexport interface SuccessResponse {\n /** Whether the operation was successful */\n success: boolean;\n}\n\n/**\n * @interface CallOptions\n * @description Options for initiating a video call\n * @property {string[]} userIds - IDs of the users to call\n * @property {CallType} [type=default] - Type of call\n * @property {boolean} [ring=true] - Whether to send ring notification\n * @property {string} [team] - Team name to call\n */\nexport interface CallOptions {\n /** User ID of the person to call */\n userIds: string[];\n /** Type of call, defaults to 'default' */\n type?: CallType;\n /** Whether to ring the other user, defaults to true */\n ring?: boolean;\n /** Team name to call */\n team?: string;\n}\n\n/**\n * @interface StreamCallPlugin\n * @description Capacitor plugin for Stream Video calling functionality\n */\nexport interface StreamCallPlugin {\n /**\n * Login to Stream Video service\n * @param {LoginOptions} options - Login configuration\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.login({\n * token: 'your-token',\n * userId: 'user-123',\n * name: 'John Doe',\n * apiKey: 'your-api-key'\n * });\n */\n login(options: LoginOptions): Promise<SuccessResponse>;\n\n /**\n * Logout from Stream Video service\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.logout();\n */\n logout(): Promise<SuccessResponse>;\n\n /**\n * Initiate a call to another user\n * @param {CallOptions} options - Call configuration\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.call({\n * userId: 'user-456',\n * type: 'video',\n * ring: true\n * });\n */\n call(options: CallOptions): Promise<SuccessResponse>;\n\n /**\n * End the current call\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.endCall();\n */\n endCall(): Promise<SuccessResponse>;\n\n /**\n * Enable or disable microphone\n * @param {{ enabled: boolean }} options - Microphone state\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.setMicrophoneEnabled({ enabled: false });\n */\n setMicrophoneEnabled(options: { enabled: boolean }): Promise<SuccessResponse>;\n\n /**\n * Enable or disable camera\n * @param {{ enabled: boolean }} options - Camera state\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.setCameraEnabled({ enabled: false });\n */\n setCameraEnabled(options: { enabled: boolean }): Promise<SuccessResponse>;\n\n /**\n * Add listener for call events\n * @param {'callEvent'} eventName - Name of the event to listen for\n * @param {(event: CallEvent) => void} listenerFunc - Callback function\n * @returns {Promise<{ remove: () => Promise<void> }>} Function to remove listener\n * @example\n * const listener = await StreamCall.addListener('callEvent', (event) => {\n * console.log(`Call ${event.callId} is now ${event.state}`);\n * });\n */\n addListener(\n eventName: 'callEvent',\n listenerFunc: (event: CallEvent) => void,\n ): Promise<{ remove: () => Promise<void> }>;\n\n /**\n * Listen for lock-screen incoming call (Android only).\n * Fired when the app is shown by full-screen intent before user interaction.\n */\n addListener(\n eventName: 'incomingCall',\n listenerFunc: (event: IncomingCallPayload) => void,\n ): Promise<{ remove: () => Promise<void> }>;\n\n /**\n * Remove all event listeners\n * @returns {Promise<void>}\n * @example\n * await StreamCall.removeAllListeners();\n */\n removeAllListeners(): Promise<void>;\n\n /**\n * Accept an incoming call\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.acceptCall();\n */\n acceptCall(): Promise<SuccessResponse>;\n\n /**\n * Reject an incoming call\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.rejectCall();\n */\n rejectCall(): Promise<SuccessResponse>;\n\n /**\n * Check if camera is enabled\n * @returns {Promise<CameraEnabledResponse>} Camera enabled status\n * @example\n * const isCameraEnabled = await StreamCall.isCameraEnabled();\n * console.log(isCameraEnabled);\n */\n isCameraEnabled(): Promise<CameraEnabledResponse>;\n\n /**\n * Get the current call status\n * @returns {Promise<CallEvent>} Current call status as a CallEvent\n * @example\n * const callStatus = await StreamCall.getCallStatus();\n * console.log(callStatus);\n */\n getCallStatus(): Promise<CallEvent>;\n\n /**\n * Set speakerphone on\n * @param {{ name: string }} options - Speakerphone name\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.setSpeaker({ name: 'speaker' });\n */\n setSpeaker(options: { name: string }): Promise<SuccessResponse>;\n\n /**\n * Switch camera\n * @param {{ camera: 'front' | 'back' }} options - Camera to switch to\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.switchCamera({ camera: 'back' });\n */\n switchCamera(options: { camera: 'front' | 'back' }): Promise<SuccessResponse>;\n\n /**\n * Get detailed information about an active call including caller details\n * @param options - Options containing the call ID\n */\n getCallInfo(options: { callId: string }): Promise<CallEvent>;\n}\n\n/**\n * @interface IncomingCallPayload\n * @description Payload delivered with \"incomingCall\" event (Android lock-screen).\n * @property {string} cid - Call CID (type:id)\n * @property {string} type - Always \"incoming\" for this event\n * @property {CallMember} [caller] - Information about the caller\n */\nexport interface IncomingCallPayload {\n /** Full call CID (e.g. default:123) */\n cid: string;\n /** Event type (currently always \"incoming\") */\n type: 'incoming';\n /** Information about the caller */\n caller?: CallMember;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * @interface LoginOptions\n * @description Configuration options for logging into the Stream Video service\n * @property {string} token - Stream Video API token for authentication\n * @property {string} userId - Unique identifier for the current user\n * @property {string} name - Display name for the current user\n * @property {string} [imageURL] - Avatar URL for the current user\n * @property {string} apiKey - Stream Video API key for your application\n * @property {string} [magicDivId] - DOM element ID where video will be rendered\n */\nexport interface LoginOptions {\n /** Stream Video API token */\n token: string;\n /** User ID for the current user */\n userId: string;\n /** Display name for the current user */\n name: string;\n /** Optional avatar URL for the current user */\n imageURL?: string;\n /** Stream Video API key */\n apiKey: string;\n /** ID of the HTML element where the video will be rendered */\n magicDivId?: string;\n pushNotificationsConfig?: PushNotificationsConfig;\n}\n\nexport interface PushNotificationsConfig {\n pushProviderName: string;\n voipProviderName: string;\n}\n\n/**\n * @typedef CallState\n * @description Represents all possible call states from API and UI\n */\nexport type CallState =\n // User-facing states\n | 'idle'\n | 'ringing'\n | 'joining'\n | 'reconnecting'\n | 'joined'\n | 'leaving'\n | 'left'\n // Event-specific states\n | 'created'\n | 'session_started'\n | 'rejected'\n | 'missed'\n | 'accepted'\n | 'ended'\n | 'unknown';\n\n/**\n * @typedef CallType\n * @description Represents the pre-defined types of a call.\n * - `default`: Simple 1-1 or group video calling with sensible defaults. Video/audio enabled, backstage disabled. Admins/hosts have elevated permissions.\n * - `audio_room`: For audio-only spaces (like Clubhouse). Backstage enabled (requires `goLive`), pre-configured permissions for requesting to speak.\n * - `livestream`: For one-to-many streaming. Backstage enabled (requires `goLive`), access granted to all authenticated users.\n * - `development`: For testing ONLY. All permissions enabled, backstage disabled. **Not recommended for production.**\n */\nexport type CallType = 'default' | 'audio_room' | 'livestream' | 'development';\n\n/**\n * @interface CallMember\n * @description Information about a call member/participant\n * @property {string} userId - User ID of the member\n * @property {string} [name] - Display name of the user\n * @property {string} [imageURL] - Profile image URL of the user\n * @property {string} [role] - Role of the user in the call\n */\nexport interface CallMember {\n /** User ID of the member */\n userId: string;\n /** Display name of the user */\n name?: string;\n /** Profile image URL of the user */\n imageURL?: string;\n /** Role of the user in the call */\n role?: string;\n}\n\n/**\n * @interface CallEvent\n * @description Event emitted when call state changes\n * @property {string} callId - Unique identifier of the call\n * @property {CallState} state - Current state of the call\n * @property {string} [userId] - User ID of the participant who triggered the event\n * @property {string} [reason] - Reason for the call state change\n * @property {CallMember} [caller] - Information about the caller (for incoming calls)\n * @property {CallMember[]} [members] - List of call members\n */\nexport interface CallEvent {\n /** ID of the call */\n callId: string;\n /** Current state of the call */\n state: CallState;\n /** User ID of the participant in the call who triggered the event */\n userId?: string;\n /** Reason for the call state change, if applicable */\n reason?: string;\n /** Information about the caller (for incoming calls) */\n caller?: CallMember;\n /** List of call members */\n members?: CallMember[];\n}\n\nexport interface CameraEnabledResponse {\n enabled: boolean;\n}\n\n/**\n * @interface SuccessResponse\n * @description Standard response indicating operation success/failure\n * @property {boolean} success - Whether the operation succeeded\n */\nexport interface SuccessResponse {\n /** Whether the operation was successful */\n success: boolean;\n}\n\n/**\n * @interface CallOptions\n * @description Options for initiating a video call\n * @property {string[]} userIds - IDs of the users to call\n * @property {CallType} [type=default] - Type of call\n * @property {boolean} [ring=true] - Whether to send ring notification\n * @property {string} [team] - Team name to call\n */\nexport interface CallOptions {\n /** User ID of the person to call */\n userIds: string[];\n /** Type of call, defaults to 'default' */\n type?: CallType;\n /** Whether to ring the other user, defaults to true */\n ring?: boolean;\n /** Team name to call */\n team?: string;\n /** Whether to start the call with video enabled, defaults to false */\n video?: boolean;\n}\n\n/**\n * @interface StreamCallPlugin\n * @description Capacitor plugin for Stream Video calling functionality\n */\nexport interface StreamCallPlugin {\n /**\n * Login to Stream Video service\n * @param {LoginOptions} options - Login configuration\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.login({\n * token: 'your-token',\n * userId: 'user-123',\n * name: 'John Doe',\n * apiKey: 'your-api-key'\n * });\n */\n login(options: LoginOptions): Promise<SuccessResponse>;\n\n /**\n * Logout from Stream Video service\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.logout();\n */\n logout(): Promise<SuccessResponse>;\n\n /**\n * Initiate a call to another user\n * @param {CallOptions} options - Call configuration\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.call({\n * userId: 'user-456',\n * type: 'video',\n * ring: true\n * });\n */\n call(options: CallOptions): Promise<SuccessResponse>;\n\n /**\n * End the current call\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.endCall();\n */\n endCall(): Promise<SuccessResponse>;\n\n /**\n * Enable or disable microphone\n * @param {{ enabled: boolean }} options - Microphone state\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.setMicrophoneEnabled({ enabled: false });\n */\n setMicrophoneEnabled(options: { enabled: boolean }): Promise<SuccessResponse>;\n\n /**\n * Enable or disable camera\n * @param {{ enabled: boolean }} options - Camera state\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.setCameraEnabled({ enabled: false });\n */\n setCameraEnabled(options: { enabled: boolean }): Promise<SuccessResponse>;\n\n /**\n * Add listener for call events\n * @param {'callEvent'} eventName - Name of the event to listen for\n * @param {(event: CallEvent) => void} listenerFunc - Callback function\n * @returns {Promise<{ remove: () => Promise<void> }>} Function to remove listener\n * @example\n * const listener = await StreamCall.addListener('callEvent', (event) => {\n * console.log(`Call ${event.callId} is now ${event.state}`);\n * });\n */\n addListener(\n eventName: 'callEvent',\n listenerFunc: (event: CallEvent) => void,\n ): Promise<{ remove: () => Promise<void> }>;\n\n /**\n * Listen for lock-screen incoming call (Android only).\n * Fired when the app is shown by full-screen intent before user interaction.\n */\n addListener(\n eventName: 'incomingCall',\n listenerFunc: (event: IncomingCallPayload) => void,\n ): Promise<{ remove: () => Promise<void> }>;\n\n /**\n * Remove all event listeners\n * @returns {Promise<void>}\n * @example\n * await StreamCall.removeAllListeners();\n */\n removeAllListeners(): Promise<void>;\n\n /**\n * Accept an incoming call\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.acceptCall();\n */\n acceptCall(): Promise<SuccessResponse>;\n\n /**\n * Reject an incoming call\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.rejectCall();\n */\n rejectCall(): Promise<SuccessResponse>;\n\n /**\n * Check if camera is enabled\n * @returns {Promise<CameraEnabledResponse>} Camera enabled status\n * @example\n * const isCameraEnabled = await StreamCall.isCameraEnabled();\n * console.log(isCameraEnabled);\n */\n isCameraEnabled(): Promise<CameraEnabledResponse>;\n\n /**\n * Get the current call status\n * @returns {Promise<CallEvent>} Current call status as a CallEvent\n * @example\n * const callStatus = await StreamCall.getCallStatus();\n * console.log(callStatus);\n */\n getCallStatus(): Promise<CallEvent>;\n\n /**\n * Set speakerphone on\n * @param {{ name: string }} options - Speakerphone name\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.setSpeaker({ name: 'speaker' });\n */\n setSpeaker(options: { name: string }): Promise<SuccessResponse>;\n\n /**\n * Switch camera\n * @param {{ camera: 'front' | 'back' }} options - Camera to switch to\n * @returns {Promise<SuccessResponse>} Success status\n * @example\n * await StreamCall.switchCamera({ camera: 'back' });\n */\n switchCamera(options: { camera: 'front' | 'back' }): Promise<SuccessResponse>;\n\n /**\n * Get detailed information about an active call including caller details\n * @param options - Options containing the call ID\n */\n getCallInfo(options: { callId: string }): Promise<CallEvent>;\n}\n\n/**\n * @interface IncomingCallPayload\n * @description Payload delivered with \"incomingCall\" event (Android lock-screen).\n * @property {string} cid - Call CID (type:id)\n * @property {string} type - Always \"incoming\" for this event\n * @property {CallMember} [caller] - Information about the caller\n */\nexport interface IncomingCallPayload {\n /** Full call CID (e.g. default:123) */\n cid: string;\n /** Event type (currently always \"incoming\") */\n type: 'incoming';\n /** Information about the caller */\n caller?: CallMember;\n}\n"]}
@@ -45,9 +45,12 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
45
45
  private var tokenSubscription: AnyCancellable?
46
46
  private var activeCallSubscription: AnyCancellable?
47
47
  private var lastVoIPToken: String?
48
+ private var touchInterceptView: TouchInterceptView?
48
49
 
49
50
  private var streamVideo: StreamVideo?
50
51
 
52
+ private var pushNotificationsConfig: PushNotificationsConfig?
53
+
51
54
  // Store current call info for getCallStatus
52
55
  private var currentCallId: String = ""
53
56
  private var currentCallState: String = ""
@@ -230,6 +233,9 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
230
233
  print("- In call state detected")
231
234
  print("- All participants: \(String(describing: viewModel.participants))")
232
235
 
236
+ // Enable touch interceptor when call becomes active
237
+ self.touchInterceptView?.setCallActive(true)
238
+
233
239
  // Create/update overlay and make visible when there's an active call
234
240
  self.createCallOverlayView()
235
241
 
@@ -283,17 +289,18 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
283
289
  self.updateCallStatusAndNotify(callId: incomingCall.id, state: "ringing", caller: caller, members: members)
284
290
  }
285
291
  } else if newState == .idle {
286
- // Get the call ID that was active before the state changed
287
- let endingCallId = viewModel.call?.cId
288
- print("Call state changed to idle. EndingCallId: \(String(describing: endingCallId)), ActiveCall: \(String(describing: self.streamVideo?.state.activeCall?.cId))")
292
+ print("Call state changed to idle. CurrentCallId: \(self.currentCallId), ActiveCall: \(String(describing: self.streamVideo?.state.activeCall?.cId))")
289
293
 
290
- // Only notify about call ending if we have a valid call ID and there's truly no active call
294
+ // Disable touch interceptor when call becomes inactive
295
+ self.touchInterceptView?.setCallActive(false)
296
+
297
+ // Only notify about call ending if we have a valid stored call ID and there's truly no active call
291
298
  // This prevents false "left" events during normal state transitions
292
- if let callId = endingCallId, !callId.isEmpty, self.streamVideo?.state.activeCall == nil {
293
- print("Call actually ending: \(callId)")
299
+ if !self.currentCallId.isEmpty && self.streamVideo?.state.activeCall == nil {
300
+ print("Call actually ending: \(self.currentCallId)")
294
301
 
295
- // Notify that call has ended - use the properly tracked call ID
296
- self.updateCallStatusAndNotify(callId: callId, state: "left")
302
+ // Notify that call has ended - use the stored call ID
303
+ self.updateCallStatusAndNotify(callId: self.currentCallId, state: "left")
297
304
 
298
305
  // Reset notification flag when call ends
299
306
  self.hasNotifiedCallJoined = false
@@ -301,7 +308,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
301
308
  // Remove the call overlay view when not in a call
302
309
  self.ensureViewRemoved()
303
310
  } else {
304
- print("Not sending left event - CallId: \(String(describing: endingCallId)), ActiveCall exists: \(self.streamVideo?.state.activeCall != nil)")
311
+ print("Not sending left event - CurrentCallId: \(self.currentCallId), ActiveCall exists: \(self.streamVideo?.state.activeCall != nil)")
305
312
  }
306
313
  }
307
314
  } catch {
@@ -355,6 +362,17 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
355
362
  customData: [:]
356
363
  )
357
364
 
365
+ // Get push notifications config if provided
366
+ if let pushConfig = call.getObject("pushNotificationsConfig") {
367
+ let pushProviderName = pushConfig["pushProviderName"] as? String ?? "ios-apn"
368
+ let voipProviderName = pushConfig["voipProviderName"] as? String ?? "ios-voip"
369
+
370
+ self.pushNotificationsConfig = PushNotificationsConfig(
371
+ pushProviderInfo: PushProviderInfo(name: pushProviderName, pushProvider: .apn),
372
+ voipPushProviderInfo: PushProviderInfo(name: voipProviderName, pushProvider: .apn)
373
+ )
374
+ }
375
+
358
376
  let credentials = UserCredentials(user: user, tokenValue: token)
359
377
  SecureUserRepository.shared.save(user: credentials)
360
378
  // Initialize Stream Video with new credentials
@@ -403,6 +421,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
403
421
  Task { @MainActor in
404
422
  // self.overlayViewModel?.updateCall(nil)
405
423
  // self.overlayViewModel?.updateStreamVideo(nil)
424
+ self.touchInterceptView?.setCallActive(false)
406
425
  self.overlayView?.isHidden = true
407
426
  self.webView?.isOpaque = true
408
427
  }
@@ -454,6 +473,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
454
473
  let callType = call.getString("type") ?? "default"
455
474
  let shouldRing = call.getBool("ring") ?? true
456
475
  let team = call.getString("team")
476
+ let video = call.getBool("video") ?? false
457
477
 
458
478
  // Generate a unique call ID
459
479
  let callId = UUID().uuidString
@@ -465,14 +485,13 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
465
485
  print("- Call Type: \(callType)")
466
486
  print("- Users: \(members)")
467
487
  print("- Should Ring: \(shouldRing)")
468
- print("- Team: \(team)")
488
+ print("- Team: \(String(describing: team))")
469
489
 
470
490
  // Create the call object
471
491
  await self.callViewModel?.startCall(
472
492
  callType: callType,
473
493
  callId: callId,
474
- members: members.map { Member(userId: $0, role: nil, customData: [:], updatedAt: nil) },
475
- ring: shouldRing
494
+ members: members.map { Member(userId: $0, role: nil, customData: [:], updatedAt: nil) }, team: team, ring: shouldRing, video: video
476
495
  )
477
496
 
478
497
  // Wait for call state to be populated by WebSocket events
@@ -585,6 +604,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
585
604
  }
586
605
 
587
606
  await MainActor.run {
607
+ self.touchInterceptView?.setCallActive(false)
588
608
  self.overlayView?.isHidden = true
589
609
  self.webView?.isOpaque = true
590
610
  }
@@ -613,6 +633,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
613
633
  await callViewModel?.hangUp()
614
634
 
615
635
  await MainActor.run {
636
+ self.touchInterceptView?.setCallActive(false)
616
637
  self.overlayView?.isHidden = true
617
638
  self.webView?.isOpaque = true
618
639
  }
@@ -771,6 +792,7 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
771
792
  apiKey: apiKey,
772
793
  user: savedCredentials.user,
773
794
  token: UserToken(stringLiteral: savedCredentials.tokenValue),
795
+ pushNotificationsConfig: self.pushNotificationsConfig ?? .default,
774
796
  tokenProvider: {completion in
775
797
  guard let savedCredentials = SecureUserRepository.shared.loadCurrentUser() else {
776
798
  print("No saved credentials or API key found, cannot refresh token")
@@ -850,6 +872,14 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
850
872
  parent.insertSubview(touchInterceptView, aboveSubview: webView)
851
873
  }
852
874
 
875
+ // Set up active call check function
876
+ touchInterceptView.setActiveCallCheck { [weak self] in
877
+ return self?.streamVideo?.state.activeCall != nil
878
+ }
879
+
880
+ // Store reference to touch intercept view
881
+ self.touchInterceptView = touchInterceptView
882
+
853
883
  // Setup constraints for touchInterceptView to cover the entire parent
854
884
  NSLayoutConstraint.activate([
855
885
  touchInterceptView.topAnchor.constraint(equalTo: parent.topAnchor),
@@ -907,6 +937,15 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
907
937
  // Ensure touch intercept view is on top
908
938
  if let touchInterceptView = parent.subviews.first(where: { $0 is TouchInterceptView }) {
909
939
  parent.bringSubviewToFront(touchInterceptView)
940
+ // Update reference and set call active
941
+ self.touchInterceptView = touchInterceptView as? TouchInterceptView
942
+
943
+ // Set up active call check function
944
+ self.touchInterceptView?.setActiveCallCheck { [weak self] in
945
+ return self?.streamVideo?.state.activeCall != nil
946
+ }
947
+
948
+ self.touchInterceptView?.setCallActive(true)
910
949
  } else {
911
950
  // Create touch intercept view if not already created
912
951
  let touchInterceptView = TouchInterceptView(frame: parent.bounds)
@@ -916,6 +955,15 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
916
955
  touchInterceptView.setupWithWebView(webView, overlayView: overlayView.view)
917
956
  parent.addSubview(touchInterceptView)
918
957
 
958
+ // Set up active call check function
959
+ touchInterceptView.setActiveCallCheck { [weak self] in
960
+ return self?.streamVideo?.state.activeCall != nil
961
+ }
962
+
963
+ // Store reference and set call active
964
+ self.touchInterceptView = touchInterceptView
965
+ self.touchInterceptView?.setCallActive(true)
966
+
919
967
  NSLayoutConstraint.activate([
920
968
  touchInterceptView.topAnchor.constraint(equalTo: parent.topAnchor),
921
969
  touchInterceptView.bottomAnchor.constraint(equalTo: parent.bottomAnchor),
@@ -926,6 +974,9 @@ public class StreamCallPlugin: CAPPlugin, CAPBridgedPlugin {
926
974
  }
927
975
 
928
976
  private func ensureViewRemoved() {
977
+ // Disable touch interceptor when overlay is removed
978
+ self.touchInterceptView?.setCallActive(false)
979
+
929
980
  // Check if we have an overlay view
930
981
  if let existingOverlayView = self.overlayView {
931
982
  print("Hiding call overlay view")
@@ -9,6 +9,8 @@ class TouchInterceptView: UIView {
9
9
  private var lastTouchPoint: CGPoint?
10
10
  private let touchThreshold: CGFloat = 5.0 // pixels
11
11
  private let timerDelay: TimeInterval = 0.1 // seconds
12
+ private var isCallActive: Bool = false
13
+ private var hasActiveCallCheck: (() -> Bool)?
12
14
 
13
15
  func setupWithWebView(_ webView: UIView, overlayView: UIView) {
14
16
  self.webView = webView
@@ -20,6 +22,34 @@ class TouchInterceptView: UIView {
20
22
  os_log(.debug, "TouchInterceptView: setupWithWebView - webView: %{public}s, overlayView: %{public}s", String(describing: webView), String(describing: overlayView))
21
23
  }
22
24
 
25
+ func setActiveCallCheck(_ check: @escaping () -> Bool) {
26
+ self.hasActiveCallCheck = check
27
+ }
28
+
29
+ func setCallActive(_ active: Bool) {
30
+ self.isCallActive = active
31
+ os_log(.debug, "TouchInterceptView: setCallActive - %{public}s", String(describing: active))
32
+
33
+ // Cancel any pending timer when call becomes inactive
34
+ if !active {
35
+ forwardTimer?.invalidate()
36
+ forwardTimer = nil
37
+ lastTouchPoint = nil
38
+ }
39
+ }
40
+
41
+ private func shouldInterceptTouches() -> Bool {
42
+ // Check both our flag and actual call state
43
+ let hasActiveCall = hasActiveCallCheck?() ?? false
44
+ let shouldIntercept = isCallActive && hasActiveCall
45
+
46
+ if isCallActive != hasActiveCall {
47
+ os_log(.debug, "TouchInterceptView: State mismatch - isCallActive: %{public}s, hasActiveCall: %{public}s", String(describing: isCallActive), String(describing: hasActiveCall))
48
+ }
49
+
50
+ return shouldIntercept
51
+ }
52
+
23
53
  private func isInteractive(_ view: UIView) -> Bool {
24
54
  if view is UIControl { return true }
25
55
  if let grs = view.gestureRecognizers, !grs.isEmpty { return true }
@@ -80,7 +110,19 @@ class TouchInterceptView: UIView {
80
110
  }
81
111
 
82
112
  override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
83
- os_log(.debug, "TouchInterceptView: hitTest entry at %{public}s", String(describing: point))
113
+
114
+ // Check if we should intercept touches
115
+ if !shouldInterceptTouches() {
116
+ if let webView = self.webView {
117
+ let webPoint = self.convert(point, to: webView)
118
+ let result = webView.hitTest(webPoint, with: event)
119
+ os_log(.debug, "TouchInterceptView: hitTest - Not intercepting, direct WebView result %{public}s at %{public}s", String(describing: result), String(describing: webPoint))
120
+ return result
121
+ }
122
+ return nil
123
+ }
124
+
125
+ os_log(.debug, "TouchInterceptView: hitTest entry at %{public}s, callActive: %{public}s", String(describing: point), String(describing: isCallActive))
84
126
 
85
127
  // Check if this is same touch location continuing
86
128
  if let lastPoint = lastTouchPoint {
@@ -120,6 +162,17 @@ class TouchInterceptView: UIView {
120
162
  }
121
163
 
122
164
  override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
165
+ // Check if we should intercept touches
166
+ if !shouldInterceptTouches() {
167
+ guard let webView = self.webView else {
168
+ return super.point(inside: point, with: event)
169
+ }
170
+ let webViewPoint = self.convert(point, to: webView)
171
+ let result = webView.point(inside: webViewPoint, with: event)
172
+ os_log(.debug, "TouchInterceptView: point(inside) - Not intercepting, WebView only (%{public}s at %{public}s) for original point %{public}s = %s", String(describing: result), String(describing: webViewPoint), String(describing: point), String(describing: result))
173
+ return result
174
+ }
175
+
123
176
  guard let webView = self.webView else {
124
177
  os_log(.debug, "TouchInterceptView: point(inside) - webView is nil for point %{public}s. Checking overlay or deferring to super.", String(describing: point))
125
178
  if let overlayView = self.overlayView, !overlayView.isHidden {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-stream-call",
3
- "version": "0.0.51",
3
+ "version": "0.0.56",
4
4
  "description": "Uses the https://getstream.io/ SDK to implement calling in Capacitor",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",