diff --git a/packages/data_importer/.gitignore b/packages/data_importer/.gitignore deleted file mode 100644 index 96486fd9302..00000000000 --- a/packages/data_importer/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -/pubspec.lock -**/doc/api/ -.dart_tool/ -.packages -build/ diff --git a/packages/data_importer/.metadata b/packages/data_importer/.metadata deleted file mode 100644 index 5bfec1bcc1e..00000000000 --- a/packages/data_importer/.metadata +++ /dev/null @@ -1,33 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled. - -version: - revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 - channel: stable - -project_type: plugin - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 - base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 - - platform: android - create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 - base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 - - platform: ios - create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 - base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 - - # User provided section - - # List of Local paths (relative to this file) that should be - # ignored by the migrate tool. - # - # Files that are not part of the templates will be ignored by default. - unmanaged_files: - - 'lib/main.dart' - - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/data_importer/analysis_options.yaml b/packages/data_importer/analysis_options.yaml deleted file mode 100644 index 773401149ed..00000000000 --- a/packages/data_importer/analysis_options.yaml +++ /dev/null @@ -1,5 +0,0 @@ -include: ../smooth_app/analysis_options.yaml - -analyzer: - exclude: - - lib/ios/model/realm_history_item.g.dart \ No newline at end of file diff --git a/packages/data_importer/android/.gitignore b/packages/data_importer/android/.gitignore deleted file mode 100644 index 161bdcdaf88..00000000000 --- a/packages/data_importer/android/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures -.cxx diff --git a/packages/data_importer/android/build.gradle b/packages/data_importer/android/build.gradle deleted file mode 100644 index ce94629d103..00000000000 --- a/packages/data_importer/android/build.gradle +++ /dev/null @@ -1,50 +0,0 @@ -group 'org.openfoodfacts.off.data_importer' -version '1.0-SNAPSHOT' - -buildscript { - ext.kotlin_version = '1.6.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - compileSdkVersion 31 - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - minSdkVersion 16 - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/packages/data_importer/android/gradle/wrapper/gradle-wrapper.properties b/packages/data_importer/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 41dfb87909a..00000000000 --- a/packages/data_importer/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/packages/data_importer/android/settings.gradle b/packages/data_importer/android/settings.gradle deleted file mode 100644 index e8b910a9b11..00000000000 --- a/packages/data_importer/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'data_importer' diff --git a/packages/data_importer/android/src/main/AndroidManifest.xml b/packages/data_importer/android/src/main/AndroidManifest.xml deleted file mode 100644 index a72b7c5d1ae..00000000000 --- a/packages/data_importer/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/packages/data_importer/android/src/main/kotlin/org/openfoodfacts/off/data_importer/DataImporterPlugin.kt b/packages/data_importer/android/src/main/kotlin/org/openfoodfacts/off/data_importer/DataImporterPlugin.kt deleted file mode 100644 index 89946f9457b..00000000000 --- a/packages/data_importer/android/src/main/kotlin/org/openfoodfacts/off/data_importer/DataImporterPlugin.kt +++ /dev/null @@ -1,64 +0,0 @@ -package org.openfoodfacts.off.data_importer - -import android.content.Context -import androidx.annotation.NonNull - -import io.flutter.embedding.engine.plugins.FlutterPlugin -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.MethodChannel.Result -import org.openfoodfacts.off.data_importer.DataImporterPlugin.Companion.LOGIN_PREFERENCES - -/** DataImporterPlugin */ -class DataImporterPlugin: FlutterPlugin, MethodCallHandler { - - companion object { - const val LOGIN_PREFERENCES = "login" - } - - private lateinit var channel : MethodChannel - private var context : Context? = null - - override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - context = flutterPluginBinding.applicationContext - channel = MethodChannel(flutterPluginBinding.binaryMessenger, "data_importer") - channel.setMethodCallHandler(this) - } - - override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { - val loginPreferences = getLoginPreferences() - if (loginPreferences == null) { - result.error("SP", "Shared Preferences don't exist!", "") - return - } - - when (call.method) { - "getUser" -> { - loginPreferences.apply { - val user = getString("user", null) - val password = getString("pass", null) - - result.success(mapOf("user" to user, "password" to password)) - } - } - "clearOldData" -> { - loginPreferences.run { - edit().clear().apply() - result.success(true) - } - } - else -> { - result.notImplemented() - } - } - } - - private fun getLoginPreferences() = - context?.getSharedPreferences(LOGIN_PREFERENCES, Context.MODE_PRIVATE) - - override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - context = null - } -} diff --git a/packages/data_importer/coverage/lcov.info b/packages/data_importer/coverage/lcov.info deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/data_importer/ios/.gitignore b/packages/data_importer/ios/.gitignore deleted file mode 100644 index 0c885071e36..00000000000 --- a/packages/data_importer/ios/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -.idea/ -.vagrant/ -.sconsign.dblite -.svn/ - -.DS_Store -*.swp -profile - -DerivedData/ -build/ -GeneratedPluginRegistrant.h -GeneratedPluginRegistrant.m - -.generated/ - -*.pbxuser -*.mode1v3 -*.mode2v3 -*.perspectivev3 - -!default.pbxuser -!default.mode1v3 -!default.mode2v3 -!default.perspectivev3 - -xcuserdata - -*.moved-aside - -*.pyc -*sync/ -Icon? -.tags* - -/Flutter/Generated.xcconfig -/Flutter/ephemeral/ -/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/packages/data_importer/ios/Assets/.gitkeep b/packages/data_importer/ios/Assets/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/data_importer/ios/Classes/DataImporterPlugin.h b/packages/data_importer/ios/Classes/DataImporterPlugin.h deleted file mode 100644 index f733d78b9e7..00000000000 --- a/packages/data_importer/ios/Classes/DataImporterPlugin.h +++ /dev/null @@ -1,4 +0,0 @@ -#import - -@interface DataImporterPlugin : NSObject -@end diff --git a/packages/data_importer/ios/Classes/DataImporterPlugin.m b/packages/data_importer/ios/Classes/DataImporterPlugin.m deleted file mode 100644 index 4541b82b02a..00000000000 --- a/packages/data_importer/ios/Classes/DataImporterPlugin.m +++ /dev/null @@ -1,15 +0,0 @@ -#import "DataImporterPlugin.h" -#if __has_include() -#import -#else -// Support project import fallback if the generated compatibility header -// is not copied when this plugin is created as a library. -// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 -#import "data_importer-Swift.h" -#endif - -@implementation DataImporterPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - [SwiftDataImporterPlugin registerWithRegistrar:registrar]; -} -@end diff --git a/packages/data_importer/ios/Classes/SwiftDataImporterPlugin.swift b/packages/data_importer/ios/Classes/SwiftDataImporterPlugin.swift deleted file mode 100644 index f764b510593..00000000000 --- a/packages/data_importer/ios/Classes/SwiftDataImporterPlugin.swift +++ /dev/null @@ -1,112 +0,0 @@ -import Flutter -import KeychainAccess -import RealmSwift -import UIKit - -public class SwiftDataImporterPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel( - name: "data_importer", binaryMessenger: registrar.messenger()) - let instance = SwiftDataImporterPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - if call.method == "getUser" { - let userName = UserDefaults.standard.string(forKey: "username") - - if userName != nil { - let keychain = Keychain(service: "org.openfoodfacts.openfoodfacts") - result(["user": userName, "password": keychain[userName!]]) - } else { - result(["user": nil, "password": nil]) - } - } else if call.method == "getHistory" { - configureRealm() - - result( - Array(getRealm().objects(HistoryItem.self).sorted(byKeyPath: "timestamp", ascending: false)) - .map { $0.barcode }) - } else if call.method == "clearOldData" { - UserDefaults.standard.removeObject(forKey: "username") - do { - try Keychain(service: "org.openfoodfacts.openfoodfacts").removeAll() - - let url = Realm.Configuration.defaultConfiguration.fileURL! - remove(realmURL: url) - result(true) - } catch { - result(false) - } - } else { - result(FlutterMethodNotImplemented) - } - } - - private func remove(realmURL: URL) { - let realmURLs = [ - realmURL, - realmURL.appendingPathExtension("lock"), - realmURL.appendingPathExtension("note"), - realmURL.appendingPathExtension("management"), - ] - for URL in realmURLs { - try? FileManager.default.removeItem(at: URL) - } - } - - private func configureRealm() { - // https://stackoverflow.com/questions/33363508/rlmexception-migration-is-required-for-object-type - let config = Realm.Configuration( - schemaVersion: 36, - // Set the block which will be called automatically when opening a Realm with - // a schema version lower than the one set above - migrationBlock: { _, oldSchemaVersion in - // Whenever your scheme changes your have to increase the schemaVersion in the migration block and update the needed migration within the block. - if oldSchemaVersion < 36 { - // Nothing to do! - // Realm will automatically detect new properties and removed properties - // And will update the schema on disk automatically - } - }) - - // Tell Realm to use this new configuration object for the default Realm - Realm.Configuration.defaultConfiguration = config - - // Now that we've told Realm how to handle the schema change, opening the file - // will automatically perform the migration - do { - _ = try Realm(configuration: config) - print("AppDelegate: Database Path : \(config.fileURL!)") - } catch { - print(error.localizedDescription) - } - } - - private func getRealm() -> Realm { - do { - return try Realm() - } catch let error as NSError { - fatalError("Could not get Realm instance") - } - fatalError("Could not get Realm instance") - } -} - -class HistoryItem: Object { - @objc dynamic var barcode = "" - @objc dynamic var productName: String? - @objc dynamic var brand: String? - @objc dynamic var quantity: String? - @objc dynamic var packaging: String? - @objc dynamic var labels: String? - @objc dynamic var imageUrl: String? - @objc dynamic var timestamp = Date() - @objc dynamic var nutriscore: String? - @objc dynamic var ecoscore: String? - let novaGroup = RealmOptional() - - override static func primaryKey() -> String? { - return "barcode" - } -} \ No newline at end of file diff --git a/packages/data_importer/ios/data_importer.podspec b/packages/data_importer/ios/data_importer.podspec deleted file mode 100644 index b733dd6968f..00000000000 --- a/packages/data_importer/ios/data_importer.podspec +++ /dev/null @@ -1,25 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint data_importer.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'data_importer' - s.version = '0.0.1' - s.summary = 'Data importer from V1' - s.description = <<-DESC -Data importer from V1 - DESC - s.homepage = 'https://openfoodfacts.org/' - s.license = { :file => '../LICENSE' } - s.author = { 'OpenFoodFacts' => 'contact@openfoodfacts.org' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'Flutter' - s.dependency 'KeychainAccess' - s.dependency 'RealmSwift' - s.platform = :ios, '9.0' - - # Flutter.framework does not contain a i386 slice. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } - s.swift_version = '5.0' -end diff --git a/packages/data_importer/lib/android/android_data_importer.dart b/packages/data_importer/lib/android/android_data_importer.dart deleted file mode 100644 index e1a352f9bcd..00000000000 --- a/packages/data_importer/lib/android/android_data_importer.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:data_importer/android/android_sqlite_importer.dart'; -import 'package:data_importer/shared/model.dart'; -import 'package:data_importer/shared/native_data_importer.dart'; -import 'package:data_importer/shared/platform_data_importer.dart'; - -/// Android Implementation with: -/// - Lists are saved in a SQLite database -/// - Credentials are saved with Shared Preferences -class AndroidDataImporter implements PlatformDataImporter { - @override - Future importLists() { - return AndroidDatabaseImporter.extract(); - } - - @override - Future importUser() { - return NativeDataImporter.getUser(); - } - - @override - Future deleteOldDataOnDevice() async { - return Future.wait(>[ - AndroidDatabaseImporter.removeDatabase(), - NativeDataImporter.clearOldData() - ]).then((List value) => !value.contains(false)); - } -} diff --git a/packages/data_importer/lib/android/android_sqlite_importer.dart b/packages/data_importer/lib/android/android_sqlite_importer.dart deleted file mode 100644 index d3300ee5642..00000000000 --- a/packages/data_importer/lib/android/android_sqlite_importer.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'dart:io'; - -import 'package:data_importer/data_importer.dart'; -import 'package:data_importer/shared/model.dart'; -import 'package:path/path.dart'; -import 'package:sqflite/sqflite.dart'; - -class AndroidDatabaseImporter { - const AndroidDatabaseImporter._(); - - static Future extract() async { - final String path = await _getDatabasePath(); - - if (!File(path).existsSync()) { - return null; - } - - final Database database = await openDatabase( - path, - version: 22, - readOnly: true, - ); - - final ImportableProductList history = - await _listProductsFromHistory(database); - final ImportableUserLists lists = await _listUserLists(database); - - await database.close(); - - return ImportableUserData( - history: history, - lists: lists, - ); - } - - static Future _getDatabasePath() async { - final String databasesPath = await getDatabasesPath(); - final String path = join(databasesPath, 'open_food_facts'); - return path; - } - - static Future _listProductsFromHistory( - Database database) async { - final List> list = await database.rawQuery(''' - SELECT BARCODE - FROM HISTORY_PRODUCT - WHERE TRIM(BARCODE) != "" - ORDER BY LAST_SEEN DESC - LIMIT ${ApplicationDataImporter.MAX_HISTORY_ITEMS} - '''); - - final ImportableProductList history = []; - - for (final Map res in list) { - final String? barcode = res['BARCODE'] as String?; - if (barcode != null) { - history.add(barcode); - } - } - return history; - } - - static Future _listUserLists(Database database) async { - final List> list = await database.rawQuery(''' - SELECT _id, listName - FROM PRODUCT_LISTS - '''); - - final ImportableUserLists lists = {}; - - for (final Map listItem in list) { - final int? listId = listItem['_id'] as int?; - final String? listName = listItem['listName'] as String?; - - if (listId != null && listName != null) { - lists.add( - ImportableUserList( - label: listName, - products: await _listProducts(database, listId), - ), - ); - } - } - return lists; - } - - static Future> _listProducts( - Database database, - int listId, - ) async { - final List> list = await database.rawQuery(''' - SELECT listId, barcode - FROM YOUR_LISTED_PRODUCT - WHERE listId = $listId - '''); - - final List barcodes = []; - - for (final Map res in list) { - final String? barcode = res['barcode'] as String?; - if (barcode != null) { - barcodes.add(barcode); - } - } - - return barcodes; - } - - static Future removeDatabase() { - return _getDatabasePath().then((String path) async { - final File file = File(path); - - if (file.existsSync()) { - await file.delete(); - } - - return true; - }).onError((Object? error, StackTrace stackTrace) => false); - } -} diff --git a/packages/data_importer/lib/data_importer.dart b/packages/data_importer/lib/data_importer.dart deleted file mode 100644 index 4806e558463..00000000000 --- a/packages/data_importer/lib/data_importer.dart +++ /dev/null @@ -1,292 +0,0 @@ -import 'package:data_importer/shared/model.dart'; -import 'package:data_importer/shared/platform_data_importer.dart'; -import 'package:data_importer_shared/data_importer_shared.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; - -/// Data importer from "V1" apps to Smoothie ("V2") with: -/// - History and user lists -/// - User credentials (login / password) -class ApplicationDataImporter { - ApplicationDataImporter({ - required this.storage, - required this.importer, - }); - - /// Some users may have a long history on the previous app, we limit the - /// import of last scanned products to this constant - static const int MAX_HISTORY_ITEMS = 1000; - - /// The maximum number of times [startImport] can be called automatically - static const int MAX_NUMBER_RETRIES = 5; - - /// In the case where we want to change the migration, we store the current - /// version - /// V1 = initial version, where all data was kept on the device - /// V2 = second version, where all data is wiped (if the process is successful) - static const int _MIGRATION_CURRENT_VERSION = 2; - static const int _MIGRATION_INITIAL_VERSION = 1; - - static const String _MIGRATION_STATUS_SUCCESS = 'success'; - static const String _MIGRATION_STATUS_ERROR = 'error'; - - /// Key to track in which version the migration was accomplished - static const String _KEY_MIGRATION_VERSION = '_data_importer_version'; - - /// Key to track the migration with values being [null] (first time), - /// [_MIGRATION_STATUS_SUCCESS] or [_MIGRATION_STATUS_ERROR] - static const String _KEY_GLOBAL_MIGRATION_STATUS = '_data_importer_from_v1'; - - /// Key to track the credentials' migration with values being [null] - /// (first time), [_MIGRATION_STATUS_SUCCESS] or [_MIGRATION_STATUS_ERROR] - static const String _KEY_CREDENTIALS_MIGRATION_STATUS = - '_data_importer_credentials_from_v1'; - - /// Key to track the lists' migration with values being [null] (first time), - /// [_MIGRATION_STATUS_SUCCESS] or [_MIGRATION_STATUS_ERROR] - static const String _KEY_LISTS_MIGRATION_STATUS = - '_data_importer_lists_from_v1'; - - /// Key to track the number of retries, in the case of an error - /// This limit cannot exceed [_MAX_NUMBER_RETRIES] - static const String _KEY_MIGRATION_RETRIES = '_data_importer_retries'; - - /// --- Errors - /// In case of a migration error, the error message is available in this key - static const String _KEY_GLOBAL_MIGRATION_STATUS_ERROR_REASON = - '_data_importer_error'; - - /// In case of a credentials' migration error, the error message is available - /// in this key - static const String _KEY_CREDENTIALS_MIGRATION_STATUS_ERROR_REASON = - '_data_importer_credentials_error'; - - /// In case of a lists' migration error, the error message is available in - /// this key - static const String _KEY_LISTS_MIGRATION_STATUS_ERROR_REASON = - '_data_importer_lists_error'; - - /// Storage used to save the keys/values defined above - final FlutterSecureStorage storage; - final DataImporter importer; - - /// The credentials migration is not mandatory, since the user may have - /// changed its password - Future requireMigration() async { - final int version = await _getMigrationVersion(); - - if (version == _MIGRATION_INITIAL_VERSION) { - // A previous migration was successful, but data is still on the device - await _resetRetriesCount(); - return true; - } - - return version < _MIGRATION_CURRENT_VERSION && - (await requireListsMigration()); - } - - Future requireCredentialsMigration() { - return storage - .read(key: _KEY_CREDENTIALS_MIGRATION_STATUS) - .then((String? value) => value != _MIGRATION_STATUS_SUCCESS); - } - - Future requireListsMigration() { - return storage - .read(key: _KEY_LISTS_MIGRATION_STATUS) - .then((String? value) => value != _MIGRATION_STATUS_SUCCESS); - } - - Future get currentRetriesCount { - return storage - .read(key: _KEY_MIGRATION_RETRIES) - .then((String? value) => value != null ? int.parse(value) : 0); - } - - Future _incrementRetriesCount() { - return currentRetriesCount.then( - (int retries) => storage.write( - key: _KEY_MIGRATION_RETRIES, - value: (retries + 1).toString(), - ), - ); - } - - Future _resetRetriesCount() { - return currentRetriesCount.then( - (int retries) => storage.write( - key: _KEY_MIGRATION_RETRIES, - value: '0', - ), - ); - } - - /// Start the importation process with both credentials and lists. - /// If the credentials fail, lists will still be migrated. - /// - /// In case of an error, this method can be called [MAX_NUMBER_RETRIES] - /// automatically or with [forceMechanism] set to [true]. - Future startImport({ - bool forceMechanism = false, - }) async { - if (await requireMigration() && - (forceMechanism || (await currentRetriesCount) < MAX_NUMBER_RETRIES)) { - try { - final PlatformDataImporter platformImporter = PlatformDataImporter(); - - if (await _getMigrationVersion() < _MIGRATION_INITIAL_VERSION) { - /// Import credentials first - try { - await startUserCredentialsMigration(platformImporter); - } catch (err) { - // Non blocking error (eg: changed password) - } - - await startListsMigration(platformImporter); - } - - if (await _deleteDataOnDevice(platformImporter) == true) { - await _markMigrationAsSuccessful(); - } else { - await _onMigrationError(); - } - } catch (err) { - await _onMigrationError(err: err); - } - } - } - - Future _onMigrationError({dynamic err}) async { - await _incrementRetriesCount(); - await _markMigrationAsFailed(err); - } - - Future startListsMigration( - PlatformDataImporter platformImporter, - ) async { - if (await requireListsMigration()) { - try { - final ImportableUserData? importLists = - await platformImporter.importLists(); - if (importLists != null) { - final bool res = await importer.importLists( - importLists.toUserData(MAX_HISTORY_ITEMS), - ); - - if (!res) { - throw Exception('Migration failed!'); - } - } - } catch (err) { - await _markListsMigrationAsFailed(err); - rethrow; - } - } - } - - Future startUserCredentialsMigration( - PlatformDataImporter platformImporter, - ) async { - if (await requireCredentialsMigration()) { - try { - final ImportableUser? user = await platformImporter.importUser(); - - if (user != null) { - await importer.importUser(user.toUser()); - } - } catch (err) { - await _markCredentialsMigrationAsFailed(err); - rethrow; - } - } - } - - Future _deleteDataOnDevice(PlatformDataImporter platformImporter) => - platformImporter.deleteOldDataOnDevice(); - - Future> _markMigrationAsSuccessful() { - return Future.wait(>[ - storage.write( - key: _KEY_GLOBAL_MIGRATION_STATUS, - value: _MIGRATION_STATUS_SUCCESS, - ), - storage.write( - key: _KEY_CREDENTIALS_MIGRATION_STATUS, - value: _MIGRATION_STATUS_SUCCESS, - ), - storage.write( - key: _KEY_LISTS_MIGRATION_STATUS, - value: _MIGRATION_STATUS_SUCCESS, - ), - storage.write( - key: _KEY_GLOBAL_MIGRATION_STATUS_ERROR_REASON, - value: null, - ), - storage.write( - key: _KEY_CREDENTIALS_MIGRATION_STATUS_ERROR_REASON, - value: null, - ), - storage.write( - key: _KEY_LISTS_MIGRATION_STATUS_ERROR_REASON, - value: null, - ), - _resetRetriesCount(), - _saveMigrationVersion(), - ]); - } - - Future> _markMigrationAsFailed(dynamic error) async { - return Future.wait(>[ - storage.write( - key: _KEY_GLOBAL_MIGRATION_STATUS, - value: _MIGRATION_STATUS_ERROR, - ), - storage.write( - key: _KEY_GLOBAL_MIGRATION_STATUS_ERROR_REASON, - value: error?.toString(), - ), - _saveMigrationVersion(), - ]); - } - - Future> _markCredentialsMigrationAsFailed(dynamic error) async { - return Future.wait(>[ - storage.write( - key: _KEY_CREDENTIALS_MIGRATION_STATUS, - value: _MIGRATION_STATUS_ERROR, - ), - storage.write( - key: _KEY_CREDENTIALS_MIGRATION_STATUS_ERROR_REASON, - value: error?.toString(), - ) - ]); - } - - Future> _markListsMigrationAsFailed(dynamic error) async { - return Future.wait(>[ - storage.write( - key: _KEY_LISTS_MIGRATION_STATUS, - value: _MIGRATION_STATUS_ERROR, - ), - storage.write( - key: _KEY_LISTS_MIGRATION_STATUS_ERROR_REASON, - value: error?.toString(), - ), - ]); - } - - Future _saveMigrationVersion() async { - return storage.write( - key: _KEY_MIGRATION_VERSION, - value: _MIGRATION_CURRENT_VERSION.toString(), - ); - } - - /// Returns the current migration version - /// If no migration was achieved, it will return 0 - Future _getMigrationVersion() async { - return int.parse(await storage.read( - key: _KEY_MIGRATION_VERSION, - ) ?? - '0'); - } -} diff --git a/packages/data_importer/lib/ios/ios_data_importer.dart b/packages/data_importer/lib/ios/ios_data_importer.dart deleted file mode 100644 index 27f52a31df8..00000000000 --- a/packages/data_importer/lib/ios/ios_data_importer.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:data_importer/shared/model.dart'; -import 'package:data_importer/shared/native_data_importer.dart'; -import 'package:data_importer/shared/platform_data_importer.dart'; - -/// iOS Implementation with: -/// - Lists (only history) are saved in a Realm database -/// - Credentials are saved with NSDefaults & KeyChain -class IOSDataImporter implements PlatformDataImporter { - /// Only imports history. - /// User lists were not implemented in V1. - @override - Future importLists() async { - final dynamic res = await NativeDataImporter.getHistory(); - - if (res is List) { - return ImportableUserData(history: res.cast()); - } else { - return null; - } - } - - /// Imports user credentials (login & password) - @override - Future importUser() async { - return NativeDataImporter.getUser(); - } - - @override - Future deleteOldDataOnDevice() { - return NativeDataImporter.clearOldData(); - } -} diff --git a/packages/data_importer/lib/shared/model.dart b/packages/data_importer/lib/shared/model.dart deleted file mode 100644 index 20029f529c8..00000000000 --- a/packages/data_importer/lib/shared/model.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'dart:math' as math; - -import 'package:data_importer_shared/data_importer_shared.dart'; - -class ImportableUser { - ImportableUser({ - required this.userName, - required this.password, - }); - - final String userName; - final String password; - - UserCredentials toUser() => UserCredentials(userName, password); -} - -class ImportableUserData { - ImportableUserData({ - Iterable? history, - this.lists, - }) : history = history - ?.where((String barcode) => barcode.isNotEmpty) - .toList(growable: false); - - final ImportableProductList? history; - final ImportableUserLists? lists; - - UserListsData toUserData(int maxHistoryLength) => UserListsData( - history?.sublist( - 0, history != null ? math.min(history!.length, maxHistoryLength) : 0), - lists?.map( - (ImportableUserList list) { - return list.toUserList(); - }, - ).toSet()); -} - -class ImportableUserList { - ImportableUserList({ - required this.label, - required this.products, - }); - - final String label; - final ImportableProductList products; - - MapEntry export() => MapEntry( - label, - { - 'barcodes': products.join(','), - }, - ); - - UserList toUserList() => UserList(label, products); -} - -typedef ImportableUserLists = Set; -typedef ImportableProductList = List; diff --git a/packages/data_importer/lib/shared/native_data_importer.dart b/packages/data_importer/lib/shared/native_data_importer.dart deleted file mode 100644 index 23406bb48c4..00000000000 --- a/packages/data_importer/lib/shared/native_data_importer.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:data_importer/shared/model.dart'; -import 'package:flutter/services.dart'; - -/// Supports both Android & iOS -class NativeDataImporter { - const NativeDataImporter._(); - - /// Method channel with three features: - /// - getUser - /// - getHistory - /// - clearOldData - static const MethodChannel methodChannel = MethodChannel('data_importer'); - - /// Returns the user credentials from V1 - /// On Android: Shared Preferences (login.xml) - /// On iOS: UserDefaults (login) + KeyChain (password) - static Future getUser() async { - final Map? user = - await methodChannel.invokeMethod>('getUser'); - - if (user != null) { - final String? userName = user['user'] as String?; - final String? password = user['password'] as String?; - - if (userName?.isNotEmpty == true && password?.isNotEmpty == true) { - return ImportableUser( - userName: userName!, - password: password!, - ); - } - } - return null; - } - - /// Returns a list of barcodes (only on iOS) - static Future getHistory() { - return methodChannel.invokeMethod('getHistory'); - } - - /// Remove old data (only used on new migration processes) - static Future clearOldData() { - return methodChannel - .invokeMethod('clearOldData') - .then((bool? value) => value ?? false) - .onError((Object? error, StackTrace stackTrace) => false); - } -} diff --git a/packages/data_importer/lib/shared/platform_data_importer.dart b/packages/data_importer/lib/shared/platform_data_importer.dart deleted file mode 100644 index b362b25dc63..00000000000 --- a/packages/data_importer/lib/shared/platform_data_importer.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'dart:io'; - -import 'package:data_importer/android/android_data_importer.dart'; -import 'package:data_importer/ios/ios_data_importer.dart'; -import 'package:data_importer/shared/model.dart'; - -abstract class PlatformDataImporter { - factory PlatformDataImporter() { - if (Platform.isAndroid) { - return AndroidDataImporter(); - } else if (Platform.isIOS) { - return IOSDataImporter(); - } else { - throw UnimplementedError('Unsupported platform!'); - } - } - - Future importUser(); - Future importLists(); - Future deleteOldDataOnDevice(); -} diff --git a/packages/data_importer/pubspec.yaml b/packages/data_importer/pubspec.yaml deleted file mode 100644 index 7f143906102..00000000000 --- a/packages/data_importer/pubspec.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: data_importer -description: Data importer from V1 -version: 1.0.0 -publish_to: none - -environment: - sdk: '>=3.1.0 <4.0.0' - -dependencies: - flutter: - sdk: flutter - - data_importer_shared: - path: ../data_importer_shared - - sqflite: ^2.0.2+1 - - flutter_secure_storage: 8.0.0 - path: ^1.8.0 - -dev_dependencies: - flutter_driver: - sdk: flutter - flutter_test: - sdk: flutter - openfoodfacts_flutter_lints: - git: https://github.com/openfoodfacts/openfoodfacts_flutter_lints.git - -flutter: - plugin: - platforms: - android: - package: org.openfoodfacts.off.data_importer - pluginClass: DataImporterPlugin - ios: - pluginClass: DataImporterPlugin \ No newline at end of file diff --git a/packages/data_importer/test/data_importer_test.dart b/packages/data_importer/test/data_importer_test.dart deleted file mode 100644 index 8963f00be1e..00000000000 --- a/packages/data_importer/test/data_importer_test.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; - -// The CI needs at least one test -void main() { - testWidgets('Fake test', (WidgetTester tester) async { - expect(true, true); - }); -} diff --git a/packages/data_importer_shared/.gitignore b/packages/data_importer_shared/.gitignore deleted file mode 100644 index 0c44ab06c36..00000000000 --- a/packages/data_importer_shared/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -# Files and directories created by pub -.dart_tool/ -.packages - -# Omit commiting pubspec.lock for library packages: -# https://dart.dev/guides/libraries/private-files#pubspeclock -pubspec.lock - -# Conventional directory for build outputs -build/ - -# Directory created by dartdoc -doc/api/ diff --git a/packages/data_importer_shared/analysis_options.yaml b/packages/data_importer_shared/analysis_options.yaml deleted file mode 100644 index 3a784401bab..00000000000 --- a/packages/data_importer_shared/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../smooth_app/analysis_options.yaml \ No newline at end of file diff --git a/packages/data_importer_shared/coverage/lcov.info b/packages/data_importer_shared/coverage/lcov.info deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/data_importer_shared/lib/data_importer_shared.dart b/packages/data_importer_shared/lib/data_importer_shared.dart deleted file mode 100644 index 7275961c51a..00000000000 --- a/packages/data_importer_shared/lib/data_importer_shared.dart +++ /dev/null @@ -1,65 +0,0 @@ -/// Support for doing something awesome. -/// -/// More dartdocs go here. -library data_importer_shared; - -class DataImporter { - DataImporter({ - required this.importUser, - required this.importLists, - }); - - final Future Function(UserCredentials user) importUser; - final Future Function(UserListsData data) importLists; -} - -class UserCredentials { - UserCredentials( - this.userName, - this.password, - ); - - final String userName; - final String password; -} - -class UserListsData { - UserListsData( - this.history, - this.lists, - ); - - final ProductList? history; - final UserLists? lists; - - Map export() { - final Map userLists = {}; - - if (lists != null) { - for (final UserList list in lists!) { - userLists[list.label] = list.export(); - } - } - - return { - 'history': { - 'barcodes': history, - }, - 'user_lists': userLists, - }; - } -} - -class UserList { - UserList(this.label, this.products); - - final String label; - final ProductList products; - - Map export() => { - 'barcodes': products, - }; -} - -typedef UserLists = Set; -typedef ProductList = List; diff --git a/packages/data_importer_shared/pubspec.yaml b/packages/data_importer_shared/pubspec.yaml deleted file mode 100644 index 1d9ca87f76e..00000000000 --- a/packages/data_importer_shared/pubspec.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: data_importer_shared -description: A starting point for Dart libraries or applications. -publish_to: none - -environment: - sdk: '>=3.1.0 <4.0.0' - -dev_dependencies: - flutter_driver: - sdk: flutter - flutter_test: - sdk: flutter - openfoodfacts_flutter_lints: - git: https://github.com/openfoodfacts/openfoodfacts_flutter_lints.git diff --git a/packages/data_importer_shared/test/data_importer_shared_test.dart b/packages/data_importer_shared/test/data_importer_shared_test.dart deleted file mode 100644 index 8963f00be1e..00000000000 --- a/packages/data_importer_shared/test/data_importer_shared_test.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; - -// The CI needs at least one test -void main() { - testWidgets('Fake test', (WidgetTester tester) async { - expect(true, true); - }); -} diff --git a/packages/smooth_app/ios/Podfile.lock b/packages/smooth_app/ios/Podfile.lock index f0a82c715f5..e40cb700fb3 100644 --- a/packages/smooth_app/ios/Podfile.lock +++ b/packages/smooth_app/ios/Podfile.lock @@ -8,10 +8,6 @@ PODS: - connectivity_plus (0.0.1): - Flutter - ReachabilitySwift - - data_importer (0.0.1): - - Flutter - - KeychainAccess - - RealmSwift - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) @@ -71,7 +67,6 @@ PODS: - Flutter - iso_countries (0.0.1): - Flutter - - KeychainAccess (4.2.2) - libwebp (1.3.1): - libwebp/demux (= 1.3.1) - libwebp/mux (= 1.3.1) @@ -126,11 +121,6 @@ PODS: - Flutter - MTBBarcodeScanner - ReachabilitySwift (5.0.0) - - Realm (10.42.0): - - Realm/Headers (= 10.42.0) - - Realm/Headers (10.42.0) - - RealmSwift (10.42.0): - - Realm (= 10.42.0) - rive_common (0.0.1): - Flutter - SDWebImage (5.17.0): @@ -166,7 +156,6 @@ DEPENDENCIES: - audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/ios`) - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - - data_importer (from `.symlinks/plugins/data_importer/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - flutter_custom_tabs (from `.symlinks/plugins/flutter_custom_tabs/ios`) @@ -202,7 +191,6 @@ SPEC REPOS: - GoogleUtilities - GoogleUtilitiesComponents - GTMSessionFetcher - - KeychainAccess - libwebp - Mantle - MLImage @@ -213,8 +201,6 @@ SPEC REPOS: - nanopb - PromisesObjC - ReachabilitySwift - - Realm - - RealmSwift - SDWebImage - SDWebImageWebPCoder - Sentry @@ -229,8 +215,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/camera_avfoundation/ios" connectivity_plus: :path: ".symlinks/plugins/connectivity_plus/ios" - data_importer: - :path: ".symlinks/plugins/data_importer/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: @@ -286,8 +270,7 @@ SPEC CHECKSUMS: app_settings: d103828c9f5d515c4df9ee754dabd443f7cedcf3 audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40 camera_avfoundation: 3125e8cd1a4387f6f31c6c63abb8a55892a9eeeb - connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a - data_importer: ab8c74aaf553878170aed03c03626d5820c5cb1f + connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89 @@ -307,7 +290,6 @@ SPEC CHECKSUMS: in_app_review: 4a97249f7a2f539a0f294c2d9196b7fe35e49541 integration_test: 13825b8a9334a850581300559b8839134b124670 iso_countries: eb09d40f388e4c65e291e0bb36a701dfe7de6c74 - KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51 libwebp: 33dc822fbbf4503668d09f7885bbfedc76c45e96 Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d MLImage: 7bb7c4264164ade9bf64f679b40fb29c8f33ee9b @@ -323,8 +305,6 @@ SPEC CHECKSUMS: PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 - Realm: 490aad28f1360e58fc22256d5d686d3a36525346 - RealmSwift: f6a9b56d747bbdd7931de1835896c5f024b6898a rive_common: 8a159d68033a8b073e5853acc50f03aa486a2888 SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9 SDWebImageWebPCoder: af09429398d99d524cae2fe00f6f0f6e491ed102 @@ -340,4 +320,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: aa97d8b016d7264ad0a1ef5c44765133ae787bc4 -COCOAPODS: 1.13.0 +COCOAPODS: 1.14.2 diff --git a/packages/smooth_app/lib/helpers/data_importer/product_list_import_export.dart b/packages/smooth_app/lib/helpers/data_importer/product_list_import_export.dart deleted file mode 100644 index 2448701f9eb..00000000000 --- a/packages/smooth_app/lib/helpers/data_importer/product_list_import_export.dart +++ /dev/null @@ -1,212 +0,0 @@ -import 'dart:convert'; - -import 'package:openfoodfacts/openfoodfacts.dart'; -import 'package:smooth_app/data_models/product_list.dart'; -import 'package:smooth_app/database/dao_product.dart'; -import 'package:smooth_app/database/dao_product_list.dart'; -import 'package:smooth_app/database/local_database.dart'; -import 'package:smooth_app/helpers/collections_helper.dart'; -import 'package:smooth_app/pages/product/common/product_refresher.dart'; -import 'package:smooth_app/query/product_query.dart'; - -/// Import / Export of product lists (with or without JSON). -/// All lists are composed of barcodes, where we support history and user lists -class ProductListImportExport { - /// With a big history, and to limit the size and duration of a single request, - /// we call multiple times the API - static const int MAX_PRODUCTS_PER_REQUEST = 250; - - static const String TMP_IMPORT = ''' - {"history":{ - "barcodes":["3274080005003", "7622210449283"]}, - "user_lists":{ - "my awesome list":{"barcodes":["5449000000996", "3017620425035"]}, - "saved for later":{"barcodes":["3175680011480", "1234567891"]} - }}'''; - - Future importFromJSON( - final String jsonEncoded, - final LocalDatabase localDatabase, - ) async { - try { - final dynamic map = json.decode(jsonEncoded); - if (map is! Map) { - throw Exception('Expected Map'); - } - - return import(ImportableLists(map), localDatabase); - } catch (err) { - return false; - } - } - - Future import( - final ImportableLists lists, - final LocalDatabase localDatabase, - ) async { - try { - final Set inputBarcodes = lists.extractBarcodes(); - final List products = []; - - // To prevent a too long request, we limit the barcodes to fetch - for (final Iterable barcodes - in inputBarcodes.split(MAX_PRODUCTS_PER_REQUEST)) { - products.addAll(await _fetchProducts(barcodes)); - } - - await _saveNewProducts(localDatabase, products); - await _saveNewProductsToLists(localDatabase, lists, products); - - return true; - } catch (err) { - return false; - } - } - - /// Fetches Products from the API - Future> _fetchProducts( - Iterable barcodes, - ) async { - final SearchResult searchResult = await OpenFoodAPIClient.searchProducts( - ProductQuery.getUser(), - ProductRefresher().getBarcodeListQueryConfiguration( - barcodes.toList(growable: false), - ), - uriHelper: ProductQuery.uriProductHelper, - ); - - return searchResult.products ?? []; - } - - /// Stores products in the database - Future _saveNewProducts( - LocalDatabase localDatabase, - List products, - ) async { - final DaoProduct daoProduct = DaoProduct(localDatabase); - final Map productsToAdd = {}; - for (final Product product in products) { - productsToAdd[product.barcode!] = product; - await daoProduct.put(product); - } - } - - /// Stores lists with their products in the database - /// History & user's lists are supported - Future _saveNewProductsToLists( - LocalDatabase localDatabase, - ImportableLists lists, - List products, - ) async { - final DaoProductList daoProductList = DaoProductList(localDatabase); - await _importHistory(lists.history, products, daoProductList); - await _importUserLists(lists.userLists, products, daoProductList); - } - - Future _importHistory( - ImportableList? list, - List fetchedProducts, - DaoProductList daoProductList, - ) async { - return _importList( - list, - ProductList.history(), - fetchedProducts, - daoProductList, - ); - } - - Future> _importUserLists( - ImportableUserLists? lists, - List fetchedProducts, - DaoProductList daoProductList, - ) async { - if (lists?.lists.isNotEmpty != true) { - return []; - } - - final List> tasks = >[]; - for (final String key in lists!.lists.keys) { - tasks.add(_importList( - lists.lists[key], - ProductList.user(key), - fetchedProducts, - daoProductList, - )); - } - - return Future.wait(tasks); - } - - Future _importList( - ImportableList? list, - ProductList productList, - List fetchedProducts, - DaoProductList daoProductList, - ) async { - if (list == null) { - return; - } - - productList.set(list.barcodes); - await DaoProduct(daoProductList.localDatabase).putAll(fetchedProducts); - return daoProductList.put(productList); - } -} - -class ImportableLists { - ImportableLists( - Map json, - ) : history = json['history'] is Map - ? ImportableList(json['history'] as Map) - : null, - userLists = json['user_lists'] is Map - ? ImportableUserLists(json['user_lists'] as Map) - : null; - - final ImportableList? history; - final ImportableUserLists? userLists; - - /// Extract barcodes from history and user's lists to have a Set (= unique) - /// of all them - Set extractBarcodes() { - final Set barcodes = {}; - - barcodes.addAllSafe(history?.barcodes); - barcodes.addAllSafe(userLists?.extractBarcodes()); - - return barcodes; - } -} - -class ImportableUserLists { - ImportableUserLists( - Map json, - ) : lists = json.map( - (String key, dynamic value) => MapEntry( - key, - ImportableList(json[key] as Map), - ), - ); - - final Map lists; - - /// Extract barcodes from all lists to have a Set (= unique) of all them - Set extractBarcodes() { - final Set barcodes = {}; - - for (final String key in lists.keys) { - barcodes.addAllSafe(lists[key]?.barcodes); - } - - return barcodes; - } -} - -class ImportableList { - ImportableList( - Map json, - ) : barcodes = (json['barcodes'] as List).cast(); - - final List barcodes; -} diff --git a/packages/smooth_app/lib/helpers/data_importer/smooth_app_data_importer.dart b/packages/smooth_app/lib/helpers/data_importer/smooth_app_data_importer.dart deleted file mode 100644 index b8d12d98415..00000000000 --- a/packages/smooth_app/lib/helpers/data_importer/smooth_app_data_importer.dart +++ /dev/null @@ -1,140 +0,0 @@ -import 'package:data_importer/data_importer.dart'; -import 'package:data_importer_shared/data_importer_shared.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:openfoodfacts/openfoodfacts.dart' as off; -import 'package:smooth_app/data_models/user_management_provider.dart'; -import 'package:smooth_app/database/local_database.dart'; -import 'package:smooth_app/helpers/data_importer/product_list_import_export.dart'; - -/// Mechanism used to transfer/import data from V1 apps (Android and iOS) -/// At any time, the current [status] is publicly available. -class SmoothAppDataImporter extends ChangeNotifier { - SmoothAppDataImporter(this.database); - - /// Database to store lists - final LocalDatabase database; - - /// Lazy initialized module - ApplicationDataImporter? _dataImporter; - SmoothAppDataImporterStatus _status = SmoothAppDataImporterStatus.notStarted; - - void _init() { - _dataImporter ??= ApplicationDataImporter( - storage: const FlutterSecureStorage(), - importer: DataImporter( - importUser: _importUser, - importLists: _importLists, - ), - ); - } - - void startMigrationAsync({ - bool forceMigration = false, - }) { - _init(); - _startMigration( - forceMigration: forceMigration, - ); - } - - Future _checkStatus() async { - if (_status == SmoothAppDataImporterStatus.notStarted) { - _init(); - - if (await _dataImporter!.requireMigration()) { - _updateStatus(SmoothAppDataImporterStatus.required); - } else { - _updateStatus(SmoothAppDataImporterStatus.alreadyDone); - } - } - } - - Future _startMigration({ - bool forceMigration = false, - }) async { - await _checkStatus(); - - if (forceMigration || await _dataImporter!.requireMigration()) { - _updateStatus(SmoothAppDataImporterStatus.inProgress); - await _dataImporter!.startImport( - forceMechanism: forceMigration, - ); - _updateStatus(SmoothAppDataImporterStatus.success); - notifyListeners(); - } else { - _updateStatus(SmoothAppDataImporterStatus.alreadyDone); - } - } - - Future _importUser(UserCredentials user) { - return UserManagementProvider().login( - off.User( - userId: user.userName, - password: user.password, - ), - ); - } - - Future _importLists(UserListsData data) { - return ProductListImportExport().import( - ImportableLists(data.export()), - database, - ); - } - - void _updateStatus(SmoothAppDataImporterStatus status) { - if (_status != status) { - _status = status; - notifyListeners(); - } - } - - SmoothAppDataImporterStatus get status { - return _status; - } -} - -enum SmoothAppDataImporterStatus { - /// Migration was accomplished in a previous run or if it's a brand new install - alreadyDone, - - /// The current migration is successful - success, - - /// The current migration has an error - error, - - /// The current migration is in progress - inProgress, - - /// No migration (which can be [alreadyDone]) - required, - - /// No migration started (call [_checkStatus] to update) - notStarted; - - String printableLabel(AppLocalizations appLocalizations) { - switch (this) { - case SmoothAppDataImporterStatus.alreadyDone: - return appLocalizations.dev_preferences_migration_status_already_done; - case SmoothAppDataImporterStatus.success: - return appLocalizations.dev_preferences_migration_status_success; - case SmoothAppDataImporterStatus.error: - return appLocalizations.dev_preferences_migration_status_error; - case SmoothAppDataImporterStatus.inProgress: - return appLocalizations.dev_preferences_migration_status_in_progress; - case SmoothAppDataImporterStatus.required: - return appLocalizations.dev_preferences_migration_status_required; - case SmoothAppDataImporterStatus.notStarted: - return appLocalizations.dev_preferences_migration_status_not_started; - } - } - - bool get canInitiateMigration => [ - SmoothAppDataImporterStatus.error, - SmoothAppDataImporterStatus.required, - SmoothAppDataImporterStatus.notStarted, - ].contains(this); -} diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index 4553986fefe..14ff1a92eb5 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -1585,10 +1585,6 @@ "dev_preferences_migration_status_in_progress": "in progress", "dev_preferences_migration_status_required": "required (click to start)", "dev_preferences_migration_status_not_started": "unknown", - "dev_preferences_import_history_title": "Import History", - "@dev_preferences_import_history_title": { - "description": "User dev preferences - Import history - Title" - }, "dev_preferences_import_history_subtitle": "Will clear history and put 3 products in there", "@dev_preferences_import_history_subtitle": { "description": "User dev preferences - Import history - Subtitle" diff --git a/packages/smooth_app/lib/main.dart b/packages/smooth_app/lib/main.dart index a050bde1b30..313d82f75ee 100644 --- a/packages/smooth_app/lib/main.dart +++ b/packages/smooth_app/lib/main.dart @@ -23,7 +23,6 @@ import 'package:smooth_app/database/dao_string.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/camera_helper.dart'; -import 'package:smooth_app/helpers/data_importer/smooth_app_data_importer.dart'; import 'package:smooth_app/helpers/entry_points_helper.dart'; import 'package:smooth_app/helpers/global_vars.dart'; import 'package:smooth_app/helpers/network_config.dart'; @@ -101,7 +100,6 @@ class SmoothApp extends StatefulWidget { State createState() => _SmoothAppState(); } -late SmoothAppDataImporter _appDataImporter; late UserPreferences _userPreferences; late ProductPreferences _productPreferences; late LocalDatabase _localDatabase; @@ -127,7 +125,6 @@ Future _init1() async { await UserManagementProvider.mountCredentials(); _userPreferences = await UserPreferences.getUserPreferences(); _localDatabase = await LocalDatabase.getLocalDatabase(); - _appDataImporter = SmoothAppDataImporter(_localDatabase); await _continuousScanModel.load(_localDatabase); _productPreferences = ProductPreferences( ProductPreferencesSelection( @@ -182,9 +179,15 @@ class _SmoothAppState extends State { if (!_screenshots) { await _userPreferences.init(_productPreferences); } + await _initAppLanguage(); return true; } + Future _initAppLanguage() { + ProductQuery.setLanguage(context, _userPreferences); + return _productPreferences.refresh(); + } + @override Widget build(BuildContext context) { return FutureBuilder( @@ -219,7 +222,6 @@ class _SmoothAppState extends State { provide(_textContrastProvider), provide(_userManagementProvider), provide(_continuousScanModel), - provide(_appDataImporter), provide(_permissionListener), ], child: AppNavigator( diff --git a/packages/smooth_app/lib/pages/navigator/app_navigator.dart b/packages/smooth_app/lib/pages/navigator/app_navigator.dart index b6b930b8364..0a012648499 100644 --- a/packages/smooth_app/lib/pages/navigator/app_navigator.dart +++ b/packages/smooth_app/lib/pages/navigator/app_navigator.dart @@ -4,9 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; -import 'package:smooth_app/data_models/product_preferences.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; -import 'package:smooth_app/helpers/data_importer/smooth_app_data_importer.dart'; import 'package:smooth_app/helpers/extension_on_text_helper.dart'; import 'package:smooth_app/pages/carousel_manager.dart'; import 'package:smooth_app/pages/navigator/error_page.dart'; @@ -18,7 +16,6 @@ import 'package:smooth_app/pages/product/edit_product_page.dart'; import 'package:smooth_app/pages/product/new_product_page.dart'; import 'package:smooth_app/pages/product/product_loader_page.dart'; import 'package:smooth_app/pages/scan/search_page.dart'; -import 'package:smooth_app/query/product_query.dart'; /// A replacement for the [Navigator], where we internally use [GoRouter]. /// By itself the [GoRouter] attribute is not accessible, to allow us to easily @@ -109,11 +106,6 @@ class _SmoothGoRouter { GoRoute( path: _InternalAppRoutes.HOME_PAGE, builder: (BuildContext context, GoRouterState state) { - if (!isInitialized) { - _initAppLanguage(context); - isInitialized = true; - } - return _findLastOnboardingPage(context); }, // We use sub-routes to allow the back button to work correctly @@ -285,18 +277,6 @@ class _SmoothGoRouter { static _SmoothGoRouter? _singleton; late GoRouter router; - // Indicates whether [_initAppLanguage] was already called - bool isInitialized = false; - - void _initAppLanguage(BuildContext context) { - final UserPreferences userPreferences = context.read(); - ProductQuery.setLanguage(context, userPreferences); - context.read().refresh(); - - // The migration requires the language to be set in the app! - context.read().startMigrationAsync(); - } - /// Extract the barcode from a path only if the route have at least 8 digits /// in the second part (we don't care about extra elements) /// Some examples: diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart index 4ccea926329..c8585117538 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart @@ -11,8 +11,6 @@ import 'package:smooth_app/data_models/product_list.dart'; import 'package:smooth_app/database/dao_product_list.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; -import 'package:smooth_app/helpers/data_importer/product_list_import_export.dart'; -import 'package:smooth_app/helpers/data_importer/smooth_app_data_importer.dart'; import 'package:smooth_app/pages/offline_data_page.dart'; import 'package:smooth_app/pages/offline_tasks_page.dart'; import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart'; @@ -228,7 +226,6 @@ class UserPreferencesDevMode extends AbstractUserPreferences { ); }, ), - _dataImporterTile(), UserPreferencesItemTile( title: appLocalizations.offline_data, onTap: () => Navigator.push( @@ -251,28 +248,6 @@ class UserPreferencesDevMode extends AbstractUserPreferences { ), ), ), - UserPreferencesItemTile( - title: appLocalizations.dev_preferences_import_history_title, - subtitle: appLocalizations.dev_preferences_import_history_subtitle, - onTap: () async { - final LocalDatabase localDatabase = context.read(); - await ProductListImportExport().importFromJSON( - ProductListImportExport.TMP_IMPORT, - localDatabase, - ); - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - appLocalizations - .dev_preferences_import_history_result_success, - ), - ), - ); - } - localDatabase.notifyListeners(); - }, - ), UserPreferencesItemTile( title: 'Add cards to scanner', subtitle: 'Adds 3 sample products to the scanner', @@ -341,25 +316,6 @@ class UserPreferencesDevMode extends AbstractUserPreferences { ), ]; - UserPreferencesItem _dataImporterTile() { - final SmoothAppDataImporterStatus status = - context.read().status; - - return UserPreferencesItemTile( - title: appLocalizations.dev_preferences_migration_title, - subtitle: appLocalizations.dev_preferences_migration_subtitle( - status.printableLabel(appLocalizations), - ), - onTap: status.canInitiateMigration - ? () { - context.read().startMigrationAsync( - forceMigration: true, - ); - } - : null, - ); - } - ScaffoldFeatureController _showSuccessMessage() => ScaffoldMessenger.of(context).showSnackBar( SnackBar( diff --git a/packages/smooth_app/macos/Podfile.lock b/packages/smooth_app/macos/Podfile.lock index 331ec976d71..f945c0e41b5 100644 --- a/packages/smooth_app/macos/Podfile.lock +++ b/packages/smooth_app/macos/Podfile.lock @@ -16,7 +16,7 @@ PODS: - FMDB/standard (2.7.5) - in_app_review (0.2.0): - FlutterMacOS - - mobile_scanner (3.0.0): + - mobile_scanner (3.5.2): - FlutterMacOS - package_info_plus (0.0.1): - FlutterMacOS @@ -112,7 +112,7 @@ SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0 - mobile_scanner: ed7618fb749adc6574563e053f3b8e5002c13994 + mobile_scanner: 621cf2c34e1c74ae7ce5c6793638ab600723bdea package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 @@ -127,4 +127,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 -COCOAPODS: 1.12.1 +COCOAPODS: 1.14.2 diff --git a/packages/smooth_app/pubspec.lock b/packages/smooth_app/pubspec.lock index 565a45356cc..97f480f4df3 100644 --- a/packages/smooth_app/pubspec.lock +++ b/packages/smooth_app/pubspec.lock @@ -381,20 +381,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" - data_importer: - dependency: "direct main" - description: - path: "../data_importer" - relative: true - source: path - version: "1.0.0" - data_importer_shared: - dependency: "direct main" - description: - path: "../data_importer_shared" - relative: true - source: path - version: "0.0.0" dbus: dependency: transitive description: diff --git a/packages/smooth_app/pubspec.yaml b/packages/smooth_app/pubspec.yaml index 89a4701c11f..adac88cda71 100644 --- a/packages/smooth_app/pubspec.yaml +++ b/packages/smooth_app/pubspec.yaml @@ -57,10 +57,6 @@ dependencies: collection: 1.17.2 path: 1.8.3 path_provider: 2.0.15 - data_importer_shared: - path: ../data_importer_shared - data_importer: - path: ../data_importer share_plus: 6.3.4 fimber: 0.6.6 shimmer: ^3.0.0