react-native-iinstall 0.2.12 → 0.2.13

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.
@@ -48,14 +48,14 @@ yarn add react-native-iinstall
48
48
 
49
49
  **Note**: The SDK automatically includes necessary dependencies like `react-native-sensors` and `react-native-view-shot`.
50
50
 
51
- ### Updating to Latest Version (v0.2.7)
51
+ ### Updating to Latest Version (v0.2.13)
52
52
 
53
53
  If you're using an older version, update to get the latest audio/video improvements:
54
54
 
55
55
  ```bash
56
- npm install react-native-iinstall@0.2.7
56
+ npm install react-native-iinstall@0.2.13
57
57
  # OR
58
- yarn add react-native-iinstall@0.2.7
58
+ yarn add react-native-iinstall@0.2.13
59
59
  ```
60
60
 
61
61
  **Key improvements in v0.2.7:**
@@ -98,7 +98,45 @@ export default App;
98
98
 
99
99
  ---
100
100
 
101
- ## 5. Audio & Video Feedback (v0.2.7+)
101
+ ## 5. Push Notification Token Registration (v0.2.13+)
102
+
103
+ If you pass `pushToken`, SDK auto-registers it to the backend using:
104
+
105
+ `POST /api/notifications/push/register`
106
+
107
+ ```tsx
108
+ import React from 'react';
109
+ import messaging from '@react-native-firebase/messaging';
110
+ import { IInstall } from 'react-native-iinstall';
111
+
112
+ const App = () => {
113
+ const [pushToken, setPushToken] = React.useState<string>();
114
+
115
+ React.useEffect(() => {
116
+ messaging().getToken().then(setPushToken).catch(() => undefined);
117
+ }, []);
118
+
119
+ return (
120
+ <IInstall
121
+ apiKey="YOUR_COPIED_API_KEY_FROM_STEP_1"
122
+ apiEndpoint="https://iinstall.app"
123
+ pushToken={pushToken}
124
+ >
125
+ <AppNavigation />
126
+ </IInstall>
127
+ );
128
+ };
129
+ ```
130
+
131
+ Manual helpers are also exported:
132
+
133
+ ```tsx
134
+ import { registerPushToken, unregisterPushToken } from 'react-native-iinstall';
135
+ ```
136
+
137
+ ---
138
+
139
+ ## 6. Audio & Video Feedback (v0.2.7+)
102
140
 
103
141
  The SDK now supports **Audio Feedback** (Voice Notes) and **Screen Recording** with enhanced compatibility and reliability.
104
142
 
@@ -155,7 +193,7 @@ Add these keys inside the `<dict>` tag:
155
193
 
156
194
  ---
157
195
 
158
- ## 6. Generate & Upload Installer
196
+ ## 7. Generate & Upload Installer
159
197
 
160
198
  1. **Build your app**:
161
199
  * **Android**: `./gradlew assembleRelease` (outputs `.apk`)
package/README.md CHANGED
@@ -49,13 +49,24 @@ Wrap your main app component with the `<IInstall>` provider.
49
49
  import React from 'react';
50
50
  import { IInstall } from 'react-native-iinstall';
51
51
  import AppNavigation from './src/AppNavigation';
52
+ import messaging from '@react-native-firebase/messaging';
52
53
 
53
54
  const App = () => {
55
+ const [pushToken, setPushToken] = React.useState<string>();
56
+
57
+ React.useEffect(() => {
58
+ messaging()
59
+ .getToken()
60
+ .then(setPushToken)
61
+ .catch(() => undefined);
62
+ }, []);
63
+
54
64
  return (
55
65
  // Get your API Key from the IInstall Dashboard (Project Settings)
56
66
  <IInstall
57
67
  apiKey="YOUR_PROJECT_API_KEY"
58
68
  apiEndpoint="https://iinstall.app" // Optional, defaults to production
69
+ pushToken={pushToken} // Optional: auto-registers token to iinstall backend
59
70
  enabled={__DEV__} // Optional: Only enable in dev/test builds
60
71
  >
61
72
  <AppNavigation />
@@ -131,6 +142,26 @@ Add to `Info.plist`:
131
142
  </IInstall>
132
143
  ```
133
144
 
145
+ ### Push Token Registration Helpers
146
+
147
+ You can also register/unregister tokens manually:
148
+
149
+ ```tsx
150
+ import { registerPushToken, unregisterPushToken } from 'react-native-iinstall';
151
+
152
+ await registerPushToken({
153
+ token: fcmToken,
154
+ apiKey: 'YOUR_PROJECT_API_KEY',
155
+ apiEndpoint: 'https://iinstall.app',
156
+ });
157
+
158
+ await unregisterPushToken({
159
+ token: fcmToken,
160
+ apiKey: 'YOUR_PROJECT_API_KEY',
161
+ apiEndpoint: 'https://iinstall.app',
162
+ });
163
+ ```
164
+
134
165
  ### Custom Triggers (Coming Soon)
135
166
  - Button-based feedback
136
167
  - Screenshot-only mode
@@ -7,6 +7,10 @@ interface IInstallWrapperProps {
7
7
  showDebugButton?: boolean;
8
8
  showFloatingButtonOnEmulator?: boolean;
9
9
  floatingButtonLabel?: string;
10
+ pushToken?: string;
11
+ autoRegisterPushToken?: boolean;
12
+ projectId?: string;
13
+ onPushTokenRegisterError?: (error: string) => void;
10
14
  }
11
15
  export declare const IInstallWrapper: React.FC<IInstallWrapperProps>;
12
16
  export default IInstallWrapper;
@@ -9,8 +9,8 @@ const index_1 = require("./index");
9
9
  // Backward-compatible wrapper. The core IInstall component now handles:
10
10
  // - shake gesture on real devices
11
11
  // - floating manual trigger button on simulator/emulator
12
- const IInstallWrapper = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enabled = true, showDebugButton: _showDebugButton = false, showFloatingButtonOnEmulator = true, floatingButtonLabel = 'Report Issue', }) => {
13
- return (<index_1.IInstall apiKey={apiKey} apiEndpoint={apiEndpoint} enabled={enabled} showFloatingButtonOnEmulator={showFloatingButtonOnEmulator} floatingButtonLabel={floatingButtonLabel}>
12
+ const IInstallWrapper = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enabled = true, showDebugButton: _showDebugButton = false, showFloatingButtonOnEmulator = true, floatingButtonLabel = 'Report Issue', pushToken, autoRegisterPushToken = true, projectId, onPushTokenRegisterError, }) => {
13
+ return (<index_1.IInstall apiKey={apiKey} apiEndpoint={apiEndpoint} enabled={enabled} showFloatingButtonOnEmulator={showFloatingButtonOnEmulator} floatingButtonLabel={floatingButtonLabel} pushToken={pushToken} autoRegisterPushToken={autoRegisterPushToken} projectId={projectId} onPushTokenRegisterError={onPushTokenRegisterError}>
14
14
  {children}
15
15
  </index_1.IInstall>);
16
16
  };
package/lib/index.d.ts CHANGED
@@ -6,7 +6,12 @@ interface IInstallProps {
6
6
  enabled?: boolean;
7
7
  showFloatingButtonOnEmulator?: boolean;
8
8
  floatingButtonLabel?: string;
9
+ pushToken?: string;
10
+ autoRegisterPushToken?: boolean;
11
+ projectId?: string;
12
+ onPushTokenRegisterError?: (error: string) => void;
9
13
  }
10
14
  export declare const IInstall: React.FC<IInstallProps>;
11
15
  export default IInstall;
12
16
  export { IInstallWrapper } from './IInstallWrapper';
17
+ export { registerPushToken, unregisterPushToken, type RegisterPushTokenParams, type UnregisterPushTokenParams, type PushRegistrationResult, type PushPlatform, } from './pushRegistration';
package/lib/index.js CHANGED
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.IInstallWrapper = exports.IInstall = void 0;
39
+ exports.unregisterPushToken = exports.registerPushToken = exports.IInstallWrapper = exports.IInstall = void 0;
40
40
  const react_1 = __importStar(require("react"));
41
41
  const react_native_1 = require("react-native");
42
42
  const react_native_view_shot_1 = require("react-native-view-shot");
@@ -44,13 +44,15 @@ const ShakeDetector_1 = require("./ShakeDetector");
44
44
  const FeedbackModal_1 = require("./FeedbackModal");
45
45
  const react_native_record_screen_1 = __importDefault(require("react-native-record-screen"));
46
46
  const react_native_device_info_1 = __importDefault(require("react-native-device-info"));
47
- const IInstall = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enabled = true, showFloatingButtonOnEmulator = true, floatingButtonLabel = 'Report Issue', }) => {
47
+ const pushRegistration_1 = require("./pushRegistration");
48
+ const IInstall = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enabled = true, showFloatingButtonOnEmulator = true, floatingButtonLabel = 'Report Issue', pushToken, autoRegisterPushToken = true, projectId, onPushTokenRegisterError, }) => {
48
49
  const [modalVisible, setModalVisible] = (0, react_1.useState)(false);
49
50
  const [screenshotUri, setScreenshotUri] = (0, react_1.useState)(null);
50
51
  const [videoUri, setVideoUri] = (0, react_1.useState)(null);
51
52
  const [isRecording, setIsRecording] = (0, react_1.useState)(false);
52
53
  const [isEmulator, setIsEmulator] = (0, react_1.useState)(false);
53
54
  const shakeDetectorRef = (0, react_1.useRef)(null);
55
+ const lastRegisteredPushTokenRef = (0, react_1.useRef)(null);
54
56
  // Refs for stable access in shake callback
55
57
  const isRecordingRef = (0, react_1.useRef)(isRecording);
56
58
  const modalVisibleRef = (0, react_1.useRef)(modalVisible);
@@ -108,6 +110,55 @@ const IInstall = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enab
108
110
  shakeDetectorRef.current?.stop();
109
111
  };
110
112
  }, [enabled, isEmulator]);
113
+ (0, react_1.useEffect)(() => {
114
+ if (!enabled || !autoRegisterPushToken || !pushToken)
115
+ return;
116
+ if (lastRegisteredPushTokenRef.current === pushToken)
117
+ return;
118
+ let cancelled = false;
119
+ const syncPushToken = async () => {
120
+ const deviceUdid = await react_native_device_info_1.default.getUniqueId().catch(() => undefined);
121
+ const result = await (0, pushRegistration_1.registerPushToken)({
122
+ token: pushToken,
123
+ apiKey,
124
+ apiEndpoint,
125
+ deviceUdid,
126
+ projectId,
127
+ });
128
+ if (cancelled)
129
+ return;
130
+ if (result.success) {
131
+ lastRegisteredPushTokenRef.current = pushToken;
132
+ }
133
+ else {
134
+ const message = result.error || 'Failed to register push token';
135
+ console.warn('IInstall: push token registration failed', message);
136
+ if (onPushTokenRegisterError) {
137
+ onPushTokenRegisterError(message);
138
+ }
139
+ }
140
+ };
141
+ syncPushToken().catch((error) => {
142
+ if (cancelled)
143
+ return;
144
+ const message = error instanceof Error ? error.message : 'Failed to register push token';
145
+ console.warn('IInstall: push token registration failed', message);
146
+ if (onPushTokenRegisterError) {
147
+ onPushTokenRegisterError(message);
148
+ }
149
+ });
150
+ return () => {
151
+ cancelled = true;
152
+ };
153
+ }, [
154
+ enabled,
155
+ autoRegisterPushToken,
156
+ pushToken,
157
+ apiKey,
158
+ apiEndpoint,
159
+ projectId,
160
+ onPushTokenRegisterError,
161
+ ]);
111
162
  const handleFloatingButtonPress = async () => {
112
163
  await handleShakeCallback.current();
113
164
  };
@@ -292,3 +343,6 @@ exports.default = exports.IInstall;
292
343
  // Export the wrapper component for simulator/emulator support
293
344
  var IInstallWrapper_1 = require("./IInstallWrapper");
294
345
  Object.defineProperty(exports, "IInstallWrapper", { enumerable: true, get: function () { return IInstallWrapper_1.IInstallWrapper; } });
346
+ var pushRegistration_2 = require("./pushRegistration");
347
+ Object.defineProperty(exports, "registerPushToken", { enumerable: true, get: function () { return pushRegistration_2.registerPushToken; } });
348
+ Object.defineProperty(exports, "unregisterPushToken", { enumerable: true, get: function () { return pushRegistration_2.unregisterPushToken; } });
@@ -0,0 +1,22 @@
1
+ export type PushPlatform = 'IOS' | 'ANDROID';
2
+ export interface RegisterPushTokenParams {
3
+ token: string;
4
+ apiKey: string;
5
+ apiEndpoint?: string;
6
+ deviceUdid?: string;
7
+ projectId?: string;
8
+ platform?: PushPlatform;
9
+ }
10
+ export interface UnregisterPushTokenParams {
11
+ token: string;
12
+ apiKey: string;
13
+ apiEndpoint?: string;
14
+ }
15
+ export interface PushRegistrationResult {
16
+ success: boolean;
17
+ data?: any;
18
+ error?: string;
19
+ status: number;
20
+ }
21
+ export declare function registerPushToken({ token, apiKey, apiEndpoint, deviceUdid, projectId, platform, }: RegisterPushTokenParams): Promise<PushRegistrationResult>;
22
+ export declare function unregisterPushToken({ token, apiKey, apiEndpoint, }: UnregisterPushTokenParams): Promise<PushRegistrationResult>;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPushToken = registerPushToken;
4
+ exports.unregisterPushToken = unregisterPushToken;
5
+ const react_native_1 = require("react-native");
6
+ function normalizeApiEndpoint(apiEndpoint) {
7
+ return apiEndpoint.replace(/\/+$/, '');
8
+ }
9
+ function resolvePlatform(platform) {
10
+ if (platform)
11
+ return platform;
12
+ return react_native_1.Platform.OS === 'ios' ? 'IOS' : 'ANDROID';
13
+ }
14
+ async function registerPushToken({ token, apiKey, apiEndpoint = 'https://iinstall.app', deviceUdid, projectId, platform, }) {
15
+ try {
16
+ const response = await fetch(`${normalizeApiEndpoint(apiEndpoint)}/api/notifications/push/register`, {
17
+ method: 'POST',
18
+ headers: { 'Content-Type': 'application/json' },
19
+ body: JSON.stringify({
20
+ token,
21
+ apiKey,
22
+ deviceUdid,
23
+ projectId,
24
+ platform: resolvePlatform(platform),
25
+ }),
26
+ });
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ const data = await response.json().catch(() => ({}));
29
+ return {
30
+ success: response.ok,
31
+ status: response.status,
32
+ data,
33
+ error: response.ok ? undefined : data?.error || 'Failed to register push token',
34
+ };
35
+ }
36
+ catch (error) {
37
+ return {
38
+ success: false,
39
+ status: 500,
40
+ error: error instanceof Error ? error.message : 'Unknown register push error',
41
+ };
42
+ }
43
+ }
44
+ async function unregisterPushToken({ token, apiKey, apiEndpoint = 'https://iinstall.app', }) {
45
+ try {
46
+ const response = await fetch(`${normalizeApiEndpoint(apiEndpoint)}/api/notifications/push/register`, {
47
+ method: 'DELETE',
48
+ headers: { 'Content-Type': 'application/json' },
49
+ body: JSON.stringify({
50
+ token,
51
+ apiKey,
52
+ }),
53
+ });
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ const data = await response.json().catch(() => ({}));
56
+ return {
57
+ success: response.ok,
58
+ status: response.status,
59
+ data,
60
+ error: response.ok ? undefined : data?.error || 'Failed to unregister push token',
61
+ };
62
+ }
63
+ catch (error) {
64
+ return {
65
+ success: false,
66
+ status: 500,
67
+ error: error instanceof Error ? error.message : 'Unknown unregister push error',
68
+ };
69
+ }
70
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-iinstall",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "🎯 IInstall React Native SDK - The ultimate beta testing & QA feedback tool. Shake-to-report with voice recordings, screen recordings, and screenshots. Zero-config setup with TypeScript support. Perfect for beta testing, QA teams, and user feedback collection.",
5
5
  "author": "TesterFlow Team",
6
6
  "license": "MIT",
@@ -10,6 +10,10 @@ interface IInstallWrapperProps {
10
10
  showDebugButton?: boolean;
11
11
  showFloatingButtonOnEmulator?: boolean;
12
12
  floatingButtonLabel?: string;
13
+ pushToken?: string;
14
+ autoRegisterPushToken?: boolean;
15
+ projectId?: string;
16
+ onPushTokenRegisterError?: (error: string) => void;
13
17
  }
14
18
 
15
19
  // Backward-compatible wrapper. The core IInstall component now handles:
@@ -23,6 +27,10 @@ export const IInstallWrapper: React.FC<IInstallWrapperProps> = ({
23
27
  showDebugButton: _showDebugButton = false,
24
28
  showFloatingButtonOnEmulator = true,
25
29
  floatingButtonLabel = 'Report Issue',
30
+ pushToken,
31
+ autoRegisterPushToken = true,
32
+ projectId,
33
+ onPushTokenRegisterError,
26
34
  }) => {
27
35
  return (
28
36
  <IInstall
@@ -31,6 +39,10 @@ export const IInstallWrapper: React.FC<IInstallWrapperProps> = ({
31
39
  enabled={enabled}
32
40
  showFloatingButtonOnEmulator={showFloatingButtonOnEmulator}
33
41
  floatingButtonLabel={floatingButtonLabel}
42
+ pushToken={pushToken}
43
+ autoRegisterPushToken={autoRegisterPushToken}
44
+ projectId={projectId}
45
+ onPushTokenRegisterError={onPushTokenRegisterError}
34
46
  >
35
47
  {children}
36
48
  </IInstall>
package/src/index.tsx CHANGED
@@ -5,6 +5,7 @@ import { ShakeDetector } from './ShakeDetector';
5
5
  import { FeedbackModal } from './FeedbackModal';
6
6
  import RecordScreen from 'react-native-record-screen';
7
7
  import DeviceInfo from 'react-native-device-info';
8
+ import { registerPushToken } from './pushRegistration';
8
9
 
9
10
  interface IInstallProps {
10
11
  apiKey: string;
@@ -13,6 +14,10 @@ interface IInstallProps {
13
14
  enabled?: boolean;
14
15
  showFloatingButtonOnEmulator?: boolean;
15
16
  floatingButtonLabel?: string;
17
+ pushToken?: string;
18
+ autoRegisterPushToken?: boolean;
19
+ projectId?: string;
20
+ onPushTokenRegisterError?: (error: string) => void;
16
21
  }
17
22
 
18
23
  export const IInstall: React.FC<IInstallProps> = ({
@@ -22,6 +27,10 @@ export const IInstall: React.FC<IInstallProps> = ({
22
27
  enabled = true,
23
28
  showFloatingButtonOnEmulator = true,
24
29
  floatingButtonLabel = 'Report Issue',
30
+ pushToken,
31
+ autoRegisterPushToken = true,
32
+ projectId,
33
+ onPushTokenRegisterError,
25
34
  }) => {
26
35
  const [modalVisible, setModalVisible] = useState(false);
27
36
  const [screenshotUri, setScreenshotUri] = useState<string | null>(null);
@@ -30,6 +39,7 @@ export const IInstall: React.FC<IInstallProps> = ({
30
39
  const [isEmulator, setIsEmulator] = useState(false);
31
40
 
32
41
  const shakeDetectorRef = useRef<ShakeDetector | null>(null);
42
+ const lastRegisteredPushTokenRef = useRef<string | null>(null);
33
43
 
34
44
  // Refs for stable access in shake callback
35
45
  const isRecordingRef = useRef(isRecording);
@@ -96,6 +106,58 @@ export const IInstall: React.FC<IInstallProps> = ({
96
106
  };
97
107
  }, [enabled, isEmulator]);
98
108
 
109
+ useEffect(() => {
110
+ if (!enabled || !autoRegisterPushToken || !pushToken) return;
111
+ if (lastRegisteredPushTokenRef.current === pushToken) return;
112
+
113
+ let cancelled = false;
114
+
115
+ const syncPushToken = async () => {
116
+ const deviceUdid = await DeviceInfo.getUniqueId().catch(() => undefined);
117
+ const result = await registerPushToken({
118
+ token: pushToken,
119
+ apiKey,
120
+ apiEndpoint,
121
+ deviceUdid,
122
+ projectId,
123
+ });
124
+
125
+ if (cancelled) return;
126
+
127
+ if (result.success) {
128
+ lastRegisteredPushTokenRef.current = pushToken;
129
+ } else {
130
+ const message = result.error || 'Failed to register push token';
131
+ console.warn('IInstall: push token registration failed', message);
132
+ if (onPushTokenRegisterError) {
133
+ onPushTokenRegisterError(message);
134
+ }
135
+ }
136
+ };
137
+
138
+ syncPushToken().catch((error) => {
139
+ if (cancelled) return;
140
+ const message =
141
+ error instanceof Error ? error.message : 'Failed to register push token';
142
+ console.warn('IInstall: push token registration failed', message);
143
+ if (onPushTokenRegisterError) {
144
+ onPushTokenRegisterError(message);
145
+ }
146
+ });
147
+
148
+ return () => {
149
+ cancelled = true;
150
+ };
151
+ }, [
152
+ enabled,
153
+ autoRegisterPushToken,
154
+ pushToken,
155
+ apiKey,
156
+ apiEndpoint,
157
+ projectId,
158
+ onPushTokenRegisterError,
159
+ ]);
160
+
99
161
  const handleFloatingButtonPress = async () => {
100
162
  await handleShakeCallback.current();
101
163
  };
@@ -329,3 +391,11 @@ export default IInstall;
329
391
 
330
392
  // Export the wrapper component for simulator/emulator support
331
393
  export { IInstallWrapper } from './IInstallWrapper';
394
+ export {
395
+ registerPushToken,
396
+ unregisterPushToken,
397
+ type RegisterPushTokenParams,
398
+ type UnregisterPushTokenParams,
399
+ type PushRegistrationResult,
400
+ type PushPlatform,
401
+ } from './pushRegistration';
@@ -0,0 +1,111 @@
1
+ import { Platform } from 'react-native';
2
+
3
+ export type PushPlatform = 'IOS' | 'ANDROID';
4
+
5
+ export interface RegisterPushTokenParams {
6
+ token: string;
7
+ apiKey: string;
8
+ apiEndpoint?: string;
9
+ deviceUdid?: string;
10
+ projectId?: string;
11
+ platform?: PushPlatform;
12
+ }
13
+
14
+ export interface UnregisterPushTokenParams {
15
+ token: string;
16
+ apiKey: string;
17
+ apiEndpoint?: string;
18
+ }
19
+
20
+ export interface PushRegistrationResult {
21
+ success: boolean;
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ data?: any;
24
+ error?: string;
25
+ status: number;
26
+ }
27
+
28
+ function normalizeApiEndpoint(apiEndpoint: string): string {
29
+ return apiEndpoint.replace(/\/+$/, '');
30
+ }
31
+
32
+ function resolvePlatform(platform?: PushPlatform): PushPlatform {
33
+ if (platform) return platform;
34
+ return Platform.OS === 'ios' ? 'IOS' : 'ANDROID';
35
+ }
36
+
37
+ export async function registerPushToken({
38
+ token,
39
+ apiKey,
40
+ apiEndpoint = 'https://iinstall.app',
41
+ deviceUdid,
42
+ projectId,
43
+ platform,
44
+ }: RegisterPushTokenParams): Promise<PushRegistrationResult> {
45
+ try {
46
+ const response = await fetch(
47
+ `${normalizeApiEndpoint(apiEndpoint)}/api/notifications/push/register`,
48
+ {
49
+ method: 'POST',
50
+ headers: { 'Content-Type': 'application/json' },
51
+ body: JSON.stringify({
52
+ token,
53
+ apiKey,
54
+ deviceUdid,
55
+ projectId,
56
+ platform: resolvePlatform(platform),
57
+ }),
58
+ }
59
+ );
60
+
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ const data: any = await response.json().catch(() => ({}));
63
+ return {
64
+ success: response.ok,
65
+ status: response.status,
66
+ data,
67
+ error: response.ok ? undefined : data?.error || 'Failed to register push token',
68
+ };
69
+ } catch (error) {
70
+ return {
71
+ success: false,
72
+ status: 500,
73
+ error: error instanceof Error ? error.message : 'Unknown register push error',
74
+ };
75
+ }
76
+ }
77
+
78
+ export async function unregisterPushToken({
79
+ token,
80
+ apiKey,
81
+ apiEndpoint = 'https://iinstall.app',
82
+ }: UnregisterPushTokenParams): Promise<PushRegistrationResult> {
83
+ try {
84
+ const response = await fetch(
85
+ `${normalizeApiEndpoint(apiEndpoint)}/api/notifications/push/register`,
86
+ {
87
+ method: 'DELETE',
88
+ headers: { 'Content-Type': 'application/json' },
89
+ body: JSON.stringify({
90
+ token,
91
+ apiKey,
92
+ }),
93
+ }
94
+ );
95
+
96
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
+ const data: any = await response.json().catch(() => ({}));
98
+ return {
99
+ success: response.ok,
100
+ status: response.status,
101
+ data,
102
+ error: response.ok ? undefined : data?.error || 'Failed to unregister push token',
103
+ };
104
+ } catch (error) {
105
+ return {
106
+ success: false,
107
+ status: 500,
108
+ error: error instanceof Error ? error.message : 'Unknown unregister push error',
109
+ };
110
+ }
111
+ }