From 34fa432b591e92e898f4503e3d824f562a140ae2 Mon Sep 17 00:00:00 2001 From: Morgan Berger Date: Fri, 23 Jan 2026 16:28:47 +0100 Subject: [PATCH 01/11] chore: update main sdk version --- RNAccessIOS.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RNAccessIOS.podspec b/RNAccessIOS.podspec index cd0bc17a..2b78ba9d 100644 --- a/RNAccessIOS.podspec +++ b/RNAccessIOS.podspec @@ -41,6 +41,6 @@ Pod::Spec.new do |s| end end - s.dependency "AccessIOS", "2.7.0" + s.dependency "AccessIOS", "2.10.0" # s.vendored_frameworks = "ios/AccessIOS.xcframework" end From 147cf94fb8a837ea155f4d7b163ce918434972f4 Mon Sep 17 00:00:00 2001 From: Morgan Berger Date: Fri, 23 Jan 2026 16:29:24 +0100 Subject: [PATCH 02/11] chore: podfile lock to new version --- example/ios/Podfile.lock | 106 +++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 01243a5f..eb520a67 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - AccessIOS (2.7.0) + - AccessIOS (2.10.0) - boost (1.84.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) @@ -1527,7 +1527,7 @@ PODS: - React-perflogger (= 0.78.2) - React-utils (= 0.78.2) - RNAccessIOS (1.0.5): - - AccessIOS (= 2.7.0) + - AccessIOS (= 2.10.0) - DoubleConversion - glog - hermes-engine @@ -1789,7 +1789,7 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - AccessIOS: 84a8c3dd1d657c1fedbdec3081c9d242b9489251 + AccessIOS: bccd4d982f22ef2124ded3f9b6e06e03d1b44b81 boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 @@ -1797,69 +1797,69 @@ SPEC CHECKSUMS: fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: eb93e2f488219332457c3c4eafd2738ddc7e80b8 hermes-engine: 2771b98fb813fdc6f92edd7c9c0035ecabf9fee7 - RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82 + RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809 RCTDeprecation: be794de7dc6ed8f9f7fbf525f86e7651b8b68746 RCTRequired: a83787b092ec554c2eb6019ff3f5b8d125472b3b RCTTypeSafety: 48ad3c858926b1c46f46a81a58822b476e178e2c React: 3b5754191f1b65f1dbc52fbea7959c3d2d9e39c9 React-callinvoker: 6beeaf4c7db11b6cc953fac45f2c76e3fb125013 - React-Core: 8a10ac9de53373a3ecb5dfcbcf56df1d3dad0861 - React-CoreModules: af6999b35c7c01b0e12b59d27f3e054e13da43b1 - React-cxxreact: 833f00155ce8c2fda17f6d286f8eaeff2ececc69 + React-Core: 88e817c42de035378cc71e009193b9a044d3f595 + React-CoreModules: dcf764d71efb4f75d38fcae8d4513b6729f49360 + React-cxxreact: 8cdcc937c5fbc406fe843a381102fd69440ca78a React-debug: 440175830c448e7e53e61ebb8d8468c3256b645e - React-defaultsnativemodule: a970effe18fe50bdbbb7115c3297f873b666d0d4 - React-domnativemodule: 45f886342a724e61531b18fba1859bb6782e5d62 - React-Fabric: 69f1881f2177a8512304a64157943548ab6df0cf - React-FabricComponents: f54111c8e2439fc273ab07483e3a7054ca1e75af - React-FabricImage: 9ad2619dfe8c386d79e8aaa87da6e8f018ab9592 + React-defaultsnativemodule: 4824bcd7b96ee2d75c28b1ca21f58976867f5535 + React-domnativemodule: a421118b475618961cf282e8ea85347cc9bb453c + React-Fabric: 6ac7de06009eb96b609a770b17abba6e460b5f45 + React-FabricComponents: e3bc2680a5a9a4917ff0c8d7f390688c30ef753c + React-FabricImage: 8bad558dec7478077974caa96acc79692d6b71f5 React-featureflags: b9cf9b35baca1c7f20c06a104ffc325a02752faa - React-featureflagsnativemodule: 7f1bc76d1d2c5bede5e753b8d188dbde7c59b12f - React-graphics: 069e0d0b31ed1e80feb023ad4f7e97f00e84f7b9 - React-hermes: 63df5ac5a944889c8758a6213b39ed825863adb7 - React-idlecallbacksnativemodule: 4c700bd7c0012adf904929075a79418b828b5ffc - React-ImageManager: 5d1ba8a7bae44ebba43fc93da64937c713d42941 - React-jserrorhandler: 0defd58f8bb797cdd0a820f733bf42d8bee708ce - React-jsi: 99d6207ec802ad73473a0dad3c9ad48cd98463f6 - React-jsiexecutor: 8c8097b4ba7e7f480582d6e6238b01be5dcc01c0 - React-jsinspector: ea148ec45bc7ff830e443383ea715f9780c15934 - React-jsinspectortracing: 46bb2841982f01e7b63eaab98140fa1de5b2a1db - React-jsitracing: c1063fc2233960d1c8322291e74bca51d25c10d7 - React-logger: 763728cf4eebc9c5dc9bfc3649e22295784f69f3 - React-Mapbuffer: 63278529b5cf531a7eaf8fc71244fabb062ca90c - React-microtasksnativemodule: 6a39463c32ce831c4c2aa8469273114d894b6be9 - React-NativeModulesApple: fd0545efbb7f936f78edd15a6564a72d2c34bb32 - React-perflogger: 5f8fa36a8e168fb355efe72099efe77213bc2ac6 - React-performancetimeline: 8c0ecfa1ae459cc5678a65f95ac3bf85644d6feb + React-featureflagsnativemodule: dc93d81da9f41f7132e24455ec8b4b60802fd5b0 + React-graphics: aaa5a38bea15d7b895b210d95d554af45a07002a + React-hermes: 08ad9fb832d1b9faef391be17309aa6a69fad23b + React-idlecallbacksnativemodule: aacea33ef6c511a9781f9286cc7cdf93f39bba14 + React-ImageManager: c596c3b658c9c14607f9183ed0f635c8dd77987c + React-jserrorhandler: 987609b2f16b7d79d63fcd621bf0110dd7400b35 + React-jsi: afa286d7e0c102c2478dc420d4f8935e13c973fc + React-jsiexecutor: 08f5b512b4db9e2f147416d60a0a797576b9cfef + React-jsinspector: 5a94bcae66e3637711c4d96a00038ab9ec935bf5 + React-jsinspectortracing: a12589a0adbb2703cbc4380dabe9a58800810923 + React-jsitracing: 0b1a403d7757cec66b7dd8b308d04db85eef75f3 + React-logger: 304814ae37503c8eb54359851cc55bd4f936b39c + React-Mapbuffer: b588d1ca18d2ce626f868f04ab12d8b1f004f12c + React-microtasksnativemodule: 11831d070aa47755bb5739069eb04ec621fec548 + React-NativeModulesApple: 79a4404ac301b40bec3b367879c5e9a9ce81683c + React-perflogger: 0ea25c109dba33d47dec36b2634bf7ea67c1a555 + React-performancetimeline: f74480de6efbcd8541c34317c0baedb433f27296 React-RCTActionSheet: 2ef95837e89b9b154f13cd8401f9054fc3076aff - React-RCTAnimation: 46abefd5acfda7e6629f9e153646deecc70babd2 - React-RCTAppDelegate: 7e58e0299e304cceee3f7019fa77bc6990f66b22 - React-RCTBlob: f68c63a801ef1d27e83c4011e3b083cc86a200d7 - React-RCTFabric: c59f41d0c4edbaac8baa232731ca09925ae4dda7 - React-RCTFBReactNativeSpec: 3240b9b8d792aa4be0fb85c9898fc183125ba8de - React-RCTImage: 34e0bba1507e55f1c614bd759eb91d9be48c8c5b - React-RCTLinking: a0b6c9f4871c18b0b81ea952f43e752718bd5f1d - React-RCTNetwork: bdafd661ac2b20d23b779e45bf7ac3e4c8bd1b60 - React-RCTSettings: 98aa5163796f43789314787b584a84eba47787a9 - React-RCTText: 424a274fc9015b29de89cf3cbcdf4dd85dd69f83 - React-RCTVibration: 92d9875a955b0adb34b4b773528fdbbbc5addd6c + React-RCTAnimation: 33d960d7f58a81779eea6dea47ad0364c67e1517 + React-RCTAppDelegate: 85c13403fd6f6b6cc630428d52bd8bd76a670dc9 + React-RCTBlob: 74c986a02d951931d2f6ed0e07ed5a7eb385bfc0 + React-RCTFabric: 384a8fea4f22fc0f21299d771971862883ba630a + React-RCTFBReactNativeSpec: eb1c3ec5149f76133593a516ff9d5efe32ebcecd + React-RCTImage: 2c58b5ddeb3c65e52f942bbe13ff9c59bd649b09 + React-RCTLinking: b6b14f8a3e62c02fc627ac4f3fb0c7bd941f907c + React-RCTNetwork: 1d050f2466c1541b339587d46f78d5eee218d626 + React-RCTSettings: 8148f6be0ccc0cfe6e313417ebf8a479caaa2146 + React-RCTText: 64114531ad1359e4e02a4a8af60df606dbbabc25 + React-RCTVibration: f4859417a7dd859b6bf18b1aba897e52beb72ef6 React-rendererconsistency: 5ac4164ec18cfdd76ed5f864dbfdc56a5a948bc9 - React-rendererdebug: 710dbd7990e355852c786aa6bc7753f6028f357a + React-rendererdebug: 3dc1d97bbee0c0c13191e501a96ed9325bbd920e React-rncore: 0bace3b991d8843bb5b57c5f2301ec6e9c94718b - React-RuntimeApple: 701ec44a8b5d863ee9b6a2b2447b6a26bb6805a1 - React-RuntimeCore: a82767065b9a936b05e209dc6987bc1ea9eb5d2d + React-RuntimeApple: 1e1e0a0c6086bc8c3b07e8f1a2f6ca99b50419a0 + React-RuntimeCore: d39322c59bef2a4b343fda663d20649f29f57fcc React-runtimeexecutor: 876dfc1d8daa819dfd039c40f78f277c5a3e66a6 - React-RuntimeHermes: e7a051fd91cab8849df56ac917022ef6064ad621 - React-runtimescheduler: c544141f2124ee3d5f3d5bf0d69f4029a61a68b0 + React-RuntimeHermes: 44f5f2baf039f249b31ea4f3e224484fd1731e0e + React-runtimescheduler: 3b3c5b50743bb8743ca49b9e5a70c2c385f156e1 React-timing: 1ee3572c398f5579c9df5bf76aacddf5683ff74e - React-utils: 18703928768cb37e70cf2efff09def12d74a399e - ReactAppDependencyProvider: 4893bde33952f997a323eb1a1ee87a72764018ff - ReactCodegen: 99ea3536c05be3c18d0c517acb56b5a6d726fc7b - ReactCommon: 865ebe76504a95e115b6229dd00a31e56d2d4bfe - RNAccessIOS: 44dc8918b3017f223d0fee3c0157e65b55eaf15d - RNPermissions: 9076a001ee581bb3a432a64ad77423fa46d9a8b3 + React-utils: 0cfb7c7fb37d4e5f31cc18ffc7426be0ae6bf907 + ReactAppDependencyProvider: b48473fe434569ff8f6cb6ed4421217ebcbda878 + ReactCodegen: 78b64f0ad96ef733616f54d0c20923f6c67287fd + ReactCommon: 547db015202a80a5b3e7e041586ea54c4a087180 + RNAccessIOS: a64ebe6dd6a59ccef0e2f3c30f36c3b3d4471383 + RNPermissions: cc1d3bbd4f7db41f7ab3a6eb84e6d8e8d020f6e4 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: e14bad835e12b6c7e2260fc320bd00e0f4b45add + Yoga: 66a9a23a82dd4081b393babe509967097759b3d6 PODFILE CHECKSUM: 3b20bcb510d182115033b052559bcc8c131548be -COCOAPODS: 1.16.2 +COCOAPODS: 1.13.0 From 3b7840713aba160cfccd1c5c10771e6167431cc3 Mon Sep 17 00:00:00 2001 From: Morgan Berger Date: Fri, 23 Jan 2026 16:43:03 +0100 Subject: [PATCH 03/11] fix: crash on register --- ios/PaywallView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/PaywallView.mm b/ios/PaywallView.mm index 48a0c185..5cd851d2 100644 --- a/ios/PaywallView.mm +++ b/ios/PaywallView.mm @@ -368,7 +368,7 @@ -(void)onRegisterNotification:(NSNotification*)notification NSString* message; - if (dict.count > 0) { + if (remoteArray.count > 0) { message = remoteArray[0][@"message"]; } From ea615b46c0dea674490541aa4490f81af23c0759 Mon Sep 17 00:00:00 2001 From: Morgan Berger Date: Fri, 23 Jan 2026 17:01:46 +0100 Subject: [PATCH 04/11] feat: modifies click event types Uniform everything into `sendClickEvent` method and handles prevented prop. --- src/Paywall/index.tsx | 50 ++++++++++++++++++++++++- src/specs/PaywallViewNativeComponent.ts | 2 + 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/Paywall/index.tsx b/src/Paywall/index.tsx index dd5e298c..b8465701 100644 --- a/src/Paywall/index.tsx +++ b/src/Paywall/index.tsx @@ -11,6 +11,7 @@ import PaywallView, { type NativeProps, type FormEvent, type RegisterEvent, + type ClickEvent, } from '../specs/PaywallViewNativeComponent'; import NativePaywallModule from '../specs/NativePaywallModule'; import type { NativeSyntheticEvent } from 'react-native'; @@ -18,7 +19,8 @@ import type { NativeSyntheticEvent } from 'react-native'; export interface PaywallProps extends Omit< NativeProps, 'appId' | 'config' | 'texts' | 'styles' | 'variables' | - 'onFormSubmit' | 'onRegister' + 'onFormSubmit' | 'onRegister' | + 'onSubscribeClick' | 'onLoginClick' | 'onDiscoveryLinkClick' | 'onDataPolicyClick' > { /** * Optional unique paywall id. When released, the snippet with the same id @@ -32,6 +34,11 @@ export interface PaywallProps extends Omit< onFormSubmit?: DirectEventHandlerWithResult; onRegister?: DirectEventHandlerWithResult; + + onSubscribeClick?: (event: NativeSyntheticEvent, prevent: () => void) => void; + onLoginClick?: (event: NativeSyntheticEvent, prevent: () => void) => void; + onDiscoveryLinkClick?: (event: NativeSyntheticEvent, prevent: () => void) => void; + onDataPolicyClick?: (event: NativeSyntheticEvent, prevent: () => void) => void; } export interface PaywallState { @@ -51,6 +58,10 @@ const Paywall = ({ onRelease, onFormSubmit, onRegister, + onSubscribeClick, + onLoginClick, + onDiscoveryLinkClick, + onDataPolicyClick, ...rest }: PaywallProps) => { const { @@ -72,6 +83,7 @@ const Paywall = ({ ...factoryConfig, ...config, }), [config, factoryConfig]); + const serializedConfig = useMemo(() => ( JSON.stringify({ ...rawConfig, @@ -92,13 +104,14 @@ const Paywall = ({ const innerRef = useRef(null); const sendMessage = ( - e: NativeSyntheticEvent, + e: NativeSyntheticEvent, type: string, data: any, ) => { const message = JSON.stringify({ type, data, + prevented: (e.nativeEvent as any).prevented, _messageId: e.nativeEvent._messageId }); @@ -114,6 +127,27 @@ const Paywall = ({ NativePaywallModule.emit('poool:rn:event.' + type, message); }; + const sendClickEvent = async ( + event: NativeSyntheticEvent, + eventName: string, + onClick: ((e: NativeSyntheticEvent, prevent: () => void) => void) | undefined, + ) => { + event.persist(); + + try { + event.nativeEvent.prevented = false; + await onClick?.(event, () => { + console.log(eventName + ' default behavior prevented'); + event.nativeEvent.prevented = true; + }); + sendMessage(event, eventName + ':resolve', {}); + } catch (error) { + sendMessage(event, eventName + ':reject', { + message: (error as Error).message || error, + }); + } + } + return ( { + await sendClickEvent(e, 'onSubscribeClick', onSubscribeClick); + }} + onLoginClick={ async (e) => { + await sendClickEvent(e, 'onLoginClick', onLoginClick); + }} + onDataPolicyClick={ async (e) => { + await sendClickEvent(e, 'onDataPolicyClick', onDataPolicyClick); + }} + onDiscoveryLinkClick={ async (e) => { + await sendClickEvent(e, 'onDiscoveryLinkClick', onDiscoveryLinkClick); + }} /> ); }; diff --git a/src/specs/PaywallViewNativeComponent.ts b/src/specs/PaywallViewNativeComponent.ts index 246b8573..17decf8c 100644 --- a/src/specs/PaywallViewNativeComponent.ts +++ b/src/specs/PaywallViewNativeComponent.ts @@ -64,6 +64,8 @@ export interface RegisterEvent { export interface ClickEvent extends WidgetEvent { url: string; button: string; + _messageId: Double; + prevented: boolean; } export interface AlternativeClickEvent extends WidgetEvent { From 7fabca2871097092773ba3c5da231b6b3bb41013 Mon Sep 17 00:00:00 2001 From: Morgan Berger Date: Fri, 23 Jan 2026 17:03:42 +0100 Subject: [PATCH 05/11] feat: adds bigger article text --- example/src/App.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 9ab4197f..15967341 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -22,6 +22,14 @@ export default function App() { } }, []); + const text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore \ + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis \ + aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat \ + cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur \ + adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation \ + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore \ + eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; + useEffect(() => { init(); }, [init]); @@ -36,7 +44,7 @@ export default function App() { Poool Access Example - Synopsis + Synopsis : {text} Full content From 9f26cebc70cc90c01cc0738628f3a11b705bba41 Mon Sep 17 00:00:00 2001 From: Morgan Berger Date: Fri, 23 Jan 2026 17:05:07 +0100 Subject: [PATCH 06/11] feat: ios notifications Now dynamically exctract notification name from message. --- ios/NativePaywallModule.mm | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ios/NativePaywallModule.mm b/ios/NativePaywallModule.mm index 62be254f..74d970e8 100644 --- a/ios/NativePaywallModule.mm +++ b/ios/NativePaywallModule.mm @@ -10,12 +10,22 @@ @implementation NativePaywallModule - (void)emit:(NSString *)event data:(NSString *)data { - NSString *notifName = @"registerNotification"; + NSString *extractedEventName = @""; + NSRange eventPrefixRange = [event rangeOfString:@"event."]; + NSRange resolveSuffixRange = [event rangeOfString:@":resolve" options:NSBackwardsSearch]; - if ([event containsString:@"onFormSubmit"]) { - notifName = @"formSubmitNotification"; + if (eventPrefixRange.location != NSNotFound && resolveSuffixRange.location != NSNotFound && + eventPrefixRange.location + eventPrefixRange.length < resolveSuffixRange.location) { + + NSRange nameRange = NSMakeRange(eventPrefixRange.location + eventPrefixRange.length, + resolveSuffixRange.location - (eventPrefixRange.location + eventPrefixRange.length)); + extractedEventName = [event substringWithRange:nameRange]; } + NSString *notifName = [[NSString alloc] initWithFormat:@"%@Notification", extractedEventName]; + + NSLog(@"Firing %@", notifName); + [NSNotificationCenter.defaultCenter postNotificationName:notifName object:self userInfo:@{@"object": data}]; From 230513b5607f47695a3e1efaa1222c2aac4a961b Mon Sep 17 00:00:00 2001 From: Morgan Berger Date: Fri, 23 Jan 2026 17:05:39 +0100 Subject: [PATCH 07/11] feat: adding ios part Handling all click events. --- ios/PaywallView.mm | 77 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/ios/PaywallView.mm b/ios/PaywallView.mm index 5cd851d2..6016fa08 100644 --- a/ios/PaywallView.mm +++ b/ios/PaywallView.mm @@ -30,6 +30,8 @@ @implementation PaywallView { NSMutableDictionary *_formSubmitObservers; NSMutableDictionary *_registerObservers; + NSMutableDictionary *_preventActionObservers; + Access * access; BOOL parented; @@ -204,42 +206,54 @@ - (void)initEvents { self.eventEmitter.onPaywallSeen(rnEvent); }]; - [access onSubscribeTappedWithOnce:false :^(ClickEvent * _Nullable event) { + [access onSubscribeTappedWithOnce:false :^(ClickEvent * _Nullable event, void (^ _Nonnull prevent)()) { + NSNumber *messageId = [self setupObserverForNotif:@"onSubscribeClickNotification" method:prevent]; + PaywallViewEventEmitter::OnSubscribeClick rnEvent = PaywallViewEventEmitter::OnSubscribeClick { [event.widget UTF8String], [event.actionName UTF8String], - [event.button UTF8String], [event.url UTF8String], + [event.button UTF8String], + [messageId doubleValue], }; self.eventEmitter.onSubscribeClick(rnEvent); }]; - - [access onLoginTappedWithOnce:false :^(ClickEvent * _Nullable event) { + + [access onLoginTappedWithOnce:false :^(ClickEvent * _Nullable event, void (^ _Nonnull prevent)()) { + NSNumber *messageId = [self setupObserverForNotif:@"onLoginClickNotification" method:prevent]; + PaywallViewEventEmitter::OnLoginClick rnEvent = PaywallViewEventEmitter::OnLoginClick { [event.widget UTF8String], [event.actionName UTF8String], - [event.button UTF8String], [event.url UTF8String], + [event.button UTF8String], + [messageId doubleValue], }; self.eventEmitter.onLoginClick(rnEvent); }]; - [access onDiscoveryLinkTappedWithOnce:false :^(ClickEvent * _Nullable event) { + [access onDiscoveryLinkTappedWithOnce:false :^(ClickEvent * _Nullable event, void (^ _Nonnull prevent)()) { + NSNumber *messageId = [self setupObserverForNotif:@"onDiscoveryLinkClickNotification" method:prevent]; + PaywallViewEventEmitter::OnDiscoveryLinkClick rnEvent = PaywallViewEventEmitter::OnDiscoveryLinkClick { [event.widget UTF8String], [event.actionName UTF8String], [event.button UTF8String], [event.url UTF8String], + [messageId doubleValue], }; self.eventEmitter.onDiscoveryLinkClick(rnEvent); }]; - [access onDataPolicyTappedWithOnce:false :^(ClickEvent * _Nullable event) { + [access onDataPolicyTappedWithOnce:false :^(ClickEvent * _Nullable event, void (^ _Nonnull prevent)()) { + NSNumber *messageId = [self setupObserverForNotif:@"onDataPolicyClickNotification" method:prevent]; + PaywallViewEventEmitter::OnDataPolicyClick rnEvent = PaywallViewEventEmitter::OnDataPolicyClick { [event.widget UTF8String], [event.actionName UTF8String], [event.button UTF8String], [event.url UTF8String], + [messageId doubleValue], }; self.eventEmitter.onDataPolicyClick(rnEvent); }]; @@ -252,7 +266,8 @@ - (void)initEvents { }; self.eventEmitter.onAlternativeClick(rnEvent); }]; - [access onError:^(ErrorEvent * _Nullable event, void (^ _Nonnull)(void)) { + + [access onErrorWithOnce:false :^(ErrorEvent * _Nullable event, void (^ _Nonnull)(void)) { PaywallViewEventEmitter::OnError rnEvent = PaywallViewEventEmitter::OnError { [event.error UTF8String] }; @@ -274,13 +289,13 @@ - (void)initEvents { self.eventEmitter.onCustomButtonClick(rnEvent); }]; - [access userDidCloseBottomSheet:^{ + [access userDidCloseBottomSheetWithOnce:false :^{ PaywallViewEventEmitter::OnDismissBottomSheet rnEvent = PaywallViewEventEmitter::OnDismissBottomSheet {}; self.eventEmitter.onDismissBottomSheet(rnEvent); }]; - [access onFormSubmitWithSubmitter:^(FormEvent * _Nonnull event, void (^ _Nonnull method)(NSArray * _Nonnull)) { - NSString *notifName = @"formSubmitNotification"; + [access onFormSubmitWithOnce:false submitter:^(FormEvent * _Nonnull event, void (^ _Nonnull method)(NSArray * _Nonnull)) { + NSString *notifName = @"onFormSubmitNotification"; NSNumber *messageId = @(arc4random()); NSString *fieldsStr = [self arrayToString:event.fields.allKeys]; @@ -304,8 +319,10 @@ - (void)initEvents { self.eventEmitter.onFormSubmit(rnEvent); }]; - [access onRegister:^(RegisterEvent * _Nonnull event, void (^ _Nonnull method)(NSString * _Nullable)) { - NSString *notifName = @"registerNotification"; + + [access onRegisterWithOnce:false :^(RegisterEvent * _Nonnull event, void (^ _Nonnull method)(NSString * _Nullable)) { + + NSString *notifName = @"onRegisterNotification"; NSNumber *messageId = @(arc4random()); NSString *newsletterId = event.newsletterId ? event.newsletterId : @""; @@ -329,6 +346,40 @@ - (void)initEvents { }]; } +-(NSNumber*)setupObserverForNotif:(NSString*)notifName method:(void (^ _Nonnull)())prevent +{ + NSNumber *messageId = @(arc4random()); + + id observer = [NSNotificationCenter.defaultCenter addObserverForName:notifName + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification * _Nonnull notification) { + [self onClickNotification:notification messageId:messageId method:prevent]; + }]; + + [self->_preventActionObservers setObject:observer forKey:messageId]; + + return messageId; +} + +-(void)onClickNotification:(NSNotification*)notification + messageId:(NSNumber*)messageId + method:(void (^ _Nonnull)())prevent +{ + NSDictionary *dataDict = [self extractDictFromNotifObject: notification.userInfo[@"object"]]; + + if (dataDict[@"_messageId"] == messageId) { + [NSNotificationCenter.defaultCenter removeObserver:_preventActionObservers[messageId]]; + [_preventActionObservers removeObjectForKey:messageId]; + + BOOL prevented = [(NSNumber*)dataDict[@"prevented"] boolValue]; + + if (prevented) { + prevent(); + } + } +} + -(void)onFormSubmitNotification:(NSNotification*)notification messageId:(NSNumber*)messageId method:(void (^ _Nonnull)(NSArray * _Nonnull))method From e2ccf21d9089f82f9b1720f73b3b57825917a871 Mon Sep 17 00:00:00 2001 From: Morgan Berger Date: Fri, 23 Jan 2026 17:06:15 +0100 Subject: [PATCH 08/11] feat: invoke events from test app --- example/src/App.tsx | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 15967341..ab02bdd5 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -37,8 +37,9 @@ export default function App() { return ( @@ -56,6 +57,27 @@ export default function App() { return [{ fieldKey: 'email', message: 'Invalid email' }]; }} + onRegister={ async (_) => { + await new Promise((resolve) => setTimeout(resolve, 1000)); + + return [{ fieldKey: 'email', message: 'Invalid register!!!!!' }]; + }} + onSubscribeClick={ (e, prevent) => { + console.log('onSubscribeClick', e.nativeEvent); + prevent(); + }} + onLoginClick={ (e, prevent) => { + console.log('onLoginClick', e.nativeEvent); + prevent(); + }} + onDiscoveryLinkClick={ (e, prevent) => { + console.log('onDiscoveryLinkClick', e.nativeEvent); + prevent(); + }} + onDataPolicyClick={ (e, prevent) => { + console.log('onDataPolicyClick', e.nativeEvent); + prevent(); + }} /> From bad1578a9dddbd6203f78382d7155c041985880d Mon Sep 17 00:00:00 2001 From: Morgan Berger Date: Fri, 23 Jan 2026 17:07:04 +0100 Subject: [PATCH 09/11] chore: updating used ruby version --- example/Gemfile.lock | 100 +++++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 22 deletions(-) diff --git a/example/Gemfile.lock b/example/Gemfile.lock index 8b1d2323..8ea1181c 100644 --- a/example/Gemfile.lock +++ b/example/Gemfile.lock @@ -1,23 +1,28 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.7) + CFPropertyList (3.0.8) + activesupport (7.2.3) base64 - nkf - rexml - activesupport (6.1.7.10) - concurrent-ruby (~> 1.0, >= 1.0.2) + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - tzinfo (~> 2.0) - zeitwerk (~> 2.3) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.8) + public_suffix (>= 2.0.2, < 8.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) atomos (0.1.3) - base64 (0.2.0) + base64 (0.3.0) + benchmark (0.5.0) + bigdecimal (4.0.1) claide (1.1.0) cocoapods (1.15.2) addressable (~> 2.8) @@ -58,30 +63,35 @@ GEM cocoapods-try (1.2.0) colored2 (3.1.2) concurrent-ruby (1.3.3) + connection_pool (3.0.2) + drb (2.2.3) escape (0.0.4) - ethon (0.16.0) + ethon (0.15.0) ffi (>= 1.15.0) - ffi (1.17.1) + ffi (1.17.3) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) httpclient (2.9.0) mutex_m - i18n (1.14.7) + i18n (1.14.8) concurrent-ruby (~> 1.0) - json (2.7.6) - minitest (5.25.4) + json (2.18.0) + logger (1.7.0) + minitest (6.0.1) + prism (~> 1.5) molinillo (0.8.0) mutex_m (0.3.0) nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - nkf (0.2.0) + prism (1.8.0) public_suffix (4.0.7) - rexml (3.4.1) + rexml (3.4.4) ruby-macho (2.5.1) - typhoeus (1.4.1) - ethon (>= 0.9.0) + securerandom (0.4.1) + typhoeus (1.5.0) + ethon (>= 0.9.0, < 0.16.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) xcodeproj (1.25.1) @@ -91,7 +101,6 @@ GEM colored2 (~> 3.1) nanaimo (~> 0.3.0) rexml (>= 3.3.6, < 4.0) - zeitwerk (2.6.18) PLATFORMS ruby @@ -102,8 +111,55 @@ DEPENDENCIES concurrent-ruby (< 1.3.4) xcodeproj (< 1.26.0) +CHECKSUMS + CFPropertyList (3.0.8) sha256=2c99d0d980536d3d7ab252f7bd59ac8be50fbdd1ff487c98c949bb66bb114261 + activesupport (7.2.3) sha256=5675c9770dac93e371412684249f9dc3c8cec104efd0624362a520ae685c7b10 + addressable (2.8.8) sha256=7c13b8f9536cf6364c03b9d417c19986019e28f7c00ac8132da4eb0fe393b057 + algoliasearch (1.27.5) sha256=26c1cddf3c2ec4bd60c148389e42702c98fdac862881dc6b07a4c0b89ffec853 + atomos (0.1.3) sha256=7d43b22f2454a36bace5532d30785b06de3711399cb1c6bf932573eda536789f + base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b + benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c + bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7 + claide (1.1.0) sha256=6d3c5c089dde904d96aa30e73306d0d4bd444b1accb9b3125ce14a3c0183f82e + cocoapods (1.15.2) sha256=f0f5153de8d028d133b96f423e04f37fb97a1da0d11dda581a9f46c0cba4090a + cocoapods-core (1.15.2) sha256=322650d97fe1ad4c0831a09669764b888bd91c6d79d0f6bb07281a17667a2136 + cocoapods-deintegrate (1.0.5) sha256=517c2a448ef563afe99b6e7668704c27f5de9e02715a88ee9de6974dc1b3f6a2 + cocoapods-downloader (2.1) sha256=bb6ebe1b3966dc4055de54f7a28b773485ac724fdf575d9bee2212d235e7b6d1 + cocoapods-plugins (1.0.0) sha256=725d17ce90b52f862e73476623fd91441b4430b742d8a071000831efb440ca9a + cocoapods-search (1.0.1) sha256=1b133b0e6719ed439bd840e84a1828cca46425ab73a11eff5e096c3b2df05589 + cocoapods-trunk (1.6.0) sha256=5f5bda8c172afead48fa2d43a718cf534b1313c367ba1194cebdeb9bfee9ed31 + cocoapods-try (1.2.0) sha256=145b946c6e7747ed0301d975165157951153d27469e6b2763c83e25c84b9defe + colored2 (3.1.2) sha256=b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a + concurrent-ruby (1.3.3) sha256=4f9cd28965c4dcf83ffd3ea7304f9323277be8525819cb18a3b61edcb56a7c6a + connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a + drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373 + escape (0.0.4) sha256=e49f44ae2b4f47c6a3abd544ae77fe4157802794e32f19b8e773cbc4dcec4169 + ethon (0.15.0) sha256=0809805a035bc10f54162ca99f15ded49e428e0488bcfe1c08c821e18261a74d + ffi (1.17.3) sha256=0e9f39f7bb3934f77ad6feab49662be77e87eedcdeb2a3f5c0234c2938563d4c + fourflusher (2.3.1) sha256=1b3de61c7c791b6a4e64f31e3719eb25203d151746bb519a0292bff1065ccaa9 + fuzzy_match (2.0.4) sha256=b5de4f95816589c5b5c3ad13770c0af539b75131c158135b3f3bbba75d0cfca5 + gh_inspector (1.1.3) sha256=04cca7171b87164e053aa43147971d3b7f500fcb58177698886b48a9fc4a1939 + httpclient (2.9.0) sha256=4b645958e494b2f86c2f8a2f304c959baa273a310e77a2931ddb986d83e498c8 + i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5 + json (2.18.0) sha256=b10506aee4183f5cf49e0efc48073d7b75843ce3782c68dbeb763351c08fd505 + logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 + minitest (6.0.1) sha256=7854c74f48e2e975969062833adc4013f249a4b212f5e7b9d5c040bf838d54bb + molinillo (0.8.0) sha256=efbff2716324e2a30bccd3eba1ff3a735f4d5d53ffddbc6a2f32c0ca9433045d + mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751 + nanaimo (0.3.0) sha256=aaaedc60497070b864a7e220f7c4b4cad3a0daddda2c30055ba8dae306342376 + nap (1.1.0) sha256=949691660f9d041d75be611bb2a8d2fd559c467537deac241f4097d9b5eea576 + netrc (0.11.0) sha256=de1ce33da8c99ab1d97871726cba75151113f117146becbe45aa85cb3dabee3f + prism (1.8.0) sha256=84453a16ef5530ea62c5f03ec16b52a459575ad4e7b9c2b360fd8ce2c39c1254 + public_suffix (4.0.7) sha256=8be161e2421f8d45b0098c042c06486789731ea93dc3a896d30554ee38b573b8 + rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 + ruby-macho (2.5.1) sha256=9075e52e0f9270b552a90b24fcc6219ad149b0d15eae1bc364ecd0ac8984f5c9 + securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 + typhoeus (1.5.0) sha256=120b67ed1ef515e6c0e938176db880f15b0916f038e78ce2a66290f3f1de3e3b + tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b + xcodeproj (1.25.1) sha256=9a2310dccf6d717076e86f602b17c640046b6f1dfe64480044596f6f2f13dc84 + RUBY VERSION - ruby 2.6.10p210 + ruby 4.0.1 BUNDLED WITH - 1.17.2 + 4.0.3 From 29d50839d8c6f4d064fa34a003b8c691b06d44b5 Mon Sep 17 00:00:00 2001 From: Morgan Berger Date: Mon, 26 Jan 2026 10:37:51 +0100 Subject: [PATCH 10/11] chore: removing debug --- ios/NativePaywallModule.mm | 4 +--- src/Paywall/index.tsx | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/ios/NativePaywallModule.mm b/ios/NativePaywallModule.mm index 74d970e8..0a8fb2f6 100644 --- a/ios/NativePaywallModule.mm +++ b/ios/NativePaywallModule.mm @@ -23,9 +23,7 @@ - (void)emit:(NSString *)event } NSString *notifName = [[NSString alloc] initWithFormat:@"%@Notification", extractedEventName]; - - NSLog(@"Firing %@", notifName); - + [NSNotificationCenter.defaultCenter postNotificationName:notifName object:self userInfo:@{@"object": data}]; diff --git a/src/Paywall/index.tsx b/src/Paywall/index.tsx index b8465701..b0634ed1 100644 --- a/src/Paywall/index.tsx +++ b/src/Paywall/index.tsx @@ -137,7 +137,6 @@ const Paywall = ({ try { event.nativeEvent.prevented = false; await onClick?.(event, () => { - console.log(eventName + ' default behavior prevented'); event.nativeEvent.prevented = true; }); sendMessage(event, eventName + ':resolve', {}); From 1fd973ed922e4d9b51fb9b9bfaa20642bfa752aa Mon Sep 17 00:00:00 2001 From: Morgan Berger Date: Mon, 26 Jan 2026 10:48:42 +0100 Subject: [PATCH 11/11] fix: applying copilot suggestions --- ios/PaywallView.mm | 3 ++- src/Paywall/index.tsx | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ios/PaywallView.mm b/ios/PaywallView.mm index 6016fa08..6a90fcbe 100644 --- a/ios/PaywallView.mm +++ b/ios/PaywallView.mm @@ -96,6 +96,7 @@ - (instancetype)initWithFrame:(CGRect)frame _formSubmitObservers = [[NSMutableDictionary alloc] init]; _registerObservers = [[NSMutableDictionary alloc] init]; + _preventActionObservers = [[NSMutableDictionary alloc] init]; } return self; } @@ -417,7 +418,7 @@ -(void)onRegisterNotification:(NSNotification*)notification if (dict[@"_messageId"] == messageId) { NSArray *remoteArray = dict[@"data"]; - NSString* message; + NSString* message = nil; if (remoteArray.count > 0) { message = remoteArray[0][@"message"]; diff --git a/src/Paywall/index.tsx b/src/Paywall/index.tsx index b0634ed1..b2f49c53 100644 --- a/src/Paywall/index.tsx +++ b/src/Paywall/index.tsx @@ -35,10 +35,10 @@ export interface PaywallProps extends Omit< onFormSubmit?: DirectEventHandlerWithResult; onRegister?: DirectEventHandlerWithResult; - onSubscribeClick?: (event: NativeSyntheticEvent, prevent: () => void) => void; - onLoginClick?: (event: NativeSyntheticEvent, prevent: () => void) => void; - onDiscoveryLinkClick?: (event: NativeSyntheticEvent, prevent: () => void) => void; - onDataPolicyClick?: (event: NativeSyntheticEvent, prevent: () => void) => void; + onSubscribeClick?: (event: NativeSyntheticEvent, prevent: () => void) => void | Promise; + onLoginClick?: (event: NativeSyntheticEvent, prevent: () => void) => void | Promise; + onDiscoveryLinkClick?: (event: NativeSyntheticEvent, prevent: () => void) => void | Promise; + onDataPolicyClick?: (event: NativeSyntheticEvent, prevent: () => void) => void | Promise; } export interface PaywallState { @@ -145,7 +145,7 @@ const Paywall = ({ message: (error as Error).message || error, }); } - } + }; return (