react-native-klarna-inapp-sdk 2.4.10 → 2.5.0

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.
Files changed (121) hide show
  1. package/README.md +2 -1
  2. package/android/build.gradle +3 -1
  3. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/KlarnaMobileSDKPackage.java +5 -1
  4. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/checkout/KlarnaCheckoutViewManager.java +4 -10
  5. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/WebViewResizeObserver.java +28 -0
  6. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/event/ComponentEventSender.java +5 -1
  7. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/ui/ResizeObserverWrapperView.java +2 -2
  8. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/ui/WrapperView.java +2 -2
  9. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/ui/YogaLayoutHelper.java +39 -0
  10. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/expresscheckout/KlarnaExpressCheckoutViewCallback.kt +30 -0
  11. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/expresscheckout/KlarnaExpressCheckoutViewEventSender.kt +80 -0
  12. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/expresscheckout/KlarnaExpressCheckoutViewManager.java +544 -0
  13. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/osm/KlarnaOSMViewEventHandler.java +30 -0
  14. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/osm/KlarnaOSMViewEventSender.java +64 -0
  15. package/android/src/main/java/com/klarna/mobile/sdk/reactnative/osm/KlarnaOSMViewManager.java +356 -0
  16. package/android/src/newarch/java/com/klarna/mobile/sdk/reactnative/spec/RNKlarnaExpressCheckoutViewSpec.java +31 -0
  17. package/android/src/newarch/java/com/klarna/mobile/sdk/reactnative/spec/RNKlarnaOSMViewSpec.java +31 -0
  18. package/android/src/oldarch/java/com/klarna/mobile/sdk/reactnative/spec/RNKlarnaExpressCheckoutViewSpec.java +36 -0
  19. package/android/src/oldarch/java/com/klarna/mobile/sdk/reactnative/spec/RNKlarnaOSMViewSpec.java +32 -0
  20. package/android/src/test/java/com/klarna/mobile/sdk/reactnative/KlarnaMobileSDKPackageTest.java +5 -1
  21. package/android/src/test/java/com/klarna/mobile/sdk/reactnative/common/ui/YogaLayoutHelperTest.java +79 -0
  22. package/android/src/test/java/com/klarna/mobile/sdk/reactnative/expresscheckout/KlarnaExpressCheckoutViewManagerTest.java +259 -0
  23. package/android/src/test/java/com/klarna/mobile/sdk/reactnative/osm/KlarnaOSMViewEventHandlerTest.java +136 -0
  24. package/android/src/test/java/com/klarna/mobile/sdk/reactnative/osm/KlarnaOSMViewEventSenderTest.java +184 -0
  25. package/android/src/test/java/com/klarna/mobile/sdk/reactnative/osm/KlarnaOSMViewManagerTest.java +385 -0
  26. package/ios/RNKlarnaMobileSDK.xcodeproj/project.pbxproj +55 -1
  27. package/ios/Sources/KlarnaExpressCheckoutViewManager.h +5 -0
  28. package/ios/Sources/KlarnaExpressCheckoutViewManager.mm +39 -0
  29. package/ios/Sources/KlarnaOSMViewManager.h +5 -0
  30. package/ios/Sources/KlarnaOSMViewManager.mm +59 -0
  31. package/ios/Sources/view/KlarnaExpressCheckoutButtonDelegateProxy.swift +145 -0
  32. package/ios/Sources/view/KlarnaExpressCheckoutViewWrapper.h +41 -0
  33. package/ios/Sources/view/KlarnaOSMViewWrapper.h +48 -0
  34. package/ios/Sources/view/newarch/KlarnaExpressCheckoutViewWrapper.mm +228 -0
  35. package/ios/Sources/view/newarch/KlarnaOSMViewWrapper.mm +294 -0
  36. package/ios/Sources/view/newarch/KlarnaStandaloneWebViewWrapper.mm +20 -13
  37. package/ios/Sources/view/oldarch/KlarnaExpressCheckoutViewWrapper.mm +222 -0
  38. package/ios/Sources/view/oldarch/KlarnaOSMViewWrapper.mm +273 -0
  39. package/ios/Sources/view/oldarch/KlarnaStandaloneWebViewWrapper.mm +11 -13
  40. package/ios/Tests/KlarnaExpressCheckoutViewWrapperTests.m +391 -0
  41. package/ios/Tests/KlarnaOSMViewWrapperTests.m +450 -0
  42. package/ios/Tests/KlarnaStandaloneWebViewWrapperTests.m +105 -0
  43. package/lib/commonjs/KlarnaExpressCheckoutView.js +75 -0
  44. package/lib/commonjs/KlarnaExpressCheckoutView.js.map +1 -0
  45. package/lib/commonjs/KlarnaOSMView.js +94 -0
  46. package/lib/commonjs/KlarnaOSMView.js.map +1 -0
  47. package/lib/commonjs/index.js +99 -0
  48. package/lib/commonjs/index.js.map +1 -1
  49. package/lib/commonjs/specs/KlarnaExpressCheckoutViewNativeComponent.js +11 -0
  50. package/lib/commonjs/specs/KlarnaExpressCheckoutViewNativeComponent.js.map +1 -0
  51. package/lib/commonjs/specs/KlarnaOSMViewNativeComponent.js +16 -0
  52. package/lib/commonjs/specs/KlarnaOSMViewNativeComponent.js.map +1 -0
  53. package/lib/commonjs/types/button/KlarnaButtonShape.js +14 -0
  54. package/lib/commonjs/types/button/KlarnaButtonShape.js.map +1 -0
  55. package/lib/commonjs/types/button/KlarnaButtonStyle.js +13 -0
  56. package/lib/commonjs/types/button/KlarnaButtonStyle.js.map +1 -0
  57. package/lib/commonjs/types/button/KlarnaButtonTheme.js +14 -0
  58. package/lib/commonjs/types/button/KlarnaButtonTheme.js.map +1 -0
  59. package/lib/commonjs/types/common/KlarnaExpressCheckoutSessionOptions.js +9 -0
  60. package/lib/commonjs/types/common/KlarnaExpressCheckoutSessionOptions.js.map +1 -0
  61. package/lib/commonjs/types/common/KlarnaOSMPlacementKey.js +2 -0
  62. package/lib/commonjs/types/common/KlarnaOSMPlacementKey.js.map +1 -0
  63. package/lib/commonjs/types/common/KlarnaTheme.js +14 -0
  64. package/lib/commonjs/types/common/KlarnaTheme.js.map +1 -0
  65. package/lib/module/KlarnaExpressCheckoutView.js +64 -0
  66. package/lib/module/KlarnaExpressCheckoutView.js.map +1 -0
  67. package/lib/module/KlarnaOSMView.js +84 -0
  68. package/lib/module/KlarnaOSMView.js.map +1 -0
  69. package/lib/module/index.js +9 -0
  70. package/lib/module/index.js.map +1 -1
  71. package/lib/module/specs/KlarnaExpressCheckoutViewNativeComponent.js +3 -0
  72. package/lib/module/specs/KlarnaExpressCheckoutViewNativeComponent.js.map +1 -0
  73. package/lib/module/specs/KlarnaOSMViewNativeComponent.js +7 -0
  74. package/lib/module/specs/KlarnaOSMViewNativeComponent.js.map +1 -0
  75. package/lib/module/types/button/KlarnaButtonShape.js +7 -0
  76. package/lib/module/types/button/KlarnaButtonShape.js.map +1 -0
  77. package/lib/module/types/button/KlarnaButtonStyle.js +6 -0
  78. package/lib/module/types/button/KlarnaButtonStyle.js.map +1 -0
  79. package/lib/module/types/button/KlarnaButtonTheme.js +7 -0
  80. package/lib/module/types/button/KlarnaButtonTheme.js.map +1 -0
  81. package/lib/module/types/common/KlarnaExpressCheckoutSessionOptions.js +2 -0
  82. package/lib/module/types/common/KlarnaExpressCheckoutSessionOptions.js.map +1 -0
  83. package/lib/module/types/common/KlarnaOSMPlacementKey.js +2 -0
  84. package/lib/module/types/common/KlarnaOSMPlacementKey.js.map +1 -0
  85. package/lib/module/types/common/KlarnaTheme.js +7 -0
  86. package/lib/module/types/common/KlarnaTheme.js.map +1 -0
  87. package/lib/typescript/KlarnaExpressCheckoutView.d.ts +47 -0
  88. package/lib/typescript/KlarnaExpressCheckoutView.d.ts.map +1 -0
  89. package/lib/typescript/KlarnaOSMView.d.ts +36 -0
  90. package/lib/typescript/KlarnaOSMView.d.ts.map +1 -0
  91. package/lib/typescript/index.d.ts +9 -0
  92. package/lib/typescript/index.d.ts.map +1 -1
  93. package/lib/typescript/specs/KlarnaExpressCheckoutViewNativeComponent.d.ts +45 -0
  94. package/lib/typescript/specs/KlarnaExpressCheckoutViewNativeComponent.d.ts.map +1 -0
  95. package/lib/typescript/specs/KlarnaOSMViewNativeComponent.d.ts +35 -0
  96. package/lib/typescript/specs/KlarnaOSMViewNativeComponent.d.ts.map +1 -0
  97. package/lib/typescript/types/button/KlarnaButtonShape.d.ts +6 -0
  98. package/lib/typescript/types/button/KlarnaButtonShape.d.ts.map +1 -0
  99. package/lib/typescript/types/button/KlarnaButtonStyle.d.ts +5 -0
  100. package/lib/typescript/types/button/KlarnaButtonStyle.d.ts.map +1 -0
  101. package/lib/typescript/types/button/KlarnaButtonTheme.d.ts +6 -0
  102. package/lib/typescript/types/button/KlarnaButtonTheme.d.ts.map +1 -0
  103. package/lib/typescript/types/common/KlarnaExpressCheckoutSessionOptions.d.ts +10 -0
  104. package/lib/typescript/types/common/KlarnaExpressCheckoutSessionOptions.d.ts.map +1 -0
  105. package/lib/typescript/types/common/KlarnaOSMPlacementKey.d.ts +2 -0
  106. package/lib/typescript/types/common/KlarnaOSMPlacementKey.d.ts.map +1 -0
  107. package/lib/typescript/types/common/KlarnaTheme.d.ts +6 -0
  108. package/lib/typescript/types/common/KlarnaTheme.d.ts.map +1 -0
  109. package/package.json +5 -3
  110. package/react-native-klarna-inapp-sdk.podspec +2 -1
  111. package/src/KlarnaExpressCheckoutView.tsx +169 -0
  112. package/src/KlarnaOSMView.tsx +157 -0
  113. package/src/index.tsx +9 -0
  114. package/src/specs/KlarnaExpressCheckoutViewNativeComponent.ts +66 -0
  115. package/src/specs/KlarnaOSMViewNativeComponent.ts +50 -0
  116. package/src/types/button/KlarnaButtonShape.ts +5 -0
  117. package/src/types/button/KlarnaButtonStyle.ts +4 -0
  118. package/src/types/button/KlarnaButtonTheme.ts +5 -0
  119. package/src/types/common/KlarnaExpressCheckoutSessionOptions.ts +13 -0
  120. package/src/types/common/KlarnaOSMPlacementKey.ts +3 -0
  121. package/src/types/common/KlarnaTheme.ts +5 -0
@@ -0,0 +1,294 @@
1
+ #if RCT_NEW_ARCH_ENABLED
2
+
3
+ #import <AVFoundation/AVFoundation.h>
4
+ #import <react/renderer/components/RNKlarnaMobileSDK/ComponentDescriptors.h>
5
+ #import <react/renderer/components/RNKlarnaMobileSDK/EventEmitters.h>
6
+ #import <react/renderer/components/RNKlarnaMobileSDK/Props.h>
7
+ #import <react/renderer/components/RNKlarnaMobileSDK/RCTComponentViewHelpers.h>
8
+ #import "../KlarnaOSMViewWrapper.h"
9
+ #import "../../common/RNMobileSDKUtils.h"
10
+ #import "RCTFabricComponentsPlugins.h"
11
+ #import <KlarnaMobileSDK/KlarnaMobileSDK.h>
12
+ #import <KlarnaMobileSDK/KlarnaMobileSDK-Swift.h>
13
+
14
+ using namespace facebook::react;
15
+
16
+ @interface KlarnaOSMViewWrapper () <KlarnaEventHandler, KlarnaSizingDelegate, RCTRNKlarnaOSMViewViewProtocol>
17
+
18
+ @property (nonatomic, strong) KlarnaOSMView* klarnaOSMView;
19
+ @property (nonatomic, assign) BOOL isOSMViewReadyEventSent;
20
+
21
+ @end
22
+
23
+ @implementation KlarnaOSMViewWrapper
24
+
25
+ #pragma mark - Initialization
26
+
27
+ - (instancetype)initWithFrame:(CGRect)frame
28
+ {
29
+ self = [super initWithFrame:frame];
30
+ if (self) {
31
+ self.isOSMViewReadyEventSent = NO;
32
+ self.backgroundColor = [UIColor clearColor];
33
+ static const auto defaultProps = std::make_shared<const RNKlarnaOSMViewProps>();
34
+ _props = defaultProps;
35
+ [self initializeKlarnaOSMView];
36
+ }
37
+
38
+ return self;
39
+ }
40
+
41
+ + (ComponentDescriptorProvider)componentDescriptorProvider
42
+ {
43
+ return concreteComponentDescriptorProvider<RNKlarnaOSMViewComponentDescriptor>();
44
+ }
45
+
46
+ Class<RCTComponentViewProtocol> RNKlarnaOSMViewCls(void)
47
+ {
48
+ return KlarnaOSMViewWrapper.class;
49
+ }
50
+
51
+ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
52
+ RCTRNKlarnaOSMViewHandleCommand(self, commandName, args);
53
+ }
54
+
55
+ #pragma mark - KlarnaOSMView Props Update
56
+
57
+ - (void)updateProps:(const facebook::react::Props::Shared &)props oldProps:(const facebook::react::Props::Shared &)oldProps {
58
+ const auto &oldViewProps = *std::static_pointer_cast<RNKlarnaOSMViewProps const>(_props);
59
+ const auto &newViewProps = *std::static_pointer_cast<RNKlarnaOSMViewProps const>(props);
60
+
61
+ if (oldViewProps.clientId != newViewProps.clientId) {
62
+ NSString *value = [[NSString alloc] initWithUTF8String:newViewProps.clientId.c_str()];
63
+ self.klarnaOSMView.clientId = value;
64
+ }
65
+
66
+ if (oldViewProps.placementKey != newViewProps.placementKey) {
67
+ NSString *value = [[NSString alloc] initWithUTF8String:newViewProps.placementKey.c_str()];
68
+ self.klarnaOSMView.placementKey = value;
69
+ }
70
+
71
+ if (oldViewProps.locale != newViewProps.locale) {
72
+ NSString *value = [[NSString alloc] initWithUTF8String:newViewProps.locale.c_str()];
73
+ if (value.length > 0) {
74
+ self.klarnaOSMView.locale = value;
75
+ }
76
+ }
77
+
78
+ if (oldViewProps.purchaseAmount != newViewProps.purchaseAmount) {
79
+ NSString *value = [[NSString alloc] initWithUTF8String:newViewProps.purchaseAmount.c_str()];
80
+ if (value.length > 0) {
81
+ NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
82
+ formatter.numberStyle = NSNumberFormatterDecimalStyle;
83
+ NSNumber *amount = [formatter numberFromString:value];
84
+ if (amount != nil) {
85
+ [self.klarnaOSMView setPurchaseAmount:amount];
86
+ }
87
+ }
88
+ }
89
+
90
+ if (oldViewProps.environment != newViewProps.environment) {
91
+ NSString *value = [[NSString alloc] initWithUTF8String:newViewProps.environment.c_str()];
92
+ if (value.length > 0) {
93
+ if ([value isEqualToString:@"playground"]) {
94
+ self.klarnaOSMView.environment = KlarnaEnvironment.playground;
95
+ } else if ([value isEqualToString:@"production"]) {
96
+ self.klarnaOSMView.environment = KlarnaEnvironment.production;
97
+ } else if ([value isEqualToString:@"staging"]) {
98
+ self.klarnaOSMView.environment = KlarnaEnvironment.staging;
99
+ }
100
+ }
101
+ }
102
+
103
+ if (oldViewProps.region != newViewProps.region) {
104
+ NSString *value = [[NSString alloc] initWithUTF8String:newViewProps.region.c_str()];
105
+ if (value.length > 0) {
106
+ if ([value isEqualToString:@"eu"]) {
107
+ self.klarnaOSMView.region = KlarnaRegion.eu;
108
+ } else if ([value isEqualToString:@"na"]) {
109
+ self.klarnaOSMView.region = KlarnaRegion.na;
110
+ } else if ([value isEqualToString:@"oc"]) {
111
+ self.klarnaOSMView.region = KlarnaRegion.oc;
112
+ }
113
+ }
114
+ }
115
+
116
+ if (oldViewProps.theme != newViewProps.theme) {
117
+ NSString *value = [[NSString alloc] initWithUTF8String:newViewProps.theme.c_str()];
118
+ if (value.length > 0) {
119
+ if ([value isEqualToString:@"light"]) {
120
+ self.klarnaOSMView.theme = KlarnaThemeLight;
121
+ } else if ([value isEqualToString:@"dark"]) {
122
+ self.klarnaOSMView.theme = KlarnaThemeDark;
123
+ } else if ([value isEqualToString:@"automatic"]) {
124
+ self.klarnaOSMView.theme = KlarnaThemeAutomatic;
125
+ }
126
+ }
127
+ }
128
+
129
+ BOOL styleChanged = NO;
130
+ if (oldViewProps.backgroundColor != newViewProps.backgroundColor) {
131
+ styleChanged = YES;
132
+ }
133
+ if (oldViewProps.textColor != newViewProps.textColor) {
134
+ styleChanged = YES;
135
+ }
136
+ if (styleChanged) {
137
+ NSString *bgColorStr = [[NSString alloc] initWithUTF8String:newViewProps.backgroundColor.c_str()];
138
+ NSString *txtColorStr = [[NSString alloc] initWithUTF8String:newViewProps.textColor.c_str()];
139
+ [self updateStyleConfigurationWithBackgroundColor:bgColorStr textColor:txtColorStr];
140
+ }
141
+
142
+ [super updateProps:props oldProps:oldProps];
143
+ }
144
+
145
+ - (void)updateEventEmitter:(const facebook::react::EventEmitter::Shared &)eventEmitter
146
+ {
147
+ [super updateEventEmitter:eventEmitter];
148
+
149
+ if (!self.isOSMViewReadyEventSent && self.klarnaOSMView != nil && _eventEmitter) {
150
+ auto emitter = std::dynamic_pointer_cast<const RNKlarnaOSMViewEventEmitter>(_eventEmitter);
151
+ if (emitter) {
152
+ emitter->onOSMViewReady({});
153
+ self.isOSMViewReadyEventSent = YES;
154
+ }
155
+ }
156
+ }
157
+
158
+ #pragma mark - KlarnaOSMView Setup
159
+
160
+ - (void)initializeKlarnaOSMView {
161
+ self.isOSMViewReadyEventSent = NO;
162
+ self.klarnaOSMView = [[KlarnaOSMView alloc] init];
163
+ self.klarnaOSMView.eventHandler = self;
164
+ self.klarnaOSMView.sizingDelegate = self;
165
+ self.klarnaOSMView.backgroundColor = [UIColor clearColor];
166
+
167
+ self.klarnaOSMView.translatesAutoresizingMaskIntoConstraints = NO;
168
+
169
+ [self addSubview:self.klarnaOSMView];
170
+
171
+ [NSLayoutConstraint activateConstraints:[[NSArray alloc] initWithObjects:
172
+ [self.klarnaOSMView.topAnchor constraintEqualToAnchor:self.topAnchor],
173
+ [self.klarnaOSMView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
174
+ [self.klarnaOSMView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
175
+ [self.klarnaOSMView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor], nil
176
+ ]];
177
+ }
178
+
179
+ #pragma mark - Style Configuration
180
+
181
+ - (void)updateStyleConfigurationWithBackgroundColor:(NSString *)bgColorStr textColor:(NSString *)txtColorStr {
182
+ UIColor *bgColor = [self colorFromHexString:bgColorStr];
183
+ UIColor *txtColor = [self colorFromHexString:txtColorStr];
184
+
185
+ if (bgColor || txtColor) {
186
+ KlarnaOSMStyleBuilder *builder = [[KlarnaOSMStyleBuilder alloc] init];
187
+ if (bgColor) {
188
+ [builder setBackgroundColor:bgColor];
189
+ }
190
+ if (txtColor) {
191
+ KlarnaTextStyleBuilder *textStyleBuilder = [[KlarnaTextStyleBuilder alloc] init];
192
+ [textStyleBuilder setTextColor:txtColor];
193
+ [builder setTextStyleConfiguration:[textStyleBuilder build]];
194
+ }
195
+ self.klarnaOSMView.styleConfiguration = [builder build];
196
+ } else {
197
+ self.klarnaOSMView.styleConfiguration = nil;
198
+ }
199
+ }
200
+
201
+ - (UIColor *)colorFromHexString:(NSString *)hexString {
202
+ if (!hexString || hexString.length == 0) {
203
+ return nil;
204
+ }
205
+
206
+ NSString *cleanString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""];
207
+ if (cleanString.length != 6 && cleanString.length != 8) {
208
+ return nil;
209
+ }
210
+
211
+ unsigned int rgbValue = 0;
212
+ NSScanner *scanner = [NSScanner scannerWithString:cleanString];
213
+ [scanner scanHexInt:&rgbValue];
214
+
215
+ if (cleanString.length == 8) {
216
+ return [UIColor colorWithRed:((rgbValue & 0xFF000000) >> 24) / 255.0
217
+ green:((rgbValue & 0x00FF0000) >> 16) / 255.0
218
+ blue:((rgbValue & 0x0000FF00) >> 8) / 255.0
219
+ alpha:(rgbValue & 0x000000FF) / 255.0];
220
+ }
221
+
222
+ return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16) / 255.0
223
+ green:((rgbValue & 0x00FF00) >> 8) / 255.0
224
+ blue:(rgbValue & 0x0000FF) / 255.0
225
+ alpha:1.0];
226
+ }
227
+
228
+ #pragma mark - Methods Exposed to React Native
229
+
230
+ - (void)render {
231
+ UIViewController *hostVC = [self reactViewController];
232
+ if (hostVC) {
233
+ self.klarnaOSMView.hostViewController = hostVC;
234
+ }
235
+ [self.klarnaOSMView render];
236
+ }
237
+
238
+ - (UIViewController *)reactViewController {
239
+ UIResponder *responder = self;
240
+ while (responder) {
241
+ if ([responder isKindOfClass:[UIViewController class]]) {
242
+ return (UIViewController *)responder;
243
+ }
244
+ responder = [responder nextResponder];
245
+ }
246
+ return nil;
247
+ }
248
+
249
+ #pragma mark - UIView Lifecycle
250
+
251
+ - (void)didMoveToWindow {
252
+ [super didMoveToWindow];
253
+ if (!self.window) {
254
+ self.isOSMViewReadyEventSent = NO;
255
+ }
256
+ }
257
+
258
+ #pragma mark - KlarnaEventHandler
259
+
260
+ - (void)klarnaComponent:(id<KlarnaComponent>)klarnaComponent dispatchedEvent:(KlarnaProductEvent *)event {
261
+ // OSM view does not emit product events
262
+ }
263
+
264
+ - (void)klarnaComponent:(id<KlarnaComponent>)klarnaComponent encounteredError:(KlarnaError *)error {
265
+ if (_eventEmitter) {
266
+ auto emitter = std::dynamic_pointer_cast<const RNKlarnaOSMViewEventEmitter>(_eventEmitter);
267
+ if (emitter) {
268
+ emitter->onError(RNKlarnaOSMViewEventEmitter::OnError{
269
+ .error = {
270
+ .name = std::string([error.name UTF8String]),
271
+ .message = std::string([error.message UTF8String]),
272
+ .isFatal = error.isFatal,
273
+ }
274
+ });
275
+ }
276
+ }
277
+ }
278
+
279
+ #pragma mark - KlarnaSizingDelegate
280
+
281
+ - (void)klarnaComponent:(id<KlarnaComponent>)klarnaComponent resizedToHeight:(CGFloat)height {
282
+ if (_eventEmitter) {
283
+ auto emitter = std::dynamic_pointer_cast<const RNKlarnaOSMViewEventEmitter>(_eventEmitter);
284
+ if (emitter) {
285
+ emitter->onResized(RNKlarnaOSMViewEventEmitter::OnResized{
286
+ .height = std::string([[[NSNumber numberWithFloat:height] stringValue] UTF8String]),
287
+ });
288
+ }
289
+ }
290
+ }
291
+
292
+ @end
293
+
294
+ #endif
@@ -19,6 +19,7 @@ using namespace facebook::react;
19
19
  @interface KlarnaStandaloneWebViewWrapper () <KlarnaStandaloneWebViewDelegate, KlarnaEventHandler, RCTRNKlarnaStandaloneWebViewViewProtocol>
20
20
 
21
21
  @property (nonatomic, strong) KlarnaStandaloneWebView *klarnaStandaloneWebView;
22
+ @property (nonatomic, strong) NSString *returnUrl;
22
23
 
23
24
  typedef void (^WebViewOperation)(WKWebView *);
24
25
 
@@ -31,11 +32,6 @@ NSString *const PROPERTY_NAME_ESTIMATED_PROGRESS = @"estimatedProgress";
31
32
 
32
33
  - (id)init {
33
34
  self = [super init];
34
- // TODO: What should we pass here for 'returnUrl'?
35
- [self initializeKlarnaStandaloneWebView: @"returnUrl://"];
36
- self.klarnaStandaloneWebView.delegate = self;
37
- self.klarnaStandaloneWebView.eventHandler = self;
38
- [self.klarnaStandaloneWebView addObserver:self forKeyPath:PROPERTY_NAME_ESTIMATED_PROGRESS options:NSKeyValueObservingOptionNew context:nil];
39
35
  return self;
40
36
  }
41
37
 
@@ -82,13 +78,17 @@ NSString *const PROPERTY_NAME_ESTIMATED_PROGRESS = @"estimatedProgress";
82
78
 
83
79
  #pragma mark - React Native Overrides
84
80
 
85
- - (void)initializeKlarnaStandaloneWebView:(nonnull NSString*)returnUrl {
86
- self.klarnaStandaloneWebView = [[KlarnaStandaloneWebView alloc] initWithReturnURL:[NSURL URLWithString:returnUrl]];
81
+ - (void)initializeKlarnaStandaloneWebView {
82
+ self.klarnaStandaloneWebView = [[KlarnaStandaloneWebView alloc] initWithReturnURL:[NSURL URLWithString:self.returnUrl]];
83
+ self.klarnaStandaloneWebView.delegate = self;
84
+ self.klarnaStandaloneWebView.eventHandler = self;
85
+ [self.klarnaStandaloneWebView addObserver:self forKeyPath:PROPERTY_NAME_ESTIMATED_PROGRESS options:NSKeyValueObservingOptionNew context:nil];
86
+
87
87
  self.klarnaStandaloneWebView.translatesAutoresizingMaskIntoConstraints = NO;
88
-
88
+
89
89
  [self addSubview:self.klarnaStandaloneWebView];
90
90
  [self setWebViewBackgroundToTransparent];
91
-
91
+
92
92
  [NSLayoutConstraint activateConstraints:[[NSArray alloc] initWithObjects:
93
93
  [self.klarnaStandaloneWebView.topAnchor constraintEqualToAnchor:self.topAnchor],
94
94
  [self.klarnaStandaloneWebView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
@@ -141,17 +141,24 @@ Class<RCTComponentViewProtocol>RNKlarnaStandaloneWebViewCls(void)
141
141
  {
142
142
  const auto &oldViewProps = *std::static_pointer_cast<RNKlarnaStandaloneWebViewProps const>(_props);
143
143
  const auto &newViewProps = *std::static_pointer_cast<RNKlarnaStandaloneWebViewProps const>(props);
144
-
144
+
145
145
  if (oldViewProps.returnUrl != newViewProps.returnUrl) {
146
146
  NSString *newReturnUrl = [[NSString alloc] initWithUTF8String: newViewProps.returnUrl.c_str()];
147
- self.klarnaStandaloneWebView.returnURL = [NSURL URLWithString:newReturnUrl];
147
+ self.returnUrl = newReturnUrl;
148
+ if (newReturnUrl.length > 0) {
149
+ if (self.klarnaStandaloneWebView == nil) {
150
+ [self initializeKlarnaStandaloneWebView];
151
+ } else {
152
+ self.klarnaStandaloneWebView.returnURL = [NSURL URLWithString:newReturnUrl];
153
+ }
154
+ }
148
155
  }
149
-
156
+
150
157
  WebViewOperation setBounces = ^(WKWebView *webView) {
151
158
  webView.scrollView.bounces = newViewProps.bounces;
152
159
  };
153
160
  [self applyOperationToWebViews:setBounces];
154
-
161
+
155
162
  [super updateProps:props oldProps:oldProps];
156
163
  }
157
164
 
@@ -0,0 +1,222 @@
1
+ #if !RCT_NEW_ARCH_ENABLED
2
+
3
+ #import <React/RCTLog.h>
4
+ #import "../KlarnaExpressCheckoutViewWrapper.h"
5
+ #import <KlarnaMobileSDK/KlarnaMobileSDK.h>
6
+ #import <KlarnaMobileSDK/KlarnaMobileSDK-Swift.h>
7
+
8
+ #if __has_include("react_native_klarna_inapp_sdk-Swift.h")
9
+ #import "react_native_klarna_inapp_sdk-Swift.h"
10
+ #elif __has_include("RNKlarnaMobileSDK-Swift.h")
11
+ #import "RNKlarnaMobileSDK-Swift.h"
12
+ #else
13
+ #import <RNKlarnaMobileSDK/RNKlarnaMobileSDK-Swift.h>
14
+ #endif
15
+
16
+ @interface KlarnaExpressCheckoutViewWrapper ()
17
+
18
+ @property (nonatomic, strong) KlarnaExpressCheckoutButton* expressCheckoutButton;
19
+ @property (nonatomic, strong) KlarnaExpressCheckoutButtonDelegateProxy* delegateProxy;
20
+ @property (nonatomic, assign) BOOL isButtonCreated;
21
+
22
+ @end
23
+
24
+ @implementation KlarnaExpressCheckoutViewWrapper
25
+
26
+ #pragma mark - Initialization
27
+
28
+ - (instancetype)initWithFrame:(CGRect)frame
29
+ {
30
+ self = [super initWithFrame:frame];
31
+ if (self) {
32
+ self.backgroundColor = [UIColor clearColor];
33
+ self.isButtonCreated = NO;
34
+ self.autoFinalize = YES;
35
+ self.collectShippingAddress = NO;
36
+ [self setupDelegateProxy];
37
+ }
38
+ return self;
39
+ }
40
+
41
+ - (void)setupDelegateProxy {
42
+ self.delegateProxy = [[KlarnaExpressCheckoutButtonDelegateProxy alloc] init];
43
+
44
+ __weak __typeof(self) weakSelf = self;
45
+
46
+ self.delegateProxy.onAuthorizedHandler = ^(NSDictionary * _Nonnull response) {
47
+ __strong __typeof(weakSelf) strongSelf = weakSelf;
48
+ if (!strongSelf || !strongSelf.onAuthorized) {
49
+ return;
50
+ }
51
+ strongSelf.onAuthorized(@{
52
+ @"authorizationResponse": response
53
+ });
54
+ };
55
+
56
+ self.delegateProxy.onErrorHandler = ^(NSString * _Nonnull name, NSString * _Nonnull message, BOOL isFatal) {
57
+ __strong __typeof(weakSelf) strongSelf = weakSelf;
58
+ if (!strongSelf || !strongSelf.onError) {
59
+ return;
60
+ }
61
+ strongSelf.onError(@{
62
+ @"error": @{
63
+ @"name": name,
64
+ @"message": message,
65
+ @"isFatal": @(isFatal),
66
+ }
67
+ });
68
+ };
69
+ }
70
+
71
+ #pragma mark - React Native Property Setters
72
+
73
+ - (void)setSessionType:(NSString *)sessionType {
74
+ _sessionType = sessionType;
75
+ }
76
+
77
+ - (void)setClientId:(NSString *)clientId {
78
+ _clientId = clientId;
79
+ }
80
+
81
+ - (void)setClientToken:(NSString *)clientToken {
82
+ _clientToken = clientToken;
83
+ }
84
+
85
+ - (void)setLocale:(NSString *)locale {
86
+ _locale = locale;
87
+ }
88
+
89
+ - (void)setEnvironment:(NSString *)environment {
90
+ _environment = environment;
91
+ }
92
+
93
+ - (void)setRegion:(NSString *)region {
94
+ _region = region;
95
+ }
96
+
97
+ - (void)setReturnUrl:(NSString *)returnUrl {
98
+ _returnUrl = returnUrl;
99
+ }
100
+
101
+ - (void)setTheme:(NSString *)theme {
102
+ _theme = theme;
103
+ }
104
+
105
+ - (void)setShape:(NSString *)shape {
106
+ _shape = shape;
107
+ }
108
+
109
+ - (void)setButtonStyle:(NSString *)buttonStyle {
110
+ _buttonStyle = buttonStyle;
111
+ }
112
+
113
+ - (void)setAutoFinalize:(BOOL)autoFinalize {
114
+ _autoFinalize = autoFinalize;
115
+ }
116
+
117
+ - (void)setCollectShippingAddress:(BOOL)collectShippingAddress {
118
+ _collectShippingAddress = collectShippingAddress;
119
+ }
120
+
121
+ - (void)setSessionData:(NSString *)sessionData {
122
+ _sessionData = sessionData;
123
+ }
124
+
125
+ #pragma mark - React Native Lifecycle
126
+
127
+ /**
128
+ * Called by React Native after a batch of prop updates is applied.
129
+ *
130
+ * The SDK button is configured entirely through its constructor, so any prop
131
+ * change recreates the button. The delegate proxy is reused across instances.
132
+ */
133
+ - (void)didSetProps:(NSArray<NSString *> *)changedProps {
134
+ [super didSetProps:changedProps];
135
+ if (self.isButtonCreated) {
136
+ [self resetButton];
137
+ }
138
+ [self createButtonIfNeeded];
139
+ }
140
+
141
+ #pragma mark - Button Creation
142
+
143
+ - (void)resetButton {
144
+ if (self.expressCheckoutButton) {
145
+ [self.expressCheckoutButton removeFromSuperview];
146
+ self.expressCheckoutButton = nil;
147
+ }
148
+ self.isButtonCreated = NO;
149
+ }
150
+
151
+ - (void)createButtonIfNeeded {
152
+ if (self.isButtonCreated) {
153
+ return;
154
+ }
155
+
156
+ KlarnaExpressCheckoutButton *button = [KlarnaExpressCheckoutHelper
157
+ createButtonWithSessionType:self.sessionType ?: @""
158
+ clientId:self.clientId ?: @""
159
+ clientToken:self.clientToken ?: @""
160
+ locale:self.locale
161
+ returnUrl:self.returnUrl ?: @""
162
+ delegate:self.delegateProxy
163
+ theme:self.theme
164
+ shape:self.shape
165
+ buttonStyle:self.buttonStyle
166
+ autoFinalize:self.autoFinalize
167
+ collectShippingAddress:self.collectShippingAddress
168
+ sessionData:self.sessionData
169
+ environment:self.environment
170
+ region:self.region];
171
+
172
+ if (button) {
173
+ self.expressCheckoutButton = button;
174
+ self.expressCheckoutButton.translatesAutoresizingMaskIntoConstraints = NO;
175
+ [self addSubview:self.expressCheckoutButton];
176
+
177
+ [NSLayoutConstraint activateConstraints:@[
178
+ [self.expressCheckoutButton.topAnchor constraintEqualToAnchor:self.topAnchor],
179
+ [self.expressCheckoutButton.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
180
+ [self.expressCheckoutButton.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
181
+ [self.expressCheckoutButton.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
182
+ ]];
183
+
184
+ self.isButtonCreated = YES;
185
+
186
+ // Send resize event after layout
187
+ __weak __typeof(self) weakResizeSelf = self;
188
+ dispatch_async(dispatch_get_main_queue(), ^{
189
+ __strong __typeof(weakResizeSelf) strongSelf = weakResizeSelf;
190
+ if (strongSelf && strongSelf.onResized && strongSelf.expressCheckoutButton) {
191
+ CGFloat height = strongSelf.expressCheckoutButton.intrinsicContentSize.height;
192
+ if (height > 0) {
193
+ strongSelf.onResized(@{
194
+ @"height": [[NSNumber numberWithFloat:height] stringValue]
195
+ });
196
+ }
197
+ }
198
+ });
199
+ }
200
+ }
201
+
202
+ #pragma mark - Layout
203
+
204
+ - (void)layoutSubviews {
205
+ [super layoutSubviews];
206
+ if (self.expressCheckoutButton) {
207
+ self.expressCheckoutButton.frame = self.bounds;
208
+ }
209
+ }
210
+
211
+ #pragma mark - UIView Lifecycle
212
+
213
+ - (void)didMoveToWindow {
214
+ [super didMoveToWindow];
215
+ if (!self.window) {
216
+ [self resetButton];
217
+ }
218
+ }
219
+
220
+ @end
221
+
222
+ #endif