@inngageregistry/inngage-react 3.3.1-alpha.1 → 4.0.0-alpha.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inngageregistry/inngage-react",
3
- "version": "3.3.1-alpha.1",
3
+ "version": "4.0.0-alpha.01",
4
4
  "description": "Inngage Plugin for React Native applications for marketing campaign optimization using Push Notification.",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.d.ts",
package/src/Inngage.ts CHANGED
@@ -3,62 +3,62 @@ import DeviceInfo from "react-native-device-info";
3
3
  import * as RNLocalize from "react-native-localize";
4
4
  import AsyncStorage from '@react-native-async-storage/async-storage';
5
5
 
6
- import { addUserDataRequest, eventRequest, formatDate, subscriptionRequestAdapter } from "./utils";
7
- import notificationsListener, { notificationsListenerProps } from "./notificationsListener";
8
- import { subscriptionApi, eventsApi, addUserDataApi } from "./services/inngage";
6
+ import { addUserDataRequest, eventRequest, formatDate } from "./utils";
7
+ import { InngageNotificationMessage } from "./firebase/notifications_listener";
9
8
  import { InngageProperties } from './models/inngage_properties';
10
9
 
11
10
  import RNPermissions, { NotificationOption, RESULTS } from 'react-native-permissions';
12
11
 
12
+ import ApiService from './services/api_service';
13
+
14
+ import {
15
+ SUBSCRIBE_REQUEST,
16
+ EVENT_REQUEST,
17
+ USER_DATA_REQUEST,
18
+ } from './models/requests';
19
+
20
+ const API_LEVEL_33 = 33;
21
+
13
22
  // --- Get Firebase Access ------/
14
- const getFirebaseAccess = async (): Promise<string | null> => {
15
- try {
16
- return await handleNotificationsPermission();
17
- } catch (error) {
18
- console.log('Erro no getFirebaseAccess: ', error);
19
- throw error;
20
- }
23
+ const getFirebaseAccess = async () => {
24
+ return await handleNotificationsPermission();
21
25
  };
22
26
 
23
27
  const handleNotificationsPermission = async () => {
24
- try {
25
- const options: NotificationOption[] = ['alert', 'badge', 'sound'];
26
- const apiLevel = await DeviceInfo.getApiLevel();
27
- const isPermissionGranted =
28
- apiLevel >= 33
29
- ? ((await RNPermissions.requestNotifications(options)).status) === RESULTS.GRANTED
30
- : (await firebase.messaging().requestPermission()) ===
31
- firebase.messaging.AuthorizationStatus.AUTHORIZED ||
32
- firebase.messaging.AuthorizationStatus.PROVISIONAL;
33
-
34
- return isPermissionGranted ? await getFirebaseToken() : null;
35
- } catch (error) {
36
- console.error(error);
37
- throw error;
28
+ const options: NotificationOption[] = ['alert', 'badge', 'sound'];
29
+ const apiLevel = await DeviceInfo.getApiLevel();
30
+
31
+ const permissionGranted =
32
+ apiLevel >= API_LEVEL_33
33
+ ? (await RNPermissions.requestNotifications(options)).status === RESULTS.GRANTED
34
+ : (await firebase.messaging().requestPermission()) ===
35
+ firebase.messaging.AuthorizationStatus.AUTHORIZED ||
36
+ firebase.messaging.AuthorizationStatus.PROVISIONAL;
37
+
38
+ if (permissionGranted) {
39
+ return await getFirebaseToken();
38
40
  }
41
+
42
+ throw new Error('Notification permission not granted');
39
43
  };
40
44
 
41
- const getFirebaseToken = async (): Promise<string | null> => {
42
- try {
43
- let fcmToken = await AsyncStorage.getItem('fcmToken');
45
+ const getFirebaseToken = async () => {
46
+ let fcmToken = await AsyncStorage.getItem('fcmToken');
44
47
 
45
- if (!fcmToken) {
46
- if (!firebase.messaging().isDeviceRegisteredForRemoteMessages) {
47
- await firebase.messaging().registerDeviceForRemoteMessages?.();
48
- }
48
+ if (!fcmToken) {
49
+ if (!firebase.messaging().isDeviceRegisteredForRemoteMessages) {
50
+ await firebase.messaging().registerDeviceForRemoteMessages?.();
51
+ }
49
52
 
50
- const newFcmToken = await firebase.messaging().getToken?.();
53
+ const newFcmToken = await firebase.messaging().getToken?.();
51
54
 
52
- if (newFcmToken) {
53
- await AsyncStorage.setItem('fcmToken', newFcmToken);
54
- return newFcmToken;
55
- }
55
+ if (newFcmToken) {
56
+ await AsyncStorage.setItem('fcmToken', newFcmToken);
57
+ return newFcmToken;
56
58
  }
57
- return fcmToken || null;
58
- } catch (error) {
59
- console.error(error);
60
- throw error;
61
59
  }
60
+
61
+ return fcmToken || null;
62
62
  };
63
63
 
64
64
  interface SubscriptionProps {
@@ -83,127 +83,124 @@ interface SendEventProps {
83
83
  eventValues?: any
84
84
  }
85
85
 
86
- const Inngage = {
87
- // ------------ Register Notification Listener ------------//
88
- RegisterNotificationListener: async (props: notificationsListenerProps) => {
86
+ class Inngage {
87
+ private static instance: Inngage;
88
+ private apiService: ApiService;
89
+
90
+ constructor() {
91
+ this.apiService = new ApiService();
92
+ }
93
+
94
+ static getInstance(): Inngage {
95
+ if (!Inngage.instance) {
96
+ Inngage.instance = new Inngage();
97
+ }
98
+
99
+ return Inngage.instance;
100
+ }
101
+
102
+ static async notificationListener(firebaseListenCallback?: any) {
89
103
  try {
90
- await notificationsListener({ ...props });
104
+ InngageNotificationMessage(firebaseListenCallback)
91
105
  } catch (e) {
92
- console.error(e);
93
- return { subscribed: false };
106
+ console.log(e)
94
107
  }
95
- },
108
+ }
96
109
 
97
- Subscribe: async ({
110
+ static async subscribe({
98
111
  appToken,
99
- dev,
100
112
  friendlyIdentifier,
101
113
  customFields,
102
- customData,
103
114
  phoneNumber,
104
115
  email,
105
- }: SubscriptionProps) => {
116
+ }: SubscriptionProps) {
117
+ const inngage = Inngage.getInstance();
118
+
106
119
  InngageProperties.appToken = appToken;
107
120
  InngageProperties.identifier = friendlyIdentifier!;
108
121
 
109
- try {
110
- const respToken = await getFirebaseAccess()
111
-
112
- const locales = RNLocalize.getLocales();
113
-
114
- const os_locale = locales ? locales[0].countryCode : ''
115
- const os_language = locales && locales.length ? locales[0].languageCode : ''
116
- const device_manufacturer = await DeviceInfo.getManufacturer();
117
- const installTime = await DeviceInfo.getFirstInstallTime();
118
- const lastUpdateTime = await DeviceInfo.getLastUpdateTime();
119
- const uuid = await DeviceInfo.getUniqueId();
120
- const app_installed_in = formatDate(installTime);
121
- const app_updated_in = formatDate(lastUpdateTime);
122
-
123
- const rawRequest = {
124
- registerSubscriberRequest: {
125
- app_token: appToken,
126
- identifier: friendlyIdentifier,
127
- registration: respToken,
128
- platform: DeviceInfo.getSystemName(),
129
- sdk: DeviceInfo.getBuildNumber(),
130
- device_model: DeviceInfo.getModel(),
131
- device_manufacturer,
132
- os_locale,
133
- os_language,
134
- os_version: DeviceInfo.getReadableVersion(),
135
- app_version: DeviceInfo.getBuildNumber(),
136
- app_installed_in,
137
- app_updated_in,
138
- uuid,
139
- phone_Number: phoneNumber,
140
- email: email,
141
- }
142
- };
143
-
144
- const request = subscriptionRequestAdapter(rawRequest, customData, customFields)
145
- const subscribe = await subscriptionApi(request, dev);
146
- console.log(await subscribe.json())
147
- return subscribe;
148
- } catch (e) {
149
- console.error(e);
150
- return { subscribed: false };
122
+ const respToken = await getFirebaseAccess();
123
+
124
+ const { countryCode: osLocale, languageCode: osLanguage } = RNLocalize.getLocales()[0] || {};
125
+ const deviceManufacturer = await DeviceInfo.getManufacturer();
126
+ const installTime = await DeviceInfo.getFirstInstallTime();
127
+ const lastUpdateTime = await DeviceInfo.getLastUpdateTime();
128
+ const uuid = await DeviceInfo.getUniqueId();
129
+ const appInstalledIn = formatDate(installTime);
130
+ const appUpdatedIn = formatDate(lastUpdateTime);
131
+
132
+ const subscription = {
133
+ ...SUBSCRIBE_REQUEST,
134
+ registerSubscriberRequest: {
135
+ app_token: appToken,
136
+ identifier: friendlyIdentifier,
137
+ registration: respToken,
138
+ platform: DeviceInfo.getSystemName(),
139
+ sdk: DeviceInfo.getBuildNumber(),
140
+ deviceModel: DeviceInfo.getModel(),
141
+ deviceManufacturer,
142
+ osLocale,
143
+ osLanguage,
144
+ os_version: DeviceInfo.getReadableVersion(),
145
+ app_version: DeviceInfo.getBuildNumber(),
146
+ appInstalledIn,
147
+ appUpdatedIn,
148
+ uuid,
149
+ phone_Number: phoneNumber,
150
+ email: email,
151
+ customFields: customFields,
152
+ }
151
153
  }
152
- },
153
154
 
154
- SendEvent: async ({
155
- appToken,
155
+ return inngage.apiService.subscribe(subscription);
156
+ }
157
+
158
+ static async sendEvent({
156
159
  eventName,
157
- identifier,
158
- registration,
159
160
  conversionEvent,
160
161
  conversionValue,
161
162
  conversionNotId,
162
163
  eventValues,
163
- }: SendEventProps) => {
164
+ }: SendEventProps) {
165
+ const inngage = Inngage.getInstance();
166
+
167
+ const registration = await getFirebaseAccess();
164
168
 
165
- const rawRequest = {
169
+ const request = eventRequest({
170
+ ...EVENT_REQUEST,
166
171
  newEventRequest: {
167
- app_token: appToken,
168
- identifier: identifier,
169
- registration: registration,
170
- event_name: eventName,
171
- conversion_event: conversionEvent ?? false,
172
- conversion_value: conversionValue ?? 0,
173
- conversion_notid: conversionNotId ?? '',
174
- event_values: eventValues ?? {}
175
- }
176
- };
172
+ appToken: InngageProperties.appToken,
173
+ identifier: InngageProperties.identifier,
174
+ registration,
175
+ eventName,
176
+ conversionEvent: !!conversionEvent,
177
+ conversionValue,
178
+ conversionNotId,
179
+ eventValues,
180
+ },
181
+ });
182
+
183
+ return inngage.apiService.sendEvent(request);
184
+ }
177
185
 
178
- const request = eventRequest(rawRequest)
179
- try {
180
- return await eventsApi(request);
181
- } catch (e) {
182
- console.error(e)
183
- return { subscribed: false };
184
- }
185
- },
186
+ static async addUserData(customFields: any): Promise<any> {
187
+ const inngage = Inngage.getInstance();
186
188
 
187
- async addUserData(customFields: any): Promise<any> {
188
- const rawRequest = {
189
+ const request = addUserDataRequest({
190
+ ...USER_DATA_REQUEST,
189
191
  fieldsRequest: {
190
- app_token: InngageProperties.appToken,
192
+ appToken: InngageProperties.appToken,
191
193
  identifier: InngageProperties.identifier,
192
- custom_field: customFields ?? {}
193
- }
194
- };
194
+ customField: customFields,
195
+ },
196
+ });
195
197
 
196
- const request = addUserDataRequest(rawRequest);
197
- try {
198
- return await addUserDataApi(request);
199
- } catch (e) {
200
- console.error(e);
201
- }
202
- },
198
+ return inngage.apiService.addUserData(request);
199
+ }
203
200
 
204
- setDebugMode(value: boolean) {
205
- InngageProperties.debugMode = value
201
+ static setDebugMode(value: boolean) {
202
+ InngageProperties.debugMode = value;
206
203
  }
207
204
  }
208
205
 
209
- export default Inngage;
206
+ export default Inngage;
package/src/api/api.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { fetchClient } from './handler'
2
+
3
+ const apiEndpoints = {
4
+ subscription: '/subscription/',
5
+ notification: '/notification/',
6
+ events: '/events/newEvent/',
7
+ addUserData: '/subscription/addCustomField',
8
+ };
9
+
10
+ const makeApiCall = (method: string, request: any, endpoint: string) => {
11
+ return fetchClient(method, request, endpoint);
12
+ };
13
+
14
+ export const subscriptionApi = (request: any) => {
15
+ return makeApiCall('POST', request, apiEndpoints.subscription);
16
+ };
17
+
18
+ export const notificationApi = (request: any) => {
19
+ return makeApiCall('POST', request, apiEndpoints.notification);
20
+ };
21
+
22
+ export const eventsApi = (request: any) => {
23
+ return makeApiCall('POST', request, apiEndpoints.events);
24
+ };
25
+
26
+ export const addUserDataApi = (request: any) => {
27
+ return makeApiCall('POST', request, apiEndpoints.addUserData);
28
+ };
@@ -0,0 +1,53 @@
1
+ const baseUrlHook = {
2
+ [false as any]: 'https://api.inngage.com.br/v1',
3
+ [undefined as any]: 'https://api.inngage.com.br/v1',
4
+ [null as any]: 'https://api.inngage.com.br/v1',
5
+ [true as any]: 'https://apid.inngage.com.br/v1',
6
+ }
7
+
8
+ const requestConfigFactory = (method, request) => {
9
+
10
+ let header: any = {
11
+ Accept: 'application/json',
12
+ 'Content-Type': 'application/json',
13
+ }
14
+ try {
15
+ if (request?.registerSubscriberRequest?.authKey) {
16
+ header = {
17
+ ...header,
18
+ Authorization: request.registerSubscriberRequest.authKey
19
+ }
20
+ }
21
+ } catch (e) {
22
+ console.error(e)
23
+ }
24
+ let objToSend = {
25
+ method,
26
+ body: JSON.stringify(request),
27
+ headers: header
28
+ }
29
+
30
+ return objToSend
31
+ }
32
+
33
+ export const fetchClient = async (
34
+ method: string,
35
+ requestBody: any,
36
+ path: string,
37
+ isDev = false
38
+ ): Promise<Response> => {
39
+ try {
40
+ const url: URL = new URL(`${baseUrlHook[isDev as any]}${path}`);
41
+ const request: RequestInit = requestConfigFactory(method, requestBody);
42
+ const response: Response = await fetch(url, request);
43
+
44
+ if (!response.ok) {
45
+ throw new Error(`Network response was not ok (${response.status})`);
46
+ }
47
+
48
+ return response;
49
+ } catch (error: any) {
50
+ console.error('Fetch Error:', error.message);
51
+ throw error;
52
+ }
53
+ };
@@ -0,0 +1,117 @@
1
+ import React from 'react';
2
+ import {
3
+ Modal,
4
+ View,
5
+ StyleSheet,
6
+ Text,
7
+ TouchableOpacity,
8
+ ImageBackground,
9
+ Platform,
10
+ PlatformColor,
11
+ } from "react-native";
12
+
13
+ export function InAppContainer(data: any): JSX.Element {
14
+ const styles = buildStyles(data)
15
+ return (
16
+ <Modal
17
+ renderToHardwareTextureAndroid={true}
18
+ transparent={true}
19
+ visible={true}>
20
+ <ImageBackground style={styles.content} source={{ uri: "" }}>
21
+ <View style={styles.messageData}>
22
+ <Text style={styles.title}>data.title</Text>
23
+ <Text style={styles.body}>Corpo</Text>
24
+ </View>
25
+ <View style={styles.footer}>
26
+ <TouchableOpacity
27
+ style={styles.button}>
28
+ <Text style={styles.textButton}>Teste</Text>
29
+ </TouchableOpacity>
30
+ <TouchableOpacity
31
+ style={styles.button}>
32
+ <Text style={styles.textButton}>Teste</Text>
33
+ </TouchableOpacity>
34
+ </View>
35
+ </ImageBackground>
36
+ </Modal>
37
+ )
38
+ }
39
+
40
+ const buildStyles = ({ data }) => StyleSheet.create({
41
+ content: Platform.select({
42
+ android: {
43
+ backgroundColor: PlatformColor("?attr/colorBackgroundFloating"),
44
+ flexDirection: "column",
45
+ borderRadius: 3,
46
+ padding: 16,
47
+ margin: 16,
48
+ overflow: "hidden",
49
+ elevation: 10,
50
+ minWidth: 300,
51
+ alignItems: "center"
52
+ },
53
+ default: {}
54
+ }),
55
+ messageData: Platform.select({
56
+ android: {
57
+ marginTop: 12
58
+ },
59
+ default: {}
60
+ }),
61
+ footer: {
62
+ flexDirection: "row",
63
+ ...Platform.select({
64
+ android: {
65
+ alignItems: "center",
66
+ justifyContent: "flex-end",
67
+ marginTop: 4,
68
+ },
69
+ default: {},
70
+ }),
71
+ },
72
+ title: Platform.select({
73
+ android: {
74
+ color: PlatformColor(
75
+ `@android:color/${"primary_text_light"}`
76
+ ),
77
+ fontWeight: "500",
78
+ fontSize: 18,
79
+ },
80
+
81
+ default: {},
82
+ }),
83
+ body: Platform.select({
84
+ android: {
85
+ color: PlatformColor(
86
+ `@android:color/${"secondary_text_light"}`
87
+ ),
88
+ fontSize: 16,
89
+ marginTop: 10,
90
+ marginBottom: 10,
91
+ },
92
+ default: {},
93
+ }),
94
+ button: Platform.select({
95
+ android: {
96
+ margin: 2,
97
+ flex: 1,
98
+ backgroundColor: "red",
99
+ justifyContent: "center",
100
+ alignItems: "center",
101
+ },
102
+ default: {},
103
+ }),
104
+ textButton: Platform.select({
105
+ android: {
106
+ color: PlatformColor(
107
+ `@android:color/${"secondary_text_light"}`
108
+ ),
109
+ textAlign: "center",
110
+ backgroundColor: "transparent",
111
+ padding: 8,
112
+ fontSize: 14,
113
+ textTransform: "uppercase",
114
+ },
115
+ default: {},
116
+ }),
117
+ })
@@ -0,0 +1,105 @@
1
+ import { useEffect } from 'react';
2
+
3
+ import AsyncStorage from '@react-native-async-storage/async-storage';
4
+
5
+ import messaging from '@react-native-firebase/messaging';
6
+ import PushNotification, { Importance } from 'react-native-push-notification'
7
+
8
+ import { InngageProperties } from '../models/inngage_properties';
9
+ import * as ApiService from '../services/api_service'
10
+
11
+ export const InngageNotificationMessage = (firebaseListenCallback?: any) => {
12
+ useEffect(() => {
13
+ PushNotification.configure({
14
+ onNotification: function (data: any) {
15
+ if (data.foreground) {
16
+ handleNotification(data)
17
+ }
18
+ },
19
+ popInitialNotification: true,
20
+ requestPermissions: true
21
+ })
22
+ PushNotification.createChannel({
23
+ channelId: 'channel_id',
24
+ channelName: 'default',
25
+ importance: Importance.HIGH,
26
+ playSound: true,
27
+ soundName: 'default',
28
+ vibrate: true
29
+ }, (created: any) => {
30
+ if (created) {
31
+ console.log('Channel created');
32
+ }
33
+ });
34
+ })
35
+ useEffect(() => {
36
+ const handleMessage = async (remoteMessage: any) => {
37
+ if (InngageProperties.getDebugMode())
38
+ console.log('Remote message received in foreground: ', remoteMessage);
39
+
40
+ if (firebaseListenCallback != null && remoteMessage != null)
41
+ firebaseListenCallback(remoteMessage.data)
42
+
43
+ PushNotification.localNotification({
44
+ autoCancel: true,
45
+ bigPictureUrl: remoteMessage.notification?.android?.imageUrl,
46
+ largeIconUrl: remoteMessage.notification?.android?.imageUrl,
47
+ title: remoteMessage.data?.title,
48
+ message: remoteMessage.data?.body,
49
+ vibration: 300,
50
+ channelId: "channel_id",
51
+ });
52
+ };
53
+
54
+ return messaging().onMessage(handleMessage);
55
+ }, []);
56
+
57
+ useEffect(() => {
58
+ const backgroundHandler = async (remoteMessage: any) => {
59
+ if (InngageProperties.getDebugMode())
60
+ console.log('Remote message received in background: ', remoteMessage);
61
+
62
+ if (remoteMessage != null)
63
+ firebaseListenCallback(remoteMessage.data)
64
+
65
+ messaging().onNotificationOpenedApp(remoteMessage => {
66
+ handleNotification(remoteMessage)
67
+ })
68
+ };
69
+
70
+ return messaging().setBackgroundMessageHandler(backgroundHandler);
71
+ }, []);
72
+
73
+ useEffect(() => {
74
+ messaging().getInitialNotification().then(async (value) => {
75
+ if (value !== null)
76
+ handleUniqueRemoteMessage(value);
77
+ });
78
+ }, [])
79
+
80
+ const handleUniqueRemoteMessage = async (
81
+ remoteMessage: { messageId?: string }) => {
82
+ try {
83
+ const lastRemoteMessageId = await AsyncStorage.getItem('LAST_REMOTE_MESSAGE_ID');
84
+ const newRemoteMessageId = remoteMessage?.messageId;
85
+
86
+ if (newRemoteMessageId && lastRemoteMessageId !== newRemoteMessageId) {
87
+ await AsyncStorage.setItem('LAST_REMOTE_MESSAGE_ID', newRemoteMessageId);
88
+ handleNotification(remoteMessage)
89
+ }
90
+ } catch (e) {
91
+ console.log(e);
92
+ }
93
+ };
94
+
95
+ async function handleNotification(remoteMessage) {
96
+ const notId = remoteMessage.data?.notId;
97
+ const request = {
98
+ notificationRequest: {
99
+ id: notId,
100
+ app_token: InngageProperties.appToken,
101
+ }
102
+ };
103
+ await ApiService.sendNotification(request);
104
+ }
105
+ }
package/src/index.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { Inapp } from './components/Inapp';
1
+ import { InAppContainer } from './components/in_app';
2
2
  import Inngage from './Inngage';
3
3
 
4
4
  //-------------- In-APP Component -------------//
5
- export { Inapp };
5
+ export { InAppContainer };
6
6
 
7
7
  //-------------- Inngage Wrapper --------------//
8
8
  export default Inngage;
@@ -0,0 +1,42 @@
1
+ export const SUBSCRIBE_REQUEST = {
2
+ registerSubscriberRequest: {
3
+ appToken: '',
4
+ identifier: '',
5
+ registration: '',
6
+ platform: '',
7
+ sdk: '',
8
+ deviceModel: '',
9
+ deviceManufacturer: '',
10
+ osLocale: '',
11
+ osLanguage: '',
12
+ osVersion: '',
13
+ appVersion: '',
14
+ appInstalledIn: '',
15
+ appUpdatedIn: '',
16
+ uuid: '',
17
+ phoneNumber: '',
18
+ email: '',
19
+ customFields: {},
20
+ },
21
+ };
22
+
23
+ export const EVENT_REQUEST = {
24
+ newEventRequest: {
25
+ appToken: '',
26
+ identifier: '',
27
+ registration: '',
28
+ eventName: '',
29
+ conversionEvent: false,
30
+ conversionValue: 0,
31
+ conversionNotId: '',
32
+ eventValues: {},
33
+ },
34
+ };
35
+
36
+ export const USER_DATA_REQUEST = {
37
+ fieldsRequest: {
38
+ appToken: '',
39
+ identifier: '',
40
+ customField: {},
41
+ },
42
+ };
@@ -0,0 +1,30 @@
1
+ import { InngageProperties } from '../models/inngage_properties';
2
+ import { subscriptionApi, notificationApi, eventsApi, addUserDataApi } from '../api/api';
3
+
4
+ class ApiService {
5
+ async subscribe(request: any) {
6
+ if (InngageProperties.getDebugMode())
7
+ console.log('INNGAGE PAYLOAD SUBSCRIPTION: ', request)
8
+ return subscriptionApi(request);
9
+ }
10
+
11
+ async sendEvent(request: any, dev = false) {
12
+ if (InngageProperties.getDebugMode())
13
+ console.log('INNGAGE PAYLOAD EVENT: ', request)
14
+ return eventsApi(request);
15
+ }
16
+
17
+ async addUserData(request: any, dev = false) {
18
+ if (InngageProperties.getDebugMode())
19
+ console.log('INNGAGE PAYLOAD ADDUSERDATA: ', request)
20
+ return addUserDataApi(request);
21
+ }
22
+ }
23
+
24
+ export async function sendNotification(request: any, dev = false) {
25
+ if (InngageProperties.getDebugMode())
26
+ console.log('INNGAGE PAYLOAD NOTIFICATION: ', request)
27
+ return notificationApi(request);
28
+ }
29
+
30
+ export default ApiService;
@@ -1,324 +0,0 @@
1
- import React, { useState, useEffect, useRef } from 'react';
2
- import {
3
- Modal,
4
- View,
5
- Text,
6
- TouchableHighlight,
7
- TouchableOpacity,
8
- Image,
9
- ScrollView,
10
- ImageBackground,
11
- Linking,
12
- Dimensions,
13
- } from "react-native";
14
- import DeviceInfo from "react-native-device-info";
15
- import AsyncStorage from '@react-native-async-storage/async-storage';
16
- import Carousel, { Pagination } from 'react-native-snap-carousel';
17
-
18
- import { showAlertLink, isEmpty } from "../utils";
19
- import { linkInApp } from "../notificationsListener";
20
- import { styleInapp, styleItem } from './styles';
21
-
22
- const SLIDER_WIDTH = Dimensions.get('window').width;
23
- const SLIDER_HEIGHT = Dimensions.get('window').height;
24
- const ITEM_WIDTH = Math.round(SLIDER_WIDTH * 0.8);
25
- const ITEM_HEIGHT = Math.round(SLIDER_HEIGHT * 0.8);
26
-
27
- export interface InappProps {
28
- mediaStyle?: any;
29
- titleStyle?: any;
30
- bodyStyle?: any;
31
- buttonLeftStyle?: any;
32
- buttonRightStyle?: any;
33
- buttonTitleLeftStyle?: any;
34
- buttonTitleRightStyle?: any;
35
- styleContainer?: any;
36
- onClose?: 'clear';
37
- }
38
- export const Inapp = (props: InappProps) => {
39
- const [data, setData] = useState<any>([])
40
- const [indIm, setIndImg] = useState(0)
41
- const [visible, setVisible] = useState(true)
42
- const [bgImage, setbgImage] = useState<any>(undefined) // TODO, need a placeholder
43
-
44
- const CarouselRef = useRef<Carousel<any>>(null);
45
- const ScrollRef1 = useRef<ScrollView>(null);
46
- const ScrollRef2 = useRef<ScrollView>(null);
47
- const ScrollRef3 = useRef<ScrollView>(null);
48
-
49
-
50
- interface _renderItemProps {
51
- item: any;
52
- index: number;
53
- }
54
- const _renderItem = ({ item, index }: _renderItemProps) => {
55
- let msg = JSON.parse(item.data.additional_data)
56
- let arrayImgs: any = []
57
- let indImg = 0
58
-
59
- const checkBG = () => {
60
- if (msg.background_img != '') {
61
- return null
62
- } else {
63
- return msg.background_color
64
- }
65
- }
66
-
67
- const itemStyles = styleItem({ msg, checkBG })
68
-
69
- const chooseRef = () => {
70
- if (index == 0) {
71
- return ScrollRef1
72
- }
73
- if (index == 1) {
74
- return ScrollRef2
75
- }
76
- if (index == 2) {
77
- return ScrollRef3
78
- }
79
- }
80
-
81
-
82
- const pagination = ref => {
83
- return (
84
- <Pagination
85
- dotsLength={arrayImgs.length}
86
- activeDotIndex={indIm}
87
- containerStyle={{ height: 2, padding: 0, margin: 0 }}
88
- renderDots={(activeIndex, total, context) => {
89
- let dots: any = []
90
- var size = 0
91
- for (let i = 0; i < total; i++) {
92
- if (activeIndex == i) {
93
- size = 13
94
- } else {
95
- size = 8
96
- }
97
- dots.push(
98
- <TouchableOpacity
99
- onPress={() => {
100
- ref.current.scrollTo({ x: i * 220, y: 0, animated: true })
101
- if (i * 220 === 0) {
102
- setIndImg(0)
103
- } else if (i * 220 === 220) {
104
- setIndImg(1)
105
- } else if (i * 220 === 440) {
106
- setIndImg(2)
107
- }
108
- }}
109
- key={i.toString()}
110
- style={[itemStyles.dot, { width: size, height: size }]}
111
- />
112
- )
113
- }
114
- return (
115
- dots
116
- )
117
- }
118
- }
119
- />
120
- );
121
- }
122
-
123
- const handleButton = (title: string, body: string, url: string, type: string) => {
124
- if (type === '' || url === '') {
125
- return
126
- }
127
- console.log(title, body, url, type)
128
- const openLinkByType = (type, url) => (type === 'deep' ? Linking.openURL(url) : linkInApp(url))
129
-
130
- return Linking.canOpenURL(url).then((supported) => {
131
- if (supported) {
132
- showAlertLink(
133
- title,
134
- body,
135
- `${DeviceInfo.getApplicationName()}`,
136
- `Acessar ${url} ?`,
137
- ).then(() => { supported && openLinkByType(type, url) })
138
- }
139
- }).catch(console.error)
140
- }
141
-
142
- const imgCarosel = () => {
143
- if (msg.rich_content.carousel == true) {
144
- if (msg.rich_content.img1 != '') {
145
- arrayImgs.push({ url: msg.rich_content.img1 })
146
- }
147
- if (msg.rich_content.img2 != '') {
148
- arrayImgs.push({ url: msg.rich_content.img2 })
149
- }
150
- if (msg.rich_content.img3 != '') {
151
- arrayImgs.push({ url: msg.rich_content.img3 })
152
- }
153
- let arrayElements = arrayImgs.map((item, index) => (
154
- <Image key={index.toString()} style={[props.mediaStyle, { width: 200, height: 200, marginRight: 10 }]} source={{ uri: item.url }} />
155
- ));
156
- return arrayElements
157
- } else if (arrayImgs.length <= 0) {
158
- return (
159
- <Image style={[props.mediaStyle, { width: 200, height: 200 }]} source={{ uri: item.data.picture }} />
160
- )
161
- }
162
- else {
163
- return (
164
- <Image style={[props.mediaStyle, { width: 200, height: 200 }]} source={{ uri: item.data.picture }} />
165
- )
166
- }
167
- }
168
- return (
169
- <View style={[itemStyles.body]}>
170
- <Text style={[itemStyles.title, props.titleStyle]}>{msg.title}</Text>
171
- <ScrollView
172
- ref={chooseRef()}
173
- horizontal
174
- snapToInterval={220}
175
- decelerationRate="fast"
176
- showsHorizontalScrollIndicator={false}
177
- style={{ width: 200, height: 240 }}
178
- contentContainerStyle={{ alignItems: 'center', justifyContent: 'center' }}
179
- onMomentumScrollEnd={(e) => {
180
- if (Math.round(e.nativeEvent.contentOffset.x) === 0
181
- ) {
182
- indImg = 0
183
- setIndImg(indImg)
184
- }
185
- if (Math.round(e.nativeEvent.contentOffset.x) === 220
186
- ) {
187
- indImg = 1
188
- setIndImg(indImg)
189
- }
190
- if (Math.round(e.nativeEvent.contentOffset.x) === 430
191
- ) {
192
- indImg = 2
193
- setIndImg(indImg)
194
- }
195
- }}
196
- >
197
- {imgCarosel()}
198
- </ScrollView>
199
- {
200
- msg.rich_content.carousel == true ?
201
- pagination(chooseRef()) : null
202
-
203
- }
204
- <Text style={[itemStyles.bodyText, props.bodyStyle]}>{msg.body}</Text>
205
- <View style={{ flexDirection: "row", marginBottom: 0, justifyContent: 'center' }}>
206
- <TouchableOpacity onPress={() => handleButton(msg.title, msg.body, msg.btn_left_action_link, msg.btn_left_action_type)} style={[itemStyles.btn_left, props.buttonLeftStyle]}>
207
- <View>
208
- <Text style={[itemStyles.btn_left_title, props.buttonTitleLeftStyle]}>{msg.btn_left_txt}</Text>
209
- </View>
210
- </TouchableOpacity>
211
- <TouchableOpacity onPress={() => handleButton(msg.title, msg.body, msg.btn_right_action_link, msg.btn_right_action_type)} style={[itemStyles.btn_right, props.buttonRightStyle]}>
212
- <View>
213
- <Text style={[itemStyles.btn_right_title, props.buttonTitleRightStyle]}>{msg.btn_right_txt}</Text>
214
- </View>
215
- </TouchableOpacity>
216
- </View>
217
- </View>
218
- );
219
- }
220
-
221
- useEffect(() => {
222
- onLoad();
223
- }, [])
224
-
225
- const onLoad = async () => {
226
- let temp: any = []
227
- const messages = JSON.parse(await AsyncStorage.getItem('inngage') ?? '[]')
228
-
229
- console.log("Messages saved on AsyncStorage: ", JSON.stringify(messages))
230
-
231
- if (messages !== null) {
232
- messages.forEach((el) => {
233
- if (!isEmpty(el)) {
234
- temp.push(el)
235
- }
236
- })
237
-
238
- let msg: any = {}
239
- if (temp.length > 0 && temp[0]?.data?.additional_data) {
240
- msg = JSON.parse(temp[0].data.additional_data)
241
- }
242
-
243
- if (msg.background_img != '') {
244
- setbgImage({ uri: msg.background_img })
245
- } else {
246
- setbgImage(undefined)
247
- }
248
- setData(temp)
249
- }
250
- }
251
-
252
- const handleClose = async () => {
253
- if (props.onClose) {
254
- if (props.onClose.toLowerCase() === 'clear') {
255
- await AsyncStorage.removeItem('inngage');
256
- }
257
- }
258
- setVisible(false)
259
- }
260
-
261
- const handleBg = index => {
262
- let msg = JSON.parse(data[index].data.additional_data)
263
- if (msg.background_img != '') {
264
- setbgImage({ uri: msg.background_img })
265
- } else {
266
- setbgImage(undefined)
267
- }
268
- }
269
-
270
- if (data.length > 0) {
271
- return (
272
- <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
273
- <Modal
274
- animationType='slide'
275
- visible={visible}
276
- transparent={true}
277
- style={{ justifyContent: 'center', alignItems: 'center', backgroundColor: 'blue' }}
278
- ><View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
279
- <View style={[styles.styleContainer, props.styleContainer]}>
280
- <ImageBackground style={{ width: '100%', alignItems: 'center', justifyContent: 'center' }} resizeMode='cover' imageStyle={{ borderRadius: 10, alignSelf: 'stretch', height: 480 }} source={bgImage}>
281
- <TouchableHighlight
282
- onPress={() => handleClose()}
283
- underlayColor='#cccccc'
284
- style={styles.closeButton}
285
- >
286
- <Text style={{ fontWeight: 'bold', color: '#ffffff' }}>
287
- X
288
- </Text>
289
- </TouchableHighlight>
290
- <Carousel
291
- vertical
292
- ref={CarouselRef}
293
- layout={'default'}
294
- layoutCardOffset={10}
295
- data={data}
296
- inactiveSlideOpacity={0}
297
- containerCustomStyle={{
298
- backgroundColor: 'white',
299
- elevation: 10,
300
- borderRadius: 10,
301
- width: SLIDER_WIDTH * 0.8,
302
- height: 480,
303
- }}
304
- contentContainerCustomStyle={{ justifyContent: 'center' }}
305
- inactiveSlideShift={0}
306
- onSnapToItem={(index) => {
307
- handleBg(index)
308
- }}
309
- renderItem={({ item, index }) => _renderItem({ item, index })}
310
- sliderHeight={500}
311
- itemHeight={500}
312
- />
313
- </ImageBackground>
314
- </View>
315
- </View>
316
- </Modal>
317
- </View>
318
- );
319
- } else {
320
- return null
321
- }
322
- };
323
-
324
- const styles = styleInapp({ SLIDER_WIDTH })
@@ -1,189 +0,0 @@
1
- import { Linking } from 'react-native'
2
- import InAppBrowser from 'react-native-inappbrowser-reborn'
3
- import messaging from '@react-native-firebase/messaging';
4
- import { showAlert } from './utils'
5
- import { notificationApi } from './services/inngage'
6
- import AsyncStorage from '@react-native-async-storage/async-storage';
7
- import PushNotification, { Importance } from 'react-native-push-notification'
8
-
9
- export const linkInApp = (link: string) => {
10
- InAppBrowser.open(link, {
11
- dismissButtonStyle: 'cancel',
12
- preferredBarTintColor: 'gray',
13
- preferredControlTintColor: 'white',
14
- readerMode: false,
15
- showTitle: true,
16
- toolbarColor: '#6200EE',
17
- secondaryToolbarColor: 'black',
18
- enableUrlBarHiding: true,
19
- enableDefaultShare: true,
20
- forceCloseOnRedirection: false,
21
- animations: {
22
- startEnter: 'slide_in_right',
23
- startExit: 'slide_out_left',
24
- endEnter: 'slide_in_right',
25
- endExit: 'slide_out_left',
26
- },
27
- })
28
- }
29
-
30
- const openLinkByType = (type: string, url: string) => {
31
- const linkTypeHandlers: Record<string, () => void> = {
32
- deep: () => Linking.openURL(url),
33
- // inapp: () => linkInApp(url),
34
- };
35
-
36
- const handler = linkTypeHandlers[type];
37
- if (handler) {
38
- handler();
39
- }
40
- };
41
-
42
- const openCommonNotification = ({ appToken, dev, remoteMessage, enableAlert, state }) => {
43
- console.log("Notification Opened from", state)
44
-
45
- if (!remoteMessage)
46
- return
47
-
48
- const { data } = remoteMessage
49
- console.log("Data:", data)
50
- if (!data || (data && !Object.keys(data).length))
51
- return
52
-
53
- const { notId, title, body, type, url } = data
54
- const request = {
55
- notificationRequest: {
56
- id: notId,
57
- app_token: appToken,
58
- }
59
- }
60
-
61
- if (url) {
62
- Linking.canOpenURL(url).then((supported) => {
63
- if (supported) {
64
- supported && openLinkByType(type, url)
65
- }
66
- }).catch(console.error)
67
- }
68
-
69
- return notificationApi(request, dev)
70
- }
71
-
72
- const handleUniqueRemoteMessage = async (
73
- remoteMessage: { messageId?: string },
74
- handleInitialNotification: (value: { messageId?: string }) => void
75
- ) => {
76
- try {
77
- const lastRemoteMessageId = await AsyncStorage.getItem('LAST_REMOTE_MESSAGE_ID');
78
- const newRemoteMessageId = remoteMessage?.messageId;
79
-
80
- if (newRemoteMessageId && lastRemoteMessageId !== newRemoteMessageId) {
81
- await AsyncStorage.setItem('LAST_REMOTE_MESSAGE_ID', newRemoteMessageId);
82
- handleInitialNotification(remoteMessage);
83
- }
84
- } catch (e) {
85
- console.log(e);
86
- }
87
- };
88
-
89
- export interface notificationsListenerProps {
90
- appToken: string,
91
- dev?: boolean,
92
- enableAlert: boolean,
93
- onNotificationOpenedApp?: any,
94
- }
95
- export default async ({ appToken, dev, enableAlert, onNotificationOpenedApp }: notificationsListenerProps) => {
96
- var messageArray: any = [];
97
-
98
- if (typeof onNotificationOpenedApp == 'function') {
99
- messaging().getInitialNotification().then(async (value) => {
100
- onNotificationOpenedApp(value?.data);
101
- // console.log("Remote message ID:", value?.messageId)
102
- if (value !== null)
103
- handleUniqueRemoteMessage(value, async (value) => {
104
- await handleInitialNotification(value);
105
- });
106
- });
107
- }
108
-
109
- const handleBackgroundMessage = async (remoteMessage: any) => {
110
- console.log('Message handled in the background!', remoteMessage);
111
- }
112
-
113
- const handleNotificationOpenedApp = async (remoteMessage: any) => {
114
- await openCommonNotification({ appToken, dev, remoteMessage, enableAlert, state: 'Background' })
115
- }
116
-
117
- const handleInitialNotification = async (remoteMessage: any) => {
118
- await openCommonNotification({ appToken, dev, remoteMessage, enableAlert, state: 'Closed' })
119
- }
120
-
121
- const handleForegroundMessage = async (remoteMessage: any) => {
122
- try {
123
- PushNotification.configure({
124
- onNotification: function (notification) {
125
- console.log('LOCAL NOTIFICATION ==>', notification)
126
-
127
- openCommonNotification({ appToken, dev, remoteMessage, enableAlert, state: 'Foreground' })
128
- },
129
- popInitialNotification: true,
130
- requestPermissions: true
131
- })
132
-
133
- PushNotification.createChannel({
134
- channelId: 'high_importance_channel',
135
- channelName: 'default',
136
- importance: Importance.HIGH,
137
- playSound: true,
138
- soundName: 'default',
139
- vibrate: true
140
- }, (created) => console.log(`createChannel returned '${created}'`));
141
-
142
- } catch (e) {
143
- console.error(e)
144
- }
145
- try {
146
- PushNotification.localNotification({
147
- autoCancel: true,
148
- title: remoteMessage.data!.title,
149
- message: remoteMessage.data!.body,
150
- vibration: 300,
151
- channelId: "high_importance_channel"
152
- });
153
-
154
- console.log('LOCAL NOTIFICATION : ')
155
- console.log(remoteMessage)
156
- } catch (e) {
157
- console.error('LOCAL NOTIFICATION ERROR: ')
158
- console.error(e)
159
- }
160
-
161
- if (remoteMessage != null && remoteMessage.data!.additional_data) {
162
- let msg = JSON.parse(remoteMessage.data!.additional_data)
163
- if (msg.inapp_message == true) {
164
- const currentMessages = await AsyncStorage.getItem('inngage');
165
- if (currentMessages !== null) {
166
- messageArray = JSON.parse(currentMessages);
167
- }
168
- messageArray.push(remoteMessage);
169
- await AsyncStorage.setItem('inngage', JSON.stringify(messageArray));
170
- }
171
- } else if (remoteMessage != null && !remoteMessage.data!.additional_data) {
172
- console.log(remoteMessage.data!.title)
173
- if (enableAlert) {
174
- showAlert(remoteMessage.data!.title, remoteMessage.data!.body)
175
- }
176
- }
177
- }
178
-
179
- messaging().setBackgroundMessageHandler(handleBackgroundMessage)
180
- messaging().onNotificationOpenedApp(handleNotificationOpenedApp)
181
- messaging().getInitialNotification().then(async (remoteMessage) => {
182
- // console.log("Remote message ID:", remoteMessage?.messageId)
183
- if (remoteMessage !== null)
184
- handleUniqueRemoteMessage(remoteMessage, async (value) => {
185
- await handleInitialNotification(value);
186
- });
187
- })
188
- messaging().onMessage(handleForegroundMessage)
189
- }
@@ -1,42 +0,0 @@
1
- const baseUrlHook = {
2
- [false as any]: 'https://api.inngage.com.br/v1',
3
- [undefined as any]: 'https://api.inngage.com.br/v1',
4
- [null as any]: 'https://api.inngage.com.br/v1',
5
- [true as any]: 'https://apid.inngage.com.br/v1',
6
- }
7
-
8
- const requestConfigFactory = (method, request) => {
9
-
10
- let header: any = {
11
- Accept: 'application/json',
12
- 'Content-Type': 'application/json',
13
- }
14
- try{
15
- if (request?.registerSubscriberRequest?.authKey) {
16
- header = {
17
- ...header,
18
- Authorization: request.registerSubscriberRequest.authKey
19
- }
20
- }
21
- }catch(e){
22
- console.error(e)
23
- }
24
- let objToSend = {
25
- method,
26
- body: JSON.stringify(request),
27
- headers: header
28
- }
29
-
30
- return objToSend
31
- }
32
-
33
- export const fetchClient = (method, requestBody, path, isDev = false): Promise<Response> => {
34
- return new Promise((resolve) => {
35
- const url = String(baseUrlHook[isDev as any]).concat(path)
36
- const request = requestConfigFactory(method, requestBody)
37
- console.log(request)
38
- fetch(url, request)
39
- .then(resolve)
40
- .catch(err => console.error('Fetch Error', err))
41
- })
42
- }
@@ -1,23 +0,0 @@
1
- import { InngageProperties } from '../models/inngage_properties'
2
- import { fetchClient } from './handler'
3
-
4
- export const subscriptionApi = (request, dev = false) => {
5
- if (InngageProperties.getDebugMode())
6
- console.log('subscriptionApi', request)
7
- return fetchClient('POST', request, '/subscription/', !!dev)
8
- }
9
- export const notificationApi = (request, dev = false) => {
10
- if (InngageProperties.getDebugMode())
11
- console.log('notificationApi', request)
12
- return fetchClient('POST', request, '/notification/', !!dev)
13
- }
14
- export const eventsApi = (request, dev = false) => {
15
- if (InngageProperties.getDebugMode())
16
- console.log('eventsApi', request)
17
- return fetchClient('POST', request, '/events/newEvent/', !!dev)
18
- }
19
- export const addUserDataApi = (request, dev = false) => {
20
- if (InngageProperties.getDebugMode())
21
- console.log('addUserData', request)
22
- return fetchClient('POST', request, '/subscription/addCustomField', !!dev)
23
- }