diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 13c2df740..c3c3fea01 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -58,7 +58,7 @@ jobs: uses: docker/setup-qemu-action@v3.0.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.1.0 + uses: docker/setup-buildx-action@v3.2.0 - name: Login to GitHub Container Registry uses: docker/login-action@v3 @@ -87,7 +87,7 @@ jobs: type=raw,value=latest,enable=${{ github.event_name == 'workflow_dispatch' }} - name: Build and push image - uses: docker/build-push-action@v5.2.0 + uses: docker/build-push-action@v5.3.0 with: file: cli/Dockerfile platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5b92e44ff..fd7904415 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -66,7 +66,7 @@ jobs: uses: docker/setup-qemu-action@v3.0.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.1.0 + uses: docker/setup-buildx-action@v3.2.0 # Workaround to fix error: # failed to push: failed to copy: io: read/write on closed pipe # See https://github.com/docker/build-push-action/issues/761 @@ -121,7 +121,7 @@ jobs: fi - name: Build and push image - uses: docker/build-push-action@v5.2.0 + uses: docker/build-push-action@v5.3.0 with: context: ${{ matrix.context }} file: ${{ matrix.file }} diff --git a/mobile/lib/modules/album/ui/album_viewer_appbar.dart b/mobile/lib/modules/album/ui/album_viewer_appbar.dart index 996e25d0f..b1a8d4c54 100644 --- a/mobile/lib/modules/album/ui/album_viewer_appbar.dart +++ b/mobile/lib/modules/album/ui/album_viewer_appbar.dart @@ -211,8 +211,8 @@ class AlbumViewerAppbar extends HookConsumerWidget return SafeArea( child: Padding( padding: const EdgeInsets.only(top: 24.0), - child: Column( - mainAxisSize: MainAxisSize.min, + child: ListView( + shrinkWrap: true, children: [ ...buildBottomSheetActions(), if (onAddPhotos != null) ...commonActions, diff --git a/mobile/lib/modules/album/views/album_options_part.dart b/mobile/lib/modules/album/views/album_options_part.dart index 832db7cb8..2f831e430 100644 --- a/mobile/lib/modules/album/views/album_options_part.dart +++ b/mobile/lib/modules/album/views/album_options_part.dart @@ -142,6 +142,7 @@ class AlbumOptionsPage extends HookConsumerWidget { buildSharedUsersList() { return ListView.builder( + primary: false, shrinkWrap: true, itemCount: sharedUsers.value.length, itemBuilder: (context, index) { @@ -188,9 +189,7 @@ class AlbumOptionsPage extends HookConsumerWidget { centerTitle: true, title: Text("translated_text_options".tr()), ), - body: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + body: ListView( children: [ if (isOwner && album.shared) SwitchListTile.adaptive( diff --git a/mobile/lib/modules/album/views/select_additional_user_for_sharing_page.dart b/mobile/lib/modules/album/views/select_additional_user_for_sharing_page.dart index cb17e6638..0a883791a 100644 --- a/mobile/lib/modules/album/views/select_additional_user_for_sharing_page.dart +++ b/mobile/lib/modules/album/views/select_additional_user_for_sharing_page.dart @@ -63,8 +63,7 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget { ), ); } - return Column( - crossAxisAlignment: CrossAxisAlignment.start, + return ListView( children: [ Wrap( children: [...usersChip], @@ -81,6 +80,7 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget { ), ), ListView.builder( + primary: false, shrinkWrap: true, itemBuilder: ((context, index) { return ListTile( diff --git a/mobile/lib/modules/album/views/select_user_for_sharing_page.dart b/mobile/lib/modules/album/views/select_user_for_sharing_page.dart index 61550e04f..763f53c65 100644 --- a/mobile/lib/modules/album/views/select_user_for_sharing_page.dart +++ b/mobile/lib/modules/album/views/select_user_for_sharing_page.dart @@ -90,8 +90,7 @@ class SelectUserForSharingPage extends HookConsumerWidget { ), ); } - return Column( - crossAxisAlignment: CrossAxisAlignment.start, + return ListView( children: [ Wrap( children: [...usersChip], @@ -108,6 +107,7 @@ class SelectUserForSharingPage extends HookConsumerWidget { ).tr(), ), ListView.builder( + primary: false, shrinkWrap: true, itemBuilder: ((context, index) { return ListTile( diff --git a/mobile/lib/modules/shared_link/views/shared_link_edit_page.dart b/mobile/lib/modules/shared_link/views/shared_link_edit_page.dart index 288fdae33..b6d892f92 100644 --- a/mobile/lib/modules/shared_link/views/shared_link_edit_page.dart +++ b/mobile/lib/modules/shared_link/views/shared_link_edit_page.dart @@ -428,10 +428,8 @@ class SharedLinkEditPage extends HookConsumerWidget { leading: const CloseButton(), centerTitle: false, ), - resizeToAvoidBottomInset: false, body: SafeArea( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: ListView( children: [ Padding( padding: const EdgeInsets.all(padding), @@ -487,7 +485,10 @@ class SharedLinkEditPage extends HookConsumerWidget { Align( alignment: Alignment.bottomRight, child: Padding( - padding: const EdgeInsets.only(right: padding + 10), + padding: const EdgeInsets.only( + right: padding + 10, + bottom: padding, + ), child: ElevatedButton( onPressed: existingLink != null ? handleEditLink : handleNewLink, @@ -508,6 +509,7 @@ class SharedLinkEditPage extends HookConsumerWidget { padding: const EdgeInsets.only( left: padding, right: padding, + bottom: padding, ), child: buildNewLinkField(), ), diff --git a/server/src/domain/library/library.service.ts b/server/src/domain/library/library.service.ts index 2699f996a..3007d1674 100644 --- a/server/src/domain/library/library.service.ts +++ b/server/src/domain/library/library.service.ts @@ -299,17 +299,26 @@ export class LibraryService extends EventEmitter { } private async scanAssets(libraryId: string, assetPaths: string[], ownerId: string, force = false) { - await this.jobRepository.queueAll( - assetPaths.map((assetPath) => ({ - name: JobName.LIBRARY_SCAN_ASSET, - data: { - id: libraryId, - assetPath: path.normalize(assetPath), - ownerId, - force, - }, - })), - ); + this.logger.verbose(`Queuing refresh of ${assetPaths.length} asset(s)`); + + // We perform this in batches to save on memory when performing large refreshes (greater than 1M assets) + const batchSize = 5000; + for (let i = 0; i < assetPaths.length; i += batchSize) { + const batch = assetPaths.slice(i, i + batchSize); + await this.jobRepository.queueAll( + batch.map((assetPath) => ({ + name: JobName.LIBRARY_SCAN_ASSET, + data: { + id: libraryId, + assetPath: assetPath, + ownerId, + force, + }, + })), + ); + } + + this.logger.debug('Asset refresh queue completed'); } private async validateImportPath(importPath: string): Promise { @@ -664,7 +673,7 @@ export class LibraryService extends EventEmitter { return false; } - this.logger.verbose(`Refreshing library: ${job.id}`); + this.logger.log(`Refreshing library: ${job.id}`); const pathValidation = await Promise.all( library.importPaths.map(async (importPath) => await this.validateImportPath(importPath)),