Merge remote-tracking branch 'origin/main' into feature/sync_assets_trashed_state

# Conflicts:
#	mobile/ios/Podfile.lock
This commit is contained in:
Peter Ombodi 2025-10-08 19:07:11 +03:00
commit 4d88ffe694
36 changed files with 451 additions and 296 deletions

View file

@ -73,7 +73,7 @@ jobs:
- name: Restore Gradle Cache - name: Restore Gradle Cache
id: cache-gradle-restore id: cache-gradle-restore
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with: with:
path: | path: |
~/.gradle/caches ~/.gradle/caches
@ -130,7 +130,7 @@ jobs:
- name: Save Gradle Cache - name: Save Gradle Cache
id: cache-gradle-save id: cache-gradle-save
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
if: github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/main'
with: with:
path: | path: |

View file

@ -20,7 +20,7 @@
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.9", "@types/micromatch": "^4.0.9",
"@types/mock-fs": "^4.13.1", "@types/mock-fs": "^4.13.1",
"@types/node": "^22.18.1", "@types/node": "^22.18.8",
"@vitest/coverage-v8": "^3.0.0", "@vitest/coverage-v8": "^3.0.0",
"byte-size": "^9.0.0", "byte-size": "^9.0.0",
"cli-progress": "^3.12.0", "cli-progress": "^3.12.0",

View file

@ -25,7 +25,7 @@
"@playwright/test": "^1.44.1", "@playwright/test": "^1.44.1",
"@socket.io/component-emitter": "^3.1.2", "@socket.io/component-emitter": "^3.1.2",
"@types/luxon": "^3.4.2", "@types/luxon": "^3.4.2",
"@types/node": "^22.18.1", "@types/node": "^22.18.8",
"@types/oidc-provider": "^9.0.0", "@types/oidc-provider": "^9.0.0",
"@types/pg": "^8.15.1", "@types/pg": "^8.15.1",
"@types/pngjs": "^6.0.4", "@types/pngjs": "^6.0.4",

View file

@ -13,8 +13,16 @@ else:
module_dir = Path(__file__).parent module_dir = Path(__file__).parent
def is_ipv6(host: str) -> bool:
try:
return ip_address(host).version == 6
except ValueError:
return False
bind_host = non_prefixed_settings.immich_host bind_host = non_prefixed_settings.immich_host
if ip_address(bind_host).version == 6: if is_ipv6(bind_host):
bind_host = f"[{bind_host}]" bind_host = f"[{bind_host}]"
bind_address = f"{bind_host}:{non_prefixed_settings.immich_port}" bind_address = f"{bind_host}:{non_prefixed_settings.immich_port}"

View file

@ -7,8 +7,16 @@ import requests
port = os.getenv("IMMICH_PORT", 3003) port = os.getenv("IMMICH_PORT", 3003)
host = os.getenv("IMMICH_HOST", "0.0.0.0") host = os.getenv("IMMICH_HOST", "0.0.0.0")
def is_ipv6(host: str) -> bool:
try:
return ip_address(host).version == 6
except ValueError:
return False
host = "localhost" if host == "0.0.0.0" else host host = "localhost" if host == "0.0.0.0" else host
host = f"[{host}]" if ip_address(host).version == 6 else host host = f"[{host}]" if is_ipv6(host) else host
try: try:
response = requests.get(f"http://{host}:{port}/ping", timeout=2) response = requests.get(f"http://{host}:{port}/ping", timeout=2)

View file

@ -64,7 +64,7 @@ PODS:
- Flutter - Flutter
- integration_test (0.0.1): - integration_test (0.0.1):
- Flutter - Flutter
- isar_flutter_libs (1.0.0): - isar_community_flutter_libs (1.0.0):
- Flutter - Flutter
- local_auth_darwin (0.0.1): - local_auth_darwin (0.0.1):
- Flutter - Flutter
@ -149,7 +149,7 @@ DEPENDENCIES:
- home_widget (from `.symlinks/plugins/home_widget/ios`) - home_widget (from `.symlinks/plugins/home_widget/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- integration_test (from `.symlinks/plugins/integration_test/ios`) - integration_test (from `.symlinks/plugins/integration_test/ios`)
- isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`) - isar_community_flutter_libs (from `.symlinks/plugins/isar_community_flutter_libs/ios`)
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`) - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
- maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`) - maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`)
- native_video_player (from `.symlinks/plugins/native_video_player/ios`) - native_video_player (from `.symlinks/plugins/native_video_player/ios`)
@ -210,8 +210,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/image_picker_ios/ios" :path: ".symlinks/plugins/image_picker_ios/ios"
integration_test: integration_test:
:path: ".symlinks/plugins/integration_test/ios" :path: ".symlinks/plugins/integration_test/ios"
isar_flutter_libs: isar_community_flutter_libs:
:path: ".symlinks/plugins/isar_flutter_libs/ios" :path: ".symlinks/plugins/isar_community_flutter_libs/ios"
local_auth_darwin: local_auth_darwin:
:path: ".symlinks/plugins/local_auth_darwin/darwin" :path: ".symlinks/plugins/local_auth_darwin/darwin"
maplibre_gl: maplibre_gl:
@ -246,46 +246,46 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/wakelock_plus/ios" :path: ".symlinks/plugins/wakelock_plus/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
background_downloader: a05c77d32a0d70615b9c04577aa203535fc924ff background_downloader: 50e91d979067b82081aba359d7d916b3ba5fadad
bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842 bonsoir_darwin: 29c7ccf356646118844721f36e1de4b61f6cbd0e
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342 device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49 file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086 flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29 flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
flutter_udid: b2417673f287ee62817a1de3d1643f47b9f508ab flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
flutter_web_auth_2: 06d500582775790a0d4c323222fcb6d7990f9603 flutter_web_auth_2: 5c8d9dcd7848b5a9efb086d24e7a9adcae979c80
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
geolocator_apple: 9bcea1918ff7f0062d98345d238ae12718acfbc1 geolocator_apple: 1560c3c875af2a412242c7a923e15d0d401966ff
home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57 home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
isar_flutter_libs: fdf730ca925d05687f36d7f1d355e482529ed097 isar_community_flutter_libs: bede843185a61a05ff364a05c9b23209523f7e0d
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3 local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
MapLibre: 69e572367f4ef6287e18246cfafc39c80cdcabcd MapLibre: 69e572367f4ef6287e18246cfafc39c80cdcabcd
maplibre_gl: 753f55d763a81cbdba087d02af02d12206e6f94e maplibre_gl: 3c924e44725147b03dda33430ad216005b40555f
native_video_player: d12af78a1a4a8cf09775a5177d5b392def6fd23c native_video_player: b65c58951ede2f93d103a25366bdebca95081265
network_info_plus: 6613d9d7cdeb0e6f366ed4dbe4b3c51c52d567a9 network_info_plus: cf61925ab5205dce05a4f0895989afdb6aade5fc
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a photo_manager: d2fbcc0f2d82458700ee6256a15018210a81d413
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868
share_handler_ios: 6dd3a4ac5ca0d955274aec712ba0ecdcaf583e7c share_handler_ios: e2244e990f826b2c8eaa291ac3831569438ba0fb
share_handler_ios_models: fc638c9b4330dc7f082586c92aee9dfa0b87b871 share_handler_ios_models: fc638c9b4330dc7f082586c92aee9dfa0b87b871
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
sqlite3: fc1400008a9b3525f5914ed715a5d1af0b8f4983 sqlite3: fc1400008a9b3525f5914ed715a5d1af0b8f4983
sqlite3_flutter_libs: cc304edcb8e1d8c595d1b08c7aeb46a47691d9db sqlite3_flutter_libs: f8fc13346870e73fe35ebf6dbb997fbcd156b241
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
wakelock_plus: 373cfe59b235a6dd5837d0fb88791d2f13a90d56 wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49
PODFILE CHECKSUM: 7ce312f2beab01395db96f6969d90a447279cf45 PODFILE CHECKSUM: 7ce312f2beab01395db96f6969d90a447279cf45

View file

@ -3,27 +3,30 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
class SearchResult { class SearchResult {
final List<BaseAsset> assets; final List<BaseAsset> assets;
final double scrollOffset;
final int? nextPage; final int? nextPage;
const SearchResult({required this.assets, this.nextPage}); const SearchResult({required this.assets, this.scrollOffset = 0.0, this.nextPage});
int get totalAssets => assets.length; SearchResult copyWith({List<BaseAsset>? assets, int? nextPage, double? scrollOffset}) {
return SearchResult(
SearchResult copyWith({List<BaseAsset>? assets, int? nextPage}) { assets: assets ?? this.assets,
return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage); nextPage: nextPage ?? this.nextPage,
scrollOffset: scrollOffset ?? this.scrollOffset,
);
} }
@override @override
String toString() => 'SearchResult(assets: $assets, nextPage: $nextPage)'; String toString() => 'SearchResult(assets: ${assets.length}, nextPage: $nextPage, scrollOffset: $scrollOffset)';
@override @override
bool operator ==(covariant SearchResult other) { bool operator ==(covariant SearchResult other) {
if (identical(this, other)) return true; if (identical(this, other)) return true;
final listEquals = const DeepCollectionEquality().equals; final listEquals = const DeepCollectionEquality().equals;
return listEquals(other.assets, assets) && other.nextPage == nextPage; return listEquals(other.assets, assets) && other.nextPage == nextPage && other.scrollOffset == scrollOffset;
} }
@override @override
int get hashCode => assets.hashCode ^ nextPage.hashCode; int get hashCode => assets.hashCode ^ nextPage.hashCode ^ scrollOffset.hashCode;
} }

View file

@ -203,7 +203,7 @@ class TimelineService {
Future<void> dispose() async { Future<void> dispose() async {
await _bucketSubscription?.cancel(); await _bucketSubscription?.cancel();
_bucketSubscription = null; _bucketSubscription = null;
_buffer.clear(); _buffer = [];
_bufferOffset = 0; _bufferOffset = 0;
} }
} }

View file

@ -132,7 +132,7 @@ const AlbumSchema = CollectionSchema(
getId: _albumGetId, getId: _albumGetId,
getLinks: _albumGetLinks, getLinks: _albumGetLinks,
attach: _albumAttach, attach: _albumAttach,
version: '3.1.8', version: '3.3.0-dev.3',
); );
int _albumEstimateSize( int _albumEstimateSize(

View file

@ -47,7 +47,7 @@ const AndroidDeviceAssetSchema = CollectionSchema(
getId: _androidDeviceAssetGetId, getId: _androidDeviceAssetGetId,
getLinks: _androidDeviceAssetGetLinks, getLinks: _androidDeviceAssetGetLinks,
attach: _androidDeviceAssetAttach, attach: _androidDeviceAssetAttach,
version: '3.1.8', version: '3.3.0-dev.3',
); );
int _androidDeviceAssetEstimateSize( int _androidDeviceAssetEstimateSize(

View file

@ -168,7 +168,7 @@ const AssetSchema = CollectionSchema(
getId: _assetGetId, getId: _assetGetId,
getLinks: _assetGetLinks, getLinks: _assetGetLinks,
attach: _assetAttach, attach: _assetAttach,
version: '3.1.8', version: '3.3.0-dev.3',
); );
int _assetEstimateSize( int _assetEstimateSize(

View file

@ -43,7 +43,7 @@ const BackupAlbumSchema = CollectionSchema(
getId: _backupAlbumGetId, getId: _backupAlbumGetId,
getLinks: _backupAlbumGetLinks, getLinks: _backupAlbumGetLinks,
attach: _backupAlbumAttach, attach: _backupAlbumAttach,
version: '3.1.8', version: '3.3.0-dev.3',
); );
int _backupAlbumEstimateSize( int _backupAlbumEstimateSize(

View file

@ -32,7 +32,7 @@ const DuplicatedAssetSchema = CollectionSchema(
getId: _duplicatedAssetGetId, getId: _duplicatedAssetGetId,
getLinks: _duplicatedAssetGetLinks, getLinks: _duplicatedAssetGetLinks,
attach: _duplicatedAssetAttach, attach: _duplicatedAssetAttach,
version: '3.1.8', version: '3.3.0-dev.3',
); );
int _duplicatedAssetEstimateSize( int _duplicatedAssetEstimateSize(

View file

@ -52,7 +52,7 @@ const ETagSchema = CollectionSchema(
getId: _eTagGetId, getId: _eTagGetId,
getLinks: _eTagGetLinks, getLinks: _eTagGetLinks,
attach: _eTagAttach, attach: _eTagAttach,
version: '3.1.8', version: '3.3.0-dev.3',
); );
int _eTagEstimateSize( int _eTagEstimateSize(

View file

@ -60,7 +60,7 @@ const IOSDeviceAssetSchema = CollectionSchema(
getId: _iOSDeviceAssetGetId, getId: _iOSDeviceAssetGetId,
getLinks: _iOSDeviceAssetGetLinks, getLinks: _iOSDeviceAssetGetLinks,
attach: _iOSDeviceAssetAttach, attach: _iOSDeviceAssetAttach,
version: '3.1.8', version: '3.3.0-dev.3',
); );
int _iOSDeviceAssetEstimateSize( int _iOSDeviceAssetEstimateSize(

View file

@ -65,7 +65,7 @@ const DeviceAssetEntitySchema = CollectionSchema(
getId: _deviceAssetEntityGetId, getId: _deviceAssetEntityGetId,
getLinks: _deviceAssetEntityGetLinks, getLinks: _deviceAssetEntityGetLinks,
attach: _deviceAssetEntityAttach, attach: _deviceAssetEntityAttach,
version: '3.1.8', version: '3.3.0-dev.3',
); );
int _deviceAssetEntityEstimateSize( int _deviceAssetEntityEstimateSize(

View file

@ -68,7 +68,7 @@ const ExifInfoSchema = CollectionSchema(
getId: _exifInfoGetId, getId: _exifInfoGetId,
getLinks: _exifInfoGetLinks, getLinks: _exifInfoGetLinks,
attach: _exifInfoAttach, attach: _exifInfoAttach,
version: '3.1.8', version: '3.3.0-dev.3',
); );
int _exifInfoEstimateSize( int _exifInfoEstimateSize(

View file

@ -37,7 +37,7 @@ const StoreValueSchema = CollectionSchema(
getId: _storeValueGetId, getId: _storeValueGetId,
getLinks: _storeValueGetLinks, getLinks: _storeValueGetLinks,
attach: _storeValueAttach, attach: _storeValueAttach,
version: '3.1.8', version: '3.3.0-dev.3',
); );
int _storeValueEstimateSize( int _storeValueEstimateSize(

View file

@ -95,7 +95,7 @@ const UserSchema = CollectionSchema(
getId: _userGetId, getId: _userGetId,
getLinks: _userGetLinks, getLinks: _userGetLinks,
attach: _userAttach, attach: _userAttach,
version: '3.1.8', version: '3.3.0-dev.3',
); );
int _userEstimateSize( int _userEstimateSize(

View file

@ -599,9 +599,9 @@ class _SearchResultGrid extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final searchResult = ref.watch(paginatedSearchProvider); final assets = ref.watch(paginatedSearchProvider.select((s) => s.assets));
if (searchResult.totalAssets == 0) { if (assets.isEmpty) {
return const _SearchEmptyContent(); return const _SearchEmptyContent();
} }
@ -615,6 +615,7 @@ class _SearchResultGrid extends ConsumerWidget {
if (metrics.pixels >= metrics.maxScrollExtent && isVerticalScroll && !isBottomSheetNotification) { if (metrics.pixels >= metrics.maxScrollExtent && isVerticalScroll && !isBottomSheetNotification) {
onScrollEnd(); onScrollEnd();
ref.read(paginatedSearchProvider.notifier).setScrollOffset(metrics.maxScrollExtent);
} }
return true; return true;
@ -623,17 +624,18 @@ class _SearchResultGrid extends ConsumerWidget {
child: ProviderScope( child: ProviderScope(
overrides: [ overrides: [
timelineServiceProvider.overrideWith((ref) { timelineServiceProvider.overrideWith((ref) {
final timelineService = ref.watch(timelineFactoryProvider).fromAssets(searchResult.assets); final timelineService = ref.watch(timelineFactoryProvider).fromAssets(assets);
ref.onDispose(timelineService.dispose); ref.onDispose(timelineService.dispose);
return timelineService; return timelineService;
}), }),
], ],
child: Timeline( child: Timeline(
key: ValueKey(searchResult.totalAssets), key: ValueKey(assets.length),
groupBy: GroupAssetsBy.none, groupBy: GroupAssetsBy.none,
appBar: null, appBar: null,
bottomSheet: const GeneralBottomSheet(minChildSize: 0.20), bottomSheet: const GeneralBottomSheet(minChildSize: 0.20),
snapToMonth: false, snapToMonth: false,
initialScrollOffset: ref.read(paginatedSearchProvider.select((s) => s.scrollOffset)),
), ),
), ),
), ),

View file

@ -24,12 +24,20 @@ class PaginatedSearchNotifier extends StateNotifier<SearchResult> {
return false; return false;
} }
state = SearchResult(assets: [...state.assets, ...result.assets], nextPage: result.nextPage); state = SearchResult(
assets: [...state.assets, ...result.assets],
nextPage: result.nextPage,
scrollOffset: state.scrollOffset,
);
return true; return true;
} }
void setScrollOffset(double offset) {
state = state.copyWith(scrollOffset: offset);
}
clear() { clear() {
state = const SearchResult(assets: [], nextPage: 1); state = const SearchResult(assets: [], nextPage: 1, scrollOffset: 0.0);
} }
} }

View file

@ -51,7 +51,7 @@ class AssetDetailBottomSheet extends ConsumerWidget {
isArchived: isArchived, isArchived: isArchived,
isTrashEnabled: isTrashEnable, isTrashEnabled: isTrashEnable,
isInLockedView: isInLockedView, isInLockedView: isInLockedView,
isStacked: asset.hasRemote && (asset as RemoteAsset).stackId != null, isStacked: asset is RemoteAsset && asset.stackId != null,
currentAlbum: currentAlbum, currentAlbum: currentAlbum,
advancedTroubleshooting: advancedTroubleshooting, advancedTroubleshooting: advancedTroubleshooting,
source: ActionSource.viewer, source: ActionSource.viewer,

View file

@ -322,6 +322,9 @@ class NativeVideoViewer extends HookConsumerWidget {
removeListeners(playerController); removeListeners(playerController);
} }
if (value != null) {
isVisible.value = _isCurrentAsset(value, asset);
}
final curAsset = currentAsset.value; final curAsset = currentAsset.value;
if (curAsset == asset) { if (curAsset == asset) {
return; return;

View file

@ -40,6 +40,7 @@ class Timeline extends StatelessWidget {
this.groupBy, this.groupBy,
this.withScrubber = true, this.withScrubber = true,
this.snapToMonth = true, this.snapToMonth = true,
this.initialScrollOffset,
}); });
final Widget? topSliverWidget; final Widget? topSliverWidget;
@ -51,6 +52,7 @@ class Timeline extends StatelessWidget {
final GroupAssetsBy? groupBy; final GroupAssetsBy? groupBy;
final bool withScrubber; final bool withScrubber;
final bool snapToMonth; final bool snapToMonth;
final double? initialScrollOffset;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -78,6 +80,7 @@ class Timeline extends StatelessWidget {
bottomSheet: bottomSheet, bottomSheet: bottomSheet,
withScrubber: withScrubber, withScrubber: withScrubber,
snapToMonth: snapToMonth, snapToMonth: snapToMonth,
initialScrollOffset: initialScrollOffset,
), ),
), ),
), ),
@ -93,6 +96,7 @@ class _SliverTimeline extends ConsumerStatefulWidget {
this.bottomSheet, this.bottomSheet,
this.withScrubber = true, this.withScrubber = true,
this.snapToMonth = true, this.snapToMonth = true,
this.initialScrollOffset,
}); });
final Widget? topSliverWidget; final Widget? topSliverWidget;
@ -101,6 +105,7 @@ class _SliverTimeline extends ConsumerStatefulWidget {
final Widget? bottomSheet; final Widget? bottomSheet;
final bool withScrubber; final bool withScrubber;
final bool snapToMonth; final bool snapToMonth;
final double? initialScrollOffset;
@override @override
ConsumerState createState() => _SliverTimelineState(); ConsumerState createState() => _SliverTimelineState();
@ -124,7 +129,10 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_scrollController = ScrollController(onAttach: _restoreScalePosition); _scrollController = ScrollController(
initialScrollOffset: widget.initialScrollOffset ?? 0.0,
onAttach: _restoreScalePosition,
);
_eventSubscription = EventStream.shared.listen(_onEvent); _eventSubscription = EventStream.shared.listen(_onEvent);
final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow); final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow);

View file

@ -77,11 +77,14 @@ class ActionNotifier extends Notifier<void> {
return _getAssets(source).whereType<RemoteAsset>().toIds().toList(growable: false); return _getAssets(source).whereType<RemoteAsset>().toIds().toList(growable: false);
} }
List<String> _getLocalIdsForSource(ActionSource source) { List<String> _getLocalIdsForSource(ActionSource source, {bool ignoreLocalOnly = false}) {
final Set<BaseAsset> assets = _getAssets(source); final Set<BaseAsset> assets = _getAssets(source);
final List<String> localIds = []; final List<String> localIds = [];
for (final asset in assets) { for (final asset in assets) {
if (ignoreLocalOnly && asset.storage != AssetState.merged) {
continue;
}
if (asset is LocalAsset) { if (asset is LocalAsset) {
localIds.add(asset.id); localIds.add(asset.id);
} else if (asset is RemoteAsset && asset.localId != null) { } else if (asset is RemoteAsset && asset.localId != null) {
@ -189,7 +192,7 @@ class ActionNotifier extends Notifier<void> {
Future<ActionResult> moveToLockFolder(ActionSource source) async { Future<ActionResult> moveToLockFolder(ActionSource source) async {
final ids = _getOwnedRemoteIdsForSource(source); final ids = _getOwnedRemoteIdsForSource(source);
final localIds = _getLocalIdsForSource(source); final localIds = _getLocalIdsForSource(source, ignoreLocalOnly: true);
try { try {
await _service.moveToLockFolder(ids, localIds); await _service.moveToLockFolder(ids, localIds);
return ActionResult(count: ids.length, success: true); return ActionResult(count: ids.length, success: true);

View file

@ -1,5 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
@ -25,7 +26,28 @@ class AssetMediaRepository {
const AssetMediaRepository(this._assetApiRepository); const AssetMediaRepository(this._assetApiRepository);
Future<List<String>> deleteAll(List<String> ids) => PhotoManager.editor.deleteWithIds(ids); Future<bool> _androidSupportsTrash() async {
if (Platform.isAndroid) {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
int sdkVersion = androidInfo.version.sdkInt;
return sdkVersion >= 31;
}
return false;
}
Future<List<String>> deleteAll(List<String> ids) async {
if (CurrentPlatform.isAndroid) {
if (await _androidSupportsTrash()) {
return PhotoManager.editor.android.moveToTrash(
ids.map((e) => AssetEntity(id: e, width: 1, height: 1, typeInt: 0)).toList(),
);
} else {
return PhotoManager.editor.deleteWithIds(ids);
}
}
return PhotoManager.editor.deleteWithIds(ids);
}
Future<asset_entity.Asset?> get(String id) async { Future<asset_entity.Asset?> get(String id) async {
final entity = await AssetEntity.fromId(id); final entity = await AssetEntity.fromId(id);

View file

@ -317,10 +317,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: connectivity_plus name: connectivity_plus
sha256: "04bf81bb0b77de31557b58d052b24b3eee33f09a6e7a8c68a3e247c7df19ec27" sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.3" version: "6.1.5"
connectivity_plus_platform_interface: connectivity_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -1022,25 +1022,34 @@ packages:
isar: isar:
dependency: "direct main" dependency: "direct main"
description: description:
name: isar path: "packages/isar"
sha256: e17a9555bc7f22ff26568b8c64d019b4ffa2dc6bd4cb1c8d9b269aefd32e53ad ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a
url: "https://pub.isar-community.dev" resolved-ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a
source: hosted url: "https://github.com/immich-app/isar"
source: git
version: "3.1.8" version: "3.1.8"
isar_flutter_libs: isar_community:
dependency: transitive
description:
name: isar_community
sha256: "28f59e54636c45ba0bb1b3b7f2656f1c50325f740cea6efcd101900be3fba546"
url: "https://pub.dev"
source: hosted
version: "3.3.0-dev.3"
isar_community_flutter_libs:
dependency: "direct main" dependency: "direct main"
description: description:
name: isar_flutter_libs name: isar_community_flutter_libs
sha256: "78710781e658ce4bff59b3f38c5b2735e899e627f4e926e1221934e77b95231a" sha256: c2934fe755bb3181cb67602fd5df0d080b3d3eb52799f98623aa4fc5acbea010
url: "https://pub.isar-community.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.8" version: "3.3.0-dev.3"
isar_generator: isar_generator:
dependency: "direct dev" dependency: "direct dev"
description: description:
path: "packages/isar_generator" path: "packages/isar_generator"
ref: v3 ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a
resolved-ref: ad574f60ed6f39d2995cd16fc7dc3de9a646ef30 resolved-ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a
url: "https://github.com/immich-app/isar" url: "https://github.com/immich-app/isar"
source: git source: git
version: "3.1.8" version: "3.1.8"

View file

@ -8,8 +8,6 @@ environment:
sdk: '>=3.8.0 <4.0.0' sdk: '>=3.8.0 <4.0.0'
flutter: 3.35.4 flutter: 3.35.4
isar_version: &isar_version 3.1.8
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
@ -81,11 +79,11 @@ dependencies:
openapi: openapi:
path: openapi path: openapi
isar: isar:
version: *isar_version git:
hosted: https://pub.isar-community.dev/ url: https://github.com/immich-app/isar
isar_flutter_libs: # contains Isar Core ref: 'bb1dca40fe87a001122e5d43bc6254718cb49f3a'
version: *isar_version path: packages/isar/
hosted: https://pub.isar-community.dev/ isar_community_flutter_libs: 3.3.0-dev.3
# DB # DB
drift: ^2.23.1 drift: ^2.23.1
drift_flutter: ^0.2.4 drift_flutter: ^0.2.4
@ -101,7 +99,7 @@ dev_dependencies:
isar_generator: isar_generator:
git: git:
url: https://github.com/immich-app/isar url: https://github.com/immich-app/isar
ref: v3 ref: 'bb1dca40fe87a001122e5d43bc6254718cb49f3a'
path: packages/isar_generator/ path: packages/isar_generator/
integration_test: integration_test:
sdk: flutter sdk: flutter

View file

@ -19,7 +19,7 @@
"@oazapfts/runtime": "^1.0.2" "@oazapfts/runtime": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.18.1", "@types/node": "^22.18.8",
"typescript": "^5.3.3" "typescript": "^5.3.3"
}, },
"repository": { "repository": {

340
pnpm-lock.yaml generated
View file

@ -63,11 +63,11 @@ importers:
specifier: ^4.13.1 specifier: ^4.13.1
version: 4.13.4 version: 4.13.4
'@types/node': '@types/node':
specifier: ^22.18.1 specifier: ^22.18.8
version: 22.18.5 version: 22.18.8
'@vitest/coverage-v8': '@vitest/coverage-v8':
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
byte-size: byte-size:
specifier: ^9.0.0 specifier: ^9.0.0
version: 9.0.1 version: 9.0.1
@ -109,16 +109,16 @@ importers:
version: 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) version: 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2)
vite: vite:
specifier: ^7.0.0 specifier: ^7.0.0
version: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) version: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
vite-tsconfig-paths: vite-tsconfig-paths:
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
vitest: vitest:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
vitest-fetch-mock: vitest-fetch-mock:
specifier: ^0.4.0 specifier: ^0.4.0
version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
yaml: yaml:
specifier: ^2.3.1 specifier: ^2.3.1
version: 2.8.1 version: 2.8.1
@ -211,8 +211,8 @@ importers:
specifier: ^3.4.2 specifier: ^3.4.2
version: 3.7.1 version: 3.7.1
'@types/node': '@types/node':
specifier: ^22.18.1 specifier: ^22.18.8
version: 22.18.5 version: 22.18.8
'@types/oidc-provider': '@types/oidc-provider':
specifier: ^9.0.0 specifier: ^9.0.0
version: 9.5.0 version: 9.5.0
@ -284,7 +284,7 @@ importers:
version: 5.2.1(encoding@0.1.13) version: 5.2.1(encoding@0.1.13)
vitest: vitest:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
open-api/typescript-sdk: open-api/typescript-sdk:
dependencies: dependencies:
@ -293,8 +293,8 @@ importers:
version: 1.0.4 version: 1.0.4
devDependencies: devDependencies:
'@types/node': '@types/node':
specifier: ^22.18.1 specifier: ^22.18.8
version: 22.18.5 version: 22.18.8
typescript: typescript:
specifier: ^5.3.3 specifier: ^5.3.3
version: 5.9.2 version: 5.9.2
@ -450,7 +450,7 @@ importers:
version: 2.0.2 version: 2.0.2
nest-commander: nest-commander:
specifier: ^3.16.0 specifier: ^3.16.0
version: 3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.5)(typescript@5.9.2) version: 3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.8)(typescript@5.9.2)
nestjs-cls: nestjs-cls:
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.4.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) version: 5.4.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2)
@ -462,7 +462,7 @@ importers:
version: 7.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) version: 7.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)
nodemailer: nodemailer:
specifier: ^7.0.0 specifier: ^7.0.0
version: 7.0.6 version: 7.0.7
openid-client: openid-client:
specifier: ^6.3.3 specifier: ^6.3.3
version: 6.8.0 version: 6.8.0
@ -532,7 +532,7 @@ importers:
version: 9.36.0 version: 9.36.0
'@nestjs/cli': '@nestjs/cli':
specifier: ^11.0.2 specifier: ^11.0.2
version: 11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.5) version: 11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.8)
'@nestjs/schematics': '@nestjs/schematics':
specifier: ^11.0.0 specifier: ^11.0.0
version: 11.0.7(chokidar@4.0.3)(typescript@5.9.2) version: 11.0.7(chokidar@4.0.3)(typescript@5.9.2)
@ -582,8 +582,8 @@ importers:
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.0.0 version: 2.0.0
'@types/node': '@types/node':
specifier: ^22.18.1 specifier: ^22.18.8
version: 22.18.5 version: 22.18.8
'@types/nodemailer': '@types/nodemailer':
specifier: ^7.0.0 specifier: ^7.0.0
version: 7.0.1 version: 7.0.1
@ -613,7 +613,7 @@ importers:
version: 13.15.3 version: 13.15.3
'@vitest/coverage-v8': '@vitest/coverage-v8':
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
eslint: eslint:
specifier: ^9.14.0 specifier: ^9.14.0
version: 9.36.0(jiti@2.5.1) version: 9.36.0(jiti@2.5.1)
@ -667,10 +667,10 @@ importers:
version: 1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.50.1) version: 1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.50.1)
vite-tsconfig-paths: vite-tsconfig-paths:
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
vitest: vitest:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
web: web:
dependencies: dependencies:
@ -4519,8 +4519,8 @@ packages:
'@types/node@20.19.2': '@types/node@20.19.2':
resolution: {integrity: sha512-9pLGGwdzOUBDYi0GNjM97FIA+f92fqSke6joWeBjWXllfNxZBs7qeMF7tvtOIsbY45xkWkxrdwUfUf3MnQa9gA==} resolution: {integrity: sha512-9pLGGwdzOUBDYi0GNjM97FIA+f92fqSke6joWeBjWXllfNxZBs7qeMF7tvtOIsbY45xkWkxrdwUfUf3MnQa9gA==}
'@types/node@22.18.5': '@types/node@22.18.8':
resolution: {integrity: sha512-g9BpPfJvxYBXUWI9bV37j6d6LTMNQ88hPwdWWUeYZnMhlo66FIg9gCc1/DZb15QylJSKwOZjwrckvOTWpOiChg==} resolution: {integrity: sha512-pAZSHMiagDR7cARo/cch1f3rXy0AEXwsVsVH09FcyeJVAzCnGgmYis7P3JidtTUjyadhTeSo8TgRPswstghDaw==}
'@types/node@24.5.1': '@types/node@24.5.1':
resolution: {integrity: sha512-/SQdmUP2xa+1rdx7VwB9yPq8PaKej8TD5cQ+XfKDPWWC+VDJU4rvVVagXqKUzhKjtFoNA8rXDJAkCxQPAe00+Q==} resolution: {integrity: sha512-/SQdmUP2xa+1rdx7VwB9yPq8PaKej8TD5cQ+XfKDPWWC+VDJU4rvVVagXqKUzhKjtFoNA8rXDJAkCxQPAe00+Q==}
@ -8398,8 +8398,8 @@ packages:
node-releases@2.0.19: node-releases@2.0.19:
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
nodemailer@7.0.6: nodemailer@7.0.7:
resolution: {integrity: sha512-F44uVzgwo49xboqbFgBGkRaiMgtoBrBEWCVincJPK9+S9Adkzt/wXCLKbf7dxucmxfTI5gHGB+bEmdyzN6QKjw==} resolution: {integrity: sha512-jGOaRznodf62TVzdyhKt/f1Q/c3kYynk8629sgJHpRzGZj01ezbgMMWJSAjHADcwTKxco3B68/R+KHJY2T5BaA==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
nopt@1.0.10: nopt@1.0.10:
@ -11423,11 +11423,11 @@ snapshots:
optionalDependencies: optionalDependencies:
chokidar: 4.0.3 chokidar: 4.0.3
'@angular-devkit/schematics-cli@19.2.15(@types/node@22.18.5)(chokidar@4.0.3)': '@angular-devkit/schematics-cli@19.2.15(@types/node@22.18.8)(chokidar@4.0.3)':
dependencies: dependencies:
'@angular-devkit/core': 19.2.15(chokidar@4.0.3) '@angular-devkit/core': 19.2.15(chokidar@4.0.3)
'@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3)
'@inquirer/prompts': 7.3.2(@types/node@22.18.5) '@inquirer/prompts': 7.3.2(@types/node@22.18.8)
ansi-colors: 4.1.3 ansi-colors: 4.1.3
symbol-observable: 4.0.0 symbol-observable: 4.0.0
yargs-parser: 21.1.1 yargs-parser: 21.1.1
@ -14019,27 +14019,27 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- '@internationalized/date' - '@internationalized/date'
'@inquirer/checkbox@4.2.1(@types/node@22.18.5)': '@inquirer/checkbox@4.2.1(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/core': 10.1.15(@types/node@22.18.5) '@inquirer/core': 10.1.15(@types/node@22.18.8)
'@inquirer/figures': 1.0.13 '@inquirer/figures': 1.0.13
'@inquirer/type': 3.0.8(@types/node@22.18.5) '@inquirer/type': 3.0.8(@types/node@22.18.8)
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
yoctocolors-cjs: 2.1.2 yoctocolors-cjs: 2.1.2
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/confirm@5.1.15(@types/node@22.18.5)': '@inquirer/confirm@5.1.15(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/core': 10.1.15(@types/node@22.18.5) '@inquirer/core': 10.1.15(@types/node@22.18.8)
'@inquirer/type': 3.0.8(@types/node@22.18.5) '@inquirer/type': 3.0.8(@types/node@22.18.8)
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/core@10.1.15(@types/node@22.18.5)': '@inquirer/core@10.1.15(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/figures': 1.0.13 '@inquirer/figures': 1.0.13
'@inquirer/type': 3.0.8(@types/node@22.18.5) '@inquirer/type': 3.0.8(@types/node@22.18.8)
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
cli-width: 4.1.0 cli-width: 4.1.0
mute-stream: 2.0.0 mute-stream: 2.0.0
@ -14047,115 +14047,115 @@ snapshots:
wrap-ansi: 6.2.0 wrap-ansi: 6.2.0
yoctocolors-cjs: 2.1.2 yoctocolors-cjs: 2.1.2
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/editor@4.2.17(@types/node@22.18.5)': '@inquirer/editor@4.2.17(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/core': 10.1.15(@types/node@22.18.5) '@inquirer/core': 10.1.15(@types/node@22.18.8)
'@inquirer/external-editor': 1.0.2(@types/node@22.18.5) '@inquirer/external-editor': 1.0.2(@types/node@22.18.8)
'@inquirer/type': 3.0.8(@types/node@22.18.5) '@inquirer/type': 3.0.8(@types/node@22.18.8)
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/expand@4.0.17(@types/node@22.18.5)': '@inquirer/expand@4.0.17(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/core': 10.1.15(@types/node@22.18.5) '@inquirer/core': 10.1.15(@types/node@22.18.8)
'@inquirer/type': 3.0.8(@types/node@22.18.5) '@inquirer/type': 3.0.8(@types/node@22.18.8)
yoctocolors-cjs: 2.1.2 yoctocolors-cjs: 2.1.2
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/external-editor@1.0.2(@types/node@22.18.5)': '@inquirer/external-editor@1.0.2(@types/node@22.18.8)':
dependencies: dependencies:
chardet: 2.1.0 chardet: 2.1.0
iconv-lite: 0.7.0 iconv-lite: 0.7.0
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/figures@1.0.13': {} '@inquirer/figures@1.0.13': {}
'@inquirer/input@4.2.1(@types/node@22.18.5)': '@inquirer/input@4.2.1(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/core': 10.1.15(@types/node@22.18.5) '@inquirer/core': 10.1.15(@types/node@22.18.8)
'@inquirer/type': 3.0.8(@types/node@22.18.5) '@inquirer/type': 3.0.8(@types/node@22.18.8)
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/number@3.0.17(@types/node@22.18.5)': '@inquirer/number@3.0.17(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/core': 10.1.15(@types/node@22.18.5) '@inquirer/core': 10.1.15(@types/node@22.18.8)
'@inquirer/type': 3.0.8(@types/node@22.18.5) '@inquirer/type': 3.0.8(@types/node@22.18.8)
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/password@4.0.17(@types/node@22.18.5)': '@inquirer/password@4.0.17(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/core': 10.1.15(@types/node@22.18.5) '@inquirer/core': 10.1.15(@types/node@22.18.8)
'@inquirer/type': 3.0.8(@types/node@22.18.5) '@inquirer/type': 3.0.8(@types/node@22.18.8)
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/prompts@7.3.2(@types/node@22.18.5)': '@inquirer/prompts@7.3.2(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/checkbox': 4.2.1(@types/node@22.18.5) '@inquirer/checkbox': 4.2.1(@types/node@22.18.8)
'@inquirer/confirm': 5.1.15(@types/node@22.18.5) '@inquirer/confirm': 5.1.15(@types/node@22.18.8)
'@inquirer/editor': 4.2.17(@types/node@22.18.5) '@inquirer/editor': 4.2.17(@types/node@22.18.8)
'@inquirer/expand': 4.0.17(@types/node@22.18.5) '@inquirer/expand': 4.0.17(@types/node@22.18.8)
'@inquirer/input': 4.2.1(@types/node@22.18.5) '@inquirer/input': 4.2.1(@types/node@22.18.8)
'@inquirer/number': 3.0.17(@types/node@22.18.5) '@inquirer/number': 3.0.17(@types/node@22.18.8)
'@inquirer/password': 4.0.17(@types/node@22.18.5) '@inquirer/password': 4.0.17(@types/node@22.18.8)
'@inquirer/rawlist': 4.1.5(@types/node@22.18.5) '@inquirer/rawlist': 4.1.5(@types/node@22.18.8)
'@inquirer/search': 3.1.0(@types/node@22.18.5) '@inquirer/search': 3.1.0(@types/node@22.18.8)
'@inquirer/select': 4.3.1(@types/node@22.18.5) '@inquirer/select': 4.3.1(@types/node@22.18.8)
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/prompts@7.8.0(@types/node@22.18.5)': '@inquirer/prompts@7.8.0(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/checkbox': 4.2.1(@types/node@22.18.5) '@inquirer/checkbox': 4.2.1(@types/node@22.18.8)
'@inquirer/confirm': 5.1.15(@types/node@22.18.5) '@inquirer/confirm': 5.1.15(@types/node@22.18.8)
'@inquirer/editor': 4.2.17(@types/node@22.18.5) '@inquirer/editor': 4.2.17(@types/node@22.18.8)
'@inquirer/expand': 4.0.17(@types/node@22.18.5) '@inquirer/expand': 4.0.17(@types/node@22.18.8)
'@inquirer/input': 4.2.1(@types/node@22.18.5) '@inquirer/input': 4.2.1(@types/node@22.18.8)
'@inquirer/number': 3.0.17(@types/node@22.18.5) '@inquirer/number': 3.0.17(@types/node@22.18.8)
'@inquirer/password': 4.0.17(@types/node@22.18.5) '@inquirer/password': 4.0.17(@types/node@22.18.8)
'@inquirer/rawlist': 4.1.5(@types/node@22.18.5) '@inquirer/rawlist': 4.1.5(@types/node@22.18.8)
'@inquirer/search': 3.1.0(@types/node@22.18.5) '@inquirer/search': 3.1.0(@types/node@22.18.8)
'@inquirer/select': 4.3.1(@types/node@22.18.5) '@inquirer/select': 4.3.1(@types/node@22.18.8)
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/rawlist@4.1.5(@types/node@22.18.5)': '@inquirer/rawlist@4.1.5(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/core': 10.1.15(@types/node@22.18.5) '@inquirer/core': 10.1.15(@types/node@22.18.8)
'@inquirer/type': 3.0.8(@types/node@22.18.5) '@inquirer/type': 3.0.8(@types/node@22.18.8)
yoctocolors-cjs: 2.1.2 yoctocolors-cjs: 2.1.2
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/search@3.1.0(@types/node@22.18.5)': '@inquirer/search@3.1.0(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/core': 10.1.15(@types/node@22.18.5) '@inquirer/core': 10.1.15(@types/node@22.18.8)
'@inquirer/figures': 1.0.13 '@inquirer/figures': 1.0.13
'@inquirer/type': 3.0.8(@types/node@22.18.5) '@inquirer/type': 3.0.8(@types/node@22.18.8)
yoctocolors-cjs: 2.1.2 yoctocolors-cjs: 2.1.2
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/select@4.3.1(@types/node@22.18.5)': '@inquirer/select@4.3.1(@types/node@22.18.8)':
dependencies: dependencies:
'@inquirer/core': 10.1.15(@types/node@22.18.5) '@inquirer/core': 10.1.15(@types/node@22.18.8)
'@inquirer/figures': 1.0.13 '@inquirer/figures': 1.0.13
'@inquirer/type': 3.0.8(@types/node@22.18.5) '@inquirer/type': 3.0.8(@types/node@22.18.8)
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
yoctocolors-cjs: 2.1.2 yoctocolors-cjs: 2.1.2
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@inquirer/type@3.0.8(@types/node@22.18.5)': '@inquirer/type@3.0.8(@types/node@22.18.8)':
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@internationalized/date@3.8.2': '@internationalized/date@3.8.2':
dependencies: dependencies:
@ -14193,7 +14193,7 @@ snapshots:
'@jest/schemas': 29.6.3 '@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4 '@types/istanbul-reports': 3.0.4
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/yargs': 17.0.33 '@types/yargs': 17.0.33
chalk: 4.1.2 chalk: 4.1.2
@ -14425,12 +14425,12 @@ snapshots:
bullmq: 5.58.5 bullmq: 5.58.5
tslib: 2.8.1 tslib: 2.8.1
'@nestjs/cli@11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.5)': '@nestjs/cli@11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.8)':
dependencies: dependencies:
'@angular-devkit/core': 19.2.15(chokidar@4.0.3) '@angular-devkit/core': 19.2.15(chokidar@4.0.3)
'@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3)
'@angular-devkit/schematics-cli': 19.2.15(@types/node@22.18.5)(chokidar@4.0.3) '@angular-devkit/schematics-cli': 19.2.15(@types/node@22.18.8)(chokidar@4.0.3)
'@inquirer/prompts': 7.8.0(@types/node@22.18.5) '@inquirer/prompts': 7.8.0(@types/node@22.18.8)
'@nestjs/schematics': 11.0.7(chokidar@4.0.3)(typescript@5.8.3) '@nestjs/schematics': 11.0.7(chokidar@4.0.3)(typescript@5.8.3)
ansis: 4.1.0 ansis: 4.1.0
chokidar: 4.0.3 chokidar: 4.0.3
@ -15829,7 +15829,7 @@ snapshots:
'@types/accepts@1.3.7': '@types/accepts@1.3.7':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/archiver@6.0.3': '@types/archiver@6.0.3':
dependencies: dependencies:
@ -15841,16 +15841,16 @@ snapshots:
'@types/bcrypt@6.0.0': '@types/bcrypt@6.0.0':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/body-parser@1.19.6': '@types/body-parser@1.19.6':
dependencies: dependencies:
'@types/connect': 3.4.38 '@types/connect': 3.4.38
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/bonjour@3.5.13': '@types/bonjour@3.5.13':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/braces@3.0.5': {} '@types/braces@3.0.5': {}
@ -15871,21 +15871,21 @@ snapshots:
'@types/cli-progress@3.11.6': '@types/cli-progress@3.11.6':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/compression@1.8.1': '@types/compression@1.8.1':
dependencies: dependencies:
'@types/express': 5.0.3 '@types/express': 5.0.3
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/connect-history-api-fallback@1.5.4': '@types/connect-history-api-fallback@1.5.4':
dependencies: dependencies:
'@types/express-serve-static-core': 5.0.6 '@types/express-serve-static-core': 5.0.6
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/connect@3.4.38': '@types/connect@3.4.38':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/content-disposition@0.5.9': {} '@types/content-disposition@0.5.9': {}
@ -15902,11 +15902,11 @@ snapshots:
'@types/connect': 3.4.38 '@types/connect': 3.4.38
'@types/express': 5.0.3 '@types/express': 5.0.3
'@types/keygrip': 1.0.6 '@types/keygrip': 1.0.6
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/cors@2.8.19': '@types/cors@2.8.19':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/debug@4.1.12': '@types/debug@4.1.12':
dependencies: dependencies:
@ -15916,13 +15916,13 @@ snapshots:
'@types/docker-modem@3.0.6': '@types/docker-modem@3.0.6':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/ssh2': 1.15.5 '@types/ssh2': 1.15.5
'@types/dockerode@3.3.42': '@types/dockerode@3.3.42':
dependencies: dependencies:
'@types/docker-modem': 3.0.6 '@types/docker-modem': 3.0.6
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/ssh2': 1.15.5 '@types/ssh2': 1.15.5
'@types/dom-to-image@2.6.7': {} '@types/dom-to-image@2.6.7': {}
@ -15945,14 +15945,14 @@ snapshots:
'@types/express-serve-static-core@4.19.6': '@types/express-serve-static-core@4.19.6':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/qs': 6.14.0 '@types/qs': 6.14.0
'@types/range-parser': 1.2.7 '@types/range-parser': 1.2.7
'@types/send': 0.17.5 '@types/send': 0.17.5
'@types/express-serve-static-core@5.0.6': '@types/express-serve-static-core@5.0.6':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/qs': 6.14.0 '@types/qs': 6.14.0
'@types/range-parser': 1.2.7 '@types/range-parser': 1.2.7
'@types/send': 0.17.5 '@types/send': 0.17.5
@ -15978,7 +15978,7 @@ snapshots:
'@types/fluent-ffmpeg@2.1.27': '@types/fluent-ffmpeg@2.1.27':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/geojson-vt@3.2.5': '@types/geojson-vt@3.2.5':
dependencies: dependencies:
@ -16010,7 +16010,7 @@ snapshots:
'@types/http-proxy@1.17.16': '@types/http-proxy@1.17.16':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/inquirer@8.2.11': '@types/inquirer@8.2.11':
dependencies: dependencies:
@ -16048,7 +16048,7 @@ snapshots:
'@types/http-errors': 2.0.5 '@types/http-errors': 2.0.5
'@types/keygrip': 1.0.6 '@types/keygrip': 1.0.6
'@types/koa-compose': 3.2.8 '@types/koa-compose': 3.2.8
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/leaflet@1.9.20': '@types/leaflet@1.9.20':
dependencies: dependencies:
@ -16080,7 +16080,7 @@ snapshots:
'@types/mock-fs@4.13.4': '@types/mock-fs@4.13.4':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/ms@2.1.0': {} '@types/ms@2.1.0': {}
@ -16090,7 +16090,7 @@ snapshots:
'@types/node-forge@1.3.11': '@types/node-forge@1.3.11':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/node@17.0.45': {} '@types/node@17.0.45': {}
@ -16102,7 +16102,7 @@ snapshots:
dependencies: dependencies:
undici-types: 6.21.0 undici-types: 6.21.0
'@types/node@22.18.5': '@types/node@22.18.8':
dependencies: dependencies:
undici-types: 6.21.0 undici-types: 6.21.0
@ -16114,7 +16114,7 @@ snapshots:
'@types/nodemailer@7.0.1': '@types/nodemailer@7.0.1':
dependencies: dependencies:
'@aws-sdk/client-sesv2': 3.890.0 '@aws-sdk/client-sesv2': 3.890.0
'@types/node': 22.18.5 '@types/node': 22.18.8
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
@ -16122,7 +16122,7 @@ snapshots:
dependencies: dependencies:
'@types/keygrip': 1.0.6 '@types/keygrip': 1.0.6
'@types/koa': 3.0.0 '@types/koa': 3.0.0
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/parse5@5.0.3': {} '@types/parse5@5.0.3': {}
@ -16132,7 +16132,7 @@ snapshots:
'@types/pg@8.15.5': '@types/pg@8.15.5':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
pg-protocol: 1.10.3 pg-protocol: 1.10.3
pg-types: 2.2.0 pg-types: 2.2.0
@ -16140,13 +16140,13 @@ snapshots:
'@types/pngjs@6.0.5': '@types/pngjs@6.0.5':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/prismjs@1.26.5': {} '@types/prismjs@1.26.5': {}
'@types/qrcode@1.5.5': '@types/qrcode@1.5.5':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/qs@6.14.0': {} '@types/qs@6.14.0': {}
@ -16175,7 +16175,7 @@ snapshots:
'@types/readdir-glob@1.1.5': '@types/readdir-glob@1.1.5':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/retry@0.12.0': {} '@types/retry@0.12.0': {}
@ -16185,14 +16185,14 @@ snapshots:
'@types/sax@1.2.7': '@types/sax@1.2.7':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/semver@7.7.1': {} '@types/semver@7.7.1': {}
'@types/send@0.17.5': '@types/send@0.17.5':
dependencies: dependencies:
'@types/mime': 1.3.5 '@types/mime': 1.3.5
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/serve-index@1.9.4': '@types/serve-index@1.9.4':
dependencies: dependencies:
@ -16201,20 +16201,20 @@ snapshots:
'@types/serve-static@1.15.8': '@types/serve-static@1.15.8':
dependencies: dependencies:
'@types/http-errors': 2.0.5 '@types/http-errors': 2.0.5
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/send': 0.17.5 '@types/send': 0.17.5
'@types/sockjs@0.3.36': '@types/sockjs@0.3.36':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/ssh2-streams@0.1.12': '@types/ssh2-streams@0.1.12':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/ssh2@0.5.52': '@types/ssh2@0.5.52':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/ssh2-streams': 0.1.12 '@types/ssh2-streams': 0.1.12
'@types/ssh2@1.15.5': '@types/ssh2@1.15.5':
@ -16225,7 +16225,7 @@ snapshots:
dependencies: dependencies:
'@types/cookiejar': 2.1.5 '@types/cookiejar': 2.1.5
'@types/methods': 1.1.4 '@types/methods': 1.1.4
'@types/node': 22.18.5 '@types/node': 22.18.8
form-data: 4.0.4 form-data: 4.0.4
'@types/supercluster@7.1.3': '@types/supercluster@7.1.3':
@ -16239,7 +16239,7 @@ snapshots:
'@types/through@0.0.33': '@types/through@0.0.33':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/ua-parser-js@0.7.39': {} '@types/ua-parser-js@0.7.39': {}
@ -16255,7 +16255,7 @@ snapshots:
'@types/ws@8.18.1': '@types/ws@8.18.1':
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
'@types/yargs-parser@21.0.3': {} '@types/yargs-parser@21.0.3': {}
@ -16358,7 +16358,7 @@ snapshots:
'@ungap/structured-clone@1.3.0': {} '@ungap/structured-clone@1.3.0': {}
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))':
dependencies: dependencies:
'@ampproject/remapping': 2.3.0 '@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2 '@bcoe/v8-coverage': 1.0.2
@ -16373,7 +16373,7 @@ snapshots:
std-env: 3.9.0 std-env: 3.9.0
test-exclude: 7.0.1 test-exclude: 7.0.1
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -16404,13 +16404,13 @@ snapshots:
chai: 5.2.0 chai: 5.2.0
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
'@vitest/mocker@3.2.4(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))':
dependencies: dependencies:
'@vitest/spy': 3.2.4 '@vitest/spy': 3.2.4
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.19 magic-string: 0.30.19
optionalDependencies: optionalDependencies:
vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
'@vitest/mocker@3.2.4(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))':
dependencies: dependencies:
@ -17974,7 +17974,7 @@ snapshots:
engine.io@6.6.4: engine.io@6.6.4:
dependencies: dependencies:
'@types/cors': 2.8.19 '@types/cors': 2.8.19
'@types/node': 22.18.5 '@types/node': 22.18.8
accepts: 1.3.8 accepts: 1.3.8
base64id: 2.0.0 base64id: 2.0.0
cookie: 0.7.2 cookie: 0.7.2
@ -18364,7 +18364,7 @@ snapshots:
eval@0.1.8: eval@0.1.8:
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
require-like: 0.1.2 require-like: 0.1.2
event-emitter@0.3.5: event-emitter@0.3.5:
@ -19343,9 +19343,9 @@ snapshots:
inline-style-parser@0.2.4: {} inline-style-parser@0.2.4: {}
inquirer@8.2.7(@types/node@22.18.5): inquirer@8.2.7(@types/node@22.18.8):
dependencies: dependencies:
'@inquirer/external-editor': 1.0.2(@types/node@22.18.5) '@inquirer/external-editor': 1.0.2(@types/node@22.18.8)
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
chalk: 4.1.2 chalk: 4.1.2
cli-cursor: 3.1.0 cli-cursor: 3.1.0
@ -19549,7 +19549,7 @@ snapshots:
jest-util@29.7.0: jest-util@29.7.0:
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 22.18.5 '@types/node': 22.18.8
chalk: 4.1.2 chalk: 4.1.2
ci-info: 3.9.0 ci-info: 3.9.0
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@ -19557,13 +19557,13 @@ snapshots:
jest-worker@27.5.1: jest-worker@27.5.1:
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
merge-stream: 2.0.0 merge-stream: 2.0.0
supports-color: 8.1.1 supports-color: 8.1.1
jest-worker@29.7.0: jest-worker@29.7.0:
dependencies: dependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
jest-util: 29.7.0 jest-util: 29.7.0
merge-stream: 2.0.0 merge-stream: 2.0.0
supports-color: 8.1.1 supports-color: 8.1.1
@ -20774,7 +20774,7 @@ snapshots:
neo-async@2.6.2: {} neo-async@2.6.2: {}
nest-commander@3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.5)(typescript@5.9.2): nest-commander@3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.8)(typescript@5.9.2):
dependencies: dependencies:
'@fig/complete-commander': 3.2.0(commander@11.1.0) '@fig/complete-commander': 3.2.0(commander@11.1.0)
'@golevelup/nestjs-discovery': 4.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) '@golevelup/nestjs-discovery': 4.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)
@ -20783,7 +20783,7 @@ snapshots:
'@types/inquirer': 8.2.11 '@types/inquirer': 8.2.11
commander: 11.1.0 commander: 11.1.0
cosmiconfig: 8.3.6(typescript@5.9.2) cosmiconfig: 8.3.6(typescript@5.9.2)
inquirer: 8.2.7(@types/node@22.18.5) inquirer: 8.2.7(@types/node@22.18.8)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- typescript - typescript
@ -20873,7 +20873,7 @@ snapshots:
node-releases@2.0.19: {} node-releases@2.0.19: {}
nodemailer@7.0.6: {} nodemailer@7.0.7: {}
nopt@1.0.10: nopt@1.0.10:
dependencies: dependencies:
@ -21871,7 +21871,7 @@ snapshots:
'@protobufjs/path': 1.1.2 '@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0 '@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0 '@protobufjs/utf8': 1.1.0
'@types/node': 22.18.5 '@types/node': 22.18.8
long: 5.3.2 long: 5.3.2
protocol-buffers-schema@3.6.0: {} protocol-buffers-schema@3.6.0: {}
@ -23737,13 +23737,13 @@ snapshots:
- rollup - rollup
- supports-color - supports-color
vite-node@3.2.4(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): vite-node@3.2.4(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
dependencies: dependencies:
cac: 6.7.14 cac: 6.7.14
debug: 4.4.3 debug: 4.4.3
es-module-lexer: 1.7.0 es-module-lexer: 1.7.0
pathe: 2.0.3 pathe: 2.0.3
vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- jiti - jiti
@ -23779,18 +23779,18 @@ snapshots:
- tsx - tsx
- yaml - yaml
vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)):
dependencies: dependencies:
debug: 4.4.3 debug: 4.4.3
globrex: 0.1.2 globrex: 0.1.2
tsconfck: 3.1.6(typescript@5.9.2) tsconfck: 3.1.6(typescript@5.9.2)
optionalDependencies: optionalDependencies:
vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
- typescript - typescript
vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
dependencies: dependencies:
esbuild: 0.25.9 esbuild: 0.25.9
fdir: 6.5.0(picomatch@4.0.3) fdir: 6.5.0(picomatch@4.0.3)
@ -23799,7 +23799,7 @@ snapshots:
rollup: 4.50.1 rollup: 4.50.1
tinyglobby: 0.2.15 tinyglobby: 0.2.15
optionalDependencies: optionalDependencies:
'@types/node': 22.18.5 '@types/node': 22.18.8
fsevents: 2.3.3 fsevents: 2.3.3
jiti: 2.5.1 jiti: 2.5.1
lightningcss: 1.30.1 lightningcss: 1.30.1
@ -23826,15 +23826,15 @@ snapshots:
optionalDependencies: optionalDependencies:
vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)):
dependencies: dependencies:
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
dependencies: dependencies:
'@types/chai': 5.2.2 '@types/chai': 5.2.2
'@vitest/expect': 3.2.4 '@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
'@vitest/pretty-format': 3.2.4 '@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4 '@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4 '@vitest/snapshot': 3.2.4
@ -23852,12 +23852,12 @@ snapshots:
tinyglobby: 0.2.15 tinyglobby: 0.2.15
tinypool: 1.1.1 tinypool: 1.1.1
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
vite-node: 3.2.4(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/debug': 4.1.12 '@types/debug': 4.1.12
'@types/node': 22.18.5 '@types/node': 22.18.8
happy-dom: 18.0.1 happy-dom: 18.0.1
jsdom: 26.1.0(canvas@2.11.2(encoding@0.1.13)) jsdom: 26.1.0(canvas@2.11.2(encoding@0.1.13))
transitivePeerDependencies: transitivePeerDependencies:
@ -23874,11 +23874,11 @@ snapshots:
- tsx - tsx
- yaml - yaml
vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.5)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1):
dependencies: dependencies:
'@types/chai': 5.2.2 '@types/chai': 5.2.2
'@vitest/expect': 3.2.4 '@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))
'@vitest/pretty-format': 3.2.4 '@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4 '@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4 '@vitest/snapshot': 3.2.4
@ -23896,12 +23896,12 @@ snapshots:
tinyglobby: 0.2.15 tinyglobby: 0.2.15
tinypool: 1.1.1 tinypool: 1.1.1
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vite: 7.1.5(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
vite-node: 3.2.4(@types/node@22.18.5)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/debug': 4.1.12 '@types/debug': 4.1.12
'@types/node': 22.18.5 '@types/node': 22.18.8
happy-dom: 18.0.1 happy-dom: 18.0.1
jsdom: 26.1.0(canvas@2.11.2) jsdom: 26.1.0(canvas@2.11.2)
transitivePeerDependencies: transitivePeerDependencies:

View file

@ -1,35 +1,50 @@
FROM ghcr.io/immich-app/base-server-dev:202509210934@sha256:b5ce2d7eaf379d4cf15efd4bab180d8afc8a80d20b36c9800f4091aca6ae267e AS builder FROM ghcr.io/immich-app/base-server-dev:202509210934@sha256:b5ce2d7eaf379d4cf15efd4bab180d8afc8a80d20b36c9800f4091aca6ae267e AS builder
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
CI=1 \ CI=1 \
COREPACK_HOME=/tmp COREPACK_HOME=/tmp \
PNPM_HOME=/buildcache/pnpm-store \
PATH="/buildcache/pnpm-store:$PATH"
RUN npm install --global corepack@latest && \ RUN npm install --global corepack@latest && \
corepack enable pnpm corepack enable pnpm && \
pnpm config set store-dir "$PNPM_HOME"
FROM builder AS server FROM builder AS server
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY ./package* ./pnpm* .pnpmfile.cjs ./
COPY ./server ./server/ COPY ./server ./server/
RUN SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile build && \ RUN --mount=type=cache,id=pnpm-server,target=/buildcache/pnpm-store \
--mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile build && \
SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned
FROM builder AS web FROM builder AS web
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY ./package* ./pnpm* .pnpmfile.cjs ./
COPY ./web ./web/ COPY ./web ./web/
COPY ./i18n ./i18n/ COPY ./i18n ./i18n/
COPY ./open-api ./open-api/ COPY ./open-api ./open-api/
RUN SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install && \ RUN --mount=type=cache,id=pnpm-web,target=/buildcache/pnpm-store \
--mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install && \
pnpm --filter @immich/sdk --filter immich-web build pnpm --filter @immich/sdk --filter immich-web build
FROM builder AS cli FROM builder AS cli
COPY ./package* ./pnpm* .pnpmfile.cjs ./
COPY ./cli ./cli/ COPY ./cli ./cli/
COPY ./open-api ./open-api/ COPY ./open-api ./open-api/
RUN pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install && \ RUN --mount=type=cache,id=pnpm-cli,target=/buildcache/pnpm-store \
--mount=type=bind,source=package.json,target=package.json \
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install && \
pnpm --filter @immich/sdk --filter @immich/cli build && \ pnpm --filter @immich/sdk --filter @immich/cli build && \
pnpm --filter @immich/cli --prod --no-optional deploy /output/cli-pruned pnpm --filter @immich/cli --prod --no-optional deploy /output/cli-pruned

View file

@ -129,7 +129,7 @@
"@types/luxon": "^3.6.2", "@types/luxon": "^3.6.2",
"@types/mock-fs": "^4.13.1", "@types/mock-fs": "^4.13.1",
"@types/multer": "^2.0.0", "@types/multer": "^2.0.0",
"@types/node": "^22.18.1", "@types/node": "^22.18.8",
"@types/nodemailer": "^7.0.0", "@types/nodemailer": "^7.0.0",
"@types/picomatch": "^4.0.0", "@types/picomatch": "^4.0.0",
"@types/pngjs": "^6.0.5", "@types/pngjs": "^6.0.5",

View file

@ -82,11 +82,6 @@ export class DayGroup {
return this.viewerAssets[0]?.asset; return this.viewerAssets[0]?.asset;
} }
getRandomAsset() {
const random = Math.floor(Math.random() * this.viewerAssets.length);
return this.viewerAssets[random];
}
*assetsIterator(options: { startAsset?: TimelineAsset; direction?: Direction } = {}) { *assetsIterator(options: { startAsset?: TimelineAsset; direction?: Direction } = {}) {
const isEarlier = (options?.direction ?? 'earlier') === 'earlier'; const isEarlier = (options?.direction ?? 'earlier') === 'earlier';
let assetIndex = options?.startAsset let assetIndex = options?.startAsset

View file

@ -233,15 +233,6 @@ export class MonthGroup {
addContext.changedDayGroups.add(dayGroup); addContext.changedDayGroups.add(dayGroup);
} }
getRandomDayGroup() {
const random = Math.floor(Math.random() * this.dayGroups.length);
return this.dayGroups[random];
}
getRandomAsset() {
return this.getRandomDayGroup()?.getRandomAsset()?.asset;
}
get viewId() { get viewId() {
const { year, month } = this.yearMonth; const { year, month } = this.yearMonth;
return year + '-' + month; return year + '-' + month;

View file

@ -580,4 +580,60 @@ describe('TimelineManager', () => {
expect(timelineManager.getMonthGroupByAssetId(assetOne.id)?.yearMonth.month).toEqual(1); expect(timelineManager.getMonthGroupByAssetId(assetOne.id)?.yearMonth.month).toEqual(1);
}); });
}); });
describe('getRandomAsset', () => {
let timelineManager: TimelineManager;
const bucketAssets: Record<string, TimelineAsset[]> = {
'2024-03-01T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
deriveLocalDateTimeFromFileCreatedAt({
...asset,
fileCreatedAt: fromISODateTimeUTCToObject('2024-03-01T00:00:00.000Z'),
}),
),
'2024-02-01T00:00:00.000Z': timelineAssetFactory.buildList(10).map((asset, idx) =>
deriveLocalDateTimeFromFileCreatedAt({
...asset,
// here we make sure that not all assets are on the first day of the month
fileCreatedAt: fromISODateTimeUTCToObject(`2024-02-0${idx < 7 ? 1 : 2}T00:00:00.000Z`),
}),
),
'2024-01-01T00:00:00.000Z': timelineAssetFactory.buildList(3).map((asset) =>
deriveLocalDateTimeFromFileCreatedAt({
...asset,
fileCreatedAt: fromISODateTimeUTCToObject('2024-01-01T00:00:00.000Z'),
}),
),
};
const bucketAssetsResponse: Record<string, TimeBucketAssetResponseDto> = Object.fromEntries(
Object.entries(bucketAssets).map(([key, assets]) => [key, toResponseDto(...assets)]),
);
beforeEach(async () => {
timelineManager = new TimelineManager();
sdkMock.getTimeBuckets.mockResolvedValue([
{ count: 1, timeBucket: '2024-03-01' },
{ count: 10, timeBucket: '2024-02-01' },
{ count: 3, timeBucket: '2024-01-01' },
]);
sdkMock.getTimeBucket.mockImplementation(({ timeBucket }) => Promise.resolve(bucketAssetsResponse[timeBucket]));
await timelineManager.updateViewport({ width: 1588, height: 0 });
});
it('gets all assets once', async () => {
const assetCount = timelineManager.assetCount;
expect(assetCount).toBe(14);
const discoveredAssets: Set<string> = new Set();
for (let idx = 0; idx < assetCount; idx++) {
const asset = await timelineManager.getRandomAsset(idx);
expect(asset).toBeDefined();
const id = asset!.id;
expect(discoveredAssets.has(id)).toBeFalsy();
discoveredAssets.add(id);
}
expect(discoveredAssets.size).toBe(assetCount);
});
});
}); });

View file

@ -451,16 +451,42 @@ export class TimelineManager {
return monthGroupInfo?.monthGroup; return monthGroupInfo?.monthGroup;
} }
async getRandomMonthGroup() { // note: the `index` input is expected to be in the range [0, assetCount). This
const random = Math.floor(Math.random() * this.months.length); // value can be passed to make the method deterministic, which is mainly useful
const month = this.months[random]; // for testing.
await this.loadMonthGroup(month.yearMonth, { cancelable: false }); async getRandomAsset(index?: number): Promise<TimelineAsset | undefined> {
return month; const randomAssetIndex = index ?? Math.floor(Math.random() * this.assetCount);
}
async getRandomAsset() { let accumulatedCount = 0;
const month = await this.getRandomMonthGroup();
return month?.getRandomAsset(); let randomMonth: MonthGroup | undefined = undefined;
for (const month of this.months) {
if (randomAssetIndex < accumulatedCount + month.assetsCount) {
randomMonth = month;
break;
}
accumulatedCount += month.assetsCount;
}
if (!randomMonth) {
return;
}
await this.loadMonthGroup(randomMonth.yearMonth, { cancelable: false });
let randomDay: DayGroup | undefined = undefined;
for (const day of randomMonth.dayGroups) {
if (randomAssetIndex < accumulatedCount + day.viewerAssets.length) {
randomDay = day;
break;
}
accumulatedCount += day.viewerAssets.length;
}
if (!randomDay) {
return;
}
return randomDay.viewerAssets[randomAssetIndex - accumulatedCount].asset;
} }
updateAssetOperation(ids: string[], operation: AssetOperation) { updateAssetOperation(ids: string[], operation: AssetOperation) {