Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mobile/drift_schemas/main/drift_schema_v22.json

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions mobile/lib/domain/models/asset_edit.model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import "package:openapi/api.dart" as api show AssetEditAction;

enum AssetEditAction { rotate, crop, mirror, other }

extension AssetEditActionExtension on AssetEditAction {
api.AssetEditAction? toDto() {
return switch (this) {
AssetEditAction.rotate => api.AssetEditAction.rotate,
AssetEditAction.crop => api.AssetEditAction.crop,
AssetEditAction.mirror => api.AssetEditAction.mirror,
AssetEditAction.other => null,
};
}
}

class AssetEdit {
final AssetEditAction action;
final Map<String, dynamic> parameters;

const AssetEdit({required this.action, required this.parameters});
}
56 changes: 32 additions & 24 deletions mobile/lib/domain/services/sync_stream.service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ class SyncStreamService {
return _syncStreamRepository.deleteAssetsV1(data.cast());
case SyncEntityType.assetExifV1:
return _syncStreamRepository.updateAssetsExifV1(data.cast());
case SyncEntityType.assetEditV1:
return _syncStreamRepository.updateAssetEditsV1(data.cast());
case SyncEntityType.assetEditDeleteV1:
return _syncStreamRepository.deleteAssetEditsV1(data.cast());
case SyncEntityType.assetMetadataV1:
return _syncStreamRepository.updateAssetsMetadataV1(data.cast());
case SyncEntityType.assetMetadataDeleteV1:
Expand Down Expand Up @@ -336,39 +340,43 @@ class SyncStreamService {
}
}

Future<void> handleWsAssetEditReadyV1Batch(List<dynamic> batchData) async {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This diff is really messy since I refactored it to not be a batch handler

if (batchData.isEmpty) return;

_logger.info('Processing batch of ${batchData.length} AssetEditReadyV1 events');

final List<SyncAssetV1> assets = [];
Future<void> handleWsAssetEditReadyV1(dynamic data) async {
_logger.info('Processing AssetEditReadyV1 event');

try {
for (final data in batchData) {
if (data is! Map<String, dynamic>) {
continue;
}

final payload = data;
final assetData = payload['asset'];
if (data is! Map<String, dynamic>) {
throw ArgumentError("Invalid data format for AssetEditReadyV1 event");
}

if (assetData == null) {
continue;
}
final payload = data;

final asset = SyncAssetV1.fromJson(assetData);
if (payload['asset'] == null) {
throw ArgumentError("Missing 'asset' field in AssetEditReadyV1 event data");
}

if (asset != null) {
assets.add(asset);
}
final asset = SyncAssetV1.fromJson(payload['asset']);
if (asset == null) {
throw ArgumentError("Failed to parse 'asset' field in AssetEditReadyV1 event data");
}

if (assets.isNotEmpty) {
await _syncStreamRepository.updateAssetsV1(assets, debugLabel: 'websocket-edit');
_logger.info('Successfully processed ${assets.length} edited assets');
List<SyncAssetEditV1> assetEdits = [];

// Edits are only send on v2.6.0+
if (payload['edit'] != null && payload['edit'] is List<dynamic>) {
assetEdits = (payload['edit'] as List<dynamic>)
.map((e) => SyncAssetEditV1.fromJson(e))
.whereType<SyncAssetEditV1>()
.toList();
}

await _syncStreamRepository.updateAssetsV1([asset], debugLabel: 'websocket-edit');
await _syncStreamRepository.replaceAssetEditsV1(asset.id, assetEdits, debugLabel: 'websocket-edit');

_logger.info(
'Successfully processed AssetEditReadyV1 event for asset ${asset.id} with ${assetEdits.length} edits',
);
} catch (error, stackTrace) {
_logger.severe("Error processing AssetEditReadyV1 websocket batch events", error, stackTrace);
_logger.severe("Error processing AssetEditReadyV1 websocket event", error, stackTrace);
}
}

Expand Down
8 changes: 4 additions & 4 deletions mobile/lib/domain/utils/background_sync.dart
Original file line number Diff line number Diff line change
Expand Up @@ -196,11 +196,11 @@ class BackgroundSyncManager {
});
}

Future<void> syncWebsocketEditBatch(List<dynamic> batchData) {
Future<void> syncWebsocketEdit(dynamic data) {
if (_syncWebsocketTask != null) {
return _syncWebsocketTask!.future;
}
_syncWebsocketTask = _handleWsAssetEditReadyV1Batch(batchData);
_syncWebsocketTask = _handleWsAssetEditReadyV1(data);
return _syncWebsocketTask!.whenComplete(() {
_syncWebsocketTask = null;
});
Expand Down Expand Up @@ -242,7 +242,7 @@ Cancelable<void> _handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) => ru
debugLabel: 'websocket-batch',
);

Cancelable<void> _handleWsAssetEditReadyV1Batch(List<dynamic> batchData) => runInIsolateGentle(
computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetEditReadyV1Batch(batchData),
Cancelable<void> _handleWsAssetEditReadyV1(dynamic data) => runInIsolateGentle(
computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetEditReadyV1(data),
debugLabel: 'websocket-edit',
);
33 changes: 33 additions & 0 deletions mobile/lib/infrastructure/entities/asset_edit.entity.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/asset_edit.model.dart';
import 'package:immich_mobile/infrastructure/entities/asset_edit.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';

@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_asset_edit_asset_id ON asset_edit_entity (asset_id)')
class AssetEditEntity extends Table with DriftDefaultsMixin {
const AssetEditEntity();

TextColumn get id => text()();

TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();

IntColumn get action => intEnum<AssetEditAction>()();

BlobColumn get parameters => blob().map(editParameterConverter)();

IntColumn get sequence => integer()();

@override
Set<Column> get primaryKey => {id};
}

final JsonTypeConverter2<Map<String, Object?>, Uint8List, Object?> editParameterConverter = TypeConverter.jsonb(
fromJson: (json) => json as Map<String, Object?>,
);

extension AssetEditEntityDataDomainEx on AssetEditEntityData {
AssetEdit toDto() {
return AssetEdit(action: action, parameters: parameters);
}
}
Loading
Loading