mirror of
https://github.com/samsonjs/immich.git
synced 2026-03-25 09:15:56 +00:00
chore: bump dart sdk to 3.8 (#20355)
* chore: bump dart sdk to 3.8 * chore: make build * make pigeon * chore: format files --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
parent
9b3718120b
commit
e52b9d15b5
643 changed files with 6519 additions and 18431 deletions
|
|
@ -1,17 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
enum ImmichColorPreset {
|
||||
indigo,
|
||||
deepPurple,
|
||||
pink,
|
||||
red,
|
||||
orange,
|
||||
yellow,
|
||||
lime,
|
||||
green,
|
||||
cyan,
|
||||
slateGray,
|
||||
}
|
||||
enum ImmichColorPreset { indigo, deepPurple, pink, red, orange, yellow, lime, green, cyan, slateGray }
|
||||
|
||||
const ImmichColorPreset defaultColorPreset = ImmichColorPreset.indigo;
|
||||
const String defaultColorPresetName = "indigo";
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
enum SortOrder {
|
||||
asc,
|
||||
desc,
|
||||
}
|
||||
enum SortOrder { asc, desc }
|
||||
|
||||
enum TextSearchType {
|
||||
context,
|
||||
filename,
|
||||
description,
|
||||
}
|
||||
enum TextSearchType { context, filename, description }
|
||||
|
||||
enum AssetVisibilityEnum { timeline, hidden, archive, locked }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,511 +2,49 @@ import 'package:flutter/material.dart';
|
|||
|
||||
const List<ColorFilter> filters = [
|
||||
//Original
|
||||
ColorFilter.matrix([
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Vintage
|
||||
ColorFilter.matrix([
|
||||
0.8,
|
||||
0.1,
|
||||
0.1,
|
||||
0,
|
||||
20,
|
||||
0.1,
|
||||
0.8,
|
||||
0.1,
|
||||
0,
|
||||
20,
|
||||
0.1,
|
||||
0.1,
|
||||
0.8,
|
||||
0,
|
||||
20,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([0.8, 0.1, 0.1, 0, 20, 0.1, 0.8, 0.1, 0, 20, 0.1, 0.1, 0.8, 0, 20, 0, 0, 0, 1, 0]),
|
||||
//Mood
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0,
|
||||
10,
|
||||
0.1,
|
||||
1,
|
||||
0.1,
|
||||
0,
|
||||
10,
|
||||
0.1,
|
||||
0.1,
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.2, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 10, 0, 0, 0, 1, 0]),
|
||||
//Crisp
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Cool
|
||||
ColorFilter.matrix([
|
||||
0.9,
|
||||
0,
|
||||
0.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
0,
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([0.9, 0, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Blush
|
||||
ColorFilter.matrix([
|
||||
1.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0,
|
||||
10,
|
||||
0.1,
|
||||
1,
|
||||
0.1,
|
||||
0,
|
||||
10,
|
||||
0.1,
|
||||
0.1,
|
||||
1,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.1, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 5, 0, 0, 0, 1, 0]),
|
||||
//Sunkissed
|
||||
ColorFilter.matrix([
|
||||
1.3,
|
||||
0,
|
||||
0.1,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
1.1,
|
||||
0.1,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0.9,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.3, 0, 0.1, 0, 15, 0, 1.1, 0.1, 0, 10, 0, 0, 0.9, 0, 5, 0, 0, 0, 1, 0]),
|
||||
//Fresh
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
20,
|
||||
0,
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
20,
|
||||
0,
|
||||
0,
|
||||
1.1,
|
||||
0,
|
||||
20,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.2, 0, 0, 0, 20, 0, 1.2, 0, 0, 20, 0, 0, 1.1, 0, 20, 0, 0, 0, 1, 0]),
|
||||
//Classic
|
||||
ColorFilter.matrix([
|
||||
1.1,
|
||||
0,
|
||||
-0.1,
|
||||
0,
|
||||
10,
|
||||
-0.1,
|
||||
1.1,
|
||||
0.1,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
-0.1,
|
||||
1.1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.1, 0, -0.1, 0, 10, -0.1, 1.1, 0.1, 0, 5, 0, -0.1, 1.1, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Lomo-ish
|
||||
ColorFilter.matrix([
|
||||
1.5,
|
||||
0,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1.45,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
0,
|
||||
1.3,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.5, 0, 0.1, 0, 0, 0, 1.45, 0, 0, 0, 0.1, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Nashville
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0.15,
|
||||
-0.15,
|
||||
0,
|
||||
15,
|
||||
0.1,
|
||||
1.1,
|
||||
0.1,
|
||||
0,
|
||||
10,
|
||||
-0.05,
|
||||
0.2,
|
||||
1.25,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.2, 0.15, -0.15, 0, 15, 0.1, 1.1, 0.1, 0, 10, -0.05, 0.2, 1.25, 0, 5, 0, 0, 0, 1, 0]),
|
||||
//Valencia
|
||||
ColorFilter.matrix([
|
||||
1.15,
|
||||
0.1,
|
||||
0.1,
|
||||
0,
|
||||
20,
|
||||
0.1,
|
||||
1.1,
|
||||
0,
|
||||
0,
|
||||
10,
|
||||
0.1,
|
||||
0.1,
|
||||
1.2,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.15, 0.1, 0.1, 0, 20, 0.1, 1.1, 0, 0, 10, 0.1, 0.1, 1.2, 0, 5, 0, 0, 0, 1, 0]),
|
||||
//Clarendon
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
1.25,
|
||||
0,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
1.3,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.2, 0, 0, 0, 10, 0, 1.25, 0, 0, 10, 0, 0, 1.3, 0, 10, 0, 0, 0, 1, 0]),
|
||||
//Moon
|
||||
ColorFilter.matrix([
|
||||
0.33,
|
||||
0.33,
|
||||
0.33,
|
||||
0,
|
||||
0,
|
||||
0.33,
|
||||
0.33,
|
||||
0.33,
|
||||
0,
|
||||
0,
|
||||
0.33,
|
||||
0.33,
|
||||
0.33,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Willow
|
||||
ColorFilter.matrix([
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0,
|
||||
20,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0,
|
||||
20,
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
0,
|
||||
20,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0, 0, 0, 1, 0]),
|
||||
//Kodak
|
||||
ColorFilter.matrix([
|
||||
1.3,
|
||||
0.1,
|
||||
-0.1,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
1.25,
|
||||
0.1,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
-0.1,
|
||||
1.1,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.3, 0.1, -0.1, 0, 10, 0, 1.25, 0.1, 0, 10, 0, -0.1, 1.1, 0, 5, 0, 0, 0, 1, 0]),
|
||||
//Frost
|
||||
ColorFilter.matrix([
|
||||
0.8,
|
||||
0.2,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
0.2,
|
||||
1.1,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
0.1,
|
||||
1.2,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([0.8, 0.2, 0.1, 0, 0, 0.2, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.2, 0, 10, 0, 0, 0, 1, 0]),
|
||||
//Night Vision
|
||||
ColorFilter.matrix([
|
||||
0.1,
|
||||
0.95,
|
||||
0.2,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
1.5,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
0.2,
|
||||
0.7,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([0.1, 0.95, 0.2, 0, 0, 0.1, 1.5, 0.1, 0, 0, 0.2, 0.7, 0, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Sunset
|
||||
ColorFilter.matrix([
|
||||
1.5,
|
||||
0.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
0.9,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
-0.1,
|
||||
-0.2,
|
||||
1.3,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.5, 0.2, 0, 0, 0, 0.1, 0.9, 0.1, 0, 0, -0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Noir
|
||||
ColorFilter.matrix([
|
||||
1.3,
|
||||
-0.3,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
-0.1,
|
||||
1.2,
|
||||
-0.1,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
-0.2,
|
||||
1.3,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.3, -0.3, 0.1, 0, 0, -0.1, 1.2, -0.1, 0, 0, 0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Dreamy
|
||||
ColorFilter.matrix([
|
||||
1.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
1.1,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
0.1,
|
||||
1.1,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.1, 0.1, 0.1, 0, 0, 0.1, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.1, 0, 15, 0, 0, 0, 1, 0]),
|
||||
//Sepia
|
||||
ColorFilter.matrix([
|
||||
0.393,
|
||||
0.769,
|
||||
0.189,
|
||||
0,
|
||||
0,
|
||||
0.349,
|
||||
0.686,
|
||||
0.168,
|
||||
0,
|
||||
0,
|
||||
0.272,
|
||||
0.534,
|
||||
0.131,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([0.393, 0.769, 0.189, 0, 0, 0.349, 0.686, 0.168, 0, 0, 0.272, 0.534, 0.131, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Radium
|
||||
ColorFilter.matrix([
|
||||
1.438,
|
||||
|
|
@ -554,212 +92,23 @@ const List<ColorFilter> filters = [
|
|||
0,
|
||||
]),
|
||||
//Purple Haze
|
||||
ColorFilter.matrix([
|
||||
1.3,
|
||||
0,
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1.1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.2,
|
||||
0,
|
||||
1.3,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.3, 0, 1.2, 0, 0, 0, 1.1, 0, 0, 0, 0.2, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Lemonade
|
||||
ColorFilter.matrix([
|
||||
1.2,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1.1,
|
||||
0.2,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
0,
|
||||
0.7,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.2, 0.1, 0, 0, 0, 0, 1.1, 0.2, 0, 0, 0.1, 0, 0.7, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Caramel
|
||||
ColorFilter.matrix([
|
||||
1.6,
|
||||
0.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
1.3,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
0.9,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.6, 0.2, 0, 0, 0, 0.1, 1.3, 0.1, 0, 0, 0, 0.1, 0.9, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Peachy
|
||||
ColorFilter.matrix([
|
||||
1.3,
|
||||
0.5,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.2,
|
||||
1.1,
|
||||
0.3,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
0.1,
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.3, 0.5, 0, 0, 0, 0.2, 1.1, 0.3, 0, 0, 0.1, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Neon
|
||||
ColorFilter.matrix([
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Cold Morning
|
||||
ColorFilter.matrix([
|
||||
0.9,
|
||||
0.1,
|
||||
0.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0.1,
|
||||
0,
|
||||
0,
|
||||
0.1,
|
||||
0,
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([0.9, 0.1, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Lush
|
||||
ColorFilter.matrix([
|
||||
0.9,
|
||||
0.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1.1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([0.9, 0.2, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.1, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Urban Neon
|
||||
ColorFilter.matrix([
|
||||
1.1,
|
||||
0,
|
||||
0.3,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.9,
|
||||
0.3,
|
||||
0,
|
||||
0,
|
||||
0.3,
|
||||
0.1,
|
||||
1.2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([1.1, 0, 0.3, 0, 0, 0, 0.9, 0.3, 0, 0, 0.3, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]),
|
||||
//Monochrome
|
||||
ColorFilter.matrix([
|
||||
0.6,
|
||||
0.2,
|
||||
0.2,
|
||||
0,
|
||||
0,
|
||||
0.2,
|
||||
0.6,
|
||||
0.2,
|
||||
0,
|
||||
0,
|
||||
0.2,
|
||||
0.2,
|
||||
0.7,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
]),
|
||||
ColorFilter.matrix([0.6, 0.2, 0.2, 0, 0, 0.2, 0.6, 0.2, 0, 0, 0.2, 0.2, 0.7, 0, 0, 0, 0, 0, 1, 0]),
|
||||
];
|
||||
|
||||
const List<String> filterNames = [
|
||||
|
|
|
|||
|
|
@ -51,7 +51,4 @@ const Map<String, Locale> locales = {
|
|||
|
||||
const String translationsPath = 'assets/i18n';
|
||||
|
||||
const List<Locale> localesNotSupportedByOverpass = [
|
||||
Locale('el', 'GR'),
|
||||
Locale('sr', 'Cyrl'),
|
||||
];
|
||||
const List<Locale> localesNotSupportedByOverpass = [Locale('el', 'GR'), Locale('sr', 'Cyrl')];
|
||||
|
|
|
|||
|
|
@ -9,11 +9,7 @@ enum AssetType {
|
|||
audio,
|
||||
}
|
||||
|
||||
enum AssetState {
|
||||
local,
|
||||
remote,
|
||||
merged,
|
||||
}
|
||||
enum AssetState { local, remote, merged }
|
||||
|
||||
sealed class BaseAsset {
|
||||
final String name;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
part of 'base_asset.model.dart';
|
||||
|
||||
enum AssetVisibility {
|
||||
timeline,
|
||||
hidden,
|
||||
archive,
|
||||
locked,
|
||||
}
|
||||
enum AssetVisibility { timeline, hidden, archive, locked }
|
||||
|
||||
// Model for an asset stored in the server
|
||||
class RemoteAsset extends BaseAsset {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,7 @@ class DeviceAsset {
|
|||
final Uint8List hash;
|
||||
final DateTime modifiedTime;
|
||||
|
||||
const DeviceAsset({
|
||||
required this.assetId,
|
||||
required this.hash,
|
||||
required this.modifiedTime,
|
||||
});
|
||||
const DeviceAsset({required this.assetId, required this.hash, required this.modifiedTime});
|
||||
|
||||
@override
|
||||
bool operator ==(covariant DeviceAsset other) {
|
||||
|
|
@ -28,11 +24,7 @@ class DeviceAsset {
|
|||
return 'DeviceAsset(assetId: $assetId, hash: $hash, modifiedTime: $modifiedTime)';
|
||||
}
|
||||
|
||||
DeviceAsset copyWith({
|
||||
String? assetId,
|
||||
Uint8List? hash,
|
||||
DateTime? modifiedTime,
|
||||
}) {
|
||||
DeviceAsset copyWith({String? assetId, Uint8List? hash, DateTime? modifiedTime}) {
|
||||
return DeviceAsset(
|
||||
assetId: assetId ?? this.assetId,
|
||||
hash: hash ?? this.hash,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,5 @@
|
|||
/// Log levels according to dart logging [Level]
|
||||
enum LogLevel {
|
||||
all,
|
||||
finest,
|
||||
finer,
|
||||
fine,
|
||||
config,
|
||||
info,
|
||||
warning,
|
||||
severe,
|
||||
shout,
|
||||
off,
|
||||
}
|
||||
enum LogLevel { all, finest, finer, fine, config, info, warning, severe, shout, off }
|
||||
|
||||
class LogMessage {
|
||||
final String message;
|
||||
|
|
|
|||
|
|
@ -13,28 +13,18 @@ enum MemoryTypeEnum {
|
|||
class MemoryData {
|
||||
final int year;
|
||||
|
||||
const MemoryData({
|
||||
required this.year,
|
||||
});
|
||||
const MemoryData({required this.year});
|
||||
|
||||
MemoryData copyWith({
|
||||
int? year,
|
||||
}) {
|
||||
return MemoryData(
|
||||
year: year ?? this.year,
|
||||
);
|
||||
MemoryData copyWith({int? year}) {
|
||||
return MemoryData(year: year ?? this.year);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'year': year,
|
||||
};
|
||||
return <String, dynamic>{'year': year};
|
||||
}
|
||||
|
||||
factory MemoryData.fromMap(Map<String, dynamic> map) {
|
||||
return MemoryData(
|
||||
year: map['year'] as int,
|
||||
);
|
||||
return MemoryData(year: map['year'] as int);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
|
|
|||
|
|
@ -5,21 +5,12 @@ class SearchResult {
|
|||
final List<BaseAsset> assets;
|
||||
final int? nextPage;
|
||||
|
||||
const SearchResult({
|
||||
required this.assets,
|
||||
this.nextPage,
|
||||
});
|
||||
const SearchResult({required this.assets, this.nextPage});
|
||||
|
||||
int get totalAssets => assets.length;
|
||||
|
||||
SearchResult copyWith({
|
||||
List<BaseAsset>? assets,
|
||||
int? nextPage,
|
||||
}) {
|
||||
return SearchResult(
|
||||
assets: assets ?? this.assets,
|
||||
nextPage: nextPage ?? this.nextPage,
|
||||
);
|
||||
SearchResult copyWith({List<BaseAsset>? assets, int? nextPage}) {
|
||||
return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ enum Setting<T> {
|
|||
loadOriginalVideo<bool>(StoreKey.loadOriginalVideo, false),
|
||||
preferRemoteImage<bool>(StoreKey.preferRemoteImage, false),
|
||||
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false),
|
||||
enableBackup<bool>(StoreKey.enableBackup, false),
|
||||
;
|
||||
enableBackup<bool>(StoreKey.enableBackup, false);
|
||||
|
||||
const Setting(this.storeKey, this.defaultValue);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,13 +14,7 @@ class Stack {
|
|||
required this.primaryAssetId,
|
||||
});
|
||||
|
||||
Stack copyWith({
|
||||
String? id,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
String? ownerId,
|
||||
String? primaryAssetId,
|
||||
}) {
|
||||
Stack copyWith({String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) {
|
||||
return Stack(
|
||||
id: id ?? this.id,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
|
|
@ -63,11 +57,7 @@ class StackResponse {
|
|||
final String primaryAssetId;
|
||||
final List<String> assetIds;
|
||||
|
||||
const StackResponse({
|
||||
required this.id,
|
||||
required this.primaryAssetId,
|
||||
required this.assetIds,
|
||||
});
|
||||
const StackResponse({required this.id, required this.primaryAssetId, required this.assetIds});
|
||||
|
||||
@override
|
||||
bool operator ==(covariant StackResponse other) {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,8 @@
|
|||
import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||
|
||||
enum GroupAssetsBy {
|
||||
day,
|
||||
month,
|
||||
auto,
|
||||
none;
|
||||
}
|
||||
enum GroupAssetsBy { day, month, auto, none }
|
||||
|
||||
enum HeaderType {
|
||||
none,
|
||||
month,
|
||||
day,
|
||||
monthAndDay;
|
||||
}
|
||||
enum HeaderType { none, month, day, monthAndDay }
|
||||
|
||||
class Bucket {
|
||||
final int assetCount;
|
||||
|
|
|
|||
|
|
@ -74,22 +74,21 @@ quotaSizeInBytes: $quotaSizeInBytes,
|
|||
bool? isPartnerSharedWith,
|
||||
int? quotaUsageInBytes,
|
||||
int? quotaSizeInBytes,
|
||||
}) =>
|
||||
UserDto(
|
||||
id: id ?? this.id,
|
||||
email: email ?? this.email,
|
||||
name: name ?? this.name,
|
||||
isAdmin: isAdmin ?? this.isAdmin,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
profileImagePath: profileImagePath ?? this.profileImagePath,
|
||||
avatarColor: avatarColor ?? this.avatarColor,
|
||||
memoryEnabled: memoryEnabled ?? this.memoryEnabled,
|
||||
inTimeline: inTimeline ?? this.inTimeline,
|
||||
isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy,
|
||||
isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith,
|
||||
quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes,
|
||||
quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes,
|
||||
);
|
||||
}) => UserDto(
|
||||
id: id ?? this.id,
|
||||
email: email ?? this.email,
|
||||
name: name ?? this.name,
|
||||
isAdmin: isAdmin ?? this.isAdmin,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
profileImagePath: profileImagePath ?? this.profileImagePath,
|
||||
avatarColor: avatarColor ?? this.avatarColor,
|
||||
memoryEnabled: memoryEnabled ?? this.memoryEnabled,
|
||||
inTimeline: inTimeline ?? this.inTimeline,
|
||||
isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy,
|
||||
isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith,
|
||||
quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes,
|
||||
quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(covariant UserDto other) {
|
||||
|
|
@ -143,13 +142,7 @@ class PartnerUserDto {
|
|||
this.profileImagePath,
|
||||
});
|
||||
|
||||
PartnerUserDto copyWith({
|
||||
String? id,
|
||||
String? email,
|
||||
String? name,
|
||||
bool? inTimeline,
|
||||
String? profileImagePath,
|
||||
}) {
|
||||
PartnerUserDto copyWith({String? id, String? email, String? name, bool? inTimeline, String? profileImagePath}) {
|
||||
return PartnerUserDto(
|
||||
id: id ?? this.id,
|
||||
email: email ?? this.email,
|
||||
|
|
|
|||
|
|
@ -24,17 +24,17 @@ enum AvatarColor {
|
|||
const AvatarColor(this.value);
|
||||
|
||||
Color toColor({bool isDarkTheme = false}) => switch (this) {
|
||||
AvatarColor.primary => isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF),
|
||||
AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182),
|
||||
AvatarColor.red => const Color.fromARGB(255, 239, 68, 68),
|
||||
AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8),
|
||||
AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246),
|
||||
AvatarColor.green => const Color.fromARGB(255, 22, 163, 74),
|
||||
AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234),
|
||||
AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12),
|
||||
AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99),
|
||||
AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6),
|
||||
};
|
||||
AvatarColor.primary => isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF),
|
||||
AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182),
|
||||
AvatarColor.red => const Color.fromARGB(255, 239, 68, 68),
|
||||
AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8),
|
||||
AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246),
|
||||
AvatarColor.green => const Color.fromARGB(255, 22, 163, 74),
|
||||
AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234),
|
||||
AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12),
|
||||
AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99),
|
||||
AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6),
|
||||
};
|
||||
}
|
||||
|
||||
class Onboarding {
|
||||
|
|
@ -193,17 +193,9 @@ class License {
|
|||
final String activationKey;
|
||||
final String licenseKey;
|
||||
|
||||
const License({
|
||||
required this.activatedAt,
|
||||
required this.activationKey,
|
||||
required this.licenseKey,
|
||||
});
|
||||
const License({required this.activatedAt, required this.activationKey, required this.licenseKey});
|
||||
|
||||
License copyWith({
|
||||
DateTime? activatedAt,
|
||||
String? activationKey,
|
||||
String? licenseKey,
|
||||
}) {
|
||||
License copyWith({DateTime? activatedAt, String? activationKey, String? licenseKey}) {
|
||||
return License(
|
||||
activatedAt: activatedAt ?? this.activatedAt,
|
||||
activationKey: activationKey ?? this.activationKey,
|
||||
|
|
@ -255,16 +247,11 @@ class UserMetadata {
|
|||
final Preferences? preferences;
|
||||
final License? license;
|
||||
|
||||
const UserMetadata({
|
||||
required this.userId,
|
||||
required this.key,
|
||||
this.onboarding,
|
||||
this.preferences,
|
||||
this.license,
|
||||
}) : assert(
|
||||
onboarding != null || preferences != null || license != null,
|
||||
'One of onboarding, preferences and license must be provided',
|
||||
);
|
||||
const UserMetadata({required this.userId, required this.key, this.onboarding, this.preferences, this.license})
|
||||
: assert(
|
||||
onboarding != null || preferences != null || license != null,
|
||||
'One of onboarding, preferences and license must be provided',
|
||||
);
|
||||
|
||||
UserMetadata copyWith({
|
||||
String? userId,
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ class AssetService {
|
|||
const AssetService({
|
||||
required RemoteAssetRepository remoteAssetRepository,
|
||||
required DriftLocalAssetRepository localAssetRepository,
|
||||
}) : _remoteAssetRepository = remoteAssetRepository,
|
||||
_localAssetRepository = localAssetRepository,
|
||||
_platform = const LocalPlatform();
|
||||
}) : _remoteAssetRepository = remoteAssetRepository,
|
||||
_localAssetRepository = localAssetRepository,
|
||||
_platform = const LocalPlatform();
|
||||
|
||||
Stream<BaseAsset?> watchAsset(BaseAsset asset) {
|
||||
final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).id;
|
||||
|
|
|
|||
|
|
@ -25,19 +25,16 @@ class HashService {
|
|||
required NativeSyncApi nativeSyncApi,
|
||||
this.batchSizeLimit = kBatchHashSizeLimit,
|
||||
this.batchFileLimit = kBatchHashFileLimit,
|
||||
}) : _localAlbumRepository = localAlbumRepository,
|
||||
_localAssetRepository = localAssetRepository,
|
||||
_storageRepository = storageRepository,
|
||||
_nativeSyncApi = nativeSyncApi;
|
||||
}) : _localAlbumRepository = localAlbumRepository,
|
||||
_localAssetRepository = localAssetRepository,
|
||||
_storageRepository = storageRepository,
|
||||
_nativeSyncApi = nativeSyncApi;
|
||||
|
||||
Future<void> hashAssets() async {
|
||||
final Stopwatch stopwatch = Stopwatch()..start();
|
||||
// Sorted by backupSelection followed by isCloud
|
||||
final localAlbums = await _localAlbumRepository.getAll(
|
||||
sortBy: {
|
||||
SortLocalAlbumsBy.backupSelection,
|
||||
SortLocalAlbumsBy.isIosSharedAlbum,
|
||||
},
|
||||
sortBy: {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum},
|
||||
);
|
||||
|
||||
for (final album in localAlbums) {
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ class LocalSyncService {
|
|||
required DriftLocalAlbumRepository localAlbumRepository,
|
||||
required NativeSyncApi nativeSyncApi,
|
||||
Platform? platform,
|
||||
}) : _localAlbumRepository = localAlbumRepository,
|
||||
_nativeSyncApi = nativeSyncApi,
|
||||
_platform = platform ?? const LocalPlatform();
|
||||
}) : _localAlbumRepository = localAlbumRepository,
|
||||
_nativeSyncApi = nativeSyncApi,
|
||||
_platform = platform ?? const LocalPlatform();
|
||||
|
||||
Future<void> sync({bool full = false}) async {
|
||||
final Stopwatch stopwatch = Stopwatch()..start();
|
||||
|
|
@ -70,9 +70,7 @@ class LocalSyncService {
|
|||
for (final album in cloudAlbums) {
|
||||
final dbAlbum = dbAlbums.firstWhereOrNull((a) => a.id == album.id);
|
||||
if (dbAlbum == null) {
|
||||
_log.warning(
|
||||
"Cloud album ${album.name} not found in local database. Skipping sync.",
|
||||
);
|
||||
_log.warning("Cloud album ${album.name} not found in local database. Skipping sync.");
|
||||
continue;
|
||||
}
|
||||
await updateAlbum(dbAlbum, album);
|
||||
|
|
@ -120,10 +118,7 @@ class LocalSyncService {
|
|||
|
||||
final assets = album.assetCount > 0 ? await _nativeSyncApi.getAssetsForAlbum(album.id) : <PlatformAsset>[];
|
||||
|
||||
await _localAlbumRepository.upsert(
|
||||
album,
|
||||
toUpsert: assets.toLocalAssets(),
|
||||
);
|
||||
await _localAlbumRepository.upsert(album, toUpsert: assets.toLocalAssets());
|
||||
_log.fine("Successfully added device album ${album.name}");
|
||||
} catch (e, s) {
|
||||
_log.warning("Error while adding device album", e, s);
|
||||
|
|
@ -146,9 +141,7 @@ class LocalSyncService {
|
|||
_log.fine("Syncing device album ${dbAlbum.name}");
|
||||
|
||||
if (_albumsEqual(deviceAlbum, dbAlbum)) {
|
||||
_log.fine(
|
||||
"Device album ${dbAlbum.name} has not changed. Skipping sync.",
|
||||
);
|
||||
_log.fine("Device album ${dbAlbum.name} has not changed. Skipping sync.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -172,10 +165,7 @@ class LocalSyncService {
|
|||
@visibleForTesting
|
||||
// The [deviceAlbum] is expected to be refreshed before calling this method
|
||||
// with modified time and asset count
|
||||
Future<bool> checkAddition(
|
||||
LocalAlbum dbAlbum,
|
||||
LocalAlbum deviceAlbum,
|
||||
) async {
|
||||
Future<bool> checkAddition(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async {
|
||||
try {
|
||||
_log.fine("Fast syncing device album ${dbAlbum.name}");
|
||||
// Assets has been modified
|
||||
|
|
@ -189,9 +179,7 @@ class LocalSyncService {
|
|||
|
||||
// Early return if no new assets were found
|
||||
if (newAssetsCount == 0) {
|
||||
_log.fine(
|
||||
"No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}",
|
||||
);
|
||||
_log.fine("No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -201,10 +189,7 @@ class LocalSyncService {
|
|||
return false;
|
||||
}
|
||||
|
||||
final newAssets = await _nativeSyncApi.getAssetsForAlbum(
|
||||
deviceAlbum.id,
|
||||
updatedTimeCond: updatedTime,
|
||||
);
|
||||
final newAssets = await _nativeSyncApi.getAssetsForAlbum(deviceAlbum.id, updatedTimeCond: updatedTime);
|
||||
|
||||
await _localAlbumRepository.upsert(
|
||||
deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection),
|
||||
|
|
@ -229,9 +214,7 @@ class LocalSyncService {
|
|||
final assetsInDb = dbAlbum.assetCount > 0 ? await _localAlbumRepository.getAssets(dbAlbum.id) : <LocalAsset>[];
|
||||
|
||||
if (deviceAlbum.assetCount == 0) {
|
||||
_log.fine(
|
||||
"Device album ${deviceAlbum.name} is empty. Removing assets from DB.",
|
||||
);
|
||||
_log.fine("Device album ${deviceAlbum.name} is empty. Removing assets from DB.");
|
||||
await _localAlbumRepository.upsert(
|
||||
deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection),
|
||||
toDelete: assetsInDb.map((a) => a.id),
|
||||
|
|
@ -239,18 +222,11 @@ class LocalSyncService {
|
|||
return true;
|
||||
}
|
||||
|
||||
final updatedDeviceAlbum = deviceAlbum.copyWith(
|
||||
backupSelection: dbAlbum.backupSelection,
|
||||
);
|
||||
final updatedDeviceAlbum = deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection);
|
||||
|
||||
if (dbAlbum.assetCount == 0) {
|
||||
_log.fine(
|
||||
"Device album ${deviceAlbum.name} is empty. Adding assets to DB.",
|
||||
);
|
||||
await _localAlbumRepository.upsert(
|
||||
updatedDeviceAlbum,
|
||||
toUpsert: assetsInDevice,
|
||||
);
|
||||
_log.fine("Device album ${deviceAlbum.name} is empty. Adding assets to DB.");
|
||||
await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsInDevice);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -282,18 +258,12 @@ class LocalSyncService {
|
|||
);
|
||||
|
||||
if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) {
|
||||
_log.fine(
|
||||
"No asset changes detected in album ${deviceAlbum.name}. Updating metadata.",
|
||||
);
|
||||
_log.fine("No asset changes detected in album ${deviceAlbum.name}. Updating metadata.");
|
||||
_localAlbumRepository.upsert(updatedDeviceAlbum);
|
||||
return true;
|
||||
}
|
||||
|
||||
await _localAlbumRepository.upsert(
|
||||
updatedDeviceAlbum,
|
||||
toUpsert: assetsToUpsert,
|
||||
toDelete: assetsToDelete,
|
||||
);
|
||||
await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsToUpsert, toDelete: assetsToDelete);
|
||||
|
||||
return true;
|
||||
} catch (e, s) {
|
||||
|
|
|
|||
|
|
@ -61,11 +61,7 @@ class LogService {
|
|||
return instance;
|
||||
}
|
||||
|
||||
LogService._(
|
||||
this._logRepository,
|
||||
this._storeRepository,
|
||||
this._shouldBuffer,
|
||||
) {
|
||||
LogService._(this._logRepository, this._storeRepository, this._shouldBuffer) {
|
||||
_logSubscription = Logger.root.onRecord.listen(_handleLogRecord);
|
||||
}
|
||||
|
||||
|
|
@ -89,10 +85,7 @@ class LogService {
|
|||
|
||||
if (_shouldBuffer) {
|
||||
_msgBuffer.add(record);
|
||||
_flushTimer ??= Timer(
|
||||
const Duration(seconds: 5),
|
||||
() => unawaited(flushBuffer()),
|
||||
);
|
||||
_flushTimer ??= Timer(const Duration(seconds: 5), () => unawaited(flushBuffer()));
|
||||
} else {
|
||||
unawaited(_logRepository.insert(record));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@ class DriftPartnerService {
|
|||
final DriftPartnerRepository _driftPartnerRepository;
|
||||
final PartnerApiRepository _partnerApiRepository;
|
||||
|
||||
const DriftPartnerService(
|
||||
this._driftPartnerRepository,
|
||||
this._partnerApiRepository,
|
||||
);
|
||||
const DriftPartnerService(this._driftPartnerRepository, this._partnerApiRepository);
|
||||
|
||||
Future<List<PartnerUserDto>> getSharedWith(String userId) {
|
||||
return _driftPartnerRepository.getSharedWith(userId);
|
||||
|
|
@ -20,9 +17,7 @@ class DriftPartnerService {
|
|||
return _driftPartnerRepository.getSharedBy(userId);
|
||||
}
|
||||
|
||||
Future<List<PartnerUserDto>> getAvailablePartners(
|
||||
String currentUserId,
|
||||
) async {
|
||||
Future<List<PartnerUserDto>> getAvailablePartners(String currentUserId) async {
|
||||
final otherUsers = await _driftPartnerRepository.getAvailablePartners(currentUserId);
|
||||
final currentPartners = await _driftPartnerRepository.getSharedBy(currentUserId);
|
||||
final available = otherUsers.where((user) {
|
||||
|
|
@ -39,10 +34,7 @@ class DriftPartnerService {
|
|||
return;
|
||||
}
|
||||
|
||||
await _partnerApiRepository.update(
|
||||
partnerId,
|
||||
inTimeline: !partner.inTimeline,
|
||||
);
|
||||
await _partnerApiRepository.update(partnerId, inTimeline: !partner.inTimeline);
|
||||
|
||||
await _driftPartnerRepository.toggleShowInTimeline(partner, userId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,11 +26,7 @@ class RemoteAlbumService {
|
|||
return _repository.get(albumId);
|
||||
}
|
||||
|
||||
List<RemoteAlbum> sortAlbums(
|
||||
List<RemoteAlbum> albums,
|
||||
RemoteAlbumSortMode sortMode, {
|
||||
bool isReverse = false,
|
||||
}) {
|
||||
List<RemoteAlbum> sortAlbums(List<RemoteAlbum> albums, RemoteAlbumSortMode sortMode, {bool isReverse = false}) {
|
||||
return sortMode.sortFn(albums, isReverse);
|
||||
}
|
||||
|
||||
|
|
@ -69,16 +65,8 @@ class RemoteAlbumService {
|
|||
return filtered;
|
||||
}
|
||||
|
||||
Future<RemoteAlbum> createAlbum({
|
||||
required String title,
|
||||
required List<String> assetIds,
|
||||
String? description,
|
||||
}) async {
|
||||
final album = await _albumApiRepository.createDriftAlbum(
|
||||
title,
|
||||
description: description,
|
||||
assetIds: assetIds,
|
||||
);
|
||||
Future<RemoteAlbum> createAlbum({required String title, required List<String> assetIds, String? description}) async {
|
||||
final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds);
|
||||
|
||||
await _repository.create(album, assetIds);
|
||||
|
||||
|
|
@ -120,14 +108,8 @@ class RemoteAlbumService {
|
|||
return _repository.getAssets(albumId);
|
||||
}
|
||||
|
||||
Future<int> addAssets({
|
||||
required String albumId,
|
||||
required List<String> assetIds,
|
||||
}) async {
|
||||
final album = await _albumApiRepository.addAssets(
|
||||
albumId,
|
||||
assetIds,
|
||||
);
|
||||
Future<int> addAssets({required String albumId, required List<String> assetIds}) async {
|
||||
final album = await _albumApiRepository.addAssets(albumId, assetIds);
|
||||
|
||||
await _repository.addAssets(albumId, album.added);
|
||||
|
||||
|
|
@ -140,10 +122,7 @@ class RemoteAlbumService {
|
|||
await _repository.deleteAlbum(albumId);
|
||||
}
|
||||
|
||||
Future<void> addUsers({
|
||||
required String albumId,
|
||||
required List<String> userIds,
|
||||
}) async {
|
||||
Future<void> addUsers({required String albumId, required List<String> userIds}) async {
|
||||
await _albumApiRepository.addUsers(albumId, userIds);
|
||||
|
||||
return _repository.addUsers(albumId, userIds);
|
||||
|
|
|
|||
|
|
@ -83,10 +83,10 @@ extension on AssetResponseDto {
|
|||
|
||||
extension on AssetTypeEnum {
|
||||
AssetType toAssetType() => switch (this) {
|
||||
AssetTypeEnum.IMAGE => AssetType.image,
|
||||
AssetTypeEnum.VIDEO => AssetType.video,
|
||||
AssetTypeEnum.AUDIO => AssetType.audio,
|
||||
AssetTypeEnum.OTHER => AssetType.other,
|
||||
_ => throw Exception('Unknown AssetType value: $this'),
|
||||
};
|
||||
AssetTypeEnum.IMAGE => AssetType.image,
|
||||
AssetTypeEnum.VIDEO => AssetType.video,
|
||||
AssetTypeEnum.AUDIO => AssetType.audio,
|
||||
AssetTypeEnum.OTHER => AssetType.other,
|
||||
_ => throw Exception('Unknown AssetType value: $this'),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,16 +24,12 @@ class StoreService {
|
|||
}
|
||||
|
||||
// TODO: Replace the implementation with the one from create after removing the typedef
|
||||
static Future<StoreService> init({
|
||||
required IsarStoreRepository storeRepository,
|
||||
}) async {
|
||||
static Future<StoreService> init({required IsarStoreRepository storeRepository}) async {
|
||||
_instance ??= await create(storeRepository: storeRepository);
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
static Future<StoreService> create({
|
||||
required IsarStoreRepository storeRepository,
|
||||
}) async {
|
||||
static Future<StoreService> create({required IsarStoreRepository storeRepository}) async {
|
||||
final instance = StoreService._(storeRepository: storeRepository);
|
||||
await instance._populateCache();
|
||||
instance._storeUpdateSubscription = instance._listenForChange();
|
||||
|
|
@ -48,8 +44,8 @@ class StoreService {
|
|||
}
|
||||
|
||||
StreamSubscription<StoreDto> _listenForChange() => _storeRepository.watchAll().listen((event) {
|
||||
_cache[event.key.id] = event.value;
|
||||
});
|
||||
_cache[event.key.id] = event.value;
|
||||
});
|
||||
|
||||
/// Disposes the store and cancels the subscription. To reuse the store call init() again
|
||||
void dispose() async {
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ class SyncStreamService {
|
|||
required SyncApiRepository syncApiRepository,
|
||||
required SyncStreamRepository syncStreamRepository,
|
||||
bool Function()? cancelChecker,
|
||||
}) : _syncApiRepository = syncApiRepository,
|
||||
_syncStreamRepository = syncStreamRepository,
|
||||
_cancelChecker = cancelChecker;
|
||||
}) : _syncApiRepository = syncApiRepository,
|
||||
_syncStreamRepository = syncStreamRepository,
|
||||
_cancelChecker = cancelChecker;
|
||||
|
||||
bool get isCancelled => _cancelChecker?.call() ?? false;
|
||||
|
||||
|
|
@ -34,9 +34,7 @@ class SyncStreamService {
|
|||
Future<void> handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) async {
|
||||
if (batchData.isEmpty) return;
|
||||
|
||||
_logger.info(
|
||||
'Processing batch of ${batchData.length} AssetUploadReadyV1 events',
|
||||
);
|
||||
_logger.info('Processing batch of ${batchData.length} AssetUploadReadyV1 events');
|
||||
|
||||
final List<SyncAssetV1> assets = [];
|
||||
final List<SyncAssetExifV1> exifs = [];
|
||||
|
|
@ -65,22 +63,12 @@ class SyncStreamService {
|
|||
}
|
||||
|
||||
if (assets.isNotEmpty && exifs.isNotEmpty) {
|
||||
await _syncStreamRepository.updateAssetsV1(
|
||||
assets,
|
||||
debugLabel: 'websocket-batch',
|
||||
);
|
||||
await _syncStreamRepository.updateAssetsExifV1(
|
||||
exifs,
|
||||
debugLabel: 'websocket-batch',
|
||||
);
|
||||
await _syncStreamRepository.updateAssetsV1(assets, debugLabel: 'websocket-batch');
|
||||
await _syncStreamRepository.updateAssetsExifV1(exifs, debugLabel: 'websocket-batch');
|
||||
_logger.info('Successfully processed ${assets.length} assets in batch');
|
||||
}
|
||||
} catch (error, stackTrace) {
|
||||
_logger.severe(
|
||||
"Error processing AssetUploadReadyV1 websocket batch events",
|
||||
error,
|
||||
stackTrace,
|
||||
);
|
||||
_logger.severe("Error processing AssetUploadReadyV1 websocket batch events", error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -114,10 +102,7 @@ class SyncStreamService {
|
|||
batch.clear();
|
||||
}
|
||||
|
||||
Future<void> _handleSyncData(
|
||||
SyncEntityType type,
|
||||
Iterable<Object> data,
|
||||
) async {
|
||||
Future<void> _handleSyncData(SyncEntityType type, Iterable<Object> data) async {
|
||||
_logger.fine("Processing sync data for $type of length ${data.length}");
|
||||
switch (type) {
|
||||
case SyncEntityType.userV1:
|
||||
|
|
@ -135,30 +120,15 @@ class SyncStreamService {
|
|||
case SyncEntityType.assetExifV1:
|
||||
return _syncStreamRepository.updateAssetsExifV1(data.cast());
|
||||
case SyncEntityType.partnerAssetV1:
|
||||
return _syncStreamRepository.updateAssetsV1(
|
||||
data.cast(),
|
||||
debugLabel: 'partner',
|
||||
);
|
||||
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner');
|
||||
case SyncEntityType.partnerAssetBackfillV1:
|
||||
return _syncStreamRepository.updateAssetsV1(
|
||||
data.cast(),
|
||||
debugLabel: 'partner backfill',
|
||||
);
|
||||
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner backfill');
|
||||
case SyncEntityType.partnerAssetDeleteV1:
|
||||
return _syncStreamRepository.deleteAssetsV1(
|
||||
data.cast(),
|
||||
debugLabel: "partner",
|
||||
);
|
||||
return _syncStreamRepository.deleteAssetsV1(data.cast(), debugLabel: "partner");
|
||||
case SyncEntityType.partnerAssetExifV1:
|
||||
return _syncStreamRepository.updateAssetsExifV1(
|
||||
data.cast(),
|
||||
debugLabel: 'partner',
|
||||
);
|
||||
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner');
|
||||
case SyncEntityType.partnerAssetExifBackfillV1:
|
||||
return _syncStreamRepository.updateAssetsExifV1(
|
||||
data.cast(),
|
||||
debugLabel: 'partner backfill',
|
||||
);
|
||||
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner backfill');
|
||||
case SyncEntityType.albumV1:
|
||||
return _syncStreamRepository.updateAlbumsV1(data.cast());
|
||||
case SyncEntityType.albumDeleteV1:
|
||||
|
|
@ -166,39 +136,21 @@ class SyncStreamService {
|
|||
case SyncEntityType.albumUserV1:
|
||||
return _syncStreamRepository.updateAlbumUsersV1(data.cast());
|
||||
case SyncEntityType.albumUserBackfillV1:
|
||||
return _syncStreamRepository.updateAlbumUsersV1(
|
||||
data.cast(),
|
||||
debugLabel: 'backfill',
|
||||
);
|
||||
return _syncStreamRepository.updateAlbumUsersV1(data.cast(), debugLabel: 'backfill');
|
||||
case SyncEntityType.albumUserDeleteV1:
|
||||
return _syncStreamRepository.deleteAlbumUsersV1(data.cast());
|
||||
case SyncEntityType.albumAssetV1:
|
||||
return _syncStreamRepository.updateAssetsV1(
|
||||
data.cast(),
|
||||
debugLabel: 'album',
|
||||
);
|
||||
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album');
|
||||
case SyncEntityType.albumAssetBackfillV1:
|
||||
return _syncStreamRepository.updateAssetsV1(
|
||||
data.cast(),
|
||||
debugLabel: 'album backfill',
|
||||
);
|
||||
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album backfill');
|
||||
case SyncEntityType.albumAssetExifV1:
|
||||
return _syncStreamRepository.updateAssetsExifV1(
|
||||
data.cast(),
|
||||
debugLabel: 'album',
|
||||
);
|
||||
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album');
|
||||
case SyncEntityType.albumAssetExifBackfillV1:
|
||||
return _syncStreamRepository.updateAssetsExifV1(
|
||||
data.cast(),
|
||||
debugLabel: 'album backfill',
|
||||
);
|
||||
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album backfill');
|
||||
case SyncEntityType.albumToAssetV1:
|
||||
return _syncStreamRepository.updateAlbumToAssetsV1(data.cast());
|
||||
case SyncEntityType.albumToAssetBackfillV1:
|
||||
return _syncStreamRepository.updateAlbumToAssetsV1(
|
||||
data.cast(),
|
||||
debugLabel: 'backfill',
|
||||
);
|
||||
return _syncStreamRepository.updateAlbumToAssetsV1(data.cast(), debugLabel: 'backfill');
|
||||
case SyncEntityType.albumToAssetDeleteV1:
|
||||
return _syncStreamRepository.deleteAlbumToAssetsV1(data.cast());
|
||||
// No-op. SyncAckV1 entities are checkpoints in the sync stream
|
||||
|
|
@ -218,28 +170,15 @@ class SyncStreamService {
|
|||
case SyncEntityType.stackDeleteV1:
|
||||
return _syncStreamRepository.deleteStacksV1(data.cast());
|
||||
case SyncEntityType.partnerStackV1:
|
||||
return _syncStreamRepository.updateStacksV1(
|
||||
data.cast(),
|
||||
debugLabel: 'partner',
|
||||
);
|
||||
return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner');
|
||||
case SyncEntityType.partnerStackBackfillV1:
|
||||
return _syncStreamRepository.updateStacksV1(
|
||||
data.cast(),
|
||||
debugLabel: 'partner backfill',
|
||||
);
|
||||
return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner backfill');
|
||||
case SyncEntityType.partnerStackDeleteV1:
|
||||
return _syncStreamRepository.deleteStacksV1(
|
||||
data.cast(),
|
||||
debugLabel: 'partner',
|
||||
);
|
||||
return _syncStreamRepository.deleteStacksV1(data.cast(), debugLabel: 'partner');
|
||||
case SyncEntityType.userMetadataV1:
|
||||
return _syncStreamRepository.updateUserMetadatasV1(
|
||||
data.cast(),
|
||||
);
|
||||
return _syncStreamRepository.updateUserMetadatasV1(data.cast());
|
||||
case SyncEntityType.userMetadataDeleteV1:
|
||||
return _syncStreamRepository.deleteUserMetadatasV1(
|
||||
data.cast(),
|
||||
);
|
||||
return _syncStreamRepository.deleteUserMetadatasV1(data.cast());
|
||||
case SyncEntityType.personV1:
|
||||
return _syncStreamRepository.updatePeopleV1(data.cast());
|
||||
case SyncEntityType.personDeleteV1:
|
||||
|
|
|
|||
|
|
@ -11,27 +11,19 @@ import 'package:immich_mobile/domain/utils/event_stream.dart';
|
|||
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart';
|
||||
import 'package:immich_mobile/utils/async_mutex.dart';
|
||||
|
||||
typedef TimelineAssetSource = Future<List<BaseAsset>> Function(
|
||||
int index,
|
||||
int count,
|
||||
);
|
||||
typedef TimelineAssetSource = Future<List<BaseAsset>> Function(int index, int count);
|
||||
|
||||
typedef TimelineBucketSource = Stream<List<Bucket>> Function();
|
||||
|
||||
typedef TimelineQuery = ({
|
||||
TimelineAssetSource assetSource,
|
||||
TimelineBucketSource bucketSource,
|
||||
});
|
||||
typedef TimelineQuery = ({TimelineAssetSource assetSource, TimelineBucketSource bucketSource});
|
||||
|
||||
class TimelineFactory {
|
||||
final DriftTimelineRepository _timelineRepository;
|
||||
final SettingsService _settingsService;
|
||||
|
||||
const TimelineFactory({
|
||||
required DriftTimelineRepository timelineRepository,
|
||||
required SettingsService settingsService,
|
||||
}) : _timelineRepository = timelineRepository,
|
||||
_settingsService = settingsService;
|
||||
const TimelineFactory({required DriftTimelineRepository timelineRepository, required SettingsService settingsService})
|
||||
: _timelineRepository = timelineRepository,
|
||||
_settingsService = settingsService;
|
||||
|
||||
GroupAssetsBy get groupBy {
|
||||
final group = GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)];
|
||||
|
|
@ -75,17 +67,11 @@ class TimelineService {
|
|||
int _totalAssets = 0;
|
||||
int get totalAssets => _totalAssets;
|
||||
|
||||
TimelineService(TimelineQuery query)
|
||||
: this._(
|
||||
assetSource: query.assetSource,
|
||||
bucketSource: query.bucketSource,
|
||||
);
|
||||
TimelineService(TimelineQuery query) : this._(assetSource: query.assetSource, bucketSource: query.bucketSource);
|
||||
|
||||
TimelineService._({
|
||||
required TimelineAssetSource assetSource,
|
||||
required TimelineBucketSource bucketSource,
|
||||
}) : _assetSource = assetSource,
|
||||
_bucketSource = bucketSource {
|
||||
TimelineService._({required TimelineAssetSource assetSource, required TimelineBucketSource bucketSource})
|
||||
: _assetSource = assetSource,
|
||||
_bucketSource = bucketSource {
|
||||
_bucketSubscription = _bucketSource().listen((buckets) {
|
||||
_mutex.run(() async {
|
||||
final totalAssets = buckets.fold<int>(0, (acc, bucket) => acc + bucket.assetCount);
|
||||
|
|
@ -103,10 +89,7 @@ class TimelineService {
|
|||
count = kTimelineAssetLoadBatchSize;
|
||||
} else {
|
||||
offset = _bufferOffset;
|
||||
count = math.min(
|
||||
_buffer.length,
|
||||
totalAssets - _bufferOffset,
|
||||
);
|
||||
count = math.min(_buffer.length, totalAssets - _bufferOffset);
|
||||
}
|
||||
_buffer = await _assetSource(offset, count);
|
||||
_bufferOffset = offset;
|
||||
|
|
@ -134,10 +117,7 @@ class TimelineService {
|
|||
// make sure to load a meaningful amount of data (and not only the requested slice)
|
||||
// otherwise, each call to [loadAssets] would result in DB call trashing performance
|
||||
// fills small requests to [kTimelineAssetLoadBatchSize], adds some legroom into the opposite scroll direction for large requests
|
||||
final len = math.max(
|
||||
kTimelineAssetLoadBatchSize,
|
||||
count + kTimelineAssetLoadOppositeSize,
|
||||
);
|
||||
final len = math.max(kTimelineAssetLoadBatchSize, count + kTimelineAssetLoadOppositeSize);
|
||||
// when scrolling forward, start shortly before the requested offset
|
||||
// when scrolling backward, end shortly after the requested offset to guard against the user scrolling
|
||||
// in the other direction a tiny bit resulting in another required load from the DB
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ class UserService {
|
|||
required IsarUserRepository isarUserRepository,
|
||||
required UserApiRepository userApiRepository,
|
||||
required StoreService storeService,
|
||||
}) : _isarUserRepository = isarUserRepository,
|
||||
_userApiRepository = userApiRepository,
|
||||
_storeService = storeService;
|
||||
}) : _isarUserRepository = isarUserRepository,
|
||||
_userApiRepository = userApiRepository,
|
||||
_storeService = storeService;
|
||||
|
||||
UserDto getMyUser() {
|
||||
return _storeService.get(StoreKey.currentUser);
|
||||
|
|
@ -44,10 +44,7 @@ class UserService {
|
|||
|
||||
Future<String?> createProfileImage(String name, Uint8List image) async {
|
||||
try {
|
||||
final path = await _userApiRepository.createProfileImage(
|
||||
name: name,
|
||||
data: image,
|
||||
);
|
||||
final path = await _userApiRepository.createProfileImage(name: name, data: image);
|
||||
final updatedUser = getMyUser().copyWith(profileImagePath: path);
|
||||
await _storeService.put(StoreKey.currentUser, updatedUser);
|
||||
await _isarUserRepository.update(updatedUser);
|
||||
|
|
|
|||
|
|
@ -66,23 +66,21 @@ class BackgroundSyncManager {
|
|||
// We use a ternary operator to avoid [_deviceAlbumSyncTask] from being
|
||||
// captured by the closure passed to [runInIsolateGentle].
|
||||
_deviceAlbumSyncTask = full
|
||||
? runInIsolateGentle(
|
||||
computation: (ref) => ref.read(localSyncServiceProvider).sync(full: true),
|
||||
)
|
||||
: runInIsolateGentle(
|
||||
computation: (ref) => ref.read(localSyncServiceProvider).sync(full: false),
|
||||
);
|
||||
? runInIsolateGentle(computation: (ref) => ref.read(localSyncServiceProvider).sync(full: true))
|
||||
: runInIsolateGentle(computation: (ref) => ref.read(localSyncServiceProvider).sync(full: false));
|
||||
|
||||
return _deviceAlbumSyncTask!.whenComplete(() {
|
||||
_deviceAlbumSyncTask = null;
|
||||
onLocalSyncComplete?.call();
|
||||
}).catchError((error) {
|
||||
onLocalSyncError?.call(error.toString());
|
||||
_deviceAlbumSyncTask = null;
|
||||
});
|
||||
return _deviceAlbumSyncTask!
|
||||
.whenComplete(() {
|
||||
_deviceAlbumSyncTask = null;
|
||||
onLocalSyncComplete?.call();
|
||||
})
|
||||
.catchError((error) {
|
||||
onLocalSyncError?.call(error.toString());
|
||||
_deviceAlbumSyncTask = null;
|
||||
});
|
||||
}
|
||||
|
||||
// No need to cancel the task, as it can also be run when the user logs out
|
||||
// No need to cancel the task, as it can also be run when the user logs out
|
||||
Future<void> hashAssets() {
|
||||
if (_hashTask != null) {
|
||||
return _hashTask!.future;
|
||||
|
|
@ -90,17 +88,17 @@ class BackgroundSyncManager {
|
|||
|
||||
onHashingStart?.call();
|
||||
|
||||
_hashTask = runInIsolateGentle(
|
||||
computation: (ref) => ref.read(hashServiceProvider).hashAssets(),
|
||||
);
|
||||
_hashTask = runInIsolateGentle(computation: (ref) => ref.read(hashServiceProvider).hashAssets());
|
||||
|
||||
return _hashTask!.whenComplete(() {
|
||||
onHashingComplete?.call();
|
||||
_hashTask = null;
|
||||
}).catchError((error) {
|
||||
onHashingError?.call(error.toString());
|
||||
_hashTask = null;
|
||||
});
|
||||
return _hashTask!
|
||||
.whenComplete(() {
|
||||
onHashingComplete?.call();
|
||||
_hashTask = null;
|
||||
})
|
||||
.catchError((error) {
|
||||
onHashingError?.call(error.toString());
|
||||
_hashTask = null;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> syncRemote() {
|
||||
|
|
@ -110,16 +108,16 @@ class BackgroundSyncManager {
|
|||
|
||||
onRemoteSyncStart?.call();
|
||||
|
||||
_syncTask = runInIsolateGentle(
|
||||
computation: (ref) => ref.read(syncStreamServiceProvider).sync(),
|
||||
);
|
||||
return _syncTask!.whenComplete(() {
|
||||
onRemoteSyncComplete?.call();
|
||||
_syncTask = null;
|
||||
}).catchError((error) {
|
||||
onRemoteSyncError?.call(error.toString());
|
||||
_syncTask = null;
|
||||
});
|
||||
_syncTask = runInIsolateGentle(computation: (ref) => ref.read(syncStreamServiceProvider).sync());
|
||||
return _syncTask!
|
||||
.whenComplete(() {
|
||||
onRemoteSyncComplete?.call();
|
||||
_syncTask = null;
|
||||
})
|
||||
.catchError((error) {
|
||||
onRemoteSyncError?.call(error.toString());
|
||||
_syncTask = null;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> syncWebsocketBatch(List<dynamic> batchData) {
|
||||
|
|
@ -133,9 +131,6 @@ class BackgroundSyncManager {
|
|||
}
|
||||
}
|
||||
|
||||
Cancelable<void> _handleWsAssetUploadReadyV1Batch(
|
||||
List<dynamic> batchData,
|
||||
) =>
|
||||
runInIsolateGentle(
|
||||
computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV1Batch(batchData),
|
||||
);
|
||||
Cancelable<void> _handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) => runInIsolateGentle(
|
||||
computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV1Batch(batchData),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,12 +28,7 @@ class EventStream {
|
|||
void Function()? onDone,
|
||||
bool? cancelOnError,
|
||||
}) {
|
||||
return where<T>().listen(
|
||||
onData,
|
||||
onError: onError,
|
||||
onDone: onDone,
|
||||
cancelOnError: cancelOnError,
|
||||
);
|
||||
return where<T>().listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError);
|
||||
}
|
||||
|
||||
/// Closes the stream controller
|
||||
|
|
|
|||
|
|
@ -113,10 +113,7 @@ class Album {
|
|||
modifiedAt.isAtSameMomentAs(other.modifiedAt) &&
|
||||
isAtSameMomentAs(startDate, other.startDate) &&
|
||||
isAtSameMomentAs(endDate, other.endDate) &&
|
||||
isAtSameMomentAs(
|
||||
lastModifiedAssetTimestamp,
|
||||
other.lastModifiedAssetTimestamp,
|
||||
) &&
|
||||
isAtSameMomentAs(lastModifiedAssetTimestamp, other.lastModifiedAssetTimestamp) &&
|
||||
shared == other.shared &&
|
||||
activityEnabled == other.activityEnabled &&
|
||||
owner.value == other.owner.value &&
|
||||
|
|
@ -169,9 +166,7 @@ class Album {
|
|||
a.thumbnail.value = await db.assets.where().remoteIdEqualTo(dto.albumThumbnailAssetId).findFirst();
|
||||
}
|
||||
if (dto.albumUsers.isNotEmpty) {
|
||||
final users = await db.users.getAllById(
|
||||
dto.albumUsers.map((e) => e.user.id).toList(growable: false),
|
||||
);
|
||||
final users = await db.users.getAllById(dto.albumUsers.map((e) => e.user.id).toList(growable: false));
|
||||
a.sharedUsers.addAll(users.cast());
|
||||
}
|
||||
if (dto.assets.isNotEmpty) {
|
||||
|
|
|
|||
BIN
mobile/lib/entities/album.entity.g.dart
generated
BIN
mobile/lib/entities/album.entity.g.dart
generated
Binary file not shown.
BIN
mobile/lib/entities/android_device_asset.entity.g.dart
generated
BIN
mobile/lib/entities/android_device_asset.entity.g.dart
generated
Binary file not shown.
|
|
@ -19,30 +19,30 @@ part 'asset.entity.g.dart';
|
|||
@Collection(inheritance: false)
|
||||
class Asset {
|
||||
Asset.remote(AssetResponseDto remote)
|
||||
: remoteId = remote.id,
|
||||
checksum = remote.checksum,
|
||||
fileCreatedAt = remote.fileCreatedAt,
|
||||
fileModifiedAt = remote.fileModifiedAt,
|
||||
updatedAt = remote.updatedAt,
|
||||
durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0,
|
||||
type = remote.type.toAssetType(),
|
||||
fileName = remote.originalFileName,
|
||||
height = remote.exifInfo?.exifImageHeight?.toInt(),
|
||||
width = remote.exifInfo?.exifImageWidth?.toInt(),
|
||||
livePhotoVideoId = remote.livePhotoVideoId,
|
||||
ownerId = fastHash(remote.ownerId),
|
||||
exifInfo = remote.exifInfo == null ? null : ExifDtoConverter.fromDto(remote.exifInfo!),
|
||||
isFavorite = remote.isFavorite,
|
||||
isArchived = remote.isArchived,
|
||||
isTrashed = remote.isTrashed,
|
||||
isOffline = remote.isOffline,
|
||||
// workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app
|
||||
// stack handling to properly handle it
|
||||
stackPrimaryAssetId = remote.stack?.primaryAssetId == remote.id ? null : remote.stack?.primaryAssetId,
|
||||
stackCount = remote.stack?.assetCount ?? 0,
|
||||
stackId = remote.stack?.id,
|
||||
thumbhash = remote.thumbhash,
|
||||
visibility = getVisibility(remote.visibility);
|
||||
: remoteId = remote.id,
|
||||
checksum = remote.checksum,
|
||||
fileCreatedAt = remote.fileCreatedAt,
|
||||
fileModifiedAt = remote.fileModifiedAt,
|
||||
updatedAt = remote.updatedAt,
|
||||
durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0,
|
||||
type = remote.type.toAssetType(),
|
||||
fileName = remote.originalFileName,
|
||||
height = remote.exifInfo?.exifImageHeight?.toInt(),
|
||||
width = remote.exifInfo?.exifImageWidth?.toInt(),
|
||||
livePhotoVideoId = remote.livePhotoVideoId,
|
||||
ownerId = fastHash(remote.ownerId),
|
||||
exifInfo = remote.exifInfo == null ? null : ExifDtoConverter.fromDto(remote.exifInfo!),
|
||||
isFavorite = remote.isFavorite,
|
||||
isArchived = remote.isArchived,
|
||||
isTrashed = remote.isTrashed,
|
||||
isOffline = remote.isOffline,
|
||||
// workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app
|
||||
// stack handling to properly handle it
|
||||
stackPrimaryAssetId = remote.stack?.primaryAssetId == remote.id ? null : remote.stack?.primaryAssetId,
|
||||
stackCount = remote.stack?.assetCount ?? 0,
|
||||
stackId = remote.stack?.id,
|
||||
thumbhash = remote.thumbhash,
|
||||
visibility = getVisibility(remote.visibility);
|
||||
|
||||
Asset({
|
||||
this.id = Isar.autoIncrement,
|
||||
|
|
@ -127,11 +127,7 @@ class Asset {
|
|||
@Index(unique: false, replace: false, type: IndexType.hash)
|
||||
String? localId;
|
||||
|
||||
@Index(
|
||||
unique: true,
|
||||
replace: false,
|
||||
composite: [CompositeIndex("checksum", type: IndexType.hash)],
|
||||
)
|
||||
@Index(unique: true, replace: false, composite: [CompositeIndex("checksum", type: IndexType.hash)])
|
||||
int ownerId;
|
||||
|
||||
DateTime fileCreatedAt;
|
||||
|
|
@ -447,33 +443,32 @@ class Asset {
|
|||
int? stackCount,
|
||||
String? thumbhash,
|
||||
AssetVisibilityEnum? visibility,
|
||||
}) =>
|
||||
Asset(
|
||||
id: id ?? this.id,
|
||||
checksum: checksum ?? this.checksum,
|
||||
remoteId: remoteId ?? this.remoteId,
|
||||
localId: localId ?? this.localId,
|
||||
ownerId: ownerId ?? this.ownerId,
|
||||
fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt,
|
||||
fileModifiedAt: fileModifiedAt ?? this.fileModifiedAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
|
||||
type: type ?? this.type,
|
||||
width: width ?? this.width,
|
||||
height: height ?? this.height,
|
||||
fileName: fileName ?? this.fileName,
|
||||
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
|
||||
isFavorite: isFavorite ?? this.isFavorite,
|
||||
isArchived: isArchived ?? this.isArchived,
|
||||
isTrashed: isTrashed ?? this.isTrashed,
|
||||
isOffline: isOffline ?? this.isOffline,
|
||||
exifInfo: exifInfo ?? this.exifInfo,
|
||||
stackId: stackId ?? this.stackId,
|
||||
stackPrimaryAssetId: stackPrimaryAssetId ?? this.stackPrimaryAssetId,
|
||||
stackCount: stackCount ?? this.stackCount,
|
||||
thumbhash: thumbhash ?? this.thumbhash,
|
||||
visibility: visibility ?? this.visibility,
|
||||
);
|
||||
}) => Asset(
|
||||
id: id ?? this.id,
|
||||
checksum: checksum ?? this.checksum,
|
||||
remoteId: remoteId ?? this.remoteId,
|
||||
localId: localId ?? this.localId,
|
||||
ownerId: ownerId ?? this.ownerId,
|
||||
fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt,
|
||||
fileModifiedAt: fileModifiedAt ?? this.fileModifiedAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
|
||||
type: type ?? this.type,
|
||||
width: width ?? this.width,
|
||||
height: height ?? this.height,
|
||||
fileName: fileName ?? this.fileName,
|
||||
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
|
||||
isFavorite: isFavorite ?? this.isFavorite,
|
||||
isArchived: isArchived ?? this.isArchived,
|
||||
isTrashed: isTrashed ?? this.isTrashed,
|
||||
isOffline: isOffline ?? this.isOffline,
|
||||
exifInfo: exifInfo ?? this.exifInfo,
|
||||
stackId: stackId ?? this.stackId,
|
||||
stackPrimaryAssetId: stackPrimaryAssetId ?? this.stackPrimaryAssetId,
|
||||
stackCount: stackCount ?? this.stackCount,
|
||||
thumbhash: thumbhash ?? this.thumbhash,
|
||||
visibility: visibility ?? this.visibility,
|
||||
);
|
||||
|
||||
Future<void> put(Isar db) async {
|
||||
await db.assets.put(this);
|
||||
|
|
@ -494,10 +489,7 @@ class Asset {
|
|||
return compareByChecksum(a, b);
|
||||
}
|
||||
|
||||
static int compareByOwnerChecksumCreatedModified(
|
||||
Asset a,
|
||||
Asset b,
|
||||
) {
|
||||
static int compareByOwnerChecksumCreatedModified(Asset a, Asset b) {
|
||||
final int ownerIdOrder = a.ownerId.compareTo(b.ownerId);
|
||||
if (ownerIdOrder != 0) return ownerIdOrder;
|
||||
final int checksumOrder = compareByChecksum(a, b);
|
||||
|
|
@ -539,11 +531,11 @@ class Asset {
|
|||
}
|
||||
|
||||
static getVisibility(AssetVisibility visibility) => switch (visibility) {
|
||||
AssetVisibility.archive => AssetVisibilityEnum.archive,
|
||||
AssetVisibility.hidden => AssetVisibilityEnum.hidden,
|
||||
AssetVisibility.locked => AssetVisibilityEnum.locked,
|
||||
AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline,
|
||||
};
|
||||
AssetVisibility.archive => AssetVisibilityEnum.archive,
|
||||
AssetVisibility.hidden => AssetVisibilityEnum.hidden,
|
||||
AssetVisibility.locked => AssetVisibilityEnum.locked,
|
||||
AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline,
|
||||
};
|
||||
}
|
||||
|
||||
enum AssetType {
|
||||
|
|
@ -556,21 +548,17 @@ enum AssetType {
|
|||
|
||||
extension AssetTypeEnumHelper on AssetTypeEnum {
|
||||
AssetType toAssetType() => switch (this) {
|
||||
AssetTypeEnum.IMAGE => AssetType.image,
|
||||
AssetTypeEnum.VIDEO => AssetType.video,
|
||||
AssetTypeEnum.AUDIO => AssetType.audio,
|
||||
AssetTypeEnum.OTHER => AssetType.other,
|
||||
_ => throw Exception(),
|
||||
};
|
||||
AssetTypeEnum.IMAGE => AssetType.image,
|
||||
AssetTypeEnum.VIDEO => AssetType.video,
|
||||
AssetTypeEnum.AUDIO => AssetType.audio,
|
||||
AssetTypeEnum.OTHER => AssetType.other,
|
||||
_ => throw Exception(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Describes where the information of this asset came from:
|
||||
/// only from the local device, only from the remote server or merged from both
|
||||
enum AssetState {
|
||||
local,
|
||||
remote,
|
||||
merged,
|
||||
}
|
||||
enum AssetState { local, remote, merged }
|
||||
|
||||
extension AssetsHelper on IsarCollection<Asset> {
|
||||
Future<int> deleteAllByRemoteId(Iterable<String> ids) => ids.isEmpty ? Future.value(0) : remote(ids).deleteAll();
|
||||
|
|
@ -579,13 +567,9 @@ extension AssetsHelper on IsarCollection<Asset> {
|
|||
Future<List<Asset>> getAllByLocalId(Iterable<String> ids) => ids.isEmpty ? Future.value([]) : local(ids).findAll();
|
||||
Future<Asset?> getByRemoteId(String id) => where().remoteIdEqualTo(id).findFirst();
|
||||
|
||||
QueryBuilder<Asset, Asset, QAfterWhereClause> remote(
|
||||
Iterable<String> ids,
|
||||
) =>
|
||||
QueryBuilder<Asset, Asset, QAfterWhereClause> remote(Iterable<String> ids) =>
|
||||
where().anyOf(ids, (q, String e) => q.remoteIdEqualTo(e));
|
||||
QueryBuilder<Asset, Asset, QAfterWhereClause> local(
|
||||
Iterable<String> ids,
|
||||
) {
|
||||
QueryBuilder<Asset, Asset, QAfterWhereClause> local(Iterable<String> ids) {
|
||||
return where().anyOf(ids, (q, String e) => q.localIdEqualTo(e));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
mobile/lib/entities/asset.entity.g.dart
generated
BIN
mobile/lib/entities/asset.entity.g.dart
generated
Binary file not shown.
|
|
@ -14,21 +14,9 @@ class BackupAlbum {
|
|||
|
||||
Id get isarId => fastHash(id);
|
||||
|
||||
BackupAlbum copyWith({
|
||||
String? id,
|
||||
DateTime? lastBackup,
|
||||
BackupSelection? selection,
|
||||
}) {
|
||||
return BackupAlbum(
|
||||
id ?? this.id,
|
||||
lastBackup ?? this.lastBackup,
|
||||
selection ?? this.selection,
|
||||
);
|
||||
BackupAlbum copyWith({String? id, DateTime? lastBackup, BackupSelection? selection}) {
|
||||
return BackupAlbum(id ?? this.id, lastBackup ?? this.lastBackup, selection ?? this.selection);
|
||||
}
|
||||
}
|
||||
|
||||
enum BackupSelection {
|
||||
none,
|
||||
select,
|
||||
exclude;
|
||||
}
|
||||
enum BackupSelection { none, select, exclude }
|
||||
|
|
|
|||
BIN
mobile/lib/entities/backup_album.entity.g.dart
generated
BIN
mobile/lib/entities/backup_album.entity.g.dart
generated
Binary file not shown.
BIN
mobile/lib/entities/duplicated_asset.entity.g.dart
generated
BIN
mobile/lib/entities/duplicated_asset.entity.g.dart
generated
Binary file not shown.
BIN
mobile/lib/entities/etag.entity.g.dart
generated
BIN
mobile/lib/entities/etag.entity.g.dart
generated
Binary file not shown.
BIN
mobile/lib/entities/ios_device_asset.entity.g.dart
generated
BIN
mobile/lib/entities/ios_device_asset.entity.g.dart
generated
Binary file not shown.
|
|
@ -15,16 +15,10 @@ extension TZExtension on Asset {
|
|||
final location = getLocation(exifInfo!.timeZone!);
|
||||
dt = TZDateTime.from(dt, location);
|
||||
} on LocationNotFoundException {
|
||||
RegExp re = RegExp(
|
||||
r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExp re = RegExp(r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$', caseSensitive: false);
|
||||
final m = re.firstMatch(exifInfo!.timeZone!);
|
||||
if (m != null) {
|
||||
final duration = Duration(
|
||||
hours: int.parse(m.group(1) ?? '0'),
|
||||
minutes: int.parse(m.group(2) ?? '0'),
|
||||
);
|
||||
final duration = Duration(hours: int.parse(m.group(1) ?? '0'), minutes: int.parse(m.group(2) ?? '0'));
|
||||
dt = dt.add(duration);
|
||||
return (dt, duration);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
|||
import 'package:immich_mobile/utils/hash.dart';
|
||||
|
||||
extension ListExtension<E> on List<E> {
|
||||
List<E> uniqueConsecutive({
|
||||
int Function(E a, E b)? compare,
|
||||
void Function(E a, E b)? onDuplicate,
|
||||
}) {
|
||||
List<E> uniqueConsecutive({int Function(E a, E b)? compare, void Function(E a, E b)? onDuplicate}) {
|
||||
compare ??= (E a, E b) => a == b ? 0 : 1;
|
||||
int i = 1, j = 1;
|
||||
for (; i < length; i++) {
|
||||
|
|
@ -45,9 +42,7 @@ extension IntListExtension on Iterable<int> {
|
|||
|
||||
extension AssetListExtension on Iterable<Asset> {
|
||||
/// Returns the assets that are already available in the Immich server
|
||||
Iterable<Asset> remoteOnly({
|
||||
void Function()? errorCallback,
|
||||
}) {
|
||||
Iterable<Asset> remoteOnly({void Function()? errorCallback}) {
|
||||
final bool onlyRemote = every((e) => e.isRemote);
|
||||
if (!onlyRemote) {
|
||||
if (errorCallback != null) errorCallback();
|
||||
|
|
@ -58,10 +53,7 @@ extension AssetListExtension on Iterable<Asset> {
|
|||
|
||||
/// Returns the assets that are owned by the user passed to the [owner] param
|
||||
/// If [owner] is null, an empty list is returned
|
||||
Iterable<Asset> ownedOnly(
|
||||
UserDto? owner, {
|
||||
void Function()? errorCallback,
|
||||
}) {
|
||||
Iterable<Asset> ownedOnly(UserDto? owner, {void Function()? errorCallback}) {
|
||||
if (owner == null) return [];
|
||||
final isarUserId = fastHash(owner.id);
|
||||
final bool onlyOwned = every((e) => e.ownerId == isarUserId);
|
||||
|
|
|
|||
|
|
@ -47,11 +47,7 @@ extension DateRangeFormatting on DateTime {
|
|||
/// - Date range of this year: "Mar 23-May 31"
|
||||
/// - Date range of other year: "Aug 28 - Sep 30, 2023"
|
||||
/// - Date range over multiple years: "Apr 17, 2021 - Apr 9, 2022"
|
||||
static String formatDateRange(
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
Locale? locale,
|
||||
) {
|
||||
static String formatDateRange(DateTime startDate, DateTime endDate, Locale? locale) {
|
||||
final now = DateTime.now();
|
||||
final currentYear = now.year;
|
||||
final localeString = locale?.toString() ?? 'en_US';
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ extension MapMarkers on MapLibreMapController {
|
|||
Future<void> addGeoJSONSourceForMarkers(List<MapMarker> markers) async {
|
||||
return addSource(
|
||||
MapUtils.defaultSourceId,
|
||||
GeojsonSourceProperties(
|
||||
data: MapUtils.generateGeoJsonForMarkers(markers.toList()),
|
||||
),
|
||||
GeojsonSourceProperties(data: MapUtils.generateGeoJsonForMarkers(markers.toList())),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -73,23 +71,13 @@ extension MapMarkers on MapLibreMapController {
|
|||
try {
|
||||
final ByteData bytes = await rootBundle.load("assets/location-pin.png");
|
||||
await addImage("mapMarker", bytes.buffer.asUint8List());
|
||||
return addSymbol(
|
||||
SymbolOptions(
|
||||
geometry: centre,
|
||||
iconImage: "mapMarker",
|
||||
iconSize: 0.15,
|
||||
iconAnchor: "bottom",
|
||||
),
|
||||
);
|
||||
return addSymbol(SymbolOptions(geometry: centre, iconImage: "mapMarker", iconSize: 0.15, iconAnchor: "bottom"));
|
||||
} finally {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
Future<LatLngBounds> getBoundsFromPoint(
|
||||
Point<double> point,
|
||||
double distance,
|
||||
) async {
|
||||
Future<LatLngBounds> getBoundsFromPoint(Point<double> point, double distance) async {
|
||||
final southWestPx = Point(point.x - distance, point.y + distance);
|
||||
final northEastPx = Point(point.x + distance, point.y - distance);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,7 @@ class FastScrollPhysics extends ScrollPhysics {
|
|||
}
|
||||
|
||||
@override
|
||||
SpringDescription get spring => const SpringDescription(
|
||||
mass: 1,
|
||||
stiffness: 402.49984375,
|
||||
damping: 40,
|
||||
);
|
||||
SpringDescription get spring => const SpringDescription(mass: 1, stiffness: 402.49984375, damping: 40);
|
||||
}
|
||||
|
||||
class FastClampingScrollPhysics extends ClampingScrollPhysics {
|
||||
|
|
@ -27,12 +23,12 @@ class FastClampingScrollPhysics extends ClampingScrollPhysics {
|
|||
|
||||
@override
|
||||
SpringDescription get spring => const SpringDescription(
|
||||
// When swiping between videos on Android, the placeholder of the first opened video
|
||||
// can briefly be seen and cause a flicker effect if the video begins to initialize
|
||||
// before the animation finishes - probably a bug in PhotoViewGallery's animation handling
|
||||
// Making the animation faster is not just stylistic, but also helps to avoid this flicker
|
||||
mass: 1,
|
||||
stiffness: 1601.2499609375,
|
||||
damping: 80,
|
||||
);
|
||||
// When swiping between videos on Android, the placeholder of the first opened video
|
||||
// can briefly be seen and cause a flicker effect if the video begins to initialize
|
||||
// before the animation finishes - probably a bug in PhotoViewGallery's animation handling
|
||||
// Making the animation faster is not just stylistic, but also helps to avoid this flicker
|
||||
mass: 1,
|
||||
stiffness: 1601.2499609375,
|
||||
damping: 80,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
extension StringExtension on String {
|
||||
String capitalize() {
|
||||
return split(" ")
|
||||
.map(
|
||||
(str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1),
|
||||
)
|
||||
.join(" ");
|
||||
return split(" ").map((str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1)).join(" ");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,16 +7,10 @@ extension ImmichColorSchemeExtensions on ColorScheme {
|
|||
|
||||
extension ColorExtensions on Color {
|
||||
Color lighten({double amount = 0.1}) {
|
||||
return Color.alphaBlend(
|
||||
Colors.white.withValues(alpha: amount),
|
||||
this,
|
||||
);
|
||||
return Color.alphaBlend(Colors.white.withValues(alpha: amount), this);
|
||||
}
|
||||
|
||||
Color darken({double amount = 0.1}) {
|
||||
return Color.alphaBlend(
|
||||
Colors.black.withValues(alpha: amount),
|
||||
this,
|
||||
);
|
||||
return Color.alphaBlend(Colors.black.withValues(alpha: amount), this);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,11 +29,7 @@ extension TextTranslateExtension on Text {
|
|||
}
|
||||
}
|
||||
|
||||
String _translateHelper(
|
||||
BuildContext? context,
|
||||
String key, [
|
||||
Map<String, Object>? args,
|
||||
]) {
|
||||
String _translateHelper(BuildContext? context, String key, [Map<String, Object>? args]) {
|
||||
if (key.isEmpty) {
|
||||
return '';
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -16,21 +16,10 @@ class DeviceAssetEntity {
|
|||
final List<byte> hash;
|
||||
final DateTime modifiedTime;
|
||||
|
||||
const DeviceAssetEntity({
|
||||
required this.assetId,
|
||||
required this.hash,
|
||||
required this.modifiedTime,
|
||||
});
|
||||
const DeviceAssetEntity({required this.assetId, required this.hash, required this.modifiedTime});
|
||||
|
||||
DeviceAsset toModel() => DeviceAsset(
|
||||
assetId: assetId,
|
||||
hash: Uint8List.fromList(hash),
|
||||
modifiedTime: modifiedTime,
|
||||
);
|
||||
DeviceAsset toModel() => DeviceAsset(assetId: assetId, hash: Uint8List.fromList(hash), modifiedTime: modifiedTime);
|
||||
|
||||
static DeviceAssetEntity fromDto(DeviceAsset dto) => DeviceAssetEntity(
|
||||
assetId: dto.assetId,
|
||||
hash: dto.hash,
|
||||
modifiedTime: dto.modifiedTime,
|
||||
);
|
||||
static DeviceAssetEntity fromDto(DeviceAsset dto) =>
|
||||
DeviceAssetEntity(assetId: dto.assetId, hash: dto.hash, modifiedTime: dto.modifiedTime);
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -52,47 +52,47 @@ class ExifInfo {
|
|||
});
|
||||
|
||||
static ExifInfo fromDto(domain.ExifInfo dto) => ExifInfo(
|
||||
id: dto.assetId,
|
||||
fileSize: dto.fileSize,
|
||||
dateTimeOriginal: dto.dateTimeOriginal,
|
||||
timeZone: dto.timeZone,
|
||||
make: dto.make,
|
||||
model: dto.model,
|
||||
lens: dto.lens,
|
||||
f: dto.f,
|
||||
mm: dto.mm,
|
||||
iso: dto.iso?.toInt(),
|
||||
exposureSeconds: dto.exposureSeconds,
|
||||
lat: dto.latitude,
|
||||
long: dto.longitude,
|
||||
city: dto.city,
|
||||
state: dto.state,
|
||||
country: dto.country,
|
||||
description: dto.description,
|
||||
orientation: dto.orientation,
|
||||
);
|
||||
id: dto.assetId,
|
||||
fileSize: dto.fileSize,
|
||||
dateTimeOriginal: dto.dateTimeOriginal,
|
||||
timeZone: dto.timeZone,
|
||||
make: dto.make,
|
||||
model: dto.model,
|
||||
lens: dto.lens,
|
||||
f: dto.f,
|
||||
mm: dto.mm,
|
||||
iso: dto.iso?.toInt(),
|
||||
exposureSeconds: dto.exposureSeconds,
|
||||
lat: dto.latitude,
|
||||
long: dto.longitude,
|
||||
city: dto.city,
|
||||
state: dto.state,
|
||||
country: dto.country,
|
||||
description: dto.description,
|
||||
orientation: dto.orientation,
|
||||
);
|
||||
|
||||
domain.ExifInfo toDto() => domain.ExifInfo(
|
||||
assetId: id,
|
||||
fileSize: fileSize,
|
||||
description: description,
|
||||
orientation: orientation,
|
||||
timeZone: timeZone,
|
||||
dateTimeOriginal: dateTimeOriginal,
|
||||
isFlipped: ExifDtoConverter.isOrientationFlipped(orientation),
|
||||
latitude: lat,
|
||||
longitude: long,
|
||||
city: city,
|
||||
state: state,
|
||||
country: country,
|
||||
make: make,
|
||||
model: model,
|
||||
lens: lens,
|
||||
f: f,
|
||||
mm: mm,
|
||||
iso: iso?.toInt(),
|
||||
exposureSeconds: exposureSeconds,
|
||||
);
|
||||
assetId: id,
|
||||
fileSize: fileSize,
|
||||
description: description,
|
||||
orientation: orientation,
|
||||
timeZone: timeZone,
|
||||
dateTimeOriginal: dateTimeOriginal,
|
||||
isFlipped: ExifDtoConverter.isOrientationFlipped(orientation),
|
||||
latitude: lat,
|
||||
longitude: long,
|
||||
city: city,
|
||||
state: state,
|
||||
country: country,
|
||||
make: make,
|
||||
model: model,
|
||||
lens: lens,
|
||||
f: f,
|
||||
mm: mm,
|
||||
iso: iso?.toInt(),
|
||||
exposureSeconds: exposureSeconds,
|
||||
);
|
||||
}
|
||||
|
||||
class RemoteExifEntity extends Table with DriftDefaultsMixin {
|
||||
|
|
@ -148,24 +148,24 @@ class RemoteExifEntity extends Table with DriftDefaultsMixin {
|
|||
|
||||
extension RemoteExifEntityDataDomainEx on RemoteExifEntityData {
|
||||
domain.ExifInfo toDto() => domain.ExifInfo(
|
||||
fileSize: fileSize,
|
||||
dateTimeOriginal: dateTimeOriginal,
|
||||
timeZone: timeZone,
|
||||
make: make,
|
||||
model: model,
|
||||
iso: iso,
|
||||
city: city,
|
||||
state: state,
|
||||
country: country,
|
||||
description: description,
|
||||
orientation: orientation,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
f: fNumber?.toDouble(),
|
||||
mm: focalLength?.toDouble(),
|
||||
lens: lens,
|
||||
width: width?.toDouble(),
|
||||
height: height?.toDouble(),
|
||||
isFlipped: ExifDtoConverter.isOrientationFlipped(orientation),
|
||||
);
|
||||
fileSize: fileSize,
|
||||
dateTimeOriginal: dateTimeOriginal,
|
||||
timeZone: timeZone,
|
||||
make: make,
|
||||
model: model,
|
||||
iso: iso,
|
||||
city: city,
|
||||
state: state,
|
||||
country: country,
|
||||
description: description,
|
||||
orientation: orientation,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
f: fNumber?.toDouble(),
|
||||
mm: focalLength?.toDouble(),
|
||||
lens: lens,
|
||||
width: width?.toDouble(),
|
||||
height: height?.toDouble(),
|
||||
isFlipped: ExifDtoConverter.isOrientationFlipped(orientation),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
BIN
mobile/lib/infrastructure/entities/exif.entity.g.dart
generated
BIN
mobile/lib/infrastructure/entities/exif.entity.g.dart
generated
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -22,17 +22,17 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
|||
|
||||
extension LocalAssetEntityDataDomainEx on LocalAssetEntityData {
|
||||
LocalAsset toDto() => LocalAsset(
|
||||
id: id,
|
||||
name: name,
|
||||
checksum: checksum,
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
durationInSeconds: durationInSeconds,
|
||||
isFavorite: isFavorite,
|
||||
height: height,
|
||||
width: width,
|
||||
remoteId: null,
|
||||
orientation: orientation,
|
||||
);
|
||||
id: id,
|
||||
name: name,
|
||||
checksum: checksum,
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
durationInSeconds: durationInSeconds,
|
||||
isFavorite: isFavorite,
|
||||
height: height,
|
||||
width: width,
|
||||
remoteId: null,
|
||||
orientation: orientation,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
BIN
mobile/lib/infrastructure/entities/log.entity.g.dart
generated
BIN
mobile/lib/infrastructure/entities/log.entity.g.dart
generated
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -5,11 +5,7 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
|||
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
@TableIndex(
|
||||
name: 'UQ_remote_asset_owner_checksum',
|
||||
columns: {#checksum, #ownerId},
|
||||
unique: true,
|
||||
)
|
||||
@TableIndex(name: 'UQ_remote_asset_owner_checksum', columns: {#checksum, #ownerId}, unique: true)
|
||||
@TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum})
|
||||
class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
||||
const RemoteAssetEntity();
|
||||
|
|
@ -40,21 +36,21 @@ class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin
|
|||
|
||||
extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData {
|
||||
RemoteAsset toDto() => RemoteAsset(
|
||||
id: id,
|
||||
name: name,
|
||||
ownerId: ownerId,
|
||||
checksum: checksum,
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
durationInSeconds: durationInSeconds,
|
||||
isFavorite: isFavorite,
|
||||
height: height,
|
||||
width: width,
|
||||
thumbHash: thumbHash,
|
||||
visibility: visibility,
|
||||
livePhotoVideoId: livePhotoVideoId,
|
||||
localId: null,
|
||||
stackId: stackId,
|
||||
);
|
||||
id: id,
|
||||
name: name,
|
||||
ownerId: ownerId,
|
||||
checksum: checksum,
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
durationInSeconds: durationInSeconds,
|
||||
isFavorite: isFavorite,
|
||||
height: height,
|
||||
width: width,
|
||||
thumbHash: thumbHash,
|
||||
visibility: visibility,
|
||||
livePhotoVideoId: livePhotoVideoId,
|
||||
localId: null,
|
||||
stackId: stackId,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
BIN
mobile/lib/infrastructure/entities/store.entity.g.dart
generated
BIN
mobile/lib/infrastructure/entities/store.entity.g.dart
generated
Binary file not shown.
|
|
@ -43,36 +43,36 @@ class User {
|
|||
});
|
||||
|
||||
static User fromDto(UserDto dto) => User(
|
||||
id: dto.id,
|
||||
updatedAt: dto.updatedAt,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
isAdmin: dto.isAdmin,
|
||||
isPartnerSharedBy: dto.isPartnerSharedBy,
|
||||
isPartnerSharedWith: dto.isPartnerSharedWith,
|
||||
profileImagePath: dto.profileImagePath ?? "",
|
||||
avatarColor: dto.avatarColor,
|
||||
memoryEnabled: dto.memoryEnabled,
|
||||
inTimeline: dto.inTimeline,
|
||||
quotaUsageInBytes: dto.quotaUsageInBytes,
|
||||
quotaSizeInBytes: dto.quotaSizeInBytes,
|
||||
);
|
||||
id: dto.id,
|
||||
updatedAt: dto.updatedAt,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
isAdmin: dto.isAdmin,
|
||||
isPartnerSharedBy: dto.isPartnerSharedBy,
|
||||
isPartnerSharedWith: dto.isPartnerSharedWith,
|
||||
profileImagePath: dto.profileImagePath ?? "",
|
||||
avatarColor: dto.avatarColor,
|
||||
memoryEnabled: dto.memoryEnabled,
|
||||
inTimeline: dto.inTimeline,
|
||||
quotaUsageInBytes: dto.quotaUsageInBytes,
|
||||
quotaSizeInBytes: dto.quotaSizeInBytes,
|
||||
);
|
||||
|
||||
UserDto toDto() => UserDto(
|
||||
id: id,
|
||||
email: email,
|
||||
name: name,
|
||||
isAdmin: isAdmin,
|
||||
updatedAt: updatedAt,
|
||||
profileImagePath: profileImagePath.isEmpty ? null : profileImagePath,
|
||||
avatarColor: avatarColor,
|
||||
memoryEnabled: memoryEnabled,
|
||||
inTimeline: inTimeline,
|
||||
isPartnerSharedBy: isPartnerSharedBy,
|
||||
isPartnerSharedWith: isPartnerSharedWith,
|
||||
quotaUsageInBytes: quotaUsageInBytes,
|
||||
quotaSizeInBytes: quotaSizeInBytes,
|
||||
);
|
||||
id: id,
|
||||
email: email,
|
||||
name: name,
|
||||
isAdmin: isAdmin,
|
||||
updatedAt: updatedAt,
|
||||
profileImagePath: profileImagePath.isEmpty ? null : profileImagePath,
|
||||
avatarColor: avatarColor,
|
||||
memoryEnabled: memoryEnabled,
|
||||
inTimeline: inTimeline,
|
||||
isPartnerSharedBy: isPartnerSharedBy,
|
||||
isPartnerSharedWith: isPartnerSharedWith,
|
||||
quotaUsageInBytes: quotaUsageInBytes,
|
||||
quotaSizeInBytes: quotaSizeInBytes,
|
||||
);
|
||||
}
|
||||
|
||||
class UserEntity extends Table with DriftDefaultsMixin {
|
||||
|
|
|
|||
Binary file not shown.
BIN
mobile/lib/infrastructure/entities/user.entity.g.dart
generated
BIN
mobile/lib/infrastructure/entities/user.entity.g.dart
generated
Binary file not shown.
Binary file not shown.
|
|
@ -6,21 +6,13 @@ import 'package:photo_manager/photo_manager.dart';
|
|||
class AssetMediaRepository {
|
||||
const AssetMediaRepository();
|
||||
|
||||
Future<Uint8List?> getThumbnail(
|
||||
String id, {
|
||||
int quality = 80,
|
||||
Size size = const Size.square(256),
|
||||
}) =>
|
||||
AssetEntity(
|
||||
id: id,
|
||||
// The below fields are not used in thumbnailDataWithSize but are required
|
||||
// to create an AssetEntity instance. It is faster to create a dummy AssetEntity
|
||||
// instance than to fetch the asset from the device first.
|
||||
typeInt: AssetType.image.index,
|
||||
width: size.width.toInt(),
|
||||
height: size.height.toInt(),
|
||||
).thumbnailDataWithSize(
|
||||
ThumbnailSize(size.width.toInt(), size.height.toInt()),
|
||||
quality: quality,
|
||||
);
|
||||
Future<Uint8List?> getThumbnail(String id, {int quality = 80, Size size = const Size.square(256)}) => AssetEntity(
|
||||
id: id,
|
||||
// The below fields are not used in thumbnailDataWithSize but are required
|
||||
// to create an AssetEntity instance. It is faster to create a dummy AssetEntity
|
||||
// instance than to fetch the asset from the device first.
|
||||
typeInt: AssetType.image.index,
|
||||
width: size.width.toInt(),
|
||||
height: size.height.toInt(),
|
||||
).thumbnailDataWithSize(ThumbnailSize(size.width.toInt(), size.height.toInt()), quality: quality);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
|
|||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.excluded),
|
||||
);
|
||||
..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.excluded));
|
||||
}
|
||||
|
||||
Future<int> getTotalCount() async {
|
||||
|
|
@ -79,9 +77,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
|
|||
|
||||
Future<int> getBackupCount(String userId) async {
|
||||
final query = _db.localAlbumAssetEntity.selectOnly(distinct: true)
|
||||
..addColumns(
|
||||
[_db.localAlbumAssetEntity.assetId],
|
||||
)
|
||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.localAlbumEntity,
|
||||
|
|
@ -112,9 +108,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
|
|||
Future<List<LocalAsset>> getCandidates(String userId) async {
|
||||
final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true)
|
||||
..addColumns([_db.localAlbumEntity.id])
|
||||
..where(
|
||||
_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected),
|
||||
);
|
||||
..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected));
|
||||
|
||||
final query = _db.localAssetEntity.select()
|
||||
..where(
|
||||
|
|
@ -138,11 +132,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
|
|||
) &
|
||||
lae.id.isNotInQuery(_getExcludedSubquery()),
|
||||
)
|
||||
..orderBy(
|
||||
[
|
||||
(localAsset) => OrderingTerm.desc(localAsset.createdAt),
|
||||
],
|
||||
);
|
||||
..orderBy([(localAsset) => OrderingTerm.desc(localAsset.createdAt)]);
|
||||
|
||||
return query.map((localAsset) => localAsset.toDto()).get();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,66 +59,58 @@ class IsarDatabaseRepository implements IDatabaseRepository {
|
|||
PersonEntity,
|
||||
AssetFaceEntity,
|
||||
],
|
||||
include: {
|
||||
'package:immich_mobile/infrastructure/entities/merged_asset.drift',
|
||||
},
|
||||
include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'},
|
||||
)
|
||||
class Drift extends $Drift implements IDatabaseRepository {
|
||||
Drift([QueryExecutor? executor])
|
||||
: super(
|
||||
executor ??
|
||||
driftDatabase(
|
||||
name: 'immich',
|
||||
native: const DriftNativeOptions(shareAcrossIsolates: true),
|
||||
),
|
||||
);
|
||||
: super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true)));
|
||||
|
||||
@override
|
||||
int get schemaVersion => 4;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
onUpgrade: (m, from, to) async {
|
||||
// Run migration steps without foreign keys and re-enable them later
|
||||
await customStatement('PRAGMA foreign_keys = OFF');
|
||||
onUpgrade: (m, from, to) async {
|
||||
// Run migration steps without foreign keys and re-enable them later
|
||||
await customStatement('PRAGMA foreign_keys = OFF');
|
||||
|
||||
await m.runMigrationSteps(
|
||||
from: from,
|
||||
to: to,
|
||||
steps: migrationSteps(
|
||||
from1To2: (m, v2) async {
|
||||
for (final entity in v2.entities) {
|
||||
await m.drop(entity);
|
||||
await m.create(entity);
|
||||
}
|
||||
},
|
||||
from2To3: (m, v3) async {
|
||||
// Removed foreign key constraint on stack.primaryAssetId
|
||||
await m.alterTable(TableMigration(v3.stackEntity));
|
||||
},
|
||||
from3To4: (m, v4) async {
|
||||
// Thumbnail path column got removed from person_entity
|
||||
await m.alterTable(TableMigration(v4.personEntity));
|
||||
// asset_face_entity is added
|
||||
await m.create(v4.assetFaceEntity);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
if (kDebugMode) {
|
||||
// Fail if the migration broke foreign keys
|
||||
final wrongFKs = await customSelect('PRAGMA foreign_key_check').get();
|
||||
assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}');
|
||||
}
|
||||
|
||||
await customStatement('PRAGMA foreign_keys = ON;');
|
||||
},
|
||||
beforeOpen: (details) async {
|
||||
await customStatement('PRAGMA foreign_keys = ON');
|
||||
await customStatement('PRAGMA synchronous = NORMAL');
|
||||
await customStatement('PRAGMA journal_mode = WAL');
|
||||
},
|
||||
await m.runMigrationSteps(
|
||||
from: from,
|
||||
to: to,
|
||||
steps: migrationSteps(
|
||||
from1To2: (m, v2) async {
|
||||
for (final entity in v2.entities) {
|
||||
await m.drop(entity);
|
||||
await m.create(entity);
|
||||
}
|
||||
},
|
||||
from2To3: (m, v3) async {
|
||||
// Removed foreign key constraint on stack.primaryAssetId
|
||||
await m.alterTable(TableMigration(v3.stackEntity));
|
||||
},
|
||||
from3To4: (m, v4) async {
|
||||
// Thumbnail path column got removed from person_entity
|
||||
await m.alterTable(TableMigration(v4.personEntity));
|
||||
// asset_face_entity is added
|
||||
await m.create(v4.assetFaceEntity);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
if (kDebugMode) {
|
||||
// Fail if the migration broke foreign keys
|
||||
final wrongFKs = await customSelect('PRAGMA foreign_key_check').get();
|
||||
assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}');
|
||||
}
|
||||
|
||||
await customStatement('PRAGMA foreign_keys = ON;');
|
||||
},
|
||||
beforeOpen: (details) async {
|
||||
await customStatement('PRAGMA foreign_keys = ON');
|
||||
await customStatement('PRAGMA synchronous = NORMAL');
|
||||
await customStatement('PRAGMA journal_mode = WAL');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class DriftDatabaseRepository implements IDatabaseRepository {
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -33,9 +33,7 @@ class IsarExifRepository extends IsarDatabaseRepository {
|
|||
|
||||
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos) {
|
||||
return transaction(() async {
|
||||
await _db.exifInfos.putAll(
|
||||
exifInfos.map(entity.ExifInfo.fromDto).toList(),
|
||||
);
|
||||
await _db.exifInfos.putAll(exifInfos.map(entity.ExifInfo.fromDto).toList());
|
||||
return exifInfos;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||
final Drift _db;
|
||||
final Platform _platform;
|
||||
const DriftLocalAlbumRepository(this._db, {Platform? platform})
|
||||
: _platform = platform ?? const LocalPlatform(),
|
||||
super(_db);
|
||||
: _platform = platform ?? const LocalPlatform(),
|
||||
super(_db);
|
||||
|
||||
Future<List<LocalAlbum>> getAll({Set<SortLocalAlbumsBy> sortBy = const {}}) {
|
||||
final assetCount = _db.localAlbumAssetEntity.assetId.count();
|
||||
|
|
@ -34,41 +34,32 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||
if (sortBy.isNotEmpty) {
|
||||
final orderings = <OrderingTerm>[];
|
||||
for (final sort in sortBy) {
|
||||
orderings.add(
|
||||
switch (sort) {
|
||||
SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id),
|
||||
SortLocalAlbumsBy.backupSelection => OrderingTerm.asc(_db.localAlbumEntity.backupSelection),
|
||||
SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum),
|
||||
SortLocalAlbumsBy.name => OrderingTerm.asc(_db.localAlbumEntity.name),
|
||||
SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount),
|
||||
},
|
||||
);
|
||||
orderings.add(switch (sort) {
|
||||
SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id),
|
||||
SortLocalAlbumsBy.backupSelection => OrderingTerm.asc(_db.localAlbumEntity.backupSelection),
|
||||
SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum),
|
||||
SortLocalAlbumsBy.name => OrderingTerm.asc(_db.localAlbumEntity.name),
|
||||
SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount),
|
||||
});
|
||||
}
|
||||
query.orderBy(orderings);
|
||||
}
|
||||
|
||||
return query
|
||||
.map(
|
||||
(row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0),
|
||||
)
|
||||
.get();
|
||||
return query.map((row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0)).get();
|
||||
}
|
||||
|
||||
Future<void> delete(String albumId) => transaction(() async {
|
||||
// Remove all assets that are only in this particular album
|
||||
// We cannot remove all assets in the album because they might be in other albums in iOS
|
||||
// That is not the case on Android since asset <-> album has one:one mapping
|
||||
final assetsToDelete = _platform.isIOS ? await _getUniqueAssetsInAlbum(albumId) : await getAssetIds(albumId);
|
||||
await _deleteAssets(assetsToDelete);
|
||||
// Remove all assets that are only in this particular album
|
||||
// We cannot remove all assets in the album because they might be in other albums in iOS
|
||||
// That is not the case on Android since asset <-> album has one:one mapping
|
||||
final assetsToDelete = _platform.isIOS ? await _getUniqueAssetsInAlbum(albumId) : await getAssetIds(albumId);
|
||||
await _deleteAssets(assetsToDelete);
|
||||
|
||||
// All the other assets that are still associated will be unlinked automatically on-cascade
|
||||
await _db.managers.localAlbumEntity.filter((a) => a.id.equals(albumId)).delete();
|
||||
});
|
||||
// All the other assets that are still associated will be unlinked automatically on-cascade
|
||||
await _db.managers.localAlbumEntity.filter((a) => a.id.equals(albumId)).delete();
|
||||
});
|
||||
|
||||
Future<void> syncDeletes(
|
||||
String albumId,
|
||||
Iterable<String> assetIdsToKeep,
|
||||
) async {
|
||||
Future<void> syncDeletes(String albumId, Iterable<String> assetIdsToKeep) async {
|
||||
if (assetIdsToKeep.isEmpty) {
|
||||
return Future.value();
|
||||
}
|
||||
|
|
@ -77,12 +68,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||
deleteSmt.where((localAsset) {
|
||||
final subQuery = _db.localAlbumAssetEntity.selectOnly()
|
||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.localAlbumEntity,
|
||||
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
|
||||
),
|
||||
]);
|
||||
..join([innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id))]);
|
||||
subQuery.where(
|
||||
_db.localAlbumEntity.id.equals(albumId) & _db.localAlbumAssetEntity.assetId.isNotIn(assetIdsToKeep),
|
||||
);
|
||||
|
|
@ -109,12 +95,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||
if (toUpsert.isNotEmpty) {
|
||||
await _upsertAssets(toUpsert);
|
||||
await _db.localAlbumAssetEntity.insertAll(
|
||||
toUpsert.map(
|
||||
(a) => LocalAlbumAssetEntityCompanion.insert(
|
||||
assetId: a.id,
|
||||
albumId: localAlbum.id,
|
||||
),
|
||||
),
|
||||
toUpsert.map((a) => LocalAlbumAssetEntityCompanion.insert(assetId: a.id, albumId: localAlbum.id)),
|
||||
mode: InsertMode.insertOrIgnore,
|
||||
);
|
||||
}
|
||||
|
|
@ -162,10 +143,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||
final subQuery = _db.localAlbumAssetEntity.selectOnly()
|
||||
..addColumns([_db.localAlbumAssetEntity.assetId])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.localAlbumEntity,
|
||||
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
|
||||
),
|
||||
innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id)),
|
||||
]);
|
||||
subQuery.where(_db.localAlbumEntity.marker_.isNotNull());
|
||||
return localAsset.id.isInQuery(subQuery);
|
||||
|
|
@ -178,16 +156,12 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
Future<List<LocalAsset>> getAssets(String albumId) {
|
||||
final query = _db.localAlbumAssetEntity.select().join(
|
||||
[
|
||||
innerJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
||||
),
|
||||
],
|
||||
)
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
|
||||
final query =
|
||||
_db.localAlbumAssetEntity.select().join([
|
||||
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
|
||||
])
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
|
||||
return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get();
|
||||
}
|
||||
|
||||
|
|
@ -224,11 +198,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||
batch.insertAll(
|
||||
_db.localAlbumAssetEntity,
|
||||
albumIds.cast<String?>().nonNulls.map(
|
||||
(albumId) => LocalAlbumAssetEntityCompanion.insert(
|
||||
assetId: assetId,
|
||||
albumId: albumId,
|
||||
),
|
||||
),
|
||||
(albumId) => LocalAlbumAssetEntityCompanion.insert(assetId: assetId, albumId: albumId),
|
||||
),
|
||||
onConflict: DoNothing(),
|
||||
);
|
||||
});
|
||||
|
|
@ -237,18 +208,12 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
Future<List<LocalAsset>> getAssetsToHash(String albumId) {
|
||||
final query = _db.localAlbumAssetEntity.select().join(
|
||||
[
|
||||
innerJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
||||
),
|
||||
],
|
||||
)
|
||||
..where(
|
||||
_db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull(),
|
||||
)
|
||||
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
|
||||
final query =
|
||||
_db.localAlbumAssetEntity.select().join([
|
||||
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
|
||||
])
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull())
|
||||
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
|
||||
|
||||
return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get();
|
||||
}
|
||||
|
|
@ -275,10 +240,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||
batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>(
|
||||
_db.localAssetEntity,
|
||||
companion,
|
||||
onConflict: DoUpdate(
|
||||
(_) => companion,
|
||||
where: (old) => old.updatedAt.isNotValue(asset.updatedAt),
|
||||
),
|
||||
onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(asset.updatedAt)),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -351,15 +313,13 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
Future<LocalAsset?> getThumbnail(String albumId) async {
|
||||
final query = _db.localAlbumAssetEntity.select().join([
|
||||
innerJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
||||
),
|
||||
])
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)])
|
||||
..limit(1);
|
||||
final query =
|
||||
_db.localAlbumAssetEntity.select().join([
|
||||
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
|
||||
])
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)])
|
||||
..limit(1);
|
||||
|
||||
final results = await query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get();
|
||||
|
||||
|
|
|
|||
|
|
@ -16,14 +16,11 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
|
|||
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.localAssetEntity.id.equals(id));
|
||||
])..where(_db.localAssetEntity.id.equals(id));
|
||||
|
||||
return query.map((row) {
|
||||
final asset = row.readTable(_db.localAssetEntity).toDto();
|
||||
return asset.copyWith(
|
||||
remoteId: row.read(_db.remoteAssetEntity.id),
|
||||
);
|
||||
return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id));
|
||||
}).watchSingleOrNull();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,30 +13,21 @@ class DriftMemoryRepository extends DriftDatabaseRepository {
|
|||
final now = DateTime.now();
|
||||
final localUtc = DateTime.utc(now.year, now.month, now.day, 0, 0, 0);
|
||||
|
||||
final query = _db.select(_db.memoryEntity).join([
|
||||
leftOuterJoin(
|
||||
_db.memoryAssetEntity,
|
||||
_db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id),
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) &
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline),
|
||||
),
|
||||
])
|
||||
..where(_db.memoryEntity.ownerId.equals(ownerId))
|
||||
..where(_db.memoryEntity.deletedAt.isNull())
|
||||
..where(
|
||||
_db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc),
|
||||
)
|
||||
..where(
|
||||
_db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc),
|
||||
)
|
||||
..orderBy([
|
||||
OrderingTerm.desc(_db.memoryEntity.memoryAt),
|
||||
OrderingTerm.asc(_db.remoteAssetEntity.createdAt),
|
||||
]);
|
||||
final query =
|
||||
_db.select(_db.memoryEntity).join([
|
||||
leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) &
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline),
|
||||
),
|
||||
])
|
||||
..where(_db.memoryEntity.ownerId.equals(ownerId))
|
||||
..where(_db.memoryEntity.deletedAt.isNull())
|
||||
..where(_db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc))
|
||||
..where(_db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc))
|
||||
..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
|
||||
|
||||
final rows = await query.get();
|
||||
|
||||
|
|
@ -59,24 +50,19 @@ class DriftMemoryRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
Future<DriftMemory?> get(String memoryId) async {
|
||||
final query = _db.select(_db.memoryEntity).join([
|
||||
leftOuterJoin(
|
||||
_db.memoryAssetEntity,
|
||||
_db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id),
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) &
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline),
|
||||
),
|
||||
])
|
||||
..where(_db.memoryEntity.id.equals(memoryId))
|
||||
..where(_db.memoryEntity.deletedAt.isNull())
|
||||
..orderBy([
|
||||
OrderingTerm.desc(_db.memoryEntity.memoryAt),
|
||||
OrderingTerm.asc(_db.remoteAssetEntity.createdAt),
|
||||
]);
|
||||
final query =
|
||||
_db.select(_db.memoryEntity).join([
|
||||
leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) &
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline),
|
||||
),
|
||||
])
|
||||
..where(_db.memoryEntity.id.equals(memoryId))
|
||||
..where(_db.memoryEntity.deletedAt.isNull())
|
||||
..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
|
||||
|
||||
final rows = await query.get();
|
||||
|
||||
|
|
|
|||
|
|
@ -9,24 +9,13 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
|
|||
|
||||
Future<List<PartnerUserDto>> getPartners(String userId) {
|
||||
final query = _db.select(_db.partnerEntity).join([
|
||||
innerJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.partnerEntity.sharedById),
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.partnerEntity.sharedWithId.equals(userId),
|
||||
);
|
||||
innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)),
|
||||
])..where(_db.partnerEntity.sharedWithId.equals(userId));
|
||||
|
||||
return query.map((row) {
|
||||
final user = row.readTable(_db.userEntity);
|
||||
final partner = row.readTable(_db.partnerEntity);
|
||||
return PartnerUserDto(
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
inTimeline: partner.inTimeline,
|
||||
);
|
||||
return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
|
||||
}).get();
|
||||
}
|
||||
|
||||
|
|
@ -35,60 +24,33 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
|
|||
final query = _db.select(_db.userEntity)..where((row) => row.id.equals(currentUserId).not());
|
||||
|
||||
return query.map((user) {
|
||||
return PartnerUserDto(
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
inTimeline: false,
|
||||
);
|
||||
return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: false);
|
||||
}).get();
|
||||
}
|
||||
|
||||
// Get users who are sharing their photos WITH the current user
|
||||
Future<List<PartnerUserDto>> getSharedWith(String partnerId) {
|
||||
final query = _db.select(_db.partnerEntity).join([
|
||||
innerJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.partnerEntity.sharedById),
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.partnerEntity.sharedWithId.equals(partnerId),
|
||||
);
|
||||
innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)),
|
||||
])..where(_db.partnerEntity.sharedWithId.equals(partnerId));
|
||||
|
||||
return query.map((row) {
|
||||
final user = row.readTable(_db.userEntity);
|
||||
final partner = row.readTable(_db.partnerEntity);
|
||||
return PartnerUserDto(
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
inTimeline: partner.inTimeline,
|
||||
);
|
||||
return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
|
||||
}).get();
|
||||
}
|
||||
|
||||
// Get users who the current user is sharing their photos TO
|
||||
Future<List<PartnerUserDto>> getSharedBy(String userId) {
|
||||
final query = _db.select(_db.partnerEntity).join([
|
||||
innerJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId),
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.partnerEntity.sharedById.equals(userId),
|
||||
);
|
||||
innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId)),
|
||||
])..where(_db.partnerEntity.sharedById.equals(userId));
|
||||
|
||||
return query.map((row) {
|
||||
final user = row.readTable(_db.userEntity);
|
||||
final partner = row.readTable(_db.partnerEntity);
|
||||
return PartnerUserDto(
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
inTimeline: partner.inTimeline,
|
||||
);
|
||||
return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
|
||||
}).get();
|
||||
}
|
||||
|
||||
|
|
@ -108,35 +70,24 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
|
|||
|
||||
Future<PartnerUserDto?> getPartner(String partnerId, String userId) {
|
||||
final query = _db.select(_db.partnerEntity).join([
|
||||
innerJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.partnerEntity.sharedById),
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.partnerEntity.sharedById.equals(partnerId) & _db.partnerEntity.sharedWithId.equals(userId),
|
||||
);
|
||||
innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)),
|
||||
])..where(_db.partnerEntity.sharedById.equals(partnerId) & _db.partnerEntity.sharedWithId.equals(userId));
|
||||
|
||||
return query.map((row) {
|
||||
final user = row.readTable(_db.userEntity);
|
||||
final partner = row.readTable(_db.partnerEntity);
|
||||
return PartnerUserDto(
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
inTimeline: partner.inTimeline,
|
||||
);
|
||||
return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
|
||||
}).getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<bool> toggleShowInTimeline(PartnerUserDto partner, String userId) {
|
||||
return _db.partnerEntity.update().replace(
|
||||
PartnerEntityCompanion(
|
||||
sharedById: Value(partner.id),
|
||||
sharedWithId: Value(userId),
|
||||
inTimeline: Value(!partner.inTimeline),
|
||||
),
|
||||
);
|
||||
PartnerEntityCompanion(
|
||||
sharedById: Value(partner.id),
|
||||
sharedWithId: Value(userId),
|
||||
inTimeline: Value(!partner.inTimeline),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<int> create(String partnerId, String userId) {
|
||||
|
|
@ -150,8 +101,6 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
Future<void> delete(String partnerId, String userId) {
|
||||
return _db.partnerEntity.deleteWhere(
|
||||
(t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId),
|
||||
);
|
||||
return _db.partnerEntity.deleteWhere((t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||
final Drift _db;
|
||||
const DriftRemoteAlbumRepository(this._db) : super(_db);
|
||||
|
||||
Future<List<RemoteAlbum>> getAll({
|
||||
Set<SortRemoteAlbumsBy> sortBy = const {SortRemoteAlbumsBy.updatedAt},
|
||||
}) {
|
||||
Future<List<RemoteAlbum>> getAll({Set<SortRemoteAlbumsBy> sortBy = const {SortRemoteAlbumsBy.updatedAt}}) {
|
||||
final assetCount = _db.remoteAlbumAssetEntity.assetId.count();
|
||||
|
||||
final query = _db.remoteAlbumEntity.select().join([
|
||||
|
|
@ -32,11 +30,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), useColumns: false),
|
||||
]);
|
||||
query
|
||||
..where(_db.remoteAssetEntity.deletedAt.isNull())
|
||||
|
|
@ -47,22 +41,19 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||
if (sortBy.isNotEmpty) {
|
||||
final orderings = <OrderingTerm>[];
|
||||
for (final sort in sortBy) {
|
||||
orderings.add(
|
||||
switch (sort) {
|
||||
SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id),
|
||||
SortRemoteAlbumsBy.updatedAt => OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt),
|
||||
},
|
||||
);
|
||||
orderings.add(switch (sort) {
|
||||
SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id),
|
||||
SortRemoteAlbumsBy.updatedAt => OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt),
|
||||
});
|
||||
}
|
||||
query.orderBy(orderings);
|
||||
}
|
||||
|
||||
return query
|
||||
.map(
|
||||
(row) => row.readTable(_db.remoteAlbumEntity).toDto(
|
||||
assetCount: row.read(assetCount) ?? 0,
|
||||
ownerName: row.read(_db.userEntity.name)!,
|
||||
),
|
||||
(row) => row
|
||||
.readTable(_db.remoteAlbumEntity)
|
||||
.toDto(assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!),
|
||||
)
|
||||
.get();
|
||||
}
|
||||
|
|
@ -70,42 +61,39 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||
Future<RemoteAlbum?> get(String albumId) {
|
||||
final assetCount = _db.remoteAlbumAssetEntity.assetId.count();
|
||||
|
||||
final query = _db.remoteAlbumEntity.select().join([
|
||||
leftOuterJoin(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull())
|
||||
..addColumns([assetCount])
|
||||
..addColumns([_db.userEntity.name])
|
||||
..groupBy([_db.remoteAlbumEntity.id]);
|
||||
final query =
|
||||
_db.remoteAlbumEntity.select().join([
|
||||
leftOuterJoin(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull())
|
||||
..addColumns([assetCount])
|
||||
..addColumns([_db.userEntity.name])
|
||||
..groupBy([_db.remoteAlbumEntity.id]);
|
||||
|
||||
return query
|
||||
.map(
|
||||
(row) => row.readTable(_db.remoteAlbumEntity).toDto(
|
||||
assetCount: row.read(assetCount) ?? 0,
|
||||
ownerName: row.read(_db.userEntity.name)!,
|
||||
),
|
||||
(row) => row
|
||||
.readTable(_db.remoteAlbumEntity)
|
||||
.toDto(assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!),
|
||||
)
|
||||
.getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<void> create(
|
||||
RemoteAlbum album,
|
||||
List<String> assetIds,
|
||||
) async {
|
||||
Future<void> create(RemoteAlbum album, List<String> assetIds) async {
|
||||
await _db.transaction(() async {
|
||||
final entity = RemoteAlbumEntityCompanion(
|
||||
id: Value(album.id),
|
||||
|
|
@ -123,17 +111,11 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||
|
||||
if (assetIds.isNotEmpty) {
|
||||
final albumAssets = assetIds.map(
|
||||
(assetId) => RemoteAlbumAssetEntityCompanion(
|
||||
albumId: Value(album.id),
|
||||
assetId: Value(assetId),
|
||||
),
|
||||
(assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(album.id), assetId: Value(assetId)),
|
||||
);
|
||||
|
||||
await _db.batch((batch) {
|
||||
batch.insertAll(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
albumAssets,
|
||||
);
|
||||
batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -141,38 +123,30 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||
|
||||
Future<void> update(RemoteAlbum album) async {
|
||||
await _db.remoteAlbumEntity.update().replace(
|
||||
RemoteAlbumEntityCompanion(
|
||||
id: Value(album.id),
|
||||
name: Value(album.name),
|
||||
ownerId: Value(album.ownerId),
|
||||
createdAt: Value(album.createdAt),
|
||||
updatedAt: Value(album.updatedAt),
|
||||
description: Value(album.description),
|
||||
thumbnailAssetId: Value(album.thumbnailAssetId),
|
||||
isActivityEnabled: Value(album.isActivityEnabled),
|
||||
order: Value(album.order),
|
||||
),
|
||||
);
|
||||
RemoteAlbumEntityCompanion(
|
||||
id: Value(album.id),
|
||||
name: Value(album.name),
|
||||
ownerId: Value(album.ownerId),
|
||||
createdAt: Value(album.createdAt),
|
||||
updatedAt: Value(album.updatedAt),
|
||||
description: Value(album.description),
|
||||
thumbnailAssetId: Value(album.thumbnailAssetId),
|
||||
isActivityEnabled: Value(album.isActivityEnabled),
|
||||
order: Value(album.order),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<int> removeAssets(String albumId, List<String> assetIds) {
|
||||
return _db.remoteAlbumAssetEntity.deleteWhere(
|
||||
(tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds),
|
||||
);
|
||||
return _db.remoteAlbumAssetEntity.deleteWhere((tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds));
|
||||
}
|
||||
|
||||
FutureOr<(DateTime, DateTime)> getDateRange(String albumId) {
|
||||
final query = _db.remoteAlbumAssetEntity.selectOnly()
|
||||
..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId))
|
||||
..addColumns([
|
||||
_db.remoteAssetEntity.createdAt.min(),
|
||||
_db.remoteAssetEntity.createdAt.max(),
|
||||
])
|
||||
..addColumns([_db.remoteAssetEntity.createdAt.min(), _db.remoteAssetEntity.createdAt.max()])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||
),
|
||||
innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)),
|
||||
]);
|
||||
|
||||
return query.map((row) {
|
||||
|
|
@ -183,8 +157,9 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
Future<List<UserDto>> getSharedUsers(String albumId) async {
|
||||
final albumUserRows =
|
||||
await (_db.select(_db.remoteAlbumUserEntity)..where((row) => row.albumId.equals(albumId))).get();
|
||||
final albumUserRows = await (_db.select(
|
||||
_db.remoteAlbumUserEntity,
|
||||
)..where((row) => row.albumId.equals(albumId))).get();
|
||||
|
||||
if (albumUserRows.isEmpty) {
|
||||
return [];
|
||||
|
|
@ -214,29 +189,19 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||
|
||||
Future<List<RemoteAsset>> getAssets(String albumId) {
|
||||
final query = _db.remoteAlbumAssetEntity.select().join([
|
||||
innerJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId));
|
||||
innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)),
|
||||
])..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId));
|
||||
|
||||
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
|
||||
}
|
||||
|
||||
Future<int> addAssets(String albumId, List<String> assetIds) async {
|
||||
final albumAssets = assetIds.map(
|
||||
(assetId) => RemoteAlbumAssetEntityCompanion(
|
||||
albumId: Value(albumId),
|
||||
assetId: Value(assetId),
|
||||
),
|
||||
(assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(albumId), assetId: Value(assetId)),
|
||||
);
|
||||
|
||||
await _db.batch((batch) {
|
||||
batch.insertAll(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
albumAssets,
|
||||
);
|
||||
batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets);
|
||||
});
|
||||
|
||||
return assetIds.length;
|
||||
|
|
@ -252,47 +217,41 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||
);
|
||||
|
||||
return _db.batch((batch) {
|
||||
batch.insertAll(
|
||||
_db.remoteAlbumUserEntity,
|
||||
albumUsers,
|
||||
);
|
||||
batch.insertAll(_db.remoteAlbumUserEntity, albumUsers);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> deleteAlbum(String albumId) async {
|
||||
return _db.transaction(() async {
|
||||
await _db.remoteAlbumEntity.deleteWhere(
|
||||
(table) => table.id.equals(albumId),
|
||||
);
|
||||
await _db.remoteAlbumEntity.deleteWhere((table) => table.id.equals(albumId));
|
||||
});
|
||||
}
|
||||
|
||||
Stream<RemoteAlbum?> watchAlbum(String albumId) {
|
||||
final query = _db.remoteAlbumEntity.select().join([
|
||||
leftOuterJoin(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAlbumEntity.id.equals(albumId))
|
||||
..addColumns([_db.userEntity.name])
|
||||
..groupBy([_db.remoteAlbumEntity.id]);
|
||||
final query =
|
||||
_db.remoteAlbumEntity.select().join([
|
||||
leftOuterJoin(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.userEntity,
|
||||
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAlbumEntity.id.equals(albumId))
|
||||
..addColumns([_db.userEntity.name])
|
||||
..groupBy([_db.remoteAlbumEntity.id]);
|
||||
|
||||
return query.map((row) {
|
||||
final album = row.readTable(_db.remoteAlbumEntity).toDto(
|
||||
ownerName: row.read(_db.userEntity.name)!,
|
||||
);
|
||||
final album = row.readTable(_db.remoteAlbumEntity).toDto(ownerName: row.read(_db.userEntity.name)!);
|
||||
return album;
|
||||
}).watchSingleOrNull();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,17 +30,16 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
SingleOrNullSelectable<RemoteAsset?> _assetSelectable(String id) {
|
||||
final query = _db.remoteAssetEntity.select().addColumns([
|
||||
_db.localAssetEntity.id,
|
||||
]).join([
|
||||
leftOuterJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAssetEntity.id.equals(id))
|
||||
..limit(1);
|
||||
final query =
|
||||
_db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([
|
||||
leftOuterJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAssetEntity.id.equals(id))
|
||||
..limit(1);
|
||||
|
||||
return query.map((row) {
|
||||
final asset = row.readTable(_db.remoteAssetEntity).toDto();
|
||||
|
|
@ -57,17 +56,16 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
Stream<RemoteAsset?> watchAsset(String id) {
|
||||
final query = _db.remoteAssetEntity.select().addColumns([
|
||||
_db.localAssetEntity.id,
|
||||
]).join([
|
||||
leftOuterJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAssetEntity.id.equals(id))
|
||||
..limit(1);
|
||||
final query =
|
||||
_db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([
|
||||
leftOuterJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAssetEntity.id.equals(id))
|
||||
..limit(1);
|
||||
|
||||
return query.map((row) {
|
||||
final asset = row.readTable(_db.remoteAssetEntity).toDto();
|
||||
|
|
@ -81,9 +79,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
final query = _db.remoteAssetEntity.select()
|
||||
..where(
|
||||
(row) => row.stackId.equals(asset.stackId!) & row.id.equals(asset.id).not(),
|
||||
)
|
||||
..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();
|
||||
|
|
@ -102,24 +98,22 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
|||
"asset",
|
||||
);
|
||||
|
||||
final query = asset.selectOnly().join([
|
||||
innerJoin(
|
||||
_db.remoteExifEntity,
|
||||
_db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..addColumns([
|
||||
_db.remoteExifEntity.city,
|
||||
_db.remoteExifEntity.assetId,
|
||||
])
|
||||
..where(
|
||||
_db.remoteExifEntity.city.isNotNull() &
|
||||
asset.ref(_db.remoteAssetEntity.deletedAt).isNull() &
|
||||
asset.ref(_db.remoteAssetEntity.visibility).equals(AssetVisibility.timeline.index),
|
||||
)
|
||||
..groupBy([_db.remoteExifEntity.city])
|
||||
..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]);
|
||||
final query =
|
||||
asset.selectOnly().join([
|
||||
innerJoin(
|
||||
_db.remoteExifEntity,
|
||||
_db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..addColumns([_db.remoteExifEntity.city, _db.remoteExifEntity.assetId])
|
||||
..where(
|
||||
_db.remoteExifEntity.city.isNotNull() &
|
||||
asset.ref(_db.remoteAssetEntity.deletedAt).isNull() &
|
||||
asset.ref(_db.remoteAssetEntity.visibility).equals(AssetVisibility.timeline.index),
|
||||
)
|
||||
..groupBy([_db.remoteExifEntity.city])
|
||||
..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]);
|
||||
|
||||
return query.map((row) {
|
||||
final assetId = row.read(_db.remoteExifEntity.assetId);
|
||||
|
|
@ -185,10 +179,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
|||
for (final id in ids) {
|
||||
batch.update(
|
||||
_db.remoteExifEntity,
|
||||
RemoteExifEntityCompanion(
|
||||
latitude: Value(location.latitude),
|
||||
longitude: Value(location.longitude),
|
||||
),
|
||||
RemoteExifEntityCompanion(latitude: Value(location.latitude), longitude: Value(location.longitude)),
|
||||
where: (e) => e.assetId.equals(id),
|
||||
);
|
||||
}
|
||||
|
|
@ -205,23 +196,14 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
|||
await _db.stackEntity.deleteWhere((row) => row.id.isIn(stackIds));
|
||||
|
||||
await _db.batch((batch) {
|
||||
final companion = StackEntityCompanion(
|
||||
ownerId: Value(userId),
|
||||
primaryAssetId: Value(stack.primaryAssetId),
|
||||
);
|
||||
final companion = StackEntityCompanion(ownerId: Value(userId), primaryAssetId: Value(stack.primaryAssetId));
|
||||
|
||||
batch.insert(
|
||||
_db.stackEntity,
|
||||
companion.copyWith(id: Value(stack.id)),
|
||||
onConflict: DoUpdate((_) => companion),
|
||||
);
|
||||
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),
|
||||
),
|
||||
RemoteAssetEntityCompanion(stackId: Value(stack.id)),
|
||||
where: (e) => e.id.equals(assetId),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,12 +66,5 @@ class SearchApiRepository extends ApiRepository {
|
|||
String? state,
|
||||
String? make,
|
||||
String? model,
|
||||
}) =>
|
||||
_api.getSearchSuggestions(
|
||||
type,
|
||||
country: country,
|
||||
state: state,
|
||||
make: make,
|
||||
model: model,
|
||||
);
|
||||
}) => _api.getSearchSuggestions(type, country: country, state: state, make: make, model: model);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,6 @@ class DriftStackRepository extends DriftDatabaseRepository {
|
|||
|
||||
extension on StackEntityData {
|
||||
Stack toDto() {
|
||||
return Stack(
|
||||
id: id,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
ownerId: ownerId,
|
||||
primaryAssetId: primaryAssetId,
|
||||
);
|
||||
return Stack(id: id, createdAt: createdAt, updatedAt: updatedAt, ownerId: ownerId, primaryAssetId: primaryAssetId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,7 @@ class IsarStoreRepository extends IsarDatabaseRepository {
|
|||
.filter()
|
||||
.anyOf(validStoreKeys, (query, id) => query.idEqualTo(id))
|
||||
.watch(fireImmediately: true)
|
||||
.asyncExpand(
|
||||
(entities) => Stream.fromFutures(
|
||||
entities.map((e) async => _toUpdateEvent(e)),
|
||||
),
|
||||
);
|
||||
.asyncExpand((entities) => Stream.fromFutures(entities.map((e) async => _toUpdateEvent(e))));
|
||||
}
|
||||
|
||||
Future<void> delete<T>(StoreKey<T> key) async {
|
||||
|
|
@ -68,14 +64,17 @@ class IsarStoreRepository extends IsarDatabaseRepository {
|
|||
return StoreDto(key, value);
|
||||
}
|
||||
|
||||
Future<T?> _toValue<T>(StoreKey<T> key, StoreValue entity) async => switch (key.type) {
|
||||
const (int) => entity.intValue,
|
||||
const (String) => entity.strValue,
|
||||
const (bool) => entity.intValue == 1,
|
||||
const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!),
|
||||
const (UserDto) => entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!),
|
||||
_ => null,
|
||||
} as T?;
|
||||
Future<T?> _toValue<T>(StoreKey<T> key, StoreValue entity) async =>
|
||||
switch (key.type) {
|
||||
const (int) => entity.intValue,
|
||||
const (String) => entity.strValue,
|
||||
const (bool) => entity.intValue == 1,
|
||||
const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!),
|
||||
const (UserDto) =>
|
||||
entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!),
|
||||
_ => null,
|
||||
}
|
||||
as T?;
|
||||
|
||||
Future<StoreValue> _fromValue<T>(StoreKey<T> key, T value) async {
|
||||
final (int? intValue, String? strValue) = switch (key.type) {
|
||||
|
|
@ -83,13 +82,8 @@ class IsarStoreRepository extends IsarDatabaseRepository {
|
|||
const (String) => (null, value as String),
|
||||
const (bool) => ((value as bool) ? 1 : 0, null),
|
||||
const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null),
|
||||
const (UserDto) => (
|
||||
null,
|
||||
(await IsarUserRepository(_db).update(value as UserDto)).id,
|
||||
),
|
||||
_ => throw UnsupportedError(
|
||||
"Unsupported primitive type: ${key.type} for key: ${key.name}",
|
||||
),
|
||||
const (UserDto) => (null, (await IsarUserRepository(_db).update(value as UserDto)).id),
|
||||
_ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"),
|
||||
};
|
||||
return StoreValue(key.id, intValue: intValue, strValue: strValue);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,10 +27,7 @@ class SyncApiRepository {
|
|||
final client = httpClient ?? http.Client();
|
||||
final endpoint = "${_api.apiClient.basePath}/sync/stream";
|
||||
|
||||
final headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/jsonlines+json',
|
||||
};
|
||||
final headers = {'Content-Type': 'application/json', 'Accept': 'application/jsonlines+json'};
|
||||
|
||||
final headerParams = <String, String>{};
|
||||
await _api.applyToParams([], headerParams);
|
||||
|
|
@ -78,10 +75,7 @@ class SyncApiRepository {
|
|||
|
||||
if (response.statusCode != 200) {
|
||||
final errorBody = await response.stream.bytesToString();
|
||||
throw ApiException(
|
||||
response.statusCode,
|
||||
'Failed to get sync stream: $errorBody',
|
||||
);
|
||||
throw ApiException(response.statusCode, 'Failed to get sync stream: $errorBody');
|
||||
}
|
||||
|
||||
await for (final chunk in response.stream.transform(utf8.decoder)) {
|
||||
|
|
|
|||
|
|
@ -42,16 +42,9 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final user in data) {
|
||||
final companion = UserEntityCompanion(
|
||||
name: Value(user.name),
|
||||
email: Value(user.email),
|
||||
);
|
||||
final companion = UserEntityCompanion(name: Value(user.name), email: Value(user.email));
|
||||
|
||||
batch.insert(
|
||||
_db.userEntity,
|
||||
companion.copyWith(id: Value(user.id)),
|
||||
onConflict: DoUpdate((_) => companion),
|
||||
);
|
||||
batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion));
|
||||
}
|
||||
});
|
||||
} catch (error, stack) {
|
||||
|
|
@ -66,10 +59,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
for (final partner in data) {
|
||||
batch.delete(
|
||||
_db.partnerEntity,
|
||||
PartnerEntityCompanion(
|
||||
sharedById: Value(partner.sharedById),
|
||||
sharedWithId: Value(partner.sharedWithId),
|
||||
),
|
||||
PartnerEntityCompanion(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -87,10 +77,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
|
||||
batch.insert(
|
||||
_db.partnerEntity,
|
||||
companion.copyWith(
|
||||
sharedById: Value(partner.sharedById),
|
||||
sharedWithId: Value(partner.sharedWithId),
|
||||
),
|
||||
companion.copyWith(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)),
|
||||
onConflict: DoUpdate((_) => companion),
|
||||
);
|
||||
}
|
||||
|
|
@ -101,24 +88,16 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> deleteAssetsV1(
|
||||
Iterable<SyncAssetDeleteV1> data, {
|
||||
String debugLabel = 'user',
|
||||
}) async {
|
||||
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data, {String debugLabel = 'user'}) async {
|
||||
try {
|
||||
await _db.remoteAssetEntity.deleteWhere(
|
||||
(row) => row.id.isIn(data.map((e) => e.assetId)),
|
||||
);
|
||||
await _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.assetId)));
|
||||
} catch (error, stack) {
|
||||
_logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateAssetsV1(
|
||||
Iterable<SyncAssetV1> data, {
|
||||
String debugLabel = 'user',
|
||||
}) async {
|
||||
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data, {String debugLabel = 'user'}) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final asset in data) {
|
||||
|
|
@ -152,10 +131,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> updateAssetsExifV1(
|
||||
Iterable<SyncAssetExifV1> data, {
|
||||
String debugLabel = 'user',
|
||||
}) async {
|
||||
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data, {String debugLabel = 'user'}) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final exif in data) {
|
||||
|
|
@ -191,20 +167,14 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
});
|
||||
} catch (error, stack) {
|
||||
_logger.severe(
|
||||
'Error: updateAssetsExifV1 - $debugLabel',
|
||||
error,
|
||||
stack,
|
||||
);
|
||||
_logger.severe('Error: updateAssetsExifV1 - $debugLabel', error, stack);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async {
|
||||
try {
|
||||
await _db.remoteAlbumEntity.deleteWhere(
|
||||
(row) => row.id.isIn(data.map((e) => e.albumId)),
|
||||
);
|
||||
await _db.remoteAlbumEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.albumId)));
|
||||
} catch (error, stack) {
|
||||
_logger.severe('Error: deleteAlbumsV1', error, stack);
|
||||
rethrow;
|
||||
|
|
@ -245,10 +215,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
for (final album in data) {
|
||||
batch.delete(
|
||||
_db.remoteAlbumUserEntity,
|
||||
RemoteAlbumUserEntityCompanion(
|
||||
albumId: Value(album.albumId),
|
||||
userId: Value(album.userId),
|
||||
),
|
||||
RemoteAlbumUserEntityCompanion(albumId: Value(album.albumId), userId: Value(album.userId)),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -258,49 +225,32 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> updateAlbumUsersV1(
|
||||
Iterable<SyncAlbumUserV1> data, {
|
||||
String debugLabel = 'user',
|
||||
}) async {
|
||||
Future<void> updateAlbumUsersV1(Iterable<SyncAlbumUserV1> data, {String debugLabel = 'user'}) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final album in data) {
|
||||
final companion = RemoteAlbumUserEntityCompanion(
|
||||
role: Value(album.role.toAlbumUserRole()),
|
||||
);
|
||||
final companion = RemoteAlbumUserEntityCompanion(role: Value(album.role.toAlbumUserRole()));
|
||||
|
||||
batch.insert(
|
||||
_db.remoteAlbumUserEntity,
|
||||
companion.copyWith(
|
||||
albumId: Value(album.albumId),
|
||||
userId: Value(album.userId),
|
||||
),
|
||||
companion.copyWith(albumId: Value(album.albumId), userId: Value(album.userId)),
|
||||
onConflict: DoUpdate((_) => companion),
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (error, stack) {
|
||||
_logger.severe(
|
||||
'Error: updateAlbumUsersV1 - $debugLabel',
|
||||
error,
|
||||
stack,
|
||||
);
|
||||
_logger.severe('Error: updateAlbumUsersV1 - $debugLabel', error, stack);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteAlbumToAssetsV1(
|
||||
Iterable<SyncAlbumToAssetDeleteV1> data,
|
||||
) async {
|
||||
Future<void> deleteAlbumToAssetsV1(Iterable<SyncAlbumToAssetDeleteV1> data) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final album in data) {
|
||||
batch.delete(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
RemoteAlbumAssetEntityCompanion(
|
||||
albumId: Value(album.albumId),
|
||||
assetId: Value(album.assetId),
|
||||
),
|
||||
RemoteAlbumAssetEntityCompanion(albumId: Value(album.albumId), assetId: Value(album.assetId)),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -310,10 +260,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> updateAlbumToAssetsV1(
|
||||
Iterable<SyncAlbumToAssetV1> data, {
|
||||
String debugLabel = 'user',
|
||||
}) async {
|
||||
Future<void> updateAlbumToAssetsV1(Iterable<SyncAlbumToAssetV1> data, {String debugLabel = 'user'}) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final album in data) {
|
||||
|
|
@ -322,19 +269,11 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
assetId: Value(album.assetId),
|
||||
);
|
||||
|
||||
batch.insert(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
companion,
|
||||
onConflict: DoNothing(),
|
||||
);
|
||||
batch.insert(_db.remoteAlbumAssetEntity, companion, onConflict: DoNothing());
|
||||
}
|
||||
});
|
||||
} catch (error, stack) {
|
||||
_logger.severe(
|
||||
'Error: updateAlbumToAssetsV1 - $debugLabel',
|
||||
error,
|
||||
stack,
|
||||
);
|
||||
_logger.severe('Error: updateAlbumToAssetsV1 - $debugLabel', error, stack);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
|
@ -371,9 +310,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
|
||||
Future<void> deleteMemoriesV1(Iterable<SyncMemoryDeleteV1> data) async {
|
||||
try {
|
||||
await _db.memoryEntity.deleteWhere(
|
||||
(row) => row.id.isIn(data.map((e) => e.memoryId)),
|
||||
);
|
||||
await _db.memoryEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.memoryId)));
|
||||
} catch (error, stack) {
|
||||
_logger.severe('Error: deleteMemoriesV1', error, stack);
|
||||
rethrow;
|
||||
|
|
@ -384,16 +321,9 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final asset in data) {
|
||||
final companion = MemoryAssetEntityCompanion(
|
||||
memoryId: Value(asset.memoryId),
|
||||
assetId: Value(asset.assetId),
|
||||
);
|
||||
final companion = MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId));
|
||||
|
||||
batch.insert(
|
||||
_db.memoryAssetEntity,
|
||||
companion,
|
||||
onConflict: DoNothing(),
|
||||
);
|
||||
batch.insert(_db.memoryAssetEntity, companion, onConflict: DoNothing());
|
||||
}
|
||||
});
|
||||
} catch (error, stack) {
|
||||
|
|
@ -402,18 +332,13 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> deleteMemoryAssetsV1(
|
||||
Iterable<SyncMemoryAssetDeleteV1> data,
|
||||
) async {
|
||||
Future<void> deleteMemoryAssetsV1(Iterable<SyncMemoryAssetDeleteV1> data) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final asset in data) {
|
||||
batch.delete(
|
||||
_db.memoryAssetEntity,
|
||||
MemoryAssetEntityCompanion(
|
||||
memoryId: Value(asset.memoryId),
|
||||
assetId: Value(asset.assetId),
|
||||
),
|
||||
MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId)),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -423,10 +348,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> updateStacksV1(
|
||||
Iterable<SyncStackV1> data, {
|
||||
String debugLabel = 'user',
|
||||
}) async {
|
||||
Future<void> updateStacksV1(Iterable<SyncStackV1> data, {String debugLabel = 'user'}) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final stack in data) {
|
||||
|
|
@ -450,36 +372,24 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> deleteStacksV1(
|
||||
Iterable<SyncStackDeleteV1> data, {
|
||||
String debugLabel = 'user',
|
||||
}) async {
|
||||
Future<void> deleteStacksV1(Iterable<SyncStackDeleteV1> data, {String debugLabel = 'user'}) async {
|
||||
try {
|
||||
await _db.stackEntity.deleteWhere(
|
||||
(row) => row.id.isIn(data.map((e) => e.stackId)),
|
||||
);
|
||||
await _db.stackEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.stackId)));
|
||||
} catch (error, stack) {
|
||||
_logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateUserMetadatasV1(
|
||||
Iterable<SyncUserMetadataV1> data,
|
||||
) async {
|
||||
Future<void> updateUserMetadatasV1(Iterable<SyncUserMetadataV1> data) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final userMetadata in data) {
|
||||
final companion = UserMetadataEntityCompanion(
|
||||
value: Value(userMetadata.value as Map<String, Object?>),
|
||||
);
|
||||
final companion = UserMetadataEntityCompanion(value: Value(userMetadata.value as Map<String, Object?>));
|
||||
|
||||
batch.insert(
|
||||
_db.userMetadataEntity,
|
||||
companion.copyWith(
|
||||
userId: Value(userMetadata.userId),
|
||||
key: Value(userMetadata.key.toUserMetadataKey()),
|
||||
),
|
||||
companion.copyWith(userId: Value(userMetadata.userId), key: Value(userMetadata.key.toUserMetadataKey())),
|
||||
onConflict: DoUpdate((_) => companion),
|
||||
);
|
||||
}
|
||||
|
|
@ -490,9 +400,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> deleteUserMetadatasV1(
|
||||
Iterable<SyncUserMetadataDeleteV1> data,
|
||||
) async {
|
||||
Future<void> deleteUserMetadatasV1(Iterable<SyncUserMetadataDeleteV1> data) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final userMetadata in data) {
|
||||
|
|
@ -540,16 +448,11 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> deletePeopleV1(
|
||||
Iterable<SyncPersonDeleteV1> data,
|
||||
) async {
|
||||
Future<void> deletePeopleV1(Iterable<SyncPersonDeleteV1> data) async {
|
||||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final person in data) {
|
||||
batch.deleteWhere(
|
||||
_db.personEntity,
|
||||
(row) => row.id.equals(person.personId),
|
||||
);
|
||||
batch.deleteWhere(_db.personEntity, (row) => row.id.equals(person.personId));
|
||||
}
|
||||
});
|
||||
} catch (error, stack) {
|
||||
|
|
@ -591,10 +494,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
try {
|
||||
await _db.batch((batch) {
|
||||
for (final assetFace in data) {
|
||||
batch.deleteWhere(
|
||||
_db.assetFaceEntity,
|
||||
(row) => row.id.equals(assetFace.assetFaceId),
|
||||
);
|
||||
batch.deleteWhere(_db.assetFaceEntity, (row) => row.id.equals(assetFace.assetFaceId));
|
||||
}
|
||||
});
|
||||
} catch (error, stack) {
|
||||
|
|
@ -606,54 +506,54 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||
|
||||
extension on AssetTypeEnum {
|
||||
AssetType toAssetType() => switch (this) {
|
||||
AssetTypeEnum.IMAGE => AssetType.image,
|
||||
AssetTypeEnum.VIDEO => AssetType.video,
|
||||
AssetTypeEnum.AUDIO => AssetType.audio,
|
||||
AssetTypeEnum.OTHER => AssetType.other,
|
||||
_ => throw Exception('Unknown AssetType value: $this'),
|
||||
};
|
||||
AssetTypeEnum.IMAGE => AssetType.image,
|
||||
AssetTypeEnum.VIDEO => AssetType.video,
|
||||
AssetTypeEnum.AUDIO => AssetType.audio,
|
||||
AssetTypeEnum.OTHER => AssetType.other,
|
||||
_ => throw Exception('Unknown AssetType value: $this'),
|
||||
};
|
||||
}
|
||||
|
||||
extension on AssetOrder {
|
||||
AlbumAssetOrder toAlbumAssetOrder() => switch (this) {
|
||||
AssetOrder.asc => AlbumAssetOrder.asc,
|
||||
AssetOrder.desc => AlbumAssetOrder.desc,
|
||||
_ => throw Exception('Unknown AssetOrder value: $this'),
|
||||
};
|
||||
AssetOrder.asc => AlbumAssetOrder.asc,
|
||||
AssetOrder.desc => AlbumAssetOrder.desc,
|
||||
_ => throw Exception('Unknown AssetOrder value: $this'),
|
||||
};
|
||||
}
|
||||
|
||||
extension on MemoryType {
|
||||
MemoryTypeEnum toMemoryType() => switch (this) {
|
||||
MemoryType.onThisDay => MemoryTypeEnum.onThisDay,
|
||||
_ => throw Exception('Unknown MemoryType value: $this'),
|
||||
};
|
||||
MemoryType.onThisDay => MemoryTypeEnum.onThisDay,
|
||||
_ => throw Exception('Unknown MemoryType value: $this'),
|
||||
};
|
||||
}
|
||||
|
||||
extension on api.AlbumUserRole {
|
||||
AlbumUserRole toAlbumUserRole() => switch (this) {
|
||||
api.AlbumUserRole.editor => AlbumUserRole.editor,
|
||||
api.AlbumUserRole.viewer => AlbumUserRole.viewer,
|
||||
_ => throw Exception('Unknown AlbumUserRole value: $this'),
|
||||
};
|
||||
api.AlbumUserRole.editor => AlbumUserRole.editor,
|
||||
api.AlbumUserRole.viewer => AlbumUserRole.viewer,
|
||||
_ => throw Exception('Unknown AlbumUserRole value: $this'),
|
||||
};
|
||||
}
|
||||
|
||||
extension on api.AssetVisibility {
|
||||
AssetVisibility toAssetVisibility() => switch (this) {
|
||||
api.AssetVisibility.timeline => AssetVisibility.timeline,
|
||||
api.AssetVisibility.hidden => AssetVisibility.hidden,
|
||||
api.AssetVisibility.archive => AssetVisibility.archive,
|
||||
api.AssetVisibility.locked => AssetVisibility.locked,
|
||||
_ => throw Exception('Unknown AssetVisibility value: $this'),
|
||||
};
|
||||
api.AssetVisibility.timeline => AssetVisibility.timeline,
|
||||
api.AssetVisibility.hidden => AssetVisibility.hidden,
|
||||
api.AssetVisibility.archive => AssetVisibility.archive,
|
||||
api.AssetVisibility.locked => AssetVisibility.locked,
|
||||
_ => throw Exception('Unknown AssetVisibility value: $this'),
|
||||
};
|
||||
}
|
||||
|
||||
extension on api.UserMetadataKey {
|
||||
UserMetadataKey toUserMetadataKey() => switch (this) {
|
||||
api.UserMetadataKey.onboarding => UserMetadataKey.onboarding,
|
||||
api.UserMetadataKey.preferences => UserMetadataKey.preferences,
|
||||
api.UserMetadataKey.license => UserMetadataKey.license,
|
||||
_ => throw Exception('Unknown UserMetadataKey value: $this'),
|
||||
};
|
||||
api.UserMetadataKey.onboarding => UserMetadataKey.onboarding,
|
||||
api.UserMetadataKey.preferences => UserMetadataKey.preferences,
|
||||
api.UserMetadataKey.license => UserMetadataKey.license,
|
||||
_ => throw Exception('Unknown UserMetadataKey value: $this'),
|
||||
};
|
||||
}
|
||||
|
||||
extension on String {
|
||||
|
|
|
|||
|
|
@ -21,9 +21,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
Stream<List<String>> watchTimelineUserIds(String userId) {
|
||||
final query = _db.partnerEntity.selectOnly()
|
||||
..addColumns([_db.partnerEntity.sharedById])
|
||||
..where(
|
||||
_db.partnerEntity.inTimeline.equals(true) & _db.partnerEntity.sharedWithId.equals(userId),
|
||||
);
|
||||
..where(_db.partnerEntity.inTimeline.equals(true) & _db.partnerEntity.sharedWithId.equals(userId));
|
||||
|
||||
return query
|
||||
.map((row) => row.read(_db.partnerEntity.sharedById)!)
|
||||
|
|
@ -33,25 +31,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
TimelineQuery main(List<String> userIds, GroupAssetsBy groupBy) => (
|
||||
bucketSource: () => _watchMainBucket(
|
||||
userIds,
|
||||
groupBy: groupBy,
|
||||
),
|
||||
assetSource: (offset, count) => _getMainBucketAssets(
|
||||
userIds,
|
||||
offset: offset,
|
||||
count: count,
|
||||
),
|
||||
);
|
||||
bucketSource: () => _watchMainBucket(userIds, groupBy: groupBy),
|
||||
assetSource: (offset, count) => _getMainBucketAssets(userIds, offset: offset, count: count),
|
||||
);
|
||||
|
||||
Stream<List<Bucket>> _watchMainBucket(
|
||||
List<String> userIds, {
|
||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||
}) {
|
||||
Stream<List<Bucket>> _watchMainBucket(List<String> userIds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
|
||||
if (groupBy == GroupAssetsBy.none) {
|
||||
throw UnsupportedError(
|
||||
"GroupAssetsBy.none is not supported for watchMainBucket",
|
||||
);
|
||||
throw UnsupportedError("GroupAssetsBy.none is not supported for watchMainBucket");
|
||||
}
|
||||
|
||||
return _db.mergedAssetDrift
|
||||
|
|
@ -64,11 +50,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
.throttle(const Duration(seconds: 3), trailing: true);
|
||||
}
|
||||
|
||||
Future<List<BaseAsset>> _getMainBucketAssets(
|
||||
List<String> userIds, {
|
||||
required int offset,
|
||||
required int count,
|
||||
}) {
|
||||
Future<List<BaseAsset>> _getMainBucketAssets(List<String> userIds, {required int offset, required int count}) {
|
||||
return _db.mergedAssetDrift
|
||||
.mergedAsset(userIds, limit: (_) => Limit(count, offset))
|
||||
.map(
|
||||
|
|
@ -109,21 +91,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
TimelineQuery localAlbum(String albumId, GroupAssetsBy groupBy) => (
|
||||
bucketSource: () => _watchLocalAlbumBucket(
|
||||
albumId,
|
||||
groupBy: groupBy,
|
||||
),
|
||||
assetSource: (offset, count) => _getLocalAlbumBucketAssets(
|
||||
albumId,
|
||||
offset: offset,
|
||||
count: count,
|
||||
),
|
||||
);
|
||||
bucketSource: () => _watchLocalAlbumBucket(albumId, groupBy: groupBy),
|
||||
assetSource: (offset, count) => _getLocalAlbumBucketAssets(albumId, offset: offset, count: count),
|
||||
);
|
||||
|
||||
Stream<List<Bucket>> _watchLocalAlbumBucket(
|
||||
String albumId, {
|
||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||
}) {
|
||||
Stream<List<Bucket>> _watchLocalAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
|
||||
if (groupBy == GroupAssetsBy.none) {
|
||||
return _db.localAlbumAssetEntity
|
||||
.count(where: (row) => row.albumId.equals(albumId))
|
||||
|
|
@ -134,22 +106,23 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
final assetCountExp = _db.localAssetEntity.id.count();
|
||||
final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy);
|
||||
|
||||
final query = _db.localAssetEntity.selectOnly().join([
|
||||
innerJoin(
|
||||
_db.localAlbumAssetEntity,
|
||||
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..addColumns([assetCountExp, dateExp])
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||
..groupBy([dateExp])
|
||||
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||
final query =
|
||||
_db.localAssetEntity.selectOnly().join([
|
||||
innerJoin(
|
||||
_db.localAlbumAssetEntity,
|
||||
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..addColumns([assetCountExp, dateExp])
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||
..groupBy([dateExp])
|
||||
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||
|
||||
return query.map((row) {
|
||||
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||
|
|
@ -158,54 +131,37 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
}).watch();
|
||||
}
|
||||
|
||||
Future<List<BaseAsset>> _getLocalAlbumBucketAssets(
|
||||
String albumId, {
|
||||
required int offset,
|
||||
required int count,
|
||||
}) {
|
||||
final query = _db.localAssetEntity.select().join(
|
||||
[
|
||||
innerJoin(
|
||||
_db.localAlbumAssetEntity,
|
||||
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
],
|
||||
)
|
||||
..addColumns([_db.remoteAssetEntity.id])
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||
..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)])
|
||||
..limit(count, offset: offset);
|
||||
Future<List<BaseAsset>> _getLocalAlbumBucketAssets(String albumId, {required int offset, required int count}) {
|
||||
final query =
|
||||
_db.localAssetEntity.select().join([
|
||||
innerJoin(
|
||||
_db.localAlbumAssetEntity,
|
||||
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
_db.remoteAssetEntity,
|
||||
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..addColumns([_db.remoteAssetEntity.id])
|
||||
..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
|
||||
..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)])
|
||||
..limit(count, offset: offset);
|
||||
|
||||
return query.map((row) {
|
||||
final asset = row.readTable(_db.localAssetEntity).toDto();
|
||||
return asset.copyWith(
|
||||
remoteId: row.read(_db.remoteAssetEntity.id),
|
||||
);
|
||||
return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id));
|
||||
}).get();
|
||||
}
|
||||
|
||||
TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => (
|
||||
bucketSource: () => _watchRemoteAlbumBucket(
|
||||
albumId,
|
||||
groupBy: groupBy,
|
||||
),
|
||||
assetSource: (offset, count) => _getRemoteAlbumBucketAssets(
|
||||
albumId,
|
||||
offset: offset,
|
||||
count: count,
|
||||
),
|
||||
);
|
||||
bucketSource: () => _watchRemoteAlbumBucket(albumId, groupBy: groupBy),
|
||||
assetSource: (offset, count) => _getRemoteAlbumBucketAssets(albumId, offset: offset, count: count),
|
||||
);
|
||||
|
||||
Stream<List<Bucket>> _watchRemoteAlbumBucket(
|
||||
String albumId, {
|
||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||
}) {
|
||||
Stream<List<Bucket>> _watchRemoteAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
|
||||
if (groupBy == GroupAssetsBy.none) {
|
||||
return _db.remoteAlbumAssetEntity
|
||||
.count(where: (row) => row.albumId.equals(albumId))
|
||||
|
|
@ -213,56 +169,53 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
.watch()
|
||||
.map((results) => results.isNotEmpty ? results.first : <Bucket>[])
|
||||
.handleError((error) {
|
||||
return [];
|
||||
});
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).watch().switchMap((albums) {
|
||||
if (albums.isEmpty) {
|
||||
return Stream.value(<Bucket>[]);
|
||||
}
|
||||
return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId)))
|
||||
.watch()
|
||||
.switchMap((albums) {
|
||||
if (albums.isEmpty) {
|
||||
return Stream.value(<Bucket>[]);
|
||||
}
|
||||
|
||||
final album = albums.first;
|
||||
final isAscending = album.order == AlbumAssetOrder.asc;
|
||||
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||
final album = albums.first;
|
||||
final isAscending = album.order == AlbumAssetOrder.asc;
|
||||
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||
|
||||
final query = _db.remoteAssetEntity.selectOnly()
|
||||
..addColumns([assetCountExp, dateExp])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
_db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId),
|
||||
)
|
||||
..groupBy([dateExp]);
|
||||
final query = _db.remoteAssetEntity.selectOnly()
|
||||
..addColumns([assetCountExp, dateExp])
|
||||
..join([
|
||||
innerJoin(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
_db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId))
|
||||
..groupBy([dateExp]);
|
||||
|
||||
if (isAscending) {
|
||||
query.orderBy([OrderingTerm.asc(dateExp)]);
|
||||
} else {
|
||||
query.orderBy([OrderingTerm.desc(dateExp)]);
|
||||
}
|
||||
if (isAscending) {
|
||||
query.orderBy([OrderingTerm.asc(dateExp)]);
|
||||
} else {
|
||||
query.orderBy([OrderingTerm.desc(dateExp)]);
|
||||
}
|
||||
|
||||
return query.map((row) {
|
||||
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||
final assetCount = row.read(assetCountExp)!;
|
||||
return TimeBucket(date: timeline, assetCount: assetCount);
|
||||
}).watch();
|
||||
}).handleError((error) {
|
||||
// If there's an error (e.g., album was deleted), return empty buckets
|
||||
return <Bucket>[];
|
||||
});
|
||||
return query.map((row) {
|
||||
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||
final assetCount = row.read(assetCountExp)!;
|
||||
return TimeBucket(date: timeline, assetCount: assetCount);
|
||||
}).watch();
|
||||
})
|
||||
.handleError((error) {
|
||||
// If there's an error (e.g., album was deleted), return empty buckets
|
||||
return <Bucket>[];
|
||||
});
|
||||
}
|
||||
|
||||
Future<List<BaseAsset>> _getRemoteAlbumBucketAssets(
|
||||
String albumId, {
|
||||
required int offset,
|
||||
required int count,
|
||||
}) async {
|
||||
Future<List<BaseAsset>> _getRemoteAlbumBucketAssets(String albumId, {required int offset, required int count}) async {
|
||||
final albumData = await (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).getSingleOrNull();
|
||||
|
||||
// If album doesn't exist (was deleted), return empty list
|
||||
|
|
@ -272,17 +225,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
|
||||
final isAscending = albumData.order == AlbumAssetOrder.asc;
|
||||
|
||||
final query = _db.remoteAssetEntity.select().join(
|
||||
[
|
||||
innerJoin(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
_db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
],
|
||||
)..where(
|
||||
_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId),
|
||||
);
|
||||
final query = _db.remoteAssetEntity.select().join([
|
||||
innerJoin(
|
||||
_db.remoteAlbumAssetEntity,
|
||||
_db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
])..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId));
|
||||
|
||||
if (isAscending) {
|
||||
query.orderBy([OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
|
||||
|
|
@ -296,69 +245,57 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
}
|
||||
|
||||
TimelineQuery fromAssets(List<BaseAsset> assets) => (
|
||||
bucketSource: () => Stream.value(_generateBuckets(assets.length)),
|
||||
assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList()),
|
||||
);
|
||||
bucketSource: () => Stream.value(_generateBuckets(assets.length)),
|
||||
assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList()),
|
||||
);
|
||||
|
||||
TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
|
||||
filter: (row) =>
|
||||
row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId),
|
||||
groupBy: groupBy,
|
||||
);
|
||||
filter: (row) =>
|
||||
row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId),
|
||||
groupBy: groupBy,
|
||||
);
|
||||
|
||||
TimelineQuery favorite(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
|
||||
filter: (row) => row.deletedAt.isNull() & row.isFavorite.equals(true) & row.ownerId.equals(userId),
|
||||
groupBy: groupBy,
|
||||
);
|
||||
filter: (row) => row.deletedAt.isNull() & row.isFavorite.equals(true) & row.ownerId.equals(userId),
|
||||
groupBy: groupBy,
|
||||
);
|
||||
|
||||
TimelineQuery trash(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
|
||||
filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId),
|
||||
groupBy: groupBy,
|
||||
joinLocal: true,
|
||||
);
|
||||
filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId),
|
||||
groupBy: groupBy,
|
||||
joinLocal: true,
|
||||
);
|
||||
|
||||
TimelineQuery archived(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
|
||||
filter: (row) =>
|
||||
row.deletedAt.isNull() & row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.archive),
|
||||
groupBy: groupBy,
|
||||
);
|
||||
filter: (row) =>
|
||||
row.deletedAt.isNull() & row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.archive),
|
||||
groupBy: groupBy,
|
||||
);
|
||||
|
||||
TimelineQuery locked(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
|
||||
filter: (row) =>
|
||||
row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.locked) & row.ownerId.equals(userId),
|
||||
groupBy: groupBy,
|
||||
);
|
||||
filter: (row) =>
|
||||
row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.locked) & row.ownerId.equals(userId),
|
||||
groupBy: groupBy,
|
||||
);
|
||||
|
||||
TimelineQuery video(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder(
|
||||
filter: (row) =>
|
||||
row.deletedAt.isNull() &
|
||||
row.type.equalsValue(AssetType.video) &
|
||||
row.visibility.equalsValue(AssetVisibility.timeline) &
|
||||
row.ownerId.equals(userId),
|
||||
groupBy: groupBy,
|
||||
);
|
||||
filter: (row) =>
|
||||
row.deletedAt.isNull() &
|
||||
row.type.equalsValue(AssetType.video) &
|
||||
row.visibility.equalsValue(AssetVisibility.timeline) &
|
||||
row.ownerId.equals(userId),
|
||||
groupBy: groupBy,
|
||||
);
|
||||
|
||||
TimelineQuery place(String place, GroupAssetsBy groupBy) => (
|
||||
bucketSource: () => _watchPlaceBucket(
|
||||
place,
|
||||
groupBy: groupBy,
|
||||
),
|
||||
assetSource: (offset, count) => _getPlaceBucketAssets(
|
||||
place,
|
||||
offset: offset,
|
||||
count: count,
|
||||
),
|
||||
);
|
||||
bucketSource: () => _watchPlaceBucket(place, groupBy: groupBy),
|
||||
assetSource: (offset, count) => _getPlaceBucketAssets(place, offset: offset, count: count),
|
||||
);
|
||||
|
||||
Stream<List<Bucket>> _watchPlaceBucket(
|
||||
String place, {
|
||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||
}) {
|
||||
Stream<List<Bucket>> _watchPlaceBucket(String place, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
|
||||
if (groupBy == GroupAssetsBy.none) {
|
||||
// TODO: implement GroupAssetBy for place
|
||||
throw UnsupportedError(
|
||||
"GroupAssetsBy.none is not supported for watchPlaceBucket",
|
||||
);
|
||||
throw UnsupportedError("GroupAssetsBy.none is not supported for watchPlaceBucket");
|
||||
}
|
||||
|
||||
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||
|
|
@ -388,27 +325,22 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
}).watch();
|
||||
}
|
||||
|
||||
Future<List<BaseAsset>> _getPlaceBucketAssets(
|
||||
String place, {
|
||||
required int offset,
|
||||
required int count,
|
||||
}) {
|
||||
final query = _db.remoteAssetEntity.select().join(
|
||||
[
|
||||
innerJoin(
|
||||
_db.remoteExifEntity,
|
||||
_db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
],
|
||||
)
|
||||
..where(
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
|
||||
_db.remoteExifEntity.city.equals(place),
|
||||
)
|
||||
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
|
||||
..limit(count, offset: offset);
|
||||
Future<List<BaseAsset>> _getPlaceBucketAssets(String place, {required int offset, required int count}) {
|
||||
final query =
|
||||
_db.remoteAssetEntity.select().join([
|
||||
innerJoin(
|
||||
_db.remoteExifEntity,
|
||||
_db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(
|
||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
|
||||
_db.remoteExifEntity.city.equals(place),
|
||||
)
|
||||
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
|
||||
..limit(count, offset: offset);
|
||||
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
|
||||
}
|
||||
|
||||
|
|
@ -419,12 +351,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
}) {
|
||||
return (
|
||||
bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy),
|
||||
assetSource: (offset, count) => _getRemoteAssets(
|
||||
filter: filter,
|
||||
offset: offset,
|
||||
count: count,
|
||||
joinLocal: joinLocal,
|
||||
),
|
||||
assetSource: (offset, count) =>
|
||||
_getRemoteAssets(filter: filter, offset: offset, count: count, joinLocal: joinLocal),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -460,17 +388,18 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||
bool joinLocal = false,
|
||||
}) {
|
||||
if (joinLocal) {
|
||||
final query = _db.remoteAssetEntity.select().join([
|
||||
leftOuterJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..addColumns([_db.localAssetEntity.id])
|
||||
..where(filter(_db.remoteAssetEntity))
|
||||
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
|
||||
..limit(count, offset: offset);
|
||||
final query =
|
||||
_db.remoteAssetEntity.select().join([
|
||||
leftOuterJoin(
|
||||
_db.localAssetEntity,
|
||||
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..addColumns([_db.localAssetEntity.id])
|
||||
..where(filter(_db.remoteAssetEntity))
|
||||
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
|
||||
..limit(count, offset: offset);
|
||||
|
||||
return query.map((row) {
|
||||
final asset = row.readTable(_db.remoteAssetEntity).toDto();
|
||||
|
|
@ -507,9 +436,7 @@ extension on Expression<DateTime> {
|
|||
return switch (groupBy) {
|
||||
GroupAssetsBy.day || GroupAssetsBy.auto => localTimeExp.date,
|
||||
GroupAssetsBy.month => localTimeExp.strftime("%Y-%m"),
|
||||
GroupAssetsBy.none => throw ArgumentError(
|
||||
"GroupAssetsBy.none is not supported for date formatting",
|
||||
),
|
||||
GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -519,9 +446,7 @@ extension on String {
|
|||
final format = switch (groupBy) {
|
||||
GroupAssetsBy.day || GroupAssetsBy.auto => "y-M-d",
|
||||
GroupAssetsBy.month => "y-M",
|
||||
GroupAssetsBy.none => throw ArgumentError(
|
||||
"GroupAssetsBy.none is not supported for date formatting",
|
||||
),
|
||||
GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"),
|
||||
};
|
||||
try {
|
||||
return DateFormat(format).parse(this);
|
||||
|
|
|
|||
|
|
@ -17,15 +17,8 @@ class UserApiRepository extends ApiRepository {
|
|||
return UserConverter.fromAdminDto(adminDto, preferenceDto);
|
||||
}
|
||||
|
||||
Future<String> createProfileImage({
|
||||
required String name,
|
||||
required Uint8List data,
|
||||
}) async {
|
||||
final res = await checkNull(
|
||||
_api.createProfileImage(
|
||||
MultipartFile.fromBytes('file', data, filename: name),
|
||||
),
|
||||
);
|
||||
Future<String> createProfileImage({required String name, required Uint8List data}) async {
|
||||
final res = await checkNull(_api.createProfileImage(MultipartFile.fromBytes('file', data, filename: name)));
|
||||
return res.profileImagePath;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,20 +18,8 @@ class DriftUserMetadataRepository extends DriftDatabaseRepository {
|
|||
|
||||
extension on UserMetadataEntityData {
|
||||
UserMetadata toDto() => switch (key) {
|
||||
UserMetadataKey.onboarding => UserMetadata(
|
||||
userId: userId,
|
||||
key: key,
|
||||
onboarding: Onboarding.fromMap(value),
|
||||
),
|
||||
UserMetadataKey.preferences => UserMetadata(
|
||||
userId: userId,
|
||||
key: key,
|
||||
preferences: Preferences.fromMap(value),
|
||||
),
|
||||
UserMetadataKey.license => UserMetadata(
|
||||
userId: userId,
|
||||
key: key,
|
||||
license: License.fromMap(value),
|
||||
),
|
||||
};
|
||||
UserMetadataKey.onboarding => UserMetadata(userId: userId, key: key, onboarding: Onboarding.fromMap(value)),
|
||||
UserMetadataKey.preferences => UserMetadata(userId: userId, key: key, preferences: Preferences.fromMap(value)),
|
||||
UserMetadataKey.license => UserMetadata(userId: userId, key: key, license: License.fromMap(value)),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,63 +6,59 @@ import 'package:openapi/api.dart';
|
|||
abstract final class UserConverter {
|
||||
/// Base user dto used where the complete user object is not required
|
||||
static UserDto fromSimpleUserDto(UserResponseDto dto) => UserDto(
|
||||
id: dto.id,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
isAdmin: false,
|
||||
updatedAt: DateTime.now(),
|
||||
profileImagePath: dto.profileImagePath,
|
||||
avatarColor: dto.avatarColor.toAvatarColor(),
|
||||
);
|
||||
id: dto.id,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
isAdmin: false,
|
||||
updatedAt: DateTime.now(),
|
||||
profileImagePath: dto.profileImagePath,
|
||||
avatarColor: dto.avatarColor.toAvatarColor(),
|
||||
);
|
||||
|
||||
static UserDto fromAdminDto(
|
||||
UserAdminResponseDto adminDto, [
|
||||
UserPreferencesResponseDto? preferenceDto,
|
||||
]) =>
|
||||
UserDto(
|
||||
id: adminDto.id,
|
||||
email: adminDto.email,
|
||||
name: adminDto.name,
|
||||
isAdmin: adminDto.isAdmin,
|
||||
updatedAt: adminDto.updatedAt,
|
||||
profileImagePath: adminDto.profileImagePath,
|
||||
avatarColor: adminDto.avatarColor.toAvatarColor(),
|
||||
memoryEnabled: preferenceDto?.memories.enabled ?? true,
|
||||
inTimeline: false,
|
||||
isPartnerSharedBy: false,
|
||||
isPartnerSharedWith: false,
|
||||
quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0,
|
||||
quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0,
|
||||
);
|
||||
static UserDto fromAdminDto(UserAdminResponseDto adminDto, [UserPreferencesResponseDto? preferenceDto]) => UserDto(
|
||||
id: adminDto.id,
|
||||
email: adminDto.email,
|
||||
name: adminDto.name,
|
||||
isAdmin: adminDto.isAdmin,
|
||||
updatedAt: adminDto.updatedAt,
|
||||
profileImagePath: adminDto.profileImagePath,
|
||||
avatarColor: adminDto.avatarColor.toAvatarColor(),
|
||||
memoryEnabled: preferenceDto?.memories.enabled ?? true,
|
||||
inTimeline: false,
|
||||
isPartnerSharedBy: false,
|
||||
isPartnerSharedWith: false,
|
||||
quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0,
|
||||
quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0,
|
||||
);
|
||||
|
||||
static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto(
|
||||
id: dto.id,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
isAdmin: false,
|
||||
updatedAt: DateTime.now(),
|
||||
profileImagePath: dto.profileImagePath,
|
||||
avatarColor: dto.avatarColor.toAvatarColor(),
|
||||
memoryEnabled: false,
|
||||
inTimeline: dto.inTimeline ?? false,
|
||||
isPartnerSharedBy: false,
|
||||
isPartnerSharedWith: false,
|
||||
quotaUsageInBytes: 0,
|
||||
quotaSizeInBytes: 0,
|
||||
);
|
||||
id: dto.id,
|
||||
email: dto.email,
|
||||
name: dto.name,
|
||||
isAdmin: false,
|
||||
updatedAt: DateTime.now(),
|
||||
profileImagePath: dto.profileImagePath,
|
||||
avatarColor: dto.avatarColor.toAvatarColor(),
|
||||
memoryEnabled: false,
|
||||
inTimeline: dto.inTimeline ?? false,
|
||||
isPartnerSharedBy: false,
|
||||
isPartnerSharedWith: false,
|
||||
quotaUsageInBytes: 0,
|
||||
quotaSizeInBytes: 0,
|
||||
);
|
||||
}
|
||||
|
||||
extension on UserAvatarColor {
|
||||
AvatarColor toAvatarColor() => switch (this) {
|
||||
UserAvatarColor.red => AvatarColor.red,
|
||||
UserAvatarColor.green => AvatarColor.green,
|
||||
UserAvatarColor.blue => AvatarColor.blue,
|
||||
UserAvatarColor.purple => AvatarColor.purple,
|
||||
UserAvatarColor.orange => AvatarColor.orange,
|
||||
UserAvatarColor.pink => AvatarColor.pink,
|
||||
UserAvatarColor.amber => AvatarColor.amber,
|
||||
UserAvatarColor.yellow => AvatarColor.yellow,
|
||||
UserAvatarColor.gray => AvatarColor.gray,
|
||||
UserAvatarColor.primary || _ => AvatarColor.primary,
|
||||
};
|
||||
UserAvatarColor.red => AvatarColor.red,
|
||||
UserAvatarColor.green => AvatarColor.green,
|
||||
UserAvatarColor.blue => AvatarColor.blue,
|
||||
UserAvatarColor.purple => AvatarColor.purple,
|
||||
UserAvatarColor.orange => AvatarColor.orange,
|
||||
UserAvatarColor.pink => AvatarColor.pink,
|
||||
UserAvatarColor.amber => AvatarColor.amber,
|
||||
UserAvatarColor.yellow => AvatarColor.yellow,
|
||||
UserAvatarColor.gray => AvatarColor.gray,
|
||||
UserAvatarColor.primary || _ => AvatarColor.primary,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,10 +50,7 @@ void main() async {
|
|||
|
||||
runApp(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
dbProvider.overrideWithValue(db),
|
||||
isarProvider.overrideWithValue(db),
|
||||
],
|
||||
overrides: [dbProvider.overrideWithValue(db), isarProvider.overrideWithValue(db)],
|
||||
child: const MainWidget(),
|
||||
),
|
||||
);
|
||||
|
|
@ -100,23 +97,15 @@ Future<void> initApp() async {
|
|||
globalConfig: (Config.holdingQueue, (1000, 1000, 1000)),
|
||||
);
|
||||
|
||||
await FileDownloader().trackTasksInGroup(
|
||||
kDownloadGroupLivePhoto,
|
||||
markDownloadedComplete: false,
|
||||
);
|
||||
await FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false);
|
||||
|
||||
await FileDownloader().trackTasks();
|
||||
|
||||
LicenseRegistry.addLicense(
|
||||
() async* {
|
||||
for (final license in nonPubLicenses.entries) {
|
||||
yield LicenseEntryWithLineBreaks(
|
||||
[license.key],
|
||||
license.value,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
LicenseRegistry.addLicense(() async* {
|
||||
for (final license in nonPubLicenses.entries) {
|
||||
yield LicenseEntryWithLineBreaks([license.key], license.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class ImmichApp extends ConsumerStatefulWidget {
|
||||
|
|
@ -160,9 +149,7 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
|
|||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||
|
||||
// Sets the navigation bar color
|
||||
SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(
|
||||
systemNavigationBarColor: Colors.transparent,
|
||||
);
|
||||
SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent);
|
||||
if (Platform.isAndroid) {
|
||||
// Android 8 does not support transparent app bars
|
||||
final info = await DeviceInfoPlugin().androidInfo;
|
||||
|
|
@ -177,40 +164,22 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
|
|||
void _configureFileDownloaderNotifications() {
|
||||
FileDownloader().configureNotificationForGroup(
|
||||
kDownloadGroupImage,
|
||||
running: TaskNotification(
|
||||
'downloading_media'.tr(),
|
||||
'${'file_name'.tr()}: {filename}',
|
||||
),
|
||||
complete: TaskNotification(
|
||||
'download_finished'.tr(),
|
||||
'${'file_name'.tr()}: {filename}',
|
||||
),
|
||||
running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'),
|
||||
complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'),
|
||||
progressBar: true,
|
||||
);
|
||||
|
||||
FileDownloader().configureNotificationForGroup(
|
||||
kDownloadGroupVideo,
|
||||
running: TaskNotification(
|
||||
'downloading_media'.tr(),
|
||||
'${'file_name'.tr()}: {filename}',
|
||||
),
|
||||
complete: TaskNotification(
|
||||
'download_finished'.tr(),
|
||||
'${'file_name'.tr()}: {filename}',
|
||||
),
|
||||
running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'),
|
||||
complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'),
|
||||
progressBar: true,
|
||||
);
|
||||
|
||||
FileDownloader().configureNotificationForGroup(
|
||||
kManualUploadGroup,
|
||||
running: TaskNotification(
|
||||
'uploading_media'.tr(),
|
||||
'${'file_name'.tr()}: {displayName}',
|
||||
),
|
||||
complete: TaskNotification(
|
||||
'upload_finished'.tr(),
|
||||
'${'file_name'.tr()}: {displayName}',
|
||||
),
|
||||
running: TaskNotification('uploading_media'.tr(), '${'file_name'.tr()}: {displayName}'),
|
||||
complete: TaskNotification('upload_finished'.tr(), '${'file_name'.tr()}: {displayName}'),
|
||||
progressBar: true,
|
||||
);
|
||||
}
|
||||
|
|
@ -222,19 +191,13 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
|
|||
final isColdStart = currentRouteName == null || currentRouteName == SplashScreenRoute.name;
|
||||
|
||||
if (deepLink.uri.scheme == "immich") {
|
||||
final proposedRoute = await deepLinkHandler.handleScheme(
|
||||
deepLink,
|
||||
isColdStart,
|
||||
);
|
||||
final proposedRoute = await deepLinkHandler.handleScheme(deepLink, isColdStart);
|
||||
|
||||
return proposedRoute;
|
||||
}
|
||||
|
||||
if (deepLink.uri.host == "my.immich.app") {
|
||||
final proposedRoute = await deepLinkHandler.handleMyImmichApp(
|
||||
deepLink,
|
||||
isColdStart,
|
||||
);
|
||||
final proposedRoute = await deepLinkHandler.handleMyImmichApp(deepLink, isColdStart);
|
||||
|
||||
return proposedRoute;
|
||||
}
|
||||
|
|
@ -275,9 +238,7 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
|
|||
final immichTheme = ref.watch(immichThemeProvider);
|
||||
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
localeProvider.overrideWithValue(context.locale),
|
||||
],
|
||||
overrides: [localeProvider.overrideWithValue(context.locale)],
|
||||
child: MaterialApp.router(
|
||||
title: 'Immich',
|
||||
debugShowCheckedModeBanner: true,
|
||||
|
|
@ -285,14 +246,8 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
|
|||
supportedLocales: context.supportedLocales,
|
||||
locale: context.locale,
|
||||
themeMode: ref.watch(immichThemeModeProvider),
|
||||
darkTheme: getThemeData(
|
||||
colorScheme: immichTheme.dark,
|
||||
locale: context.locale,
|
||||
),
|
||||
theme: getThemeData(
|
||||
colorScheme: immichTheme.light,
|
||||
locale: context.locale,
|
||||
),
|
||||
darkTheme: getThemeData(colorScheme: immichTheme.dark, locale: context.locale),
|
||||
theme: getThemeData(colorScheme: immichTheme.light, locale: context.locale),
|
||||
routerConfig: router.config(
|
||||
deepLinkBuilder: _deepLinkBuilder,
|
||||
navigatorObservers: () => [AppNavigationObserver(ref: ref), HeroController()],
|
||||
|
|
|
|||
|
|
@ -7,15 +7,9 @@ class AlbumAddAssetsResponse {
|
|||
List<String> alreadyInAlbum;
|
||||
int successfullyAdded;
|
||||
|
||||
AlbumAddAssetsResponse({
|
||||
required this.alreadyInAlbum,
|
||||
required this.successfullyAdded,
|
||||
});
|
||||
AlbumAddAssetsResponse({required this.alreadyInAlbum, required this.successfullyAdded});
|
||||
|
||||
AlbumAddAssetsResponse copyWith({
|
||||
List<String>? alreadyInAlbum,
|
||||
int? successfullyAdded,
|
||||
}) {
|
||||
AlbumAddAssetsResponse copyWith({List<String>? alreadyInAlbum, int? successfullyAdded}) {
|
||||
return AlbumAddAssetsResponse(
|
||||
alreadyInAlbum: alreadyInAlbum ?? this.alreadyInAlbum,
|
||||
successfullyAdded: successfullyAdded ?? this.successfullyAdded,
|
||||
|
|
@ -23,10 +17,7 @@ class AlbumAddAssetsResponse {
|
|||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'alreadyInAlbum': alreadyInAlbum,
|
||||
'successfullyAdded': successfullyAdded,
|
||||
};
|
||||
return <String, dynamic>{'alreadyInAlbum': alreadyInAlbum, 'successfullyAdded': successfullyAdded};
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
|
|
|||
|
|
@ -1,5 +1 @@
|
|||
enum QuickFilterMode {
|
||||
all,
|
||||
sharedWithMe,
|
||||
myAlbums,
|
||||
}
|
||||
enum QuickFilterMode { all, sharedWithMe, myAlbums }
|
||||
|
|
|
|||
|
|
@ -11,11 +11,7 @@ class AlbumViewerPageState {
|
|||
required this.editDescriptionText,
|
||||
});
|
||||
|
||||
AlbumViewerPageState copyWith({
|
||||
bool? isEditAlbum,
|
||||
String? editTitleText,
|
||||
String? editDescriptionText,
|
||||
}) {
|
||||
AlbumViewerPageState copyWith({bool? isEditAlbum, String? editTitleText, String? editDescriptionText}) {
|
||||
return AlbumViewerPageState(
|
||||
isEditAlbum: isEditAlbum ?? this.isEditAlbum,
|
||||
editTitleText: editTitleText ?? this.editTitleText,
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
|||
class AssetSelectionPageResult {
|
||||
final Set<Asset> selectedAssets;
|
||||
|
||||
const AssetSelectionPageResult({
|
||||
required this.selectedAssets,
|
||||
});
|
||||
const AssetSelectionPageResult({required this.selectedAssets});
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
|
|
|||
|
|
@ -13,12 +13,7 @@ class AssetSelectionState {
|
|||
this.selectedCount = 0,
|
||||
});
|
||||
|
||||
AssetSelectionState copyWith({
|
||||
bool? hasRemote,
|
||||
bool? hasLocal,
|
||||
bool? hasMerged,
|
||||
int? selectedCount,
|
||||
}) {
|
||||
AssetSelectionState copyWith({bool? hasRemote, bool? hasLocal, bool? hasMerged, int? selectedCount}) {
|
||||
return AssetSelectionState(
|
||||
hasRemote: hasRemote ?? this.hasRemote,
|
||||
hasLocal: hasLocal ?? this.hasLocal,
|
||||
|
|
@ -28,10 +23,10 @@ class AssetSelectionState {
|
|||
}
|
||||
|
||||
AssetSelectionState.fromSelection(Set<Asset> selection)
|
||||
: hasLocal = selection.any((e) => e.storage == AssetState.local),
|
||||
hasMerged = selection.any((e) => e.storage == AssetState.merged),
|
||||
hasRemote = selection.any((e) => e.storage == AssetState.remote),
|
||||
selectedCount = selection.length;
|
||||
: hasLocal = selection.any((e) => e.storage == AssetState.local),
|
||||
hasMerged = selection.any((e) => e.storage == AssetState.merged),
|
||||
hasRemote = selection.any((e) => e.storage == AssetState.remote),
|
||||
selectedCount = selection.length;
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue