feat: locked view mobile (#18316)

* feat: locked/private view

* feat: locked/private view

* feat: mobile lock/private view

* feat: mobile lock/private view

* merge main

* pr feedback

* pr feedback

* bottom sheet sizing

* always lock when navigating away
This commit is contained in:
Alex
2025-05-20 08:35:22 -05:00
committed by GitHub
parent 397808dd1a
commit fe71894308
57 changed files with 1893 additions and 289 deletions
+23
View File
@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
import 'package:immich_mobile/domain/interfaces/user.interface.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
@@ -239,6 +240,9 @@ class AssetService {
for (var element in assets) {
element.isArchived = isArchived;
element.visibility = isArchived
? AssetVisibilityEnum.archive
: AssetVisibilityEnum.timeline;
}
await _syncService.upsertAssetsWithExif(assets);
@@ -458,6 +462,7 @@ class AssetService {
bool shouldDeletePermanently = false,
}) async {
final candidates = assets.where((a) => a.isRemote);
if (candidates.isEmpty) {
return;
}
@@ -475,6 +480,7 @@ class AssetService {
.where((asset) => asset.storage == AssetState.merged)
.map((asset) {
asset.remoteId = null;
asset.visibility = AssetVisibilityEnum.timeline;
return asset;
})
: assets.where((asset) => asset.isRemote).map((asset) {
@@ -529,4 +535,21 @@ class AssetService {
final me = _userService.getMyUser();
return _assetRepository.getMotionAssets(me.id);
}
Future<void> setVisibility(
List<Asset> assets,
AssetVisibilityEnum visibility,
) async {
await _assetApiRepository.updateVisibility(
assets.map((asset) => asset.remoteId!).toList(),
visibility,
);
final updatedAssets = assets.map((asset) {
asset.visibility = visibility;
return asset;
}).toList();
await _assetRepository.updateAll(updatedAssets);
}
}
+12
View File
@@ -201,4 +201,16 @@ class AuthService {
return null;
}
Future<bool> unlockPinCode(String pinCode) {
return _authApiRepository.unlockPinCode(pinCode);
}
Future<void> lockPinCode() {
return _authApiRepository.lockPinCode();
}
Future<void> setupPinCode(String pinCode) {
return _authApiRepository.setupPinCode(pinCode);
}
}
@@ -0,0 +1,26 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/interfaces/biometric.interface.dart';
import 'package:immich_mobile/models/auth/biometric_status.model.dart';
import 'package:immich_mobile/repositories/biometric.repository.dart';
final localAuthServiceProvider = Provider(
(ref) => LocalAuthService(
ref.watch(biometricRepositoryProvider),
),
);
class LocalAuthService {
// final _log = Logger("LocalAuthService");
final IBiometricRepository _biometricRepository;
LocalAuthService(this._biometricRepository);
Future<BiometricStatus> getStatus() {
return _biometricRepository.getStatus();
}
Future<bool> authenticate([String? message]) async {
return _biometricRepository.authenticate(message);
}
}
+2 -5
View File
@@ -1,10 +1,10 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/interfaces/asset.interface.dart';
import 'package:immich_mobile/models/memories/memory.model.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/repositories/asset.repository.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/utils/translation.dart';
import 'package:logging/logging.dart';
final memoryServiceProvider = StateProvider<MemoryService>((ref) {
@@ -40,10 +40,7 @@ class MemoryService {
.getAllByRemoteId(memory.assets.map((e) => e.id));
final yearsAgo = now.year - memory.data.year;
if (dbAssets.isNotEmpty) {
final String title = yearsAgo <= 1
? 'memories_year_ago'.tr()
: 'memories_years_ago'
.tr(namedArgs: {'years': yearsAgo.toString()});
final String title = t('years_ago', {'years': yearsAgo.toString()});
memories.add(
Memory(
title: title,
@@ -0,0 +1,29 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/interfaces/secure_storage.interface.dart';
import 'package:immich_mobile/repositories/secure_storage.repository.dart';
final secureStorageServiceProvider = Provider(
(ref) => SecureStorageService(
ref.watch(secureStorageRepositoryProvider),
),
);
class SecureStorageService {
// final _log = Logger("LocalAuthService");
final ISecureStorageRepository _secureStorageRepository;
SecureStorageService(this._secureStorageRepository);
Future<void> write(String key, String value) async {
await _secureStorageRepository.write(key, value);
}
Future<void> delete(String key) async {
await _secureStorageRepository.delete(key);
}
Future<String?> read(String key) async {
return _secureStorageRepository.read(key);
}
}
@@ -105,4 +105,13 @@ class TimelineService {
return GroupAssetsBy
.values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)];
}
Stream<RenderList> watchLockedTimelineProvider() async* {
final user = _userService.getMyUser();
yield* _timelineRepository.watchLockedTimeline(
user.id,
_getGroupByOption(),
);
}
}