Source Code added

This commit is contained in:
Fr4nz D13trich 2026-02-02 15:06:40 +01:00
parent 800376eafd
commit 9efa9bc6dd
3912 changed files with 754770 additions and 2 deletions

View file

@ -0,0 +1,31 @@
# Infrastructure Layer
This directory contains the infrastructure layer of Immich. The infrastructure layer is responsible for the implementation details of the app. It includes data sources, APIs, and other external dependencies.
## Structure
- **[Entities](./entities/)**: These are the classes that define the database schema for the domain models.
- **[Repositories](./repositories/)**: These are the actual implementation of the domain interfaces. A single interface might have multiple implementations.
- **[Utils](./utils/)**: These are utility classes and functions specific to infrastructure implementations.
```
infrastructure/
├── entities/
│ └── user.entity.dart
├── repositories/
│ └── user.repository.dart
└── utils/
└── database_utils.dart
```
## Usage
The infrastructure layer provides concrete implementations of repository interfaces defined in the domain layer. These implementations are exposed through Riverpod providers in the root `providers` directory.
```dart
// In domain/services/user.service.dart
final userRepository = ref.watch(userRepositoryProvider);
final user = await userRepository.getUser(userId);
```
The domain layer should never directly instantiate repository implementations, but instead receive them through dependency injection.

View file

@ -0,0 +1,31 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/entities/person.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class AssetFaceEntity extends Table with DriftDefaultsMixin {
const AssetFaceEntity();
TextColumn get id => text()();
TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get personId => text().nullable().references(PersonEntity, #id, onDelete: KeyAction.setNull)();
IntColumn get imageWidth => integer()();
IntColumn get imageHeight => integer()();
IntColumn get boundingBoxX1 => integer()();
IntColumn get boundingBoxY1 => integer()();
IntColumn get boundingBoxX2 => integer()();
IntColumn get boundingBoxY2 => integer()();
TextColumn get sourceType => text()();
@override
Set<Column> get primaryKey => {id};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,27 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class AuthUserEntity extends Table with DriftDefaultsMixin {
const AuthUserEntity();
TextColumn get id => text()();
TextColumn get name => text()();
TextColumn get email => text()();
BoolColumn get isAdmin => boolean().withDefault(const Constant(false))();
// Profile image
BoolColumn get hasProfileImage => boolean().withDefault(const Constant(false))();
DateTimeColumn get profileChangedAt => dateTime().withDefault(currentDateAndTime)();
IntColumn get avatarColor => intEnum<AvatarColor>()();
// Quota
IntColumn get quotaSizeInBytes => integer().withDefault(const Constant(0))();
IntColumn get quotaUsageInBytes => integer().withDefault(const Constant(0))();
// Locked Folder
TextColumn get pinCode => text().nullable()();
@override
Set<Column> get primaryKey => {id};
}

View file

@ -0,0 +1,933 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart'
as i1;
import 'package:immich_mobile/domain/models/user.model.dart' as i2;
import 'package:immich_mobile/infrastructure/entities/auth_user.entity.dart'
as i3;
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
typedef $$AuthUserEntityTableCreateCompanionBuilder =
i1.AuthUserEntityCompanion Function({
required String id,
required String name,
required String email,
i0.Value<bool> isAdmin,
i0.Value<bool> hasProfileImage,
i0.Value<DateTime> profileChangedAt,
required i2.AvatarColor avatarColor,
i0.Value<int> quotaSizeInBytes,
i0.Value<int> quotaUsageInBytes,
i0.Value<String?> pinCode,
});
typedef $$AuthUserEntityTableUpdateCompanionBuilder =
i1.AuthUserEntityCompanion Function({
i0.Value<String> id,
i0.Value<String> name,
i0.Value<String> email,
i0.Value<bool> isAdmin,
i0.Value<bool> hasProfileImage,
i0.Value<DateTime> profileChangedAt,
i0.Value<i2.AvatarColor> avatarColor,
i0.Value<int> quotaSizeInBytes,
i0.Value<int> quotaUsageInBytes,
i0.Value<String?> pinCode,
});
class $$AuthUserEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$AuthUserEntityTable> {
$$AuthUserEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get name => $composableBuilder(
column: $table.name,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get email => $composableBuilder(
column: $table.email,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<bool> get isAdmin => $composableBuilder(
column: $table.isAdmin,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<bool> get hasProfileImage => $composableBuilder(
column: $table.hasProfileImage,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<DateTime> get profileChangedAt => $composableBuilder(
column: $table.profileChangedAt,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnWithTypeConverterFilters<i2.AvatarColor, i2.AvatarColor, int>
get avatarColor => $composableBuilder(
column: $table.avatarColor,
builder: (column) => i0.ColumnWithTypeConverterFilters(column),
);
i0.ColumnFilters<int> get quotaSizeInBytes => $composableBuilder(
column: $table.quotaSizeInBytes,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<int> get quotaUsageInBytes => $composableBuilder(
column: $table.quotaUsageInBytes,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get pinCode => $composableBuilder(
column: $table.pinCode,
builder: (column) => i0.ColumnFilters(column),
);
}
class $$AuthUserEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$AuthUserEntityTable> {
$$AuthUserEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get name => $composableBuilder(
column: $table.name,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get email => $composableBuilder(
column: $table.email,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<bool> get isAdmin => $composableBuilder(
column: $table.isAdmin,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<bool> get hasProfileImage => $composableBuilder(
column: $table.hasProfileImage,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<DateTime> get profileChangedAt => $composableBuilder(
column: $table.profileChangedAt,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<int> get avatarColor => $composableBuilder(
column: $table.avatarColor,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<int> get quotaSizeInBytes => $composableBuilder(
column: $table.quotaSizeInBytes,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<int> get quotaUsageInBytes => $composableBuilder(
column: $table.quotaUsageInBytes,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get pinCode => $composableBuilder(
column: $table.pinCode,
builder: (column) => i0.ColumnOrderings(column),
);
}
class $$AuthUserEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$AuthUserEntityTable> {
$$AuthUserEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<String> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
i0.GeneratedColumn<String> get name =>
$composableBuilder(column: $table.name, builder: (column) => column);
i0.GeneratedColumn<String> get email =>
$composableBuilder(column: $table.email, builder: (column) => column);
i0.GeneratedColumn<bool> get isAdmin =>
$composableBuilder(column: $table.isAdmin, builder: (column) => column);
i0.GeneratedColumn<bool> get hasProfileImage => $composableBuilder(
column: $table.hasProfileImage,
builder: (column) => column,
);
i0.GeneratedColumn<DateTime> get profileChangedAt => $composableBuilder(
column: $table.profileChangedAt,
builder: (column) => column,
);
i0.GeneratedColumnWithTypeConverter<i2.AvatarColor, int> get avatarColor =>
$composableBuilder(
column: $table.avatarColor,
builder: (column) => column,
);
i0.GeneratedColumn<int> get quotaSizeInBytes => $composableBuilder(
column: $table.quotaSizeInBytes,
builder: (column) => column,
);
i0.GeneratedColumn<int> get quotaUsageInBytes => $composableBuilder(
column: $table.quotaUsageInBytes,
builder: (column) => column,
);
i0.GeneratedColumn<String> get pinCode =>
$composableBuilder(column: $table.pinCode, builder: (column) => column);
}
class $$AuthUserEntityTableTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.$AuthUserEntityTable,
i1.AuthUserEntityData,
i1.$$AuthUserEntityTableFilterComposer,
i1.$$AuthUserEntityTableOrderingComposer,
i1.$$AuthUserEntityTableAnnotationComposer,
$$AuthUserEntityTableCreateCompanionBuilder,
$$AuthUserEntityTableUpdateCompanionBuilder,
(
i1.AuthUserEntityData,
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$AuthUserEntityTable,
i1.AuthUserEntityData
>,
),
i1.AuthUserEntityData,
i0.PrefetchHooks Function()
> {
$$AuthUserEntityTableTableManager(
i0.GeneratedDatabase db,
i1.$AuthUserEntityTable table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$AuthUserEntityTableFilterComposer($db: db, $table: table),
createOrderingComposer: () =>
i1.$$AuthUserEntityTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () => i1
.$$AuthUserEntityTableAnnotationComposer($db: db, $table: table),
updateCompanionCallback:
({
i0.Value<String> id = const i0.Value.absent(),
i0.Value<String> name = const i0.Value.absent(),
i0.Value<String> email = const i0.Value.absent(),
i0.Value<bool> isAdmin = const i0.Value.absent(),
i0.Value<bool> hasProfileImage = const i0.Value.absent(),
i0.Value<DateTime> profileChangedAt = const i0.Value.absent(),
i0.Value<i2.AvatarColor> avatarColor = const i0.Value.absent(),
i0.Value<int> quotaSizeInBytes = const i0.Value.absent(),
i0.Value<int> quotaUsageInBytes = const i0.Value.absent(),
i0.Value<String?> pinCode = const i0.Value.absent(),
}) => i1.AuthUserEntityCompanion(
id: id,
name: name,
email: email,
isAdmin: isAdmin,
hasProfileImage: hasProfileImage,
profileChangedAt: profileChangedAt,
avatarColor: avatarColor,
quotaSizeInBytes: quotaSizeInBytes,
quotaUsageInBytes: quotaUsageInBytes,
pinCode: pinCode,
),
createCompanionCallback:
({
required String id,
required String name,
required String email,
i0.Value<bool> isAdmin = const i0.Value.absent(),
i0.Value<bool> hasProfileImage = const i0.Value.absent(),
i0.Value<DateTime> profileChangedAt = const i0.Value.absent(),
required i2.AvatarColor avatarColor,
i0.Value<int> quotaSizeInBytes = const i0.Value.absent(),
i0.Value<int> quotaUsageInBytes = const i0.Value.absent(),
i0.Value<String?> pinCode = const i0.Value.absent(),
}) => i1.AuthUserEntityCompanion.insert(
id: id,
name: name,
email: email,
isAdmin: isAdmin,
hasProfileImage: hasProfileImage,
profileChangedAt: profileChangedAt,
avatarColor: avatarColor,
quotaSizeInBytes: quotaSizeInBytes,
quotaUsageInBytes: quotaUsageInBytes,
pinCode: pinCode,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
),
);
}
typedef $$AuthUserEntityTableProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$AuthUserEntityTable,
i1.AuthUserEntityData,
i1.$$AuthUserEntityTableFilterComposer,
i1.$$AuthUserEntityTableOrderingComposer,
i1.$$AuthUserEntityTableAnnotationComposer,
$$AuthUserEntityTableCreateCompanionBuilder,
$$AuthUserEntityTableUpdateCompanionBuilder,
(
i1.AuthUserEntityData,
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$AuthUserEntityTable,
i1.AuthUserEntityData
>,
),
i1.AuthUserEntityData,
i0.PrefetchHooks Function()
>;
class $AuthUserEntityTable extends i3.AuthUserEntity
with i0.TableInfo<$AuthUserEntityTable, i1.AuthUserEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$AuthUserEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
'id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta(
'name',
);
@override
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
'name',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _emailMeta = const i0.VerificationMeta(
'email',
);
@override
late final i0.GeneratedColumn<String> email = i0.GeneratedColumn<String>(
'email',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _isAdminMeta = const i0.VerificationMeta(
'isAdmin',
);
@override
late final i0.GeneratedColumn<bool> isAdmin = i0.GeneratedColumn<bool>(
'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 _hasProfileImageMeta =
const i0.VerificationMeta('hasProfileImage');
@override
late final i0.GeneratedColumn<bool> hasProfileImage =
i0.GeneratedColumn<bool>(
'has_profile_image',
aliasedName,
false,
type: i0.DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'CHECK ("has_profile_image" IN (0, 1))',
),
defaultValue: const i4.Constant(false),
);
static const i0.VerificationMeta _profileChangedAtMeta =
const i0.VerificationMeta('profileChangedAt');
@override
late final i0.GeneratedColumn<DateTime> profileChangedAt =
i0.GeneratedColumn<DateTime>(
'profile_changed_at',
aliasedName,
false,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: i4.currentDateAndTime,
);
@override
late final i0.GeneratedColumnWithTypeConverter<i2.AvatarColor, int>
avatarColor =
i0.GeneratedColumn<int>(
'avatar_color',
aliasedName,
false,
type: i0.DriftSqlType.int,
requiredDuringInsert: true,
).withConverter<i2.AvatarColor>(
i1.$AuthUserEntityTable.$converteravatarColor,
);
static const i0.VerificationMeta _quotaSizeInBytesMeta =
const i0.VerificationMeta('quotaSizeInBytes');
@override
late final i0.GeneratedColumn<int> quotaSizeInBytes = i0.GeneratedColumn<int>(
'quota_size_in_bytes',
aliasedName,
false,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
defaultValue: const i4.Constant(0),
);
static const i0.VerificationMeta _quotaUsageInBytesMeta =
const i0.VerificationMeta('quotaUsageInBytes');
@override
late final i0.GeneratedColumn<int> quotaUsageInBytes =
i0.GeneratedColumn<int>(
'quota_usage_in_bytes',
aliasedName,
false,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
defaultValue: const i4.Constant(0),
);
static const i0.VerificationMeta _pinCodeMeta = const i0.VerificationMeta(
'pinCode',
);
@override
late final i0.GeneratedColumn<String> pinCode = i0.GeneratedColumn<String>(
'pin_code',
aliasedName,
true,
type: i0.DriftSqlType.string,
requiredDuringInsert: false,
);
@override
List<i0.GeneratedColumn> get $columns => [
id,
name,
email,
isAdmin,
hasProfileImage,
profileChangedAt,
avatarColor,
quotaSizeInBytes,
quotaUsageInBytes,
pinCode,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'auth_user_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.AuthUserEntityData> 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('email')) {
context.handle(
_emailMeta,
email.isAcceptableOrUnknown(data['email']!, _emailMeta),
);
} else if (isInserting) {
context.missing(_emailMeta);
}
if (data.containsKey('is_admin')) {
context.handle(
_isAdminMeta,
isAdmin.isAcceptableOrUnknown(data['is_admin']!, _isAdminMeta),
);
}
if (data.containsKey('has_profile_image')) {
context.handle(
_hasProfileImageMeta,
hasProfileImage.isAcceptableOrUnknown(
data['has_profile_image']!,
_hasProfileImageMeta,
),
);
}
if (data.containsKey('profile_changed_at')) {
context.handle(
_profileChangedAtMeta,
profileChangedAt.isAcceptableOrUnknown(
data['profile_changed_at']!,
_profileChangedAtMeta,
),
);
}
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,
),
);
}
if (data.containsKey('pin_code')) {
context.handle(
_pinCodeMeta,
pinCode.isAcceptableOrUnknown(data['pin_code']!, _pinCodeMeta),
);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.AuthUserEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.AuthUserEntityData(
id: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}id'],
)!,
name: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}name'],
)!,
email: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}email'],
)!,
isAdmin: attachedDatabase.typeMapping.read(
i0.DriftSqlType.bool,
data['${effectivePrefix}is_admin'],
)!,
hasProfileImage: attachedDatabase.typeMapping.read(
i0.DriftSqlType.bool,
data['${effectivePrefix}has_profile_image'],
)!,
profileChangedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime,
data['${effectivePrefix}profile_changed_at'],
)!,
avatarColor: i1.$AuthUserEntityTable.$converteravatarColor.fromSql(
attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}avatar_color'],
)!,
),
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'],
)!,
pinCode: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}pin_code'],
),
);
}
@override
$AuthUserEntityTable createAlias(String alias) {
return $AuthUserEntityTable(attachedDatabase, alias);
}
static i0.JsonTypeConverter2<i2.AvatarColor, int, int> $converteravatarColor =
const i0.EnumIndexConverter<i2.AvatarColor>(i2.AvatarColor.values);
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class AuthUserEntityData extends i0.DataClass
implements i0.Insertable<i1.AuthUserEntityData> {
final String id;
final String name;
final String email;
final bool isAdmin;
final bool hasProfileImage;
final DateTime profileChangedAt;
final i2.AvatarColor avatarColor;
final int quotaSizeInBytes;
final int quotaUsageInBytes;
final String? pinCode;
const AuthUserEntityData({
required this.id,
required this.name,
required this.email,
required this.isAdmin,
required this.hasProfileImage,
required this.profileChangedAt,
required this.avatarColor,
required this.quotaSizeInBytes,
required this.quotaUsageInBytes,
this.pinCode,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<String>(id);
map['name'] = i0.Variable<String>(name);
map['email'] = i0.Variable<String>(email);
map['is_admin'] = i0.Variable<bool>(isAdmin);
map['has_profile_image'] = i0.Variable<bool>(hasProfileImage);
map['profile_changed_at'] = i0.Variable<DateTime>(profileChangedAt);
{
map['avatar_color'] = i0.Variable<int>(
i1.$AuthUserEntityTable.$converteravatarColor.toSql(avatarColor),
);
}
map['quota_size_in_bytes'] = i0.Variable<int>(quotaSizeInBytes);
map['quota_usage_in_bytes'] = i0.Variable<int>(quotaUsageInBytes);
if (!nullToAbsent || pinCode != null) {
map['pin_code'] = i0.Variable<String>(pinCode);
}
return map;
}
factory AuthUserEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return AuthUserEntityData(
id: serializer.fromJson<String>(json['id']),
name: serializer.fromJson<String>(json['name']),
email: serializer.fromJson<String>(json['email']),
isAdmin: serializer.fromJson<bool>(json['isAdmin']),
hasProfileImage: serializer.fromJson<bool>(json['hasProfileImage']),
profileChangedAt: serializer.fromJson<DateTime>(json['profileChangedAt']),
avatarColor: i1.$AuthUserEntityTable.$converteravatarColor.fromJson(
serializer.fromJson<int>(json['avatarColor']),
),
quotaSizeInBytes: serializer.fromJson<int>(json['quotaSizeInBytes']),
quotaUsageInBytes: serializer.fromJson<int>(json['quotaUsageInBytes']),
pinCode: serializer.fromJson<String?>(json['pinCode']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<String>(id),
'name': serializer.toJson<String>(name),
'email': serializer.toJson<String>(email),
'isAdmin': serializer.toJson<bool>(isAdmin),
'hasProfileImage': serializer.toJson<bool>(hasProfileImage),
'profileChangedAt': serializer.toJson<DateTime>(profileChangedAt),
'avatarColor': serializer.toJson<int>(
i1.$AuthUserEntityTable.$converteravatarColor.toJson(avatarColor),
),
'quotaSizeInBytes': serializer.toJson<int>(quotaSizeInBytes),
'quotaUsageInBytes': serializer.toJson<int>(quotaUsageInBytes),
'pinCode': serializer.toJson<String?>(pinCode),
};
}
i1.AuthUserEntityData copyWith({
String? id,
String? name,
String? email,
bool? isAdmin,
bool? hasProfileImage,
DateTime? profileChangedAt,
i2.AvatarColor? avatarColor,
int? quotaSizeInBytes,
int? quotaUsageInBytes,
i0.Value<String?> pinCode = const i0.Value.absent(),
}) => i1.AuthUserEntityData(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
isAdmin: isAdmin ?? this.isAdmin,
hasProfileImage: hasProfileImage ?? this.hasProfileImage,
profileChangedAt: profileChangedAt ?? this.profileChangedAt,
avatarColor: avatarColor ?? this.avatarColor,
quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes,
quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes,
pinCode: pinCode.present ? pinCode.value : this.pinCode,
);
AuthUserEntityData copyWithCompanion(i1.AuthUserEntityCompanion data) {
return AuthUserEntityData(
id: data.id.present ? data.id.value : this.id,
name: data.name.present ? data.name.value : this.name,
email: data.email.present ? data.email.value : this.email,
isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin,
hasProfileImage: data.hasProfileImage.present
? data.hasProfileImage.value
: this.hasProfileImage,
profileChangedAt: data.profileChangedAt.present
? data.profileChangedAt.value
: this.profileChangedAt,
avatarColor: data.avatarColor.present
? data.avatarColor.value
: this.avatarColor,
quotaSizeInBytes: data.quotaSizeInBytes.present
? data.quotaSizeInBytes.value
: this.quotaSizeInBytes,
quotaUsageInBytes: data.quotaUsageInBytes.present
? data.quotaUsageInBytes.value
: this.quotaUsageInBytes,
pinCode: data.pinCode.present ? data.pinCode.value : this.pinCode,
);
}
@override
String toString() {
return (StringBuffer('AuthUserEntityData(')
..write('id: $id, ')
..write('name: $name, ')
..write('email: $email, ')
..write('isAdmin: $isAdmin, ')
..write('hasProfileImage: $hasProfileImage, ')
..write('profileChangedAt: $profileChangedAt, ')
..write('avatarColor: $avatarColor, ')
..write('quotaSizeInBytes: $quotaSizeInBytes, ')
..write('quotaUsageInBytes: $quotaUsageInBytes, ')
..write('pinCode: $pinCode')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(
id,
name,
email,
isAdmin,
hasProfileImage,
profileChangedAt,
avatarColor,
quotaSizeInBytes,
quotaUsageInBytes,
pinCode,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.AuthUserEntityData &&
other.id == this.id &&
other.name == this.name &&
other.email == this.email &&
other.isAdmin == this.isAdmin &&
other.hasProfileImage == this.hasProfileImage &&
other.profileChangedAt == this.profileChangedAt &&
other.avatarColor == this.avatarColor &&
other.quotaSizeInBytes == this.quotaSizeInBytes &&
other.quotaUsageInBytes == this.quotaUsageInBytes &&
other.pinCode == this.pinCode);
}
class AuthUserEntityCompanion
extends i0.UpdateCompanion<i1.AuthUserEntityData> {
final i0.Value<String> id;
final i0.Value<String> name;
final i0.Value<String> email;
final i0.Value<bool> isAdmin;
final i0.Value<bool> hasProfileImage;
final i0.Value<DateTime> profileChangedAt;
final i0.Value<i2.AvatarColor> avatarColor;
final i0.Value<int> quotaSizeInBytes;
final i0.Value<int> quotaUsageInBytes;
final i0.Value<String?> pinCode;
const AuthUserEntityCompanion({
this.id = const i0.Value.absent(),
this.name = const i0.Value.absent(),
this.email = const i0.Value.absent(),
this.isAdmin = const i0.Value.absent(),
this.hasProfileImage = const i0.Value.absent(),
this.profileChangedAt = const i0.Value.absent(),
this.avatarColor = const i0.Value.absent(),
this.quotaSizeInBytes = const i0.Value.absent(),
this.quotaUsageInBytes = const i0.Value.absent(),
this.pinCode = const i0.Value.absent(),
});
AuthUserEntityCompanion.insert({
required String id,
required String name,
required String email,
this.isAdmin = const i0.Value.absent(),
this.hasProfileImage = const i0.Value.absent(),
this.profileChangedAt = const i0.Value.absent(),
required i2.AvatarColor avatarColor,
this.quotaSizeInBytes = const i0.Value.absent(),
this.quotaUsageInBytes = const i0.Value.absent(),
this.pinCode = const i0.Value.absent(),
}) : id = i0.Value(id),
name = i0.Value(name),
email = i0.Value(email),
avatarColor = i0.Value(avatarColor);
static i0.Insertable<i1.AuthUserEntityData> custom({
i0.Expression<String>? id,
i0.Expression<String>? name,
i0.Expression<String>? email,
i0.Expression<bool>? isAdmin,
i0.Expression<bool>? hasProfileImage,
i0.Expression<DateTime>? profileChangedAt,
i0.Expression<int>? avatarColor,
i0.Expression<int>? quotaSizeInBytes,
i0.Expression<int>? quotaUsageInBytes,
i0.Expression<String>? pinCode,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (name != null) 'name': name,
if (email != null) 'email': email,
if (isAdmin != null) 'is_admin': isAdmin,
if (hasProfileImage != null) 'has_profile_image': hasProfileImage,
if (profileChangedAt != null) 'profile_changed_at': profileChangedAt,
if (avatarColor != null) 'avatar_color': avatarColor,
if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes,
if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes,
if (pinCode != null) 'pin_code': pinCode,
});
}
i1.AuthUserEntityCompanion copyWith({
i0.Value<String>? id,
i0.Value<String>? name,
i0.Value<String>? email,
i0.Value<bool>? isAdmin,
i0.Value<bool>? hasProfileImage,
i0.Value<DateTime>? profileChangedAt,
i0.Value<i2.AvatarColor>? avatarColor,
i0.Value<int>? quotaSizeInBytes,
i0.Value<int>? quotaUsageInBytes,
i0.Value<String?>? pinCode,
}) {
return i1.AuthUserEntityCompanion(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
isAdmin: isAdmin ?? this.isAdmin,
hasProfileImage: hasProfileImage ?? this.hasProfileImage,
profileChangedAt: profileChangedAt ?? this.profileChangedAt,
avatarColor: avatarColor ?? this.avatarColor,
quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes,
quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes,
pinCode: pinCode ?? this.pinCode,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<String>(id.value);
}
if (name.present) {
map['name'] = i0.Variable<String>(name.value);
}
if (email.present) {
map['email'] = i0.Variable<String>(email.value);
}
if (isAdmin.present) {
map['is_admin'] = i0.Variable<bool>(isAdmin.value);
}
if (hasProfileImage.present) {
map['has_profile_image'] = i0.Variable<bool>(hasProfileImage.value);
}
if (profileChangedAt.present) {
map['profile_changed_at'] = i0.Variable<DateTime>(profileChangedAt.value);
}
if (avatarColor.present) {
map['avatar_color'] = i0.Variable<int>(
i1.$AuthUserEntityTable.$converteravatarColor.toSql(avatarColor.value),
);
}
if (quotaSizeInBytes.present) {
map['quota_size_in_bytes'] = i0.Variable<int>(quotaSizeInBytes.value);
}
if (quotaUsageInBytes.present) {
map['quota_usage_in_bytes'] = i0.Variable<int>(quotaUsageInBytes.value);
}
if (pinCode.present) {
map['pin_code'] = i0.Variable<String>(pinCode.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('AuthUserEntityCompanion(')
..write('id: $id, ')
..write('name: $name, ')
..write('email: $email, ')
..write('isAdmin: $isAdmin, ')
..write('hasProfileImage: $hasProfileImage, ')
..write('profileChangedAt: $profileChangedAt, ')
..write('avatarColor: $avatarColor, ')
..write('quotaSizeInBytes: $quotaSizeInBytes, ')
..write('quotaUsageInBytes: $quotaUsageInBytes, ')
..write('pinCode: $pinCode')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,25 @@
import 'dart:typed_data';
import 'package:immich_mobile/domain/models/device_asset.model.dart';
import 'package:immich_mobile/utils/hash.dart';
import 'package:isar/isar.dart';
part 'device_asset.entity.g.dart';
@Collection(inheritance: false)
class DeviceAssetEntity {
Id get id => fastHash(assetId);
@Index(replace: true, unique: true, type: IndexType.hash)
final String assetId;
@Index(unique: false, type: IndexType.hash)
final List<byte> hash;
final DateTime modifiedTime;
const DeviceAssetEntity({required this.assetId, required this.hash, required this.modifiedTime});
DeviceAsset toModel() => DeviceAsset(assetId: assetId, hash: Uint8List.fromList(hash), modifiedTime: modifiedTime);
static DeviceAssetEntity fromDto(DeviceAsset dto) =>
DeviceAssetEntity(assetId: dto.assetId, hash: dto.hash, modifiedTime: dto.modifiedTime);
}

View file

@ -0,0 +1,874 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'device_asset.entity.dart';
// **************************************************************************
// IsarCollectionGenerator
// **************************************************************************
// coverage:ignore-file
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
extension GetDeviceAssetEntityCollection on Isar {
IsarCollection<DeviceAssetEntity> get deviceAssetEntitys => this.collection();
}
const DeviceAssetEntitySchema = CollectionSchema(
name: r'DeviceAssetEntity',
id: 6967030785073446271,
properties: {
r'assetId': PropertySchema(id: 0, name: r'assetId', type: IsarType.string),
r'hash': PropertySchema(id: 1, name: r'hash', type: IsarType.byteList),
r'modifiedTime': PropertySchema(
id: 2,
name: r'modifiedTime',
type: IsarType.dateTime,
),
},
estimateSize: _deviceAssetEntityEstimateSize,
serialize: _deviceAssetEntitySerialize,
deserialize: _deviceAssetEntityDeserialize,
deserializeProp: _deviceAssetEntityDeserializeProp,
idName: r'id',
indexes: {
r'assetId': IndexSchema(
id: 174362542210192109,
name: r'assetId',
unique: true,
replace: true,
properties: [
IndexPropertySchema(
name: r'assetId',
type: IndexType.hash,
caseSensitive: true,
),
],
),
r'hash': IndexSchema(
id: -7973251393006690288,
name: r'hash',
unique: false,
replace: false,
properties: [
IndexPropertySchema(
name: r'hash',
type: IndexType.hash,
caseSensitive: false,
),
],
),
},
links: {},
embeddedSchemas: {},
getId: _deviceAssetEntityGetId,
getLinks: _deviceAssetEntityGetLinks,
attach: _deviceAssetEntityAttach,
version: '3.3.0-dev.3',
);
int _deviceAssetEntityEstimateSize(
DeviceAssetEntity object,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
var bytesCount = offsets.last;
bytesCount += 3 + object.assetId.length * 3;
bytesCount += 3 + object.hash.length;
return bytesCount;
}
void _deviceAssetEntitySerialize(
DeviceAssetEntity object,
IsarWriter writer,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
writer.writeString(offsets[0], object.assetId);
writer.writeByteList(offsets[1], object.hash);
writer.writeDateTime(offsets[2], object.modifiedTime);
}
DeviceAssetEntity _deviceAssetEntityDeserialize(
Id id,
IsarReader reader,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = DeviceAssetEntity(
assetId: reader.readString(offsets[0]),
hash: reader.readByteList(offsets[1]) ?? [],
modifiedTime: reader.readDateTime(offsets[2]),
);
return object;
}
P _deviceAssetEntityDeserializeProp<P>(
IsarReader reader,
int propertyId,
int offset,
Map<Type, List<int>> allOffsets,
) {
switch (propertyId) {
case 0:
return (reader.readString(offset)) as P;
case 1:
return (reader.readByteList(offset) ?? []) as P;
case 2:
return (reader.readDateTime(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
}
Id _deviceAssetEntityGetId(DeviceAssetEntity object) {
return object.id;
}
List<IsarLinkBase<dynamic>> _deviceAssetEntityGetLinks(
DeviceAssetEntity object,
) {
return [];
}
void _deviceAssetEntityAttach(
IsarCollection<dynamic> col,
Id id,
DeviceAssetEntity object,
) {}
extension DeviceAssetEntityByIndex on IsarCollection<DeviceAssetEntity> {
Future<DeviceAssetEntity?> getByAssetId(String assetId) {
return getByIndex(r'assetId', [assetId]);
}
DeviceAssetEntity? getByAssetIdSync(String assetId) {
return getByIndexSync(r'assetId', [assetId]);
}
Future<bool> deleteByAssetId(String assetId) {
return deleteByIndex(r'assetId', [assetId]);
}
bool deleteByAssetIdSync(String assetId) {
return deleteByIndexSync(r'assetId', [assetId]);
}
Future<List<DeviceAssetEntity?>> getAllByAssetId(List<String> assetIdValues) {
final values = assetIdValues.map((e) => [e]).toList();
return getAllByIndex(r'assetId', values);
}
List<DeviceAssetEntity?> getAllByAssetIdSync(List<String> assetIdValues) {
final values = assetIdValues.map((e) => [e]).toList();
return getAllByIndexSync(r'assetId', values);
}
Future<int> deleteAllByAssetId(List<String> assetIdValues) {
final values = assetIdValues.map((e) => [e]).toList();
return deleteAllByIndex(r'assetId', values);
}
int deleteAllByAssetIdSync(List<String> assetIdValues) {
final values = assetIdValues.map((e) => [e]).toList();
return deleteAllByIndexSync(r'assetId', values);
}
Future<Id> putByAssetId(DeviceAssetEntity object) {
return putByIndex(r'assetId', object);
}
Id putByAssetIdSync(DeviceAssetEntity object, {bool saveLinks = true}) {
return putByIndexSync(r'assetId', object, saveLinks: saveLinks);
}
Future<List<Id>> putAllByAssetId(List<DeviceAssetEntity> objects) {
return putAllByIndex(r'assetId', objects);
}
List<Id> putAllByAssetIdSync(
List<DeviceAssetEntity> objects, {
bool saveLinks = true,
}) {
return putAllByIndexSync(r'assetId', objects, saveLinks: saveLinks);
}
}
extension DeviceAssetEntityQueryWhereSort
on QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QWhere> {
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterWhere> anyId() {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(const IdWhereClause.any());
});
}
}
extension DeviceAssetEntityQueryWhere
on QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QWhereClause> {
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterWhereClause>
idEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(lower: id, upper: id));
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterWhereClause>
idNotEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
)
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
);
} else {
return query
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
)
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
);
}
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterWhereClause>
idGreaterThan(Id id, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: include),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterWhereClause>
idLessThan(Id id, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: include),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterWhereClause>
idBetween(
Id lowerId,
Id upperId, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.between(
lower: lowerId,
includeLower: includeLower,
upper: upperId,
includeUpper: includeUpper,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterWhereClause>
assetIdEqualTo(String assetId) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IndexWhereClause.equalTo(indexName: r'assetId', value: [assetId]),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterWhereClause>
assetIdNotEqualTo(String assetId) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(
IndexWhereClause.between(
indexName: r'assetId',
lower: [],
upper: [assetId],
includeUpper: false,
),
)
.addWhereClause(
IndexWhereClause.between(
indexName: r'assetId',
lower: [assetId],
includeLower: false,
upper: [],
),
);
} else {
return query
.addWhereClause(
IndexWhereClause.between(
indexName: r'assetId',
lower: [assetId],
includeLower: false,
upper: [],
),
)
.addWhereClause(
IndexWhereClause.between(
indexName: r'assetId',
lower: [],
upper: [assetId],
includeUpper: false,
),
);
}
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterWhereClause>
hashEqualTo(List<int> hash) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IndexWhereClause.equalTo(indexName: r'hash', value: [hash]),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterWhereClause>
hashNotEqualTo(List<int> hash) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(
IndexWhereClause.between(
indexName: r'hash',
lower: [],
upper: [hash],
includeUpper: false,
),
)
.addWhereClause(
IndexWhereClause.between(
indexName: r'hash',
lower: [hash],
includeLower: false,
upper: [],
),
);
} else {
return query
.addWhereClause(
IndexWhereClause.between(
indexName: r'hash',
lower: [hash],
includeLower: false,
upper: [],
),
)
.addWhereClause(
IndexWhereClause.between(
indexName: r'hash',
lower: [],
upper: [hash],
includeUpper: false,
),
);
}
});
}
}
extension DeviceAssetEntityQueryFilter
on QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QFilterCondition> {
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
assetIdEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.equalTo(
property: r'assetId',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
assetIdGreaterThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.greaterThan(
include: include,
property: r'assetId',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
assetIdLessThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.lessThan(
include: include,
property: r'assetId',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
assetIdBetween(
String lower,
String upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.between(
property: r'assetId',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
assetIdStartsWith(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.startsWith(
property: r'assetId',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
assetIdEndsWith(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.endsWith(
property: r'assetId',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
assetIdContains(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.contains(
property: r'assetId',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
assetIdMatches(String pattern, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.matches(
property: r'assetId',
wildcard: pattern,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
assetIdIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.equalTo(property: r'assetId', value: ''),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
assetIdIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.greaterThan(property: r'assetId', value: ''),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
hashElementEqualTo(int value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.equalTo(property: r'hash', value: value),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
hashElementGreaterThan(int value, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.greaterThan(
include: include,
property: r'hash',
value: value,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
hashElementLessThan(int value, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.lessThan(
include: include,
property: r'hash',
value: value,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
hashElementBetween(
int lower,
int upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.between(
property: r'hash',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
hashLengthEqualTo(int length) {
return QueryBuilder.apply(this, (query) {
return query.listLength(r'hash', length, true, length, true);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
hashIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.listLength(r'hash', 0, true, 0, true);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
hashIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.listLength(r'hash', 0, false, 999999, true);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
hashLengthLessThan(int length, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.listLength(r'hash', 0, true, length, include);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
hashLengthGreaterThan(int length, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.listLength(r'hash', length, include, 999999, true);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
hashLengthBetween(
int lower,
int upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.listLength(
r'hash',
lower,
includeLower,
upper,
includeUpper,
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
idEqualTo(Id value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.equalTo(property: r'id', value: value),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
idGreaterThan(Id value, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.greaterThan(
include: include,
property: r'id',
value: value,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
idLessThan(Id value, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.lessThan(
include: include,
property: r'id',
value: value,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
idBetween(
Id lower,
Id upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.between(
property: r'id',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
modifiedTimeEqualTo(DateTime value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.equalTo(property: r'modifiedTime', value: value),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
modifiedTimeGreaterThan(DateTime value, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.greaterThan(
include: include,
property: r'modifiedTime',
value: value,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
modifiedTimeLessThan(DateTime value, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.lessThan(
include: include,
property: r'modifiedTime',
value: value,
),
);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterFilterCondition>
modifiedTimeBetween(
DateTime lower,
DateTime upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.between(
property: r'modifiedTime',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
),
);
});
}
}
extension DeviceAssetEntityQueryObject
on QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QFilterCondition> {}
extension DeviceAssetEntityQueryLinks
on QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QFilterCondition> {}
extension DeviceAssetEntityQuerySortBy
on QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QSortBy> {
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterSortBy>
sortByAssetId() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'assetId', Sort.asc);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterSortBy>
sortByAssetIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'assetId', Sort.desc);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterSortBy>
sortByModifiedTime() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'modifiedTime', Sort.asc);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterSortBy>
sortByModifiedTimeDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'modifiedTime', Sort.desc);
});
}
}
extension DeviceAssetEntityQuerySortThenBy
on QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QSortThenBy> {
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterSortBy>
thenByAssetId() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'assetId', Sort.asc);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterSortBy>
thenByAssetIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'assetId', Sort.desc);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterSortBy> thenById() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.asc);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterSortBy>
thenByIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.desc);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterSortBy>
thenByModifiedTime() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'modifiedTime', Sort.asc);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QAfterSortBy>
thenByModifiedTimeDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'modifiedTime', Sort.desc);
});
}
}
extension DeviceAssetEntityQueryWhereDistinct
on QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QDistinct> {
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QDistinct>
distinctByAssetId({bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'assetId', caseSensitive: caseSensitive);
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QDistinct>
distinctByHash() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'hash');
});
}
QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QDistinct>
distinctByModifiedTime() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'modifiedTime');
});
}
}
extension DeviceAssetEntityQueryProperty
on QueryBuilder<DeviceAssetEntity, DeviceAssetEntity, QQueryProperty> {
QueryBuilder<DeviceAssetEntity, int, QQueryOperations> idProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'id');
});
}
QueryBuilder<DeviceAssetEntity, String, QQueryOperations> assetIdProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'assetId');
});
}
QueryBuilder<DeviceAssetEntity, List<int>, QQueryOperations> hashProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'hash');
});
}
QueryBuilder<DeviceAssetEntity, DateTime, QQueryOperations>
modifiedTimeProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'modifiedTime');
});
}
}

View file

@ -0,0 +1,172 @@
import 'package:drift/drift.dart' hide Query;
import 'package:immich_mobile/domain/models/exif.model.dart' as domain;
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
import 'package:isar/isar.dart';
part 'exif.entity.g.dart';
/// Exif information 1:1 relation with Asset
@Collection(inheritance: false)
class ExifInfo {
final Id? id;
final int? fileSize;
final DateTime? dateTimeOriginal;
final String? timeZone;
final String? make;
final String? model;
final String? lens;
final float? f;
final float? mm;
final short? iso;
final float? exposureSeconds;
final float? lat;
final float? long;
final String? city;
final String? state;
final String? country;
final String? description;
final String? orientation;
const ExifInfo({
this.id,
this.fileSize,
this.dateTimeOriginal,
this.timeZone,
this.make,
this.model,
this.lens,
this.f,
this.mm,
this.iso,
this.exposureSeconds,
this.lat,
this.long,
this.city,
this.state,
this.country,
this.description,
this.orientation,
});
static ExifInfo fromDto(domain.ExifInfo dto) => ExifInfo(
id: dto.assetId,
fileSize: dto.fileSize,
dateTimeOriginal: dto.dateTimeOriginal,
timeZone: dto.timeZone,
make: dto.make,
model: dto.model,
lens: dto.lens,
f: dto.f,
mm: dto.mm,
iso: dto.iso?.toInt(),
exposureSeconds: dto.exposureSeconds,
lat: dto.latitude,
long: dto.longitude,
city: dto.city,
state: dto.state,
country: dto.country,
description: dto.description,
orientation: dto.orientation,
);
domain.ExifInfo toDto() => domain.ExifInfo(
assetId: id,
fileSize: fileSize,
description: description,
orientation: orientation,
timeZone: timeZone,
dateTimeOriginal: dateTimeOriginal,
isFlipped: ExifDtoConverter.isOrientationFlipped(orientation),
latitude: lat,
longitude: long,
city: city,
state: state,
country: country,
make: make,
model: model,
lens: lens,
f: f,
mm: mm,
iso: iso?.toInt(),
exposureSeconds: exposureSeconds,
);
}
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)')
class RemoteExifEntity extends Table with DriftDefaultsMixin {
const RemoteExifEntity();
TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get city => text().nullable()();
TextColumn get state => text().nullable()();
TextColumn get country => text().nullable()();
DateTimeColumn get dateTimeOriginal => dateTime().nullable()();
TextColumn get description => text().nullable()();
IntColumn get height => integer().nullable()();
IntColumn get width => integer().nullable()();
TextColumn get exposureTime => text().nullable()();
RealColumn get fNumber => real().nullable()();
IntColumn get fileSize => integer().nullable()();
RealColumn get focalLength => real().nullable()();
RealColumn get latitude => real().nullable()();
RealColumn get longitude => real().nullable()();
IntColumn get iso => integer().nullable()();
TextColumn get make => text().nullable()();
TextColumn get model => text().nullable()();
TextColumn get lens => text().nullable()();
TextColumn get orientation => text().nullable()();
TextColumn get timeZone => text().nullable()();
IntColumn get rating => integer().nullable()();
TextColumn get projectionType => text().nullable()();
@override
Set<Column> get primaryKey => {assetId};
}
extension RemoteExifEntityDataDomainEx on RemoteExifEntityData {
domain.ExifInfo toDto() => domain.ExifInfo(
fileSize: fileSize,
dateTimeOriginal: dateTimeOriginal,
rating: rating,
timeZone: timeZone,
make: make,
model: model,
iso: iso,
city: city,
state: state,
country: country,
description: description,
orientation: orientation,
latitude: latitude,
longitude: longitude,
f: fNumber?.toDouble(),
mm: focalLength?.toDouble(),
lens: lens,
isFlipped: ExifDtoConverter.isOrientationFlipped(orientation),
exposureSeconds: ExifDtoConverter.exposureTimeToSeconds(exposureTime),
);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class LocalAlbumEntity extends Table with DriftDefaultsMixin {
const LocalAlbumEntity();
TextColumn get id => text()();
TextColumn get name => text()();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
IntColumn get backupSelection => intEnum<BackupSelection>()();
BoolColumn get isIosSharedAlbum => boolean().withDefault(const Constant(false))();
// // Linked album for putting assets to the remote album after finished uploading
TextColumn get linkedRemoteAlbumId =>
text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.setNull).nullable()();
// Used for mark & sweep
BoolColumn get marker_ => boolean().nullable()();
@override
Set<Column> get primaryKey => {id};
}
extension LocalAlbumEntityDataHelper on LocalAlbumEntityData {
LocalAlbum toDto({int assetCount = 0}) {
return LocalAlbum(
id: id,
name: name,
updatedAt: updatedAt,
assetCount: assetCount,
backupSelection: backupSelection,
linkedRemoteAlbumId: linkedRemoteAlbumId,
isIosSharedAlbum: isIosSharedAlbum,
);
}
}

View file

@ -0,0 +1,904 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
as i1;
import 'package:immich_mobile/domain/models/album/local_album.model.dart' as i2;
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'
as i3;
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
as i5;
import 'package:drift/internal/modular.dart' as i6;
typedef $$LocalAlbumEntityTableCreateCompanionBuilder =
i1.LocalAlbumEntityCompanion Function({
required String id,
required String name,
i0.Value<DateTime> updatedAt,
required i2.BackupSelection backupSelection,
i0.Value<bool> isIosSharedAlbum,
i0.Value<String?> linkedRemoteAlbumId,
i0.Value<bool?> marker_,
});
typedef $$LocalAlbumEntityTableUpdateCompanionBuilder =
i1.LocalAlbumEntityCompanion Function({
i0.Value<String> id,
i0.Value<String> name,
i0.Value<DateTime> updatedAt,
i0.Value<i2.BackupSelection> backupSelection,
i0.Value<bool> isIosSharedAlbum,
i0.Value<String?> linkedRemoteAlbumId,
i0.Value<bool?> marker_,
});
final class $$LocalAlbumEntityTableReferences
extends
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$LocalAlbumEntityTable,
i1.LocalAlbumEntityData
> {
$$LocalAlbumEntityTableReferences(
super.$_db,
super.$_table,
super.$_typedResult,
);
static i5.$RemoteAlbumEntityTable _linkedRemoteAlbumIdTable(
i0.GeneratedDatabase db,
) => i6.ReadDatabaseContainer(db)
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
.createAlias(
i0.$_aliasNameGenerator(
i6.ReadDatabaseContainer(db)
.resultSet<i1.$LocalAlbumEntityTable>('local_album_entity')
.linkedRemoteAlbumId,
i6.ReadDatabaseContainer(
db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity').id,
),
);
i5.$$RemoteAlbumEntityTableProcessedTableManager? get linkedRemoteAlbumId {
final $_column = $_itemColumn<String>('linked_remote_album_id');
if ($_column == null) return null;
final manager = i5
.$$RemoteAlbumEntityTableTableManager(
$_db,
i6.ReadDatabaseContainer(
$_db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_linkedRemoteAlbumIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]),
);
}
}
class $$LocalAlbumEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable> {
$$LocalAlbumEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get name => $composableBuilder(
column: $table.name,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnWithTypeConverterFilters<i2.BackupSelection, i2.BackupSelection, int>
get backupSelection => $composableBuilder(
column: $table.backupSelection,
builder: (column) => i0.ColumnWithTypeConverterFilters(column),
);
i0.ColumnFilters<bool> get isIosSharedAlbum => $composableBuilder(
column: $table.isIosSharedAlbum,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<bool> get marker_ => $composableBuilder(
column: $table.marker_,
builder: (column) => i0.ColumnFilters(column),
);
i5.$$RemoteAlbumEntityTableFilterComposer get linkedRemoteAlbumId {
final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.linkedRemoteAlbumId,
referencedTable: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$RemoteAlbumEntityTableFilterComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$LocalAlbumEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable> {
$$LocalAlbumEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get name => $composableBuilder(
column: $table.name,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<int> get backupSelection => $composableBuilder(
column: $table.backupSelection,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<bool> get isIosSharedAlbum => $composableBuilder(
column: $table.isIosSharedAlbum,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<bool> get marker_ => $composableBuilder(
column: $table.marker_,
builder: (column) => i0.ColumnOrderings(column),
);
i5.$$RemoteAlbumEntityTableOrderingComposer get linkedRemoteAlbumId {
final i5.$$RemoteAlbumEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.linkedRemoteAlbumId,
referencedTable: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$RemoteAlbumEntityTableOrderingComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$LocalAlbumEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable> {
$$LocalAlbumEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<String> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
i0.GeneratedColumn<String> get name =>
$composableBuilder(column: $table.name, builder: (column) => column);
i0.GeneratedColumn<DateTime> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
i0.GeneratedColumnWithTypeConverter<i2.BackupSelection, int>
get backupSelection => $composableBuilder(
column: $table.backupSelection,
builder: (column) => column,
);
i0.GeneratedColumn<bool> get isIosSharedAlbum => $composableBuilder(
column: $table.isIosSharedAlbum,
builder: (column) => column,
);
i0.GeneratedColumn<bool> get marker_ =>
$composableBuilder(column: $table.marker_, builder: (column) => column);
i5.$$RemoteAlbumEntityTableAnnotationComposer get linkedRemoteAlbumId {
final i5.$$RemoteAlbumEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.linkedRemoteAlbumId,
referencedTable: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$RemoteAlbumEntityTableAnnotationComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$LocalAlbumEntityTableTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.$LocalAlbumEntityTable,
i1.LocalAlbumEntityData,
i1.$$LocalAlbumEntityTableFilterComposer,
i1.$$LocalAlbumEntityTableOrderingComposer,
i1.$$LocalAlbumEntityTableAnnotationComposer,
$$LocalAlbumEntityTableCreateCompanionBuilder,
$$LocalAlbumEntityTableUpdateCompanionBuilder,
(i1.LocalAlbumEntityData, i1.$$LocalAlbumEntityTableReferences),
i1.LocalAlbumEntityData,
i0.PrefetchHooks Function({bool linkedRemoteAlbumId})
> {
$$LocalAlbumEntityTableTableManager(
i0.GeneratedDatabase db,
i1.$LocalAlbumEntityTable table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$LocalAlbumEntityTableFilterComposer($db: db, $table: table),
createOrderingComposer: () => i1
.$$LocalAlbumEntityTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
i1.$$LocalAlbumEntityTableAnnotationComposer(
$db: db,
$table: table,
),
updateCompanionCallback:
({
i0.Value<String> id = const i0.Value.absent(),
i0.Value<String> name = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<i2.BackupSelection> backupSelection =
const i0.Value.absent(),
i0.Value<bool> isIosSharedAlbum = const i0.Value.absent(),
i0.Value<String?> linkedRemoteAlbumId = const i0.Value.absent(),
i0.Value<bool?> marker_ = const i0.Value.absent(),
}) => i1.LocalAlbumEntityCompanion(
id: id,
name: name,
updatedAt: updatedAt,
backupSelection: backupSelection,
isIosSharedAlbum: isIosSharedAlbum,
linkedRemoteAlbumId: linkedRemoteAlbumId,
marker_: marker_,
),
createCompanionCallback:
({
required String id,
required String name,
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
required i2.BackupSelection backupSelection,
i0.Value<bool> isIosSharedAlbum = const i0.Value.absent(),
i0.Value<String?> linkedRemoteAlbumId = const i0.Value.absent(),
i0.Value<bool?> marker_ = const i0.Value.absent(),
}) => i1.LocalAlbumEntityCompanion.insert(
id: id,
name: name,
updatedAt: updatedAt,
backupSelection: backupSelection,
isIosSharedAlbum: isIosSharedAlbum,
linkedRemoteAlbumId: linkedRemoteAlbumId,
marker_: marker_,
),
withReferenceMapper: (p0) => p0
.map(
(e) => (
e.readTable(table),
i1.$$LocalAlbumEntityTableReferences(db, table, e),
),
)
.toList(),
prefetchHooksCallback: ({linkedRemoteAlbumId = 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 (linkedRemoteAlbumId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.linkedRemoteAlbumId,
referencedTable: i1
.$$LocalAlbumEntityTableReferences
._linkedRemoteAlbumIdTable(db),
referencedColumn: i1
.$$LocalAlbumEntityTableReferences
._linkedRemoteAlbumIdTable(db)
.id,
)
as T;
}
return state;
},
getPrefetchedDataCallback: (items) async {
return [];
},
);
},
),
);
}
typedef $$LocalAlbumEntityTableProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$LocalAlbumEntityTable,
i1.LocalAlbumEntityData,
i1.$$LocalAlbumEntityTableFilterComposer,
i1.$$LocalAlbumEntityTableOrderingComposer,
i1.$$LocalAlbumEntityTableAnnotationComposer,
$$LocalAlbumEntityTableCreateCompanionBuilder,
$$LocalAlbumEntityTableUpdateCompanionBuilder,
(i1.LocalAlbumEntityData, i1.$$LocalAlbumEntityTableReferences),
i1.LocalAlbumEntityData,
i0.PrefetchHooks Function({bool linkedRemoteAlbumId})
>;
class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
with i0.TableInfo<$LocalAlbumEntityTable, i1.LocalAlbumEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$LocalAlbumEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
'id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta(
'name',
);
@override
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
'name',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta(
'updatedAt',
);
@override
late final i0.GeneratedColumn<DateTime> updatedAt =
i0.GeneratedColumn<DateTime>(
'updated_at',
aliasedName,
false,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: i4.currentDateAndTime,
);
@override
late final i0.GeneratedColumnWithTypeConverter<i2.BackupSelection, int>
backupSelection =
i0.GeneratedColumn<int>(
'backup_selection',
aliasedName,
false,
type: i0.DriftSqlType.int,
requiredDuringInsert: true,
).withConverter<i2.BackupSelection>(
i1.$LocalAlbumEntityTable.$converterbackupSelection,
);
static const i0.VerificationMeta _isIosSharedAlbumMeta =
const i0.VerificationMeta('isIosSharedAlbum');
@override
late final i0.GeneratedColumn<bool> isIosSharedAlbum =
i0.GeneratedColumn<bool>(
'is_ios_shared_album',
aliasedName,
false,
type: i0.DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'CHECK ("is_ios_shared_album" IN (0, 1))',
),
defaultValue: const i4.Constant(false),
);
static const i0.VerificationMeta _linkedRemoteAlbumIdMeta =
const i0.VerificationMeta('linkedRemoteAlbumId');
@override
late final i0.GeneratedColumn<String> linkedRemoteAlbumId =
i0.GeneratedColumn<String>(
'linked_remote_album_id',
aliasedName,
true,
type: i0.DriftSqlType.string,
requiredDuringInsert: false,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_album_entity (id) ON DELETE SET NULL',
),
);
static const i0.VerificationMeta _marker_Meta = const i0.VerificationMeta(
'marker_',
);
@override
late final i0.GeneratedColumn<bool> marker_ = i0.GeneratedColumn<bool>(
'marker',
aliasedName,
true,
type: i0.DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'CHECK ("marker" IN (0, 1))',
),
);
@override
List<i0.GeneratedColumn> get $columns => [
id,
name,
updatedAt,
backupSelection,
isIosSharedAlbum,
linkedRemoteAlbumId,
marker_,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'local_album_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.LocalAlbumEntityData> 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('updated_at')) {
context.handle(
_updatedAtMeta,
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta),
);
}
if (data.containsKey('is_ios_shared_album')) {
context.handle(
_isIosSharedAlbumMeta,
isIosSharedAlbum.isAcceptableOrUnknown(
data['is_ios_shared_album']!,
_isIosSharedAlbumMeta,
),
);
}
if (data.containsKey('linked_remote_album_id')) {
context.handle(
_linkedRemoteAlbumIdMeta,
linkedRemoteAlbumId.isAcceptableOrUnknown(
data['linked_remote_album_id']!,
_linkedRemoteAlbumIdMeta,
),
);
}
if (data.containsKey('marker')) {
context.handle(
_marker_Meta,
marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta),
);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.LocalAlbumEntityData map(
Map<String, dynamic> data, {
String? tablePrefix,
}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.LocalAlbumEntityData(
id: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}id'],
)!,
name: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}name'],
)!,
updatedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime,
data['${effectivePrefix}updated_at'],
)!,
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
.fromSql(
attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}backup_selection'],
)!,
),
isIosSharedAlbum: attachedDatabase.typeMapping.read(
i0.DriftSqlType.bool,
data['${effectivePrefix}is_ios_shared_album'],
)!,
linkedRemoteAlbumId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}linked_remote_album_id'],
),
marker_: attachedDatabase.typeMapping.read(
i0.DriftSqlType.bool,
data['${effectivePrefix}marker'],
),
);
}
@override
$LocalAlbumEntityTable createAlias(String alias) {
return $LocalAlbumEntityTable(attachedDatabase, alias);
}
static i0.JsonTypeConverter2<i2.BackupSelection, int, int>
$converterbackupSelection = const i0.EnumIndexConverter<i2.BackupSelection>(
i2.BackupSelection.values,
);
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class LocalAlbumEntityData extends i0.DataClass
implements i0.Insertable<i1.LocalAlbumEntityData> {
final String id;
final String name;
final DateTime updatedAt;
final i2.BackupSelection backupSelection;
final bool isIosSharedAlbum;
final String? linkedRemoteAlbumId;
final bool? marker_;
const LocalAlbumEntityData({
required this.id,
required this.name,
required this.updatedAt,
required this.backupSelection,
required this.isIosSharedAlbum,
this.linkedRemoteAlbumId,
this.marker_,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<String>(id);
map['name'] = i0.Variable<String>(name);
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
{
map['backup_selection'] = i0.Variable<int>(
i1.$LocalAlbumEntityTable.$converterbackupSelection.toSql(
backupSelection,
),
);
}
map['is_ios_shared_album'] = i0.Variable<bool>(isIosSharedAlbum);
if (!nullToAbsent || linkedRemoteAlbumId != null) {
map['linked_remote_album_id'] = i0.Variable<String>(linkedRemoteAlbumId);
}
if (!nullToAbsent || marker_ != null) {
map['marker'] = i0.Variable<bool>(marker_);
}
return map;
}
factory LocalAlbumEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return LocalAlbumEntityData(
id: serializer.fromJson<String>(json['id']),
name: serializer.fromJson<String>(json['name']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
.fromJson(serializer.fromJson<int>(json['backupSelection'])),
isIosSharedAlbum: serializer.fromJson<bool>(json['isIosSharedAlbum']),
linkedRemoteAlbumId: serializer.fromJson<String?>(
json['linkedRemoteAlbumId'],
),
marker_: serializer.fromJson<bool?>(json['marker_']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<String>(id),
'name': serializer.toJson<String>(name),
'updatedAt': serializer.toJson<DateTime>(updatedAt),
'backupSelection': serializer.toJson<int>(
i1.$LocalAlbumEntityTable.$converterbackupSelection.toJson(
backupSelection,
),
),
'isIosSharedAlbum': serializer.toJson<bool>(isIosSharedAlbum),
'linkedRemoteAlbumId': serializer.toJson<String?>(linkedRemoteAlbumId),
'marker_': serializer.toJson<bool?>(marker_),
};
}
i1.LocalAlbumEntityData copyWith({
String? id,
String? name,
DateTime? updatedAt,
i2.BackupSelection? backupSelection,
bool? isIosSharedAlbum,
i0.Value<String?> linkedRemoteAlbumId = const i0.Value.absent(),
i0.Value<bool?> marker_ = const i0.Value.absent(),
}) => i1.LocalAlbumEntityData(
id: id ?? this.id,
name: name ?? this.name,
updatedAt: updatedAt ?? this.updatedAt,
backupSelection: backupSelection ?? this.backupSelection,
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
linkedRemoteAlbumId: linkedRemoteAlbumId.present
? linkedRemoteAlbumId.value
: this.linkedRemoteAlbumId,
marker_: marker_.present ? marker_.value : this.marker_,
);
LocalAlbumEntityData copyWithCompanion(i1.LocalAlbumEntityCompanion data) {
return LocalAlbumEntityData(
id: data.id.present ? data.id.value : this.id,
name: data.name.present ? data.name.value : this.name,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
backupSelection: data.backupSelection.present
? data.backupSelection.value
: this.backupSelection,
isIosSharedAlbum: data.isIosSharedAlbum.present
? data.isIosSharedAlbum.value
: this.isIosSharedAlbum,
linkedRemoteAlbumId: data.linkedRemoteAlbumId.present
? data.linkedRemoteAlbumId.value
: this.linkedRemoteAlbumId,
marker_: data.marker_.present ? data.marker_.value : this.marker_,
);
}
@override
String toString() {
return (StringBuffer('LocalAlbumEntityData(')
..write('id: $id, ')
..write('name: $name, ')
..write('updatedAt: $updatedAt, ')
..write('backupSelection: $backupSelection, ')
..write('isIosSharedAlbum: $isIosSharedAlbum, ')
..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ')
..write('marker_: $marker_')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(
id,
name,
updatedAt,
backupSelection,
isIosSharedAlbum,
linkedRemoteAlbumId,
marker_,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.LocalAlbumEntityData &&
other.id == this.id &&
other.name == this.name &&
other.updatedAt == this.updatedAt &&
other.backupSelection == this.backupSelection &&
other.isIosSharedAlbum == this.isIosSharedAlbum &&
other.linkedRemoteAlbumId == this.linkedRemoteAlbumId &&
other.marker_ == this.marker_);
}
class LocalAlbumEntityCompanion
extends i0.UpdateCompanion<i1.LocalAlbumEntityData> {
final i0.Value<String> id;
final i0.Value<String> name;
final i0.Value<DateTime> updatedAt;
final i0.Value<i2.BackupSelection> backupSelection;
final i0.Value<bool> isIosSharedAlbum;
final i0.Value<String?> linkedRemoteAlbumId;
final i0.Value<bool?> marker_;
const LocalAlbumEntityCompanion({
this.id = const i0.Value.absent(),
this.name = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
this.backupSelection = const i0.Value.absent(),
this.isIosSharedAlbum = const i0.Value.absent(),
this.linkedRemoteAlbumId = const i0.Value.absent(),
this.marker_ = const i0.Value.absent(),
});
LocalAlbumEntityCompanion.insert({
required String id,
required String name,
this.updatedAt = const i0.Value.absent(),
required i2.BackupSelection backupSelection,
this.isIosSharedAlbum = const i0.Value.absent(),
this.linkedRemoteAlbumId = const i0.Value.absent(),
this.marker_ = const i0.Value.absent(),
}) : id = i0.Value(id),
name = i0.Value(name),
backupSelection = i0.Value(backupSelection);
static i0.Insertable<i1.LocalAlbumEntityData> custom({
i0.Expression<String>? id,
i0.Expression<String>? name,
i0.Expression<DateTime>? updatedAt,
i0.Expression<int>? backupSelection,
i0.Expression<bool>? isIosSharedAlbum,
i0.Expression<String>? linkedRemoteAlbumId,
i0.Expression<bool>? marker_,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (name != null) 'name': name,
if (updatedAt != null) 'updated_at': updatedAt,
if (backupSelection != null) 'backup_selection': backupSelection,
if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum,
if (linkedRemoteAlbumId != null)
'linked_remote_album_id': linkedRemoteAlbumId,
if (marker_ != null) 'marker': marker_,
});
}
i1.LocalAlbumEntityCompanion copyWith({
i0.Value<String>? id,
i0.Value<String>? name,
i0.Value<DateTime>? updatedAt,
i0.Value<i2.BackupSelection>? backupSelection,
i0.Value<bool>? isIosSharedAlbum,
i0.Value<String?>? linkedRemoteAlbumId,
i0.Value<bool?>? marker_,
}) {
return i1.LocalAlbumEntityCompanion(
id: id ?? this.id,
name: name ?? this.name,
updatedAt: updatedAt ?? this.updatedAt,
backupSelection: backupSelection ?? this.backupSelection,
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
linkedRemoteAlbumId: linkedRemoteAlbumId ?? this.linkedRemoteAlbumId,
marker_: marker_ ?? this.marker_,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<String>(id.value);
}
if (name.present) {
map['name'] = i0.Variable<String>(name.value);
}
if (updatedAt.present) {
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
}
if (backupSelection.present) {
map['backup_selection'] = i0.Variable<int>(
i1.$LocalAlbumEntityTable.$converterbackupSelection.toSql(
backupSelection.value,
),
);
}
if (isIosSharedAlbum.present) {
map['is_ios_shared_album'] = i0.Variable<bool>(isIosSharedAlbum.value);
}
if (linkedRemoteAlbumId.present) {
map['linked_remote_album_id'] = i0.Variable<String>(
linkedRemoteAlbumId.value,
);
}
if (marker_.present) {
map['marker'] = i0.Variable<bool>(marker_.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('LocalAlbumEntityCompanion(')
..write('id: $id, ')
..write('name: $name, ')
..write('updatedAt: $updatedAt, ')
..write('backupSelection: $backupSelection, ')
..write('isIosSharedAlbum: $isIosSharedAlbum, ')
..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ')
..write('marker_: $marker_')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,18 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class LocalAlbumAssetEntity extends Table with DriftDefaultsMixin {
const LocalAlbumAssetEntity();
TextColumn get assetId => text().references(LocalAssetEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get albumId => text().references(LocalAlbumEntity, #id, onDelete: KeyAction.cascade)();
// Used for mark & sweep
BoolColumn get marker_ => boolean().nullable()();
@override
Set<Column> get primaryKey => {assetId, albumId};
}

View file

@ -0,0 +1,735 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
as i1;
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'
as i2;
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
as i3;
import 'package:drift/internal/modular.dart' as i4;
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
as i5;
typedef $$LocalAlbumAssetEntityTableCreateCompanionBuilder =
i1.LocalAlbumAssetEntityCompanion Function({
required String assetId,
required String albumId,
i0.Value<bool?> marker_,
});
typedef $$LocalAlbumAssetEntityTableUpdateCompanionBuilder =
i1.LocalAlbumAssetEntityCompanion Function({
i0.Value<String> assetId,
i0.Value<String> albumId,
i0.Value<bool?> marker_,
});
final class $$LocalAlbumAssetEntityTableReferences
extends
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$LocalAlbumAssetEntityTable,
i1.LocalAlbumAssetEntityData
> {
$$LocalAlbumAssetEntityTableReferences(
super.$_db,
super.$_table,
super.$_typedResult,
);
static i3.$LocalAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
i4.ReadDatabaseContainer(db)
.resultSet<i3.$LocalAssetEntityTable>('local_asset_entity')
.createAlias(
i0.$_aliasNameGenerator(
i4.ReadDatabaseContainer(db)
.resultSet<i1.$LocalAlbumAssetEntityTable>(
'local_album_asset_entity',
)
.assetId,
i4.ReadDatabaseContainer(
db,
).resultSet<i3.$LocalAssetEntityTable>('local_asset_entity').id,
),
);
i3.$$LocalAssetEntityTableProcessedTableManager get assetId {
final $_column = $_itemColumn<String>('asset_id')!;
final manager = i3
.$$LocalAssetEntityTableTableManager(
$_db,
i4.ReadDatabaseContainer(
$_db,
).resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]),
);
}
static i5.$LocalAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
i4.ReadDatabaseContainer(db)
.resultSet<i5.$LocalAlbumEntityTable>('local_album_entity')
.createAlias(
i0.$_aliasNameGenerator(
i4.ReadDatabaseContainer(db)
.resultSet<i1.$LocalAlbumAssetEntityTable>(
'local_album_asset_entity',
)
.albumId,
i4.ReadDatabaseContainer(
db,
).resultSet<i5.$LocalAlbumEntityTable>('local_album_entity').id,
),
);
i5.$$LocalAlbumEntityTableProcessedTableManager get albumId {
final $_column = $_itemColumn<String>('album_id')!;
final manager = i5
.$$LocalAlbumEntityTableTableManager(
$_db,
i4.ReadDatabaseContainer(
$_db,
).resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_albumIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]),
);
}
}
class $$LocalAlbumAssetEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumAssetEntityTable> {
$$LocalAlbumAssetEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<bool> get marker_ => $composableBuilder(
column: $table.marker_,
builder: (column) => i0.ColumnFilters(column),
);
i3.$$LocalAssetEntityTableFilterComposer get assetId {
final i3.$$LocalAssetEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$LocalAssetEntityTableFilterComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i5.$$LocalAlbumEntityTableFilterComposer get albumId {
final i5.$$LocalAlbumEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$LocalAlbumEntityTableFilterComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$LocalAlbumAssetEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumAssetEntityTable> {
$$LocalAlbumAssetEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<bool> get marker_ => $composableBuilder(
column: $table.marker_,
builder: (column) => i0.ColumnOrderings(column),
);
i3.$$LocalAssetEntityTableOrderingComposer get assetId {
final i3.$$LocalAssetEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$LocalAssetEntityTableOrderingComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i5.$$LocalAlbumEntityTableOrderingComposer get albumId {
final i5.$$LocalAlbumEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$LocalAlbumEntityTableOrderingComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$LocalAlbumAssetEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumAssetEntityTable> {
$$LocalAlbumAssetEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<bool> get marker_ =>
$composableBuilder(column: $table.marker_, builder: (column) => column);
i3.$$LocalAssetEntityTableAnnotationComposer get assetId {
final i3.$$LocalAssetEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$LocalAssetEntityTableAnnotationComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$LocalAssetEntityTable>('local_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i5.$$LocalAlbumEntityTableAnnotationComposer get albumId {
final i5.$$LocalAlbumEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$LocalAlbumEntityTableAnnotationComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$LocalAlbumEntityTable>('local_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$LocalAlbumAssetEntityTableTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.$LocalAlbumAssetEntityTable,
i1.LocalAlbumAssetEntityData,
i1.$$LocalAlbumAssetEntityTableFilterComposer,
i1.$$LocalAlbumAssetEntityTableOrderingComposer,
i1.$$LocalAlbumAssetEntityTableAnnotationComposer,
$$LocalAlbumAssetEntityTableCreateCompanionBuilder,
$$LocalAlbumAssetEntityTableUpdateCompanionBuilder,
(
i1.LocalAlbumAssetEntityData,
i1.$$LocalAlbumAssetEntityTableReferences,
),
i1.LocalAlbumAssetEntityData,
i0.PrefetchHooks Function({bool assetId, bool albumId})
> {
$$LocalAlbumAssetEntityTableTableManager(
i0.GeneratedDatabase db,
i1.$LocalAlbumAssetEntityTable table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$LocalAlbumAssetEntityTableFilterComposer(
$db: db,
$table: table,
),
createOrderingComposer: () =>
i1.$$LocalAlbumAssetEntityTableOrderingComposer(
$db: db,
$table: table,
),
createComputedFieldComposer: () =>
i1.$$LocalAlbumAssetEntityTableAnnotationComposer(
$db: db,
$table: table,
),
updateCompanionCallback:
({
i0.Value<String> assetId = const i0.Value.absent(),
i0.Value<String> albumId = const i0.Value.absent(),
i0.Value<bool?> marker_ = const i0.Value.absent(),
}) => i1.LocalAlbumAssetEntityCompanion(
assetId: assetId,
albumId: albumId,
marker_: marker_,
),
createCompanionCallback:
({
required String assetId,
required String albumId,
i0.Value<bool?> marker_ = const i0.Value.absent(),
}) => i1.LocalAlbumAssetEntityCompanion.insert(
assetId: assetId,
albumId: albumId,
marker_: marker_,
),
withReferenceMapper: (p0) => p0
.map(
(e) => (
e.readTable(table),
i1.$$LocalAlbumAssetEntityTableReferences(db, table, e),
),
)
.toList(),
prefetchHooksCallback: ({assetId = false, albumId = 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 (assetId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.assetId,
referencedTable: i1
.$$LocalAlbumAssetEntityTableReferences
._assetIdTable(db),
referencedColumn: i1
.$$LocalAlbumAssetEntityTableReferences
._assetIdTable(db)
.id,
)
as T;
}
if (albumId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.albumId,
referencedTable: i1
.$$LocalAlbumAssetEntityTableReferences
._albumIdTable(db),
referencedColumn: i1
.$$LocalAlbumAssetEntityTableReferences
._albumIdTable(db)
.id,
)
as T;
}
return state;
},
getPrefetchedDataCallback: (items) async {
return [];
},
);
},
),
);
}
typedef $$LocalAlbumAssetEntityTableProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$LocalAlbumAssetEntityTable,
i1.LocalAlbumAssetEntityData,
i1.$$LocalAlbumAssetEntityTableFilterComposer,
i1.$$LocalAlbumAssetEntityTableOrderingComposer,
i1.$$LocalAlbumAssetEntityTableAnnotationComposer,
$$LocalAlbumAssetEntityTableCreateCompanionBuilder,
$$LocalAlbumAssetEntityTableUpdateCompanionBuilder,
(i1.LocalAlbumAssetEntityData, i1.$$LocalAlbumAssetEntityTableReferences),
i1.LocalAlbumAssetEntityData,
i0.PrefetchHooks Function({bool assetId, bool albumId})
>;
class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity
with
i0.TableInfo<
$LocalAlbumAssetEntityTable,
i1.LocalAlbumAssetEntityData
> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$LocalAlbumAssetEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta(
'assetId',
);
@override
late final i0.GeneratedColumn<String> assetId = i0.GeneratedColumn<String>(
'asset_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES local_asset_entity (id) ON DELETE CASCADE',
),
);
static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta(
'albumId',
);
@override
late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
'album_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES local_album_entity (id) ON DELETE CASCADE',
),
);
static const i0.VerificationMeta _marker_Meta = const i0.VerificationMeta(
'marker_',
);
@override
late final i0.GeneratedColumn<bool> marker_ = i0.GeneratedColumn<bool>(
'marker',
aliasedName,
true,
type: i0.DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'CHECK ("marker" IN (0, 1))',
),
);
@override
List<i0.GeneratedColumn> get $columns => [assetId, albumId, marker_];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'local_album_asset_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.LocalAlbumAssetEntityData> instance, {
bool isInserting = false,
}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('asset_id')) {
context.handle(
_assetIdMeta,
assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta),
);
} else if (isInserting) {
context.missing(_assetIdMeta);
}
if (data.containsKey('album_id')) {
context.handle(
_albumIdMeta,
albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta),
);
} else if (isInserting) {
context.missing(_albumIdMeta);
}
if (data.containsKey('marker')) {
context.handle(
_marker_Meta,
marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta),
);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {assetId, albumId};
@override
i1.LocalAlbumAssetEntityData map(
Map<String, dynamic> data, {
String? tablePrefix,
}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.LocalAlbumAssetEntityData(
assetId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}asset_id'],
)!,
albumId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}album_id'],
)!,
marker_: attachedDatabase.typeMapping.read(
i0.DriftSqlType.bool,
data['${effectivePrefix}marker'],
),
);
}
@override
$LocalAlbumAssetEntityTable createAlias(String alias) {
return $LocalAlbumAssetEntityTable(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class LocalAlbumAssetEntityData extends i0.DataClass
implements i0.Insertable<i1.LocalAlbumAssetEntityData> {
final String assetId;
final String albumId;
final bool? marker_;
const LocalAlbumAssetEntityData({
required this.assetId,
required this.albumId,
this.marker_,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['asset_id'] = i0.Variable<String>(assetId);
map['album_id'] = i0.Variable<String>(albumId);
if (!nullToAbsent || marker_ != null) {
map['marker'] = i0.Variable<bool>(marker_);
}
return map;
}
factory LocalAlbumAssetEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return LocalAlbumAssetEntityData(
assetId: serializer.fromJson<String>(json['assetId']),
albumId: serializer.fromJson<String>(json['albumId']),
marker_: serializer.fromJson<bool?>(json['marker_']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'assetId': serializer.toJson<String>(assetId),
'albumId': serializer.toJson<String>(albumId),
'marker_': serializer.toJson<bool?>(marker_),
};
}
i1.LocalAlbumAssetEntityData copyWith({
String? assetId,
String? albumId,
i0.Value<bool?> marker_ = const i0.Value.absent(),
}) => i1.LocalAlbumAssetEntityData(
assetId: assetId ?? this.assetId,
albumId: albumId ?? this.albumId,
marker_: marker_.present ? marker_.value : this.marker_,
);
LocalAlbumAssetEntityData copyWithCompanion(
i1.LocalAlbumAssetEntityCompanion data,
) {
return LocalAlbumAssetEntityData(
assetId: data.assetId.present ? data.assetId.value : this.assetId,
albumId: data.albumId.present ? data.albumId.value : this.albumId,
marker_: data.marker_.present ? data.marker_.value : this.marker_,
);
}
@override
String toString() {
return (StringBuffer('LocalAlbumAssetEntityData(')
..write('assetId: $assetId, ')
..write('albumId: $albumId, ')
..write('marker_: $marker_')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(assetId, albumId, marker_);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.LocalAlbumAssetEntityData &&
other.assetId == this.assetId &&
other.albumId == this.albumId &&
other.marker_ == this.marker_);
}
class LocalAlbumAssetEntityCompanion
extends i0.UpdateCompanion<i1.LocalAlbumAssetEntityData> {
final i0.Value<String> assetId;
final i0.Value<String> albumId;
final i0.Value<bool?> marker_;
const LocalAlbumAssetEntityCompanion({
this.assetId = const i0.Value.absent(),
this.albumId = const i0.Value.absent(),
this.marker_ = const i0.Value.absent(),
});
LocalAlbumAssetEntityCompanion.insert({
required String assetId,
required String albumId,
this.marker_ = const i0.Value.absent(),
}) : assetId = i0.Value(assetId),
albumId = i0.Value(albumId);
static i0.Insertable<i1.LocalAlbumAssetEntityData> custom({
i0.Expression<String>? assetId,
i0.Expression<String>? albumId,
i0.Expression<bool>? marker_,
}) {
return i0.RawValuesInsertable({
if (assetId != null) 'asset_id': assetId,
if (albumId != null) 'album_id': albumId,
if (marker_ != null) 'marker': marker_,
});
}
i1.LocalAlbumAssetEntityCompanion copyWith({
i0.Value<String>? assetId,
i0.Value<String>? albumId,
i0.Value<bool?>? marker_,
}) {
return i1.LocalAlbumAssetEntityCompanion(
assetId: assetId ?? this.assetId,
albumId: albumId ?? this.albumId,
marker_: marker_ ?? this.marker_,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (assetId.present) {
map['asset_id'] = i0.Variable<String>(assetId.value);
}
if (albumId.present) {
map['album_id'] = i0.Variable<String>(albumId.value);
}
if (marker_.present) {
map['marker'] = i0.Variable<bool>(marker_.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('LocalAlbumAssetEntityCompanion(')
..write('assetId: $assetId, ')
..write('albumId: $albumId, ')
..write('marker_: $marker_')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,52 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)')
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_local_asset_cloud_id ON local_asset_entity (i_cloud_id)')
class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
const LocalAssetEntity();
TextColumn get id => text()();
TextColumn get checksum => text().nullable()();
// Only used during backup to mirror the favorite status of the asset in the server
BoolColumn get isFavorite => boolean().withDefault(const Constant(false))();
IntColumn get orientation => integer().withDefault(const Constant(0))();
TextColumn get iCloudId => text().nullable()();
DateTimeColumn get adjustmentTime => dateTime().nullable()();
RealColumn get latitude => real().nullable()();
RealColumn get longitude => real().nullable()();
@override
Set<Column> get primaryKey => {id};
}
extension LocalAssetEntityDataDomainExtension on LocalAssetEntityData {
LocalAsset toDto({String? remoteId}) => LocalAsset(
id: id,
name: name,
checksum: checksum,
type: type,
createdAt: createdAt,
updatedAt: updatedAt,
durationInSeconds: durationInSeconds,
isFavorite: isFavorite,
height: height,
width: width,
remoteId: remoteId,
orientation: orientation,
adjustmentTime: adjustmentTime,
latitude: latitude,
longitude: longitude,
cloudId: iCloudId,
isEdited: false,
);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart';
import 'package:immich_mobile/domain/models/log.model.dart' as domain;
class LogMessageEntity extends Table {
const LogMessageEntity();
@override
String get tableName => 'logger_messages';
IntColumn get id => integer().autoIncrement()();
TextColumn get message => text()();
TextColumn get details => text().nullable()();
IntColumn get level => intEnum<domain.LogLevel>()();
DateTimeColumn get createdAt => dateTime()();
TextColumn get logger => text().nullable()();
TextColumn get stack => text().nullable()();
}
extension LogMessageEntityDataDomainEx on LogMessageEntityData {
domain.LogMessage toDto() => domain.LogMessage(
message: message,
level: level,
createdAt: createdAt,
logger: logger,
error: details,
stack: stack,
);
}

View file

@ -0,0 +1,697 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart'
as i1;
import 'package:immich_mobile/domain/models/log.model.dart' as i2;
import 'package:immich_mobile/infrastructure/entities/log.entity.dart' as i3;
typedef $$LogMessageEntityTableCreateCompanionBuilder =
i1.LogMessageEntityCompanion Function({
i0.Value<int> id,
required String message,
i0.Value<String?> details,
required i2.LogLevel level,
required DateTime createdAt,
i0.Value<String?> logger,
i0.Value<String?> stack,
});
typedef $$LogMessageEntityTableUpdateCompanionBuilder =
i1.LogMessageEntityCompanion Function({
i0.Value<int> id,
i0.Value<String> message,
i0.Value<String?> details,
i0.Value<i2.LogLevel> level,
i0.Value<DateTime> createdAt,
i0.Value<String?> logger,
i0.Value<String?> stack,
});
class $$LogMessageEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LogMessageEntityTable> {
$$LogMessageEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<int> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get message => $composableBuilder(
column: $table.message,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get details => $composableBuilder(
column: $table.details,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnWithTypeConverterFilters<i2.LogLevel, i2.LogLevel, int> get level =>
$composableBuilder(
column: $table.level,
builder: (column) => i0.ColumnWithTypeConverterFilters(column),
);
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get logger => $composableBuilder(
column: $table.logger,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get stack => $composableBuilder(
column: $table.stack,
builder: (column) => i0.ColumnFilters(column),
);
}
class $$LogMessageEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LogMessageEntityTable> {
$$LogMessageEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<int> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get message => $composableBuilder(
column: $table.message,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get details => $composableBuilder(
column: $table.details,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<int> get level => $composableBuilder(
column: $table.level,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get logger => $composableBuilder(
column: $table.logger,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get stack => $composableBuilder(
column: $table.stack,
builder: (column) => i0.ColumnOrderings(column),
);
}
class $$LogMessageEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$LogMessageEntityTable> {
$$LogMessageEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<int> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
i0.GeneratedColumn<String> get message =>
$composableBuilder(column: $table.message, builder: (column) => column);
i0.GeneratedColumn<String> get details =>
$composableBuilder(column: $table.details, builder: (column) => column);
i0.GeneratedColumnWithTypeConverter<i2.LogLevel, int> get level =>
$composableBuilder(column: $table.level, builder: (column) => column);
i0.GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
i0.GeneratedColumn<String> get logger =>
$composableBuilder(column: $table.logger, builder: (column) => column);
i0.GeneratedColumn<String> get stack =>
$composableBuilder(column: $table.stack, builder: (column) => column);
}
class $$LogMessageEntityTableTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.$LogMessageEntityTable,
i1.LogMessageEntityData,
i1.$$LogMessageEntityTableFilterComposer,
i1.$$LogMessageEntityTableOrderingComposer,
i1.$$LogMessageEntityTableAnnotationComposer,
$$LogMessageEntityTableCreateCompanionBuilder,
$$LogMessageEntityTableUpdateCompanionBuilder,
(
i1.LogMessageEntityData,
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$LogMessageEntityTable,
i1.LogMessageEntityData
>,
),
i1.LogMessageEntityData,
i0.PrefetchHooks Function()
> {
$$LogMessageEntityTableTableManager(
i0.GeneratedDatabase db,
i1.$LogMessageEntityTable table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$LogMessageEntityTableFilterComposer($db: db, $table: table),
createOrderingComposer: () => i1
.$$LogMessageEntityTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
i1.$$LogMessageEntityTableAnnotationComposer(
$db: db,
$table: table,
),
updateCompanionCallback:
({
i0.Value<int> id = const i0.Value.absent(),
i0.Value<String> message = const i0.Value.absent(),
i0.Value<String?> details = const i0.Value.absent(),
i0.Value<i2.LogLevel> level = const i0.Value.absent(),
i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<String?> logger = const i0.Value.absent(),
i0.Value<String?> stack = const i0.Value.absent(),
}) => i1.LogMessageEntityCompanion(
id: id,
message: message,
details: details,
level: level,
createdAt: createdAt,
logger: logger,
stack: stack,
),
createCompanionCallback:
({
i0.Value<int> id = const i0.Value.absent(),
required String message,
i0.Value<String?> details = const i0.Value.absent(),
required i2.LogLevel level,
required DateTime createdAt,
i0.Value<String?> logger = const i0.Value.absent(),
i0.Value<String?> stack = const i0.Value.absent(),
}) => i1.LogMessageEntityCompanion.insert(
id: id,
message: message,
details: details,
level: level,
createdAt: createdAt,
logger: logger,
stack: stack,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
),
);
}
typedef $$LogMessageEntityTableProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$LogMessageEntityTable,
i1.LogMessageEntityData,
i1.$$LogMessageEntityTableFilterComposer,
i1.$$LogMessageEntityTableOrderingComposer,
i1.$$LogMessageEntityTableAnnotationComposer,
$$LogMessageEntityTableCreateCompanionBuilder,
$$LogMessageEntityTableUpdateCompanionBuilder,
(
i1.LogMessageEntityData,
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$LogMessageEntityTable,
i1.LogMessageEntityData
>,
),
i1.LogMessageEntityData,
i0.PrefetchHooks Function()
>;
class $LogMessageEntityTable extends i3.LogMessageEntity
with i0.TableInfo<$LogMessageEntityTable, i1.LogMessageEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$LogMessageEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override
late final i0.GeneratedColumn<int> id = i0.GeneratedColumn<int>(
'id',
aliasedName,
false,
hasAutoIncrement: true,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'PRIMARY KEY AUTOINCREMENT',
),
);
static const i0.VerificationMeta _messageMeta = const i0.VerificationMeta(
'message',
);
@override
late final i0.GeneratedColumn<String> message = i0.GeneratedColumn<String>(
'message',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _detailsMeta = const i0.VerificationMeta(
'details',
);
@override
late final i0.GeneratedColumn<String> details = i0.GeneratedColumn<String>(
'details',
aliasedName,
true,
type: i0.DriftSqlType.string,
requiredDuringInsert: false,
);
@override
late final i0.GeneratedColumnWithTypeConverter<i2.LogLevel, int> level =
i0.GeneratedColumn<int>(
'level',
aliasedName,
false,
type: i0.DriftSqlType.int,
requiredDuringInsert: true,
).withConverter<i2.LogLevel>(i1.$LogMessageEntityTable.$converterlevel);
static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta(
'createdAt',
);
@override
late final i0.GeneratedColumn<DateTime> createdAt =
i0.GeneratedColumn<DateTime>(
'created_at',
aliasedName,
false,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _loggerMeta = const i0.VerificationMeta(
'logger',
);
@override
late final i0.GeneratedColumn<String> logger = i0.GeneratedColumn<String>(
'logger',
aliasedName,
true,
type: i0.DriftSqlType.string,
requiredDuringInsert: false,
);
static const i0.VerificationMeta _stackMeta = const i0.VerificationMeta(
'stack',
);
@override
late final i0.GeneratedColumn<String> stack = i0.GeneratedColumn<String>(
'stack',
aliasedName,
true,
type: i0.DriftSqlType.string,
requiredDuringInsert: false,
);
@override
List<i0.GeneratedColumn> get $columns => [
id,
message,
details,
level,
createdAt,
logger,
stack,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'logger_messages';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.LogMessageEntityData> 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));
}
if (data.containsKey('message')) {
context.handle(
_messageMeta,
message.isAcceptableOrUnknown(data['message']!, _messageMeta),
);
} else if (isInserting) {
context.missing(_messageMeta);
}
if (data.containsKey('details')) {
context.handle(
_detailsMeta,
details.isAcceptableOrUnknown(data['details']!, _detailsMeta),
);
}
if (data.containsKey('created_at')) {
context.handle(
_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
);
} else if (isInserting) {
context.missing(_createdAtMeta);
}
if (data.containsKey('logger')) {
context.handle(
_loggerMeta,
logger.isAcceptableOrUnknown(data['logger']!, _loggerMeta),
);
}
if (data.containsKey('stack')) {
context.handle(
_stackMeta,
stack.isAcceptableOrUnknown(data['stack']!, _stackMeta),
);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.LogMessageEntityData map(
Map<String, dynamic> data, {
String? tablePrefix,
}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.LogMessageEntityData(
id: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}id'],
)!,
message: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}message'],
)!,
details: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}details'],
),
level: i1.$LogMessageEntityTable.$converterlevel.fromSql(
attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}level'],
)!,
),
createdAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime,
data['${effectivePrefix}created_at'],
)!,
logger: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}logger'],
),
stack: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}stack'],
),
);
}
@override
$LogMessageEntityTable createAlias(String alias) {
return $LogMessageEntityTable(attachedDatabase, alias);
}
static i0.JsonTypeConverter2<i2.LogLevel, int, int> $converterlevel =
const i0.EnumIndexConverter<i2.LogLevel>(i2.LogLevel.values);
}
class LogMessageEntityData extends i0.DataClass
implements i0.Insertable<i1.LogMessageEntityData> {
final int id;
final String message;
final String? details;
final i2.LogLevel level;
final DateTime createdAt;
final String? logger;
final String? stack;
const LogMessageEntityData({
required this.id,
required this.message,
this.details,
required this.level,
required this.createdAt,
this.logger,
this.stack,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<int>(id);
map['message'] = i0.Variable<String>(message);
if (!nullToAbsent || details != null) {
map['details'] = i0.Variable<String>(details);
}
{
map['level'] = i0.Variable<int>(
i1.$LogMessageEntityTable.$converterlevel.toSql(level),
);
}
map['created_at'] = i0.Variable<DateTime>(createdAt);
if (!nullToAbsent || logger != null) {
map['logger'] = i0.Variable<String>(logger);
}
if (!nullToAbsent || stack != null) {
map['stack'] = i0.Variable<String>(stack);
}
return map;
}
factory LogMessageEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return LogMessageEntityData(
id: serializer.fromJson<int>(json['id']),
message: serializer.fromJson<String>(json['message']),
details: serializer.fromJson<String?>(json['details']),
level: i1.$LogMessageEntityTable.$converterlevel.fromJson(
serializer.fromJson<int>(json['level']),
),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
logger: serializer.fromJson<String?>(json['logger']),
stack: serializer.fromJson<String?>(json['stack']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'message': serializer.toJson<String>(message),
'details': serializer.toJson<String?>(details),
'level': serializer.toJson<int>(
i1.$LogMessageEntityTable.$converterlevel.toJson(level),
),
'createdAt': serializer.toJson<DateTime>(createdAt),
'logger': serializer.toJson<String?>(logger),
'stack': serializer.toJson<String?>(stack),
};
}
i1.LogMessageEntityData copyWith({
int? id,
String? message,
i0.Value<String?> details = const i0.Value.absent(),
i2.LogLevel? level,
DateTime? createdAt,
i0.Value<String?> logger = const i0.Value.absent(),
i0.Value<String?> stack = const i0.Value.absent(),
}) => i1.LogMessageEntityData(
id: id ?? this.id,
message: message ?? this.message,
details: details.present ? details.value : this.details,
level: level ?? this.level,
createdAt: createdAt ?? this.createdAt,
logger: logger.present ? logger.value : this.logger,
stack: stack.present ? stack.value : this.stack,
);
LogMessageEntityData copyWithCompanion(i1.LogMessageEntityCompanion data) {
return LogMessageEntityData(
id: data.id.present ? data.id.value : this.id,
message: data.message.present ? data.message.value : this.message,
details: data.details.present ? data.details.value : this.details,
level: data.level.present ? data.level.value : this.level,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
logger: data.logger.present ? data.logger.value : this.logger,
stack: data.stack.present ? data.stack.value : this.stack,
);
}
@override
String toString() {
return (StringBuffer('LogMessageEntityData(')
..write('id: $id, ')
..write('message: $message, ')
..write('details: $details, ')
..write('level: $level, ')
..write('createdAt: $createdAt, ')
..write('logger: $logger, ')
..write('stack: $stack')
..write(')'))
.toString();
}
@override
int get hashCode =>
Object.hash(id, message, details, level, createdAt, logger, stack);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.LogMessageEntityData &&
other.id == this.id &&
other.message == this.message &&
other.details == this.details &&
other.level == this.level &&
other.createdAt == this.createdAt &&
other.logger == this.logger &&
other.stack == this.stack);
}
class LogMessageEntityCompanion
extends i0.UpdateCompanion<i1.LogMessageEntityData> {
final i0.Value<int> id;
final i0.Value<String> message;
final i0.Value<String?> details;
final i0.Value<i2.LogLevel> level;
final i0.Value<DateTime> createdAt;
final i0.Value<String?> logger;
final i0.Value<String?> stack;
const LogMessageEntityCompanion({
this.id = const i0.Value.absent(),
this.message = const i0.Value.absent(),
this.details = const i0.Value.absent(),
this.level = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(),
this.logger = const i0.Value.absent(),
this.stack = const i0.Value.absent(),
});
LogMessageEntityCompanion.insert({
this.id = const i0.Value.absent(),
required String message,
this.details = const i0.Value.absent(),
required i2.LogLevel level,
required DateTime createdAt,
this.logger = const i0.Value.absent(),
this.stack = const i0.Value.absent(),
}) : message = i0.Value(message),
level = i0.Value(level),
createdAt = i0.Value(createdAt);
static i0.Insertable<i1.LogMessageEntityData> custom({
i0.Expression<int>? id,
i0.Expression<String>? message,
i0.Expression<String>? details,
i0.Expression<int>? level,
i0.Expression<DateTime>? createdAt,
i0.Expression<String>? logger,
i0.Expression<String>? stack,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (message != null) 'message': message,
if (details != null) 'details': details,
if (level != null) 'level': level,
if (createdAt != null) 'created_at': createdAt,
if (logger != null) 'logger': logger,
if (stack != null) 'stack': stack,
});
}
i1.LogMessageEntityCompanion copyWith({
i0.Value<int>? id,
i0.Value<String>? message,
i0.Value<String?>? details,
i0.Value<i2.LogLevel>? level,
i0.Value<DateTime>? createdAt,
i0.Value<String?>? logger,
i0.Value<String?>? stack,
}) {
return i1.LogMessageEntityCompanion(
id: id ?? this.id,
message: message ?? this.message,
details: details ?? this.details,
level: level ?? this.level,
createdAt: createdAt ?? this.createdAt,
logger: logger ?? this.logger,
stack: stack ?? this.stack,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<int>(id.value);
}
if (message.present) {
map['message'] = i0.Variable<String>(message.value);
}
if (details.present) {
map['details'] = i0.Variable<String>(details.value);
}
if (level.present) {
map['level'] = i0.Variable<int>(
i1.$LogMessageEntityTable.$converterlevel.toSql(level.value),
);
}
if (createdAt.present) {
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
}
if (logger.present) {
map['logger'] = i0.Variable<String>(logger.value);
}
if (stack.present) {
map['stack'] = i0.Variable<String>(stack.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('LogMessageEntityCompanion(')
..write('id: $id, ')
..write('message: $message, ')
..write('details: $details, ')
..write('level: $level, ')
..write('createdAt: $createdAt, ')
..write('logger: $logger, ')
..write('stack: $stack')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,35 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/memory.model.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class MemoryEntity extends Table with DriftDefaultsMixin {
const MemoryEntity();
TextColumn get id => text()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get deletedAt => dateTime().nullable()();
TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
IntColumn get type => intEnum<MemoryTypeEnum>()();
TextColumn get data => text()();
BoolColumn get isSaved => boolean().withDefault(const Constant(false))();
DateTimeColumn get memoryAt => dateTime()();
DateTimeColumn get seenAt => dateTime().nullable()();
DateTimeColumn get showAt => dateTime().nullable()();
DateTimeColumn get hideAt => dateTime().nullable()();
@override
Set<Column> get primaryKey => {id};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,15 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/entities/memory.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class MemoryAssetEntity extends Table with DriftDefaultsMixin {
const MemoryAssetEntity();
TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get memoryId => text().references(MemoryEntity, #id, onDelete: KeyAction.cascade)();
@override
Set<Column> get primaryKey => {assetId, memoryId};
}

View file

@ -0,0 +1,641 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'
as i1;
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart'
as i2;
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
as i3;
import 'package:drift/internal/modular.dart' as i4;
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'
as i5;
typedef $$MemoryAssetEntityTableCreateCompanionBuilder =
i1.MemoryAssetEntityCompanion Function({
required String assetId,
required String memoryId,
});
typedef $$MemoryAssetEntityTableUpdateCompanionBuilder =
i1.MemoryAssetEntityCompanion Function({
i0.Value<String> assetId,
i0.Value<String> memoryId,
});
final class $$MemoryAssetEntityTableReferences
extends
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$MemoryAssetEntityTable,
i1.MemoryAssetEntityData
> {
$$MemoryAssetEntityTableReferences(
super.$_db,
super.$_table,
super.$_typedResult,
);
static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
i4.ReadDatabaseContainer(db)
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
.createAlias(
i0.$_aliasNameGenerator(
i4.ReadDatabaseContainer(db)
.resultSet<i1.$MemoryAssetEntityTable>('memory_asset_entity')
.assetId,
i4.ReadDatabaseContainer(
db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity').id,
),
);
i3.$$RemoteAssetEntityTableProcessedTableManager get assetId {
final $_column = $_itemColumn<String>('asset_id')!;
final manager = i3
.$$RemoteAssetEntityTableTableManager(
$_db,
i4.ReadDatabaseContainer(
$_db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]),
);
}
static i5.$MemoryEntityTable _memoryIdTable(i0.GeneratedDatabase db) =>
i4.ReadDatabaseContainer(db)
.resultSet<i5.$MemoryEntityTable>('memory_entity')
.createAlias(
i0.$_aliasNameGenerator(
i4.ReadDatabaseContainer(db)
.resultSet<i1.$MemoryAssetEntityTable>('memory_asset_entity')
.memoryId,
i4.ReadDatabaseContainer(
db,
).resultSet<i5.$MemoryEntityTable>('memory_entity').id,
),
);
i5.$$MemoryEntityTableProcessedTableManager get memoryId {
final $_column = $_itemColumn<String>('memory_id')!;
final manager = i5
.$$MemoryEntityTableTableManager(
$_db,
i4.ReadDatabaseContainer(
$_db,
).resultSet<i5.$MemoryEntityTable>('memory_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_memoryIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]),
);
}
}
class $$MemoryAssetEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$MemoryAssetEntityTable> {
$$MemoryAssetEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$RemoteAssetEntityTableFilterComposer get assetId {
final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$RemoteAssetEntityTableFilterComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i5.$$MemoryEntityTableFilterComposer get memoryId {
final i5.$$MemoryEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.memoryId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$MemoryEntityTable>('memory_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$MemoryEntityTableFilterComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$MemoryEntityTable>('memory_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$MemoryAssetEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$MemoryAssetEntityTable> {
$$MemoryAssetEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$RemoteAssetEntityTableOrderingComposer get assetId {
final i3.$$RemoteAssetEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$RemoteAssetEntityTableOrderingComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i5.$$MemoryEntityTableOrderingComposer get memoryId {
final i5.$$MemoryEntityTableOrderingComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.memoryId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$MemoryEntityTable>('memory_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$MemoryEntityTableOrderingComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$MemoryEntityTable>('memory_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$MemoryAssetEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$MemoryAssetEntityTable> {
$$MemoryAssetEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$RemoteAssetEntityTableAnnotationComposer get assetId {
final i3.$$RemoteAssetEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$RemoteAssetEntityTableAnnotationComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i5.$$MemoryEntityTableAnnotationComposer get memoryId {
final i5.$$MemoryEntityTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.memoryId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$MemoryEntityTable>('memory_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$MemoryEntityTableAnnotationComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$MemoryEntityTable>('memory_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$MemoryAssetEntityTableTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.$MemoryAssetEntityTable,
i1.MemoryAssetEntityData,
i1.$$MemoryAssetEntityTableFilterComposer,
i1.$$MemoryAssetEntityTableOrderingComposer,
i1.$$MemoryAssetEntityTableAnnotationComposer,
$$MemoryAssetEntityTableCreateCompanionBuilder,
$$MemoryAssetEntityTableUpdateCompanionBuilder,
(i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences),
i1.MemoryAssetEntityData,
i0.PrefetchHooks Function({bool assetId, bool memoryId})
> {
$$MemoryAssetEntityTableTableManager(
i0.GeneratedDatabase db,
i1.$MemoryAssetEntityTable table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$MemoryAssetEntityTableFilterComposer($db: db, $table: table),
createOrderingComposer: () => i1
.$$MemoryAssetEntityTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
i1.$$MemoryAssetEntityTableAnnotationComposer(
$db: db,
$table: table,
),
updateCompanionCallback:
({
i0.Value<String> assetId = const i0.Value.absent(),
i0.Value<String> memoryId = const i0.Value.absent(),
}) => i1.MemoryAssetEntityCompanion(
assetId: assetId,
memoryId: memoryId,
),
createCompanionCallback:
({required String assetId, required String memoryId}) =>
i1.MemoryAssetEntityCompanion.insert(
assetId: assetId,
memoryId: memoryId,
),
withReferenceMapper: (p0) => p0
.map(
(e) => (
e.readTable(table),
i1.$$MemoryAssetEntityTableReferences(db, table, e),
),
)
.toList(),
prefetchHooksCallback: ({assetId = false, memoryId = 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 (assetId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.assetId,
referencedTable: i1
.$$MemoryAssetEntityTableReferences
._assetIdTable(db),
referencedColumn: i1
.$$MemoryAssetEntityTableReferences
._assetIdTable(db)
.id,
)
as T;
}
if (memoryId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.memoryId,
referencedTable: i1
.$$MemoryAssetEntityTableReferences
._memoryIdTable(db),
referencedColumn: i1
.$$MemoryAssetEntityTableReferences
._memoryIdTable(db)
.id,
)
as T;
}
return state;
},
getPrefetchedDataCallback: (items) async {
return [];
},
);
},
),
);
}
typedef $$MemoryAssetEntityTableProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$MemoryAssetEntityTable,
i1.MemoryAssetEntityData,
i1.$$MemoryAssetEntityTableFilterComposer,
i1.$$MemoryAssetEntityTableOrderingComposer,
i1.$$MemoryAssetEntityTableAnnotationComposer,
$$MemoryAssetEntityTableCreateCompanionBuilder,
$$MemoryAssetEntityTableUpdateCompanionBuilder,
(i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences),
i1.MemoryAssetEntityData,
i0.PrefetchHooks Function({bool assetId, bool memoryId})
>;
class $MemoryAssetEntityTable extends i2.MemoryAssetEntity
with i0.TableInfo<$MemoryAssetEntityTable, i1.MemoryAssetEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$MemoryAssetEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta(
'assetId',
);
@override
late final i0.GeneratedColumn<String> assetId = i0.GeneratedColumn<String>(
'asset_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id) ON DELETE CASCADE',
),
);
static const i0.VerificationMeta _memoryIdMeta = const i0.VerificationMeta(
'memoryId',
);
@override
late final i0.GeneratedColumn<String> memoryId = i0.GeneratedColumn<String>(
'memory_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES memory_entity (id) ON DELETE CASCADE',
),
);
@override
List<i0.GeneratedColumn> get $columns => [assetId, memoryId];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'memory_asset_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.MemoryAssetEntityData> instance, {
bool isInserting = false,
}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('asset_id')) {
context.handle(
_assetIdMeta,
assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta),
);
} else if (isInserting) {
context.missing(_assetIdMeta);
}
if (data.containsKey('memory_id')) {
context.handle(
_memoryIdMeta,
memoryId.isAcceptableOrUnknown(data['memory_id']!, _memoryIdMeta),
);
} else if (isInserting) {
context.missing(_memoryIdMeta);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {assetId, memoryId};
@override
i1.MemoryAssetEntityData map(
Map<String, dynamic> data, {
String? tablePrefix,
}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.MemoryAssetEntityData(
assetId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}asset_id'],
)!,
memoryId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}memory_id'],
)!,
);
}
@override
$MemoryAssetEntityTable createAlias(String alias) {
return $MemoryAssetEntityTable(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class MemoryAssetEntityData extends i0.DataClass
implements i0.Insertable<i1.MemoryAssetEntityData> {
final String assetId;
final String memoryId;
const MemoryAssetEntityData({required this.assetId, required this.memoryId});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['asset_id'] = i0.Variable<String>(assetId);
map['memory_id'] = i0.Variable<String>(memoryId);
return map;
}
factory MemoryAssetEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return MemoryAssetEntityData(
assetId: serializer.fromJson<String>(json['assetId']),
memoryId: serializer.fromJson<String>(json['memoryId']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'assetId': serializer.toJson<String>(assetId),
'memoryId': serializer.toJson<String>(memoryId),
};
}
i1.MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) =>
i1.MemoryAssetEntityData(
assetId: assetId ?? this.assetId,
memoryId: memoryId ?? this.memoryId,
);
MemoryAssetEntityData copyWithCompanion(i1.MemoryAssetEntityCompanion data) {
return MemoryAssetEntityData(
assetId: data.assetId.present ? data.assetId.value : this.assetId,
memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId,
);
}
@override
String toString() {
return (StringBuffer('MemoryAssetEntityData(')
..write('assetId: $assetId, ')
..write('memoryId: $memoryId')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(assetId, memoryId);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.MemoryAssetEntityData &&
other.assetId == this.assetId &&
other.memoryId == this.memoryId);
}
class MemoryAssetEntityCompanion
extends i0.UpdateCompanion<i1.MemoryAssetEntityData> {
final i0.Value<String> assetId;
final i0.Value<String> memoryId;
const MemoryAssetEntityCompanion({
this.assetId = const i0.Value.absent(),
this.memoryId = const i0.Value.absent(),
});
MemoryAssetEntityCompanion.insert({
required String assetId,
required String memoryId,
}) : assetId = i0.Value(assetId),
memoryId = i0.Value(memoryId);
static i0.Insertable<i1.MemoryAssetEntityData> custom({
i0.Expression<String>? assetId,
i0.Expression<String>? memoryId,
}) {
return i0.RawValuesInsertable({
if (assetId != null) 'asset_id': assetId,
if (memoryId != null) 'memory_id': memoryId,
});
}
i1.MemoryAssetEntityCompanion copyWith({
i0.Value<String>? assetId,
i0.Value<String>? memoryId,
}) {
return i1.MemoryAssetEntityCompanion(
assetId: assetId ?? this.assetId,
memoryId: memoryId ?? this.memoryId,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (assetId.present) {
map['asset_id'] = i0.Variable<String>(assetId.value);
}
if (memoryId.present) {
map['memory_id'] = i0.Variable<String>(memoryId.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('MemoryAssetEntityCompanion(')
..write('assetId: $assetId, ')
..write('memoryId: $memoryId')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,128 @@
import 'remote_asset.entity.dart';
import 'stack.entity.dart';
import 'local_asset.entity.dart';
import 'local_album.entity.dart';
import 'local_album_asset.entity.dart';
mergedAsset:
SELECT
rae.id as remote_id,
(SELECT lae.id FROM local_asset_entity lae WHERE lae.checksum = rae.checksum LIMIT 1) as local_id,
rae.name,
rae."type",
rae.created_at as created_at,
rae.updated_at,
rae.width,
rae.height,
rae.duration_in_seconds,
rae.is_favorite,
rae.thumb_hash,
rae.checksum,
rae.owner_id,
rae.live_photo_video_id,
0 as orientation,
rae.stack_id,
NULL as i_cloud_id,
NULL as latitude,
NULL as longitude,
NULL as adjustmentTime,
rae.is_edited
FROM
remote_asset_entity rae
LEFT JOIN
stack_entity se ON rae.stack_id = se.id
WHERE
rae.deleted_at IS NULL
AND rae.visibility = 0 -- timeline visibility
AND rae.owner_id IN :user_ids
AND (
rae.stack_id IS NULL
OR rae.id = se.primary_asset_id
)
UNION ALL
SELECT
NULL as remote_id,
lae.id as local_id,
lae.name,
lae."type",
lae.created_at as created_at,
lae.updated_at,
lae.width,
lae.height,
lae.duration_in_seconds,
lae.is_favorite,
NULL as thumb_hash,
lae.checksum,
NULL as owner_id,
NULL as live_photo_video_id,
lae.orientation,
NULL as stack_id,
lae.i_cloud_id,
lae.latitude,
lae.longitude,
lae.adjustment_time,
0 as is_edited
FROM
local_asset_entity lae
WHERE NOT EXISTS (
SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids
)
AND EXISTS (
SELECT 1 FROM local_album_asset_entity laa
INNER JOIN local_album_entity la on laa.album_id = la.id
WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected
)
AND NOT EXISTS (
SELECT 1 FROM local_album_asset_entity laa
INNER JOIN local_album_entity la on laa.album_id = la.id
WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded
)
ORDER BY created_at DESC
LIMIT $limit;
mergedBucket(:group_by AS INTEGER):
SELECT
COUNT(*) as asset_count,
CASE
WHEN :group_by = 0 THEN STRFTIME('%Y-%m-%d', created_at, 'localtime') -- day
WHEN :group_by = 1 THEN STRFTIME('%Y-%m', created_at, 'localtime') -- month
END AS bucket_date
FROM
(
SELECT
rae.created_at
FROM
remote_asset_entity rae
LEFT JOIN
stack_entity se ON rae.stack_id = se.id
WHERE
rae.deleted_at IS NULL
AND rae.visibility = 0 -- timeline visibility
AND rae.owner_id in :user_ids
AND (
rae.stack_id IS NULL
OR rae.id = se.primary_asset_id
)
UNION ALL
SELECT
lae.created_at
FROM
local_asset_entity lae
WHERE NOT EXISTS (
SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids
)
AND EXISTS (
SELECT 1 FROM local_album_asset_entity laa
INNER JOIN local_album_entity la on laa.album_id = la.id
WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected
)
AND NOT EXISTS (
SELECT 1 FROM local_album_asset_entity laa
INNER JOIN local_album_entity la on laa.album_id = la.id
WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded
)
)
GROUP BY bucket_date
ORDER BY bucket_date DESC;

View file

@ -0,0 +1,173 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:drift/internal/modular.dart' as i1;
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2;
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
as i3;
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
as i4;
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart'
as i5;
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
as i6;
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
as i7;
class MergedAssetDrift extends i1.ModularAccessor {
MergedAssetDrift(i0.GeneratedDatabase db) : super(db);
i0.Selectable<MergedAssetResult> mergedAsset({
required List<String> userIds,
required MergedAsset$limit limit,
}) {
var $arrayStartIndex = 1;
final expandeduserIds = $expandVar($arrayStartIndex, userIds.length);
$arrayStartIndex += userIds.length;
final generatedlimit = $write(
limit(alias(this.localAssetEntity, 'lae')),
startIndex: $arrayStartIndex,
);
$arrayStartIndex += generatedlimit.amountOfVariables;
return customSelect(
'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, NULL AS i_cloud_id, NULL AS latitude, NULL AS longitude, NULL AS adjustmentTime, rae.is_edited FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, lae.i_cloud_id, lae.latitude, lae.longitude, lae.adjustment_time, 0 AS is_edited FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}',
variables: [
for (var $ in userIds) i0.Variable<String>($),
...generatedlimit.introducedVariables,
],
readsFrom: {
remoteAssetEntity,
localAssetEntity,
stackEntity,
localAlbumAssetEntity,
localAlbumEntity,
...generatedlimit.watchedTables,
},
).map(
(i0.QueryRow row) => MergedAssetResult(
remoteId: row.readNullable<String>('remote_id'),
localId: row.readNullable<String>('local_id'),
name: row.read<String>('name'),
type: i4.$RemoteAssetEntityTable.$convertertype.fromSql(
row.read<int>('type'),
),
createdAt: row.read<DateTime>('created_at'),
updatedAt: row.read<DateTime>('updated_at'),
width: row.readNullable<int>('width'),
height: row.readNullable<int>('height'),
durationInSeconds: row.readNullable<int>('duration_in_seconds'),
isFavorite: row.read<bool>('is_favorite'),
thumbHash: row.readNullable<String>('thumb_hash'),
checksum: row.readNullable<String>('checksum'),
ownerId: row.readNullable<String>('owner_id'),
livePhotoVideoId: row.readNullable<String>('live_photo_video_id'),
orientation: row.read<int>('orientation'),
stackId: row.readNullable<String>('stack_id'),
iCloudId: row.readNullable<String>('i_cloud_id'),
latitude: row.readNullable<double>('latitude'),
longitude: row.readNullable<double>('longitude'),
adjustmentTime: row.readNullable<DateTime>('adjustmentTime'),
isEdited: row.read<bool>('is_edited'),
),
);
}
i0.Selectable<MergedBucketResult> mergedBucket({
required int groupBy,
required List<String> userIds,
}) {
var $arrayStartIndex = 2;
final expandeduserIds = $expandVar($arrayStartIndex, userIds.length);
$arrayStartIndex += userIds.length;
return customSelect(
'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2)) GROUP BY bucket_date ORDER BY bucket_date DESC',
variables: [
i0.Variable<int>(groupBy),
for (var $ in userIds) i0.Variable<String>($),
],
readsFrom: {
remoteAssetEntity,
stackEntity,
localAssetEntity,
localAlbumAssetEntity,
localAlbumEntity,
},
).map(
(i0.QueryRow row) => MergedBucketResult(
assetCount: row.read<int>('asset_count'),
bucketDate: row.read<String>('bucket_date'),
),
);
}
i4.$RemoteAssetEntityTable get remoteAssetEntity => i1.ReadDatabaseContainer(
attachedDatabase,
).resultSet<i4.$RemoteAssetEntityTable>('remote_asset_entity');
i5.$StackEntityTable get stackEntity => i1.ReadDatabaseContainer(
attachedDatabase,
).resultSet<i5.$StackEntityTable>('stack_entity');
i3.$LocalAssetEntityTable get localAssetEntity => i1.ReadDatabaseContainer(
attachedDatabase,
).resultSet<i3.$LocalAssetEntityTable>('local_asset_entity');
i6.$LocalAlbumAssetEntityTable get localAlbumAssetEntity =>
i1.ReadDatabaseContainer(
attachedDatabase,
).resultSet<i6.$LocalAlbumAssetEntityTable>('local_album_asset_entity');
i7.$LocalAlbumEntityTable get localAlbumEntity => i1.ReadDatabaseContainer(
attachedDatabase,
).resultSet<i7.$LocalAlbumEntityTable>('local_album_entity');
}
class MergedAssetResult {
final String? remoteId;
final String? localId;
final String name;
final i2.AssetType type;
final DateTime createdAt;
final DateTime updatedAt;
final int? width;
final int? height;
final int? durationInSeconds;
final bool isFavorite;
final String? thumbHash;
final String? checksum;
final String? ownerId;
final String? livePhotoVideoId;
final int orientation;
final String? stackId;
final String? iCloudId;
final double? latitude;
final double? longitude;
final DateTime? adjustmentTime;
final bool isEdited;
MergedAssetResult({
this.remoteId,
this.localId,
required this.name,
required this.type,
required this.createdAt,
required this.updatedAt,
this.width,
this.height,
this.durationInSeconds,
required this.isFavorite,
this.thumbHash,
this.checksum,
this.ownerId,
this.livePhotoVideoId,
required this.orientation,
this.stackId,
this.iCloudId,
this.latitude,
this.longitude,
this.adjustmentTime,
required this.isEdited,
});
}
typedef MergedAsset$limit = i0.Limit Function(i3.$LocalAssetEntityTable lae);
class MergedBucketResult {
final int assetCount;
final String bucketDate;
MergedBucketResult({required this.assetCount, required this.bucketDate});
}

View file

@ -0,0 +1,16 @@
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();
TextColumn get sharedById => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get sharedWithId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
BoolColumn get inTimeline => boolean().withDefault(const Constant(false))();
@override
Set<Column> get primaryKey => {sharedById, sharedWithId};
}

View file

@ -0,0 +1,721 @@
// 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 'package:immich_mobile/infrastructure/entities/partner.entity.dart'
as i2;
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3;
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
as i4;
import 'package:drift/internal/modular.dart' as i5;
typedef $$PartnerEntityTableCreateCompanionBuilder =
i1.PartnerEntityCompanion Function({
required String sharedById,
required String sharedWithId,
i0.Value<bool> inTimeline,
});
typedef $$PartnerEntityTableUpdateCompanionBuilder =
i1.PartnerEntityCompanion Function({
i0.Value<String> sharedById,
i0.Value<String> sharedWithId,
i0.Value<bool> inTimeline,
});
final class $$PartnerEntityTableReferences
extends
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$PartnerEntityTable,
i1.PartnerEntityData
> {
$$PartnerEntityTableReferences(
super.$_db,
super.$_table,
super.$_typedResult,
);
static i4.$UserEntityTable _sharedByIdTable(i0.GeneratedDatabase db) =>
i5.ReadDatabaseContainer(db)
.resultSet<i4.$UserEntityTable>('user_entity')
.createAlias(
i0.$_aliasNameGenerator(
i5.ReadDatabaseContainer(
db,
).resultSet<i1.$PartnerEntityTable>('partner_entity').sharedById,
i5.ReadDatabaseContainer(
db,
).resultSet<i4.$UserEntityTable>('user_entity').id,
),
);
i4.$$UserEntityTableProcessedTableManager get sharedById {
final $_column = $_itemColumn<String>('shared_by_id')!;
final manager = i4
.$$UserEntityTableTableManager(
$_db,
i5.ReadDatabaseContainer(
$_db,
).resultSet<i4.$UserEntityTable>('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 i4.$UserEntityTable _sharedWithIdTable(i0.GeneratedDatabase db) =>
i5.ReadDatabaseContainer(db)
.resultSet<i4.$UserEntityTable>('user_entity')
.createAlias(
i0.$_aliasNameGenerator(
i5.ReadDatabaseContainer(db)
.resultSet<i1.$PartnerEntityTable>('partner_entity')
.sharedWithId,
i5.ReadDatabaseContainer(
db,
).resultSet<i4.$UserEntityTable>('user_entity').id,
),
);
i4.$$UserEntityTableProcessedTableManager get sharedWithId {
final $_column = $_itemColumn<String>('shared_with_id')!;
final manager = i4
.$$UserEntityTableTableManager(
$_db,
i5.ReadDatabaseContainer(
$_db,
).resultSet<i4.$UserEntityTable>('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<i0.GeneratedDatabase, i1.$PartnerEntityTable> {
$$PartnerEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<bool> get inTimeline => $composableBuilder(
column: $table.inTimeline,
builder: (column) => i0.ColumnFilters(column),
);
i4.$$UserEntityTableFilterComposer get sharedById {
final i4.$$UserEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.sharedById,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$UserEntityTableFilterComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i4.$$UserEntityTableFilterComposer get sharedWithId {
final i4.$$UserEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.sharedWithId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$UserEntityTableFilterComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$PartnerEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$PartnerEntityTable> {
$$PartnerEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<bool> get inTimeline => $composableBuilder(
column: $table.inTimeline,
builder: (column) => i0.ColumnOrderings(column),
);
i4.$$UserEntityTableOrderingComposer get sharedById {
final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.sharedById,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$UserEntityTableOrderingComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i4.$$UserEntityTableOrderingComposer get sharedWithId {
final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.sharedWithId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$UserEntityTableOrderingComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$PartnerEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$PartnerEntityTable> {
$$PartnerEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<bool> get inTimeline => $composableBuilder(
column: $table.inTimeline,
builder: (column) => column,
);
i4.$$UserEntityTableAnnotationComposer get sharedById {
final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.sharedById,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$UserEntityTableAnnotationComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i4.$$UserEntityTableAnnotationComposer get sharedWithId {
final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.sharedWithId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$UserEntityTableAnnotationComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('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<String> sharedById = const i0.Value.absent(),
i0.Value<String> sharedWithId = const i0.Value.absent(),
i0.Value<bool> inTimeline = const i0.Value.absent(),
}) => i1.PartnerEntityCompanion(
sharedById: sharedById,
sharedWithId: sharedWithId,
inTimeline: inTimeline,
),
createCompanionCallback:
({
required String sharedById,
required String sharedWithId,
i0.Value<bool> 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 i2.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<String> sharedById = i0.GeneratedColumn<String>(
'shared_by_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
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<String> sharedWithId =
i0.GeneratedColumn<String>(
'shared_with_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
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<bool> inTimeline = i0.GeneratedColumn<bool>(
'in_timeline',
aliasedName,
false,
type: i0.DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'CHECK ("in_timeline" IN (0, 1))',
),
defaultValue: const i3.Constant(false),
);
@override
List<i0.GeneratedColumn> 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<i1.PartnerEntityData> 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<i0.GeneratedColumn> get $primaryKey => {sharedById, sharedWithId};
@override
i1.PartnerEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.PartnerEntityData(
sharedById: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}shared_by_id'],
)!,
sharedWithId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
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<i1.PartnerEntityData> {
final String sharedById;
final String sharedWithId;
final bool inTimeline;
const PartnerEntityData({
required this.sharedById,
required this.sharedWithId,
required this.inTimeline,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['shared_by_id'] = i0.Variable<String>(sharedById);
map['shared_with_id'] = i0.Variable<String>(sharedWithId);
map['in_timeline'] = i0.Variable<bool>(inTimeline);
return map;
}
factory PartnerEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return PartnerEntityData(
sharedById: serializer.fromJson<String>(json['sharedById']),
sharedWithId: serializer.fromJson<String>(json['sharedWithId']),
inTimeline: serializer.fromJson<bool>(json['inTimeline']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'sharedById': serializer.toJson<String>(sharedById),
'sharedWithId': serializer.toJson<String>(sharedWithId),
'inTimeline': serializer.toJson<bool>(inTimeline),
};
}
i1.PartnerEntityData copyWith({
String? sharedById,
String? 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(sharedById, sharedWithId, inTimeline);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.PartnerEntityData &&
other.sharedById == this.sharedById &&
other.sharedWithId == this.sharedWithId &&
other.inTimeline == this.inTimeline);
}
class PartnerEntityCompanion extends i0.UpdateCompanion<i1.PartnerEntityData> {
final i0.Value<String> sharedById;
final i0.Value<String> sharedWithId;
final i0.Value<bool> inTimeline;
const PartnerEntityCompanion({
this.sharedById = const i0.Value.absent(),
this.sharedWithId = const i0.Value.absent(),
this.inTimeline = const i0.Value.absent(),
});
PartnerEntityCompanion.insert({
required String sharedById,
required String sharedWithId,
this.inTimeline = const i0.Value.absent(),
}) : sharedById = i0.Value(sharedById),
sharedWithId = i0.Value(sharedWithId);
static i0.Insertable<i1.PartnerEntityData> custom({
i0.Expression<String>? sharedById,
i0.Expression<String>? sharedWithId,
i0.Expression<bool>? 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<String>? sharedById,
i0.Value<String>? sharedWithId,
i0.Value<bool>? inTimeline,
}) {
return i1.PartnerEntityCompanion(
sharedById: sharedById ?? this.sharedById,
sharedWithId: sharedWithId ?? this.sharedWithId,
inTimeline: inTimeline ?? this.inTimeline,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (sharedById.present) {
map['shared_by_id'] = i0.Variable<String>(sharedById.value);
}
if (sharedWithId.present) {
map['shared_with_id'] = i0.Variable<String>(sharedWithId.value);
}
if (inTimeline.present) {
map['in_timeline'] = i0.Variable<bool>(inTimeline.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('PartnerEntityCompanion(')
..write('sharedById: $sharedById, ')
..write('sharedWithId: $sharedWithId, ')
..write('inTimeline: $inTimeline')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,30 @@
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 PersonEntity extends Table with DriftDefaultsMixin {
const PersonEntity();
TextColumn get id => text()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get name => text()();
TextColumn get faceAssetId => text().nullable()();
BoolColumn get isFavorite => boolean()();
BoolColumn get isHidden => boolean()();
TextColumn get color => text().nullable()();
DateTimeColumn get birthDate => dateTime().nullable()();
@override
Set<Column> get primaryKey => {id};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,31 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class RemoteAlbumEntity extends Table with DriftDefaultsMixin {
const RemoteAlbumEntity();
TextColumn get id => text()();
TextColumn get name => text()();
TextColumn get description => text().withDefault(const Constant(''))();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get thumbnailAssetId =>
text().references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull).nullable()();
BoolColumn get isActivityEnabled => boolean().withDefault(const Constant(true))();
IntColumn get order => intEnum<AlbumAssetOrder>()();
@override
Set<Column> get primaryKey => {id};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,15 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class RemoteAlbumAssetEntity extends Table with DriftDefaultsMixin {
const RemoteAlbumAssetEntity();
TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get albumId => text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)();
@override
Set<Column> get primaryKey => {assetId, albumId};
}

View file

@ -0,0 +1,668 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
as i1;
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart'
as i2;
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
as i3;
import 'package:drift/internal/modular.dart' as i4;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
as i5;
typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder =
i1.RemoteAlbumAssetEntityCompanion Function({
required String assetId,
required String albumId,
});
typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder =
i1.RemoteAlbumAssetEntityCompanion Function({
i0.Value<String> assetId,
i0.Value<String> albumId,
});
final class $$RemoteAlbumAssetEntityTableReferences
extends
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$RemoteAlbumAssetEntityTable,
i1.RemoteAlbumAssetEntityData
> {
$$RemoteAlbumAssetEntityTableReferences(
super.$_db,
super.$_table,
super.$_typedResult,
);
static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
i4.ReadDatabaseContainer(db)
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
.createAlias(
i0.$_aliasNameGenerator(
i4.ReadDatabaseContainer(db)
.resultSet<i1.$RemoteAlbumAssetEntityTable>(
'remote_album_asset_entity',
)
.assetId,
i4.ReadDatabaseContainer(
db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity').id,
),
);
i3.$$RemoteAssetEntityTableProcessedTableManager get assetId {
final $_column = $_itemColumn<String>('asset_id')!;
final manager = i3
.$$RemoteAssetEntityTableTableManager(
$_db,
i4.ReadDatabaseContainer(
$_db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]),
);
}
static i5.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
i4.ReadDatabaseContainer(db)
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
.createAlias(
i0.$_aliasNameGenerator(
i4.ReadDatabaseContainer(db)
.resultSet<i1.$RemoteAlbumAssetEntityTable>(
'remote_album_asset_entity',
)
.albumId,
i4.ReadDatabaseContainer(
db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity').id,
),
);
i5.$$RemoteAlbumEntityTableProcessedTableManager get albumId {
final $_column = $_itemColumn<String>('album_id')!;
final manager = i5
.$$RemoteAlbumEntityTableTableManager(
$_db,
i4.ReadDatabaseContainer(
$_db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_albumIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]),
);
}
}
class $$RemoteAlbumAssetEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
$$RemoteAlbumAssetEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$RemoteAssetEntityTableFilterComposer get assetId {
final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$RemoteAssetEntityTableFilterComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i5.$$RemoteAlbumEntityTableFilterComposer get albumId {
final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$RemoteAlbumEntityTableFilterComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$RemoteAlbumAssetEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
$$RemoteAlbumAssetEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$RemoteAssetEntityTableOrderingComposer get assetId {
final i3.$$RemoteAssetEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$RemoteAssetEntityTableOrderingComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i5.$$RemoteAlbumEntityTableOrderingComposer get albumId {
final i5.$$RemoteAlbumEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$RemoteAlbumEntityTableOrderingComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$RemoteAlbumAssetEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
$$RemoteAlbumAssetEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i3.$$RemoteAssetEntityTableAnnotationComposer get assetId {
final i3.$$RemoteAssetEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$RemoteAssetEntityTableAnnotationComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i5.$$RemoteAlbumEntityTableAnnotationComposer get albumId {
final i5.$$RemoteAlbumEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$RemoteAlbumEntityTableAnnotationComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$RemoteAlbumAssetEntityTableTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.$RemoteAlbumAssetEntityTable,
i1.RemoteAlbumAssetEntityData,
i1.$$RemoteAlbumAssetEntityTableFilterComposer,
i1.$$RemoteAlbumAssetEntityTableOrderingComposer,
i1.$$RemoteAlbumAssetEntityTableAnnotationComposer,
$$RemoteAlbumAssetEntityTableCreateCompanionBuilder,
$$RemoteAlbumAssetEntityTableUpdateCompanionBuilder,
(
i1.RemoteAlbumAssetEntityData,
i1.$$RemoteAlbumAssetEntityTableReferences,
),
i1.RemoteAlbumAssetEntityData,
i0.PrefetchHooks Function({bool assetId, bool albumId})
> {
$$RemoteAlbumAssetEntityTableTableManager(
i0.GeneratedDatabase db,
i1.$RemoteAlbumAssetEntityTable table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$RemoteAlbumAssetEntityTableFilterComposer(
$db: db,
$table: table,
),
createOrderingComposer: () =>
i1.$$RemoteAlbumAssetEntityTableOrderingComposer(
$db: db,
$table: table,
),
createComputedFieldComposer: () =>
i1.$$RemoteAlbumAssetEntityTableAnnotationComposer(
$db: db,
$table: table,
),
updateCompanionCallback:
({
i0.Value<String> assetId = const i0.Value.absent(),
i0.Value<String> albumId = const i0.Value.absent(),
}) => i1.RemoteAlbumAssetEntityCompanion(
assetId: assetId,
albumId: albumId,
),
createCompanionCallback:
({required String assetId, required String albumId}) =>
i1.RemoteAlbumAssetEntityCompanion.insert(
assetId: assetId,
albumId: albumId,
),
withReferenceMapper: (p0) => p0
.map(
(e) => (
e.readTable(table),
i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e),
),
)
.toList(),
prefetchHooksCallback: ({assetId = false, albumId = 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 (assetId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.assetId,
referencedTable: i1
.$$RemoteAlbumAssetEntityTableReferences
._assetIdTable(db),
referencedColumn: i1
.$$RemoteAlbumAssetEntityTableReferences
._assetIdTable(db)
.id,
)
as T;
}
if (albumId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.albumId,
referencedTable: i1
.$$RemoteAlbumAssetEntityTableReferences
._albumIdTable(db),
referencedColumn: i1
.$$RemoteAlbumAssetEntityTableReferences
._albumIdTable(db)
.id,
)
as T;
}
return state;
},
getPrefetchedDataCallback: (items) async {
return [];
},
);
},
),
);
}
typedef $$RemoteAlbumAssetEntityTableProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$RemoteAlbumAssetEntityTable,
i1.RemoteAlbumAssetEntityData,
i1.$$RemoteAlbumAssetEntityTableFilterComposer,
i1.$$RemoteAlbumAssetEntityTableOrderingComposer,
i1.$$RemoteAlbumAssetEntityTableAnnotationComposer,
$$RemoteAlbumAssetEntityTableCreateCompanionBuilder,
$$RemoteAlbumAssetEntityTableUpdateCompanionBuilder,
(
i1.RemoteAlbumAssetEntityData,
i1.$$RemoteAlbumAssetEntityTableReferences,
),
i1.RemoteAlbumAssetEntityData,
i0.PrefetchHooks Function({bool assetId, bool albumId})
>;
class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity
with
i0.TableInfo<
$RemoteAlbumAssetEntityTable,
i1.RemoteAlbumAssetEntityData
> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$RemoteAlbumAssetEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta(
'assetId',
);
@override
late final i0.GeneratedColumn<String> assetId = i0.GeneratedColumn<String>(
'asset_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id) ON DELETE CASCADE',
),
);
static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta(
'albumId',
);
@override
late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
'album_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_album_entity (id) ON DELETE CASCADE',
),
);
@override
List<i0.GeneratedColumn> get $columns => [assetId, albumId];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'remote_album_asset_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.RemoteAlbumAssetEntityData> instance, {
bool isInserting = false,
}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('asset_id')) {
context.handle(
_assetIdMeta,
assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta),
);
} else if (isInserting) {
context.missing(_assetIdMeta);
}
if (data.containsKey('album_id')) {
context.handle(
_albumIdMeta,
albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta),
);
} else if (isInserting) {
context.missing(_albumIdMeta);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {assetId, albumId};
@override
i1.RemoteAlbumAssetEntityData map(
Map<String, dynamic> data, {
String? tablePrefix,
}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.RemoteAlbumAssetEntityData(
assetId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}asset_id'],
)!,
albumId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}album_id'],
)!,
);
}
@override
$RemoteAlbumAssetEntityTable createAlias(String alias) {
return $RemoteAlbumAssetEntityTable(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class RemoteAlbumAssetEntityData extends i0.DataClass
implements i0.Insertable<i1.RemoteAlbumAssetEntityData> {
final String assetId;
final String albumId;
const RemoteAlbumAssetEntityData({
required this.assetId,
required this.albumId,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['asset_id'] = i0.Variable<String>(assetId);
map['album_id'] = i0.Variable<String>(albumId);
return map;
}
factory RemoteAlbumAssetEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return RemoteAlbumAssetEntityData(
assetId: serializer.fromJson<String>(json['assetId']),
albumId: serializer.fromJson<String>(json['albumId']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'assetId': serializer.toJson<String>(assetId),
'albumId': serializer.toJson<String>(albumId),
};
}
i1.RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) =>
i1.RemoteAlbumAssetEntityData(
assetId: assetId ?? this.assetId,
albumId: albumId ?? this.albumId,
);
RemoteAlbumAssetEntityData copyWithCompanion(
i1.RemoteAlbumAssetEntityCompanion data,
) {
return RemoteAlbumAssetEntityData(
assetId: data.assetId.present ? data.assetId.value : this.assetId,
albumId: data.albumId.present ? data.albumId.value : this.albumId,
);
}
@override
String toString() {
return (StringBuffer('RemoteAlbumAssetEntityData(')
..write('assetId: $assetId, ')
..write('albumId: $albumId')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(assetId, albumId);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.RemoteAlbumAssetEntityData &&
other.assetId == this.assetId &&
other.albumId == this.albumId);
}
class RemoteAlbumAssetEntityCompanion
extends i0.UpdateCompanion<i1.RemoteAlbumAssetEntityData> {
final i0.Value<String> assetId;
final i0.Value<String> albumId;
const RemoteAlbumAssetEntityCompanion({
this.assetId = const i0.Value.absent(),
this.albumId = const i0.Value.absent(),
});
RemoteAlbumAssetEntityCompanion.insert({
required String assetId,
required String albumId,
}) : assetId = i0.Value(assetId),
albumId = i0.Value(albumId);
static i0.Insertable<i1.RemoteAlbumAssetEntityData> custom({
i0.Expression<String>? assetId,
i0.Expression<String>? albumId,
}) {
return i0.RawValuesInsertable({
if (assetId != null) 'asset_id': assetId,
if (albumId != null) 'album_id': albumId,
});
}
i1.RemoteAlbumAssetEntityCompanion copyWith({
i0.Value<String>? assetId,
i0.Value<String>? albumId,
}) {
return i1.RemoteAlbumAssetEntityCompanion(
assetId: assetId ?? this.assetId,
albumId: albumId ?? this.albumId,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (assetId.present) {
map['asset_id'] = i0.Variable<String>(assetId.value);
}
if (albumId.present) {
map['album_id'] = i0.Variable<String>(albumId.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('RemoteAlbumAssetEntityCompanion(')
..write('assetId: $assetId, ')
..write('albumId: $albumId')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,18 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
class RemoteAlbumUserEntity extends Table with DriftDefaultsMixin {
const RemoteAlbumUserEntity();
TextColumn get albumId => text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get userId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
IntColumn get role => intEnum<AlbumUserRole>()();
@override
Set<Column> get primaryKey => {albumId, userId};
}

View file

@ -0,0 +1,739 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'
as i1;
import 'package:immich_mobile/domain/models/album/album.model.dart' as i2;
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart'
as i3;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
as i4;
import 'package:drift/internal/modular.dart' as i5;
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
as i6;
typedef $$RemoteAlbumUserEntityTableCreateCompanionBuilder =
i1.RemoteAlbumUserEntityCompanion Function({
required String albumId,
required String userId,
required i2.AlbumUserRole role,
});
typedef $$RemoteAlbumUserEntityTableUpdateCompanionBuilder =
i1.RemoteAlbumUserEntityCompanion Function({
i0.Value<String> albumId,
i0.Value<String> userId,
i0.Value<i2.AlbumUserRole> role,
});
final class $$RemoteAlbumUserEntityTableReferences
extends
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$RemoteAlbumUserEntityTable,
i1.RemoteAlbumUserEntityData
> {
$$RemoteAlbumUserEntityTableReferences(
super.$_db,
super.$_table,
super.$_typedResult,
);
static i4.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
i5.ReadDatabaseContainer(db)
.resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity')
.createAlias(
i0.$_aliasNameGenerator(
i5.ReadDatabaseContainer(db)
.resultSet<i1.$RemoteAlbumUserEntityTable>(
'remote_album_user_entity',
)
.albumId,
i5.ReadDatabaseContainer(
db,
).resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity').id,
),
);
i4.$$RemoteAlbumEntityTableProcessedTableManager get albumId {
final $_column = $_itemColumn<String>('album_id')!;
final manager = i4
.$$RemoteAlbumEntityTableTableManager(
$_db,
i5.ReadDatabaseContainer(
$_db,
).resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_albumIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]),
);
}
static i6.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) =>
i5.ReadDatabaseContainer(db)
.resultSet<i6.$UserEntityTable>('user_entity')
.createAlias(
i0.$_aliasNameGenerator(
i5.ReadDatabaseContainer(db)
.resultSet<i1.$RemoteAlbumUserEntityTable>(
'remote_album_user_entity',
)
.userId,
i5.ReadDatabaseContainer(
db,
).resultSet<i6.$UserEntityTable>('user_entity').id,
),
);
i6.$$UserEntityTableProcessedTableManager get userId {
final $_column = $_itemColumn<String>('user_id')!;
final manager = i6
.$$UserEntityTableTableManager(
$_db,
i5.ReadDatabaseContainer(
$_db,
).resultSet<i6.$UserEntityTable>('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 $$RemoteAlbumUserEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumUserEntityTable> {
$$RemoteAlbumUserEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnWithTypeConverterFilters<i2.AlbumUserRole, i2.AlbumUserRole, int>
get role => $composableBuilder(
column: $table.role,
builder: (column) => i0.ColumnWithTypeConverterFilters(column),
);
i4.$$RemoteAlbumEntityTableFilterComposer get albumId {
final i4.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$RemoteAlbumEntityTableFilterComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i6.$$UserEntityTableFilterComposer get userId {
final i6.$$UserEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.userId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i6.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i6.$$UserEntityTableFilterComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i6.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$RemoteAlbumUserEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumUserEntityTable> {
$$RemoteAlbumUserEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<int> get role => $composableBuilder(
column: $table.role,
builder: (column) => i0.ColumnOrderings(column),
);
i4.$$RemoteAlbumEntityTableOrderingComposer get albumId {
final i4.$$RemoteAlbumEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$RemoteAlbumEntityTableOrderingComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i6.$$UserEntityTableOrderingComposer get userId {
final i6.$$UserEntityTableOrderingComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.userId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i6.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i6.$$UserEntityTableOrderingComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i6.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$RemoteAlbumUserEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumUserEntityTable> {
$$RemoteAlbumUserEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumnWithTypeConverter<i2.AlbumUserRole, int> get role =>
$composableBuilder(column: $table.role, builder: (column) => column);
i4.$$RemoteAlbumEntityTableAnnotationComposer get albumId {
final i4.$$RemoteAlbumEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.albumId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$RemoteAlbumEntityTableAnnotationComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i6.$$UserEntityTableAnnotationComposer get userId {
final i6.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.userId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i6.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i6.$$UserEntityTableAnnotationComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i6.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$RemoteAlbumUserEntityTableTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.$RemoteAlbumUserEntityTable,
i1.RemoteAlbumUserEntityData,
i1.$$RemoteAlbumUserEntityTableFilterComposer,
i1.$$RemoteAlbumUserEntityTableOrderingComposer,
i1.$$RemoteAlbumUserEntityTableAnnotationComposer,
$$RemoteAlbumUserEntityTableCreateCompanionBuilder,
$$RemoteAlbumUserEntityTableUpdateCompanionBuilder,
(
i1.RemoteAlbumUserEntityData,
i1.$$RemoteAlbumUserEntityTableReferences,
),
i1.RemoteAlbumUserEntityData,
i0.PrefetchHooks Function({bool albumId, bool userId})
> {
$$RemoteAlbumUserEntityTableTableManager(
i0.GeneratedDatabase db,
i1.$RemoteAlbumUserEntityTable table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$RemoteAlbumUserEntityTableFilterComposer(
$db: db,
$table: table,
),
createOrderingComposer: () =>
i1.$$RemoteAlbumUserEntityTableOrderingComposer(
$db: db,
$table: table,
),
createComputedFieldComposer: () =>
i1.$$RemoteAlbumUserEntityTableAnnotationComposer(
$db: db,
$table: table,
),
updateCompanionCallback:
({
i0.Value<String> albumId = const i0.Value.absent(),
i0.Value<String> userId = const i0.Value.absent(),
i0.Value<i2.AlbumUserRole> role = const i0.Value.absent(),
}) => i1.RemoteAlbumUserEntityCompanion(
albumId: albumId,
userId: userId,
role: role,
),
createCompanionCallback:
({
required String albumId,
required String userId,
required i2.AlbumUserRole role,
}) => i1.RemoteAlbumUserEntityCompanion.insert(
albumId: albumId,
userId: userId,
role: role,
),
withReferenceMapper: (p0) => p0
.map(
(e) => (
e.readTable(table),
i1.$$RemoteAlbumUserEntityTableReferences(db, table, e),
),
)
.toList(),
prefetchHooksCallback: ({albumId = false, 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 (albumId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.albumId,
referencedTable: i1
.$$RemoteAlbumUserEntityTableReferences
._albumIdTable(db),
referencedColumn: i1
.$$RemoteAlbumUserEntityTableReferences
._albumIdTable(db)
.id,
)
as T;
}
if (userId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.userId,
referencedTable: i1
.$$RemoteAlbumUserEntityTableReferences
._userIdTable(db),
referencedColumn: i1
.$$RemoteAlbumUserEntityTableReferences
._userIdTable(db)
.id,
)
as T;
}
return state;
},
getPrefetchedDataCallback: (items) async {
return [];
},
);
},
),
);
}
typedef $$RemoteAlbumUserEntityTableProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$RemoteAlbumUserEntityTable,
i1.RemoteAlbumUserEntityData,
i1.$$RemoteAlbumUserEntityTableFilterComposer,
i1.$$RemoteAlbumUserEntityTableOrderingComposer,
i1.$$RemoteAlbumUserEntityTableAnnotationComposer,
$$RemoteAlbumUserEntityTableCreateCompanionBuilder,
$$RemoteAlbumUserEntityTableUpdateCompanionBuilder,
(i1.RemoteAlbumUserEntityData, i1.$$RemoteAlbumUserEntityTableReferences),
i1.RemoteAlbumUserEntityData,
i0.PrefetchHooks Function({bool albumId, bool userId})
>;
class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity
with
i0.TableInfo<
$RemoteAlbumUserEntityTable,
i1.RemoteAlbumUserEntityData
> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$RemoteAlbumUserEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta(
'albumId',
);
@override
late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
'album_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_album_entity (id) ON DELETE CASCADE',
),
);
static const i0.VerificationMeta _userIdMeta = const i0.VerificationMeta(
'userId',
);
@override
late final i0.GeneratedColumn<String> userId = i0.GeneratedColumn<String>(
'user_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE',
),
);
@override
late final i0.GeneratedColumnWithTypeConverter<i2.AlbumUserRole, int> role =
i0.GeneratedColumn<int>(
'role',
aliasedName,
false,
type: i0.DriftSqlType.int,
requiredDuringInsert: true,
).withConverter<i2.AlbumUserRole>(
i1.$RemoteAlbumUserEntityTable.$converterrole,
);
@override
List<i0.GeneratedColumn> get $columns => [albumId, userId, role];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'remote_album_user_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.RemoteAlbumUserEntityData> instance, {
bool isInserting = false,
}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('album_id')) {
context.handle(
_albumIdMeta,
albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta),
);
} else if (isInserting) {
context.missing(_albumIdMeta);
}
if (data.containsKey('user_id')) {
context.handle(
_userIdMeta,
userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta),
);
} else if (isInserting) {
context.missing(_userIdMeta);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {albumId, userId};
@override
i1.RemoteAlbumUserEntityData map(
Map<String, dynamic> data, {
String? tablePrefix,
}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.RemoteAlbumUserEntityData(
albumId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}album_id'],
)!,
userId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}user_id'],
)!,
role: i1.$RemoteAlbumUserEntityTable.$converterrole.fromSql(
attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}role'],
)!,
),
);
}
@override
$RemoteAlbumUserEntityTable createAlias(String alias) {
return $RemoteAlbumUserEntityTable(attachedDatabase, alias);
}
static i0.JsonTypeConverter2<i2.AlbumUserRole, int, int> $converterrole =
const i0.EnumIndexConverter<i2.AlbumUserRole>(i2.AlbumUserRole.values);
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class RemoteAlbumUserEntityData extends i0.DataClass
implements i0.Insertable<i1.RemoteAlbumUserEntityData> {
final String albumId;
final String userId;
final i2.AlbumUserRole role;
const RemoteAlbumUserEntityData({
required this.albumId,
required this.userId,
required this.role,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['album_id'] = i0.Variable<String>(albumId);
map['user_id'] = i0.Variable<String>(userId);
{
map['role'] = i0.Variable<int>(
i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role),
);
}
return map;
}
factory RemoteAlbumUserEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return RemoteAlbumUserEntityData(
albumId: serializer.fromJson<String>(json['albumId']),
userId: serializer.fromJson<String>(json['userId']),
role: i1.$RemoteAlbumUserEntityTable.$converterrole.fromJson(
serializer.fromJson<int>(json['role']),
),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'albumId': serializer.toJson<String>(albumId),
'userId': serializer.toJson<String>(userId),
'role': serializer.toJson<int>(
i1.$RemoteAlbumUserEntityTable.$converterrole.toJson(role),
),
};
}
i1.RemoteAlbumUserEntityData copyWith({
String? albumId,
String? userId,
i2.AlbumUserRole? role,
}) => i1.RemoteAlbumUserEntityData(
albumId: albumId ?? this.albumId,
userId: userId ?? this.userId,
role: role ?? this.role,
);
RemoteAlbumUserEntityData copyWithCompanion(
i1.RemoteAlbumUserEntityCompanion data,
) {
return RemoteAlbumUserEntityData(
albumId: data.albumId.present ? data.albumId.value : this.albumId,
userId: data.userId.present ? data.userId.value : this.userId,
role: data.role.present ? data.role.value : this.role,
);
}
@override
String toString() {
return (StringBuffer('RemoteAlbumUserEntityData(')
..write('albumId: $albumId, ')
..write('userId: $userId, ')
..write('role: $role')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(albumId, userId, role);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.RemoteAlbumUserEntityData &&
other.albumId == this.albumId &&
other.userId == this.userId &&
other.role == this.role);
}
class RemoteAlbumUserEntityCompanion
extends i0.UpdateCompanion<i1.RemoteAlbumUserEntityData> {
final i0.Value<String> albumId;
final i0.Value<String> userId;
final i0.Value<i2.AlbumUserRole> role;
const RemoteAlbumUserEntityCompanion({
this.albumId = const i0.Value.absent(),
this.userId = const i0.Value.absent(),
this.role = const i0.Value.absent(),
});
RemoteAlbumUserEntityCompanion.insert({
required String albumId,
required String userId,
required i2.AlbumUserRole role,
}) : albumId = i0.Value(albumId),
userId = i0.Value(userId),
role = i0.Value(role);
static i0.Insertable<i1.RemoteAlbumUserEntityData> custom({
i0.Expression<String>? albumId,
i0.Expression<String>? userId,
i0.Expression<int>? role,
}) {
return i0.RawValuesInsertable({
if (albumId != null) 'album_id': albumId,
if (userId != null) 'user_id': userId,
if (role != null) 'role': role,
});
}
i1.RemoteAlbumUserEntityCompanion copyWith({
i0.Value<String>? albumId,
i0.Value<String>? userId,
i0.Value<i2.AlbumUserRole>? role,
}) {
return i1.RemoteAlbumUserEntityCompanion(
albumId: albumId ?? this.albumId,
userId: userId ?? this.userId,
role: role ?? this.role,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (albumId.present) {
map['album_id'] = i0.Variable<String>(albumId.value);
}
if (userId.present) {
map['user_id'] = i0.Variable<String>(userId.value);
}
if (role.present) {
map['role'] = i0.Variable<int>(
i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role.value),
);
}
return map;
}
@override
String toString() {
return (StringBuffer('RemoteAlbumUserEntityCompanion(')
..write('albumId: $albumId, ')
..write('userId: $userId, ')
..write('role: $role')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,73 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
@TableIndex.sql(
'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)',
)
@TableIndex.sql('''
CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum
ON remote_asset_entity (owner_id, checksum)
WHERE (library_id IS NULL);
''')
@TableIndex.sql('''
CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum
ON remote_asset_entity (owner_id, library_id, checksum)
WHERE (library_id IS NOT NULL);
''')
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)')
class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
const RemoteAssetEntity();
TextColumn get id => text()();
TextColumn get checksum => text()();
BoolColumn get isFavorite => boolean().withDefault(const Constant(false))();
TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
DateTimeColumn get localDateTime => dateTime().nullable()();
TextColumn get thumbHash => text().nullable()();
DateTimeColumn get deletedAt => dateTime().nullable()();
TextColumn get livePhotoVideoId => text().nullable()();
IntColumn get visibility => intEnum<AssetVisibility>()();
TextColumn get stackId => text().nullable()();
TextColumn get libraryId => text().nullable()();
BoolColumn get isEdited => boolean().withDefault(const Constant(false))();
@override
Set<Column> get primaryKey => {id};
}
extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData {
RemoteAsset toDto({String? localId}) => RemoteAsset(
id: id,
name: name,
ownerId: ownerId,
checksum: checksum,
type: type,
createdAt: createdAt,
updatedAt: updatedAt,
durationInSeconds: durationInSeconds,
isFavorite: isFavorite,
height: height,
width: width,
thumbHash: thumbHash,
visibility: visibility,
livePhotoVideoId: livePhotoVideoId,
localId: localId,
stackId: stackId,
isEdited: isEdited,
);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_remote_asset_cloud_id ON remote_asset_cloud_id_entity (cloud_id)')
class RemoteAssetCloudIdEntity extends Table with DriftDefaultsMixin {
TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get cloudId => text().nullable()();
DateTimeColumn get createdAt => dateTime().nullable()();
DateTimeColumn get adjustmentTime => dateTime().nullable()();
RealColumn get latitude => real().nullable()();
RealColumn get longitude => real().nullable()();
@override
Set<Column> get primaryKey => {assetId};
}

View file

@ -0,0 +1,830 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.drift.dart'
as i1;
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.dart'
as i2;
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
as i3;
import 'package:drift/internal/modular.dart' as i4;
typedef $$RemoteAssetCloudIdEntityTableCreateCompanionBuilder =
i1.RemoteAssetCloudIdEntityCompanion Function({
required String assetId,
i0.Value<String?> cloudId,
i0.Value<DateTime?> createdAt,
i0.Value<DateTime?> adjustmentTime,
i0.Value<double?> latitude,
i0.Value<double?> longitude,
});
typedef $$RemoteAssetCloudIdEntityTableUpdateCompanionBuilder =
i1.RemoteAssetCloudIdEntityCompanion Function({
i0.Value<String> assetId,
i0.Value<String?> cloudId,
i0.Value<DateTime?> createdAt,
i0.Value<DateTime?> adjustmentTime,
i0.Value<double?> latitude,
i0.Value<double?> longitude,
});
final class $$RemoteAssetCloudIdEntityTableReferences
extends
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$RemoteAssetCloudIdEntityTable,
i1.RemoteAssetCloudIdEntityData
> {
$$RemoteAssetCloudIdEntityTableReferences(
super.$_db,
super.$_table,
super.$_typedResult,
);
static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
i4.ReadDatabaseContainer(db)
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
.createAlias(
i0.$_aliasNameGenerator(
i4.ReadDatabaseContainer(db)
.resultSet<i1.$RemoteAssetCloudIdEntityTable>(
'remote_asset_cloud_id_entity',
)
.assetId,
i4.ReadDatabaseContainer(
db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity').id,
),
);
i3.$$RemoteAssetEntityTableProcessedTableManager get assetId {
final $_column = $_itemColumn<String>('asset_id')!;
final manager = i3
.$$RemoteAssetEntityTableTableManager(
$_db,
i4.ReadDatabaseContainer(
$_db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]),
);
}
}
class $$RemoteAssetCloudIdEntityTableFilterComposer
extends
i0.Composer<i0.GeneratedDatabase, i1.$RemoteAssetCloudIdEntityTable> {
$$RemoteAssetCloudIdEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<String> get cloudId => $composableBuilder(
column: $table.cloudId,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<DateTime> get adjustmentTime => $composableBuilder(
column: $table.adjustmentTime,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<double> get latitude => $composableBuilder(
column: $table.latitude,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<double> get longitude => $composableBuilder(
column: $table.longitude,
builder: (column) => i0.ColumnFilters(column),
);
i3.$$RemoteAssetEntityTableFilterComposer get assetId {
final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$RemoteAssetEntityTableFilterComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$RemoteAssetCloudIdEntityTableOrderingComposer
extends
i0.Composer<i0.GeneratedDatabase, i1.$RemoteAssetCloudIdEntityTable> {
$$RemoteAssetCloudIdEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<String> get cloudId => $composableBuilder(
column: $table.cloudId,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<DateTime> get adjustmentTime => $composableBuilder(
column: $table.adjustmentTime,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<double> get latitude => $composableBuilder(
column: $table.latitude,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<double> get longitude => $composableBuilder(
column: $table.longitude,
builder: (column) => i0.ColumnOrderings(column),
);
i3.$$RemoteAssetEntityTableOrderingComposer get assetId {
final i3.$$RemoteAssetEntityTableOrderingComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$RemoteAssetEntityTableOrderingComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$RemoteAssetCloudIdEntityTableAnnotationComposer
extends
i0.Composer<i0.GeneratedDatabase, i1.$RemoteAssetCloudIdEntityTable> {
$$RemoteAssetCloudIdEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<String> get cloudId =>
$composableBuilder(column: $table.cloudId, builder: (column) => column);
i0.GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
i0.GeneratedColumn<DateTime> get adjustmentTime => $composableBuilder(
column: $table.adjustmentTime,
builder: (column) => column,
);
i0.GeneratedColumn<double> get latitude =>
$composableBuilder(column: $table.latitude, builder: (column) => column);
i0.GeneratedColumn<double> get longitude =>
$composableBuilder(column: $table.longitude, builder: (column) => column);
i3.$$RemoteAssetEntityTableAnnotationComposer get assetId {
final i3.$$RemoteAssetEntityTableAnnotationComposer composer =
$composerBuilder(
composer: this,
getCurrentColumn: (t) => t.assetId,
referencedTable: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i3.$$RemoteAssetEntityTableAnnotationComposer(
$db: $db,
$table: i4.ReadDatabaseContainer(
$db,
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$RemoteAssetCloudIdEntityTableTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.$RemoteAssetCloudIdEntityTable,
i1.RemoteAssetCloudIdEntityData,
i1.$$RemoteAssetCloudIdEntityTableFilterComposer,
i1.$$RemoteAssetCloudIdEntityTableOrderingComposer,
i1.$$RemoteAssetCloudIdEntityTableAnnotationComposer,
$$RemoteAssetCloudIdEntityTableCreateCompanionBuilder,
$$RemoteAssetCloudIdEntityTableUpdateCompanionBuilder,
(
i1.RemoteAssetCloudIdEntityData,
i1.$$RemoteAssetCloudIdEntityTableReferences,
),
i1.RemoteAssetCloudIdEntityData,
i0.PrefetchHooks Function({bool assetId})
> {
$$RemoteAssetCloudIdEntityTableTableManager(
i0.GeneratedDatabase db,
i1.$RemoteAssetCloudIdEntityTable table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$RemoteAssetCloudIdEntityTableFilterComposer(
$db: db,
$table: table,
),
createOrderingComposer: () =>
i1.$$RemoteAssetCloudIdEntityTableOrderingComposer(
$db: db,
$table: table,
),
createComputedFieldComposer: () =>
i1.$$RemoteAssetCloudIdEntityTableAnnotationComposer(
$db: db,
$table: table,
),
updateCompanionCallback:
({
i0.Value<String> assetId = const i0.Value.absent(),
i0.Value<String?> cloudId = const i0.Value.absent(),
i0.Value<DateTime?> createdAt = const i0.Value.absent(),
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
i0.Value<double?> latitude = const i0.Value.absent(),
i0.Value<double?> longitude = const i0.Value.absent(),
}) => i1.RemoteAssetCloudIdEntityCompanion(
assetId: assetId,
cloudId: cloudId,
createdAt: createdAt,
adjustmentTime: adjustmentTime,
latitude: latitude,
longitude: longitude,
),
createCompanionCallback:
({
required String assetId,
i0.Value<String?> cloudId = const i0.Value.absent(),
i0.Value<DateTime?> createdAt = const i0.Value.absent(),
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
i0.Value<double?> latitude = const i0.Value.absent(),
i0.Value<double?> longitude = const i0.Value.absent(),
}) => i1.RemoteAssetCloudIdEntityCompanion.insert(
assetId: assetId,
cloudId: cloudId,
createdAt: createdAt,
adjustmentTime: adjustmentTime,
latitude: latitude,
longitude: longitude,
),
withReferenceMapper: (p0) => p0
.map(
(e) => (
e.readTable(table),
i1.$$RemoteAssetCloudIdEntityTableReferences(db, table, e),
),
)
.toList(),
prefetchHooksCallback: ({assetId = 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 (assetId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.assetId,
referencedTable: i1
.$$RemoteAssetCloudIdEntityTableReferences
._assetIdTable(db),
referencedColumn: i1
.$$RemoteAssetCloudIdEntityTableReferences
._assetIdTable(db)
.id,
)
as T;
}
return state;
},
getPrefetchedDataCallback: (items) async {
return [];
},
);
},
),
);
}
typedef $$RemoteAssetCloudIdEntityTableProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$RemoteAssetCloudIdEntityTable,
i1.RemoteAssetCloudIdEntityData,
i1.$$RemoteAssetCloudIdEntityTableFilterComposer,
i1.$$RemoteAssetCloudIdEntityTableOrderingComposer,
i1.$$RemoteAssetCloudIdEntityTableAnnotationComposer,
$$RemoteAssetCloudIdEntityTableCreateCompanionBuilder,
$$RemoteAssetCloudIdEntityTableUpdateCompanionBuilder,
(
i1.RemoteAssetCloudIdEntityData,
i1.$$RemoteAssetCloudIdEntityTableReferences,
),
i1.RemoteAssetCloudIdEntityData,
i0.PrefetchHooks Function({bool assetId})
>;
i0.Index get idxRemoteAssetCloudId => i0.Index(
'idx_remote_asset_cloud_id',
'CREATE INDEX IF NOT EXISTS idx_remote_asset_cloud_id ON remote_asset_cloud_id_entity (cloud_id)',
);
class $RemoteAssetCloudIdEntityTable extends i2.RemoteAssetCloudIdEntity
with
i0.TableInfo<
$RemoteAssetCloudIdEntityTable,
i1.RemoteAssetCloudIdEntityData
> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$RemoteAssetCloudIdEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta(
'assetId',
);
@override
late final i0.GeneratedColumn<String> assetId = i0.GeneratedColumn<String>(
'asset_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id) ON DELETE CASCADE',
),
);
static const i0.VerificationMeta _cloudIdMeta = const i0.VerificationMeta(
'cloudId',
);
@override
late final i0.GeneratedColumn<String> cloudId = i0.GeneratedColumn<String>(
'cloud_id',
aliasedName,
true,
type: i0.DriftSqlType.string,
requiredDuringInsert: false,
);
static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta(
'createdAt',
);
@override
late final i0.GeneratedColumn<DateTime> createdAt =
i0.GeneratedColumn<DateTime>(
'created_at',
aliasedName,
true,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
);
static const i0.VerificationMeta _adjustmentTimeMeta =
const i0.VerificationMeta('adjustmentTime');
@override
late final i0.GeneratedColumn<DateTime> adjustmentTime =
i0.GeneratedColumn<DateTime>(
'adjustment_time',
aliasedName,
true,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
);
static const i0.VerificationMeta _latitudeMeta = const i0.VerificationMeta(
'latitude',
);
@override
late final i0.GeneratedColumn<double> latitude = i0.GeneratedColumn<double>(
'latitude',
aliasedName,
true,
type: i0.DriftSqlType.double,
requiredDuringInsert: false,
);
static const i0.VerificationMeta _longitudeMeta = const i0.VerificationMeta(
'longitude',
);
@override
late final i0.GeneratedColumn<double> longitude = i0.GeneratedColumn<double>(
'longitude',
aliasedName,
true,
type: i0.DriftSqlType.double,
requiredDuringInsert: false,
);
@override
List<i0.GeneratedColumn> get $columns => [
assetId,
cloudId,
createdAt,
adjustmentTime,
latitude,
longitude,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'remote_asset_cloud_id_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.RemoteAssetCloudIdEntityData> instance, {
bool isInserting = false,
}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('asset_id')) {
context.handle(
_assetIdMeta,
assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta),
);
} else if (isInserting) {
context.missing(_assetIdMeta);
}
if (data.containsKey('cloud_id')) {
context.handle(
_cloudIdMeta,
cloudId.isAcceptableOrUnknown(data['cloud_id']!, _cloudIdMeta),
);
}
if (data.containsKey('created_at')) {
context.handle(
_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
);
}
if (data.containsKey('adjustment_time')) {
context.handle(
_adjustmentTimeMeta,
adjustmentTime.isAcceptableOrUnknown(
data['adjustment_time']!,
_adjustmentTimeMeta,
),
);
}
if (data.containsKey('latitude')) {
context.handle(
_latitudeMeta,
latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta),
);
}
if (data.containsKey('longitude')) {
context.handle(
_longitudeMeta,
longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta),
);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {assetId};
@override
i1.RemoteAssetCloudIdEntityData map(
Map<String, dynamic> data, {
String? tablePrefix,
}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.RemoteAssetCloudIdEntityData(
assetId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}asset_id'],
)!,
cloudId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}cloud_id'],
),
createdAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime,
data['${effectivePrefix}created_at'],
),
adjustmentTime: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime,
data['${effectivePrefix}adjustment_time'],
),
latitude: attachedDatabase.typeMapping.read(
i0.DriftSqlType.double,
data['${effectivePrefix}latitude'],
),
longitude: attachedDatabase.typeMapping.read(
i0.DriftSqlType.double,
data['${effectivePrefix}longitude'],
),
);
}
@override
$RemoteAssetCloudIdEntityTable createAlias(String alias) {
return $RemoteAssetCloudIdEntityTable(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class RemoteAssetCloudIdEntityData extends i0.DataClass
implements i0.Insertable<i1.RemoteAssetCloudIdEntityData> {
final String assetId;
final String? cloudId;
final DateTime? createdAt;
final DateTime? adjustmentTime;
final double? latitude;
final double? longitude;
const RemoteAssetCloudIdEntityData({
required this.assetId,
this.cloudId,
this.createdAt,
this.adjustmentTime,
this.latitude,
this.longitude,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['asset_id'] = i0.Variable<String>(assetId);
if (!nullToAbsent || cloudId != null) {
map['cloud_id'] = i0.Variable<String>(cloudId);
}
if (!nullToAbsent || createdAt != null) {
map['created_at'] = i0.Variable<DateTime>(createdAt);
}
if (!nullToAbsent || adjustmentTime != null) {
map['adjustment_time'] = i0.Variable<DateTime>(adjustmentTime);
}
if (!nullToAbsent || latitude != null) {
map['latitude'] = i0.Variable<double>(latitude);
}
if (!nullToAbsent || longitude != null) {
map['longitude'] = i0.Variable<double>(longitude);
}
return map;
}
factory RemoteAssetCloudIdEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return RemoteAssetCloudIdEntityData(
assetId: serializer.fromJson<String>(json['assetId']),
cloudId: serializer.fromJson<String?>(json['cloudId']),
createdAt: serializer.fromJson<DateTime?>(json['createdAt']),
adjustmentTime: serializer.fromJson<DateTime?>(json['adjustmentTime']),
latitude: serializer.fromJson<double?>(json['latitude']),
longitude: serializer.fromJson<double?>(json['longitude']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'assetId': serializer.toJson<String>(assetId),
'cloudId': serializer.toJson<String?>(cloudId),
'createdAt': serializer.toJson<DateTime?>(createdAt),
'adjustmentTime': serializer.toJson<DateTime?>(adjustmentTime),
'latitude': serializer.toJson<double?>(latitude),
'longitude': serializer.toJson<double?>(longitude),
};
}
i1.RemoteAssetCloudIdEntityData copyWith({
String? assetId,
i0.Value<String?> cloudId = const i0.Value.absent(),
i0.Value<DateTime?> createdAt = const i0.Value.absent(),
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
i0.Value<double?> latitude = const i0.Value.absent(),
i0.Value<double?> longitude = const i0.Value.absent(),
}) => i1.RemoteAssetCloudIdEntityData(
assetId: assetId ?? this.assetId,
cloudId: cloudId.present ? cloudId.value : this.cloudId,
createdAt: createdAt.present ? createdAt.value : this.createdAt,
adjustmentTime: adjustmentTime.present
? adjustmentTime.value
: this.adjustmentTime,
latitude: latitude.present ? latitude.value : this.latitude,
longitude: longitude.present ? longitude.value : this.longitude,
);
RemoteAssetCloudIdEntityData copyWithCompanion(
i1.RemoteAssetCloudIdEntityCompanion data,
) {
return RemoteAssetCloudIdEntityData(
assetId: data.assetId.present ? data.assetId.value : this.assetId,
cloudId: data.cloudId.present ? data.cloudId.value : this.cloudId,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
adjustmentTime: data.adjustmentTime.present
? data.adjustmentTime.value
: this.adjustmentTime,
latitude: data.latitude.present ? data.latitude.value : this.latitude,
longitude: data.longitude.present ? data.longitude.value : this.longitude,
);
}
@override
String toString() {
return (StringBuffer('RemoteAssetCloudIdEntityData(')
..write('assetId: $assetId, ')
..write('cloudId: $cloudId, ')
..write('createdAt: $createdAt, ')
..write('adjustmentTime: $adjustmentTime, ')
..write('latitude: $latitude, ')
..write('longitude: $longitude')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(
assetId,
cloudId,
createdAt,
adjustmentTime,
latitude,
longitude,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.RemoteAssetCloudIdEntityData &&
other.assetId == this.assetId &&
other.cloudId == this.cloudId &&
other.createdAt == this.createdAt &&
other.adjustmentTime == this.adjustmentTime &&
other.latitude == this.latitude &&
other.longitude == this.longitude);
}
class RemoteAssetCloudIdEntityCompanion
extends i0.UpdateCompanion<i1.RemoteAssetCloudIdEntityData> {
final i0.Value<String> assetId;
final i0.Value<String?> cloudId;
final i0.Value<DateTime?> createdAt;
final i0.Value<DateTime?> adjustmentTime;
final i0.Value<double?> latitude;
final i0.Value<double?> longitude;
const RemoteAssetCloudIdEntityCompanion({
this.assetId = const i0.Value.absent(),
this.cloudId = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(),
this.adjustmentTime = const i0.Value.absent(),
this.latitude = const i0.Value.absent(),
this.longitude = const i0.Value.absent(),
});
RemoteAssetCloudIdEntityCompanion.insert({
required String assetId,
this.cloudId = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(),
this.adjustmentTime = const i0.Value.absent(),
this.latitude = const i0.Value.absent(),
this.longitude = const i0.Value.absent(),
}) : assetId = i0.Value(assetId);
static i0.Insertable<i1.RemoteAssetCloudIdEntityData> custom({
i0.Expression<String>? assetId,
i0.Expression<String>? cloudId,
i0.Expression<DateTime>? createdAt,
i0.Expression<DateTime>? adjustmentTime,
i0.Expression<double>? latitude,
i0.Expression<double>? longitude,
}) {
return i0.RawValuesInsertable({
if (assetId != null) 'asset_id': assetId,
if (cloudId != null) 'cloud_id': cloudId,
if (createdAt != null) 'created_at': createdAt,
if (adjustmentTime != null) 'adjustment_time': adjustmentTime,
if (latitude != null) 'latitude': latitude,
if (longitude != null) 'longitude': longitude,
});
}
i1.RemoteAssetCloudIdEntityCompanion copyWith({
i0.Value<String>? assetId,
i0.Value<String?>? cloudId,
i0.Value<DateTime?>? createdAt,
i0.Value<DateTime?>? adjustmentTime,
i0.Value<double?>? latitude,
i0.Value<double?>? longitude,
}) {
return i1.RemoteAssetCloudIdEntityCompanion(
assetId: assetId ?? this.assetId,
cloudId: cloudId ?? this.cloudId,
createdAt: createdAt ?? this.createdAt,
adjustmentTime: adjustmentTime ?? this.adjustmentTime,
latitude: latitude ?? this.latitude,
longitude: longitude ?? this.longitude,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (assetId.present) {
map['asset_id'] = i0.Variable<String>(assetId.value);
}
if (cloudId.present) {
map['cloud_id'] = i0.Variable<String>(cloudId.value);
}
if (createdAt.present) {
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
}
if (adjustmentTime.present) {
map['adjustment_time'] = i0.Variable<DateTime>(adjustmentTime.value);
}
if (latitude.present) {
map['latitude'] = i0.Variable<double>(latitude.value);
}
if (longitude.present) {
map['longitude'] = i0.Variable<double>(longitude.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('RemoteAssetCloudIdEntityCompanion(')
..write('assetId: $assetId, ')
..write('cloudId: $cloudId, ')
..write('createdAt: $createdAt, ')
..write('adjustmentTime: $adjustmentTime, ')
..write('latitude: $latitude, ')
..write('longitude: $longitude')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,20 @@
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 StackEntity extends Table with DriftDefaultsMixin {
const StackEntity();
TextColumn get id => text()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get primaryAssetId => text()();
@override
Set<Column> get primaryKey => {id};
}

View file

@ -0,0 +1,713 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart'
as i1;
import 'package:immich_mobile/infrastructure/entities/stack.entity.dart' as i2;
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3;
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
as i4;
import 'package:drift/internal/modular.dart' as i5;
typedef $$StackEntityTableCreateCompanionBuilder =
i1.StackEntityCompanion Function({
required String id,
i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt,
required String ownerId,
required String primaryAssetId,
});
typedef $$StackEntityTableUpdateCompanionBuilder =
i1.StackEntityCompanion Function({
i0.Value<String> id,
i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt,
i0.Value<String> ownerId,
i0.Value<String> primaryAssetId,
});
final class $$StackEntityTableReferences
extends
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$StackEntityTable,
i1.StackEntityData
> {
$$StackEntityTableReferences(super.$_db, super.$_table, super.$_typedResult);
static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) =>
i5.ReadDatabaseContainer(db)
.resultSet<i4.$UserEntityTable>('user_entity')
.createAlias(
i0.$_aliasNameGenerator(
i5.ReadDatabaseContainer(
db,
).resultSet<i1.$StackEntityTable>('stack_entity').ownerId,
i5.ReadDatabaseContainer(
db,
).resultSet<i4.$UserEntityTable>('user_entity').id,
),
);
i4.$$UserEntityTableProcessedTableManager get ownerId {
final $_column = $_itemColumn<String>('owner_id')!;
final manager = i4
.$$UserEntityTableTableManager(
$_db,
i5.ReadDatabaseContainer(
$_db,
).resultSet<i4.$UserEntityTable>('user_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_ownerIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]),
);
}
}
class $$StackEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$StackEntityTable> {
$$StackEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get primaryAssetId => $composableBuilder(
column: $table.primaryAssetId,
builder: (column) => i0.ColumnFilters(column),
);
i4.$$UserEntityTableFilterComposer get ownerId {
final i4.$$UserEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.ownerId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$UserEntityTableFilterComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$StackEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$StackEntityTable> {
$$StackEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get primaryAssetId => $composableBuilder(
column: $table.primaryAssetId,
builder: (column) => i0.ColumnOrderings(column),
);
i4.$$UserEntityTableOrderingComposer get ownerId {
final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.ownerId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$UserEntityTableOrderingComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$StackEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$StackEntityTable> {
$$StackEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<String> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
i0.GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
i0.GeneratedColumn<DateTime> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
i0.GeneratedColumn<String> get primaryAssetId => $composableBuilder(
column: $table.primaryAssetId,
builder: (column) => column,
);
i4.$$UserEntityTableAnnotationComposer get ownerId {
final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.ownerId,
referencedTable: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i4.$$UserEntityTableAnnotationComposer(
$db: $db,
$table: i5.ReadDatabaseContainer(
$db,
).resultSet<i4.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$StackEntityTableTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.$StackEntityTable,
i1.StackEntityData,
i1.$$StackEntityTableFilterComposer,
i1.$$StackEntityTableOrderingComposer,
i1.$$StackEntityTableAnnotationComposer,
$$StackEntityTableCreateCompanionBuilder,
$$StackEntityTableUpdateCompanionBuilder,
(i1.StackEntityData, i1.$$StackEntityTableReferences),
i1.StackEntityData,
i0.PrefetchHooks Function({bool ownerId})
> {
$$StackEntityTableTableManager(
i0.GeneratedDatabase db,
i1.$StackEntityTable table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$StackEntityTableFilterComposer($db: db, $table: table),
createOrderingComposer: () =>
i1.$$StackEntityTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
i1.$$StackEntityTableAnnotationComposer($db: db, $table: table),
updateCompanionCallback:
({
i0.Value<String> id = const i0.Value.absent(),
i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<String> ownerId = const i0.Value.absent(),
i0.Value<String> primaryAssetId = const i0.Value.absent(),
}) => i1.StackEntityCompanion(
id: id,
createdAt: createdAt,
updatedAt: updatedAt,
ownerId: ownerId,
primaryAssetId: primaryAssetId,
),
createCompanionCallback:
({
required String id,
i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
required String ownerId,
required String primaryAssetId,
}) => i1.StackEntityCompanion.insert(
id: id,
createdAt: createdAt,
updatedAt: updatedAt,
ownerId: ownerId,
primaryAssetId: primaryAssetId,
),
withReferenceMapper: (p0) => p0
.map(
(e) => (
e.readTable(table),
i1.$$StackEntityTableReferences(db, table, e),
),
)
.toList(),
prefetchHooksCallback: ({ownerId = 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 (ownerId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.ownerId,
referencedTable: i1.$$StackEntityTableReferences
._ownerIdTable(db),
referencedColumn: i1
.$$StackEntityTableReferences
._ownerIdTable(db)
.id,
)
as T;
}
return state;
},
getPrefetchedDataCallback: (items) async {
return [];
},
);
},
),
);
}
typedef $$StackEntityTableProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$StackEntityTable,
i1.StackEntityData,
i1.$$StackEntityTableFilterComposer,
i1.$$StackEntityTableOrderingComposer,
i1.$$StackEntityTableAnnotationComposer,
$$StackEntityTableCreateCompanionBuilder,
$$StackEntityTableUpdateCompanionBuilder,
(i1.StackEntityData, i1.$$StackEntityTableReferences),
i1.StackEntityData,
i0.PrefetchHooks Function({bool ownerId})
>;
class $StackEntityTable extends i2.StackEntity
with i0.TableInfo<$StackEntityTable, i1.StackEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$StackEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
'id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta(
'createdAt',
);
@override
late final i0.GeneratedColumn<DateTime> createdAt =
i0.GeneratedColumn<DateTime>(
'created_at',
aliasedName,
false,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: i3.currentDateAndTime,
);
static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta(
'updatedAt',
);
@override
late final i0.GeneratedColumn<DateTime> updatedAt =
i0.GeneratedColumn<DateTime>(
'updated_at',
aliasedName,
false,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: i3.currentDateAndTime,
);
static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta(
'ownerId',
);
@override
late final i0.GeneratedColumn<String> ownerId = i0.GeneratedColumn<String>(
'owner_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE',
),
);
static const i0.VerificationMeta _primaryAssetIdMeta =
const i0.VerificationMeta('primaryAssetId');
@override
late final i0.GeneratedColumn<String> primaryAssetId =
i0.GeneratedColumn<String>(
'primary_asset_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
);
@override
List<i0.GeneratedColumn> get $columns => [
id,
createdAt,
updatedAt,
ownerId,
primaryAssetId,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'stack_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.StackEntityData> 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('created_at')) {
context.handle(
_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
);
}
if (data.containsKey('updated_at')) {
context.handle(
_updatedAtMeta,
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta),
);
}
if (data.containsKey('owner_id')) {
context.handle(
_ownerIdMeta,
ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta),
);
} else if (isInserting) {
context.missing(_ownerIdMeta);
}
if (data.containsKey('primary_asset_id')) {
context.handle(
_primaryAssetIdMeta,
primaryAssetId.isAcceptableOrUnknown(
data['primary_asset_id']!,
_primaryAssetIdMeta,
),
);
} else if (isInserting) {
context.missing(_primaryAssetIdMeta);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.StackEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.StackEntityData(
id: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}id'],
)!,
createdAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime,
data['${effectivePrefix}created_at'],
)!,
updatedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime,
data['${effectivePrefix}updated_at'],
)!,
ownerId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}owner_id'],
)!,
primaryAssetId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}primary_asset_id'],
)!,
);
}
@override
$StackEntityTable createAlias(String alias) {
return $StackEntityTable(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class StackEntityData extends i0.DataClass
implements i0.Insertable<i1.StackEntityData> {
final String id;
final DateTime createdAt;
final DateTime updatedAt;
final String ownerId;
final String primaryAssetId;
const StackEntityData({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.ownerId,
required this.primaryAssetId,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<String>(id);
map['created_at'] = i0.Variable<DateTime>(createdAt);
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
map['owner_id'] = i0.Variable<String>(ownerId);
map['primary_asset_id'] = i0.Variable<String>(primaryAssetId);
return map;
}
factory StackEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return StackEntityData(
id: serializer.fromJson<String>(json['id']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
ownerId: serializer.fromJson<String>(json['ownerId']),
primaryAssetId: serializer.fromJson<String>(json['primaryAssetId']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<String>(id),
'createdAt': serializer.toJson<DateTime>(createdAt),
'updatedAt': serializer.toJson<DateTime>(updatedAt),
'ownerId': serializer.toJson<String>(ownerId),
'primaryAssetId': serializer.toJson<String>(primaryAssetId),
};
}
i1.StackEntityData copyWith({
String? id,
DateTime? createdAt,
DateTime? updatedAt,
String? ownerId,
String? primaryAssetId,
}) => i1.StackEntityData(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
);
StackEntityData copyWithCompanion(i1.StackEntityCompanion data) {
return StackEntityData(
id: data.id.present ? data.id.value : this.id,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId,
primaryAssetId: data.primaryAssetId.present
? data.primaryAssetId.value
: this.primaryAssetId,
);
}
@override
String toString() {
return (StringBuffer('StackEntityData(')
..write('id: $id, ')
..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ')
..write('ownerId: $ownerId, ')
..write('primaryAssetId: $primaryAssetId')
..write(')'))
.toString();
}
@override
int get hashCode =>
Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.StackEntityData &&
other.id == this.id &&
other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt &&
other.ownerId == this.ownerId &&
other.primaryAssetId == this.primaryAssetId);
}
class StackEntityCompanion extends i0.UpdateCompanion<i1.StackEntityData> {
final i0.Value<String> id;
final i0.Value<DateTime> createdAt;
final i0.Value<DateTime> updatedAt;
final i0.Value<String> ownerId;
final i0.Value<String> primaryAssetId;
const StackEntityCompanion({
this.id = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
this.ownerId = const i0.Value.absent(),
this.primaryAssetId = const i0.Value.absent(),
});
StackEntityCompanion.insert({
required String id,
this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
required String ownerId,
required String primaryAssetId,
}) : id = i0.Value(id),
ownerId = i0.Value(ownerId),
primaryAssetId = i0.Value(primaryAssetId);
static i0.Insertable<i1.StackEntityData> custom({
i0.Expression<String>? id,
i0.Expression<DateTime>? createdAt,
i0.Expression<DateTime>? updatedAt,
i0.Expression<String>? ownerId,
i0.Expression<String>? primaryAssetId,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (createdAt != null) 'created_at': createdAt,
if (updatedAt != null) 'updated_at': updatedAt,
if (ownerId != null) 'owner_id': ownerId,
if (primaryAssetId != null) 'primary_asset_id': primaryAssetId,
});
}
i1.StackEntityCompanion copyWith({
i0.Value<String>? id,
i0.Value<DateTime>? createdAt,
i0.Value<DateTime>? updatedAt,
i0.Value<String>? ownerId,
i0.Value<String>? primaryAssetId,
}) {
return i1.StackEntityCompanion(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<String>(id.value);
}
if (createdAt.present) {
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
}
if (updatedAt.present) {
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
}
if (ownerId.present) {
map['owner_id'] = i0.Variable<String>(ownerId.value);
}
if (primaryAssetId.present) {
map['primary_asset_id'] = i0.Variable<String>(primaryAssetId.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('StackEntityCompanion(')
..write('id: $id, ')
..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ')
..write('ownerId: $ownerId, ')
..write('primaryAssetId: $primaryAssetId')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,25 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
import 'package:isar/isar.dart';
part 'store.entity.g.dart';
/// Internal class for `Store`, do not use elsewhere.
@Collection(inheritance: false)
class StoreValue {
final Id id;
final int? intValue;
final String? strValue;
const StoreValue(this.id, {this.intValue, this.strValue});
}
class StoreEntity extends Table with DriftDefaultsMixin {
IntColumn get id => integer()();
TextColumn get stringValue => text().nullable()();
IntColumn get intValue => integer().nullable()();
@override
Set<Column> get primaryKey => {id};
}

View file

@ -0,0 +1,426 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart'
as i1;
import 'package:immich_mobile/infrastructure/entities/store.entity.dart' as i2;
typedef $$StoreEntityTableCreateCompanionBuilder =
i1.StoreEntityCompanion Function({
required int id,
i0.Value<String?> stringValue,
i0.Value<int?> intValue,
});
typedef $$StoreEntityTableUpdateCompanionBuilder =
i1.StoreEntityCompanion Function({
i0.Value<int> id,
i0.Value<String?> stringValue,
i0.Value<int?> intValue,
});
class $$StoreEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$StoreEntityTable> {
$$StoreEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<int> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get stringValue => $composableBuilder(
column: $table.stringValue,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<int> get intValue => $composableBuilder(
column: $table.intValue,
builder: (column) => i0.ColumnFilters(column),
);
}
class $$StoreEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$StoreEntityTable> {
$$StoreEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<int> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get stringValue => $composableBuilder(
column: $table.stringValue,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<int> get intValue => $composableBuilder(
column: $table.intValue,
builder: (column) => i0.ColumnOrderings(column),
);
}
class $$StoreEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$StoreEntityTable> {
$$StoreEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<int> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
i0.GeneratedColumn<String> get stringValue => $composableBuilder(
column: $table.stringValue,
builder: (column) => column,
);
i0.GeneratedColumn<int> get intValue =>
$composableBuilder(column: $table.intValue, builder: (column) => column);
}
class $$StoreEntityTableTableManager
extends
i0.RootTableManager<
i0.GeneratedDatabase,
i1.$StoreEntityTable,
i1.StoreEntityData,
i1.$$StoreEntityTableFilterComposer,
i1.$$StoreEntityTableOrderingComposer,
i1.$$StoreEntityTableAnnotationComposer,
$$StoreEntityTableCreateCompanionBuilder,
$$StoreEntityTableUpdateCompanionBuilder,
(
i1.StoreEntityData,
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$StoreEntityTable,
i1.StoreEntityData
>,
),
i1.StoreEntityData,
i0.PrefetchHooks Function()
> {
$$StoreEntityTableTableManager(
i0.GeneratedDatabase db,
i1.$StoreEntityTable table,
) : super(
i0.TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
i1.$$StoreEntityTableFilterComposer($db: db, $table: table),
createOrderingComposer: () =>
i1.$$StoreEntityTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
i1.$$StoreEntityTableAnnotationComposer($db: db, $table: table),
updateCompanionCallback:
({
i0.Value<int> id = const i0.Value.absent(),
i0.Value<String?> stringValue = const i0.Value.absent(),
i0.Value<int?> intValue = const i0.Value.absent(),
}) => i1.StoreEntityCompanion(
id: id,
stringValue: stringValue,
intValue: intValue,
),
createCompanionCallback:
({
required int id,
i0.Value<String?> stringValue = const i0.Value.absent(),
i0.Value<int?> intValue = const i0.Value.absent(),
}) => i1.StoreEntityCompanion.insert(
id: id,
stringValue: stringValue,
intValue: intValue,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
),
);
}
typedef $$StoreEntityTableProcessedTableManager =
i0.ProcessedTableManager<
i0.GeneratedDatabase,
i1.$StoreEntityTable,
i1.StoreEntityData,
i1.$$StoreEntityTableFilterComposer,
i1.$$StoreEntityTableOrderingComposer,
i1.$$StoreEntityTableAnnotationComposer,
$$StoreEntityTableCreateCompanionBuilder,
$$StoreEntityTableUpdateCompanionBuilder,
(
i1.StoreEntityData,
i0.BaseReferences<
i0.GeneratedDatabase,
i1.$StoreEntityTable,
i1.StoreEntityData
>,
),
i1.StoreEntityData,
i0.PrefetchHooks Function()
>;
class $StoreEntityTable extends i2.StoreEntity
with i0.TableInfo<$StoreEntityTable, i1.StoreEntityData> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$StoreEntityTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override
late final i0.GeneratedColumn<int> id = i0.GeneratedColumn<int>(
'id',
aliasedName,
false,
type: i0.DriftSqlType.int,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _stringValueMeta = const i0.VerificationMeta(
'stringValue',
);
@override
late final i0.GeneratedColumn<String> stringValue =
i0.GeneratedColumn<String>(
'string_value',
aliasedName,
true,
type: i0.DriftSqlType.string,
requiredDuringInsert: false,
);
static const i0.VerificationMeta _intValueMeta = const i0.VerificationMeta(
'intValue',
);
@override
late final i0.GeneratedColumn<int> intValue = i0.GeneratedColumn<int>(
'int_value',
aliasedName,
true,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
);
@override
List<i0.GeneratedColumn> get $columns => [id, stringValue, intValue];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'store_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.StoreEntityData> 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('string_value')) {
context.handle(
_stringValueMeta,
stringValue.isAcceptableOrUnknown(
data['string_value']!,
_stringValueMeta,
),
);
}
if (data.containsKey('int_value')) {
context.handle(
_intValueMeta,
intValue.isAcceptableOrUnknown(data['int_value']!, _intValueMeta),
);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.StoreEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.StoreEntityData(
id: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}id'],
)!,
stringValue: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}string_value'],
),
intValue: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}int_value'],
),
);
}
@override
$StoreEntityTable createAlias(String alias) {
return $StoreEntityTable(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class StoreEntityData extends i0.DataClass
implements i0.Insertable<i1.StoreEntityData> {
final int id;
final String? stringValue;
final int? intValue;
const StoreEntityData({required this.id, this.stringValue, this.intValue});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<int>(id);
if (!nullToAbsent || stringValue != null) {
map['string_value'] = i0.Variable<String>(stringValue);
}
if (!nullToAbsent || intValue != null) {
map['int_value'] = i0.Variable<int>(intValue);
}
return map;
}
factory StoreEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return StoreEntityData(
id: serializer.fromJson<int>(json['id']),
stringValue: serializer.fromJson<String?>(json['stringValue']),
intValue: serializer.fromJson<int?>(json['intValue']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'stringValue': serializer.toJson<String?>(stringValue),
'intValue': serializer.toJson<int?>(intValue),
};
}
i1.StoreEntityData copyWith({
int? id,
i0.Value<String?> stringValue = const i0.Value.absent(),
i0.Value<int?> intValue = const i0.Value.absent(),
}) => i1.StoreEntityData(
id: id ?? this.id,
stringValue: stringValue.present ? stringValue.value : this.stringValue,
intValue: intValue.present ? intValue.value : this.intValue,
);
StoreEntityData copyWithCompanion(i1.StoreEntityCompanion data) {
return StoreEntityData(
id: data.id.present ? data.id.value : this.id,
stringValue: data.stringValue.present
? data.stringValue.value
: this.stringValue,
intValue: data.intValue.present ? data.intValue.value : this.intValue,
);
}
@override
String toString() {
return (StringBuffer('StoreEntityData(')
..write('id: $id, ')
..write('stringValue: $stringValue, ')
..write('intValue: $intValue')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, stringValue, intValue);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.StoreEntityData &&
other.id == this.id &&
other.stringValue == this.stringValue &&
other.intValue == this.intValue);
}
class StoreEntityCompanion extends i0.UpdateCompanion<i1.StoreEntityData> {
final i0.Value<int> id;
final i0.Value<String?> stringValue;
final i0.Value<int?> intValue;
const StoreEntityCompanion({
this.id = const i0.Value.absent(),
this.stringValue = const i0.Value.absent(),
this.intValue = const i0.Value.absent(),
});
StoreEntityCompanion.insert({
required int id,
this.stringValue = const i0.Value.absent(),
this.intValue = const i0.Value.absent(),
}) : id = i0.Value(id);
static i0.Insertable<i1.StoreEntityData> custom({
i0.Expression<int>? id,
i0.Expression<String>? stringValue,
i0.Expression<int>? intValue,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (stringValue != null) 'string_value': stringValue,
if (intValue != null) 'int_value': intValue,
});
}
i1.StoreEntityCompanion copyWith({
i0.Value<int>? id,
i0.Value<String?>? stringValue,
i0.Value<int?>? intValue,
}) {
return i1.StoreEntityCompanion(
id: id ?? this.id,
stringValue: stringValue ?? this.stringValue,
intValue: intValue ?? this.intValue,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<int>(id.value);
}
if (stringValue.present) {
map['string_value'] = i0.Variable<String>(stringValue.value);
}
if (intValue.present) {
map['int_value'] = i0.Variable<int>(intValue.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('StoreEntityCompanion(')
..write('id: $id, ')
..write('stringValue: $stringValue, ')
..write('intValue: $intValue')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,596 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'store.entity.dart';
// **************************************************************************
// IsarCollectionGenerator
// **************************************************************************
// coverage:ignore-file
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
extension GetStoreValueCollection on Isar {
IsarCollection<StoreValue> get storeValues => this.collection();
}
const StoreValueSchema = CollectionSchema(
name: r'StoreValue',
id: 902899285492123510,
properties: {
r'intValue': PropertySchema(id: 0, name: r'intValue', type: IsarType.long),
r'strValue': PropertySchema(
id: 1,
name: r'strValue',
type: IsarType.string,
),
},
estimateSize: _storeValueEstimateSize,
serialize: _storeValueSerialize,
deserialize: _storeValueDeserialize,
deserializeProp: _storeValueDeserializeProp,
idName: r'id',
indexes: {},
links: {},
embeddedSchemas: {},
getId: _storeValueGetId,
getLinks: _storeValueGetLinks,
attach: _storeValueAttach,
version: '3.3.0-dev.3',
);
int _storeValueEstimateSize(
StoreValue object,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
var bytesCount = offsets.last;
{
final value = object.strValue;
if (value != null) {
bytesCount += 3 + value.length * 3;
}
}
return bytesCount;
}
void _storeValueSerialize(
StoreValue object,
IsarWriter writer,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
writer.writeLong(offsets[0], object.intValue);
writer.writeString(offsets[1], object.strValue);
}
StoreValue _storeValueDeserialize(
Id id,
IsarReader reader,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = StoreValue(
id,
intValue: reader.readLongOrNull(offsets[0]),
strValue: reader.readStringOrNull(offsets[1]),
);
return object;
}
P _storeValueDeserializeProp<P>(
IsarReader reader,
int propertyId,
int offset,
Map<Type, List<int>> allOffsets,
) {
switch (propertyId) {
case 0:
return (reader.readLongOrNull(offset)) as P;
case 1:
return (reader.readStringOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
}
Id _storeValueGetId(StoreValue object) {
return object.id;
}
List<IsarLinkBase<dynamic>> _storeValueGetLinks(StoreValue object) {
return [];
}
void _storeValueAttach(IsarCollection<dynamic> col, Id id, StoreValue object) {}
extension StoreValueQueryWhereSort
on QueryBuilder<StoreValue, StoreValue, QWhere> {
QueryBuilder<StoreValue, StoreValue, QAfterWhere> anyId() {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(const IdWhereClause.any());
});
}
}
extension StoreValueQueryWhere
on QueryBuilder<StoreValue, StoreValue, QWhereClause> {
QueryBuilder<StoreValue, StoreValue, QAfterWhereClause> idEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(lower: id, upper: id));
});
}
QueryBuilder<StoreValue, StoreValue, QAfterWhereClause> idNotEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
)
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
);
} else {
return query
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
)
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
);
}
});
}
QueryBuilder<StoreValue, StoreValue, QAfterWhereClause> idGreaterThan(
Id id, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: include),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterWhereClause> idLessThan(
Id id, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: include),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterWhereClause> idBetween(
Id lowerId,
Id upperId, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.between(
lower: lowerId,
includeLower: includeLower,
upper: upperId,
includeUpper: includeUpper,
),
);
});
}
}
extension StoreValueQueryFilter
on QueryBuilder<StoreValue, StoreValue, QFilterCondition> {
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> idEqualTo(
Id value,
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.equalTo(property: r'id', value: value),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> idGreaterThan(
Id value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.greaterThan(
include: include,
property: r'id',
value: value,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> idLessThan(
Id value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.lessThan(
include: include,
property: r'id',
value: value,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> idBetween(
Id lower,
Id upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.between(
property: r'id',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> intValueIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const FilterCondition.isNull(property: r'intValue'),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition>
intValueIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const FilterCondition.isNotNull(property: r'intValue'),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> intValueEqualTo(
int? value,
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.equalTo(property: r'intValue', value: value),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition>
intValueGreaterThan(int? value, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.greaterThan(
include: include,
property: r'intValue',
value: value,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> intValueLessThan(
int? value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.lessThan(
include: include,
property: r'intValue',
value: value,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> intValueBetween(
int? lower,
int? upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.between(
property: r'intValue',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const FilterCondition.isNull(property: r'strValue'),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition>
strValueIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const FilterCondition.isNotNull(property: r'strValue'),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueEqualTo(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.equalTo(
property: r'strValue',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition>
strValueGreaterThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.greaterThan(
include: include,
property: r'strValue',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueLessThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.lessThan(
include: include,
property: r'strValue',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueBetween(
String? lower,
String? upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.between(
property: r'strValue',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition>
strValueStartsWith(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.startsWith(
property: r'strValue',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.endsWith(
property: r'strValue',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueContains(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.contains(
property: r'strValue',
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition> strValueMatches(
String pattern, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.matches(
property: r'strValue',
wildcard: pattern,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition>
strValueIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.equalTo(property: r'strValue', value: ''),
);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterFilterCondition>
strValueIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
FilterCondition.greaterThan(property: r'strValue', value: ''),
);
});
}
}
extension StoreValueQueryObject
on QueryBuilder<StoreValue, StoreValue, QFilterCondition> {}
extension StoreValueQueryLinks
on QueryBuilder<StoreValue, StoreValue, QFilterCondition> {}
extension StoreValueQuerySortBy
on QueryBuilder<StoreValue, StoreValue, QSortBy> {
QueryBuilder<StoreValue, StoreValue, QAfterSortBy> sortByIntValue() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'intValue', Sort.asc);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterSortBy> sortByIntValueDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'intValue', Sort.desc);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterSortBy> sortByStrValue() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'strValue', Sort.asc);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterSortBy> sortByStrValueDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'strValue', Sort.desc);
});
}
}
extension StoreValueQuerySortThenBy
on QueryBuilder<StoreValue, StoreValue, QSortThenBy> {
QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenById() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.asc);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenByIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.desc);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenByIntValue() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'intValue', Sort.asc);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenByIntValueDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'intValue', Sort.desc);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenByStrValue() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'strValue', Sort.asc);
});
}
QueryBuilder<StoreValue, StoreValue, QAfterSortBy> thenByStrValueDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'strValue', Sort.desc);
});
}
}
extension StoreValueQueryWhereDistinct
on QueryBuilder<StoreValue, StoreValue, QDistinct> {
QueryBuilder<StoreValue, StoreValue, QDistinct> distinctByIntValue() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'intValue');
});
}
QueryBuilder<StoreValue, StoreValue, QDistinct> distinctByStrValue({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'strValue', caseSensitive: caseSensitive);
});
}
}
extension StoreValueQueryProperty
on QueryBuilder<StoreValue, StoreValue, QQueryProperty> {
QueryBuilder<StoreValue, int, QQueryOperations> idProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'id');
});
}
QueryBuilder<StoreValue, int?, QQueryOperations> intValueProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'intValue');
});
}
QueryBuilder<StoreValue, String?, QQueryOperations> strValueProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'strValue');
});
}
}

View file

@ -0,0 +1,50 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
enum TrashOrigin {
// do not change this order!
localSync,
remoteSync,
localUser,
}
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)')
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_album ON trashed_local_asset_entity (album_id)')
class TrashedLocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
const TrashedLocalAssetEntity();
TextColumn get id => text()();
TextColumn get albumId => text()();
TextColumn get checksum => text().nullable()();
BoolColumn get isFavorite => boolean().withDefault(const Constant(false))();
IntColumn get orientation => integer().withDefault(const Constant(0))();
IntColumn get source => intEnum<TrashOrigin>()();
@override
Set<Column> get primaryKey => {id, albumId};
}
extension TrashedLocalAssetEntityDataDomainExtension on TrashedLocalAssetEntityData {
LocalAsset toLocalAsset() => LocalAsset(
id: id,
name: name,
checksum: checksum,
type: type,
createdAt: createdAt,
updatedAt: updatedAt,
durationInSeconds: durationInSeconds,
isFavorite: isFavorite,
height: height,
width: width,
orientation: orientation,
isEdited: false,
);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,92 @@
import 'package:drift/drift.dart' hide Index;
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
import 'package:immich_mobile/utils/hash.dart';
import 'package:isar/isar.dart';
part 'user.entity.g.dart';
@Collection(inheritance: false)
class User {
Id get isarId => fastHash(id);
@Index(unique: true, replace: false, type: IndexType.hash)
final String id;
final DateTime updatedAt;
final String email;
final String name;
final bool isPartnerSharedBy;
final bool isPartnerSharedWith;
final bool isAdmin;
final String profileImagePath;
@Enumerated(EnumType.ordinal)
final AvatarColor avatarColor;
final bool memoryEnabled;
final bool inTimeline;
final int quotaUsageInBytes;
final int quotaSizeInBytes;
const User({
required this.id,
required this.updatedAt,
required this.email,
required this.name,
required this.isAdmin,
this.isPartnerSharedBy = false,
this.isPartnerSharedWith = false,
this.profileImagePath = '',
this.avatarColor = AvatarColor.primary,
this.memoryEnabled = true,
this.inTimeline = false,
this.quotaUsageInBytes = 0,
this.quotaSizeInBytes = 0,
});
static User fromDto(UserDto dto) => User(
id: dto.id,
updatedAt: dto.updatedAt ?? DateTime(2025),
email: dto.email,
name: dto.name,
isAdmin: dto.isAdmin,
isPartnerSharedBy: dto.isPartnerSharedBy,
isPartnerSharedWith: dto.isPartnerSharedWith,
profileImagePath: dto.hasProfileImage ? "HAS_PROFILE_IMAGE" : "",
avatarColor: dto.avatarColor,
memoryEnabled: dto.memoryEnabled,
inTimeline: dto.inTimeline,
quotaUsageInBytes: dto.quotaUsageInBytes,
quotaSizeInBytes: dto.quotaSizeInBytes,
);
UserDto toDto() => UserDto(
id: id,
email: email,
name: name,
isAdmin: isAdmin,
updatedAt: updatedAt,
avatarColor: avatarColor,
memoryEnabled: memoryEnabled,
inTimeline: inTimeline,
isPartnerSharedBy: isPartnerSharedBy,
isPartnerSharedWith: isPartnerSharedWith,
hasProfileImage: profileImagePath.isNotEmpty,
profileChangedAt: updatedAt,
quotaUsageInBytes: quotaUsageInBytes,
quotaSizeInBytes: quotaSizeInBytes,
);
}
class UserEntity extends Table with DriftDefaultsMixin {
const UserEntity();
TextColumn get id => text()();
TextColumn get name => text()();
TextColumn get email => text()();
// Profile image
BoolColumn get hasProfileImage => boolean().withDefault(const Constant(false))();
DateTimeColumn get profileChangedAt => dateTime().withDefault(currentDateAndTime)();
IntColumn get avatarColor => intEnum<AvatarColor>().withDefault(const Constant(0))();
@override
Set<Column> get primaryKey => {id};
}

View file

@ -0,0 +1,655 @@
// 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/domain/models/user.model.dart' 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 String id,
required String name,
required String email,
i0.Value<bool> hasProfileImage,
i0.Value<DateTime> profileChangedAt,
i0.Value<i2.AvatarColor> avatarColor,
});
typedef $$UserEntityTableUpdateCompanionBuilder =
i1.UserEntityCompanion Function({
i0.Value<String> id,
i0.Value<String> name,
i0.Value<String> email,
i0.Value<bool> hasProfileImage,
i0.Value<DateTime> profileChangedAt,
i0.Value<i2.AvatarColor> avatarColor,
});
class $$UserEntityTableFilterComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$UserEntityTable> {
$$UserEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnFilters<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get name => $composableBuilder(
column: $table.name,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<String> get email => $composableBuilder(
column: $table.email,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<bool> get hasProfileImage => $composableBuilder(
column: $table.hasProfileImage,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<DateTime> get profileChangedAt => $composableBuilder(
column: $table.profileChangedAt,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnWithTypeConverterFilters<i2.AvatarColor, i2.AvatarColor, int>
get avatarColor => $composableBuilder(
column: $table.avatarColor,
builder: (column) => i0.ColumnWithTypeConverterFilters(column),
);
}
class $$UserEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$UserEntityTable> {
$$UserEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get name => $composableBuilder(
column: $table.name,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<String> get email => $composableBuilder(
column: $table.email,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<bool> get hasProfileImage => $composableBuilder(
column: $table.hasProfileImage,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<DateTime> get profileChangedAt => $composableBuilder(
column: $table.profileChangedAt,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<int> get avatarColor => $composableBuilder(
column: $table.avatarColor,
builder: (column) => i0.ColumnOrderings(column),
);
}
class $$UserEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$UserEntityTable> {
$$UserEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumn<String> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
i0.GeneratedColumn<String> get name =>
$composableBuilder(column: $table.name, builder: (column) => column);
i0.GeneratedColumn<String> get email =>
$composableBuilder(column: $table.email, builder: (column) => column);
i0.GeneratedColumn<bool> get hasProfileImage => $composableBuilder(
column: $table.hasProfileImage,
builder: (column) => column,
);
i0.GeneratedColumn<DateTime> get profileChangedAt => $composableBuilder(
column: $table.profileChangedAt,
builder: (column) => column,
);
i0.GeneratedColumnWithTypeConverter<i2.AvatarColor, int> get avatarColor =>
$composableBuilder(
column: $table.avatarColor,
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<
i0.GeneratedDatabase,
i1.$UserEntityTable,
i1.UserEntityData
>,
),
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<String> id = const i0.Value.absent(),
i0.Value<String> name = const i0.Value.absent(),
i0.Value<String> email = const i0.Value.absent(),
i0.Value<bool> hasProfileImage = const i0.Value.absent(),
i0.Value<DateTime> profileChangedAt = const i0.Value.absent(),
i0.Value<i2.AvatarColor> avatarColor = const i0.Value.absent(),
}) => i1.UserEntityCompanion(
id: id,
name: name,
email: email,
hasProfileImage: hasProfileImage,
profileChangedAt: profileChangedAt,
avatarColor: avatarColor,
),
createCompanionCallback:
({
required String id,
required String name,
required String email,
i0.Value<bool> hasProfileImage = const i0.Value.absent(),
i0.Value<DateTime> profileChangedAt = const i0.Value.absent(),
i0.Value<i2.AvatarColor> avatarColor = const i0.Value.absent(),
}) => i1.UserEntityCompanion.insert(
id: id,
name: name,
email: email,
hasProfileImage: hasProfileImage,
profileChangedAt: profileChangedAt,
avatarColor: avatarColor,
),
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<
i0.GeneratedDatabase,
i1.$UserEntityTable,
i1.UserEntityData
>,
),
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<String> id = i0.GeneratedColumn<String>(
'id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta(
'name',
);
@override
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
'name',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _emailMeta = const i0.VerificationMeta(
'email',
);
@override
late final i0.GeneratedColumn<String> email = i0.GeneratedColumn<String>(
'email',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
);
static const i0.VerificationMeta _hasProfileImageMeta =
const i0.VerificationMeta('hasProfileImage');
@override
late final i0.GeneratedColumn<bool> hasProfileImage =
i0.GeneratedColumn<bool>(
'has_profile_image',
aliasedName,
false,
type: i0.DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'CHECK ("has_profile_image" IN (0, 1))',
),
defaultValue: const i4.Constant(false),
);
static const i0.VerificationMeta _profileChangedAtMeta =
const i0.VerificationMeta('profileChangedAt');
@override
late final i0.GeneratedColumn<DateTime> profileChangedAt =
i0.GeneratedColumn<DateTime>(
'profile_changed_at',
aliasedName,
false,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: i4.currentDateAndTime,
);
@override
late final i0.GeneratedColumnWithTypeConverter<i2.AvatarColor, int>
avatarColor = i0.GeneratedColumn<int>(
'avatar_color',
aliasedName,
false,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
defaultValue: const i4.Constant(0),
).withConverter<i2.AvatarColor>(i1.$UserEntityTable.$converteravatarColor);
@override
List<i0.GeneratedColumn> get $columns => [
id,
name,
email,
hasProfileImage,
profileChangedAt,
avatarColor,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'user_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.UserEntityData> 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('email')) {
context.handle(
_emailMeta,
email.isAcceptableOrUnknown(data['email']!, _emailMeta),
);
} else if (isInserting) {
context.missing(_emailMeta);
}
if (data.containsKey('has_profile_image')) {
context.handle(
_hasProfileImageMeta,
hasProfileImage.isAcceptableOrUnknown(
data['has_profile_image']!,
_hasProfileImageMeta,
),
);
}
if (data.containsKey('profile_changed_at')) {
context.handle(
_profileChangedAtMeta,
profileChangedAt.isAcceptableOrUnknown(
data['profile_changed_at']!,
_profileChangedAtMeta,
),
);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.UserEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.UserEntityData(
id: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}id'],
)!,
name: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}name'],
)!,
email: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}email'],
)!,
hasProfileImage: attachedDatabase.typeMapping.read(
i0.DriftSqlType.bool,
data['${effectivePrefix}has_profile_image'],
)!,
profileChangedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime,
data['${effectivePrefix}profile_changed_at'],
)!,
avatarColor: i1.$UserEntityTable.$converteravatarColor.fromSql(
attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}avatar_color'],
)!,
),
);
}
@override
$UserEntityTable createAlias(String alias) {
return $UserEntityTable(attachedDatabase, alias);
}
static i0.JsonTypeConverter2<i2.AvatarColor, int, int> $converteravatarColor =
const i0.EnumIndexConverter<i2.AvatarColor>(i2.AvatarColor.values);
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class UserEntityData extends i0.DataClass
implements i0.Insertable<i1.UserEntityData> {
final String id;
final String name;
final String email;
final bool hasProfileImage;
final DateTime profileChangedAt;
final i2.AvatarColor avatarColor;
const UserEntityData({
required this.id,
required this.name,
required this.email,
required this.hasProfileImage,
required this.profileChangedAt,
required this.avatarColor,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<String>(id);
map['name'] = i0.Variable<String>(name);
map['email'] = i0.Variable<String>(email);
map['has_profile_image'] = i0.Variable<bool>(hasProfileImage);
map['profile_changed_at'] = i0.Variable<DateTime>(profileChangedAt);
{
map['avatar_color'] = i0.Variable<int>(
i1.$UserEntityTable.$converteravatarColor.toSql(avatarColor),
);
}
return map;
}
factory UserEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return UserEntityData(
id: serializer.fromJson<String>(json['id']),
name: serializer.fromJson<String>(json['name']),
email: serializer.fromJson<String>(json['email']),
hasProfileImage: serializer.fromJson<bool>(json['hasProfileImage']),
profileChangedAt: serializer.fromJson<DateTime>(json['profileChangedAt']),
avatarColor: i1.$UserEntityTable.$converteravatarColor.fromJson(
serializer.fromJson<int>(json['avatarColor']),
),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<String>(id),
'name': serializer.toJson<String>(name),
'email': serializer.toJson<String>(email),
'hasProfileImage': serializer.toJson<bool>(hasProfileImage),
'profileChangedAt': serializer.toJson<DateTime>(profileChangedAt),
'avatarColor': serializer.toJson<int>(
i1.$UserEntityTable.$converteravatarColor.toJson(avatarColor),
),
};
}
i1.UserEntityData copyWith({
String? id,
String? name,
String? email,
bool? hasProfileImage,
DateTime? profileChangedAt,
i2.AvatarColor? avatarColor,
}) => i1.UserEntityData(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
hasProfileImage: hasProfileImage ?? this.hasProfileImage,
profileChangedAt: profileChangedAt ?? this.profileChangedAt,
avatarColor: avatarColor ?? this.avatarColor,
);
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,
email: data.email.present ? data.email.value : this.email,
hasProfileImage: data.hasProfileImage.present
? data.hasProfileImage.value
: this.hasProfileImage,
profileChangedAt: data.profileChangedAt.present
? data.profileChangedAt.value
: this.profileChangedAt,
avatarColor: data.avatarColor.present
? data.avatarColor.value
: this.avatarColor,
);
}
@override
String toString() {
return (StringBuffer('UserEntityData(')
..write('id: $id, ')
..write('name: $name, ')
..write('email: $email, ')
..write('hasProfileImage: $hasProfileImage, ')
..write('profileChangedAt: $profileChangedAt, ')
..write('avatarColor: $avatarColor')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(
id,
name,
email,
hasProfileImage,
profileChangedAt,
avatarColor,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.UserEntityData &&
other.id == this.id &&
other.name == this.name &&
other.email == this.email &&
other.hasProfileImage == this.hasProfileImage &&
other.profileChangedAt == this.profileChangedAt &&
other.avatarColor == this.avatarColor);
}
class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
final i0.Value<String> id;
final i0.Value<String> name;
final i0.Value<String> email;
final i0.Value<bool> hasProfileImage;
final i0.Value<DateTime> profileChangedAt;
final i0.Value<i2.AvatarColor> avatarColor;
const UserEntityCompanion({
this.id = const i0.Value.absent(),
this.name = const i0.Value.absent(),
this.email = const i0.Value.absent(),
this.hasProfileImage = const i0.Value.absent(),
this.profileChangedAt = const i0.Value.absent(),
this.avatarColor = const i0.Value.absent(),
});
UserEntityCompanion.insert({
required String id,
required String name,
required String email,
this.hasProfileImage = const i0.Value.absent(),
this.profileChangedAt = const i0.Value.absent(),
this.avatarColor = const i0.Value.absent(),
}) : id = i0.Value(id),
name = i0.Value(name),
email = i0.Value(email);
static i0.Insertable<i1.UserEntityData> custom({
i0.Expression<String>? id,
i0.Expression<String>? name,
i0.Expression<String>? email,
i0.Expression<bool>? hasProfileImage,
i0.Expression<DateTime>? profileChangedAt,
i0.Expression<int>? avatarColor,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (name != null) 'name': name,
if (email != null) 'email': email,
if (hasProfileImage != null) 'has_profile_image': hasProfileImage,
if (profileChangedAt != null) 'profile_changed_at': profileChangedAt,
if (avatarColor != null) 'avatar_color': avatarColor,
});
}
i1.UserEntityCompanion copyWith({
i0.Value<String>? id,
i0.Value<String>? name,
i0.Value<String>? email,
i0.Value<bool>? hasProfileImage,
i0.Value<DateTime>? profileChangedAt,
i0.Value<i2.AvatarColor>? avatarColor,
}) {
return i1.UserEntityCompanion(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
hasProfileImage: hasProfileImage ?? this.hasProfileImage,
profileChangedAt: profileChangedAt ?? this.profileChangedAt,
avatarColor: avatarColor ?? this.avatarColor,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<String>(id.value);
}
if (name.present) {
map['name'] = i0.Variable<String>(name.value);
}
if (email.present) {
map['email'] = i0.Variable<String>(email.value);
}
if (hasProfileImage.present) {
map['has_profile_image'] = i0.Variable<bool>(hasProfileImage.value);
}
if (profileChangedAt.present) {
map['profile_changed_at'] = i0.Variable<DateTime>(profileChangedAt.value);
}
if (avatarColor.present) {
map['avatar_color'] = i0.Variable<int>(
i1.$UserEntityTable.$converteravatarColor.toSql(avatarColor.value),
);
}
return map;
}
@override
String toString() {
return (StringBuffer('UserEntityCompanion(')
..write('id: $id, ')
..write('name: $name, ')
..write('email: $email, ')
..write('hasProfileImage: $hasProfileImage, ')
..write('profileChangedAt: $profileChangedAt, ')
..write('avatarColor: $avatarColor')
..write(')'))
.toString();
}
}

File diff suppressed because it is too large Load diff

View file

@ -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();
TextColumn get userId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
IntColumn get key => intEnum<UserMetadataKey>()();
BlobColumn get value => blob().map(userMetadataConverter)();
@override
Set<Column> get primaryKey => {userId, key};
}
final JsonTypeConverter2<Map<String, Object?>, Uint8List, Object?> userMetadataConverter = TypeConverter.jsonb(
fromJson: (json) => json as Map<String, Object?>,
);

View file

@ -0,0 +1,624 @@
// 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 'package:immich_mobile/domain/models/user_metadata.model.dart' as i2;
import 'dart:typed_data' 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 String userId,
required i2.UserMetadataKey key,
required Map<String, Object?> value,
});
typedef $$UserMetadataEntityTableUpdateCompanionBuilder =
i1.UserMetadataEntityCompanion Function({
i0.Value<String> userId,
i0.Value<i2.UserMetadataKey> key,
i0.Value<Map<String, Object?>> value,
});
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<i5.$UserEntityTable>('user_entity')
.createAlias(
i0.$_aliasNameGenerator(
i6.ReadDatabaseContainer(db)
.resultSet<i1.$UserMetadataEntityTable>(
'user_metadata_entity',
)
.userId,
i6.ReadDatabaseContainer(
db,
).resultSet<i5.$UserEntityTable>('user_entity').id,
),
);
i5.$$UserEntityTableProcessedTableManager get userId {
final $_column = $_itemColumn<String>('user_id')!;
final manager = i5
.$$UserEntityTableTableManager(
$_db,
i6.ReadDatabaseContainer(
$_db,
).resultSet<i5.$UserEntityTable>('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<i0.GeneratedDatabase, i1.$UserMetadataEntityTable> {
$$UserMetadataEntityTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnWithTypeConverterFilters<i2.UserMetadataKey, i2.UserMetadataKey, int>
get key => $composableBuilder(
column: $table.key,
builder: (column) => i0.ColumnWithTypeConverterFilters(column),
);
i0.ColumnWithTypeConverterFilters<
Map<String, Object?>,
Map<String, Object>,
i3.Uint8List
>
get value => $composableBuilder(
column: $table.value,
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<i5.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$UserEntityTableFilterComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$UserMetadataEntityTableOrderingComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$UserMetadataEntityTable> {
$$UserMetadataEntityTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.ColumnOrderings<int> get key => $composableBuilder(
column: $table.key,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<i3.Uint8List> get value => $composableBuilder(
column: $table.value,
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<i5.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$UserEntityTableOrderingComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
}
class $$UserMetadataEntityTableAnnotationComposer
extends i0.Composer<i0.GeneratedDatabase, i1.$UserMetadataEntityTable> {
$$UserMetadataEntityTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
i0.GeneratedColumnWithTypeConverter<i2.UserMetadataKey, int> get key =>
$composableBuilder(column: $table.key, builder: (column) => column);
i0.GeneratedColumnWithTypeConverter<Map<String, Object?>, i3.Uint8List>
get value =>
$composableBuilder(column: $table.value, builder: (column) => column);
i5.$$UserEntityTableAnnotationComposer get userId {
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.userId,
referencedTable: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$UserEntityTableAnnotationComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$UserEntityTable>('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<String> userId = const i0.Value.absent(),
i0.Value<i2.UserMetadataKey> key = const i0.Value.absent(),
i0.Value<Map<String, Object?>> value = const i0.Value.absent(),
}) => i1.UserMetadataEntityCompanion(
userId: userId,
key: key,
value: value,
),
createCompanionCallback:
({
required String userId,
required i2.UserMetadataKey key,
required Map<String, Object?> value,
}) => i1.UserMetadataEntityCompanion.insert(
userId: userId,
key: key,
value: value,
),
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<String> userId = i0.GeneratedColumn<String>(
'user_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE',
),
);
@override
late final i0.GeneratedColumnWithTypeConverter<i2.UserMetadataKey, int> key =
i0.GeneratedColumn<int>(
'key',
aliasedName,
false,
type: i0.DriftSqlType.int,
requiredDuringInsert: true,
).withConverter<i2.UserMetadataKey>(
i1.$UserMetadataEntityTable.$converterkey,
);
@override
late final i0.GeneratedColumnWithTypeConverter<
Map<String, Object?>,
i3.Uint8List
>
value =
i0.GeneratedColumn<i3.Uint8List>(
'value',
aliasedName,
false,
type: i0.DriftSqlType.blob,
requiredDuringInsert: true,
).withConverter<Map<String, Object?>>(
i1.$UserMetadataEntityTable.$convertervalue,
);
@override
List<i0.GeneratedColumn> get $columns => [userId, key, value];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'user_metadata_entity';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i1.UserMetadataEntityData> 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<i0.GeneratedColumn> get $primaryKey => {userId, key};
@override
i1.UserMetadataEntityData map(
Map<String, dynamic> data, {
String? tablePrefix,
}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.UserMetadataEntityData(
userId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}user_id'],
)!,
key: i1.$UserMetadataEntityTable.$converterkey.fromSql(
attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}key'],
)!,
),
value: i1.$UserMetadataEntityTable.$convertervalue.fromSql(
attachedDatabase.typeMapping.read(
i0.DriftSqlType.blob,
data['${effectivePrefix}value'],
)!,
),
);
}
@override
$UserMetadataEntityTable createAlias(String alias) {
return $UserMetadataEntityTable(attachedDatabase, alias);
}
static i0.JsonTypeConverter2<i2.UserMetadataKey, int, int> $converterkey =
const i0.EnumIndexConverter<i2.UserMetadataKey>(
i2.UserMetadataKey.values,
);
static i0.JsonTypeConverter2<Map<String, Object?>, i3.Uint8List, Object?>
$convertervalue = i4.userMetadataConverter;
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class UserMetadataEntityData extends i0.DataClass
implements i0.Insertable<i1.UserMetadataEntityData> {
final String userId;
final i2.UserMetadataKey key;
final Map<String, Object?> value;
const UserMetadataEntityData({
required this.userId,
required this.key,
required this.value,
});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['user_id'] = i0.Variable<String>(userId);
{
map['key'] = i0.Variable<int>(
i1.$UserMetadataEntityTable.$converterkey.toSql(key),
);
}
{
map['value'] = i0.Variable<i3.Uint8List>(
i1.$UserMetadataEntityTable.$convertervalue.toSql(value),
);
}
return map;
}
factory UserMetadataEntityData.fromJson(
Map<String, dynamic> json, {
i0.ValueSerializer? serializer,
}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return UserMetadataEntityData(
userId: serializer.fromJson<String>(json['userId']),
key: i1.$UserMetadataEntityTable.$converterkey.fromJson(
serializer.fromJson<int>(json['key']),
),
value: i1.$UserMetadataEntityTable.$convertervalue.fromJson(
serializer.fromJson<Object?>(json['value']),
),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'userId': serializer.toJson<String>(userId),
'key': serializer.toJson<int>(
i1.$UserMetadataEntityTable.$converterkey.toJson(key),
),
'value': serializer.toJson<Object?>(
i1.$UserMetadataEntityTable.$convertervalue.toJson(value),
),
};
}
i1.UserMetadataEntityData copyWith({
String? userId,
i2.UserMetadataKey? key,
Map<String, Object?>? value,
}) => i1.UserMetadataEntityData(
userId: userId ?? this.userId,
key: key ?? this.key,
value: value ?? this.value,
);
UserMetadataEntityData copyWithCompanion(
i1.UserMetadataEntityCompanion data,
) {
return UserMetadataEntityData(
userId: data.userId.present ? data.userId.value : this.userId,
key: data.key.present ? data.key.value : this.key,
value: data.value.present ? data.value.value : this.value,
);
}
@override
String toString() {
return (StringBuffer('UserMetadataEntityData(')
..write('userId: $userId, ')
..write('key: $key, ')
..write('value: $value')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(userId, key, value);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.UserMetadataEntityData &&
other.userId == this.userId &&
other.key == this.key &&
other.value == this.value);
}
class UserMetadataEntityCompanion
extends i0.UpdateCompanion<i1.UserMetadataEntityData> {
final i0.Value<String> userId;
final i0.Value<i2.UserMetadataKey> key;
final i0.Value<Map<String, Object?>> value;
const UserMetadataEntityCompanion({
this.userId = const i0.Value.absent(),
this.key = const i0.Value.absent(),
this.value = const i0.Value.absent(),
});
UserMetadataEntityCompanion.insert({
required String userId,
required i2.UserMetadataKey key,
required Map<String, Object?> value,
}) : userId = i0.Value(userId),
key = i0.Value(key),
value = i0.Value(value);
static i0.Insertable<i1.UserMetadataEntityData> custom({
i0.Expression<String>? userId,
i0.Expression<int>? key,
i0.Expression<i3.Uint8List>? value,
}) {
return i0.RawValuesInsertable({
if (userId != null) 'user_id': userId,
if (key != null) 'key': key,
if (value != null) 'value': value,
});
}
i1.UserMetadataEntityCompanion copyWith({
i0.Value<String>? userId,
i0.Value<i2.UserMetadataKey>? key,
i0.Value<Map<String, Object?>>? value,
}) {
return i1.UserMetadataEntityCompanion(
userId: userId ?? this.userId,
key: key ?? this.key,
value: value ?? this.value,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (userId.present) {
map['user_id'] = i0.Variable<String>(userId.value);
}
if (key.present) {
map['key'] = i0.Variable<int>(
i1.$UserMetadataEntityTable.$converterkey.toSql(key.value),
);
}
if (value.present) {
map['value'] = i0.Variable<i3.Uint8List>(
i1.$UserMetadataEntityTable.$convertervalue.toSql(value.value),
);
}
return map;
}
@override
String toString() {
return (StringBuffer('UserMetadataEntityCompanion(')
..write('userId: $userId, ')
..write('key: $key, ')
..write('value: $value')
..write(')'))
.toString();
}
}

View file

@ -0,0 +1,127 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:ffi/ffi.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
part 'local_image_request.dart';
part 'thumbhash_image_request.dart';
part 'remote_image_request.dart';
abstract class ImageRequest {
static int _nextRequestId = 0;
final int requestId = _nextRequestId++;
bool _isCancelled = false;
get isCancelled => _isCancelled;
ImageRequest();
Future<ImageInfo?> load(ImageDecoderCallback decode, {double scale = 1.0});
void cancel() {
if (_isCancelled) {
return;
}
_isCancelled = true;
return _onCancelled();
}
void _onCancelled();
Future<ui.FrameInfo?> _fromEncodedPlatformImage(int address, int length) async {
final pointer = Pointer<Uint8>.fromAddress(address);
if (_isCancelled) {
malloc.free(pointer);
return null;
}
final ui.ImmutableBuffer buffer;
try {
buffer = await ImmutableBuffer.fromUint8List(pointer.asTypedList(length));
} finally {
malloc.free(pointer);
}
if (_isCancelled) {
buffer.dispose();
return null;
}
final descriptor = await ui.ImageDescriptor.encoded(buffer);
buffer.dispose();
if (_isCancelled) {
descriptor.dispose();
return null;
}
final codec = await descriptor.instantiateCodec();
if (_isCancelled) {
descriptor.dispose();
codec.dispose();
return null;
}
final frame = await codec.getNextFrame();
descriptor.dispose();
codec.dispose();
if (_isCancelled) {
frame.image.dispose();
return null;
}
return frame;
}
Future<ui.FrameInfo?> _fromDecodedPlatformImage(int address, int width, int height, int rowBytes) async {
final pointer = Pointer<Uint8>.fromAddress(address);
if (_isCancelled) {
malloc.free(pointer);
return null;
}
final size = rowBytes * height;
final ui.ImmutableBuffer buffer;
try {
buffer = await ImmutableBuffer.fromUint8List(pointer.asTypedList(size));
} finally {
malloc.free(pointer);
}
if (_isCancelled) {
buffer.dispose();
return null;
}
final descriptor = ui.ImageDescriptor.raw(
buffer,
width: width,
height: height,
rowBytes: rowBytes,
pixelFormat: ui.PixelFormat.rgba8888,
);
buffer.dispose();
final codec = await descriptor.instantiateCodec();
if (_isCancelled) {
descriptor.dispose();
codec.dispose();
return null;
}
final frame = await codec.getNextFrame();
descriptor.dispose();
codec.dispose();
if (_isCancelled) {
frame.image.dispose();
return null;
}
return frame;
}
}

View file

@ -0,0 +1,38 @@
part of 'image_request.dart';
class LocalImageRequest extends ImageRequest {
final String localId;
final int width;
final int height;
final AssetType assetType;
LocalImageRequest({required this.localId, required ui.Size size, required this.assetType})
: width = size.width.toInt(),
height = size.height.toInt();
@override
Future<ImageInfo?> load(ImageDecoderCallback decode, {double scale = 1.0}) async {
if (_isCancelled) {
return null;
}
final info = await localImageApi.requestImage(
localId,
requestId: requestId,
width: width,
height: height,
isVideo: assetType == AssetType.video,
);
if (info == null) {
return null;
}
final frame = await _fromDecodedPlatformImage(info["pointer"]!, info["width"]!, info["height"]!, info["rowBytes"]!);
return frame == null ? null : ImageInfo(image: frame.image, scale: scale);
}
@override
Future<void> _onCancelled() {
return localImageApi.cancelRequest(requestId);
}
}

View file

@ -0,0 +1,29 @@
part of 'image_request.dart';
class RemoteImageRequest extends ImageRequest {
final String uri;
final Map<String, String> headers;
RemoteImageRequest({required this.uri, required this.headers});
@override
Future<ImageInfo?> load(ImageDecoderCallback decode, {double scale = 1.0}) async {
if (_isCancelled) {
return null;
}
final info = await remoteImageApi.requestImage(uri, headers: headers, requestId: requestId);
final frame = switch (info) {
{'pointer': int pointer, 'length': int length} => await _fromEncodedPlatformImage(pointer, length),
{'pointer': int pointer, 'width': int width, 'height': int height, 'rowBytes': int rowBytes} =>
await _fromDecodedPlatformImage(pointer, width, height, rowBytes),
_ => null,
};
return frame == null ? null : ImageInfo(image: frame.image, scale: scale);
}
@override
Future<void> _onCancelled() {
return remoteImageApi.cancelRequest(requestId);
}
}

View file

@ -0,0 +1,21 @@
part of 'image_request.dart';
class ThumbhashImageRequest extends ImageRequest {
final String thumbhash;
ThumbhashImageRequest({required this.thumbhash});
@override
Future<ImageInfo?> load(ImageDecoderCallback decode, {double scale = 1.0}) async {
if (_isCancelled) {
return null;
}
final Map<String, int> info = await localImageApi.getThumbhash(thumbhash);
final frame = await _fromDecodedPlatformImage(info["pointer"]!, info["width"]!, info["height"]!, info["rowBytes"]!);
return frame == null ? null : ImageInfo(image: frame.image, scale: scale);
}
@override
void _onCancelled() {}
}

View file

@ -0,0 +1,11 @@
import 'package:immich_mobile/constants/errors.dart';
class ApiRepository {
const ApiRepository();
Future<T> checkNull<T>(Future<T?> future) async {
final response = await future;
if (response == null) throw const NoResponseDtoError();
return response;
}
}

View file

@ -0,0 +1,117 @@
import 'dart:async';
import 'package:drift/drift.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
final backupRepositoryProvider = Provider<DriftBackupRepository>(
(ref) => DriftBackupRepository(ref.watch(driftProvider)),
);
class DriftBackupRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftBackupRepository(this._db) : super(_db);
_getExcludedSubquery() {
return _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId])
..join([
innerJoin(
_db.localAlbumEntity,
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
useColumns: false,
),
])
..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.excluded));
}
/// Returns all backup-related counts in a single query.
///
/// - total: number of distinct assets in selected albums, excluding those that are also in excluded albums
/// - backup: number of those assets that already exist on the server for [userId]
/// - remainder: number of those assets that do not yet exist on the server for [userId]
/// (includes processing)
/// - processing: number of those assets that are still preparing/have a null checksum
Future<({int total, int remainder, int processing})> getAllCounts(String userId) async {
const sql = '''
SELECT
COUNT(*) AS total_count,
COUNT(*) FILTER (WHERE lae.checksum IS NULL) AS processing_count,
COUNT(*) FILTER (WHERE rae.id IS NULL) AS remainder_count
FROM local_asset_entity lae
LEFT JOIN main.remote_asset_entity rae
ON lae.checksum = rae.checksum AND rae.owner_id = ?1
WHERE EXISTS (
SELECT 1
FROM local_album_asset_entity laa
INNER JOIN main.local_album_entity la on laa.album_id = la.id
WHERE laa.asset_id = lae.id
AND la.backup_selection = ?2
)
AND NOT EXISTS (
SELECT 1
FROM local_album_asset_entity laa
INNER JOIN main.local_album_entity la on laa.album_id = la.id
WHERE laa.asset_id = lae.id
AND la.backup_selection = ?3
);
''';
final row = await _db
.customSelect(
sql,
variables: [
Variable.withString(userId),
Variable.withInt(BackupSelection.selected.index),
Variable.withInt(BackupSelection.excluded.index),
],
readsFrom: {_db.localAlbumAssetEntity, _db.localAlbumEntity, _db.localAssetEntity, _db.remoteAssetEntity},
)
.getSingle();
final data = row.data;
return (
total: (data['total_count'] as int?) ?? 0,
remainder: (data['remainder_count'] as int?) ?? 0,
processing: (data['processing_count'] as int?) ?? 0,
);
}
Future<List<LocalAsset>> getCandidates(String userId, {bool onlyHashed = true}) async {
final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true)
..addColumns([_db.localAlbumEntity.id])
..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected));
final query = _db.localAssetEntity.select()
..where(
(lae) =>
existsQuery(
_db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId])
..where(
_db.localAlbumAssetEntity.albumId.isInQuery(selectedAlbumIds) &
_db.localAlbumAssetEntity.assetId.equalsExp(lae.id),
),
) &
notExistsQuery(
_db.remoteAssetEntity.selectOnly()
..addColumns([_db.remoteAssetEntity.checksum])
..where(
_db.remoteAssetEntity.checksum.equalsExp(lae.checksum) & _db.remoteAssetEntity.ownerId.equals(userId),
),
) &
lae.id.isNotInQuery(_getExcludedSubquery()),
)
..orderBy([(localAsset) => OrderingTerm.desc(localAsset.createdAt)]);
if (onlyHashed) {
query.where((lae) => lae.checksum.isNotNull());
}
return query.map((localAsset) => localAsset.toDto()).get();
}
}

View file

@ -0,0 +1,242 @@
import 'dart:async';
import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart';
import 'package:flutter/foundation.dart';
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart';
import 'package:immich_mobile/infrastructure/entities/auth_user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/memory.entity.dart';
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
import 'package:immich_mobile/infrastructure/entities/person.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.dart';
import 'package:immich_mobile/infrastructure/entities/stack.entity.dart';
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.steps.dart';
import 'package:isar/isar.dart' hide Index;
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;
class IsarDatabaseRepository implements IDatabaseRepository {
final Isar _db;
const IsarDatabaseRepository(Isar db) : _db = db;
// Isar do not support nested transactions. This is a workaround to prevent us from making nested transactions
// Reuse the current transaction if it is already active, else start a new transaction
@override
Future<T> transaction<T>(Future<T> Function() callback) =>
Zone.current[_kzoneTxn] == null ? _db.writeTxn(callback) : callback();
}
@DriftDatabase(
tables: [
AuthUserEntity,
UserEntity,
UserMetadataEntity,
PartnerEntity,
LocalAlbumEntity,
LocalAssetEntity,
LocalAlbumAssetEntity,
RemoteAssetEntity,
RemoteExifEntity,
RemoteAlbumEntity,
RemoteAlbumAssetEntity,
RemoteAlbumUserEntity,
RemoteAssetCloudIdEntity,
MemoryEntity,
MemoryAssetEntity,
StackEntity,
PersonEntity,
AssetFaceEntity,
StoreEntity,
TrashedLocalAssetEntity,
],
include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'},
)
class Drift extends $Drift implements IDatabaseRepository {
Drift([QueryExecutor? executor])
: super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true)));
Future<void> reset() async {
// https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94
await exclusively(() async {
// https://stackoverflow.com/a/65743498/25690041
await customStatement('PRAGMA writable_schema = 1;');
await customStatement('DELETE FROM sqlite_master;');
await customStatement('VACUUM;');
await customStatement('PRAGMA writable_schema = 0;');
await customStatement('PRAGMA integrity_check');
await customStatement('PRAGMA user_version = 0');
await beforeOpen(
// ignore: invalid_use_of_internal_member
resolvedEngine.executor,
OpeningDetails(null, schemaVersion),
);
await customStatement('PRAGMA user_version = $schemaVersion');
// Refresh all stream queries
notifyUpdates({for (final table in allTables) TableUpdate.onTable(table)});
});
}
@override
int get schemaVersion => 18;
@override
MigrationStrategy get migration => MigrationStrategy(
onUpgrade: (m, from, to) async {
// Run migration steps without foreign keys and re-enable them later
await customStatement('PRAGMA foreign_keys = OFF');
await m.runMigrationSteps(
from: from,
to: to,
steps: migrationSteps(
from1To2: (m, v2) async {
for (final entity in v2.entities) {
await m.drop(entity);
await m.create(entity);
}
},
from2To3: (m, v3) async {
// Removed foreign key constraint on stack.primaryAssetId
await m.alterTable(TableMigration(v3.stackEntity));
},
from3To4: (m, v4) async {
// Thumbnail path column got removed from person_entity
await m.alterTable(TableMigration(v4.personEntity));
// asset_face_entity is added
await m.create(v4.assetFaceEntity);
},
from4To5: (m, v5) async {
await m.alterTable(
TableMigration(
v5.userEntity,
newColumns: [v5.userEntity.hasProfileImage, v5.userEntity.profileChangedAt],
columnTransformer: {v5.userEntity.profileChangedAt: currentDateAndTime},
),
);
},
from5To6: (m, v6) async {
// Drops the (checksum, ownerId) and adds it back as (ownerId, checksum)
await customStatement('DROP INDEX IF EXISTS UQ_remote_asset_owner_checksum');
await m.drop(v6.idxRemoteAssetOwnerChecksum);
await m.create(v6.idxRemoteAssetOwnerChecksum);
// Adds libraryId to remote_asset_entity
await m.addColumn(v6.remoteAssetEntity, v6.remoteAssetEntity.libraryId);
await m.drop(v6.uQRemoteAssetsOwnerChecksum);
await m.create(v6.uQRemoteAssetsOwnerChecksum);
await m.drop(v6.uQRemoteAssetsOwnerLibraryChecksum);
await m.create(v6.uQRemoteAssetsOwnerLibraryChecksum);
},
from6To7: (m, v7) async {
await m.createIndex(v7.idxLatLng);
},
from7To8: (m, v8) async {
await m.create(v8.storeEntity);
},
from8To9: (m, v9) async {
await m.addColumn(v9.localAlbumEntity, v9.localAlbumEntity.linkedRemoteAlbumId);
},
from9To10: (m, v10) async {
await m.createTable(v10.authUserEntity);
await m.addColumn(v10.userEntity, v10.userEntity.avatarColor);
await m.alterTable(TableMigration(v10.userEntity));
},
from10To11: (m, v11) async {
await m.addColumn(v11.localAlbumAssetEntity, v11.localAlbumAssetEntity.marker_);
},
from11To12: (m, v12) async {
final localToUTCMapping = {
v12.localAssetEntity: [v12.localAssetEntity.createdAt, v12.localAssetEntity.updatedAt],
v12.localAlbumEntity: [v12.localAlbumEntity.updatedAt],
};
for (final entry in localToUTCMapping.entries) {
final table = entry.key;
await m.alterTable(
TableMigration(
table,
columnTransformer: {
for (final column in entry.value)
column: column.modify(const DateTimeModifier.utc()).strftime('%Y-%m-%dT%H:%M:%fZ'),
},
),
);
}
},
from12To13: (m, v13) async {
await m.create(v13.trashedLocalAssetEntity);
await m.createIndex(v13.idxTrashedLocalAssetChecksum);
await m.createIndex(v13.idxTrashedLocalAssetAlbum);
},
from13To14: (m, v14) async {
await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.adjustmentTime);
await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.latitude);
await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.longitude);
},
from14To15: (m, v15) async {
await m.alterTable(
TableMigration(
v15.trashedLocalAssetEntity,
columnTransformer: {v15.trashedLocalAssetEntity.source: Constant(TrashOrigin.localSync.index)},
newColumns: [v15.trashedLocalAssetEntity.source],
),
);
},
from15To16: (m, v16) async {
// Add i_cloud_id to local and remote asset tables
await m.addColumn(v16.localAssetEntity, v16.localAssetEntity.iCloudId);
await m.createIndex(v16.idxLocalAssetCloudId);
await m.createTable(v16.remoteAssetCloudIdEntity);
},
from16To17: (m, v17) async {
await m.addColumn(v17.remoteAssetEntity, v17.remoteAssetEntity.isEdited);
},
from17To18: (m, v18) async {
await m.createIndex(v18.idxRemoteAssetCloudId);
},
),
);
if (kDebugMode) {
// Fail if the migration broke foreign keys
final wrongFKs = await customSelect('PRAGMA foreign_key_check').get();
assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}');
}
await customStatement('PRAGMA foreign_keys = ON;');
},
beforeOpen: (details) async {
await customStatement('PRAGMA foreign_keys = ON');
await customStatement('PRAGMA synchronous = NORMAL');
await customStatement('PRAGMA journal_mode = WAL');
await customStatement('PRAGMA busy_timeout = 30000');
},
);
}
class DriftDatabaseRepository implements IDatabaseRepository {
final Drift _db;
const DriftDatabaseRepository(this._db);
@override
Future<T> transaction<T>(Future<T> Function() callback) => _db.transaction(callback);
}

View file

@ -0,0 +1,376 @@
// 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/remote_asset.entity.drift.dart'
as i2;
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart'
as i3;
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
as i4;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
as i5;
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
as i6;
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
as i7;
import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart'
as i8;
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
as i9;
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
as i10;
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
as i11;
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
as i12;
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'
as i13;
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.drift.dart'
as i14;
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'
as i15;
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'
as i16;
import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'
as i17;
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'
as i18;
import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart'
as i19;
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart'
as i20;
import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart'
as i21;
import 'package:drift/internal/modular.dart' as i22;
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.$RemoteAssetEntityTable remoteAssetEntity = i2
.$RemoteAssetEntityTable(this);
late final i3.$StackEntityTable stackEntity = i3.$StackEntityTable(this);
late final i4.$LocalAssetEntityTable localAssetEntity = i4
.$LocalAssetEntityTable(this);
late final i5.$RemoteAlbumEntityTable remoteAlbumEntity = i5
.$RemoteAlbumEntityTable(this);
late final i6.$LocalAlbumEntityTable localAlbumEntity = i6
.$LocalAlbumEntityTable(this);
late final i7.$LocalAlbumAssetEntityTable localAlbumAssetEntity = i7
.$LocalAlbumAssetEntityTable(this);
late final i8.$AuthUserEntityTable authUserEntity = i8.$AuthUserEntityTable(
this,
);
late final i9.$UserMetadataEntityTable userMetadataEntity = i9
.$UserMetadataEntityTable(this);
late final i10.$PartnerEntityTable partnerEntity = i10.$PartnerEntityTable(
this,
);
late final i11.$RemoteExifEntityTable remoteExifEntity = i11
.$RemoteExifEntityTable(this);
late final i12.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = i12
.$RemoteAlbumAssetEntityTable(this);
late final i13.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = i13
.$RemoteAlbumUserEntityTable(this);
late final i14.$RemoteAssetCloudIdEntityTable remoteAssetCloudIdEntity = i14
.$RemoteAssetCloudIdEntityTable(this);
late final i15.$MemoryEntityTable memoryEntity = i15.$MemoryEntityTable(this);
late final i16.$MemoryAssetEntityTable memoryAssetEntity = i16
.$MemoryAssetEntityTable(this);
late final i17.$PersonEntityTable personEntity = i17.$PersonEntityTable(this);
late final i18.$AssetFaceEntityTable assetFaceEntity = i18
.$AssetFaceEntityTable(this);
late final i19.$StoreEntityTable storeEntity = i19.$StoreEntityTable(this);
late final i20.$TrashedLocalAssetEntityTable trashedLocalAssetEntity = i20
.$TrashedLocalAssetEntityTable(this);
i21.MergedAssetDrift get mergedAssetDrift => i22.ReadDatabaseContainer(
this,
).accessor<i21.MergedAssetDrift>(i21.MergedAssetDrift.new);
@override
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
@override
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [
userEntity,
remoteAssetEntity,
stackEntity,
localAssetEntity,
remoteAlbumEntity,
localAlbumEntity,
localAlbumAssetEntity,
i4.idxLocalAssetChecksum,
i4.idxLocalAssetCloudId,
i2.idxRemoteAssetOwnerChecksum,
i2.uQRemoteAssetsOwnerChecksum,
i2.uQRemoteAssetsOwnerLibraryChecksum,
i2.idxRemoteAssetChecksum,
authUserEntity,
userMetadataEntity,
partnerEntity,
remoteExifEntity,
remoteAlbumAssetEntity,
remoteAlbumUserEntity,
remoteAssetCloudIdEntity,
memoryEntity,
memoryAssetEntity,
personEntity,
assetFaceEntity,
storeEntity,
trashedLocalAssetEntity,
i11.idxLatLng,
i14.idxRemoteAssetCloudId,
i20.idxTrashedLocalAssetChecksum,
i20.idxTrashedLocalAssetAlbum,
];
@override
i0.StreamQueryUpdateRules
get streamUpdateRules => const i0.StreamQueryUpdateRules([
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'user_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'user_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete)],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'user_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'remote_asset_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'remote_album_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('local_album_entity', kind: i0.UpdateKind.update),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'local_asset_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('local_album_asset_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'local_album_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('local_album_asset_entity', kind: i0.UpdateKind.delete),
],
),
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)],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'remote_asset_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'remote_asset_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('remote_album_asset_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'remote_album_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('remote_album_asset_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'remote_album_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'user_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'remote_asset_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate(
'remote_asset_cloud_id_entity',
kind: i0.UpdateKind.delete,
),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'user_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [i0.TableUpdate('memory_entity', kind: i0.UpdateKind.delete)],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'remote_asset_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'memory_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'user_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [i0.TableUpdate('person_entity', kind: i0.UpdateKind.delete)],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'remote_asset_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.delete)],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'person_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.update)],
),
]);
@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.$$RemoteAssetEntityTableTableManager get remoteAssetEntity =>
i2.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity);
i3.$$StackEntityTableTableManager get stackEntity =>
i3.$$StackEntityTableTableManager(_db, _db.stackEntity);
i4.$$LocalAssetEntityTableTableManager get localAssetEntity =>
i4.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity);
i5.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
i5.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
i6.$$LocalAlbumEntityTableTableManager get localAlbumEntity =>
i6.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity);
i7.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i7
.$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity);
i8.$$AuthUserEntityTableTableManager get authUserEntity =>
i8.$$AuthUserEntityTableTableManager(_db, _db.authUserEntity);
i9.$$UserMetadataEntityTableTableManager get userMetadataEntity =>
i9.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity);
i10.$$PartnerEntityTableTableManager get partnerEntity =>
i10.$$PartnerEntityTableTableManager(_db, _db.partnerEntity);
i11.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
i11.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
i12.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
i12.$$RemoteAlbumAssetEntityTableTableManager(
_db,
_db.remoteAlbumAssetEntity,
);
i13.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i13
.$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity);
i14.$$RemoteAssetCloudIdEntityTableTableManager
get remoteAssetCloudIdEntity =>
i14.$$RemoteAssetCloudIdEntityTableTableManager(
_db,
_db.remoteAssetCloudIdEntity,
);
i15.$$MemoryEntityTableTableManager get memoryEntity =>
i15.$$MemoryEntityTableTableManager(_db, _db.memoryEntity);
i16.$$MemoryAssetEntityTableTableManager get memoryAssetEntity =>
i16.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity);
i17.$$PersonEntityTableTableManager get personEntity =>
i17.$$PersonEntityTableTableManager(_db, _db.personEntity);
i18.$$AssetFaceEntityTableTableManager get assetFaceEntity =>
i18.$$AssetFaceEntityTableTableManager(_db, _db.assetFaceEntity);
i19.$$StoreEntityTableTableManager get storeEntity =>
i19.$$StoreEntityTableTableManager(_db, _db.storeEntity);
i20.$$TrashedLocalAssetEntityTableTableManager get trashedLocalAssetEntity =>
i20.$$TrashedLocalAssetEntityTableTableManager(
_db,
_db.trashedLocalAssetEntity,
);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,31 @@
import 'package:immich_mobile/domain/models/device_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:isar/isar.dart';
class IsarDeviceAssetRepository extends IsarDatabaseRepository {
final Isar _db;
const IsarDeviceAssetRepository(this._db) : super(_db);
Future<void> deleteIds(List<String> ids) {
return transaction(() async {
await _db.deviceAssetEntitys.deleteAllByAssetId(ids.toList());
});
}
Future<List<DeviceAsset>> getByIds(List<String> localIds) {
return _db.deviceAssetEntitys
.where()
.anyOf(localIds, (query, id) => query.assetIdEqualTo(id))
.findAll()
.then((value) => value.map((e) => e.toModel()).toList());
}
Future<bool> updateAll(List<DeviceAsset> assetHash) {
return transaction(() async {
await _db.deviceAssetEntitys.putAll(assetHash.map(DeviceAssetEntity.fromDto).toList());
return true;
});
}
}

View file

@ -0,0 +1,40 @@
import 'package:immich_mobile/domain/models/exif.model.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' as entity;
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:isar/isar.dart';
class IsarExifRepository extends IsarDatabaseRepository {
final Isar _db;
const IsarExifRepository(this._db) : super(_db);
Future<void> delete(int assetId) async {
await transaction(() async {
await _db.exifInfos.delete(assetId);
});
}
Future<void> deleteAll() async {
await transaction(() async {
await _db.exifInfos.clear();
});
}
Future<ExifInfo?> get(int assetId) async {
return (await _db.exifInfos.get(assetId))?.toDto();
}
Future<ExifInfo> update(ExifInfo exifInfo) {
return transaction(() async {
await _db.exifInfos.put(entity.ExifInfo.fromDto(exifInfo));
return exifInfo;
});
}
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos) {
return transaction(() async {
await _db.exifInfos.putAll(exifInfos.map(entity.ExifInfo.fromDto).toList());
return exifInfos;
});
}
}

View file

@ -0,0 +1,443 @@
import 'dart:async';
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum, name, assetCount, newestAsset }
class DriftLocalAlbumRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftLocalAlbumRepository(this._db) : super(_db);
Future<List<LocalAlbum>> getAll({Set<SortLocalAlbumsBy> sortBy = const {}}) {
final assetCount = _db.localAlbumAssetEntity.assetId.count();
final query = _db.localAlbumEntity.select().join([
leftOuterJoin(
_db.localAlbumAssetEntity,
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
useColumns: false,
),
]);
query
..addColumns([assetCount])
..groupBy([_db.localAlbumEntity.id]);
if (sortBy.isNotEmpty) {
final orderings = <OrderingTerm>[];
for (final sort in sortBy) {
orderings.add(switch (sort) {
SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id),
SortLocalAlbumsBy.backupSelection => OrderingTerm.asc(_db.localAlbumEntity.backupSelection),
SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum),
SortLocalAlbumsBy.name => OrderingTerm.asc(_db.localAlbumEntity.name),
SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount),
SortLocalAlbumsBy.newestAsset => OrderingTerm.desc(_db.localAlbumEntity.updatedAt),
});
}
query.orderBy(orderings);
}
return query.map((row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0)).get();
}
Future<List<LocalAlbum>> getBackupAlbums() async {
final query = _db.localAlbumEntity.select()
..where((row) => row.backupSelection.equalsValue(BackupSelection.selected));
return query.map((row) => row.toDto()).get();
}
Future<void> delete(String albumId) => transaction(() async {
// Remove all assets that are only in this particular album
// We cannot remove all assets in the album because they might be in other albums in iOS
// That is not the case on Android since asset <-> album has one:one mapping
final assetsToDelete = CurrentPlatform.isIOS ? await _getUniqueAssetsInAlbum(albumId) : await getAssetIds(albumId);
await _deleteAssets(assetsToDelete);
await _db.managers.localAlbumEntity
.filter((a) => a.id.equals(albumId) & a.backupSelection.equals(BackupSelection.none))
.delete();
});
Future<void> syncDeletes(String albumId, Iterable<String> assetIdsToKeep) async {
if (assetIdsToKeep.isEmpty) {
return Future.value();
}
return _db.transaction(() async {
await _db.managers.localAlbumAssetEntity
.filter((row) => row.albumId.id.equals(albumId))
.update((album) => album(marker_: const Value(true)));
await _db.batch((batch) {
for (final assetId in assetIdsToKeep) {
batch.update(
_db.localAlbumAssetEntity,
const LocalAlbumAssetEntityCompanion(marker_: Value(null)),
where: (row) => row.assetId.equals(assetId) & row.albumId.equals(albumId),
);
}
});
final query = _db.localAssetEntity.delete()
..where(
(row) => row.id.isInQuery(
_db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId])
..where(
_db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAlbumAssetEntity.marker_.isNotNull(),
),
),
);
await query.go();
});
}
Future<void> upsert(
LocalAlbum localAlbum, {
Iterable<LocalAsset> toUpsert = const [],
Iterable<String> toDelete = const [],
}) {
final companion = LocalAlbumEntityCompanion.insert(
id: localAlbum.id,
name: localAlbum.name,
updatedAt: Value(localAlbum.updatedAt),
backupSelection: localAlbum.backupSelection,
isIosSharedAlbum: Value(localAlbum.isIosSharedAlbum),
);
return _db.transaction(() async {
await _db.localAlbumEntity.insertOne(companion, onConflict: DoUpdate((_) => companion));
if (toUpsert.isNotEmpty) {
await _upsertAssets(toUpsert);
await _db.localAlbumAssetEntity.insertAll(
toUpsert.map((a) => LocalAlbumAssetEntityCompanion.insert(assetId: a.id, albumId: localAlbum.id)),
mode: InsertMode.insertOrIgnore,
);
}
await _removeAssets(localAlbum.id, toDelete);
});
}
Future<void> updateAll(Iterable<LocalAlbum> albums) {
return _db.transaction(() async {
await _db.localAlbumEntity.update().write(const LocalAlbumEntityCompanion(marker_: Value(true)));
await _db.batch((batch) {
for (final album in albums) {
final companion = LocalAlbumEntityCompanion.insert(
id: album.id,
name: album.name,
updatedAt: Value(album.updatedAt),
backupSelection: album.backupSelection,
isIosSharedAlbum: Value(album.isIosSharedAlbum),
marker_: const Value(null),
);
batch.insert(
_db.localAlbumEntity,
companion,
onConflict: DoUpdate(
(old) => LocalAlbumEntityCompanion(
id: companion.id,
name: companion.name,
updatedAt: companion.updatedAt,
isIosSharedAlbum: companion.isIosSharedAlbum,
marker_: companion.marker_,
),
),
);
}
});
if (CurrentPlatform.isAndroid) {
// On Android, an asset can only be in one album
// So, get the albums that are marked for deletion
// and delete all the assets that are in those albums
final deleteSmt = _db.localAssetEntity.delete();
deleteSmt.where((localAsset) {
final subQuery = _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId])
..join([
innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id)),
]);
subQuery.where(_db.localAlbumEntity.marker_.isNotNull());
return localAsset.id.isInQuery(subQuery);
});
await deleteSmt.go();
}
// Only remove albums that are not explicitly selected or excluded from backups
await _db.localAlbumEntity.deleteWhere(
(f) => f.marker_.isNotNull() & f.backupSelection.equalsValue(BackupSelection.none),
);
});
}
Future<List<LocalAsset>> getAssets(String albumId) {
final query =
_db.localAlbumAssetEntity.select().join([
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get();
}
Future<List<String>> getAssetIds(String albumId) {
final query = _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId));
return query.map((row) => row.read(_db.localAlbumAssetEntity.assetId)!).get();
}
Future<void> processDelta({
required List<LocalAsset> updates,
required List<String> deletes,
required Map<String, List<String>> assetAlbums,
}) {
return _db.transaction(() async {
await _deleteAssets(deletes);
await _upsertAssets(updates);
// The ugly casting below is required for now because the generated code
// casts the returned values from the platform during decoding them
// and iterating over them causes the type to be List<Object?> instead of
// List<String>
await _db.batch((batch) async {
assetAlbums.cast<String, List<Object?>>().forEach((assetId, albumIds) {
for (final albumId in albumIds.cast<String?>().nonNulls) {
batch.deleteWhere(_db.localAlbumAssetEntity, (f) => f.albumId.equals(albumId) & f.assetId.equals(assetId));
}
});
});
await _db.batch((batch) async {
assetAlbums.cast<String, List<Object?>>().forEach((assetId, albumIds) {
batch.insertAll(
_db.localAlbumAssetEntity,
albumIds.cast<String?>().nonNulls.map(
(albumId) => LocalAlbumAssetEntityCompanion.insert(assetId: assetId, albumId: albumId),
),
onConflict: DoNothing(),
);
});
});
});
}
Future<List<LocalAsset>> getAssetsToHash(String albumId) {
final query =
_db.localAlbumAssetEntity.select().join([
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull())
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get();
}
Future<void> updateCloudMapping(Map<String, String> cloudMapping) {
if (cloudMapping.isEmpty) {
return Future.value();
}
return _db.batch((batch) {
for (final entry in cloudMapping.entries) {
final assetId = entry.key;
final cloudId = entry.value;
batch.update(
_db.localAssetEntity,
LocalAssetEntityCompanion(iCloudId: Value(cloudId)),
where: (f) => f.id.equals(assetId),
);
}
});
}
Future<void> Function(Iterable<LocalAsset>) get _upsertAssets =>
CurrentPlatform.isIOS ? _upsertAssetsDarwin : _upsertAssetsAndroid;
Future<void> _upsertAssetsDarwin(Iterable<LocalAsset> localAssets) async {
if (localAssets.isEmpty) {
return Future.value();
}
// Reset checksum if asset changed
await _db.batch((batch) async {
for (final asset in localAssets) {
final companion = LocalAssetEntityCompanion(
checksum: const Value(null),
adjustmentTime: Value(asset.adjustmentTime),
);
batch.update(
_db.localAssetEntity,
companion,
where: (row) => row.id.equals(asset.id) & row.adjustmentTime.isNotExp(Variable(asset.adjustmentTime)),
);
}
});
return _db.batch((batch) async {
for (final asset in localAssets) {
final companion = LocalAssetEntityCompanion.insert(
name: asset.name,
type: asset.type,
createdAt: Value(asset.createdAt),
updatedAt: Value(asset.updatedAt),
width: Value(asset.width),
height: Value(asset.height),
durationInSeconds: Value(asset.durationInSeconds),
id: asset.id,
orientation: Value(asset.orientation),
isFavorite: Value(asset.isFavorite),
latitude: Value(asset.latitude),
longitude: Value(asset.longitude),
adjustmentTime: Value(asset.adjustmentTime),
);
batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>(
_db.localAssetEntity,
companion.copyWith(checksum: const Value(null)),
onConflict: DoUpdate((old) => companion),
);
}
});
}
Future<void> _upsertAssetsAndroid(Iterable<LocalAsset> localAssets) async {
if (localAssets.isEmpty) {
return Future.value();
}
return _db.batch((batch) async {
for (final asset in localAssets) {
final companion = LocalAssetEntityCompanion.insert(
name: asset.name,
type: asset.type,
createdAt: Value(asset.createdAt),
updatedAt: Value(asset.updatedAt),
width: Value(asset.width),
height: Value(asset.height),
durationInSeconds: Value(asset.durationInSeconds),
id: asset.id,
checksum: const Value(null),
orientation: Value(asset.orientation),
isFavorite: Value(asset.isFavorite),
);
batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>(
_db.localAssetEntity,
companion,
onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(asset.updatedAt)),
);
}
});
}
Future<void> _removeAssets(String albumId, Iterable<String> assetIds) async {
if (assetIds.isEmpty) {
return Future.value();
}
if (CurrentPlatform.isAndroid) {
return _deleteAssets(assetIds);
}
List<String> assetsToDelete = [];
List<String> assetsToUnLink = [];
final uniqueAssets = await _getUniqueAssetsInAlbum(albumId);
if (uniqueAssets.isEmpty) {
assetsToUnLink = assetIds.toList();
} else {
// Delete unique assets and unlink others
final uniqueSet = uniqueAssets.toSet();
for (final assetId in assetIds) {
if (uniqueSet.contains(assetId)) {
assetsToDelete.add(assetId);
} else {
assetsToUnLink.add(assetId);
}
}
}
return transaction(() async {
if (assetsToUnLink.isNotEmpty) {
await _db.batch((batch) {
for (final assetId in assetsToUnLink) {
batch.deleteWhere(
_db.localAlbumAssetEntity,
(row) => row.assetId.equals(assetId) & row.albumId.equals(albumId),
);
}
});
}
await _deleteAssets(assetsToDelete);
});
}
/// Get all asset ids that are only in this album and not in other albums.
/// This is useful in cases where the album is a smart album or a user-created album, especially on iOS
Future<List<String>> _getUniqueAssetsInAlbum(String albumId) {
final assetId = _db.localAlbumAssetEntity.assetId;
final query = _db.localAlbumAssetEntity.selectOnly()
..addColumns([assetId])
..groupBy(
[assetId],
having: _db.localAlbumAssetEntity.albumId.count().equals(1) & _db.localAlbumAssetEntity.albumId.equals(albumId),
);
return query.map((row) => row.read(assetId)!).get();
}
Future<void> _deleteAssets(Iterable<String> ids) {
if (ids.isEmpty) {
return Future.value();
}
return _db.batch((batch) {
for (final id in ids) {
batch.deleteWhere(_db.localAssetEntity, (row) => row.id.equals(id));
}
});
}
Future<LocalAsset?> getThumbnail(String albumId) async {
final query =
_db.localAlbumAssetEntity.select().join([
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)])
..limit(1);
final results = await query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get();
return results.isNotEmpty ? results.first : null;
}
Future<int> getCount() {
return _db.managers.localAlbumEntity.count();
}
Future<void> unlinkRemoteAlbum(String id) async {
final query = _db.localAlbumEntity.update()..where((row) => row.id.equals(id));
await query.write(const LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(null)));
}
Future<void> linkRemoteAlbum(String localAlbumId, String remoteAlbumId) async {
final query = _db.localAlbumEntity.update()..where((row) => row.id.equals(localAlbumId));
await query.write(LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(remoteAlbumId)));
}
}

View file

@ -0,0 +1,226 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
class RemovalCandidatesResult {
final List<LocalAsset> assets;
final int totalBytes;
const RemovalCandidatesResult({required this.assets, required this.totalBytes});
}
class DriftLocalAssetRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftLocalAssetRepository(this._db) : super(_db);
SingleOrNullSelectable<LocalAsset?> _assetSelectable(String id) {
final query = _db.localAssetEntity.select().addColumns([_db.remoteAssetEntity.id]).join([
leftOuterJoin(
_db.remoteAssetEntity,
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
useColumns: false,
),
])..where(_db.localAssetEntity.id.equals(id));
return query.map((row) {
final asset = row.readTable(_db.localAssetEntity).toDto();
return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id));
});
}
Future<LocalAsset?> get(String id) => _assetSelectable(id).getSingleOrNull();
Future<List<LocalAsset?>> getByChecksum(String checksum) {
final query = _db.localAssetEntity.select()..where((lae) => lae.checksum.equals(checksum));
return query.map((row) => row.toDto()).get();
}
Stream<LocalAsset?> watch(String id) => _assetSelectable(id).watchSingleOrNull();
Future<void> updateHashes(Map<String, String> hashes) {
if (hashes.isEmpty) {
return Future.value();
}
return _db.batch((batch) async {
for (final entry in hashes.entries) {
batch.update(
_db.localAssetEntity,
LocalAssetEntityCompanion(checksum: Value(entry.value)),
where: (e) => e.id.equals(entry.key),
);
}
});
}
Future<void> delete(List<String> ids) {
if (ids.isEmpty) {
return Future.value();
}
return _db.batch((batch) {
for (final id in ids) {
batch.deleteWhere(_db.localAssetEntity, (e) => e.id.equals(id));
}
});
}
Future<LocalAsset?> getById(String id) {
final query = _db.localAssetEntity.select()..where((lae) => lae.id.equals(id));
return query.map((row) => row.toDto()).getSingleOrNull();
}
Future<int> getCount() {
return _db.managers.localAssetEntity.count();
}
Future<int> getHashedCount() {
return _db.managers.localAssetEntity.filter((e) => e.checksum.isNull().not()).count();
}
Future<List<LocalAlbum>> getSourceAlbums(String localAssetId, {BackupSelection? backupSelection}) {
final query = _db.localAlbumEntity.select()
..where(
(lae) => existsQuery(
_db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.albumId])
..where(
_db.localAlbumAssetEntity.albumId.equalsExp(lae.id) &
_db.localAlbumAssetEntity.assetId.equals(localAssetId),
),
),
)
..orderBy([(lae) => OrderingTerm.asc(lae.name)]);
if (backupSelection != null) {
query.where((lae) => lae.backupSelection.equalsValue(backupSelection));
}
return query.map((localAlbum) => localAlbum.toDto()).get();
}
Future<Map<String, List<LocalAsset>>> getAssetsFromBackupAlbums(Iterable<String> checksums) async {
if (checksums.isEmpty) {
return {};
}
final result = <String, List<LocalAsset>>{};
for (final slice in checksums.toSet().slices(kDriftMaxChunk)) {
final rows =
await (_db.select(_db.localAlbumAssetEntity).join([
innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id)),
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
])..where(
_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected) &
_db.localAssetEntity.checksum.isIn(slice),
))
.get();
for (final row in rows) {
final albumId = row.readTable(_db.localAlbumAssetEntity).albumId;
final assetData = row.readTable(_db.localAssetEntity);
final asset = assetData.toDto();
(result[albumId] ??= <LocalAsset>[]).add(asset);
}
}
return result;
}
Future<RemovalCandidatesResult> getRemovalCandidates(
String userId,
DateTime cutoffDate, {
AssetKeepType keepMediaType = AssetKeepType.none,
bool keepFavorites = true,
Set<String> keepAlbumIds = const {},
}) async {
final iosSharedAlbumAssets = _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId])
..join([
innerJoin(
_db.localAlbumEntity,
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
useColumns: false,
),
])
..where(_db.localAlbumEntity.isIosSharedAlbum.equals(true));
final query = _db.localAssetEntity.select().join([
innerJoin(_db.remoteAssetEntity, _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum)),
leftOuterJoin(_db.remoteExifEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteExifEntity.assetId)),
]);
Expression<bool> whereClause =
_db.localAssetEntity.createdAt.isSmallerOrEqualValue(cutoffDate) &
_db.remoteAssetEntity.ownerId.equals(userId) &
_db.remoteAssetEntity.deletedAt.isNull();
// Exclude assets that are in iOS shared albums
whereClause = whereClause & _db.localAssetEntity.id.isNotInQuery(iosSharedAlbumAssets);
if (keepAlbumIds.isNotEmpty) {
final keepAlbumAssets = _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId])
..where(_db.localAlbumAssetEntity.albumId.isIn(keepAlbumIds));
whereClause = whereClause & _db.localAssetEntity.id.isNotInQuery(keepAlbumAssets);
}
if (keepMediaType == AssetKeepType.photosOnly) {
// Keep photos = delete only videos
whereClause = whereClause & _db.localAssetEntity.type.equalsValue(AssetType.video);
} else if (keepMediaType == AssetKeepType.videosOnly) {
// Keep videos = delete only photos
whereClause = whereClause & _db.localAssetEntity.type.equalsValue(AssetType.image);
}
if (keepFavorites) {
whereClause = whereClause & _db.localAssetEntity.isFavorite.equals(false);
}
query.where(whereClause);
final rows = await query.get();
final assets = rows.map((row) => row.readTable(_db.localAssetEntity).toDto()).toList();
final totalBytes = rows.fold<int>(0, (sum, row) {
final fileSize = row.readTableOrNull(_db.remoteExifEntity)?.fileSize;
return sum + (fileSize ?? 0);
});
return RemovalCandidatesResult(assets: assets, totalBytes: totalBytes);
}
Future<List<LocalAsset>> getEmptyCloudIdAssets() {
final query = _db.localAssetEntity.select()..where((row) => row.iCloudId.isNull());
return query.map((row) => row.toDto()).get();
}
Future<void> reconcileHashesFromCloudId() async {
await _db.customUpdate(
'''
UPDATE local_asset_entity
SET checksum = remote_asset_entity.checksum
FROM remote_asset_cloud_id_entity
INNER JOIN remote_asset_entity
ON remote_asset_cloud_id_entity.asset_id = remote_asset_entity.id
WHERE local_asset_entity.i_cloud_id = remote_asset_cloud_id_entity.cloud_id
AND local_asset_entity.checksum IS NULL
AND remote_asset_cloud_id_entity.adjustment_time IS local_asset_entity.adjustment_time
AND remote_asset_cloud_id_entity.latitude IS local_asset_entity.latitude
AND remote_asset_cloud_id_entity.longitude IS local_asset_entity.longitude
AND remote_asset_cloud_id_entity.created_at IS local_asset_entity.created_at
''',
updates: {_db.localAssetEntity},
updateKind: UpdateKind.update,
);
}
}

View file

@ -0,0 +1,75 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/log.model.dart';
import 'package:immich_mobile/infrastructure/entities/log.entity.dart';
import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
class LogRepository {
final DriftLogger _db;
const LogRepository(this._db);
Future<bool> deleteAll() async {
await _db.logMessageEntity.deleteAll();
return true;
}
Future<List<LogMessage>> getAll({int limit = 250}) async {
final query = _db.logMessageEntity.select()
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
..limit(limit);
return query.map((log) => log.toDto()).get();
}
LogMessageEntityCompanion _toEntityCompanion(LogMessage log) {
return LogMessageEntityCompanion.insert(
message: log.message,
level: log.level,
createdAt: log.createdAt,
logger: Value(log.logger),
details: Value(log.error),
stack: Value(log.stack),
);
}
Future<bool> insert(LogMessage log) async {
final logEntity = _toEntityCompanion(log);
try {
await _db.logMessageEntity.insertOne(logEntity);
} catch (e) {
return false;
}
return true;
}
Future<bool> insertAll(Iterable<LogMessage> logs) async {
final logEntities = logs.map(_toEntityCompanion).toList();
await _db.logMessageEntity.insertAll(logEntities);
return true;
}
Future<void> deleteByLogger(String logger) async {
await _db.logMessageEntity.deleteWhere((row) => row.logger.equals(logger));
}
Stream<List<LogMessage>> watchMessages(String logger) {
final query = _db.logMessageEntity.select()
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
..where((row) => row.logger.equals(logger));
return query.watch().map((rows) => rows.map((row) => row.toDto()).toList());
}
Future<void> truncate({int limit = kLogTruncateLimit}) async {
final totalCount = await _db.managers.logMessageEntity.count();
if (totalCount > limit) {
final rowsToDelete = totalCount - limit;
await _db.managers.logMessageEntity.orderBy((o) => o.createdAt.asc()).limit(rowsToDelete).delete();
}
}
}

View file

@ -0,0 +1,27 @@
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/log.entity.dart';
import 'logger_db.repository.drift.dart';
@DriftDatabase(tables: [LogMessageEntity])
class DriftLogger extends $DriftLogger implements IDatabaseRepository {
DriftLogger([QueryExecutor? executor])
: super(
executor ?? driftDatabase(name: 'immich_logs', native: const DriftNativeOptions(shareAcrossIsolates: true)),
);
@override
int get schemaVersion => 1;
@override
MigrationStrategy get migration => MigrationStrategy(
beforeOpen: (details) async {
await customStatement('PRAGMA foreign_keys = ON');
await customStatement('PRAGMA synchronous = NORMAL');
await customStatement('PRAGMA journal_mode = WAL');
await customStatement('PRAGMA busy_timeout = 500');
},
);
}

View file

@ -0,0 +1,27 @@
// dart format width=80
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart'
as i1;
abstract class $DriftLogger extends i0.GeneratedDatabase {
$DriftLogger(i0.QueryExecutor e) : super(e);
$DriftLoggerManager get managers => $DriftLoggerManager(this);
late final i1.$LogMessageEntityTable logMessageEntity = i1
.$LogMessageEntityTable(this);
@override
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
@override
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [logMessageEntity];
@override
i0.DriftDatabaseOptions get options =>
const i0.DriftDatabaseOptions(storeDateTimeAsText: true);
}
class $DriftLoggerManager {
final $DriftLogger _db;
$DriftLoggerManager(this._db);
i1.$$LogMessageEntityTableTableManager get logMessageEntity =>
i1.$$LogMessageEntityTableTableManager(_db, _db.logMessageEntity);
}

View file

@ -0,0 +1,85 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/map.model.dart';
import 'package:immich_mobile/domain/services/map.service.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
class DriftMapRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftMapRepository(super._db) : _db = _db;
MapQuery remote(List<String> ownerIds, TimelineMapOptions options) => _mapQueryBuilder(
assetFilter: (row) {
Expression<bool> condition =
row.deletedAt.isNull() &
row.ownerId.isIn(ownerIds) &
_db.remoteAssetEntity.visibility.isIn([
AssetVisibility.timeline.index,
if (options.includeArchived) AssetVisibility.archive.index,
]);
if (options.onlyFavorites) {
condition = condition & _db.remoteAssetEntity.isFavorite.equals(true);
}
if (options.relativeDays != 0) {
final cutoffDate = DateTime.now().toUtc().subtract(Duration(days: options.relativeDays));
condition = condition & _db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(cutoffDate);
}
return condition;
},
);
MapQuery _mapQueryBuilder({Expression<bool> Function($RemoteAssetEntityTable row)? assetFilter}) {
return (markerSource: (bounds) => _watchMapMarker(assetFilter: assetFilter, bounds: bounds));
}
Future<List<Marker>> _watchMapMarker({
Expression<bool> Function($RemoteAssetEntityTable row)? assetFilter,
LatLngBounds? bounds,
}) async {
final assetId = _db.remoteExifEntity.assetId;
final latitude = _db.remoteExifEntity.latitude;
final longitude = _db.remoteExifEntity.longitude;
final query = _db.remoteExifEntity.selectOnly()
..addColumns([assetId, latitude, longitude])
..join([innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(assetId), useColumns: false)])
..limit(10000);
if (assetFilter != null) {
query.where(assetFilter(_db.remoteAssetEntity));
}
if (bounds != null) {
query.where(_db.remoteExifEntity.inBounds(bounds));
} else {
query.where(latitude.isNotNull() & longitude.isNotNull());
}
final rows = await query.get();
return List.generate(rows.length, (i) {
final row = rows[i];
return Marker(assetId: row.read(assetId)!, location: LatLng(row.read(latitude)!, row.read(longitude)!));
}, growable: false);
}
}
extension MapBounds on $RemoteExifEntityTable {
Expression<bool> inBounds(LatLngBounds bounds) {
final southwest = bounds.southwest;
final northeast = bounds.northeast;
final latInBounds = latitude.isBetweenValues(southwest.latitude, northeast.latitude);
final longInBounds = southwest.longitude <= northeast.longitude
? longitude.isBetweenValues(southwest.longitude, northeast.longitude)
: (longitude.isBiggerOrEqualValue(southwest.longitude) | longitude.isSmallerOrEqualValue(northeast.longitude));
return latInBounds & longInBounds;
}
}

View file

@ -0,0 +1,110 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/memory.model.dart';
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
class DriftMemoryRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftMemoryRepository(this._db) : super(_db);
Future<List<DriftMemory>> getAll(String ownerId) async {
final now = DateTime.now();
final localUtc = DateTime.utc(now.year, now.month, now.day, 0, 0, 0);
final query =
_db.select(_db.memoryEntity).join([
innerJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)),
innerJoin(
_db.remoteAssetEntity,
_db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) &
_db.remoteAssetEntity.deletedAt.isNull() &
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline),
),
])
..where(_db.memoryEntity.ownerId.equals(ownerId))
..where(_db.memoryEntity.deletedAt.isNull())
..where(_db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc))
..where(_db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc))
..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
final rows = await query.get();
if (rows.isEmpty) {
return const [];
}
final Map<String, DriftMemory> memoriesMap = {};
for (final row in rows) {
final memory = row.readTable(_db.memoryEntity);
final asset = row.readTable(_db.remoteAssetEntity);
final existingMemory = memoriesMap[memory.id];
if (existingMemory != null) {
existingMemory.assets.add(asset.toDto());
} else {
final assets = [asset.toDto()];
memoriesMap[memory.id] = memory.toDto().copyWith(assets: assets);
}
}
return memoriesMap.values.toList(growable: false);
}
Future<DriftMemory?> get(String memoryId) async {
final query =
_db.select(_db.memoryEntity).join([
leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)),
leftOuterJoin(
_db.remoteAssetEntity,
_db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) &
_db.remoteAssetEntity.deletedAt.isNull() &
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline),
),
])
..where(_db.memoryEntity.id.equals(memoryId))
..where(_db.memoryEntity.deletedAt.isNull())
..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
final rows = await query.get();
if (rows.isEmpty) {
return null;
}
final memory = rows.first.readTable(_db.memoryEntity);
final assets = <RemoteAsset>[];
for (final row in rows) {
final asset = row.readTable(_db.remoteAssetEntity);
assets.add(asset.toDto());
}
return memory.toDto().copyWith(assets: assets);
}
Future<int> getCount() {
return _db.managers.memoryEntity.count();
}
}
extension on MemoryEntityData {
DriftMemory toDto() {
return DriftMemory(
id: id,
createdAt: createdAt,
updatedAt: updatedAt,
deletedAt: deletedAt,
ownerId: ownerId,
type: type,
data: MemoryData.fromJson(data),
isSaved: isSaved,
memoryAt: memoryAt,
seenAt: seenAt,
showAt: showAt,
hideAt: hideAt,
assets: [],
);
}
}

View file

@ -0,0 +1,67 @@
import 'dart:io';
import 'package:cronet_http/cronet_http.dart';
import 'package:cupertino_http/cupertino_http.dart';
import 'package:http/http.dart' as http;
import 'package:immich_mobile/utils/user_agent.dart';
import 'package:path_provider/path_provider.dart';
class NetworkRepository {
static late Directory _cachePath;
static late String _userAgent;
static final _clients = <String, http.Client>{};
static Future<void> init() {
return (
getTemporaryDirectory().then((cachePath) => _cachePath = cachePath),
getUserAgentString().then((userAgent) => _userAgent = userAgent),
).wait;
}
static void reset() {
Future.microtask(init);
for (final client in _clients.values) {
client.close();
}
_clients.clear();
}
const NetworkRepository();
/// Note: when disk caching is enabled, only one client may use a given directory at a time.
/// Different isolates or engines must use different directories.
http.Client getHttpClient(
String directoryName, {
CacheMode cacheMode = CacheMode.memory,
int diskCapacity = 0,
int maxConnections = 6,
int memoryCapacity = 10 << 20,
}) {
final cachedClient = _clients[directoryName];
if (cachedClient != null) {
return cachedClient;
}
final directory = Directory('${_cachePath.path}/$directoryName');
directory.createSync(recursive: true);
if (Platform.isAndroid) {
final engine = CronetEngine.build(
cacheMode: cacheMode,
cacheMaxSize: diskCapacity,
storagePath: directory.path,
userAgent: _userAgent,
);
return _clients[directoryName] = CronetClient.fromCronetEngine(engine, closeEngine: true);
}
final config = URLSessionConfiguration.defaultSessionConfiguration()
..httpMaximumConnectionsPerHost = maxConnections
..cache = URLCache.withCapacity(
diskCapacity: diskCapacity,
memoryCapacity: memoryCapacity,
directory: directory.uri,
)
..httpAdditionalHeaders = {'User-Agent': _userAgent};
return _clients[directoryName] = CupertinoClient.fromSessionConfiguration(config);
}
}

View file

@ -0,0 +1,106 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
class DriftPartnerRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftPartnerRepository(this._db) : super(_db);
Future<List<PartnerUserDto>> getPartners(String userId) {
final query = _db.select(_db.partnerEntity).join([
innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)),
])..where(_db.partnerEntity.sharedWithId.equals(userId));
return query.map((row) {
final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
}).get();
}
// Get users who we can share our library with
Future<List<PartnerUserDto>> getAvailablePartners(String currentUserId) {
final query = _db.select(_db.userEntity)..where((row) => row.id.equals(currentUserId).not());
return query.map((user) {
return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: false);
}).get();
}
// Get users who are sharing their photos WITH the current user
Future<List<PartnerUserDto>> getSharedWith(String partnerId) {
final query = _db.select(_db.partnerEntity).join([
innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)),
])..where(_db.partnerEntity.sharedWithId.equals(partnerId));
return query.map((row) {
final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
}).get();
}
// Get users who the current user is sharing their photos TO
Future<List<PartnerUserDto>> getSharedBy(String userId) {
final query = _db.select(_db.partnerEntity).join([
innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId)),
])..where(_db.partnerEntity.sharedById.equals(userId));
return query.map((row) {
final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
}).get();
}
Future<List<String>> getAllPartnerIds(String userId) async {
// Get users who are sharing with me (sharedWithId = userId)
final sharingWithMeQuery = _db.select(_db.partnerEntity)..where((tbl) => tbl.sharedWithId.equals(userId));
final sharingWithMe = await sharingWithMeQuery.map((row) => row.sharedById).get();
// Get users who I am sharing with (sharedById = userId)
final sharingWithThemQuery = _db.select(_db.partnerEntity)..where((tbl) => tbl.sharedById.equals(userId));
final sharingWithThem = await sharingWithThemQuery.map((row) => row.sharedWithId).get();
// Combine both lists and remove duplicates
final allPartnerIds = <String>{...sharingWithMe, ...sharingWithThem}.toList();
return allPartnerIds;
}
Future<PartnerUserDto?> getPartner(String partnerId, String userId) {
final query = _db.select(_db.partnerEntity).join([
innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)),
])..where(_db.partnerEntity.sharedById.equals(partnerId) & _db.partnerEntity.sharedWithId.equals(userId));
return query.map((row) {
final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
}).getSingleOrNull();
}
Future<bool> toggleShowInTimeline(PartnerUserDto partner, String userId) {
return _db.partnerEntity.update().replace(
PartnerEntityCompanion(
sharedById: Value(partner.id),
sharedWithId: Value(userId),
inTimeline: Value(!partner.inTimeline),
),
);
}
Future<int> create(String partnerId, String userId) {
final entity = PartnerEntityCompanion(
sharedById: Value(userId),
sharedWithId: Value(partnerId),
inTimeline: const Value(false),
);
return _db.partnerEntity.insertOne(entity);
}
Future<void> delete(String partnerId, String userId) {
return _db.partnerEntity.deleteWhere((t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId));
}
}

View file

@ -0,0 +1,67 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/person.model.dart';
import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
class DriftPeopleRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftPeopleRepository(this._db) : super(_db);
Future<List<DriftPerson>> getAssetPeople(String assetId) async {
final query = _db.select(_db.assetFaceEntity).join([
innerJoin(_db.personEntity, _db.personEntity.id.equalsExp(_db.assetFaceEntity.personId)),
])..where(_db.assetFaceEntity.assetId.equals(assetId) & _db.personEntity.isHidden.equals(false));
return query.map((row) {
final person = row.readTable(_db.personEntity);
return person.toDto();
}).get();
}
Future<List<DriftPerson>> getAllPeople() async {
final query =
_db.select(_db.personEntity).join([
leftOuterJoin(_db.assetFaceEntity, _db.assetFaceEntity.personId.equalsExp(_db.personEntity.id)),
])
..where(_db.personEntity.isHidden.equals(false))
..groupBy([_db.personEntity.id], having: _db.assetFaceEntity.id.count().isBiggerOrEqualValue(3))
..orderBy([
OrderingTerm(expression: _db.personEntity.name.equals('').not(), mode: OrderingMode.desc),
OrderingTerm(expression: _db.assetFaceEntity.id.count(), mode: OrderingMode.desc),
]);
return query.map((row) {
final person = row.readTable(_db.personEntity);
return person.toDto();
}).get();
}
Future<int> updateName(String personId, String name) {
final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId));
return query.write(PersonEntityCompanion(name: Value(name), updatedAt: Value(DateTime.now())));
}
Future<int> updateBirthday(String personId, DateTime birthday) {
final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId));
return query.write(PersonEntityCompanion(birthDate: Value(birthday), updatedAt: Value(DateTime.now())));
}
}
extension on PersonEntityData {
DriftPerson toDto() {
return DriftPerson(
id: id,
createdAt: createdAt,
updatedAt: updatedAt,
ownerId: ownerId,
name: name,
faceAssetId: faceAssetId,
isFavorite: isFavorite,
isHidden: isHidden,
color: color,
birthDate: birthDate,
);
}
}

View file

@ -0,0 +1,459 @@
import 'dart:async';
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
enum SortRemoteAlbumsBy { id, updatedAt }
class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftRemoteAlbumRepository(this._db) : super(_db);
Future<List<RemoteAlbum>> getAll({Set<SortRemoteAlbumsBy> sortBy = const {SortRemoteAlbumsBy.updatedAt}}) {
final assetCount = _db.remoteAlbumAssetEntity.assetId.count(distinct: true);
final query = _db.remoteAlbumEntity.select().join([
leftOuterJoin(
_db.remoteAlbumAssetEntity,
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false,
),
leftOuterJoin(
_db.remoteAssetEntity,
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
useColumns: false,
),
leftOuterJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), useColumns: false),
leftOuterJoin(
_db.remoteAlbumUserEntity,
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false,
),
]);
query
..where(_db.remoteAssetEntity.deletedAt.isNull())
..addColumns([assetCount])
..addColumns([_db.userEntity.name])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..groupBy([_db.remoteAlbumEntity.id]);
if (sortBy.isNotEmpty) {
final orderings = <OrderingTerm>[];
for (final sort in sortBy) {
orderings.add(switch (sort) {
SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id),
SortRemoteAlbumsBy.updatedAt => OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt),
});
}
query.orderBy(orderings);
}
return query
.map(
(row) => row
.readTable(_db.remoteAlbumEntity)
.toDto(
assetCount: row.read(assetCount) ?? 0,
ownerName: row.read(_db.userEntity.name)!,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
),
)
.get();
}
Future<RemoteAlbum?> get(String albumId) {
final assetCount = _db.remoteAlbumAssetEntity.assetId.count(distinct: true);
final query =
_db.remoteAlbumEntity.select().join([
leftOuterJoin(
_db.remoteAlbumAssetEntity,
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false,
),
leftOuterJoin(
_db.remoteAssetEntity,
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
useColumns: false,
),
leftOuterJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
useColumns: false,
),
leftOuterJoin(
_db.remoteAlbumUserEntity,
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false,
),
])
..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull())
..addColumns([assetCount])
..addColumns([_db.userEntity.name])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..groupBy([_db.remoteAlbumEntity.id]);
return query
.map(
(row) => row
.readTable(_db.remoteAlbumEntity)
.toDto(
assetCount: row.read(assetCount) ?? 0,
ownerName: row.read(_db.userEntity.name)!,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
),
)
.getSingleOrNull();
}
Future<RemoteAlbum?> getByName(String albumName, String ownerId) {
final query = _db.remoteAlbumEntity.select()
..where((row) => row.name.equals(albumName) & row.ownerId.equals(ownerId))
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
..limit(1);
return query.map((row) => row.toDto(ownerName: '', isShared: false)).getSingleOrNull();
}
Future<void> create(RemoteAlbum album, List<String> assetIds) async {
await _db.transaction(() async {
final entity = RemoteAlbumEntityCompanion(
id: Value(album.id),
name: Value(album.name),
ownerId: Value(album.ownerId),
createdAt: Value(album.createdAt),
updatedAt: Value(album.updatedAt),
description: Value(album.description),
thumbnailAssetId: Value(album.thumbnailAssetId),
isActivityEnabled: Value(album.isActivityEnabled),
order: Value(album.order),
);
await _db.remoteAlbumEntity.insertOne(entity);
if (assetIds.isNotEmpty) {
final albumAssets = assetIds.map(
(assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(album.id), assetId: Value(assetId)),
);
await _db.batch((batch) {
batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets);
});
}
});
}
Future<void> update(RemoteAlbum album) async {
await _db.remoteAlbumEntity.update().replace(
RemoteAlbumEntityCompanion(
id: Value(album.id),
name: Value(album.name),
ownerId: Value(album.ownerId),
createdAt: Value(album.createdAt),
updatedAt: Value(album.updatedAt),
description: Value(album.description),
thumbnailAssetId: Value(album.thumbnailAssetId),
isActivityEnabled: Value(album.isActivityEnabled),
order: Value(album.order),
),
);
}
Future<void> removeAssets(String albumId, List<String> assetIds) {
return _db.batch((batch) {
for (final assetId in assetIds) {
batch.deleteWhere(
_db.remoteAlbumAssetEntity,
(row) => row.albumId.equals(albumId) & row.assetId.equals(assetId),
);
}
});
}
FutureOr<(DateTime, DateTime)> getDateRange(String albumId) {
final query = _db.remoteAlbumAssetEntity.selectOnly()
..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId))
..addColumns([_db.remoteAssetEntity.createdAt.min(), _db.remoteAssetEntity.createdAt.max()])
..join([
innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)),
]);
return query.map((row) {
final minDate = row.read(_db.remoteAssetEntity.createdAt.min());
final maxDate = row.read(_db.remoteAssetEntity.createdAt.max());
return (minDate ?? DateTime.now(), maxDate ?? DateTime.now());
}).getSingle();
}
Future<List<UserDto>> getSharedUsers(String albumId) async {
final albumUserRows = await (_db.select(
_db.remoteAlbumUserEntity,
)..where((row) => row.albumId.equals(albumId))).get();
if (albumUserRows.isEmpty) {
return [];
}
final userIds = albumUserRows.map((row) => row.userId);
return (_db.select(_db.userEntity)..where((row) => row.id.isIn(userIds)))
.map(
(user) => UserDto(
id: user.id,
email: user.email,
name: user.name,
memoryEnabled: true,
inTimeline: false,
isPartnerSharedBy: false,
isPartnerSharedWith: false,
profileChangedAt: user.profileChangedAt,
hasProfileImage: user.hasProfileImage,
avatarColor: user.avatarColor,
),
)
.get();
}
Future<AlbumUserRole?> getUserRole(String albumId, String userId) async {
final query = _db.remoteAlbumUserEntity.select()
..where((row) => row.albumId.equals(albumId) & row.userId.equals(userId))
..limit(1);
final result = await query.getSingleOrNull();
return result?.role;
}
Future<List<RemoteAsset>> getAssets(String albumId) {
final query = _db.remoteAlbumAssetEntity.select().join([
innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)),
])..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId));
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
}
Future<int> addAssets(String albumId, List<String> assetIds) async {
final albumAssets = assetIds.map(
(assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(albumId), assetId: Value(assetId)),
);
await _db.batch((batch) {
batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets);
});
return assetIds.length;
}
Future<void> addUsers(String albumId, List<String> userIds) {
final albumUsers = userIds.map(
(assetId) => RemoteAlbumUserEntityCompanion(
albumId: Value(albumId),
userId: Value(assetId),
role: const Value(AlbumUserRole.editor),
),
);
return _db.batch((batch) {
batch.insertAll(_db.remoteAlbumUserEntity, albumUsers);
});
}
Future<void> removeUser(String albumId, {required String userId}) {
return _db.remoteAlbumUserEntity.deleteWhere((row) => row.albumId.equals(albumId) & row.userId.equals(userId));
}
Future<void> deleteAlbum(String albumId) async {
return _db.transaction(() async {
await _db.remoteAlbumEntity.deleteWhere((table) => table.id.equals(albumId));
});
}
Future<void> setActivityStatus(String albumId, bool isEnabled) async {
final query = _db.update(_db.remoteAlbumEntity)..where((row) => row.id.equals(albumId));
await query.write(RemoteAlbumEntityCompanion(isActivityEnabled: Value(isEnabled)));
}
Stream<RemoteAlbum?> watchAlbum(String albumId) {
final query =
_db.remoteAlbumEntity.select().join([
leftOuterJoin(
_db.remoteAlbumAssetEntity,
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false,
),
leftOuterJoin(
_db.remoteAssetEntity,
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
useColumns: false,
),
leftOuterJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
useColumns: false,
),
leftOuterJoin(
_db.remoteAlbumUserEntity,
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false,
),
])
..where(_db.remoteAlbumEntity.id.equals(albumId))
..addColumns([_db.userEntity.name])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..groupBy([_db.remoteAlbumEntity.id]);
return query.map((row) {
final album = row
.readTable(_db.remoteAlbumEntity)
.toDto(
ownerName: row.read(_db.userEntity.name)!,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
);
return album;
}).watchSingleOrNull();
}
Future<DateTime?> getNewestAssetTimestamp(String albumId) {
final query = _db.remoteAlbumAssetEntity.selectOnly()
..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId))
..addColumns([_db.remoteAssetEntity.localDateTime.max()])
..join([
innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)),
]);
return query.map((row) => row.read(_db.remoteAssetEntity.localDateTime.max())).getSingleOrNull();
}
Future<DateTime?> getOldestAssetTimestamp(String albumId) {
final query = _db.remoteAlbumAssetEntity.selectOnly()
..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId))
..addColumns([_db.remoteAssetEntity.localDateTime.min()])
..join([
innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)),
]);
return query.map((row) => row.read(_db.remoteAssetEntity.localDateTime.min())).getSingleOrNull();
}
Future<int> getCount() {
return _db.managers.remoteAlbumEntity.count();
}
Future<List<String>> getLinkedAssetIds(String userId, String localAlbumId, String remoteAlbumId) async {
// Find remote asset ids that:
// 1. Belong to the provided local album (via local_album_asset_entity)
// 2. Have been uploaded (i.e. a matching remote asset exists for the same checksum & owner)
// 3. Are NOT already in the remote album (remote_album_asset_entity)
final query = _db.remoteAssetEntity.selectOnly()
..addColumns([_db.remoteAssetEntity.id])
..join([
innerJoin(
_db.localAssetEntity,
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
useColumns: false,
),
innerJoin(
_db.localAlbumAssetEntity,
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
useColumns: false,
),
// Left join remote album assets to exclude those already in the remote album
leftOuterJoin(
_db.remoteAlbumAssetEntity,
_db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id) &
_db.remoteAlbumAssetEntity.albumId.equals(remoteAlbumId),
useColumns: false,
),
])
..where(
_db.remoteAssetEntity.ownerId.equals(userId) &
_db.remoteAssetEntity.deletedAt.isNull() &
_db.localAlbumAssetEntity.albumId.equals(localAlbumId) &
_db.remoteAlbumAssetEntity.assetId.isNull(), // only those not yet linked
);
return query.map((row) => row.read(_db.remoteAssetEntity.id)!).get();
}
Future<List<RemoteAlbum>> getAlbumsContainingAsset(String assetId) async {
// Note: this needs to be 2 queries as the where clause filtering causes the assetCount to always be 1
final albumIdsQuery = _db.remoteAlbumAssetEntity.selectOnly()
..addColumns([_db.remoteAlbumAssetEntity.albumId])
..where(_db.remoteAlbumAssetEntity.assetId.equals(assetId));
final albumIds = await albumIdsQuery.map((row) => row.read(_db.remoteAlbumAssetEntity.albumId)!).get();
if (albumIds.isEmpty) {
return [];
}
final assetCount = _db.remoteAlbumAssetEntity.assetId.count(distinct: true);
final query =
_db.remoteAlbumEntity.select().join([
leftOuterJoin(
_db.remoteAlbumAssetEntity,
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false,
),
leftOuterJoin(
_db.remoteAssetEntity,
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
useColumns: false,
),
leftOuterJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
useColumns: false,
),
leftOuterJoin(
_db.remoteAlbumUserEntity,
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false,
),
])
..where(_db.remoteAlbumEntity.id.isIn(albumIds) & _db.remoteAssetEntity.deletedAt.isNull())
..addColumns([assetCount])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..addColumns([_db.userEntity.name])
..groupBy([_db.remoteAlbumEntity.id]);
return query
.map(
(row) => row
.readTable(_db.remoteAlbumEntity)
.toDto(
ownerName: row.read(_db.userEntity.name) ?? '',
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
assetCount: row.read(assetCount) ?? 0,
),
)
.get();
}
}
extension on RemoteAlbumEntityData {
RemoteAlbum toDto({int assetCount = 0, required String ownerName, required bool isShared}) {
return RemoteAlbum(
id: id,
name: name,
ownerId: ownerId,
createdAt: createdAt,
updatedAt: updatedAt,
description: description,
thumbnailAssetId: thumbnailAssetId,
isActivityEnabled: isActivityEnabled,
order: order,
assetCount: assetCount,
ownerName: ownerName,
isShared: isShared,
);
}
}

View file

@ -0,0 +1,267 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/exif.model.dart';
import 'package:immich_mobile/domain/models/stack.model.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' hide ExifInfo;
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
class RemoteAssetRepository extends DriftDatabaseRepository {
final Drift _db;
const RemoteAssetRepository(this._db) : super(_db);
/// For testing purposes
Future<List<RemoteAsset>> getSome(String userId) {
final query = _db.remoteAssetEntity.select()
..where(
(row) =>
_db.remoteAssetEntity.ownerId.equals(userId) &
_db.remoteAssetEntity.deletedAt.isNull() &
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline),
)
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
..limit(10);
return query.map((row) => row.toDto()).get();
}
SingleOrNullSelectable<RemoteAsset?> _assetSelectable(String id) {
final query =
_db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([
leftOuterJoin(
_db.localAssetEntity,
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
useColumns: false,
),
])
..where(_db.remoteAssetEntity.id.equals(id))
..limit(1);
return query.map((row) {
final asset = row.readTable(_db.remoteAssetEntity).toDto();
return asset.copyWith(localId: row.read(_db.localAssetEntity.id));
});
}
Stream<RemoteAsset?> watch(String id) {
return _assetSelectable(id).watchSingleOrNull();
}
Future<RemoteAsset?> get(String id) {
return _assetSelectable(id).getSingleOrNull();
}
Future<RemoteAsset?> getByChecksum(String checksum) {
final query = _db.remoteAssetEntity.select()..where((row) => row.checksum.equals(checksum));
return query.map((row) => row.toDto()).getSingleOrNull();
}
Future<List<RemoteAsset>> getStackChildren(RemoteAsset asset) {
final stackId = asset.stackId;
if (stackId == null) {
return Future.value(const []);
}
final query = _db.remoteAssetEntity.select()
..where((row) => row.stackId.equals(stackId) & row.id.equals(asset.id).not())
..orderBy([(row) => OrderingTerm.desc(row.createdAt)]);
return query.map((row) => row.toDto()).get();
}
Future<ExifInfo?> getExif(String id) {
return _db.managers.remoteExifEntity
.filter((row) => row.assetId.id.equals(id))
.map((row) => row.toDto())
.getSingleOrNull();
}
Future<List<(String, String)>> getPlaces(String userId) {
final asset = Subquery(
_db.remoteAssetEntity.select()
..where((row) => row.ownerId.equals(userId))
..orderBy([(row) => OrderingTerm.desc(row.createdAt)]),
"asset",
);
final query =
asset.selectOnly().join([
innerJoin(
_db.remoteExifEntity,
_db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)),
useColumns: false,
),
])
..addColumns([_db.remoteExifEntity.city, _db.remoteExifEntity.assetId])
..where(
_db.remoteExifEntity.city.isNotNull() &
asset.ref(_db.remoteAssetEntity.deletedAt).isNull() &
asset.ref(_db.remoteAssetEntity.visibility).equals(AssetVisibility.timeline.index),
)
..groupBy([_db.remoteExifEntity.city])
..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]);
return query.map((row) {
final assetId = row.read(_db.remoteExifEntity.assetId);
final city = row.read(_db.remoteExifEntity.city);
return (city!, assetId!);
}).get();
}
Future<void> updateFavorite(List<String> ids, bool isFavorite) {
return _db.batch((batch) async {
for (final id in ids) {
batch.update(
_db.remoteAssetEntity,
RemoteAssetEntityCompanion(isFavorite: Value(isFavorite)),
where: (e) => e.id.equals(id),
);
}
});
}
Future<void> updateVisibility(List<String> ids, AssetVisibility visibility) {
return _db.batch((batch) async {
for (final id in ids) {
batch.update(
_db.remoteAssetEntity,
RemoteAssetEntityCompanion(visibility: Value(visibility)),
where: (e) => e.id.equals(id),
);
}
});
}
Future<void> trash(List<String> ids) {
return _db.batch((batch) async {
for (final id in ids) {
batch.update(
_db.remoteAssetEntity,
RemoteAssetEntityCompanion(deletedAt: Value(DateTime.now())),
where: (e) => e.id.equals(id),
);
}
});
}
Future<void> restoreTrash(List<String> ids) {
return _db.batch((batch) async {
for (final id in ids) {
batch.update(
_db.remoteAssetEntity,
const RemoteAssetEntityCompanion(deletedAt: Value(null)),
where: (e) => e.id.equals(id),
);
}
});
}
Future<void> delete(List<String> ids) {
return _db.batch((batch) {
for (final id in ids) {
batch.deleteWhere(_db.remoteAssetEntity, (row) => row.id.equals(id));
}
});
}
Future<void> updateLocation(List<String> ids, LatLng location) {
return _db.batch((batch) async {
for (final id in ids) {
batch.update(
_db.remoteExifEntity,
RemoteExifEntityCompanion(latitude: Value(location.latitude), longitude: Value(location.longitude)),
where: (e) => e.assetId.equals(id),
);
}
});
}
Future<void> updateDateTime(List<String> ids, DateTime dateTime) {
return _db.batch((batch) async {
for (final id in ids) {
batch.update(
_db.remoteExifEntity,
RemoteExifEntityCompanion(dateTimeOriginal: Value(dateTime)),
where: (e) => e.assetId.equals(id),
);
batch.update(
_db.remoteAssetEntity,
RemoteAssetEntityCompanion(createdAt: Value(dateTime)),
where: (e) => e.id.equals(id),
);
}
});
}
Future<void> stack(String userId, StackResponse stack) {
return _db.transaction(() async {
final stackIds = await _db.managers.stackEntity
.filter((row) => row.primaryAssetId.isIn(stack.assetIds))
.map((row) => row.id)
.get();
await _db.batch((batch) {
for (final stackId in stackIds) {
batch.deleteWhere(_db.stackEntity, (row) => row.id.equals(stackId));
}
});
await _db.batch((batch) {
final companion = StackEntityCompanion(ownerId: Value(userId), primaryAssetId: Value(stack.primaryAssetId));
batch.insert(_db.stackEntity, companion.copyWith(id: Value(stack.id)), onConflict: DoUpdate((_) => companion));
for (final assetId in stack.assetIds) {
batch.update(
_db.remoteAssetEntity,
RemoteAssetEntityCompanion(stackId: Value(stack.id)),
where: (e) => e.id.equals(assetId),
);
}
});
});
}
Future<void> unStack(List<String> stackIds) {
return _db.transaction(() async {
await _db.batch((batch) {
for (final stackId in stackIds) {
batch.deleteWhere(_db.stackEntity, (row) => row.id.equals(stackId));
}
});
// TODO: delete this after adding foreign key on stackId
await _db.batch((batch) {
for (final stackId in stackIds) {
batch.update(
_db.remoteAssetEntity,
const RemoteAssetEntityCompanion(stackId: Value(null)),
where: (e) => e.stackId.equals(stackId),
);
}
});
});
}
Future<void> updateDescription(String assetId, String description) async {
await (_db.remoteExifEntity.update()..where((row) => row.assetId.equals(assetId))).write(
RemoteExifEntityCompanion(description: Value(description)),
);
}
Future<void> updateRating(String assetId, int rating) async {
await (_db.remoteExifEntity.update()..where((row) => row.assetId.equals(assetId))).write(
RemoteExifEntityCompanion(rating: Value(rating)),
);
}
Future<int> getCount() {
return _db.managers.remoteAssetEntity.count();
}
}

View file

@ -0,0 +1,76 @@
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' hide AssetVisibility;
import 'package:immich_mobile/infrastructure/repositories/api.repository.dart';
import 'package:immich_mobile/models/search/search_filter.model.dart';
import 'package:openapi/api.dart';
class SearchApiRepository extends ApiRepository {
final SearchApi _api;
const SearchApiRepository(this._api);
Future<SearchResponseDto?> search(SearchFilter filter, int page) {
AssetTypeEnum? type;
if (filter.mediaType.index == AssetType.image.index) {
type = AssetTypeEnum.IMAGE;
} else if (filter.mediaType.index == AssetType.video.index) {
type = AssetTypeEnum.VIDEO;
}
if ((filter.context != null && filter.context!.isNotEmpty) ||
(filter.assetId != null && filter.assetId!.isNotEmpty)) {
return _api.searchSmart(
SmartSearchDto(
query: filter.context,
queryAssetId: filter.assetId,
language: filter.language,
country: filter.location.country,
state: filter.location.state,
city: filter.location.city,
make: filter.camera.make,
model: filter.camera.model,
takenAfter: filter.date.takenAfter,
takenBefore: filter.date.takenBefore,
visibility: filter.display.isArchive ? AssetVisibility.archive : AssetVisibility.timeline,
rating: filter.rating.rating,
isFavorite: filter.display.isFavorite ? true : null,
isNotInAlbum: filter.display.isNotInAlbum ? true : null,
personIds: filter.people.map((e) => e.id).toList(),
type: type,
page: page,
size: 100,
),
);
}
return _api.searchAssets(
MetadataSearchDto(
originalFileName: filter.filename != null && filter.filename!.isNotEmpty ? filter.filename : null,
country: filter.location.country,
description: filter.description != null && filter.description!.isNotEmpty ? filter.description : null,
ocr: filter.ocr != null && filter.ocr!.isNotEmpty ? filter.ocr : null,
state: filter.location.state,
city: filter.location.city,
make: filter.camera.make,
model: filter.camera.model,
takenAfter: filter.date.takenAfter,
takenBefore: filter.date.takenBefore,
visibility: filter.display.isArchive ? AssetVisibility.archive : AssetVisibility.timeline,
rating: filter.rating.rating,
isFavorite: filter.display.isFavorite ? true : null,
isNotInAlbum: filter.display.isNotInAlbum ? true : null,
personIds: filter.people.map((e) => e.id).toList(),
type: type,
page: page,
size: 1000,
),
);
}
Future<List<String>?> getSearchSuggestions(
SearchSuggestionType type, {
String? country,
String? state,
String? make,
String? model,
}) => _api.getSearchSuggestions(type, country: country, state: state, make: make, model: model);
}

View file

@ -0,0 +1,23 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/stack.model.dart';
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
class DriftStackRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftStackRepository(this._db) : super(_db);
Future<List<Stack>> getAll(String userId) {
final query = _db.stackEntity.select()..where((e) => e.ownerId.equals(userId));
return query.map((stack) {
return stack.toDto();
}).get();
}
}
extension on StackEntityData {
Stack toDto() {
return Stack(id: id, createdAt: createdAt, updatedAt: updatedAt, ownerId: ownerId, primaryAssetId: primaryAssetId);
}
}

View file

@ -0,0 +1,153 @@
import 'dart:io';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/platform_extensions.dart';
import 'package:logging/logging.dart';
import 'package:photo_manager/photo_manager.dart';
class StorageRepository {
final log = Logger('StorageRepository');
StorageRepository();
Future<File?> getFileForAsset(String assetId) async {
File? file;
final log = Logger('StorageRepository');
try {
final entity = await AssetEntity.fromId(assetId);
file = await entity?.originFile;
if (file == null) {
log.warning("Cannot get file for asset $assetId");
return null;
}
final exists = await file.exists();
if (!exists) {
log.warning("File for asset $assetId does not exist");
return null;
}
} catch (error, stackTrace) {
log.warning("Error getting file for asset $assetId", error, stackTrace);
}
return file;
}
Future<File?> getMotionFileForAsset(LocalAsset asset) async {
File? file;
final log = Logger('StorageRepository');
try {
final entity = await AssetEntity.fromId(asset.id);
file = await entity?.originFileWithSubtype;
if (file == null) {
log.warning(
"Cannot get motion file for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}",
);
return null;
}
final exists = await file.exists();
if (!exists) {
log.warning("Motion file for asset ${asset.id} does not exist");
return null;
}
} catch (error, stackTrace) {
log.warning(
"Error getting motion file for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}",
error,
stackTrace,
);
}
return file;
}
Future<AssetEntity?> getAssetEntityForAsset(LocalAsset asset) async {
final log = Logger('StorageRepository');
AssetEntity? entity;
try {
entity = await AssetEntity.fromId(asset.id);
if (entity == null) {
log.warning(
"Cannot get AssetEntity for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}",
);
}
} catch (error, stackTrace) {
log.warning(
"Error getting AssetEntity for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}",
error,
stackTrace,
);
}
return entity;
}
Future<bool> isAssetAvailableLocally(String assetId) async {
try {
final entity = await AssetEntity.fromId(assetId);
if (entity == null) {
log.warning("Cannot get AssetEntity for asset $assetId");
return false;
}
return await entity.isLocallyAvailable(isOrigin: true);
} catch (error, stackTrace) {
log.warning("Error checking if asset is locally available $assetId", error, stackTrace);
return false;
}
}
Future<File?> loadFileFromCloud(String assetId, {PMProgressHandler? progressHandler}) async {
try {
final entity = await AssetEntity.fromId(assetId);
if (entity == null) {
log.warning("Cannot get AssetEntity for asset $assetId");
return null;
}
return await entity.loadFile(progressHandler: progressHandler);
} catch (error, stackTrace) {
log.warning("Error loading file from cloud for asset $assetId", error, stackTrace);
return null;
}
}
Future<File?> loadMotionFileFromCloud(String assetId, {PMProgressHandler? progressHandler}) async {
try {
final entity = await AssetEntity.fromId(assetId);
if (entity == null) {
log.warning("Cannot get AssetEntity for asset $assetId");
return null;
}
return await entity.loadFile(withSubtype: true, progressHandler: progressHandler);
} catch (error, stackTrace) {
log.warning("Error loading motion file from cloud for asset $assetId", error, stackTrace);
return null;
}
}
Future<void> clearCache() async {
final log = Logger('StorageRepository');
try {
await PhotoManager.clearFileCache();
} catch (error, stackTrace) {
log.warning("Error clearing cache", error, stackTrace);
}
if (!CurrentPlatform.isIOS) {
return;
}
try {
if (await Directory.systemTemp.exists()) {
await Directory.systemTemp.delete(recursive: true);
}
} catch (error, stackTrace) {
log.warning("Error deleting temporary directory", error, stackTrace);
}
}
}

View file

@ -0,0 +1,192 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
import 'package:isar/isar.dart';
// Temporary interface until Isar is removed to make the service work with both Isar and Sqlite
abstract class IStoreRepository {
Future<bool> deleteAll();
Stream<List<StoreDto<Object>>> watchAll();
Future<void> delete<T>(StoreKey<T> key);
Future<bool> upsert<T>(StoreKey<T> key, T value);
Future<T?> tryGet<T>(StoreKey<T> key);
Stream<T?> watch<T>(StoreKey<T> key);
Future<List<StoreDto<Object>>> getAll();
}
class IsarStoreRepository extends IsarDatabaseRepository implements IStoreRepository {
final Isar _db;
final validStoreKeys = StoreKey.values.map((e) => e.id).toSet();
IsarStoreRepository(super.db) : _db = db;
@override
Future<bool> deleteAll() async {
return await transaction(() async {
await _db.storeValues.clear();
return true;
});
}
@override
Stream<List<StoreDto<Object>>> watchAll() {
return _db.storeValues
.filter()
.anyOf(validStoreKeys, (query, id) => query.idEqualTo(id))
.watch(fireImmediately: true)
.asyncMap((entities) => Future.wait(entities.map((entity) => _toUpdateEvent(entity))));
}
@override
Future<void> delete<T>(StoreKey<T> key) async {
return await transaction(() async => await _db.storeValues.delete(key.id));
}
@override
Future<bool> upsert<T>(StoreKey<T> key, T value) async {
return await transaction(() async {
await _db.storeValues.put(await _fromValue(key, value));
return true;
});
}
@override
Future<T?> tryGet<T>(StoreKey<T> key) async {
final entity = (await _db.storeValues.get(key.id));
if (entity == null) {
return null;
}
return await _toValue(key, entity);
}
@override
Stream<T?> watch<T>(StoreKey<T> key) async* {
yield* _db.storeValues
.watchObject(key.id, fireImmediately: true)
.asyncMap((e) async => e == null ? null : await _toValue(key, e));
}
Future<StoreDto<Object>> _toUpdateEvent(StoreValue entity) async {
final key = StoreKey.values.firstWhere((e) => e.id == entity.id) as StoreKey<Object>;
final value = await _toValue(key, entity);
return StoreDto(key, value);
}
Future<T?> _toValue<T>(StoreKey<T> key, StoreValue entity) async =>
switch (key.type) {
const (int) => entity.intValue,
const (String) => entity.strValue,
const (bool) => entity.intValue == 1,
const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!),
const (UserDto) =>
entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!),
_ => null,
}
as T?;
Future<StoreValue> _fromValue<T>(StoreKey<T> key, T value) async {
final (int? intValue, String? strValue) = switch (key.type) {
const (int) => (value as int, null),
const (String) => (null, value as String),
const (bool) => ((value as bool) ? 1 : 0, null),
const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null),
const (UserDto) => (null, (await IsarUserRepository(_db).update(value as UserDto)).id),
_ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"),
};
return StoreValue(key.id, intValue: intValue, strValue: strValue);
}
@override
Future<List<StoreDto<Object>>> getAll() async {
final entities = await _db.storeValues.filter().anyOf(validStoreKeys, (query, id) => query.idEqualTo(id)).findAll();
return Future.wait(entities.map((e) => _toUpdateEvent(e)).toList());
}
}
class DriftStoreRepository extends DriftDatabaseRepository implements IStoreRepository {
final Drift _db;
final validStoreKeys = StoreKey.values.map((e) => e.id).toSet();
DriftStoreRepository(super.db) : _db = db;
@override
Future<bool> deleteAll() async {
await _db.storeEntity.deleteAll();
return true;
}
@override
Future<List<StoreDto<Object>>> getAll() async {
final query = _db.storeEntity.select()..where((entity) => entity.id.isIn(validStoreKeys));
return query.asyncMap((entity) => _toUpdateEvent(entity)).get();
}
@override
Stream<List<StoreDto<Object>>> watchAll() {
final query = _db.storeEntity.select()..where((entity) => entity.id.isIn(validStoreKeys));
return query.asyncMap((entity) => _toUpdateEvent(entity)).watch();
}
@override
Future<void> delete<T>(StoreKey<T> key) async {
await _db.storeEntity.deleteWhere((entity) => entity.id.equals(key.id));
return;
}
@override
Future<bool> upsert<T>(StoreKey<T> key, T value) async {
await _db.storeEntity.insertOnConflictUpdate(await _fromValue(key, value));
return true;
}
@override
Future<T?> tryGet<T>(StoreKey<T> key) async {
final entity = await _db.managers.storeEntity.filter((entity) => entity.id.equals(key.id)).getSingleOrNull();
if (entity == null) {
return null;
}
return await _toValue(key, entity);
}
@override
Stream<T?> watch<T>(StoreKey<T> key) async* {
final query = _db.storeEntity.select()..where((entity) => entity.id.equals(key.id));
yield* query.watchSingleOrNull().asyncMap((e) async => e == null ? null : await _toValue(key, e));
}
Future<StoreDto<Object>> _toUpdateEvent(StoreEntityData entity) async {
final key = StoreKey.values.firstWhere((e) => e.id == entity.id) as StoreKey<Object>;
final value = await _toValue(key, entity);
return StoreDto(key, value);
}
Future<T?> _toValue<T>(StoreKey<T> key, StoreEntityData entity) async =>
switch (key.type) {
const (int) => entity.intValue,
const (String) => entity.stringValue,
const (bool) => entity.intValue == 1,
const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!),
const (UserDto) =>
entity.stringValue == null ? null : await DriftAuthUserRepository(_db).get(entity.stringValue!),
_ => null,
}
as T?;
Future<StoreEntityCompanion> _fromValue<T>(StoreKey<T> key, T value) async {
final (int? intValue, String? strValue) = switch (key.type) {
const (int) => (value as int, null),
const (String) => (null, value as String),
const (bool) => ((value as bool) ? 1 : 0, null),
const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null),
const (UserDto) => (null, (await DriftAuthUserRepository(_db).upsert(value as UserDto)).id),
_ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"),
};
return StoreEntityCompanion(id: Value(key.id), intValue: Value(intValue), stringValue: Value(strValue));
}
}

View file

@ -0,0 +1,199 @@
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/sync_event.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
class SyncApiRepository {
final Logger _logger = Logger('SyncApiRepository');
final ApiService _api;
SyncApiRepository(this._api);
Future<void> ack(List<String> data) {
return _api.syncApi.sendSyncAck(SyncAckSetDto(acks: data));
}
Future<void> deleteSyncAck(List<SyncEntityType> types) {
return _api.syncApi.deleteSyncAck(SyncAckDeleteDto(types: types));
}
Future<void> streamChanges(
Future<void> Function(List<SyncEvent>, Function() abort, Function() reset) onData, {
Function()? onReset,
int batchSize = kSyncEventBatchSize,
http.Client? httpClient,
}) async {
final stopwatch = Stopwatch()..start();
final client = httpClient ?? http.Client();
final endpoint = "${_api.apiClient.basePath}/sync/stream";
final headers = {'Content-Type': 'application/json', 'Accept': 'application/jsonlines+json'};
final headerParams = <String, String>{};
await _api.applyToParams([], headerParams);
headers.addAll(headerParams);
final shouldReset = Store.get(StoreKey.shouldResetSync, false);
final request = http.Request('POST', Uri.parse(endpoint));
request.headers.addAll(headers);
request.body = jsonEncode(
SyncStreamDto(
types: [
SyncRequestType.authUsersV1,
SyncRequestType.usersV1,
SyncRequestType.assetsV1,
SyncRequestType.assetExifsV1,
SyncRequestType.assetMetadataV1,
SyncRequestType.partnersV1,
SyncRequestType.partnerAssetsV1,
SyncRequestType.partnerAssetExifsV1,
SyncRequestType.albumsV1,
SyncRequestType.albumUsersV1,
SyncRequestType.albumAssetsV1,
SyncRequestType.albumAssetExifsV1,
SyncRequestType.albumToAssetsV1,
SyncRequestType.memoriesV1,
SyncRequestType.memoryToAssetsV1,
SyncRequestType.stacksV1,
SyncRequestType.partnerStacksV1,
SyncRequestType.userMetadataV1,
SyncRequestType.peopleV1,
SyncRequestType.assetFacesV1,
],
reset: shouldReset,
).toJson(),
);
String previousChunk = '';
List<String> lines = [];
bool shouldAbort = false;
void abort() {
_logger.warning("Abort requested, stopping sync stream");
shouldAbort = true;
}
final reset = onReset ?? () {};
try {
final response = await client.send(request);
if (response.statusCode != 200) {
final errorBody = await response.stream.bytesToString();
throw ApiException(response.statusCode, 'Failed to get sync stream: $errorBody');
}
// Reset after successful stream start
await Store.put(StoreKey.shouldResetSync, false);
await for (final chunk in response.stream.transform(utf8.decoder)) {
if (shouldAbort) {
break;
}
previousChunk += chunk;
final parts = previousChunk.toString().split('\n');
previousChunk = parts.removeLast();
lines.addAll(parts);
if (lines.length < batchSize) {
continue;
}
await onData(_parseLines(lines), abort, reset);
lines.clear();
}
if (lines.isNotEmpty && !shouldAbort) {
await onData(_parseLines(lines), abort, reset);
}
} catch (error, stack) {
return Future.error(error, stack);
} finally {
client.close();
}
stopwatch.stop();
_logger.info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms");
}
List<SyncEvent> _parseLines(List<String> lines) {
final List<SyncEvent> data = [];
for (final line in lines) {
final jsonData = jsonDecode(line);
final type = SyncEntityType.fromJson(jsonData['type'])!;
final dataJson = jsonData['data'];
final ack = jsonData['ack'];
final converter = _kResponseMap[type];
if (converter == null) {
_logger.warning("Unknown type $type");
continue;
}
data.add(SyncEvent(type: type, data: converter(dataJson), ack: ack));
}
return data;
}
}
const _kResponseMap = <SyncEntityType, Function(Object)>{
SyncEntityType.authUserV1: SyncAuthUserV1.fromJson,
SyncEntityType.userV1: SyncUserV1.fromJson,
SyncEntityType.userDeleteV1: SyncUserDeleteV1.fromJson,
SyncEntityType.partnerV1: SyncPartnerV1.fromJson,
SyncEntityType.partnerDeleteV1: SyncPartnerDeleteV1.fromJson,
SyncEntityType.assetV1: SyncAssetV1.fromJson,
SyncEntityType.assetDeleteV1: SyncAssetDeleteV1.fromJson,
SyncEntityType.assetExifV1: SyncAssetExifV1.fromJson,
SyncEntityType.assetMetadataV1: SyncAssetMetadataV1.fromJson,
SyncEntityType.assetMetadataDeleteV1: SyncAssetMetadataDeleteV1.fromJson,
SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson,
SyncEntityType.partnerAssetBackfillV1: SyncAssetV1.fromJson,
SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson,
SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson,
SyncEntityType.partnerAssetExifBackfillV1: SyncAssetExifV1.fromJson,
SyncEntityType.albumV1: SyncAlbumV1.fromJson,
SyncEntityType.albumDeleteV1: SyncAlbumDeleteV1.fromJson,
SyncEntityType.albumUserV1: SyncAlbumUserV1.fromJson,
SyncEntityType.albumUserBackfillV1: SyncAlbumUserV1.fromJson,
SyncEntityType.albumUserDeleteV1: SyncAlbumUserDeleteV1.fromJson,
SyncEntityType.albumAssetCreateV1: SyncAssetV1.fromJson,
SyncEntityType.albumAssetUpdateV1: SyncAssetV1.fromJson,
SyncEntityType.albumAssetBackfillV1: SyncAssetV1.fromJson,
SyncEntityType.albumAssetExifCreateV1: SyncAssetExifV1.fromJson,
SyncEntityType.albumAssetExifUpdateV1: SyncAssetExifV1.fromJson,
SyncEntityType.albumAssetExifBackfillV1: SyncAssetExifV1.fromJson,
SyncEntityType.albumToAssetV1: SyncAlbumToAssetV1.fromJson,
SyncEntityType.albumToAssetBackfillV1: SyncAlbumToAssetV1.fromJson,
SyncEntityType.albumToAssetDeleteV1: SyncAlbumToAssetDeleteV1.fromJson,
SyncEntityType.syncAckV1: _SyncEmptyDto.fromJson,
SyncEntityType.syncResetV1: _SyncEmptyDto.fromJson,
SyncEntityType.memoryV1: SyncMemoryV1.fromJson,
SyncEntityType.memoryDeleteV1: SyncMemoryDeleteV1.fromJson,
SyncEntityType.memoryToAssetV1: SyncMemoryAssetV1.fromJson,
SyncEntityType.memoryToAssetDeleteV1: SyncMemoryAssetDeleteV1.fromJson,
SyncEntityType.stackV1: SyncStackV1.fromJson,
SyncEntityType.stackDeleteV1: SyncStackDeleteV1.fromJson,
SyncEntityType.partnerStackV1: SyncStackV1.fromJson,
SyncEntityType.partnerStackBackfillV1: SyncStackV1.fromJson,
SyncEntityType.partnerStackDeleteV1: SyncStackDeleteV1.fromJson,
SyncEntityType.userMetadataV1: SyncUserMetadataV1.fromJson,
SyncEntityType.userMetadataDeleteV1: SyncUserMetadataDeleteV1.fromJson,
SyncEntityType.personV1: SyncPersonV1.fromJson,
SyncEntityType.personDeleteV1: SyncPersonDeleteV1.fromJson,
SyncEntityType.assetFaceV1: SyncAssetFaceV1.fromJson,
SyncEntityType.assetFaceDeleteV1: SyncAssetFaceDeleteV1.fromJson,
SyncEntityType.syncCompleteV1: _SyncEmptyDto.fromJson,
};
class _SyncEmptyDto {
static _SyncEmptyDto? fromJson(dynamic _) => _SyncEmptyDto();
}

View file

@ -0,0 +1,24 @@
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
class SyncMigrationRepository extends DriftDatabaseRepository {
final Drift _db;
const SyncMigrationRepository(super.db) : _db = db;
Future<void> v20260128CopyExifWidthHeightToAsset() async {
await _db.customStatement('''
UPDATE remote_asset_entity
SET width = CASE
WHEN exif.orientation IN ('5', '6', '7', '8', '-90', '90') THEN exif.height
ELSE exif.width
END,
height = CASE
WHEN exif.orientation IN ('5', '6', '7', '8', '-90', '90') THEN exif.width
ELSE exif.height
END
FROM remote_exif_entity exif
WHERE exif.asset_id = remote_asset_entity.id
AND (exif.width IS NOT NULL OR exif.height IS NOT NULL);
''');
}
}

View file

@ -0,0 +1,769 @@
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/memory.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/infrastructure/entities/asset_face.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole, UserMetadataKey;
import 'package:openapi/api.dart' hide AssetVisibility, AlbumUserRole, UserMetadataKey;
class SyncStreamRepository extends DriftDatabaseRepository {
final Logger _logger = Logger('DriftSyncStreamRepository');
final Drift _db;
SyncStreamRepository(super.db) : _db = db;
Future<void> reset() async {
_logger.fine("SyncResetV1 received. Resetting remote entities");
try {
await _db.exclusively(() async {
// foreign_keys PRAGMA is no-op within transactions
// https://www.sqlite.org/pragma.html#pragma_foreign_keys
await _db.customStatement('PRAGMA foreign_keys = OFF');
await transaction(() async {
await _db.assetFaceEntity.deleteAll();
await _db.memoryAssetEntity.deleteAll();
await _db.memoryEntity.deleteAll();
await _db.partnerEntity.deleteAll();
await _db.personEntity.deleteAll();
await _db.remoteAlbumAssetEntity.deleteAll();
await _db.remoteAlbumEntity.deleteAll();
await _db.remoteAlbumUserEntity.deleteAll();
await _db.remoteAssetEntity.deleteAll();
await _db.remoteExifEntity.deleteAll();
await _db.stackEntity.deleteAll();
await _db.authUserEntity.deleteAll();
await _db.userEntity.deleteAll();
await _db.userMetadataEntity.deleteAll();
await _db.remoteAssetCloudIdEntity.deleteAll();
});
await _db.customStatement('PRAGMA foreign_keys = ON');
});
} catch (error, stack) {
_logger.severe('Error: SyncResetV1', error, stack);
rethrow;
}
}
Future<void> updateAuthUsersV1(Iterable<SyncAuthUserV1> data) async {
try {
await _db.batch((batch) {
for (final user in data) {
final companion = AuthUserEntityCompanion(
name: Value(user.name),
email: Value(user.email),
hasProfileImage: Value(user.hasProfileImage),
profileChangedAt: Value(user.profileChangedAt),
avatarColor: Value(user.avatarColor?.toAvatarColor() ?? AvatarColor.primary),
isAdmin: Value(user.isAdmin),
pinCode: Value(user.pinCode),
quotaSizeInBytes: Value(user.quotaSizeInBytes ?? 0),
quotaUsageInBytes: Value(user.quotaUsageInBytes),
);
batch.insert(
_db.authUserEntity,
companion.copyWith(id: Value(user.id)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (error, stack) {
_logger.severe('Error: SyncAuthUserV1', error, stack);
rethrow;
}
}
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final user in data) {
batch.deleteWhere(_db.userEntity, (row) => row.id.equals(user.userId));
}
});
} catch (error, stack) {
_logger.severe('Error: SyncUserDeleteV1', error, stack);
rethrow;
}
}
Future<void> updateUsersV1(Iterable<SyncUserV1> data) async {
try {
await _db.batch((batch) {
for (final user in data) {
final companion = UserEntityCompanion(
name: Value(user.name),
email: Value(user.email),
hasProfileImage: Value(user.hasProfileImage),
profileChangedAt: Value(user.profileChangedAt),
avatarColor: Value(user.avatarColor?.toAvatarColor() ?? AvatarColor.primary),
);
batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion));
}
});
} catch (error, stack) {
_logger.severe('Error: SyncUserV1', error, stack);
rethrow;
}
}
Future<void> deletePartnerV1(Iterable<SyncPartnerDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final partner in data) {
batch.delete(
_db.partnerEntity,
PartnerEntityCompanion(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)),
);
}
});
} catch (error, stack) {
_logger.severe('Error: SyncPartnerDeleteV1', error, stack);
rethrow;
}
}
Future<void> updatePartnerV1(Iterable<SyncPartnerV1> data) async {
try {
await _db.batch((batch) {
for (final partner in data) {
final companion = PartnerEntityCompanion(inTimeline: Value(partner.inTimeline));
batch.insert(
_db.partnerEntity,
companion.copyWith(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (error, stack) {
_logger.severe('Error: SyncPartnerV1', error, stack);
rethrow;
}
}
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data, {String debugLabel = 'user'}) async {
try {
await _db.batch((batch) {
for (final asset in data) {
batch.deleteWhere(_db.remoteAssetEntity, (row) => row.id.equals(asset.assetId));
}
});
} catch (error, stack) {
_logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack);
rethrow;
}
}
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data, {String debugLabel = 'user'}) async {
try {
await _db.batch((batch) {
for (final asset in data) {
final companion = RemoteAssetEntityCompanion(
name: Value(asset.originalFileName),
type: Value(asset.type.toAssetType()),
createdAt: Value.absentIfNull(asset.fileCreatedAt),
updatedAt: Value.absentIfNull(asset.fileModifiedAt),
durationInSeconds: Value(asset.duration?.toDuration()?.inSeconds ?? 0),
checksum: Value(asset.checksum),
isFavorite: Value(asset.isFavorite),
ownerId: Value(asset.ownerId),
localDateTime: Value(asset.localDateTime),
thumbHash: Value(asset.thumbhash),
deletedAt: Value(asset.deletedAt),
visibility: Value(asset.visibility.toAssetVisibility()),
livePhotoVideoId: Value(asset.livePhotoVideoId),
stackId: Value(asset.stackId),
libraryId: Value(asset.libraryId),
width: Value(asset.width),
height: Value(asset.height),
isEdited: Value(asset.isEdited),
);
batch.insert(
_db.remoteAssetEntity,
companion.copyWith(id: Value(asset.id)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (error, stack) {
_logger.severe('Error: updateAssetsV1 - $debugLabel', error, stack);
rethrow;
}
}
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data, {String debugLabel = 'user'}) async {
try {
await _db.batch((batch) {
for (final exif in data) {
final companion = RemoteExifEntityCompanion(
city: Value(exif.city),
state: Value(exif.state),
country: Value(exif.country),
dateTimeOriginal: Value(exif.dateTimeOriginal),
description: Value(exif.description),
exposureTime: Value(exif.exposureTime),
fNumber: Value(exif.fNumber),
fileSize: Value(exif.fileSizeInByte),
focalLength: Value(exif.focalLength),
latitude: Value(exif.latitude?.toDouble()),
longitude: Value(exif.longitude?.toDouble()),
iso: Value(exif.iso),
make: Value(exif.make),
model: Value(exif.model),
orientation: Value(exif.orientation),
timeZone: Value(exif.timeZone),
rating: Value(exif.rating),
projectionType: Value(exif.projectionType),
lens: Value(exif.lensModel),
width: Value(exif.exifImageWidth),
height: Value(exif.exifImageHeight),
);
batch.insert(
_db.remoteExifEntity,
companion.copyWith(assetId: Value(exif.assetId)),
onConflict: DoUpdate((_) => companion),
);
}
});
await _db.batch((batch) {
for (final exif in data) {
int? width;
int? height;
if (ExifDtoConverter.isOrientationFlipped(exif.orientation)) {
width = exif.exifImageHeight;
height = exif.exifImageWidth;
} else {
width = exif.exifImageWidth;
height = exif.exifImageHeight;
}
batch.update(
_db.remoteAssetEntity,
RemoteAssetEntityCompanion(width: Value(width), height: Value(height)),
where: (row) => row.id.equals(exif.assetId) & row.width.isNull() & row.height.isNull(),
);
}
});
} catch (error, stack) {
_logger.severe('Error: updateAssetsExifV1 - $debugLabel', error, stack);
rethrow;
}
}
Future<void> deleteAssetsMetadataV1(Iterable<SyncAssetMetadataDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final metadata in data) {
if (metadata.key == kMobileMetadataKey) {
batch.deleteWhere(_db.remoteAssetCloudIdEntity, (row) => row.assetId.equals(metadata.assetId));
}
}
});
} catch (error, stack) {
_logger.severe('Error: deleteAssetsMetadataV1', error, stack);
rethrow;
}
}
Future<void> updateAssetsMetadataV1(Iterable<SyncAssetMetadataV1> data) async {
try {
await _db.batch((batch) {
for (final metadata in data) {
if (metadata.key == kMobileMetadataKey) {
final map = metadata.value as Map<String, Object?>;
final companion = RemoteAssetCloudIdEntityCompanion(
cloudId: Value(map['iCloudId']?.toString()),
createdAt: Value(map['createdAt'] != null ? DateTime.parse(map['createdAt'] as String) : null),
adjustmentTime: Value(
map['adjustmentTime'] != null ? DateTime.parse(map['adjustmentTime'] as String) : null,
),
latitude: Value(map['latitude'] != null ? (double.tryParse(map['latitude'] as String)) : null),
longitude: Value(map['longitude'] != null ? (double.tryParse(map['longitude'] as String)) : null),
);
batch.insert(
_db.remoteAssetCloudIdEntity,
companion.copyWith(assetId: Value(metadata.assetId)),
onConflict: DoUpdate((_) => companion),
);
}
}
});
} catch (error, stack) {
_logger.severe('Error: updateAssetsMetadataV1', error, stack);
rethrow;
}
}
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final album in data) {
batch.deleteWhere(_db.remoteAlbumEntity, (row) => row.id.equals(album.albumId));
}
});
} catch (error, stack) {
_logger.severe('Error: deleteAlbumsV1', error, stack);
rethrow;
}
}
Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data) async {
try {
await _db.batch((batch) {
for (final album in data) {
final companion = RemoteAlbumEntityCompanion(
name: Value(album.name),
description: Value(album.description),
isActivityEnabled: Value(album.isActivityEnabled),
order: Value(album.order.toAlbumAssetOrder()),
thumbnailAssetId: Value(album.thumbnailAssetId),
ownerId: Value(album.ownerId),
createdAt: Value(album.createdAt),
updatedAt: Value(album.updatedAt),
);
batch.insert(
_db.remoteAlbumEntity,
companion.copyWith(id: Value(album.id)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (error, stack) {
_logger.severe('Error: updateAlbumsV1', error, stack);
rethrow;
}
}
Future<void> deleteAlbumUsersV1(Iterable<SyncAlbumUserDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final album in data) {
batch.delete(
_db.remoteAlbumUserEntity,
RemoteAlbumUserEntityCompanion(albumId: Value(album.albumId), userId: Value(album.userId)),
);
}
});
} catch (error, stack) {
_logger.severe('Error: deleteAlbumUsersV1', error, stack);
rethrow;
}
}
Future<void> updateAlbumUsersV1(Iterable<SyncAlbumUserV1> data, {String debugLabel = 'user'}) async {
try {
await _db.batch((batch) {
for (final album in data) {
final companion = RemoteAlbumUserEntityCompanion(role: Value(album.role.toAlbumUserRole()));
batch.insert(
_db.remoteAlbumUserEntity,
companion.copyWith(albumId: Value(album.albumId), userId: Value(album.userId)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (error, stack) {
_logger.severe('Error: updateAlbumUsersV1 - $debugLabel', error, stack);
rethrow;
}
}
Future<void> deleteAlbumToAssetsV1(Iterable<SyncAlbumToAssetDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final album in data) {
batch.delete(
_db.remoteAlbumAssetEntity,
RemoteAlbumAssetEntityCompanion(albumId: Value(album.albumId), assetId: Value(album.assetId)),
);
}
});
} catch (error, stack) {
_logger.severe('Error: deleteAlbumToAssetsV1', error, stack);
rethrow;
}
}
Future<void> updateAlbumToAssetsV1(Iterable<SyncAlbumToAssetV1> data, {String debugLabel = 'user'}) async {
try {
await _db.batch((batch) {
for (final album in data) {
final companion = RemoteAlbumAssetEntityCompanion(
albumId: Value(album.albumId),
assetId: Value(album.assetId),
);
batch.insert(_db.remoteAlbumAssetEntity, companion, onConflict: DoNothing());
}
});
} catch (error, stack) {
_logger.severe('Error: updateAlbumToAssetsV1 - $debugLabel', error, stack);
rethrow;
}
}
Future<void> updateMemoriesV1(Iterable<SyncMemoryV1> data) async {
try {
await _db.batch((batch) {
for (final memory in data) {
final companion = MemoryEntityCompanion(
createdAt: Value(memory.createdAt),
deletedAt: Value(memory.deletedAt),
ownerId: Value(memory.ownerId),
type: Value(memory.type.toMemoryType()),
data: Value(jsonEncode(memory.data)),
isSaved: Value(memory.isSaved),
memoryAt: Value(memory.memoryAt),
seenAt: Value.absentIfNull(memory.seenAt),
showAt: Value.absentIfNull(memory.showAt),
hideAt: Value.absentIfNull(memory.hideAt),
);
batch.insert(
_db.memoryEntity,
companion.copyWith(id: Value(memory.id)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (error, stack) {
_logger.severe('Error: updateMemoriesV1', error, stack);
rethrow;
}
}
Future<void> deleteMemoriesV1(Iterable<SyncMemoryDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final memory in data) {
batch.deleteWhere(_db.memoryEntity, (row) => row.id.equals(memory.memoryId));
}
});
} catch (error, stack) {
_logger.severe('Error: deleteMemoriesV1', error, stack);
rethrow;
}
}
Future<void> updateMemoryAssetsV1(Iterable<SyncMemoryAssetV1> data) async {
try {
await _db.batch((batch) {
for (final asset in data) {
final companion = MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId));
batch.insert(_db.memoryAssetEntity, companion, onConflict: DoNothing());
}
});
} catch (error, stack) {
_logger.severe('Error: updateMemoryAssetsV1', error, stack);
rethrow;
}
}
Future<void> deleteMemoryAssetsV1(Iterable<SyncMemoryAssetDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final asset in data) {
batch.delete(
_db.memoryAssetEntity,
MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId)),
);
}
});
} catch (error, stack) {
_logger.severe('Error: deleteMemoryAssetsV1', error, stack);
rethrow;
}
}
Future<void> updateStacksV1(Iterable<SyncStackV1> data, {String debugLabel = 'user'}) async {
try {
await _db.batch((batch) {
for (final stack in data) {
final companion = StackEntityCompanion(
createdAt: Value(stack.createdAt),
updatedAt: Value(stack.updatedAt),
ownerId: Value(stack.ownerId),
primaryAssetId: Value(stack.primaryAssetId),
);
batch.insert(
_db.stackEntity,
companion.copyWith(id: Value(stack.id)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (error, stack) {
_logger.severe('Error: updateStacksV1 - $debugLabel', error, stack);
rethrow;
}
}
Future<void> deleteStacksV1(Iterable<SyncStackDeleteV1> data, {String debugLabel = 'user'}) async {
try {
await _db.batch((batch) {
for (final stack in data) {
batch.deleteWhere(_db.stackEntity, (row) => row.id.equals(stack.stackId));
}
});
} catch (error, stack) {
_logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack);
rethrow;
}
}
Future<void> updateUserMetadatasV1(Iterable<SyncUserMetadataV1> data) async {
try {
await _db.batch((batch) {
for (final userMetadata in data) {
final companion = UserMetadataEntityCompanion(value: Value(userMetadata.value as Map<String, Object?>));
batch.insert(
_db.userMetadataEntity,
companion.copyWith(userId: Value(userMetadata.userId), key: Value(userMetadata.key.toUserMetadataKey())),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (error, stack) {
_logger.severe('Error: deleteUserMetadatasV1', error, stack);
rethrow;
}
}
Future<void> deleteUserMetadatasV1(Iterable<SyncUserMetadataDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final userMetadata in data) {
batch.delete(
_db.userMetadataEntity,
UserMetadataEntityCompanion(
userId: Value(userMetadata.userId),
key: Value(userMetadata.key.toUserMetadataKey()),
),
);
}
});
} catch (error, stack) {
_logger.severe('Error: deleteUserMetadatasV1', error, stack);
rethrow;
}
}
Future<void> updatePeopleV1(Iterable<SyncPersonV1> data) async {
try {
await _db.batch((batch) {
for (final person in data) {
final companion = PersonEntityCompanion(
createdAt: Value(person.createdAt),
updatedAt: Value(person.updatedAt),
ownerId: Value(person.ownerId),
name: Value(person.name),
faceAssetId: Value(person.faceAssetId),
isFavorite: Value(person.isFavorite),
isHidden: Value(person.isHidden),
color: Value(person.color),
birthDate: Value(person.birthDate),
);
batch.insert(
_db.personEntity,
companion.copyWith(id: Value(person.id)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (error, stack) {
_logger.severe('Error: updatePeopleV1', error, stack);
rethrow;
}
}
Future<void> deletePeopleV1(Iterable<SyncPersonDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final person in data) {
batch.deleteWhere(_db.personEntity, (row) => row.id.equals(person.personId));
}
});
} catch (error, stack) {
_logger.severe('Error: deletePeopleV1', error, stack);
rethrow;
}
}
Future<void> updateAssetFacesV1(Iterable<SyncAssetFaceV1> data) async {
try {
await _db.batch((batch) {
for (final assetFace in data) {
final companion = AssetFaceEntityCompanion(
assetId: Value(assetFace.assetId),
personId: Value(assetFace.personId),
imageWidth: Value(assetFace.imageWidth),
imageHeight: Value(assetFace.imageHeight),
boundingBoxX1: Value(assetFace.boundingBoxX1),
boundingBoxY1: Value(assetFace.boundingBoxY1),
boundingBoxX2: Value(assetFace.boundingBoxX2),
boundingBoxY2: Value(assetFace.boundingBoxY2),
sourceType: Value(assetFace.sourceType),
);
batch.insert(
_db.assetFaceEntity,
companion.copyWith(id: Value(assetFace.id)),
onConflict: DoUpdate((_) => companion),
);
}
});
} catch (error, stack) {
_logger.severe('Error: updateAssetFacesV1', error, stack);
rethrow;
}
}
Future<void> deleteAssetFacesV1(Iterable<SyncAssetFaceDeleteV1> data) async {
try {
await _db.batch((batch) {
for (final assetFace in data) {
batch.deleteWhere(_db.assetFaceEntity, (row) => row.id.equals(assetFace.assetFaceId));
}
});
} catch (error, stack) {
_logger.severe('Error: deleteAssetFacesV1', error, stack);
rethrow;
}
}
Future<void> pruneAssets() async {
try {
await _db.transaction(() async {
final authQuery = _db.authUserEntity.selectOnly()
..addColumns([_db.authUserEntity.id])
..limit(1);
final currentUserId = await authQuery.map((row) => row.read(_db.authUserEntity.id)).getSingleOrNull();
if (currentUserId == null) {
_logger.warning('No authenticated user found during pruneAssets. Skipping asset pruning.');
return;
}
final partnerQuery = _db.partnerEntity.selectOnly()
..addColumns([_db.partnerEntity.sharedById])
..where(_db.partnerEntity.sharedWithId.equals(currentUserId));
final partnerIds = await partnerQuery.map((row) => row.read(_db.partnerEntity.sharedById)).get();
final validUsers = {currentUserId, ...partnerIds.nonNulls};
// Asset is not owned by the current user or any of their partners and is not part of any (shared) album
// Likely a stale asset that was previously shared but has been removed
await _db.remoteAssetEntity.deleteWhere((asset) {
return asset.ownerId.isNotIn(validUsers) &
asset.id.isNotInQuery(
_db.remoteAlbumAssetEntity.selectOnly()..addColumns([_db.remoteAlbumAssetEntity.assetId]),
);
});
});
} catch (error, stack) {
_logger.severe('Error: pruneAssets', error, stack);
// We do not rethrow here as this is a client-only cleanup and should not affect the sync process
}
}
}
extension on AssetTypeEnum {
AssetType toAssetType() => switch (this) {
AssetTypeEnum.IMAGE => AssetType.image,
AssetTypeEnum.VIDEO => AssetType.video,
AssetTypeEnum.AUDIO => AssetType.audio,
AssetTypeEnum.OTHER => AssetType.other,
_ => throw Exception('Unknown AssetType value: $this'),
};
}
extension on AssetOrder {
AlbumAssetOrder toAlbumAssetOrder() => switch (this) {
AssetOrder.asc => AlbumAssetOrder.asc,
AssetOrder.desc => AlbumAssetOrder.desc,
_ => throw Exception('Unknown AssetOrder value: $this'),
};
}
extension on MemoryType {
MemoryTypeEnum toMemoryType() => switch (this) {
MemoryType.onThisDay => MemoryTypeEnum.onThisDay,
_ => throw Exception('Unknown MemoryType value: $this'),
};
}
extension on api.AlbumUserRole {
AlbumUserRole toAlbumUserRole() => switch (this) {
api.AlbumUserRole.editor => AlbumUserRole.editor,
api.AlbumUserRole.viewer => AlbumUserRole.viewer,
_ => throw Exception('Unknown AlbumUserRole value: $this'),
};
}
extension on api.AssetVisibility {
AssetVisibility toAssetVisibility() => switch (this) {
api.AssetVisibility.timeline => AssetVisibility.timeline,
api.AssetVisibility.hidden => AssetVisibility.hidden,
api.AssetVisibility.archive => AssetVisibility.archive,
api.AssetVisibility.locked => AssetVisibility.locked,
_ => throw Exception('Unknown AssetVisibility value: $this'),
};
}
extension on api.UserMetadataKey {
UserMetadataKey toUserMetadataKey() => switch (this) {
api.UserMetadataKey.onboarding => UserMetadataKey.onboarding,
api.UserMetadataKey.preferences => UserMetadataKey.preferences,
api.UserMetadataKey.license => UserMetadataKey.license,
_ => throw Exception('Unknown UserMetadataKey value: $this'),
};
}
extension on String {
Duration? toDuration() {
try {
final parts = split(':').map((e) => double.parse(e).toInt()).toList(growable: false);
return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]);
} catch (_) {
return null;
}
}
}
extension on UserAvatarColor {
AvatarColor? toAvatarColor() => AvatarColor.values.firstWhereOrNull((c) => c.name == value);
}

View file

@ -0,0 +1,688 @@
import 'dart:async';
import 'package:drift/drift.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/map.repository.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
import 'package:stream_transform/stream_transform.dart';
class TimelineMapOptions {
final LatLngBounds bounds;
final bool onlyFavorites;
final bool includeArchived;
final bool withPartners;
final int relativeDays;
const TimelineMapOptions({
required this.bounds,
this.onlyFavorites = false,
this.includeArchived = false,
this.withPartners = false,
this.relativeDays = 0,
});
}
class DriftTimelineRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftTimelineRepository(super._db) : _db = _db;
Stream<List<String>> watchTimelineUserIds(String userId) {
final query = _db.partnerEntity.selectOnly()
..addColumns([_db.partnerEntity.sharedById])
..where(_db.partnerEntity.inTimeline.equals(true) & _db.partnerEntity.sharedWithId.equals(userId));
return query
.map((row) => row.read(_db.partnerEntity.sharedById)!)
.watch()
// Add current user ID to the list
.map((users) => users..add(userId));
}
TimelineQuery main(List<String> userIds, GroupAssetsBy groupBy) => (
bucketSource: () => _watchMainBucket(userIds, groupBy: groupBy),
assetSource: (offset, count) => _getMainBucketAssets(userIds, offset: offset, count: count),
origin: TimelineOrigin.main,
);
Stream<List<Bucket>> _watchMainBucket(List<String> userIds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
if (groupBy == GroupAssetsBy.none) {
throw UnsupportedError("GroupAssetsBy.none is not supported for watchMainBucket");
}
return _db.mergedAssetDrift.mergedBucket(userIds: userIds, groupBy: groupBy.index).map((row) {
final date = row.bucketDate.truncateDate(groupBy);
return TimeBucket(date: date, assetCount: row.assetCount);
}).watch();
}
Future<List<BaseAsset>> _getMainBucketAssets(List<String> userIds, {required int offset, required int count}) {
return _db.mergedAssetDrift
.mergedAsset(userIds: userIds, limit: (_) => Limit(count, offset))
.map(
(row) => row.remoteId != null && row.ownerId != null
? RemoteAsset(
id: row.remoteId!,
localId: row.localId,
name: row.name,
ownerId: row.ownerId!,
checksum: row.checksum,
type: row.type,
createdAt: row.createdAt,
updatedAt: row.updatedAt,
thumbHash: row.thumbHash,
width: row.width,
height: row.height,
isFavorite: row.isFavorite,
durationInSeconds: row.durationInSeconds,
livePhotoVideoId: row.livePhotoVideoId,
stackId: row.stackId,
isEdited: row.isEdited,
)
: LocalAsset(
id: row.localId!,
remoteId: row.remoteId,
name: row.name,
checksum: row.checksum,
type: row.type,
createdAt: row.createdAt,
updatedAt: row.updatedAt,
width: row.width,
height: row.height,
isFavorite: row.isFavorite,
durationInSeconds: row.durationInSeconds,
orientation: row.orientation,
cloudId: row.iCloudId,
latitude: row.latitude,
longitude: row.longitude,
adjustmentTime: row.adjustmentTime,
isEdited: row.isEdited,
),
)
.get();
}
TimelineQuery localAlbum(String albumId, GroupAssetsBy groupBy) => (
bucketSource: () => _watchLocalAlbumBucket(albumId, groupBy: groupBy),
assetSource: (offset, count) => _getLocalAlbumBucketAssets(albumId, offset: offset, count: count),
origin: TimelineOrigin.localAlbum,
);
Stream<List<Bucket>> _watchLocalAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
if (groupBy == GroupAssetsBy.none) {
return _db.localAlbumAssetEntity
.count(where: (row) => row.albumId.equals(albumId))
.map(_generateBuckets)
.watchSingle();
}
final assetCountExp = _db.localAssetEntity.id.count();
final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy);
final query =
_db.localAssetEntity.selectOnly().join([
innerJoin(
_db.localAlbumAssetEntity,
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
useColumns: false,
),
leftOuterJoin(
_db.remoteAssetEntity,
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
useColumns: false,
),
])
..addColumns([assetCountExp, dateExp])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
..groupBy([dateExp])
..orderBy([OrderingTerm.desc(dateExp)]);
return query.map((row) {
final timeline = row.read(dateExp)!.truncateDate(groupBy);
final assetCount = row.read(assetCountExp)!;
return TimeBucket(date: timeline, assetCount: assetCount);
}).watch();
}
Future<List<BaseAsset>> _getLocalAlbumBucketAssets(String albumId, {required int offset, required int count}) {
final query =
_db.localAssetEntity.select().join([
innerJoin(
_db.localAlbumAssetEntity,
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
useColumns: false,
),
leftOuterJoin(
_db.remoteAssetEntity,
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
useColumns: false,
),
])
..addColumns([_db.remoteAssetEntity.id])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)])
..limit(count, offset: offset);
return query
.map((row) => row.readTable(_db.localAssetEntity).toDto(remoteId: row.read(_db.remoteAssetEntity.id)))
.get();
}
TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => (
bucketSource: () => _watchRemoteAlbumBucket(albumId, groupBy: groupBy),
assetSource: (offset, count) => _getRemoteAlbumBucketAssets(albumId, offset: offset, count: count),
origin: TimelineOrigin.remoteAlbum,
);
Stream<List<Bucket>> _watchRemoteAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
if (groupBy == GroupAssetsBy.none) {
return _db.remoteAlbumAssetEntity
.count(where: (row) => row.albumId.equals(albumId))
.map(_generateBuckets)
.watch()
.map((results) => results.isNotEmpty ? results.first : const <Bucket>[])
.handleError((error) => const <Bucket>[]);
}
return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId)))
.watch()
.switchMap((albums) {
if (albums.isEmpty) {
return Stream.value(const <Bucket>[]);
}
final album = albums.first;
final isAscending = album.order == AlbumAssetOrder.asc;
final assetCountExp = _db.remoteAssetEntity.id.count();
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
final query = _db.remoteAssetEntity.selectOnly()
..addColumns([assetCountExp, dateExp])
..join([
innerJoin(
_db.remoteAlbumAssetEntity,
_db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false,
),
])
..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId))
..groupBy([dateExp]);
if (isAscending) {
query.orderBy([OrderingTerm.asc(dateExp)]);
} else {
query.orderBy([OrderingTerm.desc(dateExp)]);
}
return query.map((row) {
final timeline = row.read(dateExp)!.truncateDate(groupBy);
final assetCount = row.read(assetCountExp)!;
return TimeBucket(date: timeline, assetCount: assetCount);
}).watch();
})
// If there's an error (e.g., album was deleted), return empty buckets
.handleError((error) => const <Bucket>[]);
}
Future<List<BaseAsset>> _getRemoteAlbumBucketAssets(String albumId, {required int offset, required int count}) async {
final albumData = await (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).getSingleOrNull();
// If album doesn't exist (was deleted), return empty list
if (albumData == null) {
return const <BaseAsset>[];
}
final isAscending = albumData.order == AlbumAssetOrder.asc;
final query = _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([
innerJoin(
_db.remoteAlbumAssetEntity,
_db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false,
),
leftOuterJoin(
_db.localAssetEntity,
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
useColumns: false,
),
])..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId));
if (isAscending) {
query.orderBy([OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
} else {
query.orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]);
}
query.limit(count, offset: offset);
return query
.map((row) => row.readTable(_db.remoteAssetEntity).toDto(localId: row.read(_db.localAssetEntity.id)))
.get();
}
TimelineQuery fromAssets(List<BaseAsset> assets, TimelineOrigin origin) => (
bucketSource: () => Stream.value(_generateBuckets(assets.length)),
assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList(growable: false)),
origin: origin,
);
TimelineQuery fromAssetsWithBuckets(List<BaseAsset> assets, TimelineOrigin origin) {
// Sort assets by date descending and group by day
final sorted = List<BaseAsset>.from(assets)..sort((a, b) => b.createdAt.compareTo(a.createdAt));
final Map<DateTime, int> bucketCounts = {};
for (final asset in sorted) {
final date = DateTime(asset.createdAt.year, asset.createdAt.month, asset.createdAt.day);
bucketCounts[date] = (bucketCounts[date] ?? 0) + 1;
}
final buckets = bucketCounts.entries.map((e) => TimeBucket(date: e.key, assetCount: e.value)).toList();
return (
bucketSource: () => Stream.value(buckets),
assetSource: (offset, count) => Future.value(sorted.skip(offset).take(count).toList(growable: false)),
origin: origin,
);
}
TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
filter: (row) =>
row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId),
groupBy: groupBy,
origin: TimelineOrigin.remoteAssets,
);
TimelineQuery favorite(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
filter: (row) =>
row.deletedAt.isNull() &
row.isFavorite.equals(true) &
row.ownerId.equals(userId) &
(row.visibility.equalsValue(AssetVisibility.timeline) | row.visibility.equalsValue(AssetVisibility.archive)),
groupBy: groupBy,
origin: TimelineOrigin.favorite,
);
TimelineQuery trash(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId),
groupBy: groupBy,
origin: TimelineOrigin.trash,
joinLocal: true,
);
TimelineQuery archived(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
filter: (row) =>
row.deletedAt.isNull() & row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.archive),
groupBy: groupBy,
origin: TimelineOrigin.archive,
);
TimelineQuery locked(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
filter: (row) =>
row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.locked) & row.ownerId.equals(userId),
origin: TimelineOrigin.lockedFolder,
groupBy: groupBy,
);
TimelineQuery video(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
filter: (row) =>
row.deletedAt.isNull() &
row.type.equalsValue(AssetType.video) &
row.visibility.equalsValue(AssetVisibility.timeline) &
row.ownerId.equals(userId),
origin: TimelineOrigin.video,
groupBy: groupBy,
);
TimelineQuery place(String place, GroupAssetsBy groupBy) => (
bucketSource: () => _watchPlaceBucket(place, groupBy: groupBy),
assetSource: (offset, count) => _getPlaceBucketAssets(place, offset: offset, count: count),
origin: TimelineOrigin.place,
);
TimelineQuery person(String userId, String personId, GroupAssetsBy groupBy) => (
bucketSource: () => _watchPersonBucket(userId, personId, groupBy: groupBy),
assetSource: (offset, count) => _getPersonBucketAssets(userId, personId, offset: offset, count: count),
origin: TimelineOrigin.person,
);
Stream<List<Bucket>> _watchPlaceBucket(String place, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
if (groupBy == GroupAssetsBy.none) {
// TODO: implement GroupAssetBy for place
throw UnsupportedError("GroupAssetsBy.none is not supported for watchPlaceBucket");
}
final assetCountExp = _db.remoteAssetEntity.id.count();
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
final query = _db.remoteAssetEntity.selectOnly()
..addColumns([assetCountExp, dateExp])
..join([
innerJoin(
_db.remoteExifEntity,
_db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false,
),
])
..where(
_db.remoteExifEntity.city.equals(place) &
_db.remoteAssetEntity.deletedAt.isNull() &
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline),
)
..groupBy([dateExp])
..orderBy([OrderingTerm.desc(dateExp)]);
return query.map((row) {
final timeline = row.read(dateExp)!.truncateDate(groupBy);
final assetCount = row.read(assetCountExp)!;
return TimeBucket(date: timeline, assetCount: assetCount);
}).watch();
}
Future<List<BaseAsset>> _getPlaceBucketAssets(String place, {required int offset, required int count}) {
final query =
_db.remoteAssetEntity.select().join([
innerJoin(
_db.remoteExifEntity,
_db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false,
),
])
..where(
_db.remoteAssetEntity.deletedAt.isNull() &
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
_db.remoteExifEntity.city.equals(place),
)
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
..limit(count, offset: offset);
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
}
Stream<List<Bucket>> _watchPersonBucket(String userId, String personId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
if (groupBy == GroupAssetsBy.none) {
final query = _db.remoteAssetEntity.selectOnly()
..addColumns([_db.remoteAssetEntity.id.count()])
..join([
innerJoin(
_db.assetFaceEntity,
_db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false,
),
])
..where(
_db.remoteAssetEntity.deletedAt.isNull() &
_db.remoteAssetEntity.ownerId.equals(userId) &
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
_db.assetFaceEntity.personId.equals(personId),
);
return query.map((row) {
final count = row.read(_db.remoteAssetEntity.id.count())!;
return _generateBuckets(count);
}).watchSingle();
}
final assetCountExp = _db.remoteAssetEntity.id.count();
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
final query = _db.remoteAssetEntity.selectOnly()
..addColumns([assetCountExp, dateExp])
..join([
innerJoin(
_db.assetFaceEntity,
_db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false,
),
])
..where(
_db.remoteAssetEntity.deletedAt.isNull() &
_db.remoteAssetEntity.ownerId.equals(userId) &
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
_db.assetFaceEntity.personId.equals(personId),
)
..groupBy([dateExp])
..orderBy([OrderingTerm.desc(dateExp)]);
return query.map((row) {
final timeline = row.read(dateExp)!.truncateDate(groupBy);
final assetCount = row.read(assetCountExp)!;
return TimeBucket(date: timeline, assetCount: assetCount);
}).watch();
}
Future<List<BaseAsset>> _getPersonBucketAssets(
String userId,
String personId, {
required int offset,
required int count,
}) {
final query =
_db.remoteAssetEntity.select().join([
innerJoin(
_db.assetFaceEntity,
_db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false,
),
])
..where(
_db.remoteAssetEntity.deletedAt.isNull() &
_db.remoteAssetEntity.ownerId.equals(userId) &
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
_db.assetFaceEntity.personId.equals(personId),
)
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
..limit(count, offset: offset);
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
}
TimelineQuery map(List<String> userIds, TimelineMapOptions options, GroupAssetsBy groupBy) => (
bucketSource: () => _watchMapBucket(userIds, options, groupBy: groupBy),
assetSource: (offset, count) => _getMapBucketAssets(userIds, options, offset: offset, count: count),
origin: TimelineOrigin.map,
);
Stream<List<Bucket>> _watchMapBucket(
List<String> userId,
TimelineMapOptions options, {
GroupAssetsBy groupBy = GroupAssetsBy.day,
}) {
if (groupBy == GroupAssetsBy.none) {
// TODO: Support GroupAssetsBy.none
throw UnsupportedError("GroupAssetsBy.none is not supported for _watchMapBucket");
}
final assetCountExp = _db.remoteAssetEntity.id.count();
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
final query = _db.remoteAssetEntity.selectOnly()
..addColumns([assetCountExp, dateExp])
..join([
innerJoin(
_db.remoteExifEntity,
_db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false,
),
])
..where(
_db.remoteAssetEntity.ownerId.isIn(userId) &
_db.remoteExifEntity.inBounds(options.bounds) &
_db.remoteAssetEntity.visibility.isIn([
AssetVisibility.timeline.index,
if (options.includeArchived) AssetVisibility.archive.index,
]) &
_db.remoteAssetEntity.deletedAt.isNull(),
)
..groupBy([dateExp])
..orderBy([OrderingTerm.desc(dateExp)]);
if (options.onlyFavorites) {
query.where(_db.remoteAssetEntity.isFavorite.equals(true));
}
if (options.relativeDays != 0) {
final cutoffDate = DateTime.now().toUtc().subtract(Duration(days: options.relativeDays));
query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(cutoffDate));
}
return query.map((row) {
final timeline = row.read(dateExp)!.truncateDate(groupBy);
final assetCount = row.read(assetCountExp)!;
return TimeBucket(date: timeline, assetCount: assetCount);
}).watch();
}
Future<List<BaseAsset>> _getMapBucketAssets(
List<String> userId,
TimelineMapOptions options, {
required int offset,
required int count,
}) {
final query =
_db.remoteAssetEntity.select().join([
innerJoin(
_db.remoteExifEntity,
_db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false,
),
])
..where(
_db.remoteAssetEntity.ownerId.isIn(userId) &
_db.remoteExifEntity.inBounds(options.bounds) &
_db.remoteAssetEntity.visibility.isIn([
AssetVisibility.timeline.index,
if (options.includeArchived) AssetVisibility.archive.index,
]) &
_db.remoteAssetEntity.deletedAt.isNull(),
)
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
..limit(count, offset: offset);
if (options.onlyFavorites) {
query.where(_db.remoteAssetEntity.isFavorite.equals(true));
}
if (options.relativeDays != 0) {
final cutoffDate = DateTime.now().toUtc().subtract(Duration(days: options.relativeDays));
query.where(_db.remoteAssetEntity.createdAt.isBiggerOrEqualValue(cutoffDate));
}
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
}
@pragma('vm:prefer-inline')
TimelineQuery _remoteQueryBuilder({
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
required TimelineOrigin origin,
GroupAssetsBy groupBy = GroupAssetsBy.day,
bool joinLocal = false,
}) {
return (
bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy),
assetSource: (offset, count) =>
_getRemoteAssets(filter: filter, offset: offset, count: count, joinLocal: joinLocal),
origin: origin,
);
}
Stream<List<Bucket>> _watchRemoteBucket({
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
GroupAssetsBy groupBy = GroupAssetsBy.day,
}) {
if (groupBy == GroupAssetsBy.none) {
final query = _db.remoteAssetEntity.count(where: filter);
return query.map(_generateBuckets).watchSingle();
}
final assetCountExp = _db.remoteAssetEntity.id.count();
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
final query = _db.remoteAssetEntity.selectOnly()
..addColumns([assetCountExp, dateExp])
..where(filter(_db.remoteAssetEntity))
..groupBy([dateExp])
..orderBy([OrderingTerm.desc(dateExp)]);
return query.map((row) {
final timeline = row.read(dateExp)!.truncateDate(groupBy);
final assetCount = row.read(assetCountExp)!;
return TimeBucket(date: timeline, assetCount: assetCount);
}).watch();
}
@pragma('vm:prefer-inline')
Future<List<BaseAsset>> _getRemoteAssets({
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
required int offset,
required int count,
bool joinLocal = false,
}) {
if (joinLocal) {
final query =
_db.remoteAssetEntity.select().join([
leftOuterJoin(
_db.localAssetEntity,
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
useColumns: false,
),
])
..addColumns([_db.localAssetEntity.id])
..where(filter(_db.remoteAssetEntity))
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
..limit(count, offset: offset);
return query
.map((row) => row.readTable(_db.remoteAssetEntity).toDto(localId: row.read(_db.localAssetEntity.id)))
.get();
} else {
final query = _db.remoteAssetEntity.select()
..where(filter)
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
..limit(count, offset: offset);
return query.map((row) => row.toDto()).get();
}
}
}
List<Bucket> _generateBuckets(int count) {
final buckets = List.filled(
(count / kTimelineNoneSegmentSize).ceil(),
const Bucket(assetCount: kTimelineNoneSegmentSize),
);
if (count % kTimelineNoneSegmentSize != 0) {
buckets[buckets.length - 1] = Bucket(assetCount: count % kTimelineNoneSegmentSize);
}
return buckets;
}
extension on Expression<DateTime> {
Expression<String> dateFmt(GroupAssetsBy groupBy) {
// DateTimes are stored in UTC, so we need to convert them to local time inside the query before formatting
// to create the correct time bucket
final localTimeExp = modify(const DateTimeModifier.localTime());
return switch (groupBy) {
GroupAssetsBy.day || GroupAssetsBy.auto => localTimeExp.date,
GroupAssetsBy.month => localTimeExp.strftime("%Y-%m"),
GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"),
};
}
}
extension on String {
DateTime truncateDate(GroupAssetsBy groupBy) {
final format = switch (groupBy) {
GroupAssetsBy.day || GroupAssetsBy.auto => "y-M-d",
GroupAssetsBy.month => "y-M",
GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"),
};
return DateFormat(format, 'en').parse(this);
}
}

View file

@ -0,0 +1,307 @@
import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
typedef TrashedAsset = ({String albumId, LocalAsset asset});
class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftTrashedLocalAssetRepository(this._db) : super(_db);
Future<void> updateHashes(Map<String, String> hashes) {
if (hashes.isEmpty) {
return Future.value();
}
return _db.batch((batch) async {
for (final entry in hashes.entries) {
batch.update(
_db.trashedLocalAssetEntity,
TrashedLocalAssetEntityCompanion(checksum: Value(entry.value)),
where: (e) => e.id.equals(entry.key),
);
}
});
}
Future<List<LocalAsset>> getAssetsToHash(Iterable<String> albumIds) {
final query = _db.trashedLocalAssetEntity.select()..where((r) => r.albumId.isIn(albumIds) & r.checksum.isNull());
return query.map((row) => row.toLocalAsset()).get();
}
Future<Iterable<LocalAsset>> getToRestore() async {
final selectedAlbumIds = (_db.selectOnly(_db.localAlbumEntity)
..addColumns([_db.localAlbumEntity.id])
..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected)));
final rows =
await (_db.select(_db.trashedLocalAssetEntity).join([
innerJoin(
_db.remoteAssetEntity,
_db.remoteAssetEntity.checksum.equalsExp(_db.trashedLocalAssetEntity.checksum),
),
])..where(
_db.trashedLocalAssetEntity.source.equalsValue(TrashOrigin.remoteSync) &
_db.trashedLocalAssetEntity.albumId.isInQuery(selectedAlbumIds) &
_db.remoteAssetEntity.deletedAt.isNull(),
))
.get();
return rows.map((result) => result.readTable(_db.trashedLocalAssetEntity).toLocalAsset());
}
/// Applies resulted snapshot of trashed assets:
/// - upserts incoming rows
/// - deletes rows that are not present in the snapshot
Future<void> processTrashSnapshot(Iterable<TrashedAsset> trashedAssets) async {
if (trashedAssets.isEmpty) {
await _db.delete(_db.trashedLocalAssetEntity).go();
return;
}
final assetIds = trashedAssets.map((e) => e.asset.id).toSet();
Map<String, String> localChecksumById = await _getCachedChecksums(assetIds);
return _db.transaction(() async {
await _db.batch((batch) {
for (final item in trashedAssets) {
final effectiveChecksum = localChecksumById[item.asset.id] ?? item.asset.checksum;
final companion = TrashedLocalAssetEntityCompanion.insert(
id: item.asset.id,
albumId: item.albumId,
checksum: Value(effectiveChecksum),
name: item.asset.name,
type: item.asset.type,
createdAt: Value(item.asset.createdAt),
updatedAt: Value(item.asset.updatedAt),
width: Value(item.asset.width),
height: Value(item.asset.height),
durationInSeconds: Value(item.asset.durationInSeconds),
isFavorite: Value(item.asset.isFavorite),
orientation: Value(item.asset.orientation),
source: TrashOrigin.localSync,
);
batch.insert<$TrashedLocalAssetEntityTable, TrashedLocalAssetEntityData>(
_db.trashedLocalAssetEntity,
companion,
onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(item.asset.updatedAt)),
);
}
});
if (assetIds.length <= kDriftMaxChunk) {
await (_db.delete(_db.trashedLocalAssetEntity)..where((row) => row.id.isNotIn(assetIds))).go();
} else {
final existingIds = await (_db.selectOnly(
_db.trashedLocalAssetEntity,
)..addColumns([_db.trashedLocalAssetEntity.id])).map((r) => r.read(_db.trashedLocalAssetEntity.id)!).get();
final idToDelete = existingIds.where((id) => !assetIds.contains(id));
for (final slice in idToDelete.slices(kDriftMaxChunk)) {
await (_db.delete(_db.trashedLocalAssetEntity)..where((t) => t.id.isIn(slice))).go();
}
}
});
}
Stream<int> watchCount() {
return (_db.selectOnly(_db.trashedLocalAssetEntity)..addColumns([_db.trashedLocalAssetEntity.id.count()]))
.watchSingle()
.map((row) => row.read<int>(_db.trashedLocalAssetEntity.id.count()) ?? 0);
}
Stream<int> watchHashedCount() {
return (_db.selectOnly(_db.trashedLocalAssetEntity)
..addColumns([_db.trashedLocalAssetEntity.id.count()])
..where(_db.trashedLocalAssetEntity.checksum.isNotNull()))
.watchSingle()
.map((row) => row.read<int>(_db.trashedLocalAssetEntity.id.count()) ?? 0);
}
Future<void> trashLocalAsset(Map<String, List<LocalAsset>> assetsByAlbums) async {
if (assetsByAlbums.isEmpty) {
return Future.value();
}
final companions = <TrashedLocalAssetEntityCompanion>[];
final idToDelete = <String>{};
for (final entry in assetsByAlbums.entries) {
for (final asset in entry.value) {
idToDelete.add(asset.id);
companions.add(
TrashedLocalAssetEntityCompanion(
id: Value(asset.id),
name: Value(asset.name),
albumId: Value(entry.key),
checksum: Value(asset.checksum),
type: Value(asset.type),
width: Value(asset.width),
height: Value(asset.height),
durationInSeconds: Value(asset.durationInSeconds),
isFavorite: Value(asset.isFavorite),
orientation: Value(asset.orientation),
createdAt: Value(asset.createdAt),
updatedAt: Value(asset.updatedAt),
source: const Value(TrashOrigin.remoteSync),
),
);
}
}
await _db.transaction(() async {
for (final companion in companions) {
await _db.into(_db.trashedLocalAssetEntity).insertOnConflictUpdate(companion);
}
for (final slice in idToDelete.slices(kDriftMaxChunk)) {
await (_db.delete(_db.localAssetEntity)..where((t) => t.id.isIn(slice))).go();
}
});
}
Future<void> applyRestoredAssets(List<String> idList) async {
if (idList.isEmpty) {
return Future.value();
}
final trashedAssets = <TrashedLocalAssetEntityData>[];
for (final slice in idList.slices(kDriftMaxChunk)) {
final q = _db.select(_db.trashedLocalAssetEntity)..where((t) => t.id.isIn(slice));
trashedAssets.addAll(await q.get());
}
if (trashedAssets.isEmpty) {
return;
}
final companions = trashedAssets.map((e) {
return LocalAssetEntityCompanion.insert(
id: e.id,
name: e.name,
type: e.type,
createdAt: Value(e.createdAt),
updatedAt: Value(e.updatedAt),
width: Value(e.width),
height: Value(e.height),
durationInSeconds: Value(e.durationInSeconds),
checksum: Value(e.checksum),
isFavorite: Value(e.isFavorite),
orientation: Value(e.orientation),
);
});
await _db.transaction(() async {
for (final companion in companions) {
await _db.into(_db.localAssetEntity).insertOnConflictUpdate(companion);
}
for (final slice in idList.slices(kDriftMaxChunk)) {
await (_db.delete(_db.trashedLocalAssetEntity)..where((t) => t.id.isIn(slice))).go();
}
});
}
Future<void> applyTrashedAssets(List<String> idList) async {
if (idList.isEmpty) {
return Future.value();
}
final trashedAssets = <({LocalAssetEntityData asset, String albumId})>[];
for (final slice in idList.slices(kDriftMaxChunk)) {
final rows = await (_db.select(_db.localAlbumAssetEntity).join([
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
])..where(_db.localAlbumAssetEntity.assetId.isIn(slice))).get();
final assetsWithAlbum = rows.map(
(row) =>
(albumId: row.readTable(_db.localAlbumAssetEntity).albumId, asset: row.readTable(_db.localAssetEntity)),
);
trashedAssets.addAll(assetsWithAlbum);
}
if (trashedAssets.isEmpty) {
return;
}
final companions = trashedAssets.map((e) {
return TrashedLocalAssetEntityCompanion.insert(
id: e.asset.id,
name: e.asset.name,
type: e.asset.type,
createdAt: Value(e.asset.createdAt),
updatedAt: Value(e.asset.updatedAt),
width: Value(e.asset.width),
height: Value(e.asset.height),
durationInSeconds: Value(e.asset.durationInSeconds),
checksum: Value(e.asset.checksum),
isFavorite: Value(e.asset.isFavorite),
orientation: Value(e.asset.orientation),
source: TrashOrigin.localUser,
albumId: e.albumId,
);
});
await _db.transaction(() async {
for (final companion in companions) {
await _db.into(_db.trashedLocalAssetEntity).insertOnConflictUpdate(companion);
}
for (final slice in idList.slices(kDriftMaxChunk)) {
await (_db.delete(_db.localAssetEntity)..where((t) => t.id.isIn(slice))).go();
}
});
}
Future<Map<String, List<LocalAsset>>> getToTrash() async {
final result = <String, List<LocalAsset>>{};
final rows =
await (_db.select(_db.localAlbumAssetEntity).join([
innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id)),
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
leftOuterJoin(
_db.remoteAssetEntity,
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
),
])..where(
_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected) &
_db.remoteAssetEntity.deletedAt.isNotNull(),
))
.get();
for (final row in rows) {
final albumId = row.readTable(_db.localAlbumAssetEntity).albumId;
final asset = row.readTable(_db.localAssetEntity).toDto();
(result[albumId] ??= <LocalAsset>[]).add(asset);
}
return result;
}
//attempt to reuse existing checksums
Future<Map<String, String>> _getCachedChecksums(Set<String> assetIds) async {
final localChecksumById = <String, String>{};
for (final slice in assetIds.slices(kDriftMaxChunk)) {
final rows =
await (_db.selectOnly(_db.localAssetEntity)
..where(_db.localAssetEntity.id.isIn(slice) & _db.localAssetEntity.checksum.isNotNull())
..addColumns([_db.localAssetEntity.id, _db.localAssetEntity.checksum]))
.get();
for (final r in rows) {
localChecksumById[r.read(_db.localAssetEntity.id)!] = r.read(_db.localAssetEntity.checksum)!;
}
}
return localChecksumById;
}
}

View file

@ -0,0 +1,129 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/domain/models/user_metadata.model.dart';
import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity;
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/user_metadata.repository.dart';
import 'package:isar/isar.dart';
class IsarUserRepository extends IsarDatabaseRepository {
final Isar _db;
const IsarUserRepository(super.db) : _db = db;
Future<void> delete(List<String> ids) async {
await transaction(() async {
await _db.users.deleteAllById(ids);
});
}
Future<void> deleteAll() async {
await transaction(() async {
await _db.users.clear();
});
}
Future<List<UserDto>> getAll({SortUserBy? sortBy}) async {
return (await _db.users
.where()
.optional(
sortBy != null,
(query) => switch (sortBy!) {
SortUserBy.id => query.sortById(),
},
)
.findAll())
.map((u) => u.toDto())
.toList();
}
Future<UserDto?> getByUserId(String id) async {
return (await _db.users.getById(id))?.toDto();
}
Future<List<UserDto?>> getByUserIds(List<String> ids) async {
return (await _db.users.getAllById(ids)).map((u) => u?.toDto()).toList();
}
Future<bool> insert(UserDto user) async {
await transaction(() async {
await _db.users.put(entity.User.fromDto(user));
});
return true;
}
Future<UserDto> update(UserDto user) async {
await transaction(() async {
await _db.users.put(entity.User.fromDto(user));
});
return user;
}
Future<bool> updateAll(List<UserDto> users) async {
await transaction(() async {
await _db.users.putAll(users.map(entity.User.fromDto).toList());
});
return true;
}
}
class DriftAuthUserRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftAuthUserRepository(super.db) : _db = db;
Future<UserDto?> get(String id) async {
final user = await _db.managers.authUserEntity.filter((user) => user.id.equals(id)).getSingleOrNull();
if (user == null) return null;
final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(id));
final metadata = await query.map((row) => row.toDto()).get();
return user.toDto(metadata);
}
Future<UserDto> upsert(UserDto user) async {
await _db.authUserEntity.insertOnConflictUpdate(
AuthUserEntityCompanion(
id: Value(user.id),
name: Value(user.name),
email: Value(user.email),
hasProfileImage: Value(user.hasProfileImage),
profileChangedAt: Value(user.profileChangedAt),
isAdmin: Value(user.isAdmin),
quotaSizeInBytes: Value(user.quotaSizeInBytes),
quotaUsageInBytes: Value(user.quotaUsageInBytes),
avatarColor: Value(user.avatarColor),
),
);
return user;
}
}
extension on AuthUserEntityData {
UserDto toDto([List<UserMetadata>? metadata]) {
bool memoryEnabled = true;
if (metadata != null) {
for (final meta in metadata) {
if (meta.key == UserMetadataKey.preferences && meta.preferences != null) {
memoryEnabled = meta.preferences?.memoriesEnabled ?? true;
}
}
}
return UserDto(
id: id,
email: email,
name: name,
profileChangedAt: profileChangedAt,
hasProfileImage: hasProfileImage,
avatarColor: avatarColor,
memoryEnabled: memoryEnabled,
isAdmin: isAdmin,
quotaSizeInBytes: quotaSizeInBytes,
quotaUsageInBytes: quotaUsageInBytes,
);
}
}

View file

@ -0,0 +1,29 @@
import 'dart:typed_data';
import 'package:http/http.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/repositories/api.repository.dart';
import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
import 'package:openapi/api.dart';
class UserApiRepository extends ApiRepository {
final UsersApi _api;
const UserApiRepository(this._api);
Future<UserDto?> getMyUser() async {
final (adminDto, preferenceDto) = await (_api.getMyUser(), _api.getMyPreferences()).wait;
if (adminDto == null) return null;
return UserConverter.fromAdminDto(adminDto, preferenceDto);
}
Future<String> createProfileImage({required String name, required Uint8List data}) async {
final res = await checkNull(_api.createProfileImage(MultipartFile.fromBytes('file', data, filename: name)));
return res.profileImagePath;
}
Future<List<UserDto>> getAll() async {
final dto = await checkNull(_api.searchUsers());
return dto.map(UserConverter.fromSimpleUserDto).toList();
}
}

View file

@ -0,0 +1,25 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/user_metadata.model.dart';
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
class DriftUserMetadataRepository extends DriftDatabaseRepository {
final Drift _db;
const DriftUserMetadataRepository(this._db) : super(_db);
Future<List<UserMetadata>> getUserMetadata(String userId) {
final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(userId));
return query.map((userMetadata) {
return userMetadata.toDto();
}).get();
}
}
extension UserMetadataDataExtension on UserMetadataEntityData {
UserMetadata toDto() => switch (key) {
UserMetadataKey.onboarding => UserMetadata(userId: userId, key: key, onboarding: Onboarding.fromMap(value)),
UserMetadataKey.preferences => UserMetadata(userId: userId, key: key, preferences: Preferences.fromMap(value)),
UserMetadataKey.license => UserMetadata(userId: userId, key: key, license: License.fromMap(value)),
};
}

View file

@ -0,0 +1,12 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
mixin AssetEntityMixin on Table {
TextColumn get name => text()();
IntColumn get type => intEnum<AssetType>()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
IntColumn get width => integer().nullable()();
IntColumn get height => integer().nullable()();
IntColumn get durationInSeconds => integer().nullable()();
}

View file

@ -0,0 +1,9 @@
import 'package:drift/drift.dart';
mixin DriftDefaultsMixin on Table {
@override
bool get isStrict => true;
@override
bool get withoutRowId => true;
}

View file

@ -0,0 +1,57 @@
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(
fileSize: dto.fileSizeInByte,
description: dto.description,
orientation: dto.orientation,
timeZone: dto.timeZone,
dateTimeOriginal: dto.dateTimeOriginal,
isFlipped: isOrientationFlipped(dto.orientation),
latitude: dto.latitude?.toDouble(),
longitude: dto.longitude?.toDouble(),
city: dto.city,
state: dto.state,
country: dto.country,
make: dto.make,
model: dto.model,
lens: dto.lensModel,
f: dto.fNumber?.toDouble(),
mm: dto.focalLength?.toDouble(),
iso: dto.iso?.toInt(),
exposureSeconds: exposureTimeToSeconds(dto.exposureTime),
);
}
static bool isOrientationFlipped(String? orientation) {
final value = orientation == null ? null : int.tryParse(orientation);
if (value == null) {
return false;
}
final isRotated90CW = value == 5 || value == 6 || value == 90;
final isRotated270CW = value == 7 || value == 8 || value == -90;
return isRotated90CW || isRotated270CW;
}
static double? exposureTimeToSeconds(String? second) {
if (second == null) {
return null;
}
double? value = double.tryParse(second);
if (value != null) {
return value;
}
final parts = second.split("/");
if (parts.length == 2) {
final numerator = double.tryParse(parts.firstOrNull ?? "-");
final denominator = double.tryParse(parts.lastOrNull ?? "-");
if (numerator != null && denominator != null) {
return numerator / denominator;
}
}
return null;
}
}

View file

@ -0,0 +1,64 @@
import 'package:immich_mobile/domain/models/user.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(
id: dto.id,
email: dto.email,
name: dto.name,
isAdmin: false,
updatedAt: DateTime.now(),
hasProfileImage: dto.profileImagePath.isNotEmpty,
profileChangedAt: dto.profileChangedAt,
avatarColor: dto.avatarColor.toAvatarColor(),
);
static UserDto fromAdminDto(UserAdminResponseDto adminDto, [UserPreferencesResponseDto? preferenceDto]) => UserDto(
id: adminDto.id,
email: adminDto.email,
name: adminDto.name,
isAdmin: adminDto.isAdmin,
updatedAt: adminDto.updatedAt,
avatarColor: adminDto.avatarColor.toAvatarColor(),
memoryEnabled: preferenceDto?.memories.enabled ?? true,
inTimeline: false,
isPartnerSharedBy: false,
isPartnerSharedWith: false,
profileChangedAt: adminDto.profileChangedAt,
hasProfileImage: adminDto.profileImagePath.isNotEmpty,
quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0,
quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0,
);
static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto(
id: dto.id,
email: dto.email,
name: dto.name,
isAdmin: false,
updatedAt: DateTime.now(),
avatarColor: dto.avatarColor.toAvatarColor(),
memoryEnabled: false,
inTimeline: dto.inTimeline ?? false,
isPartnerSharedBy: false,
isPartnerSharedWith: false,
profileChangedAt: dto.profileChangedAt,
hasProfileImage: dto.profileImagePath.isNotEmpty,
);
}
extension on UserAvatarColor {
AvatarColor toAvatarColor() => switch (this) {
UserAvatarColor.red => AvatarColor.red,
UserAvatarColor.green => AvatarColor.green,
UserAvatarColor.blue => AvatarColor.blue,
UserAvatarColor.purple => AvatarColor.purple,
UserAvatarColor.orange => AvatarColor.orange,
UserAvatarColor.pink => AvatarColor.pink,
UserAvatarColor.amber => AvatarColor.amber,
UserAvatarColor.yellow => AvatarColor.yellow,
UserAvatarColor.gray => AvatarColor.gray,
UserAvatarColor.primary || _ => AvatarColor.primary,
};
}