From de0afc0e11aeb5bbcde516a811d8ebb81daeb5b7 Mon Sep 17 00:00:00 2001 From: andreykropotov Date: Tue, 23 Jun 2026 10:45:40 +0400 Subject: [PATCH 1/2] DEV-3362: loyalty methods Add joinLoyalty / getLoyaltyStatus to the Flutter SDK, bridged to the native loyalty APIs. Native dependency bump (required for loyalty): - Android: switch to JitPack com.github.rees46:android-sdk:v2.33.0 (first release exposing SDK.instance.loyaltyManager) instead of the stale Maven com.rees46:rees46-sdk:2.6.0. - iOS: bump REES46 pod 3.23.0 -> 3.28.0. Adapt the iOS plugin to REES46 3.28.0 breaking changes: - qualify REES46.Category (now ambiguous with the ObjC runtime typedef); - ProductsListResponse.products is [Product] (no categories) -> map via the Product serializer; - setProfileData moved from a ProfileData value to named parameters. Loyalty plumbing: - Pigeon joinLoyalty/getLoyaltyStatus (JSON envelope) + regenerated bindings; - Dart facade + LoyaltyJoinResponse/LoyaltyStatusResponse/LoyaltyLevel models; - Android impl via loyaltyManager.join/getStatus (Gson); - iOS impl via sdk.joinLoyalty/getLoyaltyStatus; - example Loyalty card (Join/Status); - 7 unit tests + real-network integration tests (verified on iOS simulator and Android emulator against the live API). --- android/build.gradle.kts | 10 +- .../Rees46FlutterSdkPlugin.kt | 49 ++ .../pigeon/PersonalizationApi.g.kt | 62 ++- .../integration_test/loyalty_call_test.dart | 61 +++ .../integration_test/loyalty_sdk_test.dart | 54 ++ example/ios/Podfile.lock | 22 +- example/lib/main.dart | 140 +++++ ios/Classes/Rees46FlutterSdkPlugin.swift | 134 ++++- ios/Classes/pigeon/PersonalizationApi.g.swift | 60 ++- ios/rees46_sdk.podspec | 2 +- lib/rees46_sdk.dart | 1 + lib/src/loyalty/loyalty_response.dart | 58 ++ lib/src/personalization_sdk.dart | 33 ++ lib/src/pigeon/personalization_api.g.dart | 502 ++++++++---------- pigeons/personalization_api.dart | 19 + test/loyalty_test.dart | 125 +++++ 16 files changed, 1003 insertions(+), 329 deletions(-) create mode 100644 example/integration_test/loyalty_call_test.dart create mode 100644 example/integration_test/loyalty_sdk_test.dart create mode 100644 lib/src/loyalty/loyalty_response.dart create mode 100644 test/loyalty_test.dart diff --git a/android/build.gradle.kts b/android/build.gradle.kts index dd4091d..8aff7c9 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -81,13 +81,15 @@ android { } dependencies { - // REES46 Android SDK (Maven Central). + // REES46 Android SDK (JitPack). // - // Note: the 2.x line in Maven Central uses versions like 2.6.0 (not 2.28.0). - val rees46AndroidSdkVersion = "2.6.0" + // Published from github.com/rees46/android-sdk as `com.github.rees46:android-sdk:`. + // v2.33.0 is the first release that exposes the loyalty manager + // (`SDK.instance.loyaltyManager`). + val rees46AndroidSdkVersion = "v2.33.0" add( "rees46Implementation", - "com.rees46:rees46-sdk:$rees46AndroidSdkVersion", + "com.github.rees46:android-sdk:$rees46AndroidSdkVersion", ) testImplementation("org.jetbrains.kotlin:kotlin-test") diff --git a/android/src/main/kotlin/com/rees46/rees46_flutter_sdk/Rees46FlutterSdkPlugin.kt b/android/src/main/kotlin/com/rees46/rees46_flutter_sdk/Rees46FlutterSdkPlugin.kt index 9a06bfd..30c52f6 100644 --- a/android/src/main/kotlin/com/rees46/rees46_flutter_sdk/Rees46FlutterSdkPlugin.kt +++ b/android/src/main/kotlin/com/rees46/rees46_flutter_sdk/Rees46FlutterSdkPlugin.kt @@ -305,6 +305,55 @@ class Rees46FlutterSdkPlugin : } } + override fun joinLoyalty( + phone: String, + email: String?, + firstName: String?, + lastName: String?, + callback: (Result) -> Unit, + ) { + if (phone.isBlank()) { + callback(Result.failure(FlutterError("bad_args", "phone is required", null))) + return + } + try { + SDK.instance.loyaltyManager.join( + phone = phone, + email = email, + firstName = firstName, + lastName = lastName, + onSuccess = { response -> + callback(Result.success(Gson().toJson(response))) + }, + onError = { code, message -> + callback(Result.failure(FlutterError("join_loyalty_failed", message ?: "error $code", null))) + }, + ) + } catch (t: Throwable) { + callback(Result.failure(FlutterError("join_loyalty_failed", t.message, null))) + } + } + + override fun getLoyaltyStatus(identifier: String, callback: (Result) -> Unit) { + if (identifier.isBlank()) { + callback(Result.failure(FlutterError("bad_args", "identifier is required", null))) + return + } + try { + SDK.instance.loyaltyManager.getStatus( + identifier = identifier, + onSuccess = { response -> + callback(Result.success(Gson().toJson(response))) + }, + onError = { code, message -> + callback(Result.failure(FlutterError("loyalty_status_failed", message ?: "error $code", null))) + }, + ) + } catch (t: Throwable) { + callback(Result.failure(FlutterError("loyalty_status_failed", t.message, null))) + } + } + override fun getSid(): String = SDK.instance.getSid() override fun getDid(): String? = SDK.instance.getDid() diff --git a/android/src/main/kotlin/com/rees46/rees46_flutter_sdk/pigeon/PersonalizationApi.g.kt b/android/src/main/kotlin/com/rees46/rees46_flutter_sdk/pigeon/PersonalizationApi.g.kt index e4e16b8..ad35a75 100644 --- a/android/src/main/kotlin/com/rees46/rees46_flutter_sdk/pigeon/PersonalizationApi.g.kt +++ b/android/src/main/kotlin/com/rees46/rees46_flutter_sdk/pigeon/PersonalizationApi.g.kt @@ -131,12 +131,15 @@ data class InitConfig ( } /** - * Wire format for one purchase line (maps to native [PurchaseItemRequest]; quantity not exposed). + * Wire format for one purchase line (maps to native `PurchaseItemRequest`). + * [amount] is the canonical line quantity; native's redundant `quantity` alias + * is intentionally not exposed. * * Generated class from Pigeon that represents data sent in messages. */ data class PurchaseLineItemWire ( val id: String, + /** Number of units of this product in the order (the line quantity). */ val amount: Long, val price: Double, val lineId: String? = null, @@ -355,6 +358,20 @@ interface PersonalizationHostApi { * Dart layer parses the result into [SearchFullResponse]. */ fun searchFull(query: String, paramsJson: String?, callback: (Result) -> Unit) + /** + * Joins the loyalty program (`loyalty/members/join`) and returns the + * response envelope as a JSON string `{ "status": ..., "payload": { ... } }`. + * The shop is identified by the SDK's configured `shop_id`; [phone] is required. + * Dart layer parses the result into [LoyaltyJoinResponse]. + */ + fun joinLoyalty(phone: String, email: String?, firstName: String?, lastName: String?, callback: (Result) -> Unit) + /** + * Returns the loyalty membership status (`loyalty/members/status`) as a JSON + * string `{ "status": ..., "payload": { "member": ..., "level": { ... } } }`. + * [identifier] is the member identifier (phone). + * Dart layer parses the result into [LoyaltyStatusResponse]. + */ + fun getLoyaltyStatus(identifier: String, callback: (Result) -> Unit) /** [customJson] and [recommendedSourceJson] are JSON object strings or null. */ fun trackPurchase(orderId: String, orderPrice: Double, items: List, deliveryType: String?, deliveryAddress: String?, paymentType: String?, isTaxFree: Boolean, promocode: String?, orderCash: Double?, orderBonuses: Double?, orderDelivery: Double?, orderDiscount: Double?, channel: String?, customJson: String?, recommendedSourceJson: String?, stream: String?, segment: String?, callback: (Result) -> Unit) @@ -610,6 +627,49 @@ interface PersonalizationHostApi { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.joinLoyalty$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val phoneArg = args[0] as String + val emailArg = args[1] as String? + val firstNameArg = args[2] as String? + val lastNameArg = args[3] as String? + api.joinLoyalty(phoneArg, emailArg, firstNameArg, lastNameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(PersonalizationApiPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(PersonalizationApiPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getLoyaltyStatus$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val identifierArg = args[0] as String + api.getLoyaltyStatus(identifierArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(PersonalizationApiPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(PersonalizationApiPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.trackPurchase$separatedMessageChannelSuffix", codec) if (api != null) { diff --git a/example/integration_test/loyalty_call_test.dart b/example/integration_test/loyalty_call_test.dart new file mode 100644 index 0000000..02c914e --- /dev/null +++ b/example/integration_test/loyalty_call_test.dart @@ -0,0 +1,61 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:rees46_sdk/rees46_sdk.dart'; + +/// Real-network integration test for the loyalty methods. +/// +/// Drives the actual native SDK (Android `loyaltyManager`, iOS +/// `joinLoyalty`/`getLoyaltyStatus`) against the live REES46 API using a +/// loyalty-enabled shop. Run on a booted simulator/emulator: +/// +/// flutter test integration_test/loyalty_call_test.dart -d [device-id] +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + const shopId = 'c1140c8254976de297c3caf971701a'; + const apiDomain = 'api.rees46.ru'; + const phone = '79991234567'; + + Future initSdk() async { + final sdk = PersonalizationSdk(); + await sdk.initialize( + const SdkInitConfig( + shopId: shopId, + apiDomain: apiDomain, + stream: kIsWeb ? 'web' : 'ios', + enableLogs: true, + autoSendPushToken: false, + sendAdvertisingId: false, + enableAutoPopupPresentation: false, + needReInitialization: false, + ), + ); + // Wait until the native SDK has established a device id (did) — the status + // endpoint requires it, and on Android init resolves it asynchronously. + for (var i = 0; i < 40; i++) { + final did = await sdk.getDid(); + if (did != null && did.isNotEmpty) break; + await Future.delayed(const Duration(milliseconds: 250)); + } + return sdk; + } + + testWidgets('joinLoyalty — real call returns success', (tester) async { + final sdk = await initSdk(); + + final response = await sdk.joinLoyalty(phone: phone); + debugPrint('joinLoyalty -> status=${response.status} payload=${response.payload}'); + expect(response.status, equals('success')); + }); + + testWidgets('getLoyaltyStatus — real call returns success', (tester) async { + final sdk = await initSdk(); + + final response = await sdk.getLoyaltyStatus(phone); + debugPrint( + 'getLoyaltyStatus -> status=${response.status} member=${response.member} level=${response.level?.name}', + ); + expect(response.status, equals('success')); + }); +} diff --git a/example/integration_test/loyalty_sdk_test.dart b/example/integration_test/loyalty_sdk_test.dart new file mode 100644 index 0000000..1a1080d --- /dev/null +++ b/example/integration_test/loyalty_sdk_test.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:patrol/patrol.dart'; + +import 'package:rees46_sdk_example/main.dart' as app; + +Future _initializeSdk(PatrolIntegrationTester $) async { + await $.pumpWidgetAndSettle(const app.App()); + await $( + 'Status: Initialized', + ).waitUntilExists(timeout: const Duration(seconds: 30)); + await $('Status: Initialized').scrollTo(scrollDirection: AxisDirection.up); +} + +String _labelText(PatrolIntegrationTester $, String key) { + final widget = $.tester.widget(find.byKey(Key(key))); + return widget.data ?? ''; +} + +void main() { + patrolTest('getLoyaltyStatus — real call returns success', ($) async { + await _initializeSdk($); + + await $(const Key('btn_loyalty_status')).scrollTo(); + await $(const Key('btn_loyalty_status')).tap(); + + await $( + const Key('lbl_loyalty_result'), + ).waitUntilExists(timeout: const Duration(seconds: 30)); + + expect(find.byKey(const Key('lbl_loyalty_error')), findsNothing); + + final result = _labelText($, 'lbl_loyalty_result'); + expect(result, startsWith('status:')); + expect(result, contains('success')); + }); + + patrolTest('joinLoyalty — real call returns success', ($) async { + await _initializeSdk($); + + await $(const Key('btn_join_loyalty')).scrollTo(); + await $(const Key('btn_join_loyalty')).tap(); + + await $( + const Key('lbl_loyalty_result'), + ).waitUntilExists(timeout: const Duration(seconds: 30)); + + expect(find.byKey(const Key('lbl_loyalty_error')), findsNothing); + + final result = _labelText($, 'lbl_loyalty_result'); + expect(result, startsWith('join:')); + expect(result, contains('success')); + }); +} diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index ac25e8f..8d3fb30 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,22 +1,19 @@ PODS: - CocoaAsyncSocket (7.6.5) - Flutter (1.0.0) - - integration_test (0.0.1): - - Flutter - patrol (0.0.1): - CocoaAsyncSocket (~> 7.6) - Flutter - FlutterMacOS - - personalization_flutter_sdk (0.0.1): + - REES46 (3.28.0) + - rees46_sdk (0.0.1): - Flutter - - REES46 (= 3.23.0) - - REES46 (3.23.0) + - REES46 (= 3.28.0) DEPENDENCIES: - Flutter (from `Flutter`) - - integration_test (from `.symlinks/plugins/integration_test/ios`) - patrol (from `.symlinks/plugins/patrol/darwin`) - - personalization_flutter_sdk (from `.symlinks/plugins/personalization_flutter_sdk/ios`) + - rees46_sdk (from `.symlinks/plugins/rees46_sdk/ios`) SPEC REPOS: trunk: @@ -26,20 +23,17 @@ SPEC REPOS: EXTERNAL SOURCES: Flutter: :path: Flutter - integration_test: - :path: ".symlinks/plugins/integration_test/ios" patrol: :path: ".symlinks/plugins/patrol/darwin" - personalization_flutter_sdk: - :path: ".symlinks/plugins/personalization_flutter_sdk/ios" + rees46_sdk: + :path: ".symlinks/plugins/rees46_sdk/ios" SPEC CHECKSUMS: CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 - integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e patrol: cea8074f183a2a4232d0ebd10569ae05149ada42 - personalization_flutter_sdk: 296eed2fbff082c9697c96d2176b023c173bb375 - REES46: 48ad09f18e2d75a730728c7e10f0328ffac69b08 + REES46: ecba4cbbfd060636541beedff390564561a1a562 + rees46_sdk: 58ed3f7511bca74a71702a41f7d37f757869837b PODFILE CHECKSUM: b6e248ac4c1eff5807c8b044b39b8bc326af3f5f diff --git a/example/lib/main.dart b/example/lib/main.dart index d5e0d17..19590e5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -94,12 +94,19 @@ class _InitPageState extends State { String? _searchInstantError; bool _searchInstantLoading = false; + // Loyalty state + final _loyaltyPhoneController = TextEditingController(text: '79991234567'); + String? _loyaltyResult; + String? _loyaltyError; + bool _loyaltyLoading = false; + @override void dispose() { _recBlockController.dispose(); _productIdController.dispose(); _searchQueryController.dispose(); _searchInstantQueryController.dispose(); + _loyaltyPhoneController.dispose(); super.dispose(); } @@ -337,6 +344,52 @@ class _InitPageState extends State { } } + Future _joinLoyalty() async { + final phone = _loyaltyPhoneController.text.trim(); + if (phone.isEmpty) return; + setState(() { + _loyaltyLoading = true; + _loyaltyError = null; + _loyaltyResult = null; + }); + try { + final response = await _sdk.joinLoyalty(phone: phone); + setState(() { + _loyaltyResult = 'join: status=${response.status}'; + _loyaltyLoading = false; + }); + } catch (e) { + setState(() { + _loyaltyError = 'Error: $e'; + _loyaltyLoading = false; + }); + } + } + + Future _getLoyaltyStatus() async { + final phone = _loyaltyPhoneController.text.trim(); + if (phone.isEmpty) return; + setState(() { + _loyaltyLoading = true; + _loyaltyError = null; + _loyaltyResult = null; + }); + try { + final response = await _sdk.getLoyaltyStatus(phone); + setState(() { + _loyaltyResult = + 'status: ${response.status}, member=${response.member}, ' + 'level=${response.level?.name ?? '-'}'; + _loyaltyLoading = false; + }); + } catch (e) { + setState(() { + _loyaltyError = 'Error: $e'; + _loyaltyLoading = false; + }); + } + } + Future _demoTrackEvent() async { if (_initState != InitState.initialized) return; try { @@ -507,6 +560,16 @@ class _InitPageState extends State { enabled: _initState == InitState.initialized, onSearch: _searchFull, ), + const SizedBox(height: 12), + _LoyaltyCard( + phoneController: _loyaltyPhoneController, + result: _loyaltyResult, + error: _loyaltyError, + loading: _loyaltyLoading, + enabled: _initState == InitState.initialized, + onJoin: _joinLoyalty, + onStatus: _getLoyaltyStatus, + ), const SizedBox(height: 24), Text('Tracking', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), @@ -803,6 +866,83 @@ class _ProductInfoCard extends StatelessWidget { } } +class _LoyaltyCard extends StatelessWidget { + final TextEditingController phoneController; + final String? result; + final String? error; + final bool loading; + final bool enabled; + final VoidCallback onJoin; + final VoidCallback onStatus; + + const _LoyaltyCard({ + required this.phoneController, + required this.result, + required this.error, + required this.loading, + required this.enabled, + required this.onJoin, + required this.onStatus, + }); + + @override + Widget build(BuildContext context) { + return Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Loyalty', style: Theme.of(context).textTheme.titleMedium), + const SizedBox(height: 12), + TextField( + key: const Key('field_loyalty_phone'), + controller: phoneController, + keyboardType: TextInputType.phone, + decoration: const InputDecoration( + labelText: 'Phone', + hintText: 'e.g. 79991234567', + ), + ), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: OutlinedButton( + key: const Key('btn_join_loyalty'), + onPressed: (enabled && !loading) ? onJoin : null, + child: Text(loading ? 'Loading…' : 'Join'), + ), + ), + const SizedBox(width: 8), + Expanded( + child: OutlinedButton( + key: const Key('btn_loyalty_status'), + onPressed: (enabled && !loading) ? onStatus : null, + child: Text(loading ? 'Loading…' : 'Status'), + ), + ), + ], + ), + if (result != null) ...[ + const SizedBox(height: 8), + Text(key: const Key('lbl_loyalty_result'), result!), + ], + if (error != null) ...[ + const SizedBox(height: 8), + Text( + key: const Key('lbl_loyalty_error'), + error!, + style: TextStyle(color: Theme.of(context).colorScheme.error), + ), + ], + ], + ), + ), + ); + } +} + class _ProductsListCard extends StatelessWidget { final int? total; final String? error; diff --git a/ios/Classes/Rees46FlutterSdkPlugin.swift b/ios/Classes/Rees46FlutterSdkPlugin.swift index 6dc7d3d..576ed58 100644 --- a/ios/Classes/Rees46FlutterSdkPlugin.swift +++ b/ios/Classes/Rees46FlutterSdkPlugin.swift @@ -215,7 +215,7 @@ final class PersonalizationHostApiImpl: PersonalizationHostApi { return dict } - private static func _categoryToDict(_ c: Category) -> [String: Any] { + private static func _categoryToDict(_ c: REES46.Category) -> [String: Any] { var dict: [String: Any] = ["id": c.id, "name": c.name] if let parentId = c.parentId { dict["parent_id"] = parentId } if let url = c.url { dict["url"] = url } @@ -286,8 +286,10 @@ final class PersonalizationHostApiImpl: PersonalizationHostApi { } private static func _productsListResponseToDict(_ r: ProductsListResponse) -> [String: Any] { + // As of REES46 3.24+, ProductsListResponse.products is [Product] (not [ProductInfo]); + // Product carries no categories, which the Dart parser already treats as optional. var dict: [String: Any] = [ - "products": r.products.map { _productInfoToDict($0) }, + "products": r.products.map { _searchProductToDict($0) }, "products_total": r.productsTotal, ] if let pr = r.priceRange { @@ -506,7 +508,7 @@ final class PersonalizationHostApiImpl: PersonalizationHostApi { return dict } - private static func _searchCategoryToDict(_ c: Category) -> [String: Any] { + private static func _searchCategoryToDict(_ c: REES46.Category) -> [String: Any] { var dict: [String: Any] = ["id": c.id, "name": c.name] if let url = c.url { dict["url"] = url } if let parent = c.parentId { dict["parent"] = parent } @@ -514,6 +516,78 @@ final class PersonalizationHostApiImpl: PersonalizationHostApi { return dict } + func joinLoyalty( + phone: String, + email: String?, + firstName: String?, + lastName: String?, + completion: @escaping (Result) -> Void + ) { + guard let sdk = Rees46FlutterSdkPlugin.sdk else { + completion(.failure(PigeonError(code: "not_initialized", message: "SDK is not initialized", details: nil))) + return + } + if phone.isEmpty { + completion(.failure(PigeonError(code: "bad_args", message: "phone is required", details: nil))) + return + } + sdk.joinLoyalty(phone: phone, email: email, firstName: firstName, lastName: lastName) { result in + switch result { + case .success(let response): + var dict: [String: Any] = ["payload": response.payload] + if let status = response.status { dict["status"] = status } + Self._encodeEnvelope(dict, completion: completion) + case .failure(let err): + completion(.failure(PigeonError(code: "join_loyalty_failed", message: String(describing: err), details: nil))) + } + } + } + + func getLoyaltyStatus(identifier: String, completion: @escaping (Result) -> Void) { + guard let sdk = Rees46FlutterSdkPlugin.sdk else { + completion(.failure(PigeonError(code: "not_initialized", message: "SDK is not initialized", details: nil))) + return + } + if identifier.isEmpty { + completion(.failure(PigeonError(code: "bad_args", message: "identifier is required", details: nil))) + return + } + sdk.getLoyaltyStatus(identifier: identifier) { result in + switch result { + case .success(let response): + var payload: [String: Any] = [:] + if let member = response.member { payload["member"] = member } + if let level = response.level { + var levelDict: [String: Any] = [:] + if let name = level.name { levelDict["name"] = name } + if let code = level.code { levelDict["code"] = code } + if let exp = level.expirationDate { levelDict["expiration_date"] = exp } + payload["level"] = levelDict + } + var dict: [String: Any] = ["payload": payload] + if let status = response.status { dict["status"] = status } + Self._encodeEnvelope(dict, completion: completion) + case .failure(let err): + completion(.failure(PigeonError(code: "loyalty_status_failed", message: String(describing: err), details: nil))) + } + } + } + + /// Serializes a loyalty response envelope `{ "status": ..., "payload": ... }` + /// to a JSON string, mirroring the other JSON-returning host methods. + private static func _encodeEnvelope( + _ dict: [String: Any], + completion: @escaping (Result) -> Void + ) { + guard JSONSerialization.isValidJSONObject(dict), + let data = try? JSONSerialization.data(withJSONObject: dict), + let json = String(data: data, encoding: .utf8) else { + completion(.failure(PigeonError(code: "serialization_failed", message: "Failed to serialize loyalty response", details: nil))) + return + } + completion(.success(json)) + } + func getSid() throws -> String { guard let sdk = Rees46FlutterSdkPlugin.sdk else { throw PigeonError(code: "not_initialized", message: "SDK is not initialized", details: nil) @@ -530,38 +604,46 @@ final class PersonalizationHostApiImpl: PersonalizationHostApi { completion(.failure(PigeonError(code: "not_initialized", message: "SDK is not initialized", details: nil))) return } - var profileData = ProfileData() - profileData.userEmail = params.email - profileData.userPhone = params.phone - profileData.userLoyaltyId = params.loyaltyId - profileData.firstName = params.firstName - profileData.lastName = params.lastName - profileData.age = params.age.map { Int($0) } - profileData.location = params.location - profileData.advertisingId = params.advertisingId - profileData.fbID = params.fbId - profileData.vkID = params.vkId - profileData.telegramId = params.telegramId - profileData.loyaltyCardLocation = params.loyaltyCardLocation - profileData.loyaltyStatus = params.loyaltyStatus - profileData.loyaltyBonuses = params.loyaltyBonuses.map { Int($0) } - profileData.loyaltyBonusesToNextLevel = params.loyaltyBonusesToNextLevel.map { Int($0) } - profileData.boughtSomething = params.boughtSomething - profileData.userId = params.userId + // As of REES46 3.24+, `setProfileData` takes individual named parameters + // instead of a `ProfileData` value. + var gender: Gender? if let genderStr = params.gender { - profileData.gender = genderStr == "m" ? .male : .female + gender = genderStr == "m" ? .male : .female } + var birthday: Date? if let birthdayStr = params.birthday { let fmt = DateFormatter() fmt.dateFormat = "yyyy-MM-dd" - profileData.birthday = fmt.date(from: birthdayStr) + birthday = fmt.date(from: birthdayStr) } + var customProperties: [String: Any?]? if let json = params.customPropertiesJson, let data = json.data(using: .utf8), let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] { - profileData.customProperties = obj - } - sdk.setProfileData(profileData: profileData) { result in + customProperties = obj + } + sdk.setProfileData( + userEmail: params.email, + userPhone: params.phone, + userLoyaltyId: params.loyaltyId, + birthday: birthday, + age: params.age.map { Int($0) }, + firstName: params.firstName, + lastName: params.lastName, + location: params.location, + gender: gender, + advertisingId: params.advertisingId, + fbID: params.fbId, + vkID: params.vkId, + telegramId: params.telegramId, + loyaltyCardLocation: params.loyaltyCardLocation, + loyaltyStatus: params.loyaltyStatus, + loyaltyBonuses: params.loyaltyBonuses.map { Int($0) }, + loyaltyBonusesToNextLevel: params.loyaltyBonusesToNextLevel.map { Int($0) }, + boughtSomething: params.boughtSomething, + userId: params.userId, + customProperties: customProperties + ) { result in switch result { case .success: completion(.success(())) diff --git a/ios/Classes/pigeon/PersonalizationApi.g.swift b/ios/Classes/pigeon/PersonalizationApi.g.swift index 76222a7..570c819 100644 --- a/ios/Classes/pigeon/PersonalizationApi.g.swift +++ b/ios/Classes/pigeon/PersonalizationApi.g.swift @@ -185,11 +185,14 @@ struct InitConfig: Hashable { } } -/// Wire format for one purchase line (maps to native [PurchaseItemRequest]; quantity not exposed). +/// Wire format for one purchase line (maps to native `PurchaseItemRequest`). +/// [amount] is the canonical line quantity; native's redundant `quantity` alias +/// is intentionally not exposed. /// /// Generated class from Pigeon that represents data sent in messages. struct PurchaseLineItemWire: Hashable { var id: String + /// Number of units of this product in the order (the line quantity). var amount: Int64 var price: Double var lineId: String? = nil @@ -418,6 +421,16 @@ protocol PersonalizationHostApi { /// [paramsJson] is a JSON object string with optional search parameters. /// Dart layer parses the result into [SearchFullResponse]. func searchFull(query: String, paramsJson: String?, completion: @escaping (Result) -> Void) + /// Joins the loyalty program (`loyalty/members/join`) and returns the + /// response envelope as a JSON string `{ "status": ..., "payload": { ... } }`. + /// The shop is identified by the SDK's configured `shop_id`; [phone] is required. + /// Dart layer parses the result into [LoyaltyJoinResponse]. + func joinLoyalty(phone: String, email: String?, firstName: String?, lastName: String?, completion: @escaping (Result) -> Void) + /// Returns the loyalty membership status (`loyalty/members/status`) as a JSON + /// string `{ "status": ..., "payload": { "member": ..., "level": { ... } } }`. + /// [identifier] is the member identifier (phone). + /// Dart layer parses the result into [LoyaltyStatusResponse]. + func getLoyaltyStatus(identifier: String, completion: @escaping (Result) -> Void) /// [customJson] and [recommendedSourceJson] are JSON object strings or null. func trackPurchase(orderId: String, orderPrice: Double, items: [PurchaseLineItemWire], deliveryType: String?, deliveryAddress: String?, paymentType: String?, isTaxFree: Bool, promocode: String?, orderCash: Double?, orderBonuses: Double?, orderDelivery: Double?, orderDiscount: Double?, channel: String?, customJson: String?, recommendedSourceJson: String?, stream: String?, segment: String?, completion: @escaping (Result) -> Void) } @@ -660,6 +673,51 @@ class PersonalizationHostApiSetup { } else { searchFullChannel.setMessageHandler(nil) } + /// Joins the loyalty program (`loyalty/members/join`) and returns the + /// response envelope as a JSON string `{ "status": ..., "payload": { ... } }`. + /// The shop is identified by the SDK's configured `shop_id`; [phone] is required. + /// Dart layer parses the result into [LoyaltyJoinResponse]. + let joinLoyaltyChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.joinLoyalty\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + joinLoyaltyChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let phoneArg = args[0] as! String + let emailArg: String? = nilOrValue(args[1]) + let firstNameArg: String? = nilOrValue(args[2]) + let lastNameArg: String? = nilOrValue(args[3]) + api.joinLoyalty(phone: phoneArg, email: emailArg, firstName: firstNameArg, lastName: lastNameArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + joinLoyaltyChannel.setMessageHandler(nil) + } + /// Returns the loyalty membership status (`loyalty/members/status`) as a JSON + /// string `{ "status": ..., "payload": { "member": ..., "level": { ... } } }`. + /// [identifier] is the member identifier (phone). + /// Dart layer parses the result into [LoyaltyStatusResponse]. + let getLoyaltyStatusChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getLoyaltyStatus\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getLoyaltyStatusChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let identifierArg = args[0] as! String + api.getLoyaltyStatus(identifier: identifierArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getLoyaltyStatusChannel.setMessageHandler(nil) + } /// [customJson] and [recommendedSourceJson] are JSON object strings or null. let trackPurchaseChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.trackPurchase\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { diff --git a/ios/rees46_sdk.podspec b/ios/rees46_sdk.podspec index 9a4a279..9383768 100644 --- a/ios/rees46_sdk.podspec +++ b/ios/rees46_sdk.podspec @@ -14,7 +14,7 @@ Flutter plugin wrapper around REES46 native SDK. s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.dependency 'REES46', '3.23.0' + s.dependency 'REES46', '3.28.0' s.platform = :ios, '13.0' # Flutter.framework does not contain a i386 slice. diff --git a/lib/rees46_sdk.dart b/lib/rees46_sdk.dart index 29cb996..5694828 100644 --- a/lib/rees46_sdk.dart +++ b/lib/rees46_sdk.dart @@ -1,4 +1,5 @@ export 'src/personalization_sdk.dart'; +export 'src/loyalty/loyalty_response.dart'; export 'src/profile/profile_params.dart'; export 'src/recommendation/recommendation_params.dart'; export 'src/recommendation/recommendation_response.dart'; diff --git a/lib/src/loyalty/loyalty_response.dart b/lib/src/loyalty/loyalty_response.dart new file mode 100644 index 0000000..abd545d --- /dev/null +++ b/lib/src/loyalty/loyalty_response.dart @@ -0,0 +1,58 @@ +/// Response from [PersonalizationSdk.joinLoyalty] (`loyalty/members/join`). +/// +/// The endpoint returns an envelope `{ "status": ..., "payload": { ... } }`. +/// [payload] is kept as a raw map because its shape differs between success +/// and failure responses. +class LoyaltyJoinResponse { + final String? status; + final Map payload; + + const LoyaltyJoinResponse({this.status, required this.payload}); + + factory LoyaltyJoinResponse.fromJson(Map json) { + return LoyaltyJoinResponse( + status: json['status'] as String?, + payload: + (json['payload'] as Map?)?.cast() ?? + const {}, + ); + } +} + +/// Response from [PersonalizationSdk.getLoyaltyStatus] (`loyalty/members/status`). +/// +/// Envelope: `{ "status": ..., "payload": { "member": ..., "level": { ... } } }`. +class LoyaltyStatusResponse { + final String? status; + final bool? member; + final LoyaltyLevel? level; + + const LoyaltyStatusResponse({this.status, this.member, this.level}); + + factory LoyaltyStatusResponse.fromJson(Map json) { + final payload = (json['payload'] as Map?)?.cast(); + final levelJson = (payload?['level'] as Map?)?.cast(); + return LoyaltyStatusResponse( + status: json['status'] as String?, + member: payload?['member'] as bool?, + level: levelJson == null ? null : LoyaltyLevel.fromJson(levelJson), + ); + } +} + +/// Loyalty level returned inside the `loyalty/members/status` payload. +class LoyaltyLevel { + final String? name; + final String? code; + final String? expirationDate; + + const LoyaltyLevel({this.name, this.code, this.expirationDate}); + + factory LoyaltyLevel.fromJson(Map json) { + return LoyaltyLevel( + name: json['name'] as String?, + code: json['code'] as String?, + expirationDate: json['expiration_date'] as String?, + ); + } +} diff --git a/lib/src/personalization_sdk.dart b/lib/src/personalization_sdk.dart index be5924c..8bb5071 100644 --- a/lib/src/personalization_sdk.dart +++ b/lib/src/personalization_sdk.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'pigeon/personalization_api.g.dart' as pigeon; import 'init/sdk_init_handler.dart'; +import 'loyalty/loyalty_response.dart'; import 'profile/profile_params.dart'; import 'push/push_notification_callbacks.dart'; import 'recommendation/recommendation_params.dart'; @@ -111,6 +112,38 @@ class PersonalizationSdk { ); } + /// Joins the loyalty program (native `loyalty/members/join`). + /// + /// The shop is identified by the SDK's configured `shop_id`; [phone] is + /// required, the remaining member fields are optional. + Future joinLoyalty({ + required String phone, + String? email, + String? firstName, + String? lastName, + }) async { + if (phone.isEmpty) { + throw ArgumentError.value(phone, 'phone', 'must be non-empty'); + } + final json = await _api.joinLoyalty(phone, email, firstName, lastName); + return LoyaltyJoinResponse.fromJson( + jsonDecode(json) as Map, + ); + } + + /// Returns the loyalty membership status (native `loyalty/members/status`). + /// + /// [identifier] is the member identifier (phone). + Future getLoyaltyStatus(String identifier) async { + if (identifier.isEmpty) { + throw ArgumentError.value(identifier, 'identifier', 'must be non-empty'); + } + final json = await _api.getLoyaltyStatus(identifier); + return LoyaltyStatusResponse.fromJson( + jsonDecode(json) as Map, + ); + } + Future getRecommendation( String code, { RecommendationParams? params, diff --git a/lib/src/pigeon/personalization_api.g.dart b/lib/src/pigeon/personalization_api.g.dart index 2d2afc9..c941a05 100644 --- a/lib/src/pigeon/personalization_api.g.dart +++ b/lib/src/pigeon/personalization_api.g.dart @@ -15,11 +15,7 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse({ - Object? result, - PlatformException? error, - bool empty = false, -}) { +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -28,25 +24,21 @@ List wrapResponse({ } return [error.code, error.message, error.details]; } - bool _deepEquals(Object? a, Object? b) { if (a is List && b is List) { return a.length == b.length && - a.indexed.every( - ((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]), - ); + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && - a.entries.every( - (MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key]), - ); + return a.length == b.length && a.entries.every((MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key])); } return a == b; } + class InitConfig { InitConfig({ required this.shopId, @@ -89,8 +81,7 @@ class InitConfig { } Object encode() { - return _toList(); - } + return _toList(); } static InitConfig decode(Object result) { result as List; @@ -120,10 +111,13 @@ class InitConfig { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } -/// Wire format for one purchase line (maps to native [PurchaseItemRequest]; quantity not exposed). +/// Wire format for one purchase line (maps to native `PurchaseItemRequest`). +/// [amount] is the canonical line quantity; native's redundant `quantity` alias +/// is intentionally not exposed. class PurchaseLineItemWire { PurchaseLineItemWire({ required this.id, @@ -135,6 +129,7 @@ class PurchaseLineItemWire { String id; + /// Number of units of this product in the order (the line quantity). int amount; double price; @@ -144,12 +139,17 @@ class PurchaseLineItemWire { String? fashionSize; List _toList() { - return [id, amount, price, lineId, fashionSize]; + return [ + id, + amount, + price, + lineId, + fashionSize, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static PurchaseLineItemWire decode(Object result) { result as List; @@ -176,7 +176,8 @@ class PurchaseLineItemWire { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } /// Wire format for profile fields sent to native SDK. @@ -274,8 +275,7 @@ class ProfileParamsWire { } Object encode() { - return _toList(); - } + return _toList(); } static ProfileParamsWire decode(Object result) { result as List; @@ -317,9 +317,11 @@ class ProfileParamsWire { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -327,13 +329,13 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is InitConfig) { + } else if (value is InitConfig) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is PurchaseLineItemWire) { + } else if (value is PurchaseLineItemWire) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is ProfileParamsWire) { + } else if (value is ProfileParamsWire) { buffer.putUint8(131); writeValue(buffer, value.encode()); } else { @@ -344,11 +346,11 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: return InitConfig.decode(readValue(buffer)!); - case 130: + case 130: return PurchaseLineItemWire.decode(readValue(buffer)!); - case 131: + case 131: return ProfileParamsWire.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -360,13 +362,9 @@ class PersonalizationHostApi { /// Constructor for [PersonalizationHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - PersonalizationHostApi({ - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty - ? '.$messageChannelSuffix' - : ''; + PersonalizationHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -374,17 +372,13 @@ class PersonalizationHostApi { final String pigeonVar_messageChannelSuffix; Future initialize(InitConfig config) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.initialize$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [config], + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.initialize$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([config]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -401,14 +395,12 @@ class PersonalizationHostApi { } Future getPlatformVersion() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getPlatformVersion$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getPlatformVersion$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -432,14 +424,12 @@ class PersonalizationHostApi { /// Returns the push token stored by the native SDK (if any). Future getStoredPushToken() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getStoredPushToken$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getStoredPushToken$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -457,25 +447,14 @@ class PersonalizationHostApi { } /// [customFieldsJson] is JSON object string or null (maps to native custom fields map). - Future trackEvent( - String event, - int? time, - String? category, - String? label, - int? value, - String? customFieldsJson, - ) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.trackEvent$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [event, time, category, label, value, customFieldsJson], + Future trackEvent(String event, int? time, String? category, String? label, int? value, String? customFieldsJson) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.trackEvent$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([event, time, category, label, value, customFieldsJson]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -492,17 +471,13 @@ class PersonalizationHostApi { } Future setProfile(ProfileParamsWire params) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.setProfile$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [params], + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.setProfile$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([params]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -522,17 +497,13 @@ class PersonalizationHostApi { /// [paramsJson] is a JSON object string with optional filter parameters. /// Dart layer parses the result into [RecommendationResponse]. Future getRecommendation(String code, String? paramsJson) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getRecommendation$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [code, paramsJson], + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getRecommendation$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([code, paramsJson]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -555,14 +526,12 @@ class PersonalizationHostApi { /// Returns the current session ID from the native SDK. Future getSid() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getSid$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getSid$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -586,14 +555,12 @@ class PersonalizationHostApi { /// Returns the device ID assigned by the native SDK, or null before first sync. Future getDid() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getDid$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getDid$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -613,17 +580,13 @@ class PersonalizationHostApi { /// Returns a single product's details as a JSON string. /// Dart layer parses the result into [Product]. Future getProductInfo(String itemId) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getProductInfo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [itemId], + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getProductInfo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([itemId]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -648,17 +611,13 @@ class PersonalizationHostApi { /// [paramsJson] is a JSON object with optional filter fields. /// Dart layer parses the result into [ProductsListResponse]. Future getProductsList(String? paramsJson) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getProductsList$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [paramsJson], + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getProductsList$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([paramsJson]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -683,14 +642,12 @@ class PersonalizationHostApi { /// No parameters — the native SDK decides what to return based on shop config. /// Dart layer parses the result into [SearchBlankResponse]. Future searchBlank() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchBlank$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchBlank$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -716,17 +673,13 @@ class PersonalizationHostApi { /// [paramsJson] may contain optional "locations" (String) and "excluded_brands" ([String]). /// Dart layer parses the result into [SearchInstantResponse]. Future searchInstant(String query, String? paramsJson) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchInstant$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [query, paramsJson], + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchInstant$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([query, paramsJson]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -751,17 +704,77 @@ class PersonalizationHostApi { /// [paramsJson] is a JSON object string with optional search parameters. /// Dart layer parses the result into [SearchFullResponse]. Future searchFull(String query, String? paramsJson) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchFull$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [query, paramsJson], + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchFull$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([query, paramsJson]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as String?)!; + } + } + + /// Joins the loyalty program (`loyalty/members/join`) and returns the + /// response envelope as a JSON string `{ "status": ..., "payload": { ... } }`. + /// The shop is identified by the SDK's configured `shop_id`; [phone] is required. + /// Dart layer parses the result into [LoyaltyJoinResponse]. + Future joinLoyalty(String phone, String? email, String? firstName, String? lastName) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.joinLoyalty$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([phone, email, firstName, lastName]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as String?)!; + } + } + + /// Returns the loyalty membership status (`loyalty/members/status`) as a JSON + /// string `{ "status": ..., "payload": { "member": ..., "level": { ... } } }`. + /// [identifier] is the member identifier (phone). + /// Dart layer parses the result into [LoyaltyStatusResponse]. + Future getLoyaltyStatus(String identifier) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getLoyaltyStatus$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([identifier]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -783,53 +796,14 @@ class PersonalizationHostApi { } /// [customJson] and [recommendedSourceJson] are JSON object strings or null. - Future trackPurchase( - String orderId, - double orderPrice, - List items, - String? deliveryType, - String? deliveryAddress, - String? paymentType, - bool isTaxFree, - String? promocode, - double? orderCash, - double? orderBonuses, - double? orderDelivery, - double? orderDiscount, - String? channel, - String? customJson, - String? recommendedSourceJson, - String? stream, - String? segment, - ) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.trackPurchase$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel - .send([ - orderId, - orderPrice, - items, - deliveryType, - deliveryAddress, - paymentType, - isTaxFree, - promocode, - orderCash, - orderBonuses, - orderDelivery, - orderDiscount, - channel, - customJson, - recommendedSourceJson, - stream, - segment, - ]); + Future trackPurchase(String orderId, double orderPrice, List items, String? deliveryType, String? deliveryAddress, String? paymentType, bool isTaxFree, String? promocode, double? orderCash, double? orderBonuses, double? orderDelivery, double? orderDiscount, String? channel, String? customJson, String? recommendedSourceJson, String? stream, String? segment) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.trackPurchase$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([orderId, orderPrice, items, deliveryType, deliveryAddress, paymentType, isTaxFree, promocode, orderCash, orderBonuses, orderDelivery, orderDiscount, channel, customJson, recommendedSourceJson, stream, segment]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -855,115 +829,79 @@ abstract class PersonalizationFlutterApi { void onPushClicked(Map payload); - static void setUp( - PersonalizationFlutterApi? api, { - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) { - messageChannelSuffix = messageChannelSuffix.isNotEmpty - ? '.$messageChannelSuffix' - : ''; + static void setUp(PersonalizationFlutterApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel - pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert( - message != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived was null.', - ); + assert(message != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived was null.'); final List args = (message as List?)!; - final Map? arg_payload = - (args[0] as Map?)?.cast(); - assert( - arg_payload != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived was null, expected non-null Map.', - ); + final Map? arg_payload = (args[0] as Map?)?.cast(); + assert(arg_payload != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived was null, expected non-null Map.'); try { api.onPushReceived(arg_payload!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel - pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert( - message != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered was null.', - ); + assert(message != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered was null.'); final List args = (message as List?)!; - final Map? arg_payload = - (args[0] as Map?)?.cast(); - assert( - arg_payload != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered was null, expected non-null Map.', - ); + final Map? arg_payload = (args[0] as Map?)?.cast(); + assert(arg_payload != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered was null, expected non-null Map.'); try { api.onPushDelivered(arg_payload!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel - pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger, - ); + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert( - message != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked was null.', - ); + assert(message != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked was null.'); final List args = (message as List?)!; - final Map? arg_payload = - (args[0] as Map?)?.cast(); - assert( - arg_payload != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked was null, expected non-null Map.', - ); + final Map? arg_payload = (args[0] as Map?)?.cast(); + assert(arg_payload != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked was null, expected non-null Map.'); try { api.onPushClicked(arg_payload!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString()), - ); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } diff --git a/pigeons/personalization_api.dart b/pigeons/personalization_api.dart index 2f71f42..4ffd05b 100644 --- a/pigeons/personalization_api.dart +++ b/pigeons/personalization_api.dart @@ -174,6 +174,25 @@ abstract class PersonalizationHostApi { @async String searchFull(String query, String? paramsJson); + /// Joins the loyalty program (`loyalty/members/join`) and returns the + /// response envelope as a JSON string `{ "status": ..., "payload": { ... } }`. + /// The shop is identified by the SDK's configured `shop_id`; [phone] is required. + /// Dart layer parses the result into [LoyaltyJoinResponse]. + @async + String joinLoyalty( + String phone, + String? email, + String? firstName, + String? lastName, + ); + + /// Returns the loyalty membership status (`loyalty/members/status`) as a JSON + /// string `{ "status": ..., "payload": { "member": ..., "level": { ... } } }`. + /// [identifier] is the member identifier (phone). + /// Dart layer parses the result into [LoyaltyStatusResponse]. + @async + String getLoyaltyStatus(String identifier); + /// [customJson] and [recommendedSourceJson] are JSON object strings or null. @async void trackPurchase( diff --git a/test/loyalty_test.dart b/test/loyalty_test.dart new file mode 100644 index 0000000..2296ff7 --- /dev/null +++ b/test/loyalty_test.dart @@ -0,0 +1,125 @@ +import 'dart:convert'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:rees46_sdk/rees46_sdk.dart'; +import 'package:rees46_sdk/src/pigeon/personalization_api.g.dart' as pigeon; + +const _joinChannel = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.joinLoyalty'; +const _statusChannel = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getLoyaltyStatus'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final messageCodec = pigeon.PersonalizationHostApi.pigeonChannelCodec; + ByteData reply(String value) => messageCodec.encodeMessage([value])!; + + void stub(String channel, String json) { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler(channel, (_) async => reply(json)); + } + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler(_joinChannel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler(_statusChannel, null); + }); + + // ------------------------------------------------------------------------- + // joinLoyalty + // ------------------------------------------------------------------------- + group('joinLoyalty', () { + test('parses status and raw payload', () async { + stub( + _joinChannel, + jsonEncode({ + 'status': 'success', + 'payload': {'member_id': 42, 'created': true}, + }), + ); + + final r = await PersonalizationSdk().joinLoyalty(phone: '79991234567'); + expect(r, isA()); + expect(r.status, equals('success')); + expect(r.payload['member_id'], equals(42)); + expect(r.payload['created'], isTrue); + }); + + test('missing payload defaults to empty map', () async { + stub(_joinChannel, jsonEncode({'status': 'success'})); + + final r = await PersonalizationSdk().joinLoyalty(phone: '79991234567'); + expect(r.status, equals('success')); + expect(r.payload, isEmpty); + }); + + test('empty phone throws ArgumentError (no native call)', () async { + expect( + () => PersonalizationSdk().joinLoyalty(phone: ''), + throwsA(isA()), + ); + }); + }); + + // ------------------------------------------------------------------------- + // getLoyaltyStatus + // ------------------------------------------------------------------------- + group('getLoyaltyStatus', () { + test('parses member and level', () async { + stub( + _statusChannel, + jsonEncode({ + 'status': 'success', + 'payload': { + 'member': true, + 'level': { + 'name': 'Gold', + 'code': 'gold', + 'expiration_date': '2026-12-31', + }, + }, + }), + ); + + final r = await PersonalizationSdk().getLoyaltyStatus('79991234567'); + expect(r, isA()); + expect(r.status, equals('success')); + expect(r.member, isTrue); + expect(r.level?.name, equals('Gold')); + expect(r.level?.code, equals('gold')); + expect(r.level?.expirationDate, equals('2026-12-31')); + }); + + test('absent level parses to null, non-member', () async { + stub( + _statusChannel, + jsonEncode({ + 'status': 'success', + 'payload': {'member': false}, + }), + ); + + final r = await PersonalizationSdk().getLoyaltyStatus('79991234567'); + expect(r.member, isFalse); + expect(r.level, isNull); + }); + + test('missing payload parses to nulls', () async { + stub(_statusChannel, jsonEncode({'status': 'success'})); + + final r = await PersonalizationSdk().getLoyaltyStatus('79991234567'); + expect(r.member, isNull); + expect(r.level, isNull); + }); + + test('empty identifier throws ArgumentError (no native call)', () async { + expect( + () => PersonalizationSdk().getLoyaltyStatus(''), + throwsA(isA()), + ); + }); + }); +} From 277469fc0d92de76ff29fbc53c2d7246528cb8ab Mon Sep 17 00:00:00 2001 From: andreykropotov Date: Tue, 23 Jun 2026 10:55:48 +0400 Subject: [PATCH 2/2] DEV-3362: dart format (loyalty_call_test + generated pigeon) --- .../integration_test/loyalty_call_test.dart | 4 +- lib/src/pigeon/personalization_api.g.dart | 472 ++++++++++++------ 2 files changed, 310 insertions(+), 166 deletions(-) diff --git a/example/integration_test/loyalty_call_test.dart b/example/integration_test/loyalty_call_test.dart index 02c914e..7f27e52 100644 --- a/example/integration_test/loyalty_call_test.dart +++ b/example/integration_test/loyalty_call_test.dart @@ -45,7 +45,9 @@ void main() { final sdk = await initSdk(); final response = await sdk.joinLoyalty(phone: phone); - debugPrint('joinLoyalty -> status=${response.status} payload=${response.payload}'); + debugPrint( + 'joinLoyalty -> status=${response.status} payload=${response.payload}', + ); expect(response.status, equals('success')); }); diff --git a/lib/src/pigeon/personalization_api.g.dart b/lib/src/pigeon/personalization_api.g.dart index c941a05..c8ad2cd 100644 --- a/lib/src/pigeon/personalization_api.g.dart +++ b/lib/src/pigeon/personalization_api.g.dart @@ -15,7 +15,11 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse({ + Object? result, + PlatformException? error, + bool empty = false, +}) { if (empty) { return []; } @@ -24,21 +28,25 @@ List wrapResponse({Object? result, PlatformException? error, bool empty } return [error.code, error.message, error.details]; } + bool _deepEquals(Object? a, Object? b) { if (a is List && b is List) { return a.length == b.length && - a.indexed - .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + a.indexed.every( + ((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]), + ); } if (a is Map && b is Map) { - return a.length == b.length && a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + return a.length == b.length && + a.entries.every( + (MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key]), + ); } return a == b; } - class InitConfig { InitConfig({ required this.shopId, @@ -81,7 +89,8 @@ class InitConfig { } Object encode() { - return _toList(); } + return _toList(); + } static InitConfig decode(Object result) { result as List; @@ -111,8 +120,7 @@ class InitConfig { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } /// Wire format for one purchase line (maps to native `PurchaseItemRequest`). @@ -139,17 +147,12 @@ class PurchaseLineItemWire { String? fashionSize; List _toList() { - return [ - id, - amount, - price, - lineId, - fashionSize, - ]; + return [id, amount, price, lineId, fashionSize]; } Object encode() { - return _toList(); } + return _toList(); + } static PurchaseLineItemWire decode(Object result) { result as List; @@ -176,8 +179,7 @@ class PurchaseLineItemWire { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } /// Wire format for profile fields sent to native SDK. @@ -275,7 +277,8 @@ class ProfileParamsWire { } Object encode() { - return _toList(); } + return _toList(); + } static ProfileParamsWire decode(Object result) { result as List; @@ -317,11 +320,9 @@ class ProfileParamsWire { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -329,13 +330,13 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is InitConfig) { + } else if (value is InitConfig) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is PurchaseLineItemWire) { + } else if (value is PurchaseLineItemWire) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is ProfileParamsWire) { + } else if (value is ProfileParamsWire) { buffer.putUint8(131); writeValue(buffer, value.encode()); } else { @@ -346,11 +347,11 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: return InitConfig.decode(readValue(buffer)!); - case 130: + case 130: return PurchaseLineItemWire.decode(readValue(buffer)!); - case 131: + case 131: return ProfileParamsWire.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -362,9 +363,13 @@ class PersonalizationHostApi { /// Constructor for [PersonalizationHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - PersonalizationHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + PersonalizationHostApi({ + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -372,13 +377,17 @@ class PersonalizationHostApi { final String pigeonVar_messageChannelSuffix; Future initialize(InitConfig config) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.initialize$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.initialize$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [config], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([config]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -395,12 +404,14 @@ class PersonalizationHostApi { } Future getPlatformVersion() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getPlatformVersion$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getPlatformVersion$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -424,12 +435,14 @@ class PersonalizationHostApi { /// Returns the push token stored by the native SDK (if any). Future getStoredPushToken() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getStoredPushToken$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getStoredPushToken$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -447,14 +460,25 @@ class PersonalizationHostApi { } /// [customFieldsJson] is JSON object string or null (maps to native custom fields map). - Future trackEvent(String event, int? time, String? category, String? label, int? value, String? customFieldsJson) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.trackEvent$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + Future trackEvent( + String event, + int? time, + String? category, + String? label, + int? value, + String? customFieldsJson, + ) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.trackEvent$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [event, time, category, label, value, customFieldsJson], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([event, time, category, label, value, customFieldsJson]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -471,13 +495,17 @@ class PersonalizationHostApi { } Future setProfile(ProfileParamsWire params) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.setProfile$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.setProfile$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [params], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([params]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -497,13 +525,17 @@ class PersonalizationHostApi { /// [paramsJson] is a JSON object string with optional filter parameters. /// Dart layer parses the result into [RecommendationResponse]. Future getRecommendation(String code, String? paramsJson) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getRecommendation$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getRecommendation$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [code, paramsJson], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([code, paramsJson]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -526,12 +558,14 @@ class PersonalizationHostApi { /// Returns the current session ID from the native SDK. Future getSid() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getSid$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getSid$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -555,12 +589,14 @@ class PersonalizationHostApi { /// Returns the device ID assigned by the native SDK, or null before first sync. Future getDid() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getDid$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getDid$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -580,13 +616,17 @@ class PersonalizationHostApi { /// Returns a single product's details as a JSON string. /// Dart layer parses the result into [Product]. Future getProductInfo(String itemId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getProductInfo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getProductInfo$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [itemId], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([itemId]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -611,13 +651,17 @@ class PersonalizationHostApi { /// [paramsJson] is a JSON object with optional filter fields. /// Dart layer parses the result into [ProductsListResponse]. Future getProductsList(String? paramsJson) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getProductsList$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getProductsList$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [paramsJson], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([paramsJson]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -642,12 +686,14 @@ class PersonalizationHostApi { /// No parameters — the native SDK decides what to return based on shop config. /// Dart layer parses the result into [SearchBlankResponse]. Future searchBlank() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchBlank$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchBlank$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -673,13 +719,17 @@ class PersonalizationHostApi { /// [paramsJson] may contain optional "locations" (String) and "excluded_brands" ([String]). /// Dart layer parses the result into [SearchInstantResponse]. Future searchInstant(String query, String? paramsJson) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchInstant$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchInstant$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [query, paramsJson], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([query, paramsJson]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -704,13 +754,17 @@ class PersonalizationHostApi { /// [paramsJson] is a JSON object string with optional search parameters. /// Dart layer parses the result into [SearchFullResponse]. Future searchFull(String query, String? paramsJson) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchFull$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.searchFull$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [query, paramsJson], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([query, paramsJson]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -735,14 +789,23 @@ class PersonalizationHostApi { /// response envelope as a JSON string `{ "status": ..., "payload": { ... } }`. /// The shop is identified by the SDK's configured `shop_id`; [phone] is required. /// Dart layer parses the result into [LoyaltyJoinResponse]. - Future joinLoyalty(String phone, String? email, String? firstName, String? lastName) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.joinLoyalty$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + Future joinLoyalty( + String phone, + String? email, + String? firstName, + String? lastName, + ) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.joinLoyalty$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [phone, email, firstName, lastName], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([phone, email, firstName, lastName]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -768,13 +831,17 @@ class PersonalizationHostApi { /// [identifier] is the member identifier (phone). /// Dart layer parses the result into [LoyaltyStatusResponse]. Future getLoyaltyStatus(String identifier) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getLoyaltyStatus$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.getLoyaltyStatus$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [identifier], ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([identifier]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -796,14 +863,53 @@ class PersonalizationHostApi { } /// [customJson] and [recommendedSourceJson] are JSON object strings or null. - Future trackPurchase(String orderId, double orderPrice, List items, String? deliveryType, String? deliveryAddress, String? paymentType, bool isTaxFree, String? promocode, double? orderCash, double? orderBonuses, double? orderDelivery, double? orderDiscount, String? channel, String? customJson, String? recommendedSourceJson, String? stream, String? segment) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.trackPurchase$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([orderId, orderPrice, items, deliveryType, deliveryAddress, paymentType, isTaxFree, promocode, orderCash, orderBonuses, orderDelivery, orderDiscount, channel, customJson, recommendedSourceJson, stream, segment]); + Future trackPurchase( + String orderId, + double orderPrice, + List items, + String? deliveryType, + String? deliveryAddress, + String? paymentType, + bool isTaxFree, + String? promocode, + double? orderCash, + double? orderBonuses, + double? orderDelivery, + double? orderDiscount, + String? channel, + String? customJson, + String? recommendedSourceJson, + String? stream, + String? segment, + ) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationHostApi.trackPurchase$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel + .send([ + orderId, + orderPrice, + items, + deliveryType, + deliveryAddress, + paymentType, + isTaxFree, + promocode, + orderCash, + orderBonuses, + orderDelivery, + orderDiscount, + channel, + customJson, + recommendedSourceJson, + stream, + segment, + ]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -829,79 +935,115 @@ abstract class PersonalizationFlutterApi { void onPushClicked(Map payload); - static void setUp(PersonalizationFlutterApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { - messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + static void setUp( + PersonalizationFlutterApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived$messageChannelSuffix', pigeonChannelCodec, - binaryMessenger: binaryMessenger); + final BasicMessageChannel + pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived was null.'); + assert( + message != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived was null.', + ); final List args = (message as List?)!; - final Map? arg_payload = (args[0] as Map?)?.cast(); - assert(arg_payload != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived was null, expected non-null Map.'); + final Map? arg_payload = + (args[0] as Map?)?.cast(); + assert( + arg_payload != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushReceived was null, expected non-null Map.', + ); try { api.onPushReceived(arg_payload!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered$messageChannelSuffix', pigeonChannelCodec, - binaryMessenger: binaryMessenger); + final BasicMessageChannel + pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered was null.'); + assert( + message != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered was null.', + ); final List args = (message as List?)!; - final Map? arg_payload = (args[0] as Map?)?.cast(); - assert(arg_payload != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered was null, expected non-null Map.'); + final Map? arg_payload = + (args[0] as Map?)?.cast(); + assert( + arg_payload != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushDelivered was null, expected non-null Map.', + ); try { api.onPushDelivered(arg_payload!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked$messageChannelSuffix', pigeonChannelCodec, - binaryMessenger: binaryMessenger); + final BasicMessageChannel + pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked was null.'); + assert( + message != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked was null.', + ); final List args = (message as List?)!; - final Map? arg_payload = (args[0] as Map?)?.cast(); - assert(arg_payload != null, - 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked was null, expected non-null Map.'); + final Map? arg_payload = + (args[0] as Map?)?.cast(); + assert( + arg_payload != null, + 'Argument for dev.flutter.pigeon.personalization_flutter_sdk.PersonalizationFlutterApi.onPushClicked was null, expected non-null Map.', + ); try { api.onPushClicked(arg_payload!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); } }); }