mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
add full sync
This commit is contained in:
parent
877c3b028b
commit
e81b61c98b
@ -1,19 +1,15 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
|
||||||
|
|
||||||
class Asset extends Table {
|
class Asset extends Table {
|
||||||
const Asset();
|
const Asset();
|
||||||
|
|
||||||
IntColumn get id => integer().autoIncrement()();
|
|
||||||
|
|
||||||
TextColumn get name => text()();
|
TextColumn get name => text()();
|
||||||
TextColumn get checksum => text().unique()();
|
TextColumn get checksum => text().unique()();
|
||||||
IntColumn get height => integer()();
|
IntColumn get height => integer().nullable()();
|
||||||
IntColumn get width => integer()();
|
IntColumn get width => integer().nullable()();
|
||||||
IntColumn get type => intEnum<AssetType>()();
|
IntColumn get type => intEnum<AssetType>()();
|
||||||
DateTimeColumn get createdTime => dateTime()();
|
DateTimeColumn get createdTime => dateTime()();
|
||||||
DateTimeColumn get modifiedTime =>
|
DateTimeColumn get modifiedTime =>
|
||||||
dateTime().withDefault(currentDateAndTime)();
|
dateTime().withDefault(currentDateAndTime)();
|
||||||
IntColumn get duration => integer().withDefault(const Constant(0))();
|
IntColumn get duration => integer().withDefault(const Constant(0))();
|
||||||
BoolColumn get isLivePhoto => boolean().withDefault(const Constant(false))();
|
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,8 @@ import 'package:immich_mobile/domain/entities/asset.entity.dart';
|
|||||||
class LocalAsset extends Asset {
|
class LocalAsset extends Asset {
|
||||||
const LocalAsset();
|
const LocalAsset();
|
||||||
|
|
||||||
TextColumn get localId => text().unique()();
|
TextColumn get localId => text()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {localId};
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,9 @@ import 'package:immich_mobile/domain/entities/asset.entity.dart';
|
|||||||
class RemoteAsset extends Asset {
|
class RemoteAsset extends Asset {
|
||||||
const RemoteAsset();
|
const RemoteAsset();
|
||||||
|
|
||||||
TextColumn get remoteId => text().unique()();
|
TextColumn get remoteId => text()();
|
||||||
|
TextColumn get livePhotoVideoId => text().nullable()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {remoteId};
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
abstract class IDatabaseRepository<T> {
|
|
||||||
/// Current version of the DB to aid with migration
|
|
||||||
int get schemaVersion;
|
|
||||||
|
|
||||||
/// Initializes the DB and returns the corresponding object
|
|
||||||
T init();
|
|
||||||
|
|
||||||
/// Check and migrate the DB to the latest schema
|
|
||||||
void migrateDB();
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ import 'package:immich_mobile/domain/models/log.model.dart';
|
|||||||
|
|
||||||
abstract class ILogRepository {
|
abstract class ILogRepository {
|
||||||
/// Fetches all logs
|
/// Fetches all logs
|
||||||
FutureOr<List<LogMessage>> fetchLogs();
|
FutureOr<List<LogMessage>> fetchAll();
|
||||||
|
|
||||||
/// Inserts a new log into the DB
|
/// Inserts a new log into the DB
|
||||||
FutureOr<bool> add(LogMessage log);
|
FutureOr<bool> add(LogMessage log);
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
import 'package:immich_mobile/domain/models/asset/remote_asset.model.dart';
|
||||||
|
|
||||||
|
abstract class IRemoteAssetRepository {
|
||||||
|
/// Batch insert asset
|
||||||
|
Future<bool> addAll(Iterable<RemoteAsset> assets);
|
||||||
|
}
|
@ -4,8 +4,8 @@ import 'package:immich_mobile/domain/models/user.model.dart';
|
|||||||
|
|
||||||
abstract class IUserRepository {
|
abstract class IUserRepository {
|
||||||
/// Fetches user
|
/// Fetches user
|
||||||
FutureOr<User?> getUser(String userId);
|
FutureOr<User?> fetch(String userId);
|
||||||
|
|
||||||
/// Insert user
|
/// Insert user
|
||||||
FutureOr<bool> insertUser(User user);
|
FutureOr<bool> add(User user);
|
||||||
}
|
}
|
||||||
|
@ -7,32 +7,27 @@ enum AssetType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Asset {
|
class Asset {
|
||||||
final int id;
|
|
||||||
final String name;
|
final String name;
|
||||||
final String checksum;
|
final String checksum;
|
||||||
final int height;
|
final int? height;
|
||||||
final int width;
|
final int? width;
|
||||||
final AssetType type;
|
final AssetType type;
|
||||||
final DateTime createdTime;
|
final DateTime createdTime;
|
||||||
final DateTime modifiedTime;
|
final DateTime modifiedTime;
|
||||||
final int duration;
|
final int duration;
|
||||||
final bool isLivePhoto;
|
|
||||||
|
|
||||||
const Asset({
|
const Asset({
|
||||||
required this.id,
|
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.checksum,
|
required this.checksum,
|
||||||
required this.height,
|
this.height,
|
||||||
required this.width,
|
this.width,
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.createdTime,
|
required this.createdTime,
|
||||||
required this.modifiedTime,
|
required this.modifiedTime,
|
||||||
required this.duration,
|
required this.duration,
|
||||||
required this.isLivePhoto,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Asset copyWith({
|
Asset copyWith({
|
||||||
int? id,
|
|
||||||
String? name,
|
String? name,
|
||||||
String? checksum,
|
String? checksum,
|
||||||
int? height,
|
int? height,
|
||||||
@ -41,10 +36,8 @@ class Asset {
|
|||||||
DateTime? createdTime,
|
DateTime? createdTime,
|
||||||
DateTime? modifiedTime,
|
DateTime? modifiedTime,
|
||||||
int? duration,
|
int? duration,
|
||||||
bool? isLivePhoto,
|
|
||||||
}) {
|
}) {
|
||||||
return Asset(
|
return Asset(
|
||||||
id: id ?? this.id,
|
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
checksum: checksum ?? this.checksum,
|
checksum: checksum ?? this.checksum,
|
||||||
height: height ?? this.height,
|
height: height ?? this.height,
|
||||||
@ -53,52 +46,45 @@ class Asset {
|
|||||||
createdTime: createdTime ?? this.createdTime,
|
createdTime: createdTime ?? this.createdTime,
|
||||||
modifiedTime: modifiedTime ?? this.modifiedTime,
|
modifiedTime: modifiedTime ?? this.modifiedTime,
|
||||||
duration: duration ?? this.duration,
|
duration: duration ?? this.duration,
|
||||||
isLivePhoto: isLivePhoto ?? this.isLivePhoto,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => """
|
String toString() => """
|
||||||
{
|
{
|
||||||
"id": $id,
|
|
||||||
"name": "$name",
|
"name": "$name",
|
||||||
"checksum": "$checksum",
|
"checksum": "$checksum",
|
||||||
"height": $height,
|
"height": ${height ?? "-"},
|
||||||
"width": $width,
|
"width": ${width ?? "-"},
|
||||||
"type": "$type",
|
"type": "$type",
|
||||||
"createdTime": "$createdTime",
|
"createdTime": "$createdTime",
|
||||||
"modifiedTime": "$modifiedTime",
|
"modifiedTime": "$modifiedTime",
|
||||||
"duration": "$duration",
|
"duration": "$duration",
|
||||||
"isLivePhoto": "$isLivePhoto",
|
|
||||||
}""";
|
}""";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant Asset other) {
|
bool operator ==(covariant Asset other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
return other.id == id &&
|
return other.name == name &&
|
||||||
other.name == name &&
|
|
||||||
other.checksum == checksum &&
|
other.checksum == checksum &&
|
||||||
other.height == height &&
|
other.height == height &&
|
||||||
other.width == width &&
|
other.width == width &&
|
||||||
other.type == type &&
|
other.type == type &&
|
||||||
other.createdTime == createdTime &&
|
other.createdTime == createdTime &&
|
||||||
other.modifiedTime == modifiedTime &&
|
other.modifiedTime == modifiedTime &&
|
||||||
other.duration == duration &&
|
other.duration == duration;
|
||||||
other.isLivePhoto == isLivePhoto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return id.hashCode ^
|
return name.hashCode ^
|
||||||
name.hashCode ^
|
|
||||||
checksum.hashCode ^
|
checksum.hashCode ^
|
||||||
height.hashCode ^
|
height.hashCode ^
|
||||||
width.hashCode ^
|
width.hashCode ^
|
||||||
type.hashCode ^
|
type.hashCode ^
|
||||||
createdTime.hashCode ^
|
createdTime.hashCode ^
|
||||||
modifiedTime.hashCode ^
|
modifiedTime.hashCode ^
|
||||||
duration.hashCode ^
|
duration.hashCode;
|
||||||
isLivePhoto.hashCode;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class LocalAsset extends Asset {
|
class LocalAsset extends Asset {
|
||||||
@ -7,7 +7,6 @@ class LocalAsset extends Asset {
|
|||||||
|
|
||||||
const LocalAsset({
|
const LocalAsset({
|
||||||
required this.localId,
|
required this.localId,
|
||||||
required super.id,
|
|
||||||
required super.name,
|
required super.name,
|
||||||
required super.checksum,
|
required super.checksum,
|
||||||
required super.height,
|
required super.height,
|
||||||
@ -16,23 +15,20 @@ class LocalAsset extends Asset {
|
|||||||
required super.createdTime,
|
required super.createdTime,
|
||||||
required super.modifiedTime,
|
required super.modifiedTime,
|
||||||
required super.duration,
|
required super.duration,
|
||||||
required super.isLivePhoto,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => """
|
String toString() => """
|
||||||
{
|
{
|
||||||
"id": $id,
|
|
||||||
"localId": "$localId",
|
"localId": "$localId",
|
||||||
"name": "$name",
|
"name": "$name",
|
||||||
"checksum": "$checksum",
|
"checksum": "$checksum",
|
||||||
"height": $height,
|
"height": ${height ?? "-"},
|
||||||
"width": $width,
|
"width": ${width ?? "-"},
|
||||||
"type": "$type",
|
"type": "$type",
|
||||||
"createdTime": "$createdTime",
|
"createdTime": "$createdTime",
|
||||||
"modifiedTime": "$modifiedTime",
|
"modifiedTime": "$modifiedTime",
|
||||||
"duration": "$duration",
|
"duration": "$duration",
|
||||||
"isLivePhoto": "$isLivePhoto",
|
|
||||||
}""";
|
}""";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -47,7 +43,6 @@ class LocalAsset extends Asset {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
LocalAsset copyWith({
|
LocalAsset copyWith({
|
||||||
int? id,
|
|
||||||
String? localId,
|
String? localId,
|
||||||
String? name,
|
String? name,
|
||||||
String? checksum,
|
String? checksum,
|
||||||
@ -57,10 +52,8 @@ class LocalAsset extends Asset {
|
|||||||
DateTime? createdTime,
|
DateTime? createdTime,
|
||||||
DateTime? modifiedTime,
|
DateTime? modifiedTime,
|
||||||
int? duration,
|
int? duration,
|
||||||
bool? isLivePhoto,
|
|
||||||
}) {
|
}) {
|
||||||
return LocalAsset(
|
return LocalAsset(
|
||||||
id: id ?? this.id,
|
|
||||||
localId: localId ?? this.localId,
|
localId: localId ?? this.localId,
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
checksum: checksum ?? this.checksum,
|
checksum: checksum ?? this.checksum,
|
||||||
@ -70,7 +63,6 @@ class LocalAsset extends Asset {
|
|||||||
createdTime: createdTime ?? this.createdTime,
|
createdTime: createdTime ?? this.createdTime,
|
||||||
modifiedTime: modifiedTime ?? this.modifiedTime,
|
modifiedTime: modifiedTime ?? this.modifiedTime,
|
||||||
duration: duration ?? this.duration,
|
duration: duration ?? this.duration,
|
||||||
isLivePhoto: isLivePhoto ?? this.isLivePhoto,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,13 +1,15 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
|
||||||
|
import 'package:immich_mobile/utils/extensions/string.extension.dart';
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class RemoteAsset extends Asset {
|
class RemoteAsset extends Asset {
|
||||||
final String remoteId;
|
final String remoteId;
|
||||||
|
final String? livePhotoVideoId;
|
||||||
|
|
||||||
const RemoteAsset({
|
const RemoteAsset({
|
||||||
required this.remoteId,
|
required this.remoteId,
|
||||||
required super.id,
|
|
||||||
required super.name,
|
required super.name,
|
||||||
required super.checksum,
|
required super.checksum,
|
||||||
required super.height,
|
required super.height,
|
||||||
@ -16,23 +18,22 @@ class RemoteAsset extends Asset {
|
|||||||
required super.createdTime,
|
required super.createdTime,
|
||||||
required super.modifiedTime,
|
required super.modifiedTime,
|
||||||
required super.duration,
|
required super.duration,
|
||||||
required super.isLivePhoto,
|
this.livePhotoVideoId,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => """
|
String toString() => """
|
||||||
{
|
{
|
||||||
"id": $id,
|
|
||||||
"remoteId": "$remoteId",
|
"remoteId": "$remoteId",
|
||||||
"name": "$name",
|
"name": "$name",
|
||||||
"checksum": "$checksum",
|
"checksum": "$checksum",
|
||||||
"height": $height,
|
"height": ${height ?? "-"},
|
||||||
"width": $width,
|
"width": ${width ?? "-"},
|
||||||
"type": "$type",
|
"type": "$type",
|
||||||
"createdTime": "$createdTime",
|
"createdTime": "$createdTime",
|
||||||
"modifiedTime": "$modifiedTime",
|
"modifiedTime": "$modifiedTime",
|
||||||
"duration": "$duration",
|
"duration": "$duration",
|
||||||
"isLivePhoto": "$isLivePhoto",
|
"livePhotoVideoId": "${livePhotoVideoId ?? "-"}",
|
||||||
}""";
|
}""";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -47,7 +48,6 @@ class RemoteAsset extends Asset {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
RemoteAsset copyWith({
|
RemoteAsset copyWith({
|
||||||
int? id,
|
|
||||||
String? remoteId,
|
String? remoteId,
|
||||||
String? name,
|
String? name,
|
||||||
String? checksum,
|
String? checksum,
|
||||||
@ -57,10 +57,9 @@ class RemoteAsset extends Asset {
|
|||||||
DateTime? createdTime,
|
DateTime? createdTime,
|
||||||
DateTime? modifiedTime,
|
DateTime? modifiedTime,
|
||||||
int? duration,
|
int? duration,
|
||||||
bool? isLivePhoto,
|
String? livePhotoVideoId,
|
||||||
}) {
|
}) {
|
||||||
return RemoteAsset(
|
return RemoteAsset(
|
||||||
id: id ?? this.id,
|
|
||||||
remoteId: remoteId ?? this.remoteId,
|
remoteId: remoteId ?? this.remoteId,
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
checksum: checksum ?? this.checksum,
|
checksum: checksum ?? this.checksum,
|
||||||
@ -70,7 +69,27 @@ class RemoteAsset extends Asset {
|
|||||||
createdTime: createdTime ?? this.createdTime,
|
createdTime: createdTime ?? this.createdTime,
|
||||||
modifiedTime: modifiedTime ?? this.modifiedTime,
|
modifiedTime: modifiedTime ?? this.modifiedTime,
|
||||||
duration: duration ?? this.duration,
|
duration: duration ?? this.duration,
|
||||||
isLivePhoto: isLivePhoto ?? this.isLivePhoto,
|
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory RemoteAsset.fromDto(AssetResponseDto dto) => RemoteAsset(
|
||||||
|
remoteId: dto.id,
|
||||||
|
createdTime: dto.fileCreatedAt,
|
||||||
|
duration: dto.duration.tryParseInt() ?? 0,
|
||||||
|
height: dto.exifInfo?.exifImageHeight?.toInt(),
|
||||||
|
width: dto.exifInfo?.exifImageWidth?.toInt(),
|
||||||
|
checksum: dto.checksum,
|
||||||
|
name: dto.originalFileName,
|
||||||
|
livePhotoVideoId: dto.livePhotoVideoId,
|
||||||
|
modifiedTime: dto.fileModifiedAt,
|
||||||
|
type: _toAssetType(dto.type),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AssetType _toAssetType(AssetTypeEnum type) => switch (type) {
|
||||||
|
AssetTypeEnum.AUDIO => AssetType.audio,
|
||||||
|
AssetTypeEnum.IMAGE => AssetType.image,
|
||||||
|
AssetTypeEnum.VIDEO => AssetType.video,
|
||||||
|
_ => AssetType.other,
|
||||||
|
};
|
@ -1,9 +1,7 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:drift/native.dart';
|
|
||||||
// ignore: depend_on_referenced_packages
|
// ignore: depend_on_referenced_packages
|
||||||
import 'package:drift_dev/api/migrations.dart';
|
import 'package:drift_dev/api/migrations.dart';
|
||||||
|
import 'package:drift_flutter/drift_flutter.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:immich_mobile/domain/entities/album.entity.dart';
|
import 'package:immich_mobile/domain/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/domain/entities/local_asset.entity.dart';
|
import 'package:immich_mobile/domain/entities/local_asset.entity.dart';
|
||||||
@ -11,54 +9,17 @@ import 'package:immich_mobile/domain/entities/log.entity.dart';
|
|||||||
import 'package:immich_mobile/domain/entities/remote_asset.entity.dart';
|
import 'package:immich_mobile/domain/entities/remote_asset.entity.dart';
|
||||||
import 'package:immich_mobile/domain/entities/store.entity.dart';
|
import 'package:immich_mobile/domain/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/domain/entities/user.entity.dart';
|
import 'package:immich_mobile/domain/entities/user.entity.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/database.interface.dart';
|
|
||||||
import 'package:path/path.dart' as p;
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:sqlite3/sqlite3.dart';
|
|
||||||
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
|
|
||||||
|
|
||||||
import 'database.repository.drift.dart';
|
import 'database.repository.drift.dart';
|
||||||
|
|
||||||
@DriftDatabase(tables: [Logs, Store, LocalAlbum, LocalAsset, User, RemoteAsset])
|
@DriftDatabase(tables: [Logs, Store, LocalAlbum, LocalAsset, User, RemoteAsset])
|
||||||
class DriftDatabaseRepository extends $DriftDatabaseRepository
|
class DriftDatabaseRepository extends $DriftDatabaseRepository {
|
||||||
implements IDatabaseRepository<GeneratedDatabase> {
|
DriftDatabaseRepository([QueryExecutor? executor])
|
||||||
DriftDatabaseRepository() : super(_openConnection());
|
: super(executor ?? driftDatabase(name: 'db'));
|
||||||
|
|
||||||
static LazyDatabase _openConnection() {
|
|
||||||
return LazyDatabase(() async {
|
|
||||||
final dbFolder = await getApplicationDocumentsDirectory();
|
|
||||||
final file = File(p.join(dbFolder.path, 'db.sqlite'));
|
|
||||||
|
|
||||||
// Work around limitations on old Android versions
|
|
||||||
// https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3_flutter_libs#problems-on-android-6
|
|
||||||
if (Platform.isAndroid) {
|
|
||||||
await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sqlite3 pick a more suitable location for temporary files - the
|
|
||||||
// one from the system may be inaccessible due to sandboxing.
|
|
||||||
// https://github.com/simolus3/moor/issues/876#issuecomment-710013503
|
|
||||||
final cachebase = (await getTemporaryDirectory()).path;
|
|
||||||
// We can't access /tmp on Android, which sqlite3 would try by default.
|
|
||||||
// Explicitly tell it about the correct temporary directory.
|
|
||||||
sqlite3.tempDirectory = cachebase;
|
|
||||||
|
|
||||||
return NativeDatabase.createInBackground(file);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
GeneratedDatabase init() => this;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 1;
|
int get schemaVersion => 1;
|
||||||
|
|
||||||
@override
|
|
||||||
// ignore: no-empty-block
|
|
||||||
void migrateDB() {
|
|
||||||
// Migrations are handled automatically using the migrator field
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration => MigrationStrategy(
|
MigrationStrategy get migration => MigrationStrategy(
|
||||||
onCreate: (m) => m.createAll(),
|
onCreate: (m) => m.createAll(),
|
||||||
|
@ -13,8 +13,8 @@ class LogDriftRepository implements ILogRepository {
|
|||||||
const LogDriftRepository(this.db);
|
const LogDriftRepository(this.db);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<LogMessage>> fetchLogs() async {
|
Future<List<LogMessage>> fetchAll() async {
|
||||||
return await db.managers.logs.map((l) => l.toModel()).get();
|
return await db.managers.logs.map(_toModel).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -82,15 +82,13 @@ class LogDriftRepository implements ILogRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension _LogToLogMessage on Log {
|
LogMessage _toModel(Log log) {
|
||||||
LogMessage toModel() {
|
return LogMessage(
|
||||||
return LogMessage(
|
content: log.content,
|
||||||
content: content,
|
createdAt: log.createdAt,
|
||||||
createdAt: createdAt,
|
level: log.level,
|
||||||
level: level,
|
error: log.error,
|
||||||
error: error,
|
logger: log.logger,
|
||||||
logger: logger,
|
stack: log.stack,
|
||||||
stack: stack,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:immich_mobile/domain/entities/remote_asset.entity.drift.dart';
|
||||||
|
import 'package:immich_mobile/domain/interfaces/remote_asset.interface.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/remote_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/repositories/database.repository.dart';
|
||||||
|
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
|
||||||
|
|
||||||
|
class RemoteAssetDriftRepository
|
||||||
|
with LogContext
|
||||||
|
implements IRemoteAssetRepository {
|
||||||
|
final DriftDatabaseRepository _db;
|
||||||
|
|
||||||
|
const RemoteAssetDriftRepository(this._db);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> addAll(Iterable<RemoteAsset> assets) async {
|
||||||
|
try {
|
||||||
|
await _db.batch((batch) => batch.insertAllOnConflictUpdate(
|
||||||
|
_db.remoteAsset,
|
||||||
|
assets.map(_toEntity),
|
||||||
|
));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e, s) {
|
||||||
|
log.severe("Cannot insert remote assets into table", e, s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteAssetCompanion _toEntity(RemoteAsset asset) {
|
||||||
|
return RemoteAssetCompanion.insert(
|
||||||
|
name: asset.name,
|
||||||
|
checksum: asset.checksum,
|
||||||
|
height: Value(asset.height),
|
||||||
|
width: Value(asset.width),
|
||||||
|
type: asset.type,
|
||||||
|
createdTime: asset.createdTime,
|
||||||
|
remoteId: asset.remoteId,
|
||||||
|
duration: Value(asset.duration),
|
||||||
|
modifiedTime: Value(asset.modifiedTime),
|
||||||
|
livePhotoVideoId: Value(asset.livePhotoVideoId),
|
||||||
|
);
|
||||||
|
}
|
@ -63,7 +63,7 @@ class StoreDriftRepository with LogContext implements IStoreRepository {
|
|||||||
@override
|
@override
|
||||||
FutureOr<void> clearStore() async {
|
FutureOr<void> clearStore() async {
|
||||||
await db.managers.store.delete();
|
await db.managers.store.delete();
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<T?> _getValueFromStoreData<T, U>(
|
FutureOr<T?> _getValueFromStoreData<T, U>(
|
||||||
|
@ -13,15 +13,15 @@ class UserDriftRepository with LogContext implements IUserRepository {
|
|||||||
const UserDriftRepository(this.db);
|
const UserDriftRepository(this.db);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<User?> getUser(String userId) async {
|
FutureOr<User?> fetch(String userId) async {
|
||||||
return await db.managers.user
|
return await db.managers.user
|
||||||
.filter((f) => f.id.equals(userId))
|
.filter((f) => f.id.equals(userId))
|
||||||
.map((u) => u.toModel())
|
.map(_toModel)
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> insertUser(User user) async {
|
FutureOr<bool> add(User user) async {
|
||||||
try {
|
try {
|
||||||
await db.into(db.user).insertOnConflictUpdate(
|
await db.into(db.user).insertOnConflictUpdate(
|
||||||
UserCompanion.insert(
|
UserCompanion.insert(
|
||||||
@ -46,20 +46,18 @@ class UserDriftRepository with LogContext implements IUserRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension _UserDataToUser on UserData {
|
User _toModel(UserData user) {
|
||||||
User toModel() {
|
return User(
|
||||||
return User(
|
id: user.id,
|
||||||
id: id,
|
email: user.email,
|
||||||
email: email,
|
avatarColor: user.avatarColor,
|
||||||
avatarColor: avatarColor,
|
inTimeline: user.inTimeline,
|
||||||
inTimeline: inTimeline,
|
isAdmin: user.isAdmin,
|
||||||
isAdmin: isAdmin,
|
memoryEnabled: user.memoryEnabled,
|
||||||
memoryEnabled: memoryEnabled,
|
name: user.name,
|
||||||
name: name,
|
profileImagePath: user.profileImagePath,
|
||||||
profileImagePath: profileImagePath,
|
quotaSizeInBytes: user.quotaSizeInBytes,
|
||||||
quotaSizeInBytes: quotaSizeInBytes,
|
quotaUsageInBytes: user.quotaUsageInBytes,
|
||||||
quotaUsageInBytes: quotaUsageInBytes,
|
updatedAt: user.updatedAt,
|
||||||
updatedAt: updatedAt,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
import 'package:immich_mobile/domain/models/server-info/server_config.model.dart';
|
import 'package:immich_mobile/domain/models/server-info/server_config.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/server-info/server_features.model.dart';
|
import 'package:immich_mobile/domain/models/server-info/server_features.model.dart';
|
||||||
import 'package:immich_mobile/utils/immich_api_client.dart';
|
|
||||||
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
|
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
class ServerInfoService with LogContext {
|
class ServerInfoService with LogContext {
|
||||||
final ImmichApiClient _api;
|
final ServerApi _serverInfo;
|
||||||
|
|
||||||
ServerApi get _serverInfo => _api.getServerApi();
|
const ServerInfoService(this._serverInfo);
|
||||||
|
|
||||||
ServerInfoService(this._api);
|
|
||||||
|
|
||||||
Future<ServerFeatures?> getServerFeatures() async {
|
Future<ServerFeatures?> getServerFeatures() async {
|
||||||
try {
|
try {
|
||||||
|
71
mobile-v2/lib/domain/services/sync.service.dart
Normal file
71
mobile-v2/lib/domain/services/sync.service.dart
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import 'package:drift/isolate.dart';
|
||||||
|
import 'package:immich_mobile/domain/interfaces/remote_asset.interface.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/remote_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/repositories/database.repository.dart';
|
||||||
|
import 'package:immich_mobile/service_locator.dart';
|
||||||
|
import 'package:immich_mobile/utils/constants/globals.dart';
|
||||||
|
import 'package:immich_mobile/utils/immich_api_client.dart';
|
||||||
|
import 'package:immich_mobile/utils/log_manager.dart';
|
||||||
|
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
|
class SyncService with LogContext {
|
||||||
|
final ImmichApiClient _appClient;
|
||||||
|
final DriftDatabaseRepository _db;
|
||||||
|
|
||||||
|
SyncService(this._appClient, this._db);
|
||||||
|
|
||||||
|
Future<bool> doFullSyncForUserDrift(
|
||||||
|
User user, {
|
||||||
|
DateTime? updatedUtil,
|
||||||
|
int? limit,
|
||||||
|
}) async {
|
||||||
|
final clientData = _appClient.clientData;
|
||||||
|
try {
|
||||||
|
await _db.computeWithDatabase(
|
||||||
|
connect: (connection) => DriftDatabaseRepository(connection),
|
||||||
|
computation: (database) async {
|
||||||
|
ServiceLocator.configureServicesForIsolate(database: database);
|
||||||
|
LogManager.I.init();
|
||||||
|
final logger = Logger("SyncService <Isolate>");
|
||||||
|
final syncClient =
|
||||||
|
ImmichApiClient.clientData(clientData).getSyncApi();
|
||||||
|
|
||||||
|
final chunkSize = limit ?? kFullSyncChunkSize;
|
||||||
|
final updatedTill = updatedUtil ?? DateTime.now().toUtc();
|
||||||
|
updatedUtil ??= DateTime.now().toUtc();
|
||||||
|
String? lastAssetId;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
logger.info(
|
||||||
|
"Requesting more chunks from lastId - ${lastAssetId ?? "<initial_fetch>"}",
|
||||||
|
);
|
||||||
|
|
||||||
|
final assets = await syncClient.getFullSyncForUser(AssetFullSyncDto(
|
||||||
|
limit: chunkSize,
|
||||||
|
updatedUntil: updatedTill,
|
||||||
|
lastId: lastAssetId,
|
||||||
|
userId: user.id,
|
||||||
|
));
|
||||||
|
if (assets == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await di<IRemoteAssetRepository>()
|
||||||
|
.addAll(assets.map(RemoteAsset.fromDto));
|
||||||
|
|
||||||
|
lastAssetId = assets.lastOrNull?.id;
|
||||||
|
if (assets.length != chunkSize) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (e, s) {
|
||||||
|
log.severe("Error performing full sync for user - ${user.name}", e, s);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,11 @@
|
|||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/utils/immich_api_client.dart';
|
|
||||||
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
|
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
class UserService with LogContext {
|
class UserService with LogContext {
|
||||||
final ImmichApiClient _api;
|
final UsersApi _userApi;
|
||||||
|
|
||||||
UsersApi get _userApi => _api.getUsersApi();
|
const UserService(this._userApi);
|
||||||
|
|
||||||
UserService(this._api);
|
|
||||||
|
|
||||||
Future<User?> getMyUser() async {
|
Future<User?> getMyUser() async {
|
||||||
try {
|
try {
|
||||||
@ -21,7 +18,7 @@ class UserService with LogContext {
|
|||||||
final preferencesDto = await _userApi.getMyPreferences();
|
final preferencesDto = await _userApi.getMyPreferences();
|
||||||
return User.fromAdminDto(userDto, preferencesDto);
|
return User.fromAdminDto(userDto, preferencesDto);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
log.severe("Error while fetching server features", e, s);
|
log.severe("Error while fetching my user", e, s);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ class StoreUserConverter extends IStoreConverter<User, String> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<User?> fromPrimitive(String value) async {
|
Future<User?> fromPrimitive(String value) async {
|
||||||
return await di<IUserRepository>().getUser(value);
|
return await di<IUserRepository>().fetch(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -3,6 +3,8 @@ import 'package:immich_mobile/i18n/strings.g.dart';
|
|||||||
import 'package:immich_mobile/immich_app.dart';
|
import 'package:immich_mobile/immich_app.dart';
|
||||||
import 'package:immich_mobile/service_locator.dart';
|
import 'package:immich_mobile/service_locator.dart';
|
||||||
import 'package:immich_mobile/utils/log_manager.dart';
|
import 'package:immich_mobile/utils/log_manager.dart';
|
||||||
|
// ignore: depend_on_referenced_packages
|
||||||
|
import 'package:stack_trace/stack_trace.dart' as stack_trace;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
@ -13,5 +15,11 @@ void main() {
|
|||||||
// Init localization
|
// Init localization
|
||||||
LocaleSettings.useDeviceLocale();
|
LocaleSettings.useDeviceLocale();
|
||||||
|
|
||||||
|
FlutterError.demangleStackTrace = (StackTrace stack) {
|
||||||
|
if (stack is stack_trace.Trace) return stack.vmTrace;
|
||||||
|
if (stack is stack_trace.Chain) return stack.toTrace().vmTrace;
|
||||||
|
return stack;
|
||||||
|
};
|
||||||
|
|
||||||
runApp(const ImmichApp());
|
runApp(const ImmichApp());
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/presentation/router/router.dart';
|
import 'package:immich_mobile/domain/services/sync.service.dart';
|
||||||
|
import 'package:immich_mobile/presentation/modules/common/states/current_user.state.dart';
|
||||||
|
import 'package:immich_mobile/service_locator.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class HomePage extends StatelessWidget {
|
class HomePage extends StatelessWidget {
|
||||||
@ -10,8 +12,9 @@ class HomePage extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Center(
|
return Center(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () => context.router.navigate(const SettingsRoute()),
|
onPressed: () => di<SyncService>()
|
||||||
child: const Text('Settings'),
|
.doFullSyncForUserDrift(di<CurrentUserCubit>().state),
|
||||||
|
child: const Text('Sync'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
|||||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/login.service.dart';
|
import 'package:immich_mobile/domain/services/login.service.dart';
|
||||||
|
import 'package:immich_mobile/domain/services/sync.service.dart';
|
||||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||||
import 'package:immich_mobile/i18n/strings.g.dart';
|
import 'package:immich_mobile/i18n/strings.g.dart';
|
||||||
import 'package:immich_mobile/presentation/modules/common/states/server_info/server_feature_config.state.dart';
|
import 'package:immich_mobile/presentation/modules/common/states/server_info/server_feature_config.state.dart';
|
||||||
@ -65,6 +66,7 @@ class LoginPageCubit extends Cubit<LoginPageState> with LogContext {
|
|||||||
|
|
||||||
di<IStoreRepository>().set(StoreKey.serverEndpoint, url);
|
di<IStoreRepository>().set(StoreKey.serverEndpoint, url);
|
||||||
ServiceLocator.registerPostValidationServices(url);
|
ServiceLocator.registerPostValidationServices(url);
|
||||||
|
ServiceLocator.registerPostGlobalStates();
|
||||||
|
|
||||||
// Fetch server features
|
// Fetch server features
|
||||||
await di<ServerFeatureConfigCubit>().getFeatures();
|
await di<ServerFeatureConfigCubit>().getFeatures();
|
||||||
@ -132,7 +134,9 @@ class LoginPageCubit extends Cubit<LoginPageState> with LogContext {
|
|||||||
|
|
||||||
// Register user
|
// Register user
|
||||||
ServiceLocator.registerCurrentUser(user);
|
ServiceLocator.registerCurrentUser(user);
|
||||||
await di<IUserRepository>().insertUser(user);
|
await di<IUserRepository>().add(user);
|
||||||
|
// Sync assets in background
|
||||||
|
unawaited(di<SyncService>().doFullSyncForUserDrift(user));
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isValidationInProgress: false,
|
isValidationInProgress: false,
|
||||||
|
@ -23,7 +23,7 @@ class TabControllerPage extends StatelessWidget {
|
|||||||
// Pop-back to photos tab or if already in photos tab, close the app
|
// Pop-back to photos tab or if already in photos tab, close the app
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: tabsRouter.activeIndex == 0,
|
canPop: tabsRouter.activeIndex == 0,
|
||||||
onPopInvoked: (didPop) =>
|
onPopInvokedWithResult: (didPop, _) =>
|
||||||
!didPop ? tabsRouter.setActiveIndex(0) : null,
|
!didPop ? tabsRouter.setActiveIndex(0) : null,
|
||||||
child: _TabControllerAdaptiveScaffold(
|
child: _TabControllerAdaptiveScaffold(
|
||||||
body: (ctxx) => child,
|
body: (ctxx) => child,
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/log.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/log.interface.dart';
|
||||||
|
import 'package:immich_mobile/domain/interfaces/remote_asset.interface.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/domain/repositories/database.repository.dart';
|
import 'package:immich_mobile/domain/repositories/database.repository.dart';
|
||||||
import 'package:immich_mobile/domain/repositories/log.repository.dart';
|
import 'package:immich_mobile/domain/repositories/log.repository.dart';
|
||||||
|
import 'package:immich_mobile/domain/repositories/remote_asset.repository.dart';
|
||||||
import 'package:immich_mobile/domain/repositories/store.repository.dart';
|
import 'package:immich_mobile/domain/repositories/store.repository.dart';
|
||||||
import 'package:immich_mobile/domain/repositories/user.repository.dart';
|
import 'package:immich_mobile/domain/repositories/user.repository.dart';
|
||||||
import 'package:immich_mobile/domain/services/app_setting.service.dart';
|
import 'package:immich_mobile/domain/services/app_setting.service.dart';
|
||||||
import 'package:immich_mobile/domain/services/login.service.dart';
|
import 'package:immich_mobile/domain/services/login.service.dart';
|
||||||
import 'package:immich_mobile/domain/services/server_info.service.dart';
|
import 'package:immich_mobile/domain/services/server_info.service.dart';
|
||||||
|
import 'package:immich_mobile/domain/services/sync.service.dart';
|
||||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||||
import 'package:immich_mobile/presentation/modules/common/states/current_user.state.dart';
|
import 'package:immich_mobile/presentation/modules/common/states/current_user.state.dart';
|
||||||
import 'package:immich_mobile/presentation/modules/common/states/server_info/server_feature_config.state.dart';
|
import 'package:immich_mobile/presentation/modules/common/states/server_info/server_feature_config.state.dart';
|
||||||
@ -23,41 +26,49 @@ class ServiceLocator {
|
|||||||
const ServiceLocator._internal();
|
const ServiceLocator._internal();
|
||||||
|
|
||||||
static void configureServices() {
|
static void configureServices() {
|
||||||
// Register DB
|
|
||||||
di.registerSingleton<DriftDatabaseRepository>(DriftDatabaseRepository());
|
di.registerSingleton<DriftDatabaseRepository>(DriftDatabaseRepository());
|
||||||
_registerPreValidationServices();
|
_registerRepositories();
|
||||||
|
_registerPreGlobalStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _registerPreValidationServices() {
|
static void configureServicesForIsolate({
|
||||||
// ====== DOMAIN
|
required DriftDatabaseRepository database,
|
||||||
|
}) {
|
||||||
|
di.registerSingleton<DriftDatabaseRepository>(database);
|
||||||
|
_registerRepositories();
|
||||||
|
}
|
||||||
|
|
||||||
// Init store
|
static void _registerRepositories() {
|
||||||
|
/// Repositories
|
||||||
di.registerFactory<IStoreRepository>(() => StoreDriftRepository(di()));
|
di.registerFactory<IStoreRepository>(() => StoreDriftRepository(di()));
|
||||||
// Logs
|
|
||||||
di.registerFactory<ILogRepository>(() => LogDriftRepository(di()));
|
di.registerFactory<ILogRepository>(() => LogDriftRepository(di()));
|
||||||
// App Settings
|
|
||||||
di.registerFactory<AppSettingService>(() => AppSettingService(di()));
|
di.registerFactory<AppSettingService>(() => AppSettingService(di()));
|
||||||
// User Repo
|
|
||||||
di.registerFactory<IUserRepository>(() => UserDriftRepository(di()));
|
di.registerFactory<IUserRepository>(() => UserDriftRepository(di()));
|
||||||
// Login Service
|
di.registerFactory<IRemoteAssetRepository>(
|
||||||
|
() => RemoteAssetDriftRepository(di()),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Services
|
||||||
di.registerFactory<LoginService>(() => const LoginService());
|
di.registerFactory<LoginService>(() => const LoginService());
|
||||||
|
}
|
||||||
|
|
||||||
// ====== PRESENTATION
|
static void _registerPreGlobalStates() {
|
||||||
|
|
||||||
// App router
|
|
||||||
di.registerSingleton<AppRouter>(AppRouter());
|
di.registerSingleton<AppRouter>(AppRouter());
|
||||||
// Global states
|
|
||||||
di.registerLazySingleton<AppThemeCubit>(() => AppThemeCubit(di()));
|
di.registerLazySingleton<AppThemeCubit>(() => AppThemeCubit(di()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void registerPostValidationServices(String endpoint) {
|
static void registerPostValidationServices(String endpoint) {
|
||||||
di.registerSingleton<ImmichApiClient>(ImmichApiClient(endpoint: endpoint));
|
di.registerSingleton<ImmichApiClient>(ImmichApiClient(endpoint: endpoint));
|
||||||
|
di.registerFactory<UserService>(() => UserService(
|
||||||
|
di<ImmichApiClient>().getUsersApi(),
|
||||||
|
));
|
||||||
|
di.registerFactory<ServerInfoService>(() => ServerInfoService(
|
||||||
|
di<ImmichApiClient>().getServerApi(),
|
||||||
|
));
|
||||||
|
di.registerFactory<SyncService>(() => SyncService(di(), di()));
|
||||||
|
}
|
||||||
|
|
||||||
// ====== DOMAIN
|
static void registerPostGlobalStates() {
|
||||||
di.registerFactory<UserService>(() => UserService(di()));
|
|
||||||
di.registerFactory<ServerInfoService>(() => ServerInfoService(di()));
|
|
||||||
|
|
||||||
// ====== PRESENTATION
|
|
||||||
di.registerLazySingleton<ServerFeatureConfigCubit>(
|
di.registerLazySingleton<ServerFeatureConfigCubit>(
|
||||||
() => ServerFeatureConfigCubit(di()),
|
() => ServerFeatureConfigCubit(di()),
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,9 @@ import 'package:flutter/material.dart';
|
|||||||
/// Log messages stored in the DB
|
/// Log messages stored in the DB
|
||||||
const int kLogMessageLimit = 500;
|
const int kLogMessageLimit = 500;
|
||||||
|
|
||||||
|
/// Chunked asset sync size
|
||||||
|
const int kFullSyncChunkSize = 10000;
|
||||||
|
|
||||||
/// Headers
|
/// Headers
|
||||||
// Auth header
|
// Auth header
|
||||||
const String kImmichHeaderAuthKey = "x-immich-user-token";
|
const String kImmichHeaderAuthKey = "x-immich-user-token";
|
||||||
|
3
mobile-v2/lib/utils/extensions/string.extension.dart
Normal file
3
mobile-v2/lib/utils/extensions/string.extension.dart
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
extension StringNumberUtils on String {
|
||||||
|
int? tryParseInt() => int.tryParse(this);
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
@ -10,9 +12,21 @@ import 'package:immich_mobile/utils/constants/globals.dart';
|
|||||||
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
|
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class ImmichApiClientData {
|
||||||
|
final String endpoint;
|
||||||
|
final Map<String, String> headersMap;
|
||||||
|
|
||||||
|
const ImmichApiClientData({required this.endpoint, required this.headersMap});
|
||||||
|
}
|
||||||
|
|
||||||
class ImmichApiClient extends ApiClient with LogContext {
|
class ImmichApiClient extends ApiClient with LogContext {
|
||||||
ImmichApiClient({required String endpoint}) : super(basePath: endpoint);
|
ImmichApiClient({required String endpoint}) : super(basePath: endpoint);
|
||||||
|
|
||||||
|
/// Used to recreate the client in Isolates
|
||||||
|
ImmichApiClientData get clientData =>
|
||||||
|
ImmichApiClientData(endpoint: basePath, headersMap: defaultHeaderMap);
|
||||||
|
|
||||||
Future<void> init({String? accessToken}) async {
|
Future<void> init({String? accessToken}) async {
|
||||||
final token =
|
final token =
|
||||||
accessToken ?? (await di<IStoreRepository>().get(StoreKey.accessToken));
|
accessToken ?? (await di<IStoreRepository>().get(StoreKey.accessToken));
|
||||||
@ -33,6 +47,15 @@ class ImmichApiClient extends ApiClient with LogContext {
|
|||||||
addDefaultHeader(kImmichHeaderDeviceType, Platform.operatingSystem);
|
addDefaultHeader(kImmichHeaderDeviceType, Platform.operatingSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory ImmichApiClient.clientData(ImmichApiClientData data) {
|
||||||
|
final client = ImmichApiClient(endpoint: data.endpoint);
|
||||||
|
|
||||||
|
for (final entry in data.headersMap.entries) {
|
||||||
|
client.addDefaultHeader(entry.key, entry.value);
|
||||||
|
}
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response> invokeAPI(
|
Future<Response> invokeAPI(
|
||||||
String path,
|
String path,
|
||||||
@ -85,8 +108,35 @@ class ImmichApiClient extends ApiClient with LogContext {
|
|||||||
return ApiClient.fromJson(value, targetType, growable: growable);
|
return ApiClient.fromJson(value, targetType, growable: growable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// ignore: avoid-dynamic
|
||||||
|
Future<dynamic> deserializeAsync(
|
||||||
|
String value,
|
||||||
|
String targetType, {
|
||||||
|
bool growable = false,
|
||||||
|
}) =>
|
||||||
|
deserialize(value, targetType, growable: growable);
|
||||||
|
|
||||||
|
@override
|
||||||
|
// ignore: avoid-dynamic
|
||||||
|
Future<dynamic> deserialize(
|
||||||
|
String value,
|
||||||
|
String targetType, {
|
||||||
|
bool growable = false,
|
||||||
|
}) async {
|
||||||
|
targetType = targetType.replaceAll(' ', '');
|
||||||
|
return targetType == 'String'
|
||||||
|
? value
|
||||||
|
: fromJson(
|
||||||
|
await compute((String j) => json.decode(j), value),
|
||||||
|
targetType,
|
||||||
|
growable: growable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
UsersApi getUsersApi() => UsersApi(this);
|
UsersApi getUsersApi() => UsersApi(this);
|
||||||
ServerApi getServerApi() => ServerApi(this);
|
ServerApi getServerApi() => ServerApi(this);
|
||||||
AuthenticationApi getAuthenticationApi() => AuthenticationApi(this);
|
AuthenticationApi getAuthenticationApi() => AuthenticationApi(this);
|
||||||
OAuthApi getOAuthApi() => OAuthApi(this);
|
OAuthApi getOAuthApi() => OAuthApi(this);
|
||||||
|
SyncApi getSyncApi() => SyncApi(this);
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ class LogManager {
|
|||||||
debugPrint('[${record.level.name}] [${record.time}] ${record.message}');
|
debugPrint('[${record.level.name}] [${record.time}] ${record.message}');
|
||||||
if (record.error != null && record.stackTrace != null) {
|
if (record.error != null && record.stackTrace != null) {
|
||||||
debugPrint('${record.error}');
|
debugPrint('${record.error}');
|
||||||
|
debugPrintStack(stackTrace: record.stackTrace);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
|
@ -278,6 +278,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.20.1"
|
version: "2.20.1"
|
||||||
|
drift_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: drift_flutter
|
||||||
|
sha256: c670c947fe17ad149678a43fdbbfdb69321f0c83d315043e34e8ad2729e11f49
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
dynamic_color:
|
dynamic_color:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -20,6 +20,7 @@ dependencies:
|
|||||||
flutter_bloc: ^8.1.6
|
flutter_bloc: ^8.1.6
|
||||||
# Database
|
# Database
|
||||||
drift: ^2.20.0
|
drift: ^2.20.0
|
||||||
|
drift_flutter: ^0.2.0
|
||||||
sqlite3: ^2.4.6
|
sqlite3: ^2.4.6
|
||||||
sqlite3_flutter_libs: ^0.5.24
|
sqlite3_flutter_libs: ^0.5.24
|
||||||
# Network
|
# Network
|
||||||
|
Loading…
x
Reference in New Issue
Block a user