From 5a456ef277f3fde9406299fe95993300fd58acc5 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 2 Apr 2025 19:28:17 +0530 Subject: [PATCH] feat(mobile): sqlite (#16861) * refactor: user entity * chore: rebase fixes * refactor: remove int user Id * refactor: migrate store userId from int to string * refactor: rename uid to id * feat: drift * pr feedback * refactor: move common overrides to mixin --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .gitattributes | 3 + .vscode/settings.json | 3 +- mobile/analysis_options.yaml | 2 + mobile/build.yaml | 24 ++++ .../drift_schemas/main/drift_schema_v1.json | Bin 0 -> 4108 bytes mobile/lib/domain/models/user.model.dart | 30 +---- .../domain/models/user_metadata.model.dart | 105 ++++++++++++++++++ .../entities/partner.entity.dart | 18 +++ .../entities/partner.entity.drift.dart | Bin 0 -> 23757 bytes .../infrastructure/entities/user.entity.dart | 20 ++++ .../entities/user.entity.drift.dart | Bin 0 -> 25521 bytes .../entities/user_metadata.entity.dart | 21 ++++ .../entities/user_metadata.entity.drift.dart | Bin 0 -> 17619 bytes .../repositories/db.repository.dart | 39 +++++++ .../repositories/db.repository.drift.dart | Bin 0 -> 2616 bytes .../utils/drift_default.mixin.dart | 9 ++ .../infrastructure/utils/exif.converter.dart | 1 + .../infrastructure/utils/user.converter.dart | 2 + .../widgets/common/user_circle_avatar.dart | 1 + mobile/makefile | 3 + mobile/pubspec.lock | 64 +++++++++++ mobile/pubspec.yaml | 5 + mobile/test/fixtures/user.stub.dart | 1 + 23 files changed, 321 insertions(+), 30 deletions(-) create mode 100644 mobile/build.yaml create mode 100644 mobile/drift_schemas/main/drift_schema_v1.json create mode 100644 mobile/lib/domain/models/user_metadata.model.dart create mode 100644 mobile/lib/infrastructure/entities/partner.entity.dart create mode 100644 mobile/lib/infrastructure/entities/partner.entity.drift.dart create mode 100644 mobile/lib/infrastructure/entities/user.entity.drift.dart create mode 100644 mobile/lib/infrastructure/entities/user_metadata.entity.dart create mode 100644 mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart create mode 100644 mobile/lib/infrastructure/repositories/db.repository.drift.dart create mode 100644 mobile/lib/infrastructure/utils/drift_default.mixin.dart diff --git a/.gitattributes b/.gitattributes index d321e2a91..2e8a45ca5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,6 +6,9 @@ mobile/openapi/**/*.dart linguist-generated=true mobile/lib/**/*.g.dart -diff -merge mobile/lib/**/*.g.dart linguist-generated=true +mobile/lib/**/*.drift.dart -diff -merge +mobile/lib/**/*.drift.dart linguist-generated=true + open-api/typescript-sdk/fetch-client.ts -diff -merge open-api/typescript-sdk/fetch-client.ts linguist-generated=true diff --git a/.vscode/settings.json b/.vscode/settings.json index 49dbf3944..49692809b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -39,6 +39,7 @@ ], "explorer.fileNesting.enabled": true, "explorer.fileNesting.patterns": { - "*.ts": "${capture}.spec.ts,${capture}.mock.ts" + "*.ts": "${capture}.spec.ts,${capture}.mock.ts", + "*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart" } } \ No newline at end of file diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 085449756..07c6f65b7 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -36,6 +36,8 @@ analyzer: exclude: - openapi/** - lib/generated_plugin_registrant.dart + - lib/**/*.g.dart + - lib/**/*.drift.dart plugins: - custom_lint diff --git a/mobile/build.yaml b/mobile/build.yaml new file mode 100644 index 000000000..d5de77a37 --- /dev/null +++ b/mobile/build.yaml @@ -0,0 +1,24 @@ +targets: + $default: + builders: + #drift @DriftDatabase() + drift_dev: + # Disable default builder to use modular builder instead + enabled: false + drift_dev:analyzer: + enabled: true + options: &drift_options + store_date_time_values_as_text: true + named_parameters: true + write_from_json_string_constructor: false + data_class_to_companions: false + # Required for make-migrations + databases: + main: lib/infrastructure/repositories/db.repository.dart + generate_for: &drift_generate_for + - lib/infrastructure/entities/*.dart + - lib/infrastructure/repositories/db.repository.dart + drift_dev:modular: + enabled: true + options: *drift_options + generate_for: *drift_generate_for \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v1.json b/mobile/drift_schemas/main/drift_schema_v1.json new file mode 100644 index 0000000000000000000000000000000000000000..1870ef477f91db0a749c2310c16918d9d00d2197 GIT binary patch literal 4108 zcmeHKT~8u06#XkrU&Lf$&_~{IoXwJ5T_c!%P}7u|B8{DPY&#-L`0sn~Fu)ARD)^O6 ze5Ac|@9F(IhY4k=@SL_LG!ZVgGV{`C+M>OobYvivAhFS&OYI0JPFTs6{38YKw3l8A_{vz4SUK9zAV&n+y12?wYo)CR8T0)eNW=17U@Z1vt9838)`u0-|`{R|4XK&*D{@r+AzL zbXK?-!{rD9BqCPaiiAncQe!M_4Y+b5ps`VTs?qC&mS022fZhu4!AbQ9zhhdGu>aI%lNO49MaB zKKM_dpGEB3BWQ(_SwU(ZphfSM^w>hr_<}aAR}>lvYEHtU{?TIwb;F5i>^1j_$YuGr zD>V$7#Jtp-aei7&;HMTlvj%qulPSLyEaQH-hPgE9pvC#j;2HS13}8Ipf1dITH&Vj5 zUqfA9wehA37eDC127}{WH^9bz2`?`$qO%_BbxxyhuYG#{ivGFJjpxk43dg?wN*_oqHhrlN*HxXh3-j1Ci~>q899l5M!krH?QYmU zi2%VCyMH^^YgDi;xzk+VX*1GL$R*1Ifh-iy>d4mbs;T{W8kk8j2J8pg4-`|OEkWZj z=u`MnNU`(9TcCuc859JqP)^N7)+IE{($vxE^xdXr-mlBiIof)3GQh82qch#GP^A4a zdt!2Cm&fC;>3K#PKRG>RYH`1APVR5G{YvjH=>z5frYU}d$ej00@Fo9SPLJE=eb literal 0 HcmV?d00001 diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart index ad241a8c4..abf2e5620 100644 --- a/mobile/lib/domain/models/user.model.dart +++ b/mobile/lib/domain/models/user.model.dart @@ -1,32 +1,4 @@ -import 'dart:ui'; - -enum AvatarColor { - // do not change this order or reuse indices for other purposes, adding is OK - primary, - pink, - red, - yellow, - blue, - green, - purple, - orange, - gray, - amber; - - Color toColor({bool isDarkTheme = false}) => switch (this) { - AvatarColor.primary => - isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), - AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182), - AvatarColor.red => const Color.fromARGB(255, 239, 68, 68), - AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8), - AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246), - AvatarColor.green => const Color.fromARGB(255, 22, 163, 74), - AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234), - AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12), - AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99), - AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6), - }; -} +import 'package:immich_mobile/domain/models/user_metadata.model.dart'; // TODO: Rename to User once Isar is removed class UserDto { diff --git a/mobile/lib/domain/models/user_metadata.model.dart b/mobile/lib/domain/models/user_metadata.model.dart new file mode 100644 index 000000000..158638442 --- /dev/null +++ b/mobile/lib/domain/models/user_metadata.model.dart @@ -0,0 +1,105 @@ +import 'dart:ui'; + +enum AvatarColor { + // do not change this order or reuse indices for other purposes, adding is OK + primary("primary"), + pink("pink"), + red("red"), + yellow("yellow"), + blue("blue"), + green("green"), + purple("purple"), + orange("orange"), + gray("gray"), + amber("amber"); + + final String value; + const AvatarColor(this.value); + + Color toColor({bool isDarkTheme = false}) => switch (this) { + AvatarColor.primary => + isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), + AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182), + AvatarColor.red => const Color.fromARGB(255, 239, 68, 68), + AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8), + AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246), + AvatarColor.green => const Color.fromARGB(255, 22, 163, 74), + AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234), + AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12), + AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99), + AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6), + }; +} + +class UserPreferences { + final bool foldersEnabled; + final bool memoriesEnabled; + final bool peopleEnabled; + final bool ratingsEnabled; + final bool sharedLinksEnabled; + final bool tagsEnabled; + final AvatarColor userAvatarColor; + final bool showSupportBadge; + + const UserPreferences({ + this.foldersEnabled = false, + this.memoriesEnabled = true, + this.peopleEnabled = true, + this.ratingsEnabled = false, + this.sharedLinksEnabled = true, + this.tagsEnabled = false, + this.userAvatarColor = AvatarColor.primary, + this.showSupportBadge = true, + }); + + UserPreferences copyWith({ + bool? foldersEnabled, + bool? memoriesEnabled, + bool? peopleEnabled, + bool? ratingsEnabled, + bool? sharedLinksEnabled, + bool? tagsEnabled, + AvatarColor? userAvatarColor, + bool? showSupportBadge, + }) { + return UserPreferences( + foldersEnabled: foldersEnabled ?? this.foldersEnabled, + memoriesEnabled: memoriesEnabled ?? this.memoriesEnabled, + peopleEnabled: peopleEnabled ?? this.peopleEnabled, + ratingsEnabled: ratingsEnabled ?? this.ratingsEnabled, + sharedLinksEnabled: sharedLinksEnabled ?? this.sharedLinksEnabled, + tagsEnabled: tagsEnabled ?? this.tagsEnabled, + userAvatarColor: userAvatarColor ?? this.userAvatarColor, + showSupportBadge: showSupportBadge ?? this.showSupportBadge, + ); + } + + Map toMap() { + final preferences = {}; + preferences["folders-Enabled"] = foldersEnabled; + preferences["memories-Enabled"] = memoriesEnabled; + preferences["people-Enabled"] = peopleEnabled; + preferences["ratings-Enabled"] = ratingsEnabled; + preferences["sharedLinks-Enabled"] = sharedLinksEnabled; + preferences["tags-Enabled"] = tagsEnabled; + preferences["avatar-Color"] = userAvatarColor.value; + preferences["purchase-ShowSupportBadge"] = showSupportBadge; + return preferences; + } + + factory UserPreferences.fromMap(Map map) { + return UserPreferences( + foldersEnabled: map["folders-Enabled"] as bool? ?? false, + memoriesEnabled: map["memories-Enabled"] as bool? ?? true, + peopleEnabled: map["people-Enabled"] as bool? ?? true, + ratingsEnabled: map["ratings-Enabled"] as bool? ?? false, + sharedLinksEnabled: map["sharedLinks-Enabled"] as bool? ?? true, + tagsEnabled: map["tags-Enabled"] as bool? ?? false, + userAvatarColor: AvatarColor.values.firstWhere( + (e) => e.value == map["avatar-Color"] as String?, + orElse: () => AvatarColor.primary, + ), + showSupportBadge: map["purchase-ShowSupportBadge"] as bool? ?? true, + ); + } +} diff --git a/mobile/lib/infrastructure/entities/partner.entity.dart b/mobile/lib/infrastructure/entities/partner.entity.dart new file mode 100644 index 000000000..b7925a8ee --- /dev/null +++ b/mobile/lib/infrastructure/entities/partner.entity.dart @@ -0,0 +1,18 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class PartnerEntity extends Table with DriftDefaultsMixin { + const PartnerEntity(); + + BlobColumn get sharedById => + blob().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + + BlobColumn get sharedWithId => + blob().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + + BoolColumn get inTimeline => boolean().withDefault(const Constant(false))(); + + @override + Set get primaryKey => {sharedById, sharedWithId}; +} diff --git a/mobile/lib/infrastructure/entities/partner.entity.drift.dart b/mobile/lib/infrastructure/entities/partner.entity.drift.dart new file mode 100644 index 0000000000000000000000000000000000000000..974a9e3c30367eff4c91ec7f02d9053d72776252 GIT binary patch literal 23757 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 literal 0 HcmV?d00001 diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart index 710856d9f..955b2267d 100644 --- a/mobile/lib/infrastructure/entities/user.entity.dart +++ b/mobile/lib/infrastructure/entities/user.entity.dart @@ -1,4 +1,7 @@ +import 'package:drift/drift.dart' hide Index; import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/domain/models/user_metadata.model.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; import 'package:immich_mobile/utils/hash.dart'; import 'package:isar/isar.dart'; @@ -71,3 +74,20 @@ class User { quotaSizeInBytes: quotaSizeInBytes, ); } + +class UserEntity extends Table with DriftDefaultsMixin { + const UserEntity(); + + BlobColumn get id => blob()(); + TextColumn get name => text()(); + BoolColumn get isAdmin => boolean().withDefault(const Constant(false))(); + TextColumn get email => text()(); + TextColumn get profileImagePath => text().nullable()(); + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); + // Quota + IntColumn get quotaSizeInBytes => integer().nullable()(); + IntColumn get quotaUsageInBytes => integer().withDefault(const Constant(0))(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/user.entity.drift.dart b/mobile/lib/infrastructure/entities/user.entity.drift.dart new file mode 100644 index 0000000000000000000000000000000000000000..474746a79210c40b5928a1cceac1e4b93d1bedc8 GIT binary patch literal 25521 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 B4qX5M literal 0 HcmV?d00001 diff --git a/mobile/lib/infrastructure/entities/user_metadata.entity.dart b/mobile/lib/infrastructure/entities/user_metadata.entity.dart new file mode 100644 index 000000000..ebbfeebad --- /dev/null +++ b/mobile/lib/infrastructure/entities/user_metadata.entity.dart @@ -0,0 +1,21 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/user_metadata.model.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class UserMetadataEntity extends Table with DriftDefaultsMixin { + const UserMetadataEntity(); + + BlobColumn get userId => + blob().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get preferences => text().map(userPreferenceConverter)(); + + @override + Set get primaryKey => {userId}; +} + +final JsonTypeConverter2 + userPreferenceConverter = TypeConverter.json2( + fromJson: (json) => UserPreferences.fromMap(json as Map), + toJson: (pref) => pref.toMap(), +); diff --git a/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart b/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart new file mode 100644 index 0000000000000000000000000000000000000000..9829fd1acc066d44535b6c2cbdbaa8bd81c9f52a GIT binary patch literal 17619 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 literal 0 HcmV?d00001 diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 74e182bde..997714e1b 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -1,8 +1,15 @@ import 'dart:async'; +import 'package:drift/drift.dart'; +import 'package:drift_flutter/drift_flutter.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart'; +import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'; import 'package:isar/isar.dart'; +import 'db.repository.drift.dart'; + // #zoneTxn is the symbol used by Isar to mark a transaction within the current zone // ref: isar/isar_common.dart const Symbol _kzoneTxn = #zoneTxn; @@ -17,3 +24,35 @@ class IsarDatabaseRepository implements IDatabaseRepository { Future transaction(Future Function() callback) => Zone.current[_kzoneTxn] == null ? _db.writeTxn(callback) : callback(); } + +@DriftDatabase(tables: [UserEntity, UserMetadataEntity, PartnerEntity]) +class Drift extends $Drift implements IDatabaseRepository { + Drift([QueryExecutor? executor]) + : super( + executor ?? + driftDatabase( + name: 'immich', + native: const DriftNativeOptions(shareAcrossIsolates: true), + ), + ); + + @override + int get schemaVersion => 1; + + @override + MigrationStrategy get migration => MigrationStrategy( + beforeOpen: (details) async { + await customStatement('PRAGMA journal_mode = WAL'); + await customStatement('PRAGMA foreign_keys = ON'); + }, + ); +} + +class DriftDatabaseRepository implements IDatabaseRepository { + final Drift _db; + const DriftDatabaseRepository(this._db); + + @override + Future transaction(Future Function() callback) => + _db.transaction(callback); +} diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart new file mode 100644 index 0000000000000000000000000000000000000000..a4c2b31dcd393882c131d1101f11af7b38b8796d GIT binary patch literal 2616 zcmeHJO>fgc5WV|X%%M_TDY8p@K`AL9wL%q?7MhAfRYe+i<> z5O6^v2g}}_dGq$o>`cNCVxb9^nN9`4cNx=q`f|?;Ly#+#X`J)Ud?^#00Bv%dCQ^|n z(>!CRQ7)oevBF8L<&r}83@qFTgaNsCxMe8QR7UH0nk^VHlxnGkAzegNXbe%2B#9<0 z4C=srZGsM{2mG_ZFa890o+61@ka#S@{_jS}`Sj{>_6LWaCl-8c5fMa*Fa~yxt$=`c zgetbm1@Dki1Yvy4Oc%l+{NUr4c0TWaDo}4u?l3AS(}24$0aN6tecU~449|q(>}Xga zL0Y~Wm>yLy)vix#X`EBS@n9*HNLb=v=Zdp%VpFo2iA927vr>M;bjV;BXukwHt3L9laCO5cr8WusVh2z2J+Mgcmx?#Y&LORBLD(L8d0w$mv~%b-aR1xuuUHMSo;Z zJV#f=qj3k_0stg3l~nq^mnwF6?Pm~Uf`q$$KaHkHXwu_qvHDc1$aSlEm;%7snQH*QoSg6Q4(`8zg^Glcs*Qx(e5i8$fkeInV8NEAMqyy>H*@Qx#I$J71! true; + + @override + bool get withoutRowId => true; +} diff --git a/mobile/lib/infrastructure/utils/exif.converter.dart b/mobile/lib/infrastructure/utils/exif.converter.dart index 0f6e2b029..eb9945f45 100644 --- a/mobile/lib/infrastructure/utils/exif.converter.dart +++ b/mobile/lib/infrastructure/utils/exif.converter.dart @@ -1,6 +1,7 @@ import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:openapi/api.dart'; +// TODO: Move to repository once all classes are refactored abstract final class ExifDtoConverter { static ExifInfo fromDto(ExifResponseDto dto) { return ExifInfo( diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart index fcf7ede51..eb7b24737 100644 --- a/mobile/lib/infrastructure/utils/user.converter.dart +++ b/mobile/lib/infrastructure/utils/user.converter.dart @@ -1,6 +1,8 @@ import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/domain/models/user_metadata.model.dart'; import 'package:openapi/api.dart'; +// TODO: Move to repository once all classes are refactored abstract final class UserConverter { /// Base user dto used where the complete user object is not required static UserDto fromSimpleUserDto(UserResponseDto dto) => UserDto( diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart index b3727e832..8866cb01b 100644 --- a/mobile/lib/widgets/common/user_circle_avatar.dart +++ b/mobile/lib/widgets/common/user_circle_avatar.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/domain/models/user_metadata.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/services/api.service.dart'; diff --git a/mobile/makefile b/mobile/makefile index 43bc59c7d..0931d6c16 100644 --- a/mobile/makefile +++ b/mobile/makefile @@ -14,3 +14,6 @@ create_splash: build_release_android: flutter build appbundle + +migrations: + dart run drift_dev make-migrations diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 9c841a870..e79d9f408 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -206,6 +206,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a + url: "https://pub.dev" + source: hosted + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -382,6 +390,30 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.2" + drift: + dependency: "direct main" + description: + name: drift + sha256: "14a61af39d4584faf1d73b5b35e4b758a43008cf4c0fdb0576ec8e7032c0d9a5" + url: "https://pub.dev" + source: hosted + version: "2.26.0" + drift_dev: + dependency: "direct dev" + description: + name: drift_dev + sha256: "0d3f8b33b76cf1c6a82ee34d9511c40957549c4674b8f1688609e6d6c7306588" + url: "https://pub.dev" + source: hosted + version: "2.26.0" + drift_flutter: + dependency: "direct main" + description: + name: drift_flutter + sha256: "0cadbf3b8733409a6cf61d18ba2e94e149df81df7de26f48ae0695b48fd71922" + url: "https://pub.dev" + source: hosted + version: "0.2.4" dynamic_color: dependency: "direct main" description: @@ -1288,6 +1320,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.0" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" riverpod: dependency: transitive description: @@ -1549,6 +1589,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.0" + sqlite3: + dependency: transitive + description: + name: sqlite3 + sha256: "310af39c40dd0bb2058538333c9d9840a2725ae0b9f77e4fd09ad6696aa8f66e" + url: "https://pub.dev" + source: hosted + version: "2.7.5" + sqlite3_flutter_libs: + dependency: transitive + description: + name: sqlite3_flutter_libs + sha256: "7adb4cc96dc08648a5eb1d80a7619070796ca6db03901ff2b6dcb15ee30468f3" + url: "https://pub.dev" + source: hosted + version: "0.5.31" + sqlparser: + dependency: transitive + description: + name: sqlparser + sha256: "27dd0a9f0c02e22ac0eb42a23df9ea079ce69b52bb4a3b478d64e0ef34a263ee" + url: "https://pub.dev" + source: hosted + version: "0.41.0" stack_trace: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index a778f804d..d4ab110a3 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -73,6 +73,9 @@ dependencies: isar_flutter_libs: # contains Isar Core version: *isar_version hosted: https://pub.isar-community.dev/ + # DB + drift: ^2.23.1 + drift_flutter: ^0.2.4 dependency_overrides: analyzer: ^6.0.0 @@ -99,6 +102,8 @@ dev_dependencies: immich_mobile_immich_lint: path: './immich_lint' fake_async: ^1.3.1 + # Drift generator + drift_dev: ^2.23.1 flutter: uses-material-design: true diff --git a/mobile/test/fixtures/user.stub.dart b/mobile/test/fixtures/user.stub.dart index 1dfec9b4b..764342520 100644 --- a/mobile/test/fixtures/user.stub.dart +++ b/mobile/test/fixtures/user.stub.dart @@ -1,4 +1,5 @@ import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/domain/models/user_metadata.model.dart'; abstract final class UserStub { const UserStub._();