Skip to content

Commit 656a164

Browse files
Refactor / Code improvements (#174)
* align min API with current React-Native version * fix discrepancy with Android * add ErrorBoundary component to sample and test Apps * add missing gpp string to current user status * cast dictionary to prevent build warning * use StringMap instead of Map * performance optimization * Added 30s timeout with rejection to clean up orphaned listeners Now clears both listeners and vendorStatusListeners maps Changed to use delete() to properly remove entries * Made async and awaited the native call + Added .catch() for native call errors * Add try-catch with error display * only remove specific vendor listener * use iPhone 17 for local tests
1 parent e13d6f7 commit 656a164

17 files changed

Lines changed: 308 additions & 149 deletions

android/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ Didomi_kotlinVersion=2.1.20
22

33
Didomi_buildToolsVersion=36.0.0
44
Didomi_compileSdkVersion=36
5-
Didomi_minSdkVersion=21
5+
Didomi_minSdkVersion=24
66
Didomi_targetSdkVersion=36

ios/RNDidomi.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ class RNDidomi: RCTEventEmitter {
349349
@objc(getText:resolve:reject:)
350350
dynamic func getText(key: String, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) {
351351
let text = Didomi.shared.getText(key: key)
352-
resolve(text)
352+
resolve(text ?? [:])
353353
}
354354

355355
@objc(setLogLevel:resolve:reject:)
@@ -919,8 +919,8 @@ extension RNDidomi {
919919
let callbackIndex = self?.syncAcknowledgedCallbackIndex ?? -1
920920
self?.syncAcknowledgedCallbackIndex += 1
921921
self?.syncAcknowledgedCallbacks[callbackIndex] = event.syncAcknowledged
922-
let result = [
923-
"organizationUserId": event.organizationUserId,
922+
let result: [String: Any] = [
923+
"organizationUserId": event.organizationUserId as Any,
924924
"statusApplied": event.statusApplied,
925925
"syncAcknowledgedIndex": callbackIndex
926926
]

sample/src/App.tsx

Lines changed: 38 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react';
1+
import React, { useState, useCallback } from 'react';
22

33
import { ScrollView, StyleSheet, Text, View } from 'react-native';
44
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
@@ -7,28 +7,28 @@ import Methods from './Methods';
77
import Getters from './Getters';
88
import Setters from './Setters';
99
import { TestEvent } from './Types';
10+
import ErrorBoundary from './ErrorBoundary';
1011

1112
function App() {
1213
const MAX_EVENTS_DISPLAYED = 3;
1314

1415
const [receivedEvents, setReceivedEvents] = useState<TestEvent[]>([]);
1516

16-
function pushReceivedEvent(event: TestEvent) {
17-
receivedEvents.push(event);
18-
if (receivedEvents.length > MAX_EVENTS_DISPLAYED) {
19-
receivedEvents.shift();
20-
}
21-
setReceivedEvents([
22-
...receivedEvents
23-
]);
24-
}
17+
const pushReceivedEvent = useCallback((event: TestEvent) => {
18+
setReceivedEvents(prevEvents => {
19+
const newEvents = [...prevEvents, event];
20+
return newEvents.length > MAX_EVENTS_DISPLAYED
21+
? newEvents.slice(-MAX_EVENTS_DISPLAYED)
22+
: newEvents;
23+
});
24+
}, [MAX_EVENTS_DISPLAYED]);
2525

26-
const registerListener = (eventType: DidomiEventType) => {
26+
const registerListener = useCallback((eventType: DidomiEventType) => {
2727
Didomi.addEventListener(eventType, (data: any) => {
2828
pushReceivedEvent({ name: eventType, data });
2929
console.log('event received: ' + eventType);
3030
});
31-
};
31+
}, [pushReceivedEvent]);
3232

3333
React.useEffect(() => {
3434
Didomi.removeAllEventListeners();
@@ -74,23 +74,11 @@ function App() {
7474
console.log('error');
7575
});
7676

77-
/*Didomi.addEventListener(DidomiEventType.READY, (data: any) => {
78-
setReceivedEvent({ name: DidomiEventType.READY, data });
79-
console.log("I'm ready");
80-
});
81-
82-
Didomi.addEventListener(DidomiEventType.SHOW_NOTICE, (data: any) => {
83-
setReceivedEvent({ name: DidomiEventType.SHOW_NOTICE, data });
84-
console.log('Show notice');
85-
});
86-
87-
Didomi.addEventListener(DidomiEventType.CONSENT_CHANGED, (data: any) => {
88-
setReceivedEvent({ name: DidomiEventType.CONSENT_CHANGED, data });
89-
console.log('Consent changed');
90-
});*/
91-
92-
// eslint-disable-next-line react-hooks/exhaustive-deps
93-
}, []);
77+
// Cleanup on unmount
78+
return () => {
79+
Didomi.removeAllEventListeners();
80+
};
81+
}, [registerListener]);
9482

9583
function displayEvents() {
9684
return receivedEvents.map((event)=>{
@@ -102,26 +90,28 @@ function App() {
10290
}
10391

10492
return (
105-
<SafeAreaProvider>
106-
<SafeAreaView style={{ flex: 1 }}>
107-
<View style={styles.title}>
108-
<Text style={styles.title}>
109-
LAST RECEIVED EVENTS:
110-
{ displayEvents() }
111-
</Text>
112-
</View>
113-
<ScrollView>
114-
<View style={styles.container}>
115-
<Text style={styles.title}>METHODS</Text>
116-
<Methods />
117-
<Text style={styles.title}>GETTERS</Text>
118-
<Getters />
119-
<Text style={styles.title}>SETTERS</Text>
120-
<Setters />
93+
<ErrorBoundary>
94+
<SafeAreaProvider>
95+
<SafeAreaView style={{ flex: 1 }}>
96+
<View style={styles.title}>
97+
<Text style={styles.title}>
98+
LAST RECEIVED EVENTS:
99+
{ displayEvents() }
100+
</Text>
121101
</View>
122-
</ScrollView>
123-
</SafeAreaView>
124-
</SafeAreaProvider>
102+
<ScrollView>
103+
<View style={styles.container}>
104+
<Text style={styles.title}>METHODS</Text>
105+
<Methods />
106+
<Text style={styles.title}>GETTERS</Text>
107+
<Getters />
108+
<Text style={styles.title}>SETTERS</Text>
109+
<Setters />
110+
</View>
111+
</ScrollView>
112+
</SafeAreaView>
113+
</SafeAreaProvider>
114+
</ErrorBoundary>
125115
);
126116
}
127117

sample/src/ErrorBoundary.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React, { Component, ErrorInfo, ReactNode } from 'react';
2+
import { View, Text, StyleSheet, Button } from 'react-native';
3+
4+
interface Props {
5+
children: ReactNode;
6+
}
7+
8+
interface State {
9+
hasError: boolean;
10+
error: Error | null;
11+
}
12+
13+
class ErrorBoundary extends Component<Props, State> {
14+
constructor(props: Props) {
15+
super(props);
16+
this.state = { hasError: false, error: null };
17+
}
18+
19+
static getDerivedStateFromError(error: Error): State {
20+
return { hasError: true, error };
21+
}
22+
23+
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
24+
console.error('ErrorBoundary caught an error:', error, errorInfo);
25+
}
26+
27+
handleReset = () => {
28+
this.setState({ hasError: false, error: null });
29+
};
30+
31+
render() {
32+
if (this.state.hasError) {
33+
return (
34+
<View style={styles.container}>
35+
<Text style={styles.title}>Something went wrong</Text>
36+
<Text style={styles.message}>{this.state.error?.message}</Text>
37+
<Button title="Try Again" onPress={this.handleReset} />
38+
</View>
39+
);
40+
}
41+
42+
return this.props.children;
43+
}
44+
}
45+
46+
const styles = StyleSheet.create({
47+
container: {
48+
flex: 1,
49+
justifyContent: 'center',
50+
alignItems: 'center',
51+
padding: 20,
52+
},
53+
title: {
54+
fontSize: 18,
55+
fontWeight: 'bold',
56+
marginBottom: 10,
57+
},
58+
message: {
59+
fontSize: 14,
60+
color: 'red',
61+
marginBottom: 20,
62+
textAlign: 'center',
63+
},
64+
});
65+
66+
export default ErrorBoundary;

sample/src/Setters.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default function Setters() {
2525
consent_string: '',
2626
addtl_consent: '',
2727
didomi_dcs: '',
28+
gpp_string: '',
2829
regulation: ''
2930
});
3031
}}
@@ -51,6 +52,7 @@ export default function Setters() {
5152
consent_string: '',
5253
addtl_consent: '',
5354
didomi_dcs: '',
55+
gpp_string: '',
5456
regulation: ''
5557
});
5658
}}

src/Didomi.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,17 @@ export const Didomi = {
7878
* Listen to SDK ready state
7979
*/
8080
onReady: (): Promise<void> => {
81-
var promise = DidomiListener.setOnReadyListener();
82-
RNDidomi.onReady();
81+
const promise = DidomiListener.setOnReadyListener();
82+
RNDidomi.onReady().catch((err: Error) => console.error('Didomi onReady native error:', err));
8383
return promise;
8484
},
8585

8686
/**
8787
* Listen to SDK errors
8888
*/
8989
onError: (): Promise<void> => {
90-
var promise = DidomiListener.setOnErrorListener();
91-
RNDidomi.onError();
90+
const promise = DidomiListener.setOnErrorListener();
91+
RNDidomi.onError().catch((err: Error) => console.error('Didomi onError native error:', err));
9292
return promise;
9393
},
9494

@@ -137,11 +137,11 @@ export const Didomi = {
137137
* @param vendorId: the id of the vendor
138138
* @param callback: the callback to trigger when the user status for the selected vendor changes
139139
*/
140-
addVendorStatusListener: (
140+
addVendorStatusListener: async (
141141
vendorId: string,
142142
callback: (vendorStatus: VendorStatus) => void
143-
) => {
144-
RNDidomi.listenToVendorStatus(vendorId)
143+
): Promise<void> => {
144+
await RNDidomi.listenToVendorStatus(vendorId);
145145
DidomiListener.addVendorStatusListener(vendorId, callback);
146146
},
147147

@@ -346,7 +346,7 @@ export const Didomi = {
346346
* @returns: The user LI status corresponding to the specified vendor and all its required purposes.
347347
* @deprecated use {@link #getCurrentUserStatus()} instead.
348348
*/
349-
getUserLegitimateInterestStatusForVendorAndRequiredPurposes: (vendorId: string): boolean => RNDidomi.getUserLegitimateInterestStatusForVendorAndRequiredPurposes(vendorId),
349+
getUserLegitimateInterestStatusForVendorAndRequiredPurposes: (vendorId: string): Promise<boolean> => RNDidomi.getUserLegitimateInterestStatusForVendorAndRequiredPurposes(vendorId),
350350

351351
/**
352352
* Get the current user consent status.

src/DidomiListener.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ export const DidomiListener = {
2828
}
2929
}
3030
events.forEach((el: any) => {
31-
el(_event);
31+
try {
32+
el(_event);
33+
} catch (err) {
34+
console.error('Didomi event listener error:', err);
35+
}
3236
});
3337
} else { // No listener for this event type
3438
if (eventTypeValue == "on_sync_ready") {
@@ -43,6 +47,13 @@ export const DidomiListener = {
4347
reset: () => {
4448
// Reset listeners
4549
DidomiListener.listeners = new Map();
50+
// Clear vendor status listeners and their native event emitters
51+
DidomiListener.vendorStatusListeners.forEach((_, vendorId) => {
52+
DidomiListener.eventEmitter.removeAllListeners(
53+
InternalEventType.VENDOR_STATUS_CHANGE_PREFIX + vendorId
54+
);
55+
});
56+
DidomiListener.vendorStatusListeners = new Map();
4657
},
4758

4859
addEventListener: (
@@ -71,8 +82,14 @@ export const DidomiListener = {
7182
},
7283

7384
setOnReadyListener: (): Promise<void> => {
74-
return new Promise<void>((resolve) => {
85+
return new Promise<void>((resolve, reject) => {
86+
const timeoutId = setTimeout(() => {
87+
DidomiListener.eventEmitter.removeAllListeners(InternalEventType.READY_CALLBACK);
88+
reject(new Error('Didomi SDK ready timeout'));
89+
}, 30000);
90+
7591
const listener = (_event: any) => {
92+
clearTimeout(timeoutId);
7693
resolve();
7794
DidomiListener.eventEmitter.removeAllListeners(InternalEventType.READY_CALLBACK);
7895
};
@@ -84,8 +101,14 @@ export const DidomiListener = {
84101
},
85102

86103
setOnErrorListener: (): Promise<void> => {
87-
return new Promise<void>((resolve) => {
104+
return new Promise<void>((resolve, reject) => {
105+
const timeoutId = setTimeout(() => {
106+
DidomiListener.eventEmitter.removeAllListeners(InternalEventType.ERROR_CALLBACK);
107+
reject(new Error('Didomi SDK error listener timeout'));
108+
}, 30000);
109+
88110
const listener = (_event: any) => {
111+
clearTimeout(timeoutId);
89112
resolve(_event);
90113
DidomiListener.eventEmitter.removeAllListeners(InternalEventType.ERROR_CALLBACK);
91114
};
@@ -109,7 +132,11 @@ export const DidomiListener = {
109132
let events = DidomiListener.vendorStatusListeners.get(vendorId);
110133
if (events) {
111134
events.forEach((el: any) => {
112-
el(_event);
135+
try {
136+
el(_event);
137+
} catch (err) {
138+
console.error('Didomi vendor status listener error:', err);
139+
}
113140
});
114141
}
115142
});
@@ -118,10 +145,9 @@ export const DidomiListener = {
118145
},
119146

120147
removeVendorStatusListener: (vendorId: string) => {
121-
let events = DidomiListener.vendorStatusListeners.get(vendorId);
122-
if (events) {
148+
if (DidomiListener.vendorStatusListeners.has(vendorId)) {
123149
DidomiListener.eventEmitter.removeAllListeners(InternalEventType.VENDOR_STATUS_CHANGE_PREFIX + vendorId);
124-
DidomiListener.vendorStatusListeners.set(vendorId, undefined);
150+
DidomiListener.vendorStatusListeners.delete(vendorId);
125151
}
126152
},
127153
};

0 commit comments

Comments
 (0)