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:
shenlong 2025-07-29 00:34:03 +05:30 committed by GitHub
parent 9b3718120b
commit e52b9d15b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
643 changed files with 6519 additions and 18431 deletions

View file

@ -1,17 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
enum ImmichColorPreset { enum ImmichColorPreset { indigo, deepPurple, pink, red, orange, yellow, lime, green, cyan, slateGray }
indigo,
deepPurple,
pink,
red,
orange,
yellow,
lime,
green,
cyan,
slateGray,
}
const ImmichColorPreset defaultColorPreset = ImmichColorPreset.indigo; const ImmichColorPreset defaultColorPreset = ImmichColorPreset.indigo;
const String defaultColorPresetName = "indigo"; const String defaultColorPresetName = "indigo";

View file

@ -1,13 +1,6 @@
enum SortOrder { enum SortOrder { asc, desc }
asc,
desc,
}
enum TextSearchType { enum TextSearchType { context, filename, description }
context,
filename,
description,
}
enum AssetVisibilityEnum { timeline, hidden, archive, locked } enum AssetVisibilityEnum { timeline, hidden, archive, locked }

View file

@ -2,511 +2,49 @@ import 'package:flutter/material.dart';
const List<ColorFilter> filters = [ const List<ColorFilter> filters = [
//Original //Original
ColorFilter.matrix([ ColorFilter.matrix([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]),
1,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
1,
0,
]),
//Vintage //Vintage
ColorFilter.matrix([ 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]),
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 //Mood
ColorFilter.matrix([ 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]),
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 //Crisp
ColorFilter.matrix([ 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]),
1.2,
0,
0,
0,
0,
0,
1.2,
0,
0,
0,
0,
0,
1.2,
0,
0,
0,
0,
0,
1,
0,
]),
//Cool //Cool
ColorFilter.matrix([ 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]),
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 //Blush
ColorFilter.matrix([ 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]),
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 //Sunkissed
ColorFilter.matrix([ 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]),
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 //Fresh
ColorFilter.matrix([ 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]),
1.2,
0,
0,
0,
20,
0,
1.2,
0,
0,
20,
0,
0,
1.1,
0,
20,
0,
0,
0,
1,
0,
]),
//Classic //Classic
ColorFilter.matrix([ 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]),
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 //Lomo-ish
ColorFilter.matrix([ 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]),
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 //Nashville
ColorFilter.matrix([ 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]),
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 //Valencia
ColorFilter.matrix([ 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]),
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 //Clarendon
ColorFilter.matrix([ 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]),
1.2,
0,
0,
0,
10,
0,
1.25,
0,
0,
10,
0,
0,
1.3,
0,
10,
0,
0,
0,
1,
0,
]),
//Moon //Moon
ColorFilter.matrix([ 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]),
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 //Willow
ColorFilter.matrix([ 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]),
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 //Kodak
ColorFilter.matrix([ 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]),
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 //Frost
ColorFilter.matrix([ 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]),
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 //Night Vision
ColorFilter.matrix([ 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]),
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 //Sunset
ColorFilter.matrix([ 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]),
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 //Noir
ColorFilter.matrix([ 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]),
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 //Dreamy
ColorFilter.matrix([ 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]),
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 //Sepia
ColorFilter.matrix([ 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]),
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 //Radium
ColorFilter.matrix([ ColorFilter.matrix([
1.438, 1.438,
@ -554,212 +92,23 @@ const List<ColorFilter> filters = [
0, 0,
]), ]),
//Purple Haze //Purple Haze
ColorFilter.matrix([ 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]),
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 //Lemonade
ColorFilter.matrix([ 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]),
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 //Caramel
ColorFilter.matrix([ 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]),
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 //Peachy
ColorFilter.matrix([ 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]),
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 //Neon
ColorFilter.matrix([ ColorFilter.matrix([1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0]),
1,
0,
1,
0,
0,
0,
2,
0,
0,
0,
0,
0,
3,
0,
0,
0,
0,
0,
1,
0,
]),
//Cold Morning //Cold Morning
ColorFilter.matrix([ 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]),
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 //Lush
ColorFilter.matrix([ 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]),
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 //Urban Neon
ColorFilter.matrix([ 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]),
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 //Monochrome
ColorFilter.matrix([ 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]),
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 = [ const List<String> filterNames = [

View file

@ -51,7 +51,4 @@ const Map<String, Locale> locales = {
const String translationsPath = 'assets/i18n'; const String translationsPath = 'assets/i18n';
const List<Locale> localesNotSupportedByOverpass = [ const List<Locale> localesNotSupportedByOverpass = [Locale('el', 'GR'), Locale('sr', 'Cyrl')];
Locale('el', 'GR'),
Locale('sr', 'Cyrl'),
];

View file

@ -9,11 +9,7 @@ enum AssetType {
audio, audio,
} }
enum AssetState { enum AssetState { local, remote, merged }
local,
remote,
merged,
}
sealed class BaseAsset { sealed class BaseAsset {
final String name; final String name;

View file

@ -1,11 +1,6 @@
part of 'base_asset.model.dart'; part of 'base_asset.model.dart';
enum AssetVisibility { enum AssetVisibility { timeline, hidden, archive, locked }
timeline,
hidden,
archive,
locked,
}
// Model for an asset stored in the server // Model for an asset stored in the server
class RemoteAsset extends BaseAsset { class RemoteAsset extends BaseAsset {

View file

@ -5,11 +5,7 @@ class DeviceAsset {
final Uint8List hash; final Uint8List hash;
final DateTime modifiedTime; final DateTime modifiedTime;
const DeviceAsset({ const DeviceAsset({required this.assetId, required this.hash, required this.modifiedTime});
required this.assetId,
required this.hash,
required this.modifiedTime,
});
@override @override
bool operator ==(covariant DeviceAsset other) { bool operator ==(covariant DeviceAsset other) {
@ -28,11 +24,7 @@ class DeviceAsset {
return 'DeviceAsset(assetId: $assetId, hash: $hash, modifiedTime: $modifiedTime)'; return 'DeviceAsset(assetId: $assetId, hash: $hash, modifiedTime: $modifiedTime)';
} }
DeviceAsset copyWith({ DeviceAsset copyWith({String? assetId, Uint8List? hash, DateTime? modifiedTime}) {
String? assetId,
Uint8List? hash,
DateTime? modifiedTime,
}) {
return DeviceAsset( return DeviceAsset(
assetId: assetId ?? this.assetId, assetId: assetId ?? this.assetId,
hash: hash ?? this.hash, hash: hash ?? this.hash,

View file

@ -1,16 +1,5 @@
/// Log levels according to dart logging [Level] /// Log levels according to dart logging [Level]
enum LogLevel { enum LogLevel { all, finest, finer, fine, config, info, warning, severe, shout, off }
all,
finest,
finer,
fine,
config,
info,
warning,
severe,
shout,
off,
}
class LogMessage { class LogMessage {
final String message; final String message;

View file

@ -13,28 +13,18 @@ enum MemoryTypeEnum {
class MemoryData { class MemoryData {
final int year; final int year;
const MemoryData({ const MemoryData({required this.year});
required this.year,
});
MemoryData copyWith({ MemoryData copyWith({int? year}) {
int? year, return MemoryData(year: year ?? this.year);
}) {
return MemoryData(
year: year ?? this.year,
);
} }
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return <String, dynamic>{ return <String, dynamic>{'year': year};
'year': year,
};
} }
factory MemoryData.fromMap(Map<String, dynamic> map) { factory MemoryData.fromMap(Map<String, dynamic> map) {
return MemoryData( return MemoryData(year: map['year'] as int);
year: map['year'] as int,
);
} }
String toJson() => json.encode(toMap()); String toJson() => json.encode(toMap());

View file

@ -5,21 +5,12 @@ class SearchResult {
final List<BaseAsset> assets; final List<BaseAsset> assets;
final int? nextPage; final int? nextPage;
const SearchResult({ const SearchResult({required this.assets, this.nextPage});
required this.assets,
this.nextPage,
});
int get totalAssets => assets.length; int get totalAssets => assets.length;
SearchResult copyWith({ SearchResult copyWith({List<BaseAsset>? assets, int? nextPage}) {
List<BaseAsset>? assets, return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage);
int? nextPage,
}) {
return SearchResult(
assets: assets ?? this.assets,
nextPage: nextPage ?? this.nextPage,
);
} }
@override @override

View file

@ -8,8 +8,7 @@ enum Setting<T> {
loadOriginalVideo<bool>(StoreKey.loadOriginalVideo, false), loadOriginalVideo<bool>(StoreKey.loadOriginalVideo, false),
preferRemoteImage<bool>(StoreKey.preferRemoteImage, false), preferRemoteImage<bool>(StoreKey.preferRemoteImage, false),
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false), advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false),
enableBackup<bool>(StoreKey.enableBackup, false), enableBackup<bool>(StoreKey.enableBackup, false);
;
const Setting(this.storeKey, this.defaultValue); const Setting(this.storeKey, this.defaultValue);

View file

@ -14,13 +14,7 @@ class Stack {
required this.primaryAssetId, required this.primaryAssetId,
}); });
Stack copyWith({ Stack copyWith({String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) {
String? id,
DateTime? createdAt,
DateTime? updatedAt,
String? ownerId,
String? primaryAssetId,
}) {
return Stack( return Stack(
id: id ?? this.id, id: id ?? this.id,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
@ -63,11 +57,7 @@ class StackResponse {
final String primaryAssetId; final String primaryAssetId;
final List<String> assetIds; final List<String> assetIds;
const StackResponse({ const StackResponse({required this.id, required this.primaryAssetId, required this.assetIds});
required this.id,
required this.primaryAssetId,
required this.assetIds,
});
@override @override
bool operator ==(covariant StackResponse other) { bool operator ==(covariant StackResponse other) {

View file

@ -1,18 +1,8 @@
import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart';
enum GroupAssetsBy { enum GroupAssetsBy { day, month, auto, none }
day,
month,
auto,
none;
}
enum HeaderType { enum HeaderType { none, month, day, monthAndDay }
none,
month,
day,
monthAndDay;
}
class Bucket { class Bucket {
final int assetCount; final int assetCount;

View file

@ -74,8 +74,7 @@ quotaSizeInBytes: $quotaSizeInBytes,
bool? isPartnerSharedWith, bool? isPartnerSharedWith,
int? quotaUsageInBytes, int? quotaUsageInBytes,
int? quotaSizeInBytes, int? quotaSizeInBytes,
}) => }) => UserDto(
UserDto(
id: id ?? this.id, id: id ?? this.id,
email: email ?? this.email, email: email ?? this.email,
name: name ?? this.name, name: name ?? this.name,
@ -143,13 +142,7 @@ class PartnerUserDto {
this.profileImagePath, this.profileImagePath,
}); });
PartnerUserDto copyWith({ PartnerUserDto copyWith({String? id, String? email, String? name, bool? inTimeline, String? profileImagePath}) {
String? id,
String? email,
String? name,
bool? inTimeline,
String? profileImagePath,
}) {
return PartnerUserDto( return PartnerUserDto(
id: id ?? this.id, id: id ?? this.id,
email: email ?? this.email, email: email ?? this.email,

View file

@ -193,17 +193,9 @@ class License {
final String activationKey; final String activationKey;
final String licenseKey; final String licenseKey;
const License({ const License({required this.activatedAt, required this.activationKey, required this.licenseKey});
required this.activatedAt,
required this.activationKey,
required this.licenseKey,
});
License copyWith({ License copyWith({DateTime? activatedAt, String? activationKey, String? licenseKey}) {
DateTime? activatedAt,
String? activationKey,
String? licenseKey,
}) {
return License( return License(
activatedAt: activatedAt ?? this.activatedAt, activatedAt: activatedAt ?? this.activatedAt,
activationKey: activationKey ?? this.activationKey, activationKey: activationKey ?? this.activationKey,
@ -255,13 +247,8 @@ class UserMetadata {
final Preferences? preferences; final Preferences? preferences;
final License? license; final License? license;
const UserMetadata({ const UserMetadata({required this.userId, required this.key, this.onboarding, this.preferences, this.license})
required this.userId, : assert(
required this.key,
this.onboarding,
this.preferences,
this.license,
}) : assert(
onboarding != null || preferences != null || license != null, onboarding != null || preferences != null || license != null,
'One of onboarding, preferences and license must be provided', 'One of onboarding, preferences and license must be provided',
); );

View file

@ -34,10 +34,7 @@ class HashService {
final Stopwatch stopwatch = Stopwatch()..start(); final Stopwatch stopwatch = Stopwatch()..start();
// Sorted by backupSelection followed by isCloud // Sorted by backupSelection followed by isCloud
final localAlbums = await _localAlbumRepository.getAll( final localAlbums = await _localAlbumRepository.getAll(
sortBy: { sortBy: {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum},
SortLocalAlbumsBy.backupSelection,
SortLocalAlbumsBy.isIosSharedAlbum,
},
); );
for (final album in localAlbums) { for (final album in localAlbums) {

View file

@ -70,9 +70,7 @@ class LocalSyncService {
for (final album in cloudAlbums) { for (final album in cloudAlbums) {
final dbAlbum = dbAlbums.firstWhereOrNull((a) => a.id == album.id); final dbAlbum = dbAlbums.firstWhereOrNull((a) => a.id == album.id);
if (dbAlbum == null) { if (dbAlbum == null) {
_log.warning( _log.warning("Cloud album ${album.name} not found in local database. Skipping sync.");
"Cloud album ${album.name} not found in local database. Skipping sync.",
);
continue; continue;
} }
await updateAlbum(dbAlbum, album); await updateAlbum(dbAlbum, album);
@ -120,10 +118,7 @@ class LocalSyncService {
final assets = album.assetCount > 0 ? await _nativeSyncApi.getAssetsForAlbum(album.id) : <PlatformAsset>[]; final assets = album.assetCount > 0 ? await _nativeSyncApi.getAssetsForAlbum(album.id) : <PlatformAsset>[];
await _localAlbumRepository.upsert( await _localAlbumRepository.upsert(album, toUpsert: assets.toLocalAssets());
album,
toUpsert: assets.toLocalAssets(),
);
_log.fine("Successfully added device album ${album.name}"); _log.fine("Successfully added device album ${album.name}");
} catch (e, s) { } catch (e, s) {
_log.warning("Error while adding device album", e, s); _log.warning("Error while adding device album", e, s);
@ -146,9 +141,7 @@ class LocalSyncService {
_log.fine("Syncing device album ${dbAlbum.name}"); _log.fine("Syncing device album ${dbAlbum.name}");
if (_albumsEqual(deviceAlbum, dbAlbum)) { if (_albumsEqual(deviceAlbum, dbAlbum)) {
_log.fine( _log.fine("Device album ${dbAlbum.name} has not changed. Skipping sync.");
"Device album ${dbAlbum.name} has not changed. Skipping sync.",
);
return false; return false;
} }
@ -172,10 +165,7 @@ class LocalSyncService {
@visibleForTesting @visibleForTesting
// The [deviceAlbum] is expected to be refreshed before calling this method // The [deviceAlbum] is expected to be refreshed before calling this method
// with modified time and asset count // with modified time and asset count
Future<bool> checkAddition( Future<bool> checkAddition(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async {
LocalAlbum dbAlbum,
LocalAlbum deviceAlbum,
) async {
try { try {
_log.fine("Fast syncing device album ${dbAlbum.name}"); _log.fine("Fast syncing device album ${dbAlbum.name}");
// Assets has been modified // Assets has been modified
@ -189,9 +179,7 @@ class LocalSyncService {
// Early return if no new assets were found // Early return if no new assets were found
if (newAssetsCount == 0) { if (newAssetsCount == 0) {
_log.fine( _log.fine("No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}");
"No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}",
);
return false; return false;
} }
@ -201,10 +189,7 @@ class LocalSyncService {
return false; return false;
} }
final newAssets = await _nativeSyncApi.getAssetsForAlbum( final newAssets = await _nativeSyncApi.getAssetsForAlbum(deviceAlbum.id, updatedTimeCond: updatedTime);
deviceAlbum.id,
updatedTimeCond: updatedTime,
);
await _localAlbumRepository.upsert( await _localAlbumRepository.upsert(
deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection), deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection),
@ -229,9 +214,7 @@ class LocalSyncService {
final assetsInDb = dbAlbum.assetCount > 0 ? await _localAlbumRepository.getAssets(dbAlbum.id) : <LocalAsset>[]; final assetsInDb = dbAlbum.assetCount > 0 ? await _localAlbumRepository.getAssets(dbAlbum.id) : <LocalAsset>[];
if (deviceAlbum.assetCount == 0) { if (deviceAlbum.assetCount == 0) {
_log.fine( _log.fine("Device album ${deviceAlbum.name} is empty. Removing assets from DB.");
"Device album ${deviceAlbum.name} is empty. Removing assets from DB.",
);
await _localAlbumRepository.upsert( await _localAlbumRepository.upsert(
deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection), deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection),
toDelete: assetsInDb.map((a) => a.id), toDelete: assetsInDb.map((a) => a.id),
@ -239,18 +222,11 @@ class LocalSyncService {
return true; return true;
} }
final updatedDeviceAlbum = deviceAlbum.copyWith( final updatedDeviceAlbum = deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection);
backupSelection: dbAlbum.backupSelection,
);
if (dbAlbum.assetCount == 0) { if (dbAlbum.assetCount == 0) {
_log.fine( _log.fine("Device album ${deviceAlbum.name} is empty. Adding assets to DB.");
"Device album ${deviceAlbum.name} is empty. Adding assets to DB.", await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsInDevice);
);
await _localAlbumRepository.upsert(
updatedDeviceAlbum,
toUpsert: assetsInDevice,
);
return true; return true;
} }
@ -282,18 +258,12 @@ class LocalSyncService {
); );
if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) { if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) {
_log.fine( _log.fine("No asset changes detected in album ${deviceAlbum.name}. Updating metadata.");
"No asset changes detected in album ${deviceAlbum.name}. Updating metadata.",
);
_localAlbumRepository.upsert(updatedDeviceAlbum); _localAlbumRepository.upsert(updatedDeviceAlbum);
return true; return true;
} }
await _localAlbumRepository.upsert( await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsToUpsert, toDelete: assetsToDelete);
updatedDeviceAlbum,
toUpsert: assetsToUpsert,
toDelete: assetsToDelete,
);
return true; return true;
} catch (e, s) { } catch (e, s) {

View file

@ -61,11 +61,7 @@ class LogService {
return instance; return instance;
} }
LogService._( LogService._(this._logRepository, this._storeRepository, this._shouldBuffer) {
this._logRepository,
this._storeRepository,
this._shouldBuffer,
) {
_logSubscription = Logger.root.onRecord.listen(_handleLogRecord); _logSubscription = Logger.root.onRecord.listen(_handleLogRecord);
} }
@ -89,10 +85,7 @@ class LogService {
if (_shouldBuffer) { if (_shouldBuffer) {
_msgBuffer.add(record); _msgBuffer.add(record);
_flushTimer ??= Timer( _flushTimer ??= Timer(const Duration(seconds: 5), () => unawaited(flushBuffer()));
const Duration(seconds: 5),
() => unawaited(flushBuffer()),
);
} else { } else {
unawaited(_logRepository.insert(record)); unawaited(_logRepository.insert(record));
} }

View file

@ -7,10 +7,7 @@ class DriftPartnerService {
final DriftPartnerRepository _driftPartnerRepository; final DriftPartnerRepository _driftPartnerRepository;
final PartnerApiRepository _partnerApiRepository; final PartnerApiRepository _partnerApiRepository;
const DriftPartnerService( const DriftPartnerService(this._driftPartnerRepository, this._partnerApiRepository);
this._driftPartnerRepository,
this._partnerApiRepository,
);
Future<List<PartnerUserDto>> getSharedWith(String userId) { Future<List<PartnerUserDto>> getSharedWith(String userId) {
return _driftPartnerRepository.getSharedWith(userId); return _driftPartnerRepository.getSharedWith(userId);
@ -20,9 +17,7 @@ class DriftPartnerService {
return _driftPartnerRepository.getSharedBy(userId); return _driftPartnerRepository.getSharedBy(userId);
} }
Future<List<PartnerUserDto>> getAvailablePartners( Future<List<PartnerUserDto>> getAvailablePartners(String currentUserId) async {
String currentUserId,
) async {
final otherUsers = await _driftPartnerRepository.getAvailablePartners(currentUserId); final otherUsers = await _driftPartnerRepository.getAvailablePartners(currentUserId);
final currentPartners = await _driftPartnerRepository.getSharedBy(currentUserId); final currentPartners = await _driftPartnerRepository.getSharedBy(currentUserId);
final available = otherUsers.where((user) { final available = otherUsers.where((user) {
@ -39,10 +34,7 @@ class DriftPartnerService {
return; return;
} }
await _partnerApiRepository.update( await _partnerApiRepository.update(partnerId, inTimeline: !partner.inTimeline);
partnerId,
inTimeline: !partner.inTimeline,
);
await _driftPartnerRepository.toggleShowInTimeline(partner, userId); await _driftPartnerRepository.toggleShowInTimeline(partner, userId);
} }

View file

@ -26,11 +26,7 @@ class RemoteAlbumService {
return _repository.get(albumId); return _repository.get(albumId);
} }
List<RemoteAlbum> sortAlbums( List<RemoteAlbum> sortAlbums(List<RemoteAlbum> albums, RemoteAlbumSortMode sortMode, {bool isReverse = false}) {
List<RemoteAlbum> albums,
RemoteAlbumSortMode sortMode, {
bool isReverse = false,
}) {
return sortMode.sortFn(albums, isReverse); return sortMode.sortFn(albums, isReverse);
} }
@ -69,16 +65,8 @@ class RemoteAlbumService {
return filtered; return filtered;
} }
Future<RemoteAlbum> createAlbum({ Future<RemoteAlbum> createAlbum({required String title, required List<String> assetIds, String? description}) async {
required String title, final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds);
required List<String> assetIds,
String? description,
}) async {
final album = await _albumApiRepository.createDriftAlbum(
title,
description: description,
assetIds: assetIds,
);
await _repository.create(album, assetIds); await _repository.create(album, assetIds);
@ -120,14 +108,8 @@ class RemoteAlbumService {
return _repository.getAssets(albumId); return _repository.getAssets(albumId);
} }
Future<int> addAssets({ Future<int> addAssets({required String albumId, required List<String> assetIds}) async {
required String albumId, final album = await _albumApiRepository.addAssets(albumId, assetIds);
required List<String> assetIds,
}) async {
final album = await _albumApiRepository.addAssets(
albumId,
assetIds,
);
await _repository.addAssets(albumId, album.added); await _repository.addAssets(albumId, album.added);
@ -140,10 +122,7 @@ class RemoteAlbumService {
await _repository.deleteAlbum(albumId); await _repository.deleteAlbum(albumId);
} }
Future<void> addUsers({ Future<void> addUsers({required String albumId, required List<String> userIds}) async {
required String albumId,
required List<String> userIds,
}) async {
await _albumApiRepository.addUsers(albumId, userIds); await _albumApiRepository.addUsers(albumId, userIds);
return _repository.addUsers(albumId, userIds); return _repository.addUsers(albumId, userIds);

View file

@ -24,16 +24,12 @@ class StoreService {
} }
// TODO: Replace the implementation with the one from create after removing the typedef // TODO: Replace the implementation with the one from create after removing the typedef
static Future<StoreService> init({ static Future<StoreService> init({required IsarStoreRepository storeRepository}) async {
required IsarStoreRepository storeRepository,
}) async {
_instance ??= await create(storeRepository: storeRepository); _instance ??= await create(storeRepository: storeRepository);
return _instance!; return _instance!;
} }
static Future<StoreService> create({ static Future<StoreService> create({required IsarStoreRepository storeRepository}) async {
required IsarStoreRepository storeRepository,
}) async {
final instance = StoreService._(storeRepository: storeRepository); final instance = StoreService._(storeRepository: storeRepository);
await instance._populateCache(); await instance._populateCache();
instance._storeUpdateSubscription = instance._listenForChange(); instance._storeUpdateSubscription = instance._listenForChange();

View file

@ -34,9 +34,7 @@ class SyncStreamService {
Future<void> handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) async { Future<void> handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) async {
if (batchData.isEmpty) return; if (batchData.isEmpty) return;
_logger.info( _logger.info('Processing batch of ${batchData.length} AssetUploadReadyV1 events');
'Processing batch of ${batchData.length} AssetUploadReadyV1 events',
);
final List<SyncAssetV1> assets = []; final List<SyncAssetV1> assets = [];
final List<SyncAssetExifV1> exifs = []; final List<SyncAssetExifV1> exifs = [];
@ -65,22 +63,12 @@ class SyncStreamService {
} }
if (assets.isNotEmpty && exifs.isNotEmpty) { if (assets.isNotEmpty && exifs.isNotEmpty) {
await _syncStreamRepository.updateAssetsV1( await _syncStreamRepository.updateAssetsV1(assets, debugLabel: 'websocket-batch');
assets, await _syncStreamRepository.updateAssetsExifV1(exifs, debugLabel: 'websocket-batch');
debugLabel: 'websocket-batch',
);
await _syncStreamRepository.updateAssetsExifV1(
exifs,
debugLabel: 'websocket-batch',
);
_logger.info('Successfully processed ${assets.length} assets in batch'); _logger.info('Successfully processed ${assets.length} assets in batch');
} }
} catch (error, stackTrace) { } catch (error, stackTrace) {
_logger.severe( _logger.severe("Error processing AssetUploadReadyV1 websocket batch events", error, stackTrace);
"Error processing AssetUploadReadyV1 websocket batch events",
error,
stackTrace,
);
} }
} }
@ -114,10 +102,7 @@ class SyncStreamService {
batch.clear(); batch.clear();
} }
Future<void> _handleSyncData( Future<void> _handleSyncData(SyncEntityType type, Iterable<Object> data) async {
SyncEntityType type,
Iterable<Object> data,
) async {
_logger.fine("Processing sync data for $type of length ${data.length}"); _logger.fine("Processing sync data for $type of length ${data.length}");
switch (type) { switch (type) {
case SyncEntityType.userV1: case SyncEntityType.userV1:
@ -135,30 +120,15 @@ class SyncStreamService {
case SyncEntityType.assetExifV1: case SyncEntityType.assetExifV1:
return _syncStreamRepository.updateAssetsExifV1(data.cast()); return _syncStreamRepository.updateAssetsExifV1(data.cast());
case SyncEntityType.partnerAssetV1: case SyncEntityType.partnerAssetV1:
return _syncStreamRepository.updateAssetsV1( return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner');
data.cast(),
debugLabel: 'partner',
);
case SyncEntityType.partnerAssetBackfillV1: case SyncEntityType.partnerAssetBackfillV1:
return _syncStreamRepository.updateAssetsV1( return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner backfill');
data.cast(),
debugLabel: 'partner backfill',
);
case SyncEntityType.partnerAssetDeleteV1: case SyncEntityType.partnerAssetDeleteV1:
return _syncStreamRepository.deleteAssetsV1( return _syncStreamRepository.deleteAssetsV1(data.cast(), debugLabel: "partner");
data.cast(),
debugLabel: "partner",
);
case SyncEntityType.partnerAssetExifV1: case SyncEntityType.partnerAssetExifV1:
return _syncStreamRepository.updateAssetsExifV1( return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner');
data.cast(),
debugLabel: 'partner',
);
case SyncEntityType.partnerAssetExifBackfillV1: case SyncEntityType.partnerAssetExifBackfillV1:
return _syncStreamRepository.updateAssetsExifV1( return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner backfill');
data.cast(),
debugLabel: 'partner backfill',
);
case SyncEntityType.albumV1: case SyncEntityType.albumV1:
return _syncStreamRepository.updateAlbumsV1(data.cast()); return _syncStreamRepository.updateAlbumsV1(data.cast());
case SyncEntityType.albumDeleteV1: case SyncEntityType.albumDeleteV1:
@ -166,39 +136,21 @@ class SyncStreamService {
case SyncEntityType.albumUserV1: case SyncEntityType.albumUserV1:
return _syncStreamRepository.updateAlbumUsersV1(data.cast()); return _syncStreamRepository.updateAlbumUsersV1(data.cast());
case SyncEntityType.albumUserBackfillV1: case SyncEntityType.albumUserBackfillV1:
return _syncStreamRepository.updateAlbumUsersV1( return _syncStreamRepository.updateAlbumUsersV1(data.cast(), debugLabel: 'backfill');
data.cast(),
debugLabel: 'backfill',
);
case SyncEntityType.albumUserDeleteV1: case SyncEntityType.albumUserDeleteV1:
return _syncStreamRepository.deleteAlbumUsersV1(data.cast()); return _syncStreamRepository.deleteAlbumUsersV1(data.cast());
case SyncEntityType.albumAssetV1: case SyncEntityType.albumAssetV1:
return _syncStreamRepository.updateAssetsV1( return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album');
data.cast(),
debugLabel: 'album',
);
case SyncEntityType.albumAssetBackfillV1: case SyncEntityType.albumAssetBackfillV1:
return _syncStreamRepository.updateAssetsV1( return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album backfill');
data.cast(),
debugLabel: 'album backfill',
);
case SyncEntityType.albumAssetExifV1: case SyncEntityType.albumAssetExifV1:
return _syncStreamRepository.updateAssetsExifV1( return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album');
data.cast(),
debugLabel: 'album',
);
case SyncEntityType.albumAssetExifBackfillV1: case SyncEntityType.albumAssetExifBackfillV1:
return _syncStreamRepository.updateAssetsExifV1( return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album backfill');
data.cast(),
debugLabel: 'album backfill',
);
case SyncEntityType.albumToAssetV1: case SyncEntityType.albumToAssetV1:
return _syncStreamRepository.updateAlbumToAssetsV1(data.cast()); return _syncStreamRepository.updateAlbumToAssetsV1(data.cast());
case SyncEntityType.albumToAssetBackfillV1: case SyncEntityType.albumToAssetBackfillV1:
return _syncStreamRepository.updateAlbumToAssetsV1( return _syncStreamRepository.updateAlbumToAssetsV1(data.cast(), debugLabel: 'backfill');
data.cast(),
debugLabel: 'backfill',
);
case SyncEntityType.albumToAssetDeleteV1: case SyncEntityType.albumToAssetDeleteV1:
return _syncStreamRepository.deleteAlbumToAssetsV1(data.cast()); return _syncStreamRepository.deleteAlbumToAssetsV1(data.cast());
// No-op. SyncAckV1 entities are checkpoints in the sync stream // No-op. SyncAckV1 entities are checkpoints in the sync stream
@ -218,28 +170,15 @@ class SyncStreamService {
case SyncEntityType.stackDeleteV1: case SyncEntityType.stackDeleteV1:
return _syncStreamRepository.deleteStacksV1(data.cast()); return _syncStreamRepository.deleteStacksV1(data.cast());
case SyncEntityType.partnerStackV1: case SyncEntityType.partnerStackV1:
return _syncStreamRepository.updateStacksV1( return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner');
data.cast(),
debugLabel: 'partner',
);
case SyncEntityType.partnerStackBackfillV1: case SyncEntityType.partnerStackBackfillV1:
return _syncStreamRepository.updateStacksV1( return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner backfill');
data.cast(),
debugLabel: 'partner backfill',
);
case SyncEntityType.partnerStackDeleteV1: case SyncEntityType.partnerStackDeleteV1:
return _syncStreamRepository.deleteStacksV1( return _syncStreamRepository.deleteStacksV1(data.cast(), debugLabel: 'partner');
data.cast(),
debugLabel: 'partner',
);
case SyncEntityType.userMetadataV1: case SyncEntityType.userMetadataV1:
return _syncStreamRepository.updateUserMetadatasV1( return _syncStreamRepository.updateUserMetadatasV1(data.cast());
data.cast(),
);
case SyncEntityType.userMetadataDeleteV1: case SyncEntityType.userMetadataDeleteV1:
return _syncStreamRepository.deleteUserMetadatasV1( return _syncStreamRepository.deleteUserMetadatasV1(data.cast());
data.cast(),
);
case SyncEntityType.personV1: case SyncEntityType.personV1:
return _syncStreamRepository.updatePeopleV1(data.cast()); return _syncStreamRepository.updatePeopleV1(data.cast());
case SyncEntityType.personDeleteV1: case SyncEntityType.personDeleteV1:

View file

@ -11,26 +11,18 @@ import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart';
import 'package:immich_mobile/utils/async_mutex.dart'; import 'package:immich_mobile/utils/async_mutex.dart';
typedef TimelineAssetSource = Future<List<BaseAsset>> Function( typedef TimelineAssetSource = Future<List<BaseAsset>> Function(int index, int count);
int index,
int count,
);
typedef TimelineBucketSource = Stream<List<Bucket>> Function(); typedef TimelineBucketSource = Stream<List<Bucket>> Function();
typedef TimelineQuery = ({ typedef TimelineQuery = ({TimelineAssetSource assetSource, TimelineBucketSource bucketSource});
TimelineAssetSource assetSource,
TimelineBucketSource bucketSource,
});
class TimelineFactory { class TimelineFactory {
final DriftTimelineRepository _timelineRepository; final DriftTimelineRepository _timelineRepository;
final SettingsService _settingsService; final SettingsService _settingsService;
const TimelineFactory({ const TimelineFactory({required DriftTimelineRepository timelineRepository, required SettingsService settingsService})
required DriftTimelineRepository timelineRepository, : _timelineRepository = timelineRepository,
required SettingsService settingsService,
}) : _timelineRepository = timelineRepository,
_settingsService = settingsService; _settingsService = settingsService;
GroupAssetsBy get groupBy { GroupAssetsBy get groupBy {
@ -75,16 +67,10 @@ class TimelineService {
int _totalAssets = 0; int _totalAssets = 0;
int get totalAssets => _totalAssets; int get totalAssets => _totalAssets;
TimelineService(TimelineQuery query) TimelineService(TimelineQuery query) : this._(assetSource: query.assetSource, bucketSource: query.bucketSource);
: this._(
assetSource: query.assetSource,
bucketSource: query.bucketSource,
);
TimelineService._({ TimelineService._({required TimelineAssetSource assetSource, required TimelineBucketSource bucketSource})
required TimelineAssetSource assetSource, : _assetSource = assetSource,
required TimelineBucketSource bucketSource,
}) : _assetSource = assetSource,
_bucketSource = bucketSource { _bucketSource = bucketSource {
_bucketSubscription = _bucketSource().listen((buckets) { _bucketSubscription = _bucketSource().listen((buckets) {
_mutex.run(() async { _mutex.run(() async {
@ -103,10 +89,7 @@ class TimelineService {
count = kTimelineAssetLoadBatchSize; count = kTimelineAssetLoadBatchSize;
} else { } else {
offset = _bufferOffset; offset = _bufferOffset;
count = math.min( count = math.min(_buffer.length, totalAssets - _bufferOffset);
_buffer.length,
totalAssets - _bufferOffset,
);
} }
_buffer = await _assetSource(offset, count); _buffer = await _assetSource(offset, count);
_bufferOffset = offset; _bufferOffset = offset;
@ -134,10 +117,7 @@ class TimelineService {
// make sure to load a meaningful amount of data (and not only the requested slice) // 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 // 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 // fills small requests to [kTimelineAssetLoadBatchSize], adds some legroom into the opposite scroll direction for large requests
final len = math.max( final len = math.max(kTimelineAssetLoadBatchSize, count + kTimelineAssetLoadOppositeSize);
kTimelineAssetLoadBatchSize,
count + kTimelineAssetLoadOppositeSize,
);
// when scrolling forward, start shortly before the requested offset // when scrolling forward, start shortly before the requested offset
// when scrolling backward, end shortly after the requested offset to guard against the user scrolling // 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 // in the other direction a tiny bit resulting in another required load from the DB

View file

@ -44,10 +44,7 @@ class UserService {
Future<String?> createProfileImage(String name, Uint8List image) async { Future<String?> createProfileImage(String name, Uint8List image) async {
try { try {
final path = await _userApiRepository.createProfileImage( final path = await _userApiRepository.createProfileImage(name: name, data: image);
name: name,
data: image,
);
final updatedUser = getMyUser().copyWith(profileImagePath: path); final updatedUser = getMyUser().copyWith(profileImagePath: path);
await _storeService.put(StoreKey.currentUser, updatedUser); await _storeService.put(StoreKey.currentUser, updatedUser);
await _isarUserRepository.update(updatedUser); await _isarUserRepository.update(updatedUser);

View file

@ -66,17 +66,15 @@ class BackgroundSyncManager {
// We use a ternary operator to avoid [_deviceAlbumSyncTask] from being // We use a ternary operator to avoid [_deviceAlbumSyncTask] from being
// captured by the closure passed to [runInIsolateGentle]. // captured by the closure passed to [runInIsolateGentle].
_deviceAlbumSyncTask = full _deviceAlbumSyncTask = full
? runInIsolateGentle( ? runInIsolateGentle(computation: (ref) => ref.read(localSyncServiceProvider).sync(full: true))
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: false),
);
return _deviceAlbumSyncTask!.whenComplete(() { return _deviceAlbumSyncTask!
.whenComplete(() {
_deviceAlbumSyncTask = null; _deviceAlbumSyncTask = null;
onLocalSyncComplete?.call(); onLocalSyncComplete?.call();
}).catchError((error) { })
.catchError((error) {
onLocalSyncError?.call(error.toString()); onLocalSyncError?.call(error.toString());
_deviceAlbumSyncTask = null; _deviceAlbumSyncTask = null;
}); });
@ -90,14 +88,14 @@ class BackgroundSyncManager {
onHashingStart?.call(); onHashingStart?.call();
_hashTask = runInIsolateGentle( _hashTask = runInIsolateGentle(computation: (ref) => ref.read(hashServiceProvider).hashAssets());
computation: (ref) => ref.read(hashServiceProvider).hashAssets(),
);
return _hashTask!.whenComplete(() { return _hashTask!
.whenComplete(() {
onHashingComplete?.call(); onHashingComplete?.call();
_hashTask = null; _hashTask = null;
}).catchError((error) { })
.catchError((error) {
onHashingError?.call(error.toString()); onHashingError?.call(error.toString());
_hashTask = null; _hashTask = null;
}); });
@ -110,13 +108,13 @@ class BackgroundSyncManager {
onRemoteSyncStart?.call(); onRemoteSyncStart?.call();
_syncTask = runInIsolateGentle( _syncTask = runInIsolateGentle(computation: (ref) => ref.read(syncStreamServiceProvider).sync());
computation: (ref) => ref.read(syncStreamServiceProvider).sync(), return _syncTask!
); .whenComplete(() {
return _syncTask!.whenComplete(() {
onRemoteSyncComplete?.call(); onRemoteSyncComplete?.call();
_syncTask = null; _syncTask = null;
}).catchError((error) { })
.catchError((error) {
onRemoteSyncError?.call(error.toString()); onRemoteSyncError?.call(error.toString());
_syncTask = null; _syncTask = null;
}); });
@ -133,9 +131,6 @@ class BackgroundSyncManager {
} }
} }
Cancelable<void> _handleWsAssetUploadReadyV1Batch( Cancelable<void> _handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) => runInIsolateGentle(
List<dynamic> batchData,
) =>
runInIsolateGentle(
computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV1Batch(batchData), computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV1Batch(batchData),
); );

View file

@ -28,12 +28,7 @@ class EventStream {
void Function()? onDone, void Function()? onDone,
bool? cancelOnError, bool? cancelOnError,
}) { }) {
return where<T>().listen( return where<T>().listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError);
onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError,
);
} }
/// Closes the stream controller /// Closes the stream controller

View file

@ -113,10 +113,7 @@ class Album {
modifiedAt.isAtSameMomentAs(other.modifiedAt) && modifiedAt.isAtSameMomentAs(other.modifiedAt) &&
isAtSameMomentAs(startDate, other.startDate) && isAtSameMomentAs(startDate, other.startDate) &&
isAtSameMomentAs(endDate, other.endDate) && isAtSameMomentAs(endDate, other.endDate) &&
isAtSameMomentAs( isAtSameMomentAs(lastModifiedAssetTimestamp, other.lastModifiedAssetTimestamp) &&
lastModifiedAssetTimestamp,
other.lastModifiedAssetTimestamp,
) &&
shared == other.shared && shared == other.shared &&
activityEnabled == other.activityEnabled && activityEnabled == other.activityEnabled &&
owner.value == other.owner.value && owner.value == other.owner.value &&
@ -169,9 +166,7 @@ class Album {
a.thumbnail.value = await db.assets.where().remoteIdEqualTo(dto.albumThumbnailAssetId).findFirst(); a.thumbnail.value = await db.assets.where().remoteIdEqualTo(dto.albumThumbnailAssetId).findFirst();
} }
if (dto.albumUsers.isNotEmpty) { if (dto.albumUsers.isNotEmpty) {
final users = await db.users.getAllById( final users = await db.users.getAllById(dto.albumUsers.map((e) => e.user.id).toList(growable: false));
dto.albumUsers.map((e) => e.user.id).toList(growable: false),
);
a.sharedUsers.addAll(users.cast()); a.sharedUsers.addAll(users.cast());
} }
if (dto.assets.isNotEmpty) { if (dto.assets.isNotEmpty) {

Binary file not shown.

View file

@ -127,11 +127,7 @@ class Asset {
@Index(unique: false, replace: false, type: IndexType.hash) @Index(unique: false, replace: false, type: IndexType.hash)
String? localId; String? localId;
@Index( @Index(unique: true, replace: false, composite: [CompositeIndex("checksum", type: IndexType.hash)])
unique: true,
replace: false,
composite: [CompositeIndex("checksum", type: IndexType.hash)],
)
int ownerId; int ownerId;
DateTime fileCreatedAt; DateTime fileCreatedAt;
@ -447,8 +443,7 @@ class Asset {
int? stackCount, int? stackCount,
String? thumbhash, String? thumbhash,
AssetVisibilityEnum? visibility, AssetVisibilityEnum? visibility,
}) => }) => Asset(
Asset(
id: id ?? this.id, id: id ?? this.id,
checksum: checksum ?? this.checksum, checksum: checksum ?? this.checksum,
remoteId: remoteId ?? this.remoteId, remoteId: remoteId ?? this.remoteId,
@ -494,10 +489,7 @@ class Asset {
return compareByChecksum(a, b); return compareByChecksum(a, b);
} }
static int compareByOwnerChecksumCreatedModified( static int compareByOwnerChecksumCreatedModified(Asset a, Asset b) {
Asset a,
Asset b,
) {
final int ownerIdOrder = a.ownerId.compareTo(b.ownerId); final int ownerIdOrder = a.ownerId.compareTo(b.ownerId);
if (ownerIdOrder != 0) return ownerIdOrder; if (ownerIdOrder != 0) return ownerIdOrder;
final int checksumOrder = compareByChecksum(a, b); final int checksumOrder = compareByChecksum(a, b);
@ -566,11 +558,7 @@ extension AssetTypeEnumHelper on AssetTypeEnum {
/// Describes where the information of this asset came from: /// Describes where the information of this asset came from:
/// only from the local device, only from the remote server or merged from both /// only from the local device, only from the remote server or merged from both
enum AssetState { enum AssetState { local, remote, merged }
local,
remote,
merged,
}
extension AssetsHelper on IsarCollection<Asset> { extension AssetsHelper on IsarCollection<Asset> {
Future<int> deleteAllByRemoteId(Iterable<String> ids) => ids.isEmpty ? Future.value(0) : remote(ids).deleteAll(); 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<List<Asset>> getAllByLocalId(Iterable<String> ids) => ids.isEmpty ? Future.value([]) : local(ids).findAll();
Future<Asset?> getByRemoteId(String id) => where().remoteIdEqualTo(id).findFirst(); Future<Asset?> getByRemoteId(String id) => where().remoteIdEqualTo(id).findFirst();
QueryBuilder<Asset, Asset, QAfterWhereClause> remote( QueryBuilder<Asset, Asset, QAfterWhereClause> remote(Iterable<String> ids) =>
Iterable<String> ids,
) =>
where().anyOf(ids, (q, String e) => q.remoteIdEqualTo(e)); where().anyOf(ids, (q, String e) => q.remoteIdEqualTo(e));
QueryBuilder<Asset, Asset, QAfterWhereClause> local( QueryBuilder<Asset, Asset, QAfterWhereClause> local(Iterable<String> ids) {
Iterable<String> ids,
) {
return where().anyOf(ids, (q, String e) => q.localIdEqualTo(e)); return where().anyOf(ids, (q, String e) => q.localIdEqualTo(e));
} }
} }

Binary file not shown.

View file

@ -14,21 +14,9 @@ class BackupAlbum {
Id get isarId => fastHash(id); Id get isarId => fastHash(id);
BackupAlbum copyWith({ BackupAlbum copyWith({String? id, DateTime? lastBackup, BackupSelection? selection}) {
String? id, return BackupAlbum(id ?? this.id, lastBackup ?? this.lastBackup, selection ?? this.selection);
DateTime? lastBackup,
BackupSelection? selection,
}) {
return BackupAlbum(
id ?? this.id,
lastBackup ?? this.lastBackup,
selection ?? this.selection,
);
} }
} }
enum BackupSelection { enum BackupSelection { none, select, exclude }
none,
select,
exclude;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -15,16 +15,10 @@ extension TZExtension on Asset {
final location = getLocation(exifInfo!.timeZone!); final location = getLocation(exifInfo!.timeZone!);
dt = TZDateTime.from(dt, location); dt = TZDateTime.from(dt, location);
} on LocationNotFoundException { } on LocationNotFoundException {
RegExp re = RegExp( RegExp re = RegExp(r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$', caseSensitive: false);
r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$',
caseSensitive: false,
);
final m = re.firstMatch(exifInfo!.timeZone!); final m = re.firstMatch(exifInfo!.timeZone!);
if (m != null) { if (m != null) {
final duration = Duration( final duration = Duration(hours: int.parse(m.group(1) ?? '0'), minutes: int.parse(m.group(2) ?? '0'));
hours: int.parse(m.group(1) ?? '0'),
minutes: int.parse(m.group(2) ?? '0'),
);
dt = dt.add(duration); dt = dt.add(duration);
return (dt, duration); return (dt, duration);
} }

View file

@ -6,10 +6,7 @@ import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/utils/hash.dart'; import 'package:immich_mobile/utils/hash.dart';
extension ListExtension<E> on List<E> { extension ListExtension<E> on List<E> {
List<E> uniqueConsecutive({ List<E> uniqueConsecutive({int Function(E a, E b)? compare, void Function(E a, E b)? onDuplicate}) {
int Function(E a, E b)? compare,
void Function(E a, E b)? onDuplicate,
}) {
compare ??= (E a, E b) => a == b ? 0 : 1; compare ??= (E a, E b) => a == b ? 0 : 1;
int i = 1, j = 1; int i = 1, j = 1;
for (; i < length; i++) { for (; i < length; i++) {
@ -45,9 +42,7 @@ extension IntListExtension on Iterable<int> {
extension AssetListExtension on Iterable<Asset> { extension AssetListExtension on Iterable<Asset> {
/// Returns the assets that are already available in the Immich server /// Returns the assets that are already available in the Immich server
Iterable<Asset> remoteOnly({ Iterable<Asset> remoteOnly({void Function()? errorCallback}) {
void Function()? errorCallback,
}) {
final bool onlyRemote = every((e) => e.isRemote); final bool onlyRemote = every((e) => e.isRemote);
if (!onlyRemote) { if (!onlyRemote) {
if (errorCallback != null) errorCallback(); 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 /// Returns the assets that are owned by the user passed to the [owner] param
/// If [owner] is null, an empty list is returned /// If [owner] is null, an empty list is returned
Iterable<Asset> ownedOnly( Iterable<Asset> ownedOnly(UserDto? owner, {void Function()? errorCallback}) {
UserDto? owner, {
void Function()? errorCallback,
}) {
if (owner == null) return []; if (owner == null) return [];
final isarUserId = fastHash(owner.id); final isarUserId = fastHash(owner.id);
final bool onlyOwned = every((e) => e.ownerId == isarUserId); final bool onlyOwned = every((e) => e.ownerId == isarUserId);

View file

@ -47,11 +47,7 @@ extension DateRangeFormatting on DateTime {
/// - Date range of this year: "Mar 23-May 31" /// - Date range of this year: "Mar 23-May 31"
/// - Date range of other year: "Aug 28 - Sep 30, 2023" /// - Date range of other year: "Aug 28 - Sep 30, 2023"
/// - Date range over multiple years: "Apr 17, 2021 - Apr 9, 2022" /// - Date range over multiple years: "Apr 17, 2021 - Apr 9, 2022"
static String formatDateRange( static String formatDateRange(DateTime startDate, DateTime endDate, Locale? locale) {
DateTime startDate,
DateTime endDate,
Locale? locale,
) {
final now = DateTime.now(); final now = DateTime.now();
final currentYear = now.year; final currentYear = now.year;
final localeString = locale?.toString() ?? 'en_US'; final localeString = locale?.toString() ?? 'en_US';

View file

@ -13,9 +13,7 @@ extension MapMarkers on MapLibreMapController {
Future<void> addGeoJSONSourceForMarkers(List<MapMarker> markers) async { Future<void> addGeoJSONSourceForMarkers(List<MapMarker> markers) async {
return addSource( return addSource(
MapUtils.defaultSourceId, MapUtils.defaultSourceId,
GeojsonSourceProperties( GeojsonSourceProperties(data: MapUtils.generateGeoJsonForMarkers(markers.toList())),
data: MapUtils.generateGeoJsonForMarkers(markers.toList()),
),
); );
} }
@ -73,23 +71,13 @@ extension MapMarkers on MapLibreMapController {
try { try {
final ByteData bytes = await rootBundle.load("assets/location-pin.png"); final ByteData bytes = await rootBundle.load("assets/location-pin.png");
await addImage("mapMarker", bytes.buffer.asUint8List()); await addImage("mapMarker", bytes.buffer.asUint8List());
return addSymbol( return addSymbol(SymbolOptions(geometry: centre, iconImage: "mapMarker", iconSize: 0.15, iconAnchor: "bottom"));
SymbolOptions(
geometry: centre,
iconImage: "mapMarker",
iconSize: 0.15,
iconAnchor: "bottom",
),
);
} finally { } finally {
// no-op // no-op
} }
} }
Future<LatLngBounds> getBoundsFromPoint( Future<LatLngBounds> getBoundsFromPoint(Point<double> point, double distance) async {
Point<double> point,
double distance,
) async {
final southWestPx = Point(point.x - distance, point.y + distance); final southWestPx = Point(point.x - distance, point.y + distance);
final northEastPx = Point(point.x + distance, point.y - distance); final northEastPx = Point(point.x + distance, point.y - distance);

View file

@ -10,11 +10,7 @@ class FastScrollPhysics extends ScrollPhysics {
} }
@override @override
SpringDescription get spring => const SpringDescription( SpringDescription get spring => const SpringDescription(mass: 1, stiffness: 402.49984375, damping: 40);
mass: 1,
stiffness: 402.49984375,
damping: 40,
);
} }
class FastClampingScrollPhysics extends ClampingScrollPhysics { class FastClampingScrollPhysics extends ClampingScrollPhysics {

View file

@ -1,10 +1,6 @@
extension StringExtension on String { extension StringExtension on String {
String capitalize() { String capitalize() {
return split(" ") return split(" ").map((str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1)).join(" ");
.map(
(str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1),
)
.join(" ");
} }
} }

View file

@ -7,16 +7,10 @@ extension ImmichColorSchemeExtensions on ColorScheme {
extension ColorExtensions on Color { extension ColorExtensions on Color {
Color lighten({double amount = 0.1}) { Color lighten({double amount = 0.1}) {
return Color.alphaBlend( return Color.alphaBlend(Colors.white.withValues(alpha: amount), this);
Colors.white.withValues(alpha: amount),
this,
);
} }
Color darken({double amount = 0.1}) { Color darken({double amount = 0.1}) {
return Color.alphaBlend( return Color.alphaBlend(Colors.black.withValues(alpha: amount), this);
Colors.black.withValues(alpha: amount),
this,
);
} }
} }

View file

@ -29,11 +29,7 @@ extension TextTranslateExtension on Text {
} }
} }
String _translateHelper( String _translateHelper(BuildContext? context, String key, [Map<String, Object>? args]) {
BuildContext? context,
String key, [
Map<String, Object>? args,
]) {
if (key.isEmpty) { if (key.isEmpty) {
return ''; return '';
} }

View file

@ -16,21 +16,10 @@ class DeviceAssetEntity {
final List<byte> hash; final List<byte> hash;
final DateTime modifiedTime; final DateTime modifiedTime;
const DeviceAssetEntity({ const DeviceAssetEntity({required this.assetId, required this.hash, required this.modifiedTime});
required this.assetId,
required this.hash,
required this.modifiedTime,
});
DeviceAsset toModel() => DeviceAsset( DeviceAsset toModel() => DeviceAsset(assetId: assetId, hash: Uint8List.fromList(hash), modifiedTime: modifiedTime);
assetId: assetId,
hash: Uint8List.fromList(hash),
modifiedTime: modifiedTime,
);
static DeviceAssetEntity fromDto(DeviceAsset dto) => DeviceAssetEntity( static DeviceAssetEntity fromDto(DeviceAsset dto) =>
assetId: dto.assetId, DeviceAssetEntity(assetId: dto.assetId, hash: dto.hash, modifiedTime: dto.modifiedTime);
hash: dto.hash,
modifiedTime: dto.modifiedTime,
);
} }

View file

@ -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/asset.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
@TableIndex( @TableIndex(name: 'UQ_remote_asset_owner_checksum', columns: {#checksum, #ownerId}, unique: true)
name: 'UQ_remote_asset_owner_checksum',
columns: {#checksum, #ownerId},
unique: true,
)
@TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum}) @TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum})
class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
const RemoteAssetEntity(); const RemoteAssetEntity();

View file

@ -6,12 +6,7 @@ import 'package:photo_manager/photo_manager.dart';
class AssetMediaRepository { class AssetMediaRepository {
const AssetMediaRepository(); const AssetMediaRepository();
Future<Uint8List?> getThumbnail( Future<Uint8List?> getThumbnail(String id, {int quality = 80, Size size = const Size.square(256)}) => AssetEntity(
String id, {
int quality = 80,
Size size = const Size.square(256),
}) =>
AssetEntity(
id: id, id: id,
// The below fields are not used in thumbnailDataWithSize but are required // The below fields are not used in thumbnailDataWithSize but are required
// to create an AssetEntity instance. It is faster to create a dummy AssetEntity // to create an AssetEntity instance. It is faster to create a dummy AssetEntity
@ -19,8 +14,5 @@ class AssetMediaRepository {
typeInt: AssetType.image.index, typeInt: AssetType.image.index,
width: size.width.toInt(), width: size.width.toInt(),
height: size.height.toInt(), height: size.height.toInt(),
).thumbnailDataWithSize( ).thumbnailDataWithSize(ThumbnailSize(size.width.toInt(), size.height.toInt()), quality: quality);
ThumbnailSize(size.width.toInt(), size.height.toInt()),
quality: quality,
);
} }

View file

@ -24,9 +24,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
useColumns: false, useColumns: false,
), ),
]) ])
..where( ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.excluded));
_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.excluded),
);
} }
Future<int> getTotalCount() async { Future<int> getTotalCount() async {
@ -79,9 +77,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
Future<int> getBackupCount(String userId) async { Future<int> getBackupCount(String userId) async {
final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) final query = _db.localAlbumAssetEntity.selectOnly(distinct: true)
..addColumns( ..addColumns([_db.localAlbumAssetEntity.assetId])
[_db.localAlbumAssetEntity.assetId],
)
..join([ ..join([
innerJoin( innerJoin(
_db.localAlbumEntity, _db.localAlbumEntity,
@ -112,9 +108,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
Future<List<LocalAsset>> getCandidates(String userId) async { Future<List<LocalAsset>> getCandidates(String userId) async {
final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true) final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true)
..addColumns([_db.localAlbumEntity.id]) ..addColumns([_db.localAlbumEntity.id])
..where( ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected));
_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected),
);
final query = _db.localAssetEntity.select() final query = _db.localAssetEntity.select()
..where( ..where(
@ -138,11 +132,7 @@ class DriftBackupRepository extends DriftDatabaseRepository {
) & ) &
lae.id.isNotInQuery(_getExcludedSubquery()), lae.id.isNotInQuery(_getExcludedSubquery()),
) )
..orderBy( ..orderBy([(localAsset) => OrderingTerm.desc(localAsset.createdAt)]);
[
(localAsset) => OrderingTerm.desc(localAsset.createdAt),
],
);
return query.map((localAsset) => localAsset.toDto()).get(); return query.map((localAsset) => localAsset.toDto()).get();
} }

View file

@ -59,19 +59,11 @@ class IsarDatabaseRepository implements IDatabaseRepository {
PersonEntity, PersonEntity,
AssetFaceEntity, AssetFaceEntity,
], ],
include: { include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'},
'package:immich_mobile/infrastructure/entities/merged_asset.drift',
},
) )
class Drift extends $Drift implements IDatabaseRepository { class Drift extends $Drift implements IDatabaseRepository {
Drift([QueryExecutor? executor]) Drift([QueryExecutor? executor])
: super( : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true)));
executor ??
driftDatabase(
name: 'immich',
native: const DriftNativeOptions(shareAcrossIsolates: true),
),
);
@override @override
int get schemaVersion => 4; int get schemaVersion => 4;

View file

@ -33,9 +33,7 @@ class IsarExifRepository extends IsarDatabaseRepository {
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos) { Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos) {
return transaction(() async { return transaction(() async {
await _db.exifInfos.putAll( await _db.exifInfos.putAll(exifInfos.map(entity.ExifInfo.fromDto).toList());
exifInfos.map(entity.ExifInfo.fromDto).toList(),
);
return exifInfos; return exifInfos;
}); });
} }

View file

@ -34,24 +34,18 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
if (sortBy.isNotEmpty) { if (sortBy.isNotEmpty) {
final orderings = <OrderingTerm>[]; final orderings = <OrderingTerm>[];
for (final sort in sortBy) { for (final sort in sortBy) {
orderings.add( orderings.add(switch (sort) {
switch (sort) {
SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id), SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id),
SortLocalAlbumsBy.backupSelection => OrderingTerm.asc(_db.localAlbumEntity.backupSelection), SortLocalAlbumsBy.backupSelection => OrderingTerm.asc(_db.localAlbumEntity.backupSelection),
SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum), SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum),
SortLocalAlbumsBy.name => OrderingTerm.asc(_db.localAlbumEntity.name), SortLocalAlbumsBy.name => OrderingTerm.asc(_db.localAlbumEntity.name),
SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount), SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount),
}, });
);
} }
query.orderBy(orderings); query.orderBy(orderings);
} }
return query return query.map((row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0)).get();
.map(
(row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0),
)
.get();
} }
Future<void> delete(String albumId) => transaction(() async { Future<void> delete(String albumId) => transaction(() async {
@ -65,10 +59,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
await _db.managers.localAlbumEntity.filter((a) => a.id.equals(albumId)).delete(); await _db.managers.localAlbumEntity.filter((a) => a.id.equals(albumId)).delete();
}); });
Future<void> syncDeletes( Future<void> syncDeletes(String albumId, Iterable<String> assetIdsToKeep) async {
String albumId,
Iterable<String> assetIdsToKeep,
) async {
if (assetIdsToKeep.isEmpty) { if (assetIdsToKeep.isEmpty) {
return Future.value(); return Future.value();
} }
@ -77,12 +68,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
deleteSmt.where((localAsset) { deleteSmt.where((localAsset) {
final subQuery = _db.localAlbumAssetEntity.selectOnly() final subQuery = _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId]) ..addColumns([_db.localAlbumAssetEntity.assetId])
..join([ ..join([innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id))]);
innerJoin(
_db.localAlbumEntity,
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
),
]);
subQuery.where( subQuery.where(
_db.localAlbumEntity.id.equals(albumId) & _db.localAlbumAssetEntity.assetId.isNotIn(assetIdsToKeep), _db.localAlbumEntity.id.equals(albumId) & _db.localAlbumAssetEntity.assetId.isNotIn(assetIdsToKeep),
); );
@ -109,12 +95,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
if (toUpsert.isNotEmpty) { if (toUpsert.isNotEmpty) {
await _upsertAssets(toUpsert); await _upsertAssets(toUpsert);
await _db.localAlbumAssetEntity.insertAll( await _db.localAlbumAssetEntity.insertAll(
toUpsert.map( toUpsert.map((a) => LocalAlbumAssetEntityCompanion.insert(assetId: a.id, albumId: localAlbum.id)),
(a) => LocalAlbumAssetEntityCompanion.insert(
assetId: a.id,
albumId: localAlbum.id,
),
),
mode: InsertMode.insertOrIgnore, mode: InsertMode.insertOrIgnore,
); );
} }
@ -162,10 +143,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
final subQuery = _db.localAlbumAssetEntity.selectOnly() final subQuery = _db.localAlbumAssetEntity.selectOnly()
..addColumns([_db.localAlbumAssetEntity.assetId]) ..addColumns([_db.localAlbumAssetEntity.assetId])
..join([ ..join([
innerJoin( innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id)),
_db.localAlbumEntity,
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
),
]); ]);
subQuery.where(_db.localAlbumEntity.marker_.isNotNull()); subQuery.where(_db.localAlbumEntity.marker_.isNotNull());
return localAsset.id.isInQuery(subQuery); return localAsset.id.isInQuery(subQuery);
@ -178,14 +156,10 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
} }
Future<List<LocalAsset>> getAssets(String albumId) { Future<List<LocalAsset>> getAssets(String albumId) {
final query = _db.localAlbumAssetEntity.select().join( final query =
[ _db.localAlbumAssetEntity.select().join([
innerJoin( innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
_db.localAssetEntity, ])
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
),
],
)
..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) ..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get();
@ -224,10 +198,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
batch.insertAll( batch.insertAll(
_db.localAlbumAssetEntity, _db.localAlbumAssetEntity,
albumIds.cast<String?>().nonNulls.map( albumIds.cast<String?>().nonNulls.map(
(albumId) => LocalAlbumAssetEntityCompanion.insert( (albumId) => LocalAlbumAssetEntityCompanion.insert(assetId: assetId, albumId: albumId),
assetId: assetId,
albumId: albumId,
),
), ),
onConflict: DoNothing(), onConflict: DoNothing(),
); );
@ -237,17 +208,11 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
} }
Future<List<LocalAsset>> getAssetsToHash(String albumId) { Future<List<LocalAsset>> getAssetsToHash(String albumId) {
final query = _db.localAlbumAssetEntity.select().join( final query =
[ _db.localAlbumAssetEntity.select().join([
innerJoin( innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
_db.localAssetEntity, ])
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), ..where(_db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull())
),
],
)
..where(
_db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull(),
)
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]);
return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get();
@ -275,10 +240,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>( batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>(
_db.localAssetEntity, _db.localAssetEntity,
companion, companion,
onConflict: DoUpdate( onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(asset.updatedAt)),
(_) => companion,
where: (old) => old.updatedAt.isNotValue(asset.updatedAt),
),
); );
} }
}); });
@ -351,11 +313,9 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
} }
Future<LocalAsset?> getThumbnail(String albumId) async { Future<LocalAsset?> getThumbnail(String albumId) async {
final query = _db.localAlbumAssetEntity.select().join([ final query =
innerJoin( _db.localAlbumAssetEntity.select().join([
_db.localAssetEntity, innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
),
]) ])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) ..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]) ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)])

View file

@ -16,14 +16,11 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
useColumns: false, useColumns: false,
), ),
]) ])..where(_db.localAssetEntity.id.equals(id));
..where(_db.localAssetEntity.id.equals(id));
return query.map((row) { return query.map((row) {
final asset = row.readTable(_db.localAssetEntity).toDto(); final asset = row.readTable(_db.localAssetEntity).toDto();
return asset.copyWith( return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id));
remoteId: row.read(_db.remoteAssetEntity.id),
);
}).watchSingleOrNull(); }).watchSingleOrNull();
} }

View file

@ -13,11 +13,9 @@ class DriftMemoryRepository extends DriftDatabaseRepository {
final now = DateTime.now(); final now = DateTime.now();
final localUtc = DateTime.utc(now.year, now.month, now.day, 0, 0, 0); final localUtc = DateTime.utc(now.year, now.month, now.day, 0, 0, 0);
final query = _db.select(_db.memoryEntity).join([ final query =
leftOuterJoin( _db.select(_db.memoryEntity).join([
_db.memoryAssetEntity, leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)),
_db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id),
),
leftOuterJoin( leftOuterJoin(
_db.remoteAssetEntity, _db.remoteAssetEntity,
_db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) &
@ -27,16 +25,9 @@ class DriftMemoryRepository extends DriftDatabaseRepository {
]) ])
..where(_db.memoryEntity.ownerId.equals(ownerId)) ..where(_db.memoryEntity.ownerId.equals(ownerId))
..where(_db.memoryEntity.deletedAt.isNull()) ..where(_db.memoryEntity.deletedAt.isNull())
..where( ..where(_db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc))
_db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc), ..where(_db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc))
) ..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
..where(
_db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc),
)
..orderBy([
OrderingTerm.desc(_db.memoryEntity.memoryAt),
OrderingTerm.asc(_db.remoteAssetEntity.createdAt),
]);
final rows = await query.get(); final rows = await query.get();
@ -59,11 +50,9 @@ class DriftMemoryRepository extends DriftDatabaseRepository {
} }
Future<DriftMemory?> get(String memoryId) async { Future<DriftMemory?> get(String memoryId) async {
final query = _db.select(_db.memoryEntity).join([ final query =
leftOuterJoin( _db.select(_db.memoryEntity).join([
_db.memoryAssetEntity, leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)),
_db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id),
),
leftOuterJoin( leftOuterJoin(
_db.remoteAssetEntity, _db.remoteAssetEntity,
_db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) &
@ -73,10 +62,7 @@ class DriftMemoryRepository extends DriftDatabaseRepository {
]) ])
..where(_db.memoryEntity.id.equals(memoryId)) ..where(_db.memoryEntity.id.equals(memoryId))
..where(_db.memoryEntity.deletedAt.isNull()) ..where(_db.memoryEntity.deletedAt.isNull())
..orderBy([ ..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
OrderingTerm.desc(_db.memoryEntity.memoryAt),
OrderingTerm.asc(_db.remoteAssetEntity.createdAt),
]);
final rows = await query.get(); final rows = await query.get();

View file

@ -9,24 +9,13 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
Future<List<PartnerUserDto>> getPartners(String userId) { Future<List<PartnerUserDto>> getPartners(String userId) {
final query = _db.select(_db.partnerEntity).join([ final query = _db.select(_db.partnerEntity).join([
innerJoin( innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)),
_db.userEntity, ])..where(_db.partnerEntity.sharedWithId.equals(userId));
_db.userEntity.id.equalsExp(_db.partnerEntity.sharedById),
),
])
..where(
_db.partnerEntity.sharedWithId.equals(userId),
);
return query.map((row) { return query.map((row) {
final user = row.readTable(_db.userEntity); final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity); final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto( return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
id: user.id,
email: user.email,
name: user.name,
inTimeline: partner.inTimeline,
);
}).get(); }).get();
} }
@ -35,60 +24,33 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
final query = _db.select(_db.userEntity)..where((row) => row.id.equals(currentUserId).not()); final query = _db.select(_db.userEntity)..where((row) => row.id.equals(currentUserId).not());
return query.map((user) { return query.map((user) {
return PartnerUserDto( return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: false);
id: user.id,
email: user.email,
name: user.name,
inTimeline: false,
);
}).get(); }).get();
} }
// Get users who are sharing their photos WITH the current user // Get users who are sharing their photos WITH the current user
Future<List<PartnerUserDto>> getSharedWith(String partnerId) { Future<List<PartnerUserDto>> getSharedWith(String partnerId) {
final query = _db.select(_db.partnerEntity).join([ final query = _db.select(_db.partnerEntity).join([
innerJoin( innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)),
_db.userEntity, ])..where(_db.partnerEntity.sharedWithId.equals(partnerId));
_db.userEntity.id.equalsExp(_db.partnerEntity.sharedById),
),
])
..where(
_db.partnerEntity.sharedWithId.equals(partnerId),
);
return query.map((row) { return query.map((row) {
final user = row.readTable(_db.userEntity); final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity); final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto( return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
id: user.id,
email: user.email,
name: user.name,
inTimeline: partner.inTimeline,
);
}).get(); }).get();
} }
// Get users who the current user is sharing their photos TO // Get users who the current user is sharing their photos TO
Future<List<PartnerUserDto>> getSharedBy(String userId) { Future<List<PartnerUserDto>> getSharedBy(String userId) {
final query = _db.select(_db.partnerEntity).join([ final query = _db.select(_db.partnerEntity).join([
innerJoin( innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId)),
_db.userEntity, ])..where(_db.partnerEntity.sharedById.equals(userId));
_db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId),
),
])
..where(
_db.partnerEntity.sharedById.equals(userId),
);
return query.map((row) { return query.map((row) {
final user = row.readTable(_db.userEntity); final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity); final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto( return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
id: user.id,
email: user.email,
name: user.name,
inTimeline: partner.inTimeline,
);
}).get(); }).get();
} }
@ -108,24 +70,13 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
Future<PartnerUserDto?> getPartner(String partnerId, String userId) { Future<PartnerUserDto?> getPartner(String partnerId, String userId) {
final query = _db.select(_db.partnerEntity).join([ final query = _db.select(_db.partnerEntity).join([
innerJoin( innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)),
_db.userEntity, ])..where(_db.partnerEntity.sharedById.equals(partnerId) & _db.partnerEntity.sharedWithId.equals(userId));
_db.userEntity.id.equalsExp(_db.partnerEntity.sharedById),
),
])
..where(
_db.partnerEntity.sharedById.equals(partnerId) & _db.partnerEntity.sharedWithId.equals(userId),
);
return query.map((row) { return query.map((row) {
final user = row.readTable(_db.userEntity); final user = row.readTable(_db.userEntity);
final partner = row.readTable(_db.partnerEntity); final partner = row.readTable(_db.partnerEntity);
return PartnerUserDto( return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline);
id: user.id,
email: user.email,
name: user.name,
inTimeline: partner.inTimeline,
);
}).getSingleOrNull(); }).getSingleOrNull();
} }
@ -150,8 +101,6 @@ class DriftPartnerRepository extends DriftDatabaseRepository {
} }
Future<void> delete(String partnerId, String userId) { Future<void> delete(String partnerId, String userId) {
return _db.partnerEntity.deleteWhere( return _db.partnerEntity.deleteWhere((t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId));
(t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId),
);
} }
} }

View file

@ -16,9 +16,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
final Drift _db; final Drift _db;
const DriftRemoteAlbumRepository(this._db) : super(_db); const DriftRemoteAlbumRepository(this._db) : super(_db);
Future<List<RemoteAlbum>> getAll({ Future<List<RemoteAlbum>> getAll({Set<SortRemoteAlbumsBy> sortBy = const {SortRemoteAlbumsBy.updatedAt}}) {
Set<SortRemoteAlbumsBy> sortBy = const {SortRemoteAlbumsBy.updatedAt},
}) {
final assetCount = _db.remoteAlbumAssetEntity.assetId.count(); final assetCount = _db.remoteAlbumAssetEntity.assetId.count();
final query = _db.remoteAlbumEntity.select().join([ final query = _db.remoteAlbumEntity.select().join([
@ -32,11 +30,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
useColumns: false, useColumns: false,
), ),
leftOuterJoin( leftOuterJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), useColumns: false),
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
useColumns: false,
),
]); ]);
query query
..where(_db.remoteAssetEntity.deletedAt.isNull()) ..where(_db.remoteAssetEntity.deletedAt.isNull())
@ -47,22 +41,19 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
if (sortBy.isNotEmpty) { if (sortBy.isNotEmpty) {
final orderings = <OrderingTerm>[]; final orderings = <OrderingTerm>[];
for (final sort in sortBy) { for (final sort in sortBy) {
orderings.add( orderings.add(switch (sort) {
switch (sort) {
SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id), SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id),
SortRemoteAlbumsBy.updatedAt => OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt), SortRemoteAlbumsBy.updatedAt => OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt),
}, });
);
} }
query.orderBy(orderings); query.orderBy(orderings);
} }
return query return query
.map( .map(
(row) => row.readTable(_db.remoteAlbumEntity).toDto( (row) => row
assetCount: row.read(assetCount) ?? 0, .readTable(_db.remoteAlbumEntity)
ownerName: row.read(_db.userEntity.name)!, .toDto(assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!),
),
) )
.get(); .get();
} }
@ -70,7 +61,8 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
Future<RemoteAlbum?> get(String albumId) { Future<RemoteAlbum?> get(String albumId) {
final assetCount = _db.remoteAlbumAssetEntity.assetId.count(); final assetCount = _db.remoteAlbumAssetEntity.assetId.count();
final query = _db.remoteAlbumEntity.select().join([ final query =
_db.remoteAlbumEntity.select().join([
leftOuterJoin( leftOuterJoin(
_db.remoteAlbumAssetEntity, _db.remoteAlbumAssetEntity,
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
@ -94,18 +86,14 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
return query return query
.map( .map(
(row) => row.readTable(_db.remoteAlbumEntity).toDto( (row) => row
assetCount: row.read(assetCount) ?? 0, .readTable(_db.remoteAlbumEntity)
ownerName: row.read(_db.userEntity.name)!, .toDto(assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!),
),
) )
.getSingleOrNull(); .getSingleOrNull();
} }
Future<void> create( Future<void> create(RemoteAlbum album, List<String> assetIds) async {
RemoteAlbum album,
List<String> assetIds,
) async {
await _db.transaction(() async { await _db.transaction(() async {
final entity = RemoteAlbumEntityCompanion( final entity = RemoteAlbumEntityCompanion(
id: Value(album.id), id: Value(album.id),
@ -123,17 +111,11 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
if (assetIds.isNotEmpty) { if (assetIds.isNotEmpty) {
final albumAssets = assetIds.map( final albumAssets = assetIds.map(
(assetId) => RemoteAlbumAssetEntityCompanion( (assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(album.id), assetId: Value(assetId)),
albumId: Value(album.id),
assetId: Value(assetId),
),
); );
await _db.batch((batch) { await _db.batch((batch) {
batch.insertAll( batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets);
_db.remoteAlbumAssetEntity,
albumAssets,
);
}); });
} }
}); });
@ -156,23 +138,15 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
} }
Future<int> removeAssets(String albumId, List<String> assetIds) { Future<int> removeAssets(String albumId, List<String> assetIds) {
return _db.remoteAlbumAssetEntity.deleteWhere( return _db.remoteAlbumAssetEntity.deleteWhere((tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds));
(tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds),
);
} }
FutureOr<(DateTime, DateTime)> getDateRange(String albumId) { FutureOr<(DateTime, DateTime)> getDateRange(String albumId) {
final query = _db.remoteAlbumAssetEntity.selectOnly() final query = _db.remoteAlbumAssetEntity.selectOnly()
..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)) ..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId))
..addColumns([ ..addColumns([_db.remoteAssetEntity.createdAt.min(), _db.remoteAssetEntity.createdAt.max()])
_db.remoteAssetEntity.createdAt.min(),
_db.remoteAssetEntity.createdAt.max(),
])
..join([ ..join([
innerJoin( innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)),
_db.remoteAssetEntity,
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
),
]); ]);
return query.map((row) { return query.map((row) {
@ -183,8 +157,9 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
} }
Future<List<UserDto>> getSharedUsers(String albumId) async { Future<List<UserDto>> getSharedUsers(String albumId) async {
final albumUserRows = final albumUserRows = await (_db.select(
await (_db.select(_db.remoteAlbumUserEntity)..where((row) => row.albumId.equals(albumId))).get(); _db.remoteAlbumUserEntity,
)..where((row) => row.albumId.equals(albumId))).get();
if (albumUserRows.isEmpty) { if (albumUserRows.isEmpty) {
return []; return [];
@ -214,29 +189,19 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
Future<List<RemoteAsset>> getAssets(String albumId) { Future<List<RemoteAsset>> getAssets(String albumId) {
final query = _db.remoteAlbumAssetEntity.select().join([ final query = _db.remoteAlbumAssetEntity.select().join([
innerJoin( innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)),
_db.remoteAssetEntity, ])..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId));
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
),
])
..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId));
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
} }
Future<int> addAssets(String albumId, List<String> assetIds) async { Future<int> addAssets(String albumId, List<String> assetIds) async {
final albumAssets = assetIds.map( final albumAssets = assetIds.map(
(assetId) => RemoteAlbumAssetEntityCompanion( (assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(albumId), assetId: Value(assetId)),
albumId: Value(albumId),
assetId: Value(assetId),
),
); );
await _db.batch((batch) { await _db.batch((batch) {
batch.insertAll( batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets);
_db.remoteAlbumAssetEntity,
albumAssets,
);
}); });
return assetIds.length; return assetIds.length;
@ -252,23 +217,19 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
); );
return _db.batch((batch) { return _db.batch((batch) {
batch.insertAll( batch.insertAll(_db.remoteAlbumUserEntity, albumUsers);
_db.remoteAlbumUserEntity,
albumUsers,
);
}); });
} }
Future<void> deleteAlbum(String albumId) async { Future<void> deleteAlbum(String albumId) async {
return _db.transaction(() async { return _db.transaction(() async {
await _db.remoteAlbumEntity.deleteWhere( await _db.remoteAlbumEntity.deleteWhere((table) => table.id.equals(albumId));
(table) => table.id.equals(albumId),
);
}); });
} }
Stream<RemoteAlbum?> watchAlbum(String albumId) { Stream<RemoteAlbum?> watchAlbum(String albumId) {
final query = _db.remoteAlbumEntity.select().join([ final query =
_db.remoteAlbumEntity.select().join([
leftOuterJoin( leftOuterJoin(
_db.remoteAlbumAssetEntity, _db.remoteAlbumAssetEntity,
_db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
@ -290,9 +251,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
..groupBy([_db.remoteAlbumEntity.id]); ..groupBy([_db.remoteAlbumEntity.id]);
return query.map((row) { return query.map((row) {
final album = row.readTable(_db.remoteAlbumEntity).toDto( final album = row.readTable(_db.remoteAlbumEntity).toDto(ownerName: row.read(_db.userEntity.name)!);
ownerName: row.read(_db.userEntity.name)!,
);
return album; return album;
}).watchSingleOrNull(); }).watchSingleOrNull();
} }

View file

@ -30,9 +30,8 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
} }
SingleOrNullSelectable<RemoteAsset?> _assetSelectable(String id) { SingleOrNullSelectable<RemoteAsset?> _assetSelectable(String id) {
final query = _db.remoteAssetEntity.select().addColumns([ final query =
_db.localAssetEntity.id, _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([
]).join([
leftOuterJoin( leftOuterJoin(
_db.localAssetEntity, _db.localAssetEntity,
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
@ -57,9 +56,8 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
} }
Stream<RemoteAsset?> watchAsset(String id) { Stream<RemoteAsset?> watchAsset(String id) {
final query = _db.remoteAssetEntity.select().addColumns([ final query =
_db.localAssetEntity.id, _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([
]).join([
leftOuterJoin( leftOuterJoin(
_db.localAssetEntity, _db.localAssetEntity,
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
@ -81,9 +79,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
} }
final query = _db.remoteAssetEntity.select() final query = _db.remoteAssetEntity.select()
..where( ..where((row) => row.stackId.equals(asset.stackId!) & row.id.equals(asset.id).not())
(row) => row.stackId.equals(asset.stackId!) & row.id.equals(asset.id).not(),
)
..orderBy([(row) => OrderingTerm.desc(row.createdAt)]); ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]);
return query.map((row) => row.toDto()).get(); return query.map((row) => row.toDto()).get();
@ -102,17 +98,15 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
"asset", "asset",
); );
final query = asset.selectOnly().join([ final query =
asset.selectOnly().join([
innerJoin( innerJoin(
_db.remoteExifEntity, _db.remoteExifEntity,
_db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)), _db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)),
useColumns: false, useColumns: false,
), ),
]) ])
..addColumns([ ..addColumns([_db.remoteExifEntity.city, _db.remoteExifEntity.assetId])
_db.remoteExifEntity.city,
_db.remoteExifEntity.assetId,
])
..where( ..where(
_db.remoteExifEntity.city.isNotNull() & _db.remoteExifEntity.city.isNotNull() &
asset.ref(_db.remoteAssetEntity.deletedAt).isNull() & asset.ref(_db.remoteAssetEntity.deletedAt).isNull() &
@ -185,10 +179,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
for (final id in ids) { for (final id in ids) {
batch.update( batch.update(
_db.remoteExifEntity, _db.remoteExifEntity,
RemoteExifEntityCompanion( RemoteExifEntityCompanion(latitude: Value(location.latitude), longitude: Value(location.longitude)),
latitude: Value(location.latitude),
longitude: Value(location.longitude),
),
where: (e) => e.assetId.equals(id), 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.stackEntity.deleteWhere((row) => row.id.isIn(stackIds));
await _db.batch((batch) { await _db.batch((batch) {
final companion = StackEntityCompanion( final companion = StackEntityCompanion(ownerId: Value(userId), primaryAssetId: Value(stack.primaryAssetId));
ownerId: Value(userId),
primaryAssetId: Value(stack.primaryAssetId),
);
batch.insert( batch.insert(_db.stackEntity, companion.copyWith(id: Value(stack.id)), onConflict: DoUpdate((_) => companion));
_db.stackEntity,
companion.copyWith(id: Value(stack.id)),
onConflict: DoUpdate((_) => companion),
);
for (final assetId in stack.assetIds) { for (final assetId in stack.assetIds) {
batch.update( batch.update(
_db.remoteAssetEntity, _db.remoteAssetEntity,
RemoteAssetEntityCompanion( RemoteAssetEntityCompanion(stackId: Value(stack.id)),
stackId: Value(stack.id),
),
where: (e) => e.id.equals(assetId), where: (e) => e.id.equals(assetId),
); );
} }

View file

@ -66,12 +66,5 @@ class SearchApiRepository extends ApiRepository {
String? state, String? state,
String? make, String? make,
String? model, String? model,
}) => }) => _api.getSearchSuggestions(type, country: country, state: state, make: make, model: model);
_api.getSearchSuggestions(
type,
country: country,
state: state,
make: make,
model: model,
);
} }

View file

@ -18,12 +18,6 @@ class DriftStackRepository extends DriftDatabaseRepository {
extension on StackEntityData { extension on StackEntityData {
Stack toDto() { Stack toDto() {
return Stack( return Stack(id: id, createdAt: createdAt, updatedAt: updatedAt, ownerId: ownerId, primaryAssetId: primaryAssetId);
id: id,
createdAt: createdAt,
updatedAt: updatedAt,
ownerId: ownerId,
primaryAssetId: primaryAssetId,
);
} }
} }

View file

@ -23,11 +23,7 @@ class IsarStoreRepository extends IsarDatabaseRepository {
.filter() .filter()
.anyOf(validStoreKeys, (query, id) => query.idEqualTo(id)) .anyOf(validStoreKeys, (query, id) => query.idEqualTo(id))
.watch(fireImmediately: true) .watch(fireImmediately: true)
.asyncExpand( .asyncExpand((entities) => Stream.fromFutures(entities.map((e) async => _toUpdateEvent(e))));
(entities) => Stream.fromFutures(
entities.map((e) async => _toUpdateEvent(e)),
),
);
} }
Future<void> delete<T>(StoreKey<T> key) async { Future<void> delete<T>(StoreKey<T> key) async {
@ -68,14 +64,17 @@ class IsarStoreRepository extends IsarDatabaseRepository {
return StoreDto(key, value); return StoreDto(key, value);
} }
Future<T?> _toValue<T>(StoreKey<T> key, StoreValue entity) async => switch (key.type) { Future<T?> _toValue<T>(StoreKey<T> key, StoreValue entity) async =>
switch (key.type) {
const (int) => entity.intValue, const (int) => entity.intValue,
const (String) => entity.strValue, const (String) => entity.strValue,
const (bool) => entity.intValue == 1, const (bool) => entity.intValue == 1,
const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!),
const (UserDto) => entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!), const (UserDto) =>
entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!),
_ => null, _ => null,
} as T?; }
as T?;
Future<StoreValue> _fromValue<T>(StoreKey<T> key, T value) async { Future<StoreValue> _fromValue<T>(StoreKey<T> key, T value) async {
final (int? intValue, String? strValue) = switch (key.type) { final (int? intValue, String? strValue) = switch (key.type) {
@ -83,13 +82,8 @@ class IsarStoreRepository extends IsarDatabaseRepository {
const (String) => (null, value as String), const (String) => (null, value as String),
const (bool) => ((value as bool) ? 1 : 0, null), const (bool) => ((value as bool) ? 1 : 0, null),
const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null), const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null),
const (UserDto) => ( const (UserDto) => (null, (await IsarUserRepository(_db).update(value as UserDto)).id),
null, _ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"),
(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); return StoreValue(key.id, intValue: intValue, strValue: strValue);
} }

View file

@ -27,10 +27,7 @@ class SyncApiRepository {
final client = httpClient ?? http.Client(); final client = httpClient ?? http.Client();
final endpoint = "${_api.apiClient.basePath}/sync/stream"; final endpoint = "${_api.apiClient.basePath}/sync/stream";
final headers = { final headers = {'Content-Type': 'application/json', 'Accept': 'application/jsonlines+json'};
'Content-Type': 'application/json',
'Accept': 'application/jsonlines+json',
};
final headerParams = <String, String>{}; final headerParams = <String, String>{};
await _api.applyToParams([], headerParams); await _api.applyToParams([], headerParams);
@ -78,10 +75,7 @@ class SyncApiRepository {
if (response.statusCode != 200) { if (response.statusCode != 200) {
final errorBody = await response.stream.bytesToString(); final errorBody = await response.stream.bytesToString();
throw ApiException( throw ApiException(response.statusCode, 'Failed to get sync stream: $errorBody');
response.statusCode,
'Failed to get sync stream: $errorBody',
);
} }
await for (final chunk in response.stream.transform(utf8.decoder)) { await for (final chunk in response.stream.transform(utf8.decoder)) {

View file

@ -42,16 +42,9 @@ class SyncStreamRepository extends DriftDatabaseRepository {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final user in data) { for (final user in data) {
final companion = UserEntityCompanion( final companion = UserEntityCompanion(name: Value(user.name), email: Value(user.email));
name: Value(user.name),
email: Value(user.email),
);
batch.insert( batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion));
_db.userEntity,
companion.copyWith(id: Value(user.id)),
onConflict: DoUpdate((_) => companion),
);
} }
}); });
} catch (error, stack) { } catch (error, stack) {
@ -66,10 +59,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
for (final partner in data) { for (final partner in data) {
batch.delete( batch.delete(
_db.partnerEntity, _db.partnerEntity,
PartnerEntityCompanion( PartnerEntityCompanion(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)),
sharedById: Value(partner.sharedById),
sharedWithId: Value(partner.sharedWithId),
),
); );
} }
}); });
@ -87,10 +77,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
batch.insert( batch.insert(
_db.partnerEntity, _db.partnerEntity,
companion.copyWith( companion.copyWith(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)),
sharedById: Value(partner.sharedById),
sharedWithId: Value(partner.sharedWithId),
),
onConflict: DoUpdate((_) => companion), onConflict: DoUpdate((_) => companion),
); );
} }
@ -101,24 +88,16 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
} }
Future<void> deleteAssetsV1( Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data, {String debugLabel = 'user'}) async {
Iterable<SyncAssetDeleteV1> data, {
String debugLabel = 'user',
}) async {
try { try {
await _db.remoteAssetEntity.deleteWhere( await _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.assetId)));
(row) => row.id.isIn(data.map((e) => e.assetId)),
);
} catch (error, stack) { } catch (error, stack) {
_logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack); _logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack);
rethrow; rethrow;
} }
} }
Future<void> updateAssetsV1( Future<void> updateAssetsV1(Iterable<SyncAssetV1> data, {String debugLabel = 'user'}) async {
Iterable<SyncAssetV1> data, {
String debugLabel = 'user',
}) async {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final asset in data) { for (final asset in data) {
@ -152,10 +131,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
} }
Future<void> updateAssetsExifV1( Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data, {String debugLabel = 'user'}) async {
Iterable<SyncAssetExifV1> data, {
String debugLabel = 'user',
}) async {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final exif in data) { for (final exif in data) {
@ -191,20 +167,14 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
}); });
} catch (error, stack) { } catch (error, stack) {
_logger.severe( _logger.severe('Error: updateAssetsExifV1 - $debugLabel', error, stack);
'Error: updateAssetsExifV1 - $debugLabel',
error,
stack,
);
rethrow; rethrow;
} }
} }
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async { Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async {
try { try {
await _db.remoteAlbumEntity.deleteWhere( await _db.remoteAlbumEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.albumId)));
(row) => row.id.isIn(data.map((e) => e.albumId)),
);
} catch (error, stack) { } catch (error, stack) {
_logger.severe('Error: deleteAlbumsV1', error, stack); _logger.severe('Error: deleteAlbumsV1', error, stack);
rethrow; rethrow;
@ -245,10 +215,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
for (final album in data) { for (final album in data) {
batch.delete( batch.delete(
_db.remoteAlbumUserEntity, _db.remoteAlbumUserEntity,
RemoteAlbumUserEntityCompanion( RemoteAlbumUserEntityCompanion(albumId: Value(album.albumId), userId: Value(album.userId)),
albumId: Value(album.albumId),
userId: Value(album.userId),
),
); );
} }
}); });
@ -258,49 +225,32 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
} }
Future<void> updateAlbumUsersV1( Future<void> updateAlbumUsersV1(Iterable<SyncAlbumUserV1> data, {String debugLabel = 'user'}) async {
Iterable<SyncAlbumUserV1> data, {
String debugLabel = 'user',
}) async {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final album in data) { for (final album in data) {
final companion = RemoteAlbumUserEntityCompanion( final companion = RemoteAlbumUserEntityCompanion(role: Value(album.role.toAlbumUserRole()));
role: Value(album.role.toAlbumUserRole()),
);
batch.insert( batch.insert(
_db.remoteAlbumUserEntity, _db.remoteAlbumUserEntity,
companion.copyWith( companion.copyWith(albumId: Value(album.albumId), userId: Value(album.userId)),
albumId: Value(album.albumId),
userId: Value(album.userId),
),
onConflict: DoUpdate((_) => companion), onConflict: DoUpdate((_) => companion),
); );
} }
}); });
} catch (error, stack) { } catch (error, stack) {
_logger.severe( _logger.severe('Error: updateAlbumUsersV1 - $debugLabel', error, stack);
'Error: updateAlbumUsersV1 - $debugLabel',
error,
stack,
);
rethrow; rethrow;
} }
} }
Future<void> deleteAlbumToAssetsV1( Future<void> deleteAlbumToAssetsV1(Iterable<SyncAlbumToAssetDeleteV1> data) async {
Iterable<SyncAlbumToAssetDeleteV1> data,
) async {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final album in data) { for (final album in data) {
batch.delete( batch.delete(
_db.remoteAlbumAssetEntity, _db.remoteAlbumAssetEntity,
RemoteAlbumAssetEntityCompanion( RemoteAlbumAssetEntityCompanion(albumId: Value(album.albumId), assetId: Value(album.assetId)),
albumId: Value(album.albumId),
assetId: Value(album.assetId),
),
); );
} }
}); });
@ -310,10 +260,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
} }
Future<void> updateAlbumToAssetsV1( Future<void> updateAlbumToAssetsV1(Iterable<SyncAlbumToAssetV1> data, {String debugLabel = 'user'}) async {
Iterable<SyncAlbumToAssetV1> data, {
String debugLabel = 'user',
}) async {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final album in data) { for (final album in data) {
@ -322,19 +269,11 @@ class SyncStreamRepository extends DriftDatabaseRepository {
assetId: Value(album.assetId), assetId: Value(album.assetId),
); );
batch.insert( batch.insert(_db.remoteAlbumAssetEntity, companion, onConflict: DoNothing());
_db.remoteAlbumAssetEntity,
companion,
onConflict: DoNothing(),
);
} }
}); });
} catch (error, stack) { } catch (error, stack) {
_logger.severe( _logger.severe('Error: updateAlbumToAssetsV1 - $debugLabel', error, stack);
'Error: updateAlbumToAssetsV1 - $debugLabel',
error,
stack,
);
rethrow; rethrow;
} }
} }
@ -371,9 +310,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
Future<void> deleteMemoriesV1(Iterable<SyncMemoryDeleteV1> data) async { Future<void> deleteMemoriesV1(Iterable<SyncMemoryDeleteV1> data) async {
try { try {
await _db.memoryEntity.deleteWhere( await _db.memoryEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.memoryId)));
(row) => row.id.isIn(data.map((e) => e.memoryId)),
);
} catch (error, stack) { } catch (error, stack) {
_logger.severe('Error: deleteMemoriesV1', error, stack); _logger.severe('Error: deleteMemoriesV1', error, stack);
rethrow; rethrow;
@ -384,16 +321,9 @@ class SyncStreamRepository extends DriftDatabaseRepository {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final asset in data) { for (final asset in data) {
final companion = MemoryAssetEntityCompanion( final companion = MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId));
memoryId: Value(asset.memoryId),
assetId: Value(asset.assetId),
);
batch.insert( batch.insert(_db.memoryAssetEntity, companion, onConflict: DoNothing());
_db.memoryAssetEntity,
companion,
onConflict: DoNothing(),
);
} }
}); });
} catch (error, stack) { } catch (error, stack) {
@ -402,18 +332,13 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
} }
Future<void> deleteMemoryAssetsV1( Future<void> deleteMemoryAssetsV1(Iterable<SyncMemoryAssetDeleteV1> data) async {
Iterable<SyncMemoryAssetDeleteV1> data,
) async {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final asset in data) { for (final asset in data) {
batch.delete( batch.delete(
_db.memoryAssetEntity, _db.memoryAssetEntity,
MemoryAssetEntityCompanion( MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId)),
memoryId: Value(asset.memoryId),
assetId: Value(asset.assetId),
),
); );
} }
}); });
@ -423,10 +348,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
} }
Future<void> updateStacksV1( Future<void> updateStacksV1(Iterable<SyncStackV1> data, {String debugLabel = 'user'}) async {
Iterable<SyncStackV1> data, {
String debugLabel = 'user',
}) async {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final stack in data) { for (final stack in data) {
@ -450,36 +372,24 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
} }
Future<void> deleteStacksV1( Future<void> deleteStacksV1(Iterable<SyncStackDeleteV1> data, {String debugLabel = 'user'}) async {
Iterable<SyncStackDeleteV1> data, {
String debugLabel = 'user',
}) async {
try { try {
await _db.stackEntity.deleteWhere( await _db.stackEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.stackId)));
(row) => row.id.isIn(data.map((e) => e.stackId)),
);
} catch (error, stack) { } catch (error, stack) {
_logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack); _logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack);
rethrow; rethrow;
} }
} }
Future<void> updateUserMetadatasV1( Future<void> updateUserMetadatasV1(Iterable<SyncUserMetadataV1> data) async {
Iterable<SyncUserMetadataV1> data,
) async {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final userMetadata in data) { for (final userMetadata in data) {
final companion = UserMetadataEntityCompanion( final companion = UserMetadataEntityCompanion(value: Value(userMetadata.value as Map<String, Object?>));
value: Value(userMetadata.value as Map<String, Object?>),
);
batch.insert( batch.insert(
_db.userMetadataEntity, _db.userMetadataEntity,
companion.copyWith( companion.copyWith(userId: Value(userMetadata.userId), key: Value(userMetadata.key.toUserMetadataKey())),
userId: Value(userMetadata.userId),
key: Value(userMetadata.key.toUserMetadataKey()),
),
onConflict: DoUpdate((_) => companion), onConflict: DoUpdate((_) => companion),
); );
} }
@ -490,9 +400,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
} }
Future<void> deleteUserMetadatasV1( Future<void> deleteUserMetadatasV1(Iterable<SyncUserMetadataDeleteV1> data) async {
Iterable<SyncUserMetadataDeleteV1> data,
) async {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final userMetadata in data) { for (final userMetadata in data) {
@ -540,16 +448,11 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
} }
Future<void> deletePeopleV1( Future<void> deletePeopleV1(Iterable<SyncPersonDeleteV1> data) async {
Iterable<SyncPersonDeleteV1> data,
) async {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final person in data) { for (final person in data) {
batch.deleteWhere( batch.deleteWhere(_db.personEntity, (row) => row.id.equals(person.personId));
_db.personEntity,
(row) => row.id.equals(person.personId),
);
} }
}); });
} catch (error, stack) { } catch (error, stack) {
@ -591,10 +494,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final assetFace in data) { for (final assetFace in data) {
batch.deleteWhere( batch.deleteWhere(_db.assetFaceEntity, (row) => row.id.equals(assetFace.assetFaceId));
_db.assetFaceEntity,
(row) => row.id.equals(assetFace.assetFaceId),
);
} }
}); });
} catch (error, stack) { } catch (error, stack) {

View file

@ -21,9 +21,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
Stream<List<String>> watchTimelineUserIds(String userId) { Stream<List<String>> watchTimelineUserIds(String userId) {
final query = _db.partnerEntity.selectOnly() final query = _db.partnerEntity.selectOnly()
..addColumns([_db.partnerEntity.sharedById]) ..addColumns([_db.partnerEntity.sharedById])
..where( ..where(_db.partnerEntity.inTimeline.equals(true) & _db.partnerEntity.sharedWithId.equals(userId));
_db.partnerEntity.inTimeline.equals(true) & _db.partnerEntity.sharedWithId.equals(userId),
);
return query return query
.map((row) => row.read(_db.partnerEntity.sharedById)!) .map((row) => row.read(_db.partnerEntity.sharedById)!)
@ -33,25 +31,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
} }
TimelineQuery main(List<String> userIds, GroupAssetsBy groupBy) => ( TimelineQuery main(List<String> userIds, GroupAssetsBy groupBy) => (
bucketSource: () => _watchMainBucket( bucketSource: () => _watchMainBucket(userIds, groupBy: groupBy),
userIds, assetSource: (offset, count) => _getMainBucketAssets(userIds, offset: offset, count: count),
groupBy: groupBy,
),
assetSource: (offset, count) => _getMainBucketAssets(
userIds,
offset: offset,
count: count,
),
); );
Stream<List<Bucket>> _watchMainBucket( Stream<List<Bucket>> _watchMainBucket(List<String> userIds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
List<String> userIds, {
GroupAssetsBy groupBy = GroupAssetsBy.day,
}) {
if (groupBy == GroupAssetsBy.none) { if (groupBy == GroupAssetsBy.none) {
throw UnsupportedError( throw UnsupportedError("GroupAssetsBy.none is not supported for watchMainBucket");
"GroupAssetsBy.none is not supported for watchMainBucket",
);
} }
return _db.mergedAssetDrift return _db.mergedAssetDrift
@ -64,11 +50,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
.throttle(const Duration(seconds: 3), trailing: true); .throttle(const Duration(seconds: 3), trailing: true);
} }
Future<List<BaseAsset>> _getMainBucketAssets( Future<List<BaseAsset>> _getMainBucketAssets(List<String> userIds, {required int offset, required int count}) {
List<String> userIds, {
required int offset,
required int count,
}) {
return _db.mergedAssetDrift return _db.mergedAssetDrift
.mergedAsset(userIds, limit: (_) => Limit(count, offset)) .mergedAsset(userIds, limit: (_) => Limit(count, offset))
.map( .map(
@ -109,21 +91,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
} }
TimelineQuery localAlbum(String albumId, GroupAssetsBy groupBy) => ( TimelineQuery localAlbum(String albumId, GroupAssetsBy groupBy) => (
bucketSource: () => _watchLocalAlbumBucket( bucketSource: () => _watchLocalAlbumBucket(albumId, groupBy: groupBy),
albumId, assetSource: (offset, count) => _getLocalAlbumBucketAssets(albumId, offset: offset, count: count),
groupBy: groupBy,
),
assetSource: (offset, count) => _getLocalAlbumBucketAssets(
albumId,
offset: offset,
count: count,
),
); );
Stream<List<Bucket>> _watchLocalAlbumBucket( Stream<List<Bucket>> _watchLocalAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
String albumId, {
GroupAssetsBy groupBy = GroupAssetsBy.day,
}) {
if (groupBy == GroupAssetsBy.none) { if (groupBy == GroupAssetsBy.none) {
return _db.localAlbumAssetEntity return _db.localAlbumAssetEntity
.count(where: (row) => row.albumId.equals(albumId)) .count(where: (row) => row.albumId.equals(albumId))
@ -134,7 +106,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
final assetCountExp = _db.localAssetEntity.id.count(); final assetCountExp = _db.localAssetEntity.id.count();
final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy); final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy);
final query = _db.localAssetEntity.selectOnly().join([ final query =
_db.localAssetEntity.selectOnly().join([
innerJoin( innerJoin(
_db.localAlbumAssetEntity, _db.localAlbumAssetEntity,
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
@ -158,13 +131,9 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
}).watch(); }).watch();
} }
Future<List<BaseAsset>> _getLocalAlbumBucketAssets( Future<List<BaseAsset>> _getLocalAlbumBucketAssets(String albumId, {required int offset, required int count}) {
String albumId, { final query =
required int offset, _db.localAssetEntity.select().join([
required int count,
}) {
final query = _db.localAssetEntity.select().join(
[
innerJoin( innerJoin(
_db.localAlbumAssetEntity, _db.localAlbumAssetEntity,
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
@ -175,8 +144,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
useColumns: false, useColumns: false,
), ),
], ])
)
..addColumns([_db.remoteAssetEntity.id]) ..addColumns([_db.remoteAssetEntity.id])
..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) ..where(_db.localAlbumAssetEntity.albumId.equals(albumId))
..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)])
@ -184,28 +152,16 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
return query.map((row) { return query.map((row) {
final asset = row.readTable(_db.localAssetEntity).toDto(); final asset = row.readTable(_db.localAssetEntity).toDto();
return asset.copyWith( return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id));
remoteId: row.read(_db.remoteAssetEntity.id),
);
}).get(); }).get();
} }
TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => ( TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => (
bucketSource: () => _watchRemoteAlbumBucket( bucketSource: () => _watchRemoteAlbumBucket(albumId, groupBy: groupBy),
albumId, assetSource: (offset, count) => _getRemoteAlbumBucketAssets(albumId, offset: offset, count: count),
groupBy: groupBy,
),
assetSource: (offset, count) => _getRemoteAlbumBucketAssets(
albumId,
offset: offset,
count: count,
),
); );
Stream<List<Bucket>> _watchRemoteAlbumBucket( Stream<List<Bucket>> _watchRemoteAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
String albumId, {
GroupAssetsBy groupBy = GroupAssetsBy.day,
}) {
if (groupBy == GroupAssetsBy.none) { if (groupBy == GroupAssetsBy.none) {
return _db.remoteAlbumAssetEntity return _db.remoteAlbumAssetEntity
.count(where: (row) => row.albumId.equals(albumId)) .count(where: (row) => row.albumId.equals(albumId))
@ -217,7 +173,9 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
}); });
} }
return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).watch().switchMap((albums) { return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId)))
.watch()
.switchMap((albums) {
if (albums.isEmpty) { if (albums.isEmpty) {
return Stream.value(<Bucket>[]); return Stream.value(<Bucket>[]);
} }
@ -236,9 +194,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
useColumns: false, useColumns: false,
), ),
]) ])
..where( ..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId))
_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId),
)
..groupBy([dateExp]); ..groupBy([dateExp]);
if (isAscending) { if (isAscending) {
@ -252,17 +208,14 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
final assetCount = row.read(assetCountExp)!; final assetCount = row.read(assetCountExp)!;
return TimeBucket(date: timeline, assetCount: assetCount); return TimeBucket(date: timeline, assetCount: assetCount);
}).watch(); }).watch();
}).handleError((error) { })
.handleError((error) {
// If there's an error (e.g., album was deleted), return empty buckets // If there's an error (e.g., album was deleted), return empty buckets
return <Bucket>[]; return <Bucket>[];
}); });
} }
Future<List<BaseAsset>> _getRemoteAlbumBucketAssets( Future<List<BaseAsset>> _getRemoteAlbumBucketAssets(String albumId, {required int offset, required int count}) async {
String albumId, {
required int offset,
required int count,
}) async {
final albumData = await (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).getSingleOrNull(); final albumData = await (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).getSingleOrNull();
// If album doesn't exist (was deleted), return empty list // 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 isAscending = albumData.order == AlbumAssetOrder.asc;
final query = _db.remoteAssetEntity.select().join( final query = _db.remoteAssetEntity.select().join([
[
innerJoin( innerJoin(
_db.remoteAlbumAssetEntity, _db.remoteAlbumAssetEntity,
_db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false, useColumns: false,
), ),
], ])..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId));
)..where(
_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId),
);
if (isAscending) { if (isAscending) {
query.orderBy([OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); query.orderBy([OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
@ -339,26 +288,14 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
); );
TimelineQuery place(String place, GroupAssetsBy groupBy) => ( TimelineQuery place(String place, GroupAssetsBy groupBy) => (
bucketSource: () => _watchPlaceBucket( bucketSource: () => _watchPlaceBucket(place, groupBy: groupBy),
place, assetSource: (offset, count) => _getPlaceBucketAssets(place, offset: offset, count: count),
groupBy: groupBy,
),
assetSource: (offset, count) => _getPlaceBucketAssets(
place,
offset: offset,
count: count,
),
); );
Stream<List<Bucket>> _watchPlaceBucket( Stream<List<Bucket>> _watchPlaceBucket(String place, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
String place, {
GroupAssetsBy groupBy = GroupAssetsBy.day,
}) {
if (groupBy == GroupAssetsBy.none) { if (groupBy == GroupAssetsBy.none) {
// TODO: implement GroupAssetBy for place // TODO: implement GroupAssetBy for place
throw UnsupportedError( throw UnsupportedError("GroupAssetsBy.none is not supported for watchPlaceBucket");
"GroupAssetsBy.none is not supported for watchPlaceBucket",
);
} }
final assetCountExp = _db.remoteAssetEntity.id.count(); final assetCountExp = _db.remoteAssetEntity.id.count();
@ -388,20 +325,15 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
}).watch(); }).watch();
} }
Future<List<BaseAsset>> _getPlaceBucketAssets( Future<List<BaseAsset>> _getPlaceBucketAssets(String place, {required int offset, required int count}) {
String place, { final query =
required int offset, _db.remoteAssetEntity.select().join([
required int count,
}) {
final query = _db.remoteAssetEntity.select().join(
[
innerJoin( innerJoin(
_db.remoteExifEntity, _db.remoteExifEntity,
_db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
useColumns: false, useColumns: false,
), ),
], ])
)
..where( ..where(
_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAssetEntity.deletedAt.isNull() &
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
@ -419,12 +351,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
}) { }) {
return ( return (
bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy), bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy),
assetSource: (offset, count) => _getRemoteAssets( assetSource: (offset, count) =>
filter: filter, _getRemoteAssets(filter: filter, offset: offset, count: count, joinLocal: joinLocal),
offset: offset,
count: count,
joinLocal: joinLocal,
),
); );
} }
@ -460,7 +388,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
bool joinLocal = false, bool joinLocal = false,
}) { }) {
if (joinLocal) { if (joinLocal) {
final query = _db.remoteAssetEntity.select().join([ final query =
_db.remoteAssetEntity.select().join([
leftOuterJoin( leftOuterJoin(
_db.localAssetEntity, _db.localAssetEntity,
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
@ -507,9 +436,7 @@ extension on Expression<DateTime> {
return switch (groupBy) { return switch (groupBy) {
GroupAssetsBy.day || GroupAssetsBy.auto => localTimeExp.date, GroupAssetsBy.day || GroupAssetsBy.auto => localTimeExp.date,
GroupAssetsBy.month => localTimeExp.strftime("%Y-%m"), GroupAssetsBy.month => localTimeExp.strftime("%Y-%m"),
GroupAssetsBy.none => throw ArgumentError( GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"),
"GroupAssetsBy.none is not supported for date formatting",
),
}; };
} }
} }
@ -519,9 +446,7 @@ extension on String {
final format = switch (groupBy) { final format = switch (groupBy) {
GroupAssetsBy.day || GroupAssetsBy.auto => "y-M-d", GroupAssetsBy.day || GroupAssetsBy.auto => "y-M-d",
GroupAssetsBy.month => "y-M", GroupAssetsBy.month => "y-M",
GroupAssetsBy.none => throw ArgumentError( GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"),
"GroupAssetsBy.none is not supported for date formatting",
),
}; };
try { try {
return DateFormat(format).parse(this); return DateFormat(format).parse(this);

View file

@ -17,15 +17,8 @@ class UserApiRepository extends ApiRepository {
return UserConverter.fromAdminDto(adminDto, preferenceDto); return UserConverter.fromAdminDto(adminDto, preferenceDto);
} }
Future<String> createProfileImage({ Future<String> createProfileImage({required String name, required Uint8List data}) async {
required String name, final res = await checkNull(_api.createProfileImage(MultipartFile.fromBytes('file', data, filename: name)));
required Uint8List data,
}) async {
final res = await checkNull(
_api.createProfileImage(
MultipartFile.fromBytes('file', data, filename: name),
),
);
return res.profileImagePath; return res.profileImagePath;
} }

View file

@ -18,20 +18,8 @@ class DriftUserMetadataRepository extends DriftDatabaseRepository {
extension on UserMetadataEntityData { extension on UserMetadataEntityData {
UserMetadata toDto() => switch (key) { UserMetadata toDto() => switch (key) {
UserMetadataKey.onboarding => UserMetadata( UserMetadataKey.onboarding => UserMetadata(userId: userId, key: key, onboarding: Onboarding.fromMap(value)),
userId: userId, UserMetadataKey.preferences => UserMetadata(userId: userId, key: key, preferences: Preferences.fromMap(value)),
key: key, UserMetadataKey.license => UserMetadata(userId: userId, key: key, license: License.fromMap(value)),
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),
),
}; };
} }

View file

@ -15,11 +15,7 @@ abstract final class UserConverter {
avatarColor: dto.avatarColor.toAvatarColor(), avatarColor: dto.avatarColor.toAvatarColor(),
); );
static UserDto fromAdminDto( static UserDto fromAdminDto(UserAdminResponseDto adminDto, [UserPreferencesResponseDto? preferenceDto]) => UserDto(
UserAdminResponseDto adminDto, [
UserPreferencesResponseDto? preferenceDto,
]) =>
UserDto(
id: adminDto.id, id: adminDto.id,
email: adminDto.email, email: adminDto.email,
name: adminDto.name, name: adminDto.name,

View file

@ -50,10 +50,7 @@ void main() async {
runApp( runApp(
ProviderScope( ProviderScope(
overrides: [ overrides: [dbProvider.overrideWithValue(db), isarProvider.overrideWithValue(db)],
dbProvider.overrideWithValue(db),
isarProvider.overrideWithValue(db),
],
child: const MainWidget(), child: const MainWidget(),
), ),
); );
@ -100,23 +97,15 @@ Future<void> initApp() async {
globalConfig: (Config.holdingQueue, (1000, 1000, 1000)), globalConfig: (Config.holdingQueue, (1000, 1000, 1000)),
); );
await FileDownloader().trackTasksInGroup( await FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false);
kDownloadGroupLivePhoto,
markDownloadedComplete: false,
);
await FileDownloader().trackTasks(); await FileDownloader().trackTasks();
LicenseRegistry.addLicense( LicenseRegistry.addLicense(() async* {
() async* {
for (final license in nonPubLicenses.entries) { for (final license in nonPubLicenses.entries) {
yield LicenseEntryWithLineBreaks( yield LicenseEntryWithLineBreaks([license.key], license.value);
[license.key],
license.value,
);
} }
}, });
);
} }
class ImmichApp extends ConsumerStatefulWidget { class ImmichApp extends ConsumerStatefulWidget {
@ -160,9 +149,7 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
// Sets the navigation bar color // Sets the navigation bar color
SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle( SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent);
systemNavigationBarColor: Colors.transparent,
);
if (Platform.isAndroid) { if (Platform.isAndroid) {
// Android 8 does not support transparent app bars // Android 8 does not support transparent app bars
final info = await DeviceInfoPlugin().androidInfo; final info = await DeviceInfoPlugin().androidInfo;
@ -177,40 +164,22 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
void _configureFileDownloaderNotifications() { void _configureFileDownloaderNotifications() {
FileDownloader().configureNotificationForGroup( FileDownloader().configureNotificationForGroup(
kDownloadGroupImage, kDownloadGroupImage,
running: TaskNotification( running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'),
'downloading_media'.tr(), complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'),
'${'file_name'.tr()}: {filename}',
),
complete: TaskNotification(
'download_finished'.tr(),
'${'file_name'.tr()}: {filename}',
),
progressBar: true, progressBar: true,
); );
FileDownloader().configureNotificationForGroup( FileDownloader().configureNotificationForGroup(
kDownloadGroupVideo, kDownloadGroupVideo,
running: TaskNotification( running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'),
'downloading_media'.tr(), complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'),
'${'file_name'.tr()}: {filename}',
),
complete: TaskNotification(
'download_finished'.tr(),
'${'file_name'.tr()}: {filename}',
),
progressBar: true, progressBar: true,
); );
FileDownloader().configureNotificationForGroup( FileDownloader().configureNotificationForGroup(
kManualUploadGroup, kManualUploadGroup,
running: TaskNotification( running: TaskNotification('uploading_media'.tr(), '${'file_name'.tr()}: {displayName}'),
'uploading_media'.tr(), complete: TaskNotification('upload_finished'.tr(), '${'file_name'.tr()}: {displayName}'),
'${'file_name'.tr()}: {displayName}',
),
complete: TaskNotification(
'upload_finished'.tr(),
'${'file_name'.tr()}: {displayName}',
),
progressBar: true, progressBar: true,
); );
} }
@ -222,19 +191,13 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
final isColdStart = currentRouteName == null || currentRouteName == SplashScreenRoute.name; final isColdStart = currentRouteName == null || currentRouteName == SplashScreenRoute.name;
if (deepLink.uri.scheme == "immich") { if (deepLink.uri.scheme == "immich") {
final proposedRoute = await deepLinkHandler.handleScheme( final proposedRoute = await deepLinkHandler.handleScheme(deepLink, isColdStart);
deepLink,
isColdStart,
);
return proposedRoute; return proposedRoute;
} }
if (deepLink.uri.host == "my.immich.app") { if (deepLink.uri.host == "my.immich.app") {
final proposedRoute = await deepLinkHandler.handleMyImmichApp( final proposedRoute = await deepLinkHandler.handleMyImmichApp(deepLink, isColdStart);
deepLink,
isColdStart,
);
return proposedRoute; return proposedRoute;
} }
@ -275,9 +238,7 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
final immichTheme = ref.watch(immichThemeProvider); final immichTheme = ref.watch(immichThemeProvider);
return ProviderScope( return ProviderScope(
overrides: [ overrides: [localeProvider.overrideWithValue(context.locale)],
localeProvider.overrideWithValue(context.locale),
],
child: MaterialApp.router( child: MaterialApp.router(
title: 'Immich', title: 'Immich',
debugShowCheckedModeBanner: true, debugShowCheckedModeBanner: true,
@ -285,14 +246,8 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
supportedLocales: context.supportedLocales, supportedLocales: context.supportedLocales,
locale: context.locale, locale: context.locale,
themeMode: ref.watch(immichThemeModeProvider), themeMode: ref.watch(immichThemeModeProvider),
darkTheme: getThemeData( darkTheme: getThemeData(colorScheme: immichTheme.dark, locale: context.locale),
colorScheme: immichTheme.dark, theme: getThemeData(colorScheme: immichTheme.light, locale: context.locale),
locale: context.locale,
),
theme: getThemeData(
colorScheme: immichTheme.light,
locale: context.locale,
),
routerConfig: router.config( routerConfig: router.config(
deepLinkBuilder: _deepLinkBuilder, deepLinkBuilder: _deepLinkBuilder,
navigatorObservers: () => [AppNavigationObserver(ref: ref), HeroController()], navigatorObservers: () => [AppNavigationObserver(ref: ref), HeroController()],

View file

@ -7,15 +7,9 @@ class AlbumAddAssetsResponse {
List<String> alreadyInAlbum; List<String> alreadyInAlbum;
int successfullyAdded; int successfullyAdded;
AlbumAddAssetsResponse({ AlbumAddAssetsResponse({required this.alreadyInAlbum, required this.successfullyAdded});
required this.alreadyInAlbum,
required this.successfullyAdded,
});
AlbumAddAssetsResponse copyWith({ AlbumAddAssetsResponse copyWith({List<String>? alreadyInAlbum, int? successfullyAdded}) {
List<String>? alreadyInAlbum,
int? successfullyAdded,
}) {
return AlbumAddAssetsResponse( return AlbumAddAssetsResponse(
alreadyInAlbum: alreadyInAlbum ?? this.alreadyInAlbum, alreadyInAlbum: alreadyInAlbum ?? this.alreadyInAlbum,
successfullyAdded: successfullyAdded ?? this.successfullyAdded, successfullyAdded: successfullyAdded ?? this.successfullyAdded,
@ -23,10 +17,7 @@ class AlbumAddAssetsResponse {
} }
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return <String, dynamic>{ return <String, dynamic>{'alreadyInAlbum': alreadyInAlbum, 'successfullyAdded': successfullyAdded};
'alreadyInAlbum': alreadyInAlbum,
'successfullyAdded': successfullyAdded,
};
} }
String toJson() => json.encode(toMap()); String toJson() => json.encode(toMap());

View file

@ -1,5 +1 @@
enum QuickFilterMode { enum QuickFilterMode { all, sharedWithMe, myAlbums }
all,
sharedWithMe,
myAlbums,
}

View file

@ -11,11 +11,7 @@ class AlbumViewerPageState {
required this.editDescriptionText, required this.editDescriptionText,
}); });
AlbumViewerPageState copyWith({ AlbumViewerPageState copyWith({bool? isEditAlbum, String? editTitleText, String? editDescriptionText}) {
bool? isEditAlbum,
String? editTitleText,
String? editDescriptionText,
}) {
return AlbumViewerPageState( return AlbumViewerPageState(
isEditAlbum: isEditAlbum ?? this.isEditAlbum, isEditAlbum: isEditAlbum ?? this.isEditAlbum,
editTitleText: editTitleText ?? this.editTitleText, editTitleText: editTitleText ?? this.editTitleText,

View file

@ -4,9 +4,7 @@ import 'package:immich_mobile/entities/asset.entity.dart';
class AssetSelectionPageResult { class AssetSelectionPageResult {
final Set<Asset> selectedAssets; final Set<Asset> selectedAssets;
const AssetSelectionPageResult({ const AssetSelectionPageResult({required this.selectedAssets});
required this.selectedAssets,
});
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (identical(this, other)) return true; if (identical(this, other)) return true;

View file

@ -13,12 +13,7 @@ class AssetSelectionState {
this.selectedCount = 0, this.selectedCount = 0,
}); });
AssetSelectionState copyWith({ AssetSelectionState copyWith({bool? hasRemote, bool? hasLocal, bool? hasMerged, int? selectedCount}) {
bool? hasRemote,
bool? hasLocal,
bool? hasMerged,
int? selectedCount,
}) {
return AssetSelectionState( return AssetSelectionState(
hasRemote: hasRemote ?? this.hasRemote, hasRemote: hasRemote ?? this.hasRemote,
hasLocal: hasLocal ?? this.hasLocal, hasLocal: hasLocal ?? this.hasLocal,

View file

@ -5,19 +5,10 @@ class AuxilaryEndpoint {
final String url; final String url;
final AuxCheckStatus status; final AuxCheckStatus status;
const AuxilaryEndpoint({ const AuxilaryEndpoint({required this.url, required this.status});
required this.url,
required this.status,
});
AuxilaryEndpoint copyWith({ AuxilaryEndpoint copyWith({String? url, AuxCheckStatus? status}) {
String? url, return AuxilaryEndpoint(url: url ?? this.url, status: status ?? this.status);
AuxCheckStatus? status,
}) {
return AuxilaryEndpoint(
url: url ?? this.url,
status: status ?? this.status,
);
} }
@override @override
@ -34,10 +25,7 @@ class AuxilaryEndpoint {
int get hashCode => url.hashCode ^ status.hashCode; int get hashCode => url.hashCode ^ status.hashCode;
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return <String, dynamic>{ return <String, dynamic>{'url': url, 'status': status.toMap()};
'url': url,
'status': status.toMap(),
};
} }
factory AuxilaryEndpoint.fromMap(Map<String, dynamic> map) { factory AuxilaryEndpoint.fromMap(Map<String, dynamic> map) {
@ -55,9 +43,7 @@ class AuxilaryEndpoint {
class AuxCheckStatus { class AuxCheckStatus {
final String name; final String name;
const AuxCheckStatus({ const AuxCheckStatus({required this.name});
required this.name,
});
const AuxCheckStatus._(this.name); const AuxCheckStatus._(this.name);
static const loading = AuxCheckStatus._('loading'); static const loading = AuxCheckStatus._('loading');
@ -75,24 +61,16 @@ class AuxCheckStatus {
@override @override
int get hashCode => name.hashCode; int get hashCode => name.hashCode;
AuxCheckStatus copyWith({ AuxCheckStatus copyWith({String? name}) {
String? name, return AuxCheckStatus(name: name ?? this.name);
}) {
return AuxCheckStatus(
name: name ?? this.name,
);
} }
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return <String, dynamic>{ return <String, dynamic>{'name': name};
'name': name,
};
} }
factory AuxCheckStatus.fromMap(Map<String, dynamic> map) { factory AuxCheckStatus.fromMap(Map<String, dynamic> map) {
return AuxCheckStatus( return AuxCheckStatus(name: map['name'] as String);
name: map['name'] as String,
);
} }
String toJson() => json.encode(toMap()); String toJson() => json.encode(toMap());

View file

@ -5,18 +5,12 @@ class BiometricStatus {
final List<BiometricType> availableBiometrics; final List<BiometricType> availableBiometrics;
final bool canAuthenticate; final bool canAuthenticate;
const BiometricStatus({ const BiometricStatus({required this.availableBiometrics, required this.canAuthenticate});
required this.availableBiometrics,
required this.canAuthenticate,
});
@override @override
String toString() => 'BiometricStatus(availableBiometrics: $availableBiometrics, canAuthenticate: $canAuthenticate)'; String toString() => 'BiometricStatus(availableBiometrics: $availableBiometrics, canAuthenticate: $canAuthenticate)';
BiometricStatus copyWith({ BiometricStatus copyWith({List<BiometricType>? availableBiometrics, bool? canAuthenticate}) {
List<BiometricType>? availableBiometrics,
bool? canAuthenticate,
}) {
return BiometricStatus( return BiometricStatus(
availableBiometrics: availableBiometrics ?? this.availableBiometrics, availableBiometrics: availableBiometrics ?? this.availableBiometrics,
canAuthenticate: canAuthenticate ?? this.canAuthenticate, canAuthenticate: canAuthenticate ?? this.canAuthenticate,

View file

@ -4,17 +4,9 @@ class AvailableAlbum {
final Album album; final Album album;
final int assetCount; final int assetCount;
final DateTime? lastBackup; final DateTime? lastBackup;
const AvailableAlbum({ const AvailableAlbum({required this.album, required this.assetCount, this.lastBackup});
required this.album,
required this.assetCount,
this.lastBackup,
});
AvailableAlbum copyWith({ AvailableAlbum copyWith({Album? album, int? assetCount, DateTime? lastBackup}) {
Album? album,
int? assetCount,
DateTime? lastBackup,
}) {
return AvailableAlbum( return AvailableAlbum(
album: album ?? this.album, album: album ?? this.album,
assetCount: assetCount ?? this.assetCount, assetCount: assetCount ?? this.assetCount,

View file

@ -148,10 +148,7 @@ class BackUpState {
collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) && collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) &&
collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) && collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) &&
collectionEquals(other.allUniqueAssets, allUniqueAssets) && collectionEquals(other.allUniqueAssets, allUniqueAssets) &&
collectionEquals( collectionEquals(other.selectedAlbumsBackupAssetsIds, selectedAlbumsBackupAssetsIds) &&
other.selectedAlbumsBackupAssetsIds,
selectedAlbumsBackupAssetsIds,
) &&
other.currentUploadAsset == currentUploadAsset; other.currentUploadAsset == currentUploadAsset;
} }

View file

@ -5,17 +5,9 @@ class SuccessUploadAsset {
final String remoteAssetId; final String remoteAssetId;
final bool isDuplicate; final bool isDuplicate;
const SuccessUploadAsset({ const SuccessUploadAsset({required this.candidate, required this.remoteAssetId, required this.isDuplicate});
required this.candidate,
required this.remoteAssetId,
required this.isDuplicate,
});
SuccessUploadAsset copyWith({ SuccessUploadAsset copyWith({BackupCandidate? candidate, String? remoteAssetId, bool? isDuplicate}) {
BackupCandidate? candidate,
String? remoteAssetId,
bool? isDuplicate,
}) {
return SuccessUploadAsset( return SuccessUploadAsset(
candidate: candidate ?? this.candidate, candidate: candidate ?? this.candidate,
remoteAssetId: remoteAssetId ?? this.remoteAssetId, remoteAssetId: remoteAssetId ?? this.remoteAssetId,

Some files were not shown because too many files have changed in this diff Show more