From cfb65298187ff4e5f6fb5dbd7205cbf1ed0a9edf Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Mon, 22 Jun 2026 21:31:33 +0530 Subject: [PATCH 01/22] Started working on referral UI --- assets/locales/en.po | 3 +++ lib/features/plans/plans.dart | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/assets/locales/en.po b/assets/locales/en.po index 9fb8d9c888..f120daaab8 100644 --- a/assets/locales/en.po +++ b/assets/locales/en.po @@ -1674,6 +1674,9 @@ msgstr "The VPN needs to be off before you can refresh your configuration. Turn msgid "configuration_message" msgstr "Your old configuration has been cleared. Turn the VPN on to automatically download the latest." +msgid "promo_referral_code" +msgstr "Promo / Referral Code" + diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index 07e5042c14..8b92f3b2fc 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -205,7 +205,7 @@ class _PlansState extends ConsumerState if (!isStoreVersion() && !isReferralApplied) ...{ AppTile( icon: AppImagePaths.star, - label: 'referral_code'.i18n, + label: 'promo_referral_code'.i18n, onPressed: () { context.pop(); showReferralCodeDialog(); @@ -246,14 +246,14 @@ class _PlansState extends ConsumerState AppImage(path: AppImagePaths.star, height: 48), SizedBox(height: defaultSize), Text( - 'referral_code'.i18n, + 'promo_referral_code'.i18n, style: textTheme.headlineSmall!.copyWith( color: context.textPrimary, ), ), SizedBox(height: 24), AppTextField( - label: 'referral_code'.i18n, + label: 'promo_referral_code'.i18n, controller: referralCodeController, inputFormatters: [UpperCaseTextFormatter()], hintText: 'XXXXXX', From fdcde4cbb23e99a2e9914859af61e85fb82de25b Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Mon, 29 Jun 2026 21:10:06 +0530 Subject: [PATCH 02/22] Working on referral v2 --- .../lantern/handler/MethodHandler.kt | 19 ++++++++++ go.mod | 8 ++-- go.sum | 14 +++---- lantern-core/core.go | 11 ++++++ lantern-core/ffi/ffi.go | 19 ++++++++++ lantern-core/mobile/mobile.go | 9 +++++ lib/core/models/referral_attach_response.dart | 37 +++++++++++++++++++ .../plans/provider/referral_notifier.dart | 14 +++++++ lib/lantern/lantern_core_service.dart | 6 +++ lib/lantern/lantern_ffi_service.dart | 18 +++++++++ lib/lantern/lantern_generated_bindings.dart | 15 ++++++++ lib/lantern/lantern_platform_service.dart | 18 +++++++++ lib/lantern/lantern_service.dart | 11 ++++++ 13 files changed, 187 insertions(+), 12 deletions(-) create mode 100644 lib/core/models/referral_attach_response.dart diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt b/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt index 4c9ca8c362..d6e721ca1d 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt @@ -82,6 +82,7 @@ enum class Methods(val method: String) { //Device RemoveDevice("removeDevice"), AttachReferralCode("attachReferralCode"), + AttachReferralCodeV2("attachReferralCodeV2"), // Ad blocking IsBlockAdsEnabled("isBlockAdsEnabled"), @@ -830,6 +831,24 @@ class MethodHandler : FlutterPlugin, } } + Methods.AttachReferralCodeV2.method -> { + scope.launch { + result.runCatching { + val code = call.arguments as String + val response = Mobile.referralAttachmentV2(code) + withContext(Dispatchers.Main) { + success(response) + } + }.onFailure { e -> + result.error( + "AttachReferralCodeV2", + e.localizedMessage ?: "Please try again", + e + ) + } + } + } + // Ad blocking Methods.SetBlockAdsEnabled.method -> { scope.handleResult(result, "set_block_ads_enabled") { diff --git a/go.mod b/go.mod index d45d8e9729..51ebad1e58 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/getlantern/lantern go 1.26.2 -// replace github.com/getlantern/radiance => ../radiance +replace github.com/getlantern/radiance => ../radiance // replace github.com/getlantern/lantern-server-provisioner => ../lantern-server-provisioner @@ -168,10 +168,10 @@ require ( github.com/getlantern/broflake v0.0.0-20260612203837-c4d1516de8dc // indirect github.com/getlantern/common v1.2.1-0.20260326210434-cb69537aaf46 // indirect github.com/getlantern/dnstt v0.0.0-20260603191204-3b860502c0ac // indirect - github.com/getlantern/domainfront v0.0.0-20260419161617-0bff0b2169f4 // indirect + github.com/getlantern/domainfront v0.0.0-20260625001429-518c0256669b // indirect github.com/getlantern/keepcurrent v0.0.0-20260616120552-f204338b01a3 // indirect - github.com/getlantern/kindling v0.0.0-20260611181428-9a360f63ad5a // indirect - github.com/getlantern/lantern-box v0.0.93 // indirect + github.com/getlantern/kindling v0.0.0-20260625002640-7cdf7184420c // indirect + github.com/getlantern/lantern-box v0.0.95 // indirect github.com/getlantern/lantern-water v0.0.0-20260520145825-958775d51395 // indirect github.com/getlantern/osversion v0.0.0-20240418205916-2e84a4a4e175 // indirect github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 // indirect diff --git a/go.sum b/go.sum index f95e1e0868..6e03227bf3 100644 --- a/go.sum +++ b/go.sum @@ -231,8 +231,8 @@ github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201 h1:oEZYEpZo28Wd github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201/go.mod h1:Y9WZUHEb+mpra02CbQ/QczLUe6f0Dezxaw5DCJlJQGo= github.com/getlantern/dnstt v0.0.0-20260603191204-3b860502c0ac h1:TMvkNgLVyIYAfu1dYrOuRPYMVY+cHEo1C0CYWhtSw2A= github.com/getlantern/dnstt v0.0.0-20260603191204-3b860502c0ac/go.mod h1:0+wlia2hljruM7rhjzBMFhve011lGRO0OxjVSOcXBoQ= -github.com/getlantern/domainfront v0.0.0-20260419161617-0bff0b2169f4 h1:/Q9FJvKPyuXfH6tfA+C+t9/AbvGWs3Yp9iqI74FYvb4= -github.com/getlantern/domainfront v0.0.0-20260419161617-0bff0b2169f4/go.mod h1:nsdIvgenGUqPKnRFjkssbfxnV/WYWyC0c/t15qGym/A= +github.com/getlantern/domainfront v0.0.0-20260625001429-518c0256669b h1:cM+Cwgg6EN279knxvHc4vd3MyNOpZk/49TLlg2F78Rk= +github.com/getlantern/domainfront v0.0.0-20260625001429-518c0256669b/go.mod h1:eOcE66YcVTxLiASh77rngh83B9PQtm4gSr7ZxWnaxZU= github.com/getlantern/errors v1.0.4 h1:i2iR1M9GKj4WuingpNqJ+XQEw6i6dnAgKAmLj6ZB3X0= github.com/getlantern/errors v1.0.4/go.mod h1:/Foq8jtSDGP8GOXzAjeslsC4Ar/3kB+UiQH+WyV4pzY= github.com/getlantern/golog v0.0.0-20230503153817-8e72de7e0a65 h1:NlQedYmPI3pRAXJb+hLVVDGqfvvXGRPV8vp7XOjKAZ0= @@ -243,10 +243,10 @@ github.com/getlantern/hidden v0.0.0-20220104173330-f221c5a24770 h1:cSrD9ryDfTV2y github.com/getlantern/hidden v0.0.0-20220104173330-f221c5a24770/go.mod h1:GOQsoDnEHl6ZmNIL+5uVo+JWRFWozMEp18Izcb++H+A= github.com/getlantern/keepcurrent v0.0.0-20260616120552-f204338b01a3 h1:YPBbuyvdWv+YvXDqADVwjxM0DyABg2x4UgLVKU9McKI= github.com/getlantern/keepcurrent v0.0.0-20260616120552-f204338b01a3/go.mod h1:ag5g9aWUw2FJcX5RVRpJ9EBQBy5yJuy2WXDouIn/m4w= -github.com/getlantern/kindling v0.0.0-20260611181428-9a360f63ad5a h1:w62TGPHwGqPDKq43pbwOkbvCofMY4Ldd5d17QBF9jnY= -github.com/getlantern/kindling v0.0.0-20260611181428-9a360f63ad5a/go.mod h1:9EDX+ZFoMVcd8M14LeE7ULn/TRuV9g5GijNUDgJyw2A= -github.com/getlantern/lantern-box v0.0.93 h1:R0c7NlT5M7fSp316KqKVw5KnDAk38xqbmQEue8Q+3pc= -github.com/getlantern/lantern-box v0.0.93/go.mod h1:yQhnLpnDY17+c/s+4WiiUhun3HtoHNj5NFb355ThKQk= +github.com/getlantern/kindling v0.0.0-20260625002640-7cdf7184420c h1:F4/LE+5zehr6AUfuJBab3iubscOOTdFZxwYldIpLgg8= +github.com/getlantern/kindling v0.0.0-20260625002640-7cdf7184420c/go.mod h1:rN6O8pIQ9nc7Gyvtf2GGSjiWEFIApGsRCHkgYz0h3Ak= +github.com/getlantern/lantern-box v0.0.95 h1:tsgjKQjWjPy3ZyCIYijT91W3XJY+8XENxb6mUogthI0= +github.com/getlantern/lantern-box v0.0.95/go.mod h1:yQhnLpnDY17+c/s+4WiiUhun3HtoHNj5NFb355ThKQk= github.com/getlantern/lantern-server-provisioner v0.0.0-20251031121934-8ea031fccfa9 h1:6seyD2f9tz2am0YQd/Qn+q7LFiiQgnmxgwWFnVceGZw= github.com/getlantern/lantern-server-provisioner v0.0.0-20251031121934-8ea031fccfa9/go.mod h1:s0VKrlJf/z+M0U8IKHFL2hfuflocRw3SINmMacrTlMA= github.com/getlantern/lantern-water v0.0.0-20260520145825-958775d51395 h1:grfGavAUp2E9w9ZoJuM3FyWyQ0sCJ64V4ZMKtZKRqTc= @@ -259,8 +259,6 @@ github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 h1:rtDmW8YL github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535/go.mod h1:WKJEdjMOD4IuTRYwjQHjT4bmqDl5J82RShMLxPAvi0Q= github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b h1:gMYJzEhLrmIqQ+JnjiYNm+UyUDalK3WUmVyecFwmV5g= github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b/go.mod h1:NpfXdK4ldEKkjQ4P1R+DBF4ua5VFOlxmgHROTnYrApg= -github.com/getlantern/radiance v0.0.0-20260617195940-99d3ff55fef1 h1:hs8vJYiizCTCPM8n6slL89vAVY/GAER6evgPwsrqzOg= -github.com/getlantern/radiance v0.0.0-20260617195940-99d3ff55fef1/go.mod h1:S4D2odUJoqxrEosHh6UUwFoUoY4PK55zluLUQgETFOY= github.com/getlantern/samizdat v0.0.3-0.20260529191731-5ea8ae61ddbf h1:KxiMF+oG0rTtuBi7GiIaHfccYOf69rLJ/VnO5myoYc4= github.com/getlantern/samizdat v0.0.3-0.20260529191731-5ea8ae61ddbf/go.mod h1:uEeykQSW2/6rTjfPlj3MTTo59poSHXfAHTGgzYDkbr0= github.com/getlantern/semconv v0.0.0-20260327040646-21845dda05cb h1:c5YM7b3a4r2J8Eh89KkI6M/iTFe6Bi+b8AJlfkKdFq4= diff --git a/lantern-core/core.go b/lantern-core/core.go index af51e29993..0cdcfd0a7b 100644 --- a/lantern-core/core.go +++ b/lantern-core/core.go @@ -75,6 +75,7 @@ type App interface { UpdateConfig() error ClearTunnelCache() error ReferralAttachment(referralCode string) (bool, error) + ReferralAttachmentV2(referralCode string) ([]byte, error) UpdateLocale(locale string) error UpdateTelemetryConsent(consent bool) error IsTelemetryEnabled() bool @@ -858,6 +859,16 @@ func (lc *LanternCore) ReferralAttachment(referralCode string) (bool, error) { return lc.client.ReferralAttach(lc.ctx, referralCode) } +// ReferralAttachmentV2 attaches a referral code and returns the resulting +// plans, providers, code, and discount marshalled as JSON. +func (lc *LanternCore) ReferralAttachmentV2(referralCode string) ([]byte, error) { + resp, err := lc.client.ReferralAttachV2(lc.ctx, referralCode) + if err != nil { + return nil, err + } + return json.Marshal(resp) +} + ///////////////// // Payments // ///////////////// diff --git a/lantern-core/ffi/ffi.go b/lantern-core/ffi/ffi.go index f1504d83c9..3990b725c7 100644 --- a/lantern-core/ffi/ffi.go +++ b/lantern-core/ffi/ffi.go @@ -892,6 +892,25 @@ func referralAttachment(_referralCode *C.char) *C.char { }) } +// referralAttachmentV2 attaches a referral code to the user's account and +// returns the resulting plans, providers, code, and discount as JSON. +// +//export referralAttachmentV2 +func referralAttachmentV2(_referralCode *C.char) *C.char { + referralCode := C.GoString(_referralCode) + return runOnGoStack(func() *C.char { + c, errStr := requireCore() + if errStr != nil { + return errStr + } + bytes, err := c.ReferralAttachmentV2(referralCode) + if err != nil { + return SendError(err) + } + return C.CString(string(bytes)) + }) +} + // startChangeEmail initiates the process of changing the user's email address. // //export startChangeEmail diff --git a/lantern-core/mobile/mobile.go b/lantern-core/mobile/mobile.go index 11e82681f3..66b081b9c1 100644 --- a/lantern-core/mobile/mobile.go +++ b/lantern-core/mobile/mobile.go @@ -689,6 +689,15 @@ func ReferralAttachment(referralCode string) error { }) } +// ReferralAttachmentV2 attaches a referral code and returns the resulting +// plans, providers, code, and discount as a JSON string. +func ReferralAttachmentV2(referralCode string) (string, error) { + return withCoreR(func(c lanterncore.Core) (string, error) { + b, err := c.ReferralAttachmentV2(referralCode) + return string(b), err + }) +} + func DeleteAccount(email, password string) (string, error) { return withCoreR(func(c lanterncore.Core) (string, error) { b, err := c.DeleteAccount(email, password) diff --git a/lib/core/models/referral_attach_response.dart b/lib/core/models/referral_attach_response.dart new file mode 100644 index 0000000000..cdafceb306 --- /dev/null +++ b/lib/core/models/referral_attach_response.dart @@ -0,0 +1,37 @@ +import 'package:lantern/core/models/plan_data.dart'; + +/// Response returned by the referral-attach V2 endpoint. In addition to +/// attaching the referral code it returns the resulting discounted plans, +/// payment providers, and the applied discount percentage. +class ReferralAttachV2Response { + final Providers? providers; + final List plans; + final String code; + final int discountPct; + + ReferralAttachV2Response({ + required this.providers, + required this.plans, + required this.code, + required this.discountPct, + }); + + factory ReferralAttachV2Response.fromJson(Map json) => + ReferralAttachV2Response( + providers: json["providers"] == null + ? null + : Providers.fromJson(json["providers"]), + plans: json["plans"] == null + ? [] + : List.from(json["plans"].map((x) => Plan.fromJson(x))), + code: json["code"] ?? '', + discountPct: json["discountPct"] ?? 0, + ); + + Map toJson() => { + "providers": providers?.toJson(), + "plans": List.from(plans.map((x) => x.toJson())), + "code": code, + "discountPct": discountPct, + }; +} diff --git a/lib/features/plans/provider/referral_notifier.dart b/lib/features/plans/provider/referral_notifier.dart index d5c00a4d37..2cc592df4e 100644 --- a/lib/features/plans/provider/referral_notifier.dart +++ b/lib/features/plans/provider/referral_notifier.dart @@ -1,5 +1,6 @@ import 'package:fpdart/fpdart.dart'; import 'package:lantern/core/common/common.dart'; +import 'package:lantern/core/models/referral_attach_response.dart'; import 'package:lantern/lantern/lantern_service_notifier.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -21,6 +22,19 @@ class ReferralNotifier extends _$ReferralNotifier { return result; } + /// Applies a referral code via the V2 endpoint, returning the resulting + /// discounted plans, providers and discount percentage. + Future> applyReferralCodeV2( + String code, + ) async { + final result = + await ref.read(lanternServiceProvider).attachReferralCodeV2(code); + if (result.isRight()) { + state = true; + } + return result; + } + void resetReferral() { state = false; } diff --git a/lib/lantern/lantern_core_service.dart b/lib/lantern/lantern_core_service.dart index d1ea462e2e..722323236b 100644 --- a/lib/lantern/lantern_core_service.dart +++ b/lib/lantern/lantern_core_service.dart @@ -10,6 +10,7 @@ import 'package:lantern/core/models/datacap_info.dart'; import 'package:lantern/core/models/lantern_status.dart'; import 'package:lantern/core/models/macos_extension_state.dart'; import 'package:lantern/core/models/plan_data.dart'; +import 'package:lantern/core/models/referral_attach_response.dart'; import 'package:lantern/core/models/restore_subscription_response.dart'; import 'package:lantern/core/models/private_server_status.dart'; import 'package:lantern/features/report_issue/models/report_issue_attachment.dart'; @@ -244,6 +245,11 @@ abstract class LanternCoreService { //Referral attachment Future> attachReferralCode(String code); + //Referral attachment V2 — returns discounted plans, providers and discount + Future> attachReferralCodeV2( + String code, + ); + /// Private server methods Future> digitalOceanPrivateServer(); diff --git a/lib/lantern/lantern_ffi_service.dart b/lib/lantern/lantern_ffi_service.dart index 5e55ca13b4..b1092b6049 100644 --- a/lib/lantern/lantern_ffi_service.dart +++ b/lib/lantern/lantern_ffi_service.dart @@ -29,6 +29,7 @@ import '../core/models/available_servers.dart'; import '../core/models/server_location.dart'; import '../core/models/macos_extension_state.dart'; import '../core/models/plan_data.dart'; +import '../core/models/referral_attach_response.dart'; import '../core/utils/compute_worker.dart'; export 'dart:convert'; @@ -1430,6 +1431,23 @@ class LanternFFIService implements LanternCoreService { } } + @override + Future> attachReferralCodeV2( + String code, + ) async { + try { + final result = await runInBackground(() async { + return _ffiService.referralAttachmentV2(code.toCharPtr).toDartString(); + }); + checkAPIError(result); + final response = ReferralAttachV2Response.fromJson(jsonDecode(result)); + return Right(response); + } catch (e, stackTrace) { + appLogger.error('Error attaching referral code v2', e, stackTrace); + return Left(e.toFailure()); + } + } + @override Future> completeChangeEmail({ required String newEmail, diff --git a/lib/lantern/lantern_generated_bindings.dart b/lib/lantern/lantern_generated_bindings.dart index bbbefe0732..d908c7f280 100644 --- a/lib/lantern/lantern_generated_bindings.dart +++ b/lib/lantern/lantern_generated_bindings.dart @@ -5940,6 +5940,21 @@ class LanternBindings { late final _referralAttachment = _referralAttachmentPtr .asFunction Function(ffi.Pointer)>(); + ffi.Pointer referralAttachmentV2( + ffi.Pointer _referralCode, + ) { + return _referralAttachmentV2(_referralCode); + } + + late final _referralAttachmentV2Ptr = + _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer) + > + >('referralAttachmentV2'); + late final _referralAttachmentV2 = _referralAttachmentV2Ptr + .asFunction Function(ffi.Pointer)>(); + ffi.Pointer startChangeEmail( ffi.Pointer _newEmail, ffi.Pointer _password, diff --git a/lib/lantern/lantern_platform_service.dart b/lib/lantern/lantern_platform_service.dart index 6b49b8c6c4..42c6650fc0 100644 --- a/lib/lantern/lantern_platform_service.dart +++ b/lib/lantern/lantern_platform_service.dart @@ -12,6 +12,7 @@ import 'package:lantern/core/models/available_servers.dart'; import 'package:lantern/core/models/datacap_info.dart'; import 'package:lantern/core/models/macos_extension_state.dart'; import 'package:lantern/core/models/plan_data.dart'; +import 'package:lantern/core/models/referral_attach_response.dart'; import 'package:lantern/core/models/restore_subscription_response.dart'; import 'package:lantern/core/models/private_server_status.dart'; import 'package:lantern/core/models/server_location.dart'; @@ -1530,6 +1531,23 @@ class LanternPlatformService implements LanternCoreService { } } + @override + Future> attachReferralCodeV2( + String code, + ) async { + try { + final result = await _methodChannel.invokeMethod( + 'attachReferralCodeV2', + code, + ); + final response = ReferralAttachV2Response.fromJson(jsonDecode(result!)); + return right(response); + } catch (e, stackTrace) { + appLogger.error('Error attaching referral code v2', e, stackTrace); + return Left(e.toFailure()); + } + } + EnabledAppsSnapshot _enabledApps = const EnabledAppsSnapshot.empty(); EnabledAppsSnapshot get enabledAppsSnapshot => _enabledApps; diff --git a/lib/lantern/lantern_service.dart b/lib/lantern/lantern_service.dart index 26bc4adfdb..25a654cddb 100644 --- a/lib/lantern/lantern_service.dart +++ b/lib/lantern/lantern_service.dart @@ -8,6 +8,7 @@ import 'package:lantern/core/models/lantern_status.dart'; import 'package:lantern/core/models/server_location.dart'; import 'package:lantern/core/models/macos_extension_state.dart'; import 'package:lantern/core/models/plan_data.dart'; +import 'package:lantern/core/models/referral_attach_response.dart'; import 'package:lantern/core/models/restore_subscription_response.dart'; import 'package:lantern/core/models/private_server_status.dart'; import 'package:lantern/core/services/app_purchase.dart'; @@ -834,6 +835,16 @@ class LanternService implements LanternCoreService { return _platformService.attachReferralCode(code); } + @override + Future> attachReferralCodeV2( + String code, + ) { + if (PlatformUtils.isFFISupported) { + return _ffiService.attachReferralCodeV2(code); + } + return _platformService.attachReferralCodeV2(code); + } + @override Future>> getSplitTunnelItems( SplitTunnelFilterType type, From 7aa671a89e73699667a0847142e1a262a79a0734 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Tue, 30 Jun 2026 17:46:45 +0530 Subject: [PATCH 03/22] connect endpoint --- assets/locales/en.po | 3 + lib/core/common/app_semantic_colors.dart | 9 +++ lib/core/extensions/error.dart | 3 +- lib/core/extensions/plan.dart | 11 ++++ lib/core/models/plan_data.dart | 24 +++++++ lib/core/models/referral_attach_response.dart | 41 +++++++----- lib/features/plans/feature_list.dart | 41 ++++++------ lib/features/plans/plan_item.dart | 51 +++++++++------ lib/features/plans/plans.dart | 65 +++++++++++++++++-- lib/features/plans/plans_list.dart | 28 +++++--- .../plans/provider/plans_notifier.dart | 16 +++++ .../plans/provider/referral_notifier.dart | 21 +++++- lib/lantern/lantern_ffi_service.dart | 17 +---- lib/lantern/lantern_platform_service.dart | 23 +------ macos/Runner/Handlers/MethodHandler.swift | 20 ++++++ 15 files changed, 260 insertions(+), 113 deletions(-) diff --git a/assets/locales/en.po b/assets/locales/en.po index f120daaab8..77205cc218 100644 --- a/assets/locales/en.po +++ b/assets/locales/en.po @@ -1245,6 +1245,9 @@ msgstr "Server alias cannot be empty" msgid "referral_code_applied" msgstr "Your referral code has been applied!" +msgid "discount_applied" +msgstr "%s discount applied" + msgid "referral_message_1m" msgstr "+ 15 additional days free" diff --git a/lib/core/common/app_semantic_colors.dart b/lib/core/common/app_semantic_colors.dart index 06746aeaec..059613d1b9 100644 --- a/lib/core/common/app_semantic_colors.dart +++ b/lib/core/common/app_semantic_colors.dart @@ -135,6 +135,15 @@ extension AppSemanticColors on BuildContext { Color get statusWarningBorderDot => _isDark ? AppColors.yellow4 : AppColors.yellow2; + /// text.attention Yellow.1000 — vivid attention tag, same in both themes. + Color get textAttention => AppColors.yellow10; + + /// bg.attention Yellow.300 — vivid attention tag, same in both themes. + Color get bgAttention => AppColors.yellow3; + + /// border.attention Yellow.400 — vivid attention tag, same in both themes. + Color get borderAttention => AppColors.yellow4; + /// status.success-bg-dot Green.600 light / Green.700 dark Color get statusSuccessBgDot => _isDark ? AppColors.green7 : AppColors.green6; diff --git a/lib/core/extensions/error.dart b/lib/core/extensions/error.dart index ba40f2ce3f..6830e7309c 100644 --- a/lib/core/extensions/error.dart +++ b/lib/core/extensions/error.dart @@ -72,7 +72,8 @@ extension ErrorExetension on Object { if (description.contains("error restoring purchase")) { return "purchase_restored_error".i18n; } - if (description.contains('Invalid referral')) { + if (description.contains('Invalid referral') || + description.contains('invalid-referral-code')) { return "referral_code_invalid".i18n; } if (description.contains('Cannot use your own code for promotion')) { diff --git a/lib/core/extensions/plan.dart b/lib/core/extensions/plan.dart index 05da9113fc..9205711986 100644 --- a/lib/core/extensions/plan.dart +++ b/lib/core/extensions/plan.dart @@ -19,6 +19,17 @@ extension PlanExtension on Plan { expectedMonthlyPrice.keys.first); } + /// The original (pre-discount) yearly price, reconstructed from the current + /// (discounted) price and [discountPct]. Used for the affiliate strikethrough + /// price shown next to the discounted price. + String formattedYearlyOriginalPrice(int discountPct) { + final discounted = double.parse(price.values.first.toString()); + final original = (discountPct > 0 && discountPct < 100) + ? discounted / (1 - discountPct / 100) + : discounted; + return CurrencyUtils.formatCurrency(original, price.keys.first); + } + String getDurationText() { final durationMap = { '1y': 'year', diff --git a/lib/core/models/plan_data.dart b/lib/core/models/plan_data.dart index ae9f863e60..655962be84 100644 --- a/lib/core/models/plan_data.dart +++ b/lib/core/models/plan_data.dart @@ -1,3 +1,5 @@ +import 'package:lantern/core/common/common.dart'; + class PlansData { Providers providers; List plans; @@ -7,6 +9,28 @@ class PlansData { required this.plans, }); + /// Sorts plans (best-value first, then by descending price) and orders the + /// platform's payment providers so subscription-capable ones come first. + /// Applied after fetching/attaching plans (including referral V2). + void sortPlansAndProviders() { + plans.sort((a, b) { + if (a.bestValue != b.bestValue) { + return a.bestValue ? -1 : 1; + } + // Then: sort by usdPrice descending + return b.usdPrice.compareTo(a.usdPrice); + }); + + int bySubscription(Android a, Android b) => + (b.providers.supportSubscription ? 1 : 0) - + (a.providers.supportSubscription ? 1 : 0); + if (PlatformUtils.isMobile) { + providers.android.sort(bySubscription); + } else { + providers.desktop.sort(bySubscription); + } + } + factory PlansData.fromJson(Map json) => PlansData( providers: Providers.fromJson(json["providers"]), plans: List.from(json["plans"].map((x) => Plan.fromJson(x))), diff --git a/lib/core/models/referral_attach_response.dart b/lib/core/models/referral_attach_response.dart index cdafceb306..ff4d319b18 100644 --- a/lib/core/models/referral_attach_response.dart +++ b/lib/core/models/referral_attach_response.dart @@ -1,37 +1,42 @@ import 'package:lantern/core/models/plan_data.dart'; -/// Response returned by the referral-attach V2 endpoint. In addition to -/// attaching the referral code it returns the resulting discounted plans, -/// payment providers, and the applied discount percentage. +/// The kind of code applied via the referral-attach V2 endpoint. +class ReferralType { + static const affiliate = 'affiliate'; + static const referral = 'referral'; +} + class ReferralAttachV2Response { - final Providers? providers; - final List plans; + final PlansData plansData; final String code; final int discountPct; + /// Either [ReferralType.affiliate] or [ReferralType.referral]. + final String type; + ReferralAttachV2Response({ - required this.providers, - required this.plans, + required this.plansData, required this.code, required this.discountPct, + required this.type, }); + /// Affiliate codes apply a discount to the plans (strikethrough pricing UI); + /// referral codes keep the existing per-plan bonus message UI. + bool get isAffiliate => type == ReferralType.affiliate; + factory ReferralAttachV2Response.fromJson(Map json) => ReferralAttachV2Response( - providers: json["providers"] == null - ? null - : Providers.fromJson(json["providers"]), - plans: json["plans"] == null - ? [] - : List.from(json["plans"].map((x) => Plan.fromJson(x))), + plansData: PlansData.fromJson(json)..sortPlansAndProviders(), code: json["code"] ?? '', discountPct: json["discountPct"] ?? 0, + type: json["type"] ?? ReferralType.affiliate, ); Map toJson() => { - "providers": providers?.toJson(), - "plans": List.from(plans.map((x) => x.toJson())), - "code": code, - "discountPct": discountPct, - }; + ...plansData.toJson(), + "code": code, + "discountPct": discountPct, + "type": type, + }; } diff --git a/lib/features/plans/feature_list.dart b/lib/features/plans/feature_list.dart index 199d4491ed..3649859ffc 100644 --- a/lib/features/plans/feature_list.dart +++ b/lib/features/plans/feature_list.dart @@ -11,27 +11,34 @@ class FeatureList extends StatelessWidget { return Column( mainAxisSize: MainAxisSize.min, children: [ - SizedBox(height: size24), _FeatureTile( - image: AppImagePaths.location, - title: 'select_your_server_location'.i18n), + image: AppImagePaths.location, + title: 'select_your_server_location'.i18n, + ), _FeatureTile( - image: AppImagePaths.blot, - title: 'faster_speeds_unlimited_bandwidth'.i18n), + image: AppImagePaths.blot, + title: 'faster_speeds_unlimited_bandwidth'.i18n, + ), _FeatureTile( - image: AppImagePaths.premium, - title: 'premium_servers_less_congestion'.i18n), + image: AppImagePaths.premium, + title: 'premium_servers_less_congestion'.i18n, + ), _FeatureTile( - image: AppImagePaths.eyeHide, - title: 'advanced_anti_censorship'.i18n), + image: AppImagePaths.eyeHide, + title: 'advanced_anti_censorship'.i18n, + ), _FeatureTile( - image: AppImagePaths.roundCorrect, - title: 'exclusive_access_new_features'.i18n), + image: AppImagePaths.roundCorrect, + title: 'exclusive_access_new_features'.i18n, + ), _FeatureTile( - image: AppImagePaths.connectDevice, - title: 'connect_up_to_five_devices'.i18n), + image: AppImagePaths.connectDevice, + title: 'connect_up_to_five_devices'.i18n, + ), _FeatureTile( - image: AppImagePaths.adBlock, title: 'built_in_ad_blocking'.i18n), + image: AppImagePaths.adBlock, + title: 'built_in_ad_blocking'.i18n, + ), ], ); } @@ -51,11 +58,7 @@ class _FeatureTile extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( children: [ - AppImage( - path: image, - color: context.textPrimary, - height: 24, - ), + AppImage(path: image, color: context.textPrimary, height: 24), SizedBox(width: defaultSize), Expanded( child: AutoSizeText( diff --git a/lib/features/plans/plan_item.dart b/lib/features/plans/plan_item.dart index 56d4a1da44..53d19000ee 100644 --- a/lib/features/plans/plan_item.dart +++ b/lib/features/plans/plan_item.dart @@ -11,12 +11,17 @@ class PlanItem extends StatelessWidget { final Function(Plan plans) onPressed; final String referralMessage; + /// When > 0, an affiliate discount is applied: the (discounted) price is + /// shown alongside the strikethrough original price. + final int discountPct; + const PlanItem({ super.key, required this.plan, required this.planSelected, required this.onPressed, this.referralMessage = '', + this.discountPct = 0, }); @override @@ -26,31 +31,23 @@ class PlanItem extends StatelessWidget { final finalSize = (width * 0.5) - (defaultSize * 3); return badges.Badge( showBadge: plan.bestValue ?? false, - badgeAnimation: badges.BadgeAnimation.scale( - toAnimate: false, - ), + badgeAnimation: badges.BadgeAnimation.scale(toAnimate: false), position: badges.BadgePosition.custom(start: (finalSize - 10)), // Adjust values as needed badgeStyle: badges.BadgeStyle( shape: badges.BadgeShape.square, - borderSide: BorderSide( - color: context.statusWarningText, - width: 1, - ), + borderSide: BorderSide(color: context.statusWarningText, width: 1), borderRadius: BorderRadius.circular(16), badgeColor: context.statusWarningBgDot, padding: EdgeInsets.symmetric(horizontal: 10, vertical: 6), ), - badgeContent: Text( - 'best_value'.i18n, - style: textTheme.labelMedium!, - ), + badgeContent: Text('best_value'.i18n, style: textTheme.labelMedium!), child: GestureDetector( onTap: () { onPressed.call(plan); }, child: AnimatedContainer( - margin: EdgeInsets.only(top: 16), + margin: EdgeInsets.only(top: 14), padding: EdgeInsets.symmetric(horizontal: defaultSize, vertical: 12), duration: Duration(milliseconds: 300), decoration: getPlanDecoration(planSelected, context), @@ -66,21 +63,33 @@ class PlanItem extends StatelessWidget { ), ), if (referralMessage.isNotEmpty) - Text( - referralMessage, - style: textTheme.labelMedium, - ), + Text(referralMessage, style: textTheme.labelMedium), ], ), Spacer(), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ - Text( - plan.formattedYearlyPrice, - style: textTheme.titleMedium!.copyWith( - color: context.textLink, - ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + plan.formattedYearlyPrice, + style: textTheme.titleMedium!.copyWith( + color: context.textLink, + ), + ), + if (discountPct > 0) ...[ + SizedBox(width: 6), + Text( + plan.formattedYearlyOriginalPrice(discountPct), + style: textTheme.labelMedium!.copyWith( + color: context.textTertiary, + decoration: TextDecoration.lineThrough, + ), + ), + ], + ], ), Text( '${plan.formattedMonthlyPrice}/month', diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index 8b92f3b2fc..b24fc5642d 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -9,7 +9,6 @@ import 'package:lantern/core/common/common.dart'; import 'package:lantern/core/services/app_purchase.dart'; import 'package:lantern/core/services/injection_container.dart'; import 'package:lantern/core/utils/country_code.dart'; -import 'package:lantern/core/utils/formatter.dart'; import 'package:lantern/core/utils/screen_utils.dart'; import 'package:lantern/core/widgets/app_rich_text.dart'; import 'package:lantern/core/widgets/loading_indicator.dart'; @@ -73,7 +72,7 @@ class _PlansState extends ConsumerState child: SizedBox( height: context.isSmallDevice ? size.height * 0.4 - : size.height * 0.37, + : size.height * 0.36, child: SingleChildScrollView(child: FeatureList()), ), ), @@ -90,6 +89,7 @@ class _PlansState extends ConsumerState crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SizedBox(height: 10), + _buildAffiliateBanner(), Padding( padding: EdgeInsets.only( left: context.isSmallDevice ? 16 : 0, @@ -190,6 +190,64 @@ class _PlansState extends ConsumerState ); } + /// Banner shown above the plans list when an affiliate code is applied: + /// the discount percentage plus a removable chip with the applied code. + /// Referral (non-affiliate) codes keep the existing per-plan bonus message. + Widget _buildAffiliateBanner() { + final referralApplied = ref.watch(referralProvider); + final response = ref.read(referralProvider.notifier).appliedReferral; + if (!referralApplied || response == null || !response.isAffiliate) { + return const SizedBox.shrink(); + } + return Padding( + padding: EdgeInsets.only(left: context.isSmallDevice ? 16 : 0, bottom: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: defaultSize), + child: Text( + 'discount_applied'.i18n.fill(['${response.discountPct}%']), + style: textTheme.labelMedium!.copyWith( + color: context.textSecondary, + ), + ), + ), + SizedBox(height: 6), + Align( + alignment: Alignment.centerLeft, + child: Chip( + label: Text(response.code), + labelStyle: textTheme.titleSmall!.copyWith( + color: context.textAttention, + ), + backgroundColor: context.bgAttention, + deleteIcon: Icon( + Icons.close, + size: 18, + color: context.textAttention, + ), + onDeleted: _onRemoveAffiliateCode, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24), + side: BorderSide(color: context.borderAttention), + ), + ), + ), + ], + ), + ); + } + + /// Removes the applied affiliate code: clears the referral state and + /// re-fetches the original (non-discounted) plans. + void _onRemoveAffiliateCode() { + appLogger.info('Removing applied affiliate code'); + ref.read(referralProvider.notifier).resetReferral(); + ref.read(plansProvider.notifier).fetchPlans(); + } + void onMenuTap() { final isReferralApplied = ref.read(referralProvider); showAppBottomSheet( @@ -255,7 +313,6 @@ class _PlansState extends ConsumerState AppTextField( label: 'promo_referral_code'.i18n, controller: referralCodeController, - inputFormatters: [UpperCaseTextFormatter()], hintText: 'XXXXXX', prefixIcon: AppImagePaths.star, ), @@ -289,7 +346,7 @@ class _PlansState extends ConsumerState context.showLoadingDialog(); final result = await ref .read(referralProvider.notifier) - .applyReferralCode(code); + .applyReferralCodeV2(code); result.fold( (error) { diff --git a/lib/features/plans/plans_list.dart b/lib/features/plans/plans_list.dart index b2b0e67f23..ee144c6e9c 100644 --- a/lib/features/plans/plans_list.dart +++ b/lib/features/plans/plans_list.dart @@ -11,34 +11,42 @@ import 'package:lantern/features/plans/provider/referral_notifier.dart'; class PlansListView extends HookConsumerWidget { final PlansData data; - const PlansListView({ - super.key, - required this.data, - }); + const PlansListView({super.key, required this.data}); @override Widget build(BuildContext context, WidgetRef ref) { final referralEnable = ref.watch(referralProvider); + final referralNotifier = ref.read(referralProvider.notifier); + final isAffiliate = referralNotifier.isAffiliateApplied; + final discountPct = referralNotifier.appliedReferral?.discountPct ?? 0; final size = MediaQuery.of(context).size; final plan = useState( - data.plans.firstWhere((Plan plan) => plan.bestValue == true)); + data.plans.firstWhere((Plan plan) => plan.bestValue == true), + ); ref.read(plansProvider.notifier).setSelectedPlan(plan.value); return SizedBox( height: context.isSmallDevice ? size.height * 0.21 : null, child: ListView.builder( shrinkWrap: true, itemCount: data.plans.length, - scrollDirection: - context.isSmallDevice ? Axis.horizontal : Axis.vertical, + scrollDirection: context.isSmallDevice + ? Axis.horizontal + : Axis.vertical, padding: EdgeInsets.zero, - physics: - context.isSmallDevice ? null : const NeverScrollableScrollPhysics(), + physics: context.isSmallDevice + ? null + : const NeverScrollableScrollPhysics(), itemBuilder: (context, index) { final item = data.plans[index]; return PlanItem( plan: item, planSelected: plan.value.id == item.id, - referralMessage: referralEnable ? getReferralMessage(item.id) : '', + // Affiliate codes show strikethrough discount pricing instead of + // the per-plan referral bonus message. + referralMessage: (referralEnable && !isAffiliate) + ? getReferralMessage(item.id) + : '', + discountPct: isAffiliate ? discountPct : 0, onPressed: (plans) { plan.value = plans; ref.read(plansProvider.notifier).setSelectedPlan(plans); diff --git a/lib/features/plans/provider/plans_notifier.dart b/lib/features/plans/provider/plans_notifier.dart index c341bab680..0693291f60 100644 --- a/lib/features/plans/provider/plans_notifier.dart +++ b/lib/features/plans/provider/plans_notifier.dart @@ -78,6 +78,11 @@ class PlansNotifier extends _$PlansNotifier { '[PlansNotifier] Plans fetched successfully: ${remote.plans.length} plans', ); unawaited(_storage.savePlans(remote)); + // Publish the result so standalone callers (the "Try again" button and + // removing an affiliate code) leave the loading state. When invoked + // from build() the framework also assigns the returned value — this + // extra set is harmless and redundant there. + state = AsyncData(remote); return remote; }, ); @@ -92,6 +97,17 @@ class PlansNotifier extends _$PlansNotifier { state = AsyncData(remotePlans); } + /// Replaces the current plans with [plans] — e.g. the discounted plans + /// returned after applying a referral code. Kept in-memory only: the + /// discount is session-specific and must not overwrite the cached base + /// plans used by non-referral sessions. + void updatePlans(PlansData plans) { + appLogger.info( + '[PlansNotifier] updatePlans: ${plans.plans.length} plans (referral)', + ); + state = AsyncData(plans); + } + void setSelectedPlan(Plan plan) { userSelectedPlan = plan; } diff --git a/lib/features/plans/provider/referral_notifier.dart b/lib/features/plans/provider/referral_notifier.dart index 2cc592df4e..d938d70485 100644 --- a/lib/features/plans/provider/referral_notifier.dart +++ b/lib/features/plans/provider/referral_notifier.dart @@ -1,6 +1,7 @@ import 'package:fpdart/fpdart.dart'; import 'package:lantern/core/common/common.dart'; import 'package:lantern/core/models/referral_attach_response.dart'; +import 'package:lantern/features/plans/provider/plans_notifier.dart'; import 'package:lantern/lantern/lantern_service_notifier.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -8,6 +9,15 @@ part 'referral_notifier.g.dart'; @Riverpod(keepAlive: true) class ReferralNotifier extends _$ReferralNotifier { + /// The applied V2 referral/affiliate response, populated on a successful + /// [applyReferralCodeV2]. Drives the affiliate discount UI (code chip, + /// "X% discount applied" banner, strikethrough pricing). Null when no V2 + /// code is applied. The `state` bool remains the simple applied/not flag + /// that existing widgets watch. + ReferralAttachV2Response? appliedReferral; + + bool get isAffiliateApplied => appliedReferral?.isAffiliate ?? false; + @override bool build() { return false; @@ -22,20 +32,27 @@ class ReferralNotifier extends _$ReferralNotifier { return result; } - /// Applies a referral code via the V2 endpoint, returning the resulting - /// discounted plans, providers and discount percentage. + /// Applies a referral code via the V2 endpoint. On success the returned + /// discounted plans are pushed into [plansProvider] so the plans UI updates + /// immediately, and the resulting response is returned to the caller. Future> applyReferralCodeV2( String code, ) async { final result = await ref.read(lanternServiceProvider).attachReferralCodeV2(code); if (result.isRight()) { + final response = result.getRight().toNullable(); + appliedReferral = response; state = true; + if (response != null) { + ref.read(plansProvider.notifier).updatePlans(response.plansData); + } } return result; } void resetReferral() { + appliedReferral = null; state = false; } } diff --git a/lib/lantern/lantern_ffi_service.dart b/lib/lantern/lantern_ffi_service.dart index b1092b6049..e07a8f968f 100644 --- a/lib/lantern/lantern_ffi_service.dart +++ b/lib/lantern/lantern_ffi_service.dart @@ -808,22 +808,7 @@ class LanternFFIService implements LanternCoreService { }); checkAPIError(result); final map = jsonDecode(result); - final plans = PlansData.fromJson(map); - - // Sort plans - plans.plans.sort((a, b) { - if (a.bestValue != b.bestValue) { - return a.bestValue ? -1 : 1; - } - // Then: sort by usdPrice descending - return b.usdPrice.compareTo(a.usdPrice); - }); - - plans.providers.desktop.sort((a, b) { - return (b.providers.supportSubscription ? 1 : 0) - - (a.providers.supportSubscription ? 1 : 0); - }); - + final plans = PlansData.fromJson(map)..sortPlansAndProviders(); appLogger.info('Plans: $map'); return Right(plans); } catch (e, stackTrace) { diff --git a/lib/lantern/lantern_platform_service.dart b/lib/lantern/lantern_platform_service.dart index 42c6650fc0..30eecb63fd 100644 --- a/lib/lantern/lantern_platform_service.dart +++ b/lib/lantern/lantern_platform_service.dart @@ -733,28 +733,7 @@ class LanternPlatformService implements LanternCoreService { channel, ); final map = jsonDecode(subData!); - final plans = PlansData.fromJson(map); - //Sort plans - plans.plans.sort((a, b) { - if (a.bestValue != b.bestValue) { - return a.bestValue ? -1 : 1; - } - // Then: sort by usdPrice descending - return b.usdPrice.compareTo(a.usdPrice); - }); - - //Sort provider - if (PlatformUtils.isMobile) { - plans.providers.android.sort((a, b) { - return (b.providers.supportSubscription ? 1 : 0) - - (a.providers.supportSubscription ? 1 : 0); - }); - } else { - plans.providers.desktop.sort((a, b) { - return (b.providers.supportSubscription ? 1 : 0) - - (a.providers.supportSubscription ? 1 : 0); - }); - } + final plans = PlansData.fromJson(map)..sortPlansAndProviders(); return Right(plans); } catch (e, stackTrace) { appLogger.error('Error fetching plans', e, stackTrace); diff --git a/macos/Runner/Handlers/MethodHandler.swift b/macos/Runner/Handlers/MethodHandler.swift index 818c2f981b..5905f65c4d 100644 --- a/macos/Runner/Handlers/MethodHandler.swift +++ b/macos/Runner/Handlers/MethodHandler.swift @@ -155,6 +155,10 @@ class MethodHandler { let code = call.arguments as? String ?? "" self.referralAttach(result: result, code: code) + case "attachReferralCodeV2": + let code = call.arguments as? String ?? "" + self.referralAttachV2(result: result, code: code) + // Private server methods case "digitalOcean": self.digitalOcean(result: result) @@ -807,6 +811,22 @@ class MethodHandler { } } + func referralAttachV2(result: @escaping FlutterResult, code: String) { + Task { + var error: NSError? + let json = MobileReferralAttachmentV2(code, &error) + if let error { + appLogger.error("Failed to attach referral code v2: \(error.localizedDescription)") + await self.handleFlutterError(error, result: result, code: "ATTACH_REFERRAL_CODE_V2_FAILED") + return + } + await MainActor.run { + appLogger.info("Referral code attached successfully (v2).") + result(json) + } + } + } + // MARK: - Private server methods func digitalOcean(result: @escaping FlutterResult) { From 098119be25b8a6c72399bff57f40d27acf28fd7e Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 1 Jul 2026 20:02:34 +0530 Subject: [PATCH 04/22] Update plans price to reflect discount --- assets/locales/en.po | 3 + lib/core/extensions/plan.dart | 37 ++--- lib/core/models/plan_data.dart | 139 ++++++++++-------- lib/core/models/referral_attach_response.dart | 22 +-- lib/features/auth/choose_payment_method.dart | 29 +++- .../home/provider/app_event_notifier.g.dart | 2 +- .../provider/country_code_notifier.g.dart | 2 +- lib/features/plans/plan_item.dart | 8 +- lib/features/plans/plans.dart | 7 +- lib/features/plans/plans_list.dart | 17 ++- .../plans/provider/payment_notifier.g.dart | 2 +- .../plans/provider/plans_notifier.g.dart | 2 +- .../plans/provider/referral_notifier.dart | 37 ++--- .../plans/provider/referral_notifier.g.dart | 24 +-- .../report_issue_draft_notifier.g.dart | 2 +- .../available_servers_notifier.g.dart | 2 +- 16 files changed, 187 insertions(+), 148 deletions(-) diff --git a/assets/locales/en.po b/assets/locales/en.po index 77205cc218..fbb5b52f80 100644 --- a/assets/locales/en.po +++ b/assets/locales/en.po @@ -1680,6 +1680,9 @@ msgstr "Your old configuration has been cleared. Turn the VPN on to automaticall msgid "promo_referral_code" msgstr "Promo / Referral Code" +msgid "promo_code" +msgstr "Promo Code (%s)" + diff --git a/lib/core/extensions/plan.dart b/lib/core/extensions/plan.dart index 9205711986..5ba17f02a7 100644 --- a/lib/core/extensions/plan.dart +++ b/lib/core/extensions/plan.dart @@ -1,8 +1,8 @@ import 'package:intl/intl.dart'; import 'package:lantern/core/common/common.dart'; import 'package:lantern/core/models/plan_data.dart'; -import 'package:lantern/core/utils/currency_utils.dart'; import 'package:lantern/core/models/user.dart'; +import 'package:lantern/core/utils/currency_utils.dart'; final _ddmmyyFormatter = DateFormat('dd/MM/yy'); final _mmddyyFormatter = DateFormat('MM/dd/yy'); @@ -10,32 +10,33 @@ final _mmddyyFormatter = DateFormat('MM/dd/yy'); extension PlanExtension on Plan { String get formattedYearlyPrice { return CurrencyUtils.formatCurrency( - double.parse(price.values.first.toString()), price.keys.first); + double.parse(price.values.first.toString()), + price.keys.first, + ); } String get formattedMonthlyPrice { return CurrencyUtils.formatCurrency( - double.parse(expectedMonthlyPrice.values.first.toString()), - expectedMonthlyPrice.keys.first); + double.parse(expectedMonthlyPrice.values.first.toString()), + expectedMonthlyPrice.keys.first, + ); } - /// The original (pre-discount) yearly price, reconstructed from the current - /// (discounted) price and [discountPct]. Used for the affiliate strikethrough - /// price shown next to the discounted price. - String formattedYearlyOriginalPrice(int discountPct) { - final discounted = double.parse(price.values.first.toString()); - final original = (discountPct > 0 && discountPct < 100) - ? discounted / (1 - discountPct / 100) - : discounted; - return CurrencyUtils.formatCurrency(original, price.keys.first); + /// The original (pre-discount) yearly price, taken directly from the + /// backend's `originalPrice` (no calculation). Shown as the strikethrough + /// price next to the discounted price when an affiliate code is applied. + /// Empty when the backend didn't supply an original price. + String get formatOriginalPrice { + final original = originalPrice; + if (original == null || original.isEmpty) return ''; + return CurrencyUtils.formatCurrency( + double.parse(original.values.first.toString()), + original.keys.first, + ); } String getDurationText() { - final durationMap = { - '1y': 'year', - '2y': 'two year', - '1m': 'month', - }; + final durationMap = {'1y': 'year', '2y': 'two year', '1m': 'month'}; final key = id.split('-').first; return durationMap[key] ?? ''; diff --git a/lib/core/models/plan_data.dart b/lib/core/models/plan_data.dart index 655962be84..2ee56847e2 100644 --- a/lib/core/models/plan_data.dart +++ b/lib/core/models/plan_data.dart @@ -4,10 +4,7 @@ class PlansData { Providers providers; List plans; - PlansData({ - required this.providers, - required this.plans, - }); + PlansData({required this.providers, required this.plans}); /// Sorts plans (best-value first, then by descending price) and orders the /// platform's payment providers so subscription-capable ones come first. @@ -32,9 +29,9 @@ class PlansData { } factory PlansData.fromJson(Map json) => PlansData( - providers: Providers.fromJson(json["providers"]), - plans: List.from(json["plans"].map((x) => Plan.fromJson(x))), - ); + providers: Providers.fromJson(json["providers"]), + plans: List.from(json["plans"].map((x) => Plan.fromJson(x))), + ); PlansData copyWith({ Providers? providers, @@ -48,9 +45,9 @@ class PlansData { } Map toJson() => { - "providers": providers.toJson(), - "plans": List.from(plans.map((x) => x.toJson())), - }; + "providers": providers.toJson(), + "plans": List.from(plans.map((x) => x.toJson())), + }; } class Plan { @@ -61,6 +58,14 @@ class Plan { Map expectedMonthlyPrice; bool bestValue; + // Original (pre-discount) prices. Only present when a discount (affiliate + // code) is applied; null otherwise. + int? originalUsdPrice; + int? originalUsdPrice1Y; + int? originalUsdPrice2Y; + Map? originalPrice; + Map? originalExpectedMonthlyPrice; + Plan({ required this.id, required this.description, @@ -68,65 +73,82 @@ class Plan { required this.price, required this.expectedMonthlyPrice, this.bestValue = false, + this.originalUsdPrice, + this.originalUsdPrice1Y, + this.originalUsdPrice2Y, + this.originalPrice, + this.originalExpectedMonthlyPrice, }); factory Plan.fromJson(Map json) => Plan( - id: json["id"], - description: json["description"], - usdPrice: json["usdPrice"], - price: json["price"], - expectedMonthlyPrice: json["expectedMonthlyPrice"], - bestValue: json["bestValue"] ?? false, - ); + id: json["id"], + description: json["description"], + usdPrice: json["usdPrice"], + price: json["price"], + expectedMonthlyPrice: json["expectedMonthlyPrice"], + bestValue: json["bestValue"] ?? false, + originalUsdPrice: json["originalUsdPrice"], + originalUsdPrice1Y: json["originalUsdPrice1Y"], + originalUsdPrice2Y: json["originalUsdPrice2Y"], + originalPrice: json["originalPrice"] == null + ? null + : Map.from(json["originalPrice"]), + originalExpectedMonthlyPrice: json["originalExpectedMonthlyPrice"] == null + ? null + : Map.from(json["originalExpectedMonthlyPrice"]), + ); Map toJson() => { - "id": id, - "description": description, - "usdPrice": usdPrice, - "price": price, - "expectedMonthlyPrice": expectedMonthlyPrice, - "bestValue": bestValue, - }; + "id": id, + "description": description, + "usdPrice": usdPrice, + "price": price, + "expectedMonthlyPrice": expectedMonthlyPrice, + "bestValue": bestValue, + "originalUsdPrice": originalUsdPrice, + "originalUsdPrice1Y": originalUsdPrice1Y, + "originalUsdPrice2Y": originalUsdPrice2Y, + "originalPrice": originalPrice, + "originalExpectedMonthlyPrice": originalExpectedMonthlyPrice, + }; } class Providers { List android; List desktop; - Providers({ - required this.android, - required this.desktop, - }); + Providers({required this.android, required this.desktop}); factory Providers.fromJson(Map json) => Providers( - android: - List.from(json["android"].map((x) => Android.fromJson(x))), - desktop: - List.from(json["desktop"].map((x) => Android.fromJson(x))), - ); + android: List.from( + json["android"].map((x) => Android.fromJson(x)), + ), + desktop: List.from( + json["desktop"].map((x) => Android.fromJson(x)), + ), + ); Map toJson() => { - "android": List.from(android.map((x) => x.toJson())), - "desktop": List.from(desktop.map((x) => x.toJson())), - }; + "android": List.from(android.map((x) => x.toJson())), + "desktop": List.from(desktop.map((x) => x.toJson())), + }; } class Android { String method; Provider providers; - Android({ - required this.method, - required this.providers, - }); + Android({required this.method, required this.providers}); factory Android.fromJson(Map json) => Android( - method: json["method"], - providers: Provider.fromJson(json["provider"]), - ); + method: json["method"], + providers: Provider.fromJson(json["provider"]), + ); - Map toJson() => - {"method": method, "provider": providers.toJson()}; + Map toJson() => { + "method": method, + "provider": providers.toJson(), + }; } class Provider { @@ -143,19 +165,20 @@ class Provider { }); factory Provider.fromJson(Map json) => Provider( - name: json["name"], - data: json["data"] == null - ? {} - : (json["data"] as Map) - .map((key, value) => MapEntry(key, value)), - icons: List.from(json["icons"].map((x) => x)), - supportSubscription: json["supportsSubscription"] ?? false, - ); + name: json["name"], + data: json["data"] == null + ? {} + : (json["data"] as Map).map( + (key, value) => MapEntry(key, value), + ), + icons: List.from(json["icons"].map((x) => x)), + supportSubscription: json["supportsSubscription"] ?? false, + ); Map toJson() => { - "name": name, - "data": data, - "icons": List.from(icons.map((x) => x)), - "supportsSubscription": supportSubscription, - }; + "name": name, + "data": data, + "icons": List.from(icons.map((x) => x)), + "supportsSubscription": supportSubscription, + }; } diff --git a/lib/core/models/referral_attach_response.dart b/lib/core/models/referral_attach_response.dart index ff4d319b18..7f86cd5c8e 100644 --- a/lib/core/models/referral_attach_response.dart +++ b/lib/core/models/referral_attach_response.dart @@ -1,18 +1,22 @@ import 'package:lantern/core/models/plan_data.dart'; /// The kind of code applied via the referral-attach V2 endpoint. -class ReferralType { - static const affiliate = 'affiliate'; - static const referral = 'referral'; +/// +/// - [affiliate] applies a discount to the plans (strikethrough pricing UI). +/// - [referral] keeps the existing per-plan bonus message UI. +enum ReferralType { + referral, + affiliate; + + static ReferralType fromString(String? value) => + value == 'referral' ? ReferralType.referral : ReferralType.affiliate; } class ReferralAttachV2Response { final PlansData plansData; final String code; final int discountPct; - - /// Either [ReferralType.affiliate] or [ReferralType.referral]. - final String type; + final ReferralType type; ReferralAttachV2Response({ required this.plansData, @@ -21,8 +25,6 @@ class ReferralAttachV2Response { required this.type, }); - /// Affiliate codes apply a discount to the plans (strikethrough pricing UI); - /// referral codes keep the existing per-plan bonus message UI. bool get isAffiliate => type == ReferralType.affiliate; factory ReferralAttachV2Response.fromJson(Map json) => @@ -30,13 +32,13 @@ class ReferralAttachV2Response { plansData: PlansData.fromJson(json)..sortPlansAndProviders(), code: json["code"] ?? '', discountPct: json["discountPct"] ?? 0, - type: json["type"] ?? ReferralType.affiliate, + type: ReferralType.fromString(json["type"]), ); Map toJson() => { ...plansData.toJson(), "code": code, "discountPct": discountPct, - "type": type, + "type": type.name, }; } diff --git a/lib/features/auth/choose_payment_method.dart b/lib/features/auth/choose_payment_method.dart index 935f96379f..07f7ec4c00 100644 --- a/lib/features/auth/choose_payment_method.dart +++ b/lib/features/auth/choose_payment_method.dart @@ -446,7 +446,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final referralEnable = ref.watch(referralProvider); + final referral = ref.watch(referralProvider); final theme = Theme.of(context).textTheme; return ListView.builder( shrinkWrap: true, @@ -492,7 +492,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { children: [ Text(userPlan.description, style: theme.bodyMedium), Text( - '${userPlan.formattedMonthlyPrice}/month', + userPlan.formattedYearlyPrice, style: theme.bodyMedium!.copyWith( color: context.textDisabled, ), @@ -500,7 +500,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { ], ), DividerSpace(padding: EdgeInsets.symmetric(vertical: 10)), - if (referralEnable) ...[ + if (referral != null && !referral.isAffiliate) ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -520,6 +520,29 @@ class PaymentCheckoutMethods extends HookConsumerWidget { ), DividerSpace(padding: EdgeInsets.symmetric(vertical: 10)), ], + // Affiliate codes: show the applied promo code and the discounted + // price (taken directly from the plans, same as the plan screen). + if (referral != null && referral.isAffiliate) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + 'promo_code'.i18n.fill([referral.code]), + style: theme.bodyMedium, + ), + ), + SizedBox(width: defaultSize), + Text( + userPlan.formattedYearlyPrice, + style: theme.bodyMedium!.copyWith( + color: context.textDisabled, + ), + ), + ], + ), + DividerSpace(padding: EdgeInsets.symmetric(vertical: 10)), + ], Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/lib/features/home/provider/app_event_notifier.g.dart b/lib/features/home/provider/app_event_notifier.g.dart index bffe906006..0524ea3035 100644 --- a/lib/features/home/provider/app_event_notifier.g.dart +++ b/lib/features/home/provider/app_event_notifier.g.dart @@ -42,7 +42,7 @@ final class AppEventNotifierProvider AppEventNotifier create() => AppEventNotifier(); } -String _$appEventNotifierHash() => r'12499360289be87d473f88c3ddcfd6366a523e30'; +String _$appEventNotifierHash() => r'69390162c5c3a546c5733d72bf859c624a7ca70c'; /// Listens for application-wide events and triggers corresponding actions. /// This can be used for all listening to events that go sends and handling them diff --git a/lib/features/home/provider/country_code_notifier.g.dart b/lib/features/home/provider/country_code_notifier.g.dart index 4d99d6efcb..dfbb6577a9 100644 --- a/lib/features/home/provider/country_code_notifier.g.dart +++ b/lib/features/home/provider/country_code_notifier.g.dart @@ -42,7 +42,7 @@ final class CountryCodeNotifierProvider } String _$countryCodeNotifierHash() => - r'0c1a08bc664de1c57b27ed7b9ab5872e929718d1'; + r'6d4e2e5976694966396a758ca782c20d16b3a474'; abstract class _$CountryCodeNotifier extends $Notifier { String build(); diff --git a/lib/features/plans/plan_item.dart b/lib/features/plans/plan_item.dart index 53d19000ee..7e5c1ba829 100644 --- a/lib/features/plans/plan_item.dart +++ b/lib/features/plans/plan_item.dart @@ -11,8 +11,8 @@ class PlanItem extends StatelessWidget { final Function(Plan plans) onPressed; final String referralMessage; - /// When > 0, an affiliate discount is applied: the (discounted) price is - /// shown alongside the strikethrough original price. + /// When > 0, an affiliate discount is applied: the discounted price is shown + /// alongside the strikethrough original price. final int discountPct; const PlanItem({ @@ -79,10 +79,10 @@ class PlanItem extends StatelessWidget { color: context.textLink, ), ), - if (discountPct > 0) ...[ + if (discountPct > 0 && plan.formatOriginalPrice.isNotEmpty) ...[ SizedBox(width: 6), Text( - plan.formattedYearlyOriginalPrice(discountPct), + plan.formatOriginalPrice, style: textTheme.labelMedium!.copyWith( color: context.textTertiary, decoration: TextDecoration.lineThrough, diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index b24fc5642d..6c3636a51c 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -194,9 +194,8 @@ class _PlansState extends ConsumerState /// the discount percentage plus a removable chip with the applied code. /// Referral (non-affiliate) codes keep the existing per-plan bonus message. Widget _buildAffiliateBanner() { - final referralApplied = ref.watch(referralProvider); - final response = ref.read(referralProvider.notifier).appliedReferral; - if (!referralApplied || response == null || !response.isAffiliate) { + final response = ref.watch(referralProvider); + if (response == null || !response.isAffiliate) { return const SizedBox.shrink(); } return Padding( @@ -249,7 +248,7 @@ class _PlansState extends ConsumerState } void onMenuTap() { - final isReferralApplied = ref.read(referralProvider); + final isReferralApplied = ref.read(referralProvider) != null; showAppBottomSheet( context: context, title: 'payment_options'.i18n, diff --git a/lib/features/plans/plans_list.dart b/lib/features/plans/plans_list.dart index ee144c6e9c..ca10774cb4 100644 --- a/lib/features/plans/plans_list.dart +++ b/lib/features/plans/plans_list.dart @@ -15,10 +15,13 @@ class PlansListView extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final referralEnable = ref.watch(referralProvider); - final referralNotifier = ref.read(referralProvider.notifier); - final isAffiliate = referralNotifier.isAffiliateApplied; - final discountPct = referralNotifier.appliedReferral?.discountPct ?? 0; + final referral = ref.watch(referralProvider); + // Referral (non-affiliate) codes show the per-plan bonus message; affiliate + // codes instead show strikethrough discount pricing. + final showReferralBonus = referral != null && !referral.isAffiliate; + final discountPct = (referral != null && referral.isAffiliate) + ? referral.discountPct + : 0; final size = MediaQuery.of(context).size; final plan = useState( data.plans.firstWhere((Plan plan) => plan.bestValue == true), @@ -41,12 +44,10 @@ class PlansListView extends HookConsumerWidget { return PlanItem( plan: item, planSelected: plan.value.id == item.id, - // Affiliate codes show strikethrough discount pricing instead of - // the per-plan referral bonus message. - referralMessage: (referralEnable && !isAffiliate) + referralMessage: showReferralBonus ? getReferralMessage(item.id) : '', - discountPct: isAffiliate ? discountPct : 0, + discountPct: discountPct, onPressed: (plans) { plan.value = plans; ref.read(plansProvider.notifier).setSelectedPlan(plans); diff --git a/lib/features/plans/provider/payment_notifier.g.dart b/lib/features/plans/provider/payment_notifier.g.dart index 996d453f21..9068573b14 100644 --- a/lib/features/plans/provider/payment_notifier.g.dart +++ b/lib/features/plans/provider/payment_notifier.g.dart @@ -99,7 +99,7 @@ final class PaymentNotifierProvider } } -String _$paymentNotifierHash() => r'd31174207a822e6026a5bc9647d27fe98a50a488'; +String _$paymentNotifierHash() => r'bcdbccc7c098cfc6693ee2219d7853503c8e397f'; abstract class _$PaymentNotifier extends $Notifier { void build(); diff --git a/lib/features/plans/provider/plans_notifier.g.dart b/lib/features/plans/provider/plans_notifier.g.dart index e37dcfedf6..0c6c8c325a 100644 --- a/lib/features/plans/provider/plans_notifier.g.dart +++ b/lib/features/plans/provider/plans_notifier.g.dart @@ -33,7 +33,7 @@ final class PlansNotifierProvider PlansNotifier create() => PlansNotifier(); } -String _$plansNotifierHash() => r'7e0420b132720fc5ea3d1e3f19c4ba35889e93e8'; +String _$plansNotifierHash() => r'e0e759143b5bc1ded9638c05dc1af255b78a45a2'; abstract class _$PlansNotifier extends $AsyncNotifier { FutureOr build(); diff --git a/lib/features/plans/provider/referral_notifier.dart b/lib/features/plans/provider/referral_notifier.dart index d938d70485..8052f3efbc 100644 --- a/lib/features/plans/provider/referral_notifier.dart +++ b/lib/features/plans/provider/referral_notifier.dart @@ -7,34 +7,19 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'referral_notifier.g.dart'; +/// Holds the applied V2 referral/affiliate response, or null when no code is +/// applied. The response's [ReferralType] drives the UI, so callers branch on +/// `state == null` (none) / `state!.isAffiliate` (affiliate) / else (referral) +/// and read `code`/`discountPct`/`plansData` off the same object. @Riverpod(keepAlive: true) class ReferralNotifier extends _$ReferralNotifier { - /// The applied V2 referral/affiliate response, populated on a successful - /// [applyReferralCodeV2]. Drives the affiliate discount UI (code chip, - /// "X% discount applied" banner, strikethrough pricing). Null when no V2 - /// code is applied. The `state` bool remains the simple applied/not flag - /// that existing widgets watch. - ReferralAttachV2Response? appliedReferral; - - bool get isAffiliateApplied => appliedReferral?.isAffiliate ?? false; - @override - bool build() { - return false; - } - - Future> applyReferralCode(String code) async { - final result = - await ref.read(lanternServiceProvider).attachReferralCode(code); - if (result.isRight()) { - state = true; - } - return result; - } + ReferralAttachV2Response? build() => null; /// Applies a referral code via the V2 endpoint. On success the returned /// discounted plans are pushed into [plansProvider] so the plans UI updates - /// immediately, and the resulting response is returned to the caller. + /// immediately, the response becomes the notifier state, and it is returned + /// to the caller. Future> applyReferralCodeV2( String code, ) async { @@ -42,8 +27,7 @@ class ReferralNotifier extends _$ReferralNotifier { await ref.read(lanternServiceProvider).attachReferralCodeV2(code); if (result.isRight()) { final response = result.getRight().toNullable(); - appliedReferral = response; - state = true; + state = response; if (response != null) { ref.read(plansProvider.notifier).updatePlans(response.plansData); } @@ -51,8 +35,5 @@ class ReferralNotifier extends _$ReferralNotifier { return result; } - void resetReferral() { - appliedReferral = null; - state = false; - } + void resetReferral() => state = null; } diff --git a/lib/features/plans/provider/referral_notifier.g.dart b/lib/features/plans/provider/referral_notifier.g.dart index 35265a3e28..43fec261d7 100644 --- a/lib/features/plans/provider/referral_notifier.g.dart +++ b/lib/features/plans/provider/referral_notifier.g.dart @@ -13,7 +13,7 @@ part of 'referral_notifier.dart'; final referralProvider = ReferralNotifierProvider._(); final class ReferralNotifierProvider - extends $NotifierProvider { + extends $NotifierProvider { ReferralNotifierProvider._() : super( from: null, @@ -33,27 +33,33 @@ final class ReferralNotifierProvider ReferralNotifier create() => ReferralNotifier(); /// {@macro riverpod.override_with_value} - Override overrideWithValue(bool value) { + Override overrideWithValue(ReferralAttachV2Response? value) { return $ProviderOverride( origin: this, - providerOverride: $SyncValueProvider(value), + providerOverride: $SyncValueProvider(value), ); } } -String _$referralNotifierHash() => r'6b028ac616c6b663b1e38d2769ca69f063924721'; +String _$referralNotifierHash() => r'a00524678cd445e36352bf7f53184c4da3e5095d'; -abstract class _$ReferralNotifier extends $Notifier { - bool build(); +/// Holds the applied V2 referral/affiliate response, or null when no code is +/// applied. The response's [ReferralType] drives the UI, so callers branch on +/// `state == null` (none) / `state!.isAffiliate` (affiliate) / else (referral) +/// and read `code`/`discountPct`/`plansData` off the same object. + +abstract class _$ReferralNotifier extends $Notifier { + ReferralAttachV2Response? build(); @$mustCallSuper @override void runBuild() { - final ref = this.ref as $Ref; + final ref = + this.ref as $Ref; final element = ref.element as $ClassProviderElement< - AnyNotifier, - bool, + AnyNotifier, + ReferralAttachV2Response?, Object?, Object? >; diff --git a/lib/features/report_issue/provider/report_issue_draft_notifier.g.dart b/lib/features/report_issue/provider/report_issue_draft_notifier.g.dart index 90bc01e8af..dd1d959589 100644 --- a/lib/features/report_issue/provider/report_issue_draft_notifier.g.dart +++ b/lib/features/report_issue/provider/report_issue_draft_notifier.g.dart @@ -41,7 +41,7 @@ final class ReportIssueDraftProvider } } -String _$reportIssueDraftHash() => r'b52dd92e1927fe7a602c4b9751d238f7763f162b'; +String _$reportIssueDraftHash() => r'640389ac3ba527924290e119cc6d6402f297f30b'; abstract class _$ReportIssueDraft extends $Notifier { ReportIssueDraftState build(); diff --git a/lib/features/vpn/provider/available_servers_notifier.g.dart b/lib/features/vpn/provider/available_servers_notifier.g.dart index 14df53e059..6c4ce5d190 100644 --- a/lib/features/vpn/provider/available_servers_notifier.g.dart +++ b/lib/features/vpn/provider/available_servers_notifier.g.dart @@ -34,7 +34,7 @@ final class AvailableServersNotifierProvider } String _$availableServersNotifierHash() => - r'3931c38148c638b798839183906bdf00056326b5'; + r'a8f79742495ec1900d0c79e861a1d25d6ed4003d'; abstract class _$AvailableServersNotifier extends $AsyncNotifier { From 854af8bf8edc8c0cb45b3010a4a91df6b5ae6d7e Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 1 Jul 2026 20:54:17 +0530 Subject: [PATCH 05/22] updated stripe endpoints --- .../lantern/handler/MethodHandler.kt | 3 +- assets/locales/en.po | 3 ++ ios/Runner/Handlers/MethodHandler.swift | 20 +++++++++++ lantern-core/core.go | 11 ++++--- lantern-core/ffi/ffi.go | 5 +-- lantern-core/mobile/mobile.go | 10 +++--- lib/core/extensions/plan.dart | 15 +++++++++ lib/core/models/referral_attach_response.dart | 15 +++++++++ lib/features/auth/choose_payment_method.dart | 33 ++++++++++++++----- lib/features/plans/plan_item.dart | 5 ++- lib/features/plans/plans.dart | 5 +-- lib/features/plans/plans_list.dart | 11 +++---- .../plans/provider/payment_notifier.dart | 13 +++++--- .../plans/provider/referral_notifier.dart | 9 ++--- lib/lantern/lantern_core_service.dart | 2 ++ lib/lantern/lantern_ffi_service.dart | 3 ++ lib/lantern/lantern_generated_bindings.dart | 4 +++ lib/lantern/lantern_platform_service.dart | 5 ++- lib/lantern/lantern_service.dart | 10 +++++- macos/Runner/Handlers/MethodHandler.swift | 2 ++ 20 files changed, 142 insertions(+), 42 deletions(-) diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt b/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt index d6e721ca1d..db902487dd 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt @@ -439,7 +439,8 @@ class MethodHandler : FlutterPlugin, val map = call.arguments as Map<*, *> val subscriptionData = Mobile.stripeSubscription( map["email"] as String, - map["planId"] as String + map["planId"] as String, + map["couponCode"] as? String ?: "" ) withContext(Dispatchers.Main) { success(subscriptionData) diff --git a/assets/locales/en.po b/assets/locales/en.po index fbb5b52f80..412defaea6 100644 --- a/assets/locales/en.po +++ b/assets/locales/en.po @@ -1683,6 +1683,9 @@ msgstr "Promo / Referral Code" msgid "promo_code" msgstr "Promo Code (%s)" +msgid "order_total" +msgstr "Order Total" + diff --git a/ios/Runner/Handlers/MethodHandler.swift b/ios/Runner/Handlers/MethodHandler.swift index ae1c4d2260..610fa66945 100644 --- a/ios/Runner/Handlers/MethodHandler.swift +++ b/ios/Runner/Handlers/MethodHandler.swift @@ -165,6 +165,10 @@ class MethodHandler { let code = call.arguments as? String ?? "" self.referralAttach(result: result, code: code) + case "attachReferralCodeV2": + let code = call.arguments as? String ?? "" + self.referralAttachV2(result: result, code: code) + // Private server methods case "digitalOcean": self.digitalOcean(result: result) @@ -776,6 +780,22 @@ class MethodHandler { } } + func referralAttachV2(result: @escaping FlutterResult, code: String) { + Task { + var error: NSError? + let json = MobileReferralAttachmentV2(code, &error) + if let error { + appLogger.error("Failed to attach referral code v2: \(error.localizedDescription)") + await self.handleFlutterError(error, result: result, code: "ATTACH_REFERRAL_CODE_V2_FAILED") + return + } + await MainActor.run { + appLogger.info("Referral code attached successfully (v2).") + result(json) + } + } + } + // MARK: - Private server methods func digitalOcean(result: @escaping FlutterResult) { diff --git a/lantern-core/core.go b/lantern-core/core.go index 0cdcfd0a7b..542d6f4f72 100644 --- a/lantern-core/core.go +++ b/lantern-core/core.go @@ -120,7 +120,7 @@ type PrivateServer interface { } type Payment interface { - StripeSubscription(email, planID string) (string, error) + StripeSubscription(email, planID, couponCode string) (string, error) Plans(channel string) (string, error) StripeBillingPortalUrl() (string, error) AcknowledgeGooglePurchase(purchaseToken, planId string) (string, error) @@ -130,7 +130,7 @@ type Payment interface { PaymentRedirect(provider, planID, email, idempotencyKey string) (string, error) ActivationCode(email, resellerCode string) error SubscriptionPaymentRedirectURL(redirectBody account.PaymentRedirectData) (string, error) - StripeSubscriptionPaymentRedirect(subscriptionType, planID, email, idempotencyKey string) (string, error) + StripeSubscriptionPaymentRedirect(subscriptionType, planID, email, idempotencyKey, couponCode string) (string, error) } type SplitTunnel interface { @@ -873,8 +873,8 @@ func (lc *LanternCore) ReferralAttachmentV2(referralCode string) ([]byte, error) // Payments // ///////////////// -func (lc *LanternCore) StripeSubscription(email, planID string) (string, error) { - return lc.client.NewStripeSubscription(lc.ctx, email, planID) +func (lc *LanternCore) StripeSubscription(email, planID, couponCode string) (string, error) { + return lc.client.NewStripeSubscription(lc.ctx, email, planID, couponCode) } func (lc *LanternCore) Plans(channel string) (string, error) { @@ -937,7 +937,7 @@ func (lc *LanternCore) SubscriptionPaymentRedirectURL(redirectBody account.Payme return lc.client.SubscriptionPaymentRedirectURL(lc.ctx, redirectBody) } -func (lc *LanternCore) StripeSubscriptionPaymentRedirect(subscriptionType, planID, email, idempotencyKey string) (string, error) { +func (lc *LanternCore) StripeSubscriptionPaymentRedirect(subscriptionType, planID, email, idempotencyKey, couponCode string) (string, error) { idempotencyKey, err := normalizePaymentRedirectIdempotencyKey(idempotencyKey) if err != nil { return "", err @@ -950,6 +950,7 @@ func (lc *LanternCore) StripeSubscriptionPaymentRedirect(subscriptionType, planI Email: email, BillingType: account.SubscriptionType(subscriptionType), IdempotencyKey: idempotencyKey, + CouponCode: couponCode, } return lc.SubscriptionPaymentRedirectURL(redirectBody) } diff --git a/lantern-core/ffi/ffi.go b/lantern-core/ffi/ffi.go index 3990b725c7..3b85998630 100644 --- a/lantern-core/ffi/ffi.go +++ b/lantern-core/ffi/ffi.go @@ -645,17 +645,18 @@ func fetchUserData() *C.char { // Fetch stipe subscription payment redirect link // //export stripeSubscriptionPaymentRedirect -func stripeSubscriptionPaymentRedirect(subType, _planId, _email, _idempotencyKey *C.char) *C.char { +func stripeSubscriptionPaymentRedirect(subType, _planId, _email, _idempotencyKey, _couponCode *C.char) *C.char { subscriptionType := C.GoString(subType) planID := C.GoString(_planId) email := C.GoString(_email) idempotencyKey := C.GoString(_idempotencyKey) + couponCode := C.GoString(_couponCode) return runOnGoStack(func() *C.char { c, errStr := requireCore() if errStr != nil { return errStr } - redirect, err := c.StripeSubscriptionPaymentRedirect(subscriptionType, planID, email, idempotencyKey) + redirect, err := c.StripeSubscriptionPaymentRedirect(subscriptionType, planID, email, idempotencyKey, couponCode) if err != nil { return SendError(err) } diff --git a/lantern-core/mobile/mobile.go b/lantern-core/mobile/mobile.go index 66b081b9c1..abccee90da 100644 --- a/lantern-core/mobile/mobile.go +++ b/lantern-core/mobile/mobile.go @@ -497,8 +497,10 @@ func OAuthLoginCallback(oAuthToken string) (string, error) { }) } -func StripeSubscription(email, planID string) (string, error) { - return withCoreR(func(c lanterncore.Core) (string, error) { return c.StripeSubscription(email, planID) }) +func StripeSubscription(email, planID, couponCode string) (string, error) { + return withCoreR(func(c lanterncore.Core) (string, error) { + return c.StripeSubscription(email, planID, couponCode) + }) } func Plans(channel string) (string, error) { @@ -613,10 +615,10 @@ func PaymentRedirect(provider, planId, email, idempotencyKey string) (string, er // /This is specifically for stripe subscriptions that require a redirect to complete the payment // This is only used for macos -func StripeSubscriptionPaymentRedirect(subType, planId, email, idempotencyKey string) (string, error) { +func StripeSubscriptionPaymentRedirect(subType, planId, email, idempotencyKey, couponCode string) (string, error) { slog.Debug("stripeSubscriptionPaymentRedirect called") return withCoreR(func(c lanterncore.Core) (string, error) { - return c.StripeSubscriptionPaymentRedirect(subType, planId, email, idempotencyKey) + return c.StripeSubscriptionPaymentRedirect(subType, planId, email, idempotencyKey, couponCode) }) } diff --git a/lib/core/extensions/plan.dart b/lib/core/extensions/plan.dart index 5ba17f02a7..a9e559293a 100644 --- a/lib/core/extensions/plan.dart +++ b/lib/core/extensions/plan.dart @@ -35,6 +35,21 @@ extension PlanExtension on Plan { ); } + /// The amount deducted by the affiliate discount: original − discounted + /// yearly price, both taken directly from the backend (no percentage math). + /// Shown as the "Promo Code" deduction at checkout. Empty when the backend + /// didn't supply an original price. + String get formatDiscountAmount { + final original = originalPrice; + if (original == null || original.isEmpty) return ''; + final originalAmount = double.parse(original.values.first.toString()); + final discountedAmount = double.parse(price.values.first.toString()); + return CurrencyUtils.formatCurrency( + originalAmount - discountedAmount, + price.keys.first, + ); + } + String getDurationText() { final durationMap = {'1y': 'year', '2y': 'two year', '1m': 'month'}; diff --git a/lib/core/models/referral_attach_response.dart b/lib/core/models/referral_attach_response.dart index 7f86cd5c8e..285df5b218 100644 --- a/lib/core/models/referral_attach_response.dart +++ b/lib/core/models/referral_attach_response.dart @@ -2,9 +2,11 @@ import 'package:lantern/core/models/plan_data.dart'; /// The kind of code applied via the referral-attach V2 endpoint. /// +/// - [none] nothing applied (the notifier's default/null state). /// - [affiliate] applies a discount to the plans (strikethrough pricing UI). /// - [referral] keeps the existing per-plan bonus message UI. enum ReferralType { + none, referral, affiliate; @@ -42,3 +44,16 @@ class ReferralAttachV2Response { "type": type.name, }; } + +/// Null-safe accessors for the referral notifier state +/// (`ReferralAttachV2Response?`). Lets callers branch on [type] and read +/// [code] / [discountPct] directly, without repeated `!= null` checks — a +/// null state simply reports [ReferralType.none], empty code, and 0 discount. +extension ReferralStateX on ReferralAttachV2Response? { + ReferralType get type => this?.type ?? ReferralType.none; + bool get isApplied => this != null; + bool get isAffiliate => type == ReferralType.affiliate; + bool get isReferral => type == ReferralType.referral; + String get code => this?.code ?? ''; + int get discountPct => this?.discountPct ?? 0; +} diff --git a/lib/features/auth/choose_payment_method.dart b/lib/features/auth/choose_payment_method.dart index 07f7ec4c00..3d70c1fec7 100644 --- a/lib/features/auth/choose_payment_method.dart +++ b/lib/features/auth/choose_payment_method.dart @@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:lantern/core/common/common.dart'; import 'package:lantern/core/extensions/plan.dart'; import 'package:lantern/core/models/plan_data.dart'; +import 'package:lantern/core/models/referral_attach_response.dart'; import 'package:lantern/core/services/injection_container.dart'; import 'package:lantern/core/services/stripe_service.dart'; import 'package:lantern/core/widgets/logs_path.dart'; @@ -132,6 +133,13 @@ class ChoosePaymentMethod extends HookConsumerWidget { ); } + /// The applied affiliate code (coupon) to forward to Stripe, or '' when the + /// applied code is a plain referral / nothing is applied. + String _affiliateCoupon(WidgetRef ref) { + final referral = ref.read(referralProvider); + return referral.isAffiliate ? referral.code : ''; + } + Future onSubscribe( Android provider, WidgetRef ref, @@ -192,7 +200,11 @@ class ChoosePaymentMethod extends HookConsumerWidget { context.showLoadingDialog(); ///get stripe details - final result = await payments.stripeSubscription(userPlan.id, email); + final result = await payments.stripeSubscription( + userPlan.id, + email, + couponCode: _affiliateCoupon(ref), + ); result.fold( (error) { context.showSnackBar(error.localizedErrorMessage); @@ -248,6 +260,7 @@ class ChoosePaymentMethod extends HookConsumerWidget { BillingType.subscription, userPlan.id, email, + couponCode: _affiliateCoupon(ref), ); if (!context.mounted) return; await result.fold>( @@ -492,7 +505,10 @@ class PaymentCheckoutMethods extends HookConsumerWidget { children: [ Text(userPlan.description, style: theme.bodyMedium), Text( - userPlan.formattedYearlyPrice, + (referral.isAffiliate && + userPlan.formatOriginalPrice.isNotEmpty) + ? userPlan.formatOriginalPrice + : userPlan.formattedYearlyPrice, style: theme.bodyMedium!.copyWith( color: context.textDisabled, ), @@ -500,7 +516,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { ], ), DividerSpace(padding: EdgeInsets.symmetric(vertical: 10)), - if (referral != null && !referral.isAffiliate) ...[ + if (referral.isReferral) ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -520,9 +536,10 @@ class PaymentCheckoutMethods extends HookConsumerWidget { ), DividerSpace(padding: EdgeInsets.symmetric(vertical: 10)), ], - // Affiliate codes: show the applied promo code and the discounted - // price (taken directly from the plans, same as the plan screen). - if (referral != null && referral.isAffiliate) ...[ + // Affiliate codes: show the applied promo code and the amount + // deducted (original − discounted, from the backend). + if (referral.isAffiliate && + userPlan.formatDiscountAmount.isNotEmpty) ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -534,7 +551,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { ), SizedBox(width: defaultSize), Text( - userPlan.formattedYearlyPrice, + '-${userPlan.formatDiscountAmount}', style: theme.bodyMedium!.copyWith( color: context.textDisabled, ), @@ -547,7 +564,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Order Total:', + 'order_total'.i18n, style: theme.titleSmall!.copyWith( color: context.textPrimary, ), diff --git a/lib/features/plans/plan_item.dart b/lib/features/plans/plan_item.dart index 7e5c1ba829..a8526ce8db 100644 --- a/lib/features/plans/plan_item.dart +++ b/lib/features/plans/plan_item.dart @@ -79,13 +79,16 @@ class PlanItem extends StatelessWidget { color: context.textLink, ), ), - if (discountPct > 0 && plan.formatOriginalPrice.isNotEmpty) ...[ + if (discountPct > 0 && + plan.formatOriginalPrice.isNotEmpty) ...[ SizedBox(width: 6), Text( plan.formatOriginalPrice, style: textTheme.labelMedium!.copyWith( color: context.textTertiary, decoration: TextDecoration.lineThrough, + decorationColor: context.textTertiary, + decorationThickness: 4, ), ), ], diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index 6c3636a51c..8ac499041c 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -22,6 +22,7 @@ import 'package:lantern/features/plans/provider/referral_notifier.dart'; import 'package:lantern/features/plans/restore_purchase_mixin.dart'; import '../../core/models/plan_data.dart'; +import '../../core/models/referral_attach_response.dart'; @RoutePage(name: 'Plans') class Plans extends StatefulHookConsumerWidget { @@ -195,7 +196,7 @@ class _PlansState extends ConsumerState /// Referral (non-affiliate) codes keep the existing per-plan bonus message. Widget _buildAffiliateBanner() { final response = ref.watch(referralProvider); - if (response == null || !response.isAffiliate) { + if (!response.isAffiliate) { return const SizedBox.shrink(); } return Padding( @@ -248,7 +249,7 @@ class _PlansState extends ConsumerState } void onMenuTap() { - final isReferralApplied = ref.read(referralProvider) != null; + final isReferralApplied = ref.read(referralProvider).isApplied; showAppBottomSheet( context: context, title: 'payment_options'.i18n, diff --git a/lib/features/plans/plans_list.dart b/lib/features/plans/plans_list.dart index ca10774cb4..5f690b18a1 100644 --- a/lib/features/plans/plans_list.dart +++ b/lib/features/plans/plans_list.dart @@ -3,6 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:lantern/core/common/common.dart'; import 'package:lantern/core/models/plan_data.dart'; +import 'package:lantern/core/models/referral_attach_response.dart'; import 'package:lantern/core/utils/screen_utils.dart'; import 'package:lantern/features/plans/plan_item.dart'; import 'package:lantern/features/plans/provider/plans_notifier.dart'; @@ -16,12 +17,10 @@ class PlansListView extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final referral = ref.watch(referralProvider); - // Referral (non-affiliate) codes show the per-plan bonus message; affiliate - // codes instead show strikethrough discount pricing. - final showReferralBonus = referral != null && !referral.isAffiliate; - final discountPct = (referral != null && referral.isAffiliate) - ? referral.discountPct - : 0; + // Referral codes show the per-plan bonus message; affiliate codes instead + // show strikethrough discount pricing. + final showReferralBonus = referral.isReferral; + final discountPct = referral.isAffiliate ? referral.discountPct : 0; final size = MediaQuery.of(context).size; final plan = useState( data.plans.firstWhere((Plan plan) => plan.bestValue == true), diff --git a/lib/features/plans/provider/payment_notifier.dart b/lib/features/plans/provider/payment_notifier.dart index 6dfde625b0..18de6e25b4 100644 --- a/lib/features/plans/provider/payment_notifier.dart +++ b/lib/features/plans/provider/payment_notifier.dart @@ -61,8 +61,9 @@ class PaymentNotifier extends _$PaymentNotifier { Future> stripeSubscriptionLink( BillingType type, String planId, - String email, - ) async { + String email, { + String couponCode = '', + }) async { final idempotencyKey = generatePaymentRedirectIdempotencyKey(); return ref .read(lanternServiceProvider) @@ -71,16 +72,18 @@ class PaymentNotifier extends _$PaymentNotifier { planId: planId, email: email, idempotencyKey: idempotencyKey, + couponCode: couponCode, ); } Future>> stripeSubscription( String planId, - String email, - ) async { + String email, { + String couponCode = '', + }) async { return ref .read(lanternServiceProvider) - .stipeSubscription(planId: planId, email: email); + .stipeSubscription(planId: planId, email: email, couponCode: couponCode); } Future> paymentRedirect({ diff --git a/lib/features/plans/provider/referral_notifier.dart b/lib/features/plans/provider/referral_notifier.dart index 8052f3efbc..07d386c507 100644 --- a/lib/features/plans/provider/referral_notifier.dart +++ b/lib/features/plans/provider/referral_notifier.dart @@ -7,10 +7,6 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'referral_notifier.g.dart'; -/// Holds the applied V2 referral/affiliate response, or null when no code is -/// applied. The response's [ReferralType] drives the UI, so callers branch on -/// `state == null` (none) / `state!.isAffiliate` (affiliate) / else (referral) -/// and read `code`/`discountPct`/`plansData` off the same object. @Riverpod(keepAlive: true) class ReferralNotifier extends _$ReferralNotifier { @override @@ -23,8 +19,9 @@ class ReferralNotifier extends _$ReferralNotifier { Future> applyReferralCodeV2( String code, ) async { - final result = - await ref.read(lanternServiceProvider).attachReferralCodeV2(code); + final result = await ref + .read(lanternServiceProvider) + .attachReferralCodeV2(code); if (result.isRight()) { final response = result.getRight().toNullable(); state = response; diff --git a/lib/lantern/lantern_core_service.dart b/lib/lantern/lantern_core_service.dart index 722323236b..e70228291c 100644 --- a/lib/lantern/lantern_core_service.dart +++ b/lib/lantern/lantern_core_service.dart @@ -94,6 +94,7 @@ abstract class LanternCoreService { required String planId, required String email, required String idempotencyKey, + String couponCode = '', }); Future> paymentRedirect({ @@ -107,6 +108,7 @@ abstract class LanternCoreService { Future>> stipeSubscription({ required String planId, required String email, + String couponCode = '', }); Future> stripeBillingPortal(); diff --git a/lib/lantern/lantern_ffi_service.dart b/lib/lantern/lantern_ffi_service.dart index e07a8f968f..12e0eb0518 100644 --- a/lib/lantern/lantern_ffi_service.dart +++ b/lib/lantern/lantern_ffi_service.dart @@ -744,6 +744,7 @@ class LanternFFIService implements LanternCoreService { required String planId, required String email, required String idempotencyKey, + String couponCode = '', }) async { try { appLogger.debug('Starting Stripe Subscription Payment Redirect'); @@ -753,6 +754,7 @@ class LanternFFIService implements LanternCoreService { planId.toCharPtr, email.toCharPtr, idempotencyKey.toCharPtr, + couponCode.toCharPtr, ); try { return resultPtr.toDartString(); @@ -777,6 +779,7 @@ class LanternFFIService implements LanternCoreService { Future>> stipeSubscription({ required String planId, required String email, + String couponCode = '', }) { throw Exception("Desktop flow should not be here, this is just for mobile"); } diff --git a/lib/lantern/lantern_generated_bindings.dart b/lib/lantern/lantern_generated_bindings.dart index d908c7f280..6319214058 100644 --- a/lib/lantern/lantern_generated_bindings.dart +++ b/lib/lantern/lantern_generated_bindings.dart @@ -5681,12 +5681,14 @@ class LanternBindings { ffi.Pointer _planId, ffi.Pointer _email, ffi.Pointer _idempotencyKey, + ffi.Pointer _couponCode, ) { return _stripeSubscriptionPaymentRedirect( subType, _planId, _email, _idempotencyKey, + _couponCode, ); } @@ -5698,6 +5700,7 @@ class LanternBindings { ffi.Pointer, ffi.Pointer, ffi.Pointer, + ffi.Pointer, ) > >('stripeSubscriptionPaymentRedirect'); @@ -5709,6 +5712,7 @@ class LanternBindings { ffi.Pointer, ffi.Pointer, ffi.Pointer, + ffi.Pointer, ) >(); diff --git a/lib/lantern/lantern_platform_service.dart b/lib/lantern/lantern_platform_service.dart index 30eecb63fd..02b7405d83 100644 --- a/lib/lantern/lantern_platform_service.dart +++ b/lib/lantern/lantern_platform_service.dart @@ -655,6 +655,7 @@ class LanternPlatformService implements LanternCoreService { required String planId, required String email, required String idempotencyKey, + String couponCode = '', }) async { if (!PlatformUtils.isMacOS) { return left( @@ -671,6 +672,7 @@ class LanternPlatformService implements LanternCoreService { "planId": planId, "email": email, "idempotencyKey": idempotencyKey, + "couponCode": couponCode, }); if (redirectUrl == null || redirectUrl.isEmpty) { return Left( @@ -694,11 +696,12 @@ class LanternPlatformService implements LanternCoreService { Future>> stipeSubscription({ required String planId, required String email, + String couponCode = '', }) async { try { final subData = await _methodChannel.invokeMethod( 'stripeSubscription', - {"planId": planId, "email": email}, + {"planId": planId, "email": email, "couponCode": couponCode}, ); final map = jsonDecode(subData!); return Right(map); diff --git a/lib/lantern/lantern_service.dart b/lib/lantern/lantern_service.dart index 25a654cddb..c09721ca8d 100644 --- a/lib/lantern/lantern_service.dart +++ b/lib/lantern/lantern_service.dart @@ -179,6 +179,7 @@ class LanternService implements LanternCoreService { required String planId, required String email, required String idempotencyKey, + String couponCode = '', }) { if (PlatformUtils.isFFISupported) { return _ffiService.stipeSubscriptionPaymentRedirect( @@ -186,6 +187,7 @@ class LanternService implements LanternCoreService { planId: planId, email: email, idempotencyKey: idempotencyKey, + couponCode: couponCode, ); } return _platformService.stipeSubscriptionPaymentRedirect( @@ -193,6 +195,7 @@ class LanternService implements LanternCoreService { planId: planId, email: email, idempotencyKey: idempotencyKey, + couponCode: couponCode, ); } @@ -200,11 +203,16 @@ class LanternService implements LanternCoreService { Future>> stipeSubscription({ required String planId, required String email, + String couponCode = '', }) { if (PlatformUtils.isFFISupported) { throw UnimplementedError(); } - return _platformService.stipeSubscription(planId: planId, email: email); + return _platformService.stipeSubscription( + planId: planId, + email: email, + couponCode: couponCode, + ); } @override diff --git a/macos/Runner/Handlers/MethodHandler.swift b/macos/Runner/Handlers/MethodHandler.swift index 5905f65c4d..faeb865ca2 100644 --- a/macos/Runner/Handlers/MethodHandler.swift +++ b/macos/Runner/Handlers/MethodHandler.swift @@ -1281,6 +1281,7 @@ class MethodHandler { let email = data["email"] as? String ?? "" let planId = data["planId"] as? String ?? "" let type = data["type"] as? String ?? "" + let couponCode = data["couponCode"] as? String ?? "" let idempotencyKey: String do { idempotencyKey = try self.paymentRedirectIdempotencyKey(from: data) @@ -1294,6 +1295,7 @@ class MethodHandler { planId, email, idempotencyKey, + couponCode, &error ) if let err = error { From a4fea4307b0db908aa74ec0c87cc3427f7eae133 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Thu, 2 Jul 2026 15:28:50 +0530 Subject: [PATCH 06/22] simplify few things --- lib/core/extensions/plan.dart | 41 +++++++------------ lib/core/models/referral_attach_response.dart | 2 - lib/features/auth/choose_payment_method.dart | 18 +++++--- lib/features/plans/plan_item.dart | 7 ++-- 4 files changed, 31 insertions(+), 37 deletions(-) diff --git a/lib/core/extensions/plan.dart b/lib/core/extensions/plan.dart index a9e559293a..b608941e67 100644 --- a/lib/core/extensions/plan.dart +++ b/lib/core/extensions/plan.dart @@ -8,32 +8,15 @@ final _ddmmyyFormatter = DateFormat('dd/MM/yy'); final _mmddyyFormatter = DateFormat('MM/dd/yy'); extension PlanExtension on Plan { - String get formattedYearlyPrice { - return CurrencyUtils.formatCurrency( - double.parse(price.values.first.toString()), - price.keys.first, - ); - } + String get formattedYearlyPrice => _formatPriceMap(price); - String get formattedMonthlyPrice { - return CurrencyUtils.formatCurrency( - double.parse(expectedMonthlyPrice.values.first.toString()), - expectedMonthlyPrice.keys.first, - ); - } + String get formattedMonthlyPrice => _formatPriceMap(expectedMonthlyPrice); /// The original (pre-discount) yearly price, taken directly from the /// backend's `originalPrice` (no calculation). Shown as the strikethrough /// price next to the discounted price when an affiliate code is applied. /// Empty when the backend didn't supply an original price. - String get formatOriginalPrice { - final original = originalPrice; - if (original == null || original.isEmpty) return ''; - return CurrencyUtils.formatCurrency( - double.parse(original.values.first.toString()), - original.keys.first, - ); - } + String get formatOriginalPrice => _formatPriceMap(originalPrice); /// The amount deducted by the affiliate discount: original − discounted /// yearly price, both taken directly from the backend (no percentage math). @@ -42,14 +25,20 @@ extension PlanExtension on Plan { String get formatDiscountAmount { final original = originalPrice; if (original == null || original.isEmpty) return ''; - final originalAmount = double.parse(original.values.first.toString()); - final discountedAmount = double.parse(price.values.first.toString()); - return CurrencyUtils.formatCurrency( - originalAmount - discountedAmount, - price.keys.first, - ); + final deducted = _amountOf(original) - _amountOf(price); + return CurrencyUtils.formatCurrency(deducted, price.keys.first); + } + + /// Formats the first `: amount` entry of [prices] as currency; + /// returns '' when [prices] is null or empty. + String _formatPriceMap(Map? prices) { + if (prices == null || prices.isEmpty) return ''; + return CurrencyUtils.formatCurrency(_amountOf(prices), prices.keys.first); } + double _amountOf(Map prices) => + double.parse(prices.values.first.toString()); + String getDurationText() { final durationMap = {'1y': 'year', '2y': 'two year', '1m': 'month'}; diff --git a/lib/core/models/referral_attach_response.dart b/lib/core/models/referral_attach_response.dart index 285df5b218..75a40391f6 100644 --- a/lib/core/models/referral_attach_response.dart +++ b/lib/core/models/referral_attach_response.dart @@ -27,8 +27,6 @@ class ReferralAttachV2Response { required this.type, }); - bool get isAffiliate => type == ReferralType.affiliate; - factory ReferralAttachV2Response.fromJson(Map json) => ReferralAttachV2Response( plansData: PlansData.fromJson(json)..sortPlansAndProviders(), diff --git a/lib/features/auth/choose_payment_method.dart b/lib/features/auth/choose_payment_method.dart index 3d70c1fec7..82db72cce8 100644 --- a/lib/features/auth/choose_payment_method.dart +++ b/lib/features/auth/choose_payment_method.dart @@ -461,6 +461,14 @@ class PaymentCheckoutMethods extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final referral = ref.watch(referralProvider); final theme = Theme.of(context).textTheme; + // Affiliate: show the full (pre-discount) price on the plan line and the + // deducted amount on the promo line; the discounted total stays in Order + // Total. Computed once here (constant across the provider list). + final originalPrice = userPlan.formatOriginalPrice; + final discountAmount = userPlan.formatDiscountAmount; + final showOriginalPrice = referral.isAffiliate && originalPrice.isNotEmpty; + final showDiscountDeduction = + referral.isAffiliate && discountAmount.isNotEmpty; return ListView.builder( shrinkWrap: true, itemCount: providers.length, @@ -505,9 +513,8 @@ class PaymentCheckoutMethods extends HookConsumerWidget { children: [ Text(userPlan.description, style: theme.bodyMedium), Text( - (referral.isAffiliate && - userPlan.formatOriginalPrice.isNotEmpty) - ? userPlan.formatOriginalPrice + showOriginalPrice + ? originalPrice : userPlan.formattedYearlyPrice, style: theme.bodyMedium!.copyWith( color: context.textDisabled, @@ -538,8 +545,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { ], // Affiliate codes: show the applied promo code and the amount // deducted (original − discounted, from the backend). - if (referral.isAffiliate && - userPlan.formatDiscountAmount.isNotEmpty) ...[ + if (showDiscountDeduction) ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -551,7 +557,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { ), SizedBox(width: defaultSize), Text( - '-${userPlan.formatDiscountAmount}', + '-$discountAmount', style: theme.bodyMedium!.copyWith( color: context.textDisabled, ), diff --git a/lib/features/plans/plan_item.dart b/lib/features/plans/plan_item.dart index a8526ce8db..f2c54db9ac 100644 --- a/lib/features/plans/plan_item.dart +++ b/lib/features/plans/plan_item.dart @@ -29,6 +29,8 @@ class PlanItem extends StatelessWidget { final textTheme = Theme.of(context).textTheme; final width = MediaQuery.of(context).size.width; final finalSize = (width * 0.5) - (defaultSize * 3); + final originalPrice = plan.formatOriginalPrice; + final showOriginalPrice = discountPct > 0 && originalPrice.isNotEmpty; return badges.Badge( showBadge: plan.bestValue ?? false, badgeAnimation: badges.BadgeAnimation.scale(toAnimate: false), @@ -79,11 +81,10 @@ class PlanItem extends StatelessWidget { color: context.textLink, ), ), - if (discountPct > 0 && - plan.formatOriginalPrice.isNotEmpty) ...[ + if (showOriginalPrice) ...[ SizedBox(width: 6), Text( - plan.formatOriginalPrice, + originalPrice, style: textTheme.labelMedium!.copyWith( color: context.textTertiary, decoration: TextDecoration.lineThrough, From 31222dce473dfbda39b941804c615aab85a7f2a2 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Fri, 3 Jul 2026 19:52:45 +0530 Subject: [PATCH 07/22] fix issue with default plan --- lib/features/plans/provider/plans_notifier.dart | 4 +--- lib/features/plans/provider/referral_notifier.dart | 7 +++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/features/plans/provider/plans_notifier.dart b/lib/features/plans/provider/plans_notifier.dart index 0693291f60..1b2ef86eca 100644 --- a/lib/features/plans/provider/plans_notifier.dart +++ b/lib/features/plans/provider/plans_notifier.dart @@ -102,9 +102,7 @@ class PlansNotifier extends _$PlansNotifier { /// discount is session-specific and must not overwrite the cached base /// plans used by non-referral sessions. void updatePlans(PlansData plans) { - appLogger.info( - '[PlansNotifier] updatePlans: ${plans.plans.length} plans (referral)', - ); + appLogger.info('[PlansNotifier] updatePlans: ${plans.plans.length} plans'); state = AsyncData(plans); } diff --git a/lib/features/plans/provider/referral_notifier.dart b/lib/features/plans/provider/referral_notifier.dart index 07d386c507..c39bd46297 100644 --- a/lib/features/plans/provider/referral_notifier.dart +++ b/lib/features/plans/provider/referral_notifier.dart @@ -5,6 +5,8 @@ import 'package:lantern/features/plans/provider/plans_notifier.dart'; import 'package:lantern/lantern/lantern_service_notifier.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import '../../../core/models/plan_data.dart'; + part 'referral_notifier.g.dart'; @Riverpod(keepAlive: true) @@ -27,6 +29,11 @@ class ReferralNotifier extends _$ReferralNotifier { state = response; if (response != null) { ref.read(plansProvider.notifier).updatePlans(response.plansData); + final plans = response.plansData; + final defaultPlan = plans.plans.firstWhere( + (Plan plan) => plan.bestValue == true, + ); + ref.read(plansProvider.notifier).setSelectedPlan(defaultPlan); } } return result; From 370df37de011a30528ac1908d8c3023f0850c4ff Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Fri, 3 Jul 2026 20:13:35 +0530 Subject: [PATCH 08/22] fix default value issue --- lib/features/plans/plans_list.dart | 19 +++++++++++++------ .../plans/provider/plans_notifier.dart | 1 + 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/features/plans/plans_list.dart b/lib/features/plans/plans_list.dart index 5f690b18a1..41d92ae2b0 100644 --- a/lib/features/plans/plans_list.dart +++ b/lib/features/plans/plans_list.dart @@ -22,10 +22,18 @@ class PlansListView extends HookConsumerWidget { final showReferralBonus = referral.isReferral; final discountPct = referral.isAffiliate ? referral.discountPct : 0; final size = MediaQuery.of(context).size; - final plan = useState( - data.plans.firstWhere((Plan plan) => plan.bestValue == true), + final selectedId = useState( + data.plans.firstWhere((Plan plan) => plan.bestValue == true).id, ); - ref.read(plansProvider.notifier).setSelectedPlan(plan.value); + final selectedPlan = data.plans.firstWhere( + (Plan plan) => plan.id == selectedId.value, + orElse: () => + data.plans.firstWhere((Plan plan) => plan.bestValue == true), + ); + useEffect(() { + ref.read(plansProvider.notifier).setSelectedPlan(selectedPlan); + return null; + }, [data, selectedId.value]); return SizedBox( height: context.isSmallDevice ? size.height * 0.21 : null, child: ListView.builder( @@ -42,14 +50,13 @@ class PlansListView extends HookConsumerWidget { final item = data.plans[index]; return PlanItem( plan: item, - planSelected: plan.value.id == item.id, + planSelected: selectedPlan.id == item.id, referralMessage: showReferralBonus ? getReferralMessage(item.id) : '', discountPct: discountPct, onPressed: (plans) { - plan.value = plans; - ref.read(plansProvider.notifier).setSelectedPlan(plans); + selectedId.value = plans.id; }, ); }, diff --git a/lib/features/plans/provider/plans_notifier.dart b/lib/features/plans/provider/plans_notifier.dart index 1b2ef86eca..8c4bd11090 100644 --- a/lib/features/plans/provider/plans_notifier.dart +++ b/lib/features/plans/provider/plans_notifier.dart @@ -107,6 +107,7 @@ class PlansNotifier extends _$PlansNotifier { } void setSelectedPlan(Plan plan) { + appLogger.info('[PlansNotifier] setSelectedPlan: ${plan.id}'); userSelectedPlan = plan; } From 7d004f22e9a2ca098970e5a4201f4c4bd260d0c9 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Tue, 7 Jul 2026 11:32:43 +0530 Subject: [PATCH 09/22] Added stripe support and working on google flow --- .../lantern/handler/MethodHandler.kt | 7 +- assets/locales/en.po | 4 + ios/Runner/Handlers/MethodHandler.swift | 13 +- lantern-core/core.go | 6 +- lantern-core/mobile/mobile.go | 4 +- lib/core/services/app_purchase.dart | 136 ++++++++++++++---- lib/features/plans/plans.dart | 71 +++++---- lib/lantern/lantern_platform_service.dart | 7 +- 8 files changed, 184 insertions(+), 64 deletions(-) diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt b/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt index db902487dd..1a9374beab 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt @@ -835,8 +835,11 @@ class MethodHandler : FlutterPlugin, Methods.AttachReferralCodeV2.method -> { scope.launch { result.runCatching { - val code = call.arguments as String - val response = Mobile.referralAttachmentV2(code) + val code = call.argument("code") ?: error("Missing code") + val distributionChannel = + call.argument("distributionChannel") ?: "" + val response = + Mobile.referralAttachmentV2(code, distributionChannel) withContext(Dispatchers.Main) { success(response) } diff --git a/assets/locales/en.po b/assets/locales/en.po index 412defaea6..909d597630 100644 --- a/assets/locales/en.po +++ b/assets/locales/en.po @@ -1681,6 +1681,10 @@ msgid "promo_referral_code" msgstr "Promo / Referral Code" msgid "promo_code" +msgstr "Promo Code" + + +msgid "promo_code_with" msgstr "Promo Code (%s)" msgid "order_total" diff --git a/ios/Runner/Handlers/MethodHandler.swift b/ios/Runner/Handlers/MethodHandler.swift index 610fa66945..032eeaba99 100644 --- a/ios/Runner/Handlers/MethodHandler.swift +++ b/ios/Runner/Handlers/MethodHandler.swift @@ -166,8 +166,11 @@ class MethodHandler { self.referralAttach(result: result, code: code) case "attachReferralCodeV2": - let code = call.arguments as? String ?? "" - self.referralAttachV2(result: result, code: code) + let data = call.arguments as? [String: Any] ?? [:] + let code = data["code"] as? String ?? "" + let distributionChannel = data["distributionChannel"] as? String ?? "" + self.referralAttachV2( + result: result, code: code, distributionChannel: distributionChannel) // Private server methods case "digitalOcean": @@ -780,10 +783,12 @@ class MethodHandler { } } - func referralAttachV2(result: @escaping FlutterResult, code: String) { + func referralAttachV2( + result: @escaping FlutterResult, code: String, distributionChannel: String + ) { Task { var error: NSError? - let json = MobileReferralAttachmentV2(code, &error) + let json = MobileReferralAttachmentV2(code, distributionChannel, &error) if let error { appLogger.error("Failed to attach referral code v2: \(error.localizedDescription)") await self.handleFlutterError(error, result: result, code: "ATTACH_REFERRAL_CODE_V2_FAILED") diff --git a/lantern-core/core.go b/lantern-core/core.go index 542d6f4f72..205123953b 100644 --- a/lantern-core/core.go +++ b/lantern-core/core.go @@ -75,7 +75,7 @@ type App interface { UpdateConfig() error ClearTunnelCache() error ReferralAttachment(referralCode string) (bool, error) - ReferralAttachmentV2(referralCode string) ([]byte, error) + ReferralAttachmentV2(referralCode, channel string) ([]byte, error) UpdateLocale(locale string) error UpdateTelemetryConsent(consent bool) error IsTelemetryEnabled() bool @@ -861,8 +861,8 @@ func (lc *LanternCore) ReferralAttachment(referralCode string) (bool, error) { // ReferralAttachmentV2 attaches a referral code and returns the resulting // plans, providers, code, and discount marshalled as JSON. -func (lc *LanternCore) ReferralAttachmentV2(referralCode string) ([]byte, error) { - resp, err := lc.client.ReferralAttachV2(lc.ctx, referralCode) +func (lc *LanternCore) ReferralAttachmentV2(referralCode, channel string) ([]byte, error) { + resp, err := lc.client.ReferralAttachV2(lc.ctx, referralCode, channel) if err != nil { return nil, err } diff --git a/lantern-core/mobile/mobile.go b/lantern-core/mobile/mobile.go index abccee90da..26b472d6c3 100644 --- a/lantern-core/mobile/mobile.go +++ b/lantern-core/mobile/mobile.go @@ -693,9 +693,9 @@ func ReferralAttachment(referralCode string) error { // ReferralAttachmentV2 attaches a referral code and returns the resulting // plans, providers, code, and discount as a JSON string. -func ReferralAttachmentV2(referralCode string) (string, error) { +func ReferralAttachmentV2(referralCode, channel string) (string, error) { return withCoreR(func(c lanterncore.Core) (string, error) { - b, err := c.ReferralAttachmentV2(referralCode) + b, err := c.ReferralAttachmentV2(referralCode, channel) return string(b), err }) } diff --git a/lib/core/services/app_purchase.dart b/lib/core/services/app_purchase.dart index f4de000bf0..f4237987e4 100644 --- a/lib/core/services/app_purchase.dart +++ b/lib/core/services/app_purchase.dart @@ -126,24 +126,59 @@ class AppPurchase { return ready; } - Future fetchSubscriptions({int maxAttempts = 3}) async { - // If a fetch is already running, piggy-back on its result. - if (_productsLoadedCompleter != null) { - return _productsLoadedCompleter!.future; + /// Loads the subscription SKUs to purchase from. + /// + /// By default only base plans are kept ([includeOffers] false), so the first + /// fetch never charges a promotional offer. After a referral/affiliate code + /// is applied, call again with [includeOffers] true to load only the offer + /// SKUs — then the discounted offer is what gets charged. Either way + /// [_normalizePlan] just matches by plan prefix, so the rest of the purchase + /// logic is identical for plans and offers. + Future fetchSubscriptions({ + bool includeOffers = false, + int maxAttempts = 3, + }) async { + // Piggy-back only on a fetch that's still running; a completed one must + // re-run so the SKU set can switch between base plans and offers. + final inFlight = _productsLoadedCompleter; + if (inFlight != null && !inFlight.isCompleted) { + return inFlight.future; } _productsLoaded = false; - _productsLoadedCompleter = Completer(); + final completer = Completer(); + _productsLoadedCompleter = completer; for (int attempt = 0; attempt < maxAttempts; attempt++) { try { appLogger.info( - '[AppPurchase] Fetching subscriptions, attempt: ${attempt + 1}/$maxAttempts', + '[AppPurchase] Fetching subscriptions (includeOffers=$includeOffers), ' + 'attempt: ${attempt + 1}/$maxAttempts', ); final response = await _inAppPurchase.queryProductDetails( _subscriptionIds.toSet(), ); + // Debug: dump exactly what the store returned, including per-offer + // basePlanId/offerId, so we can see whether Play is actually handing + // back an offer (vs our filter dropping it or the account being + // ineligible). Remove once offer visibility is confirmed. + for (final p in response.productDetails) { + if (p is GooglePlayProductDetails) { + final index = p.subscriptionIndex; + final offers = p.productDetails.subscriptionOfferDetails; + final offer = (index != null && offers != null && index < offers.length) + ? offers[index] + : null; + appLogger.info( + '[AppPurchase] SKU productId=${p.id} ' + 'basePlanId=${offer?.basePlanId} offerId=${offer?.offerId}', + ); + } else { + appLogger.info('[AppPurchase] SKU productId=${p.id} (non-Play)'); + } + } + if (response.error != null) { appLogger.error( '[AppPurchase] Error fetching subscriptions: ${response.error}', @@ -153,19 +188,37 @@ class AppPurchase { '[AppPurchase] Fetched 0 subscriptions. notFoundIDs=${response.notFoundIDs}', ); } else { - _subscriptionSku - ..clear() - ..addAll(response.productDetails); - - _productsLoaded = true; - if (!(_productsLoadedCompleter?.isCompleted ?? true)) { - _productsLoadedCompleter?.complete(); - } + final products = _selectSkus( + response.productDetails, + includeOffers: includeOffers, + ); appLogger.info( - '[AppPurchase] Fetched subscriptions: ${_subscriptionSku.length} items', + '[AppPurchase] Fetched ${response.productDetails.length} ' + 'subscriptions, ${products.length} match ' + '(includeOffers=$includeOffers)', ); - return; + if (products.isNotEmpty) { + _subscriptionSku + ..clear() + ..addAll(products); + _productsLoaded = true; + if (!completer.isCompleted) completer.complete(); + return; + } + + // The store is reachable and returned SKUs, but none matched the + // requested set (e.g. offers requested but none are active). + // Retrying the same query won't change that, and this isn't a + // Play-unreachable signal, so fail fast without marking the region + // censored. Callers fall back to the base-plan fetch. + final error = StateError( + 'No ${includeOffers ? 'offer' : 'base plan'} SKUs available', + ); + if (!completer.isCompleted) completer.completeError(error); + throw error; } + } on StateError { + rethrow; } catch (e, st) { appLogger.error('[AppPurchase] Error fetching subscriptions', e, st); } @@ -189,11 +242,7 @@ class AppPurchase { final error = StateError( 'Unable to load in-app purchase products after $maxAttempts attempts', ); - // Safely complete the completer with an error, if it is still pending. - if (_productsLoadedCompleter != null && - !_productsLoadedCompleter!.isCompleted) { - _productsLoadedCompleter!.completeError(error); - } + if (!completer.isCompleted) completer.completeError(error); throw error; } @@ -530,17 +579,56 @@ class AppPurchase { _onError?.call(error.toString()); } + /// Keeps only the SKUs matching the requested mode. + /// + /// On Android each Play Console offer comes back as its own + /// [GooglePlayProductDetails]: base plans have a null (or empty) offerId, + /// discount offers have a non-null offerId. When [includeOffers] is false we + /// keep base plans (a normal purchase), when true we keep only offers (an + /// applied affiliate/referral discount). iOS has no per-offer concept, so its + /// products always pass through. + List _selectSkus( + List products, { + required bool includeOffers, + }) { + if (!Platform.isAndroid) { + return products; + } + return products.where((product) { + final offerId = _offerIdFor(product); + final isOffer = offerId != null && offerId.isNotEmpty; + return includeOffers ? isOffer : !isOffer; + }).toList(); + } + + /// The Play Console offerId backing [product], or null when it's a base plan + /// (or not an Android subscription product). + String? _offerIdFor(ProductDetails product) { + if (product is! GooglePlayProductDetails) { + return null; + } + final index = product.subscriptionIndex; + final offers = product.productDetails.subscriptionOfferDetails; + if (index == null || offers == null || index >= offers.length) { + return null; + } + return offers[index].offerId; + } + + /// Resolves the store product to purchase for [planId] by matching the plan + /// prefix (e.g. "1y"). Whether this returns a base plan or a discount offer + /// depends purely on which SKU set was loaded by [fetchSubscriptions]. ProductDetails? _normalizePlan(String planId) { final plan = planId.split('-').first; appLogger.info('[AppPurchase] Normalizing planId: $planId to plan: $plan'); for (final sku in _subscriptionSku) { - final subId = sku.id.split('_').first; - if (subId == plan) { + if (sku.id.split('_').first == plan) { return sku; } } appLogger.error( - '[AppPurchase] No matching product found for planId: $planId _subscriptionSku length: ${_subscriptionSku.length}', + '[AppPurchase] No matching product found for planId: $planId ' + '_subscriptionSku length: ${_subscriptionSku.length}', ); return null; } diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index 8ac499041c..27cd0ee368 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -241,11 +241,22 @@ class _PlansState extends ConsumerState } /// Removes the applied affiliate code: clears the referral state and - /// re-fetches the original (non-discounted) plans. - void _onRemoveAffiliateCode() { + /// re-fetches the original (non-discounted) plans. On a Play Store build the + /// SKUs are also reloaded back to base plans so a later purchase is charged + /// at full price rather than the (now removed) offer. + Future _onRemoveAffiliateCode() async { appLogger.info('Removing applied affiliate code'); ref.read(referralProvider.notifier).resetReferral(); ref.read(plansProvider.notifier).fetchPlans(); + if (isStoreVersion()) { + try { + await sl().fetchSubscriptions(); + } catch (e) { + appLogger.warning( + '[Plans] Failed to reload base SKUs after removing code: $e', + ); + } + } } void onMenuTap() { @@ -260,10 +271,12 @@ class _PlansState extends ConsumerState padding: EdgeInsets.zero, controller: scrollController, children: [ - if (!isStoreVersion() && !isReferralApplied) ...{ + if (!isReferralApplied) ...{ AppTile( icon: AppImagePaths.star, - label: 'promo_referral_code'.i18n, + label: isStoreVersion() + ? 'promo_code'.i18n + : 'promo_referral_code'.i18n, onPressed: () { context.pop(); showReferralCodeDialog(); @@ -304,7 +317,7 @@ class _PlansState extends ConsumerState AppImage(path: AppImagePaths.star, height: 48), SizedBox(height: defaultSize), Text( - 'promo_referral_code'.i18n, + isStoreVersion() ? 'promo_code'.i18n : 'promo_referral_code'.i18n, style: textTheme.headlineSmall!.copyWith( color: context.textPrimary, ), @@ -347,29 +360,35 @@ class _PlansState extends ConsumerState final result = await ref .read(referralProvider.notifier) .applyReferralCodeV2(code); + if (!mounted) return; - result.fold( - (error) { - if (!mounted) { - return; - } - appLogger.error('Error applying referral code: $error'); - context.hideLoadingDialog(); - AppDialog.errorDialog( - context: context, - title: 'error'.i18n, - content: error.localizedErrorMessage, + final failure = result.getLeft().toNullable(); + if (failure != null) { + appLogger.error('Error applying referral code: $failure'); + context.hideLoadingDialog(); + AppDialog.errorDialog( + context: context, + title: 'error'.i18n, + content: failure.localizedErrorMessage, + ); + return; + } + + appLogger.info('Successfully applied referral code'); + if (isStoreVersion()) { + try { + appLogger.info('Reloading SKUs after applying referral code'); + await sl().fetchSubscriptions(includeOffers: true); + } catch (e) { + appLogger.warning( + '[Plans] No offer SKUs after applying code; base plan will be used: $e', ); - }, - (success) { - if (!mounted) { - return; - } - context.hideLoadingDialog(); - context.showSnackBar('referral_code_applied'.i18n); - appLogger.info('Successfully applied referral code'); - }, - ); + } + if (!mounted) return; + } + + context.hideLoadingDialog(); + context.showSnackBar('referral_code_applied'.i18n); } void onGetLanternProTap() { diff --git a/lib/lantern/lantern_platform_service.dart b/lib/lantern/lantern_platform_service.dart index 02b7405d83..cae353cc86 100644 --- a/lib/lantern/lantern_platform_service.dart +++ b/lib/lantern/lantern_platform_service.dart @@ -12,18 +12,18 @@ import 'package:lantern/core/models/available_servers.dart'; import 'package:lantern/core/models/datacap_info.dart'; import 'package:lantern/core/models/macos_extension_state.dart'; import 'package:lantern/core/models/plan_data.dart'; +import 'package:lantern/core/models/private_server_status.dart'; import 'package:lantern/core/models/referral_attach_response.dart'; import 'package:lantern/core/models/restore_subscription_response.dart'; -import 'package:lantern/core/models/private_server_status.dart'; import 'package:lantern/core/models/server_location.dart'; import 'package:lantern/core/models/user.dart'; import 'package:lantern/core/services/app_purchase.dart'; import 'package:lantern/core/services/injection_container.dart'; import 'package:lantern/core/utils/app_data_utils.dart'; import 'package:lantern/core/utils/enabled_apps.dart'; +import 'package:lantern/features/report_issue/models/report_issue_attachment.dart'; import 'package:lantern/lantern/lantern_core_service.dart'; import 'package:lantern/lantern/lantern_ffi_service.dart'; -import 'package:lantern/features/report_issue/models/report_issue_attachment.dart'; import '../core/models/lantern_status.dart'; import '../core/services/injection_container.dart' show sl; @@ -1518,9 +1518,10 @@ class LanternPlatformService implements LanternCoreService { String code, ) async { try { + final distributionChannel = isStoreVersion() ? 'store' : 'non-store'; final result = await _methodChannel.invokeMethod( 'attachReferralCodeV2', - code, + {'code': code, 'distributionChannel': distributionChannel}, ); final response = ReferralAttachV2Response.fromJson(jsonDecode(result!)); return right(response); From b5a47dfa0eb6f641db5ad41261f622df9e37facf Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Tue, 7 Jul 2026 17:14:12 +0530 Subject: [PATCH 10/22] Affiliate support for Google --- .../lantern/handler/MethodHandler.kt | 3 +- ios/Runner/Handlers/MethodHandler.swift | 10 +- lantern-core/core.go | 17 ++- lantern-core/mobile/mobile.go | 8 +- lib/core/common/app_dialog.dart | 7 +- lib/core/common/app_theme.dart | 8 ++ lib/core/services/app_purchase.dart | 131 +++++++++++++++++- lib/features/plans/plans.dart | 4 + .../plans/provider/payment_notifier.dart | 11 +- lib/lantern/lantern_core_service.dart | 2 + lib/lantern/lantern_ffi_service.dart | 2 + lib/lantern/lantern_platform_service.dart | 4 + lib/lantern/lantern_service.dart | 5 + 13 files changed, 194 insertions(+), 18 deletions(-) diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt b/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt index 1a9374beab..7582472cf2 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt @@ -461,7 +461,8 @@ class MethodHandler : FlutterPlugin, val map = call.arguments as Map<*, *> val subscriptionData = Mobile.acknowledgeGooglePurchase( map["purchaseToken"] as String, - map["planId"] as String + map["planId"] as String, + map["couponCode"] as? String ?: "" ) withContext(Dispatchers.Main) { success(subscriptionData.toByteArray(Charsets.UTF_8)) diff --git a/ios/Runner/Handlers/MethodHandler.swift b/ios/Runner/Handlers/MethodHandler.swift index 032eeaba99..30649fb664 100644 --- a/ios/Runner/Handlers/MethodHandler.swift +++ b/ios/Runner/Handlers/MethodHandler.swift @@ -90,7 +90,9 @@ class MethodHandler { ) return } - self.acknowledgeInAppPurchase(token: token, planId: planId, result: result) + let couponCode = map["couponCode"] as? String ?? "" + self.acknowledgeInAppPurchase( + token: token, planId: planId, couponCode: couponCode, result: result) case "restoreInAppPurchase": guard @@ -565,10 +567,12 @@ class MethodHandler { } } - func acknowledgeInAppPurchase(token: String, planId: String, result: @escaping FlutterResult) { + func acknowledgeInAppPurchase( + token: String, planId: String, couponCode: String, result: @escaping FlutterResult + ) { Task { var error: NSError? - let json = MobileAcknowledgeApplePurchase(token, planId, &error) + let json = MobileAcknowledgeApplePurchase(token, planId, couponCode, &error) if let error { await self.handleFlutterError(error, result: result, code: "ACKNOWLEDGE_FAILED") return diff --git a/lantern-core/core.go b/lantern-core/core.go index 205123953b..7c3ca9886a 100644 --- a/lantern-core/core.go +++ b/lantern-core/core.go @@ -123,8 +123,8 @@ type Payment interface { StripeSubscription(email, planID, couponCode string) (string, error) Plans(channel string) (string, error) StripeBillingPortalUrl() (string, error) - AcknowledgeGooglePurchase(purchaseToken, planId string) (string, error) - AcknowledgeApplePurchase(receipt, planII string) (string, error) + AcknowledgeGooglePurchase(purchaseToken, planId, couponCode string) (string, error) + AcknowledgeApplePurchase(receipt, planII, couponCode string) (string, error) RestoreGooglePlayPurchase(purchaseToken string) (string, error) RestoreApplePurchase(receipt string) (string, error) PaymentRedirect(provider, planID, email, idempotencyKey string) (string, error) @@ -885,19 +885,28 @@ func (lc *LanternCore) StripeBillingPortalUrl() (string, error) { return lc.client.StripeBillingPortalURL(lc.ctx) } -func (lc *LanternCore) AcknowledgeGooglePurchase(purchaseToken, planId string) (string, error) { +func (lc *LanternCore) AcknowledgeGooglePurchase(purchaseToken, planId, couponCode string) (string, error) { params := map[string]string{ "purchaseToken": purchaseToken, "planId": planId, } + // Affiliate/referral attribution: forward the applied code so the backend + // can credit the purchase to the affiliate. Google Play already applies the + // price discount via the offer SKU; this only carries attribution. + if couponCode != "" { + params["couponCode"] = couponCode + } return lc.client.VerifySubscription(lc.ctx, account.GoogleService, params) } -func (lc *LanternCore) AcknowledgeApplePurchase(receipt, planII string) (string, error) { +func (lc *LanternCore) AcknowledgeApplePurchase(receipt, planII, couponCode string) (string, error) { params := map[string]string{ "receipt": receipt, "planId": planII, } + if couponCode != "" { + params["couponCode"] = couponCode + } return lc.client.VerifySubscription(lc.ctx, account.AppleService, params) } diff --git a/lantern-core/mobile/mobile.go b/lantern-core/mobile/mobile.go index 26b472d6c3..34ea01197b 100644 --- a/lantern-core/mobile/mobile.go +++ b/lantern-core/mobile/mobile.go @@ -510,9 +510,9 @@ func StripeBillingPortalUrl() (string, error) { return withCoreR(func(c lanterncore.Core) (string, error) { return c.StripeBillingPortalUrl() }) } -func AcknowledgeGooglePurchase(purchaseToken, planId string) (string, error) { +func AcknowledgeGooglePurchase(purchaseToken, planId, couponCode string) (string, error) { return withCoreR(func(c lanterncore.Core) (string, error) { - data, err := c.AcknowledgeGooglePurchase(purchaseToken, planId) + data, err := c.AcknowledgeGooglePurchase(purchaseToken, planId, couponCode) if err != nil { return "", err } @@ -542,9 +542,9 @@ func AcknowledgeGooglePurchase(purchaseToken, planId string) (string, error) { }) } -func AcknowledgeApplePurchase(receipt, planII string) (string, error) { +func AcknowledgeApplePurchase(receipt, planII, couponCode string) (string, error) { return withCoreR(func(c lanterncore.Core) (string, error) { - data, err := c.AcknowledgeApplePurchase(receipt, planII) + data, err := c.AcknowledgeApplePurchase(receipt, planII, couponCode) if err != nil { return "", err } diff --git a/lib/core/common/app_dialog.dart b/lib/core/common/app_dialog.dart index 33cd5cdc8b..c32405998c 100644 --- a/lib/core/common/app_dialog.dart +++ b/lib/core/common/app_dialog.dart @@ -186,7 +186,7 @@ class AppDialog { actionsAlignment: MainAxisAlignment.end, actionsOverflowAlignment: OverflowBarAlignment.end, // backgroundColor and shape come from dialogTheme in app_theme.dart - contentPadding: EdgeInsets.symmetric(horizontal: size24), + contentPadding: EdgeInsets.symmetric(horizontal: defaultSize), actionsPadding: actionPadding ?? EdgeInsets.only( @@ -195,7 +195,10 @@ class AppDialog { left: defaultSize, right: defaultSize, ), - content: content, + // AlertDialog sizes to the content's intrinsic width, which collapses + // this custom content. maxFinite makes it fill instead; the min/max + // width bounds come from dialogTheme (Material 280–560dp). + content: SizedBox(width: double.maxFinite, child: content), actions: action, ); }, diff --git a/lib/core/common/app_theme.dart b/lib/core/common/app_theme.dart index 5795a3b3cd..ee9ea2496c 100644 --- a/lib/core/common/app_theme.dart +++ b/lib/core/common/app_theme.dart @@ -181,6 +181,10 @@ class AppTheme { ), titleTextStyle: AppTextStyles.headingSmall, contentTextStyle: AppTextStyles.bodyMedium, + // Material dialog width: min 280dp, max 560dp (M3 guideline). Applied + // app-wide so dialogs stay a sensible width on phones, tablets and + // desktop instead of collapsing to content or going full-bleed. + constraints: const BoxConstraints(minWidth: 280, maxWidth: 560), ), // ── Bottom Sheet ───────────────────────────────────────────────────────── @@ -395,6 +399,10 @@ class AppTheme { ), titleTextStyle: AppTextStyles.headingSmall, contentTextStyle: AppTextStyles.bodyMedium, + // Material dialog width: min 280dp, max 560dp (M3 guideline). Applied + // app-wide so dialogs stay a sensible width on phones, tablets and + // desktop instead of collapsing to content or going full-bleed. + constraints: const BoxConstraints(minWidth: 280, maxWidth: 560), ), // ── Bottom Sheet ───────────────────────────────────────────────────────── diff --git a/lib/core/services/app_purchase.dart b/lib/core/services/app_purchase.dart index f4237987e4..72c60b71b7 100644 --- a/lib/core/services/app_purchase.dart +++ b/lib/core/services/app_purchase.dart @@ -16,6 +16,7 @@ typedef PaymentErrorCallback = void Function(String error); class AppPurchase { static const _pendingPurchasePlansKey = 'pending_purchase_plans_json'; + static const _pendingPurchaseCouponsKey = 'pending_purchase_coupons_json'; static const _productPlanKeyPrefix = 'product:'; static const _transactionPlanKeyPrefix = 'transaction:'; static const _ackRetryDelays = [ @@ -44,6 +45,13 @@ class AppPurchase { // Track what plan the user selected String? _pendingPlanId; + // The affiliate/referral code applied when the purchase started, if any. + // Forwarded to the backend at acknowledgment so the sale is attributed to + // the affiliate. Empty when no code is applied. Persisted per-purchase + // (alongside the plan id) so background acknowledge retries and store + // re-delivery after a restart keep the attribution. + String _pendingCouponCode = ''; + // True while a restore flow is in progress; restored receipts should be // acknowledged so the backend can reassociate the user, even when the // device has no active subscription cached locally. @@ -271,11 +279,16 @@ class AppPurchase { required String plan, required PaymentSuccessCallback onSuccess, required void Function(String error) onError, + String couponCode = '', }) async { _onSuccess = onSuccess; _onError = onError; // Store the exact plan id user chose (ex: "1y-usd-10") _pendingPlanId = plan; + // Capture the applied affiliate/referral code so it can be sent with the + // acknowledgment. Reset to '' when none so a prior purchase's code never + // leaks into an unrelated one. + _pendingCouponCode = couponCode; if (!await _ensurePurchaseStreamReady()) { _onError?.call( @@ -314,6 +327,7 @@ class AppPurchase { '[AppPurchase] Initiating purchase for product: ${product.id} with pendingPlanId: $_pendingPlanId', ); await _rememberPendingPlanForProduct(product.id, plan); + await _rememberPendingCouponForProduct(product.id, couponCode); final started = await _inAppPurchase.buyNonConsumable( purchaseParam: purchaseParam, ); @@ -343,6 +357,7 @@ class AppPurchase { _isRestoreFlow = true; _restoreReceivedAny = false; _pendingPlanId = null; + _pendingCouponCode = ''; if (!await _ensurePurchaseStreamReady()) { _isRestoreFlow = false; @@ -538,8 +553,14 @@ class AppPurchase { '[AppPurchase] Purchase successful: ${purchaseDetails.productID}', ); final planId = await _resolvePlanId(purchaseDetails); + final couponCode = await _resolveCouponCode(purchaseDetails); await _rememberPendingPlanForPurchase(purchaseDetails, planId); - await _acknowledgePurchase(purchaseDetails, planId: planId); + await _rememberPendingCouponForPurchase(purchaseDetails, couponCode); + await _acknowledgePurchase( + purchaseDetails, + planId: planId, + couponCode: couponCode, + ); } catch (e, st) { await _handleAcknowledgeFailure(purchaseDetails, e, stackTrace: st); } @@ -565,6 +586,7 @@ class AppPurchase { appLogger.error('[AppPurchase] Error finalizing purchase: $e', e); } finally { _pendingPlanId = null; + _pendingCouponCode = ''; _isRestoreFlow = false; } } @@ -694,16 +716,31 @@ class AppPurchase { return '$prefix-usd-10'; } + /// Resolves the affiliate/referral code to send with acknowledgment. + /// + /// Prefers the code captured for the in-flight purchase; falls back to the + /// value persisted for this purchase so background retries and store + /// re-delivery after a restart still attribute the sale. Returns '' when no + /// code was applied. + Future _resolveCouponCode(PurchaseDetails purchase) async { + if (_pendingCouponCode.isNotEmpty) { + return _pendingCouponCode; + } + return await _pendingCouponForPurchase(purchase) ?? ''; + } + void clearCallbacks() { _onSuccess = null; _onError = null; _pendingPlanId = null; + _pendingCouponCode = ''; _isRestoreFlow = false; } Future _acknowledgePurchase( PurchaseDetails purchaseDetails, { required String planId, + String couponCode = '', bool isRetry = false, }) async { final key = _purchaseRetryKey(purchaseDetails); @@ -738,6 +775,7 @@ class AppPurchase { final ack = await lanternService.acknowledgeInAppPurchase( purchaseToken: purchaseToken, planId: planId, + couponCode: couponCode, ); await ack.fold( @@ -827,11 +865,13 @@ class AppPurchase { unawaited(() async { try { final planId = await _resolvePlanId(purchaseDetails); + final couponCode = await _resolveCouponCode(purchaseDetails); _ackRetryTimers.remove(key); _ackRetryAttempts[key] = (_ackRetryAttempts[key] ?? 0) + 1; await _acknowledgePurchase( purchaseDetails, planId: planId, + couponCode: couponCode, isRetry: true, ); } catch (e, st) { @@ -937,20 +977,24 @@ class AppPurchase { } Future _forgetPendingPlan(PurchaseDetails purchase) async { - if (await _forgetPendingPlanKeys(_planKeysForPurchase(purchase))) { + final keys = _planKeysForPurchase(purchase); + if (await _forgetPendingPlanKeys(keys)) { appLogger.info( '[AppPurchase] Cleared pending purchase plan: ' 'productID=${purchase.productID} purchaseID=${purchase.purchaseID}', ); } + await _forgetPendingCouponKeys(keys); } Future _forgetPendingPlanForProduct(String productID) async { - if (await _forgetPendingPlanKeys([_productPlanKey(productID)])) { + final keys = [_productPlanKey(productID)]; + if (await _forgetPendingPlanKeys(keys)) { appLogger.info( '[AppPurchase] Cleared pending purchase plan for productID=$productID', ); } + await _forgetPendingCouponKeys(keys); } /// Removes [keys] from the pending plans map, persisting only when something @@ -972,4 +1016,85 @@ class AppPurchase { Future _savePendingPurchasePlans(Map pending) => sl().setStringMap(_pendingPurchasePlansKey, pending); + + // --- Pending affiliate/referral code persistence --------------------------- + // Mirrors the pending-plan storage above, keyed by the same product/ + // transaction keys so the code applied at purchase time survives a restart + // and is available when the store re-delivers the purchase for acknowledgment. + + Future _pendingCouponForPurchase(PurchaseDetails purchase) async { + final pending = await _loadPendingPurchaseCoupons(); + for (final key in _planKeysForPurchase(purchase)) { + final coupon = pending[key]; + if (coupon != null && coupon.isNotEmpty) { + return coupon; + } + } + return null; + } + + Future _rememberPendingCouponForProduct( + String productID, + String couponCode, + ) async { + if (await _rememberPendingCoupon([_productPlanKey(productID)], couponCode)) { + appLogger.info( + '[AppPurchase] Stored pending purchase coupon: productID=$productID', + ); + } + } + + Future _rememberPendingCouponForPurchase( + PurchaseDetails purchase, + String couponCode, + ) async { + if (await _rememberPendingCoupon( + _planKeysForPurchase(purchase), + couponCode, + )) { + appLogger.info( + '[AppPurchase] Stored pending purchase coupon: ' + 'productID=${purchase.productID} purchaseID=${purchase.purchaseID}', + ); + } + } + + /// Stores [couponCode] under each of [keys], persisting only when it is + /// non-empty. Returns whether anything was written. + Future _rememberPendingCoupon( + List keys, + String couponCode, + ) async { + if (couponCode.isEmpty) { + return false; + } + final pending = await _loadPendingPurchaseCoupons(); + for (final key in keys) { + pending[key] = couponCode; + } + await _savePendingPurchaseCoupons(pending); + return true; + } + + /// Removes [keys] from the pending coupons map, persisting only when + /// something actually changed. + Future _forgetPendingCouponKeys(List keys) async { + final pending = await _loadPendingPurchaseCoupons(); + var changed = false; + for (final key in keys) { + changed = pending.remove(key) != null || changed; + } + if (changed) { + await _savePendingPurchaseCoupons(pending); + } + } + + Future> _loadPendingPurchaseCoupons() => + sl().getStringMap(_pendingPurchaseCouponsKey); + + Future _savePendingPurchaseCoupons(Map pending) => + sl().setStringMap( + _pendingPurchaseCouponsKey, + pending, + ); } diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index 27cd0ee368..4e1f2140fe 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -465,9 +465,13 @@ class _PlansState extends ConsumerState // backs out before signup completes — same protection Stripe and the // external paymentRedirect flow already get. ref.read(paymentSessionProvider.notifier).markRedirectInitiated(); + // Forward any applied affiliate/referral code so the purchase is attributed + // to the affiliate at acknowledgment. Empty when no code is applied. + final couponCode = ref.read(referralProvider).code; final payments = ref.read(paymentProvider.notifier); final result = await payments.startInAppPurchaseFlow( planId: plan.id, + couponCode: couponCode, onSuccess: (purchase) => processPurchase(purchase, plan), onError: (error) { ref.read(paymentSessionProvider.notifier).clearRedirect(); diff --git a/lib/features/plans/provider/payment_notifier.dart b/lib/features/plans/provider/payment_notifier.dart index 18de6e25b4..b0076ca28b 100644 --- a/lib/features/plans/provider/payment_notifier.dart +++ b/lib/features/plans/provider/payment_notifier.dart @@ -31,6 +31,7 @@ class PaymentNotifier extends _$PaymentNotifier { required String planId, required PaymentSuccessCallback onSuccess, required PaymentErrorCallback onError, + String couponCode = '', }) async { return ref .read(lanternServiceProvider) @@ -38,16 +39,22 @@ class PaymentNotifier extends _$PaymentNotifier { planId: planId, onSuccess: onSuccess, onError: onError, + couponCode: couponCode, ); } Future> acknowledgeInAppPurchase({ required String purchaseToken, required String planId, + String couponCode = '', }) async { return ref .read(lanternServiceProvider) - .acknowledgeInAppPurchase(purchaseToken: purchaseToken, planId: planId); + .acknowledgeInAppPurchase( + purchaseToken: purchaseToken, + planId: planId, + couponCode: couponCode, + ); } Future> restoreInAppPurchase({ @@ -109,6 +116,7 @@ class PaymentNotifier extends _$PaymentNotifier { required PaymentSuccessCallback onSuccess, required PaymentErrorCallback onError, required String provider, + String couponCode = '', }) async { if (_isAndroidStoreBuild) { // Google Play build uses IAP @@ -116,6 +124,7 @@ class PaymentNotifier extends _$PaymentNotifier { planId: planId, onSuccess: onSuccess, onError: onError, + couponCode: couponCode, ); return result.match((failure) => left(failure), (_) => right(null)); diff --git a/lib/lantern/lantern_core_service.dart b/lib/lantern/lantern_core_service.dart index e70228291c..8c8ef68814 100644 --- a/lib/lantern/lantern_core_service.dart +++ b/lib/lantern/lantern_core_service.dart @@ -118,11 +118,13 @@ abstract class LanternCoreService { required String planId, required PaymentSuccessCallback onSuccess, required PaymentErrorCallback onError, + String couponCode = '', }); Future> acknowledgeInAppPurchase({ required String purchaseToken, required String planId, + String couponCode = '', }); /// Restores a previously purchased subscription. Mobile-only. diff --git a/lib/lantern/lantern_ffi_service.dart b/lib/lantern/lantern_ffi_service.dart index 12e0eb0518..c640e61ca6 100644 --- a/lib/lantern/lantern_ffi_service.dart +++ b/lib/lantern/lantern_ffi_service.dart @@ -734,6 +734,7 @@ class LanternFFIService implements LanternCoreService { required String planId, required PaymentSuccessCallback onSuccess, required PaymentErrorCallback onError, + String couponCode = '', }) { throw UnimplementedError("This not supported on desktop"); } @@ -894,6 +895,7 @@ class LanternFFIService implements LanternCoreService { Future> acknowledgeInAppPurchase({ required String purchaseToken, required String planId, + String couponCode = '', }) { throw UnimplementedError( "This is not supported on desktop; this is only for mobile", diff --git a/lib/lantern/lantern_platform_service.dart b/lib/lantern/lantern_platform_service.dart index cae353cc86..29fbec2b52 100644 --- a/lib/lantern/lantern_platform_service.dart +++ b/lib/lantern/lantern_platform_service.dart @@ -636,12 +636,14 @@ class LanternPlatformService implements LanternCoreService { required String planId, required PaymentSuccessCallback onSuccess, required PaymentErrorCallback onError, + String couponCode = '', }) async { try { await sl().startSubscription( plan: planId, onSuccess: onSuccess, onError: onError, + couponCode: couponCode, ); return Right(unit); } catch (e) { @@ -802,11 +804,13 @@ class LanternPlatformService implements LanternCoreService { Future> acknowledgeInAppPurchase({ required String purchaseToken, required String planId, + String couponCode = '', }) async { try { await _methodChannel.invokeMethod('acknowledgeInAppPurchase', { 'purchaseToken': purchaseToken, 'planId': planId, + 'couponCode': couponCode, }); return Right('ok'); } catch (e, stackTrace) { diff --git a/lib/lantern/lantern_service.dart b/lib/lantern/lantern_service.dart index c09721ca8d..e3c72b99dd 100644 --- a/lib/lantern/lantern_service.dart +++ b/lib/lantern/lantern_service.dart @@ -154,6 +154,7 @@ class LanternService implements LanternCoreService { required String planId, required PaymentSuccessCallback onSuccess, required PaymentErrorCallback onError, + String couponCode = '', }) { if (PlatformUtils.isFFISupported) { throw UnimplementedError(); @@ -162,6 +163,7 @@ class LanternService implements LanternCoreService { planId: planId, onSuccess: onSuccess, onError: onError, + couponCode: couponCode, ); } @@ -345,16 +347,19 @@ class LanternService implements LanternCoreService { Future> acknowledgeInAppPurchase({ required String purchaseToken, required String planId, + String couponCode = '', }) { if (PlatformUtils.isFFISupported) { return _ffiService.acknowledgeInAppPurchase( purchaseToken: purchaseToken, planId: planId, + couponCode: couponCode, ); } return _platformService.acknowledgeInAppPurchase( purchaseToken: purchaseToken, planId: planId, + couponCode: couponCode, ); } From fd985571bf9813d904d224fc65093a43ca359b5f Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Tue, 7 Jul 2026 19:54:01 +0530 Subject: [PATCH 11/22] added affiliate flow support for alipay --- .../lantern/handler/MethodHandler.kt | 3 +- lantern-core/core.go | 5 +- lantern-core/ffi/ffi.go | 10 +- lantern-core/mobile/mobile.go | 4 +- lib/features/auth/choose_payment_method.dart | 1 + .../plans/provider/payment_notifier.dart | 15 +- lib/lantern/lantern_core_service.dart | 1 + lib/lantern/lantern_ffi_service.dart | 7 +- lib/lantern/lantern_generated_bindings.dart | 10202 ++++++++-------- lib/lantern/lantern_platform_service.dart | 2 + lib/lantern/lantern_service.dart | 3 + 11 files changed, 4902 insertions(+), 5351 deletions(-) diff --git a/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt b/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt index 7582472cf2..bf8afccde4 100644 --- a/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt +++ b/android/app/src/main/kotlin/org/getlantern/lantern/handler/MethodHandler.kt @@ -509,7 +509,8 @@ class MethodHandler : FlutterPlugin, map["provider"] as String, map["planId"] as String, map["email"] as String, - idempotencyKey + idempotencyKey, + map["couponCode"] as? String ?: "" ) withContext(Dispatchers.Main) { success(url) diff --git a/lantern-core/core.go b/lantern-core/core.go index 7c3ca9886a..dc6ee0a80c 100644 --- a/lantern-core/core.go +++ b/lantern-core/core.go @@ -127,7 +127,7 @@ type Payment interface { AcknowledgeApplePurchase(receipt, planII, couponCode string) (string, error) RestoreGooglePlayPurchase(purchaseToken string) (string, error) RestoreApplePurchase(receipt string) (string, error) - PaymentRedirect(provider, planID, email, idempotencyKey string) (string, error) + PaymentRedirect(provider, planID, email, idempotencyKey, couponCode string) (string, error) ActivationCode(email, resellerCode string) error SubscriptionPaymentRedirectURL(redirectBody account.PaymentRedirectData) (string, error) StripeSubscriptionPaymentRedirect(subscriptionType, planID, email, idempotencyKey, couponCode string) (string, error) @@ -964,7 +964,7 @@ func (lc *LanternCore) StripeSubscriptionPaymentRedirect(subscriptionType, planI return lc.SubscriptionPaymentRedirectURL(redirectBody) } -func (lc *LanternCore) PaymentRedirect(provider, planId, email, idempotencyKey string) (string, error) { +func (lc *LanternCore) PaymentRedirect(provider, planId, email, idempotencyKey, couponCode string) (string, error) { idempotencyKey, err := normalizePaymentRedirectIdempotencyKey(idempotencyKey) if err != nil { return "", err @@ -976,6 +976,7 @@ func (lc *LanternCore) PaymentRedirect(provider, planId, email, idempotencyKey s DeviceName: deviceName, Email: email, IdempotencyKey: idempotencyKey, + CouponCode: couponCode, } return lc.client.PaymentRedirect(lc.ctx, body) } diff --git a/lantern-core/ffi/ffi.go b/lantern-core/ffi/ffi.go index 3b85998630..8cb0779dd5 100644 --- a/lantern-core/ffi/ffi.go +++ b/lantern-core/ffi/ffi.go @@ -667,17 +667,18 @@ func stripeSubscriptionPaymentRedirect(subType, _planId, _email, _idempotencyKey // Fetch payment redirect link for providers like alipay // //export paymentRedirect -func paymentRedirect(_plan, _provider, _email, _idempotencyKey *C.char) *C.char { +func paymentRedirect(_plan, _provider, _email, _idempotencyKey, _couponCode *C.char) *C.char { plan := C.GoString(_plan) provider := C.GoString(_provider) email := C.GoString(_email) idempotencyKey := C.GoString(_idempotencyKey) + couponCode := C.GoString(_couponCode) return runOnGoStack(func() *C.char { c, errStr := requireCore() if errStr != nil { return errStr } - redirect, err := c.PaymentRedirect(provider, plan, email, idempotencyKey) + redirect, err := c.PaymentRedirect(provider, plan, email, idempotencyKey, couponCode) if err != nil { return SendError(err) } @@ -897,14 +898,15 @@ func referralAttachment(_referralCode *C.char) *C.char { // returns the resulting plans, providers, code, and discount as JSON. // //export referralAttachmentV2 -func referralAttachmentV2(_referralCode *C.char) *C.char { +func referralAttachmentV2(_referralCode, _channel *C.char) *C.char { referralCode := C.GoString(_referralCode) + channel := C.GoString(_channel) return runOnGoStack(func() *C.char { c, errStr := requireCore() if errStr != nil { return errStr } - bytes, err := c.ReferralAttachmentV2(referralCode) + bytes, err := c.ReferralAttachmentV2(referralCode, channel) if err != nil { return SendError(err) } diff --git a/lantern-core/mobile/mobile.go b/lantern-core/mobile/mobile.go index 34ea01197b..8a1cb97b45 100644 --- a/lantern-core/mobile/mobile.go +++ b/lantern-core/mobile/mobile.go @@ -607,9 +607,9 @@ func restoreSubscription(c lanterncore.Core, fn func(string) (string, error), to return data, nil } -func PaymentRedirect(provider, planId, email, idempotencyKey string) (string, error) { +func PaymentRedirect(provider, planId, email, idempotencyKey, couponCode string) (string, error) { return withCoreR(func(c lanterncore.Core) (string, error) { - return c.PaymentRedirect(provider, planId, email, idempotencyKey) + return c.PaymentRedirect(provider, planId, email, idempotencyKey, couponCode) }) } diff --git a/lib/features/auth/choose_payment_method.dart b/lib/features/auth/choose_payment_method.dart index 82db72cce8..ab686d806f 100644 --- a/lib/features/auth/choose_payment_method.dart +++ b/lib/features/auth/choose_payment_method.dart @@ -330,6 +330,7 @@ class ChoosePaymentMethod extends HookConsumerWidget { provider: provider, planId: userPlan.id, email: email, + couponCode: _affiliateCoupon(ref), ); if (!context.mounted) return; diff --git a/lib/features/plans/provider/payment_notifier.dart b/lib/features/plans/provider/payment_notifier.dart index b0076ca28b..3c18dc76bf 100644 --- a/lib/features/plans/provider/payment_notifier.dart +++ b/lib/features/plans/provider/payment_notifier.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:fpdart/fpdart.dart'; import 'package:lantern/core/common/common.dart'; import 'package:lantern/core/models/restore_subscription_response.dart'; @@ -25,8 +23,6 @@ class PaymentNotifier extends _$PaymentNotifier { @override void build() {} - bool get _isAndroidStoreBuild => Platform.isAndroid && isStoreVersion(); - Future> startInAppPurchaseFlow({ required String planId, required PaymentSuccessCallback onSuccess, @@ -90,13 +86,18 @@ class PaymentNotifier extends _$PaymentNotifier { }) async { return ref .read(lanternServiceProvider) - .stipeSubscription(planId: planId, email: email, couponCode: couponCode); + .stipeSubscription( + planId: planId, + email: email, + couponCode: couponCode, + ); } Future> paymentRedirect({ required String provider, required String planId, required String email, + String couponCode = '', }) async { final idempotencyKey = generatePaymentRedirectIdempotencyKey(); return ref @@ -106,6 +107,7 @@ class PaymentNotifier extends _$PaymentNotifier { planId: planId, email: email, idempotencyKey: idempotencyKey, + couponCode: couponCode, ); } @@ -118,7 +120,7 @@ class PaymentNotifier extends _$PaymentNotifier { required String provider, String couponCode = '', }) async { - if (_isAndroidStoreBuild) { + if (isStoreVersion()) { // Google Play build uses IAP final result = await startInAppPurchaseFlow( planId: planId, @@ -135,6 +137,7 @@ class PaymentNotifier extends _$PaymentNotifier { provider: provider, planId: planId, email: email, + couponCode: couponCode, ); return redirectResult.match( diff --git a/lib/lantern/lantern_core_service.dart b/lib/lantern/lantern_core_service.dart index 8c8ef68814..c53be1f91b 100644 --- a/lib/lantern/lantern_core_service.dart +++ b/lib/lantern/lantern_core_service.dart @@ -102,6 +102,7 @@ abstract class LanternCoreService { required String planId, required String email, required String idempotencyKey, + String couponCode = '', }); // this is used for stripe subscription diff --git a/lib/lantern/lantern_ffi_service.dart b/lib/lantern/lantern_ffi_service.dart index c640e61ca6..3eda80dafb 100644 --- a/lib/lantern/lantern_ffi_service.dart +++ b/lib/lantern/lantern_ffi_service.dart @@ -917,6 +917,7 @@ class LanternFFIService implements LanternCoreService { required String planId, required String email, required String idempotencyKey, + String couponCode = '', }) async { try { final result = await runInBackground(() async { @@ -925,6 +926,7 @@ class LanternFFIService implements LanternCoreService { provider.toCharPtr, email.toCharPtr, idempotencyKey.toCharPtr, + couponCode.toCharPtr, ); try { return resultPtr.toDartString(); @@ -1426,8 +1428,11 @@ class LanternFFIService implements LanternCoreService { String code, ) async { try { + final distributionChannel = isStoreVersion() ? 'store' : 'non-store'; final result = await runInBackground(() async { - return _ffiService.referralAttachmentV2(code.toCharPtr).toDartString(); + return _ffiService + .referralAttachmentV2(code.toCharPtr, distributionChannel.toCharPtr) + .toDartString(); }); checkAPIError(result); final response = ReferralAttachV2Response.fromJson(jsonDecode(result)); diff --git a/lib/lantern/lantern_generated_bindings.dart b/lib/lantern/lantern_generated_bindings.dart index 6319214058..450854fd31 100644 --- a/lib/lantern/lantern_generated_bindings.dart +++ b/lib/lantern/lantern_generated_bindings.dart @@ -18,162 +18,6 @@ class LanternBindings { ffi.Pointer Function(String symbolName) lookup, ) : _lookup = lookup; - void __va_start(ffi.Pointer arg0) { - return ___va_start(arg0); - } - - late final ___va_startPtr = - _lookup)>>( - '__va_start', - ); - late final ___va_start = ___va_startPtr - .asFunction)>(); - - void __security_init_cookie() { - return ___security_init_cookie(); - } - - late final ___security_init_cookiePtr = - _lookup>( - '__security_init_cookie', - ); - late final ___security_init_cookie = ___security_init_cookiePtr - .asFunction(); - - void __security_check_cookie(int _StackCookie) { - return ___security_check_cookie(_StackCookie); - } - - late final ___security_check_cookiePtr = - _lookup>( - '__security_check_cookie', - ); - late final ___security_check_cookie = ___security_check_cookiePtr - .asFunction(); - - void __report_gsfailure(int _StackCookie) { - return ___report_gsfailure(_StackCookie); - } - - late final ___report_gsfailurePtr = - _lookup>( - '__report_gsfailure', - ); - late final ___report_gsfailure = ___report_gsfailurePtr - .asFunction(); - - late final ffi.Pointer ___security_cookie = _lookup( - '__security_cookie', - ); - - int get __security_cookie => ___security_cookie.value; - - set __security_cookie(int value) => ___security_cookie.value = value; - - void _invalid_parameter_noinfo() { - return __invalid_parameter_noinfo(); - } - - late final __invalid_parameter_noinfoPtr = - _lookup>( - '_invalid_parameter_noinfo', - ); - late final __invalid_parameter_noinfo = __invalid_parameter_noinfoPtr - .asFunction(); - - void _invalid_parameter_noinfo_noreturn() { - return __invalid_parameter_noinfo_noreturn(); - } - - late final __invalid_parameter_noinfo_noreturnPtr = - _lookup>( - '_invalid_parameter_noinfo_noreturn', - ); - late final __invalid_parameter_noinfo_noreturn = - __invalid_parameter_noinfo_noreturnPtr.asFunction(); - - void _invoke_watson( - ffi.Pointer _Expression, - ffi.Pointer _FunctionName, - ffi.Pointer _FileName, - int _LineNo, - int _Reserved, - ) { - return __invoke_watson( - _Expression, - _FunctionName, - _FileName, - _LineNo, - _Reserved, - ); - } - - late final __invoke_watsonPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.UnsignedInt, - ffi.UintPtr, - ) - > - >('_invoke_watson'); - late final __invoke_watson = __invoke_watsonPtr - .asFunction< - void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, - int, - ) - >(); - - ffi.Pointer _errno() { - return __errno(); - } - - late final __errnoPtr = - _lookup Function()>>('_errno'); - late final __errno = __errnoPtr.asFunction Function()>(); - - int _set_errno(int _Value) { - return __set_errno(_Value); - } - - late final __set_errnoPtr = - _lookup>('_set_errno'); - late final __set_errno = __set_errnoPtr.asFunction(); - - int _get_errno(ffi.Pointer _Value) { - return __get_errno(_Value); - } - - late final __get_errnoPtr = - _lookup)>>( - '_get_errno', - ); - late final __get_errno = __get_errnoPtr - .asFunction)>(); - - int __threadid() { - return ___threadid(); - } - - late final ___threadidPtr = - _lookup>('__threadid'); - late final ___threadid = ___threadidPtr.asFunction(); - - int __threadhandle() { - return ___threadhandle(); - } - - late final ___threadhandlePtr = - _lookup>('__threadhandle'); - late final ___threadhandle = ___threadhandlePtr.asFunction(); - int _GoStringLen(_GoString_ s) { return __GoStringLen(s); } @@ -196,6752 +40,6192 @@ class LanternBindings { late final __GoStringPtr = __GoStringPtrPtr .asFunction Function(_GoString_)>(); - ffi.Pointer _calloc_base(int _Count, int _Size) { - return __calloc_base(_Count, _Size); + ffi.Pointer> signal( + int arg0, + ffi.Pointer> arg1, + ) { + return _signal(arg0, arg1); } - late final __calloc_basePtr = + late final _signalPtr = _lookup< - ffi.NativeFunction Function(ffi.Size, ffi.Size)> - >('_calloc_base'); - late final __calloc_base = __calloc_basePtr - .asFunction Function(int, int)>(); + ffi.NativeFunction< + ffi.Pointer> Function( + ffi.Int, + ffi.Pointer>, + ) + > + >('signal'); + late final _signal = _signalPtr + .asFunction< + ffi.Pointer> Function( + int, + ffi.Pointer>, + ) + >(); - ffi.Pointer calloc(int _Count, int _Size) { - return _calloc(_Count, _Size); + int getpriority(int arg0, int arg1) { + return _getpriority(arg0, arg1); } - late final _callocPtr = - _lookup< - ffi.NativeFunction Function(ffi.Size, ffi.Size)> - >('calloc'); - late final _calloc = _callocPtr - .asFunction Function(int, int)>(); + late final _getpriorityPtr = + _lookup>( + 'getpriority', + ); + late final _getpriority = _getpriorityPtr + .asFunction(); - int _callnewh(int _Size) { - return __callnewh(_Size); + int getiopolicy_np(int arg0, int arg1) { + return _getiopolicy_np(arg0, arg1); } - late final __callnewhPtr = - _lookup>('_callnewh'); - late final __callnewh = __callnewhPtr.asFunction(); + late final _getiopolicy_npPtr = + _lookup>( + 'getiopolicy_np', + ); + late final _getiopolicy_np = _getiopolicy_npPtr + .asFunction(); - ffi.Pointer _expand(ffi.Pointer _Block, int _Size) { - return __expand(_Block, _Size); + int getrlimit(int arg0, ffi.Pointer arg1) { + return _getrlimit(arg0, arg1); } - late final __expandPtr = + late final _getrlimitPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer, ffi.Size) - > - >('_expand'); - late final __expand = __expandPtr - .asFunction Function(ffi.Pointer, int)>(); - - void _free_base(ffi.Pointer _Block) { - return __free_base(_Block); - } - - late final __free_basePtr = - _lookup)>>( - '_free_base', - ); - late final __free_base = __free_basePtr - .asFunction)>(); + ffi.NativeFunction)> + >('getrlimit'); + late final _getrlimit = _getrlimitPtr + .asFunction)>(); - void free(ffi.Pointer _Block) { - return _free(_Block); + int getrusage(int arg0, ffi.Pointer arg1) { + return _getrusage(arg0, arg1); } - late final _freePtr = - _lookup)>>( - 'free', - ); - late final _free = _freePtr - .asFunction)>(); + late final _getrusagePtr = + _lookup< + ffi.NativeFunction)> + >('getrusage'); + late final _getrusage = _getrusagePtr + .asFunction)>(); - ffi.Pointer _malloc_base(int _Size) { - return __malloc_base(_Size); + int setpriority(int arg0, int arg1, int arg2) { + return _setpriority(arg0, arg1, arg2); } - late final __malloc_basePtr = - _lookup Function(ffi.Size)>>( - '_malloc_base', + late final _setpriorityPtr = + _lookup>( + 'setpriority', ); - late final __malloc_base = __malloc_basePtr - .asFunction Function(int)>(); + late final _setpriority = _setpriorityPtr + .asFunction(); - ffi.Pointer malloc(int _Size) { - return _malloc(_Size); + int setiopolicy_np(int arg0, int arg1, int arg2) { + return _setiopolicy_np(arg0, arg1, arg2); } - late final _mallocPtr = - _lookup Function(ffi.Size)>>( - 'malloc', + late final _setiopolicy_npPtr = + _lookup>( + 'setiopolicy_np', ); - late final _malloc = _mallocPtr - .asFunction Function(int)>(); + late final _setiopolicy_np = _setiopolicy_npPtr + .asFunction(); - int _msize_base(ffi.Pointer _Block) { - return __msize_base(_Block); + int setrlimit(int arg0, ffi.Pointer arg1) { + return _setrlimit(arg0, arg1); } - late final __msize_basePtr = - _lookup)>>( - '_msize_base', - ); - late final __msize_base = __msize_basePtr - .asFunction)>(); + late final _setrlimitPtr = + _lookup< + ffi.NativeFunction)> + >('setrlimit'); + late final _setrlimit = _setrlimitPtr + .asFunction)>(); - int _msize(ffi.Pointer _Block) { - return __msize(_Block); + int wait(ffi.Pointer arg0) { + return _wait(arg0); } - late final __msizePtr = - _lookup)>>( - '_msize', - ); - late final __msize = __msizePtr - .asFunction)>(); + late final _waitPtr = + _lookup)>>('wait'); + late final _wait = _waitPtr.asFunction)>(); - ffi.Pointer _realloc_base(ffi.Pointer _Block, int _Size) { - return __realloc_base(_Block, _Size); + int waitpid(int arg0, ffi.Pointer arg1, int arg2) { + return _waitpid(arg0, arg1, arg2); } - late final __realloc_basePtr = + late final _waitpidPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer, ffi.Size) - > - >('_realloc_base'); - late final __realloc_base = __realloc_basePtr - .asFunction Function(ffi.Pointer, int)>(); + ffi.NativeFunction, ffi.Int)> + >('waitpid'); + late final _waitpid = _waitpidPtr + .asFunction, int)>(); - ffi.Pointer realloc(ffi.Pointer _Block, int _Size) { - return _realloc(_Block, _Size); + int waitid( + idtype_t arg0, + Dart__uint32_t arg1, + ffi.Pointer arg2, + int arg3, + ) { + return _waitid(arg0.value, arg1, arg2, arg3); } - late final _reallocPtr = + late final _waitidPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer, ffi.Size) + ffi.Int Function( + ffi.UnsignedInt, + id_t, + ffi.Pointer, + ffi.Int, + ) > - >('realloc'); - late final _realloc = _reallocPtr - .asFunction Function(ffi.Pointer, int)>(); + >('waitid'); + late final _waitid = _waitidPtr + .asFunction, int)>(); - ffi.Pointer _recalloc_base( - ffi.Pointer _Block, - int _Count, - int _Size, - ) { - return __recalloc_base(_Block, _Count, _Size); + int wait3(ffi.Pointer arg0, int arg1, ffi.Pointer arg2) { + return _wait3(arg0, arg1, arg2); } - late final __recalloc_basePtr = + late final _wait3Ptr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Size, - ffi.Size, - ) + pid_t Function(ffi.Pointer, ffi.Int, ffi.Pointer) > - >('_recalloc_base'); - late final __recalloc_base = __recalloc_basePtr + >('wait3'); + late final _wait3 = _wait3Ptr .asFunction< - ffi.Pointer Function(ffi.Pointer, int, int) + int Function(ffi.Pointer, int, ffi.Pointer) >(); - ffi.Pointer _recalloc( - ffi.Pointer _Block, - int _Count, - int _Size, + int wait4( + int arg0, + ffi.Pointer arg1, + int arg2, + ffi.Pointer arg3, ) { - return __recalloc(_Block, _Count, _Size); + return _wait4(arg0, arg1, arg2, arg3); } - late final __recallocPtr = + late final _wait4Ptr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Size, - ffi.Size, + pid_t Function( + pid_t, + ffi.Pointer, + ffi.Int, + ffi.Pointer, ) > - >('_recalloc'); - late final __recalloc = __recallocPtr + >('wait4'); + late final _wait4 = _wait4Ptr .asFunction< - ffi.Pointer Function(ffi.Pointer, int, int) + int Function(int, ffi.Pointer, int, ffi.Pointer) >(); - void _aligned_free(ffi.Pointer _Block) { - return __aligned_free(_Block); + ffi.Pointer alloca(int __size) { + return _alloca(__size); } - late final __aligned_freePtr = - _lookup)>>( - '_aligned_free', + late final _allocaPtr = + _lookup Function(ffi.Size)>>( + 'alloca', ); - late final __aligned_free = __aligned_freePtr - .asFunction)>(); + late final _alloca = _allocaPtr + .asFunction Function(int)>(); + + late final ffi.Pointer ___mb_cur_max = _lookup( + '__mb_cur_max', + ); + + int get __mb_cur_max => ___mb_cur_max.value; - ffi.Pointer _aligned_malloc(int _Size, int _Alignment) { - return __aligned_malloc(_Size, _Alignment); + set __mb_cur_max(int value) => ___mb_cur_max.value = value; + + ffi.Pointer malloc_type_malloc(int size, int type_id) { + return _malloc_type_malloc(size, type_id); } - late final __aligned_mallocPtr = + late final _malloc_type_mallocPtr = _lookup< - ffi.NativeFunction Function(ffi.Size, ffi.Size)> - >('_aligned_malloc'); - late final __aligned_malloc = __aligned_mallocPtr + ffi.NativeFunction< + ffi.Pointer Function(ffi.Size, malloc_type_id_t) + > + >('malloc_type_malloc'); + late final _malloc_type_malloc = _malloc_type_mallocPtr .asFunction Function(int, int)>(); - ffi.Pointer _aligned_offset_malloc( - int _Size, - int _Alignment, - int _Offset, - ) { - return __aligned_offset_malloc(_Size, _Alignment, _Offset); + ffi.Pointer malloc_type_calloc(int count, int size, int type_id) { + return _malloc_type_calloc(count, size, type_id); } - late final __aligned_offset_mallocPtr = + late final _malloc_type_callocPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function(ffi.Size, ffi.Size, ffi.Size) + ffi.Pointer Function(ffi.Size, ffi.Size, malloc_type_id_t) > - >('_aligned_offset_malloc'); - late final __aligned_offset_malloc = __aligned_offset_mallocPtr + >('malloc_type_calloc'); + late final _malloc_type_calloc = _malloc_type_callocPtr .asFunction Function(int, int, int)>(); - int _aligned_msize( - ffi.Pointer _Block, - int _Alignment, - int _Offset, - ) { - return __aligned_msize(_Block, _Alignment, _Offset); + void malloc_type_free(ffi.Pointer ptr, int type_id) { + return _malloc_type_free(ptr, type_id); } - late final __aligned_msizePtr = + late final _malloc_type_freePtr = _lookup< ffi.NativeFunction< - ffi.Size Function(ffi.Pointer, ffi.Size, ffi.Size) + ffi.Void Function(ffi.Pointer, malloc_type_id_t) > - >('_aligned_msize'); - late final __aligned_msize = __aligned_msizePtr - .asFunction, int, int)>(); - - ffi.Pointer _aligned_offset_realloc( - ffi.Pointer _Block, - int _Size, - int _Alignment, - int _Offset, + >('malloc_type_free'); + late final _malloc_type_free = _malloc_type_freePtr + .asFunction, int)>(); + + ffi.Pointer malloc_type_realloc( + ffi.Pointer ptr, + int size, + int type_id, ) { - return __aligned_offset_realloc(_Block, _Size, _Alignment, _Offset); + return _malloc_type_realloc(ptr, size, type_id); } - late final __aligned_offset_reallocPtr = + late final _malloc_type_reallocPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( ffi.Pointer, ffi.Size, - ffi.Size, - ffi.Size, + malloc_type_id_t, ) > - >('_aligned_offset_realloc'); - late final __aligned_offset_realloc = __aligned_offset_reallocPtr + >('malloc_type_realloc'); + late final _malloc_type_realloc = _malloc_type_reallocPtr .asFunction< - ffi.Pointer Function(ffi.Pointer, int, int, int) + ffi.Pointer Function(ffi.Pointer, int, int) >(); - ffi.Pointer _aligned_offset_recalloc( - ffi.Pointer _Block, - int _Count, - int _Size, - int _Alignment, - int _Offset, + ffi.Pointer malloc_type_valloc(int size, int type_id) { + return _malloc_type_valloc(size, type_id); + } + + late final _malloc_type_vallocPtr = + _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Size, malloc_type_id_t) + > + >('malloc_type_valloc'); + late final _malloc_type_valloc = _malloc_type_vallocPtr + .asFunction Function(int, int)>(); + + ffi.Pointer malloc_type_aligned_alloc( + int alignment, + int size, + int type_id, ) { - return __aligned_offset_recalloc( - _Block, - _Count, - _Size, - _Alignment, - _Offset, - ); + return _malloc_type_aligned_alloc(alignment, size, type_id); } - late final __aligned_offset_recallocPtr = + late final _malloc_type_aligned_allocPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Size, - ffi.Size, - ffi.Size, - ffi.Size, - ) + ffi.Pointer Function(ffi.Size, ffi.Size, malloc_type_id_t) > - >('_aligned_offset_recalloc'); - late final __aligned_offset_recalloc = __aligned_offset_recallocPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - int, - int, - int, - int, - ) - >(); + >('malloc_type_aligned_alloc'); + late final _malloc_type_aligned_alloc = _malloc_type_aligned_allocPtr + .asFunction Function(int, int, int)>(); - ffi.Pointer _aligned_realloc( - ffi.Pointer _Block, - int _Size, - int _Alignment, + int malloc_type_posix_memalign( + ffi.Pointer> memptr, + int alignment, + int size, + int type_id, ) { - return __aligned_realloc(_Block, _Size, _Alignment); + return _malloc_type_posix_memalign(memptr, alignment, size, type_id); } - late final __aligned_reallocPtr = + late final _malloc_type_posix_memalignPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, + ffi.Int Function( + ffi.Pointer>, ffi.Size, ffi.Size, + malloc_type_id_t, ) > - >('_aligned_realloc'); - late final __aligned_realloc = __aligned_reallocPtr + >('malloc_type_posix_memalign'); + late final _malloc_type_posix_memalign = _malloc_type_posix_memalignPtr .asFunction< - ffi.Pointer Function(ffi.Pointer, int, int) + int Function(ffi.Pointer>, int, int, int) >(); - ffi.Pointer _aligned_recalloc( - ffi.Pointer _Block, - int _Count, - int _Size, - int _Alignment, + ffi.Pointer malloc_type_zone_malloc( + ffi.Pointer zone, + int size, + int type_id, ) { - return __aligned_recalloc(_Block, _Count, _Size, _Alignment); + return _malloc_type_zone_malloc(zone, size, type_id); } - late final __aligned_recallocPtr = + late final _malloc_type_zone_mallocPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( - ffi.Pointer, - ffi.Size, - ffi.Size, + ffi.Pointer, ffi.Size, + malloc_type_id_t, ) > - >('_aligned_recalloc'); - late final __aligned_recalloc = __aligned_recallocPtr + >('malloc_type_zone_malloc'); + late final _malloc_type_zone_malloc = _malloc_type_zone_mallocPtr .asFunction< - ffi.Pointer Function(ffi.Pointer, int, int, int) + ffi.Pointer Function(ffi.Pointer, int, int) >(); - ffi.Pointer bsearch_s( - ffi.Pointer _Key, - ffi.Pointer _Base, - int _NumOfElements, - int _SizeOfElements, - _CoreCrtSecureSearchSortCompareFunction _CompareFunction, - ffi.Pointer _Context, + ffi.Pointer malloc_type_zone_calloc( + ffi.Pointer zone, + int count, + int size, + int type_id, ) { - return _bsearch_s( - _Key, - _Base, - _NumOfElements, - _SizeOfElements, - _CompareFunction, - _Context, - ); + return _malloc_type_zone_calloc(zone, count, size, type_id); } - late final _bsearch_sPtr = + late final _malloc_type_zone_callocPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - rsize_t, - rsize_t, - _CoreCrtSecureSearchSortCompareFunction, - ffi.Pointer, + ffi.Pointer, + ffi.Size, + ffi.Size, + malloc_type_id_t, ) > - >('bsearch_s'); - late final _bsearch_s = _bsearch_sPtr + >('malloc_type_zone_calloc'); + late final _malloc_type_zone_calloc = _malloc_type_zone_callocPtr .asFunction< ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, + ffi.Pointer, + int, int, int, - _CoreCrtSecureSearchSortCompareFunction, - ffi.Pointer, ) >(); - void qsort_s( - ffi.Pointer _Base, - int _NumOfElements, - int _SizeOfElements, - _CoreCrtSecureSearchSortCompareFunction _CompareFunction, - ffi.Pointer _Context, + void malloc_type_zone_free( + ffi.Pointer zone, + ffi.Pointer ptr, + int type_id, ) { - return _qsort_s( - _Base, - _NumOfElements, - _SizeOfElements, - _CompareFunction, - _Context, - ); + return _malloc_type_zone_free(zone, ptr, type_id); } - late final _qsort_sPtr = + late final _malloc_type_zone_freePtr = _lookup< ffi.NativeFunction< ffi.Void Function( + ffi.Pointer, ffi.Pointer, - rsize_t, - rsize_t, - _CoreCrtSecureSearchSortCompareFunction, - ffi.Pointer, + malloc_type_id_t, ) > - >('qsort_s'); - late final _qsort_s = _qsort_sPtr + >('malloc_type_zone_free'); + late final _malloc_type_zone_free = _malloc_type_zone_freePtr .asFunction< - void Function( - ffi.Pointer, - int, - int, - _CoreCrtSecureSearchSortCompareFunction, - ffi.Pointer, - ) + void Function(ffi.Pointer, ffi.Pointer, int) >(); - ffi.Pointer bsearch( - ffi.Pointer _Key, - ffi.Pointer _Base, - int _NumOfElements, - int _SizeOfElements, - _CoreCrtNonSecureSearchSortCompareFunction _CompareFunction, + ffi.Pointer malloc_type_zone_realloc( + ffi.Pointer zone, + ffi.Pointer ptr, + int size, + int type_id, ) { - return _bsearch( - _Key, - _Base, - _NumOfElements, - _SizeOfElements, - _CompareFunction, - ); + return _malloc_type_zone_realloc(zone, ptr, size, type_id); } - late final _bsearchPtr = + late final _malloc_type_zone_reallocPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( - ffi.Pointer, + ffi.Pointer, ffi.Pointer, ffi.Size, - ffi.Size, - _CoreCrtNonSecureSearchSortCompareFunction, + malloc_type_id_t, ) > - >('bsearch'); - late final _bsearch = _bsearchPtr + >('malloc_type_zone_realloc'); + late final _malloc_type_zone_realloc = _malloc_type_zone_reallocPtr .asFunction< ffi.Pointer Function( - ffi.Pointer, + ffi.Pointer, ffi.Pointer, int, int, - _CoreCrtNonSecureSearchSortCompareFunction, ) >(); - void qsort( - ffi.Pointer _Base, - int _NumOfElements, - int _SizeOfElements, - _CoreCrtNonSecureSearchSortCompareFunction _CompareFunction, + ffi.Pointer malloc_type_zone_valloc( + ffi.Pointer zone, + int size, + int type_id, ) { - return _qsort(_Base, _NumOfElements, _SizeOfElements, _CompareFunction); + return _malloc_type_zone_valloc(zone, size, type_id); } - late final _qsortPtr = + late final _malloc_type_zone_vallocPtr = _lookup< ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Size, + ffi.Pointer Function( + ffi.Pointer, ffi.Size, - _CoreCrtNonSecureSearchSortCompareFunction, + malloc_type_id_t, ) > - >('qsort'); - late final _qsort = _qsortPtr + >('malloc_type_zone_valloc'); + late final _malloc_type_zone_valloc = _malloc_type_zone_vallocPtr .asFunction< - void Function( - ffi.Pointer, - int, - int, - _CoreCrtNonSecureSearchSortCompareFunction, - ) + ffi.Pointer Function(ffi.Pointer, int, int) >(); - ffi.Pointer _lfind_s( - ffi.Pointer _Key, - ffi.Pointer _Base, - ffi.Pointer _NumOfElements, - int _SizeOfElements, - _CoreCrtSecureSearchSortCompareFunction _CompareFunction, - ffi.Pointer _Context, + ffi.Pointer malloc_type_zone_memalign( + ffi.Pointer zone, + int alignment, + int size, + int type_id, ) { - return __lfind_s( - _Key, - _Base, - _NumOfElements, - _SizeOfElements, - _CompareFunction, - _Context, - ); + return _malloc_type_zone_memalign(zone, alignment, size, type_id); } - late final __lfind_sPtr = + late final _malloc_type_zone_memalignPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, + ffi.Pointer, ffi.Size, - _CoreCrtSecureSearchSortCompareFunction, - ffi.Pointer, + ffi.Size, + malloc_type_id_t, ) > - >('_lfind_s'); - late final __lfind_s = __lfind_sPtr + >('malloc_type_zone_memalign'); + late final _malloc_type_zone_memalign = _malloc_type_zone_memalignPtr .asFunction< ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, + ffi.Pointer, + int, + int, int, - _CoreCrtSecureSearchSortCompareFunction, - ffi.Pointer, ) >(); - ffi.Pointer _lfind( - ffi.Pointer _Key, - ffi.Pointer _Base, - ffi.Pointer _NumOfElements, - int _SizeOfElements, - _CoreCrtNonSecureSearchSortCompareFunction _CompareFunction, - ) { - return __lfind( - _Key, - _Base, - _NumOfElements, - _SizeOfElements, - _CompareFunction, - ); + ffi.Pointer malloc(int __size) { + return _malloc(__size); + } + + late final _mallocPtr = + _lookup Function(ffi.Size)>>( + 'malloc', + ); + late final _malloc = _mallocPtr + .asFunction Function(int)>(); + + ffi.Pointer calloc(int __count, int __size) { + return _calloc(__count, __size); } - late final __lfindPtr = + late final _callocPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.UnsignedInt, - _CoreCrtNonSecureSearchSortCompareFunction, - ) + ffi.NativeFunction Function(ffi.Size, ffi.Size)> + >('calloc'); + late final _calloc = _callocPtr + .asFunction Function(int, int)>(); + + void free(ffi.Pointer arg0) { + return _free(arg0); + } + + late final _freePtr = + _lookup)>>( + 'free', + ); + late final _free = _freePtr + .asFunction)>(); + + ffi.Pointer realloc(ffi.Pointer __ptr, int __size) { + return _realloc(__ptr, __size); + } + + late final _reallocPtr = + _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, ffi.Size) > - >('_lfind'); - late final __lfind = __lfindPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, - _CoreCrtNonSecureSearchSortCompareFunction, - ) - >(); + >('realloc'); + late final _realloc = _reallocPtr + .asFunction Function(ffi.Pointer, int)>(); + + ffi.Pointer reallocf(ffi.Pointer __ptr, int __size) { + return _reallocf(__ptr, __size); + } + + late final _reallocfPtr = + _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, ffi.Size) + > + >('reallocf'); + late final _reallocf = _reallocfPtr + .asFunction Function(ffi.Pointer, int)>(); + + ffi.Pointer valloc(int __size) { + return _valloc(__size); + } + + late final _vallocPtr = + _lookup Function(ffi.Size)>>( + 'valloc', + ); + late final _valloc = _vallocPtr + .asFunction Function(int)>(); + + ffi.Pointer aligned_alloc(int __alignment, int __size) { + return _aligned_alloc(__alignment, __size); + } + + late final _aligned_allocPtr = + _lookup< + ffi.NativeFunction Function(ffi.Size, ffi.Size)> + >('aligned_alloc'); + late final _aligned_alloc = _aligned_allocPtr + .asFunction Function(int, int)>(); - ffi.Pointer _lsearch_s( - ffi.Pointer _Key, - ffi.Pointer _Base, - ffi.Pointer _NumOfElements, - int _SizeOfElements, - _CoreCrtSecureSearchSortCompareFunction _CompareFunction, - ffi.Pointer _Context, + int posix_memalign( + ffi.Pointer> __memptr, + int __alignment, + int __size, ) { - return __lsearch_s( - _Key, - _Base, - _NumOfElements, - _SizeOfElements, - _CompareFunction, - _Context, - ); + return _posix_memalign(__memptr, __alignment, __size); } - late final __lsearch_sPtr = + late final _posix_memalignPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, + ffi.Int Function( + ffi.Pointer>, + ffi.Size, ffi.Size, - _CoreCrtSecureSearchSortCompareFunction, - ffi.Pointer, ) > - >('_lsearch_s'); - late final __lsearch_s = __lsearch_sPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, - _CoreCrtSecureSearchSortCompareFunction, - ffi.Pointer, - ) - >(); + >('posix_memalign'); + late final _posix_memalign = _posix_memalignPtr + .asFunction>, int, int)>(); - ffi.Pointer _lsearch( - ffi.Pointer _Key, - ffi.Pointer _Base, - ffi.Pointer _NumOfElements, - int _SizeOfElements, - _CoreCrtNonSecureSearchSortCompareFunction _CompareFunction, - ) { - return __lsearch( - _Key, - _Base, - _NumOfElements, - _SizeOfElements, - _CompareFunction, - ); + void abort() { + return _abort(); + } + + late final _abortPtr = _lookup>( + 'abort', + ); + late final _abort = _abortPtr.asFunction(); + + int abs(int arg0) { + return _abs(arg0); + } + + late final _absPtr = _lookup>( + 'abs', + ); + late final _abs = _absPtr.asFunction(); + + int atexit(ffi.Pointer> arg0) { + return _atexit(arg0); } - late final __lsearchPtr = + late final _atexitPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.UnsignedInt, - _CoreCrtNonSecureSearchSortCompareFunction, - ) + ffi.Int Function(ffi.Pointer>) > - >('_lsearch'); - late final __lsearch = __lsearchPtr + >('atexit'); + late final _atexit = _atexitPtr .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, - _CoreCrtNonSecureSearchSortCompareFunction, - ) + int Function(ffi.Pointer>) >(); - ffi.Pointer lfind( - ffi.Pointer _Key, - ffi.Pointer _Base, - ffi.Pointer _NumOfElements, - int _SizeOfElements, - _CoreCrtNonSecureSearchSortCompareFunction _CompareFunction, - ) { - return _lfind$1( - _Key, - _Base, - _NumOfElements, - _SizeOfElements, - _CompareFunction, - ); + int at_quick_exit(ffi.Pointer> arg0) { + return _at_quick_exit(arg0); } - late final _lfindPtr = + late final _at_quick_exitPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.UnsignedInt, - _CoreCrtNonSecureSearchSortCompareFunction, - ) + ffi.Int Function(ffi.Pointer>) > - >('lfind'); - late final _lfind$1 = _lfindPtr + >('at_quick_exit'); + late final _at_quick_exit = _at_quick_exitPtr .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, - _CoreCrtNonSecureSearchSortCompareFunction, - ) + int Function(ffi.Pointer>) >(); - ffi.Pointer lsearch( - ffi.Pointer _Key, - ffi.Pointer _Base, - ffi.Pointer _NumOfElements, - int _SizeOfElements, - _CoreCrtNonSecureSearchSortCompareFunction _CompareFunction, + double atof(ffi.Pointer arg0) { + return _atof(arg0); + } + + late final _atofPtr = + _lookup)>>( + 'atof', + ); + late final _atof = _atofPtr + .asFunction)>(); + + int atoi(ffi.Pointer arg0) { + return _atoi(arg0); + } + + late final _atoiPtr = + _lookup)>>( + 'atoi', + ); + late final _atoi = _atoiPtr.asFunction)>(); + + int atol(ffi.Pointer arg0) { + return _atol(arg0); + } + + late final _atolPtr = + _lookup)>>( + 'atol', + ); + late final _atol = _atolPtr.asFunction)>(); + + int atoll(ffi.Pointer arg0) { + return _atoll(arg0); + } + + late final _atollPtr = + _lookup)>>( + 'atoll', + ); + late final _atoll = _atollPtr + .asFunction)>(); + + ffi.Pointer bsearch( + ffi.Pointer __key, + ffi.Pointer __base, + int __nel, + int __width, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + > + __compar, ) { - return _lsearch$1( - _Key, - _Base, - _NumOfElements, - _SizeOfElements, - _CompareFunction, - ); + return _bsearch(__key, __base, __nel, __width, __compar); } - late final _lsearchPtr = + late final _bsearchPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( ffi.Pointer, ffi.Pointer, - ffi.Pointer, - ffi.UnsignedInt, - _CoreCrtNonSecureSearchSortCompareFunction, + ffi.Size, + ffi.Size, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + >, ) > - >('lsearch'); - late final _lsearch$1 = _lsearchPtr + >('bsearch'); + late final _bsearch = _bsearchPtr .asFunction< ffi.Pointer Function( ffi.Pointer, ffi.Pointer, - ffi.Pointer, int, - _CoreCrtNonSecureSearchSortCompareFunction, + int, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + >, ) >(); - int _itow_s( - int _Value, - ffi.Pointer _Buffer, - int _BufferCount, - int _Radix, - ) { - return __itow_s(_Value, _Buffer, _BufferCount, _Radix); + div_t div(int arg0, int arg1) { + return _div(arg0, arg1); } - late final __itow_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function(ffi.Int, ffi.Pointer, ffi.Size, ffi.Int) - > - >('_itow_s'); - late final __itow_s = __itow_sPtr - .asFunction, int, int)>(); - - ffi.Pointer _itow( - int _Value, - ffi.Pointer _Buffer, - int _Radix, - ) { - return __itow(_Value, _Buffer, _Radix); + late final _divPtr = + _lookup>('div'); + late final _div = _divPtr.asFunction(); + + void exit(int arg0) { + return _exit(arg0); } - late final __itowPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Int, - ffi.Pointer, - ffi.Int, - ) - > - >('_itow'); - late final __itow = __itowPtr - .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) - >(); + late final _exitPtr = _lookup>( + 'exit', + ); + late final _exit = _exitPtr.asFunction(); - int _ltow_s( - int _Value, - ffi.Pointer _Buffer, - int _BufferCount, - int _Radix, - ) { - return __ltow_s(_Value, _Buffer, _BufferCount, _Radix); + ffi.Pointer getenv(ffi.Pointer arg0) { + return _getenv(arg0); } - late final __ltow_sPtr = + late final _getenvPtr = _lookup< ffi.NativeFunction< - errno_t Function(ffi.Long, ffi.Pointer, ffi.Size, ffi.Int) + ffi.Pointer Function(ffi.Pointer) > - >('_ltow_s'); - late final __ltow_s = __ltow_sPtr - .asFunction, int, int)>(); - - ffi.Pointer _ltow( - int _Value, - ffi.Pointer _Buffer, - int _Radix, - ) { - return __ltow(_Value, _Buffer, _Radix); + >('getenv'); + late final _getenv = _getenvPtr + .asFunction Function(ffi.Pointer)>(); + + int labs(int arg0) { + return _labs(arg0); + } + + late final _labsPtr = + _lookup>('labs'); + late final _labs = _labsPtr.asFunction(); + + ldiv_t ldiv(int arg0, int arg1) { + return _ldiv(arg0, arg1); + } + + late final _ldivPtr = + _lookup>('ldiv'); + late final _ldiv = _ldivPtr.asFunction(); + + int llabs(int arg0) { + return _llabs(arg0); + } + + late final _llabsPtr = + _lookup>('llabs'); + late final _llabs = _llabsPtr.asFunction(); + + lldiv_t lldiv(int arg0, int arg1) { + return _lldiv(arg0, arg1); + } + + late final _lldivPtr = + _lookup>( + 'lldiv', + ); + late final _lldiv = _lldivPtr.asFunction(); + + int mblen(ffi.Pointer __s, int __n) { + return _mblen(__s, __n); } - late final __ltowPtr = + late final _mblenPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Long, - ffi.Pointer, - ffi.Int, - ) - > - >('_ltow'); - late final __ltow = __ltowPtr - .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) - >(); + ffi.NativeFunction, ffi.Size)> + >('mblen'); + late final _mblen = _mblenPtr + .asFunction, int)>(); - int _ultow_s( - int _Value, - ffi.Pointer _Buffer, - int _BufferCount, - int _Radix, + int mbstowcs( + ffi.Pointer arg0, + ffi.Pointer arg1, + int __n, ) { - return __ultow_s(_Value, _Buffer, _BufferCount, _Radix); + return _mbstowcs(arg0, arg1, __n); } - late final __ultow_sPtr = + late final _mbstowcsPtr = _lookup< ffi.NativeFunction< - errno_t Function( - ffi.UnsignedLong, + ffi.Size Function( ffi.Pointer, + ffi.Pointer, ffi.Size, - ffi.Int, ) > - >('_ultow_s'); - late final __ultow_s = __ultow_sPtr - .asFunction, int, int)>(); - - ffi.Pointer _ultow( - int _Value, - ffi.Pointer _Buffer, - int _Radix, - ) { - return __ultow(_Value, _Buffer, _Radix); + >('mbstowcs'); + late final _mbstowcs = _mbstowcsPtr + .asFunction< + int Function(ffi.Pointer, ffi.Pointer, int) + >(); + + int mbtowc(ffi.Pointer arg0, ffi.Pointer arg1, int __n) { + return _mbtowc(arg0, arg1, __n); } - late final __ultowPtr = + late final _mbtowcPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.UnsignedLong, + ffi.Int Function( ffi.Pointer, - ffi.Int, + ffi.Pointer, + ffi.Size, ) > - >('_ultow'); - late final __ultow = __ultowPtr + >('mbtowc'); + late final _mbtowc = _mbtowcPtr .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) + int Function(ffi.Pointer, ffi.Pointer, int) >(); - double wcstod( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, + void qsort( + ffi.Pointer __base, + int __nel, + int __width, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + > + __compar, ) { - return _wcstod(_String, _EndPtr); + return _qsort(__base, __nel, __width, __compar); } - late final _wcstodPtr = + late final _qsortPtr = _lookup< ffi.NativeFunction< - ffi.Double Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Void Function( + ffi.Pointer, + ffi.Size, + ffi.Size, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + >, ) > - >('wcstod'); - late final _wcstod = _wcstodPtr + >('qsort'); + late final _qsort = _qsortPtr .asFunction< - double Function( - ffi.Pointer, - ffi.Pointer>, + void Function( + ffi.Pointer, + int, + int, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + >, ) >(); - double _wcstod_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - _locale_t _Locale, + void quick_exit(int arg0) { + return _quick_exit(arg0); + } + + late final _quick_exitPtr = + _lookup>('quick_exit'); + late final _quick_exit = _quick_exitPtr.asFunction(); + + int rand() { + return _rand(); + } + + late final _randPtr = _lookup>('rand'); + late final _rand = _randPtr.asFunction(); + + void srand(int arg0) { + return _srand(arg0); + } + + late final _srandPtr = + _lookup>('srand'); + late final _srand = _srandPtr.asFunction(); + + double strtod( + ffi.Pointer arg0, + ffi.Pointer> arg1, ) { - return __wcstod_l(_String, _EndPtr, _Locale); + return _strtod(arg0, arg1); } - late final __wcstod_lPtr = + late final _strtodPtr = _lookup< ffi.NativeFunction< ffi.Double Function( - ffi.Pointer, - ffi.Pointer>, - _locale_t, + ffi.Pointer, + ffi.Pointer>, ) > - >('_wcstod_l'); - late final __wcstod_l = __wcstod_lPtr + >('strtod'); + late final _strtod = _strtodPtr .asFunction< double Function( - ffi.Pointer, - ffi.Pointer>, - _locale_t, + ffi.Pointer, + ffi.Pointer>, ) >(); - int wcstol( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, + double strtof( + ffi.Pointer arg0, + ffi.Pointer> arg1, ) { - return _wcstol(_String, _EndPtr, _Radix); + return _strtof(arg0, arg1); } - late final _wcstolPtr = + late final _strtofPtr = _lookup< ffi.NativeFunction< - ffi.Long Function( - ffi.Pointer, - ffi.Pointer>, - ffi.Int, + ffi.Float Function( + ffi.Pointer, + ffi.Pointer>, ) > - >('wcstol'); - late final _wcstol = _wcstolPtr + >('strtof'); + late final _strtof = _strtofPtr .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, - int, + double Function( + ffi.Pointer, + ffi.Pointer>, ) >(); - int _wcstol_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, + int strtol( + ffi.Pointer __str, + ffi.Pointer> __endptr, + int __base, ) { - return __wcstol_l(_String, _EndPtr, _Radix, _Locale); + return _strtol(__str, __endptr, __base); } - late final __wcstol_lPtr = + late final _strtolPtr = _lookup< ffi.NativeFunction< ffi.Long Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Pointer, + ffi.Pointer>, ffi.Int, - _locale_t, ) > - >('_wcstol_l'); - late final __wcstol_l = __wcstol_lPtr + >('strtol'); + late final _strtol = _strtolPtr .asFunction< int Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Pointer, + ffi.Pointer>, int, - _locale_t, ) >(); - int wcstoll( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, + int strtoll( + ffi.Pointer __str, + ffi.Pointer> __endptr, + int __base, ) { - return _wcstoll(_String, _EndPtr, _Radix); + return _strtoll(__str, __endptr, __base); } - late final _wcstollPtr = + late final _strtollPtr = _lookup< ffi.NativeFunction< ffi.LongLong Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Pointer, + ffi.Pointer>, ffi.Int, ) > - >('wcstoll'); - late final _wcstoll = _wcstollPtr + >('strtoll'); + late final _strtoll = _strtollPtr .asFunction< int Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Pointer, + ffi.Pointer>, int, ) >(); - int _wcstoll_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, + int strtoul( + ffi.Pointer __str, + ffi.Pointer> __endptr, + int __base, ) { - return __wcstoll_l(_String, _EndPtr, _Radix, _Locale); + return _strtoul(__str, __endptr, __base); } - late final __wcstoll_lPtr = + late final _strtoulPtr = _lookup< ffi.NativeFunction< - ffi.LongLong Function( - ffi.Pointer, - ffi.Pointer>, + ffi.UnsignedLong Function( + ffi.Pointer, + ffi.Pointer>, ffi.Int, - _locale_t, ) > - >('_wcstoll_l'); - late final __wcstoll_l = __wcstoll_lPtr + >('strtoul'); + late final _strtoul = _strtoulPtr .asFunction< int Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Pointer, + ffi.Pointer>, int, - _locale_t, ) >(); - int wcstoul( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, + int strtoull( + ffi.Pointer __str, + ffi.Pointer> __endptr, + int __base, ) { - return _wcstoul(_String, _EndPtr, _Radix); + return _strtoull(__str, __endptr, __base); } - late final _wcstoulPtr = + late final _strtoullPtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLong Function( - ffi.Pointer, - ffi.Pointer>, + ffi.UnsignedLongLong Function( + ffi.Pointer, + ffi.Pointer>, ffi.Int, ) > - >('wcstoul'); - late final _wcstoul = _wcstoulPtr + >('strtoull'); + late final _strtoull = _strtoullPtr .asFunction< int Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Pointer, + ffi.Pointer>, int, ) >(); - int _wcstoul_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, + int system(ffi.Pointer arg0) { + return _system(arg0); + } + + late final _systemPtr = + _lookup)>>( + 'system', + ); + late final _system = _systemPtr + .asFunction)>(); + + int wcstombs( + ffi.Pointer arg0, + ffi.Pointer arg1, + int __n, ) { - return __wcstoul_l(_String, _EndPtr, _Radix, _Locale); + return _wcstombs(arg0, arg1, __n); } - late final __wcstoul_lPtr = + late final _wcstombsPtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLong Function( + ffi.Size Function( + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - ffi.Int, - _locale_t, + ffi.Size, ) > - >('_wcstoul_l'); - late final __wcstoul_l = __wcstoul_lPtr + >('wcstombs'); + late final _wcstombs = _wcstombsPtr .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, - int, - _locale_t, - ) + int Function(ffi.Pointer, ffi.Pointer, int) >(); - int wcstoull( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, + int wctomb(ffi.Pointer arg0, int arg1) { + return _wctomb(arg0, arg1); + } + + late final _wctombPtr = + _lookup< + ffi.NativeFunction, ffi.WChar)> + >('wctomb'); + late final _wctomb = _wctombPtr + .asFunction, int)>(); + + void _Exit(int arg0) { + return __Exit(arg0); + } + + late final __ExitPtr = + _lookup>('_Exit'); + late final __Exit = __ExitPtr.asFunction(); + + int a64l(ffi.Pointer arg0) { + return _a64l(arg0); + } + + late final _a64lPtr = + _lookup)>>( + 'a64l', + ); + late final _a64l = _a64lPtr.asFunction)>(); + + double drand48() { + return _drand48(); + } + + late final _drand48Ptr = _lookup>( + 'drand48', + ); + late final _drand48 = _drand48Ptr.asFunction(); + + ffi.Pointer ecvt( + double arg0, + int arg1, + ffi.Pointer arg2, + ffi.Pointer arg3, ) { - return _wcstoull(_String, _EndPtr, _Radix); + return _ecvt(arg0, arg1, arg2, arg3); } - late final _wcstoullPtr = + late final _ecvtPtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLongLong Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Pointer Function( + ffi.Double, ffi.Int, + ffi.Pointer, + ffi.Pointer, ) > - >('wcstoull'); - late final _wcstoull = _wcstoullPtr + >('ecvt'); + late final _ecvt = _ecvtPtr .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Pointer Function( + double, int, + ffi.Pointer, + ffi.Pointer, ) >(); - int _wcstoull_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, + double erand48(ffi.Pointer arg0) { + return _erand48(arg0); + } + + late final _erand48Ptr = + _lookup< + ffi.NativeFunction)> + >('erand48'); + late final _erand48 = _erand48Ptr + .asFunction)>(); + + ffi.Pointer fcvt( + double arg0, + int arg1, + ffi.Pointer arg2, + ffi.Pointer arg3, ) { - return __wcstoull_l(_String, _EndPtr, _Radix, _Locale); + return _fcvt(arg0, arg1, arg2, arg3); } - late final __wcstoull_lPtr = + late final _fcvtPtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLongLong Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Pointer Function( + ffi.Double, ffi.Int, - _locale_t, + ffi.Pointer, + ffi.Pointer, ) > - >('_wcstoull_l'); - late final __wcstoull_l = __wcstoull_lPtr + >('fcvt'); + late final _fcvt = _fcvtPtr .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Pointer Function( + double, int, - _locale_t, + ffi.Pointer, + ffi.Pointer, ) >(); - double wcstof( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, + ffi.Pointer gcvt( + double arg0, + int arg1, + ffi.Pointer arg2, ) { - return _wcstof(_String, _EndPtr); + return _gcvt(arg0, arg1, arg2); } - late final _wcstofPtr = + late final _gcvtPtr = _lookup< ffi.NativeFunction< - ffi.Float Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Pointer Function( + ffi.Double, + ffi.Int, + ffi.Pointer, ) > - >('wcstof'); - late final _wcstof = _wcstofPtr + >('gcvt'); + late final _gcvt = _gcvtPtr .asFunction< - double Function( - ffi.Pointer, - ffi.Pointer>, - ) + ffi.Pointer Function(double, int, ffi.Pointer) >(); - double _wcstof_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - _locale_t _Locale, + int getsubopt( + ffi.Pointer> arg0, + ffi.Pointer> arg1, + ffi.Pointer> arg2, ) { - return __wcstof_l(_String, _EndPtr, _Locale); + return _getsubopt(arg0, arg1, arg2); } - late final __wcstof_lPtr = + late final _getsuboptPtr = _lookup< ffi.NativeFunction< - ffi.Float Function( - ffi.Pointer, - ffi.Pointer>, - _locale_t, + ffi.Int Function( + ffi.Pointer>, + ffi.Pointer>, + ffi.Pointer>, ) > - >('_wcstof_l'); - late final __wcstof_l = __wcstof_lPtr + >('getsubopt'); + late final _getsubopt = _getsuboptPtr .asFunction< - double Function( - ffi.Pointer, - ffi.Pointer>, - _locale_t, + int Function( + ffi.Pointer>, + ffi.Pointer>, + ffi.Pointer>, ) >(); - double _wtof(ffi.Pointer _String) { - return __wtof(_String); + int grantpt(int arg0) { + return _grantpt(arg0); } - late final __wtofPtr = - _lookup)>>( - '_wtof', - ); - late final __wtof = __wtofPtr - .asFunction)>(); + late final _grantptPtr = + _lookup>('grantpt'); + late final _grantpt = _grantptPtr.asFunction(); - double _wtof_l(ffi.Pointer _String, _locale_t _Locale) { - return __wtof_l(_String, _Locale); + ffi.Pointer initstate( + int arg0, + ffi.Pointer arg1, + int __size, + ) { + return _initstate(arg0, arg1, __size); } - late final __wtof_lPtr = + late final _initstatePtr = _lookup< ffi.NativeFunction< - ffi.Double Function(ffi.Pointer, _locale_t) + ffi.Pointer Function( + ffi.UnsignedInt, + ffi.Pointer, + ffi.Size, + ) > - >('_wtof_l'); - late final __wtof_l = __wtof_lPtr - .asFunction, _locale_t)>(); - - int _wtoi(ffi.Pointer _String) { - return __wtoi(_String); - } - - late final __wtoiPtr = - _lookup)>>( - '_wtoi', - ); - late final __wtoi = __wtoiPtr - .asFunction)>(); + >('initstate'); + late final _initstate = _initstatePtr + .asFunction< + ffi.Pointer Function(int, ffi.Pointer, int) + >(); - int _wtoi_l(ffi.Pointer _String, _locale_t _Locale) { - return __wtoi_l(_String, _Locale); + int jrand48(ffi.Pointer arg0) { + return _jrand48(arg0); } - late final __wtoi_lPtr = + late final _jrand48Ptr = _lookup< - ffi.NativeFunction, _locale_t)> - >('_wtoi_l'); - late final __wtoi_l = __wtoi_lPtr - .asFunction, _locale_t)>(); + ffi.NativeFunction)> + >('jrand48'); + late final _jrand48 = _jrand48Ptr + .asFunction)>(); - int _wtol(ffi.Pointer _String) { - return __wtol(_String); + ffi.Pointer l64a(int arg0) { + return _l64a(arg0); } - late final __wtolPtr = - _lookup)>>( - '_wtol', + late final _l64aPtr = + _lookup Function(ffi.Long)>>( + 'l64a', ); - late final __wtol = __wtolPtr - .asFunction)>(); + late final _l64a = _l64aPtr.asFunction Function(int)>(); - int _wtol_l(ffi.Pointer _String, _locale_t _Locale) { - return __wtol_l(_String, _Locale); + void lcong48(ffi.Pointer arg0) { + return _lcong48(arg0); } - late final __wtol_lPtr = + late final _lcong48Ptr = _lookup< - ffi.NativeFunction, _locale_t)> - >('_wtol_l'); - late final __wtol_l = __wtol_lPtr - .asFunction, _locale_t)>(); + ffi.NativeFunction)> + >('lcong48'); + late final _lcong48 = _lcong48Ptr + .asFunction)>(); - int _wtoll(ffi.Pointer _String) { - return __wtoll(_String); + int lrand48() { + return _lrand48(); } - late final __wtollPtr = - _lookup< - ffi.NativeFunction)> - >('_wtoll'); - late final __wtoll = __wtollPtr - .asFunction)>(); + late final _lrand48Ptr = _lookup>( + 'lrand48', + ); + late final _lrand48 = _lrand48Ptr.asFunction(); - int _wtoll_l(ffi.Pointer _String, _locale_t _Locale) { - return __wtoll_l(_String, _Locale); + ffi.Pointer mktemp(ffi.Pointer arg0) { + return _mktemp(arg0); } - late final __wtoll_lPtr = + late final _mktempPtr = _lookup< ffi.NativeFunction< - ffi.LongLong Function(ffi.Pointer, _locale_t) + ffi.Pointer Function(ffi.Pointer) > - >('_wtoll_l'); - late final __wtoll_l = __wtoll_lPtr - .asFunction, _locale_t)>(); - - int _i64tow_s( - int _Value, - ffi.Pointer _Buffer, - int _BufferCount, - int _Radix, - ) { - return __i64tow_s(_Value, _Buffer, _BufferCount, _Radix); - } + >('mktemp'); + late final _mktemp = _mktempPtr + .asFunction Function(ffi.Pointer)>(); - late final __i64tow_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.LongLong, - ffi.Pointer, - ffi.Size, - ffi.Int, - ) - > - >('_i64tow_s'); - late final __i64tow_s = __i64tow_sPtr - .asFunction, int, int)>(); - - ffi.Pointer _i64tow( - int _Value, - ffi.Pointer _Buffer, - int _Radix, - ) { - return __i64tow(_Value, _Buffer, _Radix); + int mkstemp(ffi.Pointer arg0) { + return _mkstemp(arg0); } - late final __i64towPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.LongLong, - ffi.Pointer, - ffi.Int, - ) - > - >('_i64tow'); - late final __i64tow = __i64towPtr - .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) - >(); + late final _mkstempPtr = + _lookup)>>( + 'mkstemp', + ); + late final _mkstemp = _mkstempPtr + .asFunction)>(); - int _ui64tow_s( - int _Value, - ffi.Pointer _Buffer, - int _BufferCount, - int _Radix, - ) { - return __ui64tow_s(_Value, _Buffer, _BufferCount, _Radix); + int mrand48() { + return _mrand48(); } - late final __ui64tow_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.UnsignedLongLong, - ffi.Pointer, - ffi.Size, - ffi.Int, - ) - > - >('_ui64tow_s'); - late final __ui64tow_s = __ui64tow_sPtr - .asFunction, int, int)>(); - - ffi.Pointer _ui64tow( - int _Value, - ffi.Pointer _Buffer, - int _Radix, - ) { - return __ui64tow(_Value, _Buffer, _Radix); + late final _mrand48Ptr = _lookup>( + 'mrand48', + ); + late final _mrand48 = _mrand48Ptr.asFunction(); + + int nrand48(ffi.Pointer arg0) { + return _nrand48(arg0); } - late final __ui64towPtr = + late final _nrand48Ptr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.UnsignedLongLong, - ffi.Pointer, - ffi.Int, - ) - > - >('_ui64tow'); - late final __ui64tow = __ui64towPtr - .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) - >(); + ffi.NativeFunction)> + >('nrand48'); + late final _nrand48 = _nrand48Ptr + .asFunction)>(); - int _wtoi64(ffi.Pointer _String) { - return __wtoi64(_String); + int posix_openpt(int arg0) { + return _posix_openpt(arg0); } - late final __wtoi64Ptr = - _lookup< - ffi.NativeFunction)> - >('_wtoi64'); - late final __wtoi64 = __wtoi64Ptr - .asFunction)>(); + late final _posix_openptPtr = + _lookup>('posix_openpt'); + late final _posix_openpt = _posix_openptPtr.asFunction(); - int _wtoi64_l(ffi.Pointer _String, _locale_t _Locale) { - return __wtoi64_l(_String, _Locale); + ffi.Pointer ptsname(int arg0) { + return _ptsname(arg0); } - late final __wtoi64_lPtr = - _lookup< - ffi.NativeFunction< - ffi.LongLong Function(ffi.Pointer, _locale_t) - > - >('_wtoi64_l'); - late final __wtoi64_l = __wtoi64_lPtr - .asFunction, _locale_t)>(); - - int _wcstoi64( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - ) { - return __wcstoi64(_String, _EndPtr, _Radix); + late final _ptsnamePtr = + _lookup Function(ffi.Int)>>( + 'ptsname', + ); + late final _ptsname = _ptsnamePtr + .asFunction Function(int)>(); + + int ptsname_r(int fildes, ffi.Pointer buffer, int buflen) { + return _ptsname_r(fildes, buffer, buflen); } - late final __wcstoi64Ptr = + late final _ptsname_rPtr = _lookup< ffi.NativeFunction< - ffi.LongLong Function( - ffi.Pointer, - ffi.Pointer>, - ffi.Int, - ) + ffi.Int Function(ffi.Int, ffi.Pointer, ffi.Size) > - >('_wcstoi64'); - late final __wcstoi64 = __wcstoi64Ptr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, - int, - ) - >(); + >('ptsname_r'); + late final _ptsname_r = _ptsname_rPtr + .asFunction, int)>(); + + int putenv(ffi.Pointer arg0) { + return _putenv(arg0); + } + + late final _putenvPtr = + _lookup)>>( + 'putenv', + ); + late final _putenv = _putenvPtr + .asFunction)>(); + + int random() { + return _random(); + } + + late final _randomPtr = _lookup>( + 'random', + ); + late final _random = _randomPtr.asFunction(); + + int rand_r(ffi.Pointer arg0) { + return _rand_r(arg0); + } + + late final _rand_rPtr = + _lookup< + ffi.NativeFunction)> + >('rand_r'); + late final _rand_r = _rand_rPtr + .asFunction)>(); - int _wcstoi64_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, + ffi.Pointer realpath( + ffi.Pointer arg0, + ffi.Pointer arg1, ) { - return __wcstoi64_l(_String, _EndPtr, _Radix, _Locale); + return _realpath(arg0, arg1); } - late final __wcstoi64_lPtr = + late final _realpathPtr = _lookup< ffi.NativeFunction< - ffi.LongLong Function( - ffi.Pointer, - ffi.Pointer>, - ffi.Int, - _locale_t, + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, ) > - >('_wcstoi64_l'); - late final __wcstoi64_l = __wcstoi64_lPtr + >('realpath'); + late final _realpath = _realpathPtr .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, - int, - _locale_t, + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, ) >(); - int _wcstoui64( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - ) { - return __wcstoui64(_String, _EndPtr, _Radix); + ffi.Pointer seed48(ffi.Pointer arg0) { + return _seed48(arg0); } - late final __wcstoui64Ptr = + late final _seed48Ptr = _lookup< ffi.NativeFunction< - ffi.UnsignedLongLong Function( - ffi.Pointer, - ffi.Pointer>, - ffi.Int, + ffi.Pointer Function( + ffi.Pointer, ) > - >('_wcstoui64'); - late final __wcstoui64 = __wcstoui64Ptr + >('seed48'); + late final _seed48 = _seed48Ptr .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, - int, - ) + ffi.Pointer Function(ffi.Pointer) >(); - int _wcstoui64_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, + int setenv( + ffi.Pointer __name, + ffi.Pointer __value, + int __overwrite, ) { - return __wcstoui64_l(_String, _EndPtr, _Radix, _Locale); + return _setenv(__name, __value, __overwrite); } - late final __wcstoui64_lPtr = + late final _setenvPtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLongLong Function( - ffi.Pointer, - ffi.Pointer>, + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, ffi.Int, - _locale_t, ) > - >('_wcstoui64_l'); - late final __wcstoui64_l = __wcstoui64_lPtr + >('setenv'); + late final _setenv = _setenvPtr .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, - int, - _locale_t, - ) + int Function(ffi.Pointer, ffi.Pointer, int) >(); - ffi.Pointer _wfullpath( - ffi.Pointer _Buffer, - ffi.Pointer _Path, - int _BufferCount, - ) { - return __wfullpath(_Buffer, _Path, _BufferCount); + void setkey(ffi.Pointer arg0) { + return _setkey(arg0); + } + + late final _setkeyPtr = + _lookup)>>( + 'setkey', + ); + late final _setkey = _setkeyPtr + .asFunction)>(); + + ffi.Pointer setstate(ffi.Pointer arg0) { + return _setstate(arg0); } - late final __wfullpathPtr = + late final _setstatePtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ) + ffi.Pointer Function(ffi.Pointer) > - >('_wfullpath'); - late final __wfullpath = __wfullpathPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - int, - ) - >(); + >('setstate'); + late final _setstate = _setstatePtr + .asFunction Function(ffi.Pointer)>(); - int _wmakepath_s( - ffi.Pointer _Buffer, - int _BufferCount, - ffi.Pointer _Drive, - ffi.Pointer _Dir, - ffi.Pointer _Filename, - ffi.Pointer _Ext, - ) { - return __wmakepath_s(_Buffer, _BufferCount, _Drive, _Dir, _Filename, _Ext); + void srand48(int arg0) { + return _srand48(arg0); + } + + late final _srand48Ptr = + _lookup>('srand48'); + late final _srand48 = _srand48Ptr.asFunction(); + + void srandom(int arg0) { + return _srandom(arg0); + } + + late final _srandomPtr = + _lookup>( + 'srandom', + ); + late final _srandom = _srandomPtr.asFunction(); + + int unlockpt(int arg0) { + return _unlockpt(arg0); + } + + late final _unlockptPtr = + _lookup>('unlockpt'); + late final _unlockpt = _unlockptPtr.asFunction(); + + int unsetenv(ffi.Pointer arg0) { + return _unsetenv(arg0); + } + + late final _unsetenvPtr = + _lookup)>>( + 'unsetenv', + ); + late final _unsetenv = _unsetenvPtr + .asFunction)>(); + + int arc4random() { + return _arc4random(); } - late final __wmakepath_sPtr = + late final _arc4randomPtr = + _lookup>('arc4random'); + late final _arc4random = _arc4randomPtr.asFunction(); + + void arc4random_addrandom(ffi.Pointer arg0, int __datlen) { + return _arc4random_addrandom(arg0, __datlen); + } + + late final _arc4random_addrandomPtr = _lookup< ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Size, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) + ffi.Void Function(ffi.Pointer, ffi.Int) > - >('_wmakepath_s'); - late final __wmakepath_s = __wmakepath_sPtr - .asFunction< - int Function( - ffi.Pointer, - int, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); + >('arc4random_addrandom'); + late final _arc4random_addrandom = _arc4random_addrandomPtr + .asFunction, int)>(); - void _wmakepath( - ffi.Pointer _Buffer, - ffi.Pointer _Drive, - ffi.Pointer _Dir, - ffi.Pointer _Filename, - ffi.Pointer _Ext, - ) { - return __wmakepath(_Buffer, _Drive, _Dir, _Filename, _Ext); + void arc4random_buf(ffi.Pointer __buf, int __nbytes) { + return _arc4random_buf(__buf, __nbytes); } - late final __wmakepathPtr = + late final _arc4random_bufPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('_wmakepath'); - late final __wmakepath = __wmakepathPtr - .asFunction< - void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); + ffi.NativeFunction, ffi.Size)> + >('arc4random_buf'); + late final _arc4random_buf = _arc4random_bufPtr + .asFunction, int)>(); + + void arc4random_stir() { + return _arc4random_stir(); + } + + late final _arc4random_stirPtr = + _lookup>('arc4random_stir'); + late final _arc4random_stir = _arc4random_stirPtr + .asFunction(); - void _wperror(ffi.Pointer _ErrorMessage) { - return __wperror(_ErrorMessage); + int arc4random_uniform(int __upper_bound) { + return _arc4random_uniform(__upper_bound); } - late final __wperrorPtr = - _lookup)>>( - '_wperror', + late final _arc4random_uniformPtr = + _lookup>( + 'arc4random_uniform', ); - late final __wperror = __wperrorPtr - .asFunction)>(); - - void _wsplitpath( - ffi.Pointer _FullPath, - ffi.Pointer _Drive, - ffi.Pointer _Dir, - ffi.Pointer _Filename, - ffi.Pointer _Ext, + late final _arc4random_uniform = _arc4random_uniformPtr + .asFunction(); + + ffi.Pointer cgetcap( + ffi.Pointer arg0, + ffi.Pointer arg1, + int arg2, ) { - return __wsplitpath(_FullPath, _Drive, _Dir, _Filename, _Ext); + return _cgetcap(arg0, arg1, arg2); } - late final __wsplitpathPtr = + late final _cgetcapPtr = _lookup< ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Int, ) > - >('_wsplitpath'); - late final __wsplitpath = __wsplitpathPtr + >('cgetcap'); + late final _cgetcap = _cgetcapPtr .asFunction< - void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + int, ) >(); - int _wsplitpath_s( - ffi.Pointer _FullPath, - ffi.Pointer _Drive, - int _DriveCount, - ffi.Pointer _Dir, - int _DirCount, - ffi.Pointer _Filename, - int _FilenameCount, - ffi.Pointer _Ext, - int _ExtCount, + int cgetclose() { + return _cgetclose(); + } + + late final _cgetclosePtr = _lookup>( + 'cgetclose', + ); + late final _cgetclose = _cgetclosePtr.asFunction(); + + int cgetent( + ffi.Pointer> arg0, + ffi.Pointer> arg1, + ffi.Pointer arg2, ) { - return __wsplitpath_s( - _FullPath, - _Drive, - _DriveCount, - _Dir, - _DirCount, - _Filename, - _FilenameCount, - _Ext, - _ExtCount, - ); + return _cgetent(arg0, arg1, arg2); } - late final __wsplitpath_sPtr = + late final _cgetentPtr = _lookup< ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ffi.Pointer, - ffi.Size, - ffi.Pointer, - ffi.Size, - ffi.Pointer, - ffi.Size, + ffi.Int Function( + ffi.Pointer>, + ffi.Pointer>, + ffi.Pointer, ) > - >('_wsplitpath_s'); - late final __wsplitpath_s = __wsplitpath_sPtr + >('cgetent'); + late final _cgetent = _cgetentPtr .asFunction< int Function( - ffi.Pointer, - ffi.Pointer, - int, - ffi.Pointer, - int, - ffi.Pointer, - int, - ffi.Pointer, - int, + ffi.Pointer>, + ffi.Pointer>, + ffi.Pointer, ) >(); - int _wdupenv_s( - ffi.Pointer> _Buffer, - ffi.Pointer _BufferCount, - ffi.Pointer _VarName, + int cgetfirst( + ffi.Pointer> arg0, + ffi.Pointer> arg1, ) { - return __wdupenv_s(_Buffer, _BufferCount, _VarName); + return _cgetfirst(arg0, arg1); } - late final __wdupenv_sPtr = + late final _cgetfirstPtr = _lookup< ffi.NativeFunction< - errno_t Function( - ffi.Pointer>, - ffi.Pointer, - ffi.Pointer, + ffi.Int Function( + ffi.Pointer>, + ffi.Pointer>, ) > - >('_wdupenv_s'); - late final __wdupenv_s = __wdupenv_sPtr + >('cgetfirst'); + late final _cgetfirst = _cgetfirstPtr .asFunction< int Function( - ffi.Pointer>, - ffi.Pointer, - ffi.Pointer, + ffi.Pointer>, + ffi.Pointer>, ) >(); - ffi.Pointer _wgetenv(ffi.Pointer _VarName) { - return __wgetenv(_VarName); + int cgetmatch(ffi.Pointer arg0, ffi.Pointer arg1) { + return _cgetmatch(arg0, arg1); } - late final __wgetenvPtr = + late final _cgetmatchPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) + ffi.Int Function(ffi.Pointer, ffi.Pointer) > - >('_wgetenv'); - late final __wgetenv = __wgetenvPtr - .asFunction Function(ffi.Pointer)>(); - - int _wgetenv_s( - ffi.Pointer _RequiredCount, - ffi.Pointer _Buffer, - int _BufferCount, - ffi.Pointer _VarName, + >('cgetmatch'); + late final _cgetmatch = _cgetmatchPtr + .asFunction, ffi.Pointer)>(); + + int cgetnext( + ffi.Pointer> arg0, + ffi.Pointer> arg1, ) { - return __wgetenv_s(_RequiredCount, _Buffer, _BufferCount, _VarName); + return _cgetnext(arg0, arg1); } - late final __wgetenv_sPtr = + late final _cgetnextPtr = _lookup< ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ffi.Pointer, + ffi.Int Function( + ffi.Pointer>, + ffi.Pointer>, ) > - >('_wgetenv_s'); - late final __wgetenv_s = __wgetenv_sPtr + >('cgetnext'); + late final _cgetnext = _cgetnextPtr .asFunction< int Function( - ffi.Pointer, - ffi.Pointer, - int, - ffi.Pointer, + ffi.Pointer>, + ffi.Pointer>, ) >(); - int _wputenv(ffi.Pointer _EnvString) { - return __wputenv(_EnvString); - } - - late final __wputenvPtr = - _lookup)>>( - '_wputenv', - ); - late final __wputenv = __wputenvPtr - .asFunction)>(); - - int _wputenv_s(ffi.Pointer _Name, ffi.Pointer _Value) { - return __wputenv_s(_Name, _Value); + int cgetnum( + ffi.Pointer arg0, + ffi.Pointer arg1, + ffi.Pointer arg2, + ) { + return _cgetnum(arg0, arg1, arg2); } - late final __wputenv_sPtr = + late final _cgetnumPtr = _lookup< ffi.NativeFunction< - errno_t Function(ffi.Pointer, ffi.Pointer) + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) > - >('_wputenv_s'); - late final __wputenv_s = __wputenv_sPtr + >('cgetnum'); + late final _cgetnum = _cgetnumPtr .asFunction< - int Function(ffi.Pointer, ffi.Pointer) + int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) >(); - int _wsearchenv_s( - ffi.Pointer _Filename, - ffi.Pointer _VarName, - ffi.Pointer _Buffer, - int _BufferCount, + int cgetset(ffi.Pointer arg0) { + return _cgetset(arg0); + } + + late final _cgetsetPtr = + _lookup)>>( + 'cgetset', + ); + late final _cgetset = _cgetsetPtr + .asFunction)>(); + + int cgetstr( + ffi.Pointer arg0, + ffi.Pointer arg1, + ffi.Pointer> arg2, ) { - return __wsearchenv_s(_Filename, _VarName, _Buffer, _BufferCount); + return _cgetstr(arg0, arg1, arg2); } - late final __wsearchenv_sPtr = + late final _cgetstrPtr = _lookup< ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Size, + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer>, ) > - >('_wsearchenv_s'); - late final __wsearchenv_s = __wsearchenv_sPtr + >('cgetstr'); + late final _cgetstr = _cgetstrPtr .asFunction< int Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer>, ) >(); - void _wsearchenv( - ffi.Pointer _Filename, - ffi.Pointer _VarName, - ffi.Pointer _ResultPath, + int cgetustr( + ffi.Pointer arg0, + ffi.Pointer arg1, + ffi.Pointer> arg2, ) { - return __wsearchenv(_Filename, _VarName, _ResultPath); + return _cgetustr(arg0, arg1, arg2); } - late final __wsearchenvPtr = + late final _cgetustrPtr = _lookup< ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer>, ) > - >('_wsearchenv'); - late final __wsearchenv = __wsearchenvPtr + >('cgetustr'); + late final _cgetustr = _cgetustrPtr .asFunction< - void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, + int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer>, ) >(); - int _wsystem(ffi.Pointer _Command) { - return __wsystem(_Command); + int daemon(int arg0, int arg1) { + return _daemon(arg0, arg1); } - late final __wsystemPtr = - _lookup)>>( - '_wsystem', - ); - late final __wsystem = __wsystemPtr - .asFunction)>(); + late final _daemonPtr = + _lookup>('daemon'); + late final _daemon = _daemonPtr.asFunction(); + + ffi.Pointer devname(int arg0, int arg1) { + return _devname(arg0, arg1); + } + + late final _devnamePtr = + _lookup< + ffi.NativeFunction Function(dev_t, mode_t)> + >('devname'); + late final _devname = _devnamePtr + .asFunction Function(int, int)>(); - void _swab( - ffi.Pointer _Buf1, - ffi.Pointer _Buf2, - int _SizeInBytes, + ffi.Pointer devname_r( + int arg0, + int arg1, + ffi.Pointer buf, + int len, ) { - return __swab(_Buf1, _Buf2, _SizeInBytes); + return _devname_r(arg0, arg1, buf, len); } - late final __swabPtr = + late final _devname_rPtr = _lookup< ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, + ffi.Pointer Function( + dev_t, + mode_t, ffi.Pointer, ffi.Int, ) > - >('_swab'); - late final __swab = __swabPtr + >('devname_r'); + late final _devname_r = _devname_rPtr .asFunction< - void Function(ffi.Pointer, ffi.Pointer, int) + ffi.Pointer Function(int, int, ffi.Pointer, int) >(); - void exit(int _Code) { - return _exit$1(_Code); + ffi.Pointer getbsize( + ffi.Pointer arg0, + ffi.Pointer arg1, + ) { + return _getbsize(arg0, arg1); } - late final _exitPtr = _lookup>( - 'exit', - ); - late final _exit$1 = _exitPtr.asFunction(); + late final _getbsizePtr = + _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >('getbsize'); + late final _getbsize = _getbsizePtr + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); - void _exit(int _Code) { - return __exit(_Code); + int getloadavg(ffi.Pointer arg0, int __nelem) { + return _getloadavg(arg0, __nelem); } - late final __exitPtr = - _lookup>('_exit'); - late final __exit = __exitPtr.asFunction(); + late final _getloadavgPtr = + _lookup< + ffi.NativeFunction, ffi.Int)> + >('getloadavg'); + late final _getloadavg = _getloadavgPtr + .asFunction, int)>(); - void _Exit(int _Code) { - return __Exit(_Code); + ffi.Pointer getprogname() { + return _getprogname(); } - late final __ExitPtr = - _lookup>('_Exit'); - late final __Exit = __ExitPtr.asFunction(); - - void quick_exit(int _Code) { - return _quick_exit(_Code); - } - - late final _quick_exitPtr = - _lookup>('quick_exit'); - late final _quick_exit = _quick_exitPtr.asFunction(); + late final _getprognamePtr = + _lookup Function()>>( + 'getprogname', + ); + late final _getprogname = _getprognamePtr + .asFunction Function()>(); - void abort() { - return _abort(); + void setprogname(ffi.Pointer arg0) { + return _setprogname(arg0); } - late final _abortPtr = _lookup>( - 'abort', - ); - late final _abort = _abortPtr.asFunction(); + late final _setprognamePtr = + _lookup)>>( + 'setprogname', + ); + late final _setprogname = _setprognamePtr + .asFunction)>(); - int _set_abort_behavior(int _Flags, int _Mask) { - return __set_abort_behavior(_Flags, _Mask); + int heapsort( + ffi.Pointer __base, + int __nel, + int __width, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + > + __compar, + ) { + return _heapsort(__base, __nel, __width, __compar); } - late final __set_abort_behaviorPtr = + late final _heapsortPtr = _lookup< ffi.NativeFunction< - ffi.UnsignedInt Function(ffi.UnsignedInt, ffi.UnsignedInt) + ffi.Int Function( + ffi.Pointer, + ffi.Size, + ffi.Size, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + >, + ) > - >('_set_abort_behavior'); - late final __set_abort_behavior = __set_abort_behaviorPtr - .asFunction(); + >('heapsort'); + late final _heapsort = _heapsortPtr + .asFunction< + int Function( + ffi.Pointer, + int, + int, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + >, + ) + >(); - int atexit(ffi.Pointer> arg0) { - return _atexit(arg0); + int mergesort( + ffi.Pointer __base, + int __nel, + int __width, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + > + __compar, + ) { + return _mergesort(__base, __nel, __width, __compar); } - late final _atexitPtr = + late final _mergesortPtr = _lookup< ffi.NativeFunction< - ffi.Int Function(ffi.Pointer>) + ffi.Int Function( + ffi.Pointer, + ffi.Size, + ffi.Size, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + >, + ) > - >('atexit'); - late final _atexit = _atexitPtr + >('mergesort'); + late final _mergesort = _mergesortPtr .asFunction< - int Function(ffi.Pointer>) + int Function( + ffi.Pointer, + int, + int, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + >, + ) >(); - _onexit_t _onexit(_onexit_t _Func) { - return __onexit(_Func); - } - - late final __onexitPtr = - _lookup>('_onexit'); - late final __onexit = __onexitPtr.asFunction<_onexit_t Function(_onexit_t)>(); - - int at_quick_exit(ffi.Pointer> arg0) { - return _at_quick_exit(arg0); + void psort( + ffi.Pointer __base, + int __nel, + int __width, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + > + __compar, + ) { + return _psort(__base, __nel, __width, __compar); } - late final _at_quick_exitPtr = + late final _psortPtr = _lookup< ffi.NativeFunction< - ffi.Int Function(ffi.Pointer>) + ffi.Void Function( + ffi.Pointer, + ffi.Size, + ffi.Size, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + >, + ) > - >('at_quick_exit'); - late final _at_quick_exit = _at_quick_exitPtr + >('psort'); + late final _psort = _psortPtr .asFunction< - int Function(ffi.Pointer>) + void Function( + ffi.Pointer, + int, + int, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(ffi.Pointer, ffi.Pointer) + > + >, + ) >(); - _purecall_handler _set_purecall_handler(_purecall_handler _Handler) { - return __set_purecall_handler(_Handler); + void psort_r( + ffi.Pointer __base, + int __nel, + int __width, + ffi.Pointer arg3, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + > + __compar, + ) { + return _psort_r(__base, __nel, __width, arg3, __compar); } - late final __set_purecall_handlerPtr = + late final _psort_rPtr = _lookup< - ffi.NativeFunction<_purecall_handler Function(_purecall_handler)> - >('_set_purecall_handler'); - late final __set_purecall_handler = __set_purecall_handlerPtr - .asFunction<_purecall_handler Function(_purecall_handler)>(); - - _purecall_handler _get_purecall_handler() { - return __get_purecall_handler(); - } - - late final __get_purecall_handlerPtr = - _lookup>( - '_get_purecall_handler', - ); - late final __get_purecall_handler = __get_purecall_handlerPtr - .asFunction<_purecall_handler Function()>(); + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, + ffi.Size, + ffi.Size, + ffi.Pointer, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + >, + ) + > + >('psort_r'); + late final _psort_r = _psort_rPtr + .asFunction< + void Function( + ffi.Pointer, + int, + int, + ffi.Pointer, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + >, + ) + >(); - _invalid_parameter_handler _set_invalid_parameter_handler( - _invalid_parameter_handler _Handler, + void qsort_r( + ffi.Pointer __base, + int __nel, + int __width, + ffi.Pointer arg3, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + > + __compar, ) { - return __set_invalid_parameter_handler(_Handler); + return _qsort_r(__base, __nel, __width, arg3, __compar); } - late final __set_invalid_parameter_handlerPtr = + late final _qsort_rPtr = _lookup< ffi.NativeFunction< - _invalid_parameter_handler Function(_invalid_parameter_handler) + ffi.Void Function( + ffi.Pointer, + ffi.Size, + ffi.Size, + ffi.Pointer, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + >, + ) > - >('_set_invalid_parameter_handler'); - late final __set_invalid_parameter_handler = - __set_invalid_parameter_handlerPtr - .asFunction< - _invalid_parameter_handler Function(_invalid_parameter_handler) - >(); - - _invalid_parameter_handler _get_invalid_parameter_handler() { - return __get_invalid_parameter_handler(); - } - - late final __get_invalid_parameter_handlerPtr = - _lookup>( - '_get_invalid_parameter_handler', - ); - late final __get_invalid_parameter_handler = - __get_invalid_parameter_handlerPtr - .asFunction<_invalid_parameter_handler Function()>(); + >('qsort_r'); + late final _qsort_r = _qsort_rPtr + .asFunction< + void Function( + ffi.Pointer, + int, + int, + ffi.Pointer, + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + >, + ) + >(); - _invalid_parameter_handler _set_thread_local_invalid_parameter_handler( - _invalid_parameter_handler _Handler, + int radixsort( + ffi.Pointer> __base, + int __nel, + ffi.Pointer __table, + int __endbyte, ) { - return __set_thread_local_invalid_parameter_handler(_Handler); + return _radixsort(__base, __nel, __table, __endbyte); } - late final __set_thread_local_invalid_parameter_handlerPtr = + late final _radixsortPtr = _lookup< ffi.NativeFunction< - _invalid_parameter_handler Function(_invalid_parameter_handler) + ffi.Int Function( + ffi.Pointer>, + ffi.Int, + ffi.Pointer, + ffi.UnsignedInt, + ) > - >('_set_thread_local_invalid_parameter_handler'); - late final __set_thread_local_invalid_parameter_handler = - __set_thread_local_invalid_parameter_handlerPtr - .asFunction< - _invalid_parameter_handler Function(_invalid_parameter_handler) - >(); - - _invalid_parameter_handler _get_thread_local_invalid_parameter_handler() { - return __get_thread_local_invalid_parameter_handler(); - } - - late final __get_thread_local_invalid_parameter_handlerPtr = - _lookup>( - '_get_thread_local_invalid_parameter_handler', - ); - late final __get_thread_local_invalid_parameter_handler = - __get_thread_local_invalid_parameter_handlerPtr - .asFunction<_invalid_parameter_handler Function()>(); - - int _set_error_mode(int _Mode) { - return __set_error_mode(_Mode); - } - - late final __set_error_modePtr = - _lookup>('_set_error_mode'); - late final __set_error_mode = __set_error_modePtr - .asFunction(); - - ffi.Pointer __doserrno() { - return ___doserrno(); - } - - late final ___doserrnoPtr = - _lookup Function()>>( - '__doserrno', - ); - late final ___doserrno = ___doserrnoPtr - .asFunction Function()>(); + >('radixsort'); + late final _radixsort = _radixsortPtr + .asFunction< + int Function( + ffi.Pointer>, + int, + ffi.Pointer, + int, + ) + >(); - int _set_doserrno(int _Value) { - return __set_doserrno(_Value); + int rpmatch(ffi.Pointer arg0) { + return _rpmatch(arg0); } - late final __set_doserrnoPtr = - _lookup>( - '_set_doserrno', + late final _rpmatchPtr = + _lookup)>>( + 'rpmatch', ); - late final __set_doserrno = __set_doserrnoPtr.asFunction(); - - int _get_doserrno(ffi.Pointer _Value) { - return __get_doserrno(_Value); - } - - late final __get_doserrnoPtr = - _lookup< - ffi.NativeFunction)> - >('_get_doserrno'); - late final __get_doserrno = __get_doserrnoPtr - .asFunction)>(); + late final _rpmatch = _rpmatchPtr + .asFunction)>(); - ffi.Pointer> __sys_errlist() { - return ___sys_errlist(); + int sradixsort( + ffi.Pointer> __base, + int __nel, + ffi.Pointer __table, + int __endbyte, + ) { + return _sradixsort(__base, __nel, __table, __endbyte); } - late final ___sys_errlistPtr = + late final _sradixsortPtr = _lookup< - ffi.NativeFunction> Function()> - >('__sys_errlist'); - late final ___sys_errlist = ___sys_errlistPtr - .asFunction> Function()>(); - - ffi.Pointer __sys_nerr() { - return ___sys_nerr(); - } - - late final ___sys_nerrPtr = - _lookup Function()>>( - '__sys_nerr', - ); - late final ___sys_nerr = ___sys_nerrPtr - .asFunction Function()>(); + ffi.NativeFunction< + ffi.Int Function( + ffi.Pointer>, + ffi.Int, + ffi.Pointer, + ffi.UnsignedInt, + ) + > + >('sradixsort'); + late final _sradixsort = _sradixsortPtr + .asFunction< + int Function( + ffi.Pointer>, + int, + ffi.Pointer, + int, + ) + >(); - void perror(ffi.Pointer _ErrMsg) { - return _perror(_ErrMsg); + void sranddev() { + return _sranddev(); } - late final _perrorPtr = - _lookup)>>( - 'perror', - ); - late final _perror = _perrorPtr - .asFunction)>(); + late final _sranddevPtr = _lookup>( + 'sranddev', + ); + late final _sranddev = _sranddevPtr.asFunction(); - ffi.Pointer> __p__pgmptr() { - return ___p__pgmptr(); + void srandomdev() { + return _srandomdev(); } - late final ___p__pgmptrPtr = - _lookup< - ffi.NativeFunction> Function()> - >('__p__pgmptr'); - late final ___p__pgmptr = ___p__pgmptrPtr - .asFunction> Function()>(); + late final _srandomdevPtr = _lookup>( + 'srandomdev', + ); + late final _srandomdev = _srandomdevPtr.asFunction(); - ffi.Pointer> __p__wpgmptr() { - return ___p__wpgmptr(); + int strtonum( + ffi.Pointer __numstr, + int __minval, + int __maxval, + ffi.Pointer> __errstrp, + ) { + return _strtonum(__numstr, __minval, __maxval, __errstrp); } - late final ___p__wpgmptrPtr = + late final _strtonumPtr = _lookup< - ffi.NativeFunction> Function()> - >('__p__wpgmptr'); - late final ___p__wpgmptr = ___p__wpgmptrPtr - .asFunction> Function()>(); - - ffi.Pointer __p__fmode() { - return ___p__fmode(); - } - - late final ___p__fmodePtr = - _lookup Function()>>( - '__p__fmode', - ); - late final ___p__fmode = ___p__fmodePtr - .asFunction Function()>(); + ffi.NativeFunction< + ffi.LongLong Function( + ffi.Pointer, + ffi.LongLong, + ffi.LongLong, + ffi.Pointer>, + ) + > + >('strtonum'); + late final _strtonum = _strtonumPtr + .asFunction< + int Function( + ffi.Pointer, + int, + int, + ffi.Pointer>, + ) + >(); - int _get_pgmptr(ffi.Pointer> _Value) { - return __get_pgmptr(_Value); + int strtoq( + ffi.Pointer __str, + ffi.Pointer> __endptr, + int __base, + ) { + return _strtoq(__str, __endptr, __base); } - late final __get_pgmptrPtr = + late final _strtoqPtr = _lookup< - ffi.NativeFunction>)> - >('_get_pgmptr'); - late final __get_pgmptr = __get_pgmptrPtr - .asFunction>)>(); + ffi.NativeFunction< + ffi.LongLong Function( + ffi.Pointer, + ffi.Pointer>, + ffi.Int, + ) + > + >('strtoq'); + late final _strtoq = _strtoqPtr + .asFunction< + int Function( + ffi.Pointer, + ffi.Pointer>, + int, + ) + >(); - int _get_wpgmptr(ffi.Pointer> _Value) { - return __get_wpgmptr(_Value); + int strtouq( + ffi.Pointer __str, + ffi.Pointer> __endptr, + int __base, + ) { + return _strtouq(__str, __endptr, __base); } - late final __get_wpgmptrPtr = + late final _strtouqPtr = _lookup< ffi.NativeFunction< - errno_t Function(ffi.Pointer>) + ffi.UnsignedLongLong Function( + ffi.Pointer, + ffi.Pointer>, + ffi.Int, + ) > - >('_get_wpgmptr'); - late final __get_wpgmptr = __get_wpgmptrPtr - .asFunction>)>(); - - int _set_fmode(int _Mode) { - return __set_fmode(_Mode); - } - - late final __set_fmodePtr = - _lookup>('_set_fmode'); - late final __set_fmode = __set_fmodePtr.asFunction(); - - int _get_fmode(ffi.Pointer _PMode) { - return __get_fmode(_PMode); - } - - late final __get_fmodePtr = - _lookup)>>( - '_get_fmode', - ); - late final __get_fmode = __get_fmodePtr - .asFunction)>(); - - int abs(int _Number) { - return _abs(_Number); - } - - late final _absPtr = _lookup>( - 'abs', - ); - late final _abs = _absPtr.asFunction(); - - int labs(int _Number) { - return _labs(_Number); - } + >('strtouq'); + late final _strtouq = _strtouqPtr + .asFunction< + int Function( + ffi.Pointer, + ffi.Pointer>, + int, + ) + >(); - late final _labsPtr = - _lookup>('labs'); - late final _labs = _labsPtr.asFunction(); + late final ffi.Pointer> _suboptarg = + _lookup>('suboptarg'); - int llabs(int _Number) { - return _llabs(_Number); - } + ffi.Pointer get suboptarg => _suboptarg.value; - late final _llabsPtr = - _lookup>('llabs'); - late final _llabs = _llabsPtr.asFunction(); + set suboptarg(ffi.Pointer value) => _suboptarg.value = value; - int _abs64(int _Number) { - return __abs64(_Number); + ffi.Pointer getAppDataDir() { + return _getAppDataDir(); } - late final __abs64Ptr = - _lookup>( - '_abs64', + late final _getAppDataDirPtr = + _lookup Function()>>( + 'getAppDataDir', ); - late final __abs64 = __abs64Ptr.asFunction(); + late final _getAppDataDir = _getAppDataDirPtr + .asFunction Function()>(); - int _byteswap_ushort(int _Number) { - return __byteswap_ushort(_Number); + ffi.Pointer setup( + ffi.Pointer _logDir, + ffi.Pointer _dataDir, + ffi.Pointer _locale, + ffi.Pointer _env, + int logP, + int appsP, + int statusP, + int privateServerP, + int appEventP, + int consent, + ffi.Pointer api, + ) { + return _setup( + _logDir, + _dataDir, + _locale, + _env, + logP, + appsP, + statusP, + privateServerP, + appEventP, + consent, + api, + ); } - late final __byteswap_ushortPtr = + late final _setupPtr = _lookup< - ffi.NativeFunction - >('_byteswap_ushort'); - late final __byteswap_ushort = __byteswap_ushortPtr - .asFunction(); + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int64, + ffi.Int64, + ffi.Int64, + ffi.Int64, + ffi.Int64, + ffi.Int, + ffi.Pointer, + ) + > + >('setup'); + late final _setup = _setupPtr + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + int, + int, + int, + int, + int, + ffi.Pointer, + ) + >(); - int _byteswap_ulong(int _Number) { - return __byteswap_ulong(_Number); + ffi.Pointer updateTelemetryConsent(int consent) { + return _updateTelemetryConsent(consent); } - late final __byteswap_ulongPtr = - _lookup>( - '_byteswap_ulong', + late final _updateTelemetryConsentPtr = + _lookup Function(ffi.Int)>>( + 'updateTelemetryConsent', ); - late final __byteswap_ulong = __byteswap_ulongPtr - .asFunction(); - - int _byteswap_uint64(int _Number) { - return __byteswap_uint64(_Number); - } - - late final __byteswap_uint64Ptr = - _lookup< - ffi.NativeFunction - >('_byteswap_uint64'); - late final __byteswap_uint64 = __byteswap_uint64Ptr - .asFunction(); + late final _updateTelemetryConsent = _updateTelemetryConsentPtr + .asFunction Function(int)>(); - div_t div(int _Numerator, int _Denominator) { - return _div(_Numerator, _Denominator); + int isTelemetryEnabled() { + return _isTelemetryEnabled(); } - late final _divPtr = - _lookup>('div'); - late final _div = _divPtr.asFunction(); + late final _isTelemetryEnabledPtr = + _lookup>('isTelemetryEnabled'); + late final _isTelemetryEnabled = _isTelemetryEnabledPtr + .asFunction(); - ldiv_t ldiv(int _Numerator, int _Denominator) { - return _ldiv(_Numerator, _Denominator); + int isOAuthLogin() { + return _isOAuthLogin(); } - late final _ldivPtr = - _lookup>('ldiv'); - late final _ldiv = _ldivPtr.asFunction(); + late final _isOAuthLoginPtr = _lookup>( + 'isOAuthLogin', + ); + late final _isOAuthLogin = _isOAuthLoginPtr.asFunction(); - lldiv_t lldiv(int _Numerator, int _Denominator) { - return _lldiv(_Numerator, _Denominator); + ffi.Pointer getOAuthProvider() { + return _getOAuthProvider(); } - late final _lldivPtr = - _lookup>( - 'lldiv', + late final _getOAuthProviderPtr = + _lookup Function()>>( + 'getOAuthProvider', ); - late final _lldiv = _lldivPtr.asFunction(); - - int _rotl(int _Value, int _Shift) { - return __rotl(_Value, _Shift); - } - - late final __rotlPtr = - _lookup< - ffi.NativeFunction - >('_rotl'); - late final __rotl = __rotlPtr.asFunction(); + late final _getOAuthProvider = _getOAuthProviderPtr + .asFunction Function()>(); - int _lrotl(int _Value, int _Shift) { - return __lrotl(_Value, _Shift); + ffi.Pointer availableFeatures() { + return _availableFeatures(); } - late final __lrotlPtr = - _lookup< - ffi.NativeFunction - >('_lrotl'); - late final __lrotl = __lrotlPtr.asFunction(); + late final _availableFeaturesPtr = + _lookup Function()>>( + 'availableFeatures', + ); + late final _availableFeatures = _availableFeaturesPtr + .asFunction Function()>(); - int _rotl64(int _Value, int _Shift) { - return __rotl64(_Value, _Shift); + ffi.Pointer updateLocale(ffi.Pointer _locale) { + return _updateLocale(_locale); } - late final __rotl64Ptr = + late final _updateLocalePtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLongLong Function(ffi.UnsignedLongLong, ffi.Int) + ffi.Pointer Function(ffi.Pointer) > - >('_rotl64'); - late final __rotl64 = __rotl64Ptr.asFunction(); - - int _rotr(int _Value, int _Shift) { - return __rotr(_Value, _Shift); - } - - late final __rotrPtr = - _lookup< - ffi.NativeFunction - >('_rotr'); - late final __rotr = __rotrPtr.asFunction(); + >('updateLocale'); + late final _updateLocale = _updateLocalePtr + .asFunction Function(ffi.Pointer)>(); - int _lrotr(int _Value, int _Shift) { - return __lrotr(_Value, _Shift); + ffi.Pointer addSplitTunnelItem( + ffi.Pointer filterTypeC, + ffi.Pointer itemC, + ) { + return _addSplitTunnelItem(filterTypeC, itemC); } - late final __lrotrPtr = + late final _addSplitTunnelItemPtr = _lookup< - ffi.NativeFunction - >('_lrotr'); - late final __lrotr = __lrotrPtr.asFunction(); + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >('addSplitTunnelItem'); + late final _addSplitTunnelItem = _addSplitTunnelItemPtr + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); - int _rotr64(int _Value, int _Shift) { - return __rotr64(_Value, _Shift); + ffi.Pointer removeSplitTunnelItem( + ffi.Pointer filterTypeC, + ffi.Pointer itemC, + ) { + return _removeSplitTunnelItem(filterTypeC, itemC); } - late final __rotr64Ptr = + late final _removeSplitTunnelItemPtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLongLong Function(ffi.UnsignedLongLong, ffi.Int) + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) > - >('_rotr64'); - late final __rotr64 = __rotr64Ptr.asFunction(); - - void srand(int _Seed) { - return _srand(_Seed); - } - - late final _srandPtr = - _lookup>('srand'); - late final _srand = _srandPtr.asFunction(); - - int rand() { - return _rand(); - } - - late final _randPtr = _lookup>('rand'); - late final _rand = _randPtr.asFunction(); - - double atof(ffi.Pointer _String) { - return _atof(_String); - } - - late final _atofPtr = - _lookup)>>( - 'atof', - ); - late final _atof = _atofPtr - .asFunction)>(); - - int atoi(ffi.Pointer _String) { - return _atoi(_String); - } - - late final _atoiPtr = - _lookup)>>( - 'atoi', - ); - late final _atoi = _atoiPtr.asFunction)>(); - - int atol(ffi.Pointer _String) { - return _atol(_String); - } - - late final _atolPtr = - _lookup)>>( - 'atol', - ); - late final _atol = _atolPtr.asFunction)>(); + >('removeSplitTunnelItem'); + late final _removeSplitTunnelItem = _removeSplitTunnelItemPtr + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); - int atoll(ffi.Pointer _String) { - return _atoll(_String); + ffi.Pointer setSplitTunnelingEnabled(int enabled) { + return _setSplitTunnelingEnabled(enabled); } - late final _atollPtr = - _lookup)>>( - 'atoll', + late final _setSplitTunnelingEnabledPtr = + _lookup Function(ffi.Int)>>( + 'setSplitTunnelingEnabled', ); - late final _atoll = _atollPtr - .asFunction)>(); + late final _setSplitTunnelingEnabled = _setSplitTunnelingEnabledPtr + .asFunction Function(int)>(); - int _atoi64(ffi.Pointer _String) { - return __atoi64(_String); + int isSplitTunnelingEnabled() { + return _isSplitTunnelingEnabled(); } - late final __atoi64Ptr = - _lookup)>>( - '_atoi64', + late final _isSplitTunnelingEnabledPtr = + _lookup>( + 'isSplitTunnelingEnabled', ); - late final __atoi64 = __atoi64Ptr - .asFunction)>(); + late final _isSplitTunnelingEnabled = _isSplitTunnelingEnabledPtr + .asFunction(); - double _atof_l(ffi.Pointer _String, _locale_t _Locale) { - return __atof_l(_String, _Locale); + ffi.Pointer loadInstalledApps(ffi.Pointer dataDir) { + return _loadInstalledApps(dataDir); } - late final __atof_lPtr = + late final _loadInstalledAppsPtr = _lookup< ffi.NativeFunction< - ffi.Double Function(ffi.Pointer, _locale_t) + ffi.Pointer Function(ffi.Pointer) > - >('_atof_l'); - late final __atof_l = __atof_lPtr - .asFunction, _locale_t)>(); - - int _atoi_l(ffi.Pointer _String, _locale_t _Locale) { - return __atoi_l(_String, _Locale); - } - - late final __atoi_lPtr = - _lookup< - ffi.NativeFunction, _locale_t)> - >('_atoi_l'); - late final __atoi_l = __atoi_lPtr - .asFunction, _locale_t)>(); - - int _atol_l(ffi.Pointer _String, _locale_t _Locale) { - return __atol_l(_String, _Locale); - } - - late final __atol_lPtr = - _lookup< - ffi.NativeFunction, _locale_t)> - >('_atol_l'); - late final __atol_l = __atol_lPtr - .asFunction, _locale_t)>(); + >('loadInstalledApps'); + late final _loadInstalledApps = _loadInstalledAppsPtr + .asFunction Function(ffi.Pointer)>(); - int _atoll_l(ffi.Pointer _String, _locale_t _Locale) { - return __atoll_l(_String, _Locale); + ffi.Pointer loadInstalledAppIcon( + ffi.Pointer appPathC, + ffi.Pointer iconPathC, + ) { + return _loadInstalledAppIcon(appPathC, iconPathC); } - late final __atoll_lPtr = + late final _loadInstalledAppIconPtr = _lookup< ffi.NativeFunction< - ffi.LongLong Function(ffi.Pointer, _locale_t) + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) > - >('_atoll_l'); - late final __atoll_l = __atoll_lPtr - .asFunction, _locale_t)>(); + >('loadInstalledAppIcon'); + late final _loadInstalledAppIcon = _loadInstalledAppIconPtr + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); - int _atoi64_l(ffi.Pointer _String, _locale_t _Locale) { - return __atoi64_l(_String, _Locale); + ffi.Pointer getDataCapInfo() { + return _getDataCapInfo(); } - late final __atoi64_lPtr = - _lookup< - ffi.NativeFunction< - ffi.LongLong Function(ffi.Pointer, _locale_t) - > - >('_atoi64_l'); - late final __atoi64_l = __atoi64_lPtr - .asFunction, _locale_t)>(); + late final _getDataCapInfoPtr = + _lookup Function()>>( + 'getDataCapInfo', + ); + late final _getDataCapInfo = _getDataCapInfoPtr + .asFunction Function()>(); - int _atoflt(ffi.Pointer<_CRT_FLOAT> _Result, ffi.Pointer _String) { - return __atoflt(_Result, _String); + ffi.Pointer reportIssue( + ffi.Pointer emailC, + ffi.Pointer typeC, + ffi.Pointer descC, + ffi.Pointer deviceC, + ffi.Pointer modelC, + ffi.Pointer logPathC, + ffi.Pointer attachmentsJSONC, + ) { + return _reportIssue( + emailC, + typeC, + descC, + deviceC, + modelC, + logPathC, + attachmentsJSONC, + ); } - late final __atofltPtr = + late final _reportIssuePtr = _lookup< ffi.NativeFunction< - ffi.Int Function(ffi.Pointer<_CRT_FLOAT>, ffi.Pointer) + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) > - >('_atoflt'); - late final __atoflt = __atofltPtr + >('reportIssue'); + late final _reportIssue = _reportIssuePtr .asFunction< - int Function(ffi.Pointer<_CRT_FLOAT>, ffi.Pointer) + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) >(); - int _atodbl(ffi.Pointer<_CRT_DOUBLE> _Result, ffi.Pointer _String) { - return __atodbl(_Result, _String); + ffi.Pointer getSelectedServerJSON() { + return _getSelectedServerJSON(); } - late final __atodblPtr = - _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer<_CRT_DOUBLE>, ffi.Pointer) - > - >('_atodbl'); - late final __atodbl = __atodblPtr - .asFunction< - int Function(ffi.Pointer<_CRT_DOUBLE>, ffi.Pointer) - >(); + late final _getSelectedServerJSONPtr = + _lookup Function()>>( + 'getSelectedServerJSON', + ); + late final _getSelectedServerJSON = _getSelectedServerJSONPtr + .asFunction Function()>(); - int _atoldbl(ffi.Pointer<_LDOUBLE> _Result, ffi.Pointer _String) { - return __atoldbl(_Result, _String); + ffi.Pointer getAutoLocation() { + return _getAutoLocation(); } - late final __atoldblPtr = - _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer<_LDOUBLE>, ffi.Pointer) - > - >('_atoldbl'); - late final __atoldbl = __atoldblPtr - .asFunction, ffi.Pointer)>(); - - int _atoflt_l( - ffi.Pointer<_CRT_FLOAT> _Result, - ffi.Pointer _String, - _locale_t _Locale, - ) { - return __atoflt_l(_Result, _String, _Locale); + late final _getAutoLocationPtr = + _lookup Function()>>( + 'getAutoLocation', + ); + late final _getAutoLocation = _getAutoLocationPtr + .asFunction Function()>(); + + ffi.Pointer isTagAvailable(ffi.Pointer _tag) { + return _isTagAvailable(_tag); } - late final __atoflt_lPtr = + late final _isTagAvailablePtr = _lookup< ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer<_CRT_FLOAT>, - ffi.Pointer, - _locale_t, - ) + ffi.Pointer Function(ffi.Pointer) > - >('_atoflt_l'); - late final __atoflt_l = __atoflt_lPtr - .asFunction< - int Function(ffi.Pointer<_CRT_FLOAT>, ffi.Pointer, _locale_t) - >(); + >('isTagAvailable'); + late final _isTagAvailable = _isTagAvailablePtr + .asFunction Function(ffi.Pointer)>(); - int _atodbl_l( - ffi.Pointer<_CRT_DOUBLE> _Result, - ffi.Pointer _String, - _locale_t _Locale, - ) { - return __atodbl_l(_Result, _String, _Locale); + ffi.Pointer getAvailableServers() { + return _getAvailableServers(); } - late final __atodbl_lPtr = - _lookup< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer<_CRT_DOUBLE>, - ffi.Pointer, - _locale_t, - ) - > - >('_atodbl_l'); - late final __atodbl_l = __atodbl_lPtr - .asFunction< - int Function(ffi.Pointer<_CRT_DOUBLE>, ffi.Pointer, _locale_t) - >(); + late final _getAvailableServersPtr = + _lookup Function()>>( + 'getAvailableServers', + ); + late final _getAvailableServers = _getAvailableServersPtr + .asFunction Function()>(); - int _atoldbl_l( - ffi.Pointer<_LDOUBLE> _Result, - ffi.Pointer _String, - _locale_t _Locale, - ) { - return __atoldbl_l(_Result, _String, _Locale); + ffi.Pointer startVPN() { + return _startVPN(); } - late final __atoldbl_lPtr = - _lookup< - ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer<_LDOUBLE>, - ffi.Pointer, - _locale_t, - ) - > - >('_atoldbl_l'); - late final __atoldbl_l = __atoldbl_lPtr - .asFunction< - int Function(ffi.Pointer<_LDOUBLE>, ffi.Pointer, _locale_t) - >(); + late final _startVPNPtr = + _lookup Function()>>('startVPN'); + late final _startVPN = _startVPNPtr + .asFunction Function()>(); - double strtof( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - ) { - return _strtof(_String, _EndPtr); + ffi.Pointer stopVPN() { + return _stopVPN(); } - late final _strtofPtr = + late final _stopVPNPtr = + _lookup Function()>>('stopVPN'); + late final _stopVPN = _stopVPNPtr + .asFunction Function()>(); + + ffi.Pointer connectToServer(ffi.Pointer _tag) { + return _connectToServer(_tag); + } + + late final _connectToServerPtr = _lookup< ffi.NativeFunction< - ffi.Float Function( - ffi.Pointer, - ffi.Pointer>, - ) + ffi.Pointer Function(ffi.Pointer) > - >('strtof'); - late final _strtof = _strtofPtr - .asFunction< - double Function( - ffi.Pointer, - ffi.Pointer>, - ) - >(); + >('connectToServer'); + late final _connectToServer = _connectToServerPtr + .asFunction Function(ffi.Pointer)>(); + + int isVPNConnected() { + return _isVPNConnected(); + } + + late final _isVPNConnectedPtr = + _lookup>('isVPNConnected'); + late final _isVPNConnected = _isVPNConnectedPtr.asFunction(); + + ffi.Pointer getUserData() { + return _getUserData(); + } + + late final _getUserDataPtr = + _lookup Function()>>( + 'getUserData', + ); + late final _getUserData = _getUserDataPtr + .asFunction Function()>(); + + ffi.Pointer fetchUserData() { + return _fetchUserData(); + } + + late final _fetchUserDataPtr = + _lookup Function()>>( + 'fetchUserData', + ); + late final _fetchUserData = _fetchUserDataPtr + .asFunction Function()>(); - double _strtof_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - _locale_t _Locale, + ffi.Pointer stripeSubscriptionPaymentRedirect( + ffi.Pointer subType, + ffi.Pointer _planId, + ffi.Pointer _email, + ffi.Pointer _idempotencyKey, + ffi.Pointer _couponCode, ) { - return __strtof_l(_String, _EndPtr, _Locale); + return _stripeSubscriptionPaymentRedirect( + subType, + _planId, + _email, + _idempotencyKey, + _couponCode, + ); } - late final __strtof_lPtr = + late final _stripeSubscriptionPaymentRedirectPtr = _lookup< ffi.NativeFunction< - ffi.Float Function( + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - _locale_t, ) > - >('_strtof_l'); - late final __strtof_l = __strtof_lPtr - .asFunction< - double Function( - ffi.Pointer, - ffi.Pointer>, - _locale_t, - ) - >(); + >('stripeSubscriptionPaymentRedirect'); + late final _stripeSubscriptionPaymentRedirect = + _stripeSubscriptionPaymentRedirectPtr + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + >(); - double strtod( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, + ffi.Pointer paymentRedirect( + ffi.Pointer _plan, + ffi.Pointer _provider, + ffi.Pointer _email, + ffi.Pointer _idempotencyKey, + ffi.Pointer _couponCode, ) { - return _strtod(_String, _EndPtr); + return _paymentRedirect( + _plan, + _provider, + _email, + _idempotencyKey, + _couponCode, + ); } - late final _strtodPtr = + late final _paymentRedirectPtr = _lookup< ffi.NativeFunction< - ffi.Double Function( + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, ) > - >('strtod'); - late final _strtod = _strtodPtr + >('paymentRedirect'); + late final _paymentRedirect = _paymentRedirectPtr .asFunction< - double Function( + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, ) >(); - double _strtod_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - _locale_t _Locale, - ) { - return __strtod_l(_String, _EndPtr, _Locale); + ffi.Pointer stripeBillingPortalUrl() { + return _stripeBillingPortalUrl(); } - late final __strtod_lPtr = - _lookup< - ffi.NativeFunction< - ffi.Double Function( - ffi.Pointer, - ffi.Pointer>, - _locale_t, - ) + late final _stripeBillingPortalUrlPtr = + _lookup Function()>>( + 'stripeBillingPortalUrl', + ); + late final _stripeBillingPortalUrl = _stripeBillingPortalUrlPtr + .asFunction Function()>(); + + ffi.Pointer plans() { + return _plans(); + } + + late final _plansPtr = + _lookup Function()>>('plans'); + late final _plans = _plansPtr.asFunction Function()>(); + + ffi.Pointer oauthLoginUrl(ffi.Pointer _provider) { + return _oauthLoginUrl(_provider); + } + + late final _oauthLoginUrlPtr = + _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer) > - >('_strtod_l'); - late final __strtod_l = __strtod_lPtr - .asFunction< - double Function( - ffi.Pointer, - ffi.Pointer>, - _locale_t, - ) - >(); + >('oauthLoginUrl'); + late final _oauthLoginUrl = _oauthLoginUrlPtr + .asFunction Function(ffi.Pointer)>(); - int strtol( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - ) { - return _strtol(_String, _EndPtr, _Radix); + ffi.Pointer oAuthLoginCallback(ffi.Pointer _oAuthToken) { + return _oAuthLoginCallback(_oAuthToken); } - late final _strtolPtr = + late final _oAuthLoginCallbackPtr = _lookup< ffi.NativeFunction< - ffi.Long Function( - ffi.Pointer, - ffi.Pointer>, - ffi.Int, - ) + ffi.Pointer Function(ffi.Pointer) > - >('strtol'); - late final _strtol = _strtolPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, - int, - ) - >(); + >('oAuthLoginCallback'); + late final _oAuthLoginCallback = _oAuthLoginCallbackPtr + .asFunction Function(ffi.Pointer)>(); - int _strtol_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, + ffi.Pointer login( + ffi.Pointer _email, + ffi.Pointer _password, ) { - return __strtol_l(_String, _EndPtr, _Radix, _Locale); + return _login(_email, _password); } - late final __strtol_lPtr = + late final _loginPtr = _lookup< ffi.NativeFunction< - ffi.Long Function( + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - ffi.Int, - _locale_t, ) > - >('_strtol_l'); - late final __strtol_l = __strtol_lPtr + >('login'); + late final _login = _loginPtr .asFunction< - int Function( + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - int, - _locale_t, ) >(); - int strtoll( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, + ffi.Pointer signup( + ffi.Pointer _email, + ffi.Pointer _password, ) { - return _strtoll(_String, _EndPtr, _Radix); + return _signup(_email, _password); } - late final _strtollPtr = + late final _signupPtr = _lookup< ffi.NativeFunction< - ffi.LongLong Function( + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - ffi.Int, ) > - >('strtoll'); - late final _strtoll = _strtollPtr + >('signup'); + late final _signup = _signupPtr .asFunction< - int Function( + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - int, ) >(); - int _strtoll_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, - ) { - return __strtoll_l(_String, _EndPtr, _Radix, _Locale); + ffi.Pointer logout(ffi.Pointer _email) { + return _logout(_email); } - late final __strtoll_lPtr = + late final _logoutPtr = _lookup< ffi.NativeFunction< - ffi.LongLong Function( - ffi.Pointer, - ffi.Pointer>, - ffi.Int, - _locale_t, - ) + ffi.Pointer Function(ffi.Pointer) > - >('_strtoll_l'); - late final __strtoll_l = __strtoll_lPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, - int, - _locale_t, - ) - >(); + >('logout'); + late final _logout = _logoutPtr + .asFunction Function(ffi.Pointer)>(); - int strtoul( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - ) { - return _strtoul(_String, _EndPtr, _Radix); + ffi.Pointer startRecoveryByEmail(ffi.Pointer _email) { + return _startRecoveryByEmail(_email); } - late final _strtoulPtr = + late final _startRecoveryByEmailPtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLong Function( - ffi.Pointer, - ffi.Pointer>, - ffi.Int, - ) + ffi.Pointer Function(ffi.Pointer) > - >('strtoul'); - late final _strtoul = _strtoulPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, - int, - ) - >(); + >('startRecoveryByEmail'); + late final _startRecoveryByEmail = _startRecoveryByEmailPtr + .asFunction Function(ffi.Pointer)>(); - int _strtoul_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, + ffi.Pointer validateEmailRecoveryCode( + ffi.Pointer _email, + ffi.Pointer _code, ) { - return __strtoul_l(_String, _EndPtr, _Radix, _Locale); + return _validateEmailRecoveryCode(_email, _code); } - late final __strtoul_lPtr = + late final _validateEmailRecoveryCodePtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLong Function( + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - ffi.Int, - _locale_t, ) > - >('_strtoul_l'); - late final __strtoul_l = __strtoul_lPtr + >('validateEmailRecoveryCode'); + late final _validateEmailRecoveryCode = _validateEmailRecoveryCodePtr .asFunction< - int Function( + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - int, - _locale_t, ) >(); - int strtoull( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, + ffi.Pointer completeRecoveryByEmail( + ffi.Pointer _email, + ffi.Pointer _newPassword, + ffi.Pointer _code, ) { - return _strtoull(_String, _EndPtr, _Radix); + return _completeRecoveryByEmail(_email, _newPassword, _code); } - late final _strtoullPtr = + late final _completeRecoveryByEmailPtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLongLong Function( + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - ffi.Int, ) > - >('strtoull'); - late final _strtoull = _strtoullPtr + >('completeRecoveryByEmail'); + late final _completeRecoveryByEmail = _completeRecoveryByEmailPtr .asFunction< - int Function( + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - int, ) >(); - int _strtoull_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, - ) { - return __strtoull_l(_String, _EndPtr, _Radix, _Locale); + ffi.Pointer removeDevice(ffi.Pointer deviceId) { + return _removeDevice(deviceId); } - late final __strtoull_lPtr = + late final _removeDevicePtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLongLong Function( - ffi.Pointer, - ffi.Pointer>, - ffi.Int, - _locale_t, - ) + ffi.Pointer Function(ffi.Pointer) > - >('_strtoull_l'); - late final __strtoull_l = __strtoull_lPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, - int, - _locale_t, - ) - >(); + >('removeDevice'); + late final _removeDevice = _removeDevicePtr + .asFunction Function(ffi.Pointer)>(); - int _strtoi64( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, + ffi.Pointer referralAttachment( + ffi.Pointer _referralCode, ) { - return __strtoi64(_String, _EndPtr, _Radix); + return _referralAttachment(_referralCode); } - late final __strtoi64Ptr = + late final _referralAttachmentPtr = _lookup< ffi.NativeFunction< - ffi.LongLong Function( - ffi.Pointer, - ffi.Pointer>, - ffi.Int, - ) + ffi.Pointer Function(ffi.Pointer) > - >('_strtoi64'); - late final __strtoi64 = __strtoi64Ptr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer>, - int, - ) - >(); + >('referralAttachment'); + late final _referralAttachment = _referralAttachmentPtr + .asFunction Function(ffi.Pointer)>(); - int _strtoi64_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, + ffi.Pointer referralAttachmentV2( + ffi.Pointer _referralCode, + ffi.Pointer _channel, ) { - return __strtoi64_l(_String, _EndPtr, _Radix, _Locale); + return _referralAttachmentV2(_referralCode, _channel); } - late final __strtoi64_lPtr = + late final _referralAttachmentV2Ptr = _lookup< ffi.NativeFunction< - ffi.LongLong Function( + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - ffi.Int, - _locale_t, ) > - >('_strtoi64_l'); - late final __strtoi64_l = __strtoi64_lPtr + >('referralAttachmentV2'); + late final _referralAttachmentV2 = _referralAttachmentV2Ptr .asFunction< - int Function( + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - int, - _locale_t, ) >(); - int _strtoui64( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, + ffi.Pointer startChangeEmail( + ffi.Pointer _newEmail, + ffi.Pointer _password, ) { - return __strtoui64(_String, _EndPtr, _Radix); + return _startChangeEmail(_newEmail, _password); } - late final __strtoui64Ptr = + late final _startChangeEmailPtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLongLong Function( + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - ffi.Int, ) > - >('_strtoui64'); - late final __strtoui64 = __strtoui64Ptr + >('startChangeEmail'); + late final _startChangeEmail = _startChangeEmailPtr .asFunction< - int Function( + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - int, ) >(); - int _strtoui64_l( - ffi.Pointer _String, - ffi.Pointer> _EndPtr, - int _Radix, - _locale_t _Locale, - ) { - return __strtoui64_l(_String, _EndPtr, _Radix, _Locale); + ffi.Pointer completeChangeEmail( + ffi.Pointer _newEmail, + ffi.Pointer _password, + ffi.Pointer _code, + ) { + return _completeChangeEmail(_newEmail, _password, _code); } - late final __strtoui64_lPtr = + late final _completeChangeEmailPtr = _lookup< ffi.NativeFunction< - ffi.UnsignedLongLong Function( + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - ffi.Int, - _locale_t, ) > - >('_strtoui64_l'); - late final __strtoui64_l = __strtoui64_lPtr + >('completeChangeEmail'); + late final _completeChangeEmail = _completeChangeEmailPtr .asFunction< - int Function( + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, ffi.Pointer, - ffi.Pointer>, - int, - _locale_t, ) >(); - int _itoa_s( - int _Value, - ffi.Pointer _Buffer, - int _BufferCount, - int _Radix, + ffi.Pointer deleteAccount( + ffi.Pointer _email, + ffi.Pointer _password, ) { - return __itoa_s(_Value, _Buffer, _BufferCount, _Radix); + return _deleteAccount(_email, _password); } - late final __itoa_sPtr = + late final _deleteAccountPtr = _lookup< ffi.NativeFunction< - errno_t Function(ffi.Int, ffi.Pointer, ffi.Size, ffi.Int) + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) > - >('_itoa_s'); - late final __itoa_s = __itoa_sPtr - .asFunction, int, int)>(); - - ffi.Pointer _itoa( - int _Value, - ffi.Pointer _Buffer, - int _Radix, + >('deleteAccount'); + late final _deleteAccount = _deleteAccountPtr + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); + + ffi.Pointer activationCode( + ffi.Pointer _email, + ffi.Pointer _resellerCode, ) { - return __itoa(_Value, _Buffer, _Radix); + return _activationCode(_email, _resellerCode); } - late final __itoaPtr = + late final _activationCodePtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( - ffi.Int, ffi.Pointer, - ffi.Int, + ffi.Pointer, ) > - >('_itoa'); - late final __itoa = __itoaPtr + >('activationCode'); + late final _activationCode = _activationCodePtr .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) >(); - int _ltoa_s( - int _Value, - ffi.Pointer _Buffer, - int _BufferCount, - int _Radix, - ) { - return __ltoa_s(_Value, _Buffer, _BufferCount, _Radix); + void freeCString(ffi.Pointer cstr) { + return _freeCString(cstr); } - late final __ltoa_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function(ffi.Long, ffi.Pointer, ffi.Size, ffi.Int) - > - >('_ltoa_s'); - late final __ltoa_s = __ltoa_sPtr - .asFunction, int, int)>(); - - ffi.Pointer _ltoa( - int _Value, - ffi.Pointer _Buffer, - int _Radix, - ) { - return __ltoa(_Value, _Buffer, _Radix); + late final _freeCStringPtr = + _lookup)>>( + 'freeCString', + ); + late final _freeCString = _freeCStringPtr + .asFunction)>(); + + ffi.Pointer patchSettings(ffi.Pointer patchJSON) { + return _patchSettings(patchJSON); } - late final __ltoaPtr = + late final _patchSettingsPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.Long, - ffi.Pointer, - ffi.Int, - ) + ffi.Pointer Function(ffi.Pointer) > - >('_ltoa'); - late final __ltoa = __ltoaPtr - .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) - >(); + >('patchSettings'); + late final _patchSettings = _patchSettingsPtr + .asFunction Function(ffi.Pointer)>(); - int _ultoa_s( - int _Value, - ffi.Pointer _Buffer, - int _BufferCount, - int _Radix, - ) { - return __ultoa_s(_Value, _Buffer, _BufferCount, _Radix); + ffi.Pointer getSettings() { + return _getSettings(); + } + + late final _getSettingsPtr = + _lookup Function()>>( + 'getSettings', + ); + late final _getSettings = _getSettingsPtr + .asFunction Function()>(); + + ffi.Pointer patchEnvVars(ffi.Pointer patchJSON) { + return _patchEnvVars(patchJSON); } - late final __ultoa_sPtr = + late final _patchEnvVarsPtr = _lookup< ffi.NativeFunction< - errno_t Function( - ffi.UnsignedLong, - ffi.Pointer, - ffi.Size, - ffi.Int, - ) + ffi.Pointer Function(ffi.Pointer) > - >('_ultoa_s'); - late final __ultoa_s = __ultoa_sPtr - .asFunction, int, int)>(); - - ffi.Pointer _ultoa( - int _Value, - ffi.Pointer _Buffer, - int _Radix, - ) { - return __ultoa(_Value, _Buffer, _Radix); + >('patchEnvVars'); + late final _patchEnvVars = _patchEnvVarsPtr + .asFunction Function(ffi.Pointer)>(); + + ffi.Pointer getEnvVars() { + return _getEnvVars(); + } + + late final _getEnvVarsPtr = + _lookup Function()>>( + 'getEnvVars', + ); + late final _getEnvVars = _getEnvVarsPtr + .asFunction Function()>(); + + ffi.Pointer runURLTests() { + return _runURLTests(); + } + + late final _runURLTestsPtr = + _lookup Function()>>( + 'runURLTests', + ); + late final _runURLTests = _runURLTestsPtr + .asFunction Function()>(); + + ffi.Pointer updateConfig() { + return _updateConfig(); + } + + late final _updateConfigPtr = + _lookup Function()>>( + 'updateConfig', + ); + late final _updateConfig = _updateConfigPtr + .asFunction Function()>(); + + ffi.Pointer clearTunnelCache() { + return _clearTunnelCache(); + } + + late final _clearTunnelCachePtr = + _lookup Function()>>( + 'clearTunnelCache', + ); + late final _clearTunnelCache = _clearTunnelCachePtr + .asFunction Function()>(); + + ffi.Pointer digitalOceanPrivateServer() { + return _digitalOceanPrivateServer(); + } + + late final _digitalOceanPrivateServerPtr = + _lookup Function()>>( + 'digitalOceanPrivateServer', + ); + late final _digitalOceanPrivateServer = _digitalOceanPrivateServerPtr + .asFunction Function()>(); + + ffi.Pointer googleCloudPrivateServer() { + return _googleCloudPrivateServer(); + } + + late final _googleCloudPrivateServerPtr = + _lookup Function()>>( + 'googleCloudPrivateServer', + ); + late final _googleCloudPrivateServer = _googleCloudPrivateServerPtr + .asFunction Function()>(); + + ffi.Pointer selectAccount(ffi.Pointer _account) { + return _selectAccount(_account); } - late final __ultoaPtr = + late final _selectAccountPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.UnsignedLong, - ffi.Pointer, - ffi.Int, - ) + ffi.Pointer Function(ffi.Pointer) > - >('_ultoa'); - late final __ultoa = __ultoaPtr - .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) - >(); + >('selectAccount'); + late final _selectAccount = _selectAccountPtr + .asFunction Function(ffi.Pointer)>(); - int _i64toa_s( - int _Value, - ffi.Pointer _Buffer, - int _BufferCount, - int _Radix, - ) { - return __i64toa_s(_Value, _Buffer, _BufferCount, _Radix); + ffi.Pointer selectProject(ffi.Pointer _project) { + return _selectProject(_project); } - late final __i64toa_sPtr = + late final _selectProjectPtr = _lookup< ffi.NativeFunction< - errno_t Function( - ffi.LongLong, - ffi.Pointer, - ffi.Size, - ffi.Int, - ) + ffi.Pointer Function(ffi.Pointer) > - >('_i64toa_s'); - late final __i64toa_s = __i64toa_sPtr - .asFunction, int, int)>(); - - ffi.Pointer _i64toa( - int _Value, - ffi.Pointer _Buffer, - int _Radix, + >('selectProject'); + late final _selectProject = _selectProjectPtr + .asFunction Function(ffi.Pointer)>(); + + ffi.Pointer validateSession() { + return _validateSession(); + } + + late final _validateSessionPtr = + _lookup Function()>>( + 'validateSession', + ); + late final _validateSession = _validateSessionPtr + .asFunction Function()>(); + + ffi.Pointer startDepolyment( + ffi.Pointer _selectedLocation, + ffi.Pointer _serverName, ) { - return __i64toa(_Value, _Buffer, _Radix); + return _startDepolyment(_selectedLocation, _serverName); } - late final __i64toaPtr = + late final _startDepolymentPtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( - ffi.LongLong, ffi.Pointer, - ffi.Int, + ffi.Pointer, ) > - >('_i64toa'); - late final __i64toa = __i64toaPtr + >('startDepolyment'); + late final _startDepolyment = _startDepolymentPtr .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) >(); - int _ui64toa_s( - int _Value, - ffi.Pointer _Buffer, - int _BufferCount, - int _Radix, - ) { - return __ui64toa_s(_Value, _Buffer, _BufferCount, _Radix); + ffi.Pointer cancelDepolyment() { + return _cancelDepolyment(); } - late final __ui64toa_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.UnsignedLongLong, - ffi.Pointer, - ffi.Size, - ffi.Int, - ) - > - >('_ui64toa_s'); - late final __ui64toa_s = __ui64toa_sPtr - .asFunction, int, int)>(); - - ffi.Pointer _ui64toa( - int _Value, - ffi.Pointer _Buffer, - int _Radix, + late final _cancelDepolymentPtr = + _lookup Function()>>( + 'cancelDepolyment', + ); + late final _cancelDepolyment = _cancelDepolymentPtr + .asFunction Function()>(); + + ffi.Pointer addServerManagerInstance( + ffi.Pointer _ip, + ffi.Pointer _port, + ffi.Pointer _accessToken, + ffi.Pointer _tag, ) { - return __ui64toa(_Value, _Buffer, _Radix); + return _addServerManagerInstance(_ip, _port, _accessToken, _tag); } - late final __ui64toaPtr = + late final _addServerManagerInstancePtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( - ffi.UnsignedLongLong, ffi.Pointer, - ffi.Int, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, ) > - >('_ui64toa'); - late final __ui64toa = __ui64toaPtr - .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) - >(); - - int _ecvt_s( - ffi.Pointer _Buffer, - int _BufferCount, - double _Value, - int _DigitCount, - ffi.Pointer _PtDec, - ffi.Pointer _PtSign, - ) { - return __ecvt_s( - _Buffer, - _BufferCount, - _Value, - _DigitCount, - _PtDec, - _PtSign, - ); - } - - late final __ecvt_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Size, - ffi.Double, - ffi.Int, - ffi.Pointer, - ffi.Pointer, - ) - > - >('_ecvt_s'); - late final __ecvt_s = __ecvt_sPtr - .asFunction< - int Function( - ffi.Pointer, - int, - double, - int, - ffi.Pointer, - ffi.Pointer, - ) - >(); - - ffi.Pointer _ecvt( - double _Value, - int _DigitCount, - ffi.Pointer _PtDec, - ffi.Pointer _PtSign, - ) { - return __ecvt(_Value, _DigitCount, _PtDec, _PtSign); - } - - late final __ecvtPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Double, - ffi.Int, - ffi.Pointer, - ffi.Pointer, - ) - > - >('_ecvt'); - late final __ecvt = __ecvtPtr + >('addServerManagerInstance'); + late final _addServerManagerInstance = _addServerManagerInstancePtr .asFunction< ffi.Pointer Function( - double, - int, - ffi.Pointer, - ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, ) >(); - int _fcvt_s( - ffi.Pointer _Buffer, - int _BufferCount, - double _Value, - int _FractionalDigitCount, - ffi.Pointer _PtDec, - ffi.Pointer _PtSign, + ffi.Pointer inviteToServerManagerInstance( + ffi.Pointer _ip, + ffi.Pointer _port, + ffi.Pointer _accessToken, + ffi.Pointer _inviteName, ) { - return __fcvt_s( - _Buffer, - _BufferCount, - _Value, - _FractionalDigitCount, - _PtDec, - _PtSign, + return _inviteToServerManagerInstance( + _ip, + _port, + _accessToken, + _inviteName, ); } - late final __fcvt_sPtr = + late final _inviteToServerManagerInstancePtr = _lookup< ffi.NativeFunction< - errno_t Function( + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, ffi.Pointer, - ffi.Size, - ffi.Double, - ffi.Int, - ffi.Pointer, - ffi.Pointer, ) > - >('_fcvt_s'); - late final __fcvt_s = __fcvt_sPtr + >('inviteToServerManagerInstance'); + late final _inviteToServerManagerInstance = _inviteToServerManagerInstancePtr .asFunction< - int Function( + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, ffi.Pointer, - int, - double, - int, - ffi.Pointer, - ffi.Pointer, ) >(); - ffi.Pointer _fcvt( - double _Value, - int _FractionalDigitCount, - ffi.Pointer _PtDec, - ffi.Pointer _PtSign, + ffi.Pointer revokeServerManagerInvite( + ffi.Pointer _ip, + ffi.Pointer _port, + ffi.Pointer _accessToken, + ffi.Pointer _inviteName, ) { - return __fcvt(_Value, _FractionalDigitCount, _PtDec, _PtSign); + return _revokeServerManagerInvite(_ip, _port, _accessToken, _inviteName); } - late final __fcvtPtr = + late final _revokeServerManagerInvitePtr = _lookup< ffi.NativeFunction< ffi.Pointer Function( - ffi.Double, - ffi.Int, - ffi.Pointer, - ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, ) > - >('_fcvt'); - late final __fcvt = __fcvtPtr + >('revokeServerManagerInvite'); + late final _revokeServerManagerInvite = _revokeServerManagerInvitePtr .asFunction< ffi.Pointer Function( - double, - int, - ffi.Pointer, - ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, ) >(); - int _gcvt_s( - ffi.Pointer _Buffer, - int _BufferCount, - double _Value, - int _DigitCount, - ) { - return __gcvt_s(_Buffer, _BufferCount, _Value, _DigitCount); - } - - late final __gcvt_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function(ffi.Pointer, ffi.Size, ffi.Double, ffi.Int) - > - >('_gcvt_s'); - late final __gcvt_s = __gcvt_sPtr - .asFunction, int, double, int)>(); - - ffi.Pointer _gcvt( - double _Value, - int _DigitCount, - ffi.Pointer _Buffer, + ffi.Pointer addServerBasedOnURLs( + ffi.Pointer _urls, + int _skipCertVerification, ) { - return __gcvt(_Value, _DigitCount, _Buffer); + return _addServerBasedOnURLs(_urls, _skipCertVerification); } - late final __gcvtPtr = + late final _addServerBasedOnURLsPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.Double, - ffi.Int, - ffi.Pointer, - ) + ffi.Pointer Function(ffi.Pointer, ffi.Int) > - >('_gcvt'); - late final __gcvt = __gcvtPtr - .asFunction< - ffi.Pointer Function(double, int, ffi.Pointer) - >(); - - int ___mb_cur_max_func() { - return ____mb_cur_max_func(); - } - - late final ____mb_cur_max_funcPtr = - _lookup>('___mb_cur_max_func'); - late final ____mb_cur_max_func = ____mb_cur_max_funcPtr - .asFunction(); + >('addServerBasedOnURLs'); + late final _addServerBasedOnURLs = _addServerBasedOnURLsPtr + .asFunction Function(ffi.Pointer, int)>(); - int ___mb_cur_max_l_func(_locale_t _Locale) { - return ____mb_cur_max_l_func(_Locale); + ffi.Pointer setBlockAdsEnabled(int enabled) { + return _setBlockAdsEnabled(enabled); } - late final ____mb_cur_max_l_funcPtr = - _lookup>( - '___mb_cur_max_l_func', + late final _setBlockAdsEnabledPtr = + _lookup Function(ffi.Int)>>( + 'setBlockAdsEnabled', ); - late final ____mb_cur_max_l_func = ____mb_cur_max_l_funcPtr - .asFunction(); - - int mblen(ffi.Pointer _Ch, int _MaxCount) { - return _mblen(_Ch, _MaxCount); - } - - late final _mblenPtr = - _lookup< - ffi.NativeFunction, ffi.Size)> - >('mblen'); - late final _mblen = _mblenPtr - .asFunction, int)>(); + late final _setBlockAdsEnabled = _setBlockAdsEnabledPtr + .asFunction Function(int)>(); - int _mblen_l(ffi.Pointer _Ch, int _MaxCount, _locale_t _Locale) { - return __mblen_l(_Ch, _MaxCount, _Locale); + int isBlockAdsEnabled() { + return _isBlockAdsEnabled(); } - late final __mblen_lPtr = - _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.Size, _locale_t) - > - >('_mblen_l'); - late final __mblen_l = __mblen_lPtr - .asFunction, int, _locale_t)>(); + late final _isBlockAdsEnabledPtr = + _lookup>('isBlockAdsEnabled'); + late final _isBlockAdsEnabled = _isBlockAdsEnabledPtr + .asFunction(); - int _mbstrlen(ffi.Pointer _String) { - return __mbstrlen(_String); + ffi.Pointer setSmartRoutingEnabled(int enabled) { + return _setSmartRoutingEnabled(enabled); } - late final __mbstrlenPtr = - _lookup)>>( - '_mbstrlen', + late final _setSmartRoutingEnabledPtr = + _lookup Function(ffi.Int)>>( + 'setSmartRoutingEnabled', ); - late final __mbstrlen = __mbstrlenPtr - .asFunction)>(); + late final _setSmartRoutingEnabled = _setSmartRoutingEnabledPtr + .asFunction Function(int)>(); - int _mbstrlen_l(ffi.Pointer _String, _locale_t _Locale) { - return __mbstrlen_l(_String, _Locale); + int isSmartRoutingEnabled() { + return _isSmartRoutingEnabled(); } - late final __mbstrlen_lPtr = - _lookup< - ffi.NativeFunction, _locale_t)> - >('_mbstrlen_l'); - late final __mbstrlen_l = __mbstrlen_lPtr - .asFunction, _locale_t)>(); + late final _isSmartRoutingEnabledPtr = + _lookup>('isSmartRoutingEnabled'); + late final _isSmartRoutingEnabled = _isSmartRoutingEnabledPtr + .asFunction(); - int _mbstrnlen(ffi.Pointer _String, int _MaxCount) { - return __mbstrnlen(_String, _MaxCount); + ffi.Pointer getSplitTunnelState() { + return _getSplitTunnelState(); } - late final __mbstrnlenPtr = - _lookup< - ffi.NativeFunction, ffi.Size)> - >('_mbstrnlen'); - late final __mbstrnlen = __mbstrnlenPtr - .asFunction, int)>(); - - int _mbstrnlen_l( - ffi.Pointer _String, - int _MaxCount, - _locale_t _Locale, - ) { - return __mbstrnlen_l(_String, _MaxCount, _Locale); - } + late final _getSplitTunnelStatePtr = + _lookup Function()>>( + 'getSplitTunnelState', + ); + late final _getSplitTunnelState = _getSplitTunnelStatePtr + .asFunction Function()>(); - late final __mbstrnlen_lPtr = - _lookup< - ffi.NativeFunction< - ffi.Size Function(ffi.Pointer, ffi.Size, _locale_t) - > - >('_mbstrnlen_l'); - late final __mbstrnlen_l = __mbstrnlen_lPtr - .asFunction, int, _locale_t)>(); - - int mbtowc( - ffi.Pointer _DstCh, - ffi.Pointer _SrcCh, - int _SrcSizeInBytes, - ) { - return _mbtowc(_DstCh, _SrcCh, _SrcSizeInBytes); + ffi.Pointer getSplitTunnelItems(ffi.Pointer filterTypeC) { + return _getSplitTunnelItems(filterTypeC); } - late final _mbtowcPtr = + late final _getSplitTunnelItemsPtr = _lookup< ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ) + ffi.Pointer Function(ffi.Pointer) > - >('mbtowc'); - late final _mbtowc = _mbtowcPtr - .asFunction< - int Function(ffi.Pointer, ffi.Pointer, int) - >(); + >('getSplitTunnelItems'); + late final _getSplitTunnelItems = _getSplitTunnelItemsPtr + .asFunction Function(ffi.Pointer)>(); - int _mbtowc_l( - ffi.Pointer _DstCh, - ffi.Pointer _SrcCh, - int _SrcSizeInBytes, - _locale_t _Locale, - ) { - return __mbtowc_l(_DstCh, _SrcCh, _SrcSizeInBytes, _Locale); + ffi.Pointer deletePrivateServerByName(ffi.Pointer _name) { + return _deletePrivateServerByName(_name); } - late final __mbtowc_lPtr = + late final _deletePrivateServerByNamePtr = _lookup< ffi.NativeFunction< - ffi.Int Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - _locale_t, - ) + ffi.Pointer Function(ffi.Pointer) > - >('_mbtowc_l'); - late final __mbtowc_l = __mbtowc_lPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - int, - _locale_t, - ) - >(); + >('deletePrivateServerByName'); + late final _deletePrivateServerByName = _deletePrivateServerByNamePtr + .asFunction Function(ffi.Pointer)>(); - int mbstowcs_s( - ffi.Pointer _PtNumOfCharConverted, - ffi.Pointer _DstBuf, - int _SizeInWords, - ffi.Pointer _SrcBuf, - int _MaxCount, + ffi.Pointer updatePrivateServerName( + ffi.Pointer _oldName, + ffi.Pointer _newName, ) { - return _mbstowcs_s( - _PtNumOfCharConverted, - _DstBuf, - _SizeInWords, - _SrcBuf, - _MaxCount, - ); + return _updatePrivateServerName(_oldName, _newName); } - late final _mbstowcs_sPtr = + late final _updatePrivateServerNamePtr = _lookup< ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - ffi.Size, ) > - >('mbstowcs_s'); - late final _mbstowcs_s = _mbstowcs_sPtr + >('updatePrivateServerName'); + late final _updatePrivateServerName = _updatePrivateServerNamePtr .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - int, + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, - int, ) >(); - int mbstowcs( - ffi.Pointer _Dest, - ffi.Pointer _Source, - int _MaxCount, - ) { - return _mbstowcs(_Dest, _Source, _MaxCount); + ffi.Pointer getEnabledApps() { + return _getEnabledApps(); } - late final _mbstowcsPtr = - _lookup< - ffi.NativeFunction< - ffi.Size Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ) - > - >('mbstowcs'); - late final _mbstowcs = _mbstowcsPtr - .asFunction< - int Function(ffi.Pointer, ffi.Pointer, int) - >(); + late final _getEnabledAppsPtr = + _lookup Function()>>( + 'getEnabledApps', + ); + late final _getEnabledApps = _getEnabledAppsPtr + .asFunction Function()>(); +} + +typedef __int8_t = ffi.SignedChar; +typedef Dart__int8_t = int; +typedef __uint8_t = ffi.UnsignedChar; +typedef Dart__uint8_t = int; +typedef __int16_t = ffi.Short; +typedef Dart__int16_t = int; +typedef __uint16_t = ffi.UnsignedShort; +typedef Dart__uint16_t = int; +typedef __int32_t = ffi.Int; +typedef Dart__int32_t = int; +typedef __uint32_t = ffi.UnsignedInt; +typedef Dart__uint32_t = int; +typedef __int64_t = ffi.LongLong; +typedef Dart__int64_t = int; +typedef __uint64_t = ffi.UnsignedLongLong; +typedef Dart__uint64_t = int; +typedef __darwin_intptr_t = ffi.Long; +typedef Dart__darwin_intptr_t = int; +typedef __darwin_natural_t = ffi.UnsignedInt; +typedef Dart__darwin_natural_t = int; +typedef __darwin_ct_rune_t = ffi.Int; +typedef Dart__darwin_ct_rune_t = int; + +final class __mbstate_t extends ffi.Union { + @ffi.Array.multi([128]) + external ffi.Array __mbstate8; + + @ffi.LongLong() + external int _mbstateL; +} + +typedef __darwin_mbstate_t = __mbstate_t; +typedef __darwin_ptrdiff_t = ffi.Long; +typedef Dart__darwin_ptrdiff_t = int; +typedef __darwin_size_t = ffi.UnsignedLong; +typedef Dart__darwin_size_t = int; +typedef __builtin_va_list = ffi.Pointer; +typedef __darwin_va_list = __builtin_va_list; +typedef __darwin_wchar_t = ffi.Int; +typedef Dart__darwin_wchar_t = int; +typedef __darwin_rune_t = __darwin_wchar_t; +typedef __darwin_wint_t = ffi.Int; +typedef Dart__darwin_wint_t = int; +typedef __darwin_clock_t = ffi.UnsignedLong; +typedef Dart__darwin_clock_t = int; +typedef __darwin_socklen_t = __uint32_t; +typedef __darwin_ssize_t = ffi.Long; +typedef Dart__darwin_ssize_t = int; +typedef __darwin_time_t = ffi.Long; +typedef Dart__darwin_time_t = int; +typedef __darwin_blkcnt_t = __int64_t; +typedef __darwin_blksize_t = __int32_t; +typedef __darwin_dev_t = __int32_t; +typedef __darwin_fsblkcnt_t = ffi.UnsignedInt; +typedef Dart__darwin_fsblkcnt_t = int; +typedef __darwin_fsfilcnt_t = ffi.UnsignedInt; +typedef Dart__darwin_fsfilcnt_t = int; +typedef __darwin_gid_t = __uint32_t; +typedef __darwin_id_t = __uint32_t; +typedef __darwin_ino64_t = __uint64_t; +typedef __darwin_ino_t = __darwin_ino64_t; +typedef __darwin_mach_port_name_t = __darwin_natural_t; +typedef __darwin_mach_port_t = __darwin_mach_port_name_t; +typedef __darwin_mode_t = __uint16_t; +typedef __darwin_off_t = __int64_t; +typedef __darwin_pid_t = __int32_t; +typedef __darwin_sigset_t = __uint32_t; +typedef __darwin_suseconds_t = __int32_t; +typedef __darwin_uid_t = __uint32_t; +typedef __darwin_useconds_t = __uint32_t; + +final class __darwin_pthread_handler_rec extends ffi.Struct { + external ffi.Pointer< + ffi.NativeFunction)> + > + __routine; + + external ffi.Pointer __arg; + + external ffi.Pointer<__darwin_pthread_handler_rec> __next; +} + +final class _opaque_pthread_attr_t extends ffi.Struct { + @ffi.Long() + external int __sig; + + @ffi.Array.multi([56]) + external ffi.Array __opaque; +} + +final class _opaque_pthread_cond_t extends ffi.Struct { + @ffi.Long() + external int __sig; + + @ffi.Array.multi([40]) + external ffi.Array __opaque; +} + +final class _opaque_pthread_condattr_t extends ffi.Struct { + @ffi.Long() + external int __sig; + + @ffi.Array.multi([8]) + external ffi.Array __opaque; +} + +final class _opaque_pthread_mutex_t extends ffi.Struct { + @ffi.Long() + external int __sig; + + @ffi.Array.multi([56]) + external ffi.Array __opaque; +} + +final class _opaque_pthread_mutexattr_t extends ffi.Struct { + @ffi.Long() + external int __sig; + + @ffi.Array.multi([8]) + external ffi.Array __opaque; +} + +final class _opaque_pthread_once_t extends ffi.Struct { + @ffi.Long() + external int __sig; + + @ffi.Array.multi([8]) + external ffi.Array __opaque; +} + +final class _opaque_pthread_rwlock_t extends ffi.Struct { + @ffi.Long() + external int __sig; + + @ffi.Array.multi([192]) + external ffi.Array __opaque; +} + +final class _opaque_pthread_rwlockattr_t extends ffi.Struct { + @ffi.Long() + external int __sig; + + @ffi.Array.multi([16]) + external ffi.Array __opaque; +} + +final class _opaque_pthread_t extends ffi.Struct { + @ffi.Long() + external int __sig; + + external ffi.Pointer<__darwin_pthread_handler_rec> __cleanup_stack; + + @ffi.Array.multi([8176]) + external ffi.Array __opaque; +} + +typedef __darwin_pthread_attr_t = _opaque_pthread_attr_t; +typedef __darwin_pthread_cond_t = _opaque_pthread_cond_t; +typedef __darwin_pthread_condattr_t = _opaque_pthread_condattr_t; +typedef __darwin_pthread_key_t = ffi.UnsignedLong; +typedef Dart__darwin_pthread_key_t = int; +typedef __darwin_pthread_mutex_t = _opaque_pthread_mutex_t; +typedef __darwin_pthread_mutexattr_t = _opaque_pthread_mutexattr_t; +typedef __darwin_pthread_once_t = _opaque_pthread_once_t; +typedef __darwin_pthread_rwlock_t = _opaque_pthread_rwlock_t; +typedef __darwin_pthread_rwlockattr_t = _opaque_pthread_rwlockattr_t; +typedef __darwin_pthread_t = ffi.Pointer<_opaque_pthread_t>; +typedef __darwin_nl_item = ffi.Int; +typedef Dart__darwin_nl_item = int; +typedef __darwin_wctrans_t = ffi.Int; +typedef Dart__darwin_wctrans_t = int; +typedef __darwin_wctype_t = __uint32_t; +typedef u_int8_t = ffi.UnsignedChar; +typedef Dartu_int8_t = int; +typedef u_int16_t = ffi.UnsignedShort; +typedef Dartu_int16_t = int; +typedef u_int32_t = ffi.UnsignedInt; +typedef Dartu_int32_t = int; +typedef u_int64_t = ffi.UnsignedLongLong; +typedef Dartu_int64_t = int; +typedef register_t = ffi.Int64; +typedef Dartregister_t = int; +typedef user_addr_t = u_int64_t; +typedef user_size_t = u_int64_t; +typedef user_ssize_t = ffi.Int64; +typedef Dartuser_ssize_t = int; +typedef user_long_t = ffi.Int64; +typedef Dartuser_long_t = int; +typedef user_ulong_t = u_int64_t; +typedef user_time_t = ffi.Int64; +typedef Dartuser_time_t = int; +typedef user_off_t = ffi.Int64; +typedef Dartuser_off_t = int; +typedef syscall_arg_t = u_int64_t; +typedef ptrdiff_t = __darwin_ptrdiff_t; +typedef rsize_t = __darwin_size_t; +typedef wint_t = __darwin_wint_t; + +final class _GoString_ extends ffi.Struct { + external ffi.Pointer p; + + @ptrdiff_t() + external int n; +} + +enum idtype_t { + P_ALL(0), + P_PID(1), + P_PGID(2); + + final int value; + const idtype_t(this.value); + + static idtype_t fromValue(int value) => switch (value) { + 0 => P_ALL, + 1 => P_PID, + 2 => P_PGID, + _ => throw ArgumentError('Unknown value for idtype_t: $value'), + }; +} + +typedef pid_t = __darwin_pid_t; +typedef id_t = __darwin_id_t; +typedef sig_atomic_t = ffi.Int; +typedef Dartsig_atomic_t = int; + +final class __darwin_arm_exception_state extends ffi.Struct { + @__uint32_t() + external int __exception; + + @__uint32_t() + external int __fsr; + + @__uint32_t() + external int __far; +} + +final class __darwin_arm_exception_state64 extends ffi.Struct { + @__uint64_t() + external int __far; + + @__uint32_t() + external int __esr; + + @__uint32_t() + external int __exception; +} + +final class __darwin_arm_exception_state64_v2 extends ffi.Struct { + @__uint64_t() + external int __far; + + @__uint64_t() + external int __esr; +} + +final class __darwin_arm_thread_state extends ffi.Struct { + @ffi.Array.multi([13]) + external ffi.Array<__uint32_t> __r; + + @__uint32_t() + external int __sp; + + @__uint32_t() + external int __lr; + + @__uint32_t() + external int __pc; + + @__uint32_t() + external int __cpsr; +} + +final class __darwin_arm_thread_state64 extends ffi.Struct { + @ffi.Array.multi([29]) + external ffi.Array<__uint64_t> __x; + + @__uint64_t() + external int __fp; + + @__uint64_t() + external int __lr; + + @__uint64_t() + external int __sp; + + @__uint64_t() + external int __pc; + + @__uint32_t() + external int __cpsr; + + @__uint32_t() + external int __pad; +} + +final class __darwin_arm_vfp_state extends ffi.Struct { + @ffi.Array.multi([64]) + external ffi.Array<__uint32_t> __r; + + @__uint32_t() + external int __fpscr; +} + +final class __darwin_arm_neon_state64 extends ffi.Opaque {} + +final class __darwin_arm_neon_state extends ffi.Opaque {} + +final class __arm_pagein_state extends ffi.Struct { + @ffi.Int() + external int __pagein_error; +} + +final class __darwin_arm_sme_state extends ffi.Struct { + @__uint64_t() + external int __svcr; + + @__uint64_t() + external int __tpidr2_el0; + + @__uint16_t() + external int __svl_b; +} + +final class __darwin_arm_sve_z_state extends ffi.Struct { + @ffi.Array.multi([16, 256]) + external ffi.Array> __z; +} + +final class __darwin_arm_sve_p_state extends ffi.Struct { + @ffi.Array.multi([16, 32]) + external ffi.Array> __p; +} + +final class __darwin_arm_sme_za_state extends ffi.Struct { + @ffi.Array.multi([4096]) + external ffi.Array __za; +} + +final class __darwin_arm_sme2_state extends ffi.Struct { + @ffi.Array.multi([64]) + external ffi.Array __zt0; +} + +final class __arm_legacy_debug_state extends ffi.Struct { + @ffi.Array.multi([16]) + external ffi.Array<__uint32_t> __bvr; + + @ffi.Array.multi([16]) + external ffi.Array<__uint32_t> __bcr; + + @ffi.Array.multi([16]) + external ffi.Array<__uint32_t> __wvr; + + @ffi.Array.multi([16]) + external ffi.Array<__uint32_t> __wcr; +} + +final class __darwin_arm_debug_state32 extends ffi.Struct { + @ffi.Array.multi([16]) + external ffi.Array<__uint32_t> __bvr; + + @ffi.Array.multi([16]) + external ffi.Array<__uint32_t> __bcr; + + @ffi.Array.multi([16]) + external ffi.Array<__uint32_t> __wvr; + + @ffi.Array.multi([16]) + external ffi.Array<__uint32_t> __wcr; + + @__uint64_t() + external int __mdscr_el1; +} + +final class __darwin_arm_debug_state64 extends ffi.Struct { + @ffi.Array.multi([16]) + external ffi.Array<__uint64_t> __bvr; + + @ffi.Array.multi([16]) + external ffi.Array<__uint64_t> __bcr; + + @ffi.Array.multi([16]) + external ffi.Array<__uint64_t> __wvr; + + @ffi.Array.multi([16]) + external ffi.Array<__uint64_t> __wcr; + + @__uint64_t() + external int __mdscr_el1; +} + +final class __darwin_arm_cpmu_state64 extends ffi.Struct { + @ffi.Array.multi([16]) + external ffi.Array<__uint64_t> __ctrs; +} + +final class __darwin_mcontext32 extends ffi.Struct { + external __darwin_arm_exception_state __es; + + external __darwin_arm_thread_state __ss; + + external __darwin_arm_vfp_state __fs; +} + +final class __darwin_mcontext64 extends ffi.Opaque {} + +typedef mcontext_t = ffi.Pointer<__darwin_mcontext64>; +typedef pthread_attr_t = __darwin_pthread_attr_t; + +final class __darwin_sigaltstack extends ffi.Struct { + external ffi.Pointer ss_sp; + + @__darwin_size_t() + external int ss_size; + + @ffi.Int() + external int ss_flags; +} + +typedef stack_t = __darwin_sigaltstack; + +final class __darwin_ucontext extends ffi.Struct { + @ffi.Int() + external int uc_onstack; + + @__darwin_sigset_t() + external int uc_sigmask; + + external __darwin_sigaltstack uc_stack; + + external ffi.Pointer<__darwin_ucontext> uc_link; + + @__darwin_size_t() + external int uc_mcsize; + + external ffi.Pointer<__darwin_mcontext64> uc_mcontext; +} + +typedef ucontext_t = __darwin_ucontext; +typedef sigset_t = __darwin_sigset_t; +typedef uid_t = __darwin_uid_t; + +final class sigval extends ffi.Union { + @ffi.Int() + external int sival_int; + + external ffi.Pointer sival_ptr; +} + +final class sigevent extends ffi.Struct { + @ffi.Int() + external int sigev_notify; + + @ffi.Int() + external int sigev_signo; + + external sigval sigev_value; + + external ffi.Pointer> + sigev_notify_function; + + external ffi.Pointer sigev_notify_attributes; +} + +final class __siginfo extends ffi.Struct { + @ffi.Int() + external int si_signo; + + @ffi.Int() + external int si_errno; + + @ffi.Int() + external int si_code; + + @pid_t() + external int si_pid; + + @uid_t() + external int si_uid; + + @ffi.Int() + external int si_status; + + external ffi.Pointer si_addr; + + external sigval si_value; + + @ffi.Long() + external int si_band; + + @ffi.Array.multi([7]) + external ffi.Array __pad; +} + +typedef siginfo_t = __siginfo; + +final class __sigaction_u extends ffi.Union { + external ffi.Pointer> + __sa_handler; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(ffi.Int, ffi.Pointer<__siginfo>, ffi.Pointer) + > + > + __sa_sigaction; +} + +final class __sigaction extends ffi.Struct { + external __sigaction_u __sigaction_u$1; + + external ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, + ffi.Int, + ffi.Int, + ffi.Pointer, + ffi.Pointer, + ) + > + > + sa_tramp; + + @sigset_t() + external int sa_mask; + + @ffi.Int() + external int sa_flags; +} + +final class sigaction extends ffi.Struct { + external __sigaction_u __sigaction_u$1; + + @sigset_t() + external int sa_mask; + + @ffi.Int() + external int sa_flags; +} + +typedef sig_tFunction = ffi.Void Function(ffi.Int); +typedef Dartsig_tFunction = void Function(int); +typedef sig_t = ffi.Pointer>; + +final class sigvec extends ffi.Struct { + external ffi.Pointer> + sv_handler; + + @ffi.Int() + external int sv_mask; + + @ffi.Int() + external int sv_flags; +} + +final class sigstack extends ffi.Struct { + external ffi.Pointer ss_sp; + + @ffi.Int() + external int ss_onstack; +} + +typedef int_least8_t = ffi.Int8; +typedef Dartint_least8_t = int; +typedef int_least16_t = ffi.Int16; +typedef Dartint_least16_t = int; +typedef int_least32_t = ffi.Int32; +typedef Dartint_least32_t = int; +typedef int_least64_t = ffi.Int64; +typedef Dartint_least64_t = int; +typedef uint_least8_t = ffi.Uint8; +typedef Dartuint_least8_t = int; +typedef uint_least16_t = ffi.Uint16; +typedef Dartuint_least16_t = int; +typedef uint_least32_t = ffi.Uint32; +typedef Dartuint_least32_t = int; +typedef uint_least64_t = ffi.Uint64; +typedef Dartuint_least64_t = int; +typedef int_fast8_t = ffi.Int8; +typedef Dartint_fast8_t = int; +typedef int_fast16_t = ffi.Int16; +typedef Dartint_fast16_t = int; +typedef int_fast32_t = ffi.Int32; +typedef Dartint_fast32_t = int; +typedef int_fast64_t = ffi.Int64; +typedef Dartint_fast64_t = int; +typedef uint_fast8_t = ffi.Uint8; +typedef Dartuint_fast8_t = int; +typedef uint_fast16_t = ffi.Uint16; +typedef Dartuint_fast16_t = int; +typedef uint_fast32_t = ffi.Uint32; +typedef Dartuint_fast32_t = int; +typedef uint_fast64_t = ffi.Uint64; +typedef Dartuint_fast64_t = int; +typedef intmax_t = ffi.Long; +typedef Dartintmax_t = int; +typedef uintmax_t = ffi.UnsignedLong; +typedef Dartuintmax_t = int; + +final class timeval extends ffi.Struct { + @__darwin_time_t() + external int tv_sec; + + @__darwin_suseconds_t() + external int tv_usec; +} + +typedef rlim_t = __uint64_t; + +final class rusage extends ffi.Struct { + external timeval ru_utime; + + external timeval ru_stime; + + @ffi.Long() + external int ru_maxrss; + + @ffi.Long() + external int ru_ixrss; + + @ffi.Long() + external int ru_idrss; + + @ffi.Long() + external int ru_isrss; + + @ffi.Long() + external int ru_minflt; + + @ffi.Long() + external int ru_majflt; + + @ffi.Long() + external int ru_nswap; + + @ffi.Long() + external int ru_inblock; + + @ffi.Long() + external int ru_oublock; + + @ffi.Long() + external int ru_msgsnd; + + @ffi.Long() + external int ru_msgrcv; + + @ffi.Long() + external int ru_nsignals; + + @ffi.Long() + external int ru_nvcsw; + + @ffi.Long() + external int ru_nivcsw; +} + +typedef rusage_info_t = ffi.Pointer; + +final class rusage_info_v0 extends ffi.Struct { + @ffi.Array.multi([16]) + external ffi.Array ri_uuid; + + @ffi.Uint64() + external int ri_user_time; + + @ffi.Uint64() + external int ri_system_time; + + @ffi.Uint64() + external int ri_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_interrupt_wkups; + + @ffi.Uint64() + external int ri_pageins; + + @ffi.Uint64() + external int ri_wired_size; + + @ffi.Uint64() + external int ri_resident_size; + + @ffi.Uint64() + external int ri_phys_footprint; + + @ffi.Uint64() + external int ri_proc_start_abstime; + + @ffi.Uint64() + external int ri_proc_exit_abstime; +} + +final class rusage_info_v1 extends ffi.Struct { + @ffi.Array.multi([16]) + external ffi.Array ri_uuid; + + @ffi.Uint64() + external int ri_user_time; + + @ffi.Uint64() + external int ri_system_time; + + @ffi.Uint64() + external int ri_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_interrupt_wkups; + + @ffi.Uint64() + external int ri_pageins; + + @ffi.Uint64() + external int ri_wired_size; + + @ffi.Uint64() + external int ri_resident_size; + + @ffi.Uint64() + external int ri_phys_footprint; + + @ffi.Uint64() + external int ri_proc_start_abstime; + + @ffi.Uint64() + external int ri_proc_exit_abstime; + + @ffi.Uint64() + external int ri_child_user_time; + + @ffi.Uint64() + external int ri_child_system_time; + + @ffi.Uint64() + external int ri_child_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_child_interrupt_wkups; + + @ffi.Uint64() + external int ri_child_pageins; + + @ffi.Uint64() + external int ri_child_elapsed_abstime; +} + +final class rusage_info_v2 extends ffi.Struct { + @ffi.Array.multi([16]) + external ffi.Array ri_uuid; + + @ffi.Uint64() + external int ri_user_time; + + @ffi.Uint64() + external int ri_system_time; + + @ffi.Uint64() + external int ri_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_interrupt_wkups; + + @ffi.Uint64() + external int ri_pageins; + + @ffi.Uint64() + external int ri_wired_size; + + @ffi.Uint64() + external int ri_resident_size; + + @ffi.Uint64() + external int ri_phys_footprint; + + @ffi.Uint64() + external int ri_proc_start_abstime; + + @ffi.Uint64() + external int ri_proc_exit_abstime; + + @ffi.Uint64() + external int ri_child_user_time; + + @ffi.Uint64() + external int ri_child_system_time; + + @ffi.Uint64() + external int ri_child_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_child_interrupt_wkups; + + @ffi.Uint64() + external int ri_child_pageins; + + @ffi.Uint64() + external int ri_child_elapsed_abstime; + + @ffi.Uint64() + external int ri_diskio_bytesread; + + @ffi.Uint64() + external int ri_diskio_byteswritten; +} + +final class rusage_info_v3 extends ffi.Struct { + @ffi.Array.multi([16]) + external ffi.Array ri_uuid; + + @ffi.Uint64() + external int ri_user_time; + + @ffi.Uint64() + external int ri_system_time; + + @ffi.Uint64() + external int ri_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_interrupt_wkups; + + @ffi.Uint64() + external int ri_pageins; + + @ffi.Uint64() + external int ri_wired_size; + + @ffi.Uint64() + external int ri_resident_size; + + @ffi.Uint64() + external int ri_phys_footprint; + + @ffi.Uint64() + external int ri_proc_start_abstime; + + @ffi.Uint64() + external int ri_proc_exit_abstime; + + @ffi.Uint64() + external int ri_child_user_time; + + @ffi.Uint64() + external int ri_child_system_time; + + @ffi.Uint64() + external int ri_child_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_child_interrupt_wkups; + + @ffi.Uint64() + external int ri_child_pageins; + + @ffi.Uint64() + external int ri_child_elapsed_abstime; + + @ffi.Uint64() + external int ri_diskio_bytesread; + + @ffi.Uint64() + external int ri_diskio_byteswritten; + + @ffi.Uint64() + external int ri_cpu_time_qos_default; + + @ffi.Uint64() + external int ri_cpu_time_qos_maintenance; + + @ffi.Uint64() + external int ri_cpu_time_qos_background; + + @ffi.Uint64() + external int ri_cpu_time_qos_utility; + + @ffi.Uint64() + external int ri_cpu_time_qos_legacy; + + @ffi.Uint64() + external int ri_cpu_time_qos_user_initiated; + + @ffi.Uint64() + external int ri_cpu_time_qos_user_interactive; + + @ffi.Uint64() + external int ri_billed_system_time; + + @ffi.Uint64() + external int ri_serviced_system_time; +} + +final class rusage_info_v4 extends ffi.Struct { + @ffi.Array.multi([16]) + external ffi.Array ri_uuid; + + @ffi.Uint64() + external int ri_user_time; + + @ffi.Uint64() + external int ri_system_time; + + @ffi.Uint64() + external int ri_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_interrupt_wkups; + + @ffi.Uint64() + external int ri_pageins; + + @ffi.Uint64() + external int ri_wired_size; + + @ffi.Uint64() + external int ri_resident_size; + + @ffi.Uint64() + external int ri_phys_footprint; + + @ffi.Uint64() + external int ri_proc_start_abstime; + + @ffi.Uint64() + external int ri_proc_exit_abstime; + + @ffi.Uint64() + external int ri_child_user_time; + + @ffi.Uint64() + external int ri_child_system_time; + + @ffi.Uint64() + external int ri_child_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_child_interrupt_wkups; + + @ffi.Uint64() + external int ri_child_pageins; + + @ffi.Uint64() + external int ri_child_elapsed_abstime; + + @ffi.Uint64() + external int ri_diskio_bytesread; + + @ffi.Uint64() + external int ri_diskio_byteswritten; + + @ffi.Uint64() + external int ri_cpu_time_qos_default; + + @ffi.Uint64() + external int ri_cpu_time_qos_maintenance; + + @ffi.Uint64() + external int ri_cpu_time_qos_background; + + @ffi.Uint64() + external int ri_cpu_time_qos_utility; + + @ffi.Uint64() + external int ri_cpu_time_qos_legacy; + + @ffi.Uint64() + external int ri_cpu_time_qos_user_initiated; + + @ffi.Uint64() + external int ri_cpu_time_qos_user_interactive; + + @ffi.Uint64() + external int ri_billed_system_time; + + @ffi.Uint64() + external int ri_serviced_system_time; + + @ffi.Uint64() + external int ri_logical_writes; + + @ffi.Uint64() + external int ri_lifetime_max_phys_footprint; + + @ffi.Uint64() + external int ri_instructions; + + @ffi.Uint64() + external int ri_cycles; + + @ffi.Uint64() + external int ri_billed_energy; + + @ffi.Uint64() + external int ri_serviced_energy; + + @ffi.Uint64() + external int ri_interval_max_phys_footprint; + + @ffi.Uint64() + external int ri_runnable_time; +} + +final class rusage_info_v5 extends ffi.Struct { + @ffi.Array.multi([16]) + external ffi.Array ri_uuid; + + @ffi.Uint64() + external int ri_user_time; + + @ffi.Uint64() + external int ri_system_time; + + @ffi.Uint64() + external int ri_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_interrupt_wkups; + + @ffi.Uint64() + external int ri_pageins; + + @ffi.Uint64() + external int ri_wired_size; + + @ffi.Uint64() + external int ri_resident_size; + + @ffi.Uint64() + external int ri_phys_footprint; + + @ffi.Uint64() + external int ri_proc_start_abstime; + + @ffi.Uint64() + external int ri_proc_exit_abstime; + + @ffi.Uint64() + external int ri_child_user_time; + + @ffi.Uint64() + external int ri_child_system_time; + + @ffi.Uint64() + external int ri_child_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_child_interrupt_wkups; + + @ffi.Uint64() + external int ri_child_pageins; + + @ffi.Uint64() + external int ri_child_elapsed_abstime; + + @ffi.Uint64() + external int ri_diskio_bytesread; + + @ffi.Uint64() + external int ri_diskio_byteswritten; + + @ffi.Uint64() + external int ri_cpu_time_qos_default; + + @ffi.Uint64() + external int ri_cpu_time_qos_maintenance; + + @ffi.Uint64() + external int ri_cpu_time_qos_background; + + @ffi.Uint64() + external int ri_cpu_time_qos_utility; + + @ffi.Uint64() + external int ri_cpu_time_qos_legacy; + + @ffi.Uint64() + external int ri_cpu_time_qos_user_initiated; + + @ffi.Uint64() + external int ri_cpu_time_qos_user_interactive; + + @ffi.Uint64() + external int ri_billed_system_time; + + @ffi.Uint64() + external int ri_serviced_system_time; + + @ffi.Uint64() + external int ri_logical_writes; + + @ffi.Uint64() + external int ri_lifetime_max_phys_footprint; + + @ffi.Uint64() + external int ri_instructions; + + @ffi.Uint64() + external int ri_cycles; + + @ffi.Uint64() + external int ri_billed_energy; + + @ffi.Uint64() + external int ri_serviced_energy; + + @ffi.Uint64() + external int ri_interval_max_phys_footprint; + + @ffi.Uint64() + external int ri_runnable_time; + + @ffi.Uint64() + external int ri_flags; +} + +final class rusage_info_v6 extends ffi.Struct { + @ffi.Array.multi([16]) + external ffi.Array ri_uuid; + + @ffi.Uint64() + external int ri_user_time; + + @ffi.Uint64() + external int ri_system_time; + + @ffi.Uint64() + external int ri_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_interrupt_wkups; + + @ffi.Uint64() + external int ri_pageins; + + @ffi.Uint64() + external int ri_wired_size; + + @ffi.Uint64() + external int ri_resident_size; + + @ffi.Uint64() + external int ri_phys_footprint; + + @ffi.Uint64() + external int ri_proc_start_abstime; + + @ffi.Uint64() + external int ri_proc_exit_abstime; + + @ffi.Uint64() + external int ri_child_user_time; + + @ffi.Uint64() + external int ri_child_system_time; + + @ffi.Uint64() + external int ri_child_pkg_idle_wkups; + + @ffi.Uint64() + external int ri_child_interrupt_wkups; + + @ffi.Uint64() + external int ri_child_pageins; + + @ffi.Uint64() + external int ri_child_elapsed_abstime; + + @ffi.Uint64() + external int ri_diskio_bytesread; + + @ffi.Uint64() + external int ri_diskio_byteswritten; + + @ffi.Uint64() + external int ri_cpu_time_qos_default; + + @ffi.Uint64() + external int ri_cpu_time_qos_maintenance; + + @ffi.Uint64() + external int ri_cpu_time_qos_background; + + @ffi.Uint64() + external int ri_cpu_time_qos_utility; + + @ffi.Uint64() + external int ri_cpu_time_qos_legacy; + + @ffi.Uint64() + external int ri_cpu_time_qos_user_initiated; + + @ffi.Uint64() + external int ri_cpu_time_qos_user_interactive; + + @ffi.Uint64() + external int ri_billed_system_time; + + @ffi.Uint64() + external int ri_serviced_system_time; + + @ffi.Uint64() + external int ri_logical_writes; + + @ffi.Uint64() + external int ri_lifetime_max_phys_footprint; + + @ffi.Uint64() + external int ri_instructions; + + @ffi.Uint64() + external int ri_cycles; + + @ffi.Uint64() + external int ri_billed_energy; + + @ffi.Uint64() + external int ri_serviced_energy; + + @ffi.Uint64() + external int ri_interval_max_phys_footprint; + + @ffi.Uint64() + external int ri_runnable_time; + + @ffi.Uint64() + external int ri_flags; + + @ffi.Uint64() + external int ri_user_ptime; + + @ffi.Uint64() + external int ri_system_ptime; + + @ffi.Uint64() + external int ri_pinstructions; + + @ffi.Uint64() + external int ri_pcycles; + + @ffi.Uint64() + external int ri_energy_nj; + + @ffi.Uint64() + external int ri_penergy_nj; + + @ffi.Uint64() + external int ri_secure_time_in_system; + + @ffi.Uint64() + external int ri_secure_ptime_in_system; + + @ffi.Uint64() + external int ri_neural_footprint; + + @ffi.Uint64() + external int ri_lifetime_max_neural_footprint; + + @ffi.Uint64() + external int ri_interval_max_neural_footprint; + + @ffi.Array.multi([9]) + external ffi.Array ri_reserved; +} + +typedef rusage_info_current = rusage_info_v6; + +final class rlimit extends ffi.Struct { + @rlim_t() + external int rlim_cur; + + @rlim_t() + external int rlim_max; +} + +final class proc_rlimit_control_wakeupmon extends ffi.Struct { + @ffi.Uint32() + external int wm_flags; + + @ffi.Int32() + external int wm_rate; +} + +final class wait$1 extends ffi.Opaque {} + +typedef ct_rune_t = __darwin_ct_rune_t; +typedef rune_t = __darwin_rune_t; + +final class div_t extends ffi.Struct { + @ffi.Int() + external int quot; + + @ffi.Int() + external int rem; +} + +final class ldiv_t extends ffi.Struct { + @ffi.Long() + external int quot; - int _mbstowcs_s_l( - ffi.Pointer _PtNumOfCharConverted, - ffi.Pointer _DstBuf, - int _SizeInWords, - ffi.Pointer _SrcBuf, - int _MaxCount, - _locale_t _Locale, - ) { - return __mbstowcs_s_l( - _PtNumOfCharConverted, - _DstBuf, - _SizeInWords, - _SrcBuf, - _MaxCount, - _Locale, - ); - } + @ffi.Long() + external int rem; +} - late final __mbstowcs_s_lPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ffi.Pointer, - ffi.Size, - _locale_t, - ) - > - >('_mbstowcs_s_l'); - late final __mbstowcs_s_l = __mbstowcs_s_lPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - int, - ffi.Pointer, - int, - _locale_t, - ) - >(); +final class lldiv_t extends ffi.Struct { + @ffi.LongLong() + external int quot; - int _mbstowcs_l( - ffi.Pointer _Dest, - ffi.Pointer _Source, - int _MaxCount, - _locale_t _Locale, - ) { - return __mbstowcs_l(_Dest, _Source, _MaxCount, _Locale); - } + @ffi.LongLong() + external int rem; +} - late final __mbstowcs_lPtr = - _lookup< - ffi.NativeFunction< - ffi.Size Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - _locale_t, - ) - > - >('_mbstowcs_l'); - late final __mbstowcs_l = __mbstowcs_lPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - int, - _locale_t, - ) - >(); +typedef malloc_type_id_t = ffi.UnsignedLongLong; +typedef Dartmalloc_type_id_t = int; - int wctomb(ffi.Pointer _MbCh, int _WCh) { - return _wctomb(_MbCh, _WCh); - } +final class _malloc_zone_t extends ffi.Opaque {} - late final _wctombPtr = - _lookup< - ffi.NativeFunction, ffi.WChar)> - >('wctomb'); - late final _wctomb = _wctombPtr - .asFunction, int)>(); +typedef malloc_zone_t = _malloc_zone_t; +typedef dev_t = __darwin_dev_t; +typedef mode_t = __darwin_mode_t; +typedef GoInt8 = ffi.SignedChar; +typedef DartGoInt8 = int; +typedef GoUint8 = ffi.UnsignedChar; +typedef DartGoUint8 = int; +typedef GoInt16 = ffi.Short; +typedef DartGoInt16 = int; +typedef GoUint16 = ffi.UnsignedShort; +typedef DartGoUint16 = int; +typedef GoInt32 = ffi.Int; +typedef DartGoInt32 = int; +typedef GoUint32 = ffi.UnsignedInt; +typedef DartGoUint32 = int; +typedef GoInt64 = ffi.LongLong; +typedef DartGoInt64 = int; +typedef GoUint64 = ffi.UnsignedLongLong; +typedef DartGoUint64 = int; +typedef GoInt = GoInt64; +typedef GoUint = GoUint64; +typedef GoUintptr = ffi.Size; +typedef DartGoUintptr = int; +typedef GoFloat32 = ffi.Float; +typedef DartGoFloat32 = double; +typedef GoFloat64 = ffi.Double; +typedef DartGoFloat64 = double; +typedef GoString = _GoString_; +typedef GoMap = ffi.Pointer; +typedef GoChan = ffi.Pointer; - int _wctomb_l(ffi.Pointer _MbCh, int _WCh, _locale_t _Locale) { - return __wctomb_l(_MbCh, _WCh, _Locale); - } +final class GoInterface extends ffi.Struct { + external ffi.Pointer t; - late final __wctomb_lPtr = - _lookup< - ffi.NativeFunction< - ffi.Int Function(ffi.Pointer, ffi.WChar, _locale_t) - > - >('_wctomb_l'); - late final __wctomb_l = __wctomb_lPtr - .asFunction, int, _locale_t)>(); - - int wctomb_s( - ffi.Pointer _SizeConverted, - ffi.Pointer _MbCh, - int _SizeInBytes, - int _WCh, - ) { - return _wctomb_s(_SizeConverted, _MbCh, _SizeInBytes, _WCh); - } + external ffi.Pointer v; +} - late final _wctomb_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - rsize_t, - ffi.WChar, - ) - > - >('wctomb_s'); - late final _wctomb_s = _wctomb_sPtr - .asFunction< - int Function(ffi.Pointer, ffi.Pointer, int, int) - >(); +final class GoSlice extends ffi.Struct { + external ffi.Pointer data; - int _wctomb_s_l( - ffi.Pointer _SizeConverted, - ffi.Pointer _MbCh, - int _SizeInBytes, - int _WCh, - _locale_t _Locale, - ) { - return __wctomb_s_l(_SizeConverted, _MbCh, _SizeInBytes, _WCh, _Locale); - } + @GoInt() + external int len; - late final __wctomb_s_lPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ffi.WChar, - _locale_t, - ) - > - >('_wctomb_s_l'); - late final __wctomb_s_l = __wctomb_s_lPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - int, - int, - _locale_t, - ) - >(); + @GoInt() + external int cap; +} + +const int __has_safe_buffers = 1; + +const int __DARWIN_ONLY_64_BIT_INO_T = 1; + +const int __DARWIN_ONLY_UNIX_CONFORMANCE = 1; + +const int __DARWIN_ONLY_VERS_1050 = 1; + +const int __DARWIN_UNIX03 = 1; + +const int __DARWIN_64_BIT_INO_T = 1; + +const int __DARWIN_VERS_1050 = 1; + +const int __DARWIN_NON_CANCELABLE = 0; + +const String __DARWIN_SUF_EXTSN = '\$DARWIN_EXTSN'; + +const int __DARWIN_C_ANSI = 4096; + +const int __DARWIN_C_FULL = 900000; + +const int __DARWIN_C_LEVEL = 900000; + +const int __STDC_WANT_LIB_EXT1__ = 1; + +const int __DARWIN_NO_LONG_LONG = 0; + +const int _DARWIN_FEATURE_64_BIT_INODE = 1; + +const int _DARWIN_FEATURE_ONLY_64_BIT_INODE = 1; + +const int _DARWIN_FEATURE_ONLY_VERS_1050 = 1; + +const int _DARWIN_FEATURE_ONLY_UNIX_CONFORMANCE = 1; + +const int _DARWIN_FEATURE_UNIX_CONFORMANCE = 3; + +const int __has_ptrcheck = 0; + +const int __has_bounds_safety_attributes = 0; + +const int __DARWIN_NULL = 0; + +const int __PTHREAD_SIZE__ = 8176; + +const int __PTHREAD_ATTR_SIZE__ = 56; + +const int __PTHREAD_MUTEXATTR_SIZE__ = 8; + +const int __PTHREAD_MUTEX_SIZE__ = 56; + +const int __PTHREAD_CONDATTR_SIZE__ = 8; + +const int __PTHREAD_COND_SIZE__ = 40; + +const int __PTHREAD_ONCE_SIZE__ = 8; + +const int __PTHREAD_RWLOCK_SIZE__ = 192; + +const int __PTHREAD_RWLOCKATTR_SIZE__ = 16; + +const int __DARWIN_WCHAR_MAX = 2147483647; + +const int __DARWIN_WCHAR_MIN = -2147483648; + +const int __DARWIN_WEOF = -1; + +const int _FORTIFY_SOURCE = 2; + +const int NULL = 0; + +const int USER_ADDR_NULL = 0; + +const int __API_TO_BE_DEPRECATED = 100000; + +const int __API_TO_BE_DEPRECATED_MACOS = 100000; + +const int __API_TO_BE_DEPRECATED_MACOSAPPLICATIONEXTENSION = 100000; + +const int __API_TO_BE_DEPRECATED_IOS = 100000; + +const int __API_TO_BE_DEPRECATED_IOSAPPLICATIONEXTENSION = 100000; + +const int __API_TO_BE_DEPRECATED_MACCATALYST = 100000; + +const int __API_TO_BE_DEPRECATED_MACCATALYSTAPPLICATIONEXTENSION = 100000; + +const int __API_TO_BE_DEPRECATED_WATCHOS = 100000; + +const int __API_TO_BE_DEPRECATED_WATCHOSAPPLICATIONEXTENSION = 100000; + +const int __API_TO_BE_DEPRECATED_TVOS = 100000; + +const int __API_TO_BE_DEPRECATED_TVOSAPPLICATIONEXTENSION = 100000; + +const int __API_TO_BE_DEPRECATED_DRIVERKIT = 100000; + +const int __API_TO_BE_DEPRECATED_VISIONOS = 100000; + +const int __API_TO_BE_DEPRECATED_VISIONOSAPPLICATIONEXTENSION = 100000; + +const int __API_TO_BE_DEPRECATED_KERNELKIT = 100000; + +const int __MAC_10_0 = 1000; + +const int __MAC_10_1 = 1010; + +const int __MAC_10_2 = 1020; + +const int __MAC_10_3 = 1030; + +const int __MAC_10_4 = 1040; + +const int __MAC_10_5 = 1050; + +const int __MAC_10_6 = 1060; + +const int __MAC_10_7 = 1070; + +const int __MAC_10_8 = 1080; + +const int __MAC_10_9 = 1090; + +const int __MAC_10_10 = 101000; + +const int __MAC_10_10_2 = 101002; + +const int __MAC_10_10_3 = 101003; + +const int __MAC_10_11 = 101100; + +const int __MAC_10_11_2 = 101102; + +const int __MAC_10_11_3 = 101103; + +const int __MAC_10_11_4 = 101104; + +const int __MAC_10_12 = 101200; + +const int __MAC_10_12_1 = 101201; + +const int __MAC_10_12_2 = 101202; + +const int __MAC_10_12_4 = 101204; + +const int __MAC_10_13 = 101300; + +const int __MAC_10_13_1 = 101301; + +const int __MAC_10_13_2 = 101302; + +const int __MAC_10_13_4 = 101304; + +const int __MAC_10_14 = 101400; + +const int __MAC_10_14_1 = 101401; + +const int __MAC_10_14_4 = 101404; + +const int __MAC_10_14_5 = 101405; + +const int __MAC_10_14_6 = 101406; + +const int __MAC_10_15 = 101500; + +const int __MAC_10_15_1 = 101501; + +const int __MAC_10_15_4 = 101504; + +const int __MAC_10_16 = 101600; + +const int __MAC_11_0 = 110000; + +const int __MAC_11_1 = 110100; + +const int __MAC_11_3 = 110300; + +const int __MAC_11_4 = 110400; + +const int __MAC_11_5 = 110500; + +const int __MAC_11_6 = 110600; + +const int __MAC_12_0 = 120000; + +const int __MAC_12_1 = 120100; + +const int __MAC_12_2 = 120200; + +const int __MAC_12_3 = 120300; + +const int __MAC_12_4 = 120400; + +const int __MAC_12_5 = 120500; + +const int __MAC_12_6 = 120600; + +const int __MAC_12_7 = 120700; + +const int __MAC_13_0 = 130000; + +const int __MAC_13_1 = 130100; + +const int __MAC_13_2 = 130200; + +const int __MAC_13_3 = 130300; + +const int __MAC_13_4 = 130400; + +const int __MAC_13_5 = 130500; + +const int __MAC_13_6 = 130600; + +const int __MAC_13_7 = 130700; + +const int __MAC_14_0 = 140000; + +const int __MAC_14_1 = 140100; + +const int __MAC_14_2 = 140200; + +const int __MAC_14_3 = 140300; + +const int __MAC_14_4 = 140400; + +const int __MAC_14_5 = 140500; + +const int __MAC_14_6 = 140600; + +const int __MAC_14_7 = 140700; + +const int __MAC_15_0 = 150000; + +const int __MAC_15_1 = 150100; + +const int __MAC_15_2 = 150200; + +const int __MAC_15_3 = 150300; + +const int __MAC_15_4 = 150400; + +const int __MAC_15_5 = 150500; + +const int __MAC_15_6 = 150600; + +const int __MAC_16_0 = 160000; + +const int __MAC_26_0 = 260000; + +const int __MAC_26_1 = 260100; + +const int __MAC_26_2 = 260200; + +const int __MAC_26_3 = 260300; + +const int __MAC_26_4 = 260400; + +const int __MAC_26_5 = 260500; + +const int __IPHONE_2_0 = 20000; + +const int __IPHONE_2_1 = 20100; + +const int __IPHONE_2_2 = 20200; + +const int __IPHONE_3_0 = 30000; + +const int __IPHONE_3_1 = 30100; - int wcstombs_s( - ffi.Pointer _PtNumOfCharConverted, - ffi.Pointer _Dst, - int _DstSizeInBytes, - ffi.Pointer _Src, - int _MaxCountInBytes, - ) { - return _wcstombs_s( - _PtNumOfCharConverted, - _Dst, - _DstSizeInBytes, - _Src, - _MaxCountInBytes, - ); - } +const int __IPHONE_3_2 = 30200; - late final _wcstombs_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ffi.Pointer, - ffi.Size, - ) - > - >('wcstombs_s'); - late final _wcstombs_s = _wcstombs_sPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - int, - ffi.Pointer, - int, - ) - >(); +const int __IPHONE_4_0 = 40000; - int wcstombs( - ffi.Pointer _Dest, - ffi.Pointer _Source, - int _MaxCount, - ) { - return _wcstombs(_Dest, _Source, _MaxCount); - } +const int __IPHONE_4_1 = 40100; - late final _wcstombsPtr = - _lookup< - ffi.NativeFunction< - ffi.Size Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ) - > - >('wcstombs'); - late final _wcstombs = _wcstombsPtr - .asFunction< - int Function(ffi.Pointer, ffi.Pointer, int) - >(); +const int __IPHONE_4_2 = 40200; - int _wcstombs_s_l( - ffi.Pointer _PtNumOfCharConverted, - ffi.Pointer _Dst, - int _DstSizeInBytes, - ffi.Pointer _Src, - int _MaxCountInBytes, - _locale_t _Locale, - ) { - return __wcstombs_s_l( - _PtNumOfCharConverted, - _Dst, - _DstSizeInBytes, - _Src, - _MaxCountInBytes, - _Locale, - ); - } +const int __IPHONE_4_3 = 40300; - late final __wcstombs_s_lPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ffi.Pointer, - ffi.Size, - _locale_t, - ) - > - >('_wcstombs_s_l'); - late final __wcstombs_s_l = __wcstombs_s_lPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - int, - ffi.Pointer, - int, - _locale_t, - ) - >(); +const int __IPHONE_5_0 = 50000; - int _wcstombs_l( - ffi.Pointer _Dest, - ffi.Pointer _Source, - int _MaxCount, - _locale_t _Locale, - ) { - return __wcstombs_l(_Dest, _Source, _MaxCount, _Locale); - } +const int __IPHONE_5_1 = 50100; - late final __wcstombs_lPtr = - _lookup< - ffi.NativeFunction< - ffi.Size Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - _locale_t, - ) - > - >('_wcstombs_l'); - late final __wcstombs_l = __wcstombs_lPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - int, - _locale_t, - ) - >(); +const int __IPHONE_6_0 = 60000; - ffi.Pointer _fullpath( - ffi.Pointer _Buffer, - ffi.Pointer _Path, - int _BufferCount, - ) { - return __fullpath(_Buffer, _Path, _BufferCount); - } +const int __IPHONE_6_1 = 60100; - late final __fullpathPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ) - > - >('_fullpath'); - late final __fullpath = __fullpathPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - int, - ) - >(); +const int __IPHONE_7_0 = 70000; - int _makepath_s( - ffi.Pointer _Buffer, - int _BufferCount, - ffi.Pointer _Drive, - ffi.Pointer _Dir, - ffi.Pointer _Filename, - ffi.Pointer _Ext, - ) { - return __makepath_s(_Buffer, _BufferCount, _Drive, _Dir, _Filename, _Ext); - } +const int __IPHONE_7_1 = 70100; - late final __makepath_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Size, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('_makepath_s'); - late final __makepath_s = __makepath_sPtr - .asFunction< - int Function( - ffi.Pointer, - int, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __IPHONE_8_0 = 80000; - void _makepath( - ffi.Pointer _Buffer, - ffi.Pointer _Drive, - ffi.Pointer _Dir, - ffi.Pointer _Filename, - ffi.Pointer _Ext, - ) { - return __makepath(_Buffer, _Drive, _Dir, _Filename, _Ext); - } +const int __IPHONE_8_1 = 80100; - late final __makepathPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('_makepath'); - late final __makepath = __makepathPtr - .asFunction< - void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __IPHONE_8_2 = 80200; - void _splitpath( - ffi.Pointer _FullPath, - ffi.Pointer _Drive, - ffi.Pointer _Dir, - ffi.Pointer _Filename, - ffi.Pointer _Ext, - ) { - return __splitpath(_FullPath, _Drive, _Dir, _Filename, _Ext); - } +const int __IPHONE_8_3 = 80300; - late final __splitpathPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('_splitpath'); - late final __splitpath = __splitpathPtr - .asFunction< - void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __IPHONE_8_4 = 80400; - int _splitpath_s( - ffi.Pointer _FullPath, - ffi.Pointer _Drive, - int _DriveCount, - ffi.Pointer _Dir, - int _DirCount, - ffi.Pointer _Filename, - int _FilenameCount, - ffi.Pointer _Ext, - int _ExtCount, - ) { - return __splitpath_s( - _FullPath, - _Drive, - _DriveCount, - _Dir, - _DirCount, - _Filename, - _FilenameCount, - _Ext, - _ExtCount, - ); - } +const int __IPHONE_9_0 = 90000; - late final __splitpath_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ffi.Pointer, - ffi.Size, - ffi.Pointer, - ffi.Size, - ffi.Pointer, - ffi.Size, - ) - > - >('_splitpath_s'); - late final __splitpath_s = __splitpath_sPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - int, - ffi.Pointer, - int, - ffi.Pointer, - int, - ffi.Pointer, - int, - ) - >(); +const int __IPHONE_9_1 = 90100; - int getenv_s( - ffi.Pointer _RequiredCount, - ffi.Pointer _Buffer, - int _BufferCount, - ffi.Pointer _VarName, - ) { - return _getenv_s(_RequiredCount, _Buffer, _BufferCount, _VarName); - } +const int __IPHONE_9_2 = 90200; - late final _getenv_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - rsize_t, - ffi.Pointer, - ) - > - >('getenv_s'); - late final _getenv_s = _getenv_sPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - int, - ffi.Pointer, - ) - >(); +const int __IPHONE_9_3 = 90300; - ffi.Pointer __p___argc() { - return ___p___argc(); - } +const int __IPHONE_10_0 = 100000; - late final ___p___argcPtr = - _lookup Function()>>( - '__p___argc', - ); - late final ___p___argc = ___p___argcPtr - .asFunction Function()>(); +const int __IPHONE_10_1 = 100100; - ffi.Pointer>> __p___argv() { - return ___p___argv(); - } +const int __IPHONE_10_2 = 100200; - late final ___p___argvPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer>> Function() - > - >('__p___argv'); - late final ___p___argv = ___p___argvPtr - .asFunction>> Function()>(); +const int __IPHONE_10_3 = 100300; - ffi.Pointer>> __p___wargv() { - return ___p___wargv(); - } +const int __IPHONE_11_0 = 110000; - late final ___p___wargvPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer>> Function() - > - >('__p___wargv'); - late final ___p___wargv = ___p___wargvPtr - .asFunction< - ffi.Pointer>> Function() - >(); +const int __IPHONE_11_1 = 110100; - ffi.Pointer>> __p__environ() { - return ___p__environ(); - } +const int __IPHONE_11_2 = 110200; - late final ___p__environPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer>> Function() - > - >('__p__environ'); - late final ___p__environ = ___p__environPtr - .asFunction>> Function()>(); +const int __IPHONE_11_3 = 110300; - ffi.Pointer>> __p__wenviron() { - return ___p__wenviron(); - } +const int __IPHONE_11_4 = 110400; - late final ___p__wenvironPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer>> Function() - > - >('__p__wenviron'); - late final ___p__wenviron = ___p__wenvironPtr - .asFunction< - ffi.Pointer>> Function() - >(); +const int __IPHONE_12_0 = 120000; - ffi.Pointer getenv(ffi.Pointer _VarName) { - return _getenv(_VarName); - } +const int __IPHONE_12_1 = 120100; - late final _getenvPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('getenv'); - late final _getenv = _getenvPtr - .asFunction Function(ffi.Pointer)>(); +const int __IPHONE_12_2 = 120200; - int _dupenv_s( - ffi.Pointer> _Buffer, - ffi.Pointer _BufferCount, - ffi.Pointer _VarName, - ) { - return __dupenv_s(_Buffer, _BufferCount, _VarName); - } +const int __IPHONE_12_3 = 120300; - late final __dupenv_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.Pointer>, - ffi.Pointer, - ffi.Pointer, - ) - > - >('_dupenv_s'); - late final __dupenv_s = __dupenv_sPtr - .asFunction< - int Function( - ffi.Pointer>, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __IPHONE_12_4 = 120400; - int system(ffi.Pointer _Command) { - return _system(_Command); - } +const int __IPHONE_13_0 = 130000; - late final _systemPtr = - _lookup)>>( - 'system', - ); - late final _system = _systemPtr - .asFunction)>(); +const int __IPHONE_13_1 = 130100; - int _putenv(ffi.Pointer _EnvString) { - return __putenv(_EnvString); - } +const int __IPHONE_13_2 = 130200; - late final __putenvPtr = - _lookup)>>( - '_putenv', - ); - late final __putenv = __putenvPtr - .asFunction)>(); +const int __IPHONE_13_3 = 130300; - int _putenv_s(ffi.Pointer _Name, ffi.Pointer _Value) { - return __putenv_s(_Name, _Value); - } +const int __IPHONE_13_4 = 130400; - late final __putenv_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function(ffi.Pointer, ffi.Pointer) - > - >('_putenv_s'); - late final __putenv_s = __putenv_sPtr - .asFunction, ffi.Pointer)>(); +const int __IPHONE_13_5 = 130500; + +const int __IPHONE_13_6 = 130600; - int _searchenv_s( - ffi.Pointer _Filename, - ffi.Pointer _VarName, - ffi.Pointer _Buffer, - int _BufferCount, - ) { - return __searchenv_s(_Filename, _VarName, _Buffer, _BufferCount); - } +const int __IPHONE_13_7 = 130700; - late final __searchenv_sPtr = - _lookup< - ffi.NativeFunction< - errno_t Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ) - > - >('_searchenv_s'); - late final __searchenv_s = __searchenv_sPtr - .asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, - ) - >(); +const int __IPHONE_14_0 = 140000; - void _searchenv( - ffi.Pointer _Filename, - ffi.Pointer _VarName, - ffi.Pointer _Buffer, - ) { - return __searchenv(_Filename, _VarName, _Buffer); - } +const int __IPHONE_14_1 = 140100; - late final __searchenvPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('_searchenv'); - late final __searchenv = __searchenvPtr - .asFunction< - void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __IPHONE_14_2 = 140200; - void _seterrormode(int _Mode) { - return __seterrormode(_Mode); - } +const int __IPHONE_14_3 = 140300; - late final __seterrormodePtr = - _lookup>('_seterrormode'); - late final __seterrormode = __seterrormodePtr - .asFunction(); +const int __IPHONE_14_5 = 140500; - void _beep(int _Frequency, int _Duration) { - return __beep(_Frequency, _Duration); - } +const int __IPHONE_14_6 = 140600; - late final __beepPtr = - _lookup< - ffi.NativeFunction - >('_beep'); - late final __beep = __beepPtr.asFunction(); +const int __IPHONE_14_7 = 140700; - void _sleep(int _Duration) { - return __sleep(_Duration); - } +const int __IPHONE_14_8 = 140800; - late final __sleepPtr = - _lookup>( - '_sleep', - ); - late final __sleep = __sleepPtr.asFunction(); +const int __IPHONE_15_0 = 150000; - ffi.Pointer ecvt( - double _Value, - int _DigitCount, - ffi.Pointer _PtDec, - ffi.Pointer _PtSign, - ) { - return _ecvt$1(_Value, _DigitCount, _PtDec, _PtSign); - } +const int __IPHONE_15_1 = 150100; - late final _ecvtPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Double, - ffi.Int, - ffi.Pointer, - ffi.Pointer, - ) - > - >('ecvt'); - late final _ecvt$1 = _ecvtPtr - .asFunction< - ffi.Pointer Function( - double, - int, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __IPHONE_15_2 = 150200; - ffi.Pointer fcvt( - double _Value, - int _FractionalDigitCount, - ffi.Pointer _PtDec, - ffi.Pointer _PtSign, - ) { - return _fcvt$1(_Value, _FractionalDigitCount, _PtDec, _PtSign); - } +const int __IPHONE_15_3 = 150300; - late final _fcvtPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Double, - ffi.Int, - ffi.Pointer, - ffi.Pointer, - ) - > - >('fcvt'); - late final _fcvt$1 = _fcvtPtr - .asFunction< - ffi.Pointer Function( - double, - int, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __IPHONE_15_4 = 150400; - ffi.Pointer gcvt( - double _Value, - int _DigitCount, - ffi.Pointer _DstBuf, - ) { - return _gcvt$1(_Value, _DigitCount, _DstBuf); - } +const int __IPHONE_15_5 = 150500; - late final _gcvtPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Double, - ffi.Int, - ffi.Pointer, - ) - > - >('gcvt'); - late final _gcvt$1 = _gcvtPtr - .asFunction< - ffi.Pointer Function(double, int, ffi.Pointer) - >(); +const int __IPHONE_15_6 = 150600; - ffi.Pointer itoa( - int _Value, - ffi.Pointer _Buffer, - int _Radix, - ) { - return _itoa$1(_Value, _Buffer, _Radix); - } +const int __IPHONE_15_7 = 150700; - late final _itoaPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Int, - ffi.Pointer, - ffi.Int, - ) - > - >('itoa'); - late final _itoa$1 = _itoaPtr - .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) - >(); +const int __IPHONE_15_8 = 150800; - ffi.Pointer ltoa( - int _Value, - ffi.Pointer _Buffer, - int _Radix, - ) { - return _ltoa$1(_Value, _Buffer, _Radix); - } +const int __IPHONE_16_0 = 160000; - late final _ltoaPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Long, - ffi.Pointer, - ffi.Int, - ) - > - >('ltoa'); - late final _ltoa$1 = _ltoaPtr - .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) - >(); +const int __IPHONE_16_1 = 160100; - void swab( - ffi.Pointer _Buf1, - ffi.Pointer _Buf2, - int _SizeInBytes, - ) { - return _swab$1(_Buf1, _Buf2, _SizeInBytes); - } +const int __IPHONE_16_2 = 160200; - late final _swabPtr = - _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Int, - ) - > - >('swab'); - late final _swab$1 = _swabPtr - .asFunction< - void Function(ffi.Pointer, ffi.Pointer, int) - >(); +const int __IPHONE_16_3 = 160300; - ffi.Pointer ultoa( - int _Value, - ffi.Pointer _Buffer, - int _Radix, - ) { - return _ultoa$1(_Value, _Buffer, _Radix); - } +const int __IPHONE_16_4 = 160400; - late final _ultoaPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.UnsignedLong, - ffi.Pointer, - ffi.Int, - ) - > - >('ultoa'); - late final _ultoa$1 = _ultoaPtr - .asFunction< - ffi.Pointer Function(int, ffi.Pointer, int) - >(); +const int __IPHONE_16_5 = 160500; - int putenv(ffi.Pointer _EnvString) { - return _putenv$1(_EnvString); - } +const int __IPHONE_16_6 = 160600; - late final _putenvPtr = - _lookup)>>( - 'putenv', - ); - late final _putenv$1 = _putenvPtr - .asFunction)>(); +const int __IPHONE_16_7 = 160700; - _onexit_t onexit(_onexit_t _Func) { - return _onexit$1(_Func); - } +const int __IPHONE_17_0 = 170000; - late final _onexitPtr = - _lookup>('onexit'); - late final _onexit$1 = _onexitPtr.asFunction<_onexit_t Function(_onexit_t)>(); +const int __IPHONE_17_1 = 170100; - double cabs(_Dcomplex _Z) { - return _cabs(_Z); - } +const int __IPHONE_17_2 = 170200; - late final _cabsPtr = - _lookup>('cabs'); - late final _cabs = _cabsPtr.asFunction(); +const int __IPHONE_17_3 = 170300; - _Dcomplex cacos(_Dcomplex _Z) { - return _cacos(_Z); - } +const int __IPHONE_17_4 = 170400; - late final _cacosPtr = - _lookup>('cacos'); - late final _cacos = _cacosPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __IPHONE_17_5 = 170500; - _Dcomplex cacosh(_Dcomplex _Z) { - return _cacosh(_Z); - } +const int __IPHONE_17_6 = 170600; - late final _cacoshPtr = - _lookup>('cacosh'); - late final _cacosh = _cacoshPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __IPHONE_17_7 = 170700; - double carg(_Dcomplex _Z) { - return _carg(_Z); - } +const int __IPHONE_18_0 = 180000; - late final _cargPtr = - _lookup>('carg'); - late final _carg = _cargPtr.asFunction(); +const int __IPHONE_18_1 = 180100; - _Dcomplex casin(_Dcomplex _Z) { - return _casin(_Z); - } +const int __IPHONE_18_2 = 180200; - late final _casinPtr = - _lookup>('casin'); - late final _casin = _casinPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __IPHONE_18_3 = 180300; - _Dcomplex casinh(_Dcomplex _Z) { - return _casinh(_Z); - } +const int __IPHONE_18_4 = 180400; - late final _casinhPtr = - _lookup>('casinh'); - late final _casinh = _casinhPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __IPHONE_18_5 = 180500; - _Dcomplex catan(_Dcomplex _Z) { - return _catan(_Z); - } +const int __IPHONE_18_6 = 180600; - late final _catanPtr = - _lookup>('catan'); - late final _catan = _catanPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __IPHONE_19_0 = 190000; - _Dcomplex catanh(_Dcomplex _Z) { - return _catanh(_Z); - } +const int __IPHONE_26_0 = 260000; - late final _catanhPtr = - _lookup>('catanh'); - late final _catanh = _catanhPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __IPHONE_26_1 = 260100; - _Dcomplex ccos(_Dcomplex _Z) { - return _ccos(_Z); - } +const int __IPHONE_26_2 = 260200; - late final _ccosPtr = - _lookup>('ccos'); - late final _ccos = _ccosPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __IPHONE_26_3 = 260300; - _Dcomplex ccosh(_Dcomplex _Z) { - return _ccosh(_Z); - } +const int __IPHONE_26_4 = 260400; - late final _ccoshPtr = - _lookup>('ccosh'); - late final _ccosh = _ccoshPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __IPHONE_26_5 = 260500; - _Dcomplex cexp(_Dcomplex _Z) { - return _cexp(_Z); - } +const int __WATCHOS_1_0 = 10000; - late final _cexpPtr = - _lookup>('cexp'); - late final _cexp = _cexpPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __WATCHOS_2_0 = 20000; - double cimag(_Dcomplex _Z) { - return _cimag(_Z); - } +const int __WATCHOS_2_1 = 20100; - late final _cimagPtr = - _lookup>('cimag'); - late final _cimag = _cimagPtr.asFunction(); +const int __WATCHOS_2_2 = 20200; - _Dcomplex clog(_Dcomplex _Z) { - return _clog(_Z); - } +const int __WATCHOS_3_0 = 30000; - late final _clogPtr = - _lookup>('clog'); - late final _clog = _clogPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __WATCHOS_3_1 = 30100; - _Dcomplex clog10(_Dcomplex _Z) { - return _clog10(_Z); - } +const int __WATCHOS_3_1_1 = 30101; - late final _clog10Ptr = - _lookup>('clog10'); - late final _clog10 = _clog10Ptr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __WATCHOS_3_2 = 30200; - _Dcomplex conj(_Dcomplex _Z) { - return _conj(_Z); - } +const int __WATCHOS_4_0 = 40000; - late final _conjPtr = - _lookup>('conj'); - late final _conj = _conjPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __WATCHOS_4_1 = 40100; - _Dcomplex cpow(_Dcomplex _X, _Dcomplex _Y) { - return _cpow(_X, _Y); - } +const int __WATCHOS_4_2 = 40200; - late final _cpowPtr = - _lookup>( - 'cpow', - ); - late final _cpow = _cpowPtr - .asFunction<_Dcomplex Function(_Dcomplex, _Dcomplex)>(); +const int __WATCHOS_4_3 = 40300; - _Dcomplex cproj(_Dcomplex _Z) { - return _cproj(_Z); - } +const int __WATCHOS_5_0 = 50000; - late final _cprojPtr = - _lookup>('cproj'); - late final _cproj = _cprojPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __WATCHOS_5_1 = 50100; - double creal(_Dcomplex _Z) { - return _creal(_Z); - } +const int __WATCHOS_5_2 = 50200; - late final _crealPtr = - _lookup>('creal'); - late final _creal = _crealPtr.asFunction(); +const int __WATCHOS_5_3 = 50300; - _Dcomplex csin(_Dcomplex _Z) { - return _csin(_Z); - } +const int __WATCHOS_6_0 = 60000; - late final _csinPtr = - _lookup>('csin'); - late final _csin = _csinPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __WATCHOS_6_1 = 60100; - _Dcomplex csinh(_Dcomplex _Z) { - return _csinh(_Z); - } +const int __WATCHOS_6_2 = 60200; - late final _csinhPtr = - _lookup>('csinh'); - late final _csinh = _csinhPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __WATCHOS_7_0 = 70000; - _Dcomplex csqrt(_Dcomplex _Z) { - return _csqrt(_Z); - } +const int __WATCHOS_7_1 = 70100; - late final _csqrtPtr = - _lookup>('csqrt'); - late final _csqrt = _csqrtPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __WATCHOS_7_2 = 70200; - _Dcomplex ctan(_Dcomplex _Z) { - return _ctan(_Z); - } +const int __WATCHOS_7_3 = 70300; - late final _ctanPtr = - _lookup>('ctan'); - late final _ctan = _ctanPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __WATCHOS_7_4 = 70400; - _Dcomplex ctanh(_Dcomplex _Z) { - return _ctanh(_Z); - } +const int __WATCHOS_7_5 = 70500; - late final _ctanhPtr = - _lookup>('ctanh'); - late final _ctanh = _ctanhPtr.asFunction<_Dcomplex Function(_Dcomplex)>(); +const int __WATCHOS_7_6 = 70600; - double norm(_Dcomplex _Z) { - return _norm(_Z); - } +const int __WATCHOS_8_0 = 80000; - late final _normPtr = - _lookup>('norm'); - late final _norm = _normPtr.asFunction(); +const int __WATCHOS_8_1 = 80100; - double cabsf(_Fcomplex _Z) { - return _cabsf(_Z); - } +const int __WATCHOS_8_3 = 80300; - late final _cabsfPtr = - _lookup>('cabsf'); - late final _cabsf = _cabsfPtr.asFunction(); +const int __WATCHOS_8_4 = 80400; - _Fcomplex cacosf(_Fcomplex _Z) { - return _cacosf(_Z); - } +const int __WATCHOS_8_5 = 80500; - late final _cacosfPtr = - _lookup>('cacosf'); - late final _cacosf = _cacosfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_8_6 = 80600; - _Fcomplex cacoshf(_Fcomplex _Z) { - return _cacoshf(_Z); - } +const int __WATCHOS_8_7 = 80700; - late final _cacoshfPtr = - _lookup>('cacoshf'); - late final _cacoshf = _cacoshfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_8_8 = 80800; - double cargf(_Fcomplex _Z) { - return _cargf(_Z); - } +const int __WATCHOS_9_0 = 90000; - late final _cargfPtr = - _lookup>('cargf'); - late final _cargf = _cargfPtr.asFunction(); +const int __WATCHOS_9_1 = 90100; - _Fcomplex casinf(_Fcomplex _Z) { - return _casinf(_Z); - } +const int __WATCHOS_9_2 = 90200; - late final _casinfPtr = - _lookup>('casinf'); - late final _casinf = _casinfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_9_3 = 90300; - _Fcomplex casinhf(_Fcomplex _Z) { - return _casinhf(_Z); - } +const int __WATCHOS_9_4 = 90400; - late final _casinhfPtr = - _lookup>('casinhf'); - late final _casinhf = _casinhfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_9_5 = 90500; - _Fcomplex catanf(_Fcomplex _Z) { - return _catanf(_Z); - } +const int __WATCHOS_9_6 = 90600; - late final _catanfPtr = - _lookup>('catanf'); - late final _catanf = _catanfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_10_0 = 100000; - _Fcomplex catanhf(_Fcomplex _Z) { - return _catanhf(_Z); - } +const int __WATCHOS_10_1 = 100100; - late final _catanhfPtr = - _lookup>('catanhf'); - late final _catanhf = _catanhfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_10_2 = 100200; - _Fcomplex ccosf(_Fcomplex _Z) { - return _ccosf(_Z); - } +const int __WATCHOS_10_3 = 100300; - late final _ccosfPtr = - _lookup>('ccosf'); - late final _ccosf = _ccosfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_10_4 = 100400; - _Fcomplex ccoshf(_Fcomplex _Z) { - return _ccoshf(_Z); - } +const int __WATCHOS_10_5 = 100500; - late final _ccoshfPtr = - _lookup>('ccoshf'); - late final _ccoshf = _ccoshfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_10_6 = 100600; - _Fcomplex cexpf(_Fcomplex _Z) { - return _cexpf(_Z); - } +const int __WATCHOS_10_7 = 100700; - late final _cexpfPtr = - _lookup>('cexpf'); - late final _cexpf = _cexpfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_11_0 = 110000; - double cimagf(_Fcomplex _Z) { - return _cimagf(_Z); - } +const int __WATCHOS_11_1 = 110100; - late final _cimagfPtr = - _lookup>('cimagf'); - late final _cimagf = _cimagfPtr.asFunction(); +const int __WATCHOS_11_2 = 110200; - _Fcomplex clogf(_Fcomplex _Z) { - return _clogf(_Z); - } +const int __WATCHOS_11_3 = 110300; - late final _clogfPtr = - _lookup>('clogf'); - late final _clogf = _clogfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_11_4 = 110400; - _Fcomplex clog10f(_Fcomplex _Z) { - return _clog10f(_Z); - } +const int __WATCHOS_11_5 = 110500; - late final _clog10fPtr = - _lookup>('clog10f'); - late final _clog10f = _clog10fPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_11_6 = 110600; - _Fcomplex conjf(_Fcomplex _Z) { - return _conjf(_Z); - } +const int __WATCHOS_12_0 = 120000; - late final _conjfPtr = - _lookup>('conjf'); - late final _conjf = _conjfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_26_0 = 260000; - _Fcomplex cpowf(_Fcomplex _X, _Fcomplex _Y) { - return _cpowf(_X, _Y); - } +const int __WATCHOS_26_1 = 260100; - late final _cpowfPtr = - _lookup>( - 'cpowf', - ); - late final _cpowf = _cpowfPtr - .asFunction<_Fcomplex Function(_Fcomplex, _Fcomplex)>(); +const int __WATCHOS_26_2 = 260200; - _Fcomplex cprojf(_Fcomplex _Z) { - return _cprojf(_Z); - } +const int __WATCHOS_26_3 = 260300; - late final _cprojfPtr = - _lookup>('cprojf'); - late final _cprojf = _cprojfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __WATCHOS_26_4 = 260400; - double crealf(_Fcomplex _Z) { - return _crealf(_Z); - } +const int __WATCHOS_26_5 = 260500; - late final _crealfPtr = - _lookup>('crealf'); - late final _crealf = _crealfPtr.asFunction(); +const int __TVOS_9_0 = 90000; - _Fcomplex csinf(_Fcomplex _Z) { - return _csinf(_Z); - } +const int __TVOS_9_1 = 90100; - late final _csinfPtr = - _lookup>('csinf'); - late final _csinf = _csinfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __TVOS_9_2 = 90200; - _Fcomplex csinhf(_Fcomplex _Z) { - return _csinhf(_Z); - } +const int __TVOS_10_0 = 100000; - late final _csinhfPtr = - _lookup>('csinhf'); - late final _csinhf = _csinhfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __TVOS_10_0_1 = 100001; - _Fcomplex csqrtf(_Fcomplex _Z) { - return _csqrtf(_Z); - } +const int __TVOS_10_1 = 100100; - late final _csqrtfPtr = - _lookup>('csqrtf'); - late final _csqrtf = _csqrtfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __TVOS_10_2 = 100200; - _Fcomplex ctanf(_Fcomplex _Z) { - return _ctanf(_Z); - } +const int __TVOS_11_0 = 110000; - late final _ctanfPtr = - _lookup>('ctanf'); - late final _ctanf = _ctanfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __TVOS_11_1 = 110100; - _Fcomplex ctanhf(_Fcomplex _Z) { - return _ctanhf(_Z); - } +const int __TVOS_11_2 = 110200; - late final _ctanhfPtr = - _lookup>('ctanhf'); - late final _ctanhf = _ctanhfPtr.asFunction<_Fcomplex Function(_Fcomplex)>(); +const int __TVOS_11_3 = 110300; - double normf(_Fcomplex _Z) { - return _normf(_Z); - } +const int __TVOS_11_4 = 110400; - late final _normfPtr = - _lookup>('normf'); - late final _normf = _normfPtr.asFunction(); +const int __TVOS_12_0 = 120000; - _Dcomplex _Cbuild(double _Re, double _Im) { - return __Cbuild(_Re, _Im); - } +const int __TVOS_12_1 = 120100; - late final __CbuildPtr = - _lookup>( - '_Cbuild', - ); - late final __Cbuild = __CbuildPtr - .asFunction<_Dcomplex Function(double, double)>(); +const int __TVOS_12_2 = 120200; - _Dcomplex _Cmulcc(_Dcomplex _X, _Dcomplex _Y) { - return __Cmulcc(_X, _Y); - } +const int __TVOS_12_3 = 120300; - late final __CmulccPtr = - _lookup>( - '_Cmulcc', - ); - late final __Cmulcc = __CmulccPtr - .asFunction<_Dcomplex Function(_Dcomplex, _Dcomplex)>(); +const int __TVOS_12_4 = 120400; - _Dcomplex _Cmulcr(_Dcomplex _X, double _Y) { - return __Cmulcr(_X, _Y); - } +const int __TVOS_13_0 = 130000; - late final __CmulcrPtr = - _lookup>( - '_Cmulcr', - ); - late final __Cmulcr = __CmulcrPtr - .asFunction<_Dcomplex Function(_Dcomplex, double)>(); +const int __TVOS_13_2 = 130200; - _Fcomplex _FCbuild(double _Re, double _Im) { - return __FCbuild(_Re, _Im); - } +const int __TVOS_13_3 = 130300; - late final __FCbuildPtr = - _lookup>( - '_FCbuild', - ); - late final __FCbuild = __FCbuildPtr - .asFunction<_Fcomplex Function(double, double)>(); +const int __TVOS_13_4 = 130400; - _Fcomplex _FCmulcc(_Fcomplex _X, _Fcomplex _Y) { - return __FCmulcc(_X, _Y); - } +const int __TVOS_14_0 = 140000; - late final __FCmulccPtr = - _lookup>( - '_FCmulcc', - ); - late final __FCmulcc = __FCmulccPtr - .asFunction<_Fcomplex Function(_Fcomplex, _Fcomplex)>(); +const int __TVOS_14_1 = 140100; - _Fcomplex _FCmulcr(_Fcomplex _X, double _Y) { - return __FCmulcr(_X, _Y); - } +const int __TVOS_14_2 = 140200; - late final __FCmulcrPtr = - _lookup>( - '_FCmulcr', - ); - late final __FCmulcr = __FCmulcrPtr - .asFunction<_Fcomplex Function(_Fcomplex, double)>(); +const int __TVOS_14_3 = 140300; - ffi.Pointer getAppDataDir() { - return _getAppDataDir(); - } +const int __TVOS_14_5 = 140500; - late final _getAppDataDirPtr = - _lookup Function()>>( - 'getAppDataDir', - ); - late final _getAppDataDir = _getAppDataDirPtr - .asFunction Function()>(); +const int __TVOS_14_6 = 140600; - ffi.Pointer setup( - ffi.Pointer _logDir, - ffi.Pointer _dataDir, - ffi.Pointer _locale, - ffi.Pointer _env, - int logP, - int appsP, - int statusP, - int privateServerP, - int appEventP, - int consent, - ffi.Pointer api, - ) { - return _setup( - _logDir, - _dataDir, - _locale, - _env, - logP, - appsP, - statusP, - privateServerP, - appEventP, - consent, - api, - ); - } +const int __TVOS_14_7 = 140700; + +const int __TVOS_15_0 = 150000; + +const int __TVOS_15_1 = 150100; - late final _setupPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Int64, - ffi.Int64, - ffi.Int64, - ffi.Int64, - ffi.Int64, - ffi.Int, - ffi.Pointer, - ) - > - >('setup'); - late final _setup = _setupPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, - int, - int, - int, - int, - int, - ffi.Pointer, - ) - >(); +const int __TVOS_15_2 = 150200; - ffi.Pointer updateTelemetryConsent(int consent) { - return _updateTelemetryConsent(consent); - } +const int __TVOS_15_3 = 150300; - late final _updateTelemetryConsentPtr = - _lookup Function(ffi.Int)>>( - 'updateTelemetryConsent', - ); - late final _updateTelemetryConsent = _updateTelemetryConsentPtr - .asFunction Function(int)>(); +const int __TVOS_15_4 = 150400; - int isTelemetryEnabled() { - return _isTelemetryEnabled(); - } +const int __TVOS_15_5 = 150500; - late final _isTelemetryEnabledPtr = - _lookup>('isTelemetryEnabled'); - late final _isTelemetryEnabled = _isTelemetryEnabledPtr - .asFunction(); +const int __TVOS_15_6 = 150600; - int isOAuthLogin() { - return _isOAuthLogin(); - } +const int __TVOS_16_0 = 160000; - late final _isOAuthLoginPtr = _lookup>( - 'isOAuthLogin', - ); - late final _isOAuthLogin = _isOAuthLoginPtr.asFunction(); +const int __TVOS_16_1 = 160100; - ffi.Pointer getOAuthProvider() { - return _getOAuthProvider(); - } +const int __TVOS_16_2 = 160200; - late final _getOAuthProviderPtr = - _lookup Function()>>( - 'getOAuthProvider', - ); - late final _getOAuthProvider = _getOAuthProviderPtr - .asFunction Function()>(); +const int __TVOS_16_3 = 160300; - ffi.Pointer availableFeatures() { - return _availableFeatures(); - } +const int __TVOS_16_4 = 160400; - late final _availableFeaturesPtr = - _lookup Function()>>( - 'availableFeatures', - ); - late final _availableFeatures = _availableFeaturesPtr - .asFunction Function()>(); +const int __TVOS_16_5 = 160500; - ffi.Pointer updateLocale(ffi.Pointer _locale) { - return _updateLocale(_locale); - } +const int __TVOS_16_6 = 160600; - late final _updateLocalePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('updateLocale'); - late final _updateLocale = _updateLocalePtr - .asFunction Function(ffi.Pointer)>(); +const int __TVOS_17_0 = 170000; - ffi.Pointer addSplitTunnelItem( - ffi.Pointer filterTypeC, - ffi.Pointer itemC, - ) { - return _addSplitTunnelItem(filterTypeC, itemC); - } +const int __TVOS_17_1 = 170100; - late final _addSplitTunnelItemPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - > - >('addSplitTunnelItem'); - late final _addSplitTunnelItem = _addSplitTunnelItemPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __TVOS_17_2 = 170200; - ffi.Pointer removeSplitTunnelItem( - ffi.Pointer filterTypeC, - ffi.Pointer itemC, - ) { - return _removeSplitTunnelItem(filterTypeC, itemC); - } +const int __TVOS_17_3 = 170300; - late final _removeSplitTunnelItemPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - > - >('removeSplitTunnelItem'); - late final _removeSplitTunnelItem = _removeSplitTunnelItemPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __TVOS_17_4 = 170400; - ffi.Pointer setSplitTunnelingEnabled(int enabled) { - return _setSplitTunnelingEnabled(enabled); - } +const int __TVOS_17_5 = 170500; - late final _setSplitTunnelingEnabledPtr = - _lookup Function(ffi.Int)>>( - 'setSplitTunnelingEnabled', - ); - late final _setSplitTunnelingEnabled = _setSplitTunnelingEnabledPtr - .asFunction Function(int)>(); +const int __TVOS_17_6 = 170600; - int isSplitTunnelingEnabled() { - return _isSplitTunnelingEnabled(); - } +const int __TVOS_18_0 = 180000; - late final _isSplitTunnelingEnabledPtr = - _lookup>( - 'isSplitTunnelingEnabled', - ); - late final _isSplitTunnelingEnabled = _isSplitTunnelingEnabledPtr - .asFunction(); +const int __TVOS_18_1 = 180100; - ffi.Pointer loadInstalledApps(ffi.Pointer dataDir) { - return _loadInstalledApps(dataDir); - } +const int __TVOS_18_2 = 180200; - late final _loadInstalledAppsPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('loadInstalledApps'); - late final _loadInstalledApps = _loadInstalledAppsPtr - .asFunction Function(ffi.Pointer)>(); +const int __TVOS_18_3 = 180300; - ffi.Pointer loadInstalledAppIcon( - ffi.Pointer appPathC, - ffi.Pointer iconPathC, - ) { - return _loadInstalledAppIcon(appPathC, iconPathC); - } +const int __TVOS_18_4 = 180400; - late final _loadInstalledAppIconPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - > - >('loadInstalledAppIcon'); - late final _loadInstalledAppIcon = _loadInstalledAppIconPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __TVOS_18_5 = 180500; - ffi.Pointer getDataCapInfo() { - return _getDataCapInfo(); - } +const int __TVOS_18_6 = 180600; - late final _getDataCapInfoPtr = - _lookup Function()>>( - 'getDataCapInfo', - ); - late final _getDataCapInfo = _getDataCapInfoPtr - .asFunction Function()>(); +const int __TVOS_19_0 = 190000; - ffi.Pointer reportIssue( - ffi.Pointer emailC, - ffi.Pointer typeC, - ffi.Pointer descC, - ffi.Pointer deviceC, - ffi.Pointer modelC, - ffi.Pointer logPathC, - ffi.Pointer attachmentsJSONC, - ) { - return _reportIssue( - emailC, - typeC, - descC, - deviceC, - modelC, - logPathC, - attachmentsJSONC, - ); - } +const int __TVOS_26_0 = 260000; - late final _reportIssuePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('reportIssue'); - late final _reportIssue = _reportIssuePtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __TVOS_26_1 = 260100; - ffi.Pointer getSelectedServerJSON() { - return _getSelectedServerJSON(); - } +const int __TVOS_26_2 = 260200; - late final _getSelectedServerJSONPtr = - _lookup Function()>>( - 'getSelectedServerJSON', - ); - late final _getSelectedServerJSON = _getSelectedServerJSONPtr - .asFunction Function()>(); +const int __TVOS_26_3 = 260300; + +const int __TVOS_26_4 = 260400; + +const int __TVOS_26_5 = 260500; + +const int __BRIDGEOS_2_0 = 20000; + +const int __BRIDGEOS_3_0 = 30000; + +const int __BRIDGEOS_3_1 = 30100; + +const int __BRIDGEOS_3_4 = 30400; + +const int __BRIDGEOS_4_0 = 40000; + +const int __BRIDGEOS_4_1 = 40100; + +const int __BRIDGEOS_5_0 = 50000; + +const int __BRIDGEOS_5_1 = 50100; + +const int __BRIDGEOS_5_3 = 50300; + +const int __BRIDGEOS_6_0 = 60000; - ffi.Pointer getAutoLocation() { - return _getAutoLocation(); - } +const int __BRIDGEOS_6_2 = 60200; - late final _getAutoLocationPtr = - _lookup Function()>>( - 'getAutoLocation', - ); - late final _getAutoLocation = _getAutoLocationPtr - .asFunction Function()>(); +const int __BRIDGEOS_6_4 = 60400; - ffi.Pointer isTagAvailable(ffi.Pointer _tag) { - return _isTagAvailable(_tag); - } +const int __BRIDGEOS_6_5 = 60500; - late final _isTagAvailablePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('isTagAvailable'); - late final _isTagAvailable = _isTagAvailablePtr - .asFunction Function(ffi.Pointer)>(); +const int __BRIDGEOS_6_6 = 60600; - ffi.Pointer getAvailableServers() { - return _getAvailableServers(); - } +const int __BRIDGEOS_7_0 = 70000; - late final _getAvailableServersPtr = - _lookup Function()>>( - 'getAvailableServers', - ); - late final _getAvailableServers = _getAvailableServersPtr - .asFunction Function()>(); +const int __BRIDGEOS_7_1 = 70100; - ffi.Pointer startVPN() { - return _startVPN(); - } +const int __BRIDGEOS_7_2 = 70200; - late final _startVPNPtr = - _lookup Function()>>('startVPN'); - late final _startVPN = _startVPNPtr - .asFunction Function()>(); +const int __BRIDGEOS_7_3 = 70300; - ffi.Pointer stopVPN() { - return _stopVPN(); - } +const int __BRIDGEOS_7_4 = 70400; - late final _stopVPNPtr = - _lookup Function()>>('stopVPN'); - late final _stopVPN = _stopVPNPtr - .asFunction Function()>(); +const int __BRIDGEOS_7_6 = 70600; - ffi.Pointer connectToServer(ffi.Pointer _tag) { - return _connectToServer(_tag); - } +const int __BRIDGEOS_8_0 = 80000; - late final _connectToServerPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('connectToServer'); - late final _connectToServer = _connectToServerPtr - .asFunction Function(ffi.Pointer)>(); +const int __BRIDGEOS_8_1 = 80100; - int isVPNConnected() { - return _isVPNConnected(); - } +const int __BRIDGEOS_8_2 = 80200; - late final _isVPNConnectedPtr = - _lookup>('isVPNConnected'); - late final _isVPNConnected = _isVPNConnectedPtr.asFunction(); +const int __BRIDGEOS_8_3 = 80300; - ffi.Pointer getUserData() { - return _getUserData(); - } +const int __BRIDGEOS_8_4 = 80400; - late final _getUserDataPtr = - _lookup Function()>>( - 'getUserData', - ); - late final _getUserData = _getUserDataPtr - .asFunction Function()>(); +const int __BRIDGEOS_8_5 = 80500; - ffi.Pointer fetchUserData() { - return _fetchUserData(); - } +const int __BRIDGEOS_8_6 = 80600; - late final _fetchUserDataPtr = - _lookup Function()>>( - 'fetchUserData', - ); - late final _fetchUserData = _fetchUserDataPtr - .asFunction Function()>(); +const int __BRIDGEOS_9_0 = 90000; - ffi.Pointer stripeSubscriptionPaymentRedirect( - ffi.Pointer subType, - ffi.Pointer _planId, - ffi.Pointer _email, - ffi.Pointer _idempotencyKey, - ffi.Pointer _couponCode, - ) { - return _stripeSubscriptionPaymentRedirect( - subType, - _planId, - _email, - _idempotencyKey, - _couponCode, - ); - } +const int __BRIDGEOS_9_1 = 90100; - late final _stripeSubscriptionPaymentRedirectPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('stripeSubscriptionPaymentRedirect'); - late final _stripeSubscriptionPaymentRedirect = - _stripeSubscriptionPaymentRedirectPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __BRIDGEOS_9_2 = 90200; - ffi.Pointer paymentRedirect( - ffi.Pointer _plan, - ffi.Pointer _provider, - ffi.Pointer _email, - ffi.Pointer _idempotencyKey, - ) { - return _paymentRedirect(_plan, _provider, _email, _idempotencyKey); - } +const int __BRIDGEOS_9_3 = 90300; - late final _paymentRedirectPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('paymentRedirect'); - late final _paymentRedirect = _paymentRedirectPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __BRIDGEOS_9_4 = 90400; - ffi.Pointer stripeBillingPortalUrl() { - return _stripeBillingPortalUrl(); - } +const int __BRIDGEOS_9_5 = 90500; - late final _stripeBillingPortalUrlPtr = - _lookup Function()>>( - 'stripeBillingPortalUrl', - ); - late final _stripeBillingPortalUrl = _stripeBillingPortalUrlPtr - .asFunction Function()>(); +const int __BRIDGEOS_9_6 = 90600; - ffi.Pointer plans() { - return _plans(); - } +const int __BRIDGEOS_10_0 = 100000; - late final _plansPtr = - _lookup Function()>>('plans'); - late final _plans = _plansPtr.asFunction Function()>(); +const int __BRIDGEOS_10_1 = 100100; - ffi.Pointer oauthLoginUrl(ffi.Pointer _provider) { - return _oauthLoginUrl(_provider); - } +const int __BRIDGEOS_10_2 = 100200; - late final _oauthLoginUrlPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('oauthLoginUrl'); - late final _oauthLoginUrl = _oauthLoginUrlPtr - .asFunction Function(ffi.Pointer)>(); +const int __BRIDGEOS_10_3 = 100300; - ffi.Pointer oAuthLoginCallback(ffi.Pointer _oAuthToken) { - return _oAuthLoginCallback(_oAuthToken); - } +const int __BRIDGEOS_10_4 = 100400; - late final _oAuthLoginCallbackPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('oAuthLoginCallback'); - late final _oAuthLoginCallback = _oAuthLoginCallbackPtr - .asFunction Function(ffi.Pointer)>(); +const int __BRIDGEOS_26_5 = 260500; - ffi.Pointer login( - ffi.Pointer _email, - ffi.Pointer _password, - ) { - return _login(_email, _password); - } +const int __DRIVERKIT_19_0 = 190000; - late final _loginPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - > - >('login'); - late final _login = _loginPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __DRIVERKIT_20_0 = 200000; - ffi.Pointer signup( - ffi.Pointer _email, - ffi.Pointer _password, - ) { - return _signup(_email, _password); - } +const int __DRIVERKIT_21_0 = 210000; - late final _signupPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - > - >('signup'); - late final _signup = _signupPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __DRIVERKIT_22_0 = 220000; - ffi.Pointer logout(ffi.Pointer _email) { - return _logout(_email); - } +const int __DRIVERKIT_22_4 = 220400; - late final _logoutPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('logout'); - late final _logout = _logoutPtr - .asFunction Function(ffi.Pointer)>(); +const int __DRIVERKIT_22_5 = 220500; - ffi.Pointer startRecoveryByEmail(ffi.Pointer _email) { - return _startRecoveryByEmail(_email); - } +const int __DRIVERKIT_22_6 = 220600; - late final _startRecoveryByEmailPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('startRecoveryByEmail'); - late final _startRecoveryByEmail = _startRecoveryByEmailPtr - .asFunction Function(ffi.Pointer)>(); +const int __DRIVERKIT_23_0 = 230000; - ffi.Pointer validateEmailRecoveryCode( - ffi.Pointer _email, - ffi.Pointer _code, - ) { - return _validateEmailRecoveryCode(_email, _code); - } +const int __DRIVERKIT_23_1 = 230100; + +const int __DRIVERKIT_23_2 = 230200; + +const int __DRIVERKIT_23_3 = 230300; + +const int __DRIVERKIT_23_4 = 230400; + +const int __DRIVERKIT_23_5 = 230500; - late final _validateEmailRecoveryCodePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - > - >('validateEmailRecoveryCode'); - late final _validateEmailRecoveryCode = _validateEmailRecoveryCodePtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __DRIVERKIT_23_6 = 230600; - ffi.Pointer completeRecoveryByEmail( - ffi.Pointer _email, - ffi.Pointer _newPassword, - ffi.Pointer _code, - ) { - return _completeRecoveryByEmail(_email, _newPassword, _code); - } +const int __DRIVERKIT_24_0 = 240000; - late final _completeRecoveryByEmailPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('completeRecoveryByEmail'); - late final _completeRecoveryByEmail = _completeRecoveryByEmailPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __DRIVERKIT_24_1 = 240100; - ffi.Pointer removeDevice(ffi.Pointer deviceId) { - return _removeDevice(deviceId); - } +const int __DRIVERKIT_24_2 = 240200; - late final _removeDevicePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('removeDevice'); - late final _removeDevice = _removeDevicePtr - .asFunction Function(ffi.Pointer)>(); +const int __DRIVERKIT_24_3 = 240300; - ffi.Pointer referralAttachment( - ffi.Pointer _referralCode, - ) { - return _referralAttachment(_referralCode); - } +const int __DRIVERKIT_24_4 = 240400; - late final _referralAttachmentPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('referralAttachment'); - late final _referralAttachment = _referralAttachmentPtr - .asFunction Function(ffi.Pointer)>(); +const int __DRIVERKIT_24_5 = 240500; - ffi.Pointer referralAttachmentV2( - ffi.Pointer _referralCode, - ) { - return _referralAttachmentV2(_referralCode); - } +const int __DRIVERKIT_24_6 = 240600; - late final _referralAttachmentV2Ptr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('referralAttachmentV2'); - late final _referralAttachmentV2 = _referralAttachmentV2Ptr - .asFunction Function(ffi.Pointer)>(); +const int __DRIVERKIT_25_0 = 250000; - ffi.Pointer startChangeEmail( - ffi.Pointer _newEmail, - ffi.Pointer _password, - ) { - return _startChangeEmail(_newEmail, _password); - } +const int __DRIVERKIT_25_1 = 250100; - late final _startChangeEmailPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - > - >('startChangeEmail'); - late final _startChangeEmail = _startChangeEmailPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __DRIVERKIT_25_2 = 250200; - ffi.Pointer completeChangeEmail( - ffi.Pointer _newEmail, - ffi.Pointer _password, - ffi.Pointer _code, - ) { - return _completeChangeEmail(_newEmail, _password, _code); - } +const int __DRIVERKIT_25_3 = 250300; - late final _completeChangeEmailPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('completeChangeEmail'); - late final _completeChangeEmail = _completeChangeEmailPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __DRIVERKIT_25_4 = 250400; - ffi.Pointer deleteAccount( - ffi.Pointer _email, - ffi.Pointer _password, - ) { - return _deleteAccount(_email, _password); - } +const int __DRIVERKIT_25_5 = 250500; - late final _deleteAccountPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - > - >('deleteAccount'); - late final _deleteAccount = _deleteAccountPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __VISIONOS_1_0 = 10000; - ffi.Pointer activationCode( - ffi.Pointer _email, - ffi.Pointer _resellerCode, - ) { - return _activationCode(_email, _resellerCode); - } +const int __VISIONOS_1_1 = 10100; - late final _activationCodePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - > - >('activationCode'); - late final _activationCode = _activationCodePtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int __VISIONOS_1_2 = 10200; - void freeCString(ffi.Pointer cstr) { - return _freeCString(cstr); - } +const int __VISIONOS_1_3 = 10300; - late final _freeCStringPtr = - _lookup)>>( - 'freeCString', - ); - late final _freeCString = _freeCStringPtr - .asFunction)>(); +const int __VISIONOS_2_0 = 20000; - ffi.Pointer patchSettings(ffi.Pointer patchJSON) { - return _patchSettings(patchJSON); - } +const int __VISIONOS_2_1 = 20100; - late final _patchSettingsPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('patchSettings'); - late final _patchSettings = _patchSettingsPtr - .asFunction Function(ffi.Pointer)>(); +const int __VISIONOS_2_2 = 20200; - ffi.Pointer getSettings() { - return _getSettings(); - } +const int __VISIONOS_2_3 = 20300; - late final _getSettingsPtr = - _lookup Function()>>( - 'getSettings', - ); - late final _getSettings = _getSettingsPtr - .asFunction Function()>(); +const int __VISIONOS_2_4 = 20400; - ffi.Pointer patchEnvVars(ffi.Pointer patchJSON) { - return _patchEnvVars(patchJSON); - } +const int __VISIONOS_2_5 = 20500; - late final _patchEnvVarsPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('patchEnvVars'); - late final _patchEnvVars = _patchEnvVarsPtr - .asFunction Function(ffi.Pointer)>(); +const int __VISIONOS_2_6 = 20600; - ffi.Pointer getEnvVars() { - return _getEnvVars(); - } +const int __VISIONOS_3_0 = 30000; - late final _getEnvVarsPtr = - _lookup Function()>>( - 'getEnvVars', - ); - late final _getEnvVars = _getEnvVarsPtr - .asFunction Function()>(); +const int __VISIONOS_26_0 = 260000; - ffi.Pointer runURLTests() { - return _runURLTests(); - } +const int __VISIONOS_26_1 = 260100; - late final _runURLTestsPtr = - _lookup Function()>>( - 'runURLTests', - ); - late final _runURLTests = _runURLTestsPtr - .asFunction Function()>(); +const int __VISIONOS_26_2 = 260200; - ffi.Pointer updateConfig() { - return _updateConfig(); - } +const int __VISIONOS_26_3 = 260300; - late final _updateConfigPtr = - _lookup Function()>>( - 'updateConfig', - ); - late final _updateConfig = _updateConfigPtr - .asFunction Function()>(); +const int __VISIONOS_26_4 = 260400; - ffi.Pointer clearTunnelCache() { - return _clearTunnelCache(); - } +const int __VISIONOS_26_5 = 260500; - late final _clearTunnelCachePtr = - _lookup Function()>>( - 'clearTunnelCache', - ); - late final _clearTunnelCache = _clearTunnelCachePtr - .asFunction Function()>(); +const int MAC_OS_X_VERSION_10_0 = 1000; - ffi.Pointer digitalOceanPrivateServer() { - return _digitalOceanPrivateServer(); - } +const int MAC_OS_X_VERSION_10_1 = 1010; - late final _digitalOceanPrivateServerPtr = - _lookup Function()>>( - 'digitalOceanPrivateServer', - ); - late final _digitalOceanPrivateServer = _digitalOceanPrivateServerPtr - .asFunction Function()>(); +const int MAC_OS_X_VERSION_10_2 = 1020; - ffi.Pointer googleCloudPrivateServer() { - return _googleCloudPrivateServer(); - } +const int MAC_OS_X_VERSION_10_3 = 1030; - late final _googleCloudPrivateServerPtr = - _lookup Function()>>( - 'googleCloudPrivateServer', - ); - late final _googleCloudPrivateServer = _googleCloudPrivateServerPtr - .asFunction Function()>(); +const int MAC_OS_X_VERSION_10_4 = 1040; - ffi.Pointer selectAccount(ffi.Pointer _account) { - return _selectAccount(_account); - } +const int MAC_OS_X_VERSION_10_5 = 1050; - late final _selectAccountPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('selectAccount'); - late final _selectAccount = _selectAccountPtr - .asFunction Function(ffi.Pointer)>(); +const int MAC_OS_X_VERSION_10_6 = 1060; - ffi.Pointer selectProject(ffi.Pointer _project) { - return _selectProject(_project); - } +const int MAC_OS_X_VERSION_10_7 = 1070; - late final _selectProjectPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('selectProject'); - late final _selectProject = _selectProjectPtr - .asFunction Function(ffi.Pointer)>(); +const int MAC_OS_X_VERSION_10_8 = 1080; - ffi.Pointer validateSession() { - return _validateSession(); - } +const int MAC_OS_X_VERSION_10_9 = 1090; - late final _validateSessionPtr = - _lookup Function()>>( - 'validateSession', - ); - late final _validateSession = _validateSessionPtr - .asFunction Function()>(); +const int MAC_OS_X_VERSION_10_10 = 101000; - ffi.Pointer startDepolyment( - ffi.Pointer _selectedLocation, - ffi.Pointer _serverName, - ) { - return _startDepolyment(_selectedLocation, _serverName); - } +const int MAC_OS_X_VERSION_10_10_2 = 101002; - late final _startDepolymentPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - > - >('startDepolyment'); - late final _startDepolyment = _startDepolymentPtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int MAC_OS_X_VERSION_10_10_3 = 101003; - ffi.Pointer cancelDepolyment() { - return _cancelDepolyment(); - } +const int MAC_OS_X_VERSION_10_11 = 101100; - late final _cancelDepolymentPtr = - _lookup Function()>>( - 'cancelDepolyment', - ); - late final _cancelDepolyment = _cancelDepolymentPtr - .asFunction Function()>(); +const int MAC_OS_X_VERSION_10_11_2 = 101102; - ffi.Pointer addServerManagerInstance( - ffi.Pointer _ip, - ffi.Pointer _port, - ffi.Pointer _accessToken, - ffi.Pointer _tag, - ) { - return _addServerManagerInstance(_ip, _port, _accessToken, _tag); - } +const int MAC_OS_X_VERSION_10_11_3 = 101103; - late final _addServerManagerInstancePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('addServerManagerInstance'); - late final _addServerManagerInstance = _addServerManagerInstancePtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int MAC_OS_X_VERSION_10_11_4 = 101104; - ffi.Pointer inviteToServerManagerInstance( - ffi.Pointer _ip, - ffi.Pointer _port, - ffi.Pointer _accessToken, - ffi.Pointer _inviteName, - ) { - return _inviteToServerManagerInstance( - _ip, - _port, - _accessToken, - _inviteName, - ); - } +const int MAC_OS_X_VERSION_10_12 = 101200; - late final _inviteToServerManagerInstancePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('inviteToServerManagerInstance'); - late final _inviteToServerManagerInstance = _inviteToServerManagerInstancePtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int MAC_OS_X_VERSION_10_12_1 = 101201; - ffi.Pointer revokeServerManagerInvite( - ffi.Pointer _ip, - ffi.Pointer _port, - ffi.Pointer _accessToken, - ffi.Pointer _inviteName, - ) { - return _revokeServerManagerInvite(_ip, _port, _accessToken, _inviteName); - } +const int MAC_OS_X_VERSION_10_12_2 = 101202; - late final _revokeServerManagerInvitePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - > - >('revokeServerManagerInvite'); - late final _revokeServerManagerInvite = _revokeServerManagerInvitePtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int MAC_OS_X_VERSION_10_12_4 = 101204; - ffi.Pointer addServerBasedOnURLs( - ffi.Pointer _urls, - int _skipCertVerification, - ) { - return _addServerBasedOnURLs(_urls, _skipCertVerification); - } +const int MAC_OS_X_VERSION_10_13 = 101300; - late final _addServerBasedOnURLsPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer, ffi.Int) - > - >('addServerBasedOnURLs'); - late final _addServerBasedOnURLs = _addServerBasedOnURLsPtr - .asFunction Function(ffi.Pointer, int)>(); +const int MAC_OS_X_VERSION_10_13_1 = 101301; - ffi.Pointer setBlockAdsEnabled(int enabled) { - return _setBlockAdsEnabled(enabled); - } +const int MAC_OS_X_VERSION_10_13_2 = 101302; - late final _setBlockAdsEnabledPtr = - _lookup Function(ffi.Int)>>( - 'setBlockAdsEnabled', - ); - late final _setBlockAdsEnabled = _setBlockAdsEnabledPtr - .asFunction Function(int)>(); +const int MAC_OS_X_VERSION_10_13_4 = 101304; - int isBlockAdsEnabled() { - return _isBlockAdsEnabled(); - } +const int MAC_OS_X_VERSION_10_14 = 101400; - late final _isBlockAdsEnabledPtr = - _lookup>('isBlockAdsEnabled'); - late final _isBlockAdsEnabled = _isBlockAdsEnabledPtr - .asFunction(); +const int MAC_OS_X_VERSION_10_14_1 = 101401; - ffi.Pointer setSmartRoutingEnabled(int enabled) { - return _setSmartRoutingEnabled(enabled); - } +const int MAC_OS_X_VERSION_10_14_4 = 101404; - late final _setSmartRoutingEnabledPtr = - _lookup Function(ffi.Int)>>( - 'setSmartRoutingEnabled', - ); - late final _setSmartRoutingEnabled = _setSmartRoutingEnabledPtr - .asFunction Function(int)>(); +const int MAC_OS_X_VERSION_10_14_5 = 101405; - int isSmartRoutingEnabled() { - return _isSmartRoutingEnabled(); - } +const int MAC_OS_X_VERSION_10_14_6 = 101406; - late final _isSmartRoutingEnabledPtr = - _lookup>('isSmartRoutingEnabled'); - late final _isSmartRoutingEnabled = _isSmartRoutingEnabledPtr - .asFunction(); +const int MAC_OS_X_VERSION_10_15 = 101500; - ffi.Pointer getSplitTunnelState() { - return _getSplitTunnelState(); - } +const int MAC_OS_X_VERSION_10_15_1 = 101501; - late final _getSplitTunnelStatePtr = - _lookup Function()>>( - 'getSplitTunnelState', - ); - late final _getSplitTunnelState = _getSplitTunnelStatePtr - .asFunction Function()>(); +const int MAC_OS_X_VERSION_10_15_4 = 101504; - ffi.Pointer getSplitTunnelItems(ffi.Pointer filterTypeC) { - return _getSplitTunnelItems(filterTypeC); - } +const int MAC_OS_X_VERSION_10_16 = 101600; - late final _getSplitTunnelItemsPtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('getSplitTunnelItems'); - late final _getSplitTunnelItems = _getSplitTunnelItemsPtr - .asFunction Function(ffi.Pointer)>(); +const int MAC_OS_VERSION_11_0 = 110000; - ffi.Pointer deletePrivateServerByName(ffi.Pointer _name) { - return _deletePrivateServerByName(_name); - } +const int MAC_OS_VERSION_11_1 = 110100; - late final _deletePrivateServerByNamePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer) - > - >('deletePrivateServerByName'); - late final _deletePrivateServerByName = _deletePrivateServerByNamePtr - .asFunction Function(ffi.Pointer)>(); +const int MAC_OS_VERSION_11_3 = 110300; - ffi.Pointer updatePrivateServerName( - ffi.Pointer _oldName, - ffi.Pointer _newName, - ) { - return _updatePrivateServerName(_oldName, _newName); - } +const int MAC_OS_VERSION_11_4 = 110400; - late final _updatePrivateServerNamePtr = - _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - > - >('updatePrivateServerName'); - late final _updatePrivateServerName = _updatePrivateServerNamePtr - .asFunction< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ) - >(); +const int MAC_OS_VERSION_11_5 = 110500; - ffi.Pointer getEnabledApps() { - return _getEnabledApps(); - } +const int MAC_OS_VERSION_11_6 = 110600; - late final _getEnabledAppsPtr = - _lookup Function()>>( - 'getEnabledApps', - ); - late final _getEnabledApps = _getEnabledAppsPtr - .asFunction Function()>(); -} +const int MAC_OS_VERSION_12_0 = 120000; -typedef va_list = ffi.Pointer; -typedef ptrdiff_t = ffi.LongLong; -typedef Dartptrdiff_t = int; -typedef errno_t = ffi.Int; -typedef Darterrno_t = int; -typedef wint_t = ffi.UnsignedShort; -typedef Dartwint_t = int; -typedef wctype_t = ffi.UnsignedShort; -typedef Dartwctype_t = int; -typedef __time32_t = ffi.Long; -typedef Dart__time32_t = int; -typedef __time64_t = ffi.LongLong; -typedef Dart__time64_t = int; - -final class __crt_locale_data_public extends ffi.Struct { - external ffi.Pointer _locale_pctype; +const int MAC_OS_VERSION_12_1 = 120100; - @ffi.Int() - external int _locale_mb_cur_max; +const int MAC_OS_VERSION_12_2 = 120200; - @ffi.UnsignedInt() - external int _locale_lc_codepage; -} +const int MAC_OS_VERSION_12_3 = 120300; -final class __crt_locale_data extends ffi.Opaque {} +const int MAC_OS_VERSION_12_4 = 120400; -final class __crt_multibyte_data extends ffi.Opaque {} +const int MAC_OS_VERSION_12_5 = 120500; -final class __crt_locale_pointers extends ffi.Struct { - external ffi.Pointer<__crt_locale_data> locinfo; +const int MAC_OS_VERSION_12_6 = 120600; - external ffi.Pointer<__crt_multibyte_data> mbcinfo; -} +const int MAC_OS_VERSION_12_7 = 120700; -typedef _locale_t = ffi.Pointer<__crt_locale_pointers>; +const int MAC_OS_VERSION_13_0 = 130000; -final class _Mbstatet extends ffi.Struct { - @ffi.UnsignedLong() - external int _Wchar; +const int MAC_OS_VERSION_13_1 = 130100; - @ffi.UnsignedShort() - external int _Byte; +const int MAC_OS_VERSION_13_2 = 130200; - @ffi.UnsignedShort() - external int _State; -} +const int MAC_OS_VERSION_13_3 = 130300; -typedef mbstate_t = _Mbstatet; -typedef time_t = __time64_t; -typedef rsize_t = ffi.Size; -typedef Dartrsize_t = int; +const int MAC_OS_VERSION_13_4 = 130400; -final class _GoString_ extends ffi.Struct { - external ffi.Pointer p; +const int MAC_OS_VERSION_13_5 = 130500; - @ptrdiff_t() - external int n; -} +const int MAC_OS_VERSION_13_6 = 130600; -typedef _CoreCrtSecureSearchSortCompareFunctionFunction = - ffi.Int Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ); -typedef Dart_CoreCrtSecureSearchSortCompareFunctionFunction = - int Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ); -typedef _CoreCrtSecureSearchSortCompareFunction = - ffi.Pointer< - ffi.NativeFunction<_CoreCrtSecureSearchSortCompareFunctionFunction> - >; -typedef _CoreCrtNonSecureSearchSortCompareFunctionFunction = - ffi.Int Function(ffi.Pointer, ffi.Pointer); -typedef Dart_CoreCrtNonSecureSearchSortCompareFunctionFunction = - int Function(ffi.Pointer, ffi.Pointer); -typedef _CoreCrtNonSecureSearchSortCompareFunction = - ffi.Pointer< - ffi.NativeFunction<_CoreCrtNonSecureSearchSortCompareFunctionFunction> - >; -typedef _onexit_tFunction = ffi.Int Function(); -typedef Dart_onexit_tFunction = int Function(); -typedef _onexit_t = ffi.Pointer>; -typedef _purecall_handlerFunction = ffi.Void Function(); -typedef Dart_purecall_handlerFunction = void Function(); -typedef _purecall_handler = - ffi.Pointer>; -typedef _invalid_parameter_handlerFunction = - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.UnsignedInt, - ffi.UintPtr, - ); -typedef Dart_invalid_parameter_handlerFunction = - void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, - int, - ); -typedef _invalid_parameter_handler = - ffi.Pointer>; +const int MAC_OS_VERSION_13_7 = 130700; -final class _div_t extends ffi.Struct { - @ffi.Int() - external int quot; +const int MAC_OS_VERSION_14_0 = 140000; - @ffi.Int() - external int rem; -} +const int MAC_OS_VERSION_14_1 = 140100; -typedef div_t = _div_t; +const int MAC_OS_VERSION_14_2 = 140200; -final class _ldiv_t extends ffi.Struct { - @ffi.Long() - external int quot; +const int MAC_OS_VERSION_14_3 = 140300; - @ffi.Long() - external int rem; -} +const int MAC_OS_VERSION_14_4 = 140400; -typedef ldiv_t = _ldiv_t; +const int MAC_OS_VERSION_14_5 = 140500; -final class _lldiv_t extends ffi.Struct { - @ffi.LongLong() - external int quot; +const int MAC_OS_VERSION_14_6 = 140600; - @ffi.LongLong() - external int rem; -} +const int MAC_OS_VERSION_14_7 = 140700; -typedef lldiv_t = _lldiv_t; +const int MAC_OS_VERSION_15_0 = 150000; -final class _LDOUBLE extends ffi.Struct { - @ffi.Array.multi([10]) - external ffi.Array ld; -} +const int MAC_OS_VERSION_15_1 = 150100; -final class _CRT_DOUBLE extends ffi.Struct { - @ffi.Double() - external double x; -} +const int MAC_OS_VERSION_15_2 = 150200; -final class _CRT_FLOAT extends ffi.Struct { - @ffi.Float() - external double f; -} +const int MAC_OS_VERSION_15_3 = 150300; -final class _LONGDOUBLE extends ffi.Opaque {} +const int MAC_OS_VERSION_15_4 = 150400; -final class _LDBL12 extends ffi.Struct { - @ffi.Array.multi([12]) - external ffi.Array ld12; -} +const int MAC_OS_VERSION_15_5 = 150500; -typedef int_least8_t = ffi.SignedChar; -typedef Dartint_least8_t = int; -typedef int_least16_t = ffi.Short; -typedef Dartint_least16_t = int; -typedef int_least32_t = ffi.Int; -typedef Dartint_least32_t = int; -typedef int_least64_t = ffi.LongLong; -typedef Dartint_least64_t = int; -typedef uint_least8_t = ffi.UnsignedChar; -typedef Dartuint_least8_t = int; -typedef uint_least16_t = ffi.UnsignedShort; -typedef Dartuint_least16_t = int; -typedef uint_least32_t = ffi.UnsignedInt; -typedef Dartuint_least32_t = int; -typedef uint_least64_t = ffi.UnsignedLongLong; -typedef Dartuint_least64_t = int; -typedef int_fast8_t = ffi.SignedChar; -typedef Dartint_fast8_t = int; -typedef int_fast16_t = ffi.Int; -typedef Dartint_fast16_t = int; -typedef int_fast32_t = ffi.Int; -typedef Dartint_fast32_t = int; -typedef int_fast64_t = ffi.LongLong; -typedef Dartint_fast64_t = int; -typedef uint_fast8_t = ffi.UnsignedChar; -typedef Dartuint_fast8_t = int; -typedef uint_fast16_t = ffi.UnsignedInt; -typedef Dartuint_fast16_t = int; -typedef uint_fast32_t = ffi.UnsignedInt; -typedef Dartuint_fast32_t = int; -typedef uint_fast64_t = ffi.UnsignedLongLong; -typedef Dartuint_fast64_t = int; -typedef intmax_t = ffi.LongLong; -typedef Dartintmax_t = int; -typedef uintmax_t = ffi.UnsignedLongLong; -typedef Dartuintmax_t = int; -typedef GoInt8 = ffi.SignedChar; -typedef DartGoInt8 = int; -typedef GoUint8 = ffi.UnsignedChar; -typedef DartGoUint8 = int; -typedef GoInt16 = ffi.Short; -typedef DartGoInt16 = int; -typedef GoUint16 = ffi.UnsignedShort; -typedef DartGoUint16 = int; -typedef GoInt32 = ffi.Int; -typedef DartGoInt32 = int; -typedef GoUint32 = ffi.UnsignedInt; -typedef DartGoUint32 = int; -typedef GoInt64 = ffi.LongLong; -typedef DartGoInt64 = int; -typedef GoUint64 = ffi.UnsignedLongLong; -typedef DartGoUint64 = int; -typedef GoInt = GoInt64; -typedef GoUint = GoUint64; -typedef GoUintptr = ffi.Size; -typedef DartGoUintptr = int; -typedef GoFloat32 = ffi.Float; -typedef DartGoFloat32 = double; -typedef GoFloat64 = ffi.Double; -typedef DartGoFloat64 = double; +const int MAC_OS_VERSION_15_6 = 150600; -final class _C_double_complex extends ffi.Struct { - @ffi.Array.multi([2]) - external ffi.Array _Val; -} +const int MAC_OS_VERSION_16_0 = 160000; -final class _C_float_complex extends ffi.Struct { - @ffi.Array.multi([2]) - external ffi.Array _Val; -} +const int MAC_OS_VERSION_26_0 = 260000; -final class _C_ldouble_complex extends ffi.Opaque {} +const int MAC_OS_VERSION_26_1 = 260100; -typedef _Dcomplex = _C_double_complex; -typedef _Fcomplex = _C_float_complex; -typedef _Lcomplex = _C_ldouble_complex; -typedef GoComplex64 = _Fcomplex; -typedef GoComplex128 = _Dcomplex; -typedef GoString = _GoString_; -typedef GoMap = ffi.Pointer; -typedef GoChan = ffi.Pointer; +const int MAC_OS_VERSION_26_2 = 260200; -final class GoInterface extends ffi.Struct { - external ffi.Pointer t; +const int MAC_OS_VERSION_26_3 = 260300; - external ffi.Pointer v; -} +const int MAC_OS_VERSION_26_4 = 260400; -final class GoSlice extends ffi.Struct { - external ffi.Pointer data; +const int MAC_OS_VERSION_26_5 = 260500; - @GoInt() - external int len; +const int __AVAILABILITY_VERSIONS_VERSION_HASH = 93585900; - @GoInt() - external int cap; -} +const String __AVAILABILITY_VERSIONS_VERSION_STRING = 'Local'; -const int _VCRT_COMPILER_PREPROCESSOR = 1; +const String __AVAILABILITY_FILE = 'AvailabilityVersions.h'; -const int _SAL_VERSION = 20; +const int __MAC_OS_X_VERSION_MIN_REQUIRED = 260000; -const int __SAL_H_VERSION = 180000000; +const int __MAC_OS_X_VERSION_MAX_ALLOWED = 260500; -const int _USE_DECLSPECS_FOR_SAL = 0; +const int __ENABLE_LEGACY_MAC_AVAILABILITY = 1; -const int _USE_ATTRIBUTES_FOR_SAL = 0; +const int __DARWIN_NSIG = 32; -const int _CRT_PACKING = 8; +const int NSIG = 32; -const int _VCRUNTIME_DISABLED_WARNINGS = 4514; +const int _ARM_SIGNAL_ = 1; -const int _HAS_EXCEPTIONS = 1; +const int SIGHUP = 1; -const int _WCHAR_T_DEFINED = 1; +const int SIGINT = 2; -const int NULL = 0; +const int SIGQUIT = 3; -const int _HAS_CXX17 = 0; +const int SIGILL = 4; -const int _HAS_CXX20 = 0; +const int SIGTRAP = 5; -const int _HAS_CXX23 = 0; +const int SIGABRT = 6; -const int _HAS_NODISCARD = 1; +const int SIGIOT = 6; -const int _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE = 1; +const int SIGEMT = 7; -const int _CRT_BUILD_DESKTOP_APP = 1; +const int SIGFPE = 8; -const int _UCRT_DISABLED_WARNINGS = 4324; +const int SIGKILL = 9; -const int _ARGMAX = 100; +const int SIGBUS = 10; -const int _TRUNCATE = -1; +const int SIGSEGV = 11; -const int _CRT_INT_MAX = 2147483647; +const int SIGSYS = 12; -const int _CRT_SIZE_MAX = -1; +const int SIGPIPE = 13; -const String __FILEW__ = 'C'; +const int SIGALRM = 14; -const int _CRT_FUNCTIONS_REQUIRED = 1; +const int SIGTERM = 15; -const int _CRT_HAS_CXX17 = 0; +const int SIGURG = 16; -const int _CRT_HAS_C11 = 0; +const int SIGSTOP = 17; -const int _CRT_INTERNAL_NONSTDC_NAMES = 1; +const int SIGTSTP = 18; -const int __STDC_SECURE_LIB__ = 200411; +const int SIGCONT = 19; -const int __GOT_SECURE_LIB__ = 200411; +const int SIGCHLD = 20; -const int __STDC_WANT_SECURE_LIB__ = 1; +const int SIGTTIN = 21; -const int _SECURECRT_FILL_BUFFER_PATTERN = 254; +const int SIGTTOU = 22; -const int _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES = 0; +const int SIGIO = 23; -const int _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT = 0; +const int SIGXCPU = 24; -const int _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES = 1; +const int SIGXFSZ = 25; -const int _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_MEMORY = 0; +const int SIGVTALRM = 26; -const int _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES_MEMORY = 0; +const int SIGPROF = 27; -const int _MAX_ITOSTR_BASE16_COUNT = 9; +const int SIGWINCH = 28; -const int _MAX_ITOSTR_BASE10_COUNT = 12; +const int SIGINFO = 29; -const int _MAX_ITOSTR_BASE8_COUNT = 12; +const int SIGUSR1 = 30; -const int _MAX_ITOSTR_BASE2_COUNT = 33; +const int SIGUSR2 = 31; -const int _MAX_LTOSTR_BASE16_COUNT = 9; +const int __DARWIN_OPAQUE_ARM_THREAD_STATE64 = 0; -const int _MAX_LTOSTR_BASE10_COUNT = 12; +const int SIGEV_NONE = 0; -const int _MAX_LTOSTR_BASE8_COUNT = 12; +const int SIGEV_SIGNAL = 1; -const int _MAX_LTOSTR_BASE2_COUNT = 33; +const int SIGEV_THREAD = 3; -const int _MAX_ULTOSTR_BASE16_COUNT = 9; +const int SIGEV_KEVENT = 4; -const int _MAX_ULTOSTR_BASE10_COUNT = 11; +const int ILL_NOOP = 0; -const int _MAX_ULTOSTR_BASE8_COUNT = 12; +const int ILL_ILLOPC = 1; -const int _MAX_ULTOSTR_BASE2_COUNT = 33; +const int ILL_ILLTRP = 2; -const int _MAX_I64TOSTR_BASE16_COUNT = 17; +const int ILL_PRVOPC = 3; -const int _MAX_I64TOSTR_BASE10_COUNT = 21; +const int ILL_ILLOPN = 4; -const int _MAX_I64TOSTR_BASE8_COUNT = 23; +const int ILL_ILLADR = 5; -const int _MAX_I64TOSTR_BASE2_COUNT = 65; +const int ILL_PRVREG = 6; -const int _MAX_U64TOSTR_BASE16_COUNT = 17; +const int ILL_COPROC = 7; -const int _MAX_U64TOSTR_BASE10_COUNT = 21; +const int ILL_BADSTK = 8; -const int _MAX_U64TOSTR_BASE8_COUNT = 23; +const int FPE_NOOP = 0; -const int _MAX_U64TOSTR_BASE2_COUNT = 65; +const int FPE_FLTDIV = 1; -const int CHAR_BIT = 8; +const int FPE_FLTOVF = 2; -const int SCHAR_MIN = -128; +const int FPE_FLTUND = 3; -const int SCHAR_MAX = 127; +const int FPE_FLTRES = 4; -const int UCHAR_MAX = 255; +const int FPE_FLTINV = 5; -const int CHAR_MIN = -128; +const int FPE_FLTSUB = 6; -const int CHAR_MAX = 127; +const int FPE_INTDIV = 7; -const int MB_LEN_MAX = 5; +const int FPE_INTOVF = 8; -const int SHRT_MIN = -32768; +const int SEGV_NOOP = 0; -const int SHRT_MAX = 32767; +const int SEGV_MAPERR = 1; -const int USHRT_MAX = 65535; +const int SEGV_ACCERR = 2; -const int INT_MIN = -2147483648; +const int BUS_NOOP = 0; -const int INT_MAX = 2147483647; +const int BUS_ADRALN = 1; -const int UINT_MAX = 4294967295; +const int BUS_ADRERR = 2; -const int LONG_MIN = -2147483648; +const int BUS_OBJERR = 3; -const int LONG_MAX = 2147483647; +const int TRAP_BRKPT = 1; -const int ULONG_MAX = 4294967295; +const int TRAP_TRACE = 2; -const int LLONG_MAX = 9223372036854775807; +const int CLD_NOOP = 0; -const int LLONG_MIN = -9223372036854775808; +const int CLD_EXITED = 1; -const int ULLONG_MAX = -1; +const int CLD_KILLED = 2; -const int _I8_MIN = -128; +const int CLD_DUMPED = 3; -const int _I8_MAX = 127; +const int CLD_TRAPPED = 4; -const int _UI8_MAX = 255; +const int CLD_STOPPED = 5; -const int _I16_MIN = -32768; +const int CLD_CONTINUED = 6; -const int _I16_MAX = 32767; +const int POLL_IN = 1; -const int _UI16_MAX = 65535; +const int POLL_OUT = 2; -const int _I32_MIN = -2147483648; +const int POLL_MSG = 3; -const int _I32_MAX = 2147483647; +const int POLL_ERR = 4; -const int _UI32_MAX = 4294967295; +const int POLL_PRI = 5; -const int _I64_MIN = -9223372036854775808; +const int POLL_HUP = 6; -const int _I64_MAX = 9223372036854775807; +const int SA_ONSTACK = 1; -const int _UI64_MAX = -1; +const int SA_RESTART = 2; -const int SIZE_MAX = -1; +const int SA_RESETHAND = 4; -const int RSIZE_MAX = 9223372036854775807; +const int SA_NOCLDSTOP = 8; -const int EXIT_SUCCESS = 0; +const int SA_NODEFER = 16; -const int EXIT_FAILURE = 1; +const int SA_NOCLDWAIT = 32; -const int _WRITE_ABORT_MSG = 1; +const int SA_SIGINFO = 64; -const int _CALL_REPORTFAULT = 2; +const int SA_USERTRAMP = 256; -const int _OUT_TO_DEFAULT = 0; +const int SA_64REGSET = 512; -const int _OUT_TO_STDERR = 1; +const int SA_USERSPACE_MASK = 127; -const int _OUT_TO_MSGBOX = 2; +const int SIG_BLOCK = 1; -const int _REPORT_ERRMODE = 3; +const int SIG_UNBLOCK = 2; -const int RAND_MAX = 32767; +const int SIG_SETMASK = 3; -const int _CVTBUFSIZE = 349; +const int SI_USER = 65537; -const int _MAX_PATH = 260; +const int SI_QUEUE = 65538; -const int _MAX_DRIVE = 3; +const int SI_TIMER = 65539; -const int _MAX_DIR = 256; +const int SI_ASYNCIO = 65540; -const int _MAX_FNAME = 256; +const int SI_MESGQ = 65541; -const int _MAX_EXT = 256; +const int SS_ONSTACK = 1; -const int _MAX_ENV = 32767; +const int SS_DISABLE = 4; -const int INT8_MIN = -128; +const int MINSIGSTKSZ = 32768; -const int INT16_MIN = -32768; +const int SIGSTKSZ = 131072; -const int INT32_MIN = -2147483648; +const int SV_ONSTACK = 1; -const int INT64_MIN = -9223372036854775808; +const int SV_INTERRUPT = 2; + +const int SV_RESETHAND = 4; + +const int SV_NODEFER = 16; + +const int SV_NOCLDSTOP = 8; + +const int SV_SIGINFO = 64; + +const int __WORDSIZE = 64; const int INT8_MAX = 127; @@ -6951,6 +6235,14 @@ const int INT32_MAX = 2147483647; const int INT64_MAX = 9223372036854775807; +const int INT8_MIN = -128; + +const int INT16_MIN = -32768; + +const int INT32_MIN = -2147483648; + +const int INT64_MIN = -9223372036854775808; + const int UINT8_MAX = 255; const int UINT16_MAX = 65535; @@ -6985,7 +6277,7 @@ const int UINT_LEAST64_MAX = -1; const int INT_FAST8_MIN = -128; -const int INT_FAST16_MIN = -2147483648; +const int INT_FAST16_MIN = -32768; const int INT_FAST32_MIN = -2147483648; @@ -6993,7 +6285,7 @@ const int INT_FAST64_MIN = -9223372036854775808; const int INT_FAST8_MAX = 127; -const int INT_FAST16_MAX = 2147483647; +const int INT_FAST16_MAX = 32767; const int INT_FAST32_MAX = 2147483647; @@ -7001,36 +6293,276 @@ const int INT_FAST64_MAX = 9223372036854775807; const int UINT_FAST8_MAX = 255; -const int UINT_FAST16_MAX = 4294967295; +const int UINT_FAST16_MAX = 65535; const int UINT_FAST32_MAX = 4294967295; const int UINT_FAST64_MAX = -1; -const int INTPTR_MIN = -9223372036854775808; - const int INTPTR_MAX = 9223372036854775807; -const int UINTPTR_MAX = -1; +const int INTPTR_MIN = -9223372036854775808; -const int INTMAX_MIN = -9223372036854775808; +const int UINTPTR_MAX = -1; const int INTMAX_MAX = 9223372036854775807; const int UINTMAX_MAX = -1; +const int INTMAX_MIN = -9223372036854775808; + const int PTRDIFF_MIN = -9223372036854775808; const int PTRDIFF_MAX = 9223372036854775807; +const int SIZE_MAX = -1; + +const int RSIZE_MAX = 9223372036854775807; + +const int WCHAR_MAX = 2147483647; + +const int WCHAR_MIN = -2147483648; + +const int WINT_MIN = -2147483648; + +const int WINT_MAX = 2147483647; + const int SIG_ATOMIC_MIN = -2147483648; const int SIG_ATOMIC_MAX = 2147483647; -const int WCHAR_MIN = 0; +const int PRIO_PROCESS = 0; + +const int PRIO_PGRP = 1; + +const int PRIO_USER = 2; + +const int PRIO_DARWIN_THREAD = 3; + +const int PRIO_DARWIN_PROCESS = 4; + +const int PRIO_MIN = -20; + +const int PRIO_MAX = 20; + +const int PRIO_DARWIN_BG = 4096; + +const int PRIO_DARWIN_NONUI = 4097; + +const int RUSAGE_SELF = 0; + +const int RUSAGE_CHILDREN = -1; + +const int RUSAGE_INFO_V0 = 0; + +const int RUSAGE_INFO_V1 = 1; + +const int RUSAGE_INFO_V2 = 2; + +const int RUSAGE_INFO_V3 = 3; + +const int RUSAGE_INFO_V4 = 4; + +const int RUSAGE_INFO_V5 = 5; + +const int RUSAGE_INFO_V6 = 6; + +const int RUSAGE_INFO_CURRENT = 6; + +const int RU_PROC_RUNS_RESLIDE = 1; + +const int RLIM_INFINITY = 9223372036854775807; + +const int RLIM_SAVED_MAX = 9223372036854775807; + +const int RLIM_SAVED_CUR = 9223372036854775807; + +const int RLIMIT_CPU = 0; + +const int RLIMIT_FSIZE = 1; + +const int RLIMIT_DATA = 2; + +const int RLIMIT_STACK = 3; + +const int RLIMIT_CORE = 4; + +const int RLIMIT_AS = 5; + +const int RLIMIT_RSS = 5; + +const int RLIMIT_MEMLOCK = 6; + +const int RLIMIT_NPROC = 7; + +const int RLIMIT_NOFILE = 8; + +const int RLIM_NLIMITS = 9; + +const int _RLIMIT_POSIX_FLAG = 4096; + +const int RLIMIT_WAKEUPS_MONITOR = 1; + +const int RLIMIT_CPU_USAGE_MONITOR = 2; + +const int RLIMIT_THREAD_CPULIMITS = 3; + +const int RLIMIT_FOOTPRINT_INTERVAL = 4; + +const int WAKEMON_ENABLE = 1; + +const int WAKEMON_DISABLE = 2; + +const int WAKEMON_GET_PARAMS = 4; + +const int WAKEMON_SET_DEFAULTS = 8; + +const int WAKEMON_MAKE_FATAL = 16; + +const int CPUMON_MAKE_FATAL = 4096; + +const int FOOTPRINT_INTERVAL_RESET = 1; + +const int IOPOL_TYPE_DISK = 0; + +const int IOPOL_TYPE_VFS_ATIME_UPDATES = 2; + +const int IOPOL_TYPE_VFS_MATERIALIZE_DATALESS_FILES = 3; + +const int IOPOL_TYPE_VFS_STATFS_NO_DATA_VOLUME = 4; + +const int IOPOL_TYPE_VFS_TRIGGER_RESOLVE = 5; + +const int IOPOL_TYPE_VFS_IGNORE_CONTENT_PROTECTION = 6; + +const int IOPOL_TYPE_VFS_IGNORE_PERMISSIONS = 7; -const int WCHAR_MAX = 65535; +const int IOPOL_TYPE_VFS_SKIP_MTIME_UPDATE = 8; + +const int IOPOL_TYPE_VFS_ALLOW_LOW_SPACE_WRITES = 9; + +const int IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY = 10; + +const int IOPOL_TYPE_VFS_ENTITLED_RESERVE_ACCESS = 14; + +const int IOPOL_SCOPE_PROCESS = 0; + +const int IOPOL_SCOPE_THREAD = 1; + +const int IOPOL_SCOPE_DARWIN_BG = 2; + +const int IOPOL_DEFAULT = 0; + +const int IOPOL_IMPORTANT = 1; + +const int IOPOL_PASSIVE = 2; + +const int IOPOL_THROTTLE = 3; + +const int IOPOL_UTILITY = 4; + +const int IOPOL_STANDARD = 5; + +const int IOPOL_APPLICATION = 5; + +const int IOPOL_NORMAL = 1; + +const int IOPOL_ATIME_UPDATES_DEFAULT = 0; + +const int IOPOL_ATIME_UPDATES_OFF = 1; + +const int IOPOL_MATERIALIZE_DATALESS_FILES_DEFAULT = 0; + +const int IOPOL_MATERIALIZE_DATALESS_FILES_OFF = 1; + +const int IOPOL_MATERIALIZE_DATALESS_FILES_ON = 2; + +const int IOPOL_MATERIALIZE_DATALESS_FILES_ORIG = 4; + +const int IOPOL_MATERIALIZE_DATALESS_FILES_BASIC_MASK = 3; + +const int IOPOL_VFS_STATFS_NO_DATA_VOLUME_DEFAULT = 0; + +const int IOPOL_VFS_STATFS_FORCE_NO_DATA_VOLUME = 1; + +const int IOPOL_VFS_TRIGGER_RESOLVE_DEFAULT = 0; + +const int IOPOL_VFS_TRIGGER_RESOLVE_OFF = 1; + +const int IOPOL_VFS_CONTENT_PROTECTION_DEFAULT = 0; + +const int IOPOL_VFS_CONTENT_PROTECTION_IGNORE = 1; + +const int IOPOL_VFS_IGNORE_PERMISSIONS_OFF = 0; + +const int IOPOL_VFS_IGNORE_PERMISSIONS_ON = 1; + +const int IOPOL_VFS_SKIP_MTIME_UPDATE_OFF = 0; + +const int IOPOL_VFS_SKIP_MTIME_UPDATE_ON = 1; + +const int IOPOL_VFS_SKIP_MTIME_UPDATE_IGNORE = 2; + +const int IOPOL_VFS_ALLOW_LOW_SPACE_WRITES_OFF = 0; + +const int IOPOL_VFS_ALLOW_LOW_SPACE_WRITES_ON = 1; + +const int IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_DEFAULT = 0; + +const int IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_ON = 1; + +const int IOPOL_VFS_NOCACHE_WRITE_FS_BLKSIZE_DEFAULT = 0; + +const int IOPOL_VFS_NOCACHE_WRITE_FS_BLKSIZE_ON = 1; + +const int IOPOL_VFS_ENTITLED_RESERVE_ACCESS_OFF = 0; + +const int IOPOL_VFS_ENTITLED_RESERVE_ACCESS_ON = 1; + +const int WNOHANG = 1; + +const int WUNTRACED = 2; + +const int WCOREFLAG = 128; + +const int _WSTOPPED = 127; + +const int WEXITED = 4; + +const int WSTOPPED = 8; + +const int WCONTINUED = 16; + +const int WNOWAIT = 32; + +const int WAIT_ANY = -1; + +const int WAIT_MYPGRP = 0; + +const int _QUAD_HIGHWORD = 1; + +const int _QUAD_LOWWORD = 0; + +const int __DARWIN_LITTLE_ENDIAN = 1234; + +const int __DARWIN_BIG_ENDIAN = 4321; + +const int __DARWIN_PDP_ENDIAN = 3412; + +const int LITTLE_ENDIAN = 1234; + +const int BIG_ENDIAN = 4321; + +const int PDP_ENDIAN = 3412; + +const int __DARWIN_BYTE_ORDER = 1234; + +const int BYTE_ORDER = 1234; + +const int EXIT_FAILURE = 1; + +const int EXIT_SUCCESS = 0; -const int WINT_MIN = 0; +const int RAND_MAX = 2147483647; -const int WINT_MAX = 65535; +const int _MALLOC_TYPE_MALLOC_BACKDEPLOY_PUBLIC = 1; diff --git a/lib/lantern/lantern_platform_service.dart b/lib/lantern/lantern_platform_service.dart index 29fbec2b52..b9bf3e23c5 100644 --- a/lib/lantern/lantern_platform_service.dart +++ b/lib/lantern/lantern_platform_service.dart @@ -757,6 +757,7 @@ class LanternPlatformService implements LanternCoreService { required String planId, required String email, required String idempotencyKey, + String couponCode = '', }) async { if (PlatformUtils.isIOS) { throw UnimplementedError("This not supported on IOS"); @@ -768,6 +769,7 @@ class LanternPlatformService implements LanternCoreService { 'planId': planId, 'email': email, 'idempotencyKey': idempotencyKey, + 'couponCode': couponCode, }); if (redirectUrl == null || redirectUrl.isEmpty) { return Left(Exception('No payment redirect URL returned').toFailure()); diff --git a/lib/lantern/lantern_service.dart b/lib/lantern/lantern_service.dart index e3c72b99dd..6d59e84a34 100644 --- a/lib/lantern/lantern_service.dart +++ b/lib/lantern/lantern_service.dart @@ -387,6 +387,7 @@ class LanternService implements LanternCoreService { required String planId, required String email, required String idempotencyKey, + String couponCode = '', }) { if (PlatformUtils.isFFISupported) { return _ffiService.paymentRedirect( @@ -394,6 +395,7 @@ class LanternService implements LanternCoreService { planId: planId, email: email, idempotencyKey: idempotencyKey, + couponCode: couponCode, ); } return _platformService.paymentRedirect( @@ -401,6 +403,7 @@ class LanternService implements LanternCoreService { planId: planId, email: email, idempotencyKey: idempotencyKey, + couponCode: couponCode, ); } From 7b1fa9c3606a7bc097a5d1507e4c2021d7f2a27a Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 8 Jul 2026 19:38:28 +0530 Subject: [PATCH 12/22] Added support for apple affiliate flow --- assets/locales/en.po | 2 +- lib/core/services/app_purchase.dart | 839 ++++++------------ .../purchase/pending_purchase_store.dart | 144 +++ .../purchase/purchase_acknowledger.dart | 271 ++++++ lib/features/auth/choose_payment_method.dart | 4 +- lib/features/plans/feature_list.dart | 4 - lib/features/plans/plans.dart | 4 +- macos/Runner/Handlers/MethodHandler.swift | 25 +- .../purchase/pending_purchase_store_test.dart | 156 ++++ .../purchase/purchase_acknowledger_test.dart | 204 +++++ 10 files changed, 1067 insertions(+), 586 deletions(-) create mode 100644 lib/core/services/purchase/pending_purchase_store.dart create mode 100644 lib/core/services/purchase/purchase_acknowledger.dart create mode 100644 test/core/services/purchase/pending_purchase_store_test.dart create mode 100644 test/core/services/purchase/purchase_acknowledger_test.dart diff --git a/assets/locales/en.po b/assets/locales/en.po index 909d597630..a28dfb6554 100644 --- a/assets/locales/en.po +++ b/assets/locales/en.po @@ -1447,7 +1447,7 @@ msgid "daily_data_cap_reached_message" msgstr "Speed reduced to 128 kb/sec - Resets at %s." msgid "subscription_renewal_info" -msgstr "Payment is charged to your Apple ID at purchase. Subscriptions renew automatically unless canceled at least 24 hours before the end of the current period. Manage or cancel in App Store Settings." +msgstr "Payment is charged to your Apple ID at purchase. Subscriptions renew automatically unless canceled at least 24 hours before the end of the current period." msgid "failed_to_update_routing_mode" msgstr "Failed to update routing mode. Please try again." diff --git a/lib/core/services/app_purchase.dart b/lib/core/services/app_purchase.dart index 72c60b71b7..bfd028a924 100644 --- a/lib/core/services/app_purchase.dart +++ b/lib/core/services/app_purchase.dart @@ -5,6 +5,8 @@ import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:lantern/core/common/common.dart'; import 'package:lantern/core/models/user.dart'; +import 'package:lantern/core/services/purchase/pending_purchase_store.dart'; +import 'package:lantern/core/services/purchase/purchase_acknowledger.dart'; import 'package:lantern/core/utils/country_code.dart'; import 'package:lantern/lantern/lantern_platform_service.dart'; @@ -14,53 +16,80 @@ import 'local_storage_service.dart'; typedef PaymentSuccessCallback = void Function(PurchaseDetails purchase); typedef PaymentErrorCallback = void Function(String error); +/// Mutable state of the in-progress purchase/restore flow, grouped so the +/// callbacks and pending selections have a single home and reset point. +class _PurchaseSession { + PaymentSuccessCallback? onSuccess; + PaymentErrorCallback? onError; + + /// The exact plan id the user chose (e.g. "1y-usd-10"). + String? pendingPlanId; + + /// Affiliate code applied at purchase start; forwarded to the backend for + /// attribution. Empty when none. + String pendingCouponCode = ''; + + bool isRestoreFlow = false; + + /// Set once a restored receipt arrives, so iOS can detect "nothing to + /// restore" (StoreKit emits no empty-stream event the way Play Billing does). + bool restoreReceivedAny = false; +} + class AppPurchase { - static const _pendingPurchasePlansKey = 'pending_purchase_plans_json'; - static const _pendingPurchaseCouponsKey = 'pending_purchase_coupons_json'; - static const _productPlanKeyPrefix = 'product:'; - static const _transactionPlanKeyPrefix = 'transaction:'; - static const _ackRetryDelays = [ - Duration(seconds: 5), - Duration(seconds: 15), - Duration(seconds: 45), - Duration(minutes: 2), - Duration(minutes: 5), - ]; + /// [inAppPurchase], [pendingStore] and [acknowledger] are injectable for + /// tests; production uses the store plugin singleton and the service locator. + AppPurchase({ + InAppPurchase? inAppPurchase, + PendingPurchaseStore? pendingStore, + PurchaseAcknowledger? acknowledger, + }) : _inAppPurchase = inAppPurchase ?? InAppPurchase.instance { + _pendingStore = + pendingStore ?? PendingPurchaseStore(() => sl()); + _acknowledger = + acknowledger ?? + PurchaseAcknowledger( + acknowledgeReceipt: + ({ + required String purchaseToken, + required String planId, + required String couponCode, + }) => sl().acknowledgeInAppPurchase( + purchaseToken: purchaseToken, + planId: planId, + couponCode: couponCode, + ), + resolvePlanId: _resolvePlanId, + resolveCouponCode: _resolveCouponCode, + retryKeyFor: (purchase) => _pendingStore.retryKeyFor(purchase), + isAlreadyActive: _checkIfAlreadyPurchased, + onAcknowledged: _onAcknowledged, + ); + } + + final InAppPurchase _inAppPurchase; + late final PendingPurchaseStore _pendingStore; + late final PurchaseAcknowledger _acknowledger; - final InAppPurchase _inAppPurchase = InAppPurchase.instance; StreamSubscription>? _subscription; final List _subscriptionSku = []; final List _subscriptionIds = ['1m_sub', '1y_sub']; - final Set _acknowledgeInFlight = {}; - final Map _ackRetryAttempts = {}; - final Map _ackRetryTimers = {}; - PaymentSuccessCallback? _onSuccess; - PaymentErrorCallback? _onError; + // iOS-only affiliate SKUs. Each mirrors a base plan but carries an + // introductory offer configured in App Store Connect, and is only ever + // surfaced once an affiliate/referral code is applied. StoreKit has no + // per-offer concept, so the discount has to live on a dedicated product; + // Android instead exposes the discount as a Play Console offer on the base + // product, so these are never queried there. + static const List _iosAffiliateIds = [ + '1m_sub_affiliate', + '1y_sub_affiliate', + ]; - // Tracks whether we have real product details loaded bool _productsLoaded = false; Completer? _productsLoadedCompleter; - // Track what plan the user selected - String? _pendingPlanId; - - // The affiliate/referral code applied when the purchase started, if any. - // Forwarded to the backend at acknowledgment so the sale is attributed to - // the affiliate. Empty when no code is applied. Persisted per-purchase - // (alongside the plan id) so background acknowledge retries and store - // re-delivery after a restart keep the attribution. - String _pendingCouponCode = ''; - - // True while a restore flow is in progress; restored receipts should be - // acknowledged so the backend can reassociate the user, even when the - // device has no active subscription cached locally. - bool _isRestoreFlow = false; - - // Set true when a restored receipt is delivered through the stream during - // a restore flow. Used on iOS to detect "no purchases" (StoreKit doesn't - // emit an empty stream event the way Play Billing does). - bool _restoreReceivedAny = false; + final _PurchaseSession _session = _PurchaseSession(); /// Starts listening for store purchase updates without fetching product /// details. Product lookup stays user-initiated to keep StoreKit out of the @@ -134,6 +163,15 @@ class AppPurchase { return ready; } + /// The product IDs to query from the store. iOS additionally queries the + /// dedicated affiliate SKUs (see [_iosAffiliateIds]); [_selectSkus] then + /// keeps whichever set matches the requested mode. Android never queries + /// them because its discount is an offer on the base product. + Set get _productIdsToQuery => { + ..._subscriptionIds, + if (Platform.isIOS) ..._iosAffiliateIds, + }; + /// Loads the subscription SKUs to purchase from. /// /// By default only base plans are kept ([includeOffers] false), so the first @@ -164,29 +202,9 @@ class AppPurchase { ); final response = await _inAppPurchase.queryProductDetails( - _subscriptionIds.toSet(), + _productIdsToQuery, ); - // Debug: dump exactly what the store returned, including per-offer - // basePlanId/offerId, so we can see whether Play is actually handing - // back an offer (vs our filter dropping it or the account being - // ineligible). Remove once offer visibility is confirmed. - for (final p in response.productDetails) { - if (p is GooglePlayProductDetails) { - final index = p.subscriptionIndex; - final offers = p.productDetails.subscriptionOfferDetails; - final offer = (index != null && offers != null && index < offers.length) - ? offers[index] - : null; - appLogger.info( - '[AppPurchase] SKU productId=${p.id} ' - 'basePlanId=${offer?.basePlanId} offerId=${offer?.offerId}', - ); - } else { - appLogger.info('[AppPurchase] SKU productId=${p.id} (non-Play)'); - } - } - if (response.error != null) { appLogger.error( '[AppPurchase] Error fetching subscriptions: ${response.error}', @@ -281,17 +299,17 @@ class AppPurchase { required void Function(String error) onError, String couponCode = '', }) async { - _onSuccess = onSuccess; - _onError = onError; + _session.onSuccess = onSuccess; + _session.onError = onError; // Store the exact plan id user chose (ex: "1y-usd-10") - _pendingPlanId = plan; + _session.pendingPlanId = plan; // Capture the applied affiliate/referral code so it can be sent with the // acknowledgment. Reset to '' when none so a prior purchase's code never // leaks into an unrelated one. - _pendingCouponCode = couponCode; + _session.pendingCouponCode = couponCode; if (!await _ensurePurchaseStreamReady()) { - _onError?.call( + _session.onError?.call( "Unable to access in-app purchases. Check your network and try again.", ); return; @@ -300,7 +318,7 @@ class AppPurchase { try { await _waitForProducts(); } catch (_) { - _onError?.call( + _session.onError?.call( "Unable to load in-app purchase products. Check your network and try again.", ); return; @@ -308,7 +326,7 @@ class AppPurchase { final product = _normalizePlan(plan); if (product == null) { - _onError?.call("Invalid plan: $plan"); + _session.onError?.call("Invalid plan: $plan"); return; } @@ -324,20 +342,23 @@ class AppPurchase { try { appLogger.info( - '[AppPurchase] Initiating purchase for product: ${product.id} with pendingPlanId: $_pendingPlanId', + '[AppPurchase] Initiating purchase for product: ${product.id} with pendingPlanId: ${_session.pendingPlanId}', + ); + await _pendingStore.rememberForProduct( + product.id, + planId: plan, + couponCode: couponCode, ); - await _rememberPendingPlanForProduct(product.id, plan); - await _rememberPendingCouponForProduct(product.id, couponCode); final started = await _inAppPurchase.buyNonConsumable( purchaseParam: purchaseParam, ); if (!started) { - await _forgetPendingPlanForProduct(product.id); - _onError?.call("Failed to initiate purchase flow."); + await _pendingStore.forgetProduct(product.id); + _session.onError?.call("Failed to initiate purchase flow."); } } catch (e) { - await _forgetPendingPlanForProduct(product.id); - _onError?.call("Error starting subscription: $e"); + await _pendingStore.forgetProduct(product.id); + _session.onError?.call("Error starting subscription: $e"); } } @@ -352,16 +373,16 @@ class AppPurchase { required PaymentSuccessCallback onSuccess, required PaymentErrorCallback onError, }) async { - _onSuccess = onSuccess; - _onError = onError; - _isRestoreFlow = true; - _restoreReceivedAny = false; - _pendingPlanId = null; - _pendingCouponCode = ''; + _session.onSuccess = onSuccess; + _session.onError = onError; + _session.isRestoreFlow = true; + _session.restoreReceivedAny = false; + _session.pendingPlanId = null; + _session.pendingCouponCode = ''; if (!await _ensurePurchaseStreamReady()) { - _isRestoreFlow = false; - final onError = _onError; + _session.isRestoreFlow = false; + final onError = _session.onError; clearCallbacks(); onError?.call( "Unable to access in-app purchases. Check your network and try again.", @@ -377,10 +398,10 @@ class AppPurchase { // purchases to restore, so the only signal "nothing to restore" is // the absence of a stream event within a reasonable window. Future.delayed(const Duration(seconds: 10), () { - if (_isRestoreFlow && !_restoreReceivedAny) { + if (_session.isRestoreFlow && !_session.restoreReceivedAny) { appLogger.info('[AppPurchase] iOS restore: no purchases delivered'); - _isRestoreFlow = false; - final onError = _onError; + _session.isRestoreFlow = false; + final onError = _session.onError; clearCallbacks(); onError?.call('No previous purchases found to restore.'); } @@ -388,8 +409,8 @@ class AppPurchase { } } catch (e, st) { appLogger.error('[AppPurchase] Error restoring purchases', e, st); - _isRestoreFlow = false; - final onError = _onError; + _session.isRestoreFlow = false; + final onError = _session.onError; clearCallbacks(); onError?.call('Error restoring purchases: $e'); } @@ -399,12 +420,12 @@ class AppPurchase { appLogger.info( '[AppPurchase] Received purchase updates: ${purchases.length}', ); - if (_isRestoreFlow && purchases.isEmpty) { + if (_session.isRestoreFlow && purchases.isEmpty) { appLogger.info( '[AppPurchase] Restore flow: purchase stream emitted empty list', ); - _isRestoreFlow = false; - final onError = _onError; + _session.isRestoreFlow = false; + final onError = _session.onError; clearCallbacks(); onError?.call('No previous purchases found to restore.'); return; @@ -412,9 +433,9 @@ class AppPurchase { /// During restore, if more than one restored receipt comes back, batch /// them: pick one to surface and finalize the rest. Otherwise _finalize - /// would clear _isRestoreFlow after the first item and the second + /// would clear isRestoreFlow after the first item and the second /// iteration would fall into the regular acknowledge path. - if (_isRestoreFlow && purchases.length > 1) { + if (_session.isRestoreFlow && purchases.length > 1) { final restored = purchases .where( (p) => @@ -434,7 +455,7 @@ class AppPurchase { } /// Picks the latest restored receipt, finalizes every restored receipt - /// with the store, and fires `_onSuccess` once. + /// with the store, and fires `onSuccess` once. Future _handleRestoreBatch(List restored) async { ///Pick the latest purchase restored.sort((a, b) { @@ -448,10 +469,10 @@ class AppPurchase { '[AppPurchase] Restore batch: ${restored.length} purchases, choosing ${chosen.productID}', ); - _restoreReceivedAny = true; + _session.restoreReceivedAny = true; // Complete each receipt inline (don't call _finalize — its `finally` - // clears _isRestoreFlow, which would mis-route any re-entrant stream + // clears isRestoreFlow, which would mis-route any re-entrant stream // emissions through the regular acknowledge path mid-batch and fire // a stray error before our success callback. for (final purchase in restored) { @@ -464,9 +485,9 @@ class AppPurchase { } } - final onSuccess = _onSuccess; - _isRestoreFlow = false; - _pendingPlanId = null; + final onSuccess = _session.onSuccess; + _session.isRestoreFlow = false; + _session.pendingPlanId = null; clearCallbacks(); onSuccess?.call(chosen); } @@ -476,103 +497,125 @@ class AppPurchase { '[AppPurchase] Handling purchase: ${purchaseDetails.productID} with status: ${purchaseDetails.status}', ); try { - final status = purchaseDetails.status; - if (status == PurchaseStatus.error) { - /// Error occurred during purchase - appLogger.error('Purchase error: ${purchaseDetails.error}'); - final errorMessage = purchaseDetails.error?.message ?? "Unknown error"; - await _forgetPendingPlan(purchaseDetails); - _pendingPlanId = null; - - /// Invoke error callback - _onError?.call(errorMessage); - return; - } - if (status == PurchaseStatus.canceled) { - // `canceled` is not necessarily a real user cancel — the - // in_app_purchase plugin also maps several silent Google Play - // rejections (offer ineligibility, missing payment method, region - // mismatch, billing-service hiccups) to the same status. Without - // capturing the underlying error we can't distinguish those from a - // user who actually X'd the dialog, which is the difference between - // "expected" and "a bug to fix." For Android, `error.details` - // typically contains a Map with the raw BillingClient - // `response_code` and `debug_message`. - appLogger.info( - '[AppPurchase] Purchase canceled: productID=${purchaseDetails.productID}' - ' errorCode=${purchaseDetails.error?.code}' - ' message=${purchaseDetails.error?.message}' - ' details=${purchaseDetails.error?.details}', - ); - await _forgetPendingPlan(purchaseDetails); - _pendingPlanId = null; - _onError?.call("Purchase canceled"); - return; - } - if (status == PurchaseStatus.pending) { - /// Purchase is pending (e.g. deferred payment method on Android). - /// Dismiss loading and inform the user — the purchase will complete - /// asynchronously when the payment is confirmed. - appLogger.info( - '[AppPurchase] Purchase is pending: ${purchaseDetails.productID}', - ); - _onError?.call( - "Purchase is pending. You will be notified when it completes.", - ); - return; - } - if (status == PurchaseStatus.purchased || - status == PurchaseStatus.restored) { - /// During an explicit restore flow, skip the backend acknowledge call - if (_isRestoreFlow) { - appLogger.info( - '[AppPurchase] Found restore purchase calling success', - ); - _restoreReceivedAny = true; - await _finalize(purchaseDetails); - final onSuccess = _onSuccess; - clearCallbacks(); - onSuccess?.call(purchaseDetails); - return; - } - - /// Apple sends purchase updates for previously purchased items when the app starts. - /// This check prevents processing the same subscription multiple times. - if (await _checkIfAlreadyPurchased()) { - appLogger.info( - '[AppPurchase] User has already purchased the subscription. Finalizing purchase without processing.', - ); - await _forgetPendingPlan(purchaseDetails); - await _finalize(purchaseDetails); - _onError?.call('You have already purchased this subscription.'); - return; - } - - try { - appLogger.info( - '[AppPurchase] Purchase successful: ${purchaseDetails.productID}', - ); - final planId = await _resolvePlanId(purchaseDetails); - final couponCode = await _resolveCouponCode(purchaseDetails); - await _rememberPendingPlanForPurchase(purchaseDetails, planId); - await _rememberPendingCouponForPurchase(purchaseDetails, couponCode); - await _acknowledgePurchase( - purchaseDetails, - planId: planId, - couponCode: couponCode, - ); - } catch (e, st) { - await _handleAcknowledgeFailure(purchaseDetails, e, stackTrace: st); - } - return; + switch (purchaseDetails.status) { + case PurchaseStatus.error: + await _handlePurchaseError(purchaseDetails); + case PurchaseStatus.canceled: + await _handlePurchaseCanceled(purchaseDetails); + case PurchaseStatus.pending: + _handlePurchasePending(purchaseDetails); + case PurchaseStatus.purchased: + case PurchaseStatus.restored: + await _handleCompletedPurchase(purchaseDetails); } } catch (e) { appLogger.error('[AppPurchase] Error handling purchase: $e', e); - _onError?.call(e.toString()); + _session.onError?.call(e.toString()); } } - // Separate helper to ensure the Store is cleared + Future _handlePurchaseError(PurchaseDetails purchase) async { + appLogger.error('Purchase error: ${purchase.error}'); + final errorMessage = purchase.error?.message ?? 'Unknown error'; + await _pendingStore.forgetPurchase(purchase); + _session.pendingPlanId = null; + _session.onError?.call(errorMessage); + } + + Future _handlePurchaseCanceled(PurchaseDetails purchase) async { + // `canceled` is not necessarily a real user cancel — the in_app_purchase + // plugin also maps several silent Google Play rejections (offer + // ineligibility, missing payment method, region mismatch, billing-service + // hiccups) to the same status. Without capturing the underlying error we + // can't distinguish those from a user who actually X'd the dialog, which is + // the difference between "expected" and "a bug to fix." For Android, + // `error.details` typically contains a Map with the raw BillingClient + // `response_code` and `debug_message`. + appLogger.info( + '[AppPurchase] Purchase canceled: productID=${purchase.productID}' + ' errorCode=${purchase.error?.code}' + ' message=${purchase.error?.message}' + ' details=${purchase.error?.details}', + ); + await _pendingStore.forgetPurchase(purchase); + _session.pendingPlanId = null; + _session.onError?.call('Purchase canceled'); + } + + void _handlePurchasePending(PurchaseDetails purchase) { + // Deferred payment method (e.g. on Android): the purchase completes + // asynchronously when the payment is confirmed; just inform the user. + appLogger.info('[AppPurchase] Purchase is pending: ${purchase.productID}'); + _session.onError?.call( + 'Purchase is pending. You will be notified when it completes.', + ); + } + + /// Handles a `purchased`/`restored` receipt: during an explicit restore just + /// finalize and report success; otherwise verify it with the backend (unless + /// the account is already active) through the acknowledger. + Future _handleCompletedPurchase(PurchaseDetails purchase) async { + if (_session.isRestoreFlow) { + appLogger.info('[AppPurchase] Found restore purchase calling success'); + _session.restoreReceivedAny = true; + await _finalize(purchase); + final onSuccess = _session.onSuccess; + clearCallbacks(); + onSuccess?.call(purchase); + return; + } + + // Apple re-delivers previously purchased items at launch; trust fresh + // backend state so the same subscription isn't processed twice. + if (await _checkIfAlreadyPurchased()) { + appLogger.info( + '[AppPurchase] User has already purchased the subscription. ' + 'Finalizing purchase without processing.', + ); + await _pendingStore.forgetPurchase(purchase); + await _finalize(purchase); + _session.onError?.call('You have already purchased this subscription.'); + return; + } + + appLogger.info('[AppPurchase] Purchase successful: ${purchase.productID}'); + final planId = await _resolvePlanId(purchase); + final couponCode = await _resolveCouponCode(purchase); + await _pendingStore.rememberForPurchase( + purchase, + planId: planId, + couponCode: couponCode, + ); + await _acknowledger.acknowledge( + purchase, + planId: planId, + couponCode: couponCode, + onSuccess: _onAckSuccess, + onError: _onAckError, + ); + } + + void _onAckSuccess(PurchaseDetails purchase) => + _session.onSuccess?.call(purchase); + + /// Surfaces the "will retry in the background" message and clears the + /// in-memory session, so a later background retry resolves the plan from + /// persisted metadata and a stray success can't fire the now-stale callback. + void _onAckError(String message) { + final onError = _session.onError; + _session.onSuccess = null; + _session.onError = null; + _session.pendingPlanId = null; + onError?.call(message); + } + + /// Runs once the backend confirms a purchase (or the account is found already + /// active): drop its pending metadata and complete the store transaction. + Future _onAcknowledged(PurchaseDetails purchase) async { + await _pendingStore.forgetPurchase(purchase); + await _finalize(purchase); + } + Future _finalize(PurchaseDetails purchaseDetails) async { try { if (purchaseDetails.pendingCompletePurchase) { @@ -585,9 +628,9 @@ class AppPurchase { } catch (e) { appLogger.error('[AppPurchase] Error finalizing purchase: $e', e); } finally { - _pendingPlanId = null; - _pendingCouponCode = ''; - _isRestoreFlow = false; + _session.pendingPlanId = null; + _session.pendingCouponCode = ''; + _session.isRestoreFlow = false; } } @@ -598,7 +641,7 @@ class AppPurchase { void _updateStreamOnError(Object error) { appLogger.error('[AppPurchase] Purchase stream error: $error'); - _onError?.call(error.toString()); + _session.onError?.call(error.toString()); } /// Keeps only the SKUs matching the requested mode. @@ -607,14 +650,22 @@ class AppPurchase { /// [GooglePlayProductDetails]: base plans have a null (or empty) offerId, /// discount offers have a non-null offerId. When [includeOffers] is false we /// keep base plans (a normal purchase), when true we keep only offers (an - /// applied affiliate/referral discount). iOS has no per-offer concept, so its - /// products always pass through. + /// applied affiliate/referral discount). + /// + /// iOS has no per-offer concept, so the discount lives on a dedicated + /// affiliate product ([_iosAffiliateIds]). We apply the same split there: + /// keep only the affiliate SKUs when an offer was requested, otherwise only + /// the base SKUs. Either way `_subscriptionSku` ends up holding just one set, + /// so the downstream `_normalizePlan` prefix match resolves unambiguously. List _selectSkus( List products, { required bool includeOffers, }) { if (!Platform.isAndroid) { - return products; + return products.where((product) { + final isAffiliate = _iosAffiliateIds.contains(product.id); + return includeOffers ? isAffiliate : !isAffiliate; + }).toList(); } return products.where((product) { final offerId = _offerIdFor(product); @@ -637,14 +688,19 @@ class AppPurchase { return offers[index].offerId; } + /// The plan-family prefix shared by a plan id and its store product id: + /// "1y-usd-10" → "1y", "1y_sub" → "1y", "1y_sub_affiliate" → "1y". Splitting + /// on either delimiter lets a plan and its (base or affiliate) SKU match. + static String _planPrefix(String id) => id.split(RegExp('[-_]')).first; + /// Resolves the store product to purchase for [planId] by matching the plan /// prefix (e.g. "1y"). Whether this returns a base plan or a discount offer /// depends purely on which SKU set was loaded by [fetchSubscriptions]. ProductDetails? _normalizePlan(String planId) { - final plan = planId.split('-').first; + final plan = _planPrefix(planId); appLogger.info('[AppPurchase] Normalizing planId: $planId to plan: $plan'); for (final sku in _subscriptionSku) { - if (sku.id.split('_').first == plan) { + if (_planPrefix(sku.id) == plan) { return sku; } } @@ -685,13 +741,17 @@ class AppPurchase { /// Determines the plan id to send to the backend for acknowledgment. /// - /// Prefers the exact plan the user selected, then falls back to a default. + /// Prefers the exact plan the user selected, then the value persisted for + /// this purchase (so background retries and post-restart re-delivery still + /// resolve it), then a cached plan matching the product prefix, then a + /// default. Future _resolvePlanId(PurchaseDetails purchase) async { - if (_pendingPlanId != null && _pendingPlanId!.isNotEmpty) { - return _pendingPlanId!; + final pendingPlanId = _session.pendingPlanId; + if (pendingPlanId != null && pendingPlanId.isNotEmpty) { + return pendingPlanId; } - final pendingPlan = await _pendingPlanForPurchase(purchase); + final pendingPlan = await _pendingStore.planFor(purchase); if (pendingPlan != null) { appLogger.info( '[AppPurchase] Resolved plan from pending purchase metadata: $pendingPlan', @@ -699,7 +759,7 @@ class AppPurchase { return pendingPlan; } - final prefix = purchase.productID.split('_').first; // "1y" or "1m" + final prefix = _planPrefix(purchase.productID); // "1y" or "1m" final localPlans = sl().getPlans(); if (localPlans != null) { for (final plan in localPlans.plans) { @@ -723,378 +783,17 @@ class AppPurchase { /// re-delivery after a restart still attribute the sale. Returns '' when no /// code was applied. Future _resolveCouponCode(PurchaseDetails purchase) async { - if (_pendingCouponCode.isNotEmpty) { - return _pendingCouponCode; + if (_session.pendingCouponCode.isNotEmpty) { + return _session.pendingCouponCode; } - return await _pendingCouponForPurchase(purchase) ?? ''; + return await _pendingStore.couponFor(purchase) ?? ''; } void clearCallbacks() { - _onSuccess = null; - _onError = null; - _pendingPlanId = null; - _pendingCouponCode = ''; - _isRestoreFlow = false; - } - - Future _acknowledgePurchase( - PurchaseDetails purchaseDetails, { - required String planId, - String couponCode = '', - bool isRetry = false, - }) async { - final key = _purchaseRetryKey(purchaseDetails); - if (!_acknowledgeInFlight.add(key)) { - appLogger.info( - '[AppPurchase] Acknowledgment already in flight: ' - 'productID=${purchaseDetails.productID} key=$key', - ); - return; - } - - try { - final purchaseToken = - purchaseDetails.verificationData.serverVerificationData; - appLogger.info( - '[AppPurchase] Acknowledgment ${isRetry ? 'retry' : 'start'}: ' - 'productID=${purchaseDetails.productID} ' - 'purchaseID=${purchaseDetails.purchaseID} ' - 'planId=$planId receiptLength=${purchaseToken.length}', - ); - - if (purchaseToken.isEmpty) { - await _handleAcknowledgeFailure( - purchaseDetails, - 'Missing purchase receipt', - isRetry: isRetry, - ); - return; - } - - final lanternService = sl(); - final ack = await lanternService.acknowledgeInAppPurchase( - purchaseToken: purchaseToken, - planId: planId, - couponCode: couponCode, - ); - - await ack.fold( - (error) async { - await _handleAcknowledgeFailure( - purchaseDetails, - error, - isRetry: isRetry, - ); - }, - (success) async { - appLogger.info( - '[AppPurchase] Acknowledgment successful: ' - 'productID=${purchaseDetails.productID} purchaseID=${purchaseDetails.purchaseID}', - ); - _clearAcknowledgeRetry(key); - await _forgetPendingPlan(purchaseDetails); - await _finalize(purchaseDetails); - if (!isRetry) { - _onSuccess?.call(purchaseDetails); - } - }, - ); - } finally { - _acknowledgeInFlight.remove(key); - } + _session.onSuccess = null; + _session.onError = null; + _session.pendingPlanId = null; + _session.pendingCouponCode = ''; + _session.isRestoreFlow = false; } - - Future _handleAcknowledgeFailure( - PurchaseDetails purchaseDetails, - Object error, { - bool isRetry = false, - StackTrace? stackTrace, - }) async { - appLogger.error( - '[AppPurchase] Acknowledgment failed; leaving store transaction pending ' - 'for retry: productID=${purchaseDetails.productID} ' - 'purchaseID=${purchaseDetails.purchaseID}', - error, - stackTrace, - ); - - if (await _checkIfAlreadyPurchased()) { - appLogger.info( - '[AppPurchase] Account is already active after acknowledgment failure; ' - 'finalizing store transaction', - ); - _clearAcknowledgeRetry(_purchaseRetryKey(purchaseDetails)); - await _forgetPendingPlan(purchaseDetails); - await _finalize(purchaseDetails); - if (!isRetry) { - _onSuccess?.call(purchaseDetails); - } - return; - } - - if (!isRetry) { - final onError = _onError; - _onSuccess = null; - _onError = null; - _pendingPlanId = null; - onError?.call( - 'Purchase verification failed. We will retry in the background.', - ); - } - _scheduleAcknowledgeRetry(purchaseDetails); - } - - void _scheduleAcknowledgeRetry(PurchaseDetails purchaseDetails) { - final key = _purchaseRetryKey(purchaseDetails); - if (_ackRetryTimers.containsKey(key)) { - appLogger.info( - '[AppPurchase] Acknowledgment retry already scheduled: ' - 'productID=${purchaseDetails.productID} key=$key', - ); - return; - } - - final attempt = _ackRetryAttempts[key] ?? 0; - final delay = _ackRetryDelayForAttempt(attempt); - appLogger.info( - '[AppPurchase] Scheduling acknowledgment retry ${attempt + 1} in $delay: ' - 'productID=${purchaseDetails.productID} purchaseID=${purchaseDetails.purchaseID}', - ); - - _ackRetryTimers[key] = Timer(delay, () { - unawaited(() async { - try { - final planId = await _resolvePlanId(purchaseDetails); - final couponCode = await _resolveCouponCode(purchaseDetails); - _ackRetryTimers.remove(key); - _ackRetryAttempts[key] = (_ackRetryAttempts[key] ?? 0) + 1; - await _acknowledgePurchase( - purchaseDetails, - planId: planId, - couponCode: couponCode, - isRetry: true, - ); - } catch (e, st) { - _ackRetryTimers.remove(key); - await _handleAcknowledgeFailure( - purchaseDetails, - e, - isRetry: true, - stackTrace: st, - ); - } - }()); - }); - } - - Duration _ackRetryDelayForAttempt(int attempt) { - final index = attempt >= _ackRetryDelays.length - ? _ackRetryDelays.length - 1 - : attempt; - return _ackRetryDelays[index]; - } - - void _clearAcknowledgeRetry(String key) { - _ackRetryAttempts.remove(key); - _ackRetryTimers.remove(key)?.cancel(); - } - - // The transaction key when available, otherwise the product key — i.e. the - // most specific key `_planKeysForPurchase` would store this purchase under. - String _purchaseRetryKey(PurchaseDetails purchase) => - _planKeysForPurchase(purchase).first; - - String _productPlanKey(String productID) => - '$_productPlanKeyPrefix$productID'; - - String? _transactionPlanKey(PurchaseDetails purchase) { - final purchaseID = purchase.purchaseID; - if (purchaseID != null && purchaseID.isNotEmpty) { - return '$_transactionPlanKeyPrefix$purchaseID'; - } - final transactionDate = purchase.transactionDate; - if (transactionDate != null && transactionDate.isNotEmpty) { - return '$_transactionPlanKeyPrefix${purchase.productID}:$transactionDate'; - } - return null; - } - - List _planKeysForPurchase(PurchaseDetails purchase) { - final transactionKey = _transactionPlanKey(purchase); - final keys = [_productPlanKey(purchase.productID)]; - if (transactionKey != null) { - keys.insert(0, transactionKey); - } - return keys; - } - - Future _pendingPlanForPurchase(PurchaseDetails purchase) async { - final pending = await _loadPendingPurchasePlans(); - for (final key in _planKeysForPurchase(purchase)) { - final planId = pending[key]; - if (planId != null && planId.isNotEmpty) { - return planId; - } - } - return null; - } - - Future _rememberPendingPlanForProduct( - String productID, - String planId, - ) async { - if (await _rememberPendingPlan([_productPlanKey(productID)], planId)) { - appLogger.info( - '[AppPurchase] Stored pending purchase plan: productID=$productID planId=$planId', - ); - } - } - - Future _rememberPendingPlanForPurchase( - PurchaseDetails purchase, - String planId, - ) async { - if (await _rememberPendingPlan(_planKeysForPurchase(purchase), planId)) { - appLogger.info( - '[AppPurchase] Stored pending purchase plan: ' - 'productID=${purchase.productID} purchaseID=${purchase.purchaseID} planId=$planId', - ); - } - } - - /// Stores [planId] under each of [keys], persisting only when [planId] is - /// non-empty. Returns whether anything was written. - Future _rememberPendingPlan(List keys, String planId) async { - if (planId.isEmpty) { - return false; - } - final pending = await _loadPendingPurchasePlans(); - for (final key in keys) { - pending[key] = planId; - } - await _savePendingPurchasePlans(pending); - return true; - } - - Future _forgetPendingPlan(PurchaseDetails purchase) async { - final keys = _planKeysForPurchase(purchase); - if (await _forgetPendingPlanKeys(keys)) { - appLogger.info( - '[AppPurchase] Cleared pending purchase plan: ' - 'productID=${purchase.productID} purchaseID=${purchase.purchaseID}', - ); - } - await _forgetPendingCouponKeys(keys); - } - - Future _forgetPendingPlanForProduct(String productID) async { - final keys = [_productPlanKey(productID)]; - if (await _forgetPendingPlanKeys(keys)) { - appLogger.info( - '[AppPurchase] Cleared pending purchase plan for productID=$productID', - ); - } - await _forgetPendingCouponKeys(keys); - } - - /// Removes [keys] from the pending plans map, persisting only when something - /// actually changed. Returns whether any entry was removed. - Future _forgetPendingPlanKeys(List keys) async { - final pending = await _loadPendingPurchasePlans(); - var changed = false; - for (final key in keys) { - changed = pending.remove(key) != null || changed; - } - if (changed) { - await _savePendingPurchasePlans(pending); - } - return changed; - } - - Future> _loadPendingPurchasePlans() => - sl().getStringMap(_pendingPurchasePlansKey); - - Future _savePendingPurchasePlans(Map pending) => - sl().setStringMap(_pendingPurchasePlansKey, pending); - - // --- Pending affiliate/referral code persistence --------------------------- - // Mirrors the pending-plan storage above, keyed by the same product/ - // transaction keys so the code applied at purchase time survives a restart - // and is available when the store re-delivers the purchase for acknowledgment. - - Future _pendingCouponForPurchase(PurchaseDetails purchase) async { - final pending = await _loadPendingPurchaseCoupons(); - for (final key in _planKeysForPurchase(purchase)) { - final coupon = pending[key]; - if (coupon != null && coupon.isNotEmpty) { - return coupon; - } - } - return null; - } - - Future _rememberPendingCouponForProduct( - String productID, - String couponCode, - ) async { - if (await _rememberPendingCoupon([_productPlanKey(productID)], couponCode)) { - appLogger.info( - '[AppPurchase] Stored pending purchase coupon: productID=$productID', - ); - } - } - - Future _rememberPendingCouponForPurchase( - PurchaseDetails purchase, - String couponCode, - ) async { - if (await _rememberPendingCoupon( - _planKeysForPurchase(purchase), - couponCode, - )) { - appLogger.info( - '[AppPurchase] Stored pending purchase coupon: ' - 'productID=${purchase.productID} purchaseID=${purchase.purchaseID}', - ); - } - } - - /// Stores [couponCode] under each of [keys], persisting only when it is - /// non-empty. Returns whether anything was written. - Future _rememberPendingCoupon( - List keys, - String couponCode, - ) async { - if (couponCode.isEmpty) { - return false; - } - final pending = await _loadPendingPurchaseCoupons(); - for (final key in keys) { - pending[key] = couponCode; - } - await _savePendingPurchaseCoupons(pending); - return true; - } - - /// Removes [keys] from the pending coupons map, persisting only when - /// something actually changed. - Future _forgetPendingCouponKeys(List keys) async { - final pending = await _loadPendingPurchaseCoupons(); - var changed = false; - for (final key in keys) { - changed = pending.remove(key) != null || changed; - } - if (changed) { - await _savePendingPurchaseCoupons(pending); - } - } - - Future> _loadPendingPurchaseCoupons() => - sl().getStringMap(_pendingPurchaseCouponsKey); - - Future _savePendingPurchaseCoupons(Map pending) => - sl().setStringMap( - _pendingPurchaseCouponsKey, - pending, - ); } diff --git a/lib/core/services/purchase/pending_purchase_store.dart b/lib/core/services/purchase/pending_purchase_store.dart new file mode 100644 index 0000000000..039890e01a --- /dev/null +++ b/lib/core/services/purchase/pending_purchase_store.dart @@ -0,0 +1,144 @@ +import 'package:in_app_purchase/in_app_purchase.dart'; +import 'package:lantern/core/services/local_storage_service.dart'; + +/// Persists the plan id and affiliate code chosen for an in-flight purchase, so +/// background acknowledge retries and post-restart re-delivery can still +/// attribute the sale. +/// +/// Each purchase is keyed by its store transaction id when present, falling +/// back to the product id. Plan and coupon are kept in two separate maps to +/// preserve the original on-disk format; the single remember/forget API writes +/// and clears both together so they can't drift apart. +class PendingPurchaseStore { + PendingPurchaseStore(this._storage); + + /// Resolved lazily so construction doesn't require [LocalStorageService] to + /// be registered yet, and tests can inject a fake. + final LocalStorageService Function() _storage; + + static const _plansStorageKey = 'pending_purchase_plans_json'; + static const _couponsStorageKey = 'pending_purchase_coupons_json'; + static const _productKeyPrefix = 'product:'; + static const _transactionKeyPrefix = 'transaction:'; + + // --- Public API -------------------------------------------------------- + + /// Remembers the plan/coupon at purchase initiation, keyed by product id only + /// (no transaction id exists yet). + Future rememberForProduct( + String productID, { + String planId = '', + String couponCode = '', + }) => _remember( + [_productKey(productID)], + planId: planId, + couponCode: couponCode, + ); + + /// Remembers the plan/coupon for a delivered purchase, keyed by both its + /// transaction id (when present) and product id. + Future rememberForPurchase( + PurchaseDetails purchase, { + String planId = '', + String couponCode = '', + }) => _remember( + _keysFor(purchase), + planId: planId, + couponCode: couponCode, + ); + + Future planFor(PurchaseDetails purchase) => + _firstNonEmpty(_plansStorageKey, _keysFor(purchase)); + + Future couponFor(PurchaseDetails purchase) => + _firstNonEmpty(_couponsStorageKey, _keysFor(purchase)); + + Future forgetPurchase(PurchaseDetails purchase) => + _forget(_keysFor(purchase)); + + Future forgetProduct(String productID) => + _forget([_productKey(productID)]); + + /// The most specific key for [purchase] (transaction id, else product id), + /// used to dedupe in-flight acknowledgements and their retries. + String retryKeyFor(PurchaseDetails purchase) => _keysFor(purchase).first; + + // --- Key derivation ---------------------------------------------------- + + String _productKey(String productID) => '$_productKeyPrefix$productID'; + + String? _transactionKey(PurchaseDetails purchase) { + final purchaseID = purchase.purchaseID; + if (purchaseID != null && purchaseID.isNotEmpty) { + return '$_transactionKeyPrefix$purchaseID'; + } + final transactionDate = purchase.transactionDate; + if (transactionDate != null && transactionDate.isNotEmpty) { + return '$_transactionKeyPrefix${purchase.productID}:$transactionDate'; + } + return null; + } + + /// Transaction key first (most specific) when present, then product key. + List _keysFor(PurchaseDetails purchase) { + final keys = [_productKey(purchase.productID)]; + final transactionKey = _transactionKey(purchase); + if (transactionKey != null) { + keys.insert(0, transactionKey); + } + return keys; + } + + // --- Storage internals ------------------------------------------------- + + Future _remember( + List keys, { + required String planId, + required String couponCode, + }) async { + await _putAll(_plansStorageKey, keys, planId); + await _putAll(_couponsStorageKey, keys, couponCode); + } + + Future _forget(List keys) async { + await _removeAll(_plansStorageKey, keys); + await _removeAll(_couponsStorageKey, keys); + } + + /// No-op when [value] is empty, so an absent plan/coupon never overwrites a + /// previously stored one. + Future _putAll( + String storageKey, + List keys, + String value, + ) async { + if (value.isEmpty) return; + final map = await _storage().getStringMap(storageKey); + for (final key in keys) { + map[key] = value; + } + await _storage().setStringMap(storageKey, map); + } + + Future _removeAll(String storageKey, List keys) async { + final map = await _storage().getStringMap(storageKey); + var changed = false; + for (final key in keys) { + changed = map.remove(key) != null || changed; + } + if (changed) { + await _storage().setStringMap(storageKey, map); + } + } + + Future _firstNonEmpty(String storageKey, List keys) async { + final map = await _storage().getStringMap(storageKey); + for (final key in keys) { + final value = map[key]; + if (value != null && value.isNotEmpty) { + return value; + } + } + return null; + } +} diff --git a/lib/core/services/purchase/purchase_acknowledger.dart b/lib/core/services/purchase/purchase_acknowledger.dart new file mode 100644 index 0000000000..d5a475e2dd --- /dev/null +++ b/lib/core/services/purchase/purchase_acknowledger.dart @@ -0,0 +1,271 @@ +import 'dart:async'; + +import 'package:fpdart/fpdart.dart'; +import 'package:in_app_purchase/in_app_purchase.dart'; +import 'package:lantern/core/services/logger_service.dart'; +import 'package:lantern/core/utils/failure.dart'; + +/// Sends a purchase receipt to the backend. Returns the backend response on +/// success, or a [Failure] to be retried. +typedef ReceiptAcknowledger = + Future> Function({ + required String purchaseToken, + required String planId, + required String couponCode, + }); + +/// Resolves the plan id / coupon code to send for a purchase. Re-invoked on +/// each retry so a background attempt reads the latest persisted attribution. +typedef PurchaseResolver = Future Function(PurchaseDetails purchase); + +/// Runs after a purchase is confirmed by the backend (or found already active): +/// clear its pending metadata and complete the store transaction. +typedef PurchaseSideEffect = Future Function(PurchaseDetails purchase); + +/// The stable key a purchase is deduped/retried under. +typedef RetryKey = String Function(PurchaseDetails purchase); + +typedef PurchaseSuccess = void Function(PurchaseDetails purchase); +typedef PurchaseError = void Function(String message); + +class _RetryState { + bool inFlight = false; + int attempts = 0; + Timer? timer; +} + +/// Owns the "get this receipt confirmed by the backend" state machine: +/// de-duplication of concurrent attempts and background retries with backoff, +/// until the backend confirms or the account is already active. +/// +/// All domain operations are injected, so it has no dependency on the service +/// locator or store plugin and is unit-testable in isolation. +class PurchaseAcknowledger { + PurchaseAcknowledger({ + required ReceiptAcknowledger acknowledgeReceipt, + required PurchaseResolver resolvePlanId, + required PurchaseResolver resolveCouponCode, + required RetryKey retryKeyFor, + required Future Function() isAlreadyActive, + required PurchaseSideEffect onAcknowledged, + List retryDelays = defaultRetryDelays, + }) : _acknowledgeReceipt = acknowledgeReceipt, + _resolvePlanId = resolvePlanId, + _resolveCouponCode = resolveCouponCode, + _retryKeyFor = retryKeyFor, + _isAlreadyActive = isAlreadyActive, + _onAcknowledged = onAcknowledged, + _retryDelays = retryDelays; + + static const List defaultRetryDelays = [ + Duration(seconds: 5), + Duration(seconds: 15), + Duration(seconds: 45), + Duration(minutes: 2), + Duration(minutes: 5), + ]; + + final ReceiptAcknowledger _acknowledgeReceipt; + final PurchaseResolver _resolvePlanId; + final PurchaseResolver _resolveCouponCode; + final RetryKey _retryKeyFor; + final Future Function() _isAlreadyActive; + final PurchaseSideEffect _onAcknowledged; + final List _retryDelays; + + final Map _retries = {}; + + /// Acknowledges a freshly delivered [purchase]. On success completes the + /// store transaction (via `onAcknowledged`) and fires [onSuccess]. On + /// failure, fires [onError] once and retries in the background with backoff; + /// background attempts never fire the callbacks again. + Future acknowledge( + PurchaseDetails purchase, { + required String planId, + required String couponCode, + required PurchaseSuccess onSuccess, + required PurchaseError onError, + }) => _attempt( + purchase, + planId: planId, + couponCode: couponCode, + onSuccess: onSuccess, + onError: onError, + isRetry: false, + ); + + /// Cancels all pending background retries. Not called during normal operation + /// (retries are meant to outlive a single UI flow); provided for teardown. + void dispose() { + for (final state in _retries.values) { + state.timer?.cancel(); + } + _retries.clear(); + } + + Future _attempt( + PurchaseDetails purchase, { + required String planId, + required String couponCode, + required bool isRetry, + PurchaseSuccess? onSuccess, + PurchaseError? onError, + }) async { + final key = _retryKeyFor(purchase); + final state = _retries.putIfAbsent(key, () => _RetryState()); + if (state.inFlight) { + appLogger.info( + '[PurchaseAck] Acknowledgment already in flight: ' + 'productID=${purchase.productID} key=$key', + ); + return; + } + state.inFlight = true; + + try { + final purchaseToken = purchase.verificationData.serverVerificationData; + appLogger.info( + '[PurchaseAck] ${isRetry ? 'retry' : 'start'}: ' + 'productID=${purchase.productID} purchaseID=${purchase.purchaseID} ' + 'planId=$planId receiptLength=${purchaseToken.length}', + ); + + if (purchaseToken.isEmpty) { + await _handleFailure( + purchase, + 'Missing purchase receipt', + isRetry: isRetry, + onSuccess: onSuccess, + onError: onError, + ); + return; + } + + // A thrown exception from the backend call (e.g. a network error) is + // treated exactly like a returned failure, so it is retried rather than + // propagated to the caller. + try { + final result = await _acknowledgeReceipt( + purchaseToken: purchaseToken, + planId: planId, + couponCode: couponCode, + ); + + await result.fold( + (failure) => _handleFailure( + purchase, + failure, + isRetry: isRetry, + onSuccess: onSuccess, + onError: onError, + ), + (_) async { + appLogger.info( + '[PurchaseAck] Acknowledgment successful: ' + 'productID=${purchase.productID} purchaseID=${purchase.purchaseID}', + ); + _clear(key); + await _onAcknowledged(purchase); + if (!isRetry) onSuccess?.call(purchase); + }, + ); + } catch (e) { + await _handleFailure( + purchase, + e, + isRetry: isRetry, + onSuccess: onSuccess, + onError: onError, + ); + } + } finally { + // May already be gone from the map after a successful `_clear`. + _retries[key]?.inFlight = false; + } + } + + Future _handleFailure( + PurchaseDetails purchase, + Object error, { + required bool isRetry, + PurchaseSuccess? onSuccess, + PurchaseError? onError, + }) async { + appLogger.error( + '[PurchaseAck] Acknowledgment failed; leaving store transaction pending ' + 'for retry: productID=${purchase.productID} ' + 'purchaseID=${purchase.purchaseID}', + error, + ); + + // The backend may have granted access even though this call failed (e.g. a + // prior attempt won the race). Trust fresh account state over the error. + if (await _isAlreadyActive()) { + appLogger.info( + '[PurchaseAck] Account already active after failure; finalizing: ' + 'productID=${purchase.productID}', + ); + _clear(_retryKeyFor(purchase)); + await _onAcknowledged(purchase); + if (!isRetry) onSuccess?.call(purchase); + return; + } + + if (!isRetry) { + onError?.call( + 'Purchase verification failed. We will retry in the background.', + ); + } + _scheduleRetry(purchase); + } + + void _scheduleRetry(PurchaseDetails purchase) { + final key = _retryKeyFor(purchase); + final state = _retries.putIfAbsent(key, () => _RetryState()); + if (state.timer != null) { + appLogger.info( + '[PurchaseAck] Retry already scheduled: ' + 'productID=${purchase.productID} key=$key', + ); + return; + } + + final delay = _delayForAttempt(state.attempts); + appLogger.info( + '[PurchaseAck] Scheduling retry ${state.attempts + 1} in $delay: ' + 'productID=${purchase.productID} purchaseID=${purchase.purchaseID}', + ); + + state.timer = Timer(delay, () { + unawaited(() async { + try { + final planId = await _resolvePlanId(purchase); + final couponCode = await _resolveCouponCode(purchase); + state.timer = null; + state.attempts += 1; + await _attempt( + purchase, + planId: planId, + couponCode: couponCode, + isRetry: true, + ); + } catch (e, st) { + state.timer = null; + appLogger.error('[PurchaseAck] Retry attempt threw', e, st); + await _handleFailure(purchase, e, isRetry: true); + } + }()); + }); + } + + Duration _delayForAttempt(int attempt) { + final index = attempt >= _retryDelays.length + ? _retryDelays.length - 1 + : attempt; + return _retryDelays[index]; + } + + void _clear(String key) { + _retries.remove(key)?.timer?.cancel(); + } +} diff --git a/lib/features/auth/choose_payment_method.dart b/lib/features/auth/choose_payment_method.dart index ab686d806f..34fc0bb900 100644 --- a/lib/features/auth/choose_payment_method.dart +++ b/lib/features/auth/choose_payment_method.dart @@ -90,7 +90,7 @@ class ChoosePaymentMethod extends HookConsumerWidget { icon: AppImagePaths.star, onPressed: () { appRouter.pop(); - showRferralCodeDialog(context); + showReferralCodeDialog(context); }, ), DividerSpace(), @@ -100,7 +100,7 @@ class ChoosePaymentMethod extends HookConsumerWidget { ); } - void showRferralCodeDialog(BuildContext context) { + void showReferralCodeDialog(BuildContext context) { final textTheme = Theme.of(context).textTheme; AppDialog.customDialog( context: context, diff --git a/lib/features/plans/feature_list.dart b/lib/features/plans/feature_list.dart index 3649859ffc..4f73c3a880 100644 --- a/lib/features/plans/feature_list.dart +++ b/lib/features/plans/feature_list.dart @@ -27,10 +27,6 @@ class FeatureList extends StatelessWidget { image: AppImagePaths.eyeHide, title: 'advanced_anti_censorship'.i18n, ), - _FeatureTile( - image: AppImagePaths.roundCorrect, - title: 'exclusive_access_new_features'.i18n, - ), _FeatureTile( image: AppImagePaths.connectDevice, title: 'connect_up_to_five_devices'.i18n, diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index 4e1f2140fe..df9b433494 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -73,7 +73,7 @@ class _PlansState extends ConsumerState child: SizedBox( height: context.isSmallDevice ? size.height * 0.4 - : size.height * 0.36, + : size.height * 0.3, child: SingleChildScrollView(child: FeatureList()), ), ), @@ -120,7 +120,7 @@ class _PlansState extends ConsumerState }, ), ), - SizedBox(height: 24), + SizedBox(height: defaultSize), Padding( padding: EdgeInsets.symmetric( horizontal: context.isSmallDevice ? defaultSize : 0, diff --git a/macos/Runner/Handlers/MethodHandler.swift b/macos/Runner/Handlers/MethodHandler.swift index faeb865ca2..7426325082 100644 --- a/macos/Runner/Handlers/MethodHandler.swift +++ b/macos/Runner/Handlers/MethodHandler.swift @@ -89,7 +89,9 @@ class MethodHandler { ) return } - self.acknowledgeInAppPurchase(token: token, planId: planId, result: result) + let couponCode = map["couponCode"] as? String ?? "" + self.acknowledgeInAppPurchase( + token: token, planId: planId, couponCode: couponCode, result: result) // user management case "startRecoveryByEmail": @@ -156,8 +158,11 @@ class MethodHandler { self.referralAttach(result: result, code: code) case "attachReferralCodeV2": - let code = call.arguments as? String ?? "" - self.referralAttachV2(result: result, code: code) + let data = call.arguments as? [String: Any] ?? [:] + let code = data["code"] as? String ?? "" + let distributionChannel = data["distributionChannel"] as? String ?? "" + self.referralAttachV2( + result: result, code: code, distributionChannel: distributionChannel) // Private server methods case "digitalOcean": @@ -607,10 +612,12 @@ class MethodHandler { } } - func acknowledgeInAppPurchase(token: String, planId: String, result: @escaping FlutterResult) { + func acknowledgeInAppPurchase( + token: String, planId: String, couponCode: String, result: @escaping FlutterResult + ) { Task { var error: NSError? - let json = MobileAcknowledgeApplePurchase(token, planId, &error) + let json = MobileAcknowledgeApplePurchase(token, planId, couponCode, &error) if let error { await self.handleFlutterError(error, result: result, code: "ACKNOWLEDGE_FAILED") return @@ -811,10 +818,12 @@ class MethodHandler { } } - func referralAttachV2(result: @escaping FlutterResult, code: String) { + func referralAttachV2( + result: @escaping FlutterResult, code: String, distributionChannel: String + ) { Task { var error: NSError? - let json = MobileReferralAttachmentV2(code, &error) + let json = MobileReferralAttachmentV2(code, distributionChannel, &error) if let error { appLogger.error("Failed to attach referral code v2: \(error.localizedDescription)") await self.handleFlutterError(error, result: result, code: "ATTACH_REFERRAL_CODE_V2_FAILED") @@ -1313,6 +1322,7 @@ class MethodHandler { let provider = data["provider"] as? String ?? "" let planId = data["planId"] as? String ?? "" let email = data["email"] as? String ?? "" + let couponCode = data["couponCode"] as? String ?? "" let idempotencyKey: String do { idempotencyKey = try self.paymentRedirectIdempotencyKey(from: data) @@ -1326,6 +1336,7 @@ class MethodHandler { planId, email, idempotencyKey, + couponCode, &error ) if let err = error { diff --git a/test/core/services/purchase/pending_purchase_store_test.dart b/test/core/services/purchase/pending_purchase_store_test.dart new file mode 100644 index 0000000000..814899c036 --- /dev/null +++ b/test/core/services/purchase/pending_purchase_store_test.dart @@ -0,0 +1,156 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:in_app_purchase/in_app_purchase.dart'; +import 'package:lantern/core/services/local_storage_service.dart'; +import 'package:lantern/core/services/purchase/pending_purchase_store.dart'; + +/// In-memory [LocalStorageService] backing only the string-map API the store +/// uses. `_prefs` is `late` and never touched, so subclassing is safe. +class _FakeStorage extends LocalStorageService { + final Map> maps = {}; + + @override + Future> getStringMap(String key) async => + Map.from(maps[key] ?? const {}); + + @override + Future setStringMap(String key, Map value) async { + if (value.isEmpty) { + maps.remove(key); + return; + } + maps[key] = Map.from(value); + } +} + +PurchaseDetails _purchase({ + String productID = '1y_sub', + String? purchaseID, + String? transactionDate, +}) => PurchaseDetails( + purchaseID: purchaseID, + productID: productID, + transactionDate: transactionDate, + status: PurchaseStatus.purchased, + verificationData: PurchaseVerificationData( + localVerificationData: 'local', + serverVerificationData: 'server', + source: 'test', + ), +); + +void main() { + late _FakeStorage storage; + late PendingPurchaseStore store; + + setUp(() { + storage = _FakeStorage(); + store = PendingPurchaseStore(() => storage); + }); + + group('rememberForPurchase / resolve', () { + test('round-trips plan and coupon for a delivered purchase', () async { + final purchase = _purchase(purchaseID: 'txn-1'); + await store.rememberForPurchase( + purchase, + planId: '1y-usd-10', + couponCode: 'AFF20', + ); + + expect(await store.planFor(purchase), '1y-usd-10'); + expect(await store.couponFor(purchase), 'AFF20'); + }); + + test('stores under both the transaction and product keys', () async { + final purchase = _purchase(purchaseID: 'txn-1'); + await store.rememberForPurchase(purchase, planId: '1y-usd-10'); + + expect(storage.maps['pending_purchase_plans_json'], { + 'transaction:txn-1': '1y-usd-10', + 'product:1y_sub': '1y-usd-10', + }); + }); + + test('resolves by product key when no transaction id was assigned', + () async { + // Remembered at initiation (product key only)... + await store.rememberForProduct('1y_sub', planId: '1y-usd-10'); + // ...then delivered with a transaction id. + final delivered = _purchase(purchaseID: 'txn-1'); + + expect(await store.planFor(delivered), '1y-usd-10'); + }); + + test('returns null when nothing was stored', () async { + final purchase = _purchase(purchaseID: 'txn-1'); + expect(await store.planFor(purchase), isNull); + expect(await store.couponFor(purchase), isNull); + }); + }); + + group('empty values never overwrite', () { + test('an empty coupon leaves a previously stored plan intact', () async { + final purchase = _purchase(purchaseID: 'txn-1'); + await store.rememberForPurchase(purchase, planId: '1y-usd-10'); + // Re-remember with no coupon (the common "plan only" case). + await store.rememberForPurchase(purchase, couponCode: ''); + + expect(await store.planFor(purchase), '1y-usd-10'); + expect(await store.couponFor(purchase), isNull); + expect(storage.maps.containsKey('pending_purchase_coupons_json'), isFalse); + }); + }); + + group('forgetPurchase', () { + test('clears both plan and coupon in lockstep', () async { + final purchase = _purchase(purchaseID: 'txn-1'); + await store.rememberForPurchase( + purchase, + planId: '1y-usd-10', + couponCode: 'AFF20', + ); + + await store.forgetPurchase(purchase); + + expect(await store.planFor(purchase), isNull); + expect(await store.couponFor(purchase), isNull); + expect(storage.maps, isEmpty); + }); + + test('leaves an unrelated purchase untouched', () async { + final a = _purchase(productID: '1y_sub', purchaseID: 'txn-a'); + final b = _purchase(productID: '1m_sub', purchaseID: 'txn-b'); + await store.rememberForPurchase(a, planId: '1y-usd-10'); + await store.rememberForPurchase(b, planId: '1m-usd-10'); + + await store.forgetPurchase(a); + + expect(await store.planFor(a), isNull); + expect(await store.planFor(b), '1m-usd-10'); + }); + }); + + group('retryKeyFor', () { + test('prefers the transaction id', () { + expect( + store.retryKeyFor(_purchase(purchaseID: 'txn-1')), + 'transaction:txn-1', + ); + }); + + test('falls back to productID:date when there is no purchase id', () { + expect( + store.retryKeyFor( + _purchase(productID: '1y_sub', transactionDate: '12345'), + ), + 'transaction:1y_sub:12345', + ); + }); + + test('falls back to the product key when nothing identifies the txn', () { + expect( + store.retryKeyFor(_purchase(productID: '1y_sub')), + 'product:1y_sub', + ); + }); + }); +} diff --git a/test/core/services/purchase/purchase_acknowledger_test.dart b/test/core/services/purchase/purchase_acknowledger_test.dart new file mode 100644 index 0000000000..38ee75a2e6 --- /dev/null +++ b/test/core/services/purchase/purchase_acknowledger_test.dart @@ -0,0 +1,204 @@ +import 'dart:async'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:fpdart/fpdart.dart'; +import 'package:in_app_purchase/in_app_purchase.dart'; +import 'package:lantern/core/services/purchase/purchase_acknowledger.dart'; +import 'package:lantern/core/utils/failure.dart'; + +PurchaseDetails _purchase({ + String productID = '1y_sub', + String purchaseID = 'txn-1', + String receipt = 'server-receipt', +}) => PurchaseDetails( + purchaseID: purchaseID, + productID: productID, + transactionDate: '1000', + status: PurchaseStatus.purchased, + verificationData: PurchaseVerificationData( + localVerificationData: 'local', + serverVerificationData: receipt, + source: 'test', + ), +); + +Failure _failure() => + Failure(error: 'boom', localizedErrorMessage: 'boom'); + +/// Collects callback invocations so tests can assert exact counts. +class _Recorder { + int success = 0; + int error = 0; + int acknowledged = 0; +} + +void main() { + late _Recorder rec; + + setUp(() => rec = _Recorder()); + + /// Builds an acknowledger with sensible fakes; each collaborator is + /// overridable per test. + PurchaseAcknowledger build({ + required ReceiptAcknowledger acknowledgeReceipt, + Future Function()? isAlreadyActive, + List? retryDelays, + }) => PurchaseAcknowledger( + acknowledgeReceipt: acknowledgeReceipt, + resolvePlanId: (_) async => '1y-usd-10', + resolveCouponCode: (_) async => '', + retryKeyFor: (p) => p.purchaseID ?? p.productID, + isAlreadyActive: isAlreadyActive ?? () async => false, + onAcknowledged: (_) async => rec.acknowledged++, + retryDelays: retryDelays ?? const [Duration(milliseconds: 5)], + ); + + test('confirms on the first try: finalizes and fires onSuccess once', () async { + final ack = build( + acknowledgeReceipt: ({required purchaseToken, required planId, required couponCode}) async => + right('ok'), + ); + + await ack.acknowledge( + _purchase(), + planId: '1y-usd-10', + couponCode: '', + onSuccess: (_) => rec.success++, + onError: (_) => rec.error++, + ); + + expect(rec.acknowledged, 1); + expect(rec.success, 1); + expect(rec.error, 0); + }); + + test('backend error but account already active: finalizes as success', () async { + final ack = build( + acknowledgeReceipt: ({required purchaseToken, required planId, required couponCode}) async => + left(_failure()), + isAlreadyActive: () async => true, + ); + + await ack.acknowledge( + _purchase(), + planId: '1y-usd-10', + couponCode: '', + onSuccess: (_) => rec.success++, + onError: (_) => rec.error++, + ); + + expect(rec.acknowledged, 1); + expect(rec.success, 1); + expect(rec.error, 0); + }); + + test('transient failure then background-retry success', () async { + var calls = 0; + final ack = build( + acknowledgeReceipt: ({required purchaseToken, required planId, required couponCode}) async { + calls++; + return calls == 1 ? left(_failure()) : right('ok'); + }, + ); + + await ack.acknowledge( + _purchase(), + planId: '1y-usd-10', + couponCode: '', + onSuccess: (_) => rec.success++, + onError: (_) => rec.error++, + ); + + // Initial attempt failed: user was told once, transaction not yet finalized. + expect(rec.error, 1); + expect(rec.acknowledged, 0); + + // Let the scheduled retry fire. + await Future.delayed(const Duration(milliseconds: 40)); + + expect(calls, 2); + expect(rec.acknowledged, 1); // retry finalized the transaction + expect(rec.success, 0); // background retries stay silent + expect(rec.error, 1); // no second error surfaced + }); + + test('empty receipt is treated as a failure and surfaced once', () async { + var receiptCalls = 0; + final ack = build( + acknowledgeReceipt: ({required purchaseToken, required planId, required couponCode}) async { + receiptCalls++; + return right('ok'); + }, + retryDelays: const [Duration(seconds: 30)], // don't fire during the test + ); + + await ack.acknowledge( + _purchase(receipt: ''), + planId: '1y-usd-10', + couponCode: '', + onSuccess: (_) => rec.success++, + onError: (_) => rec.error++, + ); + + expect(receiptCalls, 0); // never hit the backend with an empty receipt + expect(rec.error, 1); + expect(rec.acknowledged, 0); + ack.dispose(); // cancel the pending retry timer + }); + + test('a thrown backend error is retried, not propagated', () async { + var calls = 0; + final ack = build( + acknowledgeReceipt: ({required purchaseToken, required planId, required couponCode}) async { + calls++; + if (calls == 1) throw Exception('network down'); + return right('ok'); + }, + ); + + // Must not throw out of acknowledge(). + await ack.acknowledge( + _purchase(), + planId: '1y-usd-10', + couponCode: '', + onSuccess: (_) => rec.success++, + onError: (_) => rec.error++, + ); + + expect(rec.error, 1); // surfaced once + await Future.delayed(const Duration(milliseconds: 40)); + expect(calls, 2); // retried + expect(rec.acknowledged, 1); // and succeeded + }); + + test('a duplicate in-flight acknowledge for the same purchase is ignored', () async { + final completer = Completer>(); + final ack = build( + acknowledgeReceipt: ({required purchaseToken, required planId, required couponCode}) => + completer.future, + ); + + final purchase = _purchase(); + // Start one (does not complete yet), then a second for the same key. + final first = ack.acknowledge( + purchase, + planId: '1y-usd-10', + couponCode: '', + onSuccess: (_) => rec.success++, + onError: (_) => rec.error++, + ); + await ack.acknowledge( + purchase, + planId: '1y-usd-10', + couponCode: '', + onSuccess: (_) => rec.success++, + onError: (_) => rec.error++, + ); + + completer.complete(right('ok')); + await first; + + expect(rec.success, 1); // only the first attempt ran + expect(rec.acknowledged, 1); + }); +} From da7ddf946cce0262bac7bd910252dd3138cad2f0 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 8 Jul 2026 19:42:23 +0530 Subject: [PATCH 13/22] update UI --- lib/features/plans/plans.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index df9b433494..9dbcb150b7 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -73,7 +73,9 @@ class _PlansState extends ConsumerState child: SizedBox( height: context.isSmallDevice ? size.height * 0.4 - : size.height * 0.3, + : PlatformUtils.isIOS + ? size.height * 0.3 + : size.height * 0.35, child: SingleChildScrollView(child: FeatureList()), ), ), From 365ea7b78ea5100c00377a6109bc1ef083b55589 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Thu, 9 Jul 2026 12:11:23 +0530 Subject: [PATCH 14/22] update error logs --- lib/features/plans/plans.dart | 2 +- lib/lantern/lantern_platform_service.dart | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index 9dbcb150b7..0baec2dbf9 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -490,7 +490,7 @@ class _PlansState extends ConsumerState context.hideLoadingDialog(); if (_redirectToSignupIfPlayBlocked()) return; context.showSnackBar(error.localizedErrorMessage); - appLogger.error('Error subscribing to plan: $error'); + appLogger.error('Error subscribing to plan: $error', error); }, (_) {}); } diff --git a/lib/lantern/lantern_platform_service.dart b/lib/lantern/lantern_platform_service.dart index b9bf3e23c5..b42e332607 100644 --- a/lib/lantern/lantern_platform_service.dart +++ b/lib/lantern/lantern_platform_service.dart @@ -816,7 +816,11 @@ class LanternPlatformService implements LanternCoreService { }); return Right('ok'); } catch (e, stackTrace) { - appLogger.error('Error acknowledging in-app purchase', e, stackTrace); + appLogger.error( + 'Error acknowledging in-app purchase ${e.toString()} ', + e, + stackTrace, + ); return Left(e.toFailure()); } } From 233b18abd8e4d1de83ee69ab94028aa15b98c48f Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Thu, 9 Jul 2026 13:10:54 +0530 Subject: [PATCH 15/22] use remote radiance --- go.mod | 4 ++-- go.sum | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 51ebad1e58..50c908ee58 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/getlantern/lantern go 1.26.2 -replace github.com/getlantern/radiance => ../radiance +//replace github.com/getlantern/radiance => ../radiance // replace github.com/getlantern/lantern-server-provisioner => ../lantern-server-provisioner @@ -25,7 +25,7 @@ replace github.com/quic-go/qpack => github.com/quic-go/qpack v0.5.1 require ( github.com/alecthomas/assert/v2 v2.3.0 github.com/getlantern/lantern-server-provisioner v0.0.0-20251031121934-8ea031fccfa9 - github.com/getlantern/radiance v0.0.0-20260617195940-99d3ff55fef1 + github.com/getlantern/radiance v0.0.0-20260707114451-d023bb34053a github.com/sagernet/sing-box v1.12.22 golang.org/x/mobile v0.0.0-20250711185624-d5bb5ecc55c0 golang.org/x/sys v0.42.0 diff --git a/go.sum b/go.sum index 6e03227bf3..8cc6027481 100644 --- a/go.sum +++ b/go.sum @@ -259,6 +259,8 @@ github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 h1:rtDmW8YL github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535/go.mod h1:WKJEdjMOD4IuTRYwjQHjT4bmqDl5J82RShMLxPAvi0Q= github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b h1:gMYJzEhLrmIqQ+JnjiYNm+UyUDalK3WUmVyecFwmV5g= github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b/go.mod h1:NpfXdK4ldEKkjQ4P1R+DBF4ua5VFOlxmgHROTnYrApg= +github.com/getlantern/radiance v0.0.0-20260707114451-d023bb34053a h1:5Wx+JLjWjunE52gU3G38yeH+znbQx54KGYDXNKwWZwc= +github.com/getlantern/radiance v0.0.0-20260707114451-d023bb34053a/go.mod h1:rXbNFXzQvbnlIlaIF/6EJTwxK9DrAyKowkpUq/5udkI= github.com/getlantern/samizdat v0.0.3-0.20260529191731-5ea8ae61ddbf h1:KxiMF+oG0rTtuBi7GiIaHfccYOf69rLJ/VnO5myoYc4= github.com/getlantern/samizdat v0.0.3-0.20260529191731-5ea8ae61ddbf/go.mod h1:uEeykQSW2/6rTjfPlj3MTTo59poSHXfAHTGgzYDkbr0= github.com/getlantern/semconv v0.0.0-20260327040646-21845dda05cb h1:c5YM7b3a4r2J8Eh89KkI6M/iTFe6Bi+b8AJlfkKdFq4= From c33c702666aaa93d3fbb4ed428fcbe6eda0e3b19 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Thu, 9 Jul 2026 13:24:39 +0530 Subject: [PATCH 16/22] co-pilot is code review changes --- lib/core/models/referral_attach_response.dart | 7 +++++-- lib/features/auth/choose_payment_method.dart | 2 +- .../plans/provider/referral_notifier.dart | 16 +++++++++++----- lib/lantern/lantern_ffi_service.dart | 12 +++++++++--- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/lib/core/models/referral_attach_response.dart b/lib/core/models/referral_attach_response.dart index 75a40391f6..ca5fba692e 100644 --- a/lib/core/models/referral_attach_response.dart +++ b/lib/core/models/referral_attach_response.dart @@ -10,8 +10,11 @@ enum ReferralType { referral, affiliate; - static ReferralType fromString(String? value) => - value == 'referral' ? ReferralType.referral : ReferralType.affiliate; + static ReferralType fromString(String? value) => switch (value) { + 'referral' => ReferralType.referral, + 'affiliate' => ReferralType.affiliate, + _ => ReferralType.none, + }; } class ReferralAttachV2Response { diff --git a/lib/features/auth/choose_payment_method.dart b/lib/features/auth/choose_payment_method.dart index 34fc0bb900..87000e0beb 100644 --- a/lib/features/auth/choose_payment_method.dart +++ b/lib/features/auth/choose_payment_method.dart @@ -552,7 +552,7 @@ class PaymentCheckoutMethods extends HookConsumerWidget { children: [ Flexible( child: Text( - 'promo_code'.i18n.fill([referral.code]), + 'promo_code_with'.i18n.fill([referral.code]), style: theme.bodyMedium, ), ), diff --git a/lib/features/plans/provider/referral_notifier.dart b/lib/features/plans/provider/referral_notifier.dart index c39bd46297..6a2b16f352 100644 --- a/lib/features/plans/provider/referral_notifier.dart +++ b/lib/features/plans/provider/referral_notifier.dart @@ -29,11 +29,17 @@ class ReferralNotifier extends _$ReferralNotifier { state = response; if (response != null) { ref.read(plansProvider.notifier).updatePlans(response.plansData); - final plans = response.plansData; - final defaultPlan = plans.plans.firstWhere( - (Plan plan) => plan.bestValue == true, - ); - ref.read(plansProvider.notifier).setSelectedPlan(defaultPlan); + final plans = response.plansData.plans; + if (plans.isNotEmpty) { + // The backend may not flag a best-value plan (and discounted sets + // may omit it entirely), so fall back to the first plan rather than + // let firstWhere throw and crash the apply-code flow. + final defaultPlan = plans.firstWhere( + (Plan plan) => plan.bestValue == true, + orElse: () => plans.first, + ); + ref.read(plansProvider.notifier).setSelectedPlan(defaultPlan); + } } } return result; diff --git a/lib/lantern/lantern_ffi_service.dart b/lib/lantern/lantern_ffi_service.dart index 3eda80dafb..9a7316f4da 100644 --- a/lib/lantern/lantern_ffi_service.dart +++ b/lib/lantern/lantern_ffi_service.dart @@ -1430,9 +1430,15 @@ class LanternFFIService implements LanternCoreService { try { final distributionChannel = isStoreVersion() ? 'store' : 'non-store'; final result = await runInBackground(() async { - return _ffiService - .referralAttachmentV2(code.toCharPtr, distributionChannel.toCharPtr) - .toDartString(); + final resultPtr = _ffiService.referralAttachmentV2( + code.toCharPtr, + distributionChannel.toCharPtr, + ); + try { + return resultPtr.cast().toDartString(); + } finally { + _ffiService.freeCString(resultPtr); + } }); checkAPIError(result); final response = ReferralAttachV2Response.fromJson(jsonDecode(result)); From 1e1e4a29caad6e95931201da27ff684988b82b20 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Thu, 9 Jul 2026 13:34:07 +0530 Subject: [PATCH 17/22] code review updates --- lib/core/services/app_purchase.dart | 7 +++- .../purchase/purchase_acknowledger.dart | 24 ++++++++++++- .../purchase/purchase_acknowledger_test.dart | 34 +++++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/lib/core/services/app_purchase.dart b/lib/core/services/app_purchase.dart index bfd028a924..825673496f 100644 --- a/lib/core/services/app_purchase.dart +++ b/lib/core/services/app_purchase.dart @@ -510,7 +510,12 @@ class AppPurchase { } } catch (e) { appLogger.error('[AppPurchase] Error handling purchase: $e', e); - _session.onError?.call(e.toString()); + // Capture-clear-fire: clear the session before surfacing the error so a + // stale onSuccess/onError can't fire again when Apple re-delivers this + // pending transaction on the next launch (matches the restore paths). + final onError = _session.onError; + clearCallbacks(); + onError?.call(e.toString()); } } diff --git a/lib/core/services/purchase/purchase_acknowledger.dart b/lib/core/services/purchase/purchase_acknowledger.dart index d5a475e2dd..a9fa8bbdb1 100644 --- a/lib/core/services/purchase/purchase_acknowledger.dart +++ b/lib/core/services/purchase/purchase_acknowledger.dart @@ -49,13 +49,18 @@ class PurchaseAcknowledger { required Future Function() isAlreadyActive, required PurchaseSideEffect onAcknowledged, List retryDelays = defaultRetryDelays, + int? maxRetries, }) : _acknowledgeReceipt = acknowledgeReceipt, _resolvePlanId = resolvePlanId, _resolveCouponCode = resolveCouponCode, _retryKeyFor = retryKeyFor, _isAlreadyActive = isAlreadyActive, _onAcknowledged = onAcknowledged, - _retryDelays = retryDelays; + _retryDelays = retryDelays, + // Default to the backoff schedule's length so each defined delay runs + // exactly once. Previously the final delay repeated forever; capping to + // the schedule keeps the deliberate 5s→…→5min progression and no more. + _maxRetries = maxRetries ?? retryDelays.length; static const List defaultRetryDelays = [ Duration(seconds: 5), @@ -73,6 +78,13 @@ class PurchaseAcknowledger { final PurchaseSideEffect _onAcknowledged; final List _retryDelays; + /// Max background retries before giving up for this session. The store keeps + /// the transaction pending (never completed until acknowledged), so it is + /// re-delivered on the next launch and acknowledgment resumes — capping here + /// only stops the in-session loop from repeating the final backoff delay + /// forever (draining battery/network) when the backend is unreachable. + final int _maxRetries; + final Map _retries = {}; /// Acknowledges a freshly delivered [purchase]. On success completes the @@ -230,6 +242,16 @@ class PurchaseAcknowledger { return; } + if (state.attempts >= _maxRetries) { + appLogger.warning( + '[PurchaseAck] Max retries ($_maxRetries) exhausted this session; ' + 'giving up until re-delivery on next launch: ' + 'productID=${purchase.productID} purchaseID=${purchase.purchaseID}', + ); + _clear(_retryKeyFor(purchase)); + return; + } + final delay = _delayForAttempt(state.attempts); appLogger.info( '[PurchaseAck] Scheduling retry ${state.attempts + 1} in $delay: ' diff --git a/test/core/services/purchase/purchase_acknowledger_test.dart b/test/core/services/purchase/purchase_acknowledger_test.dart index 38ee75a2e6..632e514a93 100644 --- a/test/core/services/purchase/purchase_acknowledger_test.dart +++ b/test/core/services/purchase/purchase_acknowledger_test.dart @@ -43,6 +43,7 @@ void main() { required ReceiptAcknowledger acknowledgeReceipt, Future Function()? isAlreadyActive, List? retryDelays, + int? maxRetries, }) => PurchaseAcknowledger( acknowledgeReceipt: acknowledgeReceipt, resolvePlanId: (_) async => '1y-usd-10', @@ -51,6 +52,7 @@ void main() { isAlreadyActive: isAlreadyActive ?? () async => false, onAcknowledged: (_) async => rec.acknowledged++, retryDelays: retryDelays ?? const [Duration(milliseconds: 5)], + maxRetries: maxRetries, ); test('confirms on the first try: finalizes and fires onSuccess once', () async { @@ -171,6 +173,38 @@ void main() { expect(rec.acknowledged, 1); // and succeeded }); + test('background retries stop after maxRetries when the backend never recovers', () async { + var calls = 0; + final ack = build( + acknowledgeReceipt: ({required purchaseToken, required planId, required couponCode}) async { + calls++; + return left(_failure()); + }, + retryDelays: const [Duration(milliseconds: 1)], + maxRetries: 3, + ); + + await ack.acknowledge( + _purchase(), + planId: '1y-usd-10', + couponCode: '', + onSuccess: (_) => rec.success++, + onError: (_) => rec.error++, + ); + + // Give the bounded retry chain time to run to exhaustion. + await Future.delayed(const Duration(milliseconds: 60)); + + // 1 initial attempt + 3 retries, then the cap stops further scheduling. + expect(calls, 4); + expect(rec.error, 1); // surfaced once on the first failure + expect(rec.acknowledged, 0); + + // No more calls happen after exhaustion. + await Future.delayed(const Duration(milliseconds: 30)); + expect(calls, 4); + }); + test('a duplicate in-flight acknowledge for the same purchase is ignored', () async { final completer = Completer>(); final ack = build( From f2f57dd68bd31c113b077ae8a1d1a187d8897de6 Mon Sep 17 00:00:00 2001 From: jigar-f Date: Thu, 9 Jul 2026 13:57:09 +0530 Subject: [PATCH 18/22] update bindings --- go.mod | 2 +- go.sum | 6 ++++ lib/lantern/lantern_generated_bindings.dart | 39 ++++++++++++++++++++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 84d7b49c48..8451edb395 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ replace github.com/quic-go/qpack => github.com/quic-go/qpack v0.5.1 require ( github.com/alecthomas/assert/v2 v2.3.0 github.com/getlantern/lantern-server-provisioner v0.0.0-20251031121934-8ea031fccfa9 - github.com/getlantern/radiance v0.0.0-20260706185153-bce252479f03 + github.com/getlantern/radiance v0.0.0-20260709081430-208ad3bc7646 github.com/sagernet/sing-box v1.12.22 golang.org/x/mobile v0.0.0-20250711185624-d5bb5ecc55c0 golang.org/x/sys v0.45.0 diff --git a/go.sum b/go.sum index 239e8db7a7..1fb52574e3 100644 --- a/go.sum +++ b/go.sum @@ -60,6 +60,10 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/alexflint/go-arg v1.6.1 h1:uZogJ6VDBjcuosydKgvYYRhh9sRCusjOvoOLZopBlnA= +github.com/alexflint/go-arg v1.6.1/go.mod h1:nQ0LFYftLJ6njcaee0sU+G0iS2+2XJQfA8I062D0LGc= +github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw= +github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= github.com/alitto/pond v1.9.2 h1:9Qb75z/scEZVCoSU+osVmQ0I0JOeLfdTDafrbcJ8CLs= github.com/alitto/pond v1.9.2/go.mod h1:xQn3P/sHTYcU/1BR3i86IGIrilcrGC2LiS+E2+CJWsI= github.com/anacrolix/btree v0.0.0-20251201064447-d86c3fa41bd8 h1:c02PsmoaChabVqAFm7pqPI1UIkDdDAjUaWa6ZmfxybQ= @@ -261,6 +265,8 @@ github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b h1:gMYJzEhLrmI github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b/go.mod h1:NpfXdK4ldEKkjQ4P1R+DBF4ua5VFOlxmgHROTnYrApg= github.com/getlantern/radiance v0.0.0-20260706185153-bce252479f03 h1:yKLQK9wMXlghg4ksSDenZVYbUBphblutaFMomS6ZqQU= github.com/getlantern/radiance v0.0.0-20260706185153-bce252479f03/go.mod h1:p6wcyzCrsFfrdrLNuQirB0ugeg7OjdFlZmBgNTu4D1w= +github.com/getlantern/radiance v0.0.0-20260709081430-208ad3bc7646 h1:BNjlwSxAUNTYIEQKW2OgZ5Ghdt6ruTApd+X92obMSKU= +github.com/getlantern/radiance v0.0.0-20260709081430-208ad3bc7646/go.mod h1:rXbNFXzQvbnlIlaIF/6EJTwxK9DrAyKowkpUq/5udkI= github.com/getlantern/samizdat v0.0.3-0.20260529191731-5ea8ae61ddbf h1:KxiMF+oG0rTtuBi7GiIaHfccYOf69rLJ/VnO5myoYc4= github.com/getlantern/samizdat v0.0.3-0.20260529191731-5ea8ae61ddbf/go.mod h1:uEeykQSW2/6rTjfPlj3MTTo59poSHXfAHTGgzYDkbr0= github.com/getlantern/semconv v0.0.0-20260327040646-21845dda05cb h1:c5YM7b3a4r2J8Eh89KkI6M/iTFe6Bi+b8AJlfkKdFq4= diff --git a/lib/lantern/lantern_generated_bindings.dart b/lib/lantern/lantern_generated_bindings.dart index 8a58d1d253..eb73933dce 100644 --- a/lib/lantern/lantern_generated_bindings.dart +++ b/lib/lantern/lantern_generated_bindings.dart @@ -5681,12 +5681,14 @@ class LanternBindings { ffi.Pointer _planId, ffi.Pointer _email, ffi.Pointer _idempotencyKey, + ffi.Pointer _couponCode, ) { return _stripeSubscriptionPaymentRedirect( subType, _planId, _email, _idempotencyKey, + _couponCode, ); } @@ -5698,6 +5700,7 @@ class LanternBindings { ffi.Pointer, ffi.Pointer, ffi.Pointer, + ffi.Pointer, ) > >('stripeSubscriptionPaymentRedirect'); @@ -5709,6 +5712,7 @@ class LanternBindings { ffi.Pointer, ffi.Pointer, ffi.Pointer, + ffi.Pointer, ) >(); @@ -5717,8 +5721,15 @@ class LanternBindings { ffi.Pointer _provider, ffi.Pointer _email, ffi.Pointer _idempotencyKey, + ffi.Pointer _couponCode, ) { - return _paymentRedirect(_plan, _provider, _email, _idempotencyKey); + return _paymentRedirect( + _plan, + _provider, + _email, + _idempotencyKey, + _couponCode, + ); } late final _paymentRedirectPtr = @@ -5729,6 +5740,7 @@ class LanternBindings { ffi.Pointer, ffi.Pointer, ffi.Pointer, + ffi.Pointer, ) > >('paymentRedirect'); @@ -5739,6 +5751,7 @@ class LanternBindings { ffi.Pointer, ffi.Pointer, ffi.Pointer, + ffi.Pointer, ) >(); @@ -5964,6 +5977,30 @@ class LanternBindings { ) >(); + ffi.Pointer referralAttachmentV2( + ffi.Pointer _referralCode, + ffi.Pointer _channel, + ) { + return _referralAttachmentV2(_referralCode, _channel); + } + + late final _referralAttachmentV2Ptr = + _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >('referralAttachmentV2'); + late final _referralAttachmentV2 = _referralAttachmentV2Ptr + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); + ffi.Pointer startChangeEmail( ffi.Pointer _newEmail, ffi.Pointer _password, From 266fb89b28fae9840f305362fe3a4baa887e5ae9 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Fri, 10 Jul 2026 18:16:38 +0530 Subject: [PATCH 19/22] Do not update plans on a referral code. --- lib/core/models/referral_attach_response.dart | 14 +++++++++----- lib/features/plans/provider/referral_notifier.dart | 9 ++++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/core/models/referral_attach_response.dart b/lib/core/models/referral_attach_response.dart index ca5fba692e..acc16385e4 100644 --- a/lib/core/models/referral_attach_response.dart +++ b/lib/core/models/referral_attach_response.dart @@ -11,14 +11,14 @@ enum ReferralType { affiliate; static ReferralType fromString(String? value) => switch (value) { - 'referral' => ReferralType.referral, + 'referall' => ReferralType.referral, 'affiliate' => ReferralType.affiliate, _ => ReferralType.none, }; } class ReferralAttachV2Response { - final PlansData plansData; + final PlansData? plansData; final String code; final int discountPct; final ReferralType type; @@ -32,14 +32,18 @@ class ReferralAttachV2Response { factory ReferralAttachV2Response.fromJson(Map json) => ReferralAttachV2Response( - plansData: PlansData.fromJson(json)..sortPlansAndProviders(), + // `plans` is only present for the affiliate flow; when it's absent this + // is a referral-type response, so leave plansData null. + plansData: json['plans'] != null + ? (PlansData.fromJson(json)..sortPlansAndProviders()) + : null, code: json["code"] ?? '', + type: ReferralType.fromString(json["referralType"]), discountPct: json["discountPct"] ?? 0, - type: ReferralType.fromString(json["type"]), ); Map toJson() => { - ...plansData.toJson(), + ...?plansData?.toJson(), "code": code, "discountPct": discountPct, "type": type.name, diff --git a/lib/features/plans/provider/referral_notifier.dart b/lib/features/plans/provider/referral_notifier.dart index 6a2b16f352..687ffb639d 100644 --- a/lib/features/plans/provider/referral_notifier.dart +++ b/lib/features/plans/provider/referral_notifier.dart @@ -27,9 +27,12 @@ class ReferralNotifier extends _$ReferralNotifier { if (result.isRight()) { final response = result.getRight().toNullable(); state = response; - if (response != null) { - ref.read(plansProvider.notifier).updatePlans(response.plansData); - final plans = response.plansData.plans; + // plansData is only present for the affiliate flow; the referral flow + // has no discounted plans to push into the plans UI. + final plansData = response?.plansData; + if (plansData != null) { + ref.read(plansProvider.notifier).updatePlans(plansData); + final plans = plansData.plans; if (plans.isNotEmpty) { // The backend may not flag a best-value plan (and discounted sets // may omit it entirely), so fall back to the first plan rather than From 6950341f53b979b0fd7ad6c42f9e255d6d38437d Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Fri, 10 Jul 2026 18:17:28 +0530 Subject: [PATCH 20/22] update radiance --- go.mod | 4 ++-- go.sum | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 8451edb395..e683ca892d 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/getlantern/lantern go 1.26.2 -// replace github.com/getlantern/radiance => ../radiance +//replace github.com/getlantern/radiance => ../radiance // replace github.com/getlantern/lantern-server-provisioner => ../lantern-server-provisioner @@ -25,7 +25,7 @@ replace github.com/quic-go/qpack => github.com/quic-go/qpack v0.5.1 require ( github.com/alecthomas/assert/v2 v2.3.0 github.com/getlantern/lantern-server-provisioner v0.0.0-20251031121934-8ea031fccfa9 - github.com/getlantern/radiance v0.0.0-20260709081430-208ad3bc7646 + github.com/getlantern/radiance v0.0.0-20260710102102-8616f916e1e3 github.com/sagernet/sing-box v1.12.22 golang.org/x/mobile v0.0.0-20250711185624-d5bb5ecc55c0 golang.org/x/sys v0.45.0 diff --git a/go.sum b/go.sum index 1fb52574e3..9b936e9ed5 100644 --- a/go.sum +++ b/go.sum @@ -60,10 +60,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= -github.com/alexflint/go-arg v1.6.1 h1:uZogJ6VDBjcuosydKgvYYRhh9sRCusjOvoOLZopBlnA= -github.com/alexflint/go-arg v1.6.1/go.mod h1:nQ0LFYftLJ6njcaee0sU+G0iS2+2XJQfA8I062D0LGc= -github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw= -github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= github.com/alitto/pond v1.9.2 h1:9Qb75z/scEZVCoSU+osVmQ0I0JOeLfdTDafrbcJ8CLs= github.com/alitto/pond v1.9.2/go.mod h1:xQn3P/sHTYcU/1BR3i86IGIrilcrGC2LiS+E2+CJWsI= github.com/anacrolix/btree v0.0.0-20251201064447-d86c3fa41bd8 h1:c02PsmoaChabVqAFm7pqPI1UIkDdDAjUaWa6ZmfxybQ= @@ -263,10 +259,8 @@ github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 h1:rtDmW8YL github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535/go.mod h1:WKJEdjMOD4IuTRYwjQHjT4bmqDl5J82RShMLxPAvi0Q= github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b h1:gMYJzEhLrmIqQ+JnjiYNm+UyUDalK3WUmVyecFwmV5g= github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b/go.mod h1:NpfXdK4ldEKkjQ4P1R+DBF4ua5VFOlxmgHROTnYrApg= -github.com/getlantern/radiance v0.0.0-20260706185153-bce252479f03 h1:yKLQK9wMXlghg4ksSDenZVYbUBphblutaFMomS6ZqQU= -github.com/getlantern/radiance v0.0.0-20260706185153-bce252479f03/go.mod h1:p6wcyzCrsFfrdrLNuQirB0ugeg7OjdFlZmBgNTu4D1w= -github.com/getlantern/radiance v0.0.0-20260709081430-208ad3bc7646 h1:BNjlwSxAUNTYIEQKW2OgZ5Ghdt6ruTApd+X92obMSKU= -github.com/getlantern/radiance v0.0.0-20260709081430-208ad3bc7646/go.mod h1:rXbNFXzQvbnlIlaIF/6EJTwxK9DrAyKowkpUq/5udkI= +github.com/getlantern/radiance v0.0.0-20260710102102-8616f916e1e3 h1:YCc0+wtDuFJOHWinASKi+yg7shPyP5FsZwUJ0+aV888= +github.com/getlantern/radiance v0.0.0-20260710102102-8616f916e1e3/go.mod h1:rXbNFXzQvbnlIlaIF/6EJTwxK9DrAyKowkpUq/5udkI= github.com/getlantern/samizdat v0.0.3-0.20260529191731-5ea8ae61ddbf h1:KxiMF+oG0rTtuBi7GiIaHfccYOf69rLJ/VnO5myoYc4= github.com/getlantern/samizdat v0.0.3-0.20260529191731-5ea8ae61ddbf/go.mod h1:uEeykQSW2/6rTjfPlj3MTTo59poSHXfAHTGgzYDkbr0= github.com/getlantern/semconv v0.0.0-20260327040646-21845dda05cb h1:c5YM7b3a4r2J8Eh89KkI6M/iTFe6Bi+b8AJlfkKdFq4= From 850059a793204d216d54b850e4e7ca88f26b6763 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Mon, 13 Jul 2026 16:25:39 +0530 Subject: [PATCH 21/22] more UI chaneg --- assets/locales/en.po | 2 +- go.mod | 2 +- go.sum | 4 +- lantern-core/core.go | 8 ++- lib/core/common/app_theme.dart | 82 ++++++++++++++++++------------- lib/core/widgets/base_screen.dart | 3 ++ lib/features/plans/plans.dart | 5 +- 7 files changed, 64 insertions(+), 42 deletions(-) diff --git a/assets/locales/en.po b/assets/locales/en.po index fcab777c9d..753ab59599 100644 --- a/assets/locales/en.po +++ b/assets/locales/en.po @@ -1237,7 +1237,7 @@ msgid "referral_message_2y" msgstr "+ 2 additional month free" msgid "referral_code_invalid" -msgstr "The referral code you entered is invalid. Please check the code and try again." +msgstr "The referral/promo code you entered is invalid. Please check the code and try again." msgid "referral_code_own_invalid" msgstr "You cannot use your own referral code. Please enter a different code." diff --git a/go.mod b/go.mod index e683ca892d..18eb252c45 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ replace github.com/quic-go/qpack => github.com/quic-go/qpack v0.5.1 require ( github.com/alecthomas/assert/v2 v2.3.0 github.com/getlantern/lantern-server-provisioner v0.0.0-20251031121934-8ea031fccfa9 - github.com/getlantern/radiance v0.0.0-20260710102102-8616f916e1e3 + github.com/getlantern/radiance v0.0.0-20260713105203-0c80efbadf8e github.com/sagernet/sing-box v1.12.22 golang.org/x/mobile v0.0.0-20250711185624-d5bb5ecc55c0 golang.org/x/sys v0.45.0 diff --git a/go.sum b/go.sum index 9b936e9ed5..d933553ecb 100644 --- a/go.sum +++ b/go.sum @@ -259,8 +259,8 @@ github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 h1:rtDmW8YL github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535/go.mod h1:WKJEdjMOD4IuTRYwjQHjT4bmqDl5J82RShMLxPAvi0Q= github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b h1:gMYJzEhLrmIqQ+JnjiYNm+UyUDalK3WUmVyecFwmV5g= github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b/go.mod h1:NpfXdK4ldEKkjQ4P1R+DBF4ua5VFOlxmgHROTnYrApg= -github.com/getlantern/radiance v0.0.0-20260710102102-8616f916e1e3 h1:YCc0+wtDuFJOHWinASKi+yg7shPyP5FsZwUJ0+aV888= -github.com/getlantern/radiance v0.0.0-20260710102102-8616f916e1e3/go.mod h1:rXbNFXzQvbnlIlaIF/6EJTwxK9DrAyKowkpUq/5udkI= +github.com/getlantern/radiance v0.0.0-20260713105203-0c80efbadf8e h1:54+UT+w8P3dm9rnIQ6/mVJ67WKp1ZQlMlwNbD1SRB1k= +github.com/getlantern/radiance v0.0.0-20260713105203-0c80efbadf8e/go.mod h1:rXbNFXzQvbnlIlaIF/6EJTwxK9DrAyKowkpUq/5udkI= github.com/getlantern/samizdat v0.0.3-0.20260529191731-5ea8ae61ddbf h1:KxiMF+oG0rTtuBi7GiIaHfccYOf69rLJ/VnO5myoYc4= github.com/getlantern/samizdat v0.0.3-0.20260529191731-5ea8ae61ddbf/go.mod h1:uEeykQSW2/6rTjfPlj3MTTo59poSHXfAHTGgzYDkbr0= github.com/getlantern/semconv v0.0.0-20260327040646-21845dda05cb h1:c5YM7b3a4r2J8Eh89KkI6M/iTFe6Bi+b8AJlfkKdFq4= diff --git a/lantern-core/core.go b/lantern-core/core.go index 736a3f6e03..e5d3123144 100644 --- a/lantern-core/core.go +++ b/lantern-core/core.go @@ -861,13 +861,17 @@ func (lc *LanternCore) CompleteChangeEmail(email, password, code string) error { } func (lc *LanternCore) ReferralAttachment(referralCode string) (bool, error) { - return lc.client.ReferralAttach(lc.ctx, referralCode) + // Empty channel selects the legacy v1 referral-attach API. + if _, err := lc.client.ReferralAttach(lc.ctx, referralCode, ""); err != nil { + return false, err + } + return true, nil } // ReferralAttachmentV2 attaches a referral code and returns the resulting // plans, providers, code, and discount marshalled as JSON. func (lc *LanternCore) ReferralAttachmentV2(referralCode, channel string) ([]byte, error) { - resp, err := lc.client.ReferralAttachV2(lc.ctx, referralCode, channel) + resp, err := lc.client.ReferralAttach(lc.ctx, referralCode, channel) if err != nil { return nil, err } diff --git a/lib/core/common/app_theme.dart b/lib/core/common/app_theme.dart index ee9ea2496c..fa985bc75a 100644 --- a/lib/core/common/app_theme.dart +++ b/lib/core/common/app_theme.dart @@ -141,8 +141,10 @@ class AppTheme { inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: cs.surface, // bg.input = bg.elevated - contentPadding: - const EdgeInsets.symmetric(vertical: 20, horizontal: 16), + contentPadding: const EdgeInsets.symmetric( + vertical: 20, + horizontal: 16, + ), hintStyle: TextStyle(color: AppColors.gray4), labelStyle: TextStyle(color: cs.onSurfaceVariant), // text.secondary border: OutlineInputBorder( @@ -156,7 +158,9 @@ class AppTheme { focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide( - color: AppColors.blue8, width: 2), // border.input-focus + color: AppColors.blue8, + width: 2, + ), // border.input-focus ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), @@ -179,12 +183,10 @@ class AppTheme { borderRadius: BorderRadius.circular(16), side: BorderSide(color: cs.outlineVariant, width: 1), ), - titleTextStyle: AppTextStyles.headingSmall, - contentTextStyle: AppTextStyles.bodyMedium, - // Material dialog width: min 280dp, max 560dp (M3 guideline). Applied + // Material dialog width: min 280dp, max 520dp (M3 guideline). Applied // app-wide so dialogs stay a sensible width on phones, tablets and // desktop instead of collapsing to content or going full-bleed. - constraints: const BoxConstraints(minWidth: 280, maxWidth: 560), + constraints: const BoxConstraints(minWidth: 280, maxWidth: 520), ), // ── Bottom Sheet ───────────────────────────────────────────────────────── @@ -226,8 +228,10 @@ class AppTheme { backgroundColor: cs.primary, // action.primary.primary-bg enableFeedback: true, foregroundColor: cs.onPrimary, // action.primary.primary-text - textStyle: AppTextStyles.primaryButtonTextStyle - .copyWith(fontSize: 18.0, color: cs.onPrimary), + textStyle: AppTextStyles.primaryButtonTextStyle.copyWith( + fontSize: 18.0, + color: cs.onPrimary, + ), overlayColor: AppColors.blue6, minimumSize: const Size(double.infinity, 52), tapTargetSize: MaterialTapTargetSize.padded, @@ -296,25 +300,26 @@ class AppTheme { selectionColor: AppColors.blue7, selectionHandleColor: AppColors.blue5, ), - textTheme: GoogleFonts.urbanistTextTheme( - ThemeData(brightness: Brightness.dark).textTheme, - ).copyWith( - bodyLarge: AppTextStyles.bodyLarge, - bodyMedium: AppTextStyles.bodyMedium, - bodySmall: AppTextStyles.bodySmall, - displayLarge: AppTextStyles.displayLarge, - displayMedium: AppTextStyles.displayMedium, - displaySmall: AppTextStyles.displaySmall, - headlineLarge: AppTextStyles.headingLarge, - headlineMedium: AppTextStyles.headingMedium, - headlineSmall: AppTextStyles.headingSmall, - labelLarge: AppTextStyles.labelLarge, - labelMedium: AppTextStyles.labelMedium, - labelSmall: AppTextStyles.labelSmall, - titleLarge: AppTextStyles.titleLarge, - titleMedium: AppTextStyles.titleMedium, - titleSmall: AppTextStyles.titleSmall, - ), + textTheme: + GoogleFonts.urbanistTextTheme( + ThemeData(brightness: Brightness.dark).textTheme, + ).copyWith( + bodyLarge: AppTextStyles.bodyLarge, + bodyMedium: AppTextStyles.bodyMedium, + bodySmall: AppTextStyles.bodySmall, + displayLarge: AppTextStyles.displayLarge, + displayMedium: AppTextStyles.displayMedium, + displaySmall: AppTextStyles.displaySmall, + headlineLarge: AppTextStyles.headingLarge, + headlineMedium: AppTextStyles.headingMedium, + headlineSmall: AppTextStyles.headingSmall, + labelLarge: AppTextStyles.labelLarge, + labelMedium: AppTextStyles.labelMedium, + labelSmall: AppTextStyles.labelSmall, + titleLarge: AppTextStyles.titleLarge, + titleMedium: AppTextStyles.titleMedium, + titleSmall: AppTextStyles.titleSmall, + ), // ── AppBar ────────────────────────────────────────────────────────────── appBarTheme: AppBarTheme( @@ -358,11 +363,14 @@ class AppTheme { inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: cs.surface, // bg.input dark (gray850) - contentPadding: - const EdgeInsets.symmetric(vertical: 20, horizontal: 16), + contentPadding: const EdgeInsets.symmetric( + vertical: 20, + horizontal: 16, + ), hintStyle: TextStyle(color: AppColors.gray5), // text.disabled dark - labelStyle: - TextStyle(color: cs.onSurfaceVariant), // text.secondary dark + labelStyle: TextStyle( + color: cs.onSurfaceVariant, + ), // text.secondary dark border: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide(color: cs.outlineVariant), // border.input dark @@ -374,7 +382,9 @@ class AppTheme { focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide( - color: AppColors.blue2, width: 2), // border.input-focus dark + color: AppColors.blue2, + width: 2, + ), // border.input-focus dark ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), @@ -444,8 +454,10 @@ class AppTheme { backgroundColor: cs.primary, // action.primary.primary-bg dark enableFeedback: true, foregroundColor: cs.onPrimary, // action.primary.primary-text - textStyle: AppTextStyles.primaryButtonTextStyle - .copyWith(fontSize: 18.0, color: cs.onPrimary), + textStyle: AppTextStyles.primaryButtonTextStyle.copyWith( + fontSize: 18.0, + color: cs.onPrimary, + ), overlayColor: AppColors.blue5, // action.primary.primary-bg-hover dark minimumSize: const Size(double.infinity, 52), tapTargetSize: MaterialTapTargetSize.padded, diff --git a/lib/core/widgets/base_screen.dart b/lib/core/widgets/base_screen.dart index 37cf392dc5..8889ba2d98 100644 --- a/lib/core/widgets/base_screen.dart +++ b/lib/core/widgets/base_screen.dart @@ -11,6 +11,7 @@ class BaseScreen extends StatelessWidget { final Widget? bottomSheet; final Color? backgroundColor; final bool extendBody; + final bool? resizeToAvoidBottomInset; const BaseScreen({ super.key, @@ -22,6 +23,7 @@ class BaseScreen extends StatelessWidget { this.backgroundColor, this.extendBody = false, this.bottomSheet, + this.resizeToAvoidBottomInset, }); @override @@ -41,6 +43,7 @@ class BaseScreen extends StatelessWidget { bottomSheet: bottomSheet, bottomNavigationBar: bottomNavigationBar, extendBody: extendBody, + resizeToAvoidBottomInset: resizeToAvoidBottomInset, ); } } diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index 0baec2dbf9..c2fc310bfb 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -42,6 +42,7 @@ class _PlansState extends ConsumerState return BaseScreen( backgroundColor: context.bgElevated, padded: false, + resizeToAvoidBottomInset: false, appBar: CustomAppBar( title: SizedBox( height: 20.h, @@ -326,7 +327,9 @@ class _PlansState extends ConsumerState ), SizedBox(height: 24), AppTextField( - label: 'promo_referral_code'.i18n, + label: isStoreVersion() + ? 'promo_code'.i18n + : 'promo_referral_code'.i18n, controller: referralCodeController, hintText: 'XXXXXX', prefixIcon: AppImagePaths.star, From 010f990ae59db3b04ee2b94eac3441e01d6f98b6 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Mon, 13 Jul 2026 18:19:05 +0530 Subject: [PATCH 22/22] UI fixes --- lib/features/plans/plan_item.dart | 4 +- lib/features/plans/plans.dart | 210 +++++++++++++++--------------- 2 files changed, 105 insertions(+), 109 deletions(-) diff --git a/lib/features/plans/plan_item.dart b/lib/features/plans/plan_item.dart index f2c54db9ac..cdc7c68461 100644 --- a/lib/features/plans/plan_item.dart +++ b/lib/features/plans/plan_item.dart @@ -32,7 +32,9 @@ class PlanItem extends StatelessWidget { final originalPrice = plan.formatOriginalPrice; final showOriginalPrice = discountPct > 0 && originalPrice.isNotEmpty; return badges.Badge( - showBadge: plan.bestValue ?? false, + // Hide the Best Value badge when an affiliate discount is applied so it + // doesn't compete with the discounted strikethrough pricing. + showBadge: (plan.bestValue) && discountPct == 0, badgeAnimation: badges.BadgeAnimation.scale(toAnimate: false), position: badges.BadgePosition.custom(start: (finalSize - 10)), // Adjust values as needed diff --git a/lib/features/plans/plans.dart b/lib/features/plans/plans.dart index c2fc310bfb..627109d42b 100644 --- a/lib/features/plans/plans.dart +++ b/lib/features/plans/plans.dart @@ -66,128 +66,118 @@ class _PlansState extends ConsumerState Widget _buildBody() { final plansState = ref.watch(plansProvider); - final size = MediaQuery.of(context).size; return Column( children: [ - Padding( - padding: EdgeInsets.symmetric(horizontal: defaultSize), - child: SizedBox( - height: context.isSmallDevice - ? size.height * 0.4 - : PlatformUtils.isIOS - ? size.height * 0.3 - : size.height * 0.35, - child: SingleChildScrollView(child: FeatureList()), + // Features fill whatever space the bottom section leaves, centered. + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: defaultSize), + child: Center(child: FeatureList()), ), ), SizedBox(height: defaultSize), DividerSpace(padding: EdgeInsets.zero), - Expanded( - child: Container( - color: context.bgSurface, - padding: EdgeInsets.symmetric( - horizontal: context.isSmallDevice ? 0 : defaultSize, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - SizedBox(height: 10), - _buildAffiliateBanner(), - Padding( - padding: EdgeInsets.only( - left: context.isSmallDevice ? 16 : 0, - ), - child: plansState.when( - data: (data) { - return PlansListView(data: data); - }, - loading: () { - return Center(child: LoadingIndicator()); - }, - error: (error, stackTrace) { - return Column( - children: [ - Text( - 'plans_fetch_error'.i18n, - style: textTheme.labelLarge, - ), - AppTextButton( - label: 'Try again', - onPressed: () { - ref.read(plansProvider.notifier).fetchPlans(); - }, - ), - ], - ); - }, - ), - ), - SizedBox(height: defaultSize), - Padding( - padding: EdgeInsets.symmetric( - horizontal: context.isSmallDevice ? defaultSize : 0, - ), - child: PrimaryButton( - label: 'get_lantern_pro'.i18n, - isTaller: true, - onPressed: onGetLanternProTap, - ), - ), - if (isStoreVersion()) ...[ - SizedBox(height: 8), - Center( - child: AppRichText( - texts: '${'already_purchased'.i18n} ', - boldTexts: 'restore_purchase'.i18n, - boldUnderline: true, - boldOnPressed: _restorePurchaseFlow, - ), - ), - ], - if (PlatformUtils.isIOS) ...{ - SizedBox(height: 8), - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Text( - 'subscription_renewal_info'.i18n, - style: textTheme.labelMedium!.copyWith( - color: context.textTertiary, - ), - ), - ), - IntrinsicHeight( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - AppTextButton( - label: 'privacy_policy'.i18n, - fontSize: 12, - textColor: context.textTertiary, - onPressed: () { - UrlUtils.openWithSystemBrowser( - AppUrls.privacyPolicy, - ); - }, + // Bottom section sizes to its content: plans, then the CTA button. + Container( + color: context.bgSurface, + padding: EdgeInsets.symmetric( + horizontal: context.isSmallDevice ? 0 : defaultSize, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox(height: 10), + _buildAffiliateBanner(), + Padding( + padding: EdgeInsets.only(left: context.isSmallDevice ? 16 : 0), + child: plansState.when( + data: (data) { + return PlansListView(data: data); + }, + loading: () { + return Center(child: LoadingIndicator()); + }, + error: (error, stackTrace) { + return Column( + children: [ + Text( + 'plans_fetch_error'.i18n, + style: textTheme.labelLarge, ), - VerticalDivider(indent: 10, endIndent: 10), AppTextButton( - label: 'terms_of_service'.i18n, - fontSize: 12, - textColor: context.textTertiary, + label: 'Try again', onPressed: () { - UrlUtils.openWithSystemBrowser( - AppUrls.termsOfService, - ); + ref.read(plansProvider.notifier).fetchPlans(); }, ), ], - ), + ); + }, + ), + ), + SizedBox(height: defaultSize), + Padding( + padding: EdgeInsets.symmetric( + horizontal: context.isSmallDevice ? defaultSize : 0, + ), + child: PrimaryButton( + label: 'get_lantern_pro'.i18n, + isTaller: true, + onPressed: onGetLanternProTap, + ), + ), + if (isStoreVersion()) ...[ + SizedBox(height: 8), + Center( + child: AppRichText( + texts: '${'already_purchased'.i18n} ', + boldTexts: 'restore_purchase'.i18n, + boldUnderline: true, + boldOnPressed: _restorePurchaseFlow, ), - }, - SizedBox(height: size24), + ), ], - ), + if (PlatformUtils.isIOS) ...{ + SizedBox(height: 8), + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: Text( + 'subscription_renewal_info'.i18n, + style: textTheme.labelMedium!.copyWith( + color: context.textTertiary, + ), + ), + ), + IntrinsicHeight( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + AppTextButton( + label: 'privacy_policy'.i18n, + fontSize: 12, + textColor: context.textTertiary, + onPressed: () { + UrlUtils.openWithSystemBrowser(AppUrls.privacyPolicy); + }, + ), + VerticalDivider(indent: 10, endIndent: 10), + AppTextButton( + label: 'terms_of_service'.i18n, + fontSize: 12, + textColor: context.textTertiary, + onPressed: () { + UrlUtils.openWithSystemBrowser( + AppUrls.termsOfService, + ); + }, + ), + ], + ), + ), + }, + SizedBox(height: size24), + ], ), ), ], @@ -333,6 +323,10 @@ class _PlansState extends ConsumerState controller: referralCodeController, hintText: 'XXXXXX', prefixIcon: AppImagePaths.star, + autofocus: true, + textInputAction: TextInputAction.done, + onSubmitted: (value) => + onReferralCodeContinue(value.toUpperCase().trim()), ), ], ),