You've already forked immich
mirror of
https://github.com/immich-app/immich.git
synced 2025-07-16 07:24:40 +02:00
feat: handle live photos on new asset viewer (#19926)
sync and handle livePhotoVideoId in asset viewer Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
2
mobile/drift_schemas/main/drift_schema_v1.json
generated
2
mobile/drift_schemas/main/drift_schema_v1.json
generated
File diff suppressed because one or more lines are too long
@ -43,6 +43,8 @@ sealed class BaseAsset {
|
||||
bool get isImage => type == AssetType.image;
|
||||
bool get isVideo => type == AssetType.video;
|
||||
|
||||
bool get isMotionPhoto => livePhotoVideoId != null;
|
||||
|
||||
Duration get duration {
|
||||
final durationInSeconds = this.durationInSeconds;
|
||||
if (durationInSeconds != null) {
|
||||
|
@ -94,6 +94,7 @@ class RemoteAsset extends BaseAsset {
|
||||
bool? isFavorite,
|
||||
String? thumbHash,
|
||||
AssetVisibility? visibility,
|
||||
String? livePhotoVideoId,
|
||||
}) {
|
||||
return RemoteAsset(
|
||||
id: id ?? this.id,
|
||||
@ -110,6 +111,7 @@ class RemoteAsset extends BaseAsset {
|
||||
isFavorite: isFavorite ?? this.isFavorite,
|
||||
thumbHash: thumbHash ?? this.thumbHash,
|
||||
visibility: visibility ?? this.visibility,
|
||||
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ mergedAsset: SELECT * FROM
|
||||
rae.thumb_hash,
|
||||
rae.checksum,
|
||||
rae.owner_id,
|
||||
rae.live_photo_video_id,
|
||||
0 as orientation
|
||||
FROM
|
||||
remote_asset_entity rae
|
||||
@ -39,6 +40,7 @@ mergedAsset: SELECT * FROM
|
||||
NULL as thumb_hash,
|
||||
lae.checksum,
|
||||
NULL as owner_id,
|
||||
NULL as live_photo_video_id,
|
||||
lae.orientation
|
||||
FROM
|
||||
local_asset_entity lae
|
||||
|
@ -18,7 +18,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, 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, 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 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}',
|
||||
variables: [
|
||||
for (var $ in var1) i0.Variable<String>($),
|
||||
...generatedlimit.introducedVariables
|
||||
@ -42,6 +42,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
|
||||
thumbHash: row.readNullable<String>('thumb_hash'),
|
||||
checksum: row.readNullable<String>('checksum'),
|
||||
ownerId: row.readNullable<String>('owner_id'),
|
||||
livePhotoVideoId: row.readNullable<String>('live_photo_video_id'),
|
||||
orientation: row.read<int>('orientation'),
|
||||
));
|
||||
}
|
||||
@ -88,6 +89,7 @@ class MergedAssetResult {
|
||||
final String? thumbHash;
|
||||
final String? checksum;
|
||||
final String? ownerId;
|
||||
final String? livePhotoVideoId;
|
||||
final int orientation;
|
||||
MergedAssetResult({
|
||||
this.remoteId,
|
||||
@ -103,6 +105,7 @@ class MergedAssetResult {
|
||||
this.thumbHash,
|
||||
this.checksum,
|
||||
this.ownerId,
|
||||
this.livePhotoVideoId,
|
||||
required this.orientation,
|
||||
});
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ class RemoteAssetEntity extends Table
|
||||
|
||||
DateTimeColumn get deletedAt => dateTime().nullable()();
|
||||
|
||||
TextColumn get livePhotoVideoId => text().nullable()();
|
||||
|
||||
IntColumn get visibility => intEnum<AssetVisibility>()();
|
||||
|
||||
@override
|
||||
@ -51,6 +53,7 @@ extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData {
|
||||
width: width,
|
||||
thumbHash: thumbHash,
|
||||
visibility: visibility,
|
||||
livePhotoVideoId: livePhotoVideoId,
|
||||
localId: null,
|
||||
);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ typedef $$RemoteAssetEntityTableCreateCompanionBuilder
|
||||
i0.Value<DateTime?> localDateTime,
|
||||
i0.Value<String?> thumbHash,
|
||||
i0.Value<DateTime?> deletedAt,
|
||||
i0.Value<String?> livePhotoVideoId,
|
||||
required i2.AssetVisibility visibility,
|
||||
});
|
||||
typedef $$RemoteAssetEntityTableUpdateCompanionBuilder
|
||||
@ -45,6 +46,7 @@ typedef $$RemoteAssetEntityTableUpdateCompanionBuilder
|
||||
i0.Value<DateTime?> localDateTime,
|
||||
i0.Value<String?> thumbHash,
|
||||
i0.Value<DateTime?> deletedAt,
|
||||
i0.Value<String?> livePhotoVideoId,
|
||||
i0.Value<i2.AssetVisibility> visibility,
|
||||
});
|
||||
|
||||
@ -134,6 +136,10 @@ class $$RemoteAssetEntityTableFilterComposer
|
||||
i0.ColumnFilters<DateTime> get deletedAt => $composableBuilder(
|
||||
column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnFilters<String> get livePhotoVideoId => $composableBuilder(
|
||||
column: $table.livePhotoVideoId,
|
||||
builder: (column) => i0.ColumnFilters(column));
|
||||
|
||||
i0.ColumnWithTypeConverterFilters<i2.AssetVisibility, i2.AssetVisibility, int>
|
||||
get visibility => $composableBuilder(
|
||||
column: $table.visibility,
|
||||
@ -217,6 +223,10 @@ class $$RemoteAssetEntityTableOrderingComposer
|
||||
column: $table.deletedAt,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<String> get livePhotoVideoId => $composableBuilder(
|
||||
column: $table.livePhotoVideoId,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
|
||||
i0.ColumnOrderings<int> get visibility => $composableBuilder(
|
||||
column: $table.visibility,
|
||||
builder: (column) => i0.ColumnOrderings(column));
|
||||
@ -292,6 +302,9 @@ class $$RemoteAssetEntityTableAnnotationComposer
|
||||
i0.GeneratedColumn<DateTime> get deletedAt =>
|
||||
$composableBuilder(column: $table.deletedAt, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumn<String> get livePhotoVideoId => $composableBuilder(
|
||||
column: $table.livePhotoVideoId, builder: (column) => column);
|
||||
|
||||
i0.GeneratedColumnWithTypeConverter<i2.AssetVisibility, int> get visibility =>
|
||||
$composableBuilder(
|
||||
column: $table.visibility, builder: (column) => column);
|
||||
@ -358,6 +371,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
||||
i0.Value<String?> thumbHash = const i0.Value.absent(),
|
||||
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(),
|
||||
}) =>
|
||||
i1.RemoteAssetEntityCompanion(
|
||||
@ -375,6 +389,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
localDateTime: localDateTime,
|
||||
thumbHash: thumbHash,
|
||||
deletedAt: deletedAt,
|
||||
livePhotoVideoId: livePhotoVideoId,
|
||||
visibility: visibility,
|
||||
),
|
||||
createCompanionCallback: ({
|
||||
@ -392,6 +407,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
||||
i0.Value<String?> thumbHash = const i0.Value.absent(),
|
||||
i0.Value<DateTime?> deletedAt = const i0.Value.absent(),
|
||||
i0.Value<String?> livePhotoVideoId = const i0.Value.absent(),
|
||||
required i2.AssetVisibility visibility,
|
||||
}) =>
|
||||
i1.RemoteAssetEntityCompanion.insert(
|
||||
@ -409,6 +425,7 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager<
|
||||
localDateTime: localDateTime,
|
||||
thumbHash: thumbHash,
|
||||
deletedAt: deletedAt,
|
||||
livePhotoVideoId: livePhotoVideoId,
|
||||
visibility: visibility,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
@ -573,6 +590,12 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
||||
late final i0.GeneratedColumn<DateTime> deletedAt =
|
||||
i0.GeneratedColumn<DateTime>('deleted_at', aliasedName, true,
|
||||
type: i0.DriftSqlType.dateTime, requiredDuringInsert: false);
|
||||
static const i0.VerificationMeta _livePhotoVideoIdMeta =
|
||||
const i0.VerificationMeta('livePhotoVideoId');
|
||||
@override
|
||||
late final i0.GeneratedColumn<String> livePhotoVideoId =
|
||||
i0.GeneratedColumn<String>('live_photo_video_id', aliasedName, true,
|
||||
type: i0.DriftSqlType.string, requiredDuringInsert: false);
|
||||
@override
|
||||
late final i0.GeneratedColumnWithTypeConverter<i2.AssetVisibility, int>
|
||||
visibility = i0.GeneratedColumn<int>('visibility', aliasedName, false,
|
||||
@ -595,6 +618,7 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
||||
localDateTime,
|
||||
thumbHash,
|
||||
deletedAt,
|
||||
livePhotoVideoId,
|
||||
visibility
|
||||
];
|
||||
@override
|
||||
@ -673,6 +697,12 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
||||
context.handle(_deletedAtMeta,
|
||||
deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta));
|
||||
}
|
||||
if (data.containsKey('live_photo_video_id')) {
|
||||
context.handle(
|
||||
_livePhotoVideoIdMeta,
|
||||
livePhotoVideoId.isAcceptableOrUnknown(
|
||||
data['live_photo_video_id']!, _livePhotoVideoIdMeta));
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@ -712,6 +742,9 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
|
||||
.read(i0.DriftSqlType.string, data['${effectivePrefix}thumb_hash']),
|
||||
deletedAt: attachedDatabase.typeMapping
|
||||
.read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']),
|
||||
livePhotoVideoId: attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.string,
|
||||
data['${effectivePrefix}live_photo_video_id']),
|
||||
visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromSql(
|
||||
attachedDatabase.typeMapping.read(
|
||||
i0.DriftSqlType.int, data['${effectivePrefix}visibility'])!),
|
||||
@ -750,6 +783,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
final DateTime? localDateTime;
|
||||
final String? thumbHash;
|
||||
final DateTime? deletedAt;
|
||||
final String? livePhotoVideoId;
|
||||
final i2.AssetVisibility visibility;
|
||||
const RemoteAssetEntityData(
|
||||
{required this.name,
|
||||
@ -766,6 +800,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
this.localDateTime,
|
||||
this.thumbHash,
|
||||
this.deletedAt,
|
||||
this.livePhotoVideoId,
|
||||
required this.visibility});
|
||||
@override
|
||||
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||
@ -799,6 +834,9 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
if (!nullToAbsent || deletedAt != null) {
|
||||
map['deleted_at'] = i0.Variable<DateTime>(deletedAt);
|
||||
}
|
||||
if (!nullToAbsent || livePhotoVideoId != null) {
|
||||
map['live_photo_video_id'] = i0.Variable<String>(livePhotoVideoId);
|
||||
}
|
||||
{
|
||||
map['visibility'] = i0.Variable<int>(
|
||||
i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility));
|
||||
@ -825,6 +863,7 @@ class RemoteAssetEntityData extends i0.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: i1.$RemoteAssetEntityTable.$convertervisibility
|
||||
.fromJson(serializer.fromJson<int>(json['visibility'])),
|
||||
);
|
||||
@ -848,6 +887,7 @@ class RemoteAssetEntityData extends i0.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>(
|
||||
i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility)),
|
||||
};
|
||||
@ -868,6 +908,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
i0.Value<DateTime?> localDateTime = const i0.Value.absent(),
|
||||
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}) =>
|
||||
i1.RemoteAssetEntityData(
|
||||
name: name ?? this.name,
|
||||
@ -887,6 +928,9 @@ class RemoteAssetEntityData extends i0.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,
|
||||
);
|
||||
RemoteAssetEntityData copyWithCompanion(i1.RemoteAssetEntityCompanion data) {
|
||||
@ -910,6 +954,9 @@ class RemoteAssetEntityData extends i0.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,
|
||||
);
|
||||
@ -932,6 +979,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
..write('localDateTime: $localDateTime, ')
|
||||
..write('thumbHash: $thumbHash, ')
|
||||
..write('deletedAt: $deletedAt, ')
|
||||
..write('livePhotoVideoId: $livePhotoVideoId, ')
|
||||
..write('visibility: $visibility')
|
||||
..write(')'))
|
||||
.toString();
|
||||
@ -953,6 +1001,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
localDateTime,
|
||||
thumbHash,
|
||||
deletedAt,
|
||||
livePhotoVideoId,
|
||||
visibility);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
@ -972,6 +1021,7 @@ class RemoteAssetEntityData extends i0.DataClass
|
||||
other.localDateTime == this.localDateTime &&
|
||||
other.thumbHash == this.thumbHash &&
|
||||
other.deletedAt == this.deletedAt &&
|
||||
other.livePhotoVideoId == this.livePhotoVideoId &&
|
||||
other.visibility == this.visibility);
|
||||
}
|
||||
|
||||
@ -991,6 +1041,7 @@ class RemoteAssetEntityCompanion
|
||||
final i0.Value<DateTime?> localDateTime;
|
||||
final i0.Value<String?> thumbHash;
|
||||
final i0.Value<DateTime?> deletedAt;
|
||||
final i0.Value<String?> livePhotoVideoId;
|
||||
final i0.Value<i2.AssetVisibility> visibility;
|
||||
const RemoteAssetEntityCompanion({
|
||||
this.name = const i0.Value.absent(),
|
||||
@ -1007,6 +1058,7 @@ class RemoteAssetEntityCompanion
|
||||
this.localDateTime = const i0.Value.absent(),
|
||||
this.thumbHash = const i0.Value.absent(),
|
||||
this.deletedAt = const i0.Value.absent(),
|
||||
this.livePhotoVideoId = const i0.Value.absent(),
|
||||
this.visibility = const i0.Value.absent(),
|
||||
});
|
||||
RemoteAssetEntityCompanion.insert({
|
||||
@ -1024,6 +1076,7 @@ class RemoteAssetEntityCompanion
|
||||
this.localDateTime = const i0.Value.absent(),
|
||||
this.thumbHash = const i0.Value.absent(),
|
||||
this.deletedAt = const i0.Value.absent(),
|
||||
this.livePhotoVideoId = const i0.Value.absent(),
|
||||
required i2.AssetVisibility visibility,
|
||||
}) : name = i0.Value(name),
|
||||
type = i0.Value(type),
|
||||
@ -1046,6 +1099,7 @@ class RemoteAssetEntityCompanion
|
||||
i0.Expression<DateTime>? localDateTime,
|
||||
i0.Expression<String>? thumbHash,
|
||||
i0.Expression<DateTime>? deletedAt,
|
||||
i0.Expression<String>? livePhotoVideoId,
|
||||
i0.Expression<int>? visibility,
|
||||
}) {
|
||||
return i0.RawValuesInsertable({
|
||||
@ -1063,6 +1117,7 @@ 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,
|
||||
});
|
||||
}
|
||||
@ -1082,6 +1137,7 @@ class RemoteAssetEntityCompanion
|
||||
i0.Value<DateTime?>? localDateTime,
|
||||
i0.Value<String?>? thumbHash,
|
||||
i0.Value<DateTime?>? deletedAt,
|
||||
i0.Value<String?>? livePhotoVideoId,
|
||||
i0.Value<i2.AssetVisibility>? visibility}) {
|
||||
return i1.RemoteAssetEntityCompanion(
|
||||
name: name ?? this.name,
|
||||
@ -1098,6 +1154,7 @@ class RemoteAssetEntityCompanion
|
||||
localDateTime: localDateTime ?? this.localDateTime,
|
||||
thumbHash: thumbHash ?? this.thumbHash,
|
||||
deletedAt: deletedAt ?? this.deletedAt,
|
||||
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
|
||||
visibility: visibility ?? this.visibility,
|
||||
);
|
||||
}
|
||||
@ -1148,6 +1205,9 @@ class RemoteAssetEntityCompanion
|
||||
if (deletedAt.present) {
|
||||
map['deleted_at'] = i0.Variable<DateTime>(deletedAt.value);
|
||||
}
|
||||
if (livePhotoVideoId.present) {
|
||||
map['live_photo_video_id'] = i0.Variable<String>(livePhotoVideoId.value);
|
||||
}
|
||||
if (visibility.present) {
|
||||
map['visibility'] = i0.Variable<int>(i1
|
||||
.$RemoteAssetEntityTable.$convertervisibility
|
||||
@ -1173,6 +1233,7 @@ class RemoteAssetEntityCompanion
|
||||
..write('localDateTime: $localDateTime, ')
|
||||
..write('thumbHash: $thumbHash, ')
|
||||
..write('deletedAt: $deletedAt, ')
|
||||
..write('livePhotoVideoId: $livePhotoVideoId, ')
|
||||
..write('visibility: $visibility')
|
||||
..write(')'))
|
||||
.toString();
|
||||
|
@ -5,13 +5,13 @@ import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/memory.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
@ -134,6 +134,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
||||
thumbHash: Value(asset.thumbhash),
|
||||
deletedAt: Value(asset.deletedAt),
|
||||
visibility: Value(asset.visibility.toAssetVisibility()),
|
||||
livePhotoVideoId: Value(asset.livePhotoVideoId),
|
||||
);
|
||||
|
||||
batch.insert(
|
||||
|
@ -87,6 +87,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
||||
height: row.height,
|
||||
isFavorite: row.isFavorite,
|
||||
durationInSeconds: row.durationInSeconds,
|
||||
livePhotoVideoId: row.livePhotoVideoId,
|
||||
)
|
||||
: LocalAsset(
|
||||
id: row.localId!,
|
||||
|
@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.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/asset_viewer/is_motion_video_playing.provider.dart';
|
||||
|
||||
class MotionPhotoActionButton extends ConsumerWidget {
|
||||
const MotionPhotoActionButton({super.key, this.menuItem = true});
|
||||
|
||||
final bool menuItem;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isPlaying = ref.watch(isPlayingMotionVideoProvider);
|
||||
|
||||
return BaseActionButton(
|
||||
iconData: isPlaying
|
||||
? Icons.motion_photos_pause_outlined
|
||||
: Icons.play_circle_outline_rounded,
|
||||
label: "play_motion_photo".t(context: context),
|
||||
onPressed: ref.read(isPlayingMotionVideoProvider.notifier).toggle,
|
||||
menuItem: menuItem,
|
||||
);
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ import 'package:immich_mobile/presentation/widgets/asset_viewer/top_app_bar.widg
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||
@ -165,7 +166,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
void _onAssetChanged(int index) {
|
||||
final asset = ref.read(timelineServiceProvider).getAsset(index);
|
||||
ref.read(currentAssetNotifier.notifier).setAsset(asset);
|
||||
if (asset.isVideo) {
|
||||
if (asset.isVideo || asset.isMotionPhoto) {
|
||||
ref.read(videoPlaybackValueProvider.notifier).reset();
|
||||
ref.read(videoPlayerControlsProvider.notifier).pause();
|
||||
}
|
||||
@ -473,11 +474,16 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
}
|
||||
}
|
||||
|
||||
void _onLongPress(_, __, ___) {
|
||||
ref.read(isPlayingMotionVideoProvider.notifier).playing = true;
|
||||
}
|
||||
|
||||
PhotoViewGalleryPageOptions _assetBuilder(BuildContext ctx, int index) {
|
||||
scaffoldContext ??= ctx;
|
||||
final asset = ref.read(timelineServiceProvider).getAsset(index);
|
||||
final isPlayingMotionVideo = ref.read(isPlayingMotionVideoProvider);
|
||||
|
||||
if (asset.isImage) {
|
||||
if (asset.isImage && !isPlayingMotionVideo) {
|
||||
return _imageBuilder(ctx, asset);
|
||||
}
|
||||
|
||||
@ -500,6 +506,7 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
onDragUpdate: _onDragUpdate,
|
||||
onDragEnd: _onDragEnd,
|
||||
onTapDown: _onTapDown,
|
||||
onLongPressStart: asset.isMotionPhoto ? _onLongPress : null,
|
||||
errorBuilder: (_, __, ___) => Container(
|
||||
width: ctx.width,
|
||||
height: ctx.height,
|
||||
@ -561,6 +568,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(isPlayingMotionVideoProvider);
|
||||
|
||||
// Currently it is not possible to scroll the asset when the bottom sheet is open all the way.
|
||||
// Issue: https://github.com/flutter/flutter/issues/109037
|
||||
|
@ -6,6 +6,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/favorite_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
|
||||
@ -44,6 +45,7 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
||||
source: ActionSource.viewer,
|
||||
menuItem: true,
|
||||
),
|
||||
if (asset.isMotionPhoto) const MotionPhotoActionButton(menuItem: true),
|
||||
const _KebabMenu(),
|
||||
];
|
||||
|
||||
|
@ -270,10 +270,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
if (videoController.playbackInfo?.status == PlaybackStatus.stopped &&
|
||||
!ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting<bool>(AppSettingsEnum.loopVideo)) {
|
||||
if (videoController.playbackInfo?.status == PlaybackStatus.stopped) {
|
||||
ref.read(isPlayingMotionVideoProvider.notifier).playing = false;
|
||||
}
|
||||
}
|
||||
@ -310,7 +307,7 @@ class NativeVideoViewer extends HookConsumerWidget {
|
||||
final loopVideo = ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting<bool>(AppSettingsEnum.loopVideo);
|
||||
nc.setLoop(loopVideo);
|
||||
nc.setLoop(!asset.isMotionPhoto && loopVideo);
|
||||
|
||||
controller.value = nc;
|
||||
Timer(const Duration(milliseconds: 200), checkIfBuffering);
|
||||
|
@ -10,6 +10,7 @@ import 'package:immich_mobile/presentation/widgets/timeline/header.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||
@ -171,6 +172,7 @@ class _AssetTileWidget extends ConsumerWidget {
|
||||
ref.read(multiSelectProvider.notifier).toggleAssetSelection(asset);
|
||||
} else {
|
||||
await ref.read(timelineServiceProvider).loadAssets(assetIndex, 1);
|
||||
ref.read(isPlayingMotionVideoProvider.notifier).playing = false;
|
||||
ctx.pushRoute(
|
||||
AssetViewerRoute(
|
||||
initialIndex: assetIndex,
|
||||
|
Reference in New Issue
Block a user