preliminary auto-login
This commit is contained in:
@@ -14,7 +14,11 @@ import 'database.repository.drift.dart';
|
||||
@DriftDatabase(tables: [Logs, Store, LocalAlbum, Asset, User])
|
||||
class DriftDatabaseRepository extends $DriftDatabaseRepository {
|
||||
DriftDatabaseRepository([QueryExecutor? executor])
|
||||
: super(executor ?? driftDatabase(name: 'db'));
|
||||
: super(executor ??
|
||||
driftDatabase(
|
||||
name: 'db',
|
||||
native: const DriftNativeOptions(shareAcrossIsolates: true),
|
||||
));
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
|
||||
@@ -4,6 +4,9 @@ import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_web_auth_2/flutter_web_auth_2.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/service_locator.dart';
|
||||
import 'package:immich_mobile/utils/immich_api_client.dart';
|
||||
import 'package:immich_mobile/utils/mixins/log_context.mixin.dart';
|
||||
@@ -103,4 +106,32 @@ class LoginService with LogContext {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<bool> tryLoginFromSplash() async {
|
||||
final serverEndpoint =
|
||||
await di<IStoreRepository>().tryGet(StoreKey.serverEndpoint);
|
||||
if (serverEndpoint == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ServiceLocator.registerApiClient(serverEndpoint);
|
||||
ServiceLocator.registerPostValidationServices();
|
||||
ServiceLocator.registerPostGlobalStates();
|
||||
|
||||
final accessToken =
|
||||
await di<IStoreRepository>().tryGet(StoreKey.accessToken);
|
||||
if (accessToken == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Set token to interceptor
|
||||
await di<ImmichApiClient>().init(accessToken: accessToken);
|
||||
|
||||
final user = await di<UserService>().getMyUser();
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:drift/isolate.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:immich_mobile/domain/interfaces/asset.interface.dart';
|
||||
import 'package:immich_mobile/domain/models/asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
@@ -12,56 +13,51 @@ import 'package:logging/logging.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class SyncService with LogContext {
|
||||
final DriftDatabaseRepository _db;
|
||||
|
||||
SyncService(this._db);
|
||||
SyncService();
|
||||
|
||||
Future<bool> doFullSyncForUserDrift(
|
||||
User user, {
|
||||
DateTime? updatedUtil,
|
||||
int? limit,
|
||||
}) async {
|
||||
final helper = IsolateHelper()..preIsolateHandling();
|
||||
try {
|
||||
await _db.computeWithDatabase(
|
||||
connect: (connection) => DriftDatabaseRepository(connection),
|
||||
computation: (database) async {
|
||||
helper.postIsolateHandling(database: database);
|
||||
final logger = Logger("SyncService <Isolate>");
|
||||
final syncClient = di<ImmichApiClient>().getSyncApi();
|
||||
return await IsolateHelper.run(() async {
|
||||
try {
|
||||
final logger = Logger("SyncService <Isolate>");
|
||||
final syncClient = di<ImmichApiClient>().getSyncApi();
|
||||
|
||||
final chunkSize = limit ?? kFullSyncChunkSize;
|
||||
final updatedTill = updatedUtil ?? DateTime.now().toUtc();
|
||||
updatedUtil ??= DateTime.now().toUtc();
|
||||
String? lastAssetId;
|
||||
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>"}",
|
||||
);
|
||||
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<IAssetRepository>().addAll(assets.map(Asset.remote));
|
||||
|
||||
lastAssetId = assets.lastOrNull?.id;
|
||||
if (assets.length != chunkSize) break;
|
||||
final assets = await syncClient.getFullSyncForUser(AssetFullSyncDto(
|
||||
limit: chunkSize,
|
||||
updatedUntil: updatedTill,
|
||||
lastId: lastAssetId,
|
||||
userId: user.id,
|
||||
));
|
||||
if (assets == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
);
|
||||
} catch (e, s) {
|
||||
log.severe("Error performing full sync for user - ${user.name}", e, s);
|
||||
}
|
||||
return false;
|
||||
await di<IAssetRepository>().addAll(assets.map(Asset.remote));
|
||||
|
||||
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);
|
||||
} finally {
|
||||
await di<DriftDatabaseRepository>().close();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,19 @@ class UserService with LogContext {
|
||||
|
||||
Future<User?> getMyUser() async {
|
||||
try {
|
||||
final userDto = await _userApi.getMyUser();
|
||||
final [
|
||||
userDto as UserAdminResponseDto?,
|
||||
preferencesDto as UserPreferencesResponseDto?
|
||||
] = await Future.wait([
|
||||
_userApi.getMyUser(),
|
||||
_userApi.getMyPreferences(),
|
||||
]);
|
||||
|
||||
if (userDto == null) {
|
||||
log.severe("Cannot fetch my user.");
|
||||
return null;
|
||||
}
|
||||
|
||||
final preferencesDto = await _userApi.getMyPreferences();
|
||||
return User.fromAdminDto(userDto, preferencesDto);
|
||||
} catch (e, s) {
|
||||
log.severe("Error while fetching my user", e, s);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:immich_mobile/domain/services/login.service.dart';
|
||||
import 'package:immich_mobile/presentation/components/image/immich_logo.widget.dart';
|
||||
import 'package:immich_mobile/presentation/modules/login/states/login_page.state.dart';
|
||||
import 'package:immich_mobile/presentation/router/router.dart';
|
||||
@@ -45,6 +48,14 @@ class _SplashScreenState extends State<SplashScreenPage>
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _tryLogin() async {
|
||||
if (await di<LoginService>().tryLoginFromSplash() && mounted) {
|
||||
unawaited(context.replaceRoute(const TabControllerRoute()));
|
||||
} else {
|
||||
unawaited(context.replaceRoute(const LoginRoute()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -52,7 +63,7 @@ class _SplashScreenState extends State<SplashScreenPage>
|
||||
future: di.allReady(),
|
||||
builder: (_, snap) {
|
||||
if (snap.hasData) {
|
||||
context.replaceRoute(const LoginRoute());
|
||||
_tryLogin();
|
||||
} else if (snap.hasError) {
|
||||
log.severe(
|
||||
"Error while initializing the app",
|
||||
|
||||
@@ -92,7 +92,7 @@ class ServiceLocator {
|
||||
_registerFactory<ServerInfoService>(() => ServerInfoService(
|
||||
di<ImmichApiClient>().getServerApi(),
|
||||
));
|
||||
_registerFactory<SyncService>(() => SyncService(di()));
|
||||
_registerFactory<SyncService>(() => SyncService());
|
||||
}
|
||||
|
||||
static void registerPostGlobalStates() {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:immich_mobile/domain/repositories/database.repository.dart';
|
||||
import 'package:immich_mobile/service_locator.dart';
|
||||
import 'package:immich_mobile/utils/immich_api_client.dart';
|
||||
@@ -12,10 +16,13 @@ class _ImApiClientData {
|
||||
const _ImApiClientData({required this.endpoint, required this.headersMap});
|
||||
}
|
||||
|
||||
// !! Should be used only from the root isolate
|
||||
class IsolateHelper {
|
||||
// Cache the ApiClient to reconstruct it later after inside the isolate
|
||||
late final _ImApiClientData? _clientData;
|
||||
|
||||
static RootIsolateToken get _rootToken => RootIsolateToken.instance!;
|
||||
|
||||
IsolateHelper();
|
||||
|
||||
void preIsolateHandling() {
|
||||
@@ -26,7 +33,7 @@ class IsolateHelper {
|
||||
);
|
||||
}
|
||||
|
||||
void postIsolateHandling({required DriftDatabaseRepository database}) {
|
||||
void postIsolateHandling() {
|
||||
assert(_clientData != null);
|
||||
// Reconstruct client from cached data
|
||||
final client = ImmichApiClient(endpoint: _clientData!.endpoint);
|
||||
@@ -36,11 +43,21 @@ class IsolateHelper {
|
||||
|
||||
// Register all services in the isolates memory
|
||||
ServiceLocator.configureServicesForIsolate(
|
||||
database: database,
|
||||
database: DriftDatabaseRepository(),
|
||||
apiClient: client,
|
||||
);
|
||||
|
||||
// Init log manager to continue listening to log events
|
||||
LogManager.I.init();
|
||||
}
|
||||
|
||||
static Future<T> run<T>(FutureOr<T> Function() computation) async {
|
||||
final helper = IsolateHelper()..preIsolateHandling();
|
||||
final token = _rootToken;
|
||||
return await Isolate.run(() async {
|
||||
BackgroundIsolateBinaryMessenger.ensureInitialized(token);
|
||||
helper.postIsolateHandling();
|
||||
return await computation();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user