Mztj67_#M}W?l>kYSliK<%xAp;0j{!}J0!o7b
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
literal 0
HcmV?d00001
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8
GIT binary patch
literal 1418
zcmV;51$Fv~P)q
zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+
zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq
z^={4hPQv)y=I|4n+?>7Fim=dxt1
z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT
zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf`
zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_>
z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3
zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF
z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a
z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE
z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62(
zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;?
zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-<
z{s<&cCV_1`^TD^ia9!*mQDq&
zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw
zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv
zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF
z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC
YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H
literal 0
HcmV?d00001
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838
GIT binary patch
literal 68
zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J
Q1PU{Fy85}Sb4q9e0B4a5jsO4v
literal 0
HcmV?d00001
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838
GIT binary patch
literal 68
zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J
Q1PU{Fy85}Sb4q9e0B4a5jsO4v
literal 0
HcmV?d00001
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838
GIT binary patch
literal 68
zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J
Q1PU{Fy85}Sb4q9e0B4a5jsO4v
literal 0
HcmV?d00001
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
new file mode 100644
index 0000000..5967a7e
--- /dev/null
+++ b/ios/Runner/Info.plist
@@ -0,0 +1,74 @@
+
+
+
+
+ CADisableMinimumFrameDurationOnPhone
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Arcx
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ arcx
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneClassName
+ UIWindowScene
+ UISceneConfigurationName
+ flutter
+ UISceneDelegateClassName
+ $(PRODUCT_MODULE_NAME).SceneDelegate
+ UISceneStoryboardFile
+ Main
+
+
+
+
+ UIApplicationSupportsIndirectInputEvents
+
+ NSCameraUsageDescription
+ 用于拍摄并设置个人头像
+ NSPhotoLibraryUsageDescription
+ 用于从相册选择并设置个人头像
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..308a2a5
--- /dev/null
+++ b/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/ios/Runner/SceneDelegate.swift b/ios/Runner/SceneDelegate.swift
new file mode 100644
index 0000000..b9ce8ea
--- /dev/null
+++ b/ios/Runner/SceneDelegate.swift
@@ -0,0 +1,6 @@
+import Flutter
+import UIKit
+
+class SceneDelegate: FlutterSceneDelegate {
+
+}
diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 0000000..86a7c3b
--- /dev/null
+++ b/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,12 @@
+import Flutter
+import UIKit
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+ func testExample() {
+ // If you add code to the Runner application, consider adding tests here.
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+ }
+
+}
diff --git a/lib/app/app.dart b/lib/app/app.dart
new file mode 100644
index 0000000..35432b0
--- /dev/null
+++ b/lib/app/app.dart
@@ -0,0 +1,18 @@
+import 'package:flutter/material.dart';
+
+import '../features/home/presentation/point_book_home_screen.dart';
+import 'app_theme.dart';
+
+class ArcxApp extends StatelessWidget {
+ const ArcxApp({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: '记分本',
+ debugShowCheckedModeBanner: false,
+ theme: AppTheme.light(),
+ home: const PointBookHomeScreen(),
+ );
+ }
+}
diff --git a/lib/app/app_theme.dart b/lib/app/app_theme.dart
new file mode 100644
index 0000000..b18349d
--- /dev/null
+++ b/lib/app/app_theme.dart
@@ -0,0 +1,67 @@
+import 'package:flutter/material.dart';
+
+class AppTheme {
+ const AppTheme._();
+
+ static const Color primary = Color(0xFF1F7A4D);
+ static const Color primaryDark = Color(0xFF13533A);
+ static const Color accent = Color(0xFFE8A33D);
+ static const Color scoreRing = Color(0xFFE0B350);
+ static const Color hitPoint = Color(0xFFE5484D);
+ static const Color surfaceMuted = Color(0xFFF4F6F5);
+ static const Color textSecondary = Color(0xFF6B7280);
+
+ static ThemeData light() {
+ final base = ThemeData.light(useMaterial3: true);
+
+ return base.copyWith(
+ colorScheme: const ColorScheme.light(
+ primary: primary,
+ secondary: accent,
+ surface: Colors.white,
+ onPrimary: Colors.white,
+ onSecondary: Colors.black,
+ ),
+ scaffoldBackgroundColor: surfaceMuted,
+ appBarTheme: const AppBarTheme(
+ backgroundColor: primary,
+ foregroundColor: Colors.white,
+ elevation: 0,
+ centerTitle: true,
+ ),
+ cardTheme: CardThemeData(
+ color: Colors.white,
+ elevation: 0,
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
+ margin: EdgeInsets.zero,
+ ),
+ elevatedButtonTheme: ElevatedButtonThemeData(
+ style: ElevatedButton.styleFrom(
+ backgroundColor: primary,
+ foregroundColor: Colors.white,
+ minimumSize: const Size.fromHeight(48),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
+ ),
+ ),
+ outlinedButtonTheme: OutlinedButtonThemeData(
+ style: OutlinedButton.styleFrom(
+ foregroundColor: primary,
+ minimumSize: const Size.fromHeight(48),
+ side: const BorderSide(color: primary),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
+ ),
+ ),
+ inputDecorationTheme: InputDecorationTheme(
+ border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
+ contentPadding: const EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 14,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart
new file mode 100644
index 0000000..94321cb
--- /dev/null
+++ b/lib/bootstrap.dart
@@ -0,0 +1,11 @@
+import 'package:flutter/widgets.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import 'app/app.dart';
+import 'data/local/app_database.dart';
+
+Future bootstrap() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ await AppDatabase.instance.open();
+ runApp(const ProviderScope(child: ArcxApp()));
+}
diff --git a/lib/core/constants/app_defaults.dart b/lib/core/constants/app_defaults.dart
new file mode 100644
index 0000000..35ecd08
--- /dev/null
+++ b/lib/core/constants/app_defaults.dart
@@ -0,0 +1,15 @@
+class AppDefaults {
+ const AppDefaults._();
+
+ static const int singletonId = 1;
+
+ static const int defaultFreeTrialLimit = 2;
+ static const int nicknameRandomMin = 1000;
+ static const int nicknameRandomMax = 9999;
+
+ static const String defaultAvatarAssetPath =
+ 'assets/images/avatar_default.png';
+
+ static const String localDatabaseName = 'arcx_local_first_db';
+ static const String avatarFileName = 'avatar.png';
+}
diff --git a/lib/core/providers/app_providers.dart b/lib/core/providers/app_providers.dart
new file mode 100644
index 0000000..a76dc7a
--- /dev/null
+++ b/lib/core/providers/app_providers.dart
@@ -0,0 +1,15 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:isar/isar.dart';
+
+import '../../data/local/app_database.dart';
+
+/// Exposes the opened Isar instance. Throws if [AppDatabase.open] has not run.
+final isarProvider = FutureProvider((ref) => AppDatabase.instance.open());
+
+/// Apple IAP product identifier for the one-time unlock.
+/// Configure the matching product in App Store Connect when publishing.
+const String kUnlockProductId = 'com.shelingxingqiu.arcx.unlock';
+
+/// Whether the build is a debug build (used to surface a dev-only unlock).
+final bool kIsDebugBuild = kDebugMode;
diff --git a/lib/data/local/app_database.dart b/lib/data/local/app_database.dart
new file mode 100644
index 0000000..c522728
--- /dev/null
+++ b/lib/data/local/app_database.dart
@@ -0,0 +1,74 @@
+import 'package:isar/isar.dart';
+import 'package:path_provider/path_provider.dart';
+
+import '../../core/constants/app_defaults.dart';
+import 'models/models.dart';
+
+class AppDatabase {
+ AppDatabase._();
+
+ static final AppDatabase instance = AppDatabase._();
+
+ Isar? _isar;
+
+ Future open() async {
+ final existing = _isar;
+ if (existing != null && existing.isOpen) {
+ return existing;
+ }
+
+ final directory = await getApplicationDocumentsDirectory();
+ final isar = await Isar.open(
+ [
+ UserProfileSchema,
+ AppConfigSchema,
+ PointRecordSchema,
+ ],
+ name: AppDefaults.localDatabaseName,
+ directory: directory.path,
+ inspector: false,
+ );
+
+ await _ensureBootstrapData(isar);
+ _isar = isar;
+ return isar;
+ }
+
+ Future close() async {
+ final database = _isar;
+ if (database == null || !database.isOpen) {
+ return;
+ }
+
+ await database.close();
+ _isar = null;
+ }
+
+ Future resetSingletonData() async {
+ final isar = await open();
+ await isar.writeTxn(() async {
+ await isar.userProfiles.put(UserProfile.createDefault());
+ await isar.appConfigs.put(AppConfig.createDefault());
+ });
+ }
+
+ Future _ensureBootstrapData(Isar isar) async {
+ final hasProfile =
+ await isar.userProfiles.get(AppDefaults.singletonId) != null;
+ final hasConfig = await isar.appConfigs.get(AppDefaults.singletonId) != null;
+
+ if (hasProfile && hasConfig) {
+ return;
+ }
+
+ await isar.writeTxn(() async {
+ if (!hasProfile) {
+ await isar.userProfiles.put(UserProfile.createDefault());
+ }
+
+ if (!hasConfig) {
+ await isar.appConfigs.put(AppConfig.createDefault());
+ }
+ });
+ }
+}
diff --git a/lib/data/local/models/app_config.dart b/lib/data/local/models/app_config.dart
new file mode 100644
index 0000000..4e23ad4
--- /dev/null
+++ b/lib/data/local/models/app_config.dart
@@ -0,0 +1,76 @@
+import 'package:isar/isar.dart';
+
+import '../../../core/constants/app_defaults.dart';
+
+part 'app_config.g.dart';
+
+enum EntitlementSource {
+ none,
+ purchase,
+ restore,
+}
+
+@collection
+class AppConfig {
+ AppConfig();
+
+ Id id = AppDefaults.singletonId;
+
+ @Index(unique: true, replace: true)
+ late String configKey;
+
+ @Index(type: IndexType.value)
+ late DateTime updatedAt;
+
+ late DateTime createdAt;
+ int freeTrialLimit = AppDefaults.defaultFreeTrialLimit;
+ int usedTrialCount = 0;
+ bool isVipUnlocked = false;
+ DateTime? vipUnlockedAt;
+ String? lastIapErrorMessage;
+
+ @enumerated
+ EntitlementSource entitlementSource = EntitlementSource.none;
+
+ factory AppConfig.createDefault({DateTime? now}) {
+ final current = now ?? DateTime.now();
+
+ return AppConfig()
+ ..configKey = 'local_app_config'
+ ..createdAt = current
+ ..updatedAt = current
+ ..freeTrialLimit = AppDefaults.defaultFreeTrialLimit
+ ..usedTrialCount = 0
+ ..isVipUnlocked = false
+ ..entitlementSource = EntitlementSource.none;
+ }
+
+ int get remainingTrialCount {
+ final remaining = freeTrialLimit - usedTrialCount;
+ return remaining > 0 ? remaining : 0;
+ }
+
+ bool get canStartScoring => isVipUnlocked || usedTrialCount < freeTrialLimit;
+
+ AppConfig copyWith({
+ int? freeTrialLimit,
+ int? usedTrialCount,
+ bool? isVipUnlocked,
+ DateTime? vipUnlockedAt,
+ String? lastIapErrorMessage,
+ EntitlementSource? entitlementSource,
+ DateTime? updatedAt,
+ }) {
+ return AppConfig()
+ ..id = id
+ ..configKey = configKey
+ ..createdAt = createdAt
+ ..updatedAt = updatedAt ?? DateTime.now()
+ ..freeTrialLimit = freeTrialLimit ?? this.freeTrialLimit
+ ..usedTrialCount = usedTrialCount ?? this.usedTrialCount
+ ..isVipUnlocked = isVipUnlocked ?? this.isVipUnlocked
+ ..vipUnlockedAt = vipUnlockedAt ?? this.vipUnlockedAt
+ ..lastIapErrorMessage = lastIapErrorMessage ?? this.lastIapErrorMessage
+ ..entitlementSource = entitlementSource ?? this.entitlementSource;
+ }
+}
diff --git a/lib/data/local/models/app_config.g.dart b/lib/data/local/models/app_config.g.dart
new file mode 100644
index 0000000..798d34e
--- /dev/null
+++ b/lib/data/local/models/app_config.g.dart
@@ -0,0 +1,1718 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'app_config.dart';
+
+// **************************************************************************
+// IsarCollectionGenerator
+// **************************************************************************
+
+// coverage:ignore-file
+// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
+
+extension GetAppConfigCollection on Isar {
+ IsarCollection get appConfigs => this.collection();
+}
+
+const AppConfigSchema = CollectionSchema(
+ name: r'AppConfig',
+ id: -7085420701237142207,
+ properties: {
+ r'canStartScoring': PropertySchema(
+ id: 0,
+ name: r'canStartScoring',
+ type: IsarType.bool,
+ ),
+ r'configKey': PropertySchema(
+ id: 1,
+ name: r'configKey',
+ type: IsarType.string,
+ ),
+ r'createdAt': PropertySchema(
+ id: 2,
+ name: r'createdAt',
+ type: IsarType.dateTime,
+ ),
+ r'entitlementSource': PropertySchema(
+ id: 3,
+ name: r'entitlementSource',
+ type: IsarType.byte,
+ enumMap: _AppConfigentitlementSourceEnumValueMap,
+ ),
+ r'freeTrialLimit': PropertySchema(
+ id: 4,
+ name: r'freeTrialLimit',
+ type: IsarType.long,
+ ),
+ r'isVipUnlocked': PropertySchema(
+ id: 5,
+ name: r'isVipUnlocked',
+ type: IsarType.bool,
+ ),
+ r'lastIapErrorMessage': PropertySchema(
+ id: 6,
+ name: r'lastIapErrorMessage',
+ type: IsarType.string,
+ ),
+ r'remainingTrialCount': PropertySchema(
+ id: 7,
+ name: r'remainingTrialCount',
+ type: IsarType.long,
+ ),
+ r'updatedAt': PropertySchema(
+ id: 8,
+ name: r'updatedAt',
+ type: IsarType.dateTime,
+ ),
+ r'usedTrialCount': PropertySchema(
+ id: 9,
+ name: r'usedTrialCount',
+ type: IsarType.long,
+ ),
+ r'vipUnlockedAt': PropertySchema(
+ id: 10,
+ name: r'vipUnlockedAt',
+ type: IsarType.dateTime,
+ )
+ },
+ estimateSize: _appConfigEstimateSize,
+ serialize: _appConfigSerialize,
+ deserialize: _appConfigDeserialize,
+ deserializeProp: _appConfigDeserializeProp,
+ idName: r'id',
+ indexes: {
+ r'configKey': IndexSchema(
+ id: 148847106925441600,
+ name: r'configKey',
+ unique: true,
+ replace: true,
+ properties: [
+ IndexPropertySchema(
+ name: r'configKey',
+ type: IndexType.hash,
+ caseSensitive: true,
+ )
+ ],
+ ),
+ r'updatedAt': IndexSchema(
+ id: -6238191080293565125,
+ name: r'updatedAt',
+ unique: false,
+ replace: false,
+ properties: [
+ IndexPropertySchema(
+ name: r'updatedAt',
+ type: IndexType.value,
+ caseSensitive: false,
+ )
+ ],
+ )
+ },
+ links: {},
+ embeddedSchemas: {},
+ getId: _appConfigGetId,
+ getLinks: _appConfigGetLinks,
+ attach: _appConfigAttach,
+ version: '3.1.0+1',
+);
+
+int _appConfigEstimateSize(
+ AppConfig object,
+ List offsets,
+ Map> allOffsets,
+) {
+ var bytesCount = offsets.last;
+ bytesCount += 3 + object.configKey.length * 3;
+ {
+ final value = object.lastIapErrorMessage;
+ if (value != null) {
+ bytesCount += 3 + value.length * 3;
+ }
+ }
+ return bytesCount;
+}
+
+void _appConfigSerialize(
+ AppConfig object,
+ IsarWriter writer,
+ List offsets,
+ Map> allOffsets,
+) {
+ writer.writeBool(offsets[0], object.canStartScoring);
+ writer.writeString(offsets[1], object.configKey);
+ writer.writeDateTime(offsets[2], object.createdAt);
+ writer.writeByte(offsets[3], object.entitlementSource.index);
+ writer.writeLong(offsets[4], object.freeTrialLimit);
+ writer.writeBool(offsets[5], object.isVipUnlocked);
+ writer.writeString(offsets[6], object.lastIapErrorMessage);
+ writer.writeLong(offsets[7], object.remainingTrialCount);
+ writer.writeDateTime(offsets[8], object.updatedAt);
+ writer.writeLong(offsets[9], object.usedTrialCount);
+ writer.writeDateTime(offsets[10], object.vipUnlockedAt);
+}
+
+AppConfig _appConfigDeserialize(
+ Id id,
+ IsarReader reader,
+ List offsets,
+ Map> allOffsets,
+) {
+ final object = AppConfig();
+ object.configKey = reader.readString(offsets[1]);
+ object.createdAt = reader.readDateTime(offsets[2]);
+ object.entitlementSource = _AppConfigentitlementSourceValueEnumMap[
+ reader.readByteOrNull(offsets[3])] ??
+ EntitlementSource.none;
+ object.freeTrialLimit = reader.readLong(offsets[4]);
+ object.id = id;
+ object.isVipUnlocked = reader.readBool(offsets[5]);
+ object.lastIapErrorMessage = reader.readStringOrNull(offsets[6]);
+ object.updatedAt = reader.readDateTime(offsets[8]);
+ object.usedTrialCount = reader.readLong(offsets[9]);
+ object.vipUnlockedAt = reader.readDateTimeOrNull(offsets[10]);
+ return object;
+}
+
+P _appConfigDeserializeProp(
+ IsarReader reader,
+ int propertyId,
+ int offset,
+ Map> allOffsets,
+) {
+ switch (propertyId) {
+ case 0:
+ return (reader.readBool(offset)) as P;
+ case 1:
+ return (reader.readString(offset)) as P;
+ case 2:
+ return (reader.readDateTime(offset)) as P;
+ case 3:
+ return (_AppConfigentitlementSourceValueEnumMap[
+ reader.readByteOrNull(offset)] ??
+ EntitlementSource.none) as P;
+ case 4:
+ return (reader.readLong(offset)) as P;
+ case 5:
+ return (reader.readBool(offset)) as P;
+ case 6:
+ return (reader.readStringOrNull(offset)) as P;
+ case 7:
+ return (reader.readLong(offset)) as P;
+ case 8:
+ return (reader.readDateTime(offset)) as P;
+ case 9:
+ return (reader.readLong(offset)) as P;
+ case 10:
+ return (reader.readDateTimeOrNull(offset)) as P;
+ default:
+ throw IsarError('Unknown property with id $propertyId');
+ }
+}
+
+const _AppConfigentitlementSourceEnumValueMap = {
+ 'none': 0,
+ 'purchase': 1,
+ 'restore': 2,
+};
+const _AppConfigentitlementSourceValueEnumMap = {
+ 0: EntitlementSource.none,
+ 1: EntitlementSource.purchase,
+ 2: EntitlementSource.restore,
+};
+
+Id _appConfigGetId(AppConfig object) {
+ return object.id;
+}
+
+List> _appConfigGetLinks(AppConfig object) {
+ return [];
+}
+
+void _appConfigAttach(IsarCollection col, Id id, AppConfig object) {
+ object.id = id;
+}
+
+extension AppConfigByIndex on IsarCollection {
+ Future getByConfigKey(String configKey) {
+ return getByIndex(r'configKey', [configKey]);
+ }
+
+ AppConfig? getByConfigKeySync(String configKey) {
+ return getByIndexSync(r'configKey', [configKey]);
+ }
+
+ Future deleteByConfigKey(String configKey) {
+ return deleteByIndex(r'configKey', [configKey]);
+ }
+
+ bool deleteByConfigKeySync(String configKey) {
+ return deleteByIndexSync(r'configKey', [configKey]);
+ }
+
+ Future> getAllByConfigKey(List configKeyValues) {
+ final values = configKeyValues.map((e) => [e]).toList();
+ return getAllByIndex(r'configKey', values);
+ }
+
+ List getAllByConfigKeySync(List configKeyValues) {
+ final values = configKeyValues.map((e) => [e]).toList();
+ return getAllByIndexSync(r'configKey', values);
+ }
+
+ Future deleteAllByConfigKey(List configKeyValues) {
+ final values = configKeyValues.map((e) => [e]).toList();
+ return deleteAllByIndex(r'configKey', values);
+ }
+
+ int deleteAllByConfigKeySync(List configKeyValues) {
+ final values = configKeyValues.map((e) => [e]).toList();
+ return deleteAllByIndexSync(r'configKey', values);
+ }
+
+ Future putByConfigKey(AppConfig object) {
+ return putByIndex(r'configKey', object);
+ }
+
+ Id putByConfigKeySync(AppConfig object, {bool saveLinks = true}) {
+ return putByIndexSync(r'configKey', object, saveLinks: saveLinks);
+ }
+
+ Future> putAllByConfigKey(List objects) {
+ return putAllByIndex(r'configKey', objects);
+ }
+
+ List putAllByConfigKeySync(List objects,
+ {bool saveLinks = true}) {
+ return putAllByIndexSync(r'configKey', objects, saveLinks: saveLinks);
+ }
+}
+
+extension AppConfigQueryWhereSort
+ on QueryBuilder {
+ QueryBuilder anyId() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addWhereClause(const IdWhereClause.any());
+ });
+ }
+
+ QueryBuilder anyUpdatedAt() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addWhereClause(
+ const IndexWhereClause.any(indexName: r'updatedAt'),
+ );
+ });
+ }
+}
+
+extension AppConfigQueryWhere
+ on QueryBuilder {
+ QueryBuilder idEqualTo(Id id) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addWhereClause(IdWhereClause.between(
+ lower: id,
+ upper: id,
+ ));
+ });
+ }
+
+ QueryBuilder idNotEqualTo(Id id) {
+ return QueryBuilder.apply(this, (query) {
+ if (query.whereSort == Sort.asc) {
+ return query
+ .addWhereClause(
+ IdWhereClause.lessThan(upper: id, includeUpper: false),
+ )
+ .addWhereClause(
+ IdWhereClause.greaterThan(lower: id, includeLower: false),
+ );
+ } else {
+ return query
+ .addWhereClause(
+ IdWhereClause.greaterThan(lower: id, includeLower: false),
+ )
+ .addWhereClause(
+ IdWhereClause.lessThan(upper: id, includeUpper: false),
+ );
+ }
+ });
+ }
+
+ QueryBuilder idGreaterThan(Id id,
+ {bool include = false}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addWhereClause(
+ IdWhereClause.greaterThan(lower: id, includeLower: include),
+ );
+ });
+ }
+
+ QueryBuilder idLessThan(Id id,
+ {bool include = false}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addWhereClause(
+ IdWhereClause.lessThan(upper: id, includeUpper: include),
+ );
+ });
+ }
+
+ QueryBuilder idBetween(
+ Id lowerId,
+ Id upperId, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addWhereClause(IdWhereClause.between(
+ lower: lowerId,
+ includeLower: includeLower,
+ upper: upperId,
+ includeUpper: includeUpper,
+ ));
+ });
+ }
+
+ QueryBuilder configKeyEqualTo(
+ String configKey) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addWhereClause(IndexWhereClause.equalTo(
+ indexName: r'configKey',
+ value: [configKey],
+ ));
+ });
+ }
+
+ QueryBuilder configKeyNotEqualTo(
+ String configKey) {
+ return QueryBuilder.apply(this, (query) {
+ if (query.whereSort == Sort.asc) {
+ return query
+ .addWhereClause(IndexWhereClause.between(
+ indexName: r'configKey',
+ lower: [],
+ upper: [configKey],
+ includeUpper: false,
+ ))
+ .addWhereClause(IndexWhereClause.between(
+ indexName: r'configKey',
+ lower: [configKey],
+ includeLower: false,
+ upper: [],
+ ));
+ } else {
+ return query
+ .addWhereClause(IndexWhereClause.between(
+ indexName: r'configKey',
+ lower: [configKey],
+ includeLower: false,
+ upper: [],
+ ))
+ .addWhereClause(IndexWhereClause.between(
+ indexName: r'configKey',
+ lower: [],
+ upper: [configKey],
+ includeUpper: false,
+ ));
+ }
+ });
+ }
+
+ QueryBuilder updatedAtEqualTo(
+ DateTime updatedAt) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addWhereClause(IndexWhereClause.equalTo(
+ indexName: r'updatedAt',
+ value: [updatedAt],
+ ));
+ });
+ }
+
+ QueryBuilder updatedAtNotEqualTo(
+ DateTime updatedAt) {
+ return QueryBuilder.apply(this, (query) {
+ if (query.whereSort == Sort.asc) {
+ return query
+ .addWhereClause(IndexWhereClause.between(
+ indexName: r'updatedAt',
+ lower: [],
+ upper: [updatedAt],
+ includeUpper: false,
+ ))
+ .addWhereClause(IndexWhereClause.between(
+ indexName: r'updatedAt',
+ lower: [updatedAt],
+ includeLower: false,
+ upper: [],
+ ));
+ } else {
+ return query
+ .addWhereClause(IndexWhereClause.between(
+ indexName: r'updatedAt',
+ lower: [updatedAt],
+ includeLower: false,
+ upper: [],
+ ))
+ .addWhereClause(IndexWhereClause.between(
+ indexName: r'updatedAt',
+ lower: [],
+ upper: [updatedAt],
+ includeUpper: false,
+ ));
+ }
+ });
+ }
+
+ QueryBuilder updatedAtGreaterThan(
+ DateTime updatedAt, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addWhereClause(IndexWhereClause.between(
+ indexName: r'updatedAt',
+ lower: [updatedAt],
+ includeLower: include,
+ upper: [],
+ ));
+ });
+ }
+
+ QueryBuilder updatedAtLessThan(
+ DateTime updatedAt, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addWhereClause(IndexWhereClause.between(
+ indexName: r'updatedAt',
+ lower: [],
+ upper: [updatedAt],
+ includeUpper: include,
+ ));
+ });
+ }
+
+ QueryBuilder updatedAtBetween(
+ DateTime lowerUpdatedAt,
+ DateTime upperUpdatedAt, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addWhereClause(IndexWhereClause.between(
+ indexName: r'updatedAt',
+ lower: [lowerUpdatedAt],
+ includeLower: includeLower,
+ upper: [upperUpdatedAt],
+ includeUpper: includeUpper,
+ ));
+ });
+ }
+}
+
+extension AppConfigQueryFilter
+ on QueryBuilder {
+ QueryBuilder
+ canStartScoringEqualTo(bool value) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'canStartScoring',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder configKeyEqualTo(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'configKey',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ configKeyGreaterThan(
+ String value, {
+ bool include = false,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'configKey',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder configKeyLessThan(
+ String value, {
+ bool include = false,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'configKey',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder configKeyBetween(
+ String lower,
+ String upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'configKey',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder configKeyStartsWith(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.startsWith(
+ property: r'configKey',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder configKeyEndsWith(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.endsWith(
+ property: r'configKey',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder configKeyContains(
+ String value,
+ {bool caseSensitive = true}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.contains(
+ property: r'configKey',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder configKeyMatches(
+ String pattern,
+ {bool caseSensitive = true}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.matches(
+ property: r'configKey',
+ wildcard: pattern,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder configKeyIsEmpty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'configKey',
+ value: '',
+ ));
+ });
+ }
+
+ QueryBuilder
+ configKeyIsNotEmpty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ property: r'configKey',
+ value: '',
+ ));
+ });
+ }
+
+ QueryBuilder createdAtEqualTo(
+ DateTime value) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'createdAt',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ createdAtGreaterThan(
+ DateTime value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'createdAt',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder createdAtLessThan(
+ DateTime value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'createdAt',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder createdAtBetween(
+ DateTime lower,
+ DateTime upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'createdAt',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ ));
+ });
+ }
+
+ QueryBuilder
+ entitlementSourceEqualTo(EntitlementSource value) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'entitlementSource',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ entitlementSourceGreaterThan(
+ EntitlementSource value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'entitlementSource',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ entitlementSourceLessThan(
+ EntitlementSource value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'entitlementSource',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ entitlementSourceBetween(
+ EntitlementSource lower,
+ EntitlementSource upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'entitlementSource',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ ));
+ });
+ }
+
+ QueryBuilder
+ freeTrialLimitEqualTo(int value) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'freeTrialLimit',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ freeTrialLimitGreaterThan(
+ int value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'freeTrialLimit',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ freeTrialLimitLessThan(
+ int value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'freeTrialLimit',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ freeTrialLimitBetween(
+ int lower,
+ int upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'freeTrialLimit',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ ));
+ });
+ }
+
+ QueryBuilder idEqualTo(
+ Id value) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'id',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder idGreaterThan(
+ Id value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'id',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder idLessThan(
+ Id value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'id',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder idBetween(
+ Id lower,
+ Id upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'id',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ ));
+ });
+ }
+
+ QueryBuilder
+ isVipUnlockedEqualTo(bool value) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'isVipUnlocked',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageIsNull() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(const FilterCondition.isNull(
+ property: r'lastIapErrorMessage',
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageIsNotNull() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(const FilterCondition.isNotNull(
+ property: r'lastIapErrorMessage',
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageEqualTo(
+ String? value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'lastIapErrorMessage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageGreaterThan(
+ String? value, {
+ bool include = false,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'lastIapErrorMessage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageLessThan(
+ String? value, {
+ bool include = false,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'lastIapErrorMessage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageBetween(
+ String? lower,
+ String? upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'lastIapErrorMessage',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageStartsWith(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.startsWith(
+ property: r'lastIapErrorMessage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageEndsWith(
+ String value, {
+ bool caseSensitive = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.endsWith(
+ property: r'lastIapErrorMessage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageContains(String value, {bool caseSensitive = true}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.contains(
+ property: r'lastIapErrorMessage',
+ value: value,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageMatches(String pattern, {bool caseSensitive = true}) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.matches(
+ property: r'lastIapErrorMessage',
+ wildcard: pattern,
+ caseSensitive: caseSensitive,
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageIsEmpty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'lastIapErrorMessage',
+ value: '',
+ ));
+ });
+ }
+
+ QueryBuilder
+ lastIapErrorMessageIsNotEmpty() {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ property: r'lastIapErrorMessage',
+ value: '',
+ ));
+ });
+ }
+
+ QueryBuilder
+ remainingTrialCountEqualTo(int value) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'remainingTrialCount',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ remainingTrialCountGreaterThan(
+ int value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'remainingTrialCount',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ remainingTrialCountLessThan(
+ int value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'remainingTrialCount',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ remainingTrialCountBetween(
+ int lower,
+ int upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'remainingTrialCount',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ ));
+ });
+ }
+
+ QueryBuilder updatedAtEqualTo(
+ DateTime value) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'updatedAt',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder
+ updatedAtGreaterThan(
+ DateTime value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.greaterThan(
+ include: include,
+ property: r'updatedAt',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder updatedAtLessThan(
+ DateTime value, {
+ bool include = false,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.lessThan(
+ include: include,
+ property: r'updatedAt',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder updatedAtBetween(
+ DateTime lower,
+ DateTime upper, {
+ bool includeLower = true,
+ bool includeUpper = true,
+ }) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.between(
+ property: r'updatedAt',
+ lower: lower,
+ includeLower: includeLower,
+ upper: upper,
+ includeUpper: includeUpper,
+ ));
+ });
+ }
+
+ QueryBuilder
+ usedTrialCountEqualTo(int value) {
+ return QueryBuilder.apply(this, (query) {
+ return query.addFilterCondition(FilterCondition.equalTo(
+ property: r'usedTrialCount',
+ value: value,
+ ));
+ });
+ }
+
+ QueryBuilder