From 73d7388a80cba88d9fa7b1c6ec57347a71bde4a9 Mon Sep 17 00:00:00 2001 From: nemoryoliver Date: Tue, 25 Oct 2022 11:40:42 +0800 Subject: [PATCH] fixed importing external csv now using the unified core app framework auto tag option when importing items --- CHANGELOG.md | 6 + analysis_options.yaml | 6 +- lib/core/animations/animations.dart | 90 --- lib/core/firebase/analytics.service.dart | 54 -- lib/core/firebase/config/config.service.dart | 168 ---- .../config/models/config_app.model.dart | 83 -- .../config/models/config_general.model.dart | 359 --------- .../config/models/config_root.model.dart | 255 ------ .../config/models/config_secrets.model.dart | 186 ----- .../config/models/config_users.model.dart | 36 - lib/core/firebase/crashlytics.service.dart | 81 -- .../config_app_domains.model.dart | 0 .../models => model}/config_limits.model.dart | 4 - .../models => model}/config_web3.model.dart | 0 lib/core/form_fields/address.field.dart | 4 +- lib/core/form_fields/date.field.dart | 3 +- lib/core/form_fields/email.field.dart | 3 +- lib/core/form_fields/number.field.dart | 3 +- lib/core/form_fields/passport.field.dart | 4 +- lib/core/form_fields/password.field.dart | 20 +- lib/core/form_fields/phone.field.dart | 3 +- lib/core/form_fields/pin.field.dart | 9 +- lib/core/form_fields/section.field.dart | 2 +- lib/core/form_fields/textarea.field.dart | 4 +- lib/core/form_fields/textfield.field.dart | 4 +- lib/core/form_fields/toggle.field.dart | 2 +- lib/core/form_fields/totp.field.dart | 10 +- lib/core/form_fields/url.field.dart | 4 +- lib/core/hive/hive.service.dart | 4 +- lib/core/hive/models/item.hive.dart | 14 +- lib/core/hive/models/metadata/app.hive.dart | 53 -- lib/core/hive/models/metadata/app.hive.g.dart | 50 -- .../hive/models/metadata/device.hive.dart | 158 ---- .../hive/models/metadata/device.hive.g.dart | 56 -- .../hive/models/metadata/metadata.hive.dart | 5 +- lib/core/liso/liso.manager.dart | 7 +- .../authentication.middleware.dart | 5 +- .../notifications/notifications.manager.dart | 81 -- lib/core/persistence/mutable_value.dart | 26 - lib/core/persistence/persistence.dart | 61 +- lib/core/persistence/persistence.secret.dart | 5 +- .../persistence_builder.widget.dart | 43 - .../secret_persistence.builder.dart | 23 + lib/core/services/alchemy.service.dart | 21 +- lib/core/services/local_auth.service.dart | 116 --- lib/core/translations/en.dart | 224 ++++-- lib/core/utils/globals.dart | 127 +-- lib/core/utils/ui_utils.dart | 319 -------- lib/core/utils/utils.dart | 255 ++---- lib/features/about/about.screen.dart | 33 +- .../about/about_screen.controller.dart | 14 +- lib/features/app/app.dart | 10 +- lib/features/app/pages.dart | 51 +- lib/features/app/routes.dart | 13 +- lib/features/attachments/attachment.tile.dart | 3 +- .../attachments/attachments.screen.dart | 6 +- .../attachments_screen.controller.dart | 6 +- lib/features/autofill/autofill.service.dart | 14 +- .../autofill_picker.dialog.dart | 4 +- .../categories/categories.screen.dart | 16 +- .../categories_screen.controller.dart | 21 +- .../picker/category_picker.screen.dart | 7 +- lib/features/cipher/cipher.screen.dart | 8 +- .../cipher/cipher_screen.controller.dart | 64 +- .../connectivity/connectivity.service.dart | 46 -- .../connectivity/connectivity_bar.widget.dart | 36 - .../create_password.screen.dart | 11 +- .../create_password_screen.controller.dart | 16 +- lib/features/debug/debug.screen.dart | 5 +- .../debug/debug_screen.controller.dart | 170 ++-- lib/features/devices/devices.screen.dart | 14 +- .../devices/devices_screen.controller.dart | 9 +- lib/features/drawer/drawer.widget.dart | 121 +-- .../drawer/drawer_widget.controller.dart | 7 +- lib/features/feedback/feedback.screen.dart | 158 ---- .../feedback/feedback_screen.controller.dart | 59 -- .../files/explorer/s3_explorer.screen.dart | 4 +- .../s3_exporer_screen.controller.dart | 53 +- .../files/explorer/s3_object.tile.dart | 3 +- .../explorer/s3_object_tile.controller.dart | 23 +- .../custom_provider_screen.controller.dart | 8 +- .../provider/custom_provider_screen.dart | 9 +- .../files/provider/sync_provider.screen.dart | 2 +- lib/features/files/storage.service.dart | 15 +- lib/features/files/sync.service.dart | 55 +- .../general/appbar_leading.widget.dart | 22 - .../general/busy_indicator.widget.dart | 44 -- lib/features/general/gradient.widget.dart | 23 - lib/features/general/pro.widget.dart | 31 - lib/features/general/remote_image.widget.dart | 76 -- lib/features/general/unknown.screen.dart | 20 - lib/features/general/version.widget.dart | 24 - lib/features/groups/groups.screen.dart | 16 +- .../groups/groups_screen.controller.dart | 36 +- lib/features/import/import.screen.dart | 21 +- .../import/import_screen.controller.dart | 10 +- .../import/importers/apple.importer.dart | 19 +- .../import/importers/bitwarden.importer.dart | 39 +- .../import/importers/chrome.importer.dart | 15 +- .../import/importers/firefox.importer.dart | 13 +- .../import/importers/lastpass.importer.dart | 23 +- .../import/importers/nordpass.importer.dart | 13 +- lib/features/items/item.screen.dart | 18 +- lib/features/items/item.tile.dart | 37 +- .../items/item_screen.controller.dart | 107 +-- lib/features/items/items.controller.dart | 16 +- lib/features/items/items.service.dart | 2 +- .../explorer/vault_explorer.screen.dart | 2 +- .../vault_explorer_screen.controller.dart | 3 +- .../joined_vaults/joined_vaults.screen.dart | 15 +- .../joined_vaults_screen.controller.dart | 17 +- .../json_viewer/json_viewer.screen.dart | 5 +- lib/features/main/main.screen.dart | 62 +- lib/features/main/main_screen.controller.dart | 61 +- lib/features/menu/menu.button.dart | 11 +- lib/features/menu/menu.sheet.dart | 2 +- lib/features/otp/otp.screen.dart | 6 +- .../password_generator.screen.dart | 4 +- .../password_generator_screen.controller.dart | 4 +- lib/features/pro/pro.controller.dart | 397 ---------- lib/features/pro/upgrade/feature.tile.dart | 42 - lib/features/pro/upgrade/upgrade.screen.dart | 744 ------------------ .../upgrade/upgrade_screen.controller.dart | 240 ------ lib/features/rate/rate.widget.dart | 135 ---- lib/features/rate/rate_widget.controller.dart | 44 -- lib/features/restore/restore.screen.dart | 10 +- .../restore/restore_screen.controller.dart | 35 +- .../seed/generator/seed_generator.screen.dart | 8 +- .../seed_generator_screen.controller.dart | 6 +- lib/features/seed/seed.screen.dart | 11 +- lib/features/seed/seed_chips.widget.dart | 8 +- lib/features/seed/seed_field.widget.dart | 10 +- lib/features/seed/seed_screen.controller.dart | 17 +- lib/features/settings/settings.screen.dart | 39 +- .../settings/settings_screen.controller.dart | 43 +- .../shared_vaults/shared_vaults.screen.dart | 23 +- .../shared_vaults_screen.controller.dart | 43 +- lib/features/supabase/model/device.model.dart | 43 - .../model/entitlement_response.model.dart | 22 - lib/features/supabase/model/error.model.dart | 20 - .../supabase/model/gumroad_product.model.dart | 123 --- .../model/list_objects_response.model.dart | 3 +- lib/features/supabase/model/object.model.dart | 2 +- .../model/presign_response.model.dart | 2 +- .../supabase/model/profile.model.dart | 44 -- .../supabase/model/server_response.model.dart | 26 - .../supabase/model/stat_response.model.dart | 3 +- .../model/sync_user_response.model.dart | 32 - .../supabase/supabase_auth.service.dart | 143 ---- .../supabase/supabase_database.service.dart | 46 -- .../supabase/supabase_functions.service.dart | 243 +----- lib/features/tags/tags_input.controller.dart | 4 +- lib/features/unlock/unlock.screen.dart | 25 +- .../unlock/unlock_screen.controller.dart | 10 +- lib/features/update/update.screen.dart | 84 -- lib/features/wallet/assets/assets.screen.dart | 13 +- lib/features/wallet/nfts/nfts.screen.dart | 4 +- .../transactions/transactions.screen.dart | 2 +- lib/features/wallet/wallet.screen.dart | 33 +- lib/features/wallet/wallet.service.dart | 21 +- .../wallet/wallet_screen.controller.dart | 17 +- lib/features/welcome/welcome.screen.dart | 62 +- .../welcome/welcome_screen.controller.dart | 23 +- lib/main.dart | 170 +++- linux/flutter/generated_plugin_registrant.cc | 8 + linux/flutter/generated_plugins.cmake | 2 + macos/Flutter/GeneratedPluginRegistrant.swift | 8 +- pubspec.yaml | 69 +- .../flutter/generated_plugin_registrant.cc | 12 + windows/flutter/generated_plugins.cmake | 4 + 170 files changed, 1440 insertions(+), 6705 deletions(-) delete mode 100644 lib/core/animations/animations.dart delete mode 100644 lib/core/firebase/analytics.service.dart delete mode 100644 lib/core/firebase/config/config.service.dart delete mode 100644 lib/core/firebase/config/models/config_app.model.dart delete mode 100644 lib/core/firebase/config/models/config_general.model.dart delete mode 100644 lib/core/firebase/config/models/config_root.model.dart delete mode 100644 lib/core/firebase/config/models/config_secrets.model.dart delete mode 100644 lib/core/firebase/config/models/config_users.model.dart delete mode 100644 lib/core/firebase/crashlytics.service.dart rename lib/core/firebase/{config/models => model}/config_app_domains.model.dart (100%) rename lib/core/firebase/{config/models => model}/config_limits.model.dart (95%) rename lib/core/firebase/{config/models => model}/config_web3.model.dart (100%) delete mode 100644 lib/core/hive/models/metadata/app.hive.dart delete mode 100644 lib/core/hive/models/metadata/app.hive.g.dart delete mode 100644 lib/core/hive/models/metadata/device.hive.dart delete mode 100644 lib/core/hive/models/metadata/device.hive.g.dart delete mode 100644 lib/core/notifications/notifications.manager.dart delete mode 100644 lib/core/persistence/mutable_value.dart delete mode 100644 lib/core/persistence/persistence_builder.widget.dart create mode 100644 lib/core/persistence/secret_persistence.builder.dart delete mode 100644 lib/core/services/local_auth.service.dart delete mode 100644 lib/core/utils/ui_utils.dart delete mode 100644 lib/features/connectivity/connectivity.service.dart delete mode 100644 lib/features/connectivity/connectivity_bar.widget.dart delete mode 100644 lib/features/feedback/feedback.screen.dart delete mode 100644 lib/features/feedback/feedback_screen.controller.dart delete mode 100644 lib/features/general/appbar_leading.widget.dart delete mode 100644 lib/features/general/busy_indicator.widget.dart delete mode 100644 lib/features/general/gradient.widget.dart delete mode 100644 lib/features/general/pro.widget.dart delete mode 100644 lib/features/general/remote_image.widget.dart delete mode 100644 lib/features/general/unknown.screen.dart delete mode 100644 lib/features/general/version.widget.dart delete mode 100644 lib/features/pro/pro.controller.dart delete mode 100644 lib/features/pro/upgrade/feature.tile.dart delete mode 100644 lib/features/pro/upgrade/upgrade.screen.dart delete mode 100644 lib/features/pro/upgrade/upgrade_screen.controller.dart delete mode 100644 lib/features/rate/rate.widget.dart delete mode 100644 lib/features/rate/rate_widget.controller.dart delete mode 100644 lib/features/supabase/model/device.model.dart delete mode 100644 lib/features/supabase/model/entitlement_response.model.dart delete mode 100644 lib/features/supabase/model/error.model.dart delete mode 100644 lib/features/supabase/model/gumroad_product.model.dart delete mode 100644 lib/features/supabase/model/profile.model.dart delete mode 100644 lib/features/supabase/model/server_response.model.dart delete mode 100644 lib/features/supabase/model/sync_user_response.model.dart delete mode 100644 lib/features/supabase/supabase_auth.service.dart delete mode 100644 lib/features/supabase/supabase_database.service.dart delete mode 100644 lib/features/update/update.screen.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b86424b..55caac50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.0.1 +- fixed importing from bitwarden and lastpass +- fixed editing properties of a custom field +- under the hood improvements +- lots of bug fixes and improvements + ## 1.0.0 - ditched firebase db and auth for supabase - updated packages diff --git a/analysis_options.yaml b/analysis_options.yaml index c0ee7f8d..545729e6 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -22,13 +22,13 @@ linter: # `// ignore_for_file: name_of_lint` syntax on the line or in the file # producing the lint. rules: + depend_on_referenced_packages: false # avoid_print: false # Uncomment to disable the `avoid_print` rule # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options - dart_code_metrics: anti-patterns: - long-method @@ -50,5 +50,5 @@ dart_code_metrics: # web3dart analyzer: - exclude: - - '**/*.g.dart' \ No newline at end of file + exclude: + - "**/*.g.dart" diff --git a/lib/core/animations/animations.dart b/lib/core/animations/animations.dart deleted file mode 100644 index 9e27f23d..00000000 --- a/lib/core/animations/animations.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:sa4_migration_kit/sa4_migration_kit.dart'; -import 'package:supercharged/supercharged.dart'; - -enum AnimationProps { opacity, translationX, translationY, scale } - -class GridItemAnimation extends StatelessWidget { - final Widget child; - final double scale; - - const GridItemAnimation({ - Key? key, - required this.child, - this.scale = 0.5, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - final tween = MultiTween() - ..add(AnimationProps.opacity, 0.0.tweenTo(1.0)) - ..add( - AnimationProps.scale, - scale.tweenTo(1.0), - ); - - return PlayAnimation>( - delay: 300.milliseconds, - duration: 300.milliseconds, - tween: tween, - child: child, - builder: (context, child, value) => Transform.scale( - scale: value.get(AnimationProps.scale), - child: Opacity( - opacity: value.get(AnimationProps.opacity), - child: child, - ), - ), - ); - } -} - -class ListItemAnimation extends StatelessWidget { - final Widget child; - final Axis axis; - final Offset offset; - final Duration delay, duration; - final bool enabled; - - const ListItemAnimation({ - Key? key, - required this.child, - this.axis = Axis.vertical, - this.offset = const Offset(0.0, 50.0), - this.delay = const Duration(milliseconds: 300), - this.duration = const Duration(milliseconds: 300), - this.enabled = true, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - if (!enabled) return child; - - final tween = MultiTween() - ..add(AnimationProps.opacity, 0.0.tweenTo(1.0)) - ..add(AnimationProps.translationX, offset.dx.tweenTo(0.0)) - ..add( - AnimationProps.translationY, - offset.dy.tweenTo(0.0), - ); - - return PlayAnimation>( - delay: delay, - duration: duration, - tween: tween, - child: child, - builder: (context, child, value) { - final horizontalOffset = value.get(AnimationProps.translationX); - final verticalOffset = value.get(AnimationProps.translationY); - - return Transform.translate( - offset: Offset(horizontalOffset, verticalOffset), - child: Opacity( - opacity: value.get(AnimationProps.opacity), - child: child, - ), - ); - }, - ); - } -} diff --git a/lib/core/firebase/analytics.service.dart b/lib/core/firebase/analytics.service.dart deleted file mode 100644 index 811a0656..00000000 --- a/lib/core/firebase/analytics.service.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:console_mixin/console_mixin.dart'; -import 'package:firebase_analytics/firebase_analytics.dart'; -import 'package:get/get.dart'; - -import '../persistence/persistence.dart'; -import '../utils/globals.dart'; - -class AnalyticsService extends GetxService with ConsoleMixin { - static AnalyticsService get to => Get.find(); - - // VARIABLES - final observer = FirebaseAnalyticsObserver( - analytics: FirebaseAnalytics.instance, - ); - - // PROPERTIES - - // GETTERS - FirebaseAnalytics get instance => FirebaseAnalytics.instance; - - // INIT - @override - void onInit() async { - if (isWindowsLinux) return; - await instance.setAnalyticsCollectionEnabled(Persistence.to.analytics.val); - await instance.logAppOpen(); - super.onInit(); - } - - // FUNCTIONS - void logSignIn() { - if (isWindowsLinux) return; - instance.logLogin(); - } - - void logSignOut() { - if (isWindowsLinux) return; - instance.logEvent(name: 'logout'); - } - - void logSearch(String query) { - if (isWindowsLinux) return; - instance.logSearch(searchTerm: query); - } - - void logEvent( - String name, { - Map? parameters, - AnalyticsCallOptions? callOptions, - }) { - if (isWindowsLinux) return; - instance.logEvent(name: name); - } -} diff --git a/lib/core/firebase/config/config.service.dart b/lib/core/firebase/config/config.service.dart deleted file mode 100644 index 98727db9..00000000 --- a/lib/core/firebase/config/config.service.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'dart:convert'; - -import 'package:cloud_functions/cloud_functions.dart'; -import 'package:console_mixin/console_mixin.dart'; -import 'package:either_dart/either.dart'; -import 'package:get/get.dart'; -import 'package:liso/core/firebase/config/models/config_web3.model.dart'; -import 'package:liso/features/connectivity/connectivity.service.dart'; -import 'package:secrets/secrets.dart'; - -import '../../../features/app/routes.dart'; -import '../../../features/pro/pro.controller.dart'; -import '../../../features/supabase/supabase_auth.service.dart'; -import '../../persistence/persistence.secret.dart'; -import '../../utils/globals.dart'; -import 'models/config_app.model.dart'; -import 'models/config_app_domains.model.dart'; -import 'models/config_general.model.dart'; -import 'models/config_limits.model.dart'; -import 'models/config_root.model.dart'; -import 'models/config_secrets.model.dart'; -import 'models/config_users.model.dart'; - -class ConfigService extends GetxService with ConsoleMixin { - static ConfigService get to => Get.find(); - - // VARIABLES - var general = const ConfigGeneral(); - var app = const ConfigApp(); - var secrets = const ConfigSecrets(); - var web3 = const ConfigWeb3(); - var limits = const ConfigLimits(); - var users = const ConfigUsers(); - var appDomains = const ConfigAppDomains(); - - bool remoteFetched = false; - - // GETTERS - String get appName => general.app.name; - String get devName => general.developer.name; - bool get isReady => secrets.supabase.url.isNotEmpty; - - // INIT - - // FUNCTIONS - Future init() async { - // pre-populate with local as defaults - _prePopulate(); - fetchFromFunctions(); - } - - Future fetchFromFunctions() async { - final result = await getRemoteConfig(); - - result.fold( - (error) => console.info('remote config! functions: error: $error'), - (root) { - app = root.parameters.appConfig; - SecretPersistence.to.configApp.val = jsonEncode(app.toJson()); - - secrets = root.parameters.secretsConfig; - SecretPersistence.to.configSecrets.val = jsonEncode(secrets.toJson()); - - web3 = root.parameters.web3Config; - SecretPersistence.to.configWeb3.val = jsonEncode(web3.toJson()); - - limits = root.parameters.limitsConfig; - SecretPersistence.to.configLimits.val = jsonEncode(limits.toJson()); - - users = root.parameters.usersConfig; - SecretPersistence.to.configUsers.val = jsonEncode(users.toJson()); - - general = root.parameters.generalConfig; - SecretPersistence.to.configGeneral.val = jsonEncode(general.toJson()); - - appDomains = root.parameters.appDomainsConfig; - SecretPersistence.to.configAppDomains.val = - jsonEncode(appDomains.toJson()); - - remoteFetched = true; - console.wtf('remote config! functions: success'); - }, - ); - - postFetch(); - } - - Future> getRemoteConfig() async { - if (!ConnectivityService.to.connected.value) { - return const Left('no internet connection'); - } - - console.info('remote config! fetching...'); - HttpsCallableResult? result; - - try { - result = await FirebaseFunctions.instance - .httpsCallable('getRemoteConfig') - .call(); - } on FirebaseFunctionsException catch (e) { - return Left('error fetching remote config: $e'); - } - - // console.wtf('remote config! response: ${result.data}'); - - if (result.data == false) { - return const Left('failed to fetch remote config'); - } - - return Right(ConfigRoot.fromJson(jsonDecode(result.data))); - } - - Future _prePopulate() async { - app = ConfigApp.fromJson( - SecretPersistence.to.configApp.val.isEmpty - ? Secrets.configs.app - : jsonDecode(SecretPersistence.to.configApp.val), - ); - - secrets = ConfigSecrets.fromJson( - SecretPersistence.to.configSecrets.val.isEmpty - ? Secrets.configs.secrets - : jsonDecode(SecretPersistence.to.configSecrets.val), - ); - - web3 = ConfigWeb3.fromJson( - SecretPersistence.to.configWeb3.val.isEmpty - ? Secrets.configs.web3 - : jsonDecode(SecretPersistence.to.configWeb3.val), - ); - - limits = ConfigLimits.fromJson( - SecretPersistence.to.configLimits.val.isEmpty - ? Secrets.configs.limits - : jsonDecode(SecretPersistence.to.configLimits.val), - ); - - users = ConfigUsers.fromJson( - SecretPersistence.to.configUsers.val.isEmpty - ? Secrets.configs.users - : jsonDecode(SecretPersistence.to.configUsers.val), - ); - - general = ConfigGeneral.fromJson( - SecretPersistence.to.configGeneral.val.isEmpty - ? Secrets.configs.general - : jsonDecode(SecretPersistence.to.configGeneral.val), - ); - - appDomains = ConfigAppDomains.fromJson( - SecretPersistence.to.configAppDomains.val.isEmpty - ? Secrets.configs.appDomains - : jsonDecode(SecretPersistence.to.configAppDomains.val), - ); - } - - void postFetch() { - // initialize supabase - SupabaseAuthService.to.init(); - ProController.to.init(); - - // check if update is required - if (app.build.min > int.parse(Globals.metadata!.app.buildNumber)) { - console.error('### must update'); - Get.toNamed(Routes.update); - } - } -} diff --git a/lib/core/firebase/config/models/config_app.model.dart b/lib/core/firebase/config/models/config_app.model.dart deleted file mode 100644 index 0d2203e9..00000000 --- a/lib/core/firebase/config/models/config_app.model.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'dart:convert'; - -class ConfigApp { - const ConfigApp({ - this.id = '', - this.package = '', - this.enabled = true, - this.build = const ConfigAppBuild(), - this.beta = const ConfigAppBeta(), - }); - - final String id; - final String package; - final bool enabled; - final ConfigAppBuild build; - final ConfigAppBeta beta; - - factory ConfigApp.fromJson(Map json) => ConfigApp( - id: json["id"], - package: json["package"], - enabled: json["enabled"], - build: ConfigAppBuild.fromJson(json["build"]), - beta: ConfigAppBeta.fromJson(json["beta"]), - ); - - Map toJson() => { - "id": id, - "package": package, - "enabled": enabled, - "build": build.toJson(), - "beta": beta.toJson(), - }; - - String toJsonString() => jsonEncode(toJson()); -} - -class ConfigAppBuild { - const ConfigAppBuild({ - this.latest = 0, - this.min = 0, - this.disabled = const [], - }); - - final int latest; - final int min; - final List disabled; - - factory ConfigAppBuild.fromJson(Map json) => ConfigAppBuild( - latest: json["latest"], - min: json["min"], - disabled: List.from(json["disabled"].map((x) => x)), - ); - - Map toJson() => { - "latest": latest, - "min": min, - "disabled": List.from(disabled.map((x) => x)), - }; -} - -class ConfigAppBeta { - const ConfigAppBeta({ - this.latest = 0, - this.min = 0, - this.enabled = true, - }); - - final int latest; - final int min; - final bool enabled; - - factory ConfigAppBeta.fromJson(Map json) => ConfigAppBeta( - latest: json["latest"], - min: json["min"], - enabled: json["enabled"], - ); - - Map toJson() => { - "latest": latest, - "min": min, - "enabled": enabled, - }; -} diff --git a/lib/core/firebase/config/models/config_general.model.dart b/lib/core/firebase/config/models/config_general.model.dart deleted file mode 100644 index a195b241..00000000 --- a/lib/core/firebase/config/models/config_general.model.dart +++ /dev/null @@ -1,359 +0,0 @@ -import 'dart:convert'; - -class ConfigGeneral { - const ConfigGeneral({ - this.developer = const ConfigGeneralDeveloper(), - this.app = const ConfigGeneralApp(), - }); - - final ConfigGeneralDeveloper developer; - final ConfigGeneralApp app; - - factory ConfigGeneral.fromJson(Map json) => ConfigGeneral( - developer: ConfigGeneralDeveloper.fromJson(json["developer"]), - app: ConfigGeneralApp.fromJson(json["app"]), - ); - - Map toJson() => { - "developer": developer.toJson(), - "app": app.toJson(), - }; - - String toJsonString() => jsonEncode(toJson()); -} - -class ConfigGeneralApp { - const ConfigGeneralApp({ - this.name = '', - this.image = '', - this.shortDescription = '', - this.longDescription = '', - this.shareText = '', - this.emails = const ConfigGeneralAppEmails(), - this.links = const ConfigGeneralAppLinks(), - }); - - final String name; - final String image; - final String shortDescription; - final String longDescription; - final String shareText; - final ConfigGeneralAppEmails emails; - final ConfigGeneralAppLinks links; - - factory ConfigGeneralApp.fromJson(Map json) => - ConfigGeneralApp( - name: json["name"] ?? '', - image: json["image"] ?? '', - shortDescription: json["short_description"] ?? '', - longDescription: json["long_description"] ?? '', - shareText: json["share_text"] ?? '', - emails: ConfigGeneralAppEmails.fromJson(json["emails"]), - links: ConfigGeneralAppLinks.fromJson(json["links"]), - ); - - Map toJson() => { - "name": name, - "image": image, - "short_description": shortDescription, - "long_description": longDescription, - "share_text": shareText, - "emails": emails.toJson(), - "links": links.toJson(), - }; -} - -class ConfigGeneralAppEmails { - const ConfigGeneralAppEmails({ - this.support = '', - this.issues = '', - this.translations = '', - this.premium = '', - }); - - final String support; - final String issues; - final String translations; - final String premium; - - factory ConfigGeneralAppEmails.fromJson(Map json) => - ConfigGeneralAppEmails( - support: json["support"] ?? '', - issues: json["issues"] ?? '', - translations: json["translations"] ?? '', - premium: json["premium"] ?? '', - ); - - Map toJson() => { - "support": support, - "issues": issues, - "translations": translations, - "premium": premium, - }; -} - -class ConfigGeneralAppLinks { - const ConfigGeneralAppLinks({ - this.website = '', - this.twitter = '', - this.facebook = '', - this.instagram = '', - this.discord = '', - this.github = '', - this.privacy = '', - this.telegram = '', - this.matrix = '', - this.terms = '', - this.faqs = '', - this.roadmap = '', - this.productHunt = '', - this.reddit = '', - this.forum = '', - this.store = const ConfigGeneralStore(), - }); - - final String website; - final String twitter; - final String facebook; - final String instagram; - final String discord; - final String github; - final String privacy; - final String telegram; - final String matrix; - final String terms; - final String faqs; - final String roadmap; - final String productHunt; - final String reddit; - final String forum; - final ConfigGeneralStore store; - - factory ConfigGeneralAppLinks.fromJson(Map json) => - ConfigGeneralAppLinks( - website: json["website"] ?? '', - twitter: json["twitter"] ?? '', - facebook: json["facebook"] ?? '', - instagram: json["instagram"] ?? '', - discord: json["discord"] ?? '', - github: json["github"] ?? '', - privacy: json["privacy"] ?? '', - telegram: json["telegram"] ?? '', - matrix: json["matrix"] ?? '', - terms: json["terms"] ?? '', - faqs: json["faqs"] ?? '', - roadmap: json["roadmap"] ?? '', - productHunt: json["product_hunt"] ?? '', - reddit: json["reddit"] ?? '', - forum: json["forum"] ?? '', - store: ConfigGeneralStore.fromJson(json["store"]), - ); - - Map toJson() => { - "website": website, - "twitter": twitter, - "facebook": facebook, - "instagram": instagram, - "discord": discord, - "github": github, - "privacy": privacy, - "telegram": telegram, - "matrix": matrix, - "terms": terms, - "faqs": faqs, - "roadmap": roadmap, - "product_hunt": productHunt, - "reddit": reddit, - "forum": forum, - "store": store.toJson(), - }; -} - -class ConfigGeneralStore { - const ConfigGeneralStore({ - this.google = '', - this.apple = '', - this.amazon = '', - this.samsung = '', - this.huawei = '', - this.gumroad = '', - }); - - final String google; - final String apple; - final String amazon; - final String samsung; - final String huawei; - final String gumroad; - - factory ConfigGeneralStore.fromJson(Map json) => - ConfigGeneralStore( - google: json["google"] ?? '', - apple: json["apple"] ?? '', - amazon: json["amazon"] ?? '', - samsung: json["samsung"] ?? '', - huawei: json["huawei"] ?? '', - gumroad: json["gumroad"] ?? '', - ); - - Map toJson() => { - "google": google, - "apple": apple, - "amazon": amazon, - "samsung": samsung, - "huawei": huawei, - "gumroad": gumroad, - }; -} - -class ConfigGeneralDeveloper { - const ConfigGeneralDeveloper({ - this.name = '', - this.image = '', - this.shortDescription = '=', - this.longDescription = '', - this.address = const ConfigGeneralDeveloperAddress(), - this.emails = const ConfigGeneralDeveloperEmails(), - this.links = const ConfigGeneralDeveloperLinks(), - }); - - final String name; - final String image; - final String shortDescription; - final String longDescription; - final ConfigGeneralDeveloperAddress address; - final ConfigGeneralDeveloperEmails emails; - final ConfigGeneralDeveloperLinks links; - - factory ConfigGeneralDeveloper.fromJson(Map json) => - ConfigGeneralDeveloper( - name: json["name"] ?? '', - image: json["image"] ?? '', - shortDescription: json["short_description"] ?? '', - longDescription: json["long_description"] ?? '', - address: ConfigGeneralDeveloperAddress.fromJson(json["address"]), - emails: ConfigGeneralDeveloperEmails.fromJson(json["emails"]), - links: ConfigGeneralDeveloperLinks.fromJson(json["links"]), - ); - - Map toJson() => { - "name": name, - "image": image, - "short_description": shortDescription, - "long_description": longDescription, - "address": address.toJson(), - "emails": emails.toJson(), - "links": links.toJson(), - }; -} - -class ConfigGeneralDeveloperAddress { - const ConfigGeneralDeveloperAddress({ - this.street1 = '', - this.street2 = '', - this.city = '', - this.state = '', - this.postal = '', - this.country = '', - }); - - final String street1; - final String street2; - final String city; - final String state; - final String postal; - final String country; - - factory ConfigGeneralDeveloperAddress.fromJson(Map json) => - ConfigGeneralDeveloperAddress( - street1: json["street1"] ?? '', - street2: json["street2"] ?? '', - city: json["city"] ?? '', - state: json["state"] ?? '', - postal: json["postal"] ?? '', - country: json["country"] ?? '', - ); - - Map toJson() => { - "street1": street1, - "street2": street2, - "city": city, - "state": state, - "postal": postal, - "country": country, - }; -} - -class ConfigGeneralDeveloperEmails { - const ConfigGeneralDeveloperEmails({ - this.support = '', - this.marketing = '', - this.business = '', - }); - - final String support; - final String marketing; - final String business; - - factory ConfigGeneralDeveloperEmails.fromJson(Map json) => - ConfigGeneralDeveloperEmails( - support: json["support"] ?? '', - marketing: json["marketing"] ?? '', - business: json["business"] ?? '', - ); - - Map toJson() => { - "support": support, - "marketing": marketing, - "business": business, - }; -} - -class ConfigGeneralDeveloperLinks { - const ConfigGeneralDeveloperLinks({ - this.website = '', - this.twitter = '', - this.facebook = '', - this.instagram = '', - this.linkedin = '', - this.discord = '', - this.github = '', - this.privacy = '', - this.store = const ConfigGeneralStore(), - }); - - final String website; - final String twitter; - final String facebook; - final String instagram; - final String linkedin; - final String discord; - final String github; - final String privacy; - final ConfigGeneralStore store; - - factory ConfigGeneralDeveloperLinks.fromJson(Map json) => - ConfigGeneralDeveloperLinks( - website: json["website"] ?? '', - twitter: json["twitter"] ?? '', - facebook: json["facebook"] ?? '', - instagram: json["instagram"] ?? '', - linkedin: json["linkedin"] ?? '', - discord: json["discord"] ?? '', - github: json["github"] ?? '', - privacy: json["privacy"] ?? '', - store: ConfigGeneralStore.fromJson(json["store"]), - ); - - Map toJson() => { - "website": website, - "twitter": twitter, - "facebook": facebook, - "instagram": instagram, - "linkedin": linkedin, - "discord": discord, - "github": github, - "privacy": privacy, - "store": store.toJson(), - }; -} diff --git a/lib/core/firebase/config/models/config_root.model.dart b/lib/core/firebase/config/models/config_root.model.dart deleted file mode 100644 index a483dd17..00000000 --- a/lib/core/firebase/config/models/config_root.model.dart +++ /dev/null @@ -1,255 +0,0 @@ -// USED FOR WINDOWS ONLY - -import 'dart:convert'; - -import 'package:liso/core/firebase/config/models/config_app.model.dart'; -import 'package:liso/core/firebase/config/models/config_app_domains.model.dart'; -import 'package:liso/core/firebase/config/models/config_general.model.dart'; -import 'package:liso/core/firebase/config/models/config_limits.model.dart'; -import 'package:liso/core/firebase/config/models/config_secrets.model.dart'; -import 'package:liso/core/firebase/config/models/config_users.model.dart'; -import 'package:liso/core/firebase/config/models/config_web3.model.dart'; - -class ConfigRoot { - ConfigRoot({ - required this.conditions, - required this.parameters, - required this.parameterGroups, - required this.etag, - required this.version, - }); - - List conditions; - Parameters parameters; - ParameterGroups parameterGroups; - String etag; - Version version; - - factory ConfigRoot.fromJson(Map json) => ConfigRoot( - conditions: List.from( - json["conditions"].map((x) => Condition.fromJson(x))), - parameters: Parameters.fromJson(json["parameters"]), - parameterGroups: ParameterGroups.fromJson(json["parameterGroups"]), - etag: json["etag"], - version: Version.fromJson(json["version"]), - ); - - Map toJson() => { - "conditions": List.from(conditions.map((x) => x.toJson())), - "parameters": parameters.toJson(), - "parameterGroups": parameterGroups.toJson(), - "etag": etag, - "version": version.toJson(), - }; -} - -class Condition { - Condition({ - required this.name, - required this.expression, - required this.tagColor, - }); - - String name; - String expression; - String tagColor; - - factory Condition.fromJson(Map json) => Condition( - name: json["name"], - expression: json["expression"], - tagColor: json["tagColor"], - ); - - Map toJson() => { - "name": name, - "expression": expression, - "tagColor": tagColor, - }; -} - -class ParameterGroups { - ParameterGroups({ - required this.templates, - }); - - Templates templates; - - factory ParameterGroups.fromJson(Map json) => - ParameterGroups( - templates: Templates.fromJson(json["Templates"]), - ); - - Map toJson() => { - "Templates": templates.toJson(), - }; -} - -class Templates { - Templates({ - required this.parameters, - }); - - Map parameters; - - factory Templates.fromJson(Map json) => Templates( - parameters: Map.from(json["parameters"]).map((k, v) => - MapEntry(k, ConfigValue.fromJson(v))), - ); - - Map toJson() => { - "parameters": Map.from(parameters) - .map((k, v) => MapEntry(k, v.toJson())), - }; -} - -class ConfigValue { - ConfigValue({ - required this.defaultValue, - required this.valueType, - }); - - DefaultValue defaultValue; - String valueType; - - factory ConfigValue.fromJson(Map json) => ConfigValue( - defaultValue: DefaultValue.fromJson(json["defaultValue"]), - valueType: json["valueType"], - ); - - Map toJson() => { - "defaultValue": defaultValue.toJson(), - "valueType": valueType, - }; -} - -class DefaultValue { - DefaultValue({ - required this.value, - }); - - String value; - - factory DefaultValue.fromJson(Map json) => DefaultValue( - value: json["value"], - ); - - Map toJson() => { - "value": value, - }; -} - -class Parameters { - Parameters({ - required this.generalConfig, - required this.web3Config, - required this.secretsConfig, - required this.usersConfig, - required this.limitsConfig, - required this.appConfig, - required this.appDomainsConfig, - }); - - ConfigGeneral generalConfig; - ConfigWeb3 web3Config; - ConfigSecrets secretsConfig; - ConfigUsers usersConfig; - ConfigLimits limitsConfig; - ConfigApp appConfig; - ConfigAppDomains appDomainsConfig; - - factory Parameters.fromJson(Map json) => Parameters( - generalConfig: ConfigGeneral.fromJson( - jsonDecode( - ConfigValue.fromJson(json["general_config"]).defaultValue.value, - ), - ), - web3Config: ConfigWeb3.fromJson( - jsonDecode( - ConfigValue.fromJson(json["web3_config"]).defaultValue.value, - ), - ), - secretsConfig: ConfigSecrets.fromJson( - jsonDecode( - ConfigValue.fromJson(json["secrets_config"]).defaultValue.value, - ), - ), - usersConfig: ConfigUsers.fromJson( - jsonDecode( - ConfigValue.fromJson(json["users_config"]).defaultValue.value, - ), - ), - limitsConfig: ConfigLimits.fromJson( - jsonDecode( - ConfigValue.fromJson(json["limits_config"]).defaultValue.value, - ), - ), - appConfig: ConfigApp.fromJson( - jsonDecode( - ConfigValue.fromJson(json["app_config"]).defaultValue.value, - ), - ), - appDomainsConfig: ConfigAppDomains.fromJson( - jsonDecode( - ConfigValue.fromJson(json["app_domains_config"]).defaultValue.value, - ), - ), - ); - - Map toJson() => { - "general_config": generalConfig.toJson(), - "web3_config": web3Config.toJson(), - "secrets_config": secretsConfig.toJson(), - "users_config": usersConfig.toJson(), - "limits_config": limitsConfig.toJson(), - "app_config": appConfig.toJson(), - "app_domains_config": appDomainsConfig.toJson(), - }; -} - -class Version { - Version({ - required this.versionNumber, - required this.updateOrigin, - required this.updateType, - required this.updateUser, - required this.updateTime, - }); - - String versionNumber; - String updateOrigin; - String updateType; - UpdateUser updateUser; - String updateTime; - - factory Version.fromJson(Map json) => Version( - versionNumber: json["versionNumber"], - updateOrigin: json["updateOrigin"], - updateType: json["updateType"], - updateUser: UpdateUser.fromJson(json["updateUser"]), - updateTime: json["updateTime"], - ); - - Map toJson() => { - "versionNumber": versionNumber, - "updateOrigin": updateOrigin, - "updateType": updateType, - "updateUser": updateUser.toJson(), - "updateTime": updateTime, - }; -} - -class UpdateUser { - UpdateUser({ - required this.email, - }); - - String email; - - factory UpdateUser.fromJson(Map json) => UpdateUser( - email: json["email"], - ); - - Map toJson() => { - "email": email, - }; -} diff --git a/lib/core/firebase/config/models/config_secrets.model.dart b/lib/core/firebase/config/models/config_secrets.model.dart deleted file mode 100644 index 4ea8e900..00000000 --- a/lib/core/firebase/config/models/config_secrets.model.dart +++ /dev/null @@ -1,186 +0,0 @@ -import 'dart:convert'; - -import 'package:get/utils.dart'; -import 'package:liso/core/persistence/persistence.dart'; -import 'package:liso/core/persistence/persistence.secret.dart'; -import 'package:liso/core/utils/globals.dart'; - -class ConfigSecrets { - const ConfigSecrets({ - this.s3 = const ConfigSecretsS3(), - this.revenuecat = const ConfigSecretsRevenuecat(), - this.alchemy = const ConfigSecretsAlchemy(), - this.sentry = const ConfigSecretsSentry(), - this.supabase = const ConfigSecretsSupabase(), - }); - - final ConfigSecretsS3 s3; - final ConfigSecretsRevenuecat revenuecat; - final ConfigSecretsAlchemy alchemy; - final ConfigSecretsSentry sentry; - final ConfigSecretsSupabase supabase; - - factory ConfigSecrets.fromJson(Map json) => ConfigSecrets( - s3: ConfigSecretsS3.fromJson(json["s3"]), - revenuecat: ConfigSecretsRevenuecat.fromJson(json["revenuecat"]), - alchemy: ConfigSecretsAlchemy.fromJson(json["alchemy"]), - sentry: ConfigSecretsSentry.fromJson(json["sentry"]), - supabase: ConfigSecretsSupabase.fromJson(json["supabase"]), - ); - - Map toJson() => { - "s3": s3.toJson(), - "revenuecat": revenuecat.toJson(), - "alchemy": alchemy.toJson(), - "sentry": sentry.toJson(), - "supabase": supabase.toJson(), - }; -} - -class ConfigSecretsAlchemy { - const ConfigSecretsAlchemy({ - this.apiKey = '', - }); - - final String apiKey; - - factory ConfigSecretsAlchemy.fromJson(Map json) => - ConfigSecretsAlchemy( - apiKey: json["apiKey"], - ); - - Map toJson() => { - "apiKey": apiKey, - }; -} - -class ConfigSecretsRevenuecat { - const ConfigSecretsRevenuecat({ - this.appleApiKey = '', - this.googleApiKey = '', - }); - - final String appleApiKey; - final String googleApiKey; - - factory ConfigSecretsRevenuecat.fromJson(Map json) => - ConfigSecretsRevenuecat( - appleApiKey: json["appleApiKey"], - googleApiKey: json["googleApiKey"], - ); - - Map toJson() => { - "appleApiKey": appleApiKey, - "googleApiKey": googleApiKey, - }; - - String get apiKey { - if (GetPlatform.isAndroid) { - return googleApiKey; - } else { - return appleApiKey; - } - } -} - -class ConfigSecretsS3 { - const ConfigSecretsS3({ - this.key = '', - this.secret = '', - this.bucket = '', - this.endpoint = '', - }); - - final String key; - final String secret; - final String bucket; - final String endpoint; - - factory ConfigSecretsS3.fromJson(Map json) => - ConfigSecretsS3( - key: json["key"], - secret: json["secret"], - bucket: json["bucket"], - endpoint: json["endpoint"], - ); - - Map toJson() => { - "key": key, - "secret": secret, - "bucket": bucket, - "endpoint": endpoint, - }; - - String toJsonString() => jsonEncode(toJson()); - - String get preferredBucket { - if (Persistence.to.newSyncProvider == LisoSyncProvider.custom.name) { - return SecretPersistence.to.s3Bucket.val; - } else { - return 'liso-${Persistence.to.newSyncProvider}'; - } - } -} - -class ConfigSecretsSentry { - const ConfigSecretsSentry({ - this.dsn = '', - }); - - final String dsn; - - factory ConfigSecretsSentry.fromJson(Map json) => - ConfigSecretsSentry( - dsn: json["dsn"], - ); - - Map toJson() => { - "dsn": dsn, - }; -} - -class ConfigSecretsSupabase { - const ConfigSecretsSupabase({ - this.url = '', - this.key = '', - this.redirect = const ConfigSecretsSupabaseRedirect(), - }); - - final String url; - final String key; - final ConfigSecretsSupabaseRedirect redirect; - - factory ConfigSecretsSupabase.fromJson(Map json) => - ConfigSecretsSupabase( - url: json["url"], - key: json["key"], - redirect: ConfigSecretsSupabaseRedirect.fromJson(json["redirect"]), - ); - - Map toJson() => { - "url": url, - "key": key, - "redirect": redirect.toJson(), - }; -} - -class ConfigSecretsSupabaseRedirect { - const ConfigSecretsSupabaseRedirect({ - this.scheme = '', - this.host = '', - }); - - final String scheme; - final String host; - - factory ConfigSecretsSupabaseRedirect.fromJson(Map json) => - ConfigSecretsSupabaseRedirect( - scheme: json["scheme"], - host: json["host"], - ); - - Map toJson() => { - "scheme": scheme, - "host": host, - }; -} diff --git a/lib/core/firebase/config/models/config_users.model.dart b/lib/core/firebase/config/models/config_users.model.dart deleted file mode 100644 index ab2170c4..00000000 --- a/lib/core/firebase/config/models/config_users.model.dart +++ /dev/null @@ -1,36 +0,0 @@ -class ConfigUsers { - const ConfigUsers({ - this.users = const [], - }); - - final List users; - - factory ConfigUsers.fromJson(Map json) => ConfigUsers( - users: List.from( - json["users"].map((x) => ConfigUser.fromJson(x))), - ); - - Map toJson() => { - "users": List.from(users.map((x) => x.toJson())), - }; -} - -class ConfigUser { - const ConfigUser({ - this.address = '', - this.limits = '', - }); - - final String address; - final String limits; - - factory ConfigUser.fromJson(Map json) => ConfigUser( - address: json["address"], - limits: json["limits"], - ); - - Map toJson() => { - "address": address, - "limits": limits, - }; -} diff --git a/lib/core/firebase/crashlytics.service.dart b/lib/core/firebase/crashlytics.service.dart deleted file mode 100644 index 03cb7ae1..00000000 --- a/lib/core/firebase/crashlytics.service.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:console_mixin/console_mixin.dart'; -import 'package:firebase_crashlytics/firebase_crashlytics.dart'; -import 'package:flutter/foundation.dart'; -import 'package:get/get.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; - -import '../persistence/persistence.dart'; - -class CrashlyticsService extends GetxService with ConsoleMixin { - static CrashlyticsService get to => Get.find(); - - // VARIABLES - - // GETTERS - FirebaseCrashlytics get instance => FirebaseCrashlytics.instance; - - // INIT - - // FUNCTIONS - - void init() { - // CAPTURE FLUTTER ERRORS - FlutterError.onError = (details) { - console.error("FLUTTER_ERROR"); - record(details.exception, details.stack); - }; - - // TODO: implement new crashlytics soon - // FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; - } - - void configure() { - if (GetPlatform.isWindows) return console.warning('Not Supported'); - - instance.setCrashlyticsCollectionEnabled( - Persistence.to.crashReporting.val, - ); - } - - void record(Object e, StackTrace? s, {bool fatal = false}) { - return CrashlyticsService.recordStatic(FlutterErrorDetails( - exception: e, - stack: s, - )); - } - - static void recordStatic(FlutterErrorDetails details) async { - final console = Console(name: 'CrashlyticsService'); - final errorString = details.summary.value.toString(); - - if (kDebugMode) { - console.error('DEBUG ERROR: $errorString'); - return FlutterError.dumpErrorToConsole( - details, - forceReport: true, - ); - } - - // filtered errors - final filteredErrors = []; - - // filter unnecessary error reports - for (var e in filteredErrors) { - if (errorString.contains(e)) { - return console.error('FILTERED: $errorString'); - } - } - - // send to sentry - if (GetPlatform.isWindows) { - await Sentry.captureException( - details.exception, - stackTrace: details.stack, - ); - - return; - } - - FirebaseCrashlytics.instance.recordFlutterError(details); - } -} diff --git a/lib/core/firebase/config/models/config_app_domains.model.dart b/lib/core/firebase/model/config_app_domains.model.dart similarity index 100% rename from lib/core/firebase/config/models/config_app_domains.model.dart rename to lib/core/firebase/model/config_app_domains.model.dart diff --git a/lib/core/firebase/config/models/config_limits.model.dart b/lib/core/firebase/model/config_limits.model.dart similarity index 95% rename from lib/core/firebase/config/models/config_limits.model.dart rename to lib/core/firebase/model/config_limits.model.dart index bde81c71..c1df36ce 100644 --- a/lib/core/firebase/config/models/config_limits.model.dart +++ b/lib/core/firebase/model/config_limits.model.dart @@ -1,6 +1,5 @@ class ConfigLimits { const ConfigLimits({ - this.settings = const ConfigLimitsSettings(), this.free = const ConfigLimitsTier(), this.holder = const ConfigLimitsTier(), this.staker = const ConfigLimitsTier(), @@ -8,7 +7,6 @@ class ConfigLimits { this.pro = const ConfigLimitsTier(), }); - final ConfigLimitsSettings settings; final ConfigLimitsTier free; final ConfigLimitsTier holder; final ConfigLimitsTier staker; @@ -16,7 +14,6 @@ class ConfigLimits { final ConfigLimitsTier pro; factory ConfigLimits.fromJson(Map json) => ConfigLimits( - settings: ConfigLimitsSettings.fromJson(json["settings"]), free: ConfigLimitsTier.fromJson(json["free"]), holder: ConfigLimitsTier.fromJson(json["holder"]), staker: ConfigLimitsTier.fromJson(json["staker"]), @@ -25,7 +22,6 @@ class ConfigLimits { ); Map toJson() => { - "settings": settings.toJson(), "free": free.toJson(), "holder": holder.toJson(), "staker": staker.toJson(), diff --git a/lib/core/firebase/config/models/config_web3.model.dart b/lib/core/firebase/model/config_web3.model.dart similarity index 100% rename from lib/core/firebase/config/models/config_web3.model.dart rename to lib/core/firebase/model/config_web3.model.dart diff --git a/lib/core/form_fields/address.field.dart b/lib/core/form_fields/address.field.dart index 954621ba..7368f4a1 100644 --- a/lib/core/form_fields/address.field.dart +++ b/lib/core/form_fields/address.field.dart @@ -1,16 +1,16 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; import 'package:liso/core/form_fields/choices.field.dart'; import 'package:liso/core/hive/models/field.hive.dart'; -import 'package:liso/core/utils/globals.dart'; import '../../features/general/section.widget.dart'; import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; -import '../utils/utils.dart'; // ignore: must_be_immutable class AddressFormField extends StatefulWidget with ConsoleMixin { diff --git a/lib/core/form_fields/date.field.dart b/lib/core/form_fields/date.field.dart index 74854f9c..5b90eaeb 100644 --- a/lib/core/form_fields/date.field.dart +++ b/lib/core/form_fields/date.field.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:iconsax/iconsax.dart'; @@ -9,7 +11,6 @@ import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; import '../utils/globals.dart'; -import '../utils/utils.dart'; class DateFormField extends StatefulWidget { final HiveLisoField field; diff --git a/lib/core/form_fields/email.field.dart b/lib/core/form_fields/email.field.dart index e8ee4a39..4c32c1ba 100644 --- a/lib/core/form_fields/email.field.dart +++ b/lib/core/form_fields/email.field.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/utils.dart'; import 'package:iconsax/iconsax.dart'; @@ -8,7 +10,6 @@ import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; import '../utils/globals.dart'; -import '../utils/utils.dart'; class EmailFormField extends StatefulWidget { final HiveLisoField field; diff --git a/lib/core/form_fields/number.field.dart b/lib/core/form_fields/number.field.dart index c69e1cfc..3e9835ce 100644 --- a/lib/core/form_fields/number.field.dart +++ b/lib/core/form_fields/number.field.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/utils.dart'; import 'package:iconsax/iconsax.dart'; @@ -8,7 +10,6 @@ import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; import '../utils/globals.dart'; -import '../utils/utils.dart'; class NumberFormField extends StatefulWidget { final HiveLisoField field; diff --git a/lib/core/form_fields/passport.field.dart b/lib/core/form_fields/passport.field.dart index e1b3a8e0..581249d7 100644 --- a/lib/core/form_fields/passport.field.dart +++ b/lib/core/form_fields/passport.field.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/utils.dart'; import 'package:iconsax/iconsax.dart'; @@ -7,8 +9,6 @@ import 'package:liso/core/hive/models/field.hive.dart'; import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; -import '../utils/globals.dart'; -import '../utils/utils.dart'; class PassportFormField extends StatefulWidget { final HiveLisoField field; diff --git a/lib/core/form_fields/password.field.dart b/lib/core/form_fields/password.field.dart index c3aba5fd..0ff4a6ca 100644 --- a/lib/core/form_fields/password.field.dart +++ b/lib/core/form_fields/password.field.dart @@ -1,3 +1,6 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; @@ -8,7 +11,6 @@ import '../../features/app/routes.dart'; import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; -import '../../features/pro/pro.controller.dart'; import '../utils/globals.dart'; import '../utils/utils.dart'; @@ -56,8 +58,10 @@ class _PasswordFormFieldState extends State { onSelected: () => setState(() { obscureText = !obscureText; }), - leading: Icon(obscureText ? Iconsax.eye : Iconsax.eye_slash, - size: popupIconSize), + leading: Icon( + obscureText ? Iconsax.eye : Iconsax.eye_slash, + size: popupIconSize, + ), ), if (widget.isPasswordField && !widget.field.readOnly) ...[ ContextMenuItem( @@ -71,7 +75,7 @@ class _PasswordFormFieldState extends State { leading: Icon(Iconsax.copy, size: popupIconSize), onSelected: () => Utils.copyToClipboard(widget.fieldController.text), ), - if (!ProController.to.limits.passwordHealth) ...[ + if (!limits.passwordHealth) ...[ ContextMenuItem( title: 'Password Health', leading: Icon(Iconsax.health, size: popupIconSize), @@ -113,7 +117,7 @@ class _PasswordFormFieldState extends State { // FUNCTIONS void _generate() async { final password_ = await Utils.adaptiveRouteOpen( - name: Routes.passwordGenerator, + name: AppRoutes.passwordGenerator, parameters: {'return': 'true'}, ); @@ -138,13 +142,13 @@ class _PasswordFormFieldState extends State { decoration: InputDecoration( labelText: widget.field.data.label, hintText: widget.field.data.hint, - helperText: ProController.to.limits.passwordHealth && + helperText: limits.passwordHealth && widget.isPasswordField && widget.fieldController.text.isNotEmpty - ? Utils.strengthName(strength).toUpperCase() + ? AppUtils.strengthName(strength).toUpperCase() : null, helperStyle: TextStyle( - color: Utils.strengthColor(strength), + color: AppUtils.strengthColor(strength), fontWeight: FontWeight.bold, ), suffixIcon: ContextMenuButton( diff --git a/lib/core/form_fields/phone.field.dart b/lib/core/form_fields/phone.field.dart index b5e5fb5c..82d4c815 100644 --- a/lib/core/form_fields/phone.field.dart +++ b/lib/core/form_fields/phone.field.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/utils.dart'; import 'package:iconsax/iconsax.dart'; @@ -8,7 +10,6 @@ import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; import '../utils/globals.dart'; -import '../utils/utils.dart'; class PhoneFormField extends StatefulWidget { final HiveLisoField field; diff --git a/lib/core/form_fields/pin.field.dart b/lib/core/form_fields/pin.field.dart index 879d8fcf..280af22f 100644 --- a/lib/core/form_fields/pin.field.dart +++ b/lib/core/form_fields/pin.field.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get_utils/src/get_utils/get_utils.dart'; import 'package:iconsax/iconsax.dart'; @@ -8,7 +10,6 @@ import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; import '../utils/globals.dart'; -import '../utils/utils.dart'; class PINFormField extends StatefulWidget { final HiveLisoField field; @@ -47,8 +48,10 @@ class _PINFormFieldState extends State { onSelected: () => setState(() { obscureText = !obscureText; }), - leading: Icon(obscureText ? Iconsax.eye : Iconsax.eye_slash, - size: popupIconSize), + leading: Icon( + obscureText ? Iconsax.eye : Iconsax.eye_slash, + size: popupIconSize, + ), ), ContextMenuItem( title: 'Copy', diff --git a/lib/core/form_fields/section.field.dart b/lib/core/form_fields/section.field.dart index f64d3c4c..c0e8c4a6 100644 --- a/lib/core/form_fields/section.field.dart +++ b/lib/core/form_fields/section.field.dart @@ -1,3 +1,4 @@ +import 'package:app_core/globals.dart'; import 'package:flutter/material.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; @@ -7,7 +8,6 @@ import 'package:liso/features/general/section.widget.dart'; import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; -import '../utils/globals.dart'; class SectionFormField extends StatefulWidget { final HiveLisoField field; diff --git a/lib/core/form_fields/textarea.field.dart b/lib/core/form_fields/textarea.field.dart index 9c700799..e1fc00c6 100644 --- a/lib/core/form_fields/textarea.field.dart +++ b/lib/core/form_fields/textarea.field.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; @@ -6,8 +8,6 @@ import 'package:liso/core/hive/models/field.hive.dart'; import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; -import '../utils/globals.dart'; -import '../utils/utils.dart'; class TextAreaFormField extends StatefulWidget { final HiveLisoField field; diff --git a/lib/core/form_fields/textfield.field.dart b/lib/core/form_fields/textfield.field.dart index 2d7110c7..162c245f 100644 --- a/lib/core/form_fields/textfield.field.dart +++ b/lib/core/form_fields/textfield.field.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; @@ -6,8 +8,6 @@ import 'package:liso/features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; -import '../utils/globals.dart'; -import '../utils/utils.dart'; class TextFieldForm extends StatefulWidget { final HiveLisoField field; diff --git a/lib/core/form_fields/toggle.field.dart b/lib/core/form_fields/toggle.field.dart index 307dfe7a..1141c4cb 100644 --- a/lib/core/form_fields/toggle.field.dart +++ b/lib/core/form_fields/toggle.field.dart @@ -1,3 +1,4 @@ +import 'package:app_core/globals.dart'; import 'package:flutter/material.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; @@ -6,7 +7,6 @@ import 'package:liso/features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; -import '../utils/globals.dart'; class ToggleFieldForm extends StatefulWidget { final HiveLisoField field; diff --git a/lib/core/form_fields/totp.field.dart b/lib/core/form_fields/totp.field.dart index c362ff78..375b5ca3 100644 --- a/lib/core/form_fields/totp.field.dart +++ b/lib/core/form_fields/totp.field.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; @@ -6,8 +8,6 @@ import 'package:liso/core/hive/models/field.hive.dart'; import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; -import '../utils/globals.dart'; -import '../utils/utils.dart'; class TOTPFormField extends StatefulWidget { final HiveLisoField field; @@ -45,8 +45,10 @@ class _TOTPFormFieldState extends State { onSelected: () => setState(() { obscureText = !obscureText; }), - leading: Icon(obscureText ? Iconsax.eye : Iconsax.eye_slash, - size: popupIconSize), + leading: Icon( + obscureText ? Iconsax.eye : Iconsax.eye_slash, + size: popupIconSize, + ), ), ContextMenuItem( title: 'Copy', diff --git a/lib/core/form_fields/url.field.dart b/lib/core/form_fields/url.field.dart index 54b80e3e..4d2ed64f 100644 --- a/lib/core/form_fields/url.field.dart +++ b/lib/core/form_fields/url.field.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; @@ -6,8 +8,6 @@ import 'package:liso/core/hive/models/field.hive.dart'; import '../../features/items/item_screen.controller.dart'; import '../../features/menu/menu.button.dart'; import '../../features/menu/menu.item.dart'; -import '../utils/globals.dart'; -import '../utils/utils.dart'; class URLFormField extends StatefulWidget { final HiveLisoField field; diff --git a/lib/core/hive/hive.service.dart b/lib/core/hive/hive.service.dart index 8084a3fb..52906698 100644 --- a/lib/core/hive/hive.service.dart +++ b/lib/core/hive/hive.service.dart @@ -1,11 +1,11 @@ +import 'package:app_core/hive/models/app.hive.dart'; +import 'package:app_core/hive/models/device.hive.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:liso/core/hive/models/app_domain.hive.dart'; import 'package:liso/core/hive/models/category.hive.dart'; import 'package:liso/core/hive/models/group.hive.dart'; -import 'package:liso/core/hive/models/metadata/app.hive.dart'; -import 'package:liso/core/hive/models/metadata/device.hive.dart'; import 'package:liso/features/groups/groups.service.dart'; import '../../features/categories/categories.service.dart'; diff --git a/lib/core/hive/models/item.hive.dart b/lib/core/hive/models/item.hive.dart index 002bfeb7..3d73db20 100644 --- a/lib/core/hive/models/item.hive.dart +++ b/lib/core/hive/models/item.hive.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; @@ -9,10 +10,7 @@ import 'package:hive/hive.dart'; import 'package:intl/intl.dart'; import 'package:liso/core/utils/globals.dart'; import 'package:random_string_generator/random_string_generator.dart'; -import 'package:supercharged/supercharged.dart'; -import '../../../features/pro/pro.controller.dart'; -import '../../utils/utils.dart'; import 'field.hive.dart'; import 'metadata/metadata.hive.dart'; @@ -207,10 +205,12 @@ class HiveLisoItem extends HiveObject with EquatableMixin, ConsoleMixin { String get updatedTimeAgo => Utils.timeAgo(metadata.updatedTime, short: false); - int get daysLeftToDelete => - metadata.updatedTime.duration().inDays - - DateTime.now().duration().inDays + - ProController.to.limits.trashDays; + // int get daysLeftToDelete => + // metadata.updatedTime.duration().inDays - + // DateTime.now().duration().inDays + + // limits.trashDays; + // TODO: temporary + int get daysLeftToDelete => 30; String get subTitle { String identifier = significant.keys.first; diff --git a/lib/core/hive/models/metadata/app.hive.dart b/lib/core/hive/models/metadata/app.hive.dart deleted file mode 100644 index f0010aaa..00000000 --- a/lib/core/hive/models/metadata/app.hive.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:hive/hive.dart'; -import 'package:package_info_plus/package_info_plus.dart'; - -part 'app.hive.g.dart'; - -@HiveType(typeId: 221) -class HiveMetadataApp extends HiveObject { - @HiveField(0) - String appName; - @HiveField(1) - String packageName; - @HiveField(2) - String version; - @HiveField(3) - String buildNumber; - - HiveMetadataApp({ - required this.appName, - required this.packageName, - required this.version, - required this.buildNumber, - }); - - factory HiveMetadataApp.fromJson(Map json) => - HiveMetadataApp( - appName: json["appName"], - packageName: json["packageName"], - version: json["version"], - buildNumber: json["buildNumber"], - ); - - Map toJson() => { - "appName": appName, - "packageName": packageName, - "version": version, - "buildNumber": buildNumber, - }; - - String get formattedVersion => 'v$version+$buildNumber'; - - static Future get() async { - final packageInfo = await PackageInfo.fromPlatform(); - - return HiveMetadataApp( - appName: packageInfo.appName, - packageName: packageInfo.packageName, - version: packageInfo.version, - buildNumber: packageInfo.buildNumber, - ); - } - - static Future> getJson() async => (await get()).toJson(); -} diff --git a/lib/core/hive/models/metadata/app.hive.g.dart b/lib/core/hive/models/metadata/app.hive.g.dart deleted file mode 100644 index d6ab870e..00000000 --- a/lib/core/hive/models/metadata/app.hive.g.dart +++ /dev/null @@ -1,50 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'app.hive.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class HiveMetadataAppAdapter extends TypeAdapter { - @override - final int typeId = 221; - - @override - HiveMetadataApp read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return HiveMetadataApp( - appName: fields[0] as String, - packageName: fields[1] as String, - version: fields[2] as String, - buildNumber: fields[3] as String, - ); - } - - @override - void write(BinaryWriter writer, HiveMetadataApp obj) { - writer - ..writeByte(4) - ..writeByte(0) - ..write(obj.appName) - ..writeByte(1) - ..write(obj.packageName) - ..writeByte(2) - ..write(obj.version) - ..writeByte(3) - ..write(obj.buildNumber); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is HiveMetadataAppAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/core/hive/models/metadata/device.hive.dart b/lib/core/hive/models/metadata/device.hive.dart deleted file mode 100644 index 006f1a05..00000000 --- a/lib/core/hive/models/metadata/device.hive.dart +++ /dev/null @@ -1,158 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:device_info_plus/device_info_plus.dart'; -import 'package:get/get_utils/src/platform/platform.dart'; -import 'package:hive/hive.dart'; -import 'package:platform_device_id/platform_device_id.dart'; - -import '../../../utils/utils.dart'; - -part 'device.hive.g.dart'; - -@HiveType(typeId: 222) -class HiveMetadataDevice extends HiveObject { - @HiveField(0) - String id; - @HiveField(1) - String? name; - @HiveField(2) - String model; - @HiveField(3) - String platform; - @HiveField(4) - String osVersion; - @HiveField(5) - Map? info; - - String docId; - - HiveMetadataDevice({ - this.docId = '', - this.id = '', - this.name = '', - this.model = '', - this.platform = '', - this.osVersion = '', - this.info, - }); - - factory HiveMetadataDevice.fromJson(Map json) => - HiveMetadataDevice( - id: json["id"], - name: json["name"], - model: json["model"], - platform: json["platform"], - osVersion: json["osVersion"], - info: json["info"], - ); - - Map toJson() => { - "id": id, - "name": name, - "model": model, - "platform": platform, - "osVersion": osVersion, - "info": info, - }; - - static Future get() async { - final device = HiveMetadataDevice(platform: Utils.platformName()); - final deviceInfo = DeviceInfoPlugin(); - device.id = (await PlatformDeviceId.getDeviceId)!; - - if (GetPlatform.isWeb) { - // TODO: obtain userAgent - device.info = {'userAgent': ''}; - } else if (GetPlatform.isIOS) { - final info = await deviceInfo.iosInfo; - device.osVersion = info.systemVersion ?? ''; - device.name = info.name ?? ''; - device.model = info.utsname.machine ?? ''; - device.info = { - 'identifierForVendor': info.identifierForVendor, - 'isPhysicalDevice': info.isPhysicalDevice, - 'systemName': info.systemName, - }; - } else if (GetPlatform.isAndroid) { - final info = await deviceInfo.androidInfo; - device.osVersion = info.version.release; - device.name = info.device; - device.model = info.model; - device.info = { - 'id': info.id, - 'brand': info.brand, - 'display': info.display, - 'manufacturer': info.manufacturer, - 'product': info.product, - 'host': info.host, - }; - } else if (GetPlatform.isMacOS) { - final info = await deviceInfo.macOsInfo; - device.osVersion = info.osRelease; - device.name = info.computerName; - device.model = info.model; - device.info = { - 'activeCPUs': info.activeCPUs, - 'arch': info.arch, - 'cpuFrequency': info.cpuFrequency, - 'hostName': info.hostName, - 'kernelVersion': info.kernelVersion, - 'memorySize': info.memorySize, - 'systemGUID': info.systemGUID, - }; - } else if (GetPlatform.isWindows) { - final info = await deviceInfo.windowsInfo; - device.id = info.deviceId.replaceAll('{', '').replaceAll('}', ''); - device.name = info.computerName; - device.osVersion = '${info.productName} ${info.displayVersion}'; - device.info = { - 'majorVersion': info.majorVersion, - 'minorVersion': info.minorVersion, - 'platformId': info.platformId, - 'editionId': info.editionId, - 'releaseId': info.releaseId, - 'systemMemoryInMegabytes': info.systemMemoryInMegabytes, - }; - } else if (GetPlatform.isLinux) { - final info = await deviceInfo.linuxInfo; - device.osVersion = info.version ?? ''; - device.name = info.prettyName; - device.model = info.variant ?? ''; - device.info = { - 'buildId': info.buildId, - 'variantId': info.variantId, - 'machineId': info.machineId, - 'name': info.name, - 'versionCodename': info.versionCodename, - 'versionId': info.versionId, - }; - } - - return device; - } - - // manually obtain device id via process to avoid flashing window bug - // https://github.com/BestBurning/platform_device_id/issues/15 - static Future getWindowsDeviceID() async { - final process = await Process.start( - 'wmic', - ['csproduct', 'get', 'UUID'], - mode: ProcessStartMode.detachedWithStdio, - ); - - final result = await process.stdout.transform(utf8.decoder).toList(); - String deviceId = ''; - - for (var element in result) { - final item = element.replaceAll(RegExp('\r|\n|\\s|UUID|uuid'), ''); - if (item.isNotEmpty) { - deviceId = item; - } - } - - return deviceId; - } - - static Future> getJson() async => (await get()).toJson(); -} diff --git a/lib/core/hive/models/metadata/device.hive.g.dart b/lib/core/hive/models/metadata/device.hive.g.dart deleted file mode 100644 index 85361c90..00000000 --- a/lib/core/hive/models/metadata/device.hive.g.dart +++ /dev/null @@ -1,56 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'device.hive.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class HiveMetadataDeviceAdapter extends TypeAdapter { - @override - final int typeId = 222; - - @override - HiveMetadataDevice read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return HiveMetadataDevice( - id: fields[0] as String, - name: fields[1] as String?, - model: fields[2] as String, - platform: fields[3] as String, - osVersion: fields[4] as String, - info: (fields[5] as Map?)?.cast(), - ); - } - - @override - void write(BinaryWriter writer, HiveMetadataDevice obj) { - writer - ..writeByte(6) - ..writeByte(0) - ..write(obj.id) - ..writeByte(1) - ..write(obj.name) - ..writeByte(2) - ..write(obj.model) - ..writeByte(3) - ..write(obj.platform) - ..writeByte(4) - ..write(obj.osVersion) - ..writeByte(5) - ..write(obj.info); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is HiveMetadataDeviceAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/core/hive/models/metadata/metadata.hive.dart b/lib/core/hive/models/metadata/metadata.hive.dart index 2c6cf1a0..ad5495a0 100644 --- a/lib/core/hive/models/metadata/metadata.hive.dart +++ b/lib/core/hive/models/metadata/metadata.hive.dart @@ -1,10 +1,9 @@ import 'dart:convert'; +import 'package:app_core/hive/models/app.hive.dart'; +import 'package:app_core/hive/models/device.hive.dart'; import 'package:hive/hive.dart'; -import 'app.hive.dart'; -import 'device.hive.dart'; - part 'metadata.hive.g.dart'; @HiveType(typeId: 220) diff --git a/lib/core/liso/liso.manager.dart b/lib/core/liso/liso.manager.dart index f7e3559d..f98ee545 100644 --- a/lib/core/liso/liso.manager.dart +++ b/lib/core/liso/liso.manager.dart @@ -2,15 +2,15 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'package:app_core/persistence/persistence.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:file_picker/file_picker.dart'; import 'package:get/get.dart'; import 'package:liso/core/hive/hive.service.dart'; +import 'package:liso/core/liso/vault.model.dart'; import 'package:liso/core/middlewares/authentication.middleware.dart'; import 'package:liso/core/persistence/persistence.secret.dart'; import 'package:liso/features/categories/categories.service.dart'; -import 'package:liso/core/liso/vault.model.dart'; -import 'package:liso/core/persistence/persistence.dart'; import 'package:liso/features/drawer/drawer_widget.controller.dart'; import 'package:liso/features/files/sync.service.dart'; import 'package:liso/features/wallet/wallet.service.dart'; @@ -19,7 +19,6 @@ import 'package:purchases_flutter/purchases_flutter.dart'; import '../../features/groups/groups.service.dart'; import '../../features/items/items.service.dart'; -import '../../features/supabase/supabase_auth.service.dart'; import '../hive/models/metadata/metadata.hive.dart'; import '../services/cipher.service.dart'; import '../utils/globals.dart'; @@ -50,8 +49,6 @@ class LisoManager { if (GetPlatform.isMobile) { await FilePicker.platform.clearTemporaryFiles(); } - // reset firebase - await SupabaseAuthService.to.signOut(); // clear hives await HiveService.to.clear(); // clean temp folder diff --git a/lib/core/middlewares/authentication.middleware.dart b/lib/core/middlewares/authentication.middleware.dart index 965a44e8..89d74f52 100644 --- a/lib/core/middlewares/authentication.middleware.dart +++ b/lib/core/middlewares/authentication.middleware.dart @@ -1,12 +1,11 @@ +import 'package:app_core/firebase/crashlytics.service.dart'; +import 'package:app_core/pages/routes.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:liso/core/firebase/crashlytics.service.dart'; import 'package:liso/features/main/main_screen.controller.dart'; import 'package:liso/features/wallet/wallet.service.dart'; -import '../../features/app/routes.dart'; - class AuthenticationMiddleware extends GetMiddleware with ConsoleMixin { static bool initialized = false; static bool signedIn = false; diff --git a/lib/core/notifications/notifications.manager.dart b/lib/core/notifications/notifications.manager.dart deleted file mode 100644 index f52745a9..00000000 --- a/lib/core/notifications/notifications.manager.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:get/get_utils/src/platform/platform.dart'; -import 'package:liso/core/persistence/persistence.dart'; -import 'package:console_mixin/console_mixin.dart'; - -class NotificationsManager { - static final plugin = FlutterLocalNotificationsPlugin(); - static final console = Console(name: 'NotificationsManager'); - - static void cancel(int id) => plugin.cancel(id); - static void cancelAll() => plugin.cancelAll(); - - static void init() async { - if (GetPlatform.isWindows) return console.warning('not supported'); - - const darwinSettings = DarwinInitializationSettings( - onDidReceiveLocalNotification: onForegroundPayload, - ); - - const androidSettings = AndroidInitializationSettings('ic_notification'); - - // initialise the plugin. app_icon needs to be a added as a drawable resource to the Android head project - await plugin.initialize( - onDidReceiveNotificationResponse: onBackgroundPayload, - const InitializationSettings( - android: androidSettings, - iOS: darwinSettings, - macOS: darwinSettings, - ), - ); - - console.info("init"); - } - - static void notify({ - required final String title, - required final String body, - String payload = '', - }) async { - if (GetPlatform.isWindows) return console.warning('not supported'); - - const darwinDetails = DarwinNotificationDetails(); - const linuxDetails = LinuxNotificationDetails(); - - const androidDetails = AndroidNotificationDetails( - "general", - "General", - channelDescription: "General Notifications", - ); - - const details = NotificationDetails( - android: androidDetails, - iOS: darwinDetails, - macOS: darwinDetails, - linux: linuxDetails, - ); - - await plugin.show( - Persistence.to.notificationId.val++, - title, - body, - details, - payload: payload, - ); - - console.info('notified'); - } - - static void onBackgroundPayload(NotificationResponse? response) async { - console.info('onBackgroundPayload payload: ${response?.payload}'); - } - - static void onForegroundPayload( - int? id, - String? title, - String? body, - String? payload, - ) async { - console.info('onForegroundPayload payload: ${payload!}'); - } -} diff --git a/lib/core/persistence/mutable_value.dart b/lib/core/persistence/mutable_value.dart deleted file mode 100644 index ff7c080d..00000000 --- a/lib/core/persistence/mutable_value.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'persistence.dart'; - -class MutableValue { - final String key; - final T defaultValue; - - MutableValue(this.key, this.defaultValue); - - T get val => Persistence.box != null && Persistence.box!.isOpen - ? Persistence.box?.get(key) ?? defaultValue - : defaultValue; - - set val(T value) { - if (Persistence.box == null || !Persistence.box!.isOpen) return; - Persistence.box?.put(key, value); - Persistence.to.update(); - } -} - -extension Data on T { - MutableValue val( - String key, { - T? defaultValue, - }) => - MutableValue(key, defaultValue ?? this); -} diff --git a/lib/core/persistence/persistence.dart b/lib/core/persistence/persistence.dart index bd507351..b4e842b2 100644 --- a/lib/core/persistence/persistence.dart +++ b/lib/core/persistence/persistence.dart @@ -1,20 +1,15 @@ -import 'dart:convert'; - +import 'package:app_core/persistence/mutable_value.dart'; +import 'package:app_core/persistence/persistence.dart'; +import 'package:app_core/supabase/supabase_auth.service.dart'; import 'package:console_mixin/console_mixin.dart'; -import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; -import 'package:liso/core/persistence/mutable_value.dart'; -import 'package:secrets/secrets.dart'; -import '../../features/supabase/supabase_auth.service.dart'; -import '../liso/liso_paths.dart'; -import '../translations/data.dart'; import '../utils/globals.dart'; -class Persistence extends GetxController with ConsoleMixin { +class AppPersistence extends Persistence with ConsoleMixin { // STATIC - static Persistence get to => Get.find(); + static AppPersistence get to => Get.find(); static Box? box; // WALLET JSON @@ -25,32 +20,15 @@ class Persistence extends GetxController with ConsoleMixin { final walletPrivateKeyHex = ''.val('wallet-private-key-hex'); final walletAddress = ''.val('wallet-address'); // GENERAL - final localeCode = 'en'.val('locale-code'); - final crashReporting = true.val('crash-reporting'); - final analytics = true.val('analytics'); - final proTester = false.val('pro-tester'); - final lastBuildNumber = 0.val('last-build-number'); - final lastServerDateTime = ''.val('last-server-datetime'); final backedUpSeed = false.val('backed-up-seed-phrase'); final backedUpPassword = false.val('backed-up-password'); - final notificationId = 0.val('notification-id'); final upgradeScreenShown = false.val('upgrade-screen-shown'); final sessionCount = 1.val('session-count'); final rateDialogShown = false.val('rate-dialog-shown'); final rateCardVisibility = true.val('rate-card-visibility'); - final verifiedProCache = false.val('verified-pro-cache'); - // WINDOW SIZE - final windowWidth = 1200.0.val('window-width'); - final windowHeight = 850.0.val('window-height'); - // THEME - final theme = ThemeMode.system.name.val('theme'); - // SECURITY - final maxUnlockAttempts = 10.val('max-unlock-attempts'); - final timeLockDuration = 120.val('time-lock-duration'); // in seconds // SYNC final sync = true.val('sync'); final syncProvider = LisoSyncProvider.sia.name.val('sync-provider'); - final biometrics = true.val('biometrics'); final s3ObjectsCache = ''.val('s3-objects-cache'); // // CUSTOM SYNC PROVIDER final s3Endpoint = ''.val('s3-endpoint'); @@ -72,8 +50,6 @@ class Persistence extends GetxController with ConsoleMixin { final lastLisoBalance = 0.0.val('last-liso-balance'); final lastMaticUsdPrice = 0.0.val('last-matic-usd-price'); final lastLisoUsdPrice = 0.0.val('last-liso-usd-price'); - // SUPABASE - final supabaseSession = ''.val('supabase-session'); // DELETED IDS // GETTERS @@ -112,32 +88,5 @@ class Persistence extends GetxController with ConsoleMixin { } // STATIC - static Future open() async { - box = await Hive.openBox( - kHiveBoxPersistence, - encryptionCipher: HiveAesCipher(base64Decode(Secrets.persistenceKey)), - path: LisoPaths.hivePath, - ); - - _initLocale(); - } - - static Future reset() async { - await box?.clear(); - await box?.deleteFromDisk(); - await open(); - } - - static void _initLocale() { - final deviceLanguage = Get.deviceLocale?.languageCode; - final isSystemLocaleSupported = - translationKeys[deviceLanguage ?? 'en'] != null; - final defaultLocaleCode = isSystemLocaleSupported ? deviceLanguage : 'en'; - final localeCode = box?.get('locale code'); - - if (defaultLocaleCode != null && localeCode == null) { - box?.put('locale code', defaultLocaleCode); - } - } } diff --git a/lib/core/persistence/persistence.secret.dart b/lib/core/persistence/persistence.secret.dart index 05a79629..7f340b59 100644 --- a/lib/core/persistence/persistence.secret.dart +++ b/lib/core/persistence/persistence.secret.dart @@ -1,10 +1,10 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:app_core/persistence/mutable_value.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; -import 'package:liso/core/persistence/mutable_value.dart'; import 'package:liso/core/persistence/persistence.dart'; import 'package:secrets/secrets.dart'; @@ -82,7 +82,8 @@ class SecretPersistence extends GetxController with ConsoleMixin { } static Future migrate() async { - final p = Get.find(); + final p = Get.find(); + // if already migrated if (p.migratedSecrets.val) return; final s = Get.find(); diff --git a/lib/core/persistence/persistence_builder.widget.dart b/lib/core/persistence/persistence_builder.widget.dart deleted file mode 100644 index 30f6032d..00000000 --- a/lib/core/persistence/persistence_builder.widget.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get_state_manager/src/simple/get_state.dart'; -import 'package:liso/core/persistence/persistence.secret.dart'; - -import 'persistence.dart'; - -class PersistenceBuilder extends StatefulWidget { - final Widget Function(Persistence, BuildContext) builder; - const PersistenceBuilder({Key? key, required this.builder}) : super(key: key); - - @override - State createState() => _PersistenceBuilderState(); -} - -class _PersistenceBuilderState extends State { - @override - Widget build(BuildContext context) { - return GetBuilder( - init: Persistence.to, - builder: (_) => widget.builder(_, context), - ); - } -} - -class SecretPersistenceBuilder extends StatefulWidget { - final Widget Function(SecretPersistence, BuildContext) builder; - const SecretPersistenceBuilder({Key? key, required this.builder}) - : super(key: key); - - @override - State createState() => - _SecretPersistenceBuilderState(); -} - -class _SecretPersistenceBuilderState extends State { - @override - Widget build(BuildContext context) { - return GetBuilder( - init: SecretPersistence.to, - builder: (_) => widget.builder(_, context), - ); - } -} diff --git a/lib/core/persistence/secret_persistence.builder.dart b/lib/core/persistence/secret_persistence.builder.dart new file mode 100644 index 00000000..6c43c5c7 --- /dev/null +++ b/lib/core/persistence/secret_persistence.builder.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'persistence.secret.dart'; + +class SecretPersistenceBuilder extends StatefulWidget { + final Widget Function(SecretPersistence, BuildContext) builder; + const SecretPersistenceBuilder({Key? key, required this.builder}) + : super(key: key); + + @override + State createState() => + _SecretPersistenceBuilderState(); +} + +class _SecretPersistenceBuilderState extends State { + @override + Widget build(BuildContext context) { + return GetBuilder( + init: SecretPersistence.to, + builder: (_) => widget.builder(_, context), + ); + } +} diff --git a/lib/core/services/alchemy.service.dart b/lib/core/services/alchemy.service.dart index 8b7e9658..c925d621 100644 --- a/lib/core/services/alchemy.service.dart +++ b/lib/core/services/alchemy.service.dart @@ -1,27 +1,27 @@ import 'package:alchemy_web3/alchemy_web3.dart'; +import 'package:app_core/connectivity/connectivity.service.dart'; +import 'package:app_core/firebase/config/config.service.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:get/get.dart'; -import 'package:liso/core/persistence/persistence.dart'; import 'package:web3dart/web3dart.dart'; import '../../contracts/liso.dart'; -import '../../features/connectivity/connectivity.service.dart'; import '../../features/wallet/wallet.service.dart'; -import '../firebase/config/config.service.dart'; -import '../firebase/config/models/config_web3.model.dart'; +import '../firebase/model/config_web3.model.dart'; +import '../persistence/persistence.dart'; import '../persistence/persistence.secret.dart'; +import '../utils/globals.dart'; class AlchemyService extends GetxService with ConsoleMixin { static AlchemyService get to => Get.find(); // VARIABLES final alchemy = Alchemy(); - final persistence = Get.find(); final config = Get.find(); final wallet = Get.find(); // GETTERS - Chain get polygonChain => config.web3.chains.first; + Chain get polygonChain => configWeb3.chains.first; // INIT @@ -74,9 +74,9 @@ class AlchemyService extends GetxService with ConsoleMixin { 'Error: ${error.code} : ${error.message}', ), (response) { - persistence.lastLisoBalance.val = + AppPersistence.to.lastLisoBalance.val = response.getValueInUnit(EtherUnit.ether); - console.info('liso balance: ${persistence.lastLisoBalance.val}'); + console.info('liso balance: ${AppPersistence.to.lastLisoBalance.val}'); }, ); } @@ -95,9 +95,10 @@ class AlchemyService extends GetxService with ConsoleMixin { 'Error: ${error.code} : ${error.message}', ), (response) { - persistence.lastMaticBalance.val = + AppPersistence.to.lastMaticBalance.val = response.getValueInUnit(EtherUnit.ether); - console.info('matic balance: ${persistence.lastMaticBalance.val}'); + console + .info('matic balance: ${AppPersistence.to.lastMaticBalance.val}'); }, ); } diff --git a/lib/core/services/local_auth.service.dart b/lib/core/services/local_auth.service.dart deleted file mode 100644 index 9801f6b5..00000000 --- a/lib/core/services/local_auth.service.dart +++ /dev/null @@ -1,116 +0,0 @@ -import 'package:app_settings/app_settings.dart'; -import 'package:console_mixin/console_mixin.dart'; -import 'package:flutter/services.dart'; -import 'package:get/get.dart'; -import 'package:liso/core/firebase/config/config.service.dart'; -import 'package:liso/core/firebase/crashlytics.service.dart'; -import 'package:liso/core/utils/ui_utils.dart'; -import 'package:local_auth/error_codes.dart' as auth_error; -import 'package:local_auth/local_auth.dart'; -// ignore: depend_on_referenced_packages -import 'package:local_auth_android/local_auth_android.dart'; -// ignore: depend_on_referenced_packages -import 'package:local_auth_ios/local_auth_ios.dart'; - -import '../../features/wallet/wallet.service.dart'; -import '../persistence/persistence.dart'; -// import 'package:local_auth_windows/local_auth_windows.dart'; - -class LocalAuthService extends GetxService with ConsoleMixin { - static LocalAuthService get to => Get.find(); - - // VARIABLES - final auth = LocalAuthentication(); - - // PROPERTIES - - // INIT - - // FUNCTIONS - - Future authenticate({ - String? title, - String? subTitle, - required String body, - }) async { - bool authenticated = false; - - try { - // TODO: localize - authenticated = await auth.authenticate( - localizedReason: body, - options: const AuthenticationOptions(stickyAuth: true), - authMessages: [ - AndroidAuthMessages( - signInTitle: '${ConfigService.to.appName} Biometrics', - biometricHint: subTitle, - cancelButton: 'Cancel', - ), - const IOSAuthMessages(cancelButton: 'Cancel'), - // const WindowsAuthMessages(), - ], - ); - } on PlatformException catch (e, s) { - console.error('exception: ${e.toString()}'); - - if (e.code == auth_error.notAvailable) { - _onError(e); - } else if (e.code == auth_error.notEnrolled) { - _onError(e); - } else if (e.code == auth_error.passcodeNotSet) { - _onError(e); - } else if (e.code == auth_error.lockedOut || - e.code == auth_error.permanentlyLockedOut) { - UIUtils.showSimpleDialog( - 'Locked Out', - "Because of too many attempts you've been locked out. Please try again later.", - ); - } else if (e.code == auth_error.otherOperatingSystem) { - _failedAuth(e); - } else { - CrashlyticsService.to.record(e, s); - _failedAuth(e); - } - - return false; - } catch (e, s) { - console.error('error: ${e.toString()}'); - CrashlyticsService.to.record(e, s); - _failedAuth(e); - return false; - } - - return authenticated; - } - - void _onError(dynamic e) { - if (WalletService.to.isReady) { - _showError(); - } else { - _failedAuth(e); - } - } - - void _showError() { - UIUtils.showSimpleDialog( - 'Screenlock Required', - "Please turn it on to continue.", - closeText: 'Cancel', - actionText: 'Open Settings', - action: () { - Get.back(); - AppSettings.openLockAndPasswordSettings(); - }, - ); - } - - void _failedAuth(dynamic e) { - if (!Persistence.to.backedUpPassword.val) return; - Persistence.to.biometrics.val = false; - - UIUtils.showSimpleDialog( - 'Failed Biometrics', - 'Please try again using Master Passwords instead\n\n$e', - ); - } -} diff --git a/lib/core/translations/en.dart b/lib/core/translations/en.dart index 45c0d1aa..f766a854 100644 --- a/lib/core/translations/en.dart +++ b/lib/core/translations/en.dart @@ -2,9 +2,155 @@ final en = { "locale_string": "en", "language_string": "English", "language": "Language", - "cryptoWallet": "Crypto Wallet", + // --- + "slogan": "Start Securing Your Data", + "slogan_sub": + "Your hackproof vault for storing sensitive data, passwords, and files.", + "settings": "Settings", + "about": "About", + "need_help": "Need help?", + "remove": "Remove", + "upload": "Upload", + "none": "None", + "system": "System", + "dark": "Dark", + "light": "Light", + "licenses": "Licenses", + "developer": "Developer", + "community_help": "Community & Help", + "annual": "Yearly", + "monthly": "Monthly", + "weekly": "Weekly", + "six_month": "Every 6 Months", + "three_month": "Every 3 Months", + "two_month": "Every 2 Months", + "lifetime": "Lifetime", + "renews": "Renews", + "expires": "Expires", + "benefits": "Benefits", + "year": "Year", + "month": "Month", + "week": "Week", + "unlock_all_access": "Unlock All Access", + "upcoming_features": "Upcoming Features", + "more_upcoming_features": "More Upcoming Features", + "more_options": "More options", + "options": "Options", + "copy": "Copy", + "reset": "Reset", + "hide": "Hide", + "show": "Show", + "help_question": "Help?", + "pro_postfix": "Try @w1 Pro for FREE to unlock this feature.", + "pro_activated": "Pro Activated", + "pro_restored": "Pro Restored", + "no_purchases": "No Purchases", + "not_subscribed": "You are not subscribed to @w1 Pro", + "free_trial": "Free Trial", + "pro_thanks": "Thank you for your purchase and support", + "other_platform_access": "Other Platform Access", + "app_access": "App Access", + "cancel_anytime": "Cancel Anytime", + "try_free": "Try Free", + "subscribe": "Subscribe", + "trial_remind": "We'll remind you before your trial ends", + "easy_cancel": "2 taps to start, super easy to cancel", + "purchases_supported_on": "Purchases unsupported on", + "terms_of_use": "Terms of Use", + "privacy_policy": "Privacy Policy", + "restore_purchases": "Restore Purchases", + "size": "Size", + "authenticating": "Authenticating", + "authenticate": "Authenticate", + "welcome_to": "Welcome to", + "skip": "Skip", + "email_address": "Email address", + "email": "Email", + "required": "Required", + "invalid_email": "Invalid email", + "get_started": "Get Started", + "proceed_agreement": "By proceeding, you agree with our", + "follow": "Follow", + "newsletter": "Newsletter", + "newsletter_promise": "We promise not to spam your inbox", + "visibility": "Visibility", + "no_internet": "No Internet Connection", + "consent": "Consent", + "continue": "Continue", + "anonymous_report_desc": + "Please enable us to receive the below reports to help us identify, and fix issues, and improve the overall experience. Some personal data will be uploaded such as: User ID, Device ID, and other IDs.", + "errors_crashes": "Errors & Crashes", + "send_reports": "Send anonymous crash reports", + "send_stats": "Send anonymous usage statistics", + "usage_stats": "Usage Statistics", + "try": "Try", + "try_app_pro": "Try @w1 Pro", + "unlock_features": "Unlock Features", + "copied_to_clipboard": "Copied to Clipboard", + "choose_export_path": "Choose Export Path", + "export_successful": "Export Successful", + "save_successful": "Save Successful", + "save": "Save", + "share": "Share", + "give_rating": "Give Your Rating", + "please_give_rating": "Please give us your star rating.", + "feedback": "Feedback", + "issue": "Issue", + "suggestion": "Suggestion", + "type": "Type", + "feedback_short": "Your feedback is too short", + "write_concern": "Write your concern here", + "write_concern_helper": + "Please don't hesitate to send us your feedback and we'll be happy to chat with you. Expect a reply within 24-48 hours.", + "rate_review": "Rate & Review", + "spread_word": + "Help spread the word about why people should consider using @w1 as their screenshot designer.", + "contact": "Contact", + "search": "Search", + "empty_result": "Empty Result", + "cancel": "Cancel", + "close": "Close", + "write_review_here": "Write your review here", + "review_short": "Your review is too short", + "why_love_hate": + "Why do you love @w1? Or tell us what you don't like or what needs improvement.", + "send_feedback": "Send Feedback", + "sign_out_warning": "Are you sure you want to sign out?", + "sign_out": "Sign Out", + "sign_in": "Sign In", + "app_theme": "App Theme", + "account": "Account", + "account_desc": "Account related settings", + "delete_account": "Delete Account", + "keyboard_shortcuts": "Keyboard Shortcuts", + "keyboard_shortcuts_desc": "View and remap keyboard shortcuts", + "other_settings": "Other Settings", + "other_settings_desc": "A few other settings", + "launch_startup": "Launch at startup", + "learn_more": "Learn more", + "okay": "Okay", + "month_billed_annually": "month billed annually", + "restore": "Restore", + "delete": "Delete", + "change": "Change", + "confirm": "Confirm", + "proceed": "Proceed", + "unlock": "Unlock", + "lock": "Lock", + "theme": "Theme", + "edit": "Edit", + "file": "File", + "next": "Next", + "done": "Done", + "try_again": "Try Again", + "devices": "Devices", + "change_theme": "Change Theme", + "create": "Create", + "update": "Update", "login": "Login", "password": "Password", + // --- + "cryptoWallet": "Crypto Wallet", "master_password": "Master Password", "identity": "Identity", "note": "Secure Note", @@ -19,7 +165,6 @@ final en = { "apiCredential": "API Credential", "database": "Database", "driversLicense": "Driver's License", - "email": "Email Account", "membership": "Membership", "outdoorLicense": "Outdoor License", "rewardsProgram": "Rewards Program", @@ -42,8 +187,6 @@ final en = { "archived": "Archived", "archive": "Archive", "trash": "Trash", - "restore": "Restore", - "delete": "Delete", "delete_permanently": "Delete Permanently", "move_to_trash": "Move to trash", "move_to_archive": "Move to archive", @@ -54,14 +197,10 @@ final en = { "search_by_title": "Search by title", "date_modified": "Date Modified", "date_created": "Date Created", - "required": "Required", "too_short": "Too short", "no_items": "No items", "add_item": "Add Item", "no_files": "No Files", - "remove": "Remove", - "change": "Change", - "copy": "Copy", "protected": "Protected", "protect": "Protect", "unprotect": "Unprotect", @@ -77,31 +216,12 @@ final en = { "create_vault": "Create Vault", "export_wallet": "Export Wallet", "vault": "Vault", - "confirm": "Confirm", "generate": "Generate", - "proceed": "Proceed", - "unlock": "Unlock", - "lock": "Lock", - "cancel": "Cancel", - "reset": "Reset", - "create": "Create", - "update": "Update", "import": "Import", "export": "Export", - "continue": "Continue", - "sign_in": "Sign In", - "about": "About", - "settings": "Settings", "attempts_left": "attempts left", "attachment": "Attachment", "attachments": "Attachments", - "theme": "Theme", - "dark": "Dark", - "light": "Light", - "system": "System", - "change_theme": "Change Theme", - "save": "Save", - "edit": "Edit", "cloud_sync": "Cloud Sync", "sync": "Sync", "synchronize": "Synchronize", @@ -111,7 +231,6 @@ final en = { "server_url": "Server URL", "time_machine": "Time Machine", "file_explorer": "File Explorer", - "file": "File", "files": "Files", "existing": "Existing", "group": "Group", @@ -124,20 +243,12 @@ final en = { "seed_generator": "Seed Generator", "password_generator": "Password Generator", "weak_passwords": "Weak Passwords", - "okay": "Okay", - "next": "Next", - "done": "Done", "no_results": "No Results", - "community_help": "Community & Help", - "developer": "Developer", - "licenses": "Licenses", "delete_file": "Delete File", "confirm_delete": "Confirm Delete", "new_folder": "New Folder", "offline": "Offline", - "try_again": "Try Again", "syncing": "Syncing", - "close": "Close", "duplicate": "Duplicate", "sign": "Sign", "sign_text": "Sign Text", @@ -161,7 +272,6 @@ final en = { "secrets_desc": "Secrets Description", "joined_vaults": "Joined Vaults", "shared_vaults": "Shared Vaults", - "share": "Share", "vaults": "Vaults", "new_vault": "New Vault", "custom_vaults": "Custom Vaults", @@ -174,12 +284,10 @@ final en = { "leave": "Leave", "join": "Join", "join_shared_vault": "Join Shared Vault", - "devices": "Devices", "synced_devices": "Synced Devices", "unsync": "Unsync", "unsync_device": "Unsync Device", "purge": "Purge", - "authenticate": "Authenticate", "use": "Use", "letters": "Letters", "numbers": "Numbers", @@ -189,42 +297,4 @@ final en = { "generated_password": "Generated Password", "generated_seed_phrase": "Generated Seed Phrase", "scan": "Scan", - "annual": "Yearly", - "monthly": "Monthly", - "weekly": "Weekly", - "six_month": "Every 6 Months", - "three_month": "Every 3 Months", - "two_month": "Every 2 Months", - "lifetime": "Lifetime", - "renews": "Renews", - "expires": "Expires", - "benefits": "Benefits", - "year": "Year", - "month": "Month", - "week": "Week", - "rate_review": "Rate & Review", - "write_review_here": "Write your review here", - "review_short": "Your review is too short", - "why_love_hate": - "Why do you love @w1? Or tell us what you don't like or what needs improvement.", - "send_feedback": "Send Feedback", - "give_rating": "Give Your Rating", - "please_give_rating": "Please give us your star rating.", - "cancel_anytime": "Cancel Anytime", - "free_trial": "Free Trial", - "pro_thanks": "Thank you for your purchase and support", - "pro_postfix": "Try @w1 Pro for FREE to unlock this feature.", - "pro_activated": "Pro Activated", - "pro_restored": "Pro Restored", - "no_purchases": "No Purchases", - "not_subscribed": "You are not subscribed to @w1 Pro", - "month_billed_annually": "month billed annually", - "try_free": "Try Free", - "try": "Try", - "subscribe": "Subscribe", - "trial_remind": "We'll remind you before your trial ends", - "easy_cancel": "2 taps to start, super easy to cancel", - "purchases_supported_on": "Purchases unsupported on", - "purchase_detect": - "However, you can upgrade to Pro via an Android, iOS or MacOS device then the app will automatically detect it", }; diff --git a/lib/core/utils/globals.dart b/lib/core/utils/globals.dart index e79805c0..992c318f 100644 --- a/lib/core/utils/globals.dart +++ b/lib/core/utils/globals.dart @@ -1,19 +1,24 @@ -// COMPANY - -import 'package:flutter/material.dart'; +import 'package:app_core/controllers/pro.controller.dart'; +import 'package:app_core/globals.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; -import 'package:intl/intl.dart'; -import 'package:liso/core/hive/models/metadata/metadata.hive.dart'; -import 'package:liso/core/persistence/persistence.dart'; -import 'package:liso/core/utils/utils.dart'; -import 'package:uuid/uuid.dart'; +import 'package:liso/core/firebase/model/config_app_domains.model.dart'; +import 'package:liso/core/firebase/model/config_limits.model.dart'; +import 'package:liso/core/firebase/model/config_web3.model.dart'; + +import '../../features/wallet/wallet.service.dart'; +import '../persistence/persistence.dart'; + +bool isAutofill = false; + +late ConfigLimits configLimits; +late ConfigAppDomains configAppDomains; +late ConfigWeb3 configWeb3; // HIVE DATABASE const kHiveBoxGroups = 'groups'; const kHiveBoxCategories = 'categories'; const kHiveBoxItems = 'items'; -const kHiveBoxPersistence = 'persistence'; const kHiveBoxSecretPersistence = 'secret_persistence'; // BIOMETRIC STORAGE const kBiometricPasswordKey = 'biometric_password'; @@ -25,22 +30,15 @@ const kEncryptedExtensionExtra = '.$kVaultExtension.enc'; // FILE NAMES const kMetadataFileName = 'metadata.json'; const kVaultFileName = 'vault.$kVaultExtension'; -// DESKTOP -const kMinWindowSize = Size(400, 400); -const kDesktopChangePoint = 800.0; // responsive setting -// COLORS + // INPUT FORMATTERS final inputFormatterRestrictSpaces = FilteringTextInputFormatter.deny(RegExp(r'\s')); final inputFormatterNumericOnly = FilteringTextInputFormatter.allow(RegExp("[0-9]")); -final currencyFormatter = NumberFormat.currency(symbol: '', decimalDigits: 2); -final kFormatter = NumberFormat.compact(); - const kCipherKeySignatureMessage = 'liso'; const kAuthSignatureMessage = 'auth'; -const kS3MetadataVersion = '1'; const kVaultFormatVersion = 1; const kNonPasswordFieldIds = [ @@ -54,34 +52,6 @@ const kNonPasswordFieldIds = [ // GETTERS -bool get isReviewable => isApple || GetPlatform.isAndroid; -bool get isApple => GetPlatform.isMacOS || GetPlatform.isIOS; -bool get isLinux => GetPlatform.isLinux && !GetPlatform.isWeb; -bool get isWindows => GetPlatform.isWindows && !GetPlatform.isWeb; -bool get isMac => GetPlatform.isMacOS && !GetPlatform.isWeb; - -bool get isWindowsLinux => - !GetPlatform.isWeb && (GetPlatform.isWindows || GetPlatform.isLinux); - -bool get isDesktop => - !GetPlatform.isWeb && - (GetPlatform.isMacOS || GetPlatform.isWindows || GetPlatform.isLinux); - -bool get isLocalAuthSupported => - GetPlatform.isMobile && Persistence.to.biometrics.val; - -bool get isRateReviewSupported => - !GetPlatform.isWeb && GetPlatform.isAndroid || - GetPlatform.isIOS || - (GetPlatform.isMacOS && isMacAppStore); - -// bool get isIAPSupported => -// !GetPlatform.isWeb && (GetPlatform.isMacOS || GetPlatform.isMobile); - -bool get isIAPSupported => false; - -bool get isGumroadSupported => !isIAPSupported; - const kAppColor = Color(0xff02f297); const kAppColorDarker = Color(0xFF00A465); @@ -89,35 +59,27 @@ Color get themeColor => Get.isDarkMode ? kAppColor : kAppColorDarker; Color get proColor => Get.isDarkMode ? kAppColor : kAppColorDarker; -double get popupItemHeight => - Utils.isSmallScreen ? kMinInteractiveDimension : 30; - -double? get popupIconSize => Utils.isSmallScreen ? null : 20; - -// TODO: set before releasing a new version -const releaseMode = ReleaseMode.production; -bool get isBeta => releaseMode == ReleaseMode.beta; -// firebase emulator settings -const kUseFirebaseEmulator = false; -const kFirebaseHost = 'localhost'; -const kFirebaseAuthPort = 9099; -const kFirebaseFunctionsPort = 5001; -const kFirebaseFirestorePort = 8085; - -// TODO: set to false when publishing on Mac App Store -const isMacAppStore = true; -bool get isCryptoSupported => - Persistence.to.proTester.val || - (GetPlatform.isMacOS && !isMacAppStore) || - GetPlatform.isAndroid || - GetPlatform.isWindows; +bool get isCryptoSupported => !isApple; -// ENUMS -enum ReleaseMode { - beta, - production, +ConfigLimitsTier get limits { + if (!WalletService.to.isReady) return configLimits.free; + // check if user is a pro subscriber + if (ProController.to.isPro) return configLimits.pro; + + // TODO: check if user is a staker + + // check if user is a holder + if (AppPersistence.to.lastLisoBalance.val > + configLimits.holder.tokenThreshold) { + return configLimits.holder; + } + + // free user + return configLimits.free; } +// ENUMS + enum LisoItemSortOrder { titleAscending, titleDescending, @@ -161,25 +123,4 @@ enum LisoItemCategory { custom, } -enum LisoSyncProvider { - sia, - // ipfs, - // storj, - // skynet, - custom, -} - -class Globals { - // VARIABLES - static bool timeLockEnabled = true; - static bool isAutofill = false; - static String sessionId = const Uuid().v4(); - static HiveMetadata? metadata; - - // GETTERS - - // FUNCTIONS - static Future init() async { - metadata = await HiveMetadata.get(); - } -} +enum LisoSyncProvider { sia, custom } diff --git a/lib/core/utils/ui_utils.dart b/lib/core/utils/ui_utils.dart deleted file mode 100644 index b6fa8f13..00000000 --- a/lib/core/utils/ui_utils.dart +++ /dev/null @@ -1,319 +0,0 @@ -import 'package:app_review/app_review.dart'; -import 'package:console_mixin/console_mixin.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:liso/core/utils/utils.dart'; -import 'package:qr_flutter/qr_flutter.dart'; - -import '../../features/pro/pro.controller.dart'; -import '../../features/supabase/supabase_database.service.dart'; -import '../../features/supabase/supabase_functions.service.dart'; -import '../firebase/config/config.service.dart'; -import '../notifications/notifications.manager.dart'; - -class UIUtils { - static final console = Console(name: 'UIUtils'); - - static Future showSnackBar({ - required String title, - required String message, - final Widget? icon, - final int seconds = 7, - }) async { - Get.snackbar( - title, - message, - icon: icon ?? const Icon(Icons.info, size: 25), - titleText: Text( - title, - style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), - ), - maxWidth: 500, - messageText: Text(message, style: const TextStyle(fontSize: 14)), - duration: Duration(seconds: seconds), - borderRadius: 8, - shouldIconPulse: true, - margin: const EdgeInsets.all(8), - snackPosition: - Utils.isSmallScreen ? SnackPosition.BOTTOM : SnackPosition.TOP, - ); - } - - static Future showSimpleDialog( - String title, - String body, { - String? closeText, - Function()? action, - String? actionText, - }) async { - final content = SingleChildScrollView(child: Text(body)); - - await Get.dialog( - AlertDialog( - title: Text(title), - content: Utils.isSmallScreen - ? content - : Container( - constraints: const BoxConstraints(maxHeight: 600), - width: 400, - child: content, - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text(closeText ?? 'okay'.tr), - ), - if (action != null) ...[ - TextButton( - onPressed: action, - child: Text(actionText ?? 'okay'.tr), - ), - ] - ], - ), - ); - } - - static Future showImageDialog( - Widget image, { - required String title, - String? subTitle, - required String body, - Function()? onClose, - String? closeText, - Function()? action, - String? actionText, - ButtonStyle? actionStyle, - }) async { - final bodyContent = Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - image, - const SizedBox(height: 30), - Text( - title, - style: const TextStyle(fontSize: 25), - textAlign: TextAlign.center, - ), - const SizedBox(height: 10), - if (subTitle != null) ...[ - Text( - subTitle, - style: const TextStyle(fontSize: 15, color: Colors.grey), - textAlign: TextAlign.center, - ), - const SizedBox(height: 10), - ], - Text(body, textAlign: TextAlign.center), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: OutlinedButton( - onPressed: onClose ?? Get.back, - child: Text(closeText ?? 'okay'.tr), - ), - ), - if (action != null) ...[ - const SizedBox(width: 20), - Expanded( - child: ElevatedButton( - onPressed: action, - style: actionStyle, - child: Text(actionText ?? 'okay'.tr), - ), - ), - ] - ], - ), - ], - ); - - await Get.dialog( - AlertDialog( - title: null, - actionsAlignment: MainAxisAlignment.center, - content: Utils.isSmallScreen - ? bodyContent - : SizedBox(width: 400, child: bodyContent), - ), - ); - } - - static Future showQR( - String data, { - required String title, - required String subTitle, - }) async { - final content = Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - height: 200, - width: 200, - child: Center( - child: QrImage( - data: data, - backgroundColor: Colors.white, - ), - ), - ), - const SizedBox(height: 20), - Text( - subTitle, - style: const TextStyle(color: Colors.grey), - textAlign: TextAlign.center, - ), - ], - ); - - await Get.dialog(AlertDialog( - title: Text( - title, - textAlign: TextAlign.center, - ), - content: Utils.isSmallScreen - ? content - : Container( - constraints: const BoxConstraints(maxHeight: 600), - width: 450, - child: content, - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text('okay'.tr), - ), - ], - )); - } - - static void rateAndReview() async { - final store = ConfigService.to.general.app.links.store; - final available = await AppReview.isRequestReviewAvailable; - console.info('review available: $available'); - - if (GetPlatform.isAndroid) { - if (available) { - final result = await AppReview.openAndroidReview(); - console.info('review result: $result'); - } else { - Utils.openUrl(store.google); - } - } else if (GetPlatform.isIOS) { - if (available) { - final result = await AppReview.openIosReview(); - console.info('review result: $result'); - } else { - Utils.openUrl(store.apple); - } - } else if (GetPlatform.isMacOS) { - Utils.openUrl(store.apple); - } - } - - static Future setLicenseKey() async { - final formKey = GlobalKey(); - final keyController = TextEditingController(); - final busy = false.obs; - - void submit() async { - if (!formKey.currentState!.validate()) return; - busy.value = true; - final key = keyController.text.trim(); - final verifyResult = await SupabaseFunctionsService.to.verifyGumroad( - key, - updateEntitlement: false, - ); - - verifyResult.fold( - (left) { - return UIUtils.showSimpleDialog( - 'Failed Verifying', - left, - ); - }, - (right) async { - if (!right.entitled) { - return UIUtils.showSimpleDialog( - 'Deactivated License', - 'We are sorry to inform you that your license key is not active anymore.', - ); - } - - final updateResult = await SupabaseDBService.to.updateLicenseKey(key); - - updateResult.fold( - (left) => UIUtils.showSimpleDialog( - 'Failed Updating', - left, - ), - (right) { - Get.back(); - - ProController.to.licenseKey.value = right.gumroadLicenseKey; - ProController.to.verifiedPro.value = true; - - NotificationsManager.notify( - title: 'License Key Updated', - body: - 'Thanks for subscribing to ${ConfigService.to.appName} Pro 🎉', - ); - }, - ); - }, - ); - - busy.value = false; - } - - final content = Obx( - () => TextFormField( - enabled: !busy.value, - controller: keyController, - autofocus: true, - autovalidateMode: AutovalidateMode.onUserInteraction, - validator: (data) => - data!.length != 35 ? 'Please a valid license key' : null, - decoration: const InputDecoration( - labelText: 'License Key', - hintText: 'Gumroad License Key', - ), - ), - ); - - Get.dialog( - AlertDialog( - title: const Text('Update License Key'), // TODO: localize - content: Form( - key: formKey, - child: Utils.isSmallScreen - ? content - : SizedBox(width: 450, child: content), - ), - actions: [ - TextButton( - onPressed: Get.back, - child: Text('cancel'.tr), - ), - Obx( - () => Visibility( - visible: !busy.value, - replacement: const SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator(), - ), - child: TextButton( - onPressed: submit, - child: const Text('Update'), // TODO: localize - ), - ), - ), - ], - ), - ); - } -} diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index bda920db..d373342b 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -1,91 +1,73 @@ +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/persistence/persistence.dart'; +import 'package:app_core/widgets/consent.widget.dart'; +import 'package:app_core/widgets/remote_image.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; -import 'package:liso/core/firebase/crashlytics.service.dart'; -import 'package:liso/core/utils/ui_utils.dart'; -import 'package:liso/features/app/pages.dart'; +import 'package:qr_flutter/qr_flutter.dart'; import 'package:random_string_generator/random_string_generator.dart'; -import 'package:timeago/timeago.dart' as timeago; -import 'package:url_launcher/url_launcher.dart'; -import 'package:url_launcher/url_launcher_string.dart'; -import 'package:window_manager/window_manager.dart'; -import '../../features/feedback/feedback_screen.controller.dart'; -import '../../features/general/remote_image.widget.dart'; -import '../../features/pro/pro.controller.dart'; import '../../features/supabase/model/object.model.dart'; -import '../../features/supabase/supabase_auth.service.dart'; import '../../resources/resources.dart'; -import '../firebase/config/config.service.dart'; -import '../persistence/persistence.dart'; -import '../persistence/persistence.secret.dart'; import 'globals.dart'; -class Utils { +class AppUtils { // VARIABLES static final console = Console(name: 'Utils'); // GETTERS - static bool get isSmallScreen => - Get.mediaQuery.size.width < kDesktopChangePoint; // FUNCTIONS - static void copyToClipboard(String text) async { - if (text.isEmpty) return; - - try { - await Clipboard.setData(ClipboardData(text: text)); - - // TODO: localize - UIUtils.showSnackBar( - title: 'Copied', - message: 'Successfully copied to clipboard', - icon: const Icon(LineIcons.copy), - seconds: 4, - ); - } catch (e, s) { - CrashlyticsService.to.record(e, s); - } - } - - static String timeAgo(DateTime dateTime, {bool short = true}) { - final locale = - (Get.locale?.languageCode ?? 'en_US') + (short ? "_short" : ""); - return timeago.format(dateTime, locale: locale).replaceFirst("~", ""); - } - - // support higher refresh rate - static void setDisplayMode() async { - if (!GetPlatform.isAndroid) return; - - try { - final mode = await FlutterDisplayMode.active; - console.warning('active mode: $mode'); - final modes = await FlutterDisplayMode.supported; - - for (DisplayMode e in modes) { - console.info('display modes: $e'); - } - - await FlutterDisplayMode.setPreferredMode(modes.last); - console.info('set mode: ${modes.last}'); - } on PlatformException catch (e) { - console.error('display mode error: $e'); - } - } - - static Future setWindowSize() async { - await windowManager.setMinimumSize(kMinWindowSize); + static Future showQR( + String data, { + required String title, + required String subTitle, + }) async { + final content = Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 200, + width: 200, + child: Center( + child: QrImage( + data: data, + backgroundColor: Colors.white, + ), + ), + ), + const SizedBox(height: 20), + Text( + subTitle, + style: const TextStyle(color: Colors.grey), + textAlign: TextAlign.center, + ), + ], + ); - // set preferred size - windowManager.setSize(Size( - Persistence.to.windowWidth.val, - Persistence.to.windowHeight.val, + await Get.dialog(AlertDialog( + title: Text( + title, + textAlign: TextAlign.center, + ), + content: isSmallScreen + ? content + : Container( + constraints: const BoxConstraints(maxHeight: 600), + width: 450, + child: content, + ), + actions: [ + TextButton( + onPressed: Get.back, + child: Text('okay'.tr), + ), + ], )); } @@ -172,60 +154,6 @@ class Utils { return Icon(iconData, color: color, size: size); } - static Future? adaptiveRouteOpen({ - required String name, - String method = 'toNamed', - Map parameters = const {}, - dynamic arguments, - }) { - // Regular navigation for mobile - if (isSmallScreen) { - switch (method) { - case 'toNamed': - return Get.toNamed( - name, - parameters: parameters, - arguments: arguments, - ); - case 'offAndToNamed': - return Get.offAndToNamed( - name, - parameters: parameters, - arguments: arguments, - ); - case 'offAllNamed': - return Get.offAllNamed( - name, - parameters: parameters, - arguments: arguments, - ); - default: - } - } - - // Open page as dialog for desktop - Get.parameters = parameters; // manually pass parameters - final page = AppPages.routes.firstWhere((e) => e.name == name).page(); - // larger real estate for secure notes - final isNote = parameters['category'] == LisoItemCategory.note.name; - - final dialog = Dialog( - child: SizedBox( - width: isNote ? 800 : 600, - height: isNote ? 1100 : 900, - child: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: page, - ), - ), - ); - - return Get.dialog( - dialog, - routeSettings: RouteSettings(name: name, arguments: arguments), - ); - } - static String? validateUri(String data) { final uri = Uri.tryParse(data); @@ -345,85 +273,4 @@ class Utils { return color; } - - static String platformName() { - if (GetPlatform.isWeb) { - return "Web"; - } else if (GetPlatform.isAndroid) { - return "Android"; - } else if (GetPlatform.isIOS) { - return "iOS"; - } else if (GetPlatform.isWindows) { - return "Windows"; - } else if (GetPlatform.isMacOS) { - return "macOS"; - } else if (GetPlatform.isLinux) { - return "Linux"; - } else if (GetPlatform.isFuchsia) { - return "Fuchsia"; - } else { - return "unknown"; - } - } - - static void contactEmail({ - required String subject, - required String preBody, - required double rating, - required String previousRoute, - required FeedbackType feedbackType, - }) async { - String ratingEmojis = ''; - - for (var i = 0; i < rating.toInt(); i++) { - ratingEmojis += '✩'; - } - - final ln = GetPlatform.isIOS ? '\r\n' : '\n'; - - String body = '$preBody$ln$ln'; - - if (SupabaseAuthService.to.authenticated) { - body += 'Rating: $ratingEmojis$ln'; - body += - 'User ID: ${SupabaseAuthService.to.user!.id}\nAddress: ${SecretPersistence.to.longAddress}$ln'; - body += 'RC User ID: ${ProController.to.info.value.originalAppUserId}$ln'; - body += 'Entitlement: ${ProController.to.limits.id}$ln'; - body += 'Pro: ${ProController.to.isPro}$ln'; - } - - body += 'App Version: ${Globals.metadata?.app.formattedVersion}$ln'; - body += 'Platform: ${Utils.platformName()}$ln'; - body += 'Route: $previousRoute$ln'; - - final emails = ConfigService.to.general.app.emails; - String email = emails.support; - - if (feedbackType == FeedbackType.issue) { - email = emails.issues; - } - - final url = 'mailto:$email?subject=$subject&body=$body'; - openUrl(Uri.encodeFull(url)); - } - - static Future openUrl( - String url, { - LaunchMode mode = LaunchMode.platformDefault, - }) async { - console.info('launching: $url'); - final canLaunch = await canLaunchUrlString(url); - if (!canLaunch) console.error('cannot launch'); - launchUrlString(url, mode: mode); - } - - static Future openUri( - Uri uri, { - LaunchMode mode = LaunchMode.platformDefault, - }) async { - console.info('launching: $uri'); - final canLaunch = await canLaunchUrl(uri); - if (!canLaunch) console.error('cannot launch'); - launchUrl(uri, mode: mode); - } } diff --git a/lib/features/about/about.screen.dart b/lib/features/about/about.screen.dart index 68675ec8..272a2112 100644 --- a/lib/features/about/about.screen.dart +++ b/lib/features/about/about.screen.dart @@ -1,18 +1,17 @@ +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/remote_image.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; -import 'package:liso/core/persistence/persistence.dart'; -import 'package:liso/core/utils/ui_utils.dart'; -import 'package:liso/features/general/appbar_leading.widget.dart'; import 'package:liso/resources/resources.dart'; import 'package:share_plus/share_plus.dart'; -import '../../core/firebase/config/config.service.dart'; import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../general/remote_image.widget.dart'; import '../menu/menu.button.dart'; import 'about_screen.controller.dart'; @@ -24,32 +23,12 @@ class AboutScreen extends StatelessWidget with ConsoleMixin { final controller = Get.put(AboutScreenController()); final config = Get.find(); - final persistence = Get.find(); final content = ListView( shrinkWrap: true, padding: const EdgeInsets.symmetric(horizontal: 15), children: [ - const SizedBox(height: 20), - ListTile( - leading: Image.asset( - Images.logo, - height: 20, - color: themeColor, - ), - title: Text( - '${config.appName} ${Globals.metadata?.app.formattedVersion}', - ), - onLongPress: () { - persistence.proTester.val = !persistence.proTester.val; - - UIUtils.showSnackBar( - title: 'PRO Tester', - message: "PRO Tester mode has been enabled", - ); - }, - ), - if (isReviewable) ...[ + if (isRateReviewSupported) ...[ ListTile( leading: Icon( GetPlatform.isAndroid ? LineIcons.googlePlay : LineIcons.appStore, diff --git a/lib/features/about/about_screen.controller.dart b/lib/features/about/about_screen.controller.dart index 32bd932a..70e0dbfa 100644 --- a/lib/features/about/about_screen.controller.dart +++ b/lib/features/about/about_screen.controller.dart @@ -1,12 +1,12 @@ +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/remote_image.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:line_icons/line_icons.dart'; -import 'package:liso/features/general/remote_image.widget.dart'; -import '../../core/firebase/config/config.service.dart'; -import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; import '../../resources/resources.dart'; import '../menu/menu.item.dart'; @@ -113,8 +113,6 @@ class AboutScreenController extends GetxController with ConsoleMixin { // FUNCTIONS void showLicenses(BuildContext context) async { - final app = Globals.metadata!.app; - final icon = Padding( padding: const EdgeInsets.symmetric(vertical: 15), child: RemoteImage( @@ -127,8 +125,8 @@ class AboutScreenController extends GetxController with ConsoleMixin { showLicensePage( context: context, applicationIcon: icon, - applicationName: app.appName, - applicationVersion: app.formattedVersion, + applicationName: metadataApp.appName, + applicationVersion: metadataApp.formattedVersion, applicationLegalese: 'Copyright © ${DateTime.now().year} ${ConfigService.to.general.developer.name}\nAll rights reserved.', ); diff --git a/lib/features/app/app.dart b/lib/features/app/app.dart index ed8845ed..e7792973 100644 --- a/lib/features/app/app.dart +++ b/lib/features/app/app.dart @@ -1,14 +1,14 @@ +import 'package:app_core/firebase/analytics.service.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/persistence/persistence.dart'; +import 'package:app_core/widgets/unknown.screen.dart'; import 'package:flex_color_scheme/flex_color_scheme.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:liso/core/persistence/persistence.dart'; import 'package:liso/core/translations/data.dart'; import 'package:liso/features/app/pages.dart'; -import 'package:liso/features/app/routes.dart'; -import 'package:liso/features/general/unknown.screen.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import '../../core/firebase/analytics.service.dart'; import '../../core/utils/globals.dart'; class App extends StatelessWidget { @@ -47,7 +47,7 @@ class App extends StatelessWidget { fallbackLocale: const Locale('en', 'US'), // NAVIGATION initialRoute: Routes.main, - getPages: AppPages.routes, + getPages: Pages.data, defaultTransition: Transition.native, themeMode: ThemeMode.values.byName(persistence.theme.val), // DARK THEME diff --git a/lib/features/app/pages.dart b/lib/features/app/pages.dart index db05cec9..bf617081 100644 --- a/lib/features/app/pages.dart +++ b/lib/features/app/pages.dart @@ -1,5 +1,10 @@ +import 'package:app_core/pages/feedback/feedback.screen.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/pages/update/update.screen.dart'; +import 'package:app_core/pages/upgrade/upgrade.screen.dart'; import 'package:get/get.dart'; import 'package:liso/features/about/about.screen.dart'; +import 'package:liso/features/app/routes.dart'; import 'package:liso/features/create_password/create_password.screen.dart'; import 'package:liso/features/debug/debug.screen.dart'; import 'package:liso/features/import/import.screen.dart'; @@ -9,7 +14,6 @@ import 'package:liso/features/seed/seed.screen.dart'; import 'package:liso/features/settings/settings.screen.dart'; import 'package:liso/features/shared_vaults/shared_vaults.screen.dart'; import 'package:liso/features/unlock/unlock.screen.dart'; -import 'package:liso/features/update/update.screen.dart'; import 'package:liso/features/welcome/welcome.screen.dart'; import '../../core/middlewares/authentication.middleware.dart'; @@ -18,7 +22,6 @@ import '../categories/categories.screen.dart'; import '../categories/picker/category_picker.screen.dart'; import '../cipher/cipher.screen.dart'; import '../devices/devices.screen.dart'; -import '../feedback/feedback.screen.dart'; import '../files/explorer/s3_explorer.screen.dart'; import '../groups/groups.screen.dart'; import '../items/item.screen.dart'; @@ -26,15 +29,13 @@ import '../joined_vaults/explorer/vault_explorer.screen.dart'; import '../joined_vaults/joined_vaults.screen.dart'; import '../otp/otp.screen.dart'; import '../password_generator/password_generator.screen.dart'; -import '../pro/upgrade/upgrade.screen.dart'; import '../seed/generator/seed_generator.screen.dart'; import '../wallet/wallet.screen.dart'; -import 'routes.dart'; -class AppPages { +class Pages { static const initial = Routes.main; - static final routes = [ + static final data = [ GetPage( name: Routes.main, page: () => MainScreen(), @@ -51,31 +52,31 @@ class AppPages { page: () => const UnlockScreen(), ), GetPage( - name: Routes.createPassword, + name: AppRoutes.createPassword, page: () => const CreatePasswordScreen(), ), GetPage( - name: Routes.passwordGenerator, + name: AppRoutes.passwordGenerator, page: () => const PasswordGeneratorScreen(), ), GetPage( - name: Routes.item, + name: AppRoutes.item, page: () => const ItemScreen(), ), GetPage( - name: Routes.restore, + name: AppRoutes.restore, page: () => const RestoreScreen(), ), GetPage( - name: Routes.import, + name: AppRoutes.import, page: () => const ImportScreen(), ), GetPage( - name: Routes.seed, + name: AppRoutes.seed, page: () => const SeedScreen(), ), GetPage( - name: Routes.seedGenerator, + name: AppRoutes.seedGenerator, page: () => const SeedGeneratorScreen(), ), GetPage( @@ -95,19 +96,19 @@ class AppPages { // page: () => const CustomSyncProviderScreen(), // ), GetPage( - name: Routes.s3Explorer, + name: AppRoutes.s3Explorer, page: () => const S3ExplorerScreen(), ), GetPage( - name: Routes.attachments, + name: AppRoutes.attachments, page: () => const AttachmentsScreen(), ), GetPage( - name: Routes.wallet, + name: AppRoutes.wallet, page: () => const WalletScreen(), ), GetPage( - name: Routes.cipher, + name: AppRoutes.cipher, page: () => const CipherScreen(), ), GetPage( @@ -115,35 +116,35 @@ class AppPages { page: () => const UpgradeScreen(), ), GetPage( - name: Routes.otp, + name: AppRoutes.otp, page: () => const OTPScreen(), ), GetPage( - name: Routes.categories, + name: AppRoutes.categories, page: () => const CategoriesScreen(), ), GetPage( - name: Routes.categoryPicker, + name: AppRoutes.categoryPicker, page: () => const CategoryPickerScreen(), ), GetPage( - name: Routes.vaults, + name: AppRoutes.vaults, page: () => const GroupsScreen(), ), GetPage( - name: Routes.sharedVaults, + name: AppRoutes.sharedVaults, page: () => const SharedVaultsScreen(), ), GetPage( - name: Routes.joinedVaults, + name: AppRoutes.joinedVaults, page: () => const JoinedVaultsScreen(), ), GetPage( - name: Routes.vaultExplorer, + name: AppRoutes.vaultExplorer, page: () => const VaultExplorerScreen(), ), GetPage( - name: Routes.devices, + name: AppRoutes.devices, page: () => const DevicesScreen(), ), GetPage( diff --git a/lib/features/app/routes.dart b/lib/features/app/routes.dart index ec7d18a7..a18b4891 100644 --- a/lib/features/app/routes.dart +++ b/lib/features/app/routes.dart @@ -1,11 +1,5 @@ -abstract class Routes { - static const main = '/'; - +class AppRoutes { // SCREENS - static const unknown = '/unknown'; - static const welcome = '/welcome'; - static const settings = '/settings'; - static const about = '/about'; static const createPassword = '/create_password'; static const passwordGenerator = '/password_generator'; static const seed = '/seed'; @@ -15,8 +9,6 @@ abstract class Routes { static const export = '/export'; static const item = '/item'; static const reset = '/reset'; - static const unlock = '/unlock'; - static const upgrade = '/upgrade'; static const otp = '/otp'; static const categories = '/categories'; static const categoryPicker = '/category_picker'; @@ -25,9 +17,6 @@ abstract class Routes { static const joinedVaults = '/joined_vaults'; static const vaultExplorer = '/vault_explorer'; static const devices = '/devices'; - static const feedback = '/feedback'; - static const update = '/update'; - static const debug = '/debug'; // SYNC // static const syncProvider = '/sync_provider'; diff --git a/lib/features/attachments/attachment.tile.dart b/lib/features/attachments/attachment.tile.dart index 256b5a61..616cab5e 100644 --- a/lib/features/attachments/attachment.tile.dart +++ b/lib/features/attachments/attachment.tile.dart @@ -1,3 +1,4 @@ +import 'package:app_core/globals.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; @@ -52,7 +53,7 @@ class AttachmentTile extends StatelessWidget with ConsoleMixin { title: Text(object.maskedName), subtitle: subTitle, iconColor: themeColor, - leading: Utils.s3ContentIcon(object), + leading: AppUtils.s3ContentIcon(object), // onTap: () => Get.back(result: content.object!.eTag), trailing: ContextMenuButton( menuItems, diff --git a/lib/features/attachments/attachments.screen.dart b/lib/features/attachments/attachments.screen.dart index 6bef3253..108332e3 100644 --- a/lib/features/attachments/attachments.screen.dart +++ b/lib/features/attachments/attachments.screen.dart @@ -1,13 +1,13 @@ +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:liso/features/attachments/attachment.tile.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; import '../files/storage.service.dart'; -import '../general/appbar_leading.widget.dart'; import '../general/centered_placeholder.widget.dart'; import 'attachments_screen.controller.dart'; diff --git a/lib/features/attachments/attachments_screen.controller.dart b/lib/features/attachments/attachments_screen.controller.dart index 122915ef..46112224 100644 --- a/lib/features/attachments/attachments_screen.controller.dart +++ b/lib/features/attachments/attachments_screen.controller.dart @@ -1,7 +1,7 @@ +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:get/get.dart'; -import 'package:liso/core/utils/ui_utils.dart'; -import 'package:liso/core/utils/utils.dart'; import '../app/routes.dart'; import '../files/storage.service.dart'; @@ -33,7 +33,7 @@ class AttachmentsScreenController extends GetxController void pick() async { final eTag = await Utils.adaptiveRouteOpen( - name: Routes.s3Explorer, + name: AppRoutes.s3Explorer, parameters: {'type': 'picker'}, ); diff --git a/lib/features/autofill/autofill.service.dart b/lib/features/autofill/autofill.service.dart index e812a3fb..f90abd43 100644 --- a/lib/features/autofill/autofill.service.dart +++ b/lib/features/autofill/autofill.service.dart @@ -1,15 +1,15 @@ import 'dart:convert'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter_autofill_service/flutter_autofill_service.dart'; import 'package:get/get.dart'; -import 'package:liso/core/notifications/notifications.manager.dart'; +import 'package:liso/core/utils/globals.dart'; -import '../../core/firebase/config/config.service.dart'; import '../../core/hive/models/app_domain.hive.dart'; import '../../core/hive/models/item.hive.dart'; -import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; import '../app/routes.dart'; import '../main/main_screen.controller.dart'; import 'autofill_picker/autofill_picker.dialog.dart'; @@ -80,7 +80,7 @@ class LisoAutofillService extends GetxService with ConsoleMixin { query = metadata!.packageNames.first; } - final appDomains = ConfigService.to.appDomains.data.where((e) { + final appDomains = configAppDomains.data.where((e) { // DOMAINS if (metadata?.webDomains != null && e.uris.where((e) { @@ -120,7 +120,7 @@ class LisoAutofillService extends GetxService with ConsoleMixin { return console.error('invalid autofill metadata'); } - final appDomains = ConfigService.to.appDomains.data.where((e) { + final appDomains = configAppDomains.data.where((e) { // DOMAINS if (e.uris.where((e) { final uri = Uri.tryParse(e); @@ -178,7 +178,7 @@ class LisoAutofillService extends GetxService with ConsoleMixin { final password = metadata?.saveInfo?.password ?? ''; Utils.adaptiveRouteOpen( - name: Routes.item, + name: AppRoutes.item, parameters: { 'mode': 'saved_autofill', 'category': LisoItemCategory.login.name, diff --git a/lib/features/autofill/autofill_picker/autofill_picker.dialog.dart b/lib/features/autofill/autofill_picker/autofill_picker.dialog.dart index dec09bc6..5b760329 100644 --- a/lib/features/autofill/autofill_picker/autofill_picker.dialog.dart +++ b/lib/features/autofill/autofill_picker/autofill_picker.dialog.dart @@ -1,9 +1,9 @@ +import 'package:app_core/globals.dart'; import 'package:flutter/material.dart'; import 'package:flutter_autofill_service/flutter_autofill_service.dart'; import 'package:get/get.dart'; import '../../../core/hive/models/item.hive.dart'; -import '../../../core/utils/utils.dart'; import 'autofill_picker_dialog.controller.dart'; class AutofillPickerDialog extends StatelessWidget { @@ -74,7 +74,7 @@ class AutofillPickerDialog extends StatelessWidget { : 'Select Password', ), ), - content: Utils.isSmallScreen + content: isSmallScreen ? content : Container( constraints: const BoxConstraints(maxHeight: 600), diff --git a/lib/features/categories/categories.screen.dart b/lib/features/categories/categories.screen.dart index 1f235c04..835ca2c3 100644 --- a/lib/features/categories/categories.screen.dart +++ b/lib/features/categories/categories.screen.dart @@ -1,16 +1,16 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; +import 'package:app_core/widgets/remote_image.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; import 'package:liso/core/persistence/persistence.dart'; -import 'package:liso/features/general/remote_image.widget.dart'; -import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../general/appbar_leading.widget.dart'; -import '../general/busy_indicator.widget.dart'; import '../general/centered_placeholder.widget.dart'; import '../menu/menu.button.dart'; import '../menu/menu.item.dart'; @@ -32,7 +32,7 @@ class CategoriesScreen extends StatelessWidget with ConsoleMixin { category.metadata = await category.metadata!.getUpdated(); category.deleted = true; await category.save(); - Persistence.to.changes.val++; + AppPersistence.to.changes.val++; categoriesController.load(); } @@ -43,7 +43,7 @@ class CategoriesScreen extends StatelessWidget with ConsoleMixin { Get.dialog(AlertDialog( title: const Text('Delete Category'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : SizedBox(width: 450, child: dialogContent), actions: [ diff --git a/lib/features/categories/categories_screen.controller.dart b/lib/features/categories/categories_screen.controller.dart index 46a7bfe5..b000ad81 100644 --- a/lib/features/categories/categories_screen.controller.dart +++ b/lib/features/categories/categories_screen.controller.dart @@ -1,3 +1,8 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -5,12 +10,8 @@ import 'package:liso/core/hive/models/category.hive.dart'; import 'package:liso/core/hive/models/metadata/metadata.hive.dart'; import 'package:uuid/uuid.dart'; -import '../../core/notifications/notifications.manager.dart'; import '../../core/persistence/persistence.dart'; -import '../../core/utils/ui_utils.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../pro/pro.controller.dart'; +import '../../core/utils/globals.dart'; import 'categories.controller.dart'; import 'categories.service.dart'; @@ -52,7 +53,7 @@ class CategoriesScreenController extends GetxController with ConsoleMixin { void _showForm() async { void done() { - Persistence.to.changes.val++; + AppPersistence.to.changes.val++; CategoriesController.to.load(); // clear fields nameController.clear(); @@ -82,14 +83,13 @@ class CategoriesScreenController extends GetxController with ConsoleMixin { ); } - if (CategoriesController.to.data.length >= - ProController.to.limits.customCategories) { + if (CategoriesController.to.data.length >= limits.customCategories) { return Utils.adaptiveRouteOpen( name: Routes.upgrade, parameters: { 'title': 'Custom Categories', 'body': - 'Maximum custom categories of ${ProController.to.limits.customCategories} limit reached. Upgrade to Pro to unlock unlimited custom categories feature.', + 'Maximum custom categories of ${limits.customCategories} limit reached. Upgrade to Pro to unlock unlimited custom categories feature.', }, ); } @@ -169,8 +169,7 @@ class CategoriesScreenController extends GetxController with ConsoleMixin { Get.dialog(AlertDialog( title: Text('${createMode ? 'new' : 'update'}_category'.tr), - content: - Utils.isSmallScreen ? content : SizedBox(width: 450, child: content), + content: isSmallScreen ? content : SizedBox(width: 450, child: content), actions: [ TextButton( onPressed: Get.back, diff --git a/lib/features/categories/picker/category_picker.screen.dart b/lib/features/categories/picker/category_picker.screen.dart index fc155b4d..08fbcc55 100644 --- a/lib/features/categories/picker/category_picker.screen.dart +++ b/lib/features/categories/picker/category_picker.screen.dart @@ -1,3 +1,5 @@ +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -5,7 +7,6 @@ import 'package:get/get.dart'; import '../../../core/utils/globals.dart'; import '../../../core/utils/utils.dart'; import '../../app/routes.dart'; -import '../../general/appbar_leading.widget.dart'; import 'category_picker_screen.controller.dart'; class CategoryPickerScreen extends StatelessWidget with ConsoleMixin { @@ -22,12 +23,12 @@ class CategoryPickerScreen extends StatelessWidget with ConsoleMixin { Get.back(); Utils.adaptiveRouteOpen( - name: Routes.item, + name: AppRoutes.item, parameters: {'mode': 'add', 'category': category.id}, ); } - final icon = Utils.categoryIcon( + final icon = AppUtils.categoryIcon( category.id, color: themeColor, size: 40, diff --git a/lib/features/cipher/cipher.screen.dart b/lib/features/cipher/cipher.screen.dart index 44890f68..56c11988 100644 --- a/lib/features/cipher/cipher.screen.dart +++ b/lib/features/cipher/cipher.screen.dart @@ -1,13 +1,13 @@ +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../general/appbar_leading.widget.dart'; -import '../general/busy_indicator.widget.dart'; import 'cipher_screen.controller.dart'; class CipherScreen extends StatelessWidget with ConsoleMixin { diff --git a/lib/features/cipher/cipher_screen.controller.dart b/lib/features/cipher/cipher_screen.controller.dart index 7cce6926..b257f3af 100644 --- a/lib/features/cipher/cipher_screen.controller.dart +++ b/lib/features/cipher/cipher_screen.controller.dart @@ -1,22 +1,21 @@ import 'dart:convert'; import 'dart:io'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:liso/core/utils/ui_utils.dart'; import 'package:path/path.dart'; import 'package:share_plus/share_plus.dart'; -import '../../core/firebase/config/config.service.dart'; -import '../../core/notifications/notifications.manager.dart'; import '../../core/services/cipher.service.dart'; import '../../core/utils/file.util.dart'; import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../pro/pro.controller.dart'; class CipherScreenController extends GetxController with StateMixin, ConsoleMixin { @@ -49,7 +48,7 @@ class CipherScreenController extends GetxController // FUNCTIONS void encrypt() async { - Globals.timeLockEnabled = false; // temporarily disable + timeLockEnabled = false; // temporarily disable FilePickerResult? result; try { @@ -58,27 +57,28 @@ class CipherScreenController extends GetxController type: FileType.any, ); } catch (e) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable console.error('FilePicker error: $e'); return; } if (result == null || result.files.isEmpty) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable console.warning("canceled file picker"); return; } - if (!ProController.to.limits.cipherTool) { - return Utils.adaptiveRouteOpen( - name: Routes.upgrade, - parameters: { - 'title': 'Encryption Tool', - 'body': - 'Encrypt your ${GetPlatform.isDesktop ? 'computer' : 'precious'} files to protect them from hackers and unwanted access. Using the same military-grade encryption ${ConfigService.to.appName} uses to protect your vault. Upgrade to Pro to take advantage of this powerful feature.', - }, - ); - } + // TODO: temporary + // if (!limits.cipherTool) { + // return Utils.adaptiveRouteOpen( + // name: Routes.upgrade, + // parameters: { + // 'title': 'Encryption Tool', + // 'body': + // 'Encrypt your ${GetPlatform.isDesktop ? 'computer' : 'precious'} files to protect them from hackers and unwanted access. Using the same military-grade encryption ${ConfigService.to.appName} uses to protect your vault. Upgrade to Pro to take advantage of this powerful feature.', + // }, + // ); + // } change(false, status: RxStatus.loading()); final stopwatch = Stopwatch()..start(); @@ -88,7 +88,7 @@ class CipherScreenController extends GetxController console.info('path: $path'); if (name.contains(kEncryptedExtensionExtra)) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable change(null, status: RxStatus.success()); return UIUtils.showSimpleDialog( 'Already Encrypted', @@ -112,7 +112,7 @@ class CipherScreenController extends GetxController text: GetPlatform.isIOS ? null : name, ); - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable return change(false, status: RxStatus.success()); } @@ -121,7 +121,7 @@ class CipherScreenController extends GetxController dialogTitle: 'Choose Export Path', ); - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable // user cancelled picker if (exportPath == null) { return change(null, status: RxStatus.success()); @@ -144,7 +144,7 @@ class CipherScreenController extends GetxController } void decrypt() async { - Globals.timeLockEnabled = false; // temporarily disable + timeLockEnabled = false; // temporarily disable FilePickerResult? result; try { @@ -154,13 +154,13 @@ class CipherScreenController extends GetxController type: FileType.any, ); } catch (e) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable console.error('FilePicker error: $e'); return; } if (result == null || result.files.isEmpty) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable console.warning("canceled file picker"); return; } @@ -174,7 +174,7 @@ class CipherScreenController extends GetxController if (!name.contains('.$kVaultExtension') && !name.contains(kEncryptedExtensionExtra)) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable change(null, status: RxStatus.success()); return UIUtils.showSimpleDialog( 'Invalid ${ConfigService.to.appName} Encrypted File', @@ -198,7 +198,7 @@ class CipherScreenController extends GetxController text: GetPlatform.isIOS ? null : name, ); - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable return change(false, status: RxStatus.success()); } @@ -207,7 +207,7 @@ class CipherScreenController extends GetxController dialogTitle: 'Choose Export Path', ); - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable // user cancelled picker if (exportPath == null) { return change(null, status: RxStatus.success()); @@ -265,9 +265,7 @@ class CipherScreenController extends GetxController title: Text('encrypt_text'.tr), content: Form( key: formKey, - child: Utils.isSmallScreen - ? content - : SizedBox(width: 450, child: content), + child: isSmallScreen ? content : SizedBox(width: 450, child: content), ), actions: [ TextButton( @@ -318,9 +316,7 @@ class CipherScreenController extends GetxController title: Text('decrypt_text'.tr), content: Form( key: formKey, - child: Utils.isSmallScreen - ? content - : SizedBox(width: 450, child: content), + child: isSmallScreen ? content : SizedBox(width: 450, child: content), ), actions: [ TextButton( diff --git a/lib/features/connectivity/connectivity.service.dart b/lib/features/connectivity/connectivity.service.dart deleted file mode 100644 index f0b082fe..00000000 --- a/lib/features/connectivity/connectivity.service.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'dart:async'; - -import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:get/get.dart'; - -import 'package:console_mixin/console_mixin.dart'; -import 'package:liso/core/firebase/config/config.service.dart'; - -class ConnectivityService extends GetxService with ConsoleMixin { - static ConnectivityService get to => Get.find(); - - // VARIABLES - StreamSubscription? connectivitySubscription; - - // PROPERTIES - final connected = true.obs; - - // GETTERS - - // INIT - @override - void onInit() async { - final connectivity = Connectivity(); - - // get current status - connected.value = - await connectivity.checkConnectivity() != ConnectivityResult.none; - console.info('connected: ${connected.value}'); - - // stream subscription - connectivitySubscription = - connectivity.onConnectivityChanged.listen((result) { - connected.value = result != ConnectivityResult.none; - console.info('connected: ${connected()}'); - - if (connected.value && !ConfigService.to.remoteFetched) { - ConfigService.to.fetchFromFunctions(); - } - }); - - super.onInit(); - } - - // FUNCTIONS - void cancel() => connectivitySubscription?.cancel(); -} diff --git a/lib/features/connectivity/connectivity_bar.widget.dart b/lib/features/connectivity/connectivity_bar.widget.dart deleted file mode 100644 index a4bc9f6f..00000000 --- a/lib/features/connectivity/connectivity_bar.widget.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:app_settings/app_settings.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:liso/core/persistence/persistence_builder.widget.dart'; -import 'package:liso/features/connectivity/connectivity.service.dart'; - -class ConnectivityBar extends StatelessWidget { - const ConnectivityBar({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final content = InkWell( - onTap: GetPlatform.isMobile ? AppSettings.openWIFISettings : null, - child: Container( - height: 20, - color: Colors.red.withOpacity(0.3), - child: const Center( - child: Text( - 'No Internet Connection', - style: TextStyle(fontSize: 11), - ), - ), - ), - ); - - return PersistenceBuilder( - builder: (p, _) { - return Obx( - () => !ConnectivityService.to.connected() && p.sync.val - ? content - : const SizedBox.shrink(), - ); - }, - ); - } -} diff --git a/lib/features/create_password/create_password.screen.dart b/lib/features/create_password/create_password.screen.dart index b9354fd9..e812c52c 100644 --- a/lib/features/create_password/create_password.screen.dart +++ b/lib/features/create_password/create_password.screen.dart @@ -1,3 +1,7 @@ +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -5,11 +9,8 @@ import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icon.dart'; import 'package:liso/core/utils/styles.dart'; import 'package:liso/core/utils/utils.dart'; -import 'package:liso/features/general/appbar_leading.widget.dart'; -import 'package:liso/features/general/busy_indicator.widget.dart'; import '../../core/utils/globals.dart'; -import '../app/routes.dart'; import 'create_password_screen.controller.dart'; class CreatePasswordScreen extends StatelessWidget with ConsoleMixin { @@ -48,7 +49,7 @@ class CreatePasswordScreen extends StatelessWidget with ConsoleMixin { keyboardType: TextInputType.visiblePassword, obscureText: controller.obscurePassword(), textInputAction: TextInputAction.next, - validator: Utils.validatePassword, + validator: AppUtils.validatePassword, autovalidateMode: AutovalidateMode.onUserInteraction, autofillHints: const [AutofillHints.newPassword], decoration: InputDecoration( @@ -74,7 +75,7 @@ class CreatePasswordScreen extends StatelessWidget with ConsoleMixin { obscureText: controller.obscureConfirmPassword(), textInputAction: TextInputAction.done, onFieldSubmitted: (text) => controller.confirm(), - validator: Utils.validatePassword, + validator: AppUtils.validatePassword, autovalidateMode: AutovalidateMode.onUserInteraction, autofillHints: const [AutofillHints.password], decoration: InputDecoration( diff --git a/lib/features/create_password/create_password_screen.controller.dart b/lib/features/create_password/create_password_screen.controller.dart index 8828e41f..29f12a17 100644 --- a/lib/features/create_password/create_password_screen.controller.dart +++ b/lib/features/create_password/create_password_screen.controller.dart @@ -1,18 +1,17 @@ +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:liso/core/middlewares/authentication.middleware.dart'; -import 'package:liso/core/utils/ui_utils.dart'; -import 'package:liso/core/utils/utils.dart'; import 'package:liso/features/app/routes.dart'; import 'package:liso/features/wallet/wallet.service.dart'; -import '../../core/firebase/config/config.service.dart'; -import '../../core/notifications/notifications.manager.dart'; import '../../core/persistence/persistence.dart'; import '../main/main_screen.controller.dart'; -import '../supabase/supabase_auth.service.dart'; class CreatePasswordScreenController extends GetxController with StateMixin, ConsoleMixin { @@ -38,7 +37,7 @@ class CreatePasswordScreenController extends GetxController void generate() async { final password_ = await Utils.adaptiveRouteOpen( - name: Routes.passwordGenerator, + name: AppRoutes.passwordGenerator, parameters: {'return': 'true'}, ); @@ -56,7 +55,6 @@ class CreatePasswordScreenController extends GetxController if (!formKey.currentState!.validate()) return; if (status == RxStatus.loading()) return console.error('still busy'); change(null, status: RxStatus.loading()); - await SupabaseAuthService.to.signOut(); // just to make sure // TODO: improve password validation if (passwordController.text.trim() != @@ -74,9 +72,9 @@ class CreatePasswordScreenController extends GetxController return console.error('Passwords do not match'); } - Persistence.to.backedUpSeed.val = + AppPersistence.to.backedUpSeed.val = Get.parameters['from'] == 'restore_screen'; - Persistence.to.backedUpPassword.val = true; + AppPersistence.to.backedUpPassword.val = true; final isNewVault = Get.parameters['from'] == 'seed_screen'; await WalletService.to.create( diff --git a/lib/features/debug/debug.screen.dart b/lib/features/debug/debug.screen.dart index d1f3e2de..4d559e52 100644 --- a/lib/features/debug/debug.screen.dart +++ b/lib/features/debug/debug.screen.dart @@ -1,3 +1,4 @@ +import 'package:app_core/widgets/appbar_leading.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -5,7 +6,7 @@ import 'package:iconsax/iconsax.dart'; import '../../core/middlewares/authentication.middleware.dart'; import '../../core/utils/globals.dart'; -import '../general/appbar_leading.widget.dart'; + import '../joined_vaults/joined_vault.controller.dart'; import '../shared_vaults/shared_vault.controller.dart'; import 'debug_screen.controller.dart'; @@ -153,7 +154,7 @@ class DebugScreen extends StatelessWidget with ConsoleMixin { // onTap: () async { // UIUtils.showSimpleDialog( // 'Limits', - // jsonEncode(ProController.to.limits.toJson()), + // jsonEncode(limits.toJson()), // ); // }, // ), diff --git a/lib/features/debug/debug_screen.controller.dart b/lib/features/debug/debug_screen.controller.dart index ef02f0be..4620ec0a 100644 --- a/lib/features/debug/debug_screen.controller.dart +++ b/lib/features/debug/debug_screen.controller.dart @@ -1,14 +1,6 @@ -import 'dart:convert'; - import 'package:console_mixin/console_mixin.dart'; import 'package:flutter_autofill_service/flutter_autofill_service.dart'; import 'package:get/get.dart'; -import 'package:liso/core/utils/globals.dart'; - -import '../../core/firebase/config/config.service.dart'; -import '../../core/hive/models/app_domain.hive.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; class DebugScreenController extends GetxController with ConsoleMixin { static DebugScreenController get to => Get.find(); @@ -119,86 +111,86 @@ status: ${status.toString()}' } void saveInfo() async { - metadata = AutofillMetadata( - packageNames: {'com.instagram.android'}, - webDomains: {AutofillWebDomain(domain: 'instagram.com')}, - saveInfo: SaveInfoMetadata(username: 'theuser', password: 'thepass'), - ); - - if (metadata!.webDomains.isEmpty && metadata!.packageNames.isEmpty) { - return console.error('invalid autofill metadata'); - } - - final appDomains = ConfigService.to.appDomains.data.where((e) { - // DOMAINS - if (metadata?.webDomains != null && - e.uris.where((e) { - final uri = Uri.tryParse(e); - if (uri == null) false; - final domain = AutofillWebDomain( - scheme: uri!.scheme, - domain: uri.host, - ); - - return metadata!.webDomains.contains(domain); - }).isNotEmpty) { - return true; - } - - // PACKAGE NAMES - if (metadata?.packageNames != null && - e.appIds - .where((a) => metadata!.packageNames.contains(a)) - .isNotEmpty) { - return true; - } - - return false; - }).toList(); - - final appIds = metadata?.packageNames != null - ? metadata!.packageNames.toList() - : []; - - final uris = metadata?.webDomains != null - ? metadata!.webDomains - .toList() - .map((e) => '${e.scheme}://${e.domain}') - .toList() - : []; - - String service = ''; - - if (metadata!.packageNames.isNotEmpty) { - service = metadata!.packageNames.first; - } else if (metadata!.webDomains.isNotEmpty) { - service = metadata!.webDomains.first.domain; - } - - final appDomain = appDomains.isNotEmpty - ? appDomains.first - : HiveAppDomain( - title: service, - appIds: appIds, - uris: uris, - iconUrl: '', - ); - - console.info('app domain: ${appDomain.toJson()}'); - - final username = metadata?.saveInfo?.username ?? ''; - final password = metadata?.saveInfo?.password ?? ''; - - Utils.adaptiveRouteOpen( - name: Routes.item, - parameters: { - 'mode': 'saved_autofill', - 'category': LisoItemCategory.login.name, - 'title': '$username ${appDomain.title}', - 'username': username, - 'password': password, - 'app_domain': jsonEncode(appDomain.toJson()), - }, - ); + // metadata = AutofillMetadata( + // packageNames: {'com.instagram.android'}, + // webDomains: {AutofillWebDomain(domain: 'instagram.com')}, + // saveInfo: SaveInfoMetadata(username: 'theuser', password: 'thepass'), + // ); + + // if (metadata!.webDomains.isEmpty && metadata!.packageNames.isEmpty) { + // return console.error('invalid autofill metadata'); + // } + + // final appDomains = ConfigService.to.appDomains.data.where((e) { + // // DOMAINS + // if (metadata?.webDomains != null && + // e.uris.where((e) { + // final uri = Uri.tryParse(e); + // if (uri == null) false; + // final domain = AutofillWebDomain( + // scheme: uri!.scheme, + // domain: uri.host, + // ); + + // return metadata!.webDomains.contains(domain); + // }).isNotEmpty) { + // return true; + // } + + // // PACKAGE NAMES + // if (metadata?.packageNames != null && + // e.appIds + // .where((a) => metadata!.packageNames.contains(a)) + // .isNotEmpty) { + // return true; + // } + + // return false; + // }).toList(); + + // final appIds = metadata?.packageNames != null + // ? metadata!.packageNames.toList() + // : []; + + // final uris = metadata?.webDomains != null + // ? metadata!.webDomains + // .toList() + // .map((e) => '${e.scheme}://${e.domain}') + // .toList() + // : []; + + // String service = ''; + + // if (metadata!.packageNames.isNotEmpty) { + // service = metadata!.packageNames.first; + // } else if (metadata!.webDomains.isNotEmpty) { + // service = metadata!.webDomains.first.domain; + // } + + // final appDomain = appDomains.isNotEmpty + // ? appDomains.first + // : HiveAppDomain( + // title: service, + // appIds: appIds, + // uris: uris, + // iconUrl: '', + // ); + + // console.info('app domain: ${appDomain.toJson()}'); + + // final username = metadata?.saveInfo?.username ?? ''; + // final password = metadata?.saveInfo?.password ?? ''; + + // Utils.adaptiveRouteOpen( + // name: Routes.item, + // parameters: { + // 'mode': 'saved_autofill', + // 'category': LisoItemCategory.login.name, + // 'title': '$username ${appDomain.title}', + // 'username': username, + // 'password': password, + // 'app_domain': jsonEncode(appDomain.toJson()), + // }, + // ); } } diff --git a/lib/features/devices/devices.screen.dart b/lib/features/devices/devices.screen.dart index 1bb3d447..cbafaa3c 100644 --- a/lib/features/devices/devices.screen.dart +++ b/lib/features/devices/devices.screen.dart @@ -1,14 +1,14 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; -import 'package:liso/core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../general/appbar_leading.widget.dart'; -import '../general/busy_indicator.widget.dart'; import '../general/centered_placeholder.widget.dart'; import '../json_viewer/json_viewer.screen.dart'; import '../menu/menu.button.dart'; @@ -26,7 +26,7 @@ class DevicesScreen extends StatelessWidget with ConsoleMixin { final device = controller.data[index]; final menuItems = [ - if (device.id != Globals.metadata!.device.id) ...[ + if (device.id != metadataDevice.id) ...[ ContextMenuItem( title: 'unsync'.tr, leading: Icon(Iconsax.slash, size: popupIconSize), @@ -42,7 +42,7 @@ class DevicesScreen extends StatelessWidget with ConsoleMixin { ), ]; - final isThisDevice = device.id == Globals.metadata?.device.id; + final isThisDevice = device.id == metadataDevice.id; return ListTile( title: Text(device.model, maxLines: 1), diff --git a/lib/features/devices/devices_screen.controller.dart b/lib/features/devices/devices_screen.controller.dart index e9741f98..282bef96 100644 --- a/lib/features/devices/devices_screen.controller.dart +++ b/lib/features/devices/devices_screen.controller.dart @@ -1,11 +1,10 @@ import 'dart:async'; +import 'package:app_core/globals.dart'; +import 'package:app_core/hive/models/device.hive.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:liso/core/hive/models/metadata/device.hive.dart'; - -import '../../core/utils/utils.dart'; class DevicesScreenController extends GetxController with ConsoleMixin, StateMixin { @@ -92,7 +91,7 @@ class DevicesScreenController extends GetxController // data.remove(device); // Get.back(); // close dialog - // if (enforce && data.length <= ProController.to.limits.devices) { + // if (enforce && data.length <= limits.devices) { // Get.back(); // close screen // } } @@ -103,7 +102,7 @@ class DevicesScreenController extends GetxController Get.dialog(AlertDialog( title: Text('unsync_device'.tr), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : SizedBox( width: 450, diff --git a/lib/features/drawer/drawer.widget.dart b/lib/features/drawer/drawer.widget.dart index 29bbf5c8..f1737636 100644 --- a/lib/features/drawer/drawer.widget.dart +++ b/lib/features/drawer/drawer.widget.dart @@ -1,3 +1,8 @@ +import 'package:app_core/controllers/pro.controller.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/persistence/persistence_builder.widget.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/pro.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; @@ -7,13 +12,10 @@ import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; import 'package:liso/core/utils/globals.dart'; import 'package:liso/features/app/routes.dart'; -import 'package:liso/features/files/storage.service.dart'; -import 'package:liso/features/general/pro.widget.dart'; -import '../../../core/utils/utils.dart'; +import '../../core/persistence/persistence.dart'; import '../../resources/resources.dart'; -import '../../core/persistence/persistence_builder.widget.dart'; -import '../pro/pro.controller.dart'; +import '../files/storage.service.dart'; import 'drawer_widget.controller.dart'; class DrawerMenu extends StatelessWidget with ConsoleMixin { @@ -125,29 +127,29 @@ class DrawerMenu extends StatelessWidget with ConsoleMixin { selected: controller.filterTrashed(), ), ), - PersistenceBuilder(builder: (p, context) { - if (!p.proTester.val) { - return const SizedBox.shrink(); - } + // PersistenceBuilder(builder: (p, context) { + // if (!.proTester.val) { + // return const SizedBox.shrink(); + // } - return Obx( - () => ListTile( - selected: controller.filterDeleted(), - selectedColor: Colors.red, - leading: const Icon(Iconsax.slash), - onTap: controller.filterDeletedItems, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('Deleted'), - if (controller.deletedCount > 0) ...[ - Chip(label: Text(controller.deletedCount.toString())), - ], - ], - ), - ), - ); - }), + // return Obx( + // () => ListTile( + // selected: controller.filterDeleted(), + // selectedColor: Colors.red, + // leading: const Icon(Iconsax.slash), + // onTap: controller.filterDeletedItems, + // title: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // const Text('Deleted'), + // if (controller.deletedCount > 0) ...[ + // Chip(label: Text(controller.deletedCount.toString())), + // ], + // ], + // ), + // ), + // ); + // }), ], ), ExpansionTile( @@ -180,11 +182,11 @@ class DrawerMenu extends StatelessWidget with ConsoleMixin { ), children: [ PersistenceBuilder( - builder: (p, context) => p.sync.val + builder: (p, context) => AppPersistence.to.sync.val ? ListTile( leading: const Icon(Iconsax.document_cloud), onTap: () => Utils.adaptiveRouteOpen( - name: Routes.s3Explorer, + name: AppRoutes.s3Explorer, parameters: {'type': 'explorer'}, ), title: Row( @@ -194,7 +196,7 @@ class DrawerMenu extends StatelessWidget with ConsoleMixin { Chip( label: Obx( () => Text( - '${filesize(StorageService.to.rootInfo.value.data.size, 0)}/${filesize(ProController.to.limits.storageSize, 0)}', + '${filesize(StorageService.to.rootInfo.value.data.size, 0)}/${filesize(limits.storageSize, 0)}', style: const TextStyle(fontSize: 10), ), ), @@ -207,7 +209,7 @@ class DrawerMenu extends StatelessWidget with ConsoleMixin { () => LinearProgressIndicator( value: StorageService.to.rootInfo.value.data.size .toDouble() / - ProController.to.limits.storageSize, + limits.storageSize, backgroundColor: Colors.grey.withOpacity(0.1), ), ), @@ -224,17 +226,11 @@ class DrawerMenu extends StatelessWidget with ConsoleMixin { title: Text('wallet'.tr), leading: const Icon(Iconsax.wallet_1), onTap: () => Utils.adaptiveRouteOpen( - name: Routes.wallet, + name: AppRoutes.wallet, method: 'offAndToNamed', ), ); }), - // ListTile( - // title: Text('browser'.tr), - // leading: const Icon(Iconsax.chrome), - // enabled: false, - // // onTap: controller.browser, - // ), ListTile( title: Text('settings'.tr), leading: const Icon(Iconsax.setting_2), @@ -291,49 +287,6 @@ class DrawerMenu extends StatelessWidget with ConsoleMixin { ), ), ), - // PersistenceBuilder( - // builder: (p, context) => Obx( - // () => Column( - // children: [ - // ListTile( - // title: Row( - // children: [ - // if (!ProController.to.isPro) ...[ - // const Text( - // 'Try ', - // style: TextStyle(fontWeight: FontWeight.normal), - // ), - // ], - // const ProText(size: 16) - // ], - // ), - // subtitle: Text( - // ProController.to.isPro - // ? 'Active' - // : 'Unlock All Access', - // style: const TextStyle( - // color: Colors.grey, - // fontWeight: FontWeight.w500, - // ), - // ), - // leading: Icon(LineIcons.rocket, color: proColor), - // onTap: () { - // if (ProController.to.info.value.managementURL != - // null) { - // Utils.openUrl( - // ProController.to.info.value.managementURL!, - // ); - // } - // }, - // ) - // .animate(onPlay: (c) => c.repeat()) - // .shimmer(duration: 2000.ms) - // .shakeX(duration: 1000.ms, hz: 2, amount: 1) - // .then(delay: 3000.ms), - // ], - // ), - // ), - // ), ListTile( title: const Text('Need Help?'), subtitle: const Text("Don't hesitate to contact us"), @@ -356,7 +309,7 @@ class DrawerMenu extends StatelessWidget with ConsoleMixin { title: const Text('Encryption Tool'), leading: const Icon(Iconsax.convert_3d_cube), onTap: () => Utils.adaptiveRouteOpen( - name: Routes.cipher, + name: AppRoutes.cipher, method: 'offAndToNamed', ), ), @@ -364,7 +317,7 @@ class DrawerMenu extends StatelessWidget with ConsoleMixin { title: Text('password_generator'.tr), leading: const Icon(Iconsax.password_check), onTap: () => Utils.adaptiveRouteOpen( - name: Routes.passwordGenerator, + name: AppRoutes.passwordGenerator, method: 'offAndToNamed', parameters: {'from': 'drawer'}, ), @@ -373,7 +326,7 @@ class DrawerMenu extends StatelessWidget with ConsoleMixin { title: Text('seed_generator'.tr), leading: const Icon(Iconsax.key), onTap: () => Utils.adaptiveRouteOpen( - name: Routes.seedGenerator, + name: AppRoutes.seedGenerator, method: 'offAndToNamed', parameters: {'from': 'drawer'}, ), diff --git a/lib/features/drawer/drawer_widget.controller.dart b/lib/features/drawer/drawer_widget.controller.dart index 491c6de7..e2cbb9b8 100644 --- a/lib/features/drawer/drawer_widget.controller.dart +++ b/lib/features/drawer/drawer_widget.controller.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/persistence/persistence.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -6,7 +8,6 @@ import 'package:liso/features/groups/groups.controller.dart'; import 'package:liso/features/items/items.controller.dart'; import '../../core/hive/models/item.hive.dart'; -import '../../core/persistence/persistence.dart'; import '../../core/utils/utils.dart'; import '../categories/categories.controller.dart'; import '../shared_vaults/shared_vault.controller.dart'; @@ -119,7 +120,7 @@ class DrawerMenuController extends GetxController with ConsoleMixin { return Obx( () => ListTile( title: Text(category.reservedName, overflow: TextOverflow.ellipsis), - leading: Utils.categoryIcon(category.id), + leading: AppUtils.categoryIcon(category.id), selected: category.id == filterCategory.value, onTap: () => filterByCategory(category.id), ), @@ -263,6 +264,6 @@ class DrawerMenuController extends GetxController with ConsoleMixin { void done() async { await ItemsController.to.load(); - if (Utils.isSmallScreen) Get.back(); + if (isSmallScreen) Get.back(); } } diff --git a/lib/features/feedback/feedback.screen.dart b/lib/features/feedback/feedback.screen.dart deleted file mode 100644 index 14ec1006..00000000 --- a/lib/features/feedback/feedback.screen.dart +++ /dev/null @@ -1,158 +0,0 @@ -import 'package:console_mixin/console_mixin.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:get/get.dart'; -import 'package:iconsax/iconsax.dart'; -import 'package:line_icons/line_icons.dart'; -import 'package:liso/core/firebase/config/config.service.dart'; -import 'package:liso/core/utils/globals.dart'; - -import '../../core/utils/utils.dart'; -import '../general/appbar_leading.widget.dart'; -import 'feedback_screen.controller.dart'; - -class FeedbackScreen extends StatelessWidget with ConsoleMixin { - const FeedbackScreen({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final controller = Get.put(FeedbackScreenController()); - - final content = Form( - key: controller.formKey, - child: SingleChildScrollView( - padding: const EdgeInsets.all(15), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 20), - DropdownButtonFormField( - value: controller.feedbackType, - onChanged: (value) => controller.feedbackType = value!, - decoration: const InputDecoration(labelText: 'Type'), - items: const [ - DropdownMenuItem( - value: FeedbackType.feedback, - child: Text('Feedback'), - ), - DropdownMenuItem( - value: FeedbackType.suggestion, - child: Text('Suggestion'), - ), - DropdownMenuItem( - value: FeedbackType.issue, - child: Text('Issue'), - ), - ], - ), - const SizedBox(height: 20), - TextFormField( - autofocus: true, - controller: controller.textController, - validator: (data) => data!.split(' ').length < 5 - ? 'Your feedback is too short' - : null, - maxLength: 2000, - minLines: 3, - maxLines: 10, - decoration: const InputDecoration( - labelText: 'Write your concern here...', - alignLabelWithHint: true, - helperMaxLines: 5, - helperText: - "Please don't hesitate to send us your feedback and we'll be happy to chat with you. Expect a reply within 24-48 hours."), - ), - const SizedBox(height: 20), - RatingBar.builder( - initialRating: controller.rating.value, - minRating: 0, - direction: Axis.horizontal, - itemCount: 5, - itemPadding: const EdgeInsets.symmetric(horizontal: 4.0), - onRatingUpdate: (rating) => controller.rating.value = rating, - itemBuilder: (context, _) => const Icon( - Icons.star, - color: kAppColor, - ), - ), - Obx( - () => Visibility( - visible: controller.rating.value == 0.0, - child: Padding( - padding: const EdgeInsets.only(top: 10), - child: Text( - 'Please give us your star rating', - style: TextStyle( - color: Colors.pink.shade200, - fontSize: 11, - ), - ), - ), - ), - ), - Obx( - () => Visibility( - visible: controller.showRateButton, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 20), - ElevatedButton.icon( - onPressed: controller.review, - icon: const Icon(Icons.star_border), - label: Text( - 'Rate & Review ${ConfigService.to.appName} on ${GetPlatform.isIOS || GetPlatform.isMacOS ? 'the App Store' : 'Google Play'}', - ), - ), - const SizedBox(height: 10), - Text( - 'Help spread awareness on why people should consider using ${ConfigService.to.appName} as their secure vault.', - style: const TextStyle(color: Colors.grey), - ), - ], - ), - ), - ), - const Divider(height: 50), - ElevatedButton.icon( - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xff7289da), - ), - onPressed: () { - Utils.copyToClipboard(controller.textController.text); - - Utils.openUrl( - ConfigService.to.general.app.links.discord, - ); - }, - icon: const Icon(LineIcons.discord), - label: const Text('Send feedback via Discord'), - ), - const SizedBox(height: 10), - Text( - 'Help spread awareness on why people should consider using ${ConfigService.to.appName} as their secure vault.', - style: const TextStyle(color: Colors.grey), - ), - ], - )), - ); - - final appBar = AppBar( - title: Text('Contact ${ConfigService.to.appName}'), - leading: const AppBarLeadingButton(), - actions: [ - TextButton.icon( - label: const Text('Send'), - icon: const Icon(Iconsax.send_2), - onPressed: controller.send, - ), - const SizedBox(width: 10), - ], - ); - - return Scaffold( - appBar: appBar, - body: content, - ); - } -} diff --git a/lib/features/feedback/feedback_screen.controller.dart b/lib/features/feedback/feedback_screen.controller.dart deleted file mode 100644 index 5d6d6889..00000000 --- a/lib/features/feedback/feedback_screen.controller.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:console_mixin/console_mixin.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; - -import '../../core/firebase/config/config.service.dart'; -import '../../core/utils/globals.dart'; -import '../../core/utils/ui_utils.dart'; -import '../../core/utils/utils.dart'; - -enum FeedbackType { - feedback, - suggestion, - issue, -} - -class FeedbackScreenController extends GetxController - with StateMixin, ConsoleMixin { - // VARIABLES - final formKey = GlobalKey(); - final textController = TextEditingController(); - - var feedbackType = FeedbackType.feedback; - final rating = 0.0.obs; - - // PROPERTIES - - // GETTERS - - bool get showRateButton => rating.value >= 4.0 && isRateReviewSupported; - - // INIT - - // FUNCTIONS - - void send() async { - if (!formKey.currentState!.validate()) return; - - if (rating.value == 0.0) { - return UIUtils.showSimpleDialog( - 'Give A Rating', - 'Please give us your rating from 1 - 5 stars.', - ); - } - - Utils.contactEmail( - subject: - '${ConfigService.to.appName} ${feedbackType.toString().replaceAll('FeedbackType.', '').capitalizeFirst}', - preBody: textController.text, - rating: rating.value, - previousRoute: Get.previousRoute, - feedbackType: feedbackType, - ); - } - - void review() async { - Utils.copyToClipboard(textController.text); - UIUtils.rateAndReview(); - } -} diff --git a/lib/features/files/explorer/s3_explorer.screen.dart b/lib/features/files/explorer/s3_explorer.screen.dart index 62ec898a..f5023a64 100644 --- a/lib/features/files/explorer/s3_explorer.screen.dart +++ b/lib/features/files/explorer/s3_explorer.screen.dart @@ -1,3 +1,5 @@ +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -5,8 +7,6 @@ import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; import 'package:liso/core/utils/globals.dart'; -import '../../general/appbar_leading.widget.dart'; -import '../../general/busy_indicator.widget.dart'; import '../../general/centered_placeholder.widget.dart'; import 's3_exporer_screen.controller.dart'; import 's3_object.tile.dart'; diff --git a/lib/features/files/explorer/s3_exporer_screen.controller.dart b/lib/features/files/explorer/s3_exporer_screen.controller.dart index 571ebb43..917cf711 100644 --- a/lib/features/files/explorer/s3_exporer_screen.controller.dart +++ b/lib/features/files/explorer/s3_exporer_screen.controller.dart @@ -1,13 +1,16 @@ import 'dart:io'; +import 'package:app_core/globals.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:file_picker/file_picker.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; -import 'package:liso/core/firebase/config/config.service.dart'; -import 'package:liso/core/notifications/notifications.manager.dart'; import 'package:liso/core/persistence/persistence.secret.dart'; import 'package:liso/features/files/storage.service.dart'; import 'package:liso/features/files/sync.service.dart'; @@ -15,10 +18,7 @@ import 'package:path/path.dart'; import '../../../core/services/cipher.service.dart'; import '../../../core/utils/globals.dart'; -import '../../../core/utils/ui_utils.dart'; import '../../../core/utils/utils.dart'; -import '../../app/routes.dart'; -import '../../pro/pro.controller.dart'; import '../../supabase/model/object.model.dart'; class S3ExplorerScreenController extends GetxController @@ -98,7 +98,7 @@ class S3ExplorerScreenController extends GetxController void pickFile() async { change(true, status: RxStatus.loading()); - Globals.timeLockEnabled = false; // disable + timeLockEnabled = false; // disable FilePickerResult? result; try { @@ -106,20 +106,20 @@ class S3ExplorerScreenController extends GetxController type: FileType.any, ); } catch (e) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable console.error('FilePicker error: $e'); change(false, status: RxStatus.success()); return; } if (result == null || result.files.isEmpty) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable console.warning("canceled file picker"); change(false, status: RxStatus.success()); return; } - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable final file = File(result.files.single.path!); console.info('picked: ${file.path}'); @@ -136,7 +136,7 @@ class S3ExplorerScreenController extends GetxController ); } - if (fileSize > ProController.to.limits.uploadSize) { + if (fileSize > limits.uploadSize) { change(false, status: RxStatus.success()); return Utils.adaptiveRouteOpen( @@ -144,7 +144,7 @@ class S3ExplorerScreenController extends GetxController parameters: { 'title': 'Upload Large Files', 'body': - 'Upload size limit: ${filesize(ProController.to.limits.uploadSize)} reached. Upgrade to Pro to upload up to ${filesize(ConfigService.to.limits.pro.uploadSize)} per file.', + 'Upload size limit: ${filesize(limits.uploadSize)} reached. Upgrade to Pro to upload up to ${filesize(configLimits.pro.uploadSize)} per file.', }, ); } @@ -154,18 +154,19 @@ class S3ExplorerScreenController extends GetxController } void _upload(File file) async { - final assumedTotal = storage.rootInfo.value.data.size + await file.length(); - - if (assumedTotal >= ProController.to.limits.uploadSize) { - return Utils.adaptiveRouteOpen( - name: Routes.upgrade, - parameters: { - 'title': 'Add More Storage', - 'body': - 'Upgrade to Pro to store up to ${filesize(ConfigService.to.limits.pro.storageSize)} of files.', - }, - ); - } + // TODO: temporary + // final assumedTotal = storage.rootInfo.value.data.size + await file.length(); + + // if (assumedTotal >= limits.uploadSize) { + // return Utils.adaptiveRouteOpen( + // name: Routes.upgrade, + // parameters: { + // 'title': 'Add More Storage', + // 'body': + // 'Upgrade to Pro to store up to ${filesize(configLimits.pro.storageSize)} of files.', + // }, + // ); + // } change(true, status: RxStatus.loading()); final encryptedBytes = CipherService.to.encrypt(await file.readAsBytes()); @@ -232,7 +233,7 @@ class S3ExplorerScreenController extends GetxController textCapitalization: TextCapitalization.sentences, maxLength: 100, autovalidateMode: AutovalidateMode.onUserInteraction, - validator: (data) => Utils.validateFolderName(data!), + validator: (data) => AppUtils.validateFolderName(data!), decoration: const InputDecoration( labelText: 'Name', hintText: 'Folder Name', @@ -243,9 +244,7 @@ class S3ExplorerScreenController extends GetxController title: Text('new_folder'.tr), content: Form( key: formKey, - child: Utils.isSmallScreen - ? content - : SizedBox(width: 450, child: content), + child: isSmallScreen ? content : SizedBox(width: 450, child: content), ), actions: [ TextButton( diff --git a/lib/features/files/explorer/s3_object.tile.dart b/lib/features/files/explorer/s3_object.tile.dart index f96b7b8f..afb8dc6b 100644 --- a/lib/features/files/explorer/s3_object.tile.dart +++ b/lib/features/files/explorer/s3_object.tile.dart @@ -1,3 +1,4 @@ +import 'package:app_core/globals.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; @@ -105,7 +106,7 @@ class S3ObjectTile extends GetWidget with ConsoleMixin { // object.key.replaceAll('${SecretPersistence.to.walletAddress.val}/', ''), // ), iconColor: themeColor, - leading: Utils.s3ContentIcon(object), + leading: AppUtils.s3ContentIcon(object), enabled: !controller.busy.value, onTap: open, trailing: ContextMenuButton( diff --git a/lib/features/files/explorer/s3_object_tile.controller.dart b/lib/features/files/explorer/s3_object_tile.controller.dart index b94eaa4e..534285bc 100644 --- a/lib/features/files/explorer/s3_object_tile.controller.dart +++ b/lib/features/files/explorer/s3_object_tile.controller.dart @@ -1,5 +1,9 @@ import 'dart:io'; +import 'package:app_core/globals.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; @@ -13,13 +17,10 @@ import 'package:share_plus/share_plus.dart'; import '../../../core/liso/liso.manager.dart'; import '../../../core/liso/liso_paths.dart'; -import '../../../core/notifications/notifications.manager.dart'; import '../../../core/persistence/persistence.secret.dart'; import '../../../core/services/cipher.service.dart'; import '../../../core/utils/file.util.dart'; import '../../../core/utils/globals.dart'; -import '../../../core/utils/ui_utils.dart'; -import '../../../core/utils/utils.dart'; import '../../attachments/attachments_screen.controller.dart'; import '../../supabase/model/object.model.dart'; import '../../supabase/supabase_functions.service.dart'; @@ -43,7 +44,7 @@ class S3ObjectTileController extends GetxController void share(S3Object object) async { change('Sharing...', status: RxStatus.loading()); - final result = await SupabaseFunctionsService.to.presignUrl( + final result = await AppSupabaseFunctionsService.to.presignUrl( object: object.key, expirySeconds: 1.hours.inSeconds, ); @@ -63,7 +64,7 @@ class S3ObjectTileController extends GetxController Get.dialog(AlertDialog( title: const Text('Share Securely'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : SizedBox(width: 450, child: dialogContent), actions: [ @@ -154,7 +155,7 @@ class S3ObjectTileController extends GetxController Get.dialog(AlertDialog( title: const Text('Switch Vault'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : SizedBox(width: 450, child: dialogContent), actions: [ @@ -209,7 +210,7 @@ class S3ObjectTileController extends GetxController Get.dialog(AlertDialog( title: Text('delete'.tr), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : SizedBox( width: 450, @@ -252,7 +253,7 @@ class S3ObjectTileController extends GetxController final fileName = basename(file.path); - Globals.timeLockEnabled = false; // temporarily disable + timeLockEnabled = false; // temporarily disable if (GetPlatform.isMobile) { await Share.shareFiles( @@ -261,7 +262,7 @@ class S3ObjectTileController extends GetxController text: GetPlatform.isIOS ? null : fileName, ); - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable return change('', status: RxStatus.success()); } @@ -270,7 +271,7 @@ class S3ObjectTileController extends GetxController dialogTitle: 'Choose Export Path', ); - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable // user cancelled picker if (exportPath == null) { return change(null, status: RxStatus.success()); @@ -292,7 +293,7 @@ class S3ObjectTileController extends GetxController Get.dialog(AlertDialog( title: const Text('Download'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : SizedBox(width: 450, child: dialogContent), actions: [ diff --git a/lib/features/files/provider/custom_provider_screen.controller.dart b/lib/features/files/provider/custom_provider_screen.controller.dart index a159f50d..1f2ec3ae 100644 --- a/lib/features/files/provider/custom_provider_screen.controller.dart +++ b/lib/features/files/provider/custom_provider_screen.controller.dart @@ -1,15 +1,15 @@ import 'dart:async'; +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/ui_utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:liso/core/persistence/persistence.dart'; -import 'package:liso/core/utils/ui_utils.dart'; import 'package:minio/minio.dart'; import '../../../core/persistence/persistence.secret.dart'; import '../../../core/utils/globals.dart'; -import '../../../core/utils/utils.dart'; class CustomSyncProviderScreenController extends GetxController with StateMixin, ConsoleMixin { @@ -57,7 +57,7 @@ class CustomSyncProviderScreenController extends GetxController // FUNCTIONS void save() { - Persistence.to.syncProvider.val = LisoSyncProvider.custom.name; + AppPersistence.to.syncProvider.val = LisoSyncProvider.custom.name; persistence.s3Endpoint.val = endpointController.text; persistence.s3AccessKey.val = accessKeyController.text; persistence.s3SecretKey.val = secretKeyController.text; @@ -119,7 +119,7 @@ class CustomSyncProviderScreenController extends GetxController Get.dialog(AlertDialog( title: const Text('Connection Success'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : const SizedBox(width: 450, child: dialogContent), actions: [ diff --git a/lib/features/files/provider/custom_provider_screen.dart b/lib/features/files/provider/custom_provider_screen.dart index fa9719c1..7ee31d85 100644 --- a/lib/features/files/provider/custom_provider_screen.dart +++ b/lib/features/files/provider/custom_provider_screen.dart @@ -1,13 +1,13 @@ +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:liso/features/files/provider/custom_provider_screen.controller.dart'; -import 'package:liso/features/general/appbar_leading.widget.dart'; -import '../../../core/persistence/persistence_builder.widget.dart'; +import '../../../core/persistence/secret_persistence.builder.dart'; import '../../../core/utils/globals.dart'; -import '../../../core/utils/utils.dart'; -import '../../app/routes.dart'; class CustomSyncProviderScreen extends StatelessWidget with ConsoleMixin { const CustomSyncProviderScreen({Key? key}) : super(key: key); @@ -89,6 +89,7 @@ class CustomSyncProviderScreen extends StatelessWidget with ConsoleMixin { labelText: 'Session Token', ), ), + // TODO: temporary const Divider(), SecretPersistenceBuilder( builder: (p, context) => SwitchListTile( diff --git a/lib/features/files/provider/sync_provider.screen.dart b/lib/features/files/provider/sync_provider.screen.dart index 3a287042..9ce59d3d 100644 --- a/lib/features/files/provider/sync_provider.screen.dart +++ b/lib/features/files/provider/sync_provider.screen.dart @@ -4,7 +4,7 @@ // import 'package:iconsax/iconsax.dart'; // import 'package:liso/core/firebase/auth.service.dart'; // import 'package:liso/core/utils/ui_utils.dart'; -// import 'package:liso/features/general/appbar_leading.widget.dart'; +// // import '../../../core/persistence/persistence.dart'; // import '../../../core/persistence/persistence_builder.widget.dart'; diff --git a/lib/features/files/storage.service.dart b/lib/features/files/storage.service.dart index 21da7847..d2267b11 100644 --- a/lib/features/files/storage.service.dart +++ b/lib/features/files/storage.service.dart @@ -1,16 +1,17 @@ +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/persistence/persistence.dart'; +import 'package:app_core/supabase/model/server_response.model.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:either_dart/either.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; -import 'package:liso/core/firebase/config/config.service.dart'; import 'package:liso/core/persistence/persistence.dart'; import 'package:liso/core/persistence/persistence.secret.dart'; import '../supabase/model/list_objects_response.model.dart'; import '../supabase/model/object.model.dart'; -import '../supabase/model/server_response.model.dart'; import '../supabase/supabase_functions.service.dart'; class StorageService extends GetxService with ConsoleMixin { @@ -19,7 +20,7 @@ class StorageService extends GetxService with ConsoleMixin { // VARIABLES final config = Get.find(); final persistence = Get.find(); - final functions = Get.find(); + final functions = Get.find(); final rootInfo = const ListObjectsResponse().obs; // PROPERTIES @@ -38,14 +39,14 @@ class StorageService extends GetxService with ConsoleMixin { // FUNCTIONS Future load() async { - if (!persistence.sync.val) return console.warning('offline'); + if (!AppPersistence.to.sync.val) return console.warning('offline'); final result = await functions.listObjects(); if (result.isLeft) return console.error('failed to list objects'); rootInfo.value = result.right; } Future> remove(String object) async { - if (!persistence.sync.val) return const Left('offline'); + if (!AppPersistence.to.sync.val) return const Left('offline'); final result = await functions.deleteObjects([object]); if (result.isLeft) return Left(result.left); console.warning('response: ${result.right.data}'); @@ -56,7 +57,7 @@ class StorageService extends GetxService with ConsoleMixin { required String object, bool force = false, }) async { - if (!persistence.sync.val && !force) return const Left('offline'); + if (!AppPersistence.to.sync.val && !force) return const Left('offline'); final presignResult = await functions.presignUrl( object: object, @@ -98,7 +99,7 @@ class StorageService extends GetxService with ConsoleMixin { Uint8List bytes, { required String object, }) async { - if (!persistence.sync.val) return const Left('offline'); + if (!AppPersistence.to.sync.val) return const Left('offline'); console.info('uploading: $object...'); final presignResult = await functions.presignUrl( diff --git a/lib/features/files/sync.service.dart b/lib/features/files/sync.service.dart index 979b07e0..00d939af 100644 --- a/lib/features/files/sync.service.dart +++ b/lib/features/files/sync.service.dart @@ -1,18 +1,18 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:app_core/connectivity/connectivity.service.dart'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/persistence/persistence.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:either_dart/either.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; -import 'package:liso/core/firebase/config/config.service.dart'; import 'package:liso/core/hive/models/category.hive.dart'; import 'package:liso/core/persistence/persistence.dart'; import 'package:liso/core/services/cipher.service.dart'; -import 'package:liso/features/connectivity/connectivity.service.dart'; import 'package:liso/features/files/storage.service.dart'; import 'package:liso/features/main/main_screen.controller.dart'; -import 'package:liso/features/pro/pro.controller.dart'; import '../../core/hive/models/group.hive.dart'; import '../../core/hive/models/item.hive.dart'; @@ -49,7 +49,7 @@ class SyncService extends GetxService with ConsoleMixin { // GETTERS bool get isReady => - ConnectivityService.to.connected.value && persistence.sync.val; + ConnectivityService.to.connected.value && AppPersistence.to.sync.val; // INIT @@ -66,7 +66,7 @@ class SyncService extends GetxService with ConsoleMixin { console.info('syncing...'); syncing.value = true; - final statResult = await SupabaseFunctionsService.to.statObject( + final statResult = await AppSupabaseFunctionsService.to.statObject( kVaultFileName, ); @@ -98,7 +98,7 @@ class SyncService extends GetxService with ConsoleMixin { if (downResult.isLeft) return Left(downResult.left); // we are now ready to upSync because we are not in sync with server inSync.value = true; - Persistence.to.changes.val = 0; + AppPersistence.to.changes.val = 0; // up sync local changes with server await upSync().timeout( syncTimeoutDuration, @@ -143,23 +143,24 @@ class SyncService extends GetxService with ConsoleMixin { console.info('backup: $object...'); - // REMOVE OLDEST BACKUP IF NECESSARY - if (StorageService.to.backups.length >= ProController.to.limits.backups) { - final result = await StorageService.to.remove( - StorageService.to.backups.first.key, - ); - // abort backup if error in removing oldest backup - if (result.isLeft) { - return console.error('error removing last backup: ${result.left}'); - } else { - console.wtf( - 'removed backup: ${result.right.data} - ${StorageService.to.backups.first.name}', - ); - } - } + // TODO: temporary + // // REMOVE OLDEST BACKUP IF NECESSARY + // if (StorageService.to.backups.length >= limits.backups) { + // final result = await StorageService.to.remove( + // StorageService.to.backups.first.key, + // ); + // // abort backup if error in removing oldest backup + // if (result.isLeft) { + // return console.error('error removing last backup: ${result.left}'); + // } else { + // console.wtf( + // 'removed backup: ${result.right.data} - ${StorageService.to.backups.first.name}', + // ); + // } + // } // DO THE ACTUAL BACKUP - final presignResult = await SupabaseFunctionsService.to.presignUrl( + final presignResult = await AppSupabaseFunctionsService.to.presignUrl( object: '$kDirBackups/${DateTime.now().millisecondsSinceEpoch}-$kVaultFileName', method: 'PUT', @@ -198,7 +199,7 @@ class SyncService extends GetxService with ConsoleMixin { // BACKUP backup(kVaultFileName, encryptedBytes); - final presignResult = await SupabaseFunctionsService.to.presignUrl( + final presignResult = await AppSupabaseFunctionsService.to.presignUrl( object: kVaultFileName, method: 'PUT', ); @@ -259,7 +260,7 @@ class SyncService extends GetxService with ConsoleMixin { cipherKey: base64Decode(cipherKeyResult.right), ); - final presignResult = await SupabaseFunctionsService.to.presignUrl( + final presignResult = await AppSupabaseFunctionsService.to.presignUrl( object: '$kDirShared/${sharedVault.docId}.$kVaultExtension', method: 'PUT', ); @@ -283,7 +284,7 @@ class SyncService extends GetxService with ConsoleMixin { Future> purge() async { if (!isReady) return const Left('offline'); - final result = await SupabaseFunctionsService.to.deleteDirectory(''); + final result = await AppSupabaseFunctionsService.to.deleteDirectory(''); if (result.isLeft) return Left(result.left); return const Right(true); } @@ -307,7 +308,7 @@ class SyncService extends GetxService with ConsoleMixin { // exclude permanently flagged deleted items final deletedIds = - "${Persistence.to.deletedGroupIds},${vault.persistence['deleted-group-ids']}"; + "${AppPersistence.to.deletedGroupIds},${vault.persistence['deleted-group-ids']}"; merged.removeWhere((e) => deletedIds.contains(e.id)); // leave only the most updated items @@ -338,7 +339,7 @@ class SyncService extends GetxService with ConsoleMixin { // exclude permanently flagged deleted items final deletedIds = - "${Persistence.to.deletedCategoryIds},${vault.persistence['deleted-category-ids']}"; + "${AppPersistence.to.deletedCategoryIds},${vault.persistence['deleted-category-ids']}"; merged.removeWhere((e) => deletedIds.contains(e.id)); // leave only the most updated items @@ -371,7 +372,7 @@ class SyncService extends GetxService with ConsoleMixin { // exclude permanently flagged deleted items final deletedIds = - "${Persistence.to.deletedItemIds},${vault.persistence['deleted-item-ids']}"; + "${AppPersistence.to.deletedItemIds},${vault.persistence['deleted-item-ids']}"; merged.removeWhere((e) => deletedIds.contains(e.identifier)); // leave only the most updated items final newList = []; diff --git a/lib/features/general/appbar_leading.widget.dart b/lib/features/general/appbar_leading.widget.dart deleted file mode 100644 index 576d50a4..00000000 --- a/lib/features/general/appbar_leading.widget.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:iconsax/iconsax.dart'; -import 'package:line_icons/line_icons.dart'; - -import '../../core/utils/utils.dart'; - -class AppBarLeadingButton extends StatelessWidget { - final Function()? action; - - const AppBarLeadingButton({Key? key, this.action}) : super(key: key); - - @override - Widget build(BuildContext context) { - return IconButton( - onPressed: action ?? Get.back, - icon: Icon( - Utils.isSmallScreen ? Iconsax.arrow_left_2 : LineIcons.times, - ), - ); - } -} diff --git a/lib/features/general/busy_indicator.widget.dart b/lib/features/general/busy_indicator.widget.dart deleted file mode 100644 index 39979c6b..00000000 --- a/lib/features/general/busy_indicator.widget.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; - -class BusyIndicator extends StatelessWidget { - final String message; - final double size; - final EdgeInsets padding; - final Color? color; - - const BusyIndicator({ - Key? key, - this.message = '', - this.size = 35, - this.padding = const EdgeInsets.all(20), - this.color, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Center( - child: Padding( - padding: padding, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - height: size, - width: size, - child: CircularProgressIndicator(color: color), - ), - if (message.isNotEmpty) ...[ - const SizedBox(height: 25), - Text( - message, - style: const TextStyle(color: Colors.grey), - textAlign: TextAlign.center, - ) - ] - ], - ), - ), - ); - } -} diff --git a/lib/features/general/gradient.widget.dart b/lib/features/general/gradient.widget.dart deleted file mode 100644 index 1b7f54a1..00000000 --- a/lib/features/general/gradient.widget.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; - -class GradientWidget extends StatelessWidget { - final Widget child; - final Gradient gradient; - - const GradientWidget({ - Key? key, - required this.child, - required this.gradient, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return ShaderMask( - blendMode: BlendMode.srcIn, - shaderCallback: (bounds) => gradient.createShader( - Rect.fromLTWH(0, 0, bounds.width, bounds.height), - ), - child: child, - ); - } -} diff --git a/lib/features/general/pro.widget.dart b/lib/features/general/pro.widget.dart deleted file mode 100644 index 70903724..00000000 --- a/lib/features/general/pro.widget.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../core/firebase/config/config.service.dart'; -import '../../core/utils/globals.dart'; - -class ProText extends StatelessWidget { - final double? size; - const ProText({Key? key, this.size}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - ConfigService.to.appName, - style: TextStyle(fontSize: size, fontWeight: FontWeight.bold), - ), - const SizedBox(width: 4), - Text( - 'Pro', - style: TextStyle( - fontSize: size, - fontWeight: FontWeight.bold, - color: proColor, - ), - ), - ], - ); - } -} diff --git a/lib/features/general/remote_image.widget.dart b/lib/features/general/remote_image.widget.dart deleted file mode 100644 index 7b0273b7..00000000 --- a/lib/features/general/remote_image.widget.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; - -import '../../resources/resources.dart'; - -class RemoteImage extends StatelessWidget { - final String url; - final double? width, height; - final Alignment alignment; - final Image? placeholder; - - const RemoteImage({ - Key? key, - required this.url, - this.width, - this.height, - this.alignment = Alignment.center, - this.placeholder, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return CachedNetworkImage( - imageUrl: url, - width: width, - height: height, - placeholder: (context, _) => - placeholder ?? - Image.asset( - Images.placeholder, - fit: BoxFit.cover, - width: width, - height: height, - ), - alignment: alignment, - errorWidget: (_, str, dyn) => Placeholder( - fallbackHeight: height ?? 50, - fallbackWidth: width ?? 50, - ), - ); - } -} - -class DiceBearAvatar extends StatelessWidget { - final String sprites; - final String seed; - final double size; - - const DiceBearAvatar({ - Key? key, - this.sprites = 'big-smile', - required this.seed, - this.size = 50, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - final placeholder = ClipRRect( - borderRadius: BorderRadius.circular(size), - child: Image.asset( - Images.placeholder, - fit: BoxFit.cover, - height: size, - width: size, - ), - ); - - return SvgPicture.network( - 'https://avatars.dicebear.com/api/$sprites/$seed.svg?size=$size&radius=50', - height: size, - width: size, - placeholderBuilder: (_) => placeholder, - ); - } -} diff --git a/lib/features/general/unknown.screen.dart b/lib/features/general/unknown.screen.dart deleted file mode 100644 index 4db158c8..00000000 --- a/lib/features/general/unknown.screen.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; - -class UnknownScreen extends StatelessWidget { - const UnknownScreen({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: AppBar( - title: const Text('Unknown Route'), - centerTitle: false, - ), - body: const Center( - child: Text("You're in the wrong route."), - ), - ), - ); - } -} diff --git a/lib/features/general/version.widget.dart b/lib/features/general/version.widget.dart deleted file mode 100644 index 7436f483..00000000 --- a/lib/features/general/version.widget.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../core/utils/globals.dart'; - -class VersionText extends StatelessWidget { - const VersionText({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 30, - child: Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: const EdgeInsets.only(bottom: 15, right: 15), - child: Text( - Globals.metadata?.app.formattedVersion ?? 'Unknown Version', - style: const TextStyle(color: Colors.grey, fontSize: 10), - ), - ), - ), - ); - } -} diff --git a/lib/features/groups/groups.screen.dart b/lib/features/groups/groups.screen.dart index 9609870a..01e65d09 100644 --- a/lib/features/groups/groups.screen.dart +++ b/lib/features/groups/groups.screen.dart @@ -1,21 +1,21 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; +import 'package:app_core/widgets/remote_image.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; import 'package:liso/features/drawer/drawer_widget.controller.dart'; -import 'package:liso/features/general/remote_image.widget.dart'; import 'package:liso/features/items/items.controller.dart'; import 'package:liso/features/items/items.service.dart'; import 'package:liso/features/main/main_screen.controller.dart'; import '../../core/hive/models/item.hive.dart'; import '../../core/persistence/persistence.dart'; -import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../general/appbar_leading.widget.dart'; -import '../general/busy_indicator.widget.dart'; import '../general/centered_placeholder.widget.dart'; import '../menu/menu.button.dart'; import '../menu/menu.item.dart'; @@ -42,7 +42,7 @@ class GroupsScreen extends StatelessWidget with ConsoleMixin { group.metadata = await group.metadata!.getUpdated(); group.deleted = true; await group.save(); - Persistence.to.changes.val++; + AppPersistence.to.changes.val++; groupsController.load(); MainScreenController.to.load(); console.info('deleted'); @@ -58,7 +58,7 @@ class GroupsScreen extends StatelessWidget with ConsoleMixin { Get.dialog(AlertDialog( title: const Text('Delete Custom Vault'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : SizedBox( width: 450, diff --git a/lib/features/groups/groups_screen.controller.dart b/lib/features/groups/groups_screen.controller.dart index c84baa11..8e747f7a 100644 --- a/lib/features/groups/groups_screen.controller.dart +++ b/lib/features/groups/groups_screen.controller.dart @@ -1,3 +1,6 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/utils/ui_utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -5,12 +8,7 @@ import 'package:liso/core/hive/models/metadata/metadata.hive.dart'; import 'package:uuid/uuid.dart'; import '../../core/hive/models/group.hive.dart'; -import '../../core/notifications/notifications.manager.dart'; import '../../core/persistence/persistence.dart'; -import '../../core/utils/ui_utils.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../pro/pro.controller.dart'; import 'groups.controller.dart'; import 'groups.service.dart'; @@ -51,7 +49,7 @@ class GroupsScreenController extends GetxController with ConsoleMixin { void showForm() async { void done() { - Persistence.to.changes.val++; + AppPersistence.to.changes.val++; GroupsController.to.load(); // clear fields nameController.clear(); @@ -81,17 +79,18 @@ class GroupsScreenController extends GetxController with ConsoleMixin { ); } - if (GroupsController.to.data.length >= - ProController.to.limits.customVaults) { - return Utils.adaptiveRouteOpen( - name: Routes.upgrade, - parameters: { - 'title': 'Custom Vaults', - 'body': - 'Maximum custom vaults of ${ProController.to.limits.customVaults} limit reached. Upgrade to Pro to unlock unlimited custom vaults feature.', - }, - ); - } + // TODO: temporary + // if (GroupsController.to.data.length >= + // limits.customVaults) { + // return Utils.adaptiveRouteOpen( + // name: Routes.upgrade, + // parameters: { + // 'title': 'Custom Vaults', + // 'body': + // 'Maximum custom vaults of ${limits.customVaults} limit reached. Upgrade to Pro to unlock unlimited custom vaults feature.', + // }, + // ); + // } await GroupsService.to.box!.add(HiveLisoGroup( id: const Uuid().v4(), @@ -148,8 +147,7 @@ class GroupsScreenController extends GetxController with ConsoleMixin { Get.dialog(AlertDialog( title: Text('${createMode ? 'new' : 'update'}_custom_vault'.tr), - content: - Utils.isSmallScreen ? content : SizedBox(width: 450, child: content), + content: isSmallScreen ? content : SizedBox(width: 450, child: content), actions: [ TextButton( onPressed: Get.back, diff --git a/lib/features/import/import.screen.dart b/lib/features/import/import.screen.dart index 429f9c9a..f242910c 100644 --- a/lib/features/import/import.screen.dart +++ b/lib/features/import/import.screen.dart @@ -1,19 +1,20 @@ +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:liso/core/hive/models/group.hive.dart'; -import 'package:liso/features/general/busy_indicator.widget.dart'; import 'package:liso/features/general/widget_refresher.widget.dart'; import 'package:liso/features/groups/groups.controller.dart'; -import '../../core/firebase/config/config.service.dart'; import '../../core/utils/globals.dart'; import '../../core/utils/styles.dart'; -import '../../core/utils/utils.dart'; import '../app/routes.dart'; -import '../general/appbar_leading.widget.dart'; import 'import_screen.controller.dart'; class ImportScreen extends StatelessWidget with ConsoleMixin { @@ -62,7 +63,7 @@ class ImportScreen extends StatelessWidget with ConsoleMixin { dropdownRefresher.reload(); return await Utils.adaptiveRouteOpen( - name: Routes.vaults, + name: AppRoutes.vaults, ); } @@ -130,6 +131,16 @@ class ImportScreen extends StatelessWidget with ConsoleMixin { ), ], ), + const SizedBox(height: 10), + Obx( + () => CheckboxListTile( + title: Text( + 'Automatically tag items with (${controller.sourceFormat.value.id})', + ), + value: controller.autoTag.value, + onChanged: (value) => controller.autoTag.value = value!, + ), + ), const SizedBox(height: 20), SizedBox( width: 200, diff --git a/lib/features/import/import_screen.controller.dart b/lib/features/import/import_screen.controller.dart index c9b3a512..7cee0ee7 100644 --- a/lib/features/import/import_screen.controller.dart +++ b/lib/features/import/import_screen.controller.dart @@ -1,5 +1,7 @@ import 'dart:io'; +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/ui_utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; @@ -13,7 +15,6 @@ import 'package:liso/features/main/main_screen.controller.dart'; import 'package:path/path.dart'; import '../../core/utils/globals.dart'; -import '../../core/utils/ui_utils.dart'; import '../groups/groups.controller.dart'; import 'importers/apple.importer.dart'; import 'importers/firefox.importer.dart'; @@ -58,6 +59,7 @@ class ImportScreenController extends GetxController // PROPERTIES final busy = false.obs; + final autoTag = true.obs; // GETTERS @@ -215,7 +217,7 @@ class ImportScreenController extends GetxController if (GetPlatform.isAndroid) FilePicker.platform.clearTemporaryFiles(); change(null, status: RxStatus.loading()); - Globals.timeLockEnabled = false; // disable + timeLockEnabled = false; // disable FilePickerResult? result; try { @@ -224,7 +226,7 @@ class ImportScreenController extends GetxController allowedExtensions: kAllowedExtensions, ); } catch (e) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable console.error('FilePicker error: $e'); return; } @@ -232,7 +234,7 @@ class ImportScreenController extends GetxController change(null, status: RxStatus.success()); if (result == null || result.files.isEmpty) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable console.warning("canceled file picker"); return; } diff --git a/lib/features/import/importers/apple.importer.dart b/lib/features/import/importers/apple.importer.dart index 1a811fed..74e13963 100644 --- a/lib/features/import/importers/apple.importer.dart +++ b/lib/features/import/importers/apple.importer.dart @@ -1,3 +1,5 @@ +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/utils/ui_utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:csv/csv.dart'; import 'package:flutter/foundation.dart'; @@ -5,9 +7,7 @@ import 'package:uuid/uuid.dart'; import '../../../core/hive/models/item.hive.dart'; import '../../../core/hive/models/metadata/metadata.hive.dart'; -import '../../../core/notifications/notifications.manager.dart'; import '../../../core/utils/globals.dart'; -import '../../../core/utils/ui_utils.dart'; import '../../categories/categories.controller.dart'; import '../../groups/groups.controller.dart'; import '../../items/items.service.dart'; @@ -28,6 +28,7 @@ class AppleImporter { static Future importCSV(String csv) async { final sourceFormat = ImportScreenController.to.sourceFormat.value; + final autoTag = ImportScreenController.to.autoTag.value; const csvConverter = CsvToListConverter(); final values = csvConverter.convert(csv, eol: '\n'); final columns = values.first.sublist(0, validColumns.length); @@ -50,12 +51,12 @@ class AppleImporter { final items = values.map( (row) async { - final name = row[0]; - final url = row[1]; - final username = row[2]; - final password = row[3]; - final notes = row[4]; - final otpAuth = row[5]; + final name = row[0].toString(); + final url = row[1].toString(); + final username = row[2].toString(); + final password = row[3].toString(); + final notes = row[4].toString(); + final otpAuth = row[5].toString(); // group if (groupId == kSmartGroupId) { @@ -101,7 +102,7 @@ class AppleImporter { // protected: reprompt == '1', // favorite: favorite == '1', metadata: metadata, - tags: [sourceFormat.id.toLowerCase()], + tags: autoTag ? [sourceFormat.id.toLowerCase()] : [], ); }, ); diff --git a/lib/features/import/importers/bitwarden.importer.dart b/lib/features/import/importers/bitwarden.importer.dart index 9143945c..67c7c7dc 100644 --- a/lib/features/import/importers/bitwarden.importer.dart +++ b/lib/features/import/importers/bitwarden.importer.dart @@ -1,3 +1,5 @@ +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/utils/ui_utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:csv/csv.dart'; import 'package:flutter/foundation.dart'; @@ -7,9 +9,7 @@ import '../../../core/hive/models/field.hive.dart'; import '../../../core/hive/models/group.hive.dart'; import '../../../core/hive/models/item.hive.dart'; import '../../../core/hive/models/metadata/metadata.hive.dart'; -import '../../../core/notifications/notifications.manager.dart'; import '../../../core/utils/globals.dart'; -import '../../../core/utils/ui_utils.dart'; import '../../categories/categories.controller.dart'; import '../../groups/groups.controller.dart'; import '../../groups/groups.service.dart'; @@ -36,6 +36,7 @@ class BitwardenImporter { static Future importCSV(String csv) async { final sourceFormat = ImportScreenController.to.sourceFormat.value; + final autoTag = ImportScreenController.to.autoTag.value; const csvConverter = CsvToListConverter(); var values = csvConverter.convert(csv, eol: '\n'); final columns = values.first.map((e) => e.trim()).toList(); @@ -60,17 +61,17 @@ class BitwardenImporter { final items = values.map( (row) async { - final folder = row[0]; - final favorite = row[1]; - final type = row[2]; - final name = row[3]; - final notes = row[4]; - final String bitwardenFields = row[5]; - final reprompt = row[6]; - final url = row[7]; - final username = row[8]; - final password = row[9]; - final totp = row[10]; + final folder = row[0].toString(); + final favorite = row[1] == 1; + final type = row[2].toString(); + final name = row[3].toString(); + final notes = row[4].toString(); + final bitwardenFields = row[5].toString(); + final reprompt = row[6] == 1; + final url = row[7].toString(); + final username = row[8].toString(); + final password = row[9].toString(); + final totp = row[10].toString(); // generate group if doesn't exist if (destinationGroupId == kSmartGroupId) { @@ -102,11 +103,7 @@ class BitwardenImporter { // category var itemCategory = LisoItemCategory.login; - - if (type == 'note') { - itemCategory = LisoItemCategory.note; - } - + if (type == 'note') itemCategory = LisoItemCategory.note; // holder for custom field rows Key:Value var customFieldRows = bitwardenFields.split('\n'); @@ -166,10 +163,10 @@ class BitwardenImporter { // iconUrl: iconUrl.value, uris: url.isNotEmpty ? [url] : [], // appIds: appIds, // TODO: obtain app id from app uri - protected: reprompt == 1, - favorite: favorite == 1, + protected: reprompt, + favorite: favorite, metadata: metadata, - tags: [sourceFormat.id.toLowerCase()], + tags: autoTag ? [sourceFormat.id.toLowerCase()] : [], ); }, ); diff --git a/lib/features/import/importers/chrome.importer.dart b/lib/features/import/importers/chrome.importer.dart index 6f958ea3..cebb6e72 100644 --- a/lib/features/import/importers/chrome.importer.dart +++ b/lib/features/import/importers/chrome.importer.dart @@ -1,7 +1,8 @@ +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/utils/ui_utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:csv/csv.dart'; import 'package:flutter/foundation.dart'; -import 'package:liso/core/notifications/notifications.manager.dart'; import 'package:liso/core/utils/globals.dart'; import 'package:liso/features/items/items.service.dart'; import 'package:liso/features/main/main_screen.controller.dart'; @@ -9,7 +10,6 @@ import 'package:uuid/uuid.dart'; import '../../../core/hive/models/item.hive.dart'; import '../../../core/hive/models/metadata/metadata.hive.dart'; -import '../../../core/utils/ui_utils.dart'; import '../../categories/categories.controller.dart'; import '../../groups/groups.controller.dart'; import '../import_screen.controller.dart'; @@ -26,6 +26,7 @@ class ChromeImporter { static Future importCSV(String csv) async { final sourceFormat = ImportScreenController.to.sourceFormat.value; + final autoTag = ImportScreenController.to.autoTag.value; const csvConverter = CsvToListConverter(); var values = csvConverter.convert(csv, eol: '\n'); final columns = values.first.map((e) => e.trim()).toList(); @@ -50,10 +51,10 @@ class ChromeImporter { final items = values.map( (row) async { - final name = row[0]; - final url = row[1]; - final username = row[2]; - final password = row[3]; + final name = row[0].toString(); + final url = row[1].toString(); + final username = row[2].toString(); + final password = row[3].toString(); // group if (destinationGroupId == kSmartGroupId) { @@ -93,7 +94,7 @@ class ChromeImporter { // protected: reprompt == '1', // favorite: favorite == '1', metadata: metadata, - tags: [sourceFormat.id.toLowerCase()], + tags: autoTag ? [sourceFormat.id.toLowerCase()] : [], ); }, ); diff --git a/lib/features/import/importers/firefox.importer.dart b/lib/features/import/importers/firefox.importer.dart index 3e8ff3ee..5c400036 100644 --- a/lib/features/import/importers/firefox.importer.dart +++ b/lib/features/import/importers/firefox.importer.dart @@ -1,7 +1,8 @@ +import 'package:app_core/utils/ui_utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:csv/csv.dart'; import 'package:flutter/foundation.dart'; -import 'package:liso/core/notifications/notifications.manager.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; import 'package:liso/core/utils/globals.dart'; import 'package:liso/features/items/items.service.dart'; import 'package:liso/features/main/main_screen.controller.dart'; @@ -9,7 +10,6 @@ import 'package:uuid/uuid.dart'; import '../../../core/hive/models/item.hive.dart'; import '../../../core/hive/models/metadata/metadata.hive.dart'; -import '../../../core/utils/ui_utils.dart'; import '../../categories/categories.controller.dart'; import '../../groups/groups.controller.dart'; import '../import_screen.controller.dart'; @@ -31,6 +31,7 @@ class FirefoxImporter { static Future importCSV(String csv) async { final sourceFormat = ImportScreenController.to.sourceFormat.value; + final autoTag = ImportScreenController.to.autoTag.value; const csvConverter = CsvToListConverter(); var values = csvConverter.convert(csv); final columns = values.first.map((e) => e.trim()).toList(); @@ -55,9 +56,9 @@ class FirefoxImporter { final items = values.map( (row) async { - final url = row[0]; - final username = row[1]; - final password = row[2]; + final url = row[0].toString(); + final username = row[1].toString(); + final password = row[2].toString(); // group if (destinationGroupId == kSmartGroupId) { @@ -95,7 +96,7 @@ class FirefoxImporter { // protected: reprompt == '1', // favorite: favorite == '1', metadata: metadata, - tags: [sourceFormat.id.toLowerCase()], + tags: autoTag ? [sourceFormat.id.toLowerCase()] : [], ); }, ); diff --git a/lib/features/import/importers/lastpass.importer.dart b/lib/features/import/importers/lastpass.importer.dart index 5648b89d..fc3baba9 100644 --- a/lib/features/import/importers/lastpass.importer.dart +++ b/lib/features/import/importers/lastpass.importer.dart @@ -1,3 +1,5 @@ +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/utils/ui_utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:csv/csv.dart'; import 'package:flutter/foundation.dart'; @@ -7,9 +9,7 @@ import 'package:uuid/uuid.dart'; import '../../../core/hive/models/group.hive.dart'; import '../../../core/hive/models/item.hive.dart'; import '../../../core/hive/models/metadata/metadata.hive.dart'; -import '../../../core/notifications/notifications.manager.dart'; import '../../../core/utils/globals.dart'; -import '../../../core/utils/ui_utils.dart'; import '../../categories/categories.controller.dart'; import '../../groups/groups.controller.dart'; import '../../groups/groups.service.dart'; @@ -33,6 +33,7 @@ class LastPassImporter { static Future importCSV(String csv) async { final sourceFormat = ImportScreenController.to.sourceFormat.value; + final autoTag = ImportScreenController.to.autoTag.value; const csvConverter = CsvToListConverter(); var values = csvConverter.convert(csv, eol: '\n'); final columns = values.first.map((e) => e.trim()).toList(); @@ -57,14 +58,14 @@ class LastPassImporter { final items = values.map( (row) async { - var url = row[0]; - final username = row[1]; - final password = row[2]; - final totp = row[3]; - final extra = row[4]; // TODO: work on custom fields - final name = row[5]; - var grouping = row[6]; - final favorite = row[7]; + var url = row[0].toString(); + final username = row[1].toString(); + final password = row[2].toString(); + final totp = row[3].toString(); + final extra = row[4].toString(); // TODO: work on custom fields + final name = row[5].toString(); + var grouping = row[6].toString(); + final favorite = row[7] == 1; final hasCustomFields = extra.contains('NoteType:'); final isLastPassUri = url.contains('http://sn') || url.contains('http://xn'); @@ -190,7 +191,7 @@ class LastPassImporter { // protected: reprompt == 1, favorite: favorite == 1, metadata: metadata, - tags: [sourceFormat.id.toLowerCase()], + tags: autoTag ? [sourceFormat.id.toLowerCase()] : [], ); }, ); diff --git a/lib/features/import/importers/nordpass.importer.dart b/lib/features/import/importers/nordpass.importer.dart index bd1b0468..fb896eb5 100644 --- a/lib/features/import/importers/nordpass.importer.dart +++ b/lib/features/import/importers/nordpass.importer.dart @@ -1,7 +1,8 @@ +import 'package:app_core/utils/ui_utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:csv/csv.dart'; import 'package:flutter/foundation.dart'; -import 'package:liso/core/notifications/notifications.manager.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; import 'package:liso/core/utils/globals.dart'; import 'package:liso/features/items/items.service.dart'; import 'package:liso/features/main/main_screen.controller.dart'; @@ -9,7 +10,6 @@ import 'package:uuid/uuid.dart'; import '../../../core/hive/models/item.hive.dart'; import '../../../core/hive/models/metadata/metadata.hive.dart'; -import '../../../core/utils/ui_utils.dart'; import '../../categories/categories.controller.dart'; import '../../groups/groups.controller.dart'; import '../import_screen.controller.dart'; @@ -41,6 +41,7 @@ class NordPassImporter { static Future importCSV(String csv) async { final sourceFormat = ImportScreenController.to.sourceFormat.value; + final autoTag = ImportScreenController.to.autoTag.value; const csvConverter = CsvToListConverter(); var values = csvConverter.convert(csv); final columns = values.first.map((e) => e.trim()).toList(); @@ -66,9 +67,9 @@ class NordPassImporter { final items = values.map( (row) async { // final name = row[0]; - final url = row[1]; - final username = row[2]; - final password = row[3]; + final url = row[1].toString(); + final username = row[2].toString(); + final password = row[3].toString(); // final note = row[4]; // final cardHolderName = row[5]; // final cardNumber = row[6]; @@ -121,7 +122,7 @@ class NordPassImporter { // protected: reprompt == '1', // favorite: favorite == '1', metadata: metadata, - tags: [sourceFormat.id.toLowerCase()], + tags: autoTag ? [sourceFormat.id.toLowerCase()] : [], ); }, ); diff --git a/lib/features/items/item.screen.dart b/lib/features/items/item.screen.dart index 876a5b55..22625a2a 100644 --- a/lib/features/items/item.screen.dart +++ b/lib/features/items/item.screen.dart @@ -1,3 +1,7 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; +import 'package:app_core/widgets/remote_image.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -16,8 +20,6 @@ import '../../core/hive/models/group.hive.dart'; import '../../core/utils/globals.dart'; import '../../core/utils/utils.dart'; import '../app/routes.dart'; -import '../general/busy_indicator.widget.dart'; -import '../general/remote_image.widget.dart'; import 'item_screen.controller.dart'; class ItemScreen extends StatelessWidget with ConsoleMixin { @@ -38,7 +40,7 @@ class ItemScreen extends StatelessWidget with ConsoleMixin { controller.menuItemsChangeIcon, enabled: controller.editMode.value, child: controller.iconUrl().isEmpty - ? Utils.categoryIcon(controller.category.value) + ? AppUtils.categoryIcon(controller.category.value) : RemoteImage( url: controller.iconUrl(), width: 30, @@ -218,7 +220,7 @@ class ItemScreen extends StatelessWidget with ConsoleMixin { ), Obx( () => Visibility( - visible: Persistence.to.canShare && + visible: AppPersistence.to.canShare && ((!controller.editMode.value && controller.sharedVaultChips.isNotEmpty) || (controller.editMode.value && @@ -268,7 +270,7 @@ class ItemScreen extends StatelessWidget with ConsoleMixin { // hack to refresh dropdown text dropdownRefresher.reload(); return await Utils.adaptiveRouteOpen( - name: Routes.vaults, + name: AppRoutes.vaults, ); } @@ -368,11 +370,11 @@ class ItemScreen extends StatelessWidget with ConsoleMixin { if (await controller.canPop()) Get.back(); }, icon: Icon( - Utils.isSmallScreen ? Iconsax.arrow_left_2 : LineIcons.times, + isSmallScreen ? Iconsax.arrow_left_2 : LineIcons.times, ), ), actions: [ - if (!Utils.isSmallScreen) ...[ + if (!isSmallScreen) ...[ Obx( () => Visibility( visible: controller.editMode.value, @@ -433,7 +435,7 @@ class ItemScreen extends StatelessWidget with ConsoleMixin { final scaffold = Scaffold( appBar: appBar, - floatingActionButton: Utils.isSmallScreen ? fab : null, + floatingActionButton: isSmallScreen ? fab : null, // grey disabled fields body: Theme( data: Get.theme.copyWith(disabledColor: Colors.grey), diff --git a/lib/features/items/item.tile.dart b/lib/features/items/item.tile.dart index 65701c11..d2b4c033 100644 --- a/lib/features/items/item.tile.dart +++ b/lib/features/items/item.tile.dart @@ -1,3 +1,7 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/remote_image.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:flutter_swipe_action_cell/core/cell.dart'; @@ -18,10 +22,9 @@ import '../../core/utils/globals.dart'; import '../../core/utils/utils.dart'; import '../app/routes.dart'; import '../general/custom_chip.widget.dart'; -import '../general/remote_image.widget.dart'; import '../json_viewer/json_viewer.screen.dart'; -import '../menu/menu.sheet.dart'; import '../menu/menu.item.dart'; +import '../menu/menu.sheet.dart'; import '../shared_vaults/model/shared_vault.model.dart'; class ItemTile extends StatelessWidget with ConsoleMixin { @@ -37,7 +40,7 @@ class ItemTile extends StatelessWidget with ConsoleMixin { }) : super(key: key); void _open() async { - if (Globals.isAutofill) { + if (isAutofill) { return LisoAutofillService.to.fill(item); } @@ -54,14 +57,14 @@ class ItemTile extends StatelessWidget with ConsoleMixin { 'joinedVaultItem': joinedVaultItem.toString(), }; - Utils.adaptiveRouteOpen(name: Routes.item, parameters: parameters); + Utils.adaptiveRouteOpen(name: AppRoutes.item, parameters: parameters); } void _favorite() async { item.favorite = !item.favorite; item.metadata = await item.metadata.getUpdated(); await item.save(); - Persistence.to.changes.val++; + AppPersistence.to.changes.val++; ItemsController.to.load(); } @@ -71,7 +74,7 @@ class ItemTile extends StatelessWidget with ConsoleMixin { copy.title = '${copy.title} Copy'; copy.metadata = await copy.metadata.getUpdated(); await ItemsService.to.box!.add(copy); - Persistence.to.changes.val++; + AppPersistence.to.changes.val++; ItemsController.to.load(); } @@ -80,7 +83,7 @@ class ItemTile extends StatelessWidget with ConsoleMixin { item.deleted = false; item.metadata = await item.metadata.getUpdated(); await item.save(); - Persistence.to.changes.val++; + AppPersistence.to.changes.val++; ItemsController.to.load(); } @@ -88,7 +91,7 @@ class ItemTile extends StatelessWidget with ConsoleMixin { item.trashed = true; item.metadata = await item.metadata.getUpdated(); await item.save(); - Persistence.to.changes.val++; + AppPersistence.to.changes.val++; ItemsController.to.load(); } @@ -96,13 +99,13 @@ class ItemTile extends StatelessWidget with ConsoleMixin { item.deleted = true; item.metadata = await item.metadata.getUpdated(); await item.save(); - Persistence.to.changes.val++; + AppPersistence.to.changes.val++; ItemsController.to.load(); } void _permaDelete() async { - Persistence.to.addToDeletedItems(item.identifier); - Persistence.to.changes.val++; + AppPersistence.to.addToDeletedItems(item.identifier); + AppPersistence.to.changes.val++; await item.delete(); ItemsController.to.load(); } @@ -114,7 +117,7 @@ class ItemTile extends StatelessWidget with ConsoleMixin { return Get.dialog(AlertDialog( title: Text('delete'.tr), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : const SizedBox(width: 450, child: dialogContent), actions: [ @@ -154,7 +157,7 @@ class ItemTile extends StatelessWidget with ConsoleMixin { @override Widget build(BuildContext context) { - final isLargeScreen = !Utils.isSmallScreen; + final isLargeScreen = !isSmallScreen; final menuItems = [ if (!joinedVaultItem) ...[ @@ -290,7 +293,7 @@ class ItemTile extends StatelessWidget with ConsoleMixin { width: 35, alignment: Alignment.centerLeft, ) - : Utils.categoryIcon(item.category, color: themeColor); + : AppUtils.categoryIcon(item.category, color: themeColor); var tile = ListTile( selected: item.deleted, @@ -320,19 +323,19 @@ class ItemTile extends StatelessWidget with ConsoleMixin { bottomSubTitle, ], ), - trailing: Globals.isAutofill + trailing: isAutofill ? null : ContextMenuButton( menuItems, child: const Icon(LineIcons.verticalEllipsis), ), - onLongPress: isLargeScreen || Globals.isAutofill + onLongPress: isLargeScreen || isAutofill ? null : () => ContextMenuSheet(menuItems).show(), onTap: _open, ); - if (isLargeScreen || Globals.isAutofill) return tile; + if (isLargeScreen || isAutofill) return tile; // if small screen, add swipe actions final leadingActions = [ diff --git a/lib/features/items/item_screen.controller.dart b/lib/features/items/item_screen.controller.dart index a3336690..23313e74 100644 --- a/lib/features/items/item_screen.controller.dart +++ b/lib/features/items/item_screen.controller.dart @@ -1,6 +1,9 @@ import 'dart:async'; import 'dart:convert'; +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -31,7 +34,6 @@ import '../drawer/drawer_widget.controller.dart'; import '../json_viewer/json_viewer.screen.dart'; import '../menu/menu.button.dart'; import '../menu/menu.item.dart'; -import '../pro/pro.controller.dart'; import '../shared_vaults/shared_vault.controller.dart'; import 'items.controller.dart'; import 'items.service.dart'; @@ -945,14 +947,14 @@ class ItemScreenController extends GetxController decoration: InputDecoration( labelText: field.data.label, hintText: field.data.hint, - helperText: ProController.to.limits.passwordHealth && + helperText: limits.passwordHealth && isPasswordField && obscured && field.data.value!.isNotEmpty - ? Utils.strengthName(strength).toUpperCase() + ? AppUtils.strengthName(strength).toUpperCase() : null, helperStyle: TextStyle( - color: Utils.strengthColor(strength), + color: AppUtils.strengthColor(strength), fontWeight: FontWeight.bold, ), ), @@ -993,29 +995,31 @@ class ItemScreenController extends GetxController void add() async { if (!formKey.currentState!.validate()) return; if (!editMode.value) return console.error('not in edit mode'); - // items limit - if (ItemsController.to.itemLimitReached) { - return Utils.adaptiveRouteOpen( - name: Routes.upgrade, - parameters: { - 'title': 'Items Limit Reached', - 'body': - 'Maximum items of ${ProController.to.limits.items} limit reached. Upgrade to Pro to unlock unlimited items features', - }, - ); - } - - // protected items limit - if (protected.value && ItemsController.to.protectedItemLimitReached) { - return Utils.adaptiveRouteOpen( - name: Routes.upgrade, - parameters: { - 'title': 'Protected Items', - 'body': - 'Maximum protected items of ${ProController.to.limits.protectedItems} limit reached. Upgrade to Pro to unlock unlimited protected items feature.', - }, - ); - } + // TODO: temporary + // // items limit + // if (ItemsController.to.itemLimitReached) { + // return Utils.adaptiveRouteOpen( + // name: Routes.upgrade, + // parameters: { + // 'title': 'Items Limit Reached', + // 'body': + // 'Maximum items of ${limits.items} limit reached. Upgrade to Pro to unlock unlimited items features', + // }, + // ); + // } + + // TODO: temporary + // // protected items limit + // if (protected.value && ItemsController.to.protectedItemLimitReached) { + // return Utils.adaptiveRouteOpen( + // name: Routes.upgrade, + // parameters: { + // 'title': 'Protected Items', + // 'body': + // 'Maximum protected items of ${limits.protectedItems} limit reached. Upgrade to Pro to unlock unlimited protected items feature.', + // }, + // ); + // } final newItem = HiveLisoItem( identifier: const Uuid().v4(), @@ -1035,12 +1039,12 @@ class ItemScreenController extends GetxController ); await ItemsService.to.box!.add(newItem); - Persistence.to.changes.val++; + AppPersistence.to.changes.val++; ItemsController.to.load(); DrawerMenuController.to.update(); Get.back(); - if (Globals.isAutofill) { + if (isAutofill) { AutofillService().onSaveComplete(); } } @@ -1066,28 +1070,29 @@ class ItemScreenController extends GetxController console.wtf('appIds: $appIds'); - Persistence.to.changes.val++; + AppPersistence.to.changes.val++; ItemsController.to.load(); DrawerMenuController.to.update(); Get.back(); } void onProtectedChanged(bool? value) { - // protected items limit - if (value! && ItemsController.to.protectedItemLimitReached) { - Utils.adaptiveRouteOpen( - name: Routes.upgrade, - parameters: { - 'title': 'Protected Items', - 'body': - 'Maximum protected items of ${ProController.to.limits.protectedItems} limit reached. Upgrade to Pro to unlock unlimited protected items feature.', - }, - ); - - return; - } - - protected.value = value; + // TODO: temporary + // // protected items limit + // if (value! && ItemsController.to.protectedItemLimitReached) { + // Utils.adaptiveRouteOpen( + // name: Routes.upgrade, + // parameters: { + // 'title': 'Protected Items', + // 'body': + // 'Maximum protected items of ${limits.protectedItems} limit reached. Upgrade to Pro to unlock unlimited protected items feature.', + // }, + // ); + + // return; + // } + + protected.value = value!; } List querySuggestions(String query) { @@ -1139,9 +1144,7 @@ class ItemScreenController extends GetxController title: const Text('Custom Icon'), content: Form( key: formKey, - child: Utils.isSmallScreen - ? content - : SizedBox(width: 450, child: content), + child: isSmallScreen ? content : SizedBox(width: 450, child: content), ), actions: [ TextButton(onPressed: Get.back, child: Text('cancel'.tr)), @@ -1183,7 +1186,7 @@ class ItemScreenController extends GetxController await Get.dialog(AlertDialog( title: const Text('Unsaved Changes'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : const SizedBox(width: 450, child: dialogContent), actions: [ @@ -1204,7 +1207,7 @@ class ItemScreenController extends GetxController void attach() async { final attachments_ = await Utils.adaptiveRouteOpen( - name: Routes.attachments, + name: AppRoutes.attachments, parameters: {'attachments': attachments.join(',')}, ); @@ -1250,9 +1253,7 @@ class ItemScreenController extends GetxController title: const Text('Field Properties'), content: Form( key: formKey, - child: Utils.isSmallScreen - ? content - : SizedBox(width: 450, child: content), + child: isSmallScreen ? content : SizedBox(width: 450, child: content), ), actions: [ TextButton( diff --git a/lib/features/items/items.controller.dart b/lib/features/items/items.controller.dart index 3f6ebb39..1cf234a8 100644 --- a/lib/features/items/items.controller.dart +++ b/lib/features/items/items.controller.dart @@ -4,7 +4,6 @@ import 'package:liso/core/hive/models/item.hive.dart'; import '../../core/utils/globals.dart'; import '../drawer/drawer_widget.controller.dart'; -import '../pro/pro.controller.dart'; import 'items.service.dart'; class ItemsController extends GetxController with ConsoleMixin, StateMixin { @@ -18,11 +17,16 @@ class ItemsController extends GetxController with ConsoleMixin, StateMixin { final sortOrder = LisoItemSortOrder.dateModifiedDescending.obs; // GETTERS - bool get itemLimitReached => data.length >= ProController.to.limits.items; - bool get protectedItemLimitReached => - data.where((e) => e.protected).length >= - ProController.to.limits.protectedItems; + // TODO: temporary + // bool get itemLimitReached => data.length >= limits.items; + bool get itemLimitReached => false; + + // TODO: temporary + // bool get protectedItemLimitReached => + // data.where((e) => e.protected).length >= + // limits.protectedItems; + bool get protectedItemLimitReached => false; // INIT @override @@ -41,7 +45,7 @@ class ItemsController extends GetxController with ConsoleMixin, StateMixin { Iterable filteredItems = List.from(raw); // FILTER ITEMS WITH USERNAME / PASSWORDS ONLY - if (Globals.isAutofill) { + if (isAutofill) { filteredItems = filteredItems.where( (e) => e.usernameFields.isNotEmpty || e.passwordFields.isNotEmpty); } diff --git a/lib/features/items/items.service.dart b/lib/features/items/items.service.dart index e87f969b..dba5031f 100644 --- a/lib/features/items/items.service.dart +++ b/lib/features/items/items.service.dart @@ -67,7 +67,7 @@ class ItemsService extends GetxService with ConsoleMixin { Future deleteItems(Iterable items_) async { for (var e in items_) { - Persistence.to.addToDeletedItems(e.identifier); + AppPersistence.to.addToDeletedItems(e.identifier); await e.delete(); } } diff --git a/lib/features/joined_vaults/explorer/vault_explorer.screen.dart b/lib/features/joined_vaults/explorer/vault_explorer.screen.dart index c2f78999..b83ec98b 100644 --- a/lib/features/joined_vaults/explorer/vault_explorer.screen.dart +++ b/lib/features/joined_vaults/explorer/vault_explorer.screen.dart @@ -1,10 +1,10 @@ +import 'package:app_core/widgets/appbar_leading.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:skeletons/skeletons.dart'; -import '../../general/appbar_leading.widget.dart'; import '../../general/centered_placeholder.widget.dart'; import '../../items/item.tile.dart'; import '../../menu/menu.button.dart'; diff --git a/lib/features/joined_vaults/explorer/vault_explorer_screen.controller.dart b/lib/features/joined_vaults/explorer/vault_explorer_screen.controller.dart index b6bf3894..813c9244 100644 --- a/lib/features/joined_vaults/explorer/vault_explorer_screen.controller.dart +++ b/lib/features/joined_vaults/explorer/vault_explorer_screen.controller.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/ui_utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -11,7 +13,6 @@ import 'package:liso/features/items/items.service.dart'; import '../../../core/hive/models/item.hive.dart'; import '../../../core/services/cipher.service.dart'; import '../../../core/utils/globals.dart'; -import '../../../core/utils/ui_utils.dart'; import '../../files/storage.service.dart'; import '../../files/sync.service.dart'; import '../../menu/menu.item.dart'; diff --git a/lib/features/joined_vaults/joined_vaults.screen.dart b/lib/features/joined_vaults/joined_vaults.screen.dart index 4301af71..b86f2154 100644 --- a/lib/features/joined_vaults/joined_vaults.screen.dart +++ b/lib/features/joined_vaults/joined_vaults.screen.dart @@ -1,16 +1,17 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; +import 'package:app_core/widgets/remote_image.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; -import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; import '../app/routes.dart'; -import '../general/appbar_leading.widget.dart'; -import '../general/busy_indicator.widget.dart'; import '../general/centered_placeholder.widget.dart'; -import '../general/remote_image.widget.dart'; import '../menu/menu.button.dart'; import '../menu/menu.item.dart'; import 'explorer/vault_explorer_screen.controller.dart'; @@ -30,7 +31,7 @@ class JoinedVaultsScreen extends StatelessWidget with ConsoleMixin { void open() async { VaultExplorerScreenController.vault = vault; - Utils.adaptiveRouteOpen(name: Routes.vaultExplorer); + Utils.adaptiveRouteOpen(name: AppRoutes.vaultExplorer); } void confirmLeave() { @@ -98,7 +99,7 @@ class JoinedVaultsScreen extends StatelessWidget with ConsoleMixin { Get.dialog(AlertDialog( title: const Text('Leave Shared Vault'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : SizedBox( width: 450, diff --git a/lib/features/joined_vaults/joined_vaults_screen.controller.dart b/lib/features/joined_vaults/joined_vaults_screen.controller.dart index 252ce8b7..843f654e 100644 --- a/lib/features/joined_vaults/joined_vaults_screen.controller.dart +++ b/lib/features/joined_vaults/joined_vaults_screen.controller.dart @@ -1,14 +1,13 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:liso/features/joined_vaults/joined_vault.controller.dart'; -import '../../core/firebase/config/config.service.dart'; -import '../../core/utils/utils.dart'; - class JoinedVaultsScreenController extends GetxController with ConsoleMixin { static JoinedVaultsScreenController get to => Get.find(); @@ -79,14 +78,14 @@ class JoinedVaultsScreenController extends GetxController with ConsoleMixin { // final owner = ownerSnapshot.data()!; // // obtain owner limits - // var ownerLimits = ConfigService.to.limits.free; + // var ownerLimits = configLimits.free; // if (owner.limits == 'holder') { - // ownerLimits = ConfigService.to.limits.holder; + // ownerLimits = configLimits.holder; // } else if (owner.limits == 'staker') { - // ownerLimits = ConfigService.to.limits.staker; + // ownerLimits = configLimits.staker; // } else if (owner.limits == 'pro') { - // ownerLimits = ConfigService.to.limits.pro; + // ownerLimits = configLimits.pro; // } // if (existingMembers >= ownerLimits.sharedMembers) { @@ -284,9 +283,7 @@ class JoinedVaultsScreenController extends GetxController with ConsoleMixin { title: Text('join_shared_vault'.tr), content: Form( key: formKey, - child: Utils.isSmallScreen - ? content - : SizedBox(width: 450, child: content), + child: isSmallScreen ? content : SizedBox(width: 450, child: content), ), actions: [ TextButton( diff --git a/lib/features/json_viewer/json_viewer.screen.dart b/lib/features/json_viewer/json_viewer.screen.dart index 8191e1ec..b872ecc8 100644 --- a/lib/features/json_viewer/json_viewer.screen.dart +++ b/lib/features/json_viewer/json_viewer.screen.dart @@ -1,9 +1,8 @@ +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_json_viewer/flutter_json_viewer.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; - class JSONViewerScreen extends StatelessWidget { final Map data; diff --git a/lib/features/main/main.screen.dart b/lib/features/main/main.screen.dart index 264847f8..a2480ed3 100644 --- a/lib/features/main/main.screen.dart +++ b/lib/features/main/main.screen.dart @@ -1,33 +1,35 @@ +import 'package:app_core/config.dart'; +import 'package:app_core/connectivity/connectivity.service.dart'; +import 'package:app_core/connectivity/connectivity_bar.widget.dart'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/persistence/persistence_builder.widget.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/remote_image.widget.dart'; import 'package:badges/badges.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; +import 'package:liso/core/persistence/persistence.dart'; import 'package:liso/core/utils/globals.dart'; -import 'package:liso/features/connectivity/connectivity.service.dart'; -import 'package:liso/features/general/centered_placeholder.widget.dart'; import 'package:liso/features/items/item.tile.dart'; import 'package:liso/features/items/items.controller.dart'; import 'package:liso/features/joined_vaults/joined_vault.controller.dart'; import 'package:liso/features/menu/menu.button.dart'; -import '../../core/firebase/config/config.service.dart'; import '../../core/hive/models/group.hive.dart'; -import '../../core/persistence/persistence.dart'; -import '../../core/persistence/persistence_builder.widget.dart'; -import '../../core/utils/ui_utils.dart'; -import '../../core/utils/utils.dart'; import '../app/routes.dart'; -import '../connectivity/connectivity_bar.widget.dart'; import '../drawer/drawer.widget.dart'; import '../drawer/drawer_widget.controller.dart'; import '../files/sync.service.dart'; +import '../general/centered_placeholder.widget.dart'; import '../general/custom_chip.widget.dart'; -import '../general/remote_image.widget.dart'; import '../groups/groups.controller.dart'; import '../joined_vaults/explorer/vault_explorer_screen.controller.dart'; -import '../pro/pro.controller.dart'; import '../shared_vaults/model/shared_vault.model.dart'; import '../shared_vaults/shared_vault.controller.dart'; import 'main_screen.controller.dart'; @@ -38,8 +40,8 @@ class MainScreen extends GetResponsiveView MainScreen({Key? key}) : super( key: key, - settings: const ResponsiveScreenSettings( - desktopChangePoint: kDesktopChangePoint, + settings: ResponsiveScreenSettings( + desktopChangePoint: CoreConfig().desktopChangePoint, ), ); @@ -111,11 +113,10 @@ class MainScreen extends GetResponsiveView ); var childContent = itemsController.obx( - (_) => !ProController.to.limits.passwordHealth && - drawerController.filterPasswordHealth.value - ? weakPasswords - : listView, - // onLoading: const BusyIndicator(), + (_) => + !limits.passwordHealth && drawerController.filterPasswordHealth.value + ? weakPasswords + : listView, onEmpty: drawerController.filterPasswordHealth.value ? const CenteredPlaceholder( iconData: Icons.check, @@ -209,7 +210,7 @@ class MainScreen extends GetResponsiveView builder: (p, context) => Column( children: [ Visibility( - visible: !p.backedUpSeed.val, + visible: !AppPersistence.to.backedUpSeed.val, child: Card( elevation: 2.0, margin: const EdgeInsets.only( @@ -266,9 +267,9 @@ class MainScreen extends GetResponsiveView ), ), Visibility( - visible: p.rateCardVisibility.val && + visible: AppPersistence.to.rateCardVisibility.val && p.sessionCount.val > 15 && - isReviewable, + isRateReviewSupported, child: Card( elevation: 2.0, margin: const EdgeInsets.only( @@ -290,7 +291,7 @@ class MainScreen extends GetResponsiveView trailing: OutlinedButton( onPressed: () { UIUtils.rateAndReview(); - p.rateCardVisibility.val = false; + AppPersistence.to.rateCardVisibility.val = false; }, child: const Text('Rate'), ), @@ -359,13 +360,14 @@ class MainScreen extends GetResponsiveView child: const Icon(Iconsax.sort), ), ), - if (!Globals.isAutofill) ...[ + if (!isAutofill) ...[ PersistenceBuilder( builder: (p, context) => Visibility( - visible: Persistence.to.sync.val, + visible: AppPersistence.to.sync.val, child: Badge( - showBadge: p.sync.val && p.changes.val > 0, - badgeContent: Text(p.changes.val.toString()), + showBadge: AppPersistence.to.sync.val && + AppPersistence.to.changes.val > 0, + badgeContent: Text(AppPersistence.to.changes.val.toString()), position: BadgePosition.topEnd(top: -1, end: -5), child: Obx( () => IconButton( @@ -487,7 +489,7 @@ class MainScreen extends GetResponsiveView onTap: () async { await Future.delayed(const Duration(milliseconds: 10)); VaultExplorerScreenController.vault = vault; - Utils.adaptiveRouteOpen(name: Routes.vaultExplorer); + Utils.adaptiveRouteOpen(name: AppRoutes.vaultExplorer); }, child: Row( children: [ @@ -564,7 +566,7 @@ class MainScreen extends GetResponsiveView mainAxisSize: MainAxisSize.min, children: [ Expanded( - flex: Utils.isSmallScreen ? 1 : 0, + flex: isSmallScreen ? 1 : 0, child: Text( drawerController.filterGroupLabel, overflow: TextOverflow.fade, @@ -587,9 +589,9 @@ class MainScreen extends GetResponsiveView final appBar = AppBar( centerTitle: false, title: appBarTitle, - automaticallyImplyLeading: !Globals.isAutofill, + automaticallyImplyLeading: !isAutofill, actions: appBarActions, - leading: Globals.isAutofill || !Utils.isSmallScreen + leading: isAutofill || !isSmallScreen ? null : IconButton( onPressed: () => scaffoldKey.currentState?.openDrawer(), @@ -599,7 +601,7 @@ class MainScreen extends GetResponsiveView // TODO: show only if there are trash items - final fab = Globals.isAutofill + final fab = isAutofill ? null : Obx( () { diff --git a/lib/features/main/main_screen.controller.dart b/lib/features/main/main_screen.controller.dart index 1053d39b..0ed0e6be 100644 --- a/lib/features/main/main_screen.controller.dart +++ b/lib/features/main/main_screen.controller.dart @@ -1,6 +1,14 @@ import 'dart:async'; import 'dart:io'; +import 'package:app_core/controllers/pro.controller.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/persistence/persistence.dart'; +import 'package:app_core/supabase/supabase_auth.service.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -18,16 +26,12 @@ import 'package:liso/features/autofill/autofill.service.dart'; import 'package:liso/features/categories/categories.controller.dart'; import 'package:liso/features/items/items.controller.dart'; import 'package:liso/features/items/items.service.dart'; -import 'package:liso/features/pro/pro.controller.dart'; import 'package:liso/features/wallet/wallet.service.dart'; import 'package:path/path.dart'; import 'package:window_manager/window_manager.dart'; import '../../core/liso/liso_paths.dart'; -import '../../core/notifications/notifications.manager.dart'; import '../../core/persistence/persistence.secret.dart'; -import '../../core/services/alchemy.service.dart'; -import '../../core/utils/ui_utils.dart'; import '../../core/utils/utils.dart'; import '../drawer/drawer_widget.controller.dart'; import '../files/storage.service.dart'; @@ -55,13 +59,13 @@ class MainScreenController extends GetxController .map( (e) => ContextMenuItem( title: e.reservedName, - leading: Utils.categoryIcon( + leading: AppUtils.categoryIcon( e.id, color: themeColor, size: popupIconSize, ), onSelected: () => Utils.adaptiveRouteOpen( - name: Routes.item, + name: AppRoutes.item, parameters: {'mode': 'add', 'category': e.id}, ), ), @@ -77,7 +81,7 @@ class MainScreenController extends GetxController List get menuItems { return [ - if (persistence.sync.val) ...[ + if (AppPersistence.to.sync.val) ...[ ContextMenuItem( title: 'sync'.tr, leading: const Icon(Iconsax.cloud_change), @@ -178,18 +182,24 @@ class MainScreenController extends GetxController // INIT @override void onInit() async { - if (GetPlatform.isDesktop && !GetPlatform.isWeb) { + if (isDesktop) { window.addListener(this); window.setPreventClose(true); } + SupabaseAuthService.to.signedIn = () { + StorageService.to + .load() + .then((_) => AppSupabaseFunctionsService.to.syncUser()); + }; + console.info('onInit'); super.onInit(); } @override void onReady() { - if (GetPlatform.isDesktop && !GetPlatform.isWeb) { + if (isDesktop) { window.setBrightness( Get.isDarkMode ? Brightness.dark : Brightness.light, ); @@ -202,7 +212,7 @@ class MainScreenController extends GetxController @override void onClose() { - if (GetPlatform.isDesktop && !GetPlatform.isWeb) { + if (isDesktop) { window.removeListener(this); } @@ -214,19 +224,18 @@ class MainScreenController extends GetxController bool preventClosing = await window.isPreventClose(); final confirmClose = Get.isDialogOpen == false && preventClosing && - persistence.changes.val > 0 && - persistence.sync.val; + AppPersistence.to.changes.val > 0 && + AppPersistence.to.sync.val; if (!confirmClose) return window.destroy(); final content = Text( - 'There are ${persistence.changes.val} unsynced changes you may want to sync first before exiting.', + 'There are ${AppPersistence.to.changes.val} unsynced changes you may want to sync first before exiting.', ); Get.dialog(AlertDialog( title: const Text('Unsynced Changes'), - content: - Utils.isSmallScreen ? content : SizedBox(width: 450, child: content), + content: isSmallScreen ? content : SizedBox(width: 450, child: content), actions: [ TextButton( onPressed: Get.back, @@ -265,9 +274,7 @@ class MainScreenController extends GetxController // load listview load(); - StorageService.to.load().then((_) => SupabaseFunctionsService.to.sync()); - - if (Globals.isAutofill) { + if (isAutofill) { // show all items from all vaults drawerController.filterGroupId.value = ''; LisoAutofillService.to.request(); @@ -276,10 +283,6 @@ class MainScreenController extends GetxController // TODO: show some message a vault is required } } else { - // load balances - AlchemyService.to.init(); - AlchemyService.to.load(); - // incase cipher key is still empty for some reason // retry again after a few seconds if (SecretPersistence.to.cipherKey.isEmpty) { @@ -339,7 +342,7 @@ class MainScreenController extends GetxController return Future.value(msg); } - if (!Globals.timeLockEnabled) { + if (!timeLockEnabled) { console.warning('lifecycle: timeLock is disabled'); return Future.value(msg); } @@ -376,7 +379,7 @@ class MainScreenController extends GetxController void _updateBuildNumber() async { persistence.lastBuildNumber.val = int.parse( - Globals.metadata!.app.buildNumber, + metadataApp.buildNumber, ); } @@ -399,7 +402,7 @@ class MainScreenController extends GetxController Get.dialog(AlertDialog( title: const Text('Empty Trash'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : const SizedBox(width: 450, child: dialogContent), actions: [ @@ -434,7 +437,7 @@ class MainScreenController extends GetxController Get.dialog(AlertDialog( title: const Text('Empty Deleted'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : const SizedBox(width: 450, child: dialogContent), actions: [ @@ -468,7 +471,7 @@ class MainScreenController extends GetxController // delete imported items permanently for (var e in importedItemIds) { - Persistence.to.addToDeletedItems(e); + AppPersistence.to.addToDeletedItems(e); } final vault = await LisoManager.parseVaultBytes( @@ -491,7 +494,7 @@ class MainScreenController extends GetxController Get.dialog(AlertDialog( title: const Text('Imported Items'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : const SizedBox(width: 450, child: dialogContent), actions: [ @@ -521,7 +524,7 @@ class MainScreenController extends GetxController if (!unlocked) return; Utils.adaptiveRouteOpen( - name: Routes.seed, + name: AppRoutes.seed, parameters: {'mode': 'display'}, ); } diff --git a/lib/features/menu/menu.button.dart b/lib/features/menu/menu.button.dart index e12a702d..84eb7846 100644 --- a/lib/features/menu/menu.button.dart +++ b/lib/features/menu/menu.button.dart @@ -1,10 +1,11 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:liso/core/utils/globals.dart'; import 'package:liso/features/menu/menu.sheet.dart'; -import '../../core/utils/utils.dart'; import '../app/routes.dart'; import 'menu.item.dart'; @@ -38,7 +39,7 @@ class ContextMenuButton extends StatelessWidget with ConsoleMixin { ); // if mobile / small screen - if (Utils.isSmallScreen && sheetForSmallScreen) { + if (isSmallScreen && sheetForSmallScreen) { return InkWell( onTap: enabled ? () => ContextMenuSheet( @@ -50,10 +51,10 @@ class ContextMenuButton extends StatelessWidget with ConsoleMixin { ); } // if large screen - else if (!Utils.isSmallScreen && gridForLargeScreen) { + else if (!isSmallScreen && gridForLargeScreen) { return InkWell( onTap: enabled - ? () => Utils.adaptiveRouteOpen(name: Routes.categoryPicker) + ? () => Utils.adaptiveRouteOpen(name: AppRoutes.categoryPicker) : null, child: AbsorbPointer(child: wrappedChild), ); @@ -92,7 +93,7 @@ class ContextMenuButton extends StatelessWidget with ConsoleMixin { }, ).toList(); - if (!useMouseRegion || Utils.isSmallScreen) { + if (!useMouseRegion || isSmallScreen) { return PopupMenuButton( onSelected: (ContextMenuItem menu) => menu.onSelected?.call(), itemBuilder: (context) => popupItems, diff --git a/lib/features/menu/menu.sheet.dart b/lib/features/menu/menu.sheet.dart index 6d287627..f3ee0258 100644 --- a/lib/features/menu/menu.sheet.dart +++ b/lib/features/menu/menu.sheet.dart @@ -1,8 +1,8 @@ +import 'package:app_core/animations/animations.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import '../../core/animations/animations.dart'; import 'menu.item.dart'; class ContextMenuSheet extends StatelessWidget with ConsoleMixin { diff --git a/lib/features/otp/otp.screen.dart b/lib/features/otp/otp.screen.dart index b442d6f2..3fb84e21 100644 --- a/lib/features/otp/otp.screen.dart +++ b/lib/features/otp/otp.screen.dart @@ -1,12 +1,12 @@ +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../general/appbar_leading.widget.dart'; import 'otp_screen.controller.dart'; class OTPScreen extends StatelessWidget with ConsoleMixin { diff --git a/lib/features/password_generator/password_generator.screen.dart b/lib/features/password_generator/password_generator.screen.dart index 89703e07..b35593f3 100644 --- a/lib/features/password_generator/password_generator.screen.dart +++ b/lib/features/password_generator/password_generator.screen.dart @@ -1,11 +1,11 @@ +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:liso/core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../general/appbar_leading.widget.dart'; import 'password_generator_screen.controller.dart'; class PasswordGeneratorScreen extends StatelessWidget with ConsoleMixin { diff --git a/lib/features/password_generator/password_generator_screen.controller.dart b/lib/features/password_generator/password_generator_screen.controller.dart index ec27e30d..0d4d9059 100644 --- a/lib/features/password_generator/password_generator_screen.controller.dart +++ b/lib/features/password_generator/password_generator_screen.controller.dart @@ -1,10 +1,10 @@ +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:random_string_generator/random_string_generator.dart'; import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; import '../app/routes.dart'; class PasswordGeneratorScreenController extends GetxController @@ -82,7 +82,7 @@ class PasswordGeneratorScreenController extends GetxController void save() { Utils.adaptiveRouteOpen( - name: Routes.item, + name: AppRoutes.item, parameters: { 'mode': 'generated', 'category': LisoItemCategory.password.name, diff --git a/lib/features/pro/pro.controller.dart b/lib/features/pro/pro.controller.dart deleted file mode 100644 index bf2e826f..00000000 --- a/lib/features/pro/pro.controller.dart +++ /dev/null @@ -1,397 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:console_mixin/console_mixin.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:get/get.dart'; -import 'package:intl/intl.dart'; -import 'package:liso/core/utils/ui_utils.dart'; -import 'package:liso/features/wallet/wallet.service.dart'; -import 'package:purchases_flutter/purchases_flutter.dart'; -import 'package:supabase/supabase.dart'; - -import '../../core/firebase/config/config.service.dart'; -import '../../core/firebase/config/models/config_limits.model.dart'; -import '../../core/persistence/persistence.dart'; -import '../../core/persistence/persistence.secret.dart'; -import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../rate/rate.widget.dart'; - -class ProController extends GetxController with ConsoleMixin { - static ProController get to => Get.find(); - - // VARIABLES - - // PROPERTIES - final info = Rx(CustomerInfo.fromJson(kPurchaserInfoInitial)); - final offerings = Rx(Offerings.fromJson(kOfferingsInitial)); - final verifiedPro = Persistence.to.verifiedProCache.val.obs; - final licenseKey = ''.obs; - - // GETTERS - - bool get isPro => proEntitlement?.isActive == true || verifiedPro.value; - - EntitlementInfo? get proEntitlement => info.value.entitlements.all['pro']; - - List get packages => - offerings.value.current?.availablePackages ?? []; - - String get proPrefixString => - proEntitlement?.willRenew == true ? 'renews'.tr : 'expires'.tr; - - String get proDateString => DateFormat.yMMMMd() - .add_jm() - .format(DateTime.parse(proEntitlement!.expirationDate!).toLocal()); - - String get shortLicenseKey => licenseKey.isEmpty || - licenseKey.value.length < 35 - ? 'none'.tr - : '${licenseKey.substring(0, 7)}...${licenseKey.substring(licenseKey.value.length - 7)} ${verifiedPro.value ? '' : '- Inactive'}'; - - ConfigLimitsTier get limits { - final limits_ = ConfigService.to.limits; - if (!WalletService.to.isReady) return limits_.free; - // check if user is a pro subscriber - if (isPro) return limits_.pro; - // check if user is whitelisted by developer - final users = ConfigService.to.users.users.where( - (e) => e.address == SecretPersistence.to.walletAddress.val, - ); - - if (users.isNotEmpty) { - final user = users.first; - - if (user.limits == 'holder') { - return limits_.holder; - } else if (user.limits == 'staker') { - return limits_.staker; - } else if (user.limits == 'pro') { - return limits_.pro; - } else if (user.limits == 'trial') { - return limits_.trial; - } - } - - // TODO: check if user is a staker - // check if user is a holder - if (Persistence.to.lastLisoBalance.val > limits_.holder.tokenThreshold) { - return limits_.holder; - } - - // free user - return limits_.free; - } - - // INIT - @override - void onClose() { - if (!isIAPSupported) return; - - Purchases.removeCustomerInfoUpdateListener((info_) { - info.value = info_; - }); - - super.onClose(); - } - - @override - void onInit() { - verifiedPro.listen((value) { - Persistence.to.verifiedProCache.val = value; - }); - - super.onInit(); - } - - @override - void onReady() { - verifiedPro.value = Persistence.to.verifiedProCache.val; - super.onReady(); - } - - // FUNCTIONS - Future init() async { - if (!isIAPSupported) return; - await Purchases.setDebugLogsEnabled(true); - - await Purchases.configure( - PurchasesConfiguration(ConfigService.to.secrets.revenuecat.apiKey), - ); - - Purchases.addCustomerInfoUpdateListener((info_) { - info.value = info_; - }); - - sync(); - } - - Future invalidate() async { - if (!isIAPSupported) return; - - await Purchases.invalidateCustomerInfoCache(); - } - - Future login(User user) async { - if (!isIAPSupported) return; - await Purchases.logIn(user.id); - await Purchases.setEmail(user.email!); - } - - Future logout() async { - verifiedPro.value = false; - licenseKey.value = ''; - if (!isIAPSupported) return; - - // prevent exception if logging out with an anonymous user - if (await Purchases.isAnonymous) { - return console.error('anonymous user'); - } - - try { - await Purchases.logOut(); - } on PlatformException catch (e) { - console.error('exception error: $e'); - } catch (e) { - console.error('logout error: $e'); - } - } - - Future load() async { - if (!isIAPSupported) return; - - try { - offerings.value = await Purchases.getOfferings(); - } on PlatformException catch (e) { - console.error('load error: $e'); - _showError(e); - } - } - - Future sync() async { - if (!isIAPSupported) return; - - try { - info.value = await Purchases.getCustomerInfo(); - // console.warning('sync: ${jsonEncode(info.value.toJson())}'); - } on PlatformException catch (e) { - return console.error('sync error: $e'); - } - - console.wtf('IS PRO USER: ${ProController.to.isPro}'); - - // show upgrade screen every after 5th times opened - if (!ProController.to.isPro && (Persistence.to.sessionCount.val % 5) == 0) { - await Future.delayed(1.seconds); - - if (isIAPSupported) { - await Utils.adaptiveRouteOpen(name: Routes.upgrade); - } - } else { - if (!Persistence.to.rateDialogShown.val && - Persistence.to.sessionCount.val > 16 && - isRateReviewSupported) { - Persistence.to.rateDialogShown.val = true; - - const dialog = AlertDialog( - content: SizedBox( - width: 400, - child: RateWidget(), - ), - ); - - Get.dialog(dialog); - } - } - } - - Future purchase(Package package) async { - if (!isIAPSupported) return; - - Globals.timeLockEnabled = false; // temporarily disable - CustomerInfo? info_; - - try { - info_ = await Purchases.purchasePackage(package); - console.warning('purchase: ${jsonEncode(info_.toJson())}'); - } on PlatformException catch (e) { - console.error('purchase error: $e'); - Globals.timeLockEnabled = true; - _showError(e); - return; - } - - info.value = info_; - Globals.timeLockEnabled = true; - } - - Future restore() async { - if (!isIAPSupported) return; - - CustomerInfo? info_; - - try { - info_ = await Purchases.restorePurchases(); - console.warning('restore: ${jsonEncode(info_.toJson())}'); - } on PlatformException catch (e) { - _showError(e); - return; - } - - info.value = info_; - } - - Future _showError(PlatformException e) async { - final errorCode = PurchasesErrorHelper.getErrorCode(e); - console.error('errorCode: ${errorCode.name}'); - - String errorMessage = - 'Code: ${errorCode.name}. Please report to the developer.'; - - switch (errorCode) { - case PurchasesErrorCode.purchaseCancelledError: - errorMessage = ''; - break; - case PurchasesErrorCode.purchaseNotAllowedError: - errorMessage = - 'For some reason you or the device is not allowed to do purchases'; - break; - case PurchasesErrorCode.purchaseInvalidError: - errorMessage = 'Invalid purchase'; - break; - case PurchasesErrorCode.productAlreadyPurchasedError: - break; - case PurchasesErrorCode.productNotAvailableForPurchaseError: - errorMessage = - 'The package you selected is currently not available for purchase'; - break; - case PurchasesErrorCode.configurationError: - break; - case PurchasesErrorCode.ineligibleError: - errorMessage = 'Ineligible to purchase this package'; - break; - case PurchasesErrorCode.insufficientPermissionsError: - break; - case PurchasesErrorCode.invalidAppUserIdError: - break; - case PurchasesErrorCode.invalidAppleSubscriptionKeyError: - break; - case PurchasesErrorCode.invalidCredentialsError: - break; - case PurchasesErrorCode.invalidReceiptError: - break; - case PurchasesErrorCode.invalidSubscriberAttributesError: - break; - case PurchasesErrorCode.missingReceiptFileError: - break; - case PurchasesErrorCode.networkError: - errorMessage = 'A network error occurred. Please try again.'; - break; - case PurchasesErrorCode.operationAlreadyInProgressError: - errorMessage = 'The operation is already in progress'; - break; - case PurchasesErrorCode.paymentPendingError: - errorMessage = 'The payment is already pending'; - break; - case PurchasesErrorCode.receiptAlreadyInUseError: - break; - case PurchasesErrorCode.receiptInUseByOtherSubscriberError: - break; - case PurchasesErrorCode.storeProblemError: - errorMessage = - 'There was a problem with ${GetPlatform.isIOS || GetPlatform.isMacOS ? 'the App Store' : 'Google Play'}'; - break; - case PurchasesErrorCode.unexpectedBackendResponseError: - break; - case PurchasesErrorCode.unknownBackendError: - break; - case PurchasesErrorCode.unsupportedError: - break; - case PurchasesErrorCode.unknownError: - errorMessage = ''; - // errorMessage = 'Unknown error. Please report to the developer.'; - break; - default: - errorMessage = ''; - // errorMessage = 'Weird error. Please report to the developer.'; - break; - } - - if (errorMessage.isEmpty) return; - - await UIUtils.showSimpleDialog( - 'Purchase Error', - errorMessage, - ); - } -} - -const kPurchaserInfoInitial = { - "entitlements": {"all": {}, "active": {}}, - "allPurchaseDates": {}, - "activeSubscriptions": [], - "allPurchasedProductIdentifiers": [], - "nonSubscriptionTransactions": [], - "firstSeen": "", - "originalAppUserId": "", - "allExpirationDates": {}, - "requestDate": "", - "latestExpirationDate": null, - "originalPurchaseDate": null, - "originalApplicationVersion": null, - "managementURL": null -}; - -const kOfferingsInitial = { - "all": { - "default": { - "identifier": "", - "serverDescription": "", - "availablePackages": [], - "lifetime": null, - "annual": null, - "sixMonth": null, - "threeMonth": null, - "twoMonth": null, - "monthly": null, - "weekly": null, - } - }, - "current": { - "identifier": "", - "serverDescription": "", - "availablePackages": [], - "lifetime": null, - "annual": null, - "sixMonth": null, - "threeMonth": null, - "twoMonth": null, - "monthly": null, - } -}; - -const kPackageInitial = { - "identifier": "", - "packageType": "", - "product": { - "identifier": "", - "description": "", - "title": "", - "price": 0.0, - "priceString": "", - "currencyCode": "", - "introPrice": { - "price": 0.0, - "priceString": "", - "period": "", - "cycles": 0, - "periodUnit": "", - "periodNumberOfUnits": 0 - }, - "discounts": [] - }, - "offeringIdentifier": "annual", -}; diff --git a/lib/features/pro/upgrade/feature.tile.dart b/lib/features/pro/upgrade/feature.tile.dart deleted file mode 100644 index 1fe5e27c..00000000 --- a/lib/features/pro/upgrade/feature.tile.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:liso/core/utils/globals.dart'; - -class FeatureTile extends StatelessWidget { - final IconData iconData; - final String title; - final Widget trailing; - - const FeatureTile({ - Key? key, - required this.iconData, - required this.title, - required this.trailing, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(iconData, color: proColor), - const SizedBox(width: 20), - Text( - title, - style: const TextStyle( - fontSize: 17, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - trailing, - ], - ), - ); - } -} diff --git a/lib/features/pro/upgrade/upgrade.screen.dart b/lib/features/pro/upgrade/upgrade.screen.dart deleted file mode 100644 index 4b94732a..00000000 --- a/lib/features/pro/upgrade/upgrade.screen.dart +++ /dev/null @@ -1,744 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:console_mixin/console_mixin.dart'; -import 'package:filesize/filesize.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:iconsax/iconsax.dart'; -import 'package:line_icons/line_icons.dart'; -import 'package:liso/core/utils/globals.dart'; -import 'package:liso/features/general/pro.widget.dart'; -import 'package:liso/features/pro/pro.controller.dart'; -import 'package:liso/features/pro/upgrade/feature.tile.dart'; -import 'package:flutter_animate/flutter_animate.dart'; - -import '../../../core/firebase/config/config.service.dart'; -import '../../../core/persistence/persistence.dart'; -import '../../../core/utils/utils.dart'; -import '../../app/routes.dart'; -import '../../general/busy_indicator.widget.dart'; -import 'upgrade_screen.controller.dart'; - -class UpgradeScreen extends StatelessWidget with ConsoleMixin { - const UpgradeScreen({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final controller = Get.put(UpgradeScreenController()); - - final benefits = Obx( - () { - final limit = controller.selectedLimit; - - String formatKNumber(int number) { - if (number == 1000000) { - return 'Unlimited'; - } else { - return kFormatter.format(number); - } - } - - final kTrailingStyle = TextStyle( - color: proColor, - fontSize: 17, - fontWeight: FontWeight.w500, - ); - - return ListView( - shrinkWrap: true, - controller: ScrollController(), - children: [ - const Padding( - padding: EdgeInsets.only(left: 15, top: 15), - child: Text( - 'Unlock All Access', - style: TextStyle(color: Colors.grey), - ), - ), - if (isIAPSupported) ...[ - Obx( - () => FeatureTile( - iconData: Iconsax.calendar, - title: controller.promoText, - trailing: Icon(Icons.check, color: proColor), - ), - ) - ] else ...[ - FeatureTile( - iconData: Iconsax.calendar, - title: '1 ${'week'.tr} ${'free_trial'.tr}', - trailing: Icon(Icons.check, color: proColor), - ), - ], - FeatureTile( - iconData: Iconsax.close_circle, - title: 'cancel_anytime'.tr, - trailing: Icon(Icons.check, color: proColor), - ), - if (limit.id != 'pro' && limit.id != 'free') ...[ - ListTile( - leading: Icon(Iconsax.coin, color: proColor), - title: const Text('Min Token'), - onTap: () {}, - trailing: Text( - '${currencyFormatter.format(limit.tokenThreshold)} \$LISO', - style: kTrailingStyle, - ), - ), - ], - FeatureTile( - iconData: Iconsax.document, - title: 'Items', - trailing: Text( - formatKNumber(limit.items), - style: kTrailingStyle, - ), - ), - FeatureTile( - iconData: Iconsax.cpu, - title: 'Devices', - trailing: Text( - formatKNumber(limit.devices), - style: kTrailingStyle, - ), - ), - FeatureTile( - iconData: Iconsax.password_check, - title: '2FA Authenticator', - trailing: Icon( - limit.otpGenerator ? Icons.check : LineIcons.times, - color: limit.otpGenerator ? proColor : Colors.grey, - ), - ), - FeatureTile( - iconData: Iconsax.document_cloud, - title: 'Encrypted Cloud Storage', - trailing: Text( - filesize(1073741824), - style: kTrailingStyle, - ), - ), - FeatureTile( - iconData: Iconsax.people, - title: 'Shared Members', - trailing: Text( - formatKNumber(limit.sharedMembers), - style: kTrailingStyle, - ), - ), - FeatureTile( - iconData: Iconsax.lock, - title: 'Protected Items', - trailing: Text( - formatKNumber(limit.protectedItems), - style: kTrailingStyle, - ), - ), - FeatureTile( - iconData: Iconsax.health, - title: 'Password Health', - trailing: Icon( - limit.passwordHealth ? Icons.check : LineIcons.times, - color: limit.passwordHealth ? proColor : Colors.grey, - ), - ), - FeatureTile( - iconData: Iconsax.message_question, - title: 'Priority Support', - trailing: Icon( - limit.prioritySupport ? Icons.check : LineIcons.times, - color: limit.prioritySupport ? proColor : Colors.grey, - ), - ), - FeatureTile( - iconData: Iconsax.security_card, - title: 'File Encryption Tool', - trailing: Icon( - limit.cipherTool ? Icons.check : LineIcons.times, - color: limit.cipherTool ? proColor : Colors.grey, - ), - ), - FeatureTile( - iconData: Iconsax.direct_inbox, - title: 'Vault Backups', - trailing: Text( - formatKNumber(limit.backups), - style: kTrailingStyle, - ), - ), - FeatureTile( - iconData: Iconsax.weight, - title: 'Upload File Size', - trailing: Text( - filesize(limit.uploadSize), - style: kTrailingStyle, - ), - ), - FeatureTile( - iconData: Iconsax.document_1, - title: 'Uploaded Files', - trailing: Text( - formatKNumber(limit.files), - style: kTrailingStyle, - ), - ), - FeatureTile( - iconData: Iconsax.trash, - title: 'Undo Trash', - trailing: Text( - '${formatKNumber(limit.trashDays)} Days', - style: kTrailingStyle, - ), - ), - FeatureTile( - iconData: Iconsax.box_1, - title: 'Custom Vaults', - trailing: Text( - formatKNumber(limit.customVaults), - style: kTrailingStyle, - ), - ), - FeatureTile( - iconData: Iconsax.category, - title: 'Custom Categories', - trailing: Text( - formatKNumber(limit.customCategories), - style: kTrailingStyle, - ), - ), - // ListTile( - // trailing: Icon( - // limit.breachScanner ? Icons.check : LineIcons.times, - // color: limit.breachScanner ? proColor : Colors.grey, - // ), - // leading: Icon(Iconsax.scan, color: proColor), - // title: const Text('Breach Scanner'), - // onTap: () {}, - // ), - // ListTile( - // trailing: Icon( - // limit.nfcKeycard ? Icons.check : LineIcons.times, - // color: limit.nfcKeycard ? proColor : Colors.grey, - // ), - // leading: Icon(Iconsax.card, color: proColor), - // title: const Text('NFC Keycard Support'), - // onTap: () {}, - // ), - FeatureTile( - iconData: Iconsax.rulerpen, - title: 'Autosave + Autofill', - trailing: Icon(Icons.check, color: proColor), - ), - FeatureTile( - iconData: Iconsax.lock, - title: 'Generate Passwords', - trailing: Icon(Icons.check, color: proColor), - ), - FeatureTile( - iconData: Iconsax.finger_cricle, - title: 'Biometric Auth', - trailing: Icon(Icons.check, color: proColor), - ), - FeatureTile( - iconData: Iconsax.airplane, - title: 'Offline Mode', - trailing: Icon(Icons.check, color: proColor), - ), - // FeatureTile( - // iconData: Iconsax.data, - // title: 'Self-Hostable', - // trailing: Icon(Icons.check, color: proColor), - // ), - ], - ); - }, - ); - - final productsListView = Obx( - () => ListView.builder( - shrinkWrap: true, - padding: EdgeInsets.zero, - controller: ScrollController(), - itemCount: ProController.to.packages.length, - itemBuilder: (_, index) { - final package = ProController.to.packages[index]; - final product = package.storeProduct; - final packageType = package.packageType.name.toLowerCase(); - - Widget title = Text('Just ${product.priceString} ${packageType.tr}'); - Widget? subTitle = - product.description.isEmpty ? null : Text(product.description); - Widget? secondary; - - if (product.introductoryPrice != null) { - final intro = product.introductoryPrice!; - final periodCycle = intro.cycles > 1 - ? '${intro.cycles} ${intro.periodUnit.name.tr}s' - : intro.periodUnit.name.tr; - - title = Obx( - () => RichText( - text: TextSpan( - style: TextStyle( - fontSize: 17, - fontWeight: FontWeight.bold, - color: Get.theme.textTheme.titleLarge?.color, - ), - children: [ - TextSpan(text: product.priceString), - TextSpan(text: ' / ${controller.periodUnitName.tr}'), - ], - ), - ), - ); - - secondary = Card( - elevation: 1.0, - margin: EdgeInsets.zero, - child: Padding( - padding: const EdgeInsets.fromLTRB(10, 2, 10, 5), - child: Row( - mainAxisSize: MainAxisSize.min, - // crossAxisAlignment: CrossAxisAlignment.center, - children: [ - CachedNetworkImage( - imageUrl: 'https://i.imgur.com/zUCN6gk.png', - height: 20, - ), - const SizedBox(width: 5), - Text( - 'Gumroad', - textAlign: TextAlign.center, - style: TextStyle( - color: Get.theme.primaryColor, - fontWeight: FontWeight.bold, - fontSize: 12, - ), - ), - ], - ), - ), - ); - - subTitle = RichText( - text: TextSpan( - style: const TextStyle( - fontSize: 12, - fontStyle: FontStyle.italic, - color: Colors.grey, - ), - children: [ - const TextSpan(text: 'Start with '), - TextSpan( - text: intro.priceString, - style: const TextStyle( - color: kAppColor, - fontWeight: FontWeight.bold, - ), - ), - TextSpan(text: ' on the first $periodCycle'), - ], - ), - ); - - if (controller.isFreeTrial) { - final monthlyPrice = product.price / 12; - final currencySymbol = product.priceString.substring(0, 1); - - subTitle = RichText( - text: TextSpan( - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Colors.grey, - ), - children: [ - TextSpan( - text: - '$currencySymbol${currencyFormatter.format(monthlyPrice)}', - style: const TextStyle(color: kAppColor), - ), - const TextSpan(text: ' / month billed annually'), - ], - ), - ); - } - } - - return Obx( - () => RadioListTile( - title: title, - subtitle: subTitle, - value: package.identifier, - secondary: !isIAPSupported ? secondary : null, - groupValue: controller.identifier, - activeColor: proColor, - contentPadding: EdgeInsets.zero, - onChanged: (value) => controller.package.value = - ProController.to.packages.firstWhere( - (e) => e.identifier == value, - ), - ), - ); - }, - ), - ); - - final current = Center( - child: Column( - children: [ - Icon(Icons.check, color: proColor, size: 100), - const SizedBox(height: 10), - Text( - controller.limitIndex == 1 - ? 'Thanks for staking ♥️' - : 'Thanks for holding\n${currencyFormatter.format(Persistence.to.lastLisoBalance.val)} \$LISO ♥️', - textAlign: TextAlign.center, - ), - ], - ), - ); - - final actionCardContent = Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - isIAPSupported - ? productsListView - : RadioListTile( - title: Obx( - () => Text( - controller.gumroadProduct.value.formattedPrice, - style: TextStyle( - fontSize: 17, - fontWeight: FontWeight.bold, - color: Get.theme.textTheme.titleLarge?.color, - ), - ), - ), - contentPadding: EdgeInsets.zero, - groupValue: null, - onChanged: (_) {}, - value: 0, - ), - const SizedBox(height: 5), - ElevatedButton( - onPressed: controller.purchase, - style: ElevatedButton.styleFrom( - backgroundColor: Get.theme.primaryColor, - visualDensity: VisualDensity.standard, - padding: const EdgeInsets.symmetric( - vertical: 10, - horizontal: 15, - ), - ), - child: Obx( - () => Column( - children: [ - Text( - '${controller.isFreeTrial ? 'try_free'.tr : 'subscribe'.tr} & ${'cancel_anytime'.tr}', - textAlign: TextAlign.center, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - if (controller.isFreeTrial) ...[ - Text( - "trial_remind".tr, - textAlign: TextAlign.center, - style: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 13, - ), - ), - ], - ], - ), - ), - ) - .animate(onPlay: (c) => c.repeat()) - .shimmer(duration: 2000.ms) - .then(delay: 2000.ms), - const SizedBox(height: 5), - Text( - "easy_cancel".tr, - textAlign: TextAlign.center, - ), - // Obx( - // () { - // final limit = controller.selectedLimit; - // final isCurrent = - // controller.limitIndex == controller.tabIndex.value; - // final tokenThreshold = currencyFormatter.format( - // limit.tokenThreshold, - // ); - - // return IndexedStack( - // index: controller.tabIndex.value, - // alignment: Alignment.center, - // children: [ - // Column( - // mainAxisSize: MainAxisSize.min, - // crossAxisAlignment: CrossAxisAlignment.stretch, - // children: [ - // // Text( - // // 'Cancel anytime', - // // textAlign: TextAlign.center, - // // style: TextStyle( - // // color: proColor, - // // fontSize: 15, - // // ), - // // ), - // productsListView, - // const SizedBox(height: 5), - // ElevatedButton( - // onPressed: controller.purchase, - // style: ElevatedButton.styleFrom( - // backgroundColor: proColor, - // visualDensity: VisualDensity.standard, - // padding: const EdgeInsets.symmetric(vertical: 10), - // ), - // child: Column( - // children: [ - // Text( - // '${controller.isFreeTrial ? 'Try Free' : 'Subscribe'} & Cancel Anytime', - // style: const TextStyle( - // fontWeight: FontWeight.bold, - // fontSize: 16, - // ), - // ), - // if (controller.isFreeTrial) ...[ - // const Text( - // "We'll remind you before your trial ends", - // textAlign: TextAlign.center, - // style: TextStyle( - // fontWeight: FontWeight.normal, - // fontSize: 13, - // ), - // ), - // ], - // ], - // ), - // ), - // const SizedBox(height: 5), - // const Text( - // "2 taps to start, super easy to cancel", - // textAlign: TextAlign.center, - // ), - // ], - // ), - // Column( - // mainAxisSize: MainAxisSize.min, - // crossAxisAlignment: CrossAxisAlignment.stretch, - // children: [ - // if (isCurrent) ...[ - // current, - // ] else ...[ - // Text( - // 'Stake a minimum of $tokenThreshold \$LISO and enjoy the above Pro features', - // textAlign: TextAlign.center, - // style: - // const TextStyle(color: Colors.grey, fontSize: 15), - // ), - // const Divider(height: 10), - // ElevatedButton.icon( - // label: const Text('Stake \$LISO'), - // icon: const Icon(Iconsax.lock), - // style: - // ElevatedButton.styleFrom(backgroundColor: proColor), - // onPressed: () { - // UIUtils.showSimpleDialog( - // 'Stake \$LISO Tokens', - // 'Coming soon...', - // ); - // }, - // ), - // ] - // ], - // ), - // Column( - // mainAxisSize: MainAxisSize.min, - // crossAxisAlignment: CrossAxisAlignment.stretch, - // children: [ - // if (isCurrent) ...[ - // current, - // ] else ...[ - // Text( - // 'Hold at least $tokenThreshold \$LISO and enjoy the above Pro features', - // textAlign: TextAlign.center, - // style: - // const TextStyle(color: Colors.grey, fontSize: 15), - // ), - // const Divider(height: 10), - // ElevatedButton.icon( - // label: const Text('Buy \$LISO'), - // icon: const Icon(Iconsax.bitcoin_card), - // style: - // ElevatedButton.styleFrom(backgroundColor: proColor), - // onPressed: () { - // UIUtils.showSimpleDialog( - // 'Buy \$LISO Tokens', - // 'Coming soon...', - // ); - // }, - // ), - // ] - // ], - // ), - // Column( - // mainAxisSize: MainAxisSize.min, - // crossAxisAlignment: CrossAxisAlignment.stretch, - // children: [ - // const Text( - // 'Keep the free version because it is awesome anyway', - // textAlign: TextAlign.center, - // style: TextStyle(color: Colors.grey, fontSize: 15), - // ), - // const Divider(height: 10), - // ElevatedButton.icon( - // label: Text('Keep ${ConfigService.to.appName} Free'), - // icon: const Icon(LineIcons.heart), - // style: - // ElevatedButton.styleFrom(backgroundColor: proColor), - // onPressed: Get.back, - // ), - // ], - // ), - // ], - // ); - // }, - // ), - const SizedBox(height: 10), - ], - ); - - final actionCard = Card( - elevation: 4.0, - color: Get.isDarkMode ? const Color(0xFF0B1717) : null, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: controller.obx( - (state) => actionCardContent, - onLoading: BusyIndicator(color: proColor), - ), - ), - ); - - final content = Padding( - padding: const EdgeInsets.only(left: 5, right: 5, bottom: 5), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded(child: benefits), - actionCard, - // const SizedBox(height: 5), - // if (ProController.to.isFreeTrial) ...[ - // RichText( - // textAlign: TextAlign.center, - // text: TextSpan( - // text: '✔️ Free Trial', - // style: TextStyle(fontSize: 12, color: proColor), - // children: [ - // TextSpan( - // text: - // ' is on and will expire on ${ProController.to.freeTrialExpirationDateTimeString}', - // style: const TextStyle( - // color: Colors.grey, - // fontSize: 12, - // ), - // ) - // ], - // ), - // ), - // ], - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextButton( - style: TextButton.styleFrom( - foregroundColor: proColor, - textStyle: const TextStyle(fontSize: 10), - ), - onPressed: () => Utils.openUrl( - ConfigService.to.general.app.links.terms, - ), - child: const Text('Terms of Use'), - ), - const Text('|'), - TextButton( - style: TextButton.styleFrom( - foregroundColor: proColor, - textStyle: const TextStyle(fontSize: 10), - ), - onPressed: () => Utils.openUrl( - ConfigService.to.general.app.links.privacy, - ), - child: const Text('Privacy Policy'), - ), - const Text('|'), - TextButton( - style: TextButton.styleFrom( - foregroundColor: proColor, - textStyle: const TextStyle(fontSize: 10), - ), - onPressed: controller.restore, - child: const Text('Restore Purchases'), - ), - ], - ), - ], - ), - ); - - final appBar = AppBar( - backgroundColor: Get.isDarkMode ? Colors.transparent : null, - elevation: 0, - automaticallyImplyLeading: false, - centerTitle: false, - title: Row( - children: [ - Icon(LineIcons.rocket, color: proColor), - const SizedBox(width: 7), - const ProText(size: 23), - ], - ), - actions: [ - IconButton( - icon: const Icon(LineIcons.times), - onPressed: () { - Persistence.to.upgradeScreenShown.val = true; - Get.back(); - }, - ), - TextButton( - onPressed: () => Utils.adaptiveRouteOpen(name: Routes.feedback), - child: const Text('Need Help ?'), - ), - ], - ); - - const darkDecoration = BoxDecoration( - gradient: LinearGradient( - begin: Alignment.bottomLeft, - end: Alignment.topRight, - colors: [ - Colors.black, - Color(0xFF173030), - ], - ), - ); - - return Container( - decoration: Get.isDarkMode ? darkDecoration : null, - child: DefaultTabController( - length: controller.tabBarItems.length, - child: Scaffold( - appBar: appBar, - body: content, - backgroundColor: Get.isDarkMode ? Colors.transparent : null, - ), - ), - ); - } -} diff --git a/lib/features/pro/upgrade/upgrade_screen.controller.dart b/lib/features/pro/upgrade/upgrade_screen.controller.dart deleted file mode 100644 index bd962938..00000000 --- a/lib/features/pro/upgrade/upgrade_screen.controller.dart +++ /dev/null @@ -1,240 +0,0 @@ -import 'package:console_mixin/console_mixin.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:line_icons/line_icons.dart'; -import 'package:liso/core/firebase/config/config.service.dart'; -import 'package:liso/core/notifications/notifications.manager.dart'; -import 'package:liso/core/utils/globals.dart'; -import 'package:liso/core/utils/ui_utils.dart'; -import 'package:liso/features/pro/pro.controller.dart'; -import 'package:purchases_flutter/purchases_flutter.dart'; - -import '../../../core/firebase/config/models/config_limits.model.dart'; -import '../../../core/utils/utils.dart'; -import '../../app/routes.dart'; -import '../../supabase/model/gumroad_product.model.dart'; -import '../../supabase/supabase_functions.service.dart'; - -class UpgradeScreenController extends GetxController - with StateMixin, ConsoleMixin { - static UpgradeScreenController get to => Get.find(); - - // VARIABLES - - // PROPERTIES - final busy = false.obs; - final tabIndex = 0.obs; - final package = Rx(Package.fromJson(kPackageInitial)); - final gumroadProduct = const Product().obs; - - // GETTERS - String get identifier => package.value.identifier; - - StoreProduct get product => package.value.storeProduct; - - bool get isSubscription => product.identifier.contains('.sub.'); - - String get priceString => - product.introductoryPrice?.priceString ?? product.priceString; - - String get periodUnitName { - if (product.identifier.contains('annual')) { - return 'year'; - } else if (product.identifier.contains('month')) { - return 'month'; - } - - return 'error'; - } - - bool get isFreeTrial => product.introductoryPrice?.price == 0; - - String get promoText { - final intro = product.introductoryPrice!; - - final percentageDifference_ = - ((product.price - intro.price) / product.price) * 100; - - return isFreeTrial - ? '${intro.periodNumberOfUnits} ${GetUtils.capitalizeFirst(intro.periodUnit.name.tr)} ${'free_trial'.tr}' - : '${percentageDifference_.round()}%\nOFF'; - } - - int get limitIndex { - int index = 3; - - switch (ProController.to.limits.id) { - case 'pro': - index = 0; - break; - case 'staker': - index = 1; - break; - case 'holder': - index = 2; - break; - case 'free': - index = 3; - break; - case 'trial': - index = 4; - break; - } - - return index; - } - - List get tabBarItems => [ - const Tab(text: 'Pro'), - if (limitIndex >= 1) ...[ - const Tab(text: 'Staker'), - if (limitIndex >= 2) ...[ - const Tab(text: 'Holder'), - if (limitIndex >= 3) ...[ - const Tab(text: 'Free'), - ] - ] - ], - ]; - - ConfigLimitsTier get selectedLimit { - final limits = ConfigService.to.limits; - var limit_ = limits.free; - - if (tabIndex.value == 0) { - limit_ = limits.pro; - } else if (tabIndex.value == 1) { - limit_ = limits.staker; - } else if (tabIndex.value == 2) { - limit_ = limits.holder; - } else if (tabIndex.value == 3) { - limit_ = limits.free; - } - - return limit_; - } - - // INIT - @override - void onInit() async { - _load(); - change(null, status: RxStatus.success()); - super.onInit(); - } - - @override - void onReady() { - final title = Get.parameters['title']; - final body = Get.parameters['body']; - - if (title != null && body != null) { - UIUtils.showImageDialog( - Icon(LineIcons.rocket, size: 100, color: proColor), - title: title, - body: body, - ); - } - - super.onReady(); - } - - @override - void change(newState, {RxStatus? status}) { - busy.value = status?.isLoading ?? false; - super.change(newState, status: status); - } - - // FUNCTIONS - Future _load() async { - if (!isIAPSupported) return _loadGumroad(); - await ProController.to.load(); - - if (ProController.to.packages.isNotEmpty) { - package.value = ProController.to.packages.first; - } - } - - Future _loadGumroad() async { - change(null, status: RxStatus.loading()); - final result = await SupabaseFunctionsService.to.gumroadProductDetail(); - change(null, status: RxStatus.success()); - - result.fold( - (left) => UIUtils.showSimpleDialog( - 'Gumroad Product Error', - left, - ), - (right) { - gumroadProduct.value = right.product; - console.wtf('gumroad product: ${gumroadProduct.value.formattedPrice}'); - }, - ); - } - - void purchase() async { - if (busy.value) return console.error('still busy'); - - if (!isIAPSupported) { - Utils.openUrl( - ConfigService.to.general.app.links.store.gumroad, - ); - - Get.back(); - - return Utils.adaptiveRouteOpen( - name: Routes.settings, - parameters: {'expand': 'other_settings'}, - ); - } - - if (ProController.to.packages.isEmpty) { - return console.error('empty packages'); - } - - change(null, status: RxStatus.loading()); - - final package = ProController.to.packages.firstWhere( - (e) => e.identifier == identifier, - ); - - await ProController.to.purchase(package); - change(null, status: RxStatus.success()); - - if (ProController.to.isPro) { - NotificationsManager.notify( - title: '${ConfigService.to.appName} Pro Activated', - body: 'Thank you for upgrading', - ); - - Get.back(); - } - } - - void restore() async { - if (busy.value) return console.error('still busy'); - change(null, status: RxStatus.loading()); - await ProController.to.restore(); - change(null, status: RxStatus.success()); - - if (ProController.to.isPro) { - NotificationsManager.notify( - title: '${ConfigService.to.appName} Pro Restored', - body: 'Thanks for being a ${ConfigService.to.appName} Pro rockstar!', - ); - - Get.back(); - } else { - UIUtils.showSimpleDialog( - 'No Purchases', - 'You are not subscribed to ${ConfigService.to.appName} Pro', - ); - } - } -} - -enum Tier { - zero, - one, - two, - three, -} diff --git a/lib/features/rate/rate.widget.dart b/lib/features/rate/rate.widget.dart deleted file mode 100644 index fd49ccf9..00000000 --- a/lib/features/rate/rate.widget.dart +++ /dev/null @@ -1,135 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:get/get.dart'; -import 'package:iconsax/iconsax.dart'; - -import '../../core/firebase/config/config.service.dart'; -import '../../core/utils/utils.dart'; -import '../general/gradient.widget.dart'; -import 'rate_widget.controller.dart'; - -class RateWidget extends StatelessWidget { - const RateWidget({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final controller = Get.put(RateWidgetController()); - - return Form( - key: controller.formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - GradientWidget( - gradient: const LinearGradient( - colors: [ - Color.fromARGB(255, 255, 0, 212), - Color.fromARGB(255, 0, 166, 255), - ], - ), - child: Text( - 'rate_review'.tr, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: Utils.isSmallScreen ? 20 : 32, - fontWeight: FontWeight.bold, - ), - ), - ), - // TextButton.icon( - // onPressed: controller.skip, - // icon: const Icon(Iconsax.close_circle), - // label: Text('close'.tr), - // ), - ], - ), - const SizedBox(height: 15), - TextFormField( - autofocus: true, - controller: controller.textController, - validator: (data) => - data!.split(' ').length < 5 ? 'review_short'.tr : null, - maxLength: 2000, - minLines: 3, - maxLines: 10, - decoration: InputDecoration( - labelText: '${'write_review_here'.tr}...', - alignLabelWithHint: true, - helperMaxLines: 5, - helperText: - "why_love_hate".trParams({'w1': ConfigService.to.appName}), - ), - ), - const SizedBox(height: 20), - RatingBar.builder( - initialRating: controller.rating.value, - minRating: 0, - direction: Axis.horizontal, - itemCount: 5, - itemPadding: const EdgeInsets.symmetric(horizontal: 4.0), - onRatingUpdate: (rating) => controller.rating.value = rating, - itemBuilder: (context, _) => Icon( - Icons.star, - color: Get.theme.primaryColor, - ), - ), - Obx( - () => Visibility( - visible: controller.rating.value == 0.0, - child: Padding( - padding: const EdgeInsets.only(top: 10), - child: Text( - 'please_give_rating'.tr, - style: TextStyle( - color: Colors.pink.shade200, - fontSize: 11, - ), - ), - ), - ), - ), - Obx( - () => Visibility( - visible: controller.rating.value > 0, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 20), - if (controller.rating.value >= 4.0) ...[ - ElevatedButton.icon( - onPressed: controller.submit, - icon: const Icon(Icons.star_border), - label: Text( - 'Submit to ${'rate_review'.tr} ${ConfigService.to.appName}', - ), - ), - const SizedBox(height: 10), - Text( - 'spread_word'.trParams({'w1': ConfigService.to.appName}), - style: const TextStyle(color: Colors.grey, fontSize: 13), - ), - ] else ...[ - ElevatedButton.icon( - onPressed: controller.submit, - icon: const Icon(Iconsax.message_question), - label: Text('send_feedback'.tr), - ), - const SizedBox(height: 10), - Text( - "write_concern_helper".tr, - style: const TextStyle(color: Colors.grey), - ), - ] - ], - ), - ), - ), - ], - ), - ); - } -} diff --git a/lib/features/rate/rate_widget.controller.dart b/lib/features/rate/rate_widget.controller.dart deleted file mode 100644 index b06dc788..00000000 --- a/lib/features/rate/rate_widget.controller.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:console_mixin/console_mixin.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; - -import '../../core/firebase/analytics.service.dart'; -import '../../core/firebase/config/config.service.dart'; -import '../../core/utils/ui_utils.dart'; -import '../../core/utils/utils.dart'; -import '../feedback/feedback_screen.controller.dart'; - -class RateWidgetController extends GetxController with ConsoleMixin { - // VARIABLES - final formKey = GlobalKey(); - final textController = TextEditingController(); - - // PROPERTIES - final rating = 0.0.obs; - - // GETTERS - - // FUNCTIONS - - void skip() async { - Get.back(); - AnalyticsService.to.logEvent('skipped-rate'); - } - - Future submit() async { - if (!formKey.currentState!.validate()) return; - - if (rating.value >= 4.0) { - Utils.copyToClipboard(textController.text); - UIUtils.rateAndReview(); - } else { - Utils.contactEmail( - subject: '${ConfigService.to.appName} Review', - preBody: textController.text, - rating: rating.value, - previousRoute: Get.previousRoute, - feedbackType: FeedbackType.feedback, - ); - } - } -} diff --git a/lib/features/restore/restore.screen.dart b/lib/features/restore/restore.screen.dart index 20dfcc03..b96303a1 100644 --- a/lib/features/restore/restore.screen.dart +++ b/lib/features/restore/restore.screen.dart @@ -1,17 +1,17 @@ +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; -import 'package:liso/features/general/busy_indicator.widget.dart'; import 'package:liso/features/seed/seed_field.widget.dart'; -import '../../core/firebase/config/config.service.dart'; import '../../core/utils/globals.dart'; import '../../core/utils/styles.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../general/appbar_leading.widget.dart'; import '../general/segmented_item.widget.dart'; import 'restore_screen.controller.dart'; diff --git a/lib/features/restore/restore_screen.controller.dart b/lib/features/restore/restore_screen.controller.dart index d3f13668..86d70894 100644 --- a/lib/features/restore/restore_screen.controller.dart +++ b/lib/features/restore/restore_screen.controller.dart @@ -1,5 +1,11 @@ import 'dart:io'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/services/local_auth.service.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:either_dart/either.dart'; import 'package:file_picker/file_picker.dart'; @@ -8,7 +14,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; import 'package:iconsax/iconsax.dart'; -import 'package:liso/core/firebase/config/config.service.dart'; import 'package:liso/core/persistence/persistence.dart'; import 'package:liso/core/services/cipher.service.dart'; import 'package:liso/core/utils/globals.dart'; @@ -17,9 +22,6 @@ import 'package:path/path.dart'; import '../../core/liso/liso.manager.dart'; import '../../core/middlewares/authentication.middleware.dart'; -import '../../core/notifications/notifications.manager.dart'; -import '../../core/services/local_auth.service.dart'; -import '../../core/utils/ui_utils.dart'; import '../../core/utils/utils.dart'; import '../app/routes.dart'; import '../main/main_screen.controller.dart'; @@ -57,7 +59,7 @@ class RestoreScreenController extends GetxController // FUNCTIONS Future> _downloadVault(String address) async { - final statResult = await SupabaseFunctionsService.to.statObject( + final statResult = await AppSupabaseFunctionsService.to.statObject( kVaultFileName, address: address, ); @@ -68,7 +70,7 @@ class RestoreScreenController extends GetxController ); } - final presignResult = await SupabaseFunctionsService.to.presignUrl( + final presignResult = await AppSupabaseFunctionsService.to.presignUrl( object: kVaultFileName, address: address, method: 'GET', @@ -146,7 +148,7 @@ class RestoreScreenController extends GetxController cipherKey: cipherKey, ); // turn on sync setting if successfully imported via cloud - Persistence.to.sync.val = + AppPersistence.to.sync.val = restoreMode.value == RestoreMode.cloud ? true : false; if (isLocalAuthSupported) { @@ -158,10 +160,10 @@ class RestoreScreenController extends GetxController if (!authenticated) return change(null, status: RxStatus.success()); Get.back(); // close dialog AuthenticationMiddleware.signedIn = true; - final password = Utils.generatePassword(); + final password = AppUtils.generatePassword(); await WalletService.to.create(seed, password, false); change(null, status: RxStatus.success()); - Persistence.to.backedUpSeed.val = true; + AppPersistence.to.backedUpSeed.val = true; NotificationsManager.notify( title: 'Welcome back to ${ConfigService.to.appName}', @@ -173,7 +175,7 @@ class RestoreScreenController extends GetxController change(null, status: RxStatus.success()); Utils.adaptiveRouteOpen( - name: Routes.createPassword, + name: AppRoutes.createPassword, parameters: {'seed': seed, 'from': 'restore_screen'}, ); } @@ -188,11 +190,10 @@ class RestoreScreenController extends GetxController action: proceed, actionText: 'Restore', closeText: 'Cancel', - onClose: () { - change(null, status: RxStatus.success()); - Get.back(); - }, + onClose: Get.back, ); + + change(null, status: RxStatus.success()); } void importFile() async { @@ -200,7 +201,7 @@ class RestoreScreenController extends GetxController if (GetPlatform.isAndroid) FilePicker.platform.clearTemporaryFiles(); change(null, status: RxStatus.loading()); - Globals.timeLockEnabled = false; // disable + timeLockEnabled = false; // disable FilePickerResult? result; try { @@ -208,7 +209,7 @@ class RestoreScreenController extends GetxController type: FileType.any, ); } catch (e) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable console.error('FilePicker error: $e'); return; } @@ -216,7 +217,7 @@ class RestoreScreenController extends GetxController change(null, status: RxStatus.success()); if (result == null || result.files.isEmpty) { - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable console.warning("canceled file picker"); return; } diff --git a/lib/features/seed/generator/seed_generator.screen.dart b/lib/features/seed/generator/seed_generator.screen.dart index 4485c07f..ad629a13 100644 --- a/lib/features/seed/generator/seed_generator.screen.dart +++ b/lib/features/seed/generator/seed_generator.screen.dart @@ -1,12 +1,12 @@ +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; -import 'package:liso/core/utils/ui_utils.dart'; +import 'package:liso/core/utils/utils.dart'; import 'package:liso/features/seed/seed_chips.widget.dart'; -import '../../../core/utils/utils.dart'; -import '../../general/appbar_leading.widget.dart'; import '../../general/card_button.widget.dart'; import 'seed_generator_screen.controller.dart'; @@ -53,7 +53,7 @@ class SeedGeneratorScreen extends StatelessWidget with ConsoleMixin { CardButton( text: 'QR Code', iconData: Iconsax.barcode, - onPressed: () => UIUtils.showQR( + onPressed: () => AppUtils.showQR( controller.seed.value, title: 'Your Seed QR Code', subTitle: diff --git a/lib/features/seed/generator/seed_generator_screen.controller.dart b/lib/features/seed/generator/seed_generator_screen.controller.dart index d97bfc6c..7ed64bb5 100644 --- a/lib/features/seed/generator/seed_generator_screen.controller.dart +++ b/lib/features/seed/generator/seed_generator_screen.controller.dart @@ -1,9 +1,9 @@ -import 'package:console_mixin/console_mixin.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:bip39/bip39.dart' as bip39; +import 'package:console_mixin/console_mixin.dart'; import 'package:get/get.dart'; import '../../../core/utils/globals.dart'; -import '../../../core/utils/utils.dart'; import '../../app/routes.dart'; class SeedGeneratorScreenController extends GetxController with ConsoleMixin { @@ -34,7 +34,7 @@ class SeedGeneratorScreenController extends GetxController with ConsoleMixin { void save() { Utils.adaptiveRouteOpen( - name: Routes.item, + name: AppRoutes.item, parameters: { 'mode': 'generated', 'category': LisoItemCategory.cryptoWallet.name, diff --git a/lib/features/seed/seed.screen.dart b/lib/features/seed/seed.screen.dart index ea14c547..eeee9efa 100644 --- a/lib/features/seed/seed.screen.dart +++ b/lib/features/seed/seed.screen.dart @@ -1,17 +1,17 @@ +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; import 'package:blur/blur.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; -import 'package:liso/core/persistence/persistence.dart'; import 'package:liso/features/seed/seed_chips.widget.dart'; import 'package:liso/features/wallet/wallet.service.dart'; +import '../../core/persistence/persistence.dart'; import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../general/appbar_leading.widget.dart'; import '../menu/menu.button.dart'; import 'seed_screen.controller.dart'; @@ -75,7 +75,8 @@ class SeedScreen extends StatelessWidget with ConsoleMixin { const SizedBox(height: 20), seedPhrase, const SizedBox(height: 20), - if (!Persistence.to.backedUpSeed.val || !WalletService.to.isSaved) ...[ + if (!AppPersistence.to.backedUpSeed.val || + !WalletService.to.isSaved) ...[ ObxValue( (RxBool data) => CheckboxListTile( checkboxShape: const CircleBorder(), diff --git a/lib/features/seed/seed_chips.widget.dart b/lib/features/seed/seed_chips.widget.dart index 70ab2772..804a4b56 100644 --- a/lib/features/seed/seed_chips.widget.dart +++ b/lib/features/seed/seed_chips.widget.dart @@ -1,6 +1,6 @@ +import 'package:app_core/globals.dart'; import 'package:flutter/material.dart'; -import '../../core/utils/utils.dart'; import '../general/custom_chip.widget.dart'; class SeedChips extends StatelessWidget { @@ -11,8 +11,8 @@ class SeedChips extends StatelessWidget { @override Widget build(BuildContext context) { return Wrap( - spacing: Utils.isSmallScreen ? 5 : 5, - runSpacing: Utils.isSmallScreen ? 5 : 10, + spacing: isSmallScreen ? 5 : 5, + runSpacing: isSmallScreen ? 5 : 10, alignment: WrapAlignment.center, children: seeds .asMap() @@ -22,7 +22,7 @@ class SeedChips extends StatelessWidget { label: Text( '${e.key + 1}. ${e.value}', style: TextStyle( - fontSize: Utils.isSmallScreen ? 15 : 18, + fontSize: isSmallScreen ? 15 : 18, ), ), ), diff --git a/lib/features/seed/seed_field.widget.dart b/lib/features/seed/seed_field.widget.dart index f9e099ad..8ffe4fa4 100644 --- a/lib/features/seed/seed_field.widget.dart +++ b/lib/features/seed/seed_field.widget.dart @@ -1,3 +1,5 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:bip39/bip39.dart' as bip39; import 'package:blur/blur.dart'; import 'package:console_mixin/console_mixin.dart'; @@ -6,11 +8,9 @@ import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; +import 'package:liso/core/utils/utils.dart'; import 'package:liso/features/menu/menu.button.dart'; -import '../../core/utils/globals.dart'; -import '../../core/utils/ui_utils.dart'; -import '../../core/utils/utils.dart'; import '../app/routes.dart'; import '../menu/menu.item.dart'; @@ -38,7 +38,7 @@ class SeedField extends StatelessWidget with ConsoleMixin { void _generate() async { final seed = await Utils.adaptiveRouteOpen( - name: Routes.seedGenerator, + name: AppRoutes.seedGenerator, parameters: {'return': 'true'}, ); @@ -77,7 +77,7 @@ class SeedField extends StatelessWidget with ConsoleMixin { onSelected: () { if (fieldController!.text.isEmpty) return; - UIUtils.showQR( + AppUtils.showQR( fieldController!.text, title: 'Your Seed QR Code', subTitle: diff --git a/lib/features/seed/seed_screen.controller.dart b/lib/features/seed/seed_screen.controller.dart index 35ebe3da..eccf1c24 100644 --- a/lib/features/seed/seed_screen.controller.dart +++ b/lib/features/seed/seed_screen.controller.dart @@ -1,3 +1,7 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:bip39/bip39.dart' as bip39; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -5,12 +9,9 @@ import 'package:iconsax/iconsax.dart'; import 'package:liso/core/persistence/persistence.dart'; import 'package:liso/core/utils/utils.dart'; import 'package:liso/features/app/routes.dart'; -import 'package:bip39/bip39.dart' as bip39; import 'package:liso/features/seed/seed_field.widget.dart'; -import '../../core/utils/globals.dart'; import '../items/items.service.dart'; -import '../../core/utils/ui_utils.dart'; import '../menu/menu.item.dart'; class SeedScreenController extends GetxController with ConsoleMixin { @@ -82,11 +83,11 @@ class SeedScreenController extends GetxController with ConsoleMixin { // FUNCTIONS void continuePressed() async { - Persistence.to.backedUpSeed.val = true; + AppPersistence.to.backedUpSeed.val = true; if (isDisplayMode) return Get.back(); Utils.adaptiveRouteOpen( - name: Routes.createPassword, + name: AppRoutes.createPassword, parameters: { 'seed': seed.value, 'from': 'seed_screen', @@ -95,7 +96,7 @@ class SeedScreenController extends GetxController with ConsoleMixin { } void _showQR() { - UIUtils.showQR( + AppUtils.showQR( seed.value, title: 'Seed QR Code', subTitle: "Make sure you're in a safe location and free from prying eyes", @@ -104,7 +105,7 @@ class SeedScreenController extends GetxController with ConsoleMixin { void _generate() async { final seed_ = await Utils.adaptiveRouteOpen( - name: Routes.seedGenerator, + name: AppRoutes.seedGenerator, parameters: {'return': 'true'}, ); @@ -133,7 +134,7 @@ class SeedScreenController extends GetxController with ConsoleMixin { Get.dialog(AlertDialog( title: const Text('Enter Your Seed'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : SizedBox( width: 450, diff --git a/lib/features/settings/settings.screen.dart b/lib/features/settings/settings.screen.dart index 97c59de0..b72a0b3e 100644 --- a/lib/features/settings/settings.screen.dart +++ b/lib/features/settings/settings.screen.dart @@ -1,3 +1,13 @@ +import 'package:app_core/controllers/pro.controller.dart'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/persistence/persistence_builder.widget.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; +import 'package:app_core/widgets/pro.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -5,19 +15,11 @@ import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; import 'package:liso/core/persistence/persistence.dart'; -import 'package:liso/features/autofill/autofill.service.dart'; import 'package:liso/core/utils/globals.dart'; -import 'package:liso/core/utils/utils.dart'; import 'package:liso/features/app/routes.dart'; +import 'package:liso/features/autofill/autofill.service.dart'; import 'package:liso/features/menu/menu.button.dart'; -import 'package:liso/features/pro/pro.controller.dart'; -import '../../core/firebase/config/config.service.dart'; -import '../../core/persistence/persistence_builder.widget.dart'; -import '../../core/utils/ui_utils.dart'; -import '../general/appbar_leading.widget.dart'; -import '../general/busy_indicator.widget.dart'; -import '../general/pro.widget.dart'; import '../menu/menu.item.dart'; import 'settings_screen.controller.dart'; @@ -81,9 +83,9 @@ class SettingsScreen extends StatelessWidget with ConsoleMixin { SwitchListTile( title: const Text('Enabled'), secondary: Icon(Iconsax.cloud, color: themeColor), - value: p.sync.val, + value: AppPersistence.to.sync.val, subtitle: const Text("Keep multiple devices in sync"), - onChanged: (value) => p.sync.val = value, + onChanged: (value) => AppPersistence.to.sync.val = value, ), // TODO: temporary // if (p.sync.val) ...[ @@ -121,16 +123,17 @@ class SettingsScreen extends StatelessWidget with ConsoleMixin { trailing: const Icon(Iconsax.arrow_right_3), title: const Text('Custom Categories'), subtitle: const Text('Manage your custom categories'), - onTap: () => Utils.adaptiveRouteOpen(name: Routes.categories), + onTap: () => + Utils.adaptiveRouteOpen(name: AppRoutes.categories), ), ListTile( leading: Icon(Iconsax.briefcase, color: themeColor), trailing: const Icon(Iconsax.arrow_right_3), title: const Text('Custom Vaults'), subtitle: const Text('Manage your custom vaults'), - onTap: () => Utils.adaptiveRouteOpen(name: Routes.vaults), + onTap: () => Utils.adaptiveRouteOpen(name: AppRoutes.vaults), ), - if (Persistence.to.canShare) ...[ + if (AppPersistence.to.canShare) ...[ // TODO: temporary // ListTile( // leading: Icon(Iconsax.share, color: themeColor), @@ -156,7 +159,7 @@ class SettingsScreen extends StatelessWidget with ConsoleMixin { leading: Icon(Iconsax.box, color: themeColor), trailing: const Icon(Iconsax.arrow_right_3), onTap: () async { - if (!p.sync.val) { + if (!AppPersistence.to.sync.val) { return UIUtils.showSimpleDialog( 'Sync Required', 'Please turn on ${config.appName} Cloud Sync to use this feature', @@ -164,7 +167,7 @@ class SettingsScreen extends StatelessWidget with ConsoleMixin { } Utils.adaptiveRouteOpen( - name: Routes.s3Explorer, + name: AppRoutes.s3Explorer, parameters: {'type': 'time_machine'}, ); }, @@ -175,7 +178,7 @@ class SettingsScreen extends StatelessWidget with ConsoleMixin { trailing: const Icon(Iconsax.arrow_right_3), title: const Text('Import Items'), subtitle: const Text('Import items from external sources'), - onTap: () => Utils.adaptiveRouteOpen(name: Routes.import), + onTap: () => Utils.adaptiveRouteOpen(name: AppRoutes.import), ), ContextMenuButton( padding: EdgeInsets.zero, @@ -368,7 +371,7 @@ class SettingsScreen extends StatelessWidget with ConsoleMixin { onTap: controller.reset, onLongPress: () => Utils.adaptiveRouteOpen(name: Routes.debug), ), - if (Persistence.to.canShare) ...[ + if (AppPersistence.to.canShare) ...[ ListTile( iconColor: Colors.red, leading: const Icon(Iconsax.warning_2), diff --git a/lib/features/settings/settings_screen.controller.dart b/lib/features/settings/settings_screen.controller.dart index ae9b5232..3081b2a8 100644 --- a/lib/features/settings/settings_screen.controller.dart +++ b/lib/features/settings/settings_screen.controller.dart @@ -1,18 +1,24 @@ import 'dart:convert'; import 'dart:io'; +import 'package:app_core/controllers/pro.controller.dart'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/persistence/persistence.dart'; +import 'package:app_core/supabase/supabase_auth.service.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:intl/intl.dart'; -import 'package:liso/core/firebase/config/config.service.dart'; import 'package:liso/core/liso/liso_paths.dart'; -import 'package:liso/core/persistence/persistence.dart'; import 'package:liso/core/utils/file.util.dart'; import 'package:liso/core/utils/globals.dart'; -import 'package:liso/core/utils/ui_utils.dart'; import 'package:liso/features/autofill/autofill.service.dart'; import 'package:liso/features/categories/categories.service.dart'; import 'package:liso/features/files/sync.service.dart'; @@ -23,15 +29,11 @@ import 'package:path/path.dart'; import 'package:share_plus/share_plus.dart'; import '../../core/liso/liso.manager.dart'; -import '../../core/notifications/notifications.manager.dart'; import '../../core/persistence/persistence.secret.dart'; import '../../core/services/cipher.service.dart'; -import '../../core/utils/utils.dart'; import '../app/routes.dart'; import '../groups/groups.service.dart'; import '../menu/menu.item.dart'; -import '../pro/pro.controller.dart'; -import '../supabase/supabase_auth.service.dart'; class SettingsScreenController extends GetxController with ConsoleMixin, StateMixin { @@ -98,13 +100,13 @@ class SettingsScreenController extends GetxController await Future.delayed(200.milliseconds); ItemsController.to.load(); - if (GetPlatform.isDesktop && !GetPlatform.isWeb) { + if (isDesktop) { MainScreenController.to.window.setBrightness( mode == ThemeMode.dark ? Brightness.dark : Brightness.light, ); } - if (!Utils.isSmallScreen) Get.back(); + if (!isSmallScreen) Get.back(); } void exportWallet() async { @@ -148,13 +150,13 @@ class SettingsScreenController extends GetxController } change('Choose export path...', status: RxStatus.loading()); - Globals.timeLockEnabled = false; // temporarily disable + timeLockEnabled = false; // temporarily disable // choose directory and export file final exportPath = await FilePicker.platform.getDirectoryPath( dialogTitle: 'Choose Export Path', ); - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable // user cancelled picker if (exportPath == null) { return change(null, status: RxStatus.success()); @@ -230,13 +232,13 @@ class SettingsScreenController extends GetxController } change('Choose export path...', status: RxStatus.loading()); - Globals.timeLockEnabled = false; // temporarily disable + timeLockEnabled = false; // temporarily disable // choose directory and export file final exportPath = await FilePicker.platform.getDirectoryPath( dialogTitle: 'Choose Export Path', ); - Globals.timeLockEnabled = true; // re-enable + timeLockEnabled = true; // re-enable // user cancelled picker if (exportPath == null) { return change(null, status: RxStatus.success()); @@ -285,7 +287,7 @@ class SettingsScreenController extends GetxController if (!unlocked) return; Utils.adaptiveRouteOpen( - name: Routes.seed, + name: AppRoutes.seed, parameters: {'mode': 'display'}, ); } @@ -458,18 +460,15 @@ class SettingsScreenController extends GetxController subtitle: Text(ProController.to.isPro.toString()), dense: true, ), + // TODO: temporary // ListTile( - // title: const Text('Free Trial'), - // subtitle: Text(ProController.to.isFreeTrial.toString()), + // title: const Text('Limits'), + // subtitle: Text(limits.id), + // dense: true, // ), - ListTile( - title: const Text('Limits'), - subtitle: Text(ProController.to.limits.id), - dense: true, - ), ListTile( title: const Text('App Version'), - subtitle: Text(Globals.metadata?.app.formattedVersion ?? ''), + subtitle: Text(metadataApp.formattedVersion), dense: true, ), ListTile( diff --git a/lib/features/shared_vaults/shared_vaults.screen.dart b/lib/features/shared_vaults/shared_vaults.screen.dart index d9e4b934..4933f589 100644 --- a/lib/features/shared_vaults/shared_vaults.screen.dart +++ b/lib/features/shared_vaults/shared_vaults.screen.dart @@ -1,23 +1,23 @@ import 'dart:convert'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; +import 'package:app_core/widgets/remote_image.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; -import 'package:liso/core/utils/ui_utils.dart'; import 'package:liso/features/items/items.service.dart'; import 'package:qr_flutter/qr_flutter.dart'; -import '../../core/firebase/config/config.service.dart'; import '../../core/persistence/persistence.dart'; -import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../general/appbar_leading.widget.dart'; -import '../general/busy_indicator.widget.dart'; import '../general/centered_placeholder.widget.dart'; -import '../general/remote_image.widget.dart'; import '../menu/menu.button.dart'; import '../menu/menu.item.dart'; import 'shared_vault.controller.dart'; @@ -122,9 +122,8 @@ class SharedVaultsScreen extends StatelessWidget with ConsoleMixin { Get.dialog(AlertDialog( title: Text(vault.name), - content: Utils.isSmallScreen - ? content - : SizedBox(width: 450, child: content), + content: + isSmallScreen ? content : SizedBox(width: 450, child: content), actions: [ TextButton( onPressed: Get.back, @@ -139,7 +138,7 @@ class SharedVaultsScreen extends StatelessWidget with ConsoleMixin { } final menuItems = [ - if (Persistence.to.canShare) ...[ + if (AppPersistence.to.canShare) ...[ ContextMenuItem( title: 'share'.tr, leading: Icon(Iconsax.share, size: popupIconSize), diff --git a/lib/features/shared_vaults/shared_vaults_screen.controller.dart b/lib/features/shared_vaults/shared_vaults_screen.controller.dart index b695754d..2dd8fa6b 100644 --- a/lib/features/shared_vaults/shared_vaults_screen.controller.dart +++ b/lib/features/shared_vaults/shared_vaults_screen.controller.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -9,10 +11,6 @@ import 'package:iconsax/iconsax.dart'; import 'package:liso/features/shared_vaults/model/shared_vault.model.dart'; import 'package:liso/features/shared_vaults/shared_vault.controller.dart'; -import '../../core/firebase/config/config.service.dart'; -import '../../core/notifications/notifications.manager.dart'; -import '../../core/utils/utils.dart'; - class SharedVaultsScreenController extends GetxController with ConsoleMixin { static SharedVaultsScreenController get to => Get.find(); @@ -63,19 +61,20 @@ class SharedVaultsScreenController extends GetxController with ConsoleMixin { } void _showForm() async { - void done() { - // clear fields - nameController.clear(); - descriptionController.clear(); - cipherKeyController.clear(); - - NotificationsManager.notify( - title: 'Shared Vault ${createMode ? 'Created' : 'Updated'}', - body: nameController.text, - ); - - Get.back(); - } + // TODO: temporary + // void done() { + // // clear fields + // nameController.clear(); + // descriptionController.clear(); + // cipherKeyController.clear(); + + // NotificationsManager.notify( + // title: 'Shared Vault ${createMode ? 'Created' : 'Updated'}', + // body: nameController.text, + // ); + + // Get.back(); + // } void edit() async { // TODO: temporary @@ -111,13 +110,13 @@ class SharedVaultsScreenController extends GetxController with ConsoleMixin { // } // if (SharedVaultsController.to.data.length >= - // ProController.to.limits.sharedVaults) { + // limits.sharedVaults) { // return Utils.adaptiveRouteOpen( // name: Routes.upgrade, // parameters: { // 'title': 'Shared Vaults', // 'body': - // 'Maximum members: ${ProController.to.limits.sharedMembers} in shared vault reached. Upgrade to Pro to unlock unlimited shared vault members feature.', + // 'Maximum members: ${limits.sharedMembers} in shared vault reached. Upgrade to Pro to unlock unlimited shared vault members feature.', // }, // ); // } @@ -272,9 +271,7 @@ class SharedVaultsScreenController extends GetxController with ConsoleMixin { title: Text('${createMode ? 'New' : 'Update'} Shared Vault'), content: Form( key: formKey, - child: Utils.isSmallScreen - ? content - : SizedBox(width: 450, child: content), + child: isSmallScreen ? content : SizedBox(width: 450, child: content), ), actions: [ TextButton( @@ -330,7 +327,7 @@ class SharedVaultsScreenController extends GetxController with ConsoleMixin { Get.dialog(AlertDialog( title: Text('Delete ${object_.name}'), - content: Utils.isSmallScreen + content: isSmallScreen ? dialogContent : SizedBox( width: 450, diff --git a/lib/features/supabase/model/device.model.dart b/lib/features/supabase/model/device.model.dart deleted file mode 100644 index a0d2489e..00000000 --- a/lib/features/supabase/model/device.model.dart +++ /dev/null @@ -1,43 +0,0 @@ -class SupabaseDevice { - SupabaseDevice({ - this.id = 0, - this.userId = '', - this.createdAt, - this.updatedAt, - this.platform = '', - this.info = const {}, - this.deviceId = '', - }); - - final int id; - final String userId; - final DateTime? createdAt; - final DateTime? updatedAt; - final String platform; - final Map? info; - final String deviceId; - - factory SupabaseDevice.fromJson(Map json) => SupabaseDevice( - id: json["id"], - userId: json["user_id"], - createdAt: json["created_at"] == null - ? null - : DateTime.parse(json["created_at"]), - updatedAt: json["updated_at"] == null - ? null - : DateTime.parse(json["updated_at"]), - platform: json["platform"], - info: json["info"], - deviceId: json["device_id"], - ); - - Map toJson() => { - "id": id, - "user_id": userId, - "created_at": createdAt?.toIso8601String(), - "updated_at": updatedAt?.toIso8601String(), - "platform": platform, - "info": info, - "device_id": deviceId, - }; -} diff --git a/lib/features/supabase/model/entitlement_response.model.dart b/lib/features/supabase/model/entitlement_response.model.dart deleted file mode 100644 index 8e371d0b..00000000 --- a/lib/features/supabase/model/entitlement_response.model.dart +++ /dev/null @@ -1,22 +0,0 @@ -class EntitlementResponse { - EntitlementResponse({ - this.entitled = false, - this.expiresAt, - }); - - final bool entitled; - final DateTime? expiresAt; - - factory EntitlementResponse.fromJson(Map json) => - EntitlementResponse( - entitled: json["entitled"], - expiresAt: json["expires_at"] == null - ? null - : DateTime.parse(json["expires_at"]), - ); - - Map toJson() => { - "entitled": entitled, - "expires_at": expiresAt?.toIso8601String(), - }; -} diff --git a/lib/features/supabase/model/error.model.dart b/lib/features/supabase/model/error.model.dart deleted file mode 100644 index 70757db4..00000000 --- a/lib/features/supabase/model/error.model.dart +++ /dev/null @@ -1,20 +0,0 @@ -class SupabaseFunctionError { - SupabaseFunctionError({ - this.code = 0, - this.message = '', - }); - - final int code; - final String message; - - factory SupabaseFunctionError.fromJson(Map json) => - SupabaseFunctionError( - code: json["code"], - message: json["message"], - ); - - Map toJson() => { - "code": code, - "message": message, - }; -} diff --git a/lib/features/supabase/model/gumroad_product.model.dart b/lib/features/supabase/model/gumroad_product.model.dart deleted file mode 100644 index c5f41607..00000000 --- a/lib/features/supabase/model/gumroad_product.model.dart +++ /dev/null @@ -1,123 +0,0 @@ -class GumroadProduct { - GumroadProduct({ - this.success = false, - this.product = const Product(), - }); - - final bool success; - final Product product; - - factory GumroadProduct.fromJson(Map json) => GumroadProduct( - success: json["success"], - product: Product.fromJson(json["product"]), - ); - - Map toJson() => { - "success": success, - "product": product.toJson(), - }; -} - -class Product { - const Product({ - this.name = '', - this.previewUrl = '', - this.description = '', - this.customizablePrice = false, - this.requireShipping = false, - this.customReceipt = '', - this.customPermalink = '', - this.subscriptionDuration = '', - this.id = '', - this.url = '', - this.price = 0, - this.currency = '', - this.shortUrl = '', - this.thumbnailUrl = '', - this.formattedPrice = 'Loading...', - this.published = false, - this.shownOnProfile = false, - this.deleted = false, - this.customSummary = '', - this.isTieredMembership = false, - this.recurrences = const [], - this.salesCount = 0, - this.salesUsdCents = 0, - }); - - final String name; - final String previewUrl; - final String description; - final bool customizablePrice; - final bool requireShipping; - final String customReceipt; - final String customPermalink; - final String subscriptionDuration; - final String id; - final dynamic url; - final int price; - final String currency; - final String shortUrl; - final String thumbnailUrl; - final String formattedPrice; - final bool published; - final bool shownOnProfile; - final bool deleted; - final String customSummary; - final bool isTieredMembership; - final List recurrences; - final int salesCount; - final int salesUsdCents; - - factory Product.fromJson(Map json) => Product( - name: json["name"], - previewUrl: json["preview_url"], - description: json["description"], - customizablePrice: json["customizable_price"], - requireShipping: json["require_shipping"], - customReceipt: json["custom_receipt"], - customPermalink: json["custom_permalink"], - subscriptionDuration: json["subscription_duration"], - id: json["id"], - url: json["url"], - price: json["price"], - currency: json["currency"], - shortUrl: json["short_url"], - thumbnailUrl: json["thumbnail_url"], - formattedPrice: json["formatted_price"], - published: json["published"], - shownOnProfile: json["shown_on_profile"], - deleted: json["deleted"], - customSummary: json["custom_summary"], - isTieredMembership: json["is_tiered_membership"], - recurrences: List.from(json["recurrences"].map((x) => x)), - salesCount: json["sales_count"], - salesUsdCents: json["sales_usd_cents"], - ); - - Map toJson() => { - "name": name, - "preview_url": previewUrl, - "description": description, - "customizable_price": customizablePrice, - "require_shipping": requireShipping, - "custom_receipt": customReceipt, - "custom_permalink": customPermalink, - "subscription_duration": subscriptionDuration, - "id": id, - "url": url, - "price": price, - "currency": currency, - "short_url": shortUrl, - "thumbnail_url": thumbnailUrl, - "formatted_price": formattedPrice, - "published": published, - "shown_on_profile": shownOnProfile, - "deleted": deleted, - "custom_summary": customSummary, - "is_tiered_membership": isTieredMembership, - "recurrences": List.from(recurrences.map((x) => x)), - "sales_count": salesCount, - "sales_usd_cents": salesUsdCents, - }; -} diff --git a/lib/features/supabase/model/list_objects_response.model.dart b/lib/features/supabase/model/list_objects_response.model.dart index f80336ff..967e6cfa 100644 --- a/lib/features/supabase/model/list_objects_response.model.dart +++ b/lib/features/supabase/model/list_objects_response.model.dart @@ -1,4 +1,5 @@ -import 'error.model.dart'; +import 'package:app_core/supabase/model/error.model.dart'; + import 'object.model.dart'; class ListObjectsResponse { diff --git a/lib/features/supabase/model/object.model.dart b/lib/features/supabase/model/object.model.dart index c3304bee..ca69adbb 100644 --- a/lib/features/supabase/model/object.model.dart +++ b/lib/features/supabase/model/object.model.dart @@ -1,7 +1,7 @@ +import 'package:app_core/utils/utils.dart'; import 'package:path/path.dart'; import '../../../core/utils/globals.dart'; -import '../../../core/utils/utils.dart'; import '../../../features/files/explorer/file_extensions.dart'; class S3Object { diff --git a/lib/features/supabase/model/presign_response.model.dart b/lib/features/supabase/model/presign_response.model.dart index 7910884b..f3ccdd02 100644 --- a/lib/features/supabase/model/presign_response.model.dart +++ b/lib/features/supabase/model/presign_response.model.dart @@ -1,4 +1,4 @@ -import 'error.model.dart'; +import 'package:app_core/supabase/model/error.model.dart'; class PresignUrlResponse { PresignUrlResponse({ diff --git a/lib/features/supabase/model/profile.model.dart b/lib/features/supabase/model/profile.model.dart deleted file mode 100644 index f99d09cd..00000000 --- a/lib/features/supabase/model/profile.model.dart +++ /dev/null @@ -1,44 +0,0 @@ -class SupabaseProfile { - SupabaseProfile({ - this.id = '', - this.createdAt, - this.updatedAt, - this.gumroadLicenseKey = '', - this.revenuecatUserId = '', - this.address = '', - this.email = '', - }); - - final String id; - final DateTime? createdAt; - final DateTime? updatedAt; - final String gumroadLicenseKey; - final String revenuecatUserId; - final String address; - final String email; - - factory SupabaseProfile.fromJson(Map json) => - SupabaseProfile( - id: json["id"], - createdAt: json["created_at"] == null - ? null - : DateTime.parse(json["created_at"]), - updatedAt: json["updated_at"] == null - ? null - : DateTime.parse(json["updated_at"]), - gumroadLicenseKey: json["gumroad_license_key"] ?? '', - revenuecatUserId: json["revenuecat_user_id"] ?? '', - address: json["address"] ?? '', - email: json["email"] ?? '', - ); - - Map toJson() => { - "id": id, - "created_at": createdAt?.toIso8601String(), - "updated_at": updatedAt?.toIso8601String(), - "gumroad_license_key": gumroadLicenseKey, - "revenuecat_user_id": revenuecatUserId, - "address": address, - "email": email, - }; -} diff --git a/lib/features/supabase/model/server_response.model.dart b/lib/features/supabase/model/server_response.model.dart deleted file mode 100644 index 896c6f8d..00000000 --- a/lib/features/supabase/model/server_response.model.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'error.model.dart'; - -class ServerResponse { - ServerResponse({ - this.status = 0, - this.data = const {}, - this.errors = const [], - }); - - final int status; - final Map data; - final List errors; - - factory ServerResponse.fromJson(Map json) => ServerResponse( - status: json["status"], - data: json["data"], - errors: List.from( - json["errors"].map((x) => SupabaseFunctionError.fromJson(x))), - ); - - Map toJson() => { - "status": status, - "data": data, - "errors": List.from(errors.map((x) => x.toJson())), - }; -} diff --git a/lib/features/supabase/model/stat_response.model.dart b/lib/features/supabase/model/stat_response.model.dart index f283ab15..b80be816 100644 --- a/lib/features/supabase/model/stat_response.model.dart +++ b/lib/features/supabase/model/stat_response.model.dart @@ -1,4 +1,5 @@ -import 'error.model.dart'; +import 'package:app_core/supabase/model/error.model.dart'; + import 'object.model.dart'; class StatObjectResponse { diff --git a/lib/features/supabase/model/sync_user_response.model.dart b/lib/features/supabase/model/sync_user_response.model.dart deleted file mode 100644 index 6114e464..00000000 --- a/lib/features/supabase/model/sync_user_response.model.dart +++ /dev/null @@ -1,32 +0,0 @@ -class SyncUserResponse { - SyncUserResponse({ - this.profileId = '', - this.deviceId = '', - this.sessionId = 0, - this.licenseKey = '', - this.rcUserId = '', - }); - - final String profileId; - final String deviceId; - final int sessionId; - final String licenseKey; - final String rcUserId; - - factory SyncUserResponse.fromJson(Map json) => - SyncUserResponse( - profileId: json["profileId"], - deviceId: json["deviceId"], - sessionId: json["sessionId"], - licenseKey: json["licenseKey"] ?? '', - rcUserId: json["rcUserId"] ?? '', - ); - - Map toJson() => { - "profileId": profileId, - "deviceId": deviceId, - "sessionId": sessionId, - "licenseKey": licenseKey, - "rcUserId": rcUserId, - }; -} diff --git a/lib/features/supabase/supabase_auth.service.dart b/lib/features/supabase/supabase_auth.service.dart deleted file mode 100644 index 02817b6a..00000000 --- a/lib/features/supabase/supabase_auth.service.dart +++ /dev/null @@ -1,143 +0,0 @@ -import 'package:console_mixin/console_mixin.dart'; -import 'package:easy_debounce/easy_debounce.dart'; -import 'package:get/get.dart'; -import 'package:supabase/supabase.dart'; - -import '../../core/firebase/analytics.service.dart'; -import '../../core/firebase/config/config.service.dart'; -import '../../core/firebase/crashlytics.service.dart'; -import '../../core/persistence/persistence.dart'; -import '../../core/persistence/persistence.secret.dart'; -import '../../core/utils/globals.dart'; -import '../pro/pro.controller.dart'; -import '../wallet/wallet.service.dart'; - -class SupabaseAuthService extends GetxService with ConsoleMixin { - static SupabaseAuthService get to => Get.find(); - - // VARIABLES - final persistence = Get.find(); - final config = Get.find(); - - SupabaseClient? client; - - // GETTERS - bool get authenticated => user != null; - User? get user => client?.auth.currentUser; - - @override - void onReady() { - init(); - super.onReady(); - } - - // FUNCTIONS - Future init() async { - client = SupabaseClient( - config.secrets.supabase.url, - config.secrets.supabase.key, - ); - - initAuthState(); - recoverSession(); - } - - void initAuthState() { - client!.auth.onAuthStateChange.listen((data) { - console.info( - 'onAuthStateChange! ${data.event}: user id: ${data.session?.user.id}}', - ); - - persistence.supabaseSession.val = - data.session?.persistSessionString ?? ''; - - if (data.event == AuthChangeEvent.signedIn) { - EasyDebounce.debounce('auth-sign-in', 5.seconds, () async { - if (!authenticated) return; - - if (!isWindowsLinux) { - await CrashlyticsService.to.instance.setUserIdentifier(user!.id); - await AnalyticsService.to.instance.setUserId(id: user!.id); - } - - ProController.to.login(user!); - AnalyticsService.to.logSignIn(); - - // refresh token 2 minutes before expiration time - if (data.session?.expiresIn != null) { - final refreshAfter = (data.session!.expiresIn! - 120); - Future.delayed(refreshAfter.seconds) - .then((value) => recoverSession()); - } - }); - } else if (data.event == AuthChangeEvent.signedOut) { - EasyDebounce.debounce('auth-sign-out', 5.seconds, () { - ProController.to.logout(); - AnalyticsService.to.logSignOut(); - }); - } else if (data.event == AuthChangeEvent.tokenRefreshed) { - // - } else if (data.event == AuthChangeEvent.userUpdated) { - // - } - }); - } - - Future recoverSession() async { - var sessionString = persistence.supabaseSession.val; - if (sessionString.isEmpty) return console.warning('no supabase session'); - - try { - final session = await client!.auth.recoverSession(sessionString); - console.info('recovered session! user id: ${session.user?.id}'); - } on AuthException catch (e) { - console.error('recover session error: $e'); - } catch (e, s) { - CrashlyticsService.to.record(e, s); - console.error('recover session exception: $e'); - } - } - - Future signIn(String email, String password) async { - try { - await client!.auth.signInWithPassword(email: email, password: password); - } on AuthException catch (e) { - // invalid credentials - if (e.statusCode == '400') { - return console.error('signIn error: $e'); - } else { - return console.error('signIn error: $e'); - } - } catch (e, s) { - CrashlyticsService.to.record(e, s); - return console.error('signIn exception: $e'); - } - } - - Future authenticate() async { - if (authenticated) return console.info('already authenticated'); - final address = SecretPersistence.to.walletAddress.val; - final email = '$address@liso.dev'; - final password = await WalletService.to.sign(kAuthSignatureMessage); - - try { - await client!.auth.signUp(email: email, password: password); - } on AuthException catch (e) { - // already registered - if (e.statusCode == '400') { - await signIn(email, password); - } else { - return console.error('signUp error: $e'); - } - } catch (e, s) { - CrashlyticsService.to.record(e, s); - return console.error('signIn exception: $e'); - } - - console.wtf('authentication successful'); - } - - Future signOut() async { - client!.auth.signOut(); - } -} diff --git a/lib/features/supabase/supabase_database.service.dart b/lib/features/supabase/supabase_database.service.dart deleted file mode 100644 index 08281dca..00000000 --- a/lib/features/supabase/supabase_database.service.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:console_mixin/console_mixin.dart'; -import 'package:either_dart/either.dart'; -import 'package:get/get.dart'; - -import 'model/profile.model.dart'; -import 'supabase_auth.service.dart'; - -class SupabaseDBService extends GetxService with ConsoleMixin { - static SupabaseDBService get to => Get.find(); - - // VARIABLES - final auth = Get.find(); - - // GETTERS - - // FUNCTIONS - - Future> updateLicenseKey(String key) async { - if (!auth.authenticated) { - console.warning('not authenticated'); - return const Left('not authenticated'); - } - - // UPDATE PROFILE - try { - final response = await auth.client!.from('profiles').upsert( - { - 'id': auth.user!.id, - 'gumroad_license_key': key, - 'updated_at': 'now()', - }, - ).select(); - - // console.info('Upsert success! $response'); - - if (response.isEmpty) { - return const Left('Upsert Profile Error: empty data response'); - } - - final profile = SupabaseProfile.fromJson(response.first); - return Right(profile); - } catch (e) { - return Left('Upsert Profile Error: $e'); - } - } -} diff --git a/lib/features/supabase/supabase_functions.service.dart b/lib/features/supabase/supabase_functions.service.dart index 56c786a9..0a02028f 100644 --- a/lib/features/supabase/supabase_functions.service.dart +++ b/lib/features/supabase/supabase_functions.service.dart @@ -1,39 +1,28 @@ -import 'dart:convert'; - -import 'package:console_mixin/console_mixin.dart'; +import 'package:app_core/persistence/persistence.dart'; +import 'package:app_core/supabase/model/server_response.model.dart'; +import 'package:app_core/supabase/supabase_functions.service.dart'; import 'package:either_dart/either.dart'; import 'package:get/get.dart'; -import 'package:purchases_flutter/purchases_flutter.dart'; +import 'package:liso/core/liso/liso.manager.dart'; import 'package:secrets/secrets.dart'; +import 'package:purchases_flutter/purchases_flutter.dart'; -import '../../core/firebase/config/config.service.dart'; -import '../../core/liso/liso.manager.dart'; import '../../core/persistence/persistence.dart'; import '../../core/persistence/persistence.secret.dart'; -import '../../core/services/cipher.service.dart'; -import '../../core/utils/globals.dart'; import '../categories/categories.service.dart'; import '../files/storage.service.dart'; import '../groups/groups.service.dart'; import '../items/items.service.dart'; import '../joined_vaults/joined_vault.controller.dart'; -import '../pro/pro.controller.dart'; import '../shared_vaults/shared_vault.controller.dart'; -import 'model/entitlement_response.model.dart'; -import 'model/gumroad_product.model.dart'; import 'model/list_objects_response.model.dart'; import 'model/presign_response.model.dart'; -import 'model/server_response.model.dart'; import 'model/stat_response.model.dart'; -import 'model/sync_user_response.model.dart'; -import 'supabase_auth.service.dart'; -class SupabaseFunctionsService extends GetxService with ConsoleMixin { - static SupabaseFunctionsService get to => Get.find(); +class AppSupabaseFunctionsService extends SupabaseFunctionsService { + static AppSupabaseFunctionsService get to => Get.find(); // VARIABLES - final auth = Get.find(); - final config = Get.find(); final persistence = Get.find(); final spersistence = Get.find(); @@ -164,198 +153,42 @@ class SupabaseFunctionsService extends GetxService with ConsoleMixin { return Right(PresignUrlResponse.fromJson(response.data)); } - Future sync() async { - if (!auth.authenticated) return console.warning('not authenticated'); - console.info('sync...'); - - // calculate vault byte size - final encryptedVaultBytes = CipherService.to.encrypt( - utf8.encode(await LisoManager.compactJson()), - ); + Future syncUser() async { + if (await Purchases.isAnonymous) { + return console.error('cannot sync anonymous user'); + } - final storage = Get.find(); - final data = storage.rootInfo.value.data; + final objects = StorageService.to.rootInfo.value.data; - final response = await auth.client!.functions.invoke( - 'sync-user', - body: { - if (isIAPSupported) ...{ - "rcUserId": await Purchases.appUserID, + final data = { + "address": spersistence.walletAddress.val, + "metadata": { + 'size': { + 'storage': objects.size, + 'vault': (await LisoManager.compactJson()).length, }, - "email": auth.user?.email, - "phone": auth.user?.phone, - "address": spersistence.walletAddress.val, - "userMetadata": auth.user?.userMetadata, - "metadata": { - 'size': { - 'storage': data.size, - 'vault': encryptedVaultBytes.length, - }, - 'count': { - 'items': ItemsService.to.data.length, - 'groups': GroupsService.to.data.length, - 'categories': CategoriesService.to.data.length, - 'files': data.count, - 'sharedVaults': SharedVaultsController.to.data.length, - 'joinedVaults': JoinedVaultsController.to.data.length, - }, - 'settings': { - 'sync': persistence.sync.val, - 'theme': persistence.theme.val, - 'syncProvider': persistence.newSyncProvider, - 'biometrics': persistence.biometrics.val, - 'analytics': persistence.analytics.val, - 'crashReporting': persistence.crashReporting.val, - 'backedUpSeed': persistence.backedUpSeed.val, - 'backedUpPassword': persistence.backedUpPassword.val, - 'localeCode': persistence.localeCode.val, - } + 'count': { + 'items': ItemsService.to.data.length, + 'groups': GroupsService.to.data.length, + 'categories': CategoriesService.to.data.length, + 'files': objects.count, + 'sharedVaults': SharedVaultsController.to.data.length, + 'joinedVaults': JoinedVaultsController.to.data.length, }, - "device": Globals.metadata!.device.toJson() - }, - ); - - if (response.status != 200) { - return console.error( - 'supabase error: ${response.status}: ${response.data}', - ); - } - - final serverResponse = ServerResponse.fromJson(response.data); - - if (serverResponse.errors.isNotEmpty) { - return console.error('server error: ${serverResponse.errors}'); - } - - // console.wtf('synced: ${jsonEncode(serverResponse.toJson())}'); - final syncUserResponse = SyncUserResponse.fromJson(serverResponse.data); - ProController.to.licenseKey.value = syncUserResponse.licenseKey; - - // VERIFY PRO - if (ProController.to.proEntitlement?.isActive != true) { - if (syncUserResponse.licenseKey.length >= 35) { - verifyGumroad(syncUserResponse.licenseKey); - } else if (syncUserResponse.rcUserId.isNotEmpty && !isIAPSupported) { - verifyRevenueCat(syncUserResponse.rcUserId); - } else { - ProController.to.verifiedPro.value = false; + 'settings': { + 'sync': AppPersistence.to.sync.val, + 'theme': persistence.theme.val, + 'syncProvider': AppPersistence.to.newSyncProvider, + 'biometrics': persistence.biometrics.val, + 'analytics': persistence.analytics.val, + 'crashReporting': persistence.crashReporting.val, + 'backedUpSeed': AppPersistence.to.backedUpSeed.val, + 'backedUpPassword': AppPersistence.to.backedUpPassword.val, + 'localeCode': persistence.localeCode.val, + } } - } - } - - Future> gumroadProductDetail() async { - console.info('gumroadProductDetail...'); - - final response = await auth.client!.functions.invoke( - 'gumroad-product-detail', - body: {"localeCode": Get.locale?.languageCode}, - ); - - if (response.status != 200) { - return Left('supabase error: ${response.status}: ${response.data}'); - } - - final serverResponse = ServerResponse.fromJson(response.data); - - if (serverResponse.errors.isNotEmpty) { - String errors = ''; - - for (var e in serverResponse.errors) { - errors += '${e.code}: ${e.message}'; - } - - console.error('server error: $errors'); - return Left(errors); - } - - final product = GumroadProduct.fromJson(serverResponse.data); - // console.info('product: ${jsonEncode(product.toJson())}'); - return Right(product); - } - - Future> verifyGumroad(String licenseKey, - {bool updateEntitlement = true}) async { - if (!auth.authenticated) { - console.warning('not authenticated'); - return const Left('Please sign in to continue'); - } - - console.info('verifyGumroad...'); - - final response = await auth.client!.functions.invoke( - 'verify-gumroad', - body: {"licenseKey": licenseKey}, - ); - - if (response.status != 200) { - return Left('supabase error: ${response.status}: ${response.data}'); - } - - final serverResponse = ServerResponse.fromJson(response.data); - - if (serverResponse.errors.isNotEmpty) { - String errors = ''; - - for (var e in serverResponse.errors) { - errors += '${e.code}: ${e.message}'; - } - - console.error('server error: $errors'); - ProController.to.verifiedPro.value = false; - return Left(errors); - } - - final entitlement = EntitlementResponse.fromJson(serverResponse.data); - console.info('entitlement: ${jsonEncode(entitlement.toJson())}'); - - if (updateEntitlement) { - ProController.to.verifiedPro.value = entitlement.entitled; - if (entitlement.entitled) console.wtf('PRO ENTITLED'); - } - - return Right(entitlement); - } - - Future> verifyRevenueCat(String rcUserId, - {bool updateEntitlement = true}) async { - if (!auth.authenticated) { - console.warning('not authenticated'); - return const Left('Please sign in to continue'); - } - - console.info('verifyRevenueCat...'); - - final response = await auth.client!.functions.invoke( - 'verify-revenuecat', - body: {"userId": rcUserId}, - ); - - if (response.status != 200) { - return Left('supabase error: ${response.status}: ${response.data}'); - } - - final serverResponse = ServerResponse.fromJson(response.data); - - if (serverResponse.errors.isNotEmpty) { - String errors = ''; - - for (var e in serverResponse.errors) { - errors += '${e.code}: ${e.message}'; - } - - console.error('server error: $errors'); - ProController.to.verifiedPro.value = false; - return Left(errors); - } - - final entitlement = EntitlementResponse.fromJson(serverResponse.data); - console.wtf('entitlement: ${jsonEncode(entitlement.toJson())}'); - - if (updateEntitlement) { - ProController.to.verifiedPro.value = entitlement.entitled; - if (entitlement.entitled) console.wtf('PRO ENTITLED'); - } + }; - return Right(entitlement); + SupabaseFunctionsService.to.sync(data: data); } } diff --git a/lib/features/tags/tags_input.controller.dart b/lib/features/tags/tags_input.controller.dart index 98533622..fc1ff017 100644 --- a/lib/features/tags/tags_input.controller.dart +++ b/lib/features/tags/tags_input.controller.dart @@ -1,8 +1,8 @@ +import 'package:app_core/globals.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:line_icons/line_icons.dart'; -import '../../core/utils/utils.dart'; import '../items/items.service.dart'; class TagsInputController extends GetxController { @@ -133,7 +133,7 @@ class TagsInputController extends GetxController { // pre-load suggestions suggestions.value = query(''); - if (Utils.isSmallScreen) { + if (isSmallScreen) { await Get.bottomSheet(content); } else { Get.dialog(Dialog( diff --git a/lib/features/unlock/unlock.screen.dart b/lib/features/unlock/unlock.screen.dart index 85b353d6..b1e9219f 100644 --- a/lib/features/unlock/unlock.screen.dart +++ b/lib/features/unlock/unlock.screen.dart @@ -1,18 +1,19 @@ +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; +import 'package:app_core/widgets/logo.widget.dart'; +import 'package:app_core/widgets/version.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; import 'package:liso/core/utils/globals.dart'; -import 'package:liso/features/general/busy_indicator.widget.dart'; -import 'package:liso/features/general/version.widget.dart'; -import 'package:liso/resources/resources.dart'; -import '../../core/firebase/config/config.service.dart'; import '../../core/persistence/persistence.secret.dart'; import '../../core/utils/utils.dart'; -import '../app/routes.dart'; -import '../general/remote_image.widget.dart'; import 'unlock_screen.controller.dart'; class UnlockScreen extends StatelessWidget with ConsoleMixin { @@ -25,14 +26,10 @@ class UnlockScreen extends StatelessWidget with ConsoleMixin { final content = Column( mainAxisSize: MainAxisSize.min, children: [ - RemoteImage( - url: ConfigService.to.general.app.image, - height: 100, - placeholder: Image.asset(Images.logo, height: 100), - ), + LogoWidget(size: 100), const SizedBox(height: 20), Text( - ConfigService.to.appName, + metadataApp.appName, style: const TextStyle( fontSize: 30, fontWeight: FontWeight.bold, @@ -72,7 +69,7 @@ class UnlockScreen extends StatelessWidget with ConsoleMixin { textInputAction: TextInputAction.go, onChanged: controller.onChanged, onFieldSubmitted: (text) => controller.unlock(), - validator: Utils.validatePassword, + validator: AppUtils.validatePassword, autovalidateMode: AutovalidateMode.onUserInteraction, autofillHints: const [AutofillHints.password], decoration: InputDecoration( @@ -110,7 +107,7 @@ class UnlockScreen extends StatelessWidget with ConsoleMixin { return WillPopScope( onWillPop: () => Future.value( - controller.promptMode || Globals.isAutofill, + controller.promptMode || isAutofill, ), child: Scaffold( appBar: AppBar( diff --git a/lib/features/unlock/unlock_screen.controller.dart b/lib/features/unlock/unlock_screen.controller.dart index c28c4bf2..97692b9f 100644 --- a/lib/features/unlock/unlock_screen.controller.dart +++ b/lib/features/unlock/unlock_screen.controller.dart @@ -1,18 +1,19 @@ import 'dart:async'; +import 'package:app_core/globals.dart'; +import 'package:app_core/persistence/persistence.dart'; +import 'package:app_core/services/local_auth.service.dart'; +import 'package:app_core/utils/ui_utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:liso/core/liso/liso.manager.dart'; import 'package:liso/core/middlewares/authentication.middleware.dart'; -import 'package:liso/core/persistence/persistence.dart'; -import 'package:liso/core/utils/ui_utils.dart'; import '../../core/hive/hive.service.dart'; import '../../core/persistence/persistence.secret.dart'; -import '../../core/services/local_auth.service.dart'; -import '../../core/utils/globals.dart'; +import '../../core/utils/utils.dart'; import '../main/main_screen.controller.dart'; import '../wallet/wallet.service.dart'; @@ -70,6 +71,7 @@ class UnlockScreenController extends GetxController void unlock() async { if (status == RxStatus.loading()) return console.error('still busy'); + await UIUtils.showConsent(); await Get.closeCurrentSnackbar(); change(null, status: RxStatus.loading()); final password = passwordController.text.trim(); diff --git a/lib/features/update/update.screen.dart b/lib/features/update/update.screen.dart deleted file mode 100644 index 30123110..00000000 --- a/lib/features/update/update.screen.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:iconsax/iconsax.dart'; -import 'package:liso/core/firebase/analytics.service.dart'; -import 'package:liso/core/utils/styles.dart'; -import 'package:liso/resources/resources.dart'; - -import '../../core/firebase/config/config.service.dart'; -import '../../core/utils/globals.dart'; -import '../../core/utils/utils.dart'; -import '../general/remote_image.widget.dart'; -import '../general/version.widget.dart'; - -class UpdateScreen extends StatelessWidget { - const UpdateScreen({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final content = Container( - constraints: Styles.containerConstraints, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - RemoteImage( - url: ConfigService.to.general.app.image, - height: 150, - placeholder: Image.asset(Images.logo, height: 200), - ), - const SizedBox(height: 40), - const Text( - 'Update Required', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 30, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 10), - const Text( - "Please update to the latest version to enjoy the latest features, bug fixes, and security patches.", - textAlign: TextAlign.center, - style: TextStyle(fontSize: 15), - ), - const SizedBox(height: 20), - SizedBox( - width: 200, - child: ElevatedButton.icon( - label: const Text('Download'), - icon: const Icon(Iconsax.arrow_down_2), - onPressed: () { - AnalyticsService.to.logEvent('download_required_update'); - - Utils.openUrl( - ConfigService.to.general.app.links.website, - ); - }, - ), - ), - const SizedBox(height: 20), - const Divider(), - const SizedBox(height: 20), - Text( - 'Current build # ${Globals.metadata?.app.buildNumber}', - style: const TextStyle(color: Colors.grey), - ), - Text( - 'Minimum build # ${ConfigService.to.app.build.min}', - style: const TextStyle(color: Colors.grey), - ), - ], - ), - ); - - return WillPopScope( - onWillPop: () => Future.value(false), - child: Scaffold( - bottomNavigationBar: const VersionText(), - body: Padding( - padding: const EdgeInsets.all(20), - child: Center(child: content), - ), - ), - ); - } -} diff --git a/lib/features/wallet/assets/assets.screen.dart b/lib/features/wallet/assets/assets.screen.dart index 24e2b1e5..f1833579 100644 --- a/lib/features/wallet/assets/assets.screen.dart +++ b/lib/features/wallet/assets/assets.screen.dart @@ -1,13 +1,14 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/persistence/persistence_builder.widget.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:liso/features/general/keep_alive.widget.dart'; -import '../../../core/persistence/persistence_builder.widget.dart'; -import '../../../core/utils/globals.dart'; +import '../../../core/persistence/persistence.dart'; import '../../../resources/resources.dart'; -import '../../general/busy_indicator.widget.dart'; import '../../general/centered_placeholder.widget.dart'; import '../wallet.service.dart'; import 'assets_screen.controller.dart'; @@ -22,10 +23,12 @@ class AssetsScreen extends StatelessWidget with ConsoleMixin { final listView = PersistenceBuilder( builder: (p, context) { - final liso = currencyFormatter.format(p.lastLisoBalance.val); + final liso = + currencyFormatter.format(AppPersistence.to.lastLisoBalance.val); final lisoUsd = currencyFormatter.format(wallet.lisoUsdBalance); - final matic = currencyFormatter.format(p.lastMaticBalance.val); + final matic = + currencyFormatter.format(AppPersistence.to.lastMaticBalance.val); final maticUsd = currencyFormatter.format(wallet.maticUsdBalance); return ListView( diff --git a/lib/features/wallet/nfts/nfts.screen.dart b/lib/features/wallet/nfts/nfts.screen.dart index cf98e25f..6b267966 100644 --- a/lib/features/wallet/nfts/nfts.screen.dart +++ b/lib/features/wallet/nfts/nfts.screen.dart @@ -1,12 +1,12 @@ +import 'package:app_core/widgets/busy_indicator.widget.dart'; +import 'package:app_core/widgets/remote_image.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:liso/features/general/keep_alive.widget.dart'; -import '../../general/busy_indicator.widget.dart'; import '../../general/centered_placeholder.widget.dart'; -import '../../general/remote_image.widget.dart'; import 'nfts_screen.controller.dart'; class NFTsScreen extends StatelessWidget with ConsoleMixin { diff --git a/lib/features/wallet/transactions/transactions.screen.dart b/lib/features/wallet/transactions/transactions.screen.dart index fbb45f3c..c7a9ab57 100644 --- a/lib/features/wallet/transactions/transactions.screen.dart +++ b/lib/features/wallet/transactions/transactions.screen.dart @@ -1,10 +1,10 @@ +import 'package:app_core/widgets/busy_indicator.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:liso/features/general/keep_alive.widget.dart'; -import '../../general/busy_indicator.widget.dart'; import '../../general/centered_placeholder.widget.dart'; import 'transactions_screen.controller.dart'; diff --git a/lib/features/wallet/wallet.screen.dart b/lib/features/wallet/wallet.screen.dart index c64f19e6..61b070ec 100644 --- a/lib/features/wallet/wallet.screen.dart +++ b/lib/features/wallet/wallet.screen.dart @@ -1,22 +1,21 @@ +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/persistence/persistence_builder.widget.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/appbar_leading.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:line_icons/line_icons.dart'; import 'package:liso/core/utils/globals.dart'; -import 'package:liso/core/utils/ui_utils.dart'; -import 'package:liso/features/general/appbar_leading.widget.dart'; -import 'package:liso/features/general/remote_image.widget.dart'; import 'package:liso/features/wallet/assets/assets.screen.dart'; import 'package:liso/features/wallet/transactions/transactions.screen.dart'; import 'package:liso/features/wallet/wallet.service.dart'; -import '../../core/firebase/config/config.service.dart'; import '../../core/persistence/persistence.secret.dart'; -import '../../core/persistence/persistence_builder.widget.dart'; -import '../../core/utils/utils.dart'; import '../../resources/resources.dart'; -import '../app/routes.dart'; import '../general/card_button.widget.dart'; import '../menu/menu.button.dart'; import 'nfts/nfts.screen.dart'; @@ -145,15 +144,6 @@ class WalletScreen extends StatelessWidget with ConsoleMixin { ], ), ), - if (isBeta) ...[ - TextButton.icon( - icon: const Icon(LineIcons.discord), - label: const Text('Need Testnet \$LISO?'), - onPressed: () => Utils.openUrl( - ConfigService.to.general.app.links.discord, - ), - ), - ] ], ), ); @@ -181,10 +171,11 @@ class WalletScreen extends StatelessWidget with ConsoleMixin { children: [ Row( children: [ - DiceBearAvatar( - seed: SecretPersistence.to.longAddress, - size: 30, - ), + // TODO: temporary + // DiceBearAvatar( + // seed: SecretPersistence.to.longAddress, + // size: 30, + // ), const SizedBox(width: 10), Column( mainAxisSize: MainAxisSize.min, @@ -198,7 +189,7 @@ class WalletScreen extends StatelessWidget with ConsoleMixin { ), ), Text( - Utils.isSmallScreen + isSmallScreen ? SecretPersistence.to.shortAddress : SecretPersistence.to.longAddress, overflow: TextOverflow.ellipsis, diff --git a/lib/features/wallet/wallet.service.dart b/lib/features/wallet/wallet.service.dart index 56283a02..e80ee993 100644 --- a/lib/features/wallet/wallet.service.dart +++ b/lib/features/wallet/wallet.service.dart @@ -2,6 +2,9 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; +import 'package:app_core/firebase/analytics.service.dart'; +import 'package:app_core/supabase/supabase_auth.service.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:bip32/bip32.dart' as bip32; import 'package:bip39/bip39.dart' as bip39; import 'package:coingecko_api/coingecko_api.dart'; @@ -12,8 +15,8 @@ import 'package:eth_sig_util/eth_sig_util.dart'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:hex/hex.dart'; -import 'package:liso/core/firebase/analytics.service.dart'; import 'package:liso/core/persistence/persistence.dart'; +import 'package:liso/core/utils/utils.dart'; import 'package:web3dart/web3dart.dart'; import 'package:worker_manager/worker_manager.dart'; @@ -24,7 +27,6 @@ import '../../core/persistence/persistence.secret.dart'; import '../../core/utils/globals.dart'; import '../categories/categories.controller.dart'; import '../items/items.service.dart'; -import '../supabase/supabase_auth.service.dart'; class WalletService extends GetxService with ConsoleMixin { static WalletService get to => Get.find(); @@ -56,11 +58,12 @@ class WalletService extends GetxService with ConsoleMixin { double get totalUsdBalance => maticUsdBalance + lisoUsdBalance; double get maticUsdBalance => - Persistence.to.lastMaticBalance.val * - Persistence.to.lastMaticUsdPrice.val; + AppPersistence.to.lastMaticBalance.val * + AppPersistence.to.lastMaticUsdPrice.val; double get lisoUsdBalance => - Persistence.to.lastLisoBalance.val * Persistence.to.lastLisoUsdPrice.val; + AppPersistence.to.lastLisoBalance.val * + AppPersistence.to.lastLisoUsdPrice.val; @override void onInit() { @@ -88,7 +91,8 @@ class WalletService extends GetxService with ConsoleMixin { ); } - Persistence.to.lastMaticUsdPrice.val = result.data.first.getPriceIn('usd')!; + AppPersistence.to.lastMaticUsdPrice.val = + result.data.first.getPriceIn('usd')!; } Wallet mnemonicToWallet( @@ -188,7 +192,10 @@ class WalletService extends GetxService with ConsoleMixin { SecretPersistence.to.walletSignature.val = signature; // // from the first 32 bits of the signature // cipherKey = Uint8List.fromList(utf8.encode(signature).sublist(0, 32)); - SupabaseAuthService.to.authenticate(); + + final email = '${SecretPersistence.to.walletAddress.val}@liso.dev'; + final password = await WalletService.to.sign(kAuthSignatureMessage); + SupabaseAuthService.to.authenticate(email: email, password: password); if (!GetPlatform.isWindows) { AnalyticsService.to.instance.setUserProperty( diff --git a/lib/features/wallet/wallet_screen.controller.dart b/lib/features/wallet/wallet_screen.controller.dart index ca5675f9..631e9b98 100644 --- a/lib/features/wallet/wallet_screen.controller.dart +++ b/lib/features/wallet/wallet_screen.controller.dart @@ -1,18 +1,19 @@ import 'package:alchemy_web3/alchemy_web3.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/ui_utils.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:liso/contracts/liso.dart'; import 'package:liso/core/services/alchemy.service.dart'; -import 'package:liso/core/utils/ui_utils.dart'; import 'package:liso/features/wallet/nfts/nfts_screen.controller.dart'; import 'package:liso/features/wallet/transactions/transactions_screen.controller.dart'; import 'package:liso/features/wallet/wallet.service.dart'; import 'package:qr_flutter/qr_flutter.dart'; import '../../core/persistence/persistence.secret.dart'; -import '../../core/utils/utils.dart'; import '../../resources/resources.dart'; import '../menu/menu.item.dart'; import 'assets/assets_screen.controller.dart'; @@ -97,8 +98,9 @@ class WalletScreenController extends GetxController with ConsoleMixin { ), const SizedBox(height: 10), TextButton.icon( - onPressed: () => - Utils.copyToClipboard(SecretPersistence.to.walletAddress.val), + onPressed: () => Utils.copyToClipboard( + SecretPersistence.to.walletAddress.val, + ), icon: const Icon(Iconsax.copy), label: const Text('Copy Address'), ), @@ -107,8 +109,7 @@ class WalletScreenController extends GetxController with ConsoleMixin { Get.dialog(AlertDialog( title: const Text('Receive'), - content: - Utils.isSmallScreen ? content : SizedBox(width: 450, child: content), + content: isSmallScreen ? content : SizedBox(width: 450, child: content), actions: [ TextButton( onPressed: Get.back, @@ -259,9 +260,7 @@ class WalletScreenController extends GetxController with ConsoleMixin { title: Text('sign_text'.tr), content: Form( key: formKey, - child: Utils.isSmallScreen - ? content - : SizedBox(width: 450, child: content), + child: isSmallScreen ? content : SizedBox(width: 450, child: content), ), actions: [ TextButton( diff --git a/lib/features/welcome/welcome.screen.dart b/lib/features/welcome/welcome.screen.dart index 66be9829..acc48fe3 100644 --- a/lib/features/welcome/welcome.screen.dart +++ b/lib/features/welcome/welcome.screen.dart @@ -1,17 +1,18 @@ +import 'package:app_core/config.dart'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/utils/utils.dart'; +import 'package:app_core/widgets/busy_indicator.widget.dart'; +import 'package:app_core/widgets/gradient.widget.dart'; +import 'package:app_core/widgets/logo.widget.dart'; +import 'package:app_core/widgets/version.widget.dart'; import 'package:console_mixin/console_mixin.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:get/get.dart'; import 'package:iconsax/iconsax.dart'; import 'package:liso/core/utils/styles.dart'; -import 'package:liso/resources/resources.dart'; -import '../../core/firebase/config/config.service.dart'; -import '../../core/utils/utils.dart'; -import '../general/busy_indicator.widget.dart'; -import '../general/gradient.widget.dart'; -import '../general/remote_image.widget.dart'; -import '../general/version.widget.dart'; import 'welcome_screen.controller.dart'; class WelcomeScreen extends StatelessWidget with ConsoleMixin { @@ -30,27 +31,18 @@ class WelcomeScreen extends StatelessWidget with ConsoleMixin { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ - if (Utils.isSmallScreen) ...[ + if (isSmallScreen) ...[ const SizedBox(height: 100), ], - RemoteImage( - url: ConfigService.to.general.app.image, - height: 150, - placeholder: Image.asset(Images.logo, height: 200), - ), + const LogoWidget(size: 200), const SizedBox(height: 40), Animate( - child: const GradientWidget( - gradient: LinearGradient( - colors: [ - Color.fromARGB(255, 0, 171, 105), - Color.fromARGB(255, 0, 255, 213), - ], - ), + child: GradientWidget( + gradient: LinearGradient(colors: CoreConfig().gradientColors), child: Text( - 'Get Secured Now', + 'slogan'.tr, textAlign: TextAlign.center, - style: TextStyle( + style: const TextStyle( fontSize: 30, fontWeight: FontWeight.bold, ), @@ -62,7 +54,7 @@ class WelcomeScreen extends StatelessWidget with ConsoleMixin { .then(delay: 3000.ms), const SizedBox(height: 10), Text( - ConfigService.to.general.app.longDescription, + 'slogan_sub'.tr, textAlign: TextAlign.center, style: const TextStyle(fontSize: 15), ), @@ -136,19 +128,19 @@ class WelcomeScreen extends StatelessWidget with ConsoleMixin { ], ); - const darkDecoration = BoxDecoration( - gradient: LinearGradient( - begin: Alignment.bottomLeft, - end: Alignment.topRight, - colors: [ - Colors.black, - Color(0xFF173030), - ], - ), - ); - return Container( - decoration: Get.isDarkMode ? darkDecoration : null, + decoration: Get.isDarkMode + ? const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomLeft, + end: Alignment.topRight, + colors: [ + Colors.black, + Color(0xFF173030), + ], + ), + ) + : null, child: Scaffold( backgroundColor: Get.isDarkMode ? Colors.transparent : null, bottomNavigationBar: bottomBar, diff --git a/lib/features/welcome/welcome_screen.controller.dart b/lib/features/welcome/welcome_screen.controller.dart index e88a74ee..90108d75 100644 --- a/lib/features/welcome/welcome_screen.controller.dart +++ b/lib/features/welcome/welcome_screen.controller.dart @@ -1,18 +1,19 @@ +import 'package:app_core/controllers/pro.controller.dart'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/notifications/notifications.manager.dart'; +import 'package:app_core/pages/routes.dart'; +import 'package:app_core/services/local_auth.service.dart'; +import 'package:app_core/utils/utils.dart'; import 'package:bip39/bip39.dart' as bip39; import 'package:console_mixin/console_mixin.dart'; import 'package:get/get.dart'; -import 'package:liso/core/firebase/config/config.service.dart'; import 'package:liso/core/middlewares/authentication.middleware.dart'; import 'package:liso/core/persistence/persistence.dart'; -import 'package:liso/core/utils/globals.dart'; -import 'package:liso/features/pro/pro.controller.dart'; -import '../../core/notifications/notifications.manager.dart'; -import '../../core/services/local_auth.service.dart'; import '../../core/utils/utils.dart'; import '../app/routes.dart'; import '../main/main_screen.controller.dart'; -import '../supabase/supabase_auth.service.dart'; import '../wallet/wallet.service.dart'; class WelcomeScreenController extends GetxController @@ -34,16 +35,15 @@ class WelcomeScreenController extends GetxController void create() async { // show upgrade screen - if (!Persistence.to.upgradeScreenShown.val && !ProController.to.isPro) { + if (!AppPersistence.to.upgradeScreenShown.val && !ProController.to.isPro) { await Utils.adaptiveRouteOpen(name: Routes.upgrade); } change(null, status: RxStatus.loading()); - await SupabaseAuthService.to.signOut(); // just to make sure if (!isLocalAuthSupported) { change(null, status: RxStatus.success()); - return Utils.adaptiveRouteOpen(name: Routes.seed); + return Utils.adaptiveRouteOpen(name: AppRoutes.seed); } // TODO: custom localized reason @@ -54,7 +54,7 @@ class WelcomeScreenController extends GetxController if (!authenticated) return change(null, status: RxStatus.success()); final seed = bip39.generateMnemonic(strength: 256); - final password = Utils.generatePassword(); + final password = AppUtils.generatePassword(); await WalletService.to.create(seed, password, true); change(null, status: RxStatus.success()); @@ -68,7 +68,6 @@ class WelcomeScreenController extends GetxController } void restore() async { - await SupabaseAuthService.to.signOut(); // just to make sure - Utils.adaptiveRouteOpen(name: Routes.restore); + Utils.adaptiveRouteOpen(name: AppRoutes.restore); } } diff --git a/lib/main.dart b/lib/main.dart index 89f6dd6b..f5f82852 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,15 @@ import 'dart:async'; - +import 'dart:convert'; + +import 'package:app_core/config.dart'; +import 'package:app_core/firebase/config/config.service.dart'; +import 'package:app_core/firebase/config/models/config_root.model.dart'; +import 'package:app_core/firebase/crashlytics.service.dart'; +import 'package:app_core/globals.dart'; +import 'package:app_core/pages/upgrade/upgrade_config.dart'; +import 'package:app_core/supabase/supabase_auth.service.dart'; import 'package:console_mixin/console_mixin.dart'; +import 'package:filesize/filesize.dart'; import 'package:firebase_core/firebase_core.dart'; // ignore: library_prefixes import 'package:firebase_dart/firebase_dart.dart' as firebaseDesktop; @@ -9,34 +18,32 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; +import 'package:liso/core/firebase/model/config_app_domains.model.dart'; +import 'package:liso/core/firebase/model/config_limits.model.dart'; +import 'package:liso/core/firebase/model/config_web3.model.dart'; import 'package:liso/core/hive/hive.service.dart'; import 'package:liso/core/persistence/persistence.secret.dart'; import 'package:liso/core/services/alchemy.service.dart'; import 'package:liso/core/services/cipher.service.dart'; -import 'package:liso/core/utils/globals.dart'; import 'package:liso/features/autofill/autofill.service.dart'; import 'package:liso/features/groups/groups.service.dart'; -import 'package:liso/features/pro/pro.controller.dart'; +import 'package:liso/features/supabase/supabase_functions.service.dart'; import 'package:liso/features/wallet/wallet.service.dart'; +import 'package:liso/resources/resources.dart'; import 'package:secrets/firebase_options.dart'; import 'package:secrets/secrets.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:window_manager/window_manager.dart'; import 'package:worker_manager/worker_manager.dart'; -import 'core/firebase/analytics.service.dart'; -import 'core/firebase/config/config.service.dart'; -import 'core/firebase/crashlytics.service.dart'; import 'core/flavors/flavors.dart'; import 'core/liso/liso_paths.dart'; -import 'core/notifications/notifications.manager.dart'; import 'core/persistence/persistence.dart'; -import 'core/services/local_auth.service.dart'; -import 'core/utils/utils.dart'; +import 'core/translations/data.dart'; +import 'core/utils/globals.dart'; import 'features/app/app.dart'; +import 'features/app/pages.dart'; import 'features/categories/categories.controller.dart'; import 'features/categories/categories.service.dart'; -import 'features/connectivity/connectivity.service.dart'; import 'features/drawer/drawer_widget.controller.dart'; import 'features/files/explorer/s3_object_tile.controller.dart'; import 'features/files/storage.service.dart'; @@ -47,13 +54,69 @@ import 'features/items/items.service.dart'; import 'features/joined_vaults/joined_vault.controller.dart'; import 'features/main/main_screen.controller.dart'; import 'features/shared_vaults/shared_vault.controller.dart'; -import 'features/supabase/supabase_auth.service.dart'; -import 'features/supabase/supabase_database.service.dart'; -import 'features/supabase/supabase_functions.service.dart'; + +void initUpgradeConfig() { + String formatKNumber(int number) { + if (number == 1000000) { + return 'Unlimited'; + } else { + return kFormatter.format(number); + } + } + + final upgradeConfig = UpgradeConfig( + features: [ + if (isIAPSupported) ...[ + // TODO: temporary + // controller.promoText + ] else ...[ + '1 ${'week'.tr} ${'free_trial'.tr}', + ], + 'cancel_anytime'.tr, + '${formatKNumber(configLimits.pro.items)} Items', + '${formatKNumber(configLimits.pro.devices)} Devices', + '2FA Authenticator', + '${filesize(1073741824)} Encrypted Cloud Storage', + '${formatKNumber(configLimits.pro.sharedMembers)} Shared Members', + '${formatKNumber(configLimits.pro.protectedItems)} Protected Items', + 'Password Health', + 'Priority Support', + 'Encryption Tool', + '${formatKNumber(configLimits.pro.backups)} Vault Backups', + '${filesize(configLimits.pro.uploadSize)} Upload File Size', + '${formatKNumber(configLimits.pro.files)} Max Stored Files', + 'Undo Trash up to ${formatKNumber(configLimits.pro.trashDays)} Days', + '${formatKNumber(configLimits.pro.customVaults)} Custom Vaults', + '${formatKNumber(configLimits.pro.customCategories)} Custom Categories', + 'Autosave + Autofill', + 'Generate Passwords', + 'Biometric Auth', + 'Offline Mode', + ], + upcomingFeatures: [ + 'Self-Hostable', + 'Breach Scanner', + 'NFC Keycard Support', + 'YubiKey Support', + ], + darkDecoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomLeft, + end: Alignment.topRight, + colors: [ + Colors.black, + Color(0xFF173030), + ], + ), + ), + ); + + CoreConfig().upgradeConfig = upgradeConfig; +} void init(Flavor flavor, {bool autofill = false}) async { Flavors.flavor = flavor; - Globals.isAutofill = autofill; + isAutofill = autofill; final console = Console(name: 'Main'); console.wtf('Flavor: ${flavor.name}'); @@ -90,29 +153,37 @@ void init(Flavor flavor, {bool autofill = false}) async { isolatesCount: kDebugMode ? 2 : 50, ); - // GetX services - Get.lazyPut(() => CrashlyticsService()); - Get.lazyPut(() => Persistence()); + // init core config + final core = CoreConfig().init( + persistenceCipherKey: Secrets.persistenceKey, + translationKeys: translationKeys, + pages: Pages.data, + // onCloseUpgradeScreen: AppUtils.showContestDialog, // TODO: temporary + logoDarkPath: Images.logo, + logoLightPath: Images.logo, // TODO: light logo + allowAnonymousRcUserSync: false, + gradientColors: const [ + Color.fromARGB(255, 0, 171, 105), + Color.fromARGB(255, 0, 255, 213), + ], + ); + + // services + Get.lazyPut(() => AppSupabaseFunctionsService()); + Get.lazyPut(() => AppPersistence()); Get.lazyPut(() => SecretPersistence()); Get.lazyPut(() => WalletService()); - Get.lazyPut(() => ConnectivityService()); Get.lazyPut(() => CipherService()); Get.lazyPut(() => LisoAutofillService()); - Get.lazyPut(() => SupabaseDBService()); - Get.lazyPut(() => SupabaseFunctionsService()); - Get.lazyPut(() => SupabaseAuthService()); Get.lazyPut(() => AlchemyService()); Get.lazyPut(() => SyncService()); Get.lazyPut(() => StorageService()); - Get.lazyPut(() => ConfigService()); - Get.lazyPut(() => LocalAuthService()); Get.lazyPut(() => HiveService()); Get.lazyPut(() => ItemsService()); Get.lazyPut(() => GroupsService()); Get.lazyPut(() => CategoriesService()); - // permanent controllers - Get.put(AnalyticsService()); + // controllers Get.put(ItemsController()); Get.put(GroupsController()); Get.put(CategoriesController()); @@ -120,29 +191,48 @@ void init(Flavor flavor, {bool autofill = false}) async { Get.put(MainScreenController()); Get.put(SharedVaultsController()); Get.put(JoinedVaultsController()); - Get.put(ProController()); // create controllers Get.create(() => S3ObjectTileController()); // initializations - CrashlyticsService.to.init(); - await Globals.init(); + await ConfigService.to.init( + postInit: (parameters) { + configLimits = ConfigLimits.fromJson( + jsonDecode( + ConfigValue.fromJson(parameters["limits_config"]) + .defaultValue + .value, + ), + ); + + configAppDomains = ConfigAppDomains.fromJson( + jsonDecode( + ConfigValue.fromJson(parameters["app_domains_config"]) + .defaultValue + .value, + ), + ); + + configWeb3 = ConfigWeb3.fromJson( + jsonDecode( + ConfigValue.fromJson(parameters["web3_config"]).defaultValue.value, + ), + ); + + initUpgradeConfig(); + + // load balances + AlchemyService.to.init(); + AlchemyService.to.load(); + }, + ); + + await core.postInit(); await LisoPaths.init(); - await Persistence.open(); await SecretPersistence.open(); await SecretPersistence.migrate(); HiveService.init(); - await ConfigService.to.init(); - - // init - NotificationsManager.init(); - Utils.setDisplayMode(); // refresh rate - - if (isDesktop) { - await windowManager.ensureInitialized(); - await Utils.setWindowSize(); // for desktop - } LicenseRegistry.addLicense(() async* { final license = await rootBundle.loadString('google_fonts/OFL.txt'); diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index dcb1dc2c..83059db1 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,14 +6,19 @@ #include "generated_plugin_registrant.h" +#include #include #include #include #include +#include #include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) hotkey_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "HotkeyManagerPlugin"); + hotkey_manager_plugin_register_with_registrar(hotkey_manager_registrar); g_autoptr(FlPluginRegistrar) pasteboard_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); pasteboard_plugin_register_with_registrar(pasteboard_registrar); @@ -26,6 +31,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) sentry_flutter_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin"); sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar); + g_autoptr(FlPluginRegistrar) system_tray_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SystemTrayPlugin"); + system_tray_plugin_register_with_registrar(system_tray_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 194c3a20..414978e7 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,10 +3,12 @@ # list(APPEND FLUTTER_PLUGIN_LIST + hotkey_manager pasteboard platform_device_id_linux screen_retriever sentry_flutter + system_tray url_launcher_linux window_manager ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 50e169e1..b34e91e3 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import app_links_macos import cloud_functions import connectivity_plus import device_info_plus @@ -12,6 +13,7 @@ import firebase_analytics import firebase_core import firebase_crashlytics import flutter_local_notifications +import hotkey_manager import package_info_plus import pasteboard import path_provider_macos @@ -21,11 +23,12 @@ import purchases_flutter import screen_retriever import sentry_flutter import share_plus -import sqflite +import system_tray import url_launcher_macos import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) FLTFirebaseFunctionsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFunctionsPlugin")) ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) @@ -33,6 +36,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) + HotkeyManagerPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) @@ -42,7 +46,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) - SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) + SystemTrayPlugin.register(with: registry.registrar(forPlugin: "SystemTrayPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index 89529181..a7a5d8b0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: liso description: Liso publish_to: "none" -version: 1.0.0+42 +version: 1.0.1+43 environment: sdk: ">=2.17.3 <3.0.0" @@ -10,8 +10,9 @@ dependencies: flutter: sdk: flutter - # STATE MANAGEMENT - get: ^4.6.5 + # CORE FRAMEWORK + app_core: + path: ../../Stackwares/app-core # CRYTPOGRAPHY + SECURITY secrets: @@ -28,104 +29,48 @@ dependencies: bip32: ^2.0.0 # Derive Path hex: ^0.2.0 # Hex encoding & decoding encrypt: ^5.0.1 # File Encryption - local_auth: ^2.1.2 # check biometric availability - # walletconnect_dart: ^0.0.11 otp: ^3.1.1 - # FIREBASE - firebase_core: 2.1.0 - firebase_analytics: 10.0.2 - firebase_crashlytics: 3.0.2 - firebase_performance: 0.9.0+2 - cloud_functions: 4.0.2 - # FIREBASE DESKTOP - firebase_core_desktop: 1.0.2 - firebase_functions_desktop: 0.2.0+3 - firebase_core_platform_interface: 4.5.1 - # FIREBASE DESKTOP WORKAROUND - firebase_dart: ^1.0.11 - # SUPABASE - supabase: ^1.0.1 - # IO - hive: ^2.2.3 # database - path_provider: ^2.0.11 - path: ^1.8.1 filesize: ^2.0.1 # file size calculator file_picker: ^5.2.2 # exporting + importing vault file - http: ^0.13.5 # web3dart client # API # minio: ^3.5.0 # S3 API minio: git: https://github.com/oliverbytes/minio-dart.git coingecko_api: ^1.1.0+2 # Coingecko API - purchases_flutter: ^4.3.0 # UI - line_icons: ^2.0.1 # icons font_awesome_flutter: ^10.2.1 # icons - iconsax: ^0.0.8 - flex_color_scheme: 6.0.1 # themes - simple_animations: 5.0.0+2 - sa4_migration_kit: ^1.0.0 blur: ^3.1.0 # blurring of seed phrases flutter_json_viewer: ^1.0.1 #json viewer flutter_swipe_action_cell: ^3.0.4 flutter_quill: ^6.0.10 badges: ^2.0.3 # pending changes indicator - cached_network_image: ^3.2.2 # load remote images percent_indicator: ^4.2.2 # circular progress indicator flutter_svg: ^1.1.5 # dice bear avatars qr_flutter: ^4.0.0 # generate QR Codes skeletons: ^0.0.3 # shimmer loading effects - flutter_rating_bar: ^4.0.1 - flutter_animate: ^1.0.0 # TOOLS - url_launcher: ^6.1.6 - supercharged: ^2.1.1 # useful utils - timeago: ^3.3.0 - flutter_local_notifications: ^12.0.2 - either_dart: ^0.2.0 # functional programming + error handling - intl: ^0.17.0 # date formatting - share_plus: ^6.0.1 # exporting files and sharing texts equatable: ^2.0.5 - app_settings: ^4.1.8 - uuid: ^3.0.6 # unique identifiers for items - window_manager: ^0.2.7 # set desktop window size - flutter_displaymode: ^0.4.0 # allow higher refresh rates - console_mixin: ^0.0.1 # logger random_string_generator: ^2.0.0 humanizer: ^2.0.0 worker_manager: ^4.4.6 # multi threading flutter_autofill_service: ^0.14.0 # android autofill plugun - app_review: ^2.1.2+1 # request for review on the app store csv: ^5.0.1 # csv parsing tool - sentry_flutter: ^6.13.1 # crash reporting tool for windows - easy_debounce: ^2.0.2+1 - - # INFO - device_info_plus: ^7.0.1 - platform_device_id: ^1.0.1 - package_info_plus: ^3.0.1 - connectivity_plus: ^3.0.2 # internet connectivity checking -dependency_overrides: +dependency_overrides: package_info_plus: ^3.0.1 - # for firebase_functions_desktop on windows firebase_core: ^2.1.0 cloud_functions: ^4.0.2 dev_dependencies: - # flutter_test: - # sdk: flutter + flutter_test: + sdk: flutter flutter_lints: ^2.0.1 - hive_generator: ^1.1.3 # hive database - build_runner: ^2.3.2 - flutter_native_splash: ^2.2.11 - dart_code_metrics: ^4.16.0 # checking unused stuffs flutter: uses-material-design: true diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 5318566c..3b6c884b 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,23 +6,33 @@ #include "generated_plugin_registrant.h" +#include #include +#include #include #include +#include #include #include #include #include +#include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + AppLinksWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AppLinksWindowsPlugin")); ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + HotkeyManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("HotkeyManagerPlugin")); LocalAuthPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("LocalAuthPlugin")); PasteboardPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PasteboardPlugin")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); PlatformDeviceIdWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PlatformDeviceIdWindowsPlugin")); ScreenRetrieverPluginRegisterWithRegistrar( @@ -31,6 +41,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("SentryFlutterPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + SystemTrayPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SystemTrayPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); WindowManagerPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index dc437568..3fd6c0b5 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,13 +3,17 @@ # list(APPEND FLUTTER_PLUGIN_LIST + app_links_windows connectivity_plus + hotkey_manager local_auth_windows pasteboard + permission_handler_windows platform_device_id_windows screen_retriever sentry_flutter share_plus + system_tray url_launcher_windows window_manager )