rns-nativecall 0.8.0 → 0.8.1

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/index.d.ts CHANGED
@@ -80,6 +80,8 @@ export interface CallHandlerType {
80
80
 
81
81
  requestOverlayPermission(): Promise<boolean>;
82
82
 
83
+ reportRemoteEnded(uuid: string, reason: number): void;
84
+
83
85
  checkOverlayPermission(): Promise<boolean>;
84
86
 
85
87
  /**
package/index.js CHANGED
@@ -47,6 +47,11 @@ export const CallHandler = {
47
47
  },
48
48
  // ------------------------------------
49
49
 
50
+ reportRemoteEnded: async (uuid, endReason) => {
51
+ if (!CallModule?.reportRemoteEnded) return;
52
+ await CallModule.reportRemoteEnded(uuid.toLowerCase().trim(), endReason);
53
+ },
54
+
50
55
  requestOverlayPermission: async () => {
51
56
  if (!CallModule?.requestOverlayPermission) return false;
52
57
  return await CallModule.requestOverlayPermission();
package/ios/CallModule.m CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  @interface CallModule ()
5
5
  @property (nonatomic, strong) NSString *pendingCallUuid;
6
+ @property (nonatomic, strong) NSMutableDictionary *pendingEvents;
6
7
  @end
7
8
 
8
9
  @implementation CallModule
@@ -20,6 +21,7 @@ RCT_EXPORT_MODULE();
20
21
  - (instancetype)init {
21
22
  self = [super init];
22
23
  if (self) {
24
+ self.pendingEvents = [NSMutableDictionary new];
23
25
  NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"] ?: [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
24
26
 
25
27
  CXProviderConfiguration *config = [[CXProviderConfiguration alloc] initWithLocalizedName:appName];
@@ -35,6 +37,34 @@ RCT_EXPORT_MODULE();
35
37
  return self;
36
38
  }
37
39
 
40
+ // MARK: - New Parity Methods (Synced with Android)
41
+
42
+ RCT_EXPORT_METHOD(checkCallValidity:(NSString *)uuidString resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
43
+ BOOL isCurrent = [uuidString isEqualToString:self.currentCallUUID.UUIDString];
44
+ resolve(@{
45
+ @"isValid": @(isCurrent),
46
+ @"isCanceled": @(!isCurrent)
47
+ });
48
+ }
49
+
50
+ RCT_EXPORT_METHOD(getInitialCallData:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
51
+ NSMutableDictionary *result = [NSMutableDictionary new];
52
+
53
+ if (self.pendingCallUuid) {
54
+ result[@"default"] = @{@"callUuid": self.pendingCallUuid};
55
+ self.pendingCallUuid = nil;
56
+ }
57
+
58
+ if (self.pendingEvents.count > 0) {
59
+ [result addEntriesFromDictionary:self.pendingEvents];
60
+ [self.pendingEvents removeAllObjects];
61
+ }
62
+
63
+ resolve(result.count > 0 ? result : [NSNull null]);
64
+ }
65
+
66
+ // MARK: - Core Logic
67
+
38
68
  RCT_EXPORT_METHOD(displayIncomingCall:(NSString *)uuidString
39
69
  name:(NSString *)name
40
70
  callType:(NSString *)callType
@@ -50,9 +80,9 @@ RCT_EXPORT_METHOD(displayIncomingCall:(NSString *)uuidString
50
80
  self.currentCallUUID = uuid;
51
81
 
52
82
  AVAudioSession *session = [AVAudioSession sharedInstance];
53
- [session setCategory:AVAudioSessionCategoryPlayAndRecord
54
- mode:AVAudioSessionModeVoiceChat
55
- options:AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionDefaultToSpeaker
83
+ [session setCategory:AVAudioSessionCategoryPlayAndRecord
84
+ mode:AVAudioSessionModeVoiceChat
85
+ options:AVAudioSessionCategoryOptionAllowBluetoothHFP | AVAudioSessionCategoryOptionDefaultToSpeaker
56
86
  error:nil];
57
87
 
58
88
  CXCallUpdate *update = [[CXCallUpdate alloc] init];
@@ -68,38 +98,35 @@ RCT_EXPORT_METHOD(displayIncomingCall:(NSString *)uuidString
68
98
  }];
69
99
  }
70
100
 
71
- RCT_EXPORT_METHOD(endNativeCall:(NSString *)uuidString)
72
- {
101
+ // Handles programmatic ending (e.g., via FCM "CANCEL")
102
+ RCT_EXPORT_METHOD(reportRemoteEnded:(NSString *)uuidString reason:(NSInteger)reason) {
73
103
  NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
74
104
  if (!uuid) return;
75
105
 
76
- [self.provider reportCallWithUUID:uuid
77
- endedAtDate:[NSDate date]
78
- reason:CXCallEndedReasonRemoteEnded];
79
- self.currentCallUUID = nil;
80
- self.pendingCallUuid = nil;
81
- }
82
-
83
- RCT_EXPORT_METHOD(getInitialCallData:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
84
- if (self.pendingCallUuid) {
85
- resolve(@{@"callUuid": self.pendingCallUuid});
86
- self.pendingCallUuid = nil;
87
- } else {
88
- resolve([NSNull null]);
106
+ // Report to CallKit that the remote user ended the call
107
+ [self.provider reportCallWithUUID:uuid
108
+ endedAtDate:[NSDate date]
109
+ reason:(CXCallEndedReason)reason];
110
+
111
+ // Clear local tracking
112
+ if ([uuid isEqual:self.currentCallUUID]) {
113
+ self.currentCallUUID = nil;
114
+ self.pendingCallUuid = nil;
89
115
  }
90
116
  }
91
117
 
92
- RCT_EXPORT_METHOD(checkTelecomPermissions:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
93
- resolve(@YES);
118
+ RCT_EXPORT_METHOD(endNativeCall:(NSString *)uuidString)
119
+ {
120
+ [self reportRemoteEnded:uuidString reason:CXCallEndedReasonRemoteEnded];
94
121
  }
95
122
 
96
123
  // MARK: - CXProviderDelegate
97
124
 
98
125
  - (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
99
126
  AVAudioSession *session = [AVAudioSession sharedInstance];
100
- [session setCategory:AVAudioSessionCategoryPlayAndRecord
101
- mode:AVAudioSessionModeVoiceChat
102
- options:AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionDefaultToSpeaker
127
+ [session setCategory:AVAudioSessionCategoryPlayAndRecord
128
+ mode:AVAudioSessionModeVoiceChat
129
+ options:AVAudioSessionCategoryOptionAllowBluetoothHFP | AVAudioSessionCategoryOptionDefaultToSpeaker
103
130
  error:nil];
104
131
  [session setActive:YES error:nil];
105
132
 
@@ -108,9 +135,12 @@ RCT_EXPORT_METHOD(checkTelecomPermissions:(RCTPromiseResolveBlock)resolve reject
108
135
  NSString *uuidStr = [action.callUUID.UUIDString lowercaseString];
109
136
  self.pendingCallUuid = uuidStr;
110
137
 
111
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
112
- // NUDGE: Force the window to become visible.
113
- // This helps transition from "Ghost Mode" to "Active UI" once the user unlocks.
138
+ // Switch from system UI to App UI
139
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
140
+ [self.provider reportCallWithUUID:action.callUUID
141
+ endedAtDate:[NSDate date]
142
+ reason:CXCallEndedReasonAnsweredElsewhere];
143
+
114
144
  dispatch_async(dispatch_get_main_queue(), ^{
115
145
  [[[UIApplication sharedApplication] keyWindow] makeKeyAndVisible];
116
146
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rns-nativecall",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "High-performance React Native module for handling native VoIP call UI on Android and iOS.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",