diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e44a1152..f8f8a09d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -6,18 +6,30 @@ PODS: - Flutter - flutter_web_browser (0.17.1): - Flutter - - FMDB (2.7.5): - - FMDB/standard (= 2.7.5) - - FMDB/standard (2.7.5) - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - - sqflite (0.0.2): + - sqlite3 (3.38.5): + - sqlite3/common (= 3.38.5) + - sqlite3/common (3.38.5) + - sqlite3/fts5 (3.38.5): + - sqlite3/common + - sqlite3/json1 (3.38.5): + - sqlite3/common + - sqlite3/perf-threadsafe (3.38.5): + - sqlite3/common + - sqlite3/rtree (3.38.5): + - sqlite3/common + - sqlite3_flutter_libs (0.0.1): - Flutter - - FMDB (>= 2.7.5) + - sqlite3 (~> 3.38.2) + - sqlite3/fts5 + - sqlite3/json1 + - sqlite3/perf-threadsafe + - sqlite3/rtree DEPENDENCIES: - Flutter (from `Flutter`) @@ -26,11 +38,11 @@ DEPENDENCIES: - flutter_web_browser (from `.symlinks/plugins/flutter_web_browser/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - - sqflite (from `.symlinks/plugins/sqflite/ios`) + - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) SPEC REPOS: trunk: - - FMDB + - sqlite3 EXTERNAL SOURCES: Flutter: @@ -45,19 +57,19 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/path_provider_foundation/darwin" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" - sqflite: - :path: ".symlinks/plugins/sqflite/ios" + sqlite3_flutter_libs: + :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be flutter_web_browser: 7bccaafbb0c5b8862afe7bcd158f15557109f61f - FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 - sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + sqlite3: 93442daab2ab1825bc9c05faa85814a1996f5b65 + sqlite3_flutter_libs: e21c8c90448ce608a0d685e717727c46f37c6695 PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/ios/Runner.xcodeproj/build/.last_build_id b/ios/Runner.xcodeproj/build/.last_build_id new file mode 100644 index 00000000..45723836 --- /dev/null +++ b/ios/Runner.xcodeproj/build/.last_build_id @@ -0,0 +1 @@ +be0f9cbaccd7d29ffb981a4ff244b5fd \ No newline at end of file diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 718511a0..da6fa4cd 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -341,6 +341,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 21BC3E4201BD6DDEC1196AA9 /* Pods-Runner.debug-prod.xcconfig */; buildSettings = { + DEVELOPMENT_TEAM = LHNK868N8C; PRODUCT_NAME = Runner; }; name = "Debug-prod"; @@ -349,6 +350,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = E65075458592F9F001F344C1 /* Pods-Runner.profile-prod.xcconfig */; buildSettings = { + DEVELOPMENT_TEAM = LHNK868N8C; PRODUCT_NAME = Runner; }; name = "Profile-prod"; @@ -357,6 +359,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 2B82596BB830AA758A68457E /* Pods-Runner.release-prod.xcconfig */; buildSettings = { + DEVELOPMENT_TEAM = LHNK868N8C; PRODUCT_NAME = Runner; }; name = "Release-prod"; @@ -488,6 +491,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = LHNK868N8C; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -762,6 +766,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = LHNK868N8C; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -783,6 +788,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = LHNK868N8C; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -877,6 +883,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = LHNK868N8C; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -972,6 +979,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = LHNK868N8C; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1064,6 +1072,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = LHNK868N8C; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1155,6 +1164,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = LHNK868N8C; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1244,6 +1254,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = LHNK868N8C; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1333,6 +1344,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = LHNK868N8C; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/lib/core/di/app_providers_module.dart b/lib/core/di/app_providers_module.dart index 3d2f8597..39928f2d 100644 --- a/lib/core/di/app_providers_module.dart +++ b/lib/core/di/app_providers_module.dart @@ -1,5 +1,4 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:flutter_template/core/source/common/app_database.dart'; import 'package:get_it/get_it.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_template/core/source/common/local_shared_preferences_storage.dart'; @@ -20,9 +19,6 @@ class AppProvidersModule { extension _GetItDiModuleExtensions on GetIt { void _setupModule() { - registerSingletonAsync( - () => $FloorAppDatabase.databaseBuilder('app_database.db').build(), - ); registerLazySingleton(FlutterSecureStorage.new); registerSingletonAsync(() => SharedPreferences.getInstance()); diff --git a/lib/core/di/di_repository_module.dart b/lib/core/di/di_repository_module.dart index a16ca042..e33b4a04 100644 --- a/lib/core/di/di_repository_module.dart +++ b/lib/core/di/di_repository_module.dart @@ -1,3 +1,4 @@ +import 'package:drift/native.dart'; import 'package:flutter_template/core/repository/project_repository.dart'; import 'package:flutter_template/core/repository/session_repository.dart'; import 'package:flutter_template/core/source/auth_local_source.dart'; @@ -35,6 +36,7 @@ extension _GetItDiModuleExtensions on GetIt { } void _setupSources() { + registerLazySingleton(() => AppDatabase(NativeDatabase.memory())); registerLazySingleton(() => AuthLocalSource(get())); registerLazySingleton(() => AuthRemoteSource(get())); registerLazySingleton(() => get().projectLocalSource); diff --git a/lib/core/model/db/project.dart b/lib/core/model/db/project.dart new file mode 100644 index 00000000..356b2406 --- /dev/null +++ b/lib/core/model/db/project.dart @@ -0,0 +1,20 @@ +part of 'package:flutter_template/core/source/common/app_database.dart'; + +@DataClassName('Project') +class ProjectTable extends Table { + IntColumn get id => integer().autoIncrement()(); + + TextColumn get name => text()(); + + TextColumn get description => text()(); + + TextColumn get url => text()(); + + @JsonKey('image_url') + TextColumn get imageUrl => text()(); + + TextColumn get language => text()(); + + @override + Set get primaryKey => {id}; +} diff --git a/lib/core/model/db/repository_db_entity.dart b/lib/core/model/db/repository_db_entity.dart deleted file mode 100644 index c82b94be..00000000 --- a/lib/core/model/db/repository_db_entity.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:floor/floor.dart'; - -@Entity(tableName: 'projects') -class ProjectDbEntity { - @primaryKey - final int id; - final String name; - final String description; - final String url; - final String imageUrl; - final String language; - - ProjectDbEntity({ - required this.id, - required this.name, - required this.description, - required this.url, - required this.imageUrl, - required this.language, - }); -} diff --git a/lib/core/model/project.dart b/lib/core/model/project.dart deleted file mode 100644 index f2916543..00000000 --- a/lib/core/model/project.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'project.freezed.dart'; - -part 'project.g.dart'; - -@freezed -class Project with _$Project { - @JsonSerializable() - factory Project({ - required int id, - required String name, - required String description, - required String url, - required String imageUrl, - required String language, - }) = _Project; - - factory Project.fromJson(Map json) => - _$ProjectFromJson(json); -} diff --git a/lib/core/model/serializer/project_serializer.dart b/lib/core/model/serializer/project_serializer.dart deleted file mode 100644 index 2a0dd94f..00000000 --- a/lib/core/model/serializer/project_serializer.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter_template/core/model/db/repository_db_entity.dart'; -import 'package:flutter_template/core/model/project.dart'; -import 'package:stock/stock.dart'; - -class ProjectStockTypeMapper extends StockTypeMapper { - @override - ProjectDbEntity fromOutput(Project value) => ProjectDbEntity( - id: value.id, - description: value.description, - name: value.name, - url: value.url, - language: value.language, - imageUrl: value.imageUrl, - ); - - @override - Project fromInput(ProjectDbEntity value) => Project( - id: value.id, - description: value.description, - name: value.name, - url: value.url, - language: value.language, - imageUrl: value.imageUrl, - ); -} - -class ProjectListStockTypeMapper - extends StockTypeMapper, List> { - final _projectSerializer = ProjectStockTypeMapper(); - - @override - List fromInput(List value) => - value.map(_projectSerializer.fromInput).toList(); - - @override - List fromOutput(List value) => - value.map(_projectSerializer.fromOutput).toList(); -} diff --git a/lib/core/repository/project_repository.dart b/lib/core/repository/project_repository.dart index 0fc38a7b..8d1c6964 100644 --- a/lib/core/repository/project_repository.dart +++ b/lib/core/repository/project_repository.dart @@ -1,6 +1,4 @@ -import 'package:flutter_template/core/model/db/repository_db_entity.dart'; -import 'package:flutter_template/core/model/project.dart'; -import 'package:flutter_template/core/model/serializer/project_serializer.dart'; +import 'package:flutter_template/core/source/common/app_database.dart'; import 'package:flutter_template/core/source/project_local_source.dart'; import 'package:flutter_template/core/source/project_remote_source.dart'; import 'package:stock/stock.dart'; @@ -19,10 +17,10 @@ class ProjectRepository { fetcher: Fetcher.ofFuture( (_) => _projectRemoteSource.getProjects(), ), - sourceOfTruth: SourceOfTruth>( + sourceOfTruth: SourceOfTruth>( reader: (_) => _projectLocalSource.getProjects(), writer: (_, value) => _projectLocalSource.replaceProjects(value), - ).mapToUsingMapper(ProjectListStockTypeMapper()), + ), ); Stream?> getProjects() => _store diff --git a/lib/core/repository/session_repository.dart b/lib/core/repository/session_repository.dart index dee2c7e8..eaf882da 100644 --- a/lib/core/repository/session_repository.dart +++ b/lib/core/repository/session_repository.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:drift/drift.dart'; import 'package:flutter_template/core/model/authentication_status.dart'; import 'package:flutter_template/core/model/user.dart'; import 'package:flutter_template/core/source/auth_local_source.dart'; @@ -36,7 +37,7 @@ class SessionRepository { } Future logOut() async { - await _appDataBase.clearAllTables(); + await _appDataBase.projectTable.deleteAll(); await _authLocalSource.saveUserToken(null); await _authLocalSource.saveUserInfo(null); } diff --git a/lib/core/source/common/app_database.dart b/lib/core/source/common/app_database.dart index 46f1d740..9b1f5a4d 100644 --- a/lib/core/source/common/app_database.dart +++ b/lib/core/source/common/app_database.dart @@ -1,19 +1,23 @@ -// database.dart -import 'dart:async'; - -import 'package:floor/floor.dart'; -import 'package:flutter_template/core/model/db/repository_db_entity.dart'; +import 'package:drift/drift.dart'; import 'package:flutter_template/core/source/project_local_source.dart'; -import 'package:sqflite/sqflite.dart' as sqflite; part 'app_database.g.dart'; -@Database(version: 1, entities: [ProjectDbEntity]) -abstract class AppDatabase extends FloorDatabase { - ProjectLocalSource get projectLocalSource; +part 'package:flutter_template/core/model/db/project.dart'; + +@DriftDatabase( + tables: [ + ProjectTable, + ], + daos: [ProjectLocalSource], +) +class AppDatabase extends _$AppDatabase { + AppDatabase(QueryExecutor e) : super(e); - Future clearAllTables() async { - await database.delete('projects'); - } + @override + int get schemaVersion => 1; + @override + // TODO: implement migration + MigrationStrategy get migration => super.migration; } diff --git a/lib/core/source/common/app_database.g.dart b/lib/core/source/common/app_database.g.dart index b842747f..68c8a167 100644 --- a/lib/core/source/common/app_database.g.dart +++ b/lib/core/source/common/app_database.g.dart @@ -2,166 +2,340 @@ part of 'app_database.dart'; -// ************************************************************************** -// FloorGenerator -// ************************************************************************** - -// ignore: avoid_classes_with_only_static_members -class $FloorAppDatabase { - /// Creates a database builder for a persistent database. - /// Once a database is built, you should keep a reference to it and re-use it. - static _$AppDatabaseBuilder databaseBuilder(String name) => - _$AppDatabaseBuilder(name); - - /// Creates a database builder for an in memory database. - /// Information stored in an in memory database disappears when the process is killed. - /// Once a database is built, you should keep a reference to it and re-use it. - static _$AppDatabaseBuilder inMemoryDatabaseBuilder() => - _$AppDatabaseBuilder(null); -} - -class _$AppDatabaseBuilder { - _$AppDatabaseBuilder(this.name); - - final String? name; - - final List _migrations = []; - - Callback? _callback; - - /// Adds migrations to the builder. - _$AppDatabaseBuilder addMigrations(List migrations) { - _migrations.addAll(migrations); - return this; +// ignore_for_file: type=lint +class $ProjectTableTable extends ProjectTable + with TableInfo<$ProjectTableTable, Project> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $ProjectTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + @override + late final GeneratedColumn name = GeneratedColumn( + 'name', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _descriptionMeta = + const VerificationMeta('description'); + @override + late final GeneratedColumn description = GeneratedColumn( + 'description', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _urlMeta = const VerificationMeta('url'); + @override + late final GeneratedColumn url = GeneratedColumn( + 'url', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _imageUrlMeta = + const VerificationMeta('imageUrl'); + @override + late final GeneratedColumn imageUrl = GeneratedColumn( + 'image_url', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _languageMeta = + const VerificationMeta('language'); + @override + late final GeneratedColumn language = GeneratedColumn( + 'language', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + @override + List get $columns => + [id, name, description, url, imageUrl, language]; + @override + String get aliasedName => _alias ?? 'project_table'; + @override + String get actualTableName => 'project_table'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('description')) { + context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, _descriptionMeta)); + } else if (isInserting) { + context.missing(_descriptionMeta); + } + if (data.containsKey('url')) { + context.handle( + _urlMeta, url.isAcceptableOrUnknown(data['url']!, _urlMeta)); + } else if (isInserting) { + context.missing(_urlMeta); + } + if (data.containsKey('image_url')) { + context.handle(_imageUrlMeta, + imageUrl.isAcceptableOrUnknown(data['image_url']!, _imageUrlMeta)); + } else if (isInserting) { + context.missing(_imageUrlMeta); + } + if (data.containsKey('language')) { + context.handle(_languageMeta, + language.isAcceptableOrUnknown(data['language']!, _languageMeta)); + } else if (isInserting) { + context.missing(_languageMeta); + } + return context; } - /// Adds a database [Callback] to the builder. - _$AppDatabaseBuilder addCallback(Callback callback) { - _callback = callback; - return this; + @override + Set get $primaryKey => {id}; + @override + Project map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return Project( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}name'])!, + description: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}description'])!, + url: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}url'])!, + imageUrl: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}image_url'])!, + language: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}language'])!, + ); } - /// Creates the database and initializes it. - Future build() async { - final path = name != null - ? await sqfliteDatabaseFactory.getDatabasePath(name!) - : ':memory:'; - final database = _$AppDatabase(); - database.database = await database.open( - path, - _migrations, - _callback, - ); - return database; + @override + $ProjectTableTable createAlias(String alias) { + return $ProjectTableTable(attachedDatabase, alias); } } -class _$AppDatabase extends AppDatabase { - _$AppDatabase([StreamController? listener]) { - changeListener = listener ?? StreamController.broadcast(); +class Project extends DataClass implements Insertable { + final int id; + final String name; + final String description; + final String url; + final String imageUrl; + final String language; + const Project( + {required this.id, + required this.name, + required this.description, + required this.url, + required this.imageUrl, + required this.language}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['url'] = Variable(url); + map['image_url'] = Variable(imageUrl); + map['language'] = Variable(language); + return map; } - ProjectLocalSource? _projectLocalSourceInstance; - - Future open( - String path, - List migrations, [ - Callback? callback, - ]) async { - final databaseOptions = sqflite.OpenDatabaseOptions( - version: 1, - onConfigure: (database) async { - await database.execute('PRAGMA foreign_keys = ON'); - await callback?.onConfigure?.call(database); - }, - onOpen: (database) async { - await callback?.onOpen?.call(database); - }, - onUpgrade: (database, startVersion, endVersion) async { - await MigrationAdapter.runMigrations( - database, startVersion, endVersion, migrations); - - await callback?.onUpgrade?.call(database, startVersion, endVersion); - }, - onCreate: (database, version) async { - await database.execute( - 'CREATE TABLE IF NOT EXISTS `projects` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `description` TEXT NOT NULL, `url` TEXT NOT NULL, `imageUrl` TEXT NOT NULL, `language` TEXT NOT NULL, PRIMARY KEY (`id`))'); - - await callback?.onCreate?.call(database, version); - }, + ProjectTableCompanion toCompanion(bool nullToAbsent) { + return ProjectTableCompanion( + id: Value(id), + name: Value(name), + description: Value(description), + url: Value(url), + imageUrl: Value(imageUrl), + language: Value(language), ); - return sqfliteDatabaseFactory.openDatabase(path, options: databaseOptions); } + factory Project.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return Project( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + url: serializer.fromJson(json['url']), + imageUrl: serializer.fromJson(json['image_url']), + language: serializer.fromJson(json['language']), + ); + } @override - ProjectLocalSource get projectLocalSource { - return _projectLocalSourceInstance ??= - _$ProjectLocalSource(database, changeListener); + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'url': serializer.toJson(url), + 'image_url': serializer.toJson(imageUrl), + 'language': serializer.toJson(language), + }; } -} - -class _$ProjectLocalSource extends ProjectLocalSource { - _$ProjectLocalSource( - this.database, - this.changeListener, - ) : _queryAdapter = QueryAdapter(database, changeListener), - _projectDbEntityInsertionAdapter = InsertionAdapter( - database, - 'projects', - (ProjectDbEntity item) => { - 'id': item.id, - 'name': item.name, - 'description': item.description, - 'url': item.url, - 'imageUrl': item.imageUrl, - 'language': item.language - }, - changeListener); - final sqflite.DatabaseExecutor database; - - final StreamController changeListener; + Project copyWith( + {int? id, + String? name, + String? description, + String? url, + String? imageUrl, + String? language}) => + Project( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + url: url ?? this.url, + imageUrl: imageUrl ?? this.imageUrl, + language: language ?? this.language, + ); + @override + String toString() { + return (StringBuffer('Project(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('url: $url, ') + ..write('imageUrl: $imageUrl, ') + ..write('language: $language') + ..write(')')) + .toString(); + } - final QueryAdapter _queryAdapter; + @override + int get hashCode => + Object.hash(id, name, description, url, imageUrl, language); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is Project && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.url == this.url && + other.imageUrl == this.imageUrl && + other.language == this.language); +} - final InsertionAdapter _projectDbEntityInsertionAdapter; +class ProjectTableCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value url; + final Value imageUrl; + final Value language; + const ProjectTableCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.url = const Value.absent(), + this.imageUrl = const Value.absent(), + this.language = const Value.absent(), + }); + ProjectTableCompanion.insert({ + this.id = const Value.absent(), + required String name, + required String description, + required String url, + required String imageUrl, + required String language, + }) : name = Value(name), + description = Value(description), + url = Value(url), + imageUrl = Value(imageUrl), + language = Value(language); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? url, + Expression? imageUrl, + Expression? language, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (url != null) 'url': url, + if (imageUrl != null) 'image_url': imageUrl, + if (language != null) 'language': language, + }); + } - @override - Stream> getProjects() { - return _queryAdapter.queryListStream('SELECT * FROM projects', - mapper: (Map row) => ProjectDbEntity( - id: row['id'] as int, - name: row['name'] as String, - description: row['description'] as String, - url: row['url'] as String, - imageUrl: row['imageUrl'] as String, - language: row['language'] as String), - queryableName: 'projects', - isView: false); + ProjectTableCompanion copyWith( + {Value? id, + Value? name, + Value? description, + Value? url, + Value? imageUrl, + Value? language}) { + return ProjectTableCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + url: url ?? this.url, + imageUrl: imageUrl ?? this.imageUrl, + language: language ?? this.language, + ); } @override - Future deleteAllProjects() async { - await _queryAdapter.queryNoReturn('DELETE FROM projects'); + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (url.present) { + map['url'] = Variable(url.value); + } + if (imageUrl.present) { + map['image_url'] = Variable(imageUrl.value); + } + if (language.present) { + map['language'] = Variable(language.value); + } + return map; } @override - Future insertProjects(List projects) async { - await _projectDbEntityInsertionAdapter.insertList( - projects, OnConflictStrategy.replace); + String toString() { + return (StringBuffer('ProjectTableCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('url: $url, ') + ..write('imageUrl: $imageUrl, ') + ..write('language: $language') + ..write(')')) + .toString(); } +} +abstract class _$AppDatabase extends GeneratedDatabase { + _$AppDatabase(QueryExecutor e) : super(e); + late final $ProjectTableTable projectTable = $ProjectTableTable(this); + late final ProjectLocalSource projectLocalSource = + ProjectLocalSource(this as AppDatabase); @override - Future replaceProjects(List? projects) async { - if (database is sqflite.Transaction) { - await super.replaceProjects(projects); - } else { - await (database as sqflite.Database) - .transaction((transaction) async { - final transactionDatabase = _$AppDatabase(changeListener) - ..database = transaction; - await transactionDatabase.projectLocalSource.replaceProjects(projects); - }); - } - } + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [projectTable]; } diff --git a/lib/core/source/project_local_source.dart b/lib/core/source/project_local_source.dart index c0452abd..5058d3c8 100644 --- a/lib/core/source/project_local_source.dart +++ b/lib/core/source/project_local_source.dart @@ -1,33 +1,27 @@ import 'dart:async'; -import 'package:floor/floor.dart'; -import 'package:flutter_template/core/model/db/repository_db_entity.dart'; +import 'package:drift/drift.dart'; +import 'package:flutter_template/core/source/common/app_database.dart'; -@dao -abstract class ProjectLocalSource { - // It's set in the DAO creation - late final StreamController changeListener; +part 'project_local_source.g.dart'; - @Query('SELECT * FROM projects') - Stream> getProjects(); +@DriftAccessor(tables: [ProjectTable]) +class ProjectLocalSource extends DatabaseAccessor + with _$ProjectLocalSourceMixin { + ProjectLocalSource(super.attachedDatabase); - @Insert(onConflict: OnConflictStrategy.replace) - Future insertProjects(List projects); + Stream> getProjects() => select(projectTable).watch(); - @Query('DELETE FROM projects') - Future deleteAllProjects(); + Future insertProjects(List projects) => + projectTable.insertAll(projects); - @transaction - Future replaceProjects(List? projects) async { - await deleteAllProjects(); - if (projects != null) { - await insertProjects(projects); - } - // Floor notifier does not work very well - // https://github.com/pinchbv/floor/issues/360 - // https://github.com/pinchbv/floor/issues/603 - if (projects == null || projects.isEmpty) { - changeListener.add('projects'); - } - } + Future deleteAllProjects() => projectTable.deleteAll(); + + Future replaceProjects(List? projects) async => + transaction(() async { + await deleteAllProjects(); + if (projects != null) { + await insertProjects(projects); + } + }); } diff --git a/lib/core/source/project_local_source.g.dart b/lib/core/source/project_local_source.g.dart new file mode 100644 index 00000000..4cb42dc2 --- /dev/null +++ b/lib/core/source/project_local_source.g.dart @@ -0,0 +1,8 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'project_local_source.dart'; + +// ignore_for_file: type=lint +mixin _$ProjectLocalSourceMixin on DatabaseAccessor { + $ProjectTableTable get projectTable => attachedDatabase.projectTable; +} diff --git a/lib/core/source/project_remote_source.dart b/lib/core/source/project_remote_source.dart index 9c6f6b3d..e7290dcc 100644 --- a/lib/core/source/project_remote_source.dart +++ b/lib/core/source/project_remote_source.dart @@ -1,5 +1,5 @@ import 'package:flutter_template/core/model/service/service_response.dart'; -import 'package:flutter_template/core/model/project.dart'; +import 'package:flutter_template/core/source/common/app_database.dart'; import 'package:flutter_template/core/source/common/http_service.dart'; class ProjectRemoteSource { diff --git a/lib/ui/welcome/welcome_cubit.dart b/lib/ui/welcome/welcome_cubit.dart index af4edf80..0f0ff1f0 100644 --- a/lib/ui/welcome/welcome_cubit.dart +++ b/lib/ui/welcome/welcome_cubit.dart @@ -3,9 +3,9 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_template/core/common/extension/stream_future_extensions.dart'; import 'package:flutter_template/core/di/di_provider.dart'; -import 'package:flutter_template/core/model/project.dart'; import 'package:flutter_template/core/repository/project_repository.dart'; import 'package:flutter_template/core/repository/session_repository.dart'; +import 'package:flutter_template/core/source/common/app_database.dart'; import 'package:flutter_template/ui/section/error_handler/global_event_handler_cubit.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; diff --git a/lib/ui/welcome/welcome_screen.dart b/lib/ui/welcome/welcome_screen.dart index 3aaaf28b..6f1bfaba 100644 --- a/lib/ui/welcome/welcome_screen.dart +++ b/lib/ui/welcome/welcome_screen.dart @@ -1,7 +1,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_template/core/model/project.dart'; +import 'package:flutter_template/core/source/common/app_database.dart'; import 'package:flutter_template/ui/extensions/context_extensions.dart'; import 'package:flutter_template/ui/section/error_handler/global_event_handler_cubit.dart'; import 'package:flutter_template/ui/theme/app_theme.dart'; diff --git a/pubspec.lock b/pubspec.lock index 2d7cbb59..28610399 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -73,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.1.1" + bazel_worker: + dependency: transitive + description: + name: bazel_worker + sha256: "6aa1de567844e1a1db3ac16959159b31a71128fdd27d63d861ad5aa7299e48c5" + url: "https://pub.dev" + source: hosted + version: "1.0.3" bloc: dependency: transitive description: @@ -113,6 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + build_modules: + dependency: transitive + description: + name: build_modules + sha256: "14a8e9bf4b1ae3f5d6c925c1386da59e818c68cc6f1f6fda991f7c6b74c6ebb9" + url: "https://pub.dev" + source: hosted + version: "5.0.4" build_resolvers: dependency: transitive description: @@ -137,6 +153,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.2.7" + build_web_compilers: + dependency: "direct dev" + description: + name: build_web_compilers + sha256: f6450115ab551223683963a00348445930a9d3d3bc4b330a907daa641cd202ae + url: "https://pub.dev" + source: hosted + version: "4.0.0" built_collection: dependency: transitive description: @@ -289,6 +313,22 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.2" + drift: + dependency: "direct main" + description: + name: drift + sha256: "01e7237766b3404f08489ed0dc531a2fa5f5a42d7fa7787da6cc0a10e8d2632f" + url: "https://pub.dev" + source: hosted + version: "2.10.0" + drift_dev: + dependency: "direct dev" + description: + name: drift_dev + sha256: "407eb1f149332238c567f06cc89b6aaee848de00d9031c76dc372dc16d993bca" + url: "https://pub.dev" + source: hosted + version: "2.10.0" equatable: dependency: "direct main" description: @@ -329,30 +369,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - floor: - dependency: "direct main" - description: - name: floor - sha256: "52a8eac2c8d274e7c0c54251226f59786bb5b749365a2d8537d8095aa5132d92" - url: "https://pub.dev" - source: hosted - version: "1.4.2" - floor_annotation: - dependency: transitive - description: - name: floor_annotation - sha256: fa3fa4f198cdd1d922a69ceb06e54663fe59256bf1cb3c036eff206b445a6960 - url: "https://pub.dev" - source: hosted - version: "1.4.2" - floor_generator: - dependency: "direct dev" - description: - name: floor_generator - sha256: "40aaf1b619adc03367ce4b7c79161e3198d43b572b5ec9cc99a4a89de27b08d2" - url: "https://pub.dev" - source: hosted - version: "1.4.2" flutter: dependency: "direct main" description: flutter @@ -645,14 +661,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" - lists: - dependency: transitive - description: - name: lists - sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27" - url: "https://pub.dev" - source: hosted - version: "1.0.1" logger: dependency: "direct main" description: @@ -837,6 +845,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.4" + protobuf: + dependency: transitive + description: + name: protobuf + sha256: "4034a02b7e231e7e60bff30a8ac13a7347abfdac0798595fae0b90a3f0afe759" + url: "https://pub.dev" + source: hosted + version: "3.0.0" provider: dependency: transitive description: @@ -869,6 +885,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.2" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" rxdart: dependency: "direct main" description: @@ -877,6 +901,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.27.7" + scratch_space: + dependency: transitive + description: + name: scratch_space + sha256: "8510fbff458d733a58fc427057d1ac86303b376d609d6e1bc43f240aad9aa445" + url: "https://pub.dev" + source: hosted + version: "1.0.2" shared_preferences: dependency: "direct main" description: @@ -970,6 +1002,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.3" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" source_span: dependency: transitive description: @@ -986,46 +1026,30 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" - sqflite: + sqlite3: dependency: "direct main" description: - name: sqflite - sha256: b4d6710e1200e96845747e37338ea8a819a12b51689a3bcf31eff0003b37a0b9 - url: "https://pub.dev" - source: hosted - version: "2.2.8+4" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: e77abf6ff961d69dfef41daccbb66b51e9983cdd5cb35bf30733598057401555 - url: "https://pub.dev" - source: hosted - version: "2.4.5" - sqflite_common_ffi: - dependency: transitive - description: - name: sqflite_common_ffi - sha256: "55641c9310e1da0856559f077ccc3cb7637b718d10b1fd8461c8088ef2d2f720" + name: sqlite3 + sha256: f7511ddd6a2dda8ded9d849f8a925bb6020e0faa59db2443debc18d484e59401 url: "https://pub.dev" source: hosted - version: "2.2.4" - sqlite3: - dependency: transitive + version: "2.0.0" + sqlite3_flutter_libs: + dependency: "direct dev" description: - name: sqlite3 - sha256: a3ba4b66a7ab170ce7aa3f5ac43c19ee8d6637afbe7b7c95c94112b4f4d91566 + name: sqlite3_flutter_libs + sha256: bd9029e8ff8404de323986365e60f9eed6c91515fc606fce75ba3aab9318cc3b url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "0.5.5" sqlparser: dependency: transitive description: name: sqlparser - sha256: "91f47610aa54d8abf9d795a7b4e49b2a788f65d7493d5a68fbf180c3cbcc6f38" + sha256: "9611f46d30a4e8286e54d17a1b5182d132512dc6fc3da90c45ad8ec2828a58b1" url: "https://pub.dev" source: hosted - version: "0.27.0" + version: "0.30.3" stack_trace: dependency: "direct main" description: @@ -1066,22 +1090,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" - strings: - dependency: transitive - description: - name: strings - sha256: "5af86299505c299640f5564e187c1a2ee9d6308c540e8d65f6385f5c67019122" - url: "https://pub.dev" - source: hosted - version: "0.2.2" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" - url: "https://pub.dev" - source: hosted - version: "3.1.0" term_glyph: dependency: transitive description: @@ -1122,14 +1130,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" - unicode: - dependency: transitive - description: - name: unicode - sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1" - url: "https://pub.dev" - source: hosted - version: "0.3.1" universal_io: dependency: transitive description: @@ -1203,5 +1203,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.0.0 <3.1.0" flutter: ">=3.10.2" diff --git a/pubspec.yaml b/pubspec.yaml index 20193a04..bd71efb4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,7 +26,6 @@ dependencies: dartx: 1.1.0 dio: 5.1.2 equatable: 2.0.5 - floor: 1.4.2 flutter_bloc: 8.1.3 flutter_dotenv: 5.1.0 flutter_native_splash: 2.3.1 @@ -42,9 +41,11 @@ dependencies: material_color_generator: 1.1.0 rxdart: 0.27.7 shared_preferences: 2.1.1 - sqflite: 2.2.8+4 stack_trace: 1.11.0 stock: 1.0.1 + drift: 2.10.0 + sqlite3: 2.0.0 + # Remove when dart_code_metrics updates its dependencies dependency_overrides: @@ -57,14 +58,16 @@ dev_dependencies: auto_route_generator: 7.1.1 build_runner: 2.4.4 dart_code_metrics: 5.7.4 - floor_generator: 1.4.2 flutter_flavorizr: 2.2.0 flutter_gen_runner: 5.3.1 flutter_launcher_icons: 0.13.1 flutter_lints: 2.0.1 freezed: 2.3.5 json_serializable: 6.7.0 + build_web_compilers: 4.0.0 lints: 2.1.1 + drift_dev: 2.10.0 + sqlite3_flutter_libs: 0.5.5 flutter: generate: true