Skip to content

Commit 733ebd3

Browse files
authored
Merge branch 'loren/embedded/master' into loren/embedded/MOB-12260-create-embedded-tab-in-example-app
2 parents 25ec86d + 074d64f commit 733ebd3

File tree

13 files changed

+712
-68
lines changed

13 files changed

+712
-68
lines changed

CHANGELOG.md

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
1-
## 2.2.0-alpha.1
2-
3-
### Updates
4-
* [SDK-149] Added logout functionality
5-
6-
### Fixes
7-
* [SDK-151] Fixed "cannot read property authtoken of undefined" error
8-
9-
10-
## 2.2.0-alpha.0
1+
## 2.2.0
112

123
### Updates
134
- Updated Android SDK version to [3.6.2](https://github.com/Iterable/iterable-android-sdk/releases/tag/3.6.2)
@@ -18,13 +9,19 @@
189
- Added `onJwtError` and `retryPolicy` for control over JWT flow
1910
- Moved all native calls to `IterableApi.ts`
2011
- Added JWT example to our example app
12+
- Changed `onJWTError` to `onJwtError`
13+
- Changed `IterableRetryBackoff` enum keys to be lowercase for consistency
14+
across application
15+
- [SDK-149] Added logout functionality
2116

2217
### Fixes
2318
- Created a standalone `IterableLogger` to avoid circular dependencies
19+
- [SDK-151] Fixed "cannot read property authtoken of undefined" error
20+
- Fixed Android `retryInterval` not being updated on re-initialization.
2421

2522
## 2.1.0
2623
### Updates
27-
* SDK is now compatible with both New Architecture and Legacy Architecture. Fix
24+
- SDK is now compatible with both New Architecture and Legacy Architecture. Fix
2825
for #691, #602, #563.
2926

3027
### Fixes

android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.iterable.iterableapi.IterableActionContext;
2525
import com.iterable.iterableapi.IterableApi;
2626
import com.iterable.iterableapi.IterableAuthHandler;
27+
import com.iterable.iterableapi.IterableAuthManager;
2728
import com.iterable.iterableapi.IterableConfig;
2829
import com.iterable.iterableapi.IterableCustomActionHandler;
2930
import com.iterable.iterableapi.IterableAttributionInfo;
@@ -88,7 +89,36 @@ public void initializeWithApiKey(String apiKey, ReadableMap configReadableMap, S
8889
configBuilder.setAuthHandler(this);
8990
}
9091

91-
IterableApi.initialize(reactContext, apiKey, configBuilder.build());
92+
IterableConfig config = configBuilder.build();
93+
IterableApi.initialize(reactContext, apiKey, config);
94+
95+
// Update retry policy on existing authManager if it was already created
96+
// This fixes the issue where retryInterval is not respected after
97+
// re-initialization
98+
// TODO [SDK-197]: Fix the root cause of this issue, instead of this hack
99+
try {
100+
// Use reflection to access package-private fields and methods
101+
java.lang.reflect.Field configRetryPolicyField = config.getClass().getDeclaredField("retryPolicy");
102+
configRetryPolicyField.setAccessible(true);
103+
Object retryPolicy = configRetryPolicyField.get(config);
104+
105+
if (retryPolicy != null) {
106+
java.lang.reflect.Method getAuthManagerMethod = IterableApi.getInstance().getClass().getDeclaredMethod("getAuthManager");
107+
getAuthManagerMethod.setAccessible(true);
108+
IterableAuthManager authManager = (IterableAuthManager) getAuthManagerMethod.invoke(IterableApi.getInstance());
109+
110+
if (authManager != null) {
111+
// Update the retry policy field on the authManager
112+
java.lang.reflect.Field authRetryPolicyField = authManager.getClass().getDeclaredField("authRetryPolicy");
113+
authRetryPolicyField.setAccessible(true);
114+
authRetryPolicyField.set(authManager, retryPolicy);
115+
IterableLogger.d(TAG, "Updated retry policy on existing authManager");
116+
}
117+
}
118+
} catch (Exception e) {
119+
IterableLogger.e(TAG, "Failed to update retry policy: " + e.getMessage());
120+
}
121+
92122
IterableApi.getInstance().setDeviceAttribute("reactNativeSDKVersion", version);
93123

94124
IterableApi.getInstance().getInAppManager().addListener(this);
@@ -122,7 +152,36 @@ public void initialize2WithApiKey(String apiKey, ReadableMap configReadableMap,
122152
// override in the Android SDK. Check with @Ayyanchira and @evantk91 to
123153
// see what the best approach is.
124154

125-
IterableApi.initialize(reactContext, apiKey, configBuilder.build());
155+
IterableConfig config = configBuilder.build();
156+
IterableApi.initialize(reactContext, apiKey, config);
157+
158+
// Update retry policy on existing authManager if it was already created
159+
// This fixes the issue where retryInterval is not respected after
160+
// re-initialization
161+
// TODO [SDK-197]: Fix the root cause of this issue, instead of this hack
162+
try {
163+
// Use reflection to access package-private fields and methods
164+
java.lang.reflect.Field configRetryPolicyField = config.getClass().getDeclaredField("retryPolicy");
165+
configRetryPolicyField.setAccessible(true);
166+
Object retryPolicy = configRetryPolicyField.get(config);
167+
168+
if (retryPolicy != null) {
169+
java.lang.reflect.Method getAuthManagerMethod = IterableApi.getInstance().getClass().getDeclaredMethod("getAuthManager");
170+
getAuthManagerMethod.setAccessible(true);
171+
IterableAuthManager authManager = (IterableAuthManager) getAuthManagerMethod.invoke(IterableApi.getInstance());
172+
173+
if (authManager != null) {
174+
// Update the retry policy field on the authManager
175+
java.lang.reflect.Field authRetryPolicyField = authManager.getClass().getDeclaredField("authRetryPolicy");
176+
authRetryPolicyField.setAccessible(true);
177+
authRetryPolicyField.set(authManager, retryPolicy);
178+
IterableLogger.d(TAG, "Updated retry policy on existing authManager");
179+
}
180+
}
181+
} catch (Exception e) {
182+
IterableLogger.e(TAG, "Failed to update retry policy: " + e.getMessage());
183+
}
184+
126185
IterableApi.getInstance().setDeviceAttribute("reactNativeSDKVersion", version);
127186

128187
IterableApi.getInstance().getInAppManager().addListener(this);

example/ios/ReactNativeSdkExample/Info.plist

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -52,27 +52,5 @@
5252
</array>
5353
<key>UIViewControllerBasedStatusBarAppearance</key>
5454
<false/>
55-
<key>UIAppFonts</key>
56-
<array>
57-
<string>AntDesign.ttf</string>
58-
<string>Entypo.ttf</string>
59-
<string>EvilIcons.ttf</string>
60-
<string>Feather.ttf</string>
61-
<string>FontAwesome.ttf</string>
62-
<string>FontAwesome5_Brands.ttf</string>
63-
<string>FontAwesome5_Regular.ttf</string>
64-
<string>FontAwesome5_Solid.ttf</string>
65-
<string>FontAwesome6_Brands.ttf</string>
66-
<string>FontAwesome6_Regular.ttf</string>
67-
<string>FontAwesome6_Solid.ttf</string>
68-
<string>Foundation.ttf</string>
69-
<string>Ionicons.ttf</string>
70-
<string>MaterialIcons.ttf</string>
71-
<string>MaterialCommunityIcons.ttf</string>
72-
<string>SimpleLineIcons.ttf</string>
73-
<string>Octicons.ttf</string>
74-
<string>Zocial.ttf</string>
75-
<string>Fontisto.ttf</string>
76-
</array>
7755
</dict>
7856
</plist>

example/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"@babel/core": "^7.25.2",
2626
"@babel/preset-env": "^7.25.3",
2727
"@babel/runtime": "^7.25.0",
28-
"@react-native-community/cli": "18.0.0",
28+
"@react-native-community/cli": "18.0.1",
2929
"@react-native-community/cli-platform-android": "18.0.0",
3030
"@react-native-community/cli-platform-ios": "18.0.0",
3131
"@react-native/babel-preset": "0.79.3",

example/src/hooks/useIterableApp.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
8989

9090
const getIsEmail = (id: string) => EMAIL_REGEX.test(id);
9191

92+
let lastTimeStamp = 0;
93+
9294
export const IterableAppProvider: FunctionComponent<
9395
React.PropsWithChildren<unknown>
9496
> = ({ children }) => {
@@ -141,17 +143,15 @@ export const IterableAppProvider: FunctionComponent<
141143

142144
const initialize = useCallback(
143145
(navigation: Navigation) => {
144-
if (getUserId()) {
145-
login();
146-
}
146+
logout();
147147

148148
const config = new IterableConfig();
149149

150150
config.inAppDisplayInterval = 1.0; // Min gap between in-apps. No need to set this in production.
151151

152152
config.retryPolicy = {
153153
maxRetry: 5,
154-
retryInterval: 10,
154+
retryInterval: 5,
155155
retryBackoff: IterableRetryBackoff.linear,
156156
};
157157

@@ -199,8 +199,16 @@ export const IterableAppProvider: FunctionComponent<
199199
process.env.ITBL_JWT_SECRET
200200
) {
201201
config.authHandler = async () => {
202+
console.group('authHandler');
203+
const now = Date.now();
204+
if (lastTimeStamp !== 0) {
205+
console.log('Time since last call:', now - lastTimeStamp);
206+
}
207+
lastTimeStamp = now;
208+
console.groupEnd();
209+
210+
// return 'InvalidToken'; // Uncomment this to test the failure callback
202211
const token = await getJwtToken();
203-
// return 'SomethingNotValid'; // Uncomment this to test the failure callback
204212
return token;
205213
};
206214
}
@@ -219,6 +227,10 @@ export const IterableAppProvider: FunctionComponent<
219227
.then((isSuccessful) => {
220228
setIsInitialized(isSuccessful);
221229

230+
if (isSuccessful && getUserId()) {
231+
return login();
232+
}
233+
222234
if (!isSuccessful) {
223235
return Promise.reject('`Iterable.initialize` failed');
224236
}
@@ -242,6 +254,8 @@ export const IterableAppProvider: FunctionComponent<
242254
const logout = useCallback(() => {
243255
Iterable.setEmail(null);
244256
Iterable.setUserId(null);
257+
Iterable.logout();
258+
lastTimeStamp = 0;
245259
setIsLoggedIn(false);
246260
}, []);
247261

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@iterable/react-native-sdk",
3-
"version": "2.2.0-alpha.1",
3+
"version": "2.2.0",
44
"description": "Iterable SDK for React Native.",
55
"source": "./src/index.tsx",
66
"main": "./lib/module/index.js",

src/__mocks__/MockRNIterableAPI.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,28 @@ export class MockRNIterableAPI {
8686
});
8787
}
8888

89+
static async getInboxMessages(): Promise<IterableInAppMessage[] | undefined> {
90+
return await new Promise((resolve) => {
91+
// Filter messages that are marked for inbox
92+
const inboxMessages =
93+
MockRNIterableAPI.messages?.filter((msg) => msg.saveToInbox) || [];
94+
resolve(inboxMessages);
95+
});
96+
}
97+
98+
static async getHtmlInAppContentForMessage(
99+
messageId: string
100+
): Promise<unknown> {
101+
return await new Promise((resolve) => {
102+
// Mock HTML content for testing
103+
const mockHtmlContent = {
104+
edgeInsets: { top: 10, left: 20, bottom: 30, right: 40 },
105+
html: `<div>Mock HTML content for message ${messageId}</div>`,
106+
};
107+
resolve(mockHtmlContent);
108+
});
109+
}
110+
89111
static setAutoDisplayPaused = jest.fn();
90112

91113
static showMessage = jest.fn(
@@ -113,22 +135,12 @@ export class MockRNIterableAPI {
113135

114136
static updateSubscriptions = jest.fn();
115137

116-
static getInboxMessages = jest.fn(
117-
async (): Promise<IterableInAppMessage[] | undefined> => {
118-
return await new Promise((resolve) => {
119-
resolve(MockRNIterableAPI.messages);
120-
});
121-
}
122-
);
123-
124138
static startSession = jest.fn();
125139

126140
static endSession = jest.fn();
127141

128142
static updateVisibleRows = jest.fn();
129143

130-
static getHtmlInAppContentForMessage = jest.fn();
131-
132144
// set messages function is to set the messages static property
133145
// this is for testing purposes only
134146
static setMessages(messages: IterableInAppMessage[]): void {

src/core/classes/Iterable.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -912,10 +912,16 @@ export class Iterable {
912912
private static removeAllEventListeners() {
913913
RNEventEmitter.removeAllListeners(IterableEventName.handleUrlCalled);
914914
RNEventEmitter.removeAllListeners(IterableEventName.handleInAppCalled);
915-
RNEventEmitter.removeAllListeners(IterableEventName.handleCustomActionCalled);
915+
RNEventEmitter.removeAllListeners(
916+
IterableEventName.handleCustomActionCalled
917+
);
916918
RNEventEmitter.removeAllListeners(IterableEventName.handleAuthCalled);
917-
RNEventEmitter.removeAllListeners(IterableEventName.handleAuthSuccessCalled);
918-
RNEventEmitter.removeAllListeners(IterableEventName.handleAuthFailureCalled);
919+
RNEventEmitter.removeAllListeners(
920+
IterableEventName.handleAuthSuccessCalled
921+
);
922+
RNEventEmitter.removeAllListeners(
923+
IterableEventName.handleAuthFailureCalled
924+
);
919925
}
920926

921927
/**
@@ -1015,11 +1021,15 @@ export class Iterable {
10151021
}
10161022
}, 1000);
10171023
} else if (typeof promiseResult === 'string') {
1018-
//If promise only returns string
1024+
// If promise only returns string
1025+
Iterable.authManager.passAlongAuthToken(promiseResult);
1026+
} else if (promiseResult === null || promiseResult === undefined) {
1027+
// Even though this will cause authentication to fail, we want to
1028+
// allow for this for JWT handling.
10191029
Iterable.authManager.passAlongAuthToken(promiseResult);
10201030
} else {
10211031
IterableLogger?.log(
1022-
'Unexpected promise returned. Auth token expects promise of String or AuthResponse type.'
1032+
'Unexpected promise returned. Auth token expects promise of String, null, undefined, or AuthResponse type.'
10231033
);
10241034
}
10251035
})

src/core/classes/IterableApi.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ describe('IterableApi', () => {
730730
const result = await IterableApi.getInboxMessages();
731731

732732
// THEN the messages are returned
733-
expect(result).toBe(mockMessages);
733+
expect(result).toStrictEqual(mockMessages);
734734
});
735735
});
736736

src/core/classes/IterableConfig.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,9 @@ export class IterableConfig {
202202
* ```
203203
*
204204
* @returns A promise that resolves to an `IterableAuthResponse`, a `string`,
205-
* or `undefined`.
205+
* `null`, or `undefined`.
206206
*/
207-
authHandler?: () => Promise<IterableAuthResponse | string | undefined>;
207+
authHandler?: () => Promise<IterableAuthResponse | string | null | undefined>;
208208

209209
/**
210210
* A callback function that is called when the SDK encounters an error while

0 commit comments

Comments
 (0)