mirror of
https://github.com/immich-app/immich.git
synced 2025-02-04 01:09:14 -05:00
preliminary auto-login
This commit is contained in:
parent
e9b023a09e
commit
1786438123
13 changed files with 193 additions and 63 deletions
2
mobile-v2/.gitignore
vendored
2
mobile-v2/.gitignore
vendored
|
@ -3,6 +3,8 @@
|
|||
*.gr.dart
|
||||
*.drift.dart
|
||||
*.gen.dart
|
||||
*.g.swift
|
||||
*.g.kt
|
||||
openapi/*
|
||||
|
||||
# Miscellaneous
|
||||
|
|
|
@ -1,8 +1,23 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<!-- photo_manager -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
<uses-permission android:name="android.permission.MANAGE_MEDIA" />
|
||||
<!-- /photo_manager -->
|
||||
|
||||
<application
|
||||
android:label="immich"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:label="Immich"
|
||||
android:name=".ImmichApp"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:largeHeap="true"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
|
|
@ -1,53 +1,87 @@
|
|||
PODS:
|
||||
- device_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_web_auth_2 (3.0.0):
|
||||
- Flutter
|
||||
- package_info_plus (0.4.5):
|
||||
- Flutter
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- photo_manager (2.0.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- "sqlite3 (3.45.3+1)":
|
||||
- "sqlite3/common (= 3.45.3+1)"
|
||||
- "sqlite3/common (3.45.3+1)"
|
||||
- "sqlite3/fts5 (3.45.3+1)":
|
||||
- sqflite (0.0.3):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- "sqlite3 (3.46.1+1)":
|
||||
- "sqlite3/common (= 3.46.1+1)"
|
||||
- "sqlite3/common (3.46.1+1)"
|
||||
- "sqlite3/dbstatvtab (3.46.1+1)":
|
||||
- sqlite3/common
|
||||
- "sqlite3/perf-threadsafe (3.45.3+1)":
|
||||
- "sqlite3/fts5 (3.46.1+1)":
|
||||
- sqlite3/common
|
||||
- "sqlite3/rtree (3.45.3+1)":
|
||||
- "sqlite3/perf-threadsafe (3.46.1+1)":
|
||||
- sqlite3/common
|
||||
- "sqlite3/rtree (3.46.1+1)":
|
||||
- sqlite3/common
|
||||
- sqlite3_flutter_libs (0.0.1):
|
||||
- Flutter
|
||||
- "sqlite3 (~> 3.45.3+1)"
|
||||
- "sqlite3 (~> 3.46.0+1)"
|
||||
- sqlite3/dbstatvtab
|
||||
- sqlite3/fts5
|
||||
- sqlite3/perf-threadsafe
|
||||
- sqlite3/rtree
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_web_auth_2 (from `.symlinks/plugins/flutter_web_auth_2/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- sqlite3
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
device_info_plus:
|
||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_web_auth_2:
|
||||
:path: ".symlinks/plugins/flutter_web_auth_2/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
photo_manager:
|
||||
:path: ".symlinks/plugins/photo_manager/ios"
|
||||
sqflite:
|
||||
:path: ".symlinks/plugins/sqflite/darwin"
|
||||
sqlite3_flutter_libs:
|
||||
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_web_auth_2: 051cf9f5dc366f31b5dcc4e2952c2b954767be8a
|
||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
|
||||
sqlite3: 02d1f07eaaa01f80a1c16b4b31dfcbb3345ee01a
|
||||
sqlite3_flutter_libs: 9bfe005308998aeca155330bbc2ea6dddf834a3b
|
||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
|
||||
sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
|
||||
PODFILE CHECKSUM: 64c9b5291666c0ca3caabdfe9865c141ac40321d
|
||||
|
||||
|
|
|
@ -57,5 +57,9 @@
|
|||
<string>https</string>
|
||||
</array>
|
||||
<!-- /URL Launcher -->
|
||||
<!-- photo_manager -->
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>In order to access iOS photo library for local assets and albums</string>
|
||||
<!-- /photo_manager -->
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -359,10 +359,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_adaptive_scaffold
|
||||
sha256: "6b587d439c7da037432bbfc78d9676e1d08f2d7490f08e8d689a20f08e049802"
|
||||
sha256: "8c515a2cb8abb3a567f8e77f10b33f47bb6fcadfe31f62364e0aca36280cdf93"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.6"
|
||||
version: "0.3.1"
|
||||
flutter_bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -769,6 +769,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
pigeon:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: pigeon
|
||||
sha256: "95481446c02fa79fcf0e8014882f8a3b87fd06c257e9e1c3d4cc6d102a925ad8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "22.4.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -48,7 +48,7 @@ dependencies:
|
|||
flutter_web_auth_2: ^3.1.2
|
||||
# components
|
||||
material_symbols_icons: ^4.2785.1
|
||||
flutter_adaptive_scaffold: ^0.2.6
|
||||
flutter_adaptive_scaffold: ^0.3.1
|
||||
scrollable_positioned_list: ^0.3.8
|
||||
cached_network_image: ^3.4.1
|
||||
flutter_cache_manager: ^3.4.1
|
||||
|
@ -77,6 +77,8 @@ dev_dependencies:
|
|||
slang_build_runner: ^3.31.0
|
||||
# Assets constant generator
|
||||
flutter_gen_runner: ^5.7.0
|
||||
# Type safe platform channels
|
||||
pigeon: ^22.4.0
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
|
Loading…
Add table
Reference in a new issue