feat: mobile editing

This commit is contained in:
bwees
2026-01-20 10:23:43 -06:00
parent f0cf3311d5
commit 6b6e203806
66 changed files with 12103 additions and 650 deletions

View File

@@ -22,6 +22,7 @@ import 'schema_v16.dart' as v16;
import 'schema_v17.dart' as v17;
import 'schema_v18.dart' as v18;
import 'schema_v19.dart' as v19;
import 'schema_v20.dart' as v20;
class GeneratedHelper implements SchemaInstantiationHelper {
@override
@@ -65,6 +66,8 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v18.DatabaseAtV18(db);
case 19:
return v19.DatabaseAtV19(db);
case 20:
return v20.DatabaseAtV20(db);
default:
throw MissingSchemaException(version, versions);
}
@@ -90,5 +93,6 @@ class GeneratedHelper implements SchemaInstantiationHelper {
17,
18,
19,
20,
];
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,321 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:immich_mobile/domain/models/asset_edit.model.dart';
import 'package:immich_mobile/utils/editor.utils.dart';
List<AssetEdit> normalizedToEdits(NormalizedTransform transform) {
List<AssetEdit> edits = [];
if (transform.mirrorHorizontal) {
edits.add(const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}));
}
if (transform.mirrorVertical) {
edits.add(const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}));
}
if (transform.rotation != 0) {
edits.add(AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": transform.rotation}));
}
return edits;
}
bool compareEditAffines(List<AssetEdit> editsA, List<AssetEdit> editsB) {
final normA = buildAffineFromEdits(editsA);
final normB = buildAffineFromEdits(editsB);
return ((normA.a - normB.a).abs() < 0.0001 &&
(normA.b - normB.b).abs() < 0.0001 &&
(normA.c - normB.c).abs() < 0.0001 &&
(normA.d - normB.d).abs() < 0.0001);
}
void main() {
group('normalizeEdits', () {
test('should handle no edits', () {
final edits = <AssetEdit>[];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle a single 90° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 90}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle a single 180° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 180}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle a single 270° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 270}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle a single horizontal mirror', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle a single vertical mirror', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle 90° rotation + horizontal mirror', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 90}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle 90° rotation + vertical mirror', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 90}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle 90° rotation + both mirrors', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 90}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle 180° rotation + horizontal mirror', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 180}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle 180° rotation + vertical mirror', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 180}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle 180° rotation + both mirrors', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 180}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle 270° rotation + horizontal mirror', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 270}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle 270° rotation + vertical mirror', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 270}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle 270° rotation + both mirrors', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 270}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle horizontal mirror + 90° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 90}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle horizontal mirror + 180° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 180}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle horizontal mirror + 270° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 270}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle vertical mirror + 90° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 90}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle vertical mirror + 180° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 180}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle vertical mirror + 270° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 270}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle both mirrors + 90° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 90}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle both mirrors + 180° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 180}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
test('should handle both mirrors + 270° rotation', () {
final edits = <AssetEdit>[
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "horizontal"}),
const AssetEdit(action: AssetEditAction.mirror, parameters: {"axis": "vertical"}),
const AssetEdit(action: AssetEditAction.rotate, parameters: {"angle": 270}),
];
final result = normalizeTransformEdits(edits);
final normalizedEdits = normalizedToEdits(result);
expect(compareEditAffines(normalizedEdits, edits), true);
});
});
}