feat: show stacks in asset viewer (#19935)

* feat: show stacks in asset viewer

* fix: global key issue and flash on stack asset change

* feat(mobile): stack and unstack action (#19941)

* feat(mobile): stack and unstack action

* add custom model

* use stackId from ActionSource

* Update mobile/lib/providers/infrastructure/action.provider.dart

Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>

---------

Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>

* fix: lint

* fix: bad merge

* fix: test

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
Co-authored-by: Daimolean <92239625+wuzihao051119@users.noreply.github.com>
Co-authored-by: wuzihao051119 <wuzihao051119@outlook.com>
This commit is contained in:
shenlong
2025-07-18 10:01:04 +05:30
committed by GitHub
parent 546f841b2c
commit f32cd74232
41 changed files with 1568 additions and 802 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -23,7 +23,7 @@ class ImmichLinter extends PluginBase {
return rules;
}
static makeCode(String name, LintOptions options) => LintCode(
static LintCode makeCode(String name, LintOptions options) => LintCode(
name: name,
problemMessage: options.json["message"] as String,
errorSeverity: ErrorSeverity.WARNING,
@@ -45,6 +45,7 @@ class LocalAsset extends BaseAsset {
}''';
}
// Not checking for remoteId here
@override
bool operator ==(Object other) {
if (other is! LocalAsset) return false;
@@ -14,6 +14,8 @@ class RemoteAsset extends BaseAsset {
final String? thumbHash;
final AssetVisibility visibility;
final String ownerId;
final String? stackId;
final int stackCount;
const RemoteAsset({
required this.id,
@@ -31,6 +33,8 @@ class RemoteAsset extends BaseAsset {
this.thumbHash,
this.visibility = AssetVisibility.timeline,
super.livePhotoVideoId,
this.stackId,
this.stackCount = 0,
});
@override
@@ -56,9 +60,14 @@ class RemoteAsset extends BaseAsset {
isFavorite: $isFavorite,
thumbHash: ${thumbHash ?? "<NA>"},
visibility: $visibility,
stackId: ${stackId ?? "<NA>"},
stackCount: $stackCount,
checksum: $checksum,
livePhotoVideoId: ${livePhotoVideoId ?? "<NA>"},
}''';
}
// Not checking for localId here
@override
bool operator ==(Object other) {
if (other is! RemoteAsset) return false;
@@ -67,7 +76,9 @@ class RemoteAsset extends BaseAsset {
id == other.id &&
ownerId == other.ownerId &&
thumbHash == other.thumbHash &&
visibility == other.visibility;
visibility == other.visibility &&
stackId == other.stackId &&
stackCount == other.stackCount;
}
@override
@@ -77,7 +88,9 @@ class RemoteAsset extends BaseAsset {
ownerId.hashCode ^
localId.hashCode ^
thumbHash.hashCode ^
visibility.hashCode;
visibility.hashCode ^
stackId.hashCode ^
stackCount.hashCode;
RemoteAsset copyWith({
String? id,
@@ -95,6 +108,8 @@ class RemoteAsset extends BaseAsset {
String? thumbHash,
AssetVisibility? visibility,
String? livePhotoVideoId,
String? stackId,
int? stackCount,
}) {
return RemoteAsset(
id: id ?? this.id,
@@ -112,6 +127,8 @@ class RemoteAsset extends BaseAsset {
thumbHash: thumbHash ?? this.thumbHash,
visibility: visibility ?? this.visibility,
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
stackId: stackId ?? this.stackId,
stackCount: stackCount ?? this.stackCount,
);
}
}
+24
View File
@@ -82,3 +82,27 @@ class Stack {
primaryAssetId.hashCode;
}
}
class StackResponse {
final String id;
final String primaryAssetId;
final List<String> assetIds;
const StackResponse({
required this.id,
required this.primaryAssetId,
required this.assetIds,
});
@override
bool operator ==(covariant StackResponse other) {
if (identical(this, other)) return true;
return other.id == id &&
other.primaryAssetId == primaryAssetId &&
other.assetIds == assetIds;
}
@override
int get hashCode => id.hashCode ^ primaryAssetId.hashCode ^ assetIds.hashCode;
}
@@ -1,3 +1,5 @@
import 'package:immich_mobile/domain/utils/event_stream.dart';
enum GroupAssetsBy {
day,
month,
@@ -38,3 +40,7 @@ class TimeBucket extends Bucket {
@override
int get hashCode => super.hashCode ^ date.hashCode;
}
class TimelineReloadEvent extends Event {
const TimelineReloadEvent();
}
@@ -24,6 +24,17 @@ class AssetService {
: _remoteAssetRepository.watchAsset(id);
}
Future<List<RemoteAsset>> getStack(RemoteAsset asset) async {
if (asset.stackId == null) {
return [];
}
return _remoteAssetRepository.getStackChildren(asset).then((assets) {
// Include the primary asset in the stack as the first item
return [asset, ...assets];
});
}
Future<ExifInfo?> getExif(BaseAsset asset) async {
if (!asset.hasRemote) {
return null;
+1 -9
View File
@@ -1,17 +1,9 @@
import 'dart:async';
sealed class Event {
class Event {
const Event();
}
class TimelineReloadEvent extends Event {
const TimelineReloadEvent();
}
class ViewerOpenBottomSheetEvent extends Event {
const ViewerOpenBottomSheetEvent();
}
class EventStream {
EventStream._();
@@ -1,5 +1,6 @@
import 'remote_asset.entity.dart';
import 'local_asset.entity.dart';
import 'stack.entity.dart';
mergedAsset: SELECT * FROM
(
@@ -18,13 +19,33 @@ mergedAsset: SELECT * FROM
rae.checksum,
rae.owner_id,
rae.live_photo_video_id,
0 as orientation
0 as orientation,
rae.stack_id,
COALESCE(stack_count.total_count, 0) AS stack_count
FROM
remote_asset_entity rae
LEFT JOIN
local_asset_entity lae ON rae.checksum = lae.checksum
LEFT JOIN
stack_entity se ON rae.stack_id = se.id
LEFT JOIN
(SELECT
stack_id,
COUNT(*) AS total_count
FROM remote_asset_entity
WHERE deleted_at IS NULL
AND visibility = 0
AND stack_id IS NOT NULL
GROUP BY stack_id
) AS stack_count ON rae.stack_id = stack_count.stack_id
WHERE
rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id in ?
rae.deleted_at IS NULL
AND rae.visibility = 0
AND rae.owner_id in ?
AND (
rae.stack_id IS NULL
OR rae.id = se.primary_asset_id
)
UNION ALL
SELECT
NULL as remote_id,
@@ -41,7 +62,9 @@ mergedAsset: SELECT * FROM
lae.checksum,
NULL as owner_id,
NULL as live_photo_video_id,
lae.orientation
lae.orientation,
NULL as stack_id,
0 AS stack_count
FROM
local_asset_entity lae
LEFT JOIN
@@ -68,8 +91,16 @@ FROM
remote_asset_entity rae
LEFT JOIN
local_asset_entity lae ON rae.checksum = lae.checksum
LEFT JOIN
stack_entity se ON rae.stack_id = se.id
WHERE
rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id in ?
rae.deleted_at IS NULL
AND rae.visibility = 0
AND rae.owner_id in ?
AND (
rae.stack_id IS NULL
OR rae.id = se.primary_asset_id
)
UNION ALL
SELECT
lae.name,
+15 -2
View File
@@ -7,6 +7,8 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.
as i3;
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
as i4;
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart'
as i5;
class MergedAssetDrift extends i1.ModularAccessor {
MergedAssetDrift(i0.GeneratedDatabase db) : super(db);
@@ -18,7 +20,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
final generatedlimit = $write(limit, startIndex: $arrayStartIndex);
$arrayStartIndex += generatedlimit.amountOfVariables;
return customSelect(
'SELECT * FROM (SELECT rae.id AS remote_id, lae.id AS local_id, rae.name, rae.type, rae.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 FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.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 FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}',
'SELECT * FROM (SELECT rae.id AS remote_id, lae.id AS local_id, rae.name, rae.type, rae.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, COALESCE(stack_count.total_count, 0) AS stack_count FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity AS se ON rae.stack_id = se.id LEFT JOIN (SELECT stack_id, COUNT(*) AS total_count FROM remote_asset_entity WHERE deleted_at IS NULL AND visibility = 0 AND stack_id IS NOT NULL GROUP BY stack_id) AS stack_count ON rae.stack_id = stack_count.stack_id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) 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, 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, 0 AS stack_count FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}',
variables: [
for (var $ in var1) i0.Variable<String>($),
...generatedlimit.introducedVariables
@@ -26,6 +28,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
readsFrom: {
remoteAssetEntity,
localAssetEntity,
stackEntity,
...generatedlimit.watchedTables,
}).map((i0.QueryRow row) => MergedAssetResult(
remoteId: row.readNullable<String>('remote_id'),
@@ -44,6 +47,8 @@ class MergedAssetDrift extends i1.ModularAccessor {
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'),
stackCount: row.read<int>('stack_count'),
));
}
@@ -53,7 +58,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
final expandedvar2 = $expandVar($arrayStartIndex, var2.length);
$arrayStartIndex += var2.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.name, rae.created_at FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) UNION ALL SELECT lae.name, lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) GROUP BY bucket_date ORDER BY bucket_date DESC',
'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.name, rae.created_at FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum 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 ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.name, lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) GROUP BY bucket_date ORDER BY bucket_date DESC',
variables: [
i0.Variable<int>(groupBy),
for (var $ in var2) i0.Variable<String>($)
@@ -61,6 +66,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
readsFrom: {
remoteAssetEntity,
localAssetEntity,
stackEntity,
}).map((i0.QueryRow row) => MergedBucketResult(
assetCount: row.read<int>('asset_count'),
bucketDate: row.read<String>('bucket_date'),
@@ -73,6 +79,9 @@ class MergedAssetDrift extends i1.ModularAccessor {
i4.$LocalAssetEntityTable get localAssetEntity =>
i1.ReadDatabaseContainer(attachedDatabase)
.resultSet<i4.$LocalAssetEntityTable>('local_asset_entity');
i5.$StackEntityTable get stackEntity =>
i1.ReadDatabaseContainer(attachedDatabase)
.resultSet<i5.$StackEntityTable>('stack_entity');
}
class MergedAssetResult {
@@ -91,6 +100,8 @@ class MergedAssetResult {
final String? ownerId;
final String? livePhotoVideoId;
final int orientation;
final String? stackId;
final int stackCount;
MergedAssetResult({
this.remoteId,
this.localId,
@@ -107,6 +118,8 @@ class MergedAssetResult {
this.ownerId,
this.livePhotoVideoId,
required this.orientation,
this.stackId,
required this.stackCount,
});
}
@@ -34,6 +34,8 @@ class RemoteAssetEntity extends Table
IntColumn get visibility => intEnum<AssetVisibility>()();
TextColumn get stackId => text().nullable()();
@override
Set<Column> get primaryKey => {id};
}
@@ -55,5 +57,6 @@ extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData {
visibility: visibility,
livePhotoVideoId: livePhotoVideoId,
localId: null,
stackId: stackId,
);
}
@@ -29,6 +29,7 @@ typedef $$RemoteAssetEntityTableCreateCompanionBuilder
i0.Value<DateTime?> deletedAt,
i0.Value<String?> livePhotoVideoId,
required i2.AssetVisibility visibility,
i0.Value<String?> stackId,
});
typedef $$RemoteAssetEntityTableUpdateCompanionBuilder
= i1.RemoteAssetEntityCompanion Function({
@@ -48,6 +49,7 @@ typedef $$RemoteAssetEntityTableUpdateCompanionBuilder
i0.Value<DateTime?> deletedAt,
i0.Value<String?> livePhotoVideoId,
i0.Value<i2.AssetVisibility> visibility,
i0.Value<String?> stackId,
});
final class $$RemoteAssetEntityTableReferences extends i0.BaseReferences<
@@ -145,6 +147,9 @@ class $$RemoteAssetEntityTableFilterComposer
column: $table.visibility,
builder: (column) => i0.ColumnWithTypeConverterFilters(column));
i0.ColumnFilters<String> get stackId => $composableBuilder(
column: $table.stackId, builder: (column) => i0.ColumnFilters(column));
i5.$$UserEntityTableFilterComposer get ownerId {
final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
composer: this,
@@ -231,6 +236,9 @@ class $$RemoteAssetEntityTableOrderingComposer
column: $table.visibility,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<String> get stackId => $composableBuilder(
column: $table.stackId, builder: (column) => i0.ColumnOrderings(column));
i5.$$UserEntityTableOrderingComposer get ownerId {
final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
composer: this,
@@ -309,6 +317,9 @@ class $$RemoteAssetEntityTableAnnotationComposer
$composableBuilder(
column: $table.visibility, builder: (column) => column);
i0.GeneratedColumn<String> get stackId =>
$composableBuilder(column: $table.stackId, builder: (column) => column);
i5.$$UserEntityTableAnnotationComposer get ownerId {
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
composer: this,
@@ -373,6 +384,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
i0.Value<String?> livePhotoVideoId = const i0.Value.absent(),
i0.Value<i2.AssetVisibility> visibility = const i0.Value.absent(),
i0.Value<String?> stackId = const i0.Value.absent(),
}) =>
i1.RemoteAssetEntityCompanion(
name: name,
@@ -391,6 +403,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
deletedAt: deletedAt,
livePhotoVideoId: livePhotoVideoId,
visibility: visibility,
stackId: stackId,
),
createCompanionCallback: ({
required String name,
@@ -409,6 +422,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
i0.Value<String?> livePhotoVideoId = const i0.Value.absent(),
required i2.AssetVisibility visibility,
i0.Value<String?> stackId = const i0.Value.absent(),
}) =>
i1.RemoteAssetEntityCompanion.insert(
name: name,
@@ -427,6 +441,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
deletedAt: deletedAt,
livePhotoVideoId: livePhotoVideoId,
visibility: visibility,
stackId: stackId,
),
withReferenceMapper: (p0) => p0
.map((e) => (
@@ -602,6 +617,12 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
type: i0.DriftSqlType.int, requiredDuringInsert: true)
.withConverter<i2.AssetVisibility>(
i1.$RemoteAssetEntityTable.$convertervisibility);
static const i0.VerificationMeta _stackIdMeta =
const i0.VerificationMeta('stackId');
@override
late final i0.GeneratedColumn<String> stackId = i0.GeneratedColumn<String>(
'stack_id', aliasedName, true,
type: i0.DriftSqlType.string, requiredDuringInsert: false);
@override
List<i0.GeneratedColumn> get $columns => [
name,
@@ -619,7 +640,8 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
thumbHash,
deletedAt,
livePhotoVideoId,
visibility
visibility,
stackId
];
@override
String get aliasedName => _alias ?? actualTableName;
@@ -703,6 +725,10 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
livePhotoVideoId.isAcceptableOrUnknown(
data['live_photo_video_id']!, _livePhotoVideoIdMeta));
}
if (data.containsKey('stack_id')) {
context.handle(_stackIdMeta,
stackId.isAcceptableOrUnknown(data['stack_id']!, _stackIdMeta));
}
return context;
}
@@ -748,6 +774,8 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromSql(
attachedDatabase.typeMapping.read(
i0.DriftSqlType.int, data['${effectivePrefix}visibility'])!),
stackId: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}stack_id']),
);
}
@@ -785,6 +813,7 @@ class RemoteAssetEntityData extends i0.DataClass
final DateTime? deletedAt;
final String? livePhotoVideoId;
final i2.AssetVisibility visibility;
final String? stackId;
const RemoteAssetEntityData(
{required this.name,
required this.type,
@@ -801,7 +830,8 @@ class RemoteAssetEntityData extends i0.DataClass
this.thumbHash,
this.deletedAt,
this.livePhotoVideoId,
required this.visibility});
required this.visibility,
this.stackId});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
@@ -841,6 +871,9 @@ class RemoteAssetEntityData extends i0.DataClass
map['visibility'] = i0.Variable<int>(
i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility));
}
if (!nullToAbsent || stackId != null) {
map['stack_id'] = i0.Variable<String>(stackId);
}
return map;
}
@@ -866,6 +899,7 @@ class RemoteAssetEntityData extends i0.DataClass
livePhotoVideoId: serializer.fromJson<String?>(json['livePhotoVideoId']),
visibility: i1.$RemoteAssetEntityTable.$convertervisibility
.fromJson(serializer.fromJson<int>(json['visibility'])),
stackId: serializer.fromJson<String?>(json['stackId']),
);
}
@override
@@ -890,6 +924,7 @@ class RemoteAssetEntityData extends i0.DataClass
'livePhotoVideoId': serializer.toJson<String?>(livePhotoVideoId),
'visibility': serializer.toJson<int>(
i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility)),
'stackId': serializer.toJson<String?>(stackId),
};
}
@@ -909,7 +944,8 @@ class RemoteAssetEntityData extends i0.DataClass
i0.Value<String?> thumbHash = const i0.Value.absent(),
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
i0.Value<String?> livePhotoVideoId = const i0.Value.absent(),
i2.AssetVisibility? visibility}) =>
i2.AssetVisibility? visibility,
i0.Value<String?> stackId = const i0.Value.absent()}) =>
i1.RemoteAssetEntityData(
name: name ?? this.name,
type: type ?? this.type,
@@ -932,6 +968,7 @@ class RemoteAssetEntityData extends i0.DataClass
? livePhotoVideoId.value
: this.livePhotoVideoId,
visibility: visibility ?? this.visibility,
stackId: stackId.present ? stackId.value : this.stackId,
);
RemoteAssetEntityData copyWithCompanion(i1.RemoteAssetEntityCompanion data) {
return RemoteAssetEntityData(
@@ -959,6 +996,7 @@ class RemoteAssetEntityData extends i0.DataClass
: this.livePhotoVideoId,
visibility:
data.visibility.present ? data.visibility.value : this.visibility,
stackId: data.stackId.present ? data.stackId.value : this.stackId,
);
}
@@ -980,7 +1018,8 @@ class RemoteAssetEntityData extends i0.DataClass
..write('thumbHash: $thumbHash, ')
..write('deletedAt: $deletedAt, ')
..write('livePhotoVideoId: $livePhotoVideoId, ')
..write('visibility: $visibility')
..write('visibility: $visibility, ')
..write('stackId: $stackId')
..write(')'))
.toString();
}
@@ -1002,7 +1041,8 @@ class RemoteAssetEntityData extends i0.DataClass
thumbHash,
deletedAt,
livePhotoVideoId,
visibility);
visibility,
stackId);
@override
bool operator ==(Object other) =>
identical(this, other) ||
@@ -1022,7 +1062,8 @@ class RemoteAssetEntityData extends i0.DataClass
other.thumbHash == this.thumbHash &&
other.deletedAt == this.deletedAt &&
other.livePhotoVideoId == this.livePhotoVideoId &&
other.visibility == this.visibility);
other.visibility == this.visibility &&
other.stackId == this.stackId);
}
class RemoteAssetEntityCompanion
@@ -1043,6 +1084,7 @@ class RemoteAssetEntityCompanion
final i0.Value<DateTime?> deletedAt;
final i0.Value<String?> livePhotoVideoId;
final i0.Value<i2.AssetVisibility> visibility;
final i0.Value<String?> stackId;
const RemoteAssetEntityCompanion({
this.name = const i0.Value.absent(),
this.type = const i0.Value.absent(),
@@ -1060,6 +1102,7 @@ class RemoteAssetEntityCompanion
this.deletedAt = const i0.Value.absent(),
this.livePhotoVideoId = const i0.Value.absent(),
this.visibility = const i0.Value.absent(),
this.stackId = const i0.Value.absent(),
});
RemoteAssetEntityCompanion.insert({
required String name,
@@ -1078,6 +1121,7 @@ class RemoteAssetEntityCompanion
this.deletedAt = const i0.Value.absent(),
this.livePhotoVideoId = const i0.Value.absent(),
required i2.AssetVisibility visibility,
this.stackId = const i0.Value.absent(),
}) : name = i0.Value(name),
type = i0.Value(type),
id = i0.Value(id),
@@ -1101,6 +1145,7 @@ class RemoteAssetEntityCompanion
i0.Expression<DateTime>? deletedAt,
i0.Expression<String>? livePhotoVideoId,
i0.Expression<int>? visibility,
i0.Expression<String>? stackId,
}) {
return i0.RawValuesInsertable({
if (name != null) 'name': name,
@@ -1119,6 +1164,7 @@ class RemoteAssetEntityCompanion
if (deletedAt != null) 'deleted_at': deletedAt,
if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId,
if (visibility != null) 'visibility': visibility,
if (stackId != null) 'stack_id': stackId,
});
}
@@ -1138,7 +1184,8 @@ class RemoteAssetEntityCompanion
i0.Value<String?>? thumbHash,
i0.Value<DateTime?>? deletedAt,
i0.Value<String?>? livePhotoVideoId,
i0.Value<i2.AssetVisibility>? visibility}) {
i0.Value<i2.AssetVisibility>? visibility,
i0.Value<String?>? stackId}) {
return i1.RemoteAssetEntityCompanion(
name: name ?? this.name,
type: type ?? this.type,
@@ -1156,6 +1203,7 @@ class RemoteAssetEntityCompanion
deletedAt: deletedAt ?? this.deletedAt,
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
visibility: visibility ?? this.visibility,
stackId: stackId ?? this.stackId,
);
}
@@ -1213,6 +1261,9 @@ class RemoteAssetEntityCompanion
.$RemoteAssetEntityTable.$convertervisibility
.toSql(visibility.value));
}
if (stackId.present) {
map['stack_id'] = i0.Variable<String>(stackId.value);
}
return map;
}
@@ -1234,7 +1285,8 @@ class RemoteAssetEntityCompanion
..write('thumbHash: $thumbHash, ')
..write('deletedAt: $deletedAt, ')
..write('livePhotoVideoId: $livePhotoVideoId, ')
..write('visibility: $visibility')
..write('visibility: $visibility, ')
..write('stackId: $stackId')
..write(')'))
.toString();
}
@@ -7,27 +7,27 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.
as i2;
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
as i3;
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
as i4;
import 'package:immich_mobile/infrastructure/entities/partner.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/exif.entity.drift.dart'
as i8;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
as i9;
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
as i10;
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'
as i11;
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'
as i12;
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'
as i13;
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart'
as i4;
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
as i5;
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
as i6;
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
as i7;
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
as i8;
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
as i9;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
as i10;
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
as i11;
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'
as i12;
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'
as i13;
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'
as i14;
import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart'
as i15;
@@ -41,26 +41,26 @@ abstract class $Drift extends i0.GeneratedDatabase {
i2.$RemoteAssetEntityTable(this);
late final i3.$LocalAssetEntityTable localAssetEntity =
i3.$LocalAssetEntityTable(this);
late final i4.$UserMetadataEntityTable userMetadataEntity =
i4.$UserMetadataEntityTable(this);
late final i5.$PartnerEntityTable partnerEntity =
i5.$PartnerEntityTable(this);
late final i6.$LocalAlbumEntityTable localAlbumEntity =
i6.$LocalAlbumEntityTable(this);
late final i7.$LocalAlbumAssetEntityTable localAlbumAssetEntity =
i7.$LocalAlbumAssetEntityTable(this);
late final i8.$RemoteExifEntityTable remoteExifEntity =
i8.$RemoteExifEntityTable(this);
late final i9.$RemoteAlbumEntityTable remoteAlbumEntity =
i9.$RemoteAlbumEntityTable(this);
late final i10.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity =
i10.$RemoteAlbumAssetEntityTable(this);
late final i11.$RemoteAlbumUserEntityTable remoteAlbumUserEntity =
i11.$RemoteAlbumUserEntityTable(this);
late final i12.$MemoryEntityTable memoryEntity = i12.$MemoryEntityTable(this);
late final i13.$MemoryAssetEntityTable memoryAssetEntity =
i13.$MemoryAssetEntityTable(this);
late final i14.$StackEntityTable stackEntity = i14.$StackEntityTable(this);
late final i4.$StackEntityTable stackEntity = i4.$StackEntityTable(this);
late final i5.$UserMetadataEntityTable userMetadataEntity =
i5.$UserMetadataEntityTable(this);
late final i6.$PartnerEntityTable partnerEntity =
i6.$PartnerEntityTable(this);
late final i7.$LocalAlbumEntityTable localAlbumEntity =
i7.$LocalAlbumEntityTable(this);
late final i8.$LocalAlbumAssetEntityTable localAlbumAssetEntity =
i8.$LocalAlbumAssetEntityTable(this);
late final i9.$RemoteExifEntityTable remoteExifEntity =
i9.$RemoteExifEntityTable(this);
late final i10.$RemoteAlbumEntityTable remoteAlbumEntity =
i10.$RemoteAlbumEntityTable(this);
late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity =
i11.$RemoteAlbumAssetEntityTable(this);
late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity =
i12.$RemoteAlbumUserEntityTable(this);
late final i13.$MemoryEntityTable memoryEntity = i13.$MemoryEntityTable(this);
late final i14.$MemoryAssetEntityTable memoryAssetEntity =
i14.$MemoryAssetEntityTable(this);
i15.MergedAssetDrift get mergedAssetDrift => i16.ReadDatabaseContainer(this)
.accessor<i15.MergedAssetDrift>(i15.MergedAssetDrift.new);
@override
@@ -71,6 +71,7 @@ abstract class $Drift extends i0.GeneratedDatabase {
userEntity,
remoteAssetEntity,
localAssetEntity,
stackEntity,
i3.idxLocalAssetChecksum,
i2.uQRemoteAssetOwnerChecksum,
i2.idxRemoteAssetChecksum,
@@ -83,8 +84,7 @@ abstract class $Drift extends i0.GeneratedDatabase {
remoteAlbumAssetEntity,
remoteAlbumUserEntity,
memoryEntity,
memoryAssetEntity,
stackEntity
memoryAssetEntity
];
@override
i0.StreamQueryUpdateRules get streamUpdateRules =>
@@ -97,6 +97,13 @@ abstract class $Drift extends i0.GeneratedDatabase {
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),
@@ -209,13 +216,6 @@ abstract class $Drift extends i0.GeneratedDatabase {
i0.TableUpdate('memory_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),
],
),
],
);
@override
@@ -232,27 +232,27 @@ class $DriftManager {
i2.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity);
i3.$$LocalAssetEntityTableTableManager get localAssetEntity =>
i3.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity);
i4.$$UserMetadataEntityTableTableManager get userMetadataEntity =>
i4.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity);
i5.$$PartnerEntityTableTableManager get partnerEntity =>
i5.$$PartnerEntityTableTableManager(_db, _db.partnerEntity);
i6.$$LocalAlbumEntityTableTableManager get localAlbumEntity =>
i6.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity);
i7.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i7
i4.$$StackEntityTableTableManager get stackEntity =>
i4.$$StackEntityTableTableManager(_db, _db.stackEntity);
i5.$$UserMetadataEntityTableTableManager get userMetadataEntity =>
i5.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity);
i6.$$PartnerEntityTableTableManager get partnerEntity =>
i6.$$PartnerEntityTableTableManager(_db, _db.partnerEntity);
i7.$$LocalAlbumEntityTableTableManager get localAlbumEntity =>
i7.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity);
i8.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i8
.$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity);
i8.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
i8.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
i9.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
i9.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
i10.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
i10.$$RemoteAlbumAssetEntityTableTableManager(
i9.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
i9.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
i10.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
i10.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
i11.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
i11.$$RemoteAlbumAssetEntityTableTableManager(
_db, _db.remoteAlbumAssetEntity);
i11.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i11
i12.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i12
.$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity);
i12.$$MemoryEntityTableTableManager get memoryEntity =>
i12.$$MemoryEntityTableTableManager(_db, _db.memoryEntity);
i13.$$MemoryAssetEntityTableTableManager get memoryAssetEntity =>
i13.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity);
i14.$$StackEntityTableTableManager get stackEntity =>
i14.$$StackEntityTableTableManager(_db, _db.stackEntity);
i13.$$MemoryEntityTableTableManager get memoryEntity =>
i13.$$MemoryEntityTableTableManager(_db, _db.memoryEntity);
i14.$$MemoryAssetEntityTableTableManager get memoryAssetEntity =>
i14.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity);
}
@@ -12,6 +12,7 @@ final class Schema2 extends i0.VersionedSchema {
userEntity,
remoteAssetEntity,
localAssetEntity,
stackEntity,
idxLocalAssetChecksum,
uQRemoteAssetOwnerChecksum,
idxRemoteAssetChecksum,
@@ -25,7 +26,6 @@ final class Schema2 extends i0.VersionedSchema {
remoteAlbumUserEntity,
memoryEntity,
memoryAssetEntity,
stackEntity,
];
late final Shape0 userEntity = Shape0(
source: i0.VersionedTable(
@@ -73,6 +73,7 @@ final class Schema2 extends i0.VersionedSchema {
_column_18,
_column_19,
_column_20,
_column_21,
],
attachedDatabase: database,
),
@@ -94,9 +95,27 @@ final class Schema2 extends i0.VersionedSchema {
_column_11,
_column_12,
_column_0,
_column_21,
_column_14,
_column_22,
_column_14,
_column_23,
],
attachedDatabase: database,
),
alias: null);
late final Shape3 stackEntity = Shape3(
source: i0.VersionedTable(
entityName: 'stack_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: [
'PRIMARY KEY(id)',
],
columns: [
_column_0,
_column_9,
_column_5,
_column_15,
_column_24,
],
attachedDatabase: database,
),
@@ -108,7 +127,7 @@ final class Schema2 extends i0.VersionedSchema {
'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)');
final i1.Index idxRemoteAssetChecksum = i1.Index('idx_remote_asset_checksum',
'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)');
late final Shape3 userMetadataEntity = Shape3(
late final Shape4 userMetadataEntity = Shape4(
source: i0.VersionedTable(
entityName: 'user_metadata_entity',
withoutRowId: true,
@@ -117,14 +136,14 @@ final class Schema2 extends i0.VersionedSchema {
'PRIMARY KEY(user_id, "key")',
],
columns: [
_column_23,
_column_24,
_column_25,
_column_26,
_column_27,
],
attachedDatabase: database,
),
alias: null);
late final Shape4 partnerEntity = Shape4(
late final Shape5 partnerEntity = Shape5(
source: i0.VersionedTable(
entityName: 'partner_entity',
withoutRowId: true,
@@ -133,14 +152,14 @@ final class Schema2 extends i0.VersionedSchema {
'PRIMARY KEY(shared_by_id, shared_with_id)',
],
columns: [
_column_26,
_column_27,
_column_28,
_column_29,
_column_30,
],
attachedDatabase: database,
),
alias: null);
late final Shape5 localAlbumEntity = Shape5(
late final Shape6 localAlbumEntity = Shape6(
source: i0.VersionedTable(
entityName: 'local_album_entity',
withoutRowId: true,
@@ -152,14 +171,14 @@ final class Schema2 extends i0.VersionedSchema {
_column_0,
_column_1,
_column_5,
_column_29,
_column_30,
_column_31,
_column_32,
_column_33,
],
attachedDatabase: database,
),
alias: null);
late final Shape6 localAlbumAssetEntity = Shape6(
late final Shape7 localAlbumAssetEntity = Shape7(
source: i0.VersionedTable(
entityName: 'local_album_asset_entity',
withoutRowId: true,
@@ -168,13 +187,13 @@ final class Schema2 extends i0.VersionedSchema {
'PRIMARY KEY(asset_id, album_id)',
],
columns: [
_column_32,
_column_33,
_column_34,
_column_35,
],
attachedDatabase: database,
),
alias: null);
late final Shape7 remoteExifEntity = Shape7(
late final Shape8 remoteExifEntity = Shape8(
source: i0.VersionedTable(
entityName: 'remote_exif_entity',
withoutRowId: true,
@@ -183,16 +202,14 @@ final class Schema2 extends i0.VersionedSchema {
'PRIMARY KEY(asset_id)',
],
columns: [
_column_34,
_column_35,
_column_36,
_column_37,
_column_38,
_column_39,
_column_11,
_column_10,
_column_40,
_column_41,
_column_11,
_column_10,
_column_42,
_column_43,
_column_44,
@@ -205,11 +222,13 @@ final class Schema2 extends i0.VersionedSchema {
_column_51,
_column_52,
_column_53,
_column_54,
_column_55,
],
attachedDatabase: database,
),
alias: null);
late final Shape8 remoteAlbumEntity = Shape8(
late final Shape9 remoteAlbumEntity = Shape9(
source: i0.VersionedTable(
entityName: 'remote_album_entity',
withoutRowId: true,
@@ -220,18 +239,18 @@ final class Schema2 extends i0.VersionedSchema {
columns: [
_column_0,
_column_1,
_column_54,
_column_56,
_column_9,
_column_5,
_column_15,
_column_55,
_column_56,
_column_57,
_column_58,
_column_59,
],
attachedDatabase: database,
),
alias: null);
late final Shape6 remoteAlbumAssetEntity = Shape6(
late final Shape7 remoteAlbumAssetEntity = Shape7(
source: i0.VersionedTable(
entityName: 'remote_album_asset_entity',
withoutRowId: true,
@@ -240,13 +259,13 @@ final class Schema2 extends i0.VersionedSchema {
'PRIMARY KEY(asset_id, album_id)',
],
columns: [
_column_34,
_column_58,
_column_36,
_column_60,
],
attachedDatabase: database,
),
alias: null);
late final Shape9 remoteAlbumUserEntity = Shape9(
late final Shape10 remoteAlbumUserEntity = Shape10(
source: i0.VersionedTable(
entityName: 'remote_album_user_entity',
withoutRowId: true,
@@ -255,14 +274,14 @@ final class Schema2 extends i0.VersionedSchema {
'PRIMARY KEY(album_id, user_id)',
],
columns: [
_column_58,
_column_23,
_column_59,
_column_60,
_column_25,
_column_61,
],
attachedDatabase: database,
),
alias: null);
late final Shape10 memoryEntity = Shape10(
late final Shape11 memoryEntity = Shape11(
source: i0.VersionedTable(
entityName: 'memory_entity',
withoutRowId: true,
@@ -277,17 +296,17 @@ final class Schema2 extends i0.VersionedSchema {
_column_18,
_column_15,
_column_8,
_column_60,
_column_61,
_column_62,
_column_63,
_column_64,
_column_65,
_column_66,
_column_67,
],
attachedDatabase: database,
),
alias: null);
late final Shape11 memoryAssetEntity = Shape11(
late final Shape12 memoryAssetEntity = Shape12(
source: i0.VersionedTable(
entityName: 'memory_asset_entity',
withoutRowId: true,
@@ -296,26 +315,8 @@ final class Schema2 extends i0.VersionedSchema {
'PRIMARY KEY(asset_id, memory_id)',
],
columns: [
_column_34,
_column_66,
],
attachedDatabase: database,
),
alias: null);
late final Shape12 stackEntity = Shape12(
source: i0.VersionedTable(
entityName: 'stack_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: [
'PRIMARY KEY(id)',
],
columns: [
_column_0,
_column_9,
_column_5,
_column_15,
_column_67,
_column_36,
_column_68,
],
attachedDatabase: database,
),
@@ -405,6 +406,8 @@ class Shape1 extends i0.VersionedTable {
columnsByName['live_photo_video_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get visibility =>
columnsByName['visibility']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get stackId =>
columnsByName['stack_id']! as i1.GeneratedColumn<String>;
}
i1.GeneratedColumn<int> _column_8(String aliasedName) =>
@@ -452,6 +455,9 @@ i1.GeneratedColumn<String> _column_19(String aliasedName) =>
i1.GeneratedColumn<int> _column_20(String aliasedName) =>
i1.GeneratedColumn<int>('visibility', aliasedName, false,
type: i1.DriftSqlType.int);
i1.GeneratedColumn<String> _column_21(String aliasedName) =>
i1.GeneratedColumn<String>('stack_id', aliasedName, true,
type: i1.DriftSqlType.string);
class Shape2 extends i0.VersionedTable {
Shape2({required super.source, required super.alias}) : super.aliased();
@@ -479,15 +485,35 @@ class Shape2 extends i0.VersionedTable {
columnsByName['orientation']! as i1.GeneratedColumn<int>;
}
i1.GeneratedColumn<String> _column_21(String aliasedName) =>
i1.GeneratedColumn<String> _column_22(String aliasedName) =>
i1.GeneratedColumn<String>('checksum', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<int> _column_22(String aliasedName) =>
i1.GeneratedColumn<int> _column_23(String aliasedName) =>
i1.GeneratedColumn<int>('orientation', aliasedName, false,
type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0'));
class Shape3 extends i0.VersionedTable {
Shape3({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get id =>
columnsByName['id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get updatedAt =>
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<String> get ownerId =>
columnsByName['owner_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get primaryAssetId =>
columnsByName['primary_asset_id']! as i1.GeneratedColumn<String>;
}
i1.GeneratedColumn<String> _column_24(String aliasedName) =>
i1.GeneratedColumn<String>('primary_asset_id', aliasedName, false,
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id)'));
class Shape4 extends i0.VersionedTable {
Shape4({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get userId =>
columnsByName['user_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get key =>
@@ -496,20 +522,20 @@ class Shape3 extends i0.VersionedTable {
columnsByName['value']! as i1.GeneratedColumn<i2.Uint8List>;
}
i1.GeneratedColumn<String> _column_23(String aliasedName) =>
i1.GeneratedColumn<String> _column_25(String aliasedName) =>
i1.GeneratedColumn<String>('user_id', aliasedName, false,
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE'));
i1.GeneratedColumn<int> _column_24(String aliasedName) =>
i1.GeneratedColumn<int> _column_26(String aliasedName) =>
i1.GeneratedColumn<int>('key', aliasedName, false,
type: i1.DriftSqlType.int);
i1.GeneratedColumn<i2.Uint8List> _column_25(String aliasedName) =>
i1.GeneratedColumn<i2.Uint8List> _column_27(String aliasedName) =>
i1.GeneratedColumn<i2.Uint8List>('value', aliasedName, false,
type: i1.DriftSqlType.blob);
class Shape4 extends i0.VersionedTable {
Shape4({required super.source, required super.alias}) : super.aliased();
class Shape5 extends i0.VersionedTable {
Shape5({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get sharedById =>
columnsByName['shared_by_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get sharedWithId =>
@@ -518,25 +544,25 @@ class Shape4 extends i0.VersionedTable {
columnsByName['in_timeline']! as i1.GeneratedColumn<bool>;
}
i1.GeneratedColumn<String> _column_26(String aliasedName) =>
i1.GeneratedColumn<String> _column_28(String aliasedName) =>
i1.GeneratedColumn<String>('shared_by_id', aliasedName, false,
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE'));
i1.GeneratedColumn<String> _column_27(String aliasedName) =>
i1.GeneratedColumn<String> _column_29(String aliasedName) =>
i1.GeneratedColumn<String>('shared_with_id', aliasedName, false,
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE'));
i1.GeneratedColumn<bool> _column_28(String aliasedName) =>
i1.GeneratedColumn<bool> _column_30(String aliasedName) =>
i1.GeneratedColumn<bool>('in_timeline', aliasedName, false,
type: i1.DriftSqlType.bool,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'CHECK ("in_timeline" IN (0, 1))'),
defaultValue: const CustomExpression('0'));
class Shape5 extends i0.VersionedTable {
Shape5({required super.source, required super.alias}) : super.aliased();
class Shape6 extends i0.VersionedTable {
Shape6({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get id =>
columnsByName['id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get name =>
@@ -551,42 +577,42 @@ class Shape5 extends i0.VersionedTable {
columnsByName['marker']! as i1.GeneratedColumn<bool>;
}
i1.GeneratedColumn<int> _column_29(String aliasedName) =>
i1.GeneratedColumn<int> _column_31(String aliasedName) =>
i1.GeneratedColumn<int>('backup_selection', aliasedName, false,
type: i1.DriftSqlType.int);
i1.GeneratedColumn<bool> _column_30(String aliasedName) =>
i1.GeneratedColumn<bool> _column_32(String aliasedName) =>
i1.GeneratedColumn<bool>('is_ios_shared_album', aliasedName, false,
type: i1.DriftSqlType.bool,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'CHECK ("is_ios_shared_album" IN (0, 1))'),
defaultValue: const CustomExpression('0'));
i1.GeneratedColumn<bool> _column_31(String aliasedName) =>
i1.GeneratedColumn<bool> _column_33(String aliasedName) =>
i1.GeneratedColumn<bool>('marker', aliasedName, true,
type: i1.DriftSqlType.bool,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'CHECK ("marker" IN (0, 1))'));
class Shape6 extends i0.VersionedTable {
Shape6({required super.source, required super.alias}) : super.aliased();
class Shape7 extends i0.VersionedTable {
Shape7({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get assetId =>
columnsByName['asset_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get albumId =>
columnsByName['album_id']! as i1.GeneratedColumn<String>;
}
i1.GeneratedColumn<String> _column_32(String aliasedName) =>
i1.GeneratedColumn<String> _column_34(String aliasedName) =>
i1.GeneratedColumn<String>('asset_id', aliasedName, false,
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES local_asset_entity (id) ON DELETE CASCADE'));
i1.GeneratedColumn<String> _column_33(String aliasedName) =>
i1.GeneratedColumn<String> _column_35(String aliasedName) =>
i1.GeneratedColumn<String>('album_id', aliasedName, false,
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES local_album_entity (id) ON DELETE CASCADE'));
class Shape7 extends i0.VersionedTable {
Shape7({required super.source, required super.alias}) : super.aliased();
class Shape8 extends i0.VersionedTable {
Shape8({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get assetId =>
columnsByName['asset_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get city =>
@@ -633,71 +659,71 @@ class Shape7 extends i0.VersionedTable {
columnsByName['projection_type']! as i1.GeneratedColumn<String>;
}
i1.GeneratedColumn<String> _column_34(String aliasedName) =>
i1.GeneratedColumn<String> _column_36(String aliasedName) =>
i1.GeneratedColumn<String>('asset_id', aliasedName, false,
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id) ON DELETE CASCADE'));
i1.GeneratedColumn<String> _column_35(String aliasedName) =>
i1.GeneratedColumn<String> _column_37(String aliasedName) =>
i1.GeneratedColumn<String>('city', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<String> _column_36(String aliasedName) =>
i1.GeneratedColumn<String> _column_38(String aliasedName) =>
i1.GeneratedColumn<String>('state', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<String> _column_37(String aliasedName) =>
i1.GeneratedColumn<String> _column_39(String aliasedName) =>
i1.GeneratedColumn<String>('country', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<DateTime> _column_38(String aliasedName) =>
i1.GeneratedColumn<DateTime> _column_40(String aliasedName) =>
i1.GeneratedColumn<DateTime>('date_time_original', aliasedName, true,
type: i1.DriftSqlType.dateTime);
i1.GeneratedColumn<String> _column_39(String aliasedName) =>
i1.GeneratedColumn<String> _column_41(String aliasedName) =>
i1.GeneratedColumn<String>('description', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<String> _column_40(String aliasedName) =>
i1.GeneratedColumn<String> _column_42(String aliasedName) =>
i1.GeneratedColumn<String>('exposure_time', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<double> _column_41(String aliasedName) =>
i1.GeneratedColumn<double> _column_43(String aliasedName) =>
i1.GeneratedColumn<double>('f_number', aliasedName, true,
type: i1.DriftSqlType.double);
i1.GeneratedColumn<int> _column_42(String aliasedName) =>
i1.GeneratedColumn<int> _column_44(String aliasedName) =>
i1.GeneratedColumn<int>('file_size', aliasedName, true,
type: i1.DriftSqlType.int);
i1.GeneratedColumn<double> _column_43(String aliasedName) =>
i1.GeneratedColumn<double> _column_45(String aliasedName) =>
i1.GeneratedColumn<double>('focal_length', aliasedName, true,
type: i1.DriftSqlType.double);
i1.GeneratedColumn<double> _column_44(String aliasedName) =>
i1.GeneratedColumn<double> _column_46(String aliasedName) =>
i1.GeneratedColumn<double>('latitude', aliasedName, true,
type: i1.DriftSqlType.double);
i1.GeneratedColumn<double> _column_45(String aliasedName) =>
i1.GeneratedColumn<double> _column_47(String aliasedName) =>
i1.GeneratedColumn<double>('longitude', aliasedName, true,
type: i1.DriftSqlType.double);
i1.GeneratedColumn<int> _column_46(String aliasedName) =>
i1.GeneratedColumn<int> _column_48(String aliasedName) =>
i1.GeneratedColumn<int>('iso', aliasedName, true,
type: i1.DriftSqlType.int);
i1.GeneratedColumn<String> _column_47(String aliasedName) =>
i1.GeneratedColumn<String> _column_49(String aliasedName) =>
i1.GeneratedColumn<String>('make', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<String> _column_48(String aliasedName) =>
i1.GeneratedColumn<String> _column_50(String aliasedName) =>
i1.GeneratedColumn<String>('model', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<String> _column_49(String aliasedName) =>
i1.GeneratedColumn<String> _column_51(String aliasedName) =>
i1.GeneratedColumn<String>('lens', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<String> _column_50(String aliasedName) =>
i1.GeneratedColumn<String> _column_52(String aliasedName) =>
i1.GeneratedColumn<String>('orientation', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<String> _column_51(String aliasedName) =>
i1.GeneratedColumn<String> _column_53(String aliasedName) =>
i1.GeneratedColumn<String>('time_zone', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<int> _column_52(String aliasedName) =>
i1.GeneratedColumn<int> _column_54(String aliasedName) =>
i1.GeneratedColumn<int>('rating', aliasedName, true,
type: i1.DriftSqlType.int);
i1.GeneratedColumn<String> _column_53(String aliasedName) =>
i1.GeneratedColumn<String> _column_55(String aliasedName) =>
i1.GeneratedColumn<String>('projection_type', aliasedName, true,
type: i1.DriftSqlType.string);
class Shape8 extends i0.VersionedTable {
Shape8({required super.source, required super.alias}) : super.aliased();
class Shape9 extends i0.VersionedTable {
Shape9({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get id =>
columnsByName['id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get name =>
@@ -718,32 +744,32 @@ class Shape8 extends i0.VersionedTable {
columnsByName['order']! as i1.GeneratedColumn<int>;
}
i1.GeneratedColumn<String> _column_54(String aliasedName) =>
i1.GeneratedColumn<String> _column_56(String aliasedName) =>
i1.GeneratedColumn<String>('description', aliasedName, false,
type: i1.DriftSqlType.string,
defaultValue: const CustomExpression('\'\''));
i1.GeneratedColumn<String> _column_55(String aliasedName) =>
i1.GeneratedColumn<String> _column_57(String aliasedName) =>
i1.GeneratedColumn<String>('thumbnail_asset_id', aliasedName, true,
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id) ON DELETE SET NULL'));
i1.GeneratedColumn<bool> _column_56(String aliasedName) =>
i1.GeneratedColumn<bool> _column_58(String aliasedName) =>
i1.GeneratedColumn<bool>('is_activity_enabled', aliasedName, false,
type: i1.DriftSqlType.bool,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'CHECK ("is_activity_enabled" IN (0, 1))'),
defaultValue: const CustomExpression('1'));
i1.GeneratedColumn<int> _column_57(String aliasedName) =>
i1.GeneratedColumn<int> _column_59(String aliasedName) =>
i1.GeneratedColumn<int>('order', aliasedName, false,
type: i1.DriftSqlType.int);
i1.GeneratedColumn<String> _column_58(String aliasedName) =>
i1.GeneratedColumn<String> _column_60(String aliasedName) =>
i1.GeneratedColumn<String>('album_id', aliasedName, false,
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_album_entity (id) ON DELETE CASCADE'));
class Shape9 extends i0.VersionedTable {
Shape9({required super.source, required super.alias}) : super.aliased();
class Shape10 extends i0.VersionedTable {
Shape10({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get albumId =>
columnsByName['album_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get userId =>
@@ -752,12 +778,12 @@ class Shape9 extends i0.VersionedTable {
columnsByName['role']! as i1.GeneratedColumn<int>;
}
i1.GeneratedColumn<int> _column_59(String aliasedName) =>
i1.GeneratedColumn<int> _column_61(String aliasedName) =>
i1.GeneratedColumn<int>('role', aliasedName, false,
type: i1.DriftSqlType.int);
class Shape10 extends i0.VersionedTable {
Shape10({required super.source, required super.alias}) : super.aliased();
class Shape11 extends i0.VersionedTable {
Shape11({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get id =>
columnsByName['id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<DateTime> get createdAt =>
@@ -784,61 +810,41 @@ class Shape10 extends i0.VersionedTable {
columnsByName['hide_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<String> _column_60(String aliasedName) =>
i1.GeneratedColumn<String> _column_62(String aliasedName) =>
i1.GeneratedColumn<String>('data', aliasedName, false,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<bool> _column_61(String aliasedName) =>
i1.GeneratedColumn<bool> _column_63(String aliasedName) =>
i1.GeneratedColumn<bool>('is_saved', aliasedName, false,
type: i1.DriftSqlType.bool,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'CHECK ("is_saved" IN (0, 1))'),
defaultValue: const CustomExpression('0'));
i1.GeneratedColumn<DateTime> _column_62(String aliasedName) =>
i1.GeneratedColumn<DateTime> _column_64(String aliasedName) =>
i1.GeneratedColumn<DateTime>('memory_at', aliasedName, false,
type: i1.DriftSqlType.dateTime);
i1.GeneratedColumn<DateTime> _column_63(String aliasedName) =>
i1.GeneratedColumn<DateTime> _column_65(String aliasedName) =>
i1.GeneratedColumn<DateTime>('seen_at', aliasedName, true,
type: i1.DriftSqlType.dateTime);
i1.GeneratedColumn<DateTime> _column_64(String aliasedName) =>
i1.GeneratedColumn<DateTime> _column_66(String aliasedName) =>
i1.GeneratedColumn<DateTime>('show_at', aliasedName, true,
type: i1.DriftSqlType.dateTime);
i1.GeneratedColumn<DateTime> _column_65(String aliasedName) =>
i1.GeneratedColumn<DateTime> _column_67(String aliasedName) =>
i1.GeneratedColumn<DateTime>('hide_at', aliasedName, true,
type: i1.DriftSqlType.dateTime);
class Shape11 extends i0.VersionedTable {
Shape11({required super.source, required super.alias}) : super.aliased();
class Shape12 extends i0.VersionedTable {
Shape12({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get assetId =>
columnsByName['asset_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get memoryId =>
columnsByName['memory_id']! as i1.GeneratedColumn<String>;
}
i1.GeneratedColumn<String> _column_66(String aliasedName) =>
i1.GeneratedColumn<String> _column_68(String aliasedName) =>
i1.GeneratedColumn<String>('memory_id', aliasedName, false,
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES memory_entity (id) ON DELETE CASCADE'));
class Shape12 extends i0.VersionedTable {
Shape12({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get id =>
columnsByName['id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get updatedAt =>
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<String> get ownerId =>
columnsByName['owner_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get primaryAssetId =>
columnsByName['primary_asset_id']! as i1.GeneratedColumn<String>;
}
i1.GeneratedColumn<String> _column_67(String aliasedName) =>
i1.GeneratedColumn<String>('primary_asset_id', aliasedName, false,
type: i1.DriftSqlType.string,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id)'));
i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
}) {
@@ -1,11 +1,13 @@
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';
@@ -30,25 +32,66 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
}
Stream<RemoteAsset?> watchAsset(String id) {
final query = _db.remoteAssetEntity
.select()
.addColumns([_db.localAssetEntity.id]).join([
final stackCountRef = _db.stackEntity.id.count();
final query = _db.remoteAssetEntity.select().addColumns([
_db.localAssetEntity.id,
_db.stackEntity.primaryAssetId,
stackCountRef,
]).join([
leftOuterJoin(
_db.localAssetEntity,
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
useColumns: false,
),
leftOuterJoin(
_db.stackEntity,
_db.stackEntity.primaryAssetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false,
),
leftOuterJoin(
_db.remoteAssetEntity.createAlias('stacked_assets'),
_db.stackEntity.id.equalsExp(
_db.remoteAssetEntity.createAlias('stacked_assets').stackId,
),
useColumns: false,
),
])
..where(_db.remoteAssetEntity.id.equals(id));
..where(_db.remoteAssetEntity.id.equals(id))
..groupBy([
_db.remoteAssetEntity.id,
_db.localAssetEntity.id,
_db.stackEntity.primaryAssetId,
]);
return query.map((row) {
final asset = row.readTable(_db.remoteAssetEntity).toDto();
final primaryAssetId = row.read(_db.stackEntity.primaryAssetId);
final stackCount =
primaryAssetId == id ? (row.read(stackCountRef) ?? 0) : 0;
return asset.copyWith(
localId: row.read(_db.localAssetEntity.id),
stackCount: stackCount,
);
}).watchSingleOrNull();
}
Future<List<RemoteAsset>> getStackChildren(RemoteAsset asset) {
if (asset.stackId == null) {
return Future.value([]);
}
final query = _db.remoteAssetEntity.select()
..where(
(row) =>
row.stackId.equals(asset.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))
@@ -146,4 +189,53 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
}
});
}
Future<void> stack(String userId, StackResponse stack) {
return _db.transaction(() async {
final stackIds = await _db.managers.stackEntity
.filter((row) => row.primaryAssetId.id.isIn(stack.assetIds))
.map((row) => row.id)
.get();
await _db.stackEntity.deleteWhere((row) => row.id.isIn(stackIds));
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.stackEntity.deleteWhere((row) => row.id.isIn(stackIds));
// TODO: delete this after adding foreign key on stackId
await _db.batch((batch) {
batch.update(
_db.remoteAssetEntity,
const RemoteAssetEntityCompanion(stackId: Value(null)),
where: (e) => e.stackId.isIn(stackIds),
);
});
});
}
}
@@ -137,6 +137,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
deletedAt: Value(asset.deletedAt),
visibility: Value(asset.visibility.toAssetVisibility()),
livePhotoVideoId: Value(asset.livePhotoVideoId),
stackId: Value(asset.stackId),
);
batch.insert(
@@ -89,6 +89,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
isFavorite: row.isFavorite,
durationInSeconds: row.durationInSeconds,
livePhotoVideoId: row.livePhotoVideoId,
stackId: row.stackId,
stackCount: row.stackCount,
)
: LocalAsset(
id: row.localId!,
@@ -13,7 +13,7 @@ class MainTimelinePage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final memoryLaneProvider = ref.watch(driftMemoryFutureProvider);
return memoryLaneProvider.when(
return memoryLaneProvider.maybeWhen(
data: (memories) {
return memories.isEmpty
? const Timeline(showStorageIndicator: true)
@@ -26,8 +26,7 @@ class MainTimelinePage extends ConsumerWidget {
showStorageIndicator: true,
);
},
loading: () => const Timeline(showStorageIndicator: true),
error: (error, stackTrace) => const Timeline(showStorageIndicator: true),
orElse: () => const Timeline(showStorageIndicator: true),
);
}
}
@@ -1,16 +1,56 @@
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart';
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
class StackActionButton extends ConsumerWidget {
const StackActionButton({super.key});
final ActionSource source;
const StackActionButton({super.key, required this.source});
void _onTap(BuildContext context, WidgetRef ref) async {
if (!context.mounted) {
return;
}
final user = ref.watch(currentUserProvider);
if (user == null) {
throw Exception('User must be logged in to access stack action');
}
final result =
await ref.read(actionProvider.notifier).stack(user.id, source);
ref.read(multiSelectProvider.notifier).reset();
final successMessage = 'stack_action_prompt'.t(
context: context,
args: {'count': result.count.toString()},
);
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);
}
}
@override
Widget build(BuildContext context, WidgetRef ref) {
return BaseActionButton(
iconData: Icons.filter_none_rounded,
label: "stack".t(context: context),
onPressed: () => _onTap(context, ref),
);
}
}
@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart';
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
class UnStackActionButton extends ConsumerWidget {
final ActionSource source;
const UnStackActionButton({super.key, required this.source});
void _onTap(BuildContext context, WidgetRef ref) async {
if (!context.mounted) {
return;
}
final result = await ref.read(actionProvider.notifier).unStack(source);
ref.read(multiSelectProvider.notifier).reset();
final successMessage = 'unstack_action_prompt'.t(
context: context,
args: {'count': result.count.toString()},
);
if (context.mounted) {
ImmichToast.show(
context: context,
msg: result.success
? successMessage
: 'scaffold_body_error_occurred'.t(context: context),
gravity: ToastGravity.BOTTOM,
toastType: result.success ? ToastType.success : ToastType.error,
);
}
}
@override
Widget build(BuildContext context, WidgetRef ref) {
return BaseActionButton(
iconData: Icons.filter_none_rounded,
label: "unstack".t(context: context),
onPressed: () => _onTap(context, ref),
);
}
}
@@ -0,0 +1,24 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
class StackChildrenNotifier
extends AutoDisposeFamilyAsyncNotifier<List<RemoteAsset>, BaseAsset?> {
@override
Future<List<RemoteAsset>> build(BaseAsset? asset) async {
if (asset == null ||
asset is! RemoteAsset ||
asset.stackId == null ||
// The stackCount check is to ensure we only fetch stacks for timelines that have stacks
asset.stackCount == 0) {
return const [];
}
return ref.watch(assetServiceProvider).getStack(asset);
}
}
final stackChildrenNotifier = AsyncNotifierProvider.autoDispose
.family<StackChildrenNotifier, List<RemoteAsset>, BaseAsset?>(
StackChildrenNotifier.new,
);
@@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
class AssetStackRow extends ConsumerWidget {
const AssetStackRow({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
int opacity = ref.watch(
assetViewerProvider.select((state) => state.backgroundOpacity),
);
final showControls =
ref.watch(assetViewerProvider.select((s) => s.showingControls));
if (!showControls) {
opacity = 0;
}
final asset = ref.watch(assetViewerProvider.select((s) => s.currentAsset));
return IgnorePointer(
ignoring: opacity < 255,
child: AnimatedOpacity(
opacity: opacity / 255,
duration: Durations.short2,
child: ref.watch(stackChildrenNotifier(asset)).when(
data: (state) => SizedBox.square(
dimension: 80,
child: _StackList(stack: state),
),
error: (_, __) => const SizedBox.shrink(),
loading: () => const SizedBox.shrink(),
),
),
);
}
}
class _StackList extends ConsumerWidget {
final List<RemoteAsset> stack;
const _StackList({required this.stack});
@override
Widget build(BuildContext context, WidgetRef ref) {
return ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.only(
left: 5,
right: 5,
bottom: 30,
),
itemCount: stack.length,
itemBuilder: (ctx, index) {
final asset = stack[index];
return Padding(
padding: const EdgeInsets.only(right: 5),
child: GestureDetector(
onTap: () {
ref.read(assetViewerProvider.notifier).setStackIndex(index);
ref.read(currentAssetNotifier.notifier).setAsset(asset);
},
child: Container(
height: 60,
width: 60,
decoration: index ==
ref.watch(assetViewerProvider.select((s) => s.stackIndex))
? const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(6)),
border: Border.fromBorderSide(
BorderSide(color: Colors.white, width: 2),
),
)
: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(6)),
border: null,
),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(4)),
child: Stack(
fit: StackFit.expand,
children: [
Image(
fit: BoxFit.cover,
image: getThumbnailImageProvider(
remoteId: asset.id,
size: const Size.square(60),
),
),
if (asset.isVideo)
const Icon(
Icons.play_circle_outline_rounded,
color: Colors.white,
size: 16,
shadows: [
Shadow(
blurRadius: 5.0,
color: Color.fromRGBO(0, 0, 0, 0.6),
offset: Offset(0.0, 0.0),
),
],
),
],
),
),
),
),
);
},
);
}
}
@@ -5,10 +5,13 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/domain/utils/event_stream.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/scroll_extensions.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.widget.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_bar.widget.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet.widget.dart';
@@ -85,6 +88,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
double previousExtent = _kBottomSheetMinimumExtent;
Offset dragDownPosition = Offset.zero;
int totalAssets = 0;
int stackIndex = 0;
BuildContext? scaffoldContext;
Map<String, GlobalKey> videoPlayerKeys = {};
@@ -167,6 +171,10 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
void _onAssetChanged(int index) {
final asset = ref.read(timelineServiceProvider).getAsset(index);
// Always holds the current asset from the timeline
ref.read(assetViewerProvider.notifier).setAsset(asset);
// The currentAssetNotifier actually holds the current asset that is displayed
// which could be stack children as well
ref.read(currentAssetNotifier.notifier).setAsset(asset);
if (asset.isVideo || asset.isMotionPhoto) {
ref.read(videoPlaybackValueProvider.notifier).reset();
@@ -488,7 +496,12 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
ImageChunkEvent? progress,
int index,
) {
final asset = ref.read(timelineServiceProvider).getAsset(index);
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
if (stackChildren != null && stackChildren.isNotEmpty) {
asset = stackChildren
.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
}
return Container(
width: double.infinity,
height: double.infinity,
@@ -516,9 +529,14 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
PhotoViewGalleryPageOptions _assetBuilder(BuildContext ctx, int index) {
scaffoldContext ??= ctx;
final asset = ref.read(timelineServiceProvider).getAsset(index);
final isPlayingMotionVideo = ref.read(isPlayingMotionVideoProvider);
BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index);
final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull;
if (stackChildren != null && stackChildren.isNotEmpty) {
asset = stackChildren
.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex)));
}
final isPlayingMotionVideo = ref.read(isPlayingMotionVideoProvider);
if (asset.isImage && !isPlayingMotionVideo) {
return _imageBuilder(ctx, asset);
}
@@ -604,6 +622,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
// Using multiple selectors to avoid unnecessary rebuilds for other state changes
ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet));
ref.watch(assetViewerProvider.select((s) => s.backgroundOpacity));
ref.watch(assetViewerProvider.select((s) => s.stackIndex));
ref.watch(isPlayingMotionVideoProvider);
// Listen for casting changes and send initial asset to the cast provider
@@ -645,7 +664,17 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
backgroundDecoration: BoxDecoration(color: backgroundColor),
enablePanAlways: true,
),
bottomNavigationBar: const ViewerBottomBar(),
bottomNavigationBar: showingBottomSheet
? const SizedBox.shrink()
: const Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AssetStackRow(),
ViewerBottomBar(),
],
),
),
);
}
@@ -1,26 +1,40 @@
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
class ViewerOpenBottomSheetEvent extends Event {
const ViewerOpenBottomSheetEvent();
}
class AssetViewerState {
final int backgroundOpacity;
final bool showingBottomSheet;
final bool showingControls;
final BaseAsset? currentAsset;
final int stackIndex;
const AssetViewerState({
this.backgroundOpacity = 255,
this.showingBottomSheet = false,
this.showingControls = true,
this.currentAsset,
this.stackIndex = 0,
});
AssetViewerState copyWith({
int? backgroundOpacity,
bool? showingBottomSheet,
bool? showingControls,
BaseAsset? currentAsset,
int? stackIndex,
}) {
return AssetViewerState(
backgroundOpacity: backgroundOpacity ?? this.backgroundOpacity,
showingBottomSheet: showingBottomSheet ?? this.showingBottomSheet,
showingControls: showingControls ?? this.showingControls,
currentAsset: currentAsset ?? this.currentAsset,
stackIndex: stackIndex ?? this.stackIndex,
);
}
@@ -36,14 +50,18 @@ class AssetViewerState {
return other is AssetViewerState &&
other.backgroundOpacity == backgroundOpacity &&
other.showingBottomSheet == showingBottomSheet &&
other.showingControls == showingControls;
other.showingControls == showingControls &&
other.currentAsset == currentAsset &&
other.stackIndex == stackIndex;
}
@override
int get hashCode =>
backgroundOpacity.hashCode ^
showingBottomSheet.hashCode ^
showingControls.hashCode;
showingControls.hashCode ^
currentAsset.hashCode ^
stackIndex.hashCode;
}
class AssetViewerStateNotifier extends AutoDisposeNotifier<AssetViewerState> {
@@ -52,6 +70,10 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier<AssetViewerState> {
return const AssetViewerState();
}
void setAsset(BaseAsset? asset) {
state = state.copyWith(currentAsset: asset, stackIndex: 0);
}
void setOpacity(int opacity) {
state = state.copyWith(
backgroundOpacity: opacity,
@@ -76,6 +98,10 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier<AssetViewerState> {
void toggleControls() {
state = state.copyWith(showingControls: !state.showingControls);
}
void setStackIndex(int index) {
state = state.copyWith(stackIndex: index);
}
}
final assetViewerProvider =
@@ -49,7 +49,7 @@ class ArchiveBottomSheet extends ConsumerWidget {
const MoveToLockFolderActionButton(
source: ActionSource.timeline,
),
const StackActionButton(),
const StackActionButton(source: ActionSource.timeline),
],
if (multiselect.hasLocal) ...[
const DeleteLocalActionButton(source: ActionSource.timeline),
@@ -49,7 +49,7 @@ class FavoriteBottomSheet extends ConsumerWidget {
const MoveToLockFolderActionButton(
source: ActionSource.timeline,
),
const StackActionButton(),
const StackActionButton(source: ActionSource.timeline),
],
if (multiselect.hasLocal) ...[
const DeleteLocalActionButton(source: ActionSource.timeline),
@@ -49,7 +49,7 @@ class GeneralBottomSheet extends ConsumerWidget {
const MoveToLockFolderActionButton(
source: ActionSource.timeline,
),
const StackActionButton(),
const StackActionButton(source: ActionSource.timeline),
],
if (multiselect.hasLocal) ...[
const DeleteLocalActionButton(source: ActionSource.timeline),
@@ -52,7 +52,7 @@ class RemoteAlbumBottomSheet extends ConsumerWidget {
const MoveToLockFolderActionButton(
source: ActionSource.timeline,
),
const StackActionButton(),
const StackActionButton(source: ActionSource.timeline),
],
if (multiselect.hasLocal) ...[
const DeleteLocalActionButton(source: ActionSource.timeline),
@@ -53,6 +53,9 @@ class ThumbnailTile extends ConsumerWidget {
)
: const BoxDecoration();
final hasStack =
asset is RemoteAsset && (asset as RemoteAsset).stackCount > 0;
return Stack(
children: [
AnimatedContainer(
@@ -75,6 +78,19 @@ class ThumbnailTile extends ConsumerWidget {
),
),
),
if (hasStack)
Align(
alignment: Alignment.topRight,
child: Padding(
padding: EdgeInsets.only(
right: 10.0,
top: asset.isVideo ? 24.0 : 6.0,
),
child: _StackIndicator(
stackCount: (asset as RemoteAsset).stackCount,
),
),
),
if (asset.isVideo)
Align(
alignment: Alignment.topRight,
@@ -182,6 +198,40 @@ class _SelectionIndicator extends StatelessWidget {
}
}
class _StackIndicator extends StatelessWidget {
final int stackCount;
const _StackIndicator({required this.stackCount});
@override
Widget build(BuildContext context) {
return Row(
spacing: 3,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
// CrossAxisAlignment.start looks more centered vertically than CrossAxisAlignment.center
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
stackCount.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
shadows: [
Shadow(
blurRadius: 5.0,
color: Color.fromRGBO(0, 0, 0, 0.6),
),
],
),
),
const _TileOverlayIcon(Icons.burst_mode_rounded),
],
);
}
}
class _VideoIndicator extends StatelessWidget {
final Duration duration;
const _VideoIndicator(this.duration);
@@ -192,8 +242,8 @@ class _VideoIndicator extends StatelessWidget {
spacing: 3,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
// CrossAxisAlignment.end looks more centered vertically than CrossAxisAlignment.center
crossAxisAlignment: CrossAxisAlignment.end,
// CrossAxisAlignment.start looks more centered vertically than CrossAxisAlignment.center
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
duration.format(),
@@ -15,6 +15,7 @@ class TimelineArgs {
final double spacing;
final int columnCount;
final bool showStorageIndicator;
final bool withStack;
final GroupAssetsBy? groupBy;
const TimelineArgs({
@@ -23,6 +24,7 @@ class TimelineArgs {
this.spacing = kTimelineSpacing,
this.columnCount = kTimelineColumnCount,
this.showStorageIndicator = false,
this.withStack = false,
this.groupBy,
});
@@ -33,6 +35,7 @@ class TimelineArgs {
maxHeight == other.maxHeight &&
columnCount == other.columnCount &&
showStorageIndicator == other.showStorageIndicator &&
withStack == other.withStack &&
groupBy == other.groupBy;
}
@@ -43,6 +46,7 @@ class TimelineArgs {
spacing.hashCode ^
columnCount.hashCode ^
showStorageIndicator.hashCode ^
withStack.hashCode ^
groupBy.hashCode;
}
@@ -28,6 +28,7 @@ class Timeline extends StatelessWidget {
this.topSliverWidget,
this.topSliverWidgetHeight,
this.showStorageIndicator = false,
this.withStack = false,
this.appBar = const ImmichSliverAppBar(
floating: true,
pinned: false,
@@ -42,6 +43,7 @@ class Timeline extends StatelessWidget {
final bool showStorageIndicator;
final Widget? appBar;
final Widget? bottomSheet;
final bool withStack;
final GroupAssetsBy? groupBy;
@override
@@ -58,6 +60,7 @@ class Timeline extends StatelessWidget {
settingsProvider.select((s) => s.get(Setting.tilesPerRow)),
),
showStorageIndicator: showStorageIndicator,
withStack: withStack,
groupBy: groupBy,
),
),
@@ -50,7 +50,7 @@ class ActionNotifier extends Notifier<void> {
return _getIdsForSource<LocalAsset>(source).toIds().toList(growable: false);
}
List<String> _getOwnedRemoteForSource(ActionSource source) {
List<String> _getOwnedRemoteIdsForSource(ActionSource source) {
final ownerId = ref.read(currentUserProvider)?.id;
return _getIdsForSource<RemoteAsset>(source)
.ownedAssets(ownerId)
@@ -58,6 +58,20 @@ class ActionNotifier extends Notifier<void> {
.toList(growable: false);
}
List<RemoteAsset> _getOwnedRemoteAssetsForSource(ActionSource source) {
final ownerId = ref.read(currentUserProvider)?.id;
return _getIdsForSource<RemoteAsset>(source).ownedAssets(ownerId).toList();
}
Iterable<T> _getIdsForSource<T extends BaseAsset>(ActionSource source) {
final Set<BaseAsset> assets = _getAssets(source);
return switch (T) {
const (RemoteAsset) => assets.whereType<RemoteAsset>(),
const (LocalAsset) => assets.whereType<LocalAsset>(),
_ => const [],
} as Iterable<T>;
}
Set<BaseAsset> _getAssets(ActionSource source) {
return switch (source) {
ActionSource.timeline => ref.read(multiSelectProvider).selectedAssets,
@@ -68,15 +82,6 @@ class ActionNotifier extends Notifier<void> {
};
}
Iterable<T> _getIdsForSource<T extends BaseAsset>(ActionSource source) {
final Set<BaseAsset> assets = _getAssets(source);
return switch (T) {
const (RemoteAsset) => assets.whereType<RemoteAsset>(),
const (LocalAsset) => assets.whereType<LocalAsset>(),
_ => const [],
} as Iterable<T>;
}
Future<ActionResult> shareLink(
ActionSource source,
BuildContext context,
@@ -96,7 +101,7 @@ class ActionNotifier extends Notifier<void> {
}
Future<ActionResult> favorite(ActionSource source) async {
final ids = _getOwnedRemoteForSource(source);
final ids = _getOwnedRemoteIdsForSource(source);
try {
await _service.favorite(ids);
return ActionResult(count: ids.length, success: true);
@@ -111,7 +116,7 @@ class ActionNotifier extends Notifier<void> {
}
Future<ActionResult> unFavorite(ActionSource source) async {
final ids = _getOwnedRemoteForSource(source);
final ids = _getOwnedRemoteIdsForSource(source);
try {
await _service.unFavorite(ids);
return ActionResult(count: ids.length, success: true);
@@ -126,7 +131,7 @@ class ActionNotifier extends Notifier<void> {
}
Future<ActionResult> archive(ActionSource source) async {
final ids = _getOwnedRemoteForSource(source);
final ids = _getOwnedRemoteIdsForSource(source);
try {
await _service.archive(ids);
return ActionResult(count: ids.length, success: true);
@@ -141,7 +146,7 @@ class ActionNotifier extends Notifier<void> {
}
Future<ActionResult> unArchive(ActionSource source) async {
final ids = _getOwnedRemoteForSource(source);
final ids = _getOwnedRemoteIdsForSource(source);
try {
await _service.unArchive(ids);
return ActionResult(count: ids.length, success: true);
@@ -156,7 +161,7 @@ class ActionNotifier extends Notifier<void> {
}
Future<ActionResult> moveToLockFolder(ActionSource source) async {
final ids = _getOwnedRemoteForSource(source);
final ids = _getOwnedRemoteIdsForSource(source);
try {
await _service.moveToLockFolder(ids);
return ActionResult(count: ids.length, success: true);
@@ -171,7 +176,7 @@ class ActionNotifier extends Notifier<void> {
}
Future<ActionResult> removeFromLockFolder(ActionSource source) async {
final ids = _getOwnedRemoteForSource(source);
final ids = _getOwnedRemoteIdsForSource(source);
try {
await _service.removeFromLockFolder(ids);
return ActionResult(count: ids.length, success: true);
@@ -186,7 +191,7 @@ class ActionNotifier extends Notifier<void> {
}
Future<ActionResult> trash(ActionSource source) async {
final ids = _getOwnedRemoteForSource(source);
final ids = _getOwnedRemoteIdsForSource(source);
try {
await _service.trash(ids);
return ActionResult(count: ids.length, success: true);
@@ -201,7 +206,7 @@ class ActionNotifier extends Notifier<void> {
}
Future<ActionResult> delete(ActionSource source) async {
final ids = _getOwnedRemoteForSource(source);
final ids = _getOwnedRemoteIdsForSource(source);
try {
await _service.delete(ids);
return ActionResult(count: ids.length, success: true);
@@ -234,7 +239,7 @@ class ActionNotifier extends Notifier<void> {
ActionSource source,
BuildContext context,
) async {
final ids = _getOwnedRemoteForSource(source);
final ids = _getOwnedRemoteIdsForSource(source);
try {
final isEdited = await _service.editLocation(ids, context);
if (!isEdited) {
@@ -270,6 +275,35 @@ class ActionNotifier extends Notifier<void> {
}
}
Future<ActionResult> stack(String userId, ActionSource source) async {
final ids = _getOwnedRemoteIdsForSource(source);
try {
await _service.stack(userId, ids);
return ActionResult(count: ids.length, success: true);
} catch (error, stack) {
_logger.severe('Failed to stack assets', error, stack);
return ActionResult(
count: ids.length,
success: false,
error: error.toString(),
);
}
}
Future<ActionResult> unStack(ActionSource source) async {
final assets = _getOwnedRemoteAssetsForSource(source);
try {
await _service.unStack(assets.map((e) => e.stackId).nonNulls.toList());
return ActionResult(count: assets.length, success: true);
} catch (error, stack) {
_logger.severe('Failed to unstack assets', error, stack);
return ActionResult(
count: assets.length,
success: false,
);
}
}
Future<ActionResult> shareAssets(ActionSource source) async {
final ids = _getAssets(source).toList(growable: false);
@@ -1,6 +1,7 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:http/http.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/stack.model.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/repositories/api.repository.dart';
@@ -11,14 +12,16 @@ final assetApiRepositoryProvider = Provider(
(ref) => AssetApiRepository(
ref.watch(apiServiceProvider).assetsApi,
ref.watch(apiServiceProvider).searchApi,
ref.watch(apiServiceProvider).stacksApi,
),
);
class AssetApiRepository extends ApiRepository {
final AssetsApi _api;
final SearchApi _searchApi;
final StacksApi _stacksApi;
AssetApiRepository(this._api, this._searchApi);
AssetApiRepository(this._api, this._searchApi, this._stacksApi);
Future<Asset> update(String id, {String? description}) async {
final response = await checkNull(
@@ -84,6 +87,17 @@ class AssetApiRepository extends ApiRepository {
);
}
Future<StackResponse> stack(List<String> ids) async {
final responseDto =
await checkNull(_stacksApi.createStack(StackCreateDto(assetIds: ids)));
return responseDto.toStack();
}
Future<void> unStack(List<String> ids) async {
return _stacksApi.deleteStacks(BulkIdsDto(ids: ids));
}
Future<Response> downloadAsset(String id) {
return _api.downloadAssetWithHttpInfo(id);
}
@@ -102,3 +116,13 @@ class AssetApiRepository extends ApiRepository {
return response.originalMimeType;
}
}
extension on StackResponseDto {
StackResponse toStack() {
return StackResponse(
id: id,
primaryAssetId: primaryAssetId,
assetIds: assets.map((asset) => asset.id).toList(),
);
}
}
+10
View File
@@ -166,6 +166,16 @@ class ActionService {
return removedCount;
}
Future<void> stack(String userId, List<String> remoteIds) async {
final stack = await _assetApiRepository.stack(remoteIds);
await _remoteAssetRepository.stack(userId, stack);
}
Future<void> unStack(List<String> stackIds) async {
await _remoteAssetRepository.unStack(stackIds);
await _assetApiRepository.unStack(stackIds);
}
Future<int> shareAssets(List<BaseAsset> assets) {
return _assetMediaRepository.shareAssets(assets);
}
@@ -4,6 +4,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/domain/utils/event_stream.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
@@ -6,6 +6,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/domain/utils/event_stream.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
+8 -2
View File
@@ -63,8 +63,14 @@ class MapThumbnail extends HookConsumerWidget {
}
Future<void> onStyleLoaded() async {
if (showMarkerPin && controller.value != null) {
await controller.value?.addMarkerAtLatLng(centre);
try {
if (showMarkerPin && controller.value != null) {
await controller.value?.addMarkerAtLatLng(centre);
}
} finally {
// Calling methods on the controller after it is disposed will throw an error
// We do not have a way to check if the controller is disposed for now
// https://github.com/maplibre/flutter-maplibre-gl/issues/192
}
styleLoaded.value = true;
}
+327 -263
View File
@@ -410,9 +410,15 @@ class RemoteAssetEntity extends Table
late final GeneratedColumn<DateTime> deletedAt = GeneratedColumn<DateTime>(
'deleted_at', aliasedName, true,
type: DriftSqlType.dateTime, requiredDuringInsert: false);
late final GeneratedColumn<String> livePhotoVideoId = GeneratedColumn<String>(
'live_photo_video_id', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false);
late final GeneratedColumn<int> visibility = GeneratedColumn<int>(
'visibility', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
late final GeneratedColumn<String> stackId = GeneratedColumn<String>(
'stack_id', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false);
@override
List<GeneratedColumn> get $columns => [
name,
@@ -429,7 +435,9 @@ class RemoteAssetEntity extends Table
localDateTime,
thumbHash,
deletedAt,
visibility
livePhotoVideoId,
visibility,
stackId
];
@override
String get aliasedName => _alias ?? actualTableName;
@@ -470,8 +478,12 @@ class RemoteAssetEntity extends Table
.read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']),
deletedAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']),
livePhotoVideoId: attachedDatabase.typeMapping.read(
DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']),
visibility: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}visibility'])!,
stackId: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}stack_id']),
);
}
@@ -502,7 +514,9 @@ class RemoteAssetEntityData extends DataClass
final DateTime? localDateTime;
final String? thumbHash;
final DateTime? deletedAt;
final String? livePhotoVideoId;
final int visibility;
final String? stackId;
const RemoteAssetEntityData(
{required this.name,
required this.type,
@@ -518,7 +532,9 @@ class RemoteAssetEntityData extends DataClass
this.localDateTime,
this.thumbHash,
this.deletedAt,
required this.visibility});
this.livePhotoVideoId,
required this.visibility,
this.stackId});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
@@ -548,7 +564,13 @@ class RemoteAssetEntityData extends DataClass
if (!nullToAbsent || deletedAt != null) {
map['deleted_at'] = Variable<DateTime>(deletedAt);
}
if (!nullToAbsent || livePhotoVideoId != null) {
map['live_photo_video_id'] = Variable<String>(livePhotoVideoId);
}
map['visibility'] = Variable<int>(visibility);
if (!nullToAbsent || stackId != null) {
map['stack_id'] = Variable<String>(stackId);
}
return map;
}
@@ -570,7 +592,9 @@ class RemoteAssetEntityData extends DataClass
localDateTime: serializer.fromJson<DateTime?>(json['localDateTime']),
thumbHash: serializer.fromJson<String?>(json['thumbHash']),
deletedAt: serializer.fromJson<DateTime?>(json['deletedAt']),
livePhotoVideoId: serializer.fromJson<String?>(json['livePhotoVideoId']),
visibility: serializer.fromJson<int>(json['visibility']),
stackId: serializer.fromJson<String?>(json['stackId']),
);
}
@override
@@ -591,7 +615,9 @@ class RemoteAssetEntityData extends DataClass
'localDateTime': serializer.toJson<DateTime?>(localDateTime),
'thumbHash': serializer.toJson<String?>(thumbHash),
'deletedAt': serializer.toJson<DateTime?>(deletedAt),
'livePhotoVideoId': serializer.toJson<String?>(livePhotoVideoId),
'visibility': serializer.toJson<int>(visibility),
'stackId': serializer.toJson<String?>(stackId),
};
}
@@ -610,7 +636,9 @@ class RemoteAssetEntityData extends DataClass
Value<DateTime?> localDateTime = const Value.absent(),
Value<String?> thumbHash = const Value.absent(),
Value<DateTime?> deletedAt = const Value.absent(),
int? visibility}) =>
Value<String?> livePhotoVideoId = const Value.absent(),
int? visibility,
Value<String?> stackId = const Value.absent()}) =>
RemoteAssetEntityData(
name: name ?? this.name,
type: type ?? this.type,
@@ -629,7 +657,11 @@ class RemoteAssetEntityData extends DataClass
localDateTime.present ? localDateTime.value : this.localDateTime,
thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash,
deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt,
livePhotoVideoId: livePhotoVideoId.present
? livePhotoVideoId.value
: this.livePhotoVideoId,
visibility: visibility ?? this.visibility,
stackId: stackId.present ? stackId.value : this.stackId,
);
RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) {
return RemoteAssetEntityData(
@@ -652,8 +684,12 @@ class RemoteAssetEntityData extends DataClass
: this.localDateTime,
thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash,
deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt,
livePhotoVideoId: data.livePhotoVideoId.present
? data.livePhotoVideoId.value
: this.livePhotoVideoId,
visibility:
data.visibility.present ? data.visibility.value : this.visibility,
stackId: data.stackId.present ? data.stackId.value : this.stackId,
);
}
@@ -674,7 +710,9 @@ class RemoteAssetEntityData extends DataClass
..write('localDateTime: $localDateTime, ')
..write('thumbHash: $thumbHash, ')
..write('deletedAt: $deletedAt, ')
..write('visibility: $visibility')
..write('livePhotoVideoId: $livePhotoVideoId, ')
..write('visibility: $visibility, ')
..write('stackId: $stackId')
..write(')'))
.toString();
}
@@ -695,7 +733,9 @@ class RemoteAssetEntityData extends DataClass
localDateTime,
thumbHash,
deletedAt,
visibility);
livePhotoVideoId,
visibility,
stackId);
@override
bool operator ==(Object other) =>
identical(this, other) ||
@@ -714,7 +754,9 @@ class RemoteAssetEntityData extends DataClass
other.localDateTime == this.localDateTime &&
other.thumbHash == this.thumbHash &&
other.deletedAt == this.deletedAt &&
other.visibility == this.visibility);
other.livePhotoVideoId == this.livePhotoVideoId &&
other.visibility == this.visibility &&
other.stackId == this.stackId);
}
class RemoteAssetEntityCompanion
@@ -733,7 +775,9 @@ class RemoteAssetEntityCompanion
final Value<DateTime?> localDateTime;
final Value<String?> thumbHash;
final Value<DateTime?> deletedAt;
final Value<String?> livePhotoVideoId;
final Value<int> visibility;
final Value<String?> stackId;
const RemoteAssetEntityCompanion({
this.name = const Value.absent(),
this.type = const Value.absent(),
@@ -749,7 +793,9 @@ class RemoteAssetEntityCompanion
this.localDateTime = const Value.absent(),
this.thumbHash = const Value.absent(),
this.deletedAt = const Value.absent(),
this.livePhotoVideoId = const Value.absent(),
this.visibility = const Value.absent(),
this.stackId = const Value.absent(),
});
RemoteAssetEntityCompanion.insert({
required String name,
@@ -766,7 +812,9 @@ class RemoteAssetEntityCompanion
this.localDateTime = const Value.absent(),
this.thumbHash = const Value.absent(),
this.deletedAt = const Value.absent(),
this.livePhotoVideoId = const Value.absent(),
required int visibility,
this.stackId = const Value.absent(),
}) : name = Value(name),
type = Value(type),
id = Value(id),
@@ -788,7 +836,9 @@ class RemoteAssetEntityCompanion
Expression<DateTime>? localDateTime,
Expression<String>? thumbHash,
Expression<DateTime>? deletedAt,
Expression<String>? livePhotoVideoId,
Expression<int>? visibility,
Expression<String>? stackId,
}) {
return RawValuesInsertable({
if (name != null) 'name': name,
@@ -805,7 +855,9 @@ class RemoteAssetEntityCompanion
if (localDateTime != null) 'local_date_time': localDateTime,
if (thumbHash != null) 'thumb_hash': thumbHash,
if (deletedAt != null) 'deleted_at': deletedAt,
if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId,
if (visibility != null) 'visibility': visibility,
if (stackId != null) 'stack_id': stackId,
});
}
@@ -824,7 +876,9 @@ class RemoteAssetEntityCompanion
Value<DateTime?>? localDateTime,
Value<String?>? thumbHash,
Value<DateTime?>? deletedAt,
Value<int>? visibility}) {
Value<String?>? livePhotoVideoId,
Value<int>? visibility,
Value<String?>? stackId}) {
return RemoteAssetEntityCompanion(
name: name ?? this.name,
type: type ?? this.type,
@@ -840,7 +894,9 @@ class RemoteAssetEntityCompanion
localDateTime: localDateTime ?? this.localDateTime,
thumbHash: thumbHash ?? this.thumbHash,
deletedAt: deletedAt ?? this.deletedAt,
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
visibility: visibility ?? this.visibility,
stackId: stackId ?? this.stackId,
);
}
@@ -889,9 +945,15 @@ class RemoteAssetEntityCompanion
if (deletedAt.present) {
map['deleted_at'] = Variable<DateTime>(deletedAt.value);
}
if (livePhotoVideoId.present) {
map['live_photo_video_id'] = Variable<String>(livePhotoVideoId.value);
}
if (visibility.present) {
map['visibility'] = Variable<int>(visibility.value);
}
if (stackId.present) {
map['stack_id'] = Variable<String>(stackId.value);
}
return map;
}
@@ -912,7 +974,9 @@ class RemoteAssetEntityCompanion
..write('localDateTime: $localDateTime, ')
..write('thumbHash: $thumbHash, ')
..write('deletedAt: $deletedAt, ')
..write('visibility: $visibility')
..write('livePhotoVideoId: $livePhotoVideoId, ')
..write('visibility: $visibility, ')
..write('stackId: $stackId')
..write(')'))
.toString();
}
@@ -1351,6 +1415,258 @@ class LocalAssetEntityCompanion extends UpdateCompanion<LocalAssetEntityData> {
}
}
class StackEntity extends Table with TableInfo<StackEntity, StackEntityData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
StackEntity(this.attachedDatabase, [this._alias]);
late final GeneratedColumn<String> id = GeneratedColumn<String>(
'id', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: const CustomExpression('CURRENT_TIMESTAMP'));
late final GeneratedColumn<DateTime> updatedAt = GeneratedColumn<DateTime>(
'updated_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: const CustomExpression('CURRENT_TIMESTAMP'));
late final GeneratedColumn<String> ownerId = GeneratedColumn<String>(
'owner_id', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE'));
late final GeneratedColumn<String> primaryAssetId = GeneratedColumn<String>(
'primary_asset_id', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id)'));
@override
List<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
Set<GeneratedColumn> get $primaryKey => {id};
@override
StackEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return StackEntityData(
id: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}id'])!,
createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
updatedAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
ownerId: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!,
primaryAssetId: attachedDatabase.typeMapping.read(
DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!,
);
}
@override
StackEntity createAlias(String alias) {
return StackEntity(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class StackEntityData extends DataClass implements Insertable<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, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<String>(id);
map['created_at'] = Variable<DateTime>(createdAt);
map['updated_at'] = Variable<DateTime>(updatedAt);
map['owner_id'] = Variable<String>(ownerId);
map['primary_asset_id'] = Variable<String>(primaryAssetId);
return map;
}
factory StackEntityData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= 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({ValueSerializer? serializer}) {
serializer ??= 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),
};
}
StackEntityData copyWith(
{String? id,
DateTime? createdAt,
DateTime? updatedAt,
String? ownerId,
String? primaryAssetId}) =>
StackEntityData(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
);
StackEntityData copyWithCompanion(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 StackEntityData &&
other.id == this.id &&
other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt &&
other.ownerId == this.ownerId &&
other.primaryAssetId == this.primaryAssetId);
}
class StackEntityCompanion extends UpdateCompanion<StackEntityData> {
final Value<String> id;
final Value<DateTime> createdAt;
final Value<DateTime> updatedAt;
final Value<String> ownerId;
final Value<String> primaryAssetId;
const StackEntityCompanion({
this.id = const Value.absent(),
this.createdAt = const Value.absent(),
this.updatedAt = const Value.absent(),
this.ownerId = const Value.absent(),
this.primaryAssetId = const Value.absent(),
});
StackEntityCompanion.insert({
required String id,
this.createdAt = const Value.absent(),
this.updatedAt = const Value.absent(),
required String ownerId,
required String primaryAssetId,
}) : id = Value(id),
ownerId = Value(ownerId),
primaryAssetId = Value(primaryAssetId);
static Insertable<StackEntityData> custom({
Expression<String>? id,
Expression<DateTime>? createdAt,
Expression<DateTime>? updatedAt,
Expression<String>? ownerId,
Expression<String>? primaryAssetId,
}) {
return 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,
});
}
StackEntityCompanion copyWith(
{Value<String>? id,
Value<DateTime>? createdAt,
Value<DateTime>? updatedAt,
Value<String>? ownerId,
Value<String>? primaryAssetId}) {
return StackEntityCompanion(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<String>(id.value);
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
if (updatedAt.present) {
map['updated_at'] = Variable<DateTime>(updatedAt.value);
}
if (ownerId.present) {
map['owner_id'] = Variable<String>(ownerId.value);
}
if (primaryAssetId.present) {
map['primary_asset_id'] = 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();
}
}
class UserMetadataEntity extends Table
with TableInfo<UserMetadataEntity, UserMetadataEntityData> {
@override
@@ -4329,263 +4645,12 @@ class MemoryAssetEntityCompanion
}
}
class StackEntity extends Table with TableInfo<StackEntity, StackEntityData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
StackEntity(this.attachedDatabase, [this._alias]);
late final GeneratedColumn<String> id = GeneratedColumn<String>(
'id', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: const CustomExpression('CURRENT_TIMESTAMP'));
late final GeneratedColumn<DateTime> updatedAt = GeneratedColumn<DateTime>(
'updated_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: const CustomExpression('CURRENT_TIMESTAMP'));
late final GeneratedColumn<String> ownerId = GeneratedColumn<String>(
'owner_id', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE'));
late final GeneratedColumn<String> primaryAssetId = GeneratedColumn<String>(
'primary_asset_id', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id)'));
@override
List<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
Set<GeneratedColumn> get $primaryKey => {id};
@override
StackEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return StackEntityData(
id: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}id'])!,
createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
updatedAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
ownerId: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!,
primaryAssetId: attachedDatabase.typeMapping.read(
DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!,
);
}
@override
StackEntity createAlias(String alias) {
return StackEntity(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class StackEntityData extends DataClass implements Insertable<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, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<String>(id);
map['created_at'] = Variable<DateTime>(createdAt);
map['updated_at'] = Variable<DateTime>(updatedAt);
map['owner_id'] = Variable<String>(ownerId);
map['primary_asset_id'] = Variable<String>(primaryAssetId);
return map;
}
factory StackEntityData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= 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({ValueSerializer? serializer}) {
serializer ??= 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),
};
}
StackEntityData copyWith(
{String? id,
DateTime? createdAt,
DateTime? updatedAt,
String? ownerId,
String? primaryAssetId}) =>
StackEntityData(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
);
StackEntityData copyWithCompanion(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 StackEntityData &&
other.id == this.id &&
other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt &&
other.ownerId == this.ownerId &&
other.primaryAssetId == this.primaryAssetId);
}
class StackEntityCompanion extends UpdateCompanion<StackEntityData> {
final Value<String> id;
final Value<DateTime> createdAt;
final Value<DateTime> updatedAt;
final Value<String> ownerId;
final Value<String> primaryAssetId;
const StackEntityCompanion({
this.id = const Value.absent(),
this.createdAt = const Value.absent(),
this.updatedAt = const Value.absent(),
this.ownerId = const Value.absent(),
this.primaryAssetId = const Value.absent(),
});
StackEntityCompanion.insert({
required String id,
this.createdAt = const Value.absent(),
this.updatedAt = const Value.absent(),
required String ownerId,
required String primaryAssetId,
}) : id = Value(id),
ownerId = Value(ownerId),
primaryAssetId = Value(primaryAssetId);
static Insertable<StackEntityData> custom({
Expression<String>? id,
Expression<DateTime>? createdAt,
Expression<DateTime>? updatedAt,
Expression<String>? ownerId,
Expression<String>? primaryAssetId,
}) {
return 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,
});
}
StackEntityCompanion copyWith(
{Value<String>? id,
Value<DateTime>? createdAt,
Value<DateTime>? updatedAt,
Value<String>? ownerId,
Value<String>? primaryAssetId}) {
return StackEntityCompanion(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<String>(id.value);
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
if (updatedAt.present) {
map['updated_at'] = Variable<DateTime>(updatedAt.value);
}
if (ownerId.present) {
map['owner_id'] = Variable<String>(ownerId.value);
}
if (primaryAssetId.present) {
map['primary_asset_id'] = 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();
}
}
class DatabaseAtV1 extends GeneratedDatabase {
DatabaseAtV1(QueryExecutor e) : super(e);
late final UserEntity userEntity = UserEntity(this);
late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this);
late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this);
late final StackEntity stackEntity = StackEntity(this);
late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum',
'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)');
late final Index uQRemoteAssetOwnerChecksum = Index(
@@ -4606,7 +4671,6 @@ class DatabaseAtV1 extends GeneratedDatabase {
RemoteAlbumUserEntity(this);
late final MemoryEntity memoryEntity = MemoryEntity(this);
late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this);
late final StackEntity stackEntity = StackEntity(this);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@@ -4615,6 +4679,7 @@ class DatabaseAtV1 extends GeneratedDatabase {
userEntity,
remoteAssetEntity,
localAssetEntity,
stackEntity,
idxLocalAssetChecksum,
uQRemoteAssetOwnerChecksum,
idxRemoteAssetChecksum,
@@ -4627,8 +4692,7 @@ class DatabaseAtV1 extends GeneratedDatabase {
remoteAlbumAssetEntity,
remoteAlbumUserEntity,
memoryEntity,
memoryAssetEntity,
stackEntity
memoryAssetEntity
];
@override
int get schemaVersion => 1;
+293 -263
View File
@@ -416,6 +416,9 @@ class RemoteAssetEntity extends Table
late final GeneratedColumn<int> visibility = GeneratedColumn<int>(
'visibility', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
late final GeneratedColumn<String> stackId = GeneratedColumn<String>(
'stack_id', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false);
@override
List<GeneratedColumn> get $columns => [
name,
@@ -433,7 +436,8 @@ class RemoteAssetEntity extends Table
thumbHash,
deletedAt,
livePhotoVideoId,
visibility
visibility,
stackId
];
@override
String get aliasedName => _alias ?? actualTableName;
@@ -478,6 +482,8 @@ class RemoteAssetEntity extends Table
DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']),
visibility: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}visibility'])!,
stackId: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}stack_id']),
);
}
@@ -510,6 +516,7 @@ class RemoteAssetEntityData extends DataClass
final DateTime? deletedAt;
final String? livePhotoVideoId;
final int visibility;
final String? stackId;
const RemoteAssetEntityData(
{required this.name,
required this.type,
@@ -526,7 +533,8 @@ class RemoteAssetEntityData extends DataClass
this.thumbHash,
this.deletedAt,
this.livePhotoVideoId,
required this.visibility});
required this.visibility,
this.stackId});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
@@ -560,6 +568,9 @@ class RemoteAssetEntityData extends DataClass
map['live_photo_video_id'] = Variable<String>(livePhotoVideoId);
}
map['visibility'] = Variable<int>(visibility);
if (!nullToAbsent || stackId != null) {
map['stack_id'] = Variable<String>(stackId);
}
return map;
}
@@ -583,6 +594,7 @@ class RemoteAssetEntityData extends DataClass
deletedAt: serializer.fromJson<DateTime?>(json['deletedAt']),
livePhotoVideoId: serializer.fromJson<String?>(json['livePhotoVideoId']),
visibility: serializer.fromJson<int>(json['visibility']),
stackId: serializer.fromJson<String?>(json['stackId']),
);
}
@override
@@ -605,6 +617,7 @@ class RemoteAssetEntityData extends DataClass
'deletedAt': serializer.toJson<DateTime?>(deletedAt),
'livePhotoVideoId': serializer.toJson<String?>(livePhotoVideoId),
'visibility': serializer.toJson<int>(visibility),
'stackId': serializer.toJson<String?>(stackId),
};
}
@@ -624,7 +637,8 @@ class RemoteAssetEntityData extends DataClass
Value<String?> thumbHash = const Value.absent(),
Value<DateTime?> deletedAt = const Value.absent(),
Value<String?> livePhotoVideoId = const Value.absent(),
int? visibility}) =>
int? visibility,
Value<String?> stackId = const Value.absent()}) =>
RemoteAssetEntityData(
name: name ?? this.name,
type: type ?? this.type,
@@ -647,6 +661,7 @@ class RemoteAssetEntityData extends DataClass
? livePhotoVideoId.value
: this.livePhotoVideoId,
visibility: visibility ?? this.visibility,
stackId: stackId.present ? stackId.value : this.stackId,
);
RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) {
return RemoteAssetEntityData(
@@ -674,6 +689,7 @@ class RemoteAssetEntityData extends DataClass
: this.livePhotoVideoId,
visibility:
data.visibility.present ? data.visibility.value : this.visibility,
stackId: data.stackId.present ? data.stackId.value : this.stackId,
);
}
@@ -695,7 +711,8 @@ class RemoteAssetEntityData extends DataClass
..write('thumbHash: $thumbHash, ')
..write('deletedAt: $deletedAt, ')
..write('livePhotoVideoId: $livePhotoVideoId, ')
..write('visibility: $visibility')
..write('visibility: $visibility, ')
..write('stackId: $stackId')
..write(')'))
.toString();
}
@@ -717,7 +734,8 @@ class RemoteAssetEntityData extends DataClass
thumbHash,
deletedAt,
livePhotoVideoId,
visibility);
visibility,
stackId);
@override
bool operator ==(Object other) =>
identical(this, other) ||
@@ -737,7 +755,8 @@ class RemoteAssetEntityData extends DataClass
other.thumbHash == this.thumbHash &&
other.deletedAt == this.deletedAt &&
other.livePhotoVideoId == this.livePhotoVideoId &&
other.visibility == this.visibility);
other.visibility == this.visibility &&
other.stackId == this.stackId);
}
class RemoteAssetEntityCompanion
@@ -758,6 +777,7 @@ class RemoteAssetEntityCompanion
final Value<DateTime?> deletedAt;
final Value<String?> livePhotoVideoId;
final Value<int> visibility;
final Value<String?> stackId;
const RemoteAssetEntityCompanion({
this.name = const Value.absent(),
this.type = const Value.absent(),
@@ -775,6 +795,7 @@ class RemoteAssetEntityCompanion
this.deletedAt = const Value.absent(),
this.livePhotoVideoId = const Value.absent(),
this.visibility = const Value.absent(),
this.stackId = const Value.absent(),
});
RemoteAssetEntityCompanion.insert({
required String name,
@@ -793,6 +814,7 @@ class RemoteAssetEntityCompanion
this.deletedAt = const Value.absent(),
this.livePhotoVideoId = const Value.absent(),
required int visibility,
this.stackId = const Value.absent(),
}) : name = Value(name),
type = Value(type),
id = Value(id),
@@ -816,6 +838,7 @@ class RemoteAssetEntityCompanion
Expression<DateTime>? deletedAt,
Expression<String>? livePhotoVideoId,
Expression<int>? visibility,
Expression<String>? stackId,
}) {
return RawValuesInsertable({
if (name != null) 'name': name,
@@ -834,6 +857,7 @@ class RemoteAssetEntityCompanion
if (deletedAt != null) 'deleted_at': deletedAt,
if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId,
if (visibility != null) 'visibility': visibility,
if (stackId != null) 'stack_id': stackId,
});
}
@@ -853,7 +877,8 @@ class RemoteAssetEntityCompanion
Value<String?>? thumbHash,
Value<DateTime?>? deletedAt,
Value<String?>? livePhotoVideoId,
Value<int>? visibility}) {
Value<int>? visibility,
Value<String?>? stackId}) {
return RemoteAssetEntityCompanion(
name: name ?? this.name,
type: type ?? this.type,
@@ -871,6 +896,7 @@ class RemoteAssetEntityCompanion
deletedAt: deletedAt ?? this.deletedAt,
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
visibility: visibility ?? this.visibility,
stackId: stackId ?? this.stackId,
);
}
@@ -925,6 +951,9 @@ class RemoteAssetEntityCompanion
if (visibility.present) {
map['visibility'] = Variable<int>(visibility.value);
}
if (stackId.present) {
map['stack_id'] = Variable<String>(stackId.value);
}
return map;
}
@@ -946,7 +975,8 @@ class RemoteAssetEntityCompanion
..write('thumbHash: $thumbHash, ')
..write('deletedAt: $deletedAt, ')
..write('livePhotoVideoId: $livePhotoVideoId, ')
..write('visibility: $visibility')
..write('visibility: $visibility, ')
..write('stackId: $stackId')
..write(')'))
.toString();
}
@@ -1385,6 +1415,258 @@ class LocalAssetEntityCompanion extends UpdateCompanion<LocalAssetEntityData> {
}
}
class StackEntity extends Table with TableInfo<StackEntity, StackEntityData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
StackEntity(this.attachedDatabase, [this._alias]);
late final GeneratedColumn<String> id = GeneratedColumn<String>(
'id', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: const CustomExpression('CURRENT_TIMESTAMP'));
late final GeneratedColumn<DateTime> updatedAt = GeneratedColumn<DateTime>(
'updated_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: const CustomExpression('CURRENT_TIMESTAMP'));
late final GeneratedColumn<String> ownerId = GeneratedColumn<String>(
'owner_id', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE'));
late final GeneratedColumn<String> primaryAssetId = GeneratedColumn<String>(
'primary_asset_id', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id)'));
@override
List<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
Set<GeneratedColumn> get $primaryKey => {id};
@override
StackEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return StackEntityData(
id: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}id'])!,
createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
updatedAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
ownerId: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!,
primaryAssetId: attachedDatabase.typeMapping.read(
DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!,
);
}
@override
StackEntity createAlias(String alias) {
return StackEntity(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class StackEntityData extends DataClass implements Insertable<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, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<String>(id);
map['created_at'] = Variable<DateTime>(createdAt);
map['updated_at'] = Variable<DateTime>(updatedAt);
map['owner_id'] = Variable<String>(ownerId);
map['primary_asset_id'] = Variable<String>(primaryAssetId);
return map;
}
factory StackEntityData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= 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({ValueSerializer? serializer}) {
serializer ??= 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),
};
}
StackEntityData copyWith(
{String? id,
DateTime? createdAt,
DateTime? updatedAt,
String? ownerId,
String? primaryAssetId}) =>
StackEntityData(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
);
StackEntityData copyWithCompanion(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 StackEntityData &&
other.id == this.id &&
other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt &&
other.ownerId == this.ownerId &&
other.primaryAssetId == this.primaryAssetId);
}
class StackEntityCompanion extends UpdateCompanion<StackEntityData> {
final Value<String> id;
final Value<DateTime> createdAt;
final Value<DateTime> updatedAt;
final Value<String> ownerId;
final Value<String> primaryAssetId;
const StackEntityCompanion({
this.id = const Value.absent(),
this.createdAt = const Value.absent(),
this.updatedAt = const Value.absent(),
this.ownerId = const Value.absent(),
this.primaryAssetId = const Value.absent(),
});
StackEntityCompanion.insert({
required String id,
this.createdAt = const Value.absent(),
this.updatedAt = const Value.absent(),
required String ownerId,
required String primaryAssetId,
}) : id = Value(id),
ownerId = Value(ownerId),
primaryAssetId = Value(primaryAssetId);
static Insertable<StackEntityData> custom({
Expression<String>? id,
Expression<DateTime>? createdAt,
Expression<DateTime>? updatedAt,
Expression<String>? ownerId,
Expression<String>? primaryAssetId,
}) {
return 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,
});
}
StackEntityCompanion copyWith(
{Value<String>? id,
Value<DateTime>? createdAt,
Value<DateTime>? updatedAt,
Value<String>? ownerId,
Value<String>? primaryAssetId}) {
return StackEntityCompanion(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<String>(id.value);
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
if (updatedAt.present) {
map['updated_at'] = Variable<DateTime>(updatedAt.value);
}
if (ownerId.present) {
map['owner_id'] = Variable<String>(ownerId.value);
}
if (primaryAssetId.present) {
map['primary_asset_id'] = 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();
}
}
class UserMetadataEntity extends Table
with TableInfo<UserMetadataEntity, UserMetadataEntityData> {
@override
@@ -4363,263 +4645,12 @@ class MemoryAssetEntityCompanion
}
}
class StackEntity extends Table with TableInfo<StackEntity, StackEntityData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
StackEntity(this.attachedDatabase, [this._alias]);
late final GeneratedColumn<String> id = GeneratedColumn<String>(
'id', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: const CustomExpression('CURRENT_TIMESTAMP'));
late final GeneratedColumn<DateTime> updatedAt = GeneratedColumn<DateTime>(
'updated_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: const CustomExpression('CURRENT_TIMESTAMP'));
late final GeneratedColumn<String> ownerId = GeneratedColumn<String>(
'owner_id', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE'));
late final GeneratedColumn<String> primaryAssetId = GeneratedColumn<String>(
'primary_asset_id', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES remote_asset_entity (id)'));
@override
List<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
Set<GeneratedColumn> get $primaryKey => {id};
@override
StackEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return StackEntityData(
id: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}id'])!,
createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
updatedAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
ownerId: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!,
primaryAssetId: attachedDatabase.typeMapping.read(
DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!,
);
}
@override
StackEntity createAlias(String alias) {
return StackEntity(attachedDatabase, alias);
}
@override
bool get withoutRowId => true;
@override
bool get isStrict => true;
}
class StackEntityData extends DataClass implements Insertable<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, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<String>(id);
map['created_at'] = Variable<DateTime>(createdAt);
map['updated_at'] = Variable<DateTime>(updatedAt);
map['owner_id'] = Variable<String>(ownerId);
map['primary_asset_id'] = Variable<String>(primaryAssetId);
return map;
}
factory StackEntityData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= 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({ValueSerializer? serializer}) {
serializer ??= 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),
};
}
StackEntityData copyWith(
{String? id,
DateTime? createdAt,
DateTime? updatedAt,
String? ownerId,
String? primaryAssetId}) =>
StackEntityData(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
);
StackEntityData copyWithCompanion(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 StackEntityData &&
other.id == this.id &&
other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt &&
other.ownerId == this.ownerId &&
other.primaryAssetId == this.primaryAssetId);
}
class StackEntityCompanion extends UpdateCompanion<StackEntityData> {
final Value<String> id;
final Value<DateTime> createdAt;
final Value<DateTime> updatedAt;
final Value<String> ownerId;
final Value<String> primaryAssetId;
const StackEntityCompanion({
this.id = const Value.absent(),
this.createdAt = const Value.absent(),
this.updatedAt = const Value.absent(),
this.ownerId = const Value.absent(),
this.primaryAssetId = const Value.absent(),
});
StackEntityCompanion.insert({
required String id,
this.createdAt = const Value.absent(),
this.updatedAt = const Value.absent(),
required String ownerId,
required String primaryAssetId,
}) : id = Value(id),
ownerId = Value(ownerId),
primaryAssetId = Value(primaryAssetId);
static Insertable<StackEntityData> custom({
Expression<String>? id,
Expression<DateTime>? createdAt,
Expression<DateTime>? updatedAt,
Expression<String>? ownerId,
Expression<String>? primaryAssetId,
}) {
return 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,
});
}
StackEntityCompanion copyWith(
{Value<String>? id,
Value<DateTime>? createdAt,
Value<DateTime>? updatedAt,
Value<String>? ownerId,
Value<String>? primaryAssetId}) {
return StackEntityCompanion(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
primaryAssetId: primaryAssetId ?? this.primaryAssetId,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<String>(id.value);
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
if (updatedAt.present) {
map['updated_at'] = Variable<DateTime>(updatedAt.value);
}
if (ownerId.present) {
map['owner_id'] = Variable<String>(ownerId.value);
}
if (primaryAssetId.present) {
map['primary_asset_id'] = 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();
}
}
class DatabaseAtV2 extends GeneratedDatabase {
DatabaseAtV2(QueryExecutor e) : super(e);
late final UserEntity userEntity = UserEntity(this);
late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this);
late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this);
late final StackEntity stackEntity = StackEntity(this);
late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum',
'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)');
late final Index uQRemoteAssetOwnerChecksum = Index(
@@ -4640,7 +4671,6 @@ class DatabaseAtV2 extends GeneratedDatabase {
RemoteAlbumUserEntity(this);
late final MemoryEntity memoryEntity = MemoryEntity(this);
late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this);
late final StackEntity stackEntity = StackEntity(this);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@@ -4649,6 +4679,7 @@ class DatabaseAtV2 extends GeneratedDatabase {
userEntity,
remoteAssetEntity,
localAssetEntity,
stackEntity,
idxLocalAssetChecksum,
uQRemoteAssetOwnerChecksum,
idxRemoteAssetChecksum,
@@ -4661,8 +4692,7 @@ class DatabaseAtV2 extends GeneratedDatabase {
remoteAlbumAssetEntity,
remoteAlbumUserEntity,
memoryEntity,
memoryAssetEntity,
stackEntity
memoryAssetEntity
];
@override
int get schemaVersion => 2;