diff --git a/.gitattributes b/.gitattributes index d321e2a910..2e8a45ca5c 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 49dbf3944c..49692809bc 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 085449756d..07c6f65b71 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 0000000000..d5de77a377 --- /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 0000000000..1870ef477f --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v1.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"blob","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"preferences","getter_name":"preferences","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userPreferenceConverter","dart_type_name":"UserPreferences"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"blob","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"blob","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}}]} \ No newline at end of file diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart index ad241a8c48..abf2e5620b 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 0000000000..1586384422 --- /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 0000000000..b7925a8eea --- /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 0000000000..974a9e3c30 --- /dev/null +++ b/mobile/lib/infrastructure/entities/partner.entity.drift.dart @@ -0,0 +1,610 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' + as i1; +import 'dart:typed_data' as i2; +import 'package:immich_mobile/infrastructure/entities/partner.entity.dart' + as i3; +import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; +import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' + as i5; +import 'package:drift/internal/modular.dart' as i6; + +typedef $$PartnerEntityTableCreateCompanionBuilder = i1.PartnerEntityCompanion + Function({ + required i2.Uint8List sharedById, + required i2.Uint8List sharedWithId, + i0.Value inTimeline, +}); +typedef $$PartnerEntityTableUpdateCompanionBuilder = i1.PartnerEntityCompanion + Function({ + i0.Value sharedById, + i0.Value sharedWithId, + i0.Value inTimeline, +}); + +final class $$PartnerEntityTableReferences extends i0.BaseReferences< + i0.GeneratedDatabase, i1.$PartnerEntityTable, i1.PartnerEntityData> { + $$PartnerEntityTableReferences( + super.$_db, super.$_table, super.$_typedResult); + + static i5.$UserEntityTable _sharedByIdTable(i0.GeneratedDatabase db) => + i6.ReadDatabaseContainer(db) + .resultSet('user_entity') + .createAlias(i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer(db) + .resultSet('partner_entity') + .sharedById, + i6.ReadDatabaseContainer(db) + .resultSet('user_entity') + .id)); + + i5.$$UserEntityTableProcessedTableManager get sharedById { + final $_column = $_itemColumn('shared_by_id')!; + + final manager = i5 + .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer($_db) + .resultSet('user_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_sharedByIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } + + static i5.$UserEntityTable _sharedWithIdTable(i0.GeneratedDatabase db) => + i6.ReadDatabaseContainer(db) + .resultSet('user_entity') + .createAlias(i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer(db) + .resultSet('partner_entity') + .sharedWithId, + i6.ReadDatabaseContainer(db) + .resultSet('user_entity') + .id)); + + i5.$$UserEntityTableProcessedTableManager get sharedWithId { + final $_column = $_itemColumn('shared_with_id')!; + + final manager = i5 + .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer($_db) + .resultSet('user_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_sharedWithIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } +} + +class $$PartnerEntityTableFilterComposer + extends i0.Composer { + $$PartnerEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get inTimeline => $composableBuilder( + column: $table.inTimeline, builder: (column) => i0.ColumnFilters(column)); + + i5.$$UserEntityTableFilterComposer get sharedById { + final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$UserEntityTableFilterComposer get sharedWithId { + final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$PartnerEntityTableOrderingComposer + extends i0.Composer { + $$PartnerEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get inTimeline => $composableBuilder( + column: $table.inTimeline, + builder: (column) => i0.ColumnOrderings(column)); + + i5.$$UserEntityTableOrderingComposer get sharedById { + final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$UserEntityTableOrderingComposer get sharedWithId { + final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$PartnerEntityTableAnnotationComposer + extends i0.Composer { + $$PartnerEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get inTimeline => $composableBuilder( + column: $table.inTimeline, builder: (column) => column); + + i5.$$UserEntityTableAnnotationComposer get sharedById { + final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + i5.$$UserEntityTableAnnotationComposer get sharedWithId { + final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$PartnerEntityTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData, + i1.$$PartnerEntityTableFilterComposer, + i1.$$PartnerEntityTableOrderingComposer, + i1.$$PartnerEntityTableAnnotationComposer, + $$PartnerEntityTableCreateCompanionBuilder, + $$PartnerEntityTableUpdateCompanionBuilder, + (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), + i1.PartnerEntityData, + i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})> { + $$PartnerEntityTableTableManager( + i0.GeneratedDatabase db, i1.$PartnerEntityTable table) + : super(i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$PartnerEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$PartnerEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + i1.$$PartnerEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + i0.Value sharedById = const i0.Value.absent(), + i0.Value sharedWithId = const i0.Value.absent(), + i0.Value inTimeline = const i0.Value.absent(), + }) => + i1.PartnerEntityCompanion( + sharedById: sharedById, + sharedWithId: sharedWithId, + inTimeline: inTimeline, + ), + createCompanionCallback: ({ + required i2.Uint8List sharedById, + required i2.Uint8List sharedWithId, + i0.Value inTimeline = const i0.Value.absent(), + }) => + i1.PartnerEntityCompanion.insert( + sharedById: sharedById, + sharedWithId: sharedWithId, + inTimeline: inTimeline, + ), + withReferenceMapper: (p0) => p0 + .map((e) => ( + e.readTable(table), + i1.$$PartnerEntityTableReferences(db, table, e) + )) + .toList(), + prefetchHooksCallback: ({sharedById = false, sharedWithId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (sharedById) { + state = state.withJoin( + currentTable: table, + currentColumn: table.sharedById, + referencedTable: + i1.$$PartnerEntityTableReferences._sharedByIdTable(db), + referencedColumn: i1.$$PartnerEntityTableReferences + ._sharedByIdTable(db) + .id, + ) as T; + } + if (sharedWithId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.sharedWithId, + referencedTable: i1.$$PartnerEntityTableReferences + ._sharedWithIdTable(db), + referencedColumn: i1.$$PartnerEntityTableReferences + ._sharedWithIdTable(db) + .id, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + )); +} + +typedef $$PartnerEntityTableProcessedTableManager = i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData, + i1.$$PartnerEntityTableFilterComposer, + i1.$$PartnerEntityTableOrderingComposer, + i1.$$PartnerEntityTableAnnotationComposer, + $$PartnerEntityTableCreateCompanionBuilder, + $$PartnerEntityTableUpdateCompanionBuilder, + (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), + i1.PartnerEntityData, + i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})>; + +class $PartnerEntityTable extends i3.PartnerEntity + with i0.TableInfo<$PartnerEntityTable, i1.PartnerEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $PartnerEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _sharedByIdMeta = + const i0.VerificationMeta('sharedById'); + @override + late final i0.GeneratedColumn sharedById = + i0.GeneratedColumn('shared_by_id', aliasedName, false, + type: i0.DriftSqlType.blob, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + static const i0.VerificationMeta _sharedWithIdMeta = + const i0.VerificationMeta('sharedWithId'); + @override + late final i0.GeneratedColumn sharedWithId = + i0.GeneratedColumn('shared_with_id', aliasedName, false, + type: i0.DriftSqlType.blob, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + static const i0.VerificationMeta _inTimelineMeta = + const i0.VerificationMeta('inTimeline'); + @override + late final i0.GeneratedColumn inTimeline = i0.GeneratedColumn( + 'in_timeline', aliasedName, false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))'), + defaultValue: const i4.Constant(false)); + @override + List get $columns => + [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, + {bool isInserting = false}) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('shared_by_id')) { + context.handle( + _sharedByIdMeta, + sharedById.isAcceptableOrUnknown( + data['shared_by_id']!, _sharedByIdMeta)); + } else if (isInserting) { + context.missing(_sharedByIdMeta); + } + if (data.containsKey('shared_with_id')) { + context.handle( + _sharedWithIdMeta, + sharedWithId.isAcceptableOrUnknown( + data['shared_with_id']!, _sharedWithIdMeta)); + } else if (isInserting) { + context.missing(_sharedWithIdMeta); + } + if (data.containsKey('in_timeline')) { + context.handle( + _inTimelineMeta, + inTimeline.isAcceptableOrUnknown( + data['in_timeline']!, _inTimelineMeta)); + } + return context; + } + + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + i1.PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.PartnerEntityData( + sharedById: attachedDatabase.typeMapping + .read(i0.DriftSqlType.blob, data['${effectivePrefix}shared_by_id'])!, + sharedWithId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.blob, data['${effectivePrefix}shared_with_id'])!, + inTimeline: attachedDatabase.typeMapping + .read(i0.DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + ); + } + + @override + $PartnerEntityTable createAlias(String alias) { + return $PartnerEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends i0.DataClass + implements i0.Insertable { + final i2.Uint8List sharedById; + final i2.Uint8List sharedWithId; + final bool inTimeline; + const PartnerEntityData( + {required this.sharedById, + required this.sharedWithId, + required this.inTimeline}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = i0.Variable(sharedById); + map['shared_with_id'] = i0.Variable(sharedWithId); + map['in_timeline'] = i0.Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson(Map json, + {i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + i1.PartnerEntityData copyWith( + {i2.Uint8List? sharedById, + i2.Uint8List? sharedWithId, + bool? inTimeline}) => + i1.PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(i1.PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: + data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: + data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(i0.$driftBlobEquality.hash(sharedById), + i0.$driftBlobEquality.hash(sharedWithId), inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.PartnerEntityData && + i0.$driftBlobEquality.equals(other.sharedById, this.sharedById) && + i0.$driftBlobEquality.equals(other.sharedWithId, this.sharedWithId) && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends i0.UpdateCompanion { + final i0.Value sharedById; + final i0.Value sharedWithId; + final i0.Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const i0.Value.absent(), + this.sharedWithId = const i0.Value.absent(), + this.inTimeline = const i0.Value.absent(), + }); + PartnerEntityCompanion.insert({ + required i2.Uint8List sharedById, + required i2.Uint8List sharedWithId, + this.inTimeline = const i0.Value.absent(), + }) : sharedById = i0.Value(sharedById), + sharedWithId = i0.Value(sharedWithId); + static i0.Insertable custom({ + i0.Expression? sharedById, + i0.Expression? sharedWithId, + i0.Expression? inTimeline, + }) { + return i0.RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + i1.PartnerEntityCompanion copyWith( + {i0.Value? sharedById, + i0.Value? sharedWithId, + i0.Value? inTimeline}) { + return i1.PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = i0.Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = i0.Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = i0.Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart index 710856d9f7..955b2267d1 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 0000000000..474746a792 --- /dev/null +++ b/mobile/lib/infrastructure/entities/user.entity.drift.dart @@ -0,0 +1,656 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' + as i1; +import 'dart:typed_data' as i2; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as i3; +import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; + +typedef $$UserEntityTableCreateCompanionBuilder = i1.UserEntityCompanion + Function({ + required i2.Uint8List id, + required String name, + i0.Value isAdmin, + required String email, + i0.Value profileImagePath, + i0.Value updatedAt, + i0.Value quotaSizeInBytes, + i0.Value quotaUsageInBytes, +}); +typedef $$UserEntityTableUpdateCompanionBuilder = i1.UserEntityCompanion + Function({ + i0.Value id, + i0.Value name, + i0.Value isAdmin, + i0.Value email, + i0.Value profileImagePath, + i0.Value updatedAt, + i0.Value quotaSizeInBytes, + i0.Value quotaUsageInBytes, +}); + +class $$UserEntityTableFilterComposer + extends i0.Composer { + $$UserEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get name => $composableBuilder( + column: $table.name, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get isAdmin => $composableBuilder( + column: $table.isAdmin, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get email => $composableBuilder( + column: $table.email, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get profileImagePath => $composableBuilder( + column: $table.profileImagePath, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get updatedAt => $composableBuilder( + column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get quotaSizeInBytes => $composableBuilder( + column: $table.quotaSizeInBytes, + builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get quotaUsageInBytes => $composableBuilder( + column: $table.quotaUsageInBytes, + builder: (column) => i0.ColumnFilters(column)); +} + +class $$UserEntityTableOrderingComposer + extends i0.Composer { + $$UserEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get name => $composableBuilder( + column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get isAdmin => $composableBuilder( + column: $table.isAdmin, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get email => $composableBuilder( + column: $table.email, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get profileImagePath => $composableBuilder( + column: $table.profileImagePath, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get quotaSizeInBytes => $composableBuilder( + column: $table.quotaSizeInBytes, + builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get quotaUsageInBytes => $composableBuilder( + column: $table.quotaUsageInBytes, + builder: (column) => i0.ColumnOrderings(column)); +} + +class $$UserEntityTableAnnotationComposer + extends i0.Composer { + $$UserEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + i0.GeneratedColumn get isAdmin => + $composableBuilder(column: $table.isAdmin, builder: (column) => column); + + i0.GeneratedColumn get email => + $composableBuilder(column: $table.email, builder: (column) => column); + + i0.GeneratedColumn get profileImagePath => $composableBuilder( + column: $table.profileImagePath, builder: (column) => column); + + i0.GeneratedColumn get updatedAt => + $composableBuilder(column: $table.updatedAt, builder: (column) => column); + + i0.GeneratedColumn get quotaSizeInBytes => $composableBuilder( + column: $table.quotaSizeInBytes, builder: (column) => column); + + i0.GeneratedColumn get quotaUsageInBytes => $composableBuilder( + column: $table.quotaUsageInBytes, builder: (column) => column); +} + +class $$UserEntityTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData, + i1.$$UserEntityTableFilterComposer, + i1.$$UserEntityTableOrderingComposer, + i1.$$UserEntityTableAnnotationComposer, + $$UserEntityTableCreateCompanionBuilder, + $$UserEntityTableUpdateCompanionBuilder, + ( + i1.UserEntityData, + i0.BaseReferences + ), + i1.UserEntityData, + i0.PrefetchHooks Function()> { + $$UserEntityTableTableManager( + i0.GeneratedDatabase db, i1.$UserEntityTable table) + : super(i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$UserEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$UserEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + i1.$$UserEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value isAdmin = const i0.Value.absent(), + i0.Value email = const i0.Value.absent(), + i0.Value profileImagePath = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value quotaSizeInBytes = const i0.Value.absent(), + i0.Value quotaUsageInBytes = const i0.Value.absent(), + }) => + i1.UserEntityCompanion( + id: id, + name: name, + isAdmin: isAdmin, + email: email, + profileImagePath: profileImagePath, + updatedAt: updatedAt, + quotaSizeInBytes: quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes, + ), + createCompanionCallback: ({ + required i2.Uint8List id, + required String name, + i0.Value isAdmin = const i0.Value.absent(), + required String email, + i0.Value profileImagePath = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value quotaSizeInBytes = const i0.Value.absent(), + i0.Value quotaUsageInBytes = const i0.Value.absent(), + }) => + i1.UserEntityCompanion.insert( + id: id, + name: name, + isAdmin: isAdmin, + email: email, + profileImagePath: profileImagePath, + updatedAt: updatedAt, + quotaSizeInBytes: quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + )); +} + +typedef $$UserEntityTableProcessedTableManager = i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData, + i1.$$UserEntityTableFilterComposer, + i1.$$UserEntityTableOrderingComposer, + i1.$$UserEntityTableAnnotationComposer, + $$UserEntityTableCreateCompanionBuilder, + $$UserEntityTableUpdateCompanionBuilder, + ( + i1.UserEntityData, + i0.BaseReferences + ), + i1.UserEntityData, + i0.PrefetchHooks Function()>; + +class $UserEntityTable extends i3.UserEntity + with i0.TableInfo<$UserEntityTable, i1.UserEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $UserEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = + i0.GeneratedColumn('id', aliasedName, false, + type: i0.DriftSqlType.blob, requiredDuringInsert: true); + static const i0.VerificationMeta _nameMeta = + const i0.VerificationMeta('name'); + @override + late final i0.GeneratedColumn name = i0.GeneratedColumn( + 'name', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + static const i0.VerificationMeta _isAdminMeta = + const i0.VerificationMeta('isAdmin'); + @override + late final i0.GeneratedColumn isAdmin = i0.GeneratedColumn( + 'is_admin', aliasedName, false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + i0.GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), + defaultValue: const i4.Constant(false)); + static const i0.VerificationMeta _emailMeta = + const i0.VerificationMeta('email'); + @override + late final i0.GeneratedColumn email = i0.GeneratedColumn( + 'email', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true); + static const i0.VerificationMeta _profileImagePathMeta = + const i0.VerificationMeta('profileImagePath'); + @override + late final i0.GeneratedColumn profileImagePath = + i0.GeneratedColumn('profile_image_path', aliasedName, true, + type: i0.DriftSqlType.string, requiredDuringInsert: false); + static const i0.VerificationMeta _updatedAtMeta = + const i0.VerificationMeta('updatedAt'); + @override + late final i0.GeneratedColumn updatedAt = + i0.GeneratedColumn('updated_at', aliasedName, false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime); + static const i0.VerificationMeta _quotaSizeInBytesMeta = + const i0.VerificationMeta('quotaSizeInBytes'); + @override + late final i0.GeneratedColumn quotaSizeInBytes = i0.GeneratedColumn( + 'quota_size_in_bytes', aliasedName, true, + type: i0.DriftSqlType.int, requiredDuringInsert: false); + static const i0.VerificationMeta _quotaUsageInBytesMeta = + const i0.VerificationMeta('quotaUsageInBytes'); + @override + late final i0.GeneratedColumn quotaUsageInBytes = + i0.GeneratedColumn('quota_usage_in_bytes', aliasedName, false, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const i4.Constant(0)); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, + {bool isInserting = false}) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('is_admin')) { + context.handle(_isAdminMeta, + isAdmin.isAcceptableOrUnknown(data['is_admin']!, _isAdminMeta)); + } + if (data.containsKey('email')) { + context.handle( + _emailMeta, email.isAcceptableOrUnknown(data['email']!, _emailMeta)); + } else if (isInserting) { + context.missing(_emailMeta); + } + if (data.containsKey('profile_image_path')) { + context.handle( + _profileImagePathMeta, + profileImagePath.isAcceptableOrUnknown( + data['profile_image_path']!, _profileImagePathMeta)); + } + if (data.containsKey('updated_at')) { + context.handle(_updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + } + if (data.containsKey('quota_size_in_bytes')) { + context.handle( + _quotaSizeInBytesMeta, + quotaSizeInBytes.isAcceptableOrUnknown( + data['quota_size_in_bytes']!, _quotaSizeInBytesMeta)); + } + if (data.containsKey('quota_usage_in_bytes')) { + context.handle( + _quotaUsageInBytesMeta, + quotaUsageInBytes.isAcceptableOrUnknown( + data['quota_usage_in_bytes']!, _quotaUsageInBytesMeta)); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.UserEntityData( + id: attachedDatabase.typeMapping + .read(i0.DriftSqlType.blob, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, + isAdmin: attachedDatabase.typeMapping + .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, + email: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}email'])!, + profileImagePath: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, data['${effectivePrefix}profile_image_path']), + updatedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + ); + } + + @override + $UserEntityTable createAlias(String alias) { + return $UserEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends i0.DataClass + implements i0.Insertable { + final i2.Uint8List id; + final String name; + final bool isAdmin; + final String email; + final String? profileImagePath; + final DateTime updatedAt; + final int? quotaSizeInBytes; + final int quotaUsageInBytes; + const UserEntityData( + {required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['name'] = i0.Variable(name); + map['is_admin'] = i0.Variable(isAdmin); + map['email'] = i0.Variable(email); + if (!nullToAbsent || profileImagePath != null) { + map['profile_image_path'] = i0.Variable(profileImagePath); + } + map['updated_at'] = i0.Variable(updatedAt); + if (!nullToAbsent || quotaSizeInBytes != null) { + map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes); + } + map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes); + return map; + } + + factory UserEntityData.fromJson(Map json, + {i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + profileImagePath: serializer.fromJson(json['profileImagePath']), + updatedAt: serializer.fromJson(json['updatedAt']), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'profileImagePath': serializer.toJson(profileImagePath), + 'updatedAt': serializer.toJson(updatedAt), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + }; + } + + i1.UserEntityData copyWith( + {i2.Uint8List? id, + String? name, + bool? isAdmin, + String? email, + i0.Value profileImagePath = const i0.Value.absent(), + DateTime? updatedAt, + i0.Value quotaSizeInBytes = const i0.Value.absent(), + int? quotaUsageInBytes}) => + i1.UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + UserEntityData copyWithCompanion(i1.UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(i0.$driftBlobEquality.hash(id), name, isAdmin, + email, profileImagePath, updatedAt, quotaSizeInBytes, quotaUsageInBytes); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.UserEntityData && + i0.$driftBlobEquality.equals(other.id, this.id) && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.profileImagePath == this.profileImagePath && + other.updatedAt == this.updatedAt && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes); +} + +class UserEntityCompanion extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value name; + final i0.Value isAdmin; + final i0.Value email; + final i0.Value profileImagePath; + final i0.Value updatedAt; + final i0.Value quotaSizeInBytes; + final i0.Value quotaUsageInBytes; + const UserEntityCompanion({ + this.id = const i0.Value.absent(), + this.name = const i0.Value.absent(), + this.isAdmin = const i0.Value.absent(), + this.email = const i0.Value.absent(), + this.profileImagePath = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + this.quotaSizeInBytes = const i0.Value.absent(), + this.quotaUsageInBytes = const i0.Value.absent(), + }); + UserEntityCompanion.insert({ + required i2.Uint8List id, + required String name, + this.isAdmin = const i0.Value.absent(), + required String email, + this.profileImagePath = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + this.quotaSizeInBytes = const i0.Value.absent(), + this.quotaUsageInBytes = const i0.Value.absent(), + }) : id = i0.Value(id), + name = i0.Value(name), + email = i0.Value(email); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? name, + i0.Expression? isAdmin, + i0.Expression? email, + i0.Expression? profileImagePath, + i0.Expression? updatedAt, + i0.Expression? quotaSizeInBytes, + i0.Expression? quotaUsageInBytes, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (profileImagePath != null) 'profile_image_path': profileImagePath, + if (updatedAt != null) 'updated_at': updatedAt, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + }); + } + + i1.UserEntityCompanion copyWith( + {i0.Value? id, + i0.Value? name, + i0.Value? isAdmin, + i0.Value? email, + i0.Value? profileImagePath, + i0.Value? updatedAt, + i0.Value? quotaSizeInBytes, + i0.Value? quotaUsageInBytes}) { + return i1.UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath ?? this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (name.present) { + map['name'] = i0.Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = i0.Variable(isAdmin.value); + } + if (email.present) { + map['email'] = i0.Variable(email.value); + } + if (profileImagePath.present) { + map['profile_image_path'] = i0.Variable(profileImagePath.value); + } + if (updatedAt.present) { + map['updated_at'] = i0.Variable(updatedAt.value); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } +} 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 0000000000..ebbfeebadd --- /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 0000000000..9829fd1acc --- /dev/null +++ b/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart @@ -0,0 +1,468 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' + as i1; +import 'dart:typed_data' as i2; +import 'package:immich_mobile/domain/models/user_metadata.model.dart' as i3; +import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart' + as i4; +import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' + as i5; +import 'package:drift/internal/modular.dart' as i6; + +typedef $$UserMetadataEntityTableCreateCompanionBuilder + = i1.UserMetadataEntityCompanion Function({ + required i2.Uint8List userId, + required i3.UserPreferences preferences, +}); +typedef $$UserMetadataEntityTableUpdateCompanionBuilder + = i1.UserMetadataEntityCompanion Function({ + i0.Value userId, + i0.Value preferences, +}); + +final class $$UserMetadataEntityTableReferences extends i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData> { + $$UserMetadataEntityTableReferences( + super.$_db, super.$_table, super.$_typedResult); + + static i5.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) => + i6.ReadDatabaseContainer(db) + .resultSet('user_entity') + .createAlias(i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer(db) + .resultSet( + 'user_metadata_entity') + .userId, + i6.ReadDatabaseContainer(db) + .resultSet('user_entity') + .id)); + + i5.$$UserEntityTableProcessedTableManager get userId { + final $_column = $_itemColumn('user_id')!; + + final manager = i5 + .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer($_db) + .resultSet('user_entity')) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_userIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } +} + +class $$UserMetadataEntityTableFilterComposer + extends i0.Composer { + $$UserMetadataEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnWithTypeConverterFilters + get preferences => $composableBuilder( + column: $table.preferences, + builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + + i5.$$UserEntityTableFilterComposer get userId { + final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$UserMetadataEntityTableOrderingComposer + extends i0.Composer { + $$UserMetadataEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get preferences => $composableBuilder( + column: $table.preferences, + builder: (column) => i0.ColumnOrderings(column)); + + i5.$$UserEntityTableOrderingComposer get userId { + final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$UserMetadataEntityTableAnnotationComposer + extends i0.Composer { + $$UserMetadataEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumnWithTypeConverter + get preferences => $composableBuilder( + column: $table.preferences, builder: (column) => column); + + i5.$$UserEntityTableAnnotationComposer get userId { + final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer($db) + .resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$UserMetadataEntityTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData, + i1.$$UserMetadataEntityTableFilterComposer, + i1.$$UserMetadataEntityTableOrderingComposer, + i1.$$UserMetadataEntityTableAnnotationComposer, + $$UserMetadataEntityTableCreateCompanionBuilder, + $$UserMetadataEntityTableUpdateCompanionBuilder, + (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), + i1.UserMetadataEntityData, + i0.PrefetchHooks Function({bool userId})> { + $$UserMetadataEntityTableTableManager( + i0.GeneratedDatabase db, i1.$UserMetadataEntityTable table) + : super(i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => i1 + .$$UserMetadataEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$UserMetadataEntityTableOrderingComposer( + $db: db, $table: table), + createComputedFieldComposer: () => + i1.$$UserMetadataEntityTableAnnotationComposer( + $db: db, $table: table), + updateCompanionCallback: ({ + i0.Value userId = const i0.Value.absent(), + i0.Value preferences = const i0.Value.absent(), + }) => + i1.UserMetadataEntityCompanion( + userId: userId, + preferences: preferences, + ), + createCompanionCallback: ({ + required i2.Uint8List userId, + required i3.UserPreferences preferences, + }) => + i1.UserMetadataEntityCompanion.insert( + userId: userId, + preferences: preferences, + ), + withReferenceMapper: (p0) => p0 + .map((e) => ( + e.readTable(table), + i1.$$UserMetadataEntityTableReferences(db, table, e) + )) + .toList(), + prefetchHooksCallback: ({userId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (userId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.userId, + referencedTable: + i1.$$UserMetadataEntityTableReferences._userIdTable(db), + referencedColumn: i1.$$UserMetadataEntityTableReferences + ._userIdTable(db) + .id, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + )); +} + +typedef $$UserMetadataEntityTableProcessedTableManager + = i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData, + i1.$$UserMetadataEntityTableFilterComposer, + i1.$$UserMetadataEntityTableOrderingComposer, + i1.$$UserMetadataEntityTableAnnotationComposer, + $$UserMetadataEntityTableCreateCompanionBuilder, + $$UserMetadataEntityTableUpdateCompanionBuilder, + (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), + i1.UserMetadataEntityData, + i0.PrefetchHooks Function({bool userId})>; + +class $UserMetadataEntityTable extends i4.UserMetadataEntity + with i0.TableInfo<$UserMetadataEntityTable, i1.UserMetadataEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $UserMetadataEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _userIdMeta = + const i0.VerificationMeta('userId'); + @override + late final i0.GeneratedColumn userId = + i0.GeneratedColumn('user_id', aliasedName, false, + type: i0.DriftSqlType.blob, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE')); + @override + late final i0.GeneratedColumnWithTypeConverter + preferences = i0.GeneratedColumn( + 'preferences', aliasedName, false, + type: i0.DriftSqlType.string, requiredDuringInsert: true) + .withConverter( + i1.$UserMetadataEntityTable.$converterpreferences); + @override + List get $columns => [userId, preferences]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, + {bool isInserting = false}) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('user_id')) { + context.handle(_userIdMeta, + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); + } else if (isInserting) { + context.missing(_userIdMeta); + } + return context; + } + + @override + Set get $primaryKey => {userId}; + @override + i1.UserMetadataEntityData map(Map data, + {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.UserMetadataEntityData( + userId: attachedDatabase.typeMapping + .read(i0.DriftSqlType.blob, data['${effectivePrefix}user_id'])!, + preferences: i1.$UserMetadataEntityTable.$converterpreferences.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, data['${effectivePrefix}preferences'])!), + ); + } + + @override + $UserMetadataEntityTable createAlias(String alias) { + return $UserMetadataEntityTable(attachedDatabase, alias); + } + + static i0.JsonTypeConverter2 + $converterpreferences = i4.userPreferenceConverter; + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends i0.DataClass + implements i0.Insertable { + final i2.Uint8List userId; + final i3.UserPreferences preferences; + const UserMetadataEntityData( + {required this.userId, required this.preferences}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = i0.Variable(userId); + { + map['preferences'] = i0.Variable( + i1.$UserMetadataEntityTable.$converterpreferences.toSql(preferences)); + } + return map; + } + + factory UserMetadataEntityData.fromJson(Map json, + {i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + preferences: i1.$UserMetadataEntityTable.$converterpreferences + .fromJson(serializer.fromJson(json['preferences'])), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'preferences': serializer.toJson(i1 + .$UserMetadataEntityTable.$converterpreferences + .toJson(preferences)), + }; + } + + i1.UserMetadataEntityData copyWith( + {i2.Uint8List? userId, i3.UserPreferences? preferences}) => + i1.UserMetadataEntityData( + userId: userId ?? this.userId, + preferences: preferences ?? this.preferences, + ); + UserMetadataEntityData copyWithCompanion( + i1.UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + preferences: + data.preferences.present ? data.preferences.value : this.preferences, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('preferences: $preferences') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(i0.$driftBlobEquality.hash(userId), preferences); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.UserMetadataEntityData && + i0.$driftBlobEquality.equals(other.userId, this.userId) && + other.preferences == this.preferences); +} + +class UserMetadataEntityCompanion + extends i0.UpdateCompanion { + final i0.Value userId; + final i0.Value preferences; + const UserMetadataEntityCompanion({ + this.userId = const i0.Value.absent(), + this.preferences = const i0.Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required i2.Uint8List userId, + required i3.UserPreferences preferences, + }) : userId = i0.Value(userId), + preferences = i0.Value(preferences); + static i0.Insertable custom({ + i0.Expression? userId, + i0.Expression? preferences, + }) { + return i0.RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (preferences != null) 'preferences': preferences, + }); + } + + i1.UserMetadataEntityCompanion copyWith( + {i0.Value? userId, + i0.Value? preferences}) { + return i1.UserMetadataEntityCompanion( + userId: userId ?? this.userId, + preferences: preferences ?? this.preferences, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = i0.Variable(userId.value); + } + if (preferences.present) { + map['preferences'] = i0.Variable(i1 + .$UserMetadataEntityTable.$converterpreferences + .toSql(preferences.value)); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('preferences: $preferences') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 74e182bdee..997714e1b6 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 0000000000..a4c2b31dcd --- /dev/null +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -0,0 +1,67 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' + as i1; +import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' + as i2; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' + as i3; + +abstract class $Drift extends i0.GeneratedDatabase { + $Drift(i0.QueryExecutor e) : super(e); + $DriftManager get managers => $DriftManager(this); + late final i1.$UserEntityTable userEntity = i1.$UserEntityTable(this); + late final i2.$UserMetadataEntityTable userMetadataEntity = + i2.$UserMetadataEntityTable(this); + late final i3.$PartnerEntityTable partnerEntity = + i3.$PartnerEntityTable(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => + [userEntity, userMetadataEntity, partnerEntity]; + @override + i0.StreamQueryUpdateRules get streamUpdateRules => + const i0.StreamQueryUpdateRules( + [ + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('user_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('user_metadata_entity', + kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('user_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName('user_entity', + limitUpdateKind: i0.UpdateKind.delete), + result: [ + i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), + ], + ), + ], + ); + @override + i0.DriftDatabaseOptions get options => + const i0.DriftDatabaseOptions(storeDateTimeAsText: true); +} + +class $DriftManager { + final $Drift _db; + $DriftManager(this._db); + i1.$$UserEntityTableTableManager get userEntity => + i1.$$UserEntityTableTableManager(_db, _db.userEntity); + i2.$$UserMetadataEntityTableTableManager get userMetadataEntity => + i2.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); + i3.$$PartnerEntityTableTableManager get partnerEntity => + i3.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); +} diff --git a/mobile/lib/infrastructure/utils/drift_default.mixin.dart b/mobile/lib/infrastructure/utils/drift_default.mixin.dart new file mode 100644 index 0000000000..1ba4589ed5 --- /dev/null +++ b/mobile/lib/infrastructure/utils/drift_default.mixin.dart @@ -0,0 +1,9 @@ +import 'package:drift/drift.dart'; + +mixin DriftDefaultsMixin on Table { + @override + bool get isStrict => 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 0f6e2b0295..eb9945f454 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 fcf7ede51c..eb7b24737e 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 b3727e8323..8866cb01b0 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 43bc59c7d4..0931d6c167 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 9c841a870e..e79d9f4084 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 a778f804d0..d4ab110a3e 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 1dfec9b4b1..764342520f 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._();