mirror of
https://github.com/immich-app/immich.git
synced 2026-01-22 09:28:56 -08:00
Compare commits
4 Commits
renovate/n
...
fix/foregr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ccc05feeb | ||
|
|
7cbfc12e0d | ||
|
|
c320146538 | ||
|
|
3304c8efd8 |
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:immich_mobile/domain/utils/migrate_cloud_ids.dart' as m;
|
||||
import 'package:immich_mobile/domain/utils/sync_linked_album.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/sync.provider.dart';
|
||||
import 'package:immich_mobile/utils/isolate.dart';
|
||||
@@ -23,13 +22,8 @@ class BackgroundSyncManager {
|
||||
final SyncCallback? onHashingComplete;
|
||||
final SyncErrorCallback? onHashingError;
|
||||
|
||||
final SyncCallback? onCloudIdSyncStart;
|
||||
final SyncCallback? onCloudIdSyncComplete;
|
||||
final SyncErrorCallback? onCloudIdSyncError;
|
||||
|
||||
Cancelable<bool?>? _syncTask;
|
||||
Cancelable<void>? _syncWebsocketTask;
|
||||
Cancelable<void>? _cloudIdSyncTask;
|
||||
Cancelable<void>? _deviceAlbumSyncTask;
|
||||
Cancelable<void>? _linkedAlbumSyncTask;
|
||||
Cancelable<void>? _hashTask;
|
||||
@@ -44,9 +38,6 @@ class BackgroundSyncManager {
|
||||
this.onHashingStart,
|
||||
this.onHashingComplete,
|
||||
this.onHashingError,
|
||||
this.onCloudIdSyncStart,
|
||||
this.onCloudIdSyncComplete,
|
||||
this.onCloudIdSyncError,
|
||||
});
|
||||
|
||||
Future<void> cancel() async {
|
||||
@@ -64,12 +55,6 @@ class BackgroundSyncManager {
|
||||
_syncWebsocketTask?.cancel();
|
||||
_syncWebsocketTask = null;
|
||||
|
||||
if (_cloudIdSyncTask != null) {
|
||||
futures.add(_cloudIdSyncTask!.future);
|
||||
}
|
||||
_cloudIdSyncTask?.cancel();
|
||||
_cloudIdSyncTask = null;
|
||||
|
||||
if (_linkedAlbumSyncTask != null) {
|
||||
futures.add(_linkedAlbumSyncTask!.future);
|
||||
}
|
||||
@@ -216,25 +201,6 @@ class BackgroundSyncManager {
|
||||
_linkedAlbumSyncTask = null;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> syncCloudIds() {
|
||||
if (_cloudIdSyncTask != null) {
|
||||
return _cloudIdSyncTask!.future;
|
||||
}
|
||||
|
||||
onCloudIdSyncStart?.call();
|
||||
|
||||
_cloudIdSyncTask = runInIsolateGentle(computation: m.syncCloudIds);
|
||||
return _cloudIdSyncTask!
|
||||
.whenComplete(() {
|
||||
onCloudIdSyncComplete?.call();
|
||||
_cloudIdSyncTask = null;
|
||||
})
|
||||
.catchError((error) {
|
||||
onCloudIdSyncError?.call(error.toString());
|
||||
_cloudIdSyncTask = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Cancelable<void> _handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) => runInIsolateGentle(
|
||||
|
||||
@@ -10,14 +10,13 @@ import 'package:immich_mobile/infrastructure/repositories/local_album.repository
|
||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/sync.provider.dart';
|
||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
// ignore: import_rule_openapi
|
||||
import 'package:openapi/api.dart' hide AssetVisibility;
|
||||
|
||||
Future<void> syncCloudIds(ProviderContainer ref) async {
|
||||
Future<void> syncCloudIds(Ref ref) async {
|
||||
if (!CurrentPlatform.isIOS) {
|
||||
return;
|
||||
}
|
||||
@@ -35,14 +34,6 @@ Future<void> syncCloudIds(ProviderContainer ref) async {
|
||||
}
|
||||
final canBulkUpdateMetadata = serverInfo.serverVersion.isAtLeast(major: 2, minor: 5);
|
||||
|
||||
// Wait for remote sync to complete, so we have up-to-date asset metadata entries
|
||||
try {
|
||||
await ref.read(syncStreamServiceProvider).sync();
|
||||
} catch (e, s) {
|
||||
logger.fine('Failed to complete remote sync before cloudId migration.', e, s);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the mapping for backed up assets that have a cloud ID locally but do not have a cloud ID on the server
|
||||
final currentUser = ref.read(currentUserProvider);
|
||||
if (currentUser == null) {
|
||||
|
||||
@@ -75,7 +75,6 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
||||
_resumeBackup(backupProvider);
|
||||
}),
|
||||
_resumeBackup(backupProvider),
|
||||
backgroundManager.syncCloudIds(),
|
||||
]);
|
||||
} else {
|
||||
await backgroundManager.hashAssets();
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:async';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/log.service.dart';
|
||||
import 'package:immich_mobile/domain/utils/migrate_cloud_ids.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
||||
@@ -156,11 +157,11 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
||||
]);
|
||||
if (syncSuccess) {
|
||||
await Future.wait([
|
||||
_safeRun(syncCloudIds(_ref), "syncCloudIds"),
|
||||
_safeRun(backgroundManager.hashAssets(), "hashAssets").then((_) {
|
||||
_resumeBackup();
|
||||
}),
|
||||
_resumeBackup(),
|
||||
_safeRun(backgroundManager.syncCloudIds(), "syncCloudIds"),
|
||||
]);
|
||||
} else {
|
||||
await _safeRun(backgroundManager.hashAssets(), "hashAssets");
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_udid/flutter_udid.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/domain/utils/migrate_cloud_ids.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/models/auth/auth_state.model.dart';
|
||||
import 'package:immich_mobile/models/auth/login_response.model.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
import 'package:immich_mobile/services/api.service.dart';
|
||||
import 'package:immich_mobile/services/auth.service.dart';
|
||||
import 'package:immich_mobile/services/background_upload.service.dart';
|
||||
import 'package:immich_mobile/services/foreground_upload.service.dart';
|
||||
import 'package:immich_mobile/services/secure_storage.service.dart';
|
||||
import 'package:immich_mobile/services/background_upload.service.dart';
|
||||
import 'package:immich_mobile/services/widget.service.dart';
|
||||
import 'package:immich_mobile/utils/debug_print.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
@@ -172,6 +176,15 @@ class AuthNotifier extends StateNotifier<AuthState> {
|
||||
isAdmin: user.isAdmin,
|
||||
);
|
||||
|
||||
// TODO: Temporarily run cloud Id sync here until we have a better place to do it
|
||||
unawaited(
|
||||
_ref.read(backgroundSyncProvider).syncRemote().then((success) {
|
||||
if (success) {
|
||||
return syncCloudIds(_ref);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,6 @@ final backgroundSyncProvider = Provider<BackgroundSyncManager>((ref) {
|
||||
onHashingStart: syncStatusNotifier.startHashJob,
|
||||
onHashingComplete: syncStatusNotifier.completeHashJob,
|
||||
onHashingError: syncStatusNotifier.errorHashJob,
|
||||
onCloudIdSyncStart: syncStatusNotifier.startCloudIdSync,
|
||||
onCloudIdSyncComplete: syncStatusNotifier.completeCloudIdSync,
|
||||
onCloudIdSyncError: syncStatusNotifier.errorCloudIdSync,
|
||||
);
|
||||
ref.onDispose(manager.cancel);
|
||||
return manager;
|
||||
|
||||
@@ -21,7 +21,6 @@ class SyncStatusState {
|
||||
final SyncStatus remoteSyncStatus;
|
||||
final SyncStatus localSyncStatus;
|
||||
final SyncStatus hashJobStatus;
|
||||
final SyncStatus cloudIdSyncStatus;
|
||||
|
||||
final String? errorMessage;
|
||||
|
||||
@@ -29,7 +28,6 @@ class SyncStatusState {
|
||||
this.remoteSyncStatus = SyncStatus.idle,
|
||||
this.localSyncStatus = SyncStatus.idle,
|
||||
this.hashJobStatus = SyncStatus.idle,
|
||||
this.cloudIdSyncStatus = SyncStatus.idle,
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
@@ -37,14 +35,12 @@ class SyncStatusState {
|
||||
SyncStatus? remoteSyncStatus,
|
||||
SyncStatus? localSyncStatus,
|
||||
SyncStatus? hashJobStatus,
|
||||
SyncStatus? cloudIdSyncStatus,
|
||||
String? errorMessage,
|
||||
}) {
|
||||
return SyncStatusState(
|
||||
remoteSyncStatus: remoteSyncStatus ?? this.remoteSyncStatus,
|
||||
localSyncStatus: localSyncStatus ?? this.localSyncStatus,
|
||||
hashJobStatus: hashJobStatus ?? this.hashJobStatus,
|
||||
cloudIdSyncStatus: cloudIdSyncStatus ?? this.cloudIdSyncStatus,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
);
|
||||
}
|
||||
@@ -52,7 +48,6 @@ class SyncStatusState {
|
||||
bool get isRemoteSyncing => remoteSyncStatus == SyncStatus.syncing;
|
||||
bool get isLocalSyncing => localSyncStatus == SyncStatus.syncing;
|
||||
bool get isHashing => hashJobStatus == SyncStatus.syncing;
|
||||
bool get isCloudIdSyncing => cloudIdSyncStatus == SyncStatus.syncing;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
@@ -61,12 +56,11 @@ class SyncStatusState {
|
||||
other.remoteSyncStatus == remoteSyncStatus &&
|
||||
other.localSyncStatus == localSyncStatus &&
|
||||
other.hashJobStatus == hashJobStatus &&
|
||||
other.cloudIdSyncStatus == cloudIdSyncStatus &&
|
||||
other.errorMessage == errorMessage;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(remoteSyncStatus, localSyncStatus, hashJobStatus, cloudIdSyncStatus, errorMessage);
|
||||
int get hashCode => Object.hash(remoteSyncStatus, localSyncStatus, hashJobStatus, errorMessage);
|
||||
}
|
||||
|
||||
class SyncStatusNotifier extends Notifier<SyncStatusState> {
|
||||
@@ -77,7 +71,6 @@ class SyncStatusNotifier extends Notifier<SyncStatusState> {
|
||||
remoteSyncStatus: SyncStatus.idle,
|
||||
localSyncStatus: SyncStatus.idle,
|
||||
hashJobStatus: SyncStatus.idle,
|
||||
cloudIdSyncStatus: SyncStatus.idle,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -116,18 +109,6 @@ class SyncStatusNotifier extends Notifier<SyncStatusState> {
|
||||
void startHashJob() => setHashJobStatus(SyncStatus.syncing);
|
||||
void completeHashJob() => setHashJobStatus(SyncStatus.success);
|
||||
void errorHashJob(String error) => setHashJobStatus(SyncStatus.error, error);
|
||||
|
||||
///
|
||||
/// Cloud ID Sync Job
|
||||
///
|
||||
|
||||
void setCloudIdSyncStatus(SyncStatus status, [String? errorMessage]) {
|
||||
state = state.copyWith(cloudIdSyncStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null);
|
||||
}
|
||||
|
||||
void startCloudIdSync() => setCloudIdSyncStatus(SyncStatus.syncing);
|
||||
void completeCloudIdSync() => setCloudIdSyncStatus(SyncStatus.success);
|
||||
void errorCloudIdSync(String error) => setCloudIdSyncStatus(SyncStatus.error, error);
|
||||
}
|
||||
|
||||
final syncStatusProvider = NotifierProvider<SyncStatusNotifier, SyncStatusState>(SyncStatusNotifier.new);
|
||||
|
||||
6
mobile/openapi/lib/model/permission.dart
generated
6
mobile/openapi/lib/model/permission.dart
generated
@@ -82,6 +82,8 @@ class Permission {
|
||||
static const timelinePeriodRead = Permission._(r'timeline.read');
|
||||
static const timelinePeriodDownload = Permission._(r'timeline.download');
|
||||
static const maintenance = Permission._(r'maintenance');
|
||||
static const mapPeriodRead = Permission._(r'map.read');
|
||||
static const mapPeriodSearch = Permission._(r'map.search');
|
||||
static const memoryPeriodCreate = Permission._(r'memory.create');
|
||||
static const memoryPeriodRead = Permission._(r'memory.read');
|
||||
static const memoryPeriodUpdate = Permission._(r'memory.update');
|
||||
@@ -238,6 +240,8 @@ class Permission {
|
||||
timelinePeriodRead,
|
||||
timelinePeriodDownload,
|
||||
maintenance,
|
||||
mapPeriodRead,
|
||||
mapPeriodSearch,
|
||||
memoryPeriodCreate,
|
||||
memoryPeriodRead,
|
||||
memoryPeriodUpdate,
|
||||
@@ -429,6 +433,8 @@ class PermissionTypeTransformer {
|
||||
case r'timeline.read': return Permission.timelinePeriodRead;
|
||||
case r'timeline.download': return Permission.timelinePeriodDownload;
|
||||
case r'maintenance': return Permission.maintenance;
|
||||
case r'map.read': return Permission.mapPeriodRead;
|
||||
case r'map.search': return Permission.mapPeriodSearch;
|
||||
case r'memory.create': return Permission.memoryPeriodCreate;
|
||||
case r'memory.read': return Permission.memoryPeriodRead;
|
||||
case r'memory.update': return Permission.memoryPeriodUpdate;
|
||||
|
||||
@@ -6305,6 +6305,7 @@
|
||||
"state": "Stable"
|
||||
}
|
||||
],
|
||||
"x-immich-permission": "map.read",
|
||||
"x-immich-state": "Stable"
|
||||
}
|
||||
},
|
||||
@@ -6376,6 +6377,7 @@
|
||||
"state": "Stable"
|
||||
}
|
||||
],
|
||||
"x-immich-permission": "map.search",
|
||||
"x-immich-state": "Stable"
|
||||
}
|
||||
},
|
||||
@@ -18966,6 +18968,8 @@
|
||||
"timeline.read",
|
||||
"timeline.download",
|
||||
"maintenance",
|
||||
"map.read",
|
||||
"map.search",
|
||||
"memory.create",
|
||||
"memory.read",
|
||||
"memory.update",
|
||||
|
||||
@@ -5534,6 +5534,8 @@ export enum Permission {
|
||||
TimelineRead = "timeline.read",
|
||||
TimelineDownload = "timeline.download",
|
||||
Maintenance = "maintenance",
|
||||
MapRead = "map.read",
|
||||
MapSearch = "map.search",
|
||||
MemoryCreate = "memory.create",
|
||||
MemoryRead = "memory.read",
|
||||
MemoryUpdate = "memory.update",
|
||||
|
||||
53
pnpm-lock.yaml
generated
53
pnpm-lock.yaml
generated
@@ -36,7 +36,7 @@ importers:
|
||||
version: 1.20.1
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.23
|
||||
version: 4.17.22
|
||||
micromatch:
|
||||
specifier: ^4.0.8
|
||||
version: 4.0.8
|
||||
@@ -489,7 +489,7 @@ importers:
|
||||
version: 3.0.0(kysely@0.28.2)(postgres@3.4.8)
|
||||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.23
|
||||
version: 4.17.21
|
||||
luxon:
|
||||
specifier: ^3.4.2
|
||||
version: 3.7.2
|
||||
@@ -802,7 +802,7 @@ importers:
|
||||
version: 4.1.0
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.23
|
||||
version: 4.17.22
|
||||
luxon:
|
||||
specifier: ^3.4.4
|
||||
version: 3.7.2
|
||||
@@ -8916,8 +8916,8 @@ packages:
|
||||
lodash-es@4.17.21:
|
||||
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
|
||||
|
||||
lodash-es@4.17.23:
|
||||
resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==}
|
||||
lodash-es@4.17.22:
|
||||
resolution: {integrity: sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==}
|
||||
|
||||
lodash.camelcase@4.3.0:
|
||||
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
|
||||
@@ -8964,9 +8964,6 @@ packages:
|
||||
lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
|
||||
lodash@4.17.23:
|
||||
resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}
|
||||
|
||||
log-symbols@4.1.0:
|
||||
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -14545,7 +14542,7 @@ snapshots:
|
||||
html-tags: 3.3.1
|
||||
html-webpack-plugin: 5.6.5(webpack@5.104.1)
|
||||
leven: 3.1.0
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
open: 8.4.2
|
||||
p-map: 4.0.0
|
||||
prompts: 2.4.2
|
||||
@@ -14662,7 +14659,7 @@ snapshots:
|
||||
cheerio: 1.0.0-rc.12
|
||||
feed: 4.2.2
|
||||
fs-extra: 11.3.2
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
schema-dts: 1.1.5
|
||||
@@ -14704,7 +14701,7 @@ snapshots:
|
||||
combine-promises: 1.2.0
|
||||
fs-extra: 11.3.2
|
||||
js-yaml: 4.1.1
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
schema-dts: 1.1.5
|
||||
@@ -15017,7 +15014,7 @@ snapshots:
|
||||
'@mdx-js/react': 3.1.1(@types/react@19.2.8)(react@18.3.1)
|
||||
clsx: 2.1.1
|
||||
infima: 0.2.0-alpha.45
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
nprogress: 0.2.0
|
||||
postcss: 8.5.6
|
||||
prism-react-renderer: 2.4.1(react@18.3.1)
|
||||
@@ -15115,7 +15112,7 @@ snapshots:
|
||||
clsx: 2.1.1
|
||||
eta: 2.2.0
|
||||
fs-extra: 11.3.2
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
tslib: 2.8.1
|
||||
@@ -15190,7 +15187,7 @@ snapshots:
|
||||
fs-extra: 11.3.2
|
||||
joi: 17.13.3
|
||||
js-yaml: 4.1.1
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
tslib: 2.8.1
|
||||
transitivePeerDependencies:
|
||||
- '@swc/core'
|
||||
@@ -15215,7 +15212,7 @@ snapshots:
|
||||
gray-matter: 4.0.3
|
||||
jiti: 1.21.7
|
||||
js-yaml: 4.1.1
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
micromatch: 4.0.8
|
||||
p-queue: 6.6.2
|
||||
prompts: 2.4.2
|
||||
@@ -15600,7 +15597,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.12(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.12(@nestjs/common@11.1.12(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.12)(@nestjs/websockets@11.1.12)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
|
||||
'@grpc/grpc-js@1.14.3':
|
||||
dependencies:
|
||||
@@ -18845,7 +18842,7 @@ snapshots:
|
||||
graceful-fs: 4.2.11
|
||||
is-stream: 2.0.1
|
||||
lazystream: 1.0.1
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
normalize-path: 3.0.0
|
||||
readable-stream: 4.7.0
|
||||
|
||||
@@ -19335,7 +19332,7 @@ snapshots:
|
||||
chevrotain-allstar@0.3.1(chevrotain@11.0.3):
|
||||
dependencies:
|
||||
chevrotain: 11.0.3
|
||||
lodash-es: 4.17.23
|
||||
lodash-es: 4.17.22
|
||||
|
||||
chevrotain@11.0.3:
|
||||
dependencies:
|
||||
@@ -20030,7 +20027,7 @@ snapshots:
|
||||
dagre-d3-es@7.0.13:
|
||||
dependencies:
|
||||
d3: 7.9.0
|
||||
lodash-es: 4.17.23
|
||||
lodash-es: 4.17.22
|
||||
|
||||
data-urls@3.0.2:
|
||||
dependencies:
|
||||
@@ -21578,7 +21575,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/html-minifier-terser': 6.1.0
|
||||
html-minifier-terser: 6.1.0
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
pretty-error: 4.0.0
|
||||
tapable: 2.3.0
|
||||
optionalDependencies:
|
||||
@@ -21772,7 +21769,7 @@ snapshots:
|
||||
cli-cursor: 3.1.0
|
||||
cli-width: 3.0.0
|
||||
figures: 3.2.0
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
mute-stream: 0.0.8
|
||||
ora: 5.4.1
|
||||
run-async: 2.4.1
|
||||
@@ -22379,7 +22376,7 @@ snapshots:
|
||||
|
||||
lodash-es@4.17.21: {}
|
||||
|
||||
lodash-es@4.17.23: {}
|
||||
lodash-es@4.17.22: {}
|
||||
|
||||
lodash.camelcase@4.3.0: {}
|
||||
|
||||
@@ -22411,8 +22408,6 @@ snapshots:
|
||||
|
||||
lodash@4.17.21: {}
|
||||
|
||||
lodash@4.17.23: {}
|
||||
|
||||
log-symbols@4.1.0:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
@@ -22815,7 +22810,7 @@ snapshots:
|
||||
dompurify: 3.3.1
|
||||
katex: 0.16.27
|
||||
khroma: 2.1.0
|
||||
lodash-es: 4.17.23
|
||||
lodash-es: 4.17.22
|
||||
marked: 16.4.2
|
||||
roughjs: 4.6.6
|
||||
stylis: 4.3.6
|
||||
@@ -23388,7 +23383,7 @@ snapshots:
|
||||
|
||||
node-emoji@1.11.0:
|
||||
dependencies:
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
|
||||
node-emoji@2.2.0:
|
||||
dependencies:
|
||||
@@ -24389,7 +24384,7 @@ snapshots:
|
||||
|
||||
pretty-error@4.0.0:
|
||||
dependencies:
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
renderkid: 3.0.0
|
||||
|
||||
pretty-format@27.5.1:
|
||||
@@ -24862,7 +24857,7 @@ snapshots:
|
||||
css-select: 4.3.0
|
||||
dom-converter: 0.2.0
|
||||
htmlparser2: 6.1.0
|
||||
lodash: 4.17.23
|
||||
lodash: 4.17.21
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
repeat-string@1.6.1: {}
|
||||
@@ -25719,7 +25714,7 @@ snapshots:
|
||||
json-source-map: 0.6.1
|
||||
jsonpath-plus: 10.3.0
|
||||
jsonrepair: 3.13.1
|
||||
lodash-es: 4.17.23
|
||||
lodash-es: 4.17.22
|
||||
memoize-one: 6.0.0
|
||||
natural-compare-lite: 1.4.0
|
||||
sass: 1.97.1
|
||||
|
||||
@@ -38,11 +38,6 @@
|
||||
<a href="README_th_TH.md">ภาษาไทย</a>
|
||||
</p>
|
||||
|
||||
## Warnung
|
||||
|
||||
- ⚠️ Das Projekt befindet sich in **sehr aktiver** Entwicklung.
|
||||
- ⚠️ Gehe von möglichen Fehlern und von Änderungen mit Breaking-Changes aus.
|
||||
- ⚠️ **Nutze die App auf keinen Fall als einziges Speichermedium für deine Fotos und Videos.**
|
||||
- ⚠️ Befolge immer die [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) Backup-Regel für deine wertvollen Fotos und Videos!
|
||||
|
||||
> [!NOTE]
|
||||
@@ -62,7 +57,7 @@
|
||||
|
||||
## Demo
|
||||
|
||||
Die Web-Demo kannst Du unter https://demo.immich.app finden. Für die Handy-App kannst Du `https://demo.immich.app` als `Server Endpoint URL` angeben.
|
||||
Die Web-Demo kannst Du unter https://demo.immich.app finden. Für die Smartphone-App kannst Du `https://demo.immich.app` als `Server Endpoint URL` angeben.
|
||||
|
||||
### Login Daten
|
||||
|
||||
@@ -93,7 +88,7 @@ Die Web-Demo kannst Du unter https://demo.immich.app finden. Für die Handy-App
|
||||
| LivePhoto/MotionPhoto Sicherung und Wiedergabe | Ja | Ja |
|
||||
| Unterstützung für 360-Grad-Bilder | Nein | Ja |
|
||||
| Benutzerdefinierte Speicherstruktur | Ja | Ja |
|
||||
| Öffentliches Teilen | Nein | Ja |
|
||||
| Öffentliches Teilen | Ja | Ja |
|
||||
| Archiv und Favoriten | Ja | Ja |
|
||||
| Globale Karte | Ja | Ja |
|
||||
| Partnerfreigabe (Teilen) | Ja | Ja |
|
||||
@@ -103,7 +98,7 @@ Die Web-Demo kannst Du unter https://demo.immich.app finden. Für die Handy-App
|
||||
| Schreibgeschützte Gallerie | Ja | Ja |
|
||||
| Gestapelte Bilder | Ja | Ja |
|
||||
| Tags | Nein | Ja |
|
||||
| Ordner-Ansicht | Nein | Ja |
|
||||
| Ordner-Ansicht | Ja | Ja |
|
||||
|
||||
|
||||
## Übersetzungen
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
MapReverseGeocodeDto,
|
||||
MapReverseGeocodeResponseDto,
|
||||
} from 'src/dtos/map.dto';
|
||||
import { ApiTag } from 'src/enum';
|
||||
import { ApiTag, Permission } from 'src/enum';
|
||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||
import { MapService } from 'src/services/map.service';
|
||||
|
||||
@@ -18,7 +18,7 @@ export class MapController {
|
||||
constructor(private service: MapService) {}
|
||||
|
||||
@Get('markers')
|
||||
@Authenticated()
|
||||
@Authenticated({ permission: Permission.MapRead })
|
||||
@Endpoint({
|
||||
summary: 'Retrieve map markers',
|
||||
description: 'Retrieve a list of latitude and longitude coordinates for every asset with location data.',
|
||||
@@ -28,8 +28,8 @@ export class MapController {
|
||||
return this.service.getMapMarkers(auth, options);
|
||||
}
|
||||
|
||||
@Authenticated()
|
||||
@Get('reverse-geocode')
|
||||
@Authenticated({ permission: Permission.MapSearch })
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Endpoint({
|
||||
summary: 'Reverse geocode coordinates',
|
||||
|
||||
@@ -160,6 +160,9 @@ export enum Permission {
|
||||
|
||||
Maintenance = 'maintenance',
|
||||
|
||||
MapRead = 'map.read',
|
||||
MapSearch = 'map.search',
|
||||
|
||||
MemoryCreate = 'memory.create',
|
||||
MemoryRead = 'memory.read',
|
||||
MemoryUpdate = 'memory.update',
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
import { modalManager, toastManager, type ActionItem } from '@immich/ui';
|
||||
import {
|
||||
mdiDeleteRestore,
|
||||
mdiInformationOutline,
|
||||
mdiLockReset,
|
||||
mdiLockSmart,
|
||||
mdiPencilOutline,
|
||||
@@ -46,6 +47,12 @@ export const getUserAdminsActions = ($t: MessageFormatter) => {
|
||||
};
|
||||
|
||||
export const getUserAdminActions = ($t: MessageFormatter, user: UserAdminResponseDto) => {
|
||||
const Detail: ActionItem = {
|
||||
icon: mdiInformationOutline,
|
||||
title: $t('details'),
|
||||
onAction: () => goto(Route.viewUser(user)),
|
||||
};
|
||||
|
||||
const Update: ActionItem = {
|
||||
icon: mdiPencilOutline,
|
||||
title: $t('edit'),
|
||||
@@ -92,7 +99,7 @@ export const getUserAdminActions = ($t: MessageFormatter, user: UserAdminRespons
|
||||
onAction: () => handleResetPinCodeUserAdmin(user),
|
||||
};
|
||||
|
||||
return { Update, Delete, Restore, ResetPassword, ResetPinCode };
|
||||
return { Detail, Update, Delete, Restore, ResetPassword, ResetPinCode };
|
||||
};
|
||||
|
||||
export const handleCreateUserAdmin = async (dto: UserAdminCreateDto) => {
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<script lang="ts">
|
||||
import AdminPageLayout from '$lib/components/layouts/AdminPageLayout.svelte';
|
||||
import OnEvents from '$lib/components/OnEvents.svelte';
|
||||
import { getUserAdminsActions, handleNavigateUserAdmin } from '$lib/services/user-admin.service';
|
||||
import { Route } from '$lib/route';
|
||||
import { getUserAdminActions, getUserAdminsActions } from '$lib/services/user-admin.service';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { getByteUnitString } from '$lib/utils/byte-units';
|
||||
import { searchUsersAdmin, type UserAdminResponseDto } from '@immich/sdk';
|
||||
import {
|
||||
Button,
|
||||
CommandPaletteDefaultProvider,
|
||||
Container,
|
||||
ContextMenuButton,
|
||||
Icon,
|
||||
Link,
|
||||
MenuItemType,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
@@ -46,11 +49,16 @@
|
||||
|
||||
const { Create } = $derived(getUserAdminsActions($t));
|
||||
|
||||
const getActionsForUser = (user: UserAdminResponseDto) => {
|
||||
const { Detail, Update, Delete, ResetPassword, ResetPinCode } = getUserAdminActions($t, user);
|
||||
return [Detail, Update, ResetPassword, ResetPinCode, MenuItemType.Divider, Delete];
|
||||
};
|
||||
|
||||
const classes = {
|
||||
column1: 'w-8/12 sm:w-5/12 lg:w-6/12 xl:w-4/12 2xl:w-5/12',
|
||||
column2: 'hidden sm:block w-3/12',
|
||||
column3: 'hidden xl:block w-3/12 2xl:w-2/12',
|
||||
column4: 'w-4/12 lg:w-3/12 xl:w-2/12',
|
||||
column1: 'w-8/12 md:w-5/12 lg:w-4/12',
|
||||
column2: 'hidden md:block md:w-5/12 lg:w-4/12',
|
||||
column3: 'hidden lg:block lg:w-2/12',
|
||||
column4: 'w-4/12 md:w-2/12 flex justify-end',
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -68,16 +76,18 @@
|
||||
<Container center size="large">
|
||||
<Table class="mt-4" striped spacing="small" size="small">
|
||||
<TableHeader>
|
||||
<TableHeading class={classes.column1}>{$t('email')}</TableHeading>
|
||||
<TableHeading class={classes.column2}>{$t('name')}</TableHeading>
|
||||
<TableHeading class={classes.column1}>{$t('name')}</TableHeading>
|
||||
<TableHeading class={classes.column2}>{$t('email')}</TableHeading>
|
||||
<TableHeading class={classes.column3}>{$t('has_quota')}</TableHeading>
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
{#each users as user (user.id)}
|
||||
<TableRow color={user.deletedAt ? 'danger' : undefined}>
|
||||
<TableCell class={classes.column1}>{user.email}</TableCell>
|
||||
<TableCell class={classes.column2}>{user.name}</TableCell>
|
||||
<TableCell class={classes.column1}>
|
||||
<Link href={Route.viewUser(user)}>{user.name}</Link>
|
||||
</TableCell>
|
||||
<TableCell class={classes.column2}>{user.email}</TableCell>
|
||||
<TableCell class={classes.column3}>
|
||||
<div class="container mx-auto flex flex-wrap justify-center">
|
||||
{#if user.quotaSizeInBytes !== null && user.quotaSizeInBytes >= 0}
|
||||
@@ -88,7 +98,7 @@
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class={classes.column4}>
|
||||
<Button onclick={() => handleNavigateUserAdmin(user)}>{$t('view')}</Button>
|
||||
<ContextMenuButton color="primary" aria-label={$t('open')} items={getActionsForUser(user)} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
|
||||
@@ -198,8 +198,8 @@
|
||||
})}
|
||||
>
|
||||
<p class="font-medium text-immich-dark-gray dark:text-white mb-2">{$t('storage')}</p>
|
||||
<div class="mt-4 h-[7px] w-full rounded-full bg-gray-200 dark:bg-gray-700">
|
||||
<div class="h-[7px] rounded-full {getUsageClass()}" style="width: {usedPercentage}%"></div>
|
||||
<div class="mt-4 h-1.75 w-full rounded-full bg-gray-200 dark:bg-gray-700">
|
||||
<div class="h-1.75 rounded-full {getUsageClass()}" style="width: {usedPercentage}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user