You've already forked immich
							
							
				mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 00:18:28 +02:00 
			
		
		
		
	Modify Album API endpoint to return a count attribute instead of a full assets array (#454)
* Change API to return assets count and change web behavior accordingly * Refactor assets.length * Explicitly declare type of assetCount so Dart SDK understand it * Finished refactoring on mobile
This commit is contained in:
		| @@ -61,13 +61,13 @@ class AlbumThumbnailCard extends StatelessWidget { | ||||
|               mainAxisSize: MainAxisSize.min, | ||||
|               children: [ | ||||
|                 Text( | ||||
|                   album.assets.length == 1 | ||||
|                   album.assetCount == 1 | ||||
|                       ? 'album_thumbnail_card_item' | ||||
|                       : 'album_thumbnail_card_items', | ||||
|                   style: const TextStyle( | ||||
|                     fontSize: 10, | ||||
|                   ), | ||||
|                 ).tr(args: ['${album.assets.length }']), | ||||
|                 ).tr(args: ['${album.assetCount}']), | ||||
|                 if (album.shared) | ||||
|                   const Text( | ||||
|                     'album_thumbnail_card_shared', | ||||
|   | ||||
| @@ -203,7 +203,7 @@ class AlbumViewerPage extends HookConsumerWidget { | ||||
|                   assetList: albumInfo.assets, | ||||
|                 ); | ||||
|               }, | ||||
|               childCount: albumInfo.assets.length, | ||||
|               childCount: albumInfo.assetCount, | ||||
|             ), | ||||
|           ), | ||||
|         ); | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import 'package:openapi/api.dart'; | ||||
| ## Properties | ||||
| Name | Type | Description | Notes | ||||
| ------------ | ------------- | ------------- | ------------- | ||||
| **assetCount** | **int** |  |  | ||||
| **id** | **String** |  |  | ||||
| **ownerId** | **String** |  |  | ||||
| **albumName** | **String** |  |  | ||||
|   | ||||
| @@ -13,6 +13,7 @@ part of openapi.api; | ||||
| class AlbumResponseDto { | ||||
|   /// Returns a new [AlbumResponseDto] instance. | ||||
|   AlbumResponseDto({ | ||||
|     required this.assetCount, | ||||
|     required this.id, | ||||
|     required this.ownerId, | ||||
|     required this.albumName, | ||||
| @@ -23,6 +24,8 @@ class AlbumResponseDto { | ||||
|     this.assets = const [], | ||||
|   }); | ||||
|  | ||||
|   int assetCount; | ||||
|  | ||||
|   String id; | ||||
|  | ||||
|   String ownerId; | ||||
| @@ -41,6 +44,7 @@ class AlbumResponseDto { | ||||
|  | ||||
|   @override | ||||
|   bool operator ==(Object other) => identical(this, other) || other is AlbumResponseDto && | ||||
|      other.assetCount == assetCount && | ||||
|      other.id == id && | ||||
|      other.ownerId == ownerId && | ||||
|      other.albumName == albumName && | ||||
| @@ -53,6 +57,7 @@ class AlbumResponseDto { | ||||
|   @override | ||||
|   int get hashCode => | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (assetCount.hashCode) + | ||||
|     (id.hashCode) + | ||||
|     (ownerId.hashCode) + | ||||
|     (albumName.hashCode) + | ||||
| @@ -63,10 +68,11 @@ class AlbumResponseDto { | ||||
|     (assets.hashCode); | ||||
|  | ||||
|   @override | ||||
|   String toString() => 'AlbumResponseDto[id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets]'; | ||||
|   String toString() => 'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets]'; | ||||
|  | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final _json = <String, dynamic>{}; | ||||
|       _json[r'assetCount'] = assetCount; | ||||
|       _json[r'id'] = id; | ||||
|       _json[r'ownerId'] = ownerId; | ||||
|       _json[r'albumName'] = albumName; | ||||
| @@ -101,6 +107,7 @@ class AlbumResponseDto { | ||||
|       }()); | ||||
|  | ||||
|       return AlbumResponseDto( | ||||
|         assetCount: mapValueOfType<int>(json, r'assetCount')!, | ||||
|         id: mapValueOfType<String>(json, r'id')!, | ||||
|         ownerId: mapValueOfType<String>(json, r'ownerId')!, | ||||
|         albumName: mapValueOfType<String>(json, r'albumName')!, | ||||
| @@ -158,6 +165,7 @@ class AlbumResponseDto { | ||||
|  | ||||
|   /// The list of required keys that must be present in a JSON. | ||||
|   static const requiredKeys = <String>{ | ||||
|     'assetCount', | ||||
|     'id', | ||||
|     'ownerId', | ||||
|     'albumName', | ||||
|   | ||||
| @@ -76,72 +76,69 @@ class AssetResponseDto { | ||||
|   SmartInfoResponseDto? smartInfo; | ||||
|  | ||||
|   @override | ||||
|   bool operator ==(Object other) => | ||||
|       identical(this, other) || | ||||
|       other is AssetResponseDto && | ||||
|           other.type == type && | ||||
|           other.id == id && | ||||
|           other.deviceAssetId == deviceAssetId && | ||||
|           other.ownerId == ownerId && | ||||
|           other.deviceId == deviceId && | ||||
|           other.originalPath == originalPath && | ||||
|           other.resizePath == resizePath && | ||||
|           other.createdAt == createdAt && | ||||
|           other.modifiedAt == modifiedAt && | ||||
|           other.isFavorite == isFavorite && | ||||
|           other.mimeType == mimeType && | ||||
|           other.duration == duration && | ||||
|           other.webpPath == webpPath && | ||||
|           other.encodedVideoPath == encodedVideoPath && | ||||
|           other.exifInfo == exifInfo && | ||||
|           other.smartInfo == smartInfo; | ||||
|   bool operator ==(Object other) => identical(this, other) || other is AssetResponseDto && | ||||
|      other.type == type && | ||||
|      other.id == id && | ||||
|      other.deviceAssetId == deviceAssetId && | ||||
|      other.ownerId == ownerId && | ||||
|      other.deviceId == deviceId && | ||||
|      other.originalPath == originalPath && | ||||
|      other.resizePath == resizePath && | ||||
|      other.createdAt == createdAt && | ||||
|      other.modifiedAt == modifiedAt && | ||||
|      other.isFavorite == isFavorite && | ||||
|      other.mimeType == mimeType && | ||||
|      other.duration == duration && | ||||
|      other.webpPath == webpPath && | ||||
|      other.encodedVideoPath == encodedVideoPath && | ||||
|      other.exifInfo == exifInfo && | ||||
|      other.smartInfo == smartInfo; | ||||
|  | ||||
|   @override | ||||
|   int get hashCode => | ||||
|       // ignore: unnecessary_parenthesis | ||||
|       (type.hashCode) + | ||||
|       (id.hashCode) + | ||||
|       (deviceAssetId.hashCode) + | ||||
|       (ownerId.hashCode) + | ||||
|       (deviceId.hashCode) + | ||||
|       (originalPath.hashCode) + | ||||
|       (resizePath == null ? 0 : resizePath!.hashCode) + | ||||
|       (createdAt.hashCode) + | ||||
|       (modifiedAt.hashCode) + | ||||
|       (isFavorite.hashCode) + | ||||
|       (mimeType == null ? 0 : mimeType!.hashCode) + | ||||
|       (duration.hashCode) + | ||||
|       (webpPath == null ? 0 : webpPath!.hashCode) + | ||||
|       (encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) + | ||||
|       (exifInfo == null ? 0 : exifInfo!.hashCode) + | ||||
|       (smartInfo == null ? 0 : smartInfo!.hashCode); | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (type.hashCode) + | ||||
|     (id.hashCode) + | ||||
|     (deviceAssetId.hashCode) + | ||||
|     (ownerId.hashCode) + | ||||
|     (deviceId.hashCode) + | ||||
|     (originalPath.hashCode) + | ||||
|     (resizePath == null ? 0 : resizePath!.hashCode) + | ||||
|     (createdAt.hashCode) + | ||||
|     (modifiedAt.hashCode) + | ||||
|     (isFavorite.hashCode) + | ||||
|     (mimeType == null ? 0 : mimeType!.hashCode) + | ||||
|     (duration.hashCode) + | ||||
|     (webpPath == null ? 0 : webpPath!.hashCode) + | ||||
|     (encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) + | ||||
|     (exifInfo == null ? 0 : exifInfo!.hashCode) + | ||||
|     (smartInfo == null ? 0 : smartInfo!.hashCode); | ||||
|  | ||||
|   @override | ||||
|   String toString() => | ||||
|       'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo]'; | ||||
|   String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo]'; | ||||
|  | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final _json = <String, dynamic>{}; | ||||
|     _json[r'type'] = type; | ||||
|     _json[r'id'] = id; | ||||
|     _json[r'deviceAssetId'] = deviceAssetId; | ||||
|     _json[r'ownerId'] = ownerId; | ||||
|     _json[r'deviceId'] = deviceId; | ||||
|     _json[r'originalPath'] = originalPath; | ||||
|       _json[r'type'] = type; | ||||
|       _json[r'id'] = id; | ||||
|       _json[r'deviceAssetId'] = deviceAssetId; | ||||
|       _json[r'ownerId'] = ownerId; | ||||
|       _json[r'deviceId'] = deviceId; | ||||
|       _json[r'originalPath'] = originalPath; | ||||
|     if (resizePath != null) { | ||||
|       _json[r'resizePath'] = resizePath; | ||||
|     } else { | ||||
|       _json[r'resizePath'] = null; | ||||
|     } | ||||
|     _json[r'createdAt'] = createdAt; | ||||
|     _json[r'modifiedAt'] = modifiedAt; | ||||
|     _json[r'isFavorite'] = isFavorite; | ||||
|       _json[r'createdAt'] = createdAt; | ||||
|       _json[r'modifiedAt'] = modifiedAt; | ||||
|       _json[r'isFavorite'] = isFavorite; | ||||
|     if (mimeType != null) { | ||||
|       _json[r'mimeType'] = mimeType; | ||||
|     } else { | ||||
|       _json[r'mimeType'] = null; | ||||
|     } | ||||
|     _json[r'duration'] = duration; | ||||
|       _json[r'duration'] = duration; | ||||
|     if (webpPath != null) { | ||||
|       _json[r'webpPath'] = webpPath; | ||||
|     } else { | ||||
| @@ -177,10 +174,8 @@ class AssetResponseDto { | ||||
|       // Note 2: this code is stripped in release mode! | ||||
|       assert(() { | ||||
|         requiredKeys.forEach((key) { | ||||
|           assert(json.containsKey(key), | ||||
|               'Required key "AssetResponseDto[$key]" is missing from JSON.'); | ||||
|           assert(json[key] != null, | ||||
|               'Required key "AssetResponseDto[$key]" has a null value in JSON.'); | ||||
|           assert(json.containsKey(key), 'Required key "AssetResponseDto[$key]" is missing from JSON.'); | ||||
|           assert(json[key] != null, 'Required key "AssetResponseDto[$key]" has a null value in JSON.'); | ||||
|         }); | ||||
|         return true; | ||||
|       }()); | ||||
| @@ -207,10 +202,7 @@ class AssetResponseDto { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   static List<AssetResponseDto>? listFromJson( | ||||
|     dynamic json, { | ||||
|     bool growable = false, | ||||
|   }) { | ||||
|   static List<AssetResponseDto>? listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <AssetResponseDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
| @@ -238,18 +230,12 @@ class AssetResponseDto { | ||||
|   } | ||||
|  | ||||
|   // maps a json object with a list of AssetResponseDto-objects as value to a dart map | ||||
|   static Map<String, List<AssetResponseDto>> mapListFromJson( | ||||
|     dynamic json, { | ||||
|     bool growable = false, | ||||
|   }) { | ||||
|   static Map<String, List<AssetResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<AssetResponseDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = AssetResponseDto.listFromJson( | ||||
|           entry.value, | ||||
|           growable: growable, | ||||
|         ); | ||||
|         final value = AssetResponseDto.listFromJson(entry.value, growable: growable,); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
| @@ -276,3 +262,4 @@ class AssetResponseDto { | ||||
|     'encodedVideoPath', | ||||
|   }; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -134,21 +134,14 @@ export class AlbumRepository implements IAlbumRepository { | ||||
|         .leftJoinAndSelect('album.sharedUsers', 'sharedUser') | ||||
|         .leftJoinAndSelect('sharedUser.userInfo', 'userInfo') | ||||
|         .where('album.ownerId = :ownerId', { ownerId: userId }); | ||||
|       // .orWhere((qb) => { | ||||
|       //   const subQuery = qb | ||||
|       //     .subQuery() | ||||
|       //     .select('userAlbum.albumId') | ||||
|       //     .from(UserAlbumEntity, 'userAlbum') | ||||
|       //     .where('userAlbum.sharedUserId = :sharedUserId', { sharedUserId: userId }) | ||||
|       //     .getQuery(); | ||||
|       //   return `album.id IN ${subQuery}`; | ||||
|       // }); | ||||
|     } | ||||
|  | ||||
|     // Get information of assets in albums | ||||
|     query = query | ||||
|       .leftJoinAndSelect('album.assets', 'assets') | ||||
|       .leftJoinAndSelect('assets.assetInfo', 'assetInfo') | ||||
|       .orderBy('"assetInfo"."createdAt"::timestamptz', 'ASC'); | ||||
|  | ||||
|     const albums = await query.getMany(); | ||||
|  | ||||
|     albums.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf()); | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import { AddUsersDto } from './dto/add-users.dto'; | ||||
| import { RemoveAssetsDto } from './dto/remove-assets.dto'; | ||||
| import { UpdateAlbumDto } from './dto/update-album.dto'; | ||||
| import { GetAlbumsDto } from './dto/get-albums.dto'; | ||||
| import { AlbumResponseDto, mapAlbum } from './response-dto/album-response.dto'; | ||||
| import { AlbumResponseDto, mapAlbum, mapAlbumExcludeAssetInfo } from './response-dto/album-response.dto'; | ||||
| import { ALBUM_REPOSITORY, IAlbumRepository } from './album-repository'; | ||||
|  | ||||
| @Injectable() | ||||
| @@ -49,7 +49,8 @@ export class AlbumService { | ||||
|    */ | ||||
|   async getAllAlbums(authUser: AuthUserDto, getAlbumsDto: GetAlbumsDto): Promise<AlbumResponseDto[]> { | ||||
|     const albums = await this._albumRepository.getList(authUser.id, getAlbumsDto); | ||||
|     return albums.map((album) => mapAlbum(album)); | ||||
|  | ||||
|     return albums.map((album) => mapAlbumExcludeAssetInfo(album)); | ||||
|   } | ||||
|  | ||||
|   async getAlbumInfo(authUser: AuthUserDto, albumId: string): Promise<AlbumResponseDto> { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import { AlbumEntity } from '../../../../../../libs/database/src/entities/album.entity'; | ||||
| import { UserResponseDto, mapUser } from '../../user/response-dto/user-response.dto'; | ||||
| import { AssetResponseDto, mapAsset } from '../../asset/response-dto/asset-response.dto'; | ||||
| import { ApiProperty } from '@nestjs/swagger'; | ||||
|  | ||||
| export class AlbumResponseDto { | ||||
|   id!: string; | ||||
| @@ -11,6 +12,9 @@ export class AlbumResponseDto { | ||||
|   shared!: boolean; | ||||
|   sharedUsers!: UserResponseDto[]; | ||||
|   assets!: AssetResponseDto[]; | ||||
|  | ||||
|   @ApiProperty({ type: 'integer' }) | ||||
|   assetCount!: number; | ||||
| } | ||||
|  | ||||
| export function mapAlbum(entity: AlbumEntity): AlbumResponseDto { | ||||
| @@ -24,5 +28,21 @@ export function mapAlbum(entity: AlbumEntity): AlbumResponseDto { | ||||
|     sharedUsers, | ||||
|     shared: sharedUsers.length > 0, | ||||
|     assets: entity.assets?.map((assetAlbum) => mapAsset(assetAlbum.assetInfo)) || [], | ||||
|     assetCount: entity.assets?.length || 0, | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function mapAlbumExcludeAssetInfo(entity: AlbumEntity): AlbumResponseDto { | ||||
|   const sharedUsers = entity.sharedUsers?.map((userAlbum) => mapUser(userAlbum.userInfo)) || []; | ||||
|   return { | ||||
|     albumName: entity.albumName, | ||||
|     albumThumbnailAssetId: entity.albumThumbnailAssetId, | ||||
|     createdAt: entity.createdAt, | ||||
|     id: entity.id, | ||||
|     ownerId: entity.ownerId, | ||||
|     sharedUsers, | ||||
|     shared: sharedUsers.length > 0, | ||||
|     assets: [], | ||||
|     assetCount: entity.assets?.length || 0, | ||||
|   }; | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -90,6 +90,12 @@ export interface AdminSignupResponseDto { | ||||
|  * @interface AlbumResponseDto | ||||
|  */ | ||||
| export interface AlbumResponseDto { | ||||
|     /** | ||||
|      *  | ||||
|      * @type {number} | ||||
|      * @memberof AlbumResponseDto | ||||
|      */ | ||||
|     'assetCount': number; | ||||
|     /** | ||||
|      *  | ||||
|      * @type {string} | ||||
|   | ||||
| @@ -32,7 +32,7 @@ | ||||
| 	}; | ||||
|  | ||||
| 	onMount(async () => { | ||||
| 		imageData = await loadHighQualityThumbnail(album.albumThumbnailAssetId) || 'no-thumbnail.png'; | ||||
| 		imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || 'no-thumbnail.png'; | ||||
| 	}); | ||||
| </script> | ||||
|  | ||||
| @@ -67,7 +67,7 @@ | ||||
| 		</p> | ||||
|  | ||||
| 		<span class="text-xs flex gap-2"> | ||||
| 			<p>{album.assets.length} items</p> | ||||
| 			<p>{album.assetCount} items</p> | ||||
|  | ||||
| 			{#if album.shared} | ||||
| 				<p>·</p> | ||||
|   | ||||
| @@ -61,7 +61,7 @@ | ||||
|  | ||||
| 	$: { | ||||
| 		if (album.assets?.length < 6) { | ||||
| 			thumbnailSize = Math.floor(viewWidth / album.assets.length - album.assets.length); | ||||
| 			thumbnailSize = Math.floor(viewWidth / album.assetCount - album.assetCount); | ||||
| 		} else { | ||||
| 			thumbnailSize = Math.floor(viewWidth / 6 - 6); | ||||
| 		} | ||||
| @@ -69,7 +69,7 @@ | ||||
|  | ||||
| 	const getDateRange = () => { | ||||
| 		const startDate = new Date(album.assets[0].createdAt); | ||||
| 		const endDate = new Date(album.assets[album.assets.length - 1].createdAt); | ||||
| 		const endDate = new Date(album.assets[album.assetCount - 1].createdAt); | ||||
|  | ||||
| 		const timeFormatOption: Intl.DateTimeFormatOptions = { | ||||
| 			month: 'short', | ||||
| @@ -135,7 +135,7 @@ | ||||
| 	}; | ||||
| 	const navigateAssetForward = () => { | ||||
| 		try { | ||||
| 			if (currentViewAssetIndex < album.assets.length - 1) { | ||||
| 			if (currentViewAssetIndex < album.assetCount - 1) { | ||||
| 				currentViewAssetIndex++; | ||||
| 				selectedAsset = album.assets[currentViewAssetIndex]; | ||||
| 				pushState(selectedAsset.id); | ||||
| @@ -296,7 +296,7 @@ | ||||
| 	{#if !isMultiSelectionMode} | ||||
| 		<ControlAppBar on:close-button-click={() => goto(backUrl)} backIcon={ArrowLeft}> | ||||
| 			<svelte:fragment slot="trailing"> | ||||
| 				{#if album.assets.length > 0} | ||||
| 				{#if album.assetCount > 0} | ||||
| 					<CircleIconButton | ||||
| 						title="Add Photos" | ||||
| 						on:click={() => (isShowAssetSelection = true)} | ||||
| @@ -322,7 +322,7 @@ | ||||
|  | ||||
| 				{#if isCreatingSharedAlbum && album.sharedUsers.length == 0} | ||||
| 					<button | ||||
| 						disabled={album.assets.length == 0} | ||||
| 						disabled={album.assetCount == 0} | ||||
| 						on:click={() => (isShowShareUserSelection = true)} | ||||
| 						class="immich-text-button border bg-immich-primary text-gray-50 hover:bg-immich-primary/75 px-6 text-sm disabled:opacity-25 disabled:bg-gray-500 disabled:cursor-not-allowed" | ||||
| 						><span class="px-2">Share</span></button | ||||
| @@ -351,7 +351,7 @@ | ||||
| 			bind:this={titleInput} | ||||
| 		/> | ||||
|  | ||||
| 		{#if album.assets.length > 0} | ||||
| 		{#if album.assetCount > 0} | ||||
| 			<p class="my-4 text-sm text-gray-500 font-medium">{getDateRange()}</p> | ||||
| 		{/if} | ||||
|  | ||||
| @@ -375,11 +375,11 @@ | ||||
| 			</div> | ||||
| 		{/if} | ||||
|  | ||||
| 		{#if album.assets.length > 0} | ||||
| 		{#if album.assetCount > 0} | ||||
| 			<div class="flex flex-wrap gap-1 w-full pb-20" bind:clientWidth={viewWidth}> | ||||
| 				{#each album.assets as asset} | ||||
| 					{#key asset.id} | ||||
| 						{#if album.assets.length < 7} | ||||
| 						{#if album.assetCount < 7} | ||||
| 							<ImmichThumbnail | ||||
| 								{asset} | ||||
| 								{thumbnailSize} | ||||
|   | ||||
| @@ -61,7 +61,7 @@ | ||||
|  | ||||
| 		// Delete album that has no photos and is named 'Untitled' | ||||
| 		for (const album of albums) { | ||||
| 			if (album.albumName === 'Untitled' && album.assets.length === 0) { | ||||
| 			if (album.albumName === 'Untitled' && album.assetCount === 0) { | ||||
| 				const isDeleted = await autoDeleteAlbum(album); | ||||
|  | ||||
| 				if (isDeleted) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user