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:
@@ -1,4 +1,5 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/enums.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
@@ -48,4 +49,27 @@ class AssetApiRepository extends ApiRepository implements IAssetApiRepository {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateVisibility(
|
||||
List<String> ids,
|
||||
AssetVisibilityEnum visibility,
|
||||
) async {
|
||||
return _api.updateAssets(
|
||||
AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility)),
|
||||
);
|
||||
}
|
||||
|
||||
_mapVisibility(AssetVisibilityEnum visibility) {
|
||||
switch (visibility) {
|
||||
case AssetVisibilityEnum.timeline:
|
||||
return AssetVisibility.timeline;
|
||||
case AssetVisibilityEnum.hidden:
|
||||
return AssetVisibility.hidden;
|
||||
case AssetVisibilityEnum.locked:
|
||||
return AssetVisibility.locked;
|
||||
case AssetVisibilityEnum.archive:
|
||||
return AssetVisibility.archive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,4 +55,26 @@ class AuthApiRepository extends ApiRepository implements IAuthApiRepository {
|
||||
userId: dto.userId,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> unlockPinCode(String pinCode) async {
|
||||
try {
|
||||
await _apiService.authenticationApi
|
||||
.unlockAuthSession(SessionUnlockDto(pinCode: pinCode));
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setupPinCode(String pinCode) {
|
||||
return _apiService.authenticationApi
|
||||
.setupPinCode(PinCodeSetupDto(pinCode: pinCode));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> lockPinCode() {
|
||||
return _apiService.authenticationApi.lockAuthSession();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
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:local_auth/local_auth.dart';
|
||||
|
||||
final biometricRepositoryProvider =
|
||||
Provider((ref) => BiometricRepository(LocalAuthentication()));
|
||||
|
||||
class BiometricRepository implements IBiometricRepository {
|
||||
final LocalAuthentication _localAuth;
|
||||
|
||||
BiometricRepository(this._localAuth);
|
||||
|
||||
@override
|
||||
Future<BiometricStatus> getStatus() async {
|
||||
final bool canAuthenticateWithBiometrics =
|
||||
await _localAuth.canCheckBiometrics;
|
||||
final bool canAuthenticate =
|
||||
canAuthenticateWithBiometrics || await _localAuth.isDeviceSupported();
|
||||
final availableBiometric = await _localAuth.getAvailableBiometrics();
|
||||
|
||||
return BiometricStatus(
|
||||
canAuthenticate: canAuthenticate,
|
||||
availableBiometrics: availableBiometric,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> authenticate(String? message) async {
|
||||
return _localAuth.authenticate(
|
||||
localizedReason: message ?? 'please_auth_to_access'.tr(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/interfaces/secure_storage.interface.dart';
|
||||
|
||||
final secureStorageRepositoryProvider =
|
||||
Provider((ref) => SecureStorageRepository(const FlutterSecureStorage()));
|
||||
|
||||
class SecureStorageRepository implements ISecureStorageRepository {
|
||||
final FlutterSecureStorage _secureStorage;
|
||||
|
||||
SecureStorageRepository(this._secureStorage);
|
||||
|
||||
@override
|
||||
Future<String?> read(String key) {
|
||||
return _secureStorage.read(key: key);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> write(String key, String value) {
|
||||
return _secureStorage.write(key: key, value: value);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String key) {
|
||||
return _secureStorage.delete(key: key);
|
||||
}
|
||||
}
|
||||
@@ -45,8 +45,8 @@ class TimelineRepository extends DatabaseRepository
|
||||
.where()
|
||||
.ownerIdEqualToAnyChecksum(fastHash(userId))
|
||||
.filter()
|
||||
.isArchivedEqualTo(true)
|
||||
.isTrashedEqualTo(false)
|
||||
.visibilityEqualTo(AssetVisibilityEnum.archive)
|
||||
.sortByFileCreatedAtDesc();
|
||||
|
||||
return _watchRenderList(query, GroupAssetsBy.none);
|
||||
@@ -59,6 +59,8 @@ class TimelineRepository extends DatabaseRepository
|
||||
.ownerIdEqualToAnyChecksum(fastHash(userId))
|
||||
.filter()
|
||||
.isFavoriteEqualTo(true)
|
||||
.not()
|
||||
.visibilityEqualTo(AssetVisibilityEnum.locked)
|
||||
.isTrashedEqualTo(false)
|
||||
.sortByFileCreatedAtDesc();
|
||||
|
||||
@@ -94,8 +96,8 @@ class TimelineRepository extends DatabaseRepository
|
||||
Stream<RenderList> watchAllVideosTimeline() {
|
||||
final query = db.assets
|
||||
.filter()
|
||||
.isArchivedEqualTo(false)
|
||||
.isTrashedEqualTo(false)
|
||||
.visibilityEqualTo(AssetVisibilityEnum.timeline)
|
||||
.typeEqualTo(AssetType.video)
|
||||
.sortByFileCreatedAtDesc();
|
||||
|
||||
@@ -111,9 +113,9 @@ class TimelineRepository extends DatabaseRepository
|
||||
.where()
|
||||
.ownerIdEqualToAnyChecksum(fastHash(userId))
|
||||
.filter()
|
||||
.isArchivedEqualTo(false)
|
||||
.isTrashedEqualTo(false)
|
||||
.stackPrimaryAssetIdIsNull()
|
||||
.visibilityEqualTo(AssetVisibilityEnum.timeline)
|
||||
.sortByFileCreatedAtDesc();
|
||||
|
||||
return _watchRenderList(query, groupAssetByOption);
|
||||
@@ -129,8 +131,8 @@ class TimelineRepository extends DatabaseRepository
|
||||
.where()
|
||||
.anyOf(isarUserIds, (qb, id) => qb.ownerIdEqualToAnyChecksum(id))
|
||||
.filter()
|
||||
.isArchivedEqualTo(false)
|
||||
.isTrashedEqualTo(false)
|
||||
.visibilityEqualTo(AssetVisibilityEnum.timeline)
|
||||
.stackPrimaryAssetIdIsNull()
|
||||
.sortByFileCreatedAtDesc();
|
||||
return _watchRenderList(query, groupAssetByOption);
|
||||
@@ -151,6 +153,7 @@ class TimelineRepository extends DatabaseRepository
|
||||
.remoteIdIsNotNull()
|
||||
.filter()
|
||||
.ownerIdEqualTo(fastHash(userId))
|
||||
.visibilityEqualTo(AssetVisibilityEnum.timeline)
|
||||
.isTrashedEqualTo(false)
|
||||
.stackPrimaryAssetIdIsNull()
|
||||
.sortByFileCreatedAtDesc();
|
||||
@@ -158,6 +161,22 @@ class TimelineRepository extends DatabaseRepository
|
||||
return _watchRenderList(query, GroupAssetsBy.none);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<RenderList> watchLockedTimeline(
|
||||
String userId,
|
||||
GroupAssetsBy getGroupByOption,
|
||||
) {
|
||||
final query = db.assets
|
||||
.where()
|
||||
.ownerIdEqualToAnyChecksum(fastHash(userId))
|
||||
.filter()
|
||||
.visibilityEqualTo(AssetVisibilityEnum.locked)
|
||||
.isTrashedEqualTo(false)
|
||||
.sortByFileCreatedAtDesc();
|
||||
|
||||
return _watchRenderList(query, getGroupByOption);
|
||||
}
|
||||
|
||||
Stream<RenderList> _watchRenderList(
|
||||
QueryBuilder<Asset, Asset, QAfterSortBy> query,
|
||||
GroupAssetsBy groupAssetsBy,
|
||||
|
||||
Reference in New Issue
Block a user