mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	feat(server): trash asset (#4015)
* refactor(server): delete assets endpoint * fix: formatting * chore: cleanup * chore: open api * chore(mobile): replace DeleteAssetDTO with BulkIdsDTOs * feat: trash an asset * chore(server): formatting * chore: open api * chore: wording * chore: open-api * feat(server): add withDeleted to getAssets queries * WIP: mobile-recycle-bin * feat(server): recycle-bin to system config * feat(web): use recycle-bin system config * chore(server): domain assetcore removed * chore(server): rename recycle-bin to trash * chore(web): rename recycle-bin to trash * chore(server): always send soft deleted assets for getAllByUserId * chore(web): formatting * feat(server): permanent delete assets older than trashed period * feat(web): trash empty placeholder image * feat(server): empty trash * feat(web): empty trash * WIP: mobile-recycle-bin * refactor(server): empty / restore trash to separate endpoint * test(server): handle failures * test(server): fix e2e server-info test * test(server): deletion test refactor * feat(mobile): use map settings from server-config to enable / disable map * feat(mobile): trash asset * fix(server): operations on assets in trash * feat(web): show trash statistics * fix(web): handle trash enabled * fix(mobile): restore updates from trash * fix(server): ignore trashed assets for person * fix(server): add / remove search index when trashed / restored * chore(web): format * fix(server): asset service test * fix(server): include trashed assts for duplicates from uploads * feat(mobile): no dialog for trash, always dialog for permanent delete * refactor(mobile): use isar where instead of dart filter * refactor(mobile): asset provide - handle deletes in single db txn * chore(mobile): review changes * feat(web): confirmation before empty trash * server: review changes * fix(server): handle library changes * fix: filter external assets from getting trashed / deleted * fix(server): empty-bin * feat: broadcast config update events through ws * change order of trash button on mobile * styling * fix(mobile): do not show trashed toast for local only assets --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
		
							
								
								
									
										437
									
								
								cli/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										437
									
								
								cli/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							| @@ -356,6 +356,25 @@ export interface AllJobStatusResponseDto { | |||||||
|      */ |      */ | ||||||
|     'videoConversion': JobStatusDto; |     'videoConversion': JobStatusDto; | ||||||
| } | } | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * @export | ||||||
|  |  * @interface AssetBulkDeleteDto | ||||||
|  |  */ | ||||||
|  | export interface AssetBulkDeleteDto { | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof AssetBulkDeleteDto | ||||||
|  |      */ | ||||||
|  |     'force'?: boolean; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {Array<string>} | ||||||
|  |      * @memberof AssetBulkDeleteDto | ||||||
|  |      */ | ||||||
|  |     'ids': Array<string>; | ||||||
|  | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
| @@ -657,6 +676,12 @@ export interface AssetResponseDto { | |||||||
|      * @memberof AssetResponseDto |      * @memberof AssetResponseDto | ||||||
|      */ |      */ | ||||||
|     'isReadOnly': boolean; |     'isReadOnly': boolean; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof AssetResponseDto | ||||||
|  |      */ | ||||||
|  |     'isTrashed': boolean; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {string} |      * @type {string} | ||||||
| @@ -1357,54 +1382,6 @@ export interface CuratedObjectsResponseDto { | |||||||
|      */ |      */ | ||||||
|     'resizePath': string; |     'resizePath': string; | ||||||
| } | } | ||||||
| /** |  | ||||||
|  *  |  | ||||||
|  * @export |  | ||||||
|  * @interface DeleteAssetDto |  | ||||||
|  */ |  | ||||||
| export interface DeleteAssetDto { |  | ||||||
|     /** |  | ||||||
|      *  |  | ||||||
|      * @type {Array<string>} |  | ||||||
|      * @memberof DeleteAssetDto |  | ||||||
|      */ |  | ||||||
|     'ids': Array<string>; |  | ||||||
| } |  | ||||||
| /** |  | ||||||
|  *  |  | ||||||
|  * @export |  | ||||||
|  * @interface DeleteAssetResponseDto |  | ||||||
|  */ |  | ||||||
| export interface DeleteAssetResponseDto { |  | ||||||
|     /** |  | ||||||
|      *  |  | ||||||
|      * @type {string} |  | ||||||
|      * @memberof DeleteAssetResponseDto |  | ||||||
|      */ |  | ||||||
|     'id': string; |  | ||||||
|     /** |  | ||||||
|      *  |  | ||||||
|      * @type {DeleteAssetStatus} |  | ||||||
|      * @memberof DeleteAssetResponseDto |  | ||||||
|      */ |  | ||||||
|     'status': DeleteAssetStatus; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  *  |  | ||||||
|  * @export |  | ||||||
|  * @enum {string} |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| export const DeleteAssetStatus = { |  | ||||||
|     Success: 'SUCCESS', |  | ||||||
|     Failed: 'FAILED' |  | ||||||
| } as const; |  | ||||||
| 
 |  | ||||||
| export type DeleteAssetStatus = typeof DeleteAssetStatus[keyof typeof DeleteAssetStatus]; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
| @@ -2623,6 +2600,12 @@ export interface ServerConfigDto { | |||||||
|      * @memberof ServerConfigDto |      * @memberof ServerConfigDto | ||||||
|      */ |      */ | ||||||
|     'oauthButtonText': string; |     'oauthButtonText': string; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {number} | ||||||
|  |      * @memberof ServerConfigDto | ||||||
|  |      */ | ||||||
|  |     'trashDays': number; | ||||||
| } | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
| @@ -2696,6 +2679,12 @@ export interface ServerFeaturesDto { | |||||||
|      * @memberof ServerFeaturesDto |      * @memberof ServerFeaturesDto | ||||||
|      */ |      */ | ||||||
|     'tagImage': boolean; |     'tagImage': boolean; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof ServerFeaturesDto | ||||||
|  |      */ | ||||||
|  |     'trash': boolean; | ||||||
| } | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
| @@ -3139,6 +3128,12 @@ export interface SystemConfigDto { | |||||||
|      * @memberof SystemConfigDto |      * @memberof SystemConfigDto | ||||||
|      */ |      */ | ||||||
|     'thumbnail': SystemConfigThumbnailDto; |     'thumbnail': SystemConfigThumbnailDto; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {SystemConfigTrashDto} | ||||||
|  |      * @memberof SystemConfigDto | ||||||
|  |      */ | ||||||
|  |     'trash': SystemConfigTrashDto; | ||||||
| } | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
| @@ -3594,6 +3589,25 @@ export interface SystemConfigThumbnailDto { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * @export | ||||||
|  |  * @interface SystemConfigTrashDto | ||||||
|  |  */ | ||||||
|  | export interface SystemConfigTrashDto { | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {number} | ||||||
|  |      * @memberof SystemConfigTrashDto | ||||||
|  |      */ | ||||||
|  |     'days': number; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof SystemConfigTrashDto | ||||||
|  |      */ | ||||||
|  |     'enabled': boolean; | ||||||
|  | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
| @@ -5682,13 +5696,13 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {DeleteAssetDto} deleteAssetDto  |          * @param {AssetBulkDeleteDto} assetBulkDeleteDto  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         deleteAsset: async (deleteAssetDto: DeleteAssetDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { |         deleteAssets: async (assetBulkDeleteDto: AssetBulkDeleteDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|             // verify required parameter 'deleteAssetDto' is not null or undefined
 |             // verify required parameter 'assetBulkDeleteDto' is not null or undefined
 | ||||||
|             assertParamExists('deleteAsset', 'deleteAssetDto', deleteAssetDto) |             assertParamExists('deleteAssets', 'assetBulkDeleteDto', assetBulkDeleteDto) | ||||||
|             const localVarPath = `/asset`; |             const localVarPath = `/asset`; | ||||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 |             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); |             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||||
| @@ -5717,7 +5731,7 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; |             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; |             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||||
|             localVarRequestOptions.data = serializeDataIfNeeded(deleteAssetDto, localVarRequestOptions, configuration) |             localVarRequestOptions.data = serializeDataIfNeeded(assetBulkDeleteDto, localVarRequestOptions, configuration) | ||||||
| 
 | 
 | ||||||
|             return { |             return { | ||||||
|                 url: toPathString(localVarUrlObj), |                 url: toPathString(localVarUrlObj), | ||||||
| @@ -5811,6 +5825,44 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|      |      | ||||||
|  |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
|  |             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||||
|  |             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||||
|  | 
 | ||||||
|  |             return { | ||||||
|  |                 url: toPathString(localVarUrlObj), | ||||||
|  |                 options: localVarRequestOptions, | ||||||
|  |             }; | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         emptyTrash: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|  |             const localVarPath = `/asset/trash/empty`; | ||||||
|  |             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||||
|  |             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||||
|  |             let baseOptions; | ||||||
|  |             if (configuration) { | ||||||
|  |                 baseOptions = configuration.baseOptions; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; | ||||||
|  |             const localVarHeaderParameter = {} as any; | ||||||
|  |             const localVarQueryParameter = {} as any; | ||||||
|  | 
 | ||||||
|  |             // authentication cookie required
 | ||||||
|  | 
 | ||||||
|  |             // authentication api_key required
 | ||||||
|  |             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||||
|  | 
 | ||||||
|  |             // authentication bearer required
 | ||||||
|  |             // http bearer authentication required
 | ||||||
|  |             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |      | ||||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; |             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; |             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||||
| @@ -5979,10 +6031,11 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|          *  |          *  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getAssetStats: async (isArchived?: boolean, isFavorite?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { |         getAssetStats: async (isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|             const localVarPath = `/asset/statistics`; |             const localVarPath = `/asset/statistics`; | ||||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 |             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); |             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||||
| @@ -6012,6 +6065,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|                 localVarQueryParameter['isFavorite'] = isFavorite; |                 localVarQueryParameter['isFavorite'] = isFavorite; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             if (isTrashed !== undefined) { | ||||||
|  |                 localVarQueryParameter['isTrashed'] = isTrashed; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|      |      | ||||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
| @@ -6084,11 +6141,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|          * @param {string} [personId]  |          * @param {string} [personId]  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {string} [key]  |          * @param {string} [key]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getByTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { |         getByTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|             // verify required parameter 'size' is not null or undefined
 |             // verify required parameter 'size' is not null or undefined
 | ||||||
|             assertParamExists('getByTimeBucket', 'size', size) |             assertParamExists('getByTimeBucket', 'size', size) | ||||||
|             // verify required parameter 'timeBucket' is not null or undefined
 |             // verify required parameter 'timeBucket' is not null or undefined
 | ||||||
| @@ -6138,6 +6196,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|                 localVarQueryParameter['isFavorite'] = isFavorite; |                 localVarQueryParameter['isFavorite'] = isFavorite; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             if (isTrashed !== undefined) { | ||||||
|  |                 localVarQueryParameter['isTrashed'] = isTrashed; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             if (timeBucket !== undefined) { |             if (timeBucket !== undefined) { | ||||||
|                 localVarQueryParameter['timeBucket'] = timeBucket; |                 localVarQueryParameter['timeBucket'] = timeBucket; | ||||||
|             } |             } | ||||||
| @@ -6447,11 +6509,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|          * @param {string} [personId]  |          * @param {string} [personId]  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {string} [key]  |          * @param {string} [key]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { |         getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|             // verify required parameter 'size' is not null or undefined
 |             // verify required parameter 'size' is not null or undefined
 | ||||||
|             assertParamExists('getTimeBuckets', 'size', size) |             assertParamExists('getTimeBuckets', 'size', size) | ||||||
|             const localVarPath = `/asset/time-buckets`; |             const localVarPath = `/asset/time-buckets`; | ||||||
| @@ -6499,6 +6562,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|                 localVarQueryParameter['isFavorite'] = isFavorite; |                 localVarQueryParameter['isFavorite'] = isFavorite; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             if (isTrashed !== undefined) { | ||||||
|  |                 localVarQueryParameter['isTrashed'] = isTrashed; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             if (key !== undefined) { |             if (key !== undefined) { | ||||||
|                 localVarQueryParameter['key'] = key; |                 localVarQueryParameter['key'] = key; | ||||||
|             } |             } | ||||||
| @@ -6600,6 +6667,88 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|                 options: localVarRequestOptions, |                 options: localVarRequestOptions, | ||||||
|             }; |             }; | ||||||
|         }, |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {BulkIdsDto} bulkIdsDto  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         restoreAssets: async (bulkIdsDto: BulkIdsDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|  |             // verify required parameter 'bulkIdsDto' is not null or undefined
 | ||||||
|  |             assertParamExists('restoreAssets', 'bulkIdsDto', bulkIdsDto) | ||||||
|  |             const localVarPath = `/asset/restore`; | ||||||
|  |             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||||
|  |             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||||
|  |             let baseOptions; | ||||||
|  |             if (configuration) { | ||||||
|  |                 baseOptions = configuration.baseOptions; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; | ||||||
|  |             const localVarHeaderParameter = {} as any; | ||||||
|  |             const localVarQueryParameter = {} as any; | ||||||
|  | 
 | ||||||
|  |             // authentication cookie required
 | ||||||
|  | 
 | ||||||
|  |             // authentication api_key required
 | ||||||
|  |             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||||
|  | 
 | ||||||
|  |             // authentication bearer required
 | ||||||
|  |             // http bearer authentication required
 | ||||||
|  |             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  |             localVarHeaderParameter['Content-Type'] = 'application/json'; | ||||||
|  | 
 | ||||||
|  |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
|  |             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||||
|  |             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||||
|  |             localVarRequestOptions.data = serializeDataIfNeeded(bulkIdsDto, localVarRequestOptions, configuration) | ||||||
|  | 
 | ||||||
|  |             return { | ||||||
|  |                 url: toPathString(localVarUrlObj), | ||||||
|  |                 options: localVarRequestOptions, | ||||||
|  |             }; | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         restoreTrash: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|  |             const localVarPath = `/asset/trash/restore`; | ||||||
|  |             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||||
|  |             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||||
|  |             let baseOptions; | ||||||
|  |             if (configuration) { | ||||||
|  |                 baseOptions = configuration.baseOptions; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; | ||||||
|  |             const localVarHeaderParameter = {} as any; | ||||||
|  |             const localVarQueryParameter = {} as any; | ||||||
|  | 
 | ||||||
|  |             // authentication cookie required
 | ||||||
|  | 
 | ||||||
|  |             // authentication api_key required
 | ||||||
|  |             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||||
|  | 
 | ||||||
|  |             // authentication bearer required
 | ||||||
|  |             // http bearer authentication required
 | ||||||
|  |             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
|  |             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||||
|  |             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||||
|  | 
 | ||||||
|  |             return { | ||||||
|  |                 url: toPathString(localVarUrlObj), | ||||||
|  |                 options: localVarRequestOptions, | ||||||
|  |             }; | ||||||
|  |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {AssetJobsDto} assetJobsDto  |          * @param {AssetJobsDto} assetJobsDto  | ||||||
| @@ -7014,12 +7163,12 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {DeleteAssetDto} deleteAssetDto  |          * @param {AssetBulkDeleteDto} assetBulkDeleteDto  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         async deleteAsset(deleteAssetDto: DeleteAssetDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<DeleteAssetResponseDto>>> { |         async deleteAssets(assetBulkDeleteDto: AssetBulkDeleteDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { | ||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAsset(deleteAssetDto, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAssets(assetBulkDeleteDto, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
| @@ -7044,6 +7193,15 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.downloadFile(id, key, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.downloadFile(id, key, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         async emptyTrash(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { | ||||||
|  |             const localVarAxiosArgs = await localVarAxiosParamCreator.emptyTrash(options); | ||||||
|  |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|  |         }, | ||||||
|         /** |         /** | ||||||
|          * Get all AssetEntity belong to the user |          * Get all AssetEntity belong to the user | ||||||
|          * @param {string} [userId]  |          * @param {string} [userId]  | ||||||
| @@ -7083,11 +7241,12 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|          *  |          *  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         async getAssetStats(isArchived?: boolean, isFavorite?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetStatsResponseDto>> { |         async getAssetStats(isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetStatsResponseDto>> { | ||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetStats(isArchived, isFavorite, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetStats(isArchived, isFavorite, isTrashed, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
| @@ -7111,12 +7270,13 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|          * @param {string} [personId]  |          * @param {string} [personId]  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {string} [key]  |          * @param {string} [key]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         async getByTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> { |         async getByTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> { | ||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, key, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, key, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
| @@ -7190,12 +7350,13 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|          * @param {string} [personId]  |          * @param {string} [personId]  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {string} [key]  |          * @param {string} [key]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<TimeBucketResponseDto>>> { |         async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<TimeBucketResponseDto>>> { | ||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, key, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, key, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
| @@ -7218,6 +7379,25 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.importFile(importAssetDto, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.importFile(importAssetDto, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {BulkIdsDto} bulkIdsDto  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         async restoreAssets(bulkIdsDto: BulkIdsDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { | ||||||
|  |             const localVarAxiosArgs = await localVarAxiosParamCreator.restoreAssets(bulkIdsDto, options); | ||||||
|  |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         async restoreTrash(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { | ||||||
|  |             const localVarAxiosArgs = await localVarAxiosParamCreator.restoreTrash(options); | ||||||
|  |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|  |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {AssetJobsDto} assetJobsDto  |          * @param {AssetJobsDto} assetJobsDto  | ||||||
| @@ -7336,12 +7516,12 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {AssetApiDeleteAssetRequest} requestParameters Request parameters. |          * @param {AssetApiDeleteAssetsRequest} requestParameters Request parameters. | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         deleteAsset(requestParameters: AssetApiDeleteAssetRequest, options?: AxiosRequestConfig): AxiosPromise<Array<DeleteAssetResponseDto>> { |         deleteAssets(requestParameters: AssetApiDeleteAssetsRequest, options?: AxiosRequestConfig): AxiosPromise<void> { | ||||||
|             return localVarFp.deleteAsset(requestParameters.deleteAssetDto, options).then((request) => request(axios, basePath)); |             return localVarFp.deleteAssets(requestParameters.assetBulkDeleteDto, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
| @@ -7361,6 +7541,14 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|         downloadFile(requestParameters: AssetApiDownloadFileRequest, options?: AxiosRequestConfig): AxiosPromise<File> { |         downloadFile(requestParameters: AssetApiDownloadFileRequest, options?: AxiosRequestConfig): AxiosPromise<File> { | ||||||
|             return localVarFp.downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(axios, basePath)); |             return localVarFp.downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         emptyTrash(options?: AxiosRequestConfig): AxiosPromise<void> { | ||||||
|  |             return localVarFp.emptyTrash(options).then((request) => request(axios, basePath)); | ||||||
|  |         }, | ||||||
|         /** |         /** | ||||||
|          * Get all AssetEntity belong to the user |          * Get all AssetEntity belong to the user | ||||||
|          * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. |          * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. | ||||||
| @@ -7394,7 +7582,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<AssetStatsResponseDto> { |         getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<AssetStatsResponseDto> { | ||||||
|             return localVarFp.getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, options).then((request) => request(axios, basePath)); |             return localVarFp.getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
| @@ -7412,7 +7600,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> { |         getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> { | ||||||
|             return localVarFp.getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(axios, basePath)); |             return localVarFp.getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
| @@ -7473,7 +7661,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig): AxiosPromise<Array<TimeBucketResponseDto>> { |         getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig): AxiosPromise<Array<TimeBucketResponseDto>> { | ||||||
|             return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(axios, basePath)); |             return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          * Get all asset of a device that are in the database, ID only. |          * Get all asset of a device that are in the database, ID only. | ||||||
| @@ -7493,6 +7681,23 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|         importFile(requestParameters: AssetApiImportFileRequest, options?: AxiosRequestConfig): AxiosPromise<AssetFileUploadResponseDto> { |         importFile(requestParameters: AssetApiImportFileRequest, options?: AxiosRequestConfig): AxiosPromise<AssetFileUploadResponseDto> { | ||||||
|             return localVarFp.importFile(requestParameters.importAssetDto, options).then((request) => request(axios, basePath)); |             return localVarFp.importFile(requestParameters.importAssetDto, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {AssetApiRestoreAssetsRequest} requestParameters Request parameters. | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         restoreAssets(requestParameters: AssetApiRestoreAssetsRequest, options?: AxiosRequestConfig): AxiosPromise<void> { | ||||||
|  |             return localVarFp.restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(axios, basePath)); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         restoreTrash(options?: AxiosRequestConfig): AxiosPromise<void> { | ||||||
|  |             return localVarFp.restoreTrash(options).then((request) => request(axios, basePath)); | ||||||
|  |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. |          * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. | ||||||
| @@ -7600,17 +7805,17 @@ export interface AssetApiCheckExistingAssetsRequest { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Request parameters for deleteAsset operation in AssetApi. |  * Request parameters for deleteAssets operation in AssetApi. | ||||||
|  * @export |  * @export | ||||||
|  * @interface AssetApiDeleteAssetRequest |  * @interface AssetApiDeleteAssetsRequest | ||||||
|  */ |  */ | ||||||
| export interface AssetApiDeleteAssetRequest { | export interface AssetApiDeleteAssetsRequest { | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {DeleteAssetDto} |      * @type {AssetBulkDeleteDto} | ||||||
|      * @memberof AssetApiDeleteAsset |      * @memberof AssetApiDeleteAssets | ||||||
|      */ |      */ | ||||||
|     readonly deleteAssetDto: DeleteAssetDto |     readonly assetBulkDeleteDto: AssetBulkDeleteDto | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @@ -7744,6 +7949,13 @@ export interface AssetApiGetAssetStatsRequest { | |||||||
|      * @memberof AssetApiGetAssetStats |      * @memberof AssetApiGetAssetStats | ||||||
|      */ |      */ | ||||||
|     readonly isFavorite?: boolean |     readonly isFavorite?: boolean | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof AssetApiGetAssetStats | ||||||
|  |      */ | ||||||
|  |     readonly isTrashed?: boolean | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @@ -7829,6 +8041,13 @@ export interface AssetApiGetByTimeBucketRequest { | |||||||
|      */ |      */ | ||||||
|     readonly isFavorite?: boolean |     readonly isFavorite?: boolean | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof AssetApiGetByTimeBucket | ||||||
|  |      */ | ||||||
|  |     readonly isTrashed?: boolean | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {string} |      * @type {string} | ||||||
| @@ -7976,6 +8195,13 @@ export interface AssetApiGetTimeBucketsRequest { | |||||||
|      */ |      */ | ||||||
|     readonly isFavorite?: boolean |     readonly isFavorite?: boolean | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof AssetApiGetTimeBuckets | ||||||
|  |      */ | ||||||
|  |     readonly isTrashed?: boolean | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {string} |      * @type {string} | ||||||
| @@ -8012,6 +8238,20 @@ export interface AssetApiImportFileRequest { | |||||||
|     readonly importAssetDto: ImportAssetDto |     readonly importAssetDto: ImportAssetDto | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Request parameters for restoreAssets operation in AssetApi. | ||||||
|  |  * @export | ||||||
|  |  * @interface AssetApiRestoreAssetsRequest | ||||||
|  |  */ | ||||||
|  | export interface AssetApiRestoreAssetsRequest { | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {BulkIdsDto} | ||||||
|  |      * @memberof AssetApiRestoreAssets | ||||||
|  |      */ | ||||||
|  |     readonly bulkIdsDto: BulkIdsDto | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Request parameters for runAssetJobs operation in AssetApi. |  * Request parameters for runAssetJobs operation in AssetApi. | ||||||
|  * @export |  * @export | ||||||
| @@ -8271,13 +8511,13 @@ export class AssetApi extends BaseAPI { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @param {AssetApiDeleteAssetRequest} requestParameters Request parameters. |      * @param {AssetApiDeleteAssetsRequest} requestParameters Request parameters. | ||||||
|      * @param {*} [options] Override http request option. |      * @param {*} [options] Override http request option. | ||||||
|      * @throws {RequiredError} |      * @throws {RequiredError} | ||||||
|      * @memberof AssetApi |      * @memberof AssetApi | ||||||
|      */ |      */ | ||||||
|     public deleteAsset(requestParameters: AssetApiDeleteAssetRequest, options?: AxiosRequestConfig) { |     public deleteAssets(requestParameters: AssetApiDeleteAssetsRequest, options?: AxiosRequestConfig) { | ||||||
|         return AssetApiFp(this.configuration).deleteAsset(requestParameters.deleteAssetDto, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).deleteAssets(requestParameters.assetBulkDeleteDto, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @@ -8302,6 +8542,16 @@ export class AssetApi extends BaseAPI { | |||||||
|         return AssetApiFp(this.configuration).downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @param {*} [options] Override http request option. | ||||||
|  |      * @throws {RequiredError} | ||||||
|  |      * @memberof AssetApi | ||||||
|  |      */ | ||||||
|  |     public emptyTrash(options?: AxiosRequestConfig) { | ||||||
|  |         return AssetApiFp(this.configuration).emptyTrash(options).then((request) => request(this.axios, this.basePath)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Get all AssetEntity belong to the user |      * Get all AssetEntity belong to the user | ||||||
|      * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. |      * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. | ||||||
| @@ -8342,7 +8592,7 @@ export class AssetApi extends BaseAPI { | |||||||
|      * @memberof AssetApi |      * @memberof AssetApi | ||||||
|      */ |      */ | ||||||
|     public getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig) { |     public getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig) { | ||||||
|         return AssetApiFp(this.configuration).getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @@ -8364,7 +8614,7 @@ export class AssetApi extends BaseAPI { | |||||||
|      * @memberof AssetApi |      * @memberof AssetApi | ||||||
|      */ |      */ | ||||||
|     public getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig) { |     public getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig) { | ||||||
|         return AssetApiFp(this.configuration).getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @@ -8439,7 +8689,7 @@ export class AssetApi extends BaseAPI { | |||||||
|      * @memberof AssetApi |      * @memberof AssetApi | ||||||
|      */ |      */ | ||||||
|     public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig) { |     public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig) { | ||||||
|         return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @@ -8464,6 +8714,27 @@ export class AssetApi extends BaseAPI { | |||||||
|         return AssetApiFp(this.configuration).importFile(requestParameters.importAssetDto, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).importFile(requestParameters.importAssetDto, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @param {AssetApiRestoreAssetsRequest} requestParameters Request parameters. | ||||||
|  |      * @param {*} [options] Override http request option. | ||||||
|  |      * @throws {RequiredError} | ||||||
|  |      * @memberof AssetApi | ||||||
|  |      */ | ||||||
|  |     public restoreAssets(requestParameters: AssetApiRestoreAssetsRequest, options?: AxiosRequestConfig) { | ||||||
|  |         return AssetApiFp(this.configuration).restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(this.axios, this.basePath)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @param {*} [options] Override http request option. | ||||||
|  |      * @throws {RequiredError} | ||||||
|  |      * @memberof AssetApi | ||||||
|  |      */ | ||||||
|  |     public restoreTrash(options?: AxiosRequestConfig) { | ||||||
|  |         return AssetApiFp(this.configuration).restoreTrash(options).then((request) => request(this.axios, this.basePath)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. |      * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. | ||||||
|   | |||||||
| @@ -322,5 +322,17 @@ | |||||||
|   "map_no_location_permission_title": "Location Permission denied", |   "map_no_location_permission_title": "Location Permission denied", | ||||||
|   "map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?", |   "map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?", | ||||||
|   "map_location_dialog_cancel": "Cancel", |   "map_location_dialog_cancel": "Cancel", | ||||||
|   "map_location_dialog_yes": "Yes" |   "map_location_dialog_yes": "Yes", | ||||||
|  |   "trash_page_title": "Trash ({})", | ||||||
|  |   "trash_page_info": "Backed up items will be permanently deleted after {} days", | ||||||
|  |   "trash_page_no_assets": "No trashed assets", | ||||||
|  |   "trash_page_delete": "Delete", | ||||||
|  |   "trash_page_delete_all": "Delete All", | ||||||
|  |   "trash_page_restore": "Restore", | ||||||
|  |   "trash_page_restore_all": "Restore All", | ||||||
|  |   "trash_page_select_btn": "Select", | ||||||
|  |   "trash_page_select_assets_btn": "Select assets", | ||||||
|  |   "trash_page_empty_trash_btn": "Empty trash", | ||||||
|  |   "trash_page_empty_trash_dialog_ok": "Ok", | ||||||
|  |   "trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich" | ||||||
| } | } | ||||||
|   | |||||||
| @@ -169,4 +169,4 @@ SPEC CHECKSUMS: | |||||||
|  |  | ||||||
| PODFILE CHECKSUM: 599d8aeb73728400c15364e734525722250a5382 | PODFILE CHECKSUM: 599d8aeb73728400c15364e734525722250a5382 | ||||||
|  |  | ||||||
| COCOAPODS: 1.12.1 | COCOAPODS: 1.11.3 | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ final archiveProvider = StreamProvider<RenderList>((ref) async* { | |||||||
|       .ownerIdEqualToAnyChecksum(user.isarId) |       .ownerIdEqualToAnyChecksum(user.isarId) | ||||||
|       .filter() |       .filter() | ||||||
|       .isArchivedEqualTo(true) |       .isArchivedEqualTo(true) | ||||||
|  |       .isTrashedEqualTo(false) | ||||||
|       .sortByFileCreatedAt(); |       .sortByFileCreatedAt(); | ||||||
|   final settings = ref.watch(appSettingsServiceProvider); |   final settings = ref.watch(appSettingsServiceProvider); | ||||||
|   final groupBy = |   final groupBy = | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import 'package:cached_network_image/cached_network_image.dart'; | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter/services.dart'; | import 'package:flutter/services.dart'; | ||||||
| import 'package:flutter_hooks/flutter_hooks.dart' hide Store; | import 'package:flutter_hooks/flutter_hooks.dart' hide Store; | ||||||
|  | import 'package:fluttertoast/fluttertoast.dart'; | ||||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
| import 'package:immich_mobile/modules/asset_viewer/providers/show_controls.provider.dart'; | import 'package:immich_mobile/modules/asset_viewer/providers/show_controls.provider.dart'; | ||||||
| import 'package:immich_mobile/modules/asset_viewer/providers/video_player_controls_provider.dart'; | import 'package:immich_mobile/modules/asset_viewer/providers/video_player_controls_provider.dart'; | ||||||
| @@ -19,11 +20,14 @@ import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart' | |||||||
| import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.dart'; | import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.dart'; | ||||||
| import 'package:immich_mobile/modules/home/ui/upload_dialog.dart'; | import 'package:immich_mobile/modules/home/ui/upload_dialog.dart'; | ||||||
| import 'package:immich_mobile/shared/cache/original_image_provider.dart'; | import 'package:immich_mobile/shared/cache/original_image_provider.dart'; | ||||||
|  | import 'package:immich_mobile/routing/router.dart'; | ||||||
| import 'package:immich_mobile/shared/models/store.dart'; | import 'package:immich_mobile/shared/models/store.dart'; | ||||||
| import 'package:immich_mobile/modules/home/ui/delete_dialog.dart'; | import 'package:immich_mobile/modules/home/ui/delete_dialog.dart'; | ||||||
| import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; | import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; | ||||||
| import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; | import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; | ||||||
|  | import 'package:immich_mobile/shared/providers/server_info.provider.dart'; | ||||||
| import 'package:immich_mobile/shared/ui/immich_image.dart'; | import 'package:immich_mobile/shared/ui/immich_image.dart'; | ||||||
|  | import 'package:immich_mobile/shared/ui/immich_toast.dart'; | ||||||
| import 'package:immich_mobile/shared/ui/photo_view/photo_view_gallery.dart'; | import 'package:immich_mobile/shared/ui/photo_view/photo_view_gallery.dart'; | ||||||
| import 'package:immich_mobile/shared/ui/photo_view/src/photo_view_computed_scale.dart'; | import 'package:immich_mobile/shared/ui/photo_view/src/photo_view_computed_scale.dart'; | ||||||
| import 'package:immich_mobile/shared/ui/photo_view/src/photo_view_scale_state.dart'; | import 'package:immich_mobile/shared/ui/photo_view/src/photo_view_scale_state.dart'; | ||||||
| @@ -67,6 +71,12 @@ class GalleryViewerPage extends HookConsumerWidget { | |||||||
|     final header = {"Authorization": authToken}; |     final header = {"Authorization": authToken}; | ||||||
|     final currentIndex = useState(initialIndex); |     final currentIndex = useState(initialIndex); | ||||||
|     final currentAsset = loadAsset(currentIndex.value); |     final currentAsset = loadAsset(currentIndex.value); | ||||||
|  |     final isTrashEnabled = | ||||||
|  |         ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); | ||||||
|  |     final navStack = AutoRouter.of(context).stackData; | ||||||
|  |     final isFromTrash = isTrashEnabled && | ||||||
|  |         navStack.length > 2 && | ||||||
|  |         navStack.elementAt(navStack.length - 2).name == TrashRoute.name; | ||||||
|  |  | ||||||
|     Asset asset() => currentAsset; |     Asset asset() => currentAsset; | ||||||
|  |  | ||||||
| @@ -161,12 +171,13 @@ class GalleryViewerPage extends HookConsumerWidget { | |||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void handleDelete(Asset deleteAsset) { |     void handleDelete(Asset deleteAsset) async { | ||||||
|       showDialog( |       Future<bool> onDelete(bool force) async { | ||||||
|         context: context, |         final isDeleted = await ref.read(assetProvider.notifier).deleteAssets( | ||||||
|         builder: (BuildContext _) { |           {deleteAsset}, | ||||||
|           return DeleteDialog( |           force: force, | ||||||
|             onDelete: () { |         ); | ||||||
|  |         if (isDeleted) { | ||||||
|           if (totalAssets == 1) { |           if (totalAssets == 1) { | ||||||
|             // Handle only one asset |             // Handle only one asset | ||||||
|             AutoRouter.of(context).pop(); |             AutoRouter.of(context).pop(); | ||||||
| @@ -177,9 +188,30 @@ class GalleryViewerPage extends HookConsumerWidget { | |||||||
|               curve: Curves.fastLinearToSlowEaseIn, |               curve: Curves.fastLinearToSlowEaseIn, | ||||||
|             ); |             ); | ||||||
|           } |           } | ||||||
|               ref.watch(assetProvider.notifier).deleteAssets({deleteAsset}); |         } | ||||||
|             }, |         return isDeleted; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Asset is trashed | ||||||
|  |       if (isTrashEnabled && !isFromTrash) { | ||||||
|  |         final isDeleted = await onDelete(false); | ||||||
|  |         // Can only trash assets stored in server. Local assets are always permanently removed for now | ||||||
|  |         if (context.mounted && isDeleted && deleteAsset.isRemote) { | ||||||
|  |           ImmichToast.show( | ||||||
|  |             durationInSecond: 1, | ||||||
|  |             context: context, | ||||||
|  |             msg: 'Asset trashed', | ||||||
|  |             gravity: ToastGravity.BOTTOM, | ||||||
|           ); |           ); | ||||||
|  |         } | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Asset is permanently removed | ||||||
|  |       showDialog( | ||||||
|  |         context: context, | ||||||
|  |         builder: (BuildContext _) { | ||||||
|  |           return DeleteDialog(onDelete: () => onDelete(true)); | ||||||
|         }, |         }, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -81,7 +81,9 @@ class BackupControllerPage extends HookConsumerWidget { | |||||||
|           context: context, |           context: context, | ||||||
|           msg: "Deleting ${assets.length} assets on the server...", |           msg: "Deleting ${assets.length} assets on the server...", | ||||||
|         ); |         ); | ||||||
|         await ref.read(assetProvider.notifier).deleteAssets(assets); |         await ref | ||||||
|  |             .read(assetProvider.notifier) | ||||||
|  |             .deleteAssets(assets, force: true); | ||||||
|         ImmichToast.show( |         ImmichToast.show( | ||||||
|           context: context, |           context: context, | ||||||
|           msg: "Deleted ${assets.length} assets on the server. " |           msg: "Deleted ${assets.length} assets on the server. " | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ final favoriteAssetsProvider = StreamProvider<RenderList>((ref) async* { | |||||||
|       .ownerIdEqualToAnyChecksum(user.isarId) |       .ownerIdEqualToAnyChecksum(user.isarId) | ||||||
|       .filter() |       .filter() | ||||||
|       .isFavoriteEqualTo(true) |       .isFavoriteEqualTo(true) | ||||||
|  |       .isTrashedEqualTo(false) | ||||||
|       .sortByFileCreatedAt(); |       .sortByFileCreatedAt(); | ||||||
|   final settings = ref.watch(appSettingsServiceProvider); |   final settings = ref.watch(appSettingsServiceProvider); | ||||||
|   final groupBy = |   final groupBy = | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import 'package:immich_mobile/modules/album/ui/add_to_album_sliverlist.dart'; | |||||||
| import 'package:immich_mobile/modules/home/ui/delete_dialog.dart'; | import 'package:immich_mobile/modules/home/ui/delete_dialog.dart'; | ||||||
| import 'package:immich_mobile/modules/home/ui/upload_dialog.dart'; | import 'package:immich_mobile/modules/home/ui/upload_dialog.dart'; | ||||||
| import 'package:immich_mobile/shared/models/asset.dart'; | import 'package:immich_mobile/shared/models/asset.dart'; | ||||||
|  | import 'package:immich_mobile/shared/providers/server_info.provider.dart'; | ||||||
| import 'package:immich_mobile/shared/ui/drag_sheet.dart'; | import 'package:immich_mobile/shared/ui/drag_sheet.dart'; | ||||||
| import 'package:immich_mobile/shared/models/album.dart'; | import 'package:immich_mobile/shared/models/album.dart'; | ||||||
|  |  | ||||||
| @@ -43,6 +44,8 @@ class ControlBottomAppBar extends ConsumerWidget { | |||||||
|   Widget build(BuildContext context, WidgetRef ref) { |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|     var isDarkMode = Theme.of(context).brightness == Brightness.dark; |     var isDarkMode = Theme.of(context).brightness == Brightness.dark; | ||||||
|     var hasRemote = selectionAssetState == AssetState.remote; |     var hasRemote = selectionAssetState == AssetState.remote; | ||||||
|  |     final trashEnabled = | ||||||
|  |         ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); | ||||||
|  |  | ||||||
|     Widget renderActionButtons() { |     Widget renderActionButtons() { | ||||||
|       return Row( |       return Row( | ||||||
| @@ -70,14 +73,20 @@ class ControlBottomAppBar extends ConsumerWidget { | |||||||
|             iconData: Icons.delete_outline_rounded, |             iconData: Icons.delete_outline_rounded, | ||||||
|             label: "control_bottom_app_bar_delete".tr(), |             label: "control_bottom_app_bar_delete".tr(), | ||||||
|             onPressed: enabled |             onPressed: enabled | ||||||
|                 ? () => showDialog( |                 ? () { | ||||||
|  |                     if (!trashEnabled) { | ||||||
|  |                       showDialog( | ||||||
|                         context: context, |                         context: context, | ||||||
|                         builder: (BuildContext context) { |                         builder: (BuildContext context) { | ||||||
|                           return DeleteDialog( |                           return DeleteDialog( | ||||||
|                             onDelete: onDelete, |                             onDelete: onDelete, | ||||||
|                           ); |                           ); | ||||||
|                         }, |                         }, | ||||||
|                     ) |                       ); | ||||||
|  |                     } else { | ||||||
|  |                       onDelete(); | ||||||
|  |                     } | ||||||
|  |                   } | ||||||
|                 : null, |                 : null, | ||||||
|           ), |           ), | ||||||
|           if (!hasRemote) |           if (!hasRemote) | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import 'package:immich_mobile/modules/home/ui/profile_drawer/server_info_box.dar | |||||||
| import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; | import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; | ||||||
| import 'package:immich_mobile/routing/router.dart'; | import 'package:immich_mobile/routing/router.dart'; | ||||||
| import 'package:immich_mobile/shared/providers/asset.provider.dart'; | import 'package:immich_mobile/shared/providers/asset.provider.dart'; | ||||||
|  | import 'package:immich_mobile/shared/providers/server_info.provider.dart'; | ||||||
| import 'package:immich_mobile/shared/providers/websocket.provider.dart'; | import 'package:immich_mobile/shared/providers/websocket.provider.dart'; | ||||||
|  |  | ||||||
| class ProfileDrawer extends HookConsumerWidget { | class ProfileDrawer extends HookConsumerWidget { | ||||||
| @@ -16,6 +17,9 @@ class ProfileDrawer extends HookConsumerWidget { | |||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context, WidgetRef ref) { |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|  |     final trashEnabled = | ||||||
|  |         ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); | ||||||
|  |  | ||||||
|     buildSignOutButton() { |     buildSignOutButton() { | ||||||
|       return ListTile( |       return ListTile( | ||||||
|         leading: SizedBox( |         leading: SizedBox( | ||||||
| @@ -91,6 +95,29 @@ class ProfileDrawer extends HookConsumerWidget { | |||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     buildTrashButton() { | ||||||
|  |       return ListTile( | ||||||
|  |         leading: SizedBox( | ||||||
|  |           height: double.infinity, | ||||||
|  |           child: Icon( | ||||||
|  |             Icons.delete_rounded, | ||||||
|  |             color: Theme.of(context).textTheme.labelMedium?.color, | ||||||
|  |             size: 20, | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |         title: Text( | ||||||
|  |           "Trash", | ||||||
|  |           style: Theme.of(context) | ||||||
|  |               .textTheme | ||||||
|  |               .labelLarge | ||||||
|  |               ?.copyWith(fontWeight: FontWeight.bold), | ||||||
|  |         ).tr(), | ||||||
|  |         onTap: () { | ||||||
|  |           AutoRouter.of(context).push(const TrashRoute()); | ||||||
|  |         }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return Drawer( |     return Drawer( | ||||||
|       shape: const RoundedRectangleBorder( |       shape: const RoundedRectangleBorder( | ||||||
|         borderRadius: BorderRadius.zero, |         borderRadius: BorderRadius.zero, | ||||||
| @@ -105,6 +132,7 @@ class ProfileDrawer extends HookConsumerWidget { | |||||||
|               const ProfileDrawerHeader(), |               const ProfileDrawerHeader(), | ||||||
|               buildSettingButton(), |               buildSettingButton(), | ||||||
|               buildAppLogButton(), |               buildAppLogButton(), | ||||||
|  |               if (trashEnabled) buildTrashButton(), | ||||||
|               buildSignOutButton(), |               buildSignOutButton(), | ||||||
|             ], |             ], | ||||||
|           ), |           ), | ||||||
|   | |||||||
| @@ -43,6 +43,8 @@ class HomePage extends HookConsumerWidget { | |||||||
|     final sharedAlbums = ref.watch(sharedAlbumProvider); |     final sharedAlbums = ref.watch(sharedAlbumProvider); | ||||||
|     final albumService = ref.watch(albumServiceProvider); |     final albumService = ref.watch(albumServiceProvider); | ||||||
|     final currentUser = ref.watch(currentUserProvider); |     final currentUser = ref.watch(currentUserProvider); | ||||||
|  |     final trashEnabled = | ||||||
|  |         ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); | ||||||
|  |  | ||||||
|     final tipOneOpacity = useState(0.0); |     final tipOneOpacity = useState(0.0); | ||||||
|     final refreshCount = useState(0); |     final refreshCount = useState(0); | ||||||
| @@ -139,7 +141,21 @@ class HomePage extends HookConsumerWidget { | |||||||
|       void onDelete() async { |       void onDelete() async { | ||||||
|         processing.value = true; |         processing.value = true; | ||||||
|         try { |         try { | ||||||
|           await ref.read(assetProvider.notifier).deleteAssets(selection.value); |           await ref | ||||||
|  |               .read(assetProvider.notifier) | ||||||
|  |               .deleteAssets(selection.value, force: !trashEnabled); | ||||||
|  |  | ||||||
|  |           final hasRemote = selection.value.any((a) => a.isRemote); | ||||||
|  |           final assetOrAssets = selection.value.length > 1 ? 'assets' : 'asset'; | ||||||
|  |           final trashOrRemoved = | ||||||
|  |               !trashEnabled ? 'deleted permanently' : 'trashed'; | ||||||
|  |           if (hasRemote) { | ||||||
|  |             ImmichToast.show( | ||||||
|  |               context: context, | ||||||
|  |               msg: '${selection.value.length} $assetOrAssets $trashOrRemoved', | ||||||
|  |               gravity: ToastGravity.BOTTOM, | ||||||
|  |             ); | ||||||
|  |           } | ||||||
|           selectionEnabledHook.value = false; |           selectionEnabledHook.value = false; | ||||||
|         } finally { |         } finally { | ||||||
|           processing.value = false; |           processing.value = false; | ||||||
|   | |||||||
							
								
								
									
										144
									
								
								mobile/lib/modules/trash/providers/trashed_asset.provider.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								mobile/lib/modules/trash/providers/trashed_asset.provider.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | |||||||
|  | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
|  | import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart'; | ||||||
|  | import 'package:immich_mobile/modules/trash/services/trash.service.dart'; | ||||||
|  | import 'package:immich_mobile/shared/models/asset.dart'; | ||||||
|  | import 'package:immich_mobile/shared/models/exif_info.dart'; | ||||||
|  | import 'package:immich_mobile/shared/providers/asset.provider.dart'; | ||||||
|  | import 'package:immich_mobile/shared/providers/db.provider.dart'; | ||||||
|  | import 'package:immich_mobile/shared/providers/user.provider.dart'; | ||||||
|  | import 'package:isar/isar.dart'; | ||||||
|  | import 'package:logging/logging.dart'; | ||||||
|  |  | ||||||
|  | class TrashNotifier extends StateNotifier<bool> { | ||||||
|  |   final Isar _db; | ||||||
|  |   final Ref _ref; | ||||||
|  |   final TrashService _trashService; | ||||||
|  |   final _log = Logger('TrashNotifier'); | ||||||
|  |  | ||||||
|  |   TrashNotifier( | ||||||
|  |     this._trashService, | ||||||
|  |     this._db, | ||||||
|  |     this._ref, | ||||||
|  |   ) : super(false); | ||||||
|  |  | ||||||
|  |   Future<void> emptyTrash() async { | ||||||
|  |     try { | ||||||
|  |       final user = _ref.read(currentUserProvider); | ||||||
|  |       if (user == null) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       await _trashService.emptyTrash(); | ||||||
|  |  | ||||||
|  |       final dbIds = await _db.assets | ||||||
|  |           .where() | ||||||
|  |           .remoteIdIsNotNull() | ||||||
|  |           .filter() | ||||||
|  |           .ownerIdEqualTo(user.isarId) | ||||||
|  |           .isTrashedEqualTo(true) | ||||||
|  |           .idProperty() | ||||||
|  |           .findAll(); | ||||||
|  |  | ||||||
|  |       await _db.writeTxn(() async { | ||||||
|  |         await _db.exifInfos.deleteAll(dbIds); | ||||||
|  |         await _db.assets.deleteAll(dbIds); | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       // Refresh assets in background | ||||||
|  |       Future.delayed( | ||||||
|  |         const Duration(seconds: 4), | ||||||
|  |         () async => await _ref.read(assetProvider.notifier).getAllAsset(), | ||||||
|  |       ); | ||||||
|  |     } catch (error, stack) { | ||||||
|  |       _log.severe("Cannot empty trash ${error.toString()}", error, stack); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Future<bool> restoreAssets(Iterable<Asset> assetList) async { | ||||||
|  |     try { | ||||||
|  |       final result = await _trashService.restoreAssets(assetList); | ||||||
|  |  | ||||||
|  |       if (result) { | ||||||
|  |         final remoteAssets = assetList.where((a) => a.isRemote).toList(); | ||||||
|  |  | ||||||
|  |         final updatedAssets = remoteAssets.map((e) { | ||||||
|  |           e.isTrashed = false; | ||||||
|  |           return e; | ||||||
|  |         }).toList(); | ||||||
|  |  | ||||||
|  |         await _db.writeTxn(() async { | ||||||
|  |           await _db.assets.putAll(updatedAssets); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // Refresh assets in background | ||||||
|  |         Future.delayed( | ||||||
|  |           const Duration(seconds: 4), | ||||||
|  |           () async => await _ref.read(assetProvider.notifier).getAllAsset(), | ||||||
|  |         ); | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |     } catch (error, stack) { | ||||||
|  |       _log.severe("Cannot restore trash ${error.toString()}", error, stack); | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Future<void> restoreTrash() async { | ||||||
|  |     try { | ||||||
|  |       final user = _ref.read(currentUserProvider); | ||||||
|  |       if (user == null) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       await _trashService.restoreTrash(); | ||||||
|  |  | ||||||
|  |       final assets = await _db.assets | ||||||
|  |           .where() | ||||||
|  |           .remoteIdIsNotNull() | ||||||
|  |           .filter() | ||||||
|  |           .ownerIdEqualTo(user.isarId) | ||||||
|  |           .isTrashedEqualTo(true) | ||||||
|  |           .findAll(); | ||||||
|  |  | ||||||
|  |       final updatedAssets = assets.map((e) { | ||||||
|  |         e.isTrashed = false; | ||||||
|  |         return e; | ||||||
|  |       }).toList(); | ||||||
|  |  | ||||||
|  |       await _db.writeTxn(() async { | ||||||
|  |         await _db.assets.putAll(updatedAssets); | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       // Refresh assets in background | ||||||
|  |       Future.delayed( | ||||||
|  |         const Duration(seconds: 4), | ||||||
|  |         () async => await _ref.read(assetProvider.notifier).getAllAsset(), | ||||||
|  |       ); | ||||||
|  |     } catch (error, stack) { | ||||||
|  |       _log.severe("Cannot restore trash ${error.toString()}", error, stack); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | final trashProvider = StateNotifierProvider<TrashNotifier, bool>((ref) { | ||||||
|  |   return TrashNotifier( | ||||||
|  |     ref.watch(trashServiceProvider), | ||||||
|  |     ref.watch(dbProvider), | ||||||
|  |     ref, | ||||||
|  |   ); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | final trashedAssetsProvider = StreamProvider<RenderList>((ref) async* { | ||||||
|  |   final user = ref.read(currentUserProvider); | ||||||
|  |   if (user == null) return; | ||||||
|  |   final query = ref | ||||||
|  |       .watch(dbProvider) | ||||||
|  |       .assets | ||||||
|  |       .filter() | ||||||
|  |       .ownerIdEqualTo(user.isarId) | ||||||
|  |       .isTrashedEqualTo(true) | ||||||
|  |       .sortByFileCreatedAt(); | ||||||
|  |   const groupBy = GroupAssetsBy.none; | ||||||
|  |   yield await RenderList.fromQuery(query, groupBy); | ||||||
|  |   await for (final _ in query.watchLazy()) { | ||||||
|  |     yield await RenderList.fromQuery(query, groupBy); | ||||||
|  |   } | ||||||
|  | }); | ||||||
							
								
								
									
										48
									
								
								mobile/lib/modules/trash/services/trash.service.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								mobile/lib/modules/trash/services/trash.service.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
|  | import 'package:immich_mobile/shared/models/asset.dart'; | ||||||
|  | import 'package:immich_mobile/shared/providers/api.provider.dart'; | ||||||
|  | import 'package:immich_mobile/shared/services/api.service.dart'; | ||||||
|  | import 'package:logging/logging.dart'; | ||||||
|  | import 'package:openapi/api.dart'; | ||||||
|  |  | ||||||
|  | final trashServiceProvider = Provider<TrashService>((ref) { | ||||||
|  |   return TrashService( | ||||||
|  |     ref.watch(apiServiceProvider), | ||||||
|  |   ); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | class TrashService { | ||||||
|  |   final _log = Logger("TrashService"); | ||||||
|  |  | ||||||
|  |   final ApiService _apiService; | ||||||
|  |  | ||||||
|  |   TrashService(this._apiService); | ||||||
|  |  | ||||||
|  |   Future<bool> restoreAssets(Iterable<Asset> assetList) async { | ||||||
|  |     try { | ||||||
|  |       List<String> remoteIds = | ||||||
|  |           assetList.where((a) => a.isRemote).map((e) => e.remoteId!).toList(); | ||||||
|  |       await _apiService.assetApi.restoreAssets(BulkIdsDto(ids: remoteIds)); | ||||||
|  |       return true; | ||||||
|  |     } catch (error, stack) { | ||||||
|  |       _log.severe("Cannot restore assets ${error.toString()}", error, stack); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Future<void> emptyTrash() async { | ||||||
|  |     try { | ||||||
|  |       await _apiService.assetApi.emptyTrash(); | ||||||
|  |     } catch (error, stack) { | ||||||
|  |       _log.severe("Cannot empty trash ${error.toString()}", error, stack); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Future<void> restoreTrash() async { | ||||||
|  |     try { | ||||||
|  |       await _apiService.assetApi.restoreTrash(); | ||||||
|  |     } catch (error, stack) { | ||||||
|  |       _log.severe("Cannot restore trash ${error.toString()}", error, stack); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										276
									
								
								mobile/lib/modules/trash/views/trash_page.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								mobile/lib/modules/trash/views/trash_page.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,276 @@ | |||||||
|  | import 'package:auto_route/auto_route.dart'; | ||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_hooks/flutter_hooks.dart'; | ||||||
|  | import 'package:fluttertoast/fluttertoast.dart'; | ||||||
|  | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
|  | import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart'; | ||||||
|  | import 'package:immich_mobile/modules/home/ui/delete_dialog.dart'; | ||||||
|  | import 'package:immich_mobile/modules/trash/providers/trashed_asset.provider.dart'; | ||||||
|  | import 'package:immich_mobile/shared/models/asset.dart'; | ||||||
|  | import 'package:immich_mobile/shared/providers/asset.provider.dart'; | ||||||
|  | import 'package:immich_mobile/shared/providers/server_info.provider.dart'; | ||||||
|  | import 'package:immich_mobile/shared/ui/confirm_dialog.dart'; | ||||||
|  | import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; | ||||||
|  | import 'package:immich_mobile/shared/ui/immich_toast.dart'; | ||||||
|  |  | ||||||
|  | class TrashPage extends HookConsumerWidget { | ||||||
|  |   const TrashPage({super.key}); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|  |     final trashedAssets = ref.watch(trashedAssetsProvider); | ||||||
|  |     final trashDays = | ||||||
|  |         ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays)); | ||||||
|  |     final selectionEnabledHook = useState(false); | ||||||
|  |     final selection = useState(<Asset>{}); | ||||||
|  |     final processing = useState(false); | ||||||
|  |  | ||||||
|  |     void selectionListener( | ||||||
|  |       bool multiselect, | ||||||
|  |       Set<Asset> selectedAssets, | ||||||
|  |     ) { | ||||||
|  |       selectionEnabledHook.value = multiselect; | ||||||
|  |       selection.value = selectedAssets; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     onEmptyTrash() async { | ||||||
|  |       processing.value = true; | ||||||
|  |       await ref.read(trashProvider.notifier).emptyTrash(); | ||||||
|  |       processing.value = false; | ||||||
|  |       selectionEnabledHook.value = false; | ||||||
|  |       if (context.mounted) { | ||||||
|  |         ImmichToast.show( | ||||||
|  |           context: context, | ||||||
|  |           msg: 'Emptied trash', | ||||||
|  |           gravity: ToastGravity.BOTTOM, | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     handleEmptyTrash() async { | ||||||
|  |       await showDialog( | ||||||
|  |         context: context, | ||||||
|  |         builder: (context) => ConfirmDialog( | ||||||
|  |           onOk: () => onEmptyTrash(), | ||||||
|  |           title: "trash_page_empty_trash_btn".tr(), | ||||||
|  |           ok: "trash_page_empty_trash_dialog_ok".tr(), | ||||||
|  |           content: "trash_page_empty_trash_dialog_content".tr(), | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Future<void> onPermanentlyDelete() async { | ||||||
|  |       processing.value = true; | ||||||
|  |       try { | ||||||
|  |         if (selection.value.isNotEmpty) { | ||||||
|  |           await ref | ||||||
|  |               .read(assetProvider.notifier) | ||||||
|  |               .deleteAssets(selection.value, force: true); | ||||||
|  |  | ||||||
|  |           final assetOrAssets = selection.value.length > 1 ? 'assets' : 'asset'; | ||||||
|  |           if (context.mounted) { | ||||||
|  |             ImmichToast.show( | ||||||
|  |               context: context, | ||||||
|  |               msg: | ||||||
|  |                   '${selection.value.length} $assetOrAssets deleted permanently', | ||||||
|  |               gravity: ToastGravity.BOTTOM, | ||||||
|  |             ); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } finally { | ||||||
|  |         processing.value = false; | ||||||
|  |         selectionEnabledHook.value = false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     handlePermanentDelete() async { | ||||||
|  |       await showDialog( | ||||||
|  |         context: context, | ||||||
|  |         builder: (context) => DeleteDialog( | ||||||
|  |           onDelete: () => onPermanentlyDelete(), | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Future<void> handleRestoreAll() async { | ||||||
|  |       processing.value = true; | ||||||
|  |       await ref.read(trashProvider.notifier).restoreTrash(); | ||||||
|  |       processing.value = false; | ||||||
|  |       selectionEnabledHook.value = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Future<void> handleRestore() async { | ||||||
|  |       processing.value = true; | ||||||
|  |       try { | ||||||
|  |         if (selection.value.isNotEmpty) { | ||||||
|  |           final result = await ref | ||||||
|  |               .read(trashProvider.notifier) | ||||||
|  |               .restoreAssets(selection.value); | ||||||
|  |  | ||||||
|  |           final assetOrAssets = selection.value.length > 1 ? 'assets' : 'asset'; | ||||||
|  |           if (result && context.mounted) { | ||||||
|  |             ImmichToast.show( | ||||||
|  |               context: context, | ||||||
|  |               msg: | ||||||
|  |                   '${selection.value.length} $assetOrAssets restored successfully', | ||||||
|  |               gravity: ToastGravity.BOTTOM, | ||||||
|  |             ); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } finally { | ||||||
|  |         processing.value = false; | ||||||
|  |         selectionEnabledHook.value = false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     String getAppBarTitle(String count) { | ||||||
|  |       if (selectionEnabledHook.value) { | ||||||
|  |         return selection.value.isNotEmpty | ||||||
|  |             ? "${selection.value.length}" | ||||||
|  |             : "trash_page_select_assets_btn".tr(); | ||||||
|  |       } | ||||||
|  |       return 'trash_page_title'.tr(args: [count]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     AppBar buildAppBar(String count) { | ||||||
|  |       return AppBar( | ||||||
|  |         leading: IconButton( | ||||||
|  |           onPressed: !selectionEnabledHook.value | ||||||
|  |               ? () => AutoRouter.of(context).pop() | ||||||
|  |               : () { | ||||||
|  |                   selectionEnabledHook.value = false; | ||||||
|  |                   selection.value = {}; | ||||||
|  |                 }, | ||||||
|  |           icon: !selectionEnabledHook.value | ||||||
|  |               ? const Icon(Icons.arrow_back_ios_rounded) | ||||||
|  |               : const Icon(Icons.close_rounded), | ||||||
|  |         ), | ||||||
|  |         centerTitle: !selectionEnabledHook.value, | ||||||
|  |         automaticallyImplyLeading: false, | ||||||
|  |         title: Text(getAppBarTitle(count)), | ||||||
|  |         actions: <Widget>[ | ||||||
|  |           if (!selectionEnabledHook.value) | ||||||
|  |             PopupMenuButton<void Function()>( | ||||||
|  |               itemBuilder: (context) { | ||||||
|  |                 return [ | ||||||
|  |                   PopupMenuItem( | ||||||
|  |                     value: () => selectionEnabledHook.value = true, | ||||||
|  |                     child: const Text('trash_page_select_btn').tr(), | ||||||
|  |                   ), | ||||||
|  |                   PopupMenuItem( | ||||||
|  |                     value: handleEmptyTrash, | ||||||
|  |                     child: const Text('trash_page_empty_trash_btn').tr(), | ||||||
|  |                   ), | ||||||
|  |                 ]; | ||||||
|  |               }, | ||||||
|  |               onSelected: (fn) => fn(), | ||||||
|  |             ), | ||||||
|  |         ], | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Widget buildBottomBar() { | ||||||
|  |       return SafeArea( | ||||||
|  |         child: Align( | ||||||
|  |           alignment: Alignment.bottomCenter, | ||||||
|  |           child: SizedBox( | ||||||
|  |             height: 64, | ||||||
|  |             child: Container( | ||||||
|  |               color: Theme.of(context).canvasColor, | ||||||
|  |               child: Row( | ||||||
|  |                 mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||||||
|  |                 children: [ | ||||||
|  |                   TextButton.icon( | ||||||
|  |                     icon: Icon( | ||||||
|  |                       Icons.delete_forever, | ||||||
|  |                       color: Colors.red[400], | ||||||
|  |                     ), | ||||||
|  |                     label: Text( | ||||||
|  |                       selection.value.isEmpty | ||||||
|  |                           ? 'trash_page_delete_all'.tr() | ||||||
|  |                           : 'trash_page_delete'.tr(), | ||||||
|  |                       style: TextStyle( | ||||||
|  |                         fontSize: 14, | ||||||
|  |                         color: Colors.red[400], | ||||||
|  |                         fontWeight: FontWeight.bold, | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                     onPressed: processing.value | ||||||
|  |                         ? null | ||||||
|  |                         : selection.value.isEmpty | ||||||
|  |                             ? handleEmptyTrash | ||||||
|  |                             : handlePermanentDelete, | ||||||
|  |                   ), | ||||||
|  |                   TextButton.icon( | ||||||
|  |                     icon: const Icon( | ||||||
|  |                       Icons.history_rounded, | ||||||
|  |                     ), | ||||||
|  |                     label: Text( | ||||||
|  |                       selection.value.isEmpty | ||||||
|  |                           ? 'trash_page_restore_all'.tr() | ||||||
|  |                           : 'trash_page_restore'.tr(), | ||||||
|  |                       style: const TextStyle( | ||||||
|  |                         fontSize: 14, | ||||||
|  |                         fontWeight: FontWeight.bold, | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                     onPressed: processing.value | ||||||
|  |                         ? null | ||||||
|  |                         : selection.value.isEmpty | ||||||
|  |                             ? handleRestoreAll | ||||||
|  |                             : handleRestore, | ||||||
|  |                   ), | ||||||
|  |                 ], | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return trashedAssets.when( | ||||||
|  |       loading: () => Scaffold( | ||||||
|  |         appBar: buildAppBar("?"), | ||||||
|  |         body: const Center(child: CircularProgressIndicator()), | ||||||
|  |       ), | ||||||
|  |       error: (error, stackTrace) => Scaffold( | ||||||
|  |         appBar: buildAppBar("!"), | ||||||
|  |         body: Center(child: Text(error.toString())), | ||||||
|  |       ), | ||||||
|  |       data: (data) => Scaffold( | ||||||
|  |         appBar: buildAppBar(data.totalAssets.toString()), | ||||||
|  |         body: data.isEmpty | ||||||
|  |             ? Center( | ||||||
|  |                 child: Text('trash_page_no_assets'.tr()), | ||||||
|  |               ) | ||||||
|  |             : Stack( | ||||||
|  |                 children: [ | ||||||
|  |                   SafeArea( | ||||||
|  |                     child: ImmichAssetGrid( | ||||||
|  |                       renderList: data, | ||||||
|  |                       listener: selectionListener, | ||||||
|  |                       selectionActive: selectionEnabledHook.value, | ||||||
|  |                       showMultiSelectIndicator: false, | ||||||
|  |                       topWidget: Padding( | ||||||
|  |                         padding: const EdgeInsets.only( | ||||||
|  |                           top: 24, | ||||||
|  |                           bottom: 24, | ||||||
|  |                           left: 12, | ||||||
|  |                           right: 12, | ||||||
|  |                         ), | ||||||
|  |                         child: const Text( | ||||||
|  |                           "trash_page_info", | ||||||
|  |                         ).tr(args: ["$trashDays"]), | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |                   if (selectionEnabledHook.value) buildBottomBar(), | ||||||
|  |                   if (processing.value) | ||||||
|  |                     const Center(child: ImmichLoadingIndicator()), | ||||||
|  |                 ], | ||||||
|  |               ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -28,6 +28,7 @@ import 'package:immich_mobile/modules/login/views/change_password_page.dart'; | |||||||
| import 'package:immich_mobile/modules/login/views/login_page.dart'; | import 'package:immich_mobile/modules/login/views/login_page.dart'; | ||||||
| import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart'; | import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart'; | ||||||
| import 'package:immich_mobile/modules/onboarding/views/permission_onboarding_page.dart'; | import 'package:immich_mobile/modules/onboarding/views/permission_onboarding_page.dart'; | ||||||
|  | import 'package:immich_mobile/modules/trash/views/trash_page.dart'; | ||||||
| import 'package:immich_mobile/modules/search/views/all_motion_videos_page.dart'; | import 'package:immich_mobile/modules/search/views/all_motion_videos_page.dart'; | ||||||
| import 'package:immich_mobile/modules/search/views/all_people_page.dart'; | import 'package:immich_mobile/modules/search/views/all_people_page.dart'; | ||||||
| import 'package:immich_mobile/modules/search/views/all_videos_page.dart'; | import 'package:immich_mobile/modules/search/views/all_videos_page.dart'; | ||||||
| @@ -155,6 +156,7 @@ part 'router.gr.dart'; | |||||||
|     AutoRoute(page: MemoryPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute(page: MemoryPage, guards: [AuthGuard, DuplicateGuard]), | ||||||
|     AutoRoute(page: MapPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute(page: MapPage, guards: [AuthGuard, DuplicateGuard]), | ||||||
|     AutoRoute(page: AlbumOptionsPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute(page: AlbumOptionsPage, guards: [AuthGuard, DuplicateGuard]), | ||||||
|  |     AutoRoute(page: TrashPage, guards: [AuthGuard, DuplicateGuard]), | ||||||
|   ], |   ], | ||||||
| ) | ) | ||||||
| class AppRouter extends _$AppRouter { | class AppRouter extends _$AppRouter { | ||||||
|   | |||||||
| @@ -312,6 +312,12 @@ class _$AppRouter extends RootStackRouter { | |||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
|     }, |     }, | ||||||
|  |     TrashRoute.name: (routeData) { | ||||||
|  |       return MaterialPageX<dynamic>( | ||||||
|  |         routeData: routeData, | ||||||
|  |         child: const TrashPage(), | ||||||
|  |       ); | ||||||
|  |     }, | ||||||
|     HomeRoute.name: (routeData) { |     HomeRoute.name: (routeData) { | ||||||
|       return MaterialPageX<dynamic>( |       return MaterialPageX<dynamic>( | ||||||
|         routeData: routeData, |         routeData: routeData, | ||||||
| @@ -624,6 +630,14 @@ class _$AppRouter extends RootStackRouter { | |||||||
|             duplicateGuard, |             duplicateGuard, | ||||||
|           ], |           ], | ||||||
|         ), |         ), | ||||||
|  |         RouteConfig( | ||||||
|  |           TrashRoute.name, | ||||||
|  |           path: '/trash-page', | ||||||
|  |           guards: [ | ||||||
|  |             authGuard, | ||||||
|  |             duplicateGuard, | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|       ]; |       ]; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1394,6 +1408,18 @@ class AlbumOptionsRouteArgs { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// generated route for | ||||||
|  | /// [TrashPage] | ||||||
|  | class TrashRoute extends PageRouteInfo<void> { | ||||||
|  |   const TrashRoute() | ||||||
|  |       : super( | ||||||
|  |           TrashRoute.name, | ||||||
|  |           path: '/trash-page', | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |   static const String name = 'TrashRoute'; | ||||||
|  | } | ||||||
|  |  | ||||||
| /// generated route for | /// generated route for | ||||||
| /// [HomePage] | /// [HomePage] | ||||||
| class HomeRoute extends PageRouteInfo<void> { | class HomeRoute extends PageRouteInfo<void> { | ||||||
|   | |||||||
| @@ -30,7 +30,8 @@ class Asset { | |||||||
|         exifInfo = |         exifInfo = | ||||||
|             remote.exifInfo != null ? ExifInfo.fromDto(remote.exifInfo!) : null, |             remote.exifInfo != null ? ExifInfo.fromDto(remote.exifInfo!) : null, | ||||||
|         isFavorite = remote.isFavorite, |         isFavorite = remote.isFavorite, | ||||||
|         isArchived = remote.isArchived; |         isArchived = remote.isArchived, | ||||||
|  |         isTrashed = remote.isTrashed; | ||||||
|  |  | ||||||
|   Asset.local(AssetEntity local, List<int> hash) |   Asset.local(AssetEntity local, List<int> hash) | ||||||
|       : localId = local.id, |       : localId = local.id, | ||||||
| @@ -45,6 +46,7 @@ class Asset { | |||||||
|         updatedAt = local.modifiedDateTime, |         updatedAt = local.modifiedDateTime, | ||||||
|         isFavorite = local.isFavorite, |         isFavorite = local.isFavorite, | ||||||
|         isArchived = false, |         isArchived = false, | ||||||
|  |         isTrashed = false, | ||||||
|         fileCreatedAt = local.createDateTime { |         fileCreatedAt = local.createDateTime { | ||||||
|     if (fileCreatedAt.year == 1970) { |     if (fileCreatedAt.year == 1970) { | ||||||
|       fileCreatedAt = fileModifiedAt; |       fileCreatedAt = fileModifiedAt; | ||||||
| @@ -74,6 +76,7 @@ class Asset { | |||||||
|     this.exifInfo, |     this.exifInfo, | ||||||
|     required this.isFavorite, |     required this.isFavorite, | ||||||
|     required this.isArchived, |     required this.isArchived, | ||||||
|  |     required this.isTrashed, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   @ignore |   @ignore | ||||||
| @@ -138,6 +141,8 @@ class Asset { | |||||||
|  |  | ||||||
|   bool isArchived; |   bool isArchived; | ||||||
|  |  | ||||||
|  |   bool isTrashed; | ||||||
|  |  | ||||||
|   @ignore |   @ignore | ||||||
|   ExifInfo? exifInfo; |   ExifInfo? exifInfo; | ||||||
|  |  | ||||||
| @@ -194,7 +199,8 @@ class Asset { | |||||||
|         livePhotoVideoId == other.livePhotoVideoId && |         livePhotoVideoId == other.livePhotoVideoId && | ||||||
|         isFavorite == other.isFavorite && |         isFavorite == other.isFavorite && | ||||||
|         isLocal == other.isLocal && |         isLocal == other.isLocal && | ||||||
|         isArchived == other.isArchived; |         isArchived == other.isArchived && | ||||||
|  |         isTrashed == other.isTrashed; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
| @@ -216,7 +222,8 @@ class Asset { | |||||||
|       livePhotoVideoId.hashCode ^ |       livePhotoVideoId.hashCode ^ | ||||||
|       isFavorite.hashCode ^ |       isFavorite.hashCode ^ | ||||||
|       isLocal.hashCode ^ |       isLocal.hashCode ^ | ||||||
|       isArchived.hashCode; |       isArchived.hashCode ^ | ||||||
|  |       isTrashed.hashCode; | ||||||
|  |  | ||||||
|   /// Returns `true` if this [Asset] can updated with values from parameter [a] |   /// Returns `true` if this [Asset] can updated with values from parameter [a] | ||||||
|   bool canUpdate(Asset a) { |   bool canUpdate(Asset a) { | ||||||
| @@ -229,8 +236,9 @@ class Asset { | |||||||
|         width == null && a.width != null || |         width == null && a.width != null || | ||||||
|         height == null && a.height != null || |         height == null && a.height != null || | ||||||
|         livePhotoVideoId == null && a.livePhotoVideoId != null || |         livePhotoVideoId == null && a.livePhotoVideoId != null || | ||||||
|         !isRemote && a.isRemote && isFavorite != a.isFavorite || |         isFavorite != a.isFavorite || | ||||||
|         !isRemote && a.isRemote && isArchived != a.isArchived; |         isArchived != a.isArchived || | ||||||
|  |         isTrashed != a.isTrashed; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /// Returns a new [Asset] with values from this and merged & updated with [a] |   /// Returns a new [Asset] with values from this and merged & updated with [a] | ||||||
| @@ -261,6 +269,7 @@ class Asset { | |||||||
|           livePhotoVideoId: livePhotoVideoId, |           livePhotoVideoId: livePhotoVideoId, | ||||||
|           isFavorite: isFavorite, |           isFavorite: isFavorite, | ||||||
|           isArchived: isArchived, |           isArchived: isArchived, | ||||||
|  |           isTrashed: isTrashed, | ||||||
|         ); |         ); | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
| @@ -275,6 +284,7 @@ class Asset { | |||||||
|           // isFavorite + isArchived are not set by device-only assets |           // isFavorite + isArchived are not set by device-only assets | ||||||
|           isFavorite: a.isFavorite, |           isFavorite: a.isFavorite, | ||||||
|           isArchived: a.isArchived, |           isArchived: a.isArchived, | ||||||
|  |           isTrashed: a.isTrashed, | ||||||
|           exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo, |           exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo, | ||||||
|         ); |         ); | ||||||
|       } else { |       } else { | ||||||
| @@ -306,6 +316,7 @@ class Asset { | |||||||
|     String? livePhotoVideoId, |     String? livePhotoVideoId, | ||||||
|     bool? isFavorite, |     bool? isFavorite, | ||||||
|     bool? isArchived, |     bool? isArchived, | ||||||
|  |     bool? isTrashed, | ||||||
|     ExifInfo? exifInfo, |     ExifInfo? exifInfo, | ||||||
|   }) => |   }) => | ||||||
|       Asset( |       Asset( | ||||||
| @@ -325,6 +336,7 @@ class Asset { | |||||||
|         livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, |         livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, | ||||||
|         isFavorite: isFavorite ?? this.isFavorite, |         isFavorite: isFavorite ?? this.isFavorite, | ||||||
|         isArchived: isArchived ?? this.isArchived, |         isArchived: isArchived ?? this.isArchived, | ||||||
|  |         isTrashed: isTrashed ?? this.isTrashed, | ||||||
|         exifInfo: exifInfo ?? this.exifInfo, |         exifInfo: exifInfo ?? this.exifInfo, | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
| @@ -378,7 +390,8 @@ class Asset { | |||||||
|   "storage": "$storage", |   "storage": "$storage", | ||||||
|   "width": ${width ?? "N/A"}, |   "width": ${width ?? "N/A"}, | ||||||
|   "height": ${height ?? "N/A"}, |   "height": ${height ?? "N/A"}, | ||||||
|   "isArchived": $isArchived |   "isArchived": $isArchived, | ||||||
|  |   "isTrashed": $isTrashed, | ||||||
| }"""; | }"""; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -57,39 +57,44 @@ const AssetSchema = CollectionSchema( | |||||||
|       name: r'isFavorite', |       name: r'isFavorite', | ||||||
|       type: IsarType.bool, |       type: IsarType.bool, | ||||||
|     ), |     ), | ||||||
|     r'livePhotoVideoId': PropertySchema( |     r'isTrashed': PropertySchema( | ||||||
|       id: 8, |       id: 8, | ||||||
|  |       name: r'isTrashed', | ||||||
|  |       type: IsarType.bool, | ||||||
|  |     ), | ||||||
|  |     r'livePhotoVideoId': PropertySchema( | ||||||
|  |       id: 9, | ||||||
|       name: r'livePhotoVideoId', |       name: r'livePhotoVideoId', | ||||||
|       type: IsarType.string, |       type: IsarType.string, | ||||||
|     ), |     ), | ||||||
|     r'localId': PropertySchema( |     r'localId': PropertySchema( | ||||||
|       id: 9, |       id: 10, | ||||||
|       name: r'localId', |       name: r'localId', | ||||||
|       type: IsarType.string, |       type: IsarType.string, | ||||||
|     ), |     ), | ||||||
|     r'ownerId': PropertySchema( |     r'ownerId': PropertySchema( | ||||||
|       id: 10, |       id: 11, | ||||||
|       name: r'ownerId', |       name: r'ownerId', | ||||||
|       type: IsarType.long, |       type: IsarType.long, | ||||||
|     ), |     ), | ||||||
|     r'remoteId': PropertySchema( |     r'remoteId': PropertySchema( | ||||||
|       id: 11, |       id: 12, | ||||||
|       name: r'remoteId', |       name: r'remoteId', | ||||||
|       type: IsarType.string, |       type: IsarType.string, | ||||||
|     ), |     ), | ||||||
|     r'type': PropertySchema( |     r'type': PropertySchema( | ||||||
|       id: 12, |       id: 13, | ||||||
|       name: r'type', |       name: r'type', | ||||||
|       type: IsarType.byte, |       type: IsarType.byte, | ||||||
|       enumMap: _AssettypeEnumValueMap, |       enumMap: _AssettypeEnumValueMap, | ||||||
|     ), |     ), | ||||||
|     r'updatedAt': PropertySchema( |     r'updatedAt': PropertySchema( | ||||||
|       id: 13, |       id: 14, | ||||||
|       name: r'updatedAt', |       name: r'updatedAt', | ||||||
|       type: IsarType.dateTime, |       type: IsarType.dateTime, | ||||||
|     ), |     ), | ||||||
|     r'width': PropertySchema( |     r'width': PropertySchema( | ||||||
|       id: 14, |       id: 15, | ||||||
|       name: r'width', |       name: r'width', | ||||||
|       type: IsarType.int, |       type: IsarType.int, | ||||||
|     ) |     ) | ||||||
| @@ -196,13 +201,14 @@ void _assetSerialize( | |||||||
|   writer.writeInt(offsets[5], object.height); |   writer.writeInt(offsets[5], object.height); | ||||||
|   writer.writeBool(offsets[6], object.isArchived); |   writer.writeBool(offsets[6], object.isArchived); | ||||||
|   writer.writeBool(offsets[7], object.isFavorite); |   writer.writeBool(offsets[7], object.isFavorite); | ||||||
|   writer.writeString(offsets[8], object.livePhotoVideoId); |   writer.writeBool(offsets[8], object.isTrashed); | ||||||
|   writer.writeString(offsets[9], object.localId); |   writer.writeString(offsets[9], object.livePhotoVideoId); | ||||||
|   writer.writeLong(offsets[10], object.ownerId); |   writer.writeString(offsets[10], object.localId); | ||||||
|   writer.writeString(offsets[11], object.remoteId); |   writer.writeLong(offsets[11], object.ownerId); | ||||||
|   writer.writeByte(offsets[12], object.type.index); |   writer.writeString(offsets[12], object.remoteId); | ||||||
|   writer.writeDateTime(offsets[13], object.updatedAt); |   writer.writeByte(offsets[13], object.type.index); | ||||||
|   writer.writeInt(offsets[14], object.width); |   writer.writeDateTime(offsets[14], object.updatedAt); | ||||||
|  |   writer.writeInt(offsets[15], object.width); | ||||||
| } | } | ||||||
|  |  | ||||||
| Asset _assetDeserialize( | Asset _assetDeserialize( | ||||||
| @@ -221,14 +227,15 @@ Asset _assetDeserialize( | |||||||
|     id: id, |     id: id, | ||||||
|     isArchived: reader.readBool(offsets[6]), |     isArchived: reader.readBool(offsets[6]), | ||||||
|     isFavorite: reader.readBool(offsets[7]), |     isFavorite: reader.readBool(offsets[7]), | ||||||
|     livePhotoVideoId: reader.readStringOrNull(offsets[8]), |     isTrashed: reader.readBool(offsets[8]), | ||||||
|     localId: reader.readStringOrNull(offsets[9]), |     livePhotoVideoId: reader.readStringOrNull(offsets[9]), | ||||||
|     ownerId: reader.readLong(offsets[10]), |     localId: reader.readStringOrNull(offsets[10]), | ||||||
|     remoteId: reader.readStringOrNull(offsets[11]), |     ownerId: reader.readLong(offsets[11]), | ||||||
|     type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[12])] ?? |     remoteId: reader.readStringOrNull(offsets[12]), | ||||||
|  |     type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[13])] ?? | ||||||
|         AssetType.other, |         AssetType.other, | ||||||
|     updatedAt: reader.readDateTime(offsets[13]), |     updatedAt: reader.readDateTime(offsets[14]), | ||||||
|     width: reader.readIntOrNull(offsets[14]), |     width: reader.readIntOrNull(offsets[15]), | ||||||
|   ); |   ); | ||||||
|   return object; |   return object; | ||||||
| } | } | ||||||
| @@ -257,19 +264,21 @@ P _assetDeserializeProp<P>( | |||||||
|     case 7: |     case 7: | ||||||
|       return (reader.readBool(offset)) as P; |       return (reader.readBool(offset)) as P; | ||||||
|     case 8: |     case 8: | ||||||
|       return (reader.readStringOrNull(offset)) as P; |       return (reader.readBool(offset)) as P; | ||||||
|     case 9: |     case 9: | ||||||
|       return (reader.readStringOrNull(offset)) as P; |       return (reader.readStringOrNull(offset)) as P; | ||||||
|     case 10: |     case 10: | ||||||
|       return (reader.readLong(offset)) as P; |  | ||||||
|     case 11: |  | ||||||
|       return (reader.readStringOrNull(offset)) as P; |       return (reader.readStringOrNull(offset)) as P; | ||||||
|  |     case 11: | ||||||
|  |       return (reader.readLong(offset)) as P; | ||||||
|     case 12: |     case 12: | ||||||
|  |       return (reader.readStringOrNull(offset)) as P; | ||||||
|  |     case 13: | ||||||
|       return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ?? |       return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ?? | ||||||
|           AssetType.other) as P; |           AssetType.other) as P; | ||||||
|     case 13: |  | ||||||
|       return (reader.readDateTime(offset)) as P; |  | ||||||
|     case 14: |     case 14: | ||||||
|  |       return (reader.readDateTime(offset)) as P; | ||||||
|  |     case 15: | ||||||
|       return (reader.readIntOrNull(offset)) as P; |       return (reader.readIntOrNull(offset)) as P; | ||||||
|     default: |     default: | ||||||
|       throw IsarError('Unknown property with id $propertyId'); |       throw IsarError('Unknown property with id $propertyId'); | ||||||
| @@ -1290,6 +1299,16 @@ extension AssetQueryFilter on QueryBuilder<Asset, Asset, QFilterCondition> { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   QueryBuilder<Asset, Asset, QAfterFilterCondition> isTrashedEqualTo( | ||||||
|  |       bool value) { | ||||||
|  |     return QueryBuilder.apply(this, (query) { | ||||||
|  |       return query.addFilterCondition(FilterCondition.equalTo( | ||||||
|  |         property: r'isTrashed', | ||||||
|  |         value: value, | ||||||
|  |       )); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   QueryBuilder<Asset, Asset, QAfterFilterCondition> livePhotoVideoIdIsNull() { |   QueryBuilder<Asset, Asset, QAfterFilterCondition> livePhotoVideoIdIsNull() { | ||||||
|     return QueryBuilder.apply(this, (query) { |     return QueryBuilder.apply(this, (query) { | ||||||
|       return query.addFilterCondition(const FilterCondition.isNull( |       return query.addFilterCondition(const FilterCondition.isNull( | ||||||
| @@ -2058,6 +2077,18 @@ extension AssetQuerySortBy on QueryBuilder<Asset, Asset, QSortBy> { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   QueryBuilder<Asset, Asset, QAfterSortBy> sortByIsTrashed() { | ||||||
|  |     return QueryBuilder.apply(this, (query) { | ||||||
|  |       return query.addSortBy(r'isTrashed', Sort.asc); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   QueryBuilder<Asset, Asset, QAfterSortBy> sortByIsTrashedDesc() { | ||||||
|  |     return QueryBuilder.apply(this, (query) { | ||||||
|  |       return query.addSortBy(r'isTrashed', Sort.desc); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   QueryBuilder<Asset, Asset, QAfterSortBy> sortByLivePhotoVideoId() { |   QueryBuilder<Asset, Asset, QAfterSortBy> sortByLivePhotoVideoId() { | ||||||
|     return QueryBuilder.apply(this, (query) { |     return QueryBuilder.apply(this, (query) { | ||||||
|       return query.addSortBy(r'livePhotoVideoId', Sort.asc); |       return query.addSortBy(r'livePhotoVideoId', Sort.asc); | ||||||
| @@ -2252,6 +2283,18 @@ extension AssetQuerySortThenBy on QueryBuilder<Asset, Asset, QSortThenBy> { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   QueryBuilder<Asset, Asset, QAfterSortBy> thenByIsTrashed() { | ||||||
|  |     return QueryBuilder.apply(this, (query) { | ||||||
|  |       return query.addSortBy(r'isTrashed', Sort.asc); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   QueryBuilder<Asset, Asset, QAfterSortBy> thenByIsTrashedDesc() { | ||||||
|  |     return QueryBuilder.apply(this, (query) { | ||||||
|  |       return query.addSortBy(r'isTrashed', Sort.desc); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   QueryBuilder<Asset, Asset, QAfterSortBy> thenByLivePhotoVideoId() { |   QueryBuilder<Asset, Asset, QAfterSortBy> thenByLivePhotoVideoId() { | ||||||
|     return QueryBuilder.apply(this, (query) { |     return QueryBuilder.apply(this, (query) { | ||||||
|       return query.addSortBy(r'livePhotoVideoId', Sort.asc); |       return query.addSortBy(r'livePhotoVideoId', Sort.asc); | ||||||
| @@ -2388,6 +2431,12 @@ extension AssetQueryWhereDistinct on QueryBuilder<Asset, Asset, QDistinct> { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   QueryBuilder<Asset, Asset, QDistinct> distinctByIsTrashed() { | ||||||
|  |     return QueryBuilder.apply(this, (query) { | ||||||
|  |       return query.addDistinctBy(r'isTrashed'); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   QueryBuilder<Asset, Asset, QDistinct> distinctByLivePhotoVideoId( |   QueryBuilder<Asset, Asset, QDistinct> distinctByLivePhotoVideoId( | ||||||
|       {bool caseSensitive = true}) { |       {bool caseSensitive = true}) { | ||||||
|     return QueryBuilder.apply(this, (query) { |     return QueryBuilder.apply(this, (query) { | ||||||
| @@ -2490,6 +2539,12 @@ extension AssetQueryProperty on QueryBuilder<Asset, Asset, QQueryProperty> { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   QueryBuilder<Asset, bool, QQueryOperations> isTrashedProperty() { | ||||||
|  |     return QueryBuilder.apply(this, (query) { | ||||||
|  |       return query.addPropertyName(r'isTrashed'); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   QueryBuilder<Asset, String?, QQueryOperations> livePhotoVideoIdProperty() { |   QueryBuilder<Asset, String?, QQueryOperations> livePhotoVideoIdProperty() { | ||||||
|     return QueryBuilder.apply(this, (query) { |     return QueryBuilder.apply(this, (query) { | ||||||
|       return query.addPropertyName(r'livePhotoVideoId'); |       return query.addPropertyName(r'livePhotoVideoId'); | ||||||
|   | |||||||
| @@ -15,7 +15,6 @@ import 'package:immich_mobile/shared/services/user.service.dart'; | |||||||
| import 'package:immich_mobile/utils/db.dart'; | import 'package:immich_mobile/utils/db.dart'; | ||||||
| import 'package:isar/isar.dart'; | import 'package:isar/isar.dart'; | ||||||
| import 'package:logging/logging.dart'; | import 'package:logging/logging.dart'; | ||||||
| import 'package:openapi/api.dart'; |  | ||||||
| import 'package:photo_manager/photo_manager.dart'; | import 'package:photo_manager/photo_manager.dart'; | ||||||
|  |  | ||||||
| class AssetNotifier extends StateNotifier<bool> { | class AssetNotifier extends StateNotifier<bool> { | ||||||
| @@ -92,23 +91,45 @@ class AssetNotifier extends StateNotifier<bool> { | |||||||
|     await _syncService.syncNewAssetToDb(newAsset); |     await _syncService.syncNewAssetToDb(newAsset); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<void> deleteAssets(Iterable<Asset> deleteAssets) async { |   Future<bool> deleteAssets( | ||||||
|  |     Iterable<Asset> deleteAssets, { | ||||||
|  |     bool? force = false, | ||||||
|  |   }) async { | ||||||
|     _deleteInProgress = true; |     _deleteInProgress = true; | ||||||
|     state = true; |     state = true; | ||||||
|     try { |     try { | ||||||
|       final localDeleted = await _deleteLocalAssets(deleteAssets); |       final localDeleted = await _deleteLocalAssets(deleteAssets); | ||||||
|       final remoteDeleted = await _deleteRemoteAssets(deleteAssets); |       final remoteDeleted = await _deleteRemoteAssets(deleteAssets, force); | ||||||
|       if (localDeleted.isNotEmpty || remoteDeleted.isNotEmpty) { |       if (localDeleted.isNotEmpty || remoteDeleted.isNotEmpty) { | ||||||
|         final dbIds = deleteAssets.map((e) => e.id).toList(); |         List<Asset>? assetsToUpdate; | ||||||
|  |         // Local only assets are permanently deleted for now. So always remove them from db | ||||||
|  |         final dbIds = deleteAssets | ||||||
|  |             .where((a) => a.isLocal && !a.isRemote) | ||||||
|  |             .map((e) => e.id) | ||||||
|  |             .toList(); | ||||||
|  |         if (force == null || !force) { | ||||||
|  |           assetsToUpdate = remoteDeleted.map((e) { | ||||||
|  |             e.isTrashed = true; | ||||||
|  |             return e; | ||||||
|  |           }).toList(); | ||||||
|  |         } else { | ||||||
|  |           // Add all remote assets to be deleted from isar as since they are permanently deleted | ||||||
|  |           dbIds.addAll(remoteDeleted.map((e) => e.id)); | ||||||
|  |         } | ||||||
|         await _db.writeTxn(() async { |         await _db.writeTxn(() async { | ||||||
|  |           if (assetsToUpdate != null) { | ||||||
|  |             await _db.assets.putAll(assetsToUpdate); | ||||||
|  |           } | ||||||
|           await _db.exifInfos.deleteAll(dbIds); |           await _db.exifInfos.deleteAll(dbIds); | ||||||
|           await _db.assets.deleteAll(dbIds); |           await _db.assets.deleteAll(dbIds); | ||||||
|         }); |         }); | ||||||
|  |         return true; | ||||||
|       } |       } | ||||||
|     } finally { |     } finally { | ||||||
|       _deleteInProgress = false; |       _deleteInProgress = false; | ||||||
|       state = false; |       state = false; | ||||||
|     } |     } | ||||||
|  |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<List<String>> _deleteLocalAssets( |   Future<List<String>> _deleteLocalAssets( | ||||||
| @@ -127,15 +148,14 @@ class AssetNotifier extends StateNotifier<bool> { | |||||||
|     return []; |     return []; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<Iterable<String>> _deleteRemoteAssets( |   Future<Iterable<Asset>> _deleteRemoteAssets( | ||||||
|     Iterable<Asset> assetsToDelete, |     Iterable<Asset> assetsToDelete, | ||||||
|  |     bool? force, | ||||||
|   ) async { |   ) async { | ||||||
|     final Iterable<Asset> remote = assetsToDelete.where((e) => e.isRemote); |     final Iterable<Asset> remote = assetsToDelete.where((e) => e.isRemote); | ||||||
|     final List<DeleteAssetResponseDto> deleteAssetResult = |  | ||||||
|         await _assetService.deleteAssets(remote) ?? []; |     final isSuccess = await _assetService.deleteAssets(remote, force: force); | ||||||
|     return deleteAssetResult |     return isSuccess ? remote : []; | ||||||
|         .where((a) => a.status == DeleteAssetStatus.SUCCESS) |  | ||||||
|         .map((a) => a.id); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<void> toggleFavorite(List<Asset> assets, bool status) async { |   Future<void> toggleFavorite(List<Asset> assets, bool status) async { | ||||||
| @@ -190,6 +210,7 @@ final assetsProvider = | |||||||
|       .ownerIdEqualToAnyChecksum(userId) |       .ownerIdEqualToAnyChecksum(userId) | ||||||
|       .filter() |       .filter() | ||||||
|       .isArchivedEqualTo(false) |       .isArchivedEqualTo(false) | ||||||
|  |       .isTrashedEqualTo(false) | ||||||
|       .sortByFileCreatedAtDesc(); |       .sortByFileCreatedAtDesc(); | ||||||
|   final settings = ref.watch(appSettingsServiceProvider); |   final settings = ref.watch(appSettingsServiceProvider); | ||||||
|   final groupBy = |   final groupBy = | ||||||
| @@ -210,6 +231,7 @@ final remoteAssetsProvider = | |||||||
|       .remoteIdIsNotNull() |       .remoteIdIsNotNull() | ||||||
|       .filter() |       .filter() | ||||||
|       .ownerIdEqualTo(userId) |       .ownerIdEqualTo(userId) | ||||||
|  |       .isTrashedEqualTo(false) | ||||||
|       .sortByFileCreatedAtDesc(); |       .sortByFileCreatedAtDesc(); | ||||||
|   final settings = ref.watch(appSettingsServiceProvider); |   final settings = ref.watch(appSettingsServiceProvider); | ||||||
|   final groupBy = |   final groupBy = | ||||||
|   | |||||||
| @@ -26,12 +26,14 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> { | |||||||
|               search: true, |               search: true, | ||||||
|               sidecar: true, |               sidecar: true, | ||||||
|               tagImage: true, |               tagImage: true, | ||||||
|  |               trash: true, | ||||||
|               reverseGeocoding: true, |               reverseGeocoding: true, | ||||||
|             ), |             ), | ||||||
|             serverConfig: ServerConfigDto( |             serverConfig: ServerConfigDto( | ||||||
|               loginPageMessage: "", |               loginPageMessage: "", | ||||||
|               mapTileUrl: "https://tile.openstreetmap.org/{z}/{x}/{y}.png", |               mapTileUrl: "https://tile.openstreetmap.org/{z}/{x}/{y}.png", | ||||||
|               oauthButtonText: "", |               oauthButtonText: "", | ||||||
|  |               trashDays: 30, | ||||||
|             ), |             ), | ||||||
|             isVersionMismatch: false, |             isVersionMismatch: false, | ||||||
|             versionMismatchErrorMessage: "", |             versionMismatchErrorMessage: "", | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import 'package:immich_mobile/modules/login/providers/authentication.provider.da | |||||||
| import 'package:immich_mobile/shared/models/asset.dart'; | import 'package:immich_mobile/shared/models/asset.dart'; | ||||||
| import 'package:immich_mobile/shared/models/store.dart'; | import 'package:immich_mobile/shared/models/store.dart'; | ||||||
| import 'package:immich_mobile/shared/providers/asset.provider.dart'; | import 'package:immich_mobile/shared/providers/asset.provider.dart'; | ||||||
|  | import 'package:immich_mobile/shared/providers/server_info.provider.dart'; | ||||||
| import 'package:logging/logging.dart'; | import 'package:logging/logging.dart'; | ||||||
| import 'package:openapi/api.dart'; | import 'package:openapi/api.dart'; | ||||||
| import 'package:socket_io_client/socket_io_client.dart'; | import 'package:socket_io_client/socket_io_client.dart'; | ||||||
| @@ -92,6 +93,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> { | |||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         socket.on('on_upload_success', _handleOnUploadSuccess); |         socket.on('on_upload_success', _handleOnUploadSuccess); | ||||||
|  |         socket.on('on_config_update', _handleOnConfigUpdate); | ||||||
|       } catch (e) { |       } catch (e) { | ||||||
|         debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}"); |         debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}"); | ||||||
|       } |       } | ||||||
| @@ -126,6 +128,11 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> { | |||||||
|       ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset); |       ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   _handleOnConfigUpdate(dynamic data) { | ||||||
|  |     ref.read(serverInfoProvider.notifier).getServerFeatures(); | ||||||
|  |     ref.read(serverInfoProvider.notifier).getServerConfig(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| final websocketProvider = | final websocketProvider = | ||||||
|   | |||||||
| @@ -64,7 +64,9 @@ class AssetService { | |||||||
|   Future<List<Asset>?> _getRemoteAssets(User user) async { |   Future<List<Asset>?> _getRemoteAssets(User user) async { | ||||||
|     try { |     try { | ||||||
|       final List<AssetResponseDto>? assets = |       final List<AssetResponseDto>? assets = | ||||||
|           await _apiService.assetApi.getAllAssets(userId: user.id); |           await _apiService.assetApi.getAllAssets( | ||||||
|  |         userId: user.id, | ||||||
|  |       ); | ||||||
|       if (assets == null) { |       if (assets == null) { | ||||||
|         return null; |         return null; | ||||||
|       } else if (assets.isNotEmpty && assets.first.ownerId != user.id) { |       } else if (assets.isNotEmpty && assets.first.ownerId != user.id) { | ||||||
| @@ -84,9 +86,10 @@ class AssetService { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<List<DeleteAssetResponseDto>?> deleteAssets( |   Future<bool> deleteAssets( | ||||||
|     Iterable<Asset> deleteAssets, |     Iterable<Asset> deleteAssets, { | ||||||
|   ) async { |     bool? force = false, | ||||||
|  |   }) async { | ||||||
|     try { |     try { | ||||||
|       final List<String> payload = []; |       final List<String> payload = []; | ||||||
|  |  | ||||||
| @@ -94,12 +97,17 @@ class AssetService { | |||||||
|         payload.add(asset.remoteId!); |         payload.add(asset.remoteId!); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       return await _apiService.assetApi |       await _apiService.assetApi.deleteAssets( | ||||||
|           .deleteAsset(DeleteAssetDto(ids: payload)); |         AssetBulkDeleteDto( | ||||||
|  |           ids: payload, | ||||||
|  |           force: force, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |       return true; | ||||||
|     } catch (error, stack) { |     } catch (error, stack) { | ||||||
|       log.severe("Error deleteAssets  ${error.toString()}", error, stack); |       log.severe("Error deleteAssets  ${error.toString()}", error, stack); | ||||||
|       return null; |  | ||||||
|     } |     } | ||||||
|  |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /// Loads the exif information from the database. If there is none, loads |   /// Loads the exif information from the database. If there is none, loads | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										15
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							| @@ -15,6 +15,7 @@ doc/AlbumCountResponseDto.md | |||||||
| doc/AlbumResponseDto.md | doc/AlbumResponseDto.md | ||||||
| doc/AllJobStatusResponseDto.md | doc/AllJobStatusResponseDto.md | ||||||
| doc/AssetApi.md | doc/AssetApi.md | ||||||
|  | doc/AssetBulkDeleteDto.md | ||||||
| doc/AssetBulkUpdateDto.md | doc/AssetBulkUpdateDto.md | ||||||
| doc/AssetBulkUploadCheckDto.md | doc/AssetBulkUploadCheckDto.md | ||||||
| doc/AssetBulkUploadCheckItem.md | doc/AssetBulkUploadCheckItem.md | ||||||
| @@ -53,9 +54,6 @@ doc/CreateTagDto.md | |||||||
| doc/CreateUserDto.md | doc/CreateUserDto.md | ||||||
| doc/CuratedLocationsResponseDto.md | doc/CuratedLocationsResponseDto.md | ||||||
| doc/CuratedObjectsResponseDto.md | doc/CuratedObjectsResponseDto.md | ||||||
| doc/DeleteAssetDto.md |  | ||||||
| doc/DeleteAssetResponseDto.md |  | ||||||
| doc/DeleteAssetStatus.md |  | ||||||
| doc/DownloadArchiveInfo.md | doc/DownloadArchiveInfo.md | ||||||
| doc/DownloadInfoDto.md | doc/DownloadInfoDto.md | ||||||
| doc/DownloadResponseDto.md | doc/DownloadResponseDto.md | ||||||
| @@ -131,6 +129,7 @@ doc/SystemConfigReverseGeocodingDto.md | |||||||
| doc/SystemConfigStorageTemplateDto.md | doc/SystemConfigStorageTemplateDto.md | ||||||
| doc/SystemConfigTemplateStorageOptionDto.md | doc/SystemConfigTemplateStorageOptionDto.md | ||||||
| doc/SystemConfigThumbnailDto.md | doc/SystemConfigThumbnailDto.md | ||||||
|  | doc/SystemConfigTrashDto.md | ||||||
| doc/TagApi.md | doc/TagApi.md | ||||||
| doc/TagResponseDto.md | doc/TagResponseDto.md | ||||||
| doc/TagTypeEnum.md | doc/TagTypeEnum.md | ||||||
| @@ -186,6 +185,7 @@ lib/model/api_key_create_dto.dart | |||||||
| lib/model/api_key_create_response_dto.dart | lib/model/api_key_create_response_dto.dart | ||||||
| lib/model/api_key_response_dto.dart | lib/model/api_key_response_dto.dart | ||||||
| lib/model/api_key_update_dto.dart | lib/model/api_key_update_dto.dart | ||||||
|  | lib/model/asset_bulk_delete_dto.dart | ||||||
| lib/model/asset_bulk_update_dto.dart | lib/model/asset_bulk_update_dto.dart | ||||||
| lib/model/asset_bulk_upload_check_dto.dart | lib/model/asset_bulk_upload_check_dto.dart | ||||||
| lib/model/asset_bulk_upload_check_item.dart | lib/model/asset_bulk_upload_check_item.dart | ||||||
| @@ -222,9 +222,6 @@ lib/model/create_tag_dto.dart | |||||||
| lib/model/create_user_dto.dart | lib/model/create_user_dto.dart | ||||||
| lib/model/curated_locations_response_dto.dart | lib/model/curated_locations_response_dto.dart | ||||||
| lib/model/curated_objects_response_dto.dart | lib/model/curated_objects_response_dto.dart | ||||||
| lib/model/delete_asset_dto.dart |  | ||||||
| lib/model/delete_asset_response_dto.dart |  | ||||||
| lib/model/delete_asset_status.dart |  | ||||||
| lib/model/download_archive_info.dart | lib/model/download_archive_info.dart | ||||||
| lib/model/download_info_dto.dart | lib/model/download_info_dto.dart | ||||||
| lib/model/download_response_dto.dart | lib/model/download_response_dto.dart | ||||||
| @@ -291,6 +288,7 @@ lib/model/system_config_reverse_geocoding_dto.dart | |||||||
| lib/model/system_config_storage_template_dto.dart | lib/model/system_config_storage_template_dto.dart | ||||||
| lib/model/system_config_template_storage_option_dto.dart | lib/model/system_config_template_storage_option_dto.dart | ||||||
| lib/model/system_config_thumbnail_dto.dart | lib/model/system_config_thumbnail_dto.dart | ||||||
|  | lib/model/system_config_trash_dto.dart | ||||||
| lib/model/tag_response_dto.dart | lib/model/tag_response_dto.dart | ||||||
| lib/model/tag_type_enum.dart | lib/model/tag_type_enum.dart | ||||||
| lib/model/thumbnail_format.dart | lib/model/thumbnail_format.dart | ||||||
| @@ -322,6 +320,7 @@ test/api_key_create_response_dto_test.dart | |||||||
| test/api_key_response_dto_test.dart | test/api_key_response_dto_test.dart | ||||||
| test/api_key_update_dto_test.dart | test/api_key_update_dto_test.dart | ||||||
| test/asset_api_test.dart | test/asset_api_test.dart | ||||||
|  | test/asset_bulk_delete_dto_test.dart | ||||||
| test/asset_bulk_update_dto_test.dart | test/asset_bulk_update_dto_test.dart | ||||||
| test/asset_bulk_upload_check_dto_test.dart | test/asset_bulk_upload_check_dto_test.dart | ||||||
| test/asset_bulk_upload_check_item_test.dart | test/asset_bulk_upload_check_item_test.dart | ||||||
| @@ -360,9 +359,6 @@ test/create_tag_dto_test.dart | |||||||
| test/create_user_dto_test.dart | test/create_user_dto_test.dart | ||||||
| test/curated_locations_response_dto_test.dart | test/curated_locations_response_dto_test.dart | ||||||
| test/curated_objects_response_dto_test.dart | test/curated_objects_response_dto_test.dart | ||||||
| test/delete_asset_dto_test.dart |  | ||||||
| test/delete_asset_response_dto_test.dart |  | ||||||
| test/delete_asset_status_test.dart |  | ||||||
| test/download_archive_info_test.dart | test/download_archive_info_test.dart | ||||||
| test/download_info_dto_test.dart | test/download_info_dto_test.dart | ||||||
| test/download_response_dto_test.dart | test/download_response_dto_test.dart | ||||||
| @@ -438,6 +434,7 @@ test/system_config_reverse_geocoding_dto_test.dart | |||||||
| test/system_config_storage_template_dto_test.dart | test/system_config_storage_template_dto_test.dart | ||||||
| test/system_config_template_storage_option_dto_test.dart | test/system_config_template_storage_option_dto_test.dart | ||||||
| test/system_config_thumbnail_dto_test.dart | test/system_config_thumbnail_dto_test.dart | ||||||
|  | test/system_config_trash_dto_test.dart | ||||||
| test/tag_api_test.dart | test/tag_api_test.dart | ||||||
| test/tag_response_dto_test.dart | test/tag_response_dto_test.dart | ||||||
| test/tag_type_enum_test.dart | test/tag_type_enum_test.dart | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							| @@ -90,9 +90,10 @@ Class | Method | HTTP request | Description | |||||||
| *AssetApi* | [**bulkUploadCheck**](doc//AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |  | *AssetApi* | [**bulkUploadCheck**](doc//AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |  | ||||||
| *AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |  | *AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |  | ||||||
| *AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |  | *AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |  | ||||||
| *AssetApi* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset |  | *AssetApi* | [**deleteAssets**](doc//AssetApi.md#deleteassets) | **DELETE** /asset |  | ||||||
| *AssetApi* | [**downloadArchive**](doc//AssetApi.md#downloadarchive) | **POST** /asset/download/archive |  | *AssetApi* | [**downloadArchive**](doc//AssetApi.md#downloadarchive) | **POST** /asset/download/archive |  | ||||||
| *AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **POST** /asset/download/{id} |  | *AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **POST** /asset/download/{id} |  | ||||||
|  | *AssetApi* | [**emptyTrash**](doc//AssetApi.md#emptytrash) | **POST** /asset/trash/empty |  | ||||||
| *AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |  | *AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |  | ||||||
| *AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |  | *AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |  | ||||||
| *AssetApi* | [**getAssetSearchTerms**](doc//AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |  | *AssetApi* | [**getAssetSearchTerms**](doc//AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |  | ||||||
| @@ -108,6 +109,8 @@ Class | Method | HTTP request | Description | |||||||
| *AssetApi* | [**getTimeBuckets**](doc//AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets |  | *AssetApi* | [**getTimeBuckets**](doc//AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets |  | ||||||
| *AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |  | *AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |  | ||||||
| *AssetApi* | [**importFile**](doc//AssetApi.md#importfile) | **POST** /asset/import |  | *AssetApi* | [**importFile**](doc//AssetApi.md#importfile) | **POST** /asset/import |  | ||||||
|  | *AssetApi* | [**restoreAssets**](doc//AssetApi.md#restoreassets) | **POST** /asset/restore |  | ||||||
|  | *AssetApi* | [**restoreTrash**](doc//AssetApi.md#restoretrash) | **POST** /asset/trash/restore |  | ||||||
| *AssetApi* | [**runAssetJobs**](doc//AssetApi.md#runassetjobs) | **POST** /asset/jobs |  | *AssetApi* | [**runAssetJobs**](doc//AssetApi.md#runassetjobs) | **POST** /asset/jobs |  | ||||||
| *AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search |  | *AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search |  | ||||||
| *AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} |  | *AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} |  | ||||||
| @@ -201,6 +204,7 @@ Class | Method | HTTP request | Description | |||||||
|  - [AlbumCountResponseDto](doc//AlbumCountResponseDto.md) |  - [AlbumCountResponseDto](doc//AlbumCountResponseDto.md) | ||||||
|  - [AlbumResponseDto](doc//AlbumResponseDto.md) |  - [AlbumResponseDto](doc//AlbumResponseDto.md) | ||||||
|  - [AllJobStatusResponseDto](doc//AllJobStatusResponseDto.md) |  - [AllJobStatusResponseDto](doc//AllJobStatusResponseDto.md) | ||||||
|  |  - [AssetBulkDeleteDto](doc//AssetBulkDeleteDto.md) | ||||||
|  - [AssetBulkUpdateDto](doc//AssetBulkUpdateDto.md) |  - [AssetBulkUpdateDto](doc//AssetBulkUpdateDto.md) | ||||||
|  - [AssetBulkUploadCheckDto](doc//AssetBulkUploadCheckDto.md) |  - [AssetBulkUploadCheckDto](doc//AssetBulkUploadCheckDto.md) | ||||||
|  - [AssetBulkUploadCheckItem](doc//AssetBulkUploadCheckItem.md) |  - [AssetBulkUploadCheckItem](doc//AssetBulkUploadCheckItem.md) | ||||||
| @@ -237,9 +241,6 @@ Class | Method | HTTP request | Description | |||||||
|  - [CreateUserDto](doc//CreateUserDto.md) |  - [CreateUserDto](doc//CreateUserDto.md) | ||||||
|  - [CuratedLocationsResponseDto](doc//CuratedLocationsResponseDto.md) |  - [CuratedLocationsResponseDto](doc//CuratedLocationsResponseDto.md) | ||||||
|  - [CuratedObjectsResponseDto](doc//CuratedObjectsResponseDto.md) |  - [CuratedObjectsResponseDto](doc//CuratedObjectsResponseDto.md) | ||||||
|  - [DeleteAssetDto](doc//DeleteAssetDto.md) |  | ||||||
|  - [DeleteAssetResponseDto](doc//DeleteAssetResponseDto.md) |  | ||||||
|  - [DeleteAssetStatus](doc//DeleteAssetStatus.md) |  | ||||||
|  - [DownloadArchiveInfo](doc//DownloadArchiveInfo.md) |  - [DownloadArchiveInfo](doc//DownloadArchiveInfo.md) | ||||||
|  - [DownloadInfoDto](doc//DownloadInfoDto.md) |  - [DownloadInfoDto](doc//DownloadInfoDto.md) | ||||||
|  - [DownloadResponseDto](doc//DownloadResponseDto.md) |  - [DownloadResponseDto](doc//DownloadResponseDto.md) | ||||||
| @@ -306,6 +307,7 @@ Class | Method | HTTP request | Description | |||||||
|  - [SystemConfigStorageTemplateDto](doc//SystemConfigStorageTemplateDto.md) |  - [SystemConfigStorageTemplateDto](doc//SystemConfigStorageTemplateDto.md) | ||||||
|  - [SystemConfigTemplateStorageOptionDto](doc//SystemConfigTemplateStorageOptionDto.md) |  - [SystemConfigTemplateStorageOptionDto](doc//SystemConfigTemplateStorageOptionDto.md) | ||||||
|  - [SystemConfigThumbnailDto](doc//SystemConfigThumbnailDto.md) |  - [SystemConfigThumbnailDto](doc//SystemConfigThumbnailDto.md) | ||||||
|  |  - [SystemConfigTrashDto](doc//SystemConfigTrashDto.md) | ||||||
|  - [TagResponseDto](doc//TagResponseDto.md) |  - [TagResponseDto](doc//TagResponseDto.md) | ||||||
|  - [TagTypeEnum](doc//TagTypeEnum.md) |  - [TagTypeEnum](doc//TagTypeEnum.md) | ||||||
|  - [ThumbnailFormat](doc//ThumbnailFormat.md) |  - [ThumbnailFormat](doc//ThumbnailFormat.md) | ||||||
|   | |||||||
							
								
								
									
										194
									
								
								mobile/openapi/doc/AssetApi.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										194
									
								
								mobile/openapi/doc/AssetApi.md
									
									
									
										generated
									
									
									
								
							| @@ -12,9 +12,10 @@ Method | HTTP request | Description | |||||||
| [**bulkUploadCheck**](AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |  | [**bulkUploadCheck**](AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |  | ||||||
| [**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |  | [**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |  | ||||||
| [**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |  | [**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |  | ||||||
| [**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset |  | [**deleteAssets**](AssetApi.md#deleteassets) | **DELETE** /asset |  | ||||||
| [**downloadArchive**](AssetApi.md#downloadarchive) | **POST** /asset/download/archive |  | [**downloadArchive**](AssetApi.md#downloadarchive) | **POST** /asset/download/archive |  | ||||||
| [**downloadFile**](AssetApi.md#downloadfile) | **POST** /asset/download/{id} |  | [**downloadFile**](AssetApi.md#downloadfile) | **POST** /asset/download/{id} |  | ||||||
|  | [**emptyTrash**](AssetApi.md#emptytrash) | **POST** /asset/trash/empty |  | ||||||
| [**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset |  | [**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset |  | ||||||
| [**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |  | [**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |  | ||||||
| [**getAssetSearchTerms**](AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |  | [**getAssetSearchTerms**](AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |  | ||||||
| @@ -30,6 +31,8 @@ Method | HTTP request | Description | |||||||
| [**getTimeBuckets**](AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets |  | [**getTimeBuckets**](AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets |  | ||||||
| [**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |  | [**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |  | ||||||
| [**importFile**](AssetApi.md#importfile) | **POST** /asset/import |  | [**importFile**](AssetApi.md#importfile) | **POST** /asset/import |  | ||||||
|  | [**restoreAssets**](AssetApi.md#restoreassets) | **POST** /asset/restore |  | ||||||
|  | [**restoreTrash**](AssetApi.md#restoretrash) | **POST** /asset/trash/restore |  | ||||||
| [**runAssetJobs**](AssetApi.md#runassetjobs) | **POST** /asset/jobs |  | [**runAssetJobs**](AssetApi.md#runassetjobs) | **POST** /asset/jobs |  | ||||||
| [**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search |  | [**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search |  | ||||||
| [**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{id} |  | [**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{id} |  | ||||||
| @@ -211,8 +214,8 @@ Name | Type | Description  | Notes | |||||||
| 
 | 
 | ||||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
| # **deleteAsset** | # **deleteAssets** | ||||||
| > List<DeleteAssetResponseDto> deleteAsset(deleteAssetDto) | > deleteAssets(assetBulkDeleteDto) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @@ -235,13 +238,12 @@ import 'package:openapi/api.dart'; | |||||||
| //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||||
| 
 | 
 | ||||||
| final api_instance = AssetApi(); | final api_instance = AssetApi(); | ||||||
| final deleteAssetDto = DeleteAssetDto(); // DeleteAssetDto |  | final assetBulkDeleteDto = AssetBulkDeleteDto(); // AssetBulkDeleteDto |  | ||||||
| 
 | 
 | ||||||
| try { | try { | ||||||
|     final result = api_instance.deleteAsset(deleteAssetDto); |     api_instance.deleteAssets(assetBulkDeleteDto); | ||||||
|     print(result); |  | ||||||
| } catch (e) { | } catch (e) { | ||||||
|     print('Exception when calling AssetApi->deleteAsset: $e\n'); |     print('Exception when calling AssetApi->deleteAssets: $e\n'); | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| @@ -249,11 +251,11 @@ try { | |||||||
| 
 | 
 | ||||||
| Name | Type | Description  | Notes | Name | Type | Description  | Notes | ||||||
| ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ||||||
|  **deleteAssetDto** | [**DeleteAssetDto**](DeleteAssetDto.md)|  |  |  **assetBulkDeleteDto** | [**AssetBulkDeleteDto**](AssetBulkDeleteDto.md)|  |  | ||||||
| 
 | 
 | ||||||
| ### Return type | ### Return type | ||||||
| 
 | 
 | ||||||
| [**List<DeleteAssetResponseDto>**](DeleteAssetResponseDto.md) | void (empty response body) | ||||||
| 
 | 
 | ||||||
| ### Authorization | ### Authorization | ||||||
| 
 | 
 | ||||||
| @@ -262,7 +264,7 @@ Name | Type | Description  | Notes | |||||||
| ### HTTP request headers | ### HTTP request headers | ||||||
| 
 | 
 | ||||||
|  - **Content-Type**: application/json |  - **Content-Type**: application/json | ||||||
|  - **Accept**: application/json |  - **Accept**: Not defined | ||||||
| 
 | 
 | ||||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
| @@ -380,6 +382,56 @@ Name | Type | Description  | Notes | |||||||
| 
 | 
 | ||||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
|  | # **emptyTrash** | ||||||
|  | > emptyTrash() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### Example | ||||||
|  | ```dart | ||||||
|  | import 'package:openapi/api.dart'; | ||||||
|  | // TODO Configure API key authorization: cookie | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY'; | ||||||
|  | // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer'; | ||||||
|  | // TODO Configure API key authorization: api_key | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY'; | ||||||
|  | // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer'; | ||||||
|  | // TODO Configure HTTP Bearer authorization: bearer | ||||||
|  | // Case 1. Use String Token | ||||||
|  | //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); | ||||||
|  | // Case 2. Use Function which generate token. | ||||||
|  | // String yourTokenGeneratorFunction() { ... } | ||||||
|  | //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||||
|  | 
 | ||||||
|  | final api_instance = AssetApi(); | ||||||
|  | 
 | ||||||
|  | try { | ||||||
|  |     api_instance.emptyTrash(); | ||||||
|  | } catch (e) { | ||||||
|  |     print('Exception when calling AssetApi->emptyTrash: $e\n'); | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Parameters | ||||||
|  | This endpoint does not need any parameter. | ||||||
|  | 
 | ||||||
|  | ### Return type | ||||||
|  | 
 | ||||||
|  | void (empty response body) | ||||||
|  | 
 | ||||||
|  | ### Authorization | ||||||
|  | 
 | ||||||
|  | [cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) | ||||||
|  | 
 | ||||||
|  | ### HTTP request headers | ||||||
|  | 
 | ||||||
|  |  - **Content-Type**: Not defined | ||||||
|  |  - **Accept**: Not defined | ||||||
|  | 
 | ||||||
|  | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||||
|  | 
 | ||||||
| # **getAllAssets** | # **getAllAssets** | ||||||
| > List<AssetResponseDto> getAllAssets(userId, isFavorite, isArchived, skip, updatedAfter, ifNoneMatch) | > List<AssetResponseDto> getAllAssets(userId, isFavorite, isArchived, skip, updatedAfter, ifNoneMatch) | ||||||
| 
 | 
 | ||||||
| @@ -558,7 +610,7 @@ This endpoint does not need any parameter. | |||||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
| # **getAssetStats** | # **getAssetStats** | ||||||
| > AssetStatsResponseDto getAssetStats(isArchived, isFavorite) | > AssetStatsResponseDto getAssetStats(isArchived, isFavorite, isTrashed) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @@ -583,9 +635,10 @@ import 'package:openapi/api.dart'; | |||||||
| final api_instance = AssetApi(); | final api_instance = AssetApi(); | ||||||
| final isArchived = true; // bool |  | final isArchived = true; // bool |  | ||||||
| final isFavorite = true; // bool |  | final isFavorite = true; // bool |  | ||||||
|  | final isTrashed = true; // bool |  | ||||||
| 
 | 
 | ||||||
| try { | try { | ||||||
|     final result = api_instance.getAssetStats(isArchived, isFavorite); |     final result = api_instance.getAssetStats(isArchived, isFavorite, isTrashed); | ||||||
|     print(result); |     print(result); | ||||||
| } catch (e) { | } catch (e) { | ||||||
|     print('Exception when calling AssetApi->getAssetStats: $e\n'); |     print('Exception when calling AssetApi->getAssetStats: $e\n'); | ||||||
| @@ -598,6 +651,7 @@ Name | Type | Description  | Notes | |||||||
| ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ||||||
|  **isArchived** | **bool**|  | [optional]  |  **isArchived** | **bool**|  | [optional]  | ||||||
|  **isFavorite** | **bool**|  | [optional]  |  **isFavorite** | **bool**|  | [optional]  | ||||||
|  |  **isTrashed** | **bool**|  | [optional]  | ||||||
| 
 | 
 | ||||||
| ### Return type | ### Return type | ||||||
| 
 | 
 | ||||||
| @@ -674,7 +728,7 @@ Name | Type | Description  | Notes | |||||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
| # **getByTimeBucket** | # **getByTimeBucket** | ||||||
| > List<AssetResponseDto> getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, key) | > List<AssetResponseDto> getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, key) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @@ -704,10 +758,11 @@ final albumId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | | |||||||
| final personId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |  | final personId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |  | ||||||
| final isArchived = true; // bool |  | final isArchived = true; // bool |  | ||||||
| final isFavorite = true; // bool |  | final isFavorite = true; // bool |  | ||||||
|  | final isTrashed = true; // bool |  | ||||||
| final key = key_example; // String |  | final key = key_example; // String |  | ||||||
| 
 | 
 | ||||||
| try { | try { | ||||||
|     final result = api_instance.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, key); |     final result = api_instance.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, key); | ||||||
|     print(result); |     print(result); | ||||||
| } catch (e) { | } catch (e) { | ||||||
|     print('Exception when calling AssetApi->getByTimeBucket: $e\n'); |     print('Exception when calling AssetApi->getByTimeBucket: $e\n'); | ||||||
| @@ -725,6 +780,7 @@ Name | Type | Description  | Notes | |||||||
|  **personId** | **String**|  | [optional]  |  **personId** | **String**|  | [optional]  | ||||||
|  **isArchived** | **bool**|  | [optional]  |  **isArchived** | **bool**|  | [optional]  | ||||||
|  **isFavorite** | **bool**|  | [optional]  |  **isFavorite** | **bool**|  | [optional]  | ||||||
|  |  **isTrashed** | **bool**|  | [optional]  | ||||||
|  **key** | **String**|  | [optional]  |  **key** | **String**|  | [optional]  | ||||||
| 
 | 
 | ||||||
| ### Return type | ### Return type | ||||||
| @@ -1075,7 +1131,7 @@ Name | Type | Description  | Notes | |||||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
| # **getTimeBuckets** | # **getTimeBuckets** | ||||||
| > List<TimeBucketResponseDto> getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, key) | > List<TimeBucketResponseDto> getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, key) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @@ -1104,10 +1160,11 @@ final albumId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | | |||||||
| final personId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |  | final personId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |  | ||||||
| final isArchived = true; // bool |  | final isArchived = true; // bool |  | ||||||
| final isFavorite = true; // bool |  | final isFavorite = true; // bool |  | ||||||
|  | final isTrashed = true; // bool |  | ||||||
| final key = key_example; // String |  | final key = key_example; // String |  | ||||||
| 
 | 
 | ||||||
| try { | try { | ||||||
|     final result = api_instance.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, key); |     final result = api_instance.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, key); | ||||||
|     print(result); |     print(result); | ||||||
| } catch (e) { | } catch (e) { | ||||||
|     print('Exception when calling AssetApi->getTimeBuckets: $e\n'); |     print('Exception when calling AssetApi->getTimeBuckets: $e\n'); | ||||||
| @@ -1124,6 +1181,7 @@ Name | Type | Description  | Notes | |||||||
|  **personId** | **String**|  | [optional]  |  **personId** | **String**|  | [optional]  | ||||||
|  **isArchived** | **bool**|  | [optional]  |  **isArchived** | **bool**|  | [optional]  | ||||||
|  **isFavorite** | **bool**|  | [optional]  |  **isFavorite** | **bool**|  | [optional]  | ||||||
|  |  **isTrashed** | **bool**|  | [optional]  | ||||||
|  **key** | **String**|  | [optional]  |  **key** | **String**|  | [optional]  | ||||||
| 
 | 
 | ||||||
| ### Return type | ### Return type | ||||||
| @@ -1253,6 +1311,110 @@ Name | Type | Description  | Notes | |||||||
| 
 | 
 | ||||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
|  | # **restoreAssets** | ||||||
|  | > restoreAssets(bulkIdsDto) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### Example | ||||||
|  | ```dart | ||||||
|  | import 'package:openapi/api.dart'; | ||||||
|  | // TODO Configure API key authorization: cookie | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY'; | ||||||
|  | // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer'; | ||||||
|  | // TODO Configure API key authorization: api_key | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY'; | ||||||
|  | // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer'; | ||||||
|  | // TODO Configure HTTP Bearer authorization: bearer | ||||||
|  | // Case 1. Use String Token | ||||||
|  | //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); | ||||||
|  | // Case 2. Use Function which generate token. | ||||||
|  | // String yourTokenGeneratorFunction() { ... } | ||||||
|  | //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||||
|  | 
 | ||||||
|  | final api_instance = AssetApi(); | ||||||
|  | final bulkIdsDto = BulkIdsDto(); // BulkIdsDto |  | ||||||
|  | 
 | ||||||
|  | try { | ||||||
|  |     api_instance.restoreAssets(bulkIdsDto); | ||||||
|  | } catch (e) { | ||||||
|  |     print('Exception when calling AssetApi->restoreAssets: $e\n'); | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Parameters | ||||||
|  | 
 | ||||||
|  | Name | Type | Description  | Notes | ||||||
|  | ------------- | ------------- | ------------- | ------------- | ||||||
|  |  **bulkIdsDto** | [**BulkIdsDto**](BulkIdsDto.md)|  |  | ||||||
|  | 
 | ||||||
|  | ### Return type | ||||||
|  | 
 | ||||||
|  | void (empty response body) | ||||||
|  | 
 | ||||||
|  | ### Authorization | ||||||
|  | 
 | ||||||
|  | [cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) | ||||||
|  | 
 | ||||||
|  | ### HTTP request headers | ||||||
|  | 
 | ||||||
|  |  - **Content-Type**: application/json | ||||||
|  |  - **Accept**: Not defined | ||||||
|  | 
 | ||||||
|  | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||||
|  | 
 | ||||||
|  | # **restoreTrash** | ||||||
|  | > restoreTrash() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### Example | ||||||
|  | ```dart | ||||||
|  | import 'package:openapi/api.dart'; | ||||||
|  | // TODO Configure API key authorization: cookie | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY'; | ||||||
|  | // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer'; | ||||||
|  | // TODO Configure API key authorization: api_key | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY'; | ||||||
|  | // uncomment below to setup prefix (e.g. Bearer) for API key, if needed | ||||||
|  | //defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer'; | ||||||
|  | // TODO Configure HTTP Bearer authorization: bearer | ||||||
|  | // Case 1. Use String Token | ||||||
|  | //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); | ||||||
|  | // Case 2. Use Function which generate token. | ||||||
|  | // String yourTokenGeneratorFunction() { ... } | ||||||
|  | //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); | ||||||
|  | 
 | ||||||
|  | final api_instance = AssetApi(); | ||||||
|  | 
 | ||||||
|  | try { | ||||||
|  |     api_instance.restoreTrash(); | ||||||
|  | } catch (e) { | ||||||
|  |     print('Exception when calling AssetApi->restoreTrash: $e\n'); | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Parameters | ||||||
|  | This endpoint does not need any parameter. | ||||||
|  | 
 | ||||||
|  | ### Return type | ||||||
|  | 
 | ||||||
|  | void (empty response body) | ||||||
|  | 
 | ||||||
|  | ### Authorization | ||||||
|  | 
 | ||||||
|  | [cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) | ||||||
|  | 
 | ||||||
|  | ### HTTP request headers | ||||||
|  | 
 | ||||||
|  |  - **Content-Type**: Not defined | ||||||
|  |  - **Accept**: Not defined | ||||||
|  | 
 | ||||||
|  | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||||
|  | 
 | ||||||
| # **runAssetJobs** | # **runAssetJobs** | ||||||
| > runAssetJobs(assetJobsDto) | > runAssetJobs(assetJobsDto) | ||||||
| 
 | 
 | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| # openapi.model.DeleteAssetDto | # openapi.model.AssetBulkDeleteDto | ||||||
| 
 | 
 | ||||||
| ## Load the model package | ## Load the model package | ||||||
| ```dart | ```dart | ||||||
| @@ -8,6 +8,7 @@ import 'package:openapi/api.dart'; | |||||||
| ## Properties | ## Properties | ||||||
| Name | Type | Description | Notes | Name | Type | Description | Notes | ||||||
| ------------ | ------------- | ------------- | ------------- | ------------ | ------------- | ------------- | ------------- | ||||||
|  | **force** | **bool** |  | [optional]  | ||||||
| **ids** | **List<String>** |  | [default to const []] | **ids** | **List<String>** |  | [default to const []] | ||||||
| 
 | 
 | ||||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||||
							
								
								
									
										1
									
								
								mobile/openapi/doc/AssetResponseDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								mobile/openapi/doc/AssetResponseDto.md
									
									
									
										generated
									
									
									
								
							| @@ -21,6 +21,7 @@ Name | Type | Description | Notes | |||||||
| **isFavorite** | **bool** |  |  | **isFavorite** | **bool** |  |  | ||||||
| **isOffline** | **bool** |  |  | **isOffline** | **bool** |  |  | ||||||
| **isReadOnly** | **bool** |  |  | **isReadOnly** | **bool** |  |  | ||||||
|  | **isTrashed** | **bool** |  |  | ||||||
| **libraryId** | **String** |  |  | **libraryId** | **String** |  |  | ||||||
| **livePhotoVideoId** | **String** |  | [optional]  | **livePhotoVideoId** | **String** |  | [optional]  | ||||||
| **localDateTime** | [**DateTime**](DateTime.md) |  |  | **localDateTime** | [**DateTime**](DateTime.md) |  |  | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								mobile/openapi/doc/DeleteAssetResponseDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										16
									
								
								mobile/openapi/doc/DeleteAssetResponseDto.md
									
									
									
										generated
									
									
									
								
							| @@ -1,16 +0,0 @@ | |||||||
| # openapi.model.DeleteAssetResponseDto |  | ||||||
| 
 |  | ||||||
| ## Load the model package |  | ||||||
| ```dart |  | ||||||
| import 'package:openapi/api.dart'; |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## Properties |  | ||||||
| Name | Type | Description | Notes |  | ||||||
| ------------ | ------------- | ------------- | ------------- |  | ||||||
| **id** | **String** |  |  |  | ||||||
| **status** | [**DeleteAssetStatus**](DeleteAssetStatus.md) |  |  |  | ||||||
| 
 |  | ||||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
							
								
								
									
										1
									
								
								mobile/openapi/doc/ServerConfigDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								mobile/openapi/doc/ServerConfigDto.md
									
									
									
										generated
									
									
									
								
							| @@ -11,6 +11,7 @@ Name | Type | Description | Notes | |||||||
| **loginPageMessage** | **String** |  |  | **loginPageMessage** | **String** |  |  | ||||||
| **mapTileUrl** | **String** |  |  | **mapTileUrl** | **String** |  |  | ||||||
| **oauthButtonText** | **String** |  |  | **oauthButtonText** | **String** |  |  | ||||||
|  | **trashDays** | **int** |  |  | ||||||
| 
 | 
 | ||||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								mobile/openapi/doc/ServerFeaturesDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								mobile/openapi/doc/ServerFeaturesDto.md
									
									
									
										generated
									
									
									
								
							| @@ -19,6 +19,7 @@ Name | Type | Description | Notes | |||||||
| **search** | **bool** |  |  | **search** | **bool** |  |  | ||||||
| **sidecar** | **bool** |  |  | **sidecar** | **bool** |  |  | ||||||
| **tagImage** | **bool** |  |  | **tagImage** | **bool** |  |  | ||||||
|  | **trash** | **bool** |  |  | ||||||
| 
 | 
 | ||||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								mobile/openapi/doc/SystemConfigDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								mobile/openapi/doc/SystemConfigDto.md
									
									
									
										generated
									
									
									
								
							| @@ -17,6 +17,7 @@ Name | Type | Description | Notes | |||||||
| **reverseGeocoding** | [**SystemConfigReverseGeocodingDto**](SystemConfigReverseGeocodingDto.md) |  |  | **reverseGeocoding** | [**SystemConfigReverseGeocodingDto**](SystemConfigReverseGeocodingDto.md) |  |  | ||||||
| **storageTemplate** | [**SystemConfigStorageTemplateDto**](SystemConfigStorageTemplateDto.md) |  |  | **storageTemplate** | [**SystemConfigStorageTemplateDto**](SystemConfigStorageTemplateDto.md) |  |  | ||||||
| **thumbnail** | [**SystemConfigThumbnailDto**](SystemConfigThumbnailDto.md) |  |  | **thumbnail** | [**SystemConfigThumbnailDto**](SystemConfigThumbnailDto.md) |  |  | ||||||
|  | **trash** | [**SystemConfigTrashDto**](SystemConfigTrashDto.md) |  |  | ||||||
| 
 | 
 | ||||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| # openapi.model.DeleteAssetStatus | # openapi.model.SystemConfigTrashDto | ||||||
| 
 | 
 | ||||||
| ## Load the model package | ## Load the model package | ||||||
| ```dart | ```dart | ||||||
| @@ -8,6 +8,8 @@ import 'package:openapi/api.dart'; | |||||||
| ## Properties | ## Properties | ||||||
| Name | Type | Description | Notes | Name | Type | Description | Notes | ||||||
| ------------ | ------------- | ------------- | ------------- | ------------ | ------------- | ------------- | ------------- | ||||||
|  | **days** | **int** |  |  | ||||||
|  | **enabled** | **bool** |  |  | ||||||
| 
 | 
 | ||||||
| [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||||
| 
 | 
 | ||||||
							
								
								
									
										5
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							| @@ -54,6 +54,7 @@ part 'model/admin_signup_response_dto.dart'; | |||||||
| part 'model/album_count_response_dto.dart'; | part 'model/album_count_response_dto.dart'; | ||||||
| part 'model/album_response_dto.dart'; | part 'model/album_response_dto.dart'; | ||||||
| part 'model/all_job_status_response_dto.dart'; | part 'model/all_job_status_response_dto.dart'; | ||||||
|  | part 'model/asset_bulk_delete_dto.dart'; | ||||||
| part 'model/asset_bulk_update_dto.dart'; | part 'model/asset_bulk_update_dto.dart'; | ||||||
| part 'model/asset_bulk_upload_check_dto.dart'; | part 'model/asset_bulk_upload_check_dto.dart'; | ||||||
| part 'model/asset_bulk_upload_check_item.dart'; | part 'model/asset_bulk_upload_check_item.dart'; | ||||||
| @@ -90,9 +91,6 @@ part 'model/create_tag_dto.dart'; | |||||||
| part 'model/create_user_dto.dart'; | part 'model/create_user_dto.dart'; | ||||||
| part 'model/curated_locations_response_dto.dart'; | part 'model/curated_locations_response_dto.dart'; | ||||||
| part 'model/curated_objects_response_dto.dart'; | part 'model/curated_objects_response_dto.dart'; | ||||||
| part 'model/delete_asset_dto.dart'; |  | ||||||
| part 'model/delete_asset_response_dto.dart'; |  | ||||||
| part 'model/delete_asset_status.dart'; |  | ||||||
| part 'model/download_archive_info.dart'; | part 'model/download_archive_info.dart'; | ||||||
| part 'model/download_info_dto.dart'; | part 'model/download_info_dto.dart'; | ||||||
| part 'model/download_response_dto.dart'; | part 'model/download_response_dto.dart'; | ||||||
| @@ -159,6 +157,7 @@ part 'model/system_config_reverse_geocoding_dto.dart'; | |||||||
| part 'model/system_config_storage_template_dto.dart'; | part 'model/system_config_storage_template_dto.dart'; | ||||||
| part 'model/system_config_template_storage_option_dto.dart'; | part 'model/system_config_template_storage_option_dto.dart'; | ||||||
| part 'model/system_config_thumbnail_dto.dart'; | part 'model/system_config_thumbnail_dto.dart'; | ||||||
|  | part 'model/system_config_trash_dto.dart'; | ||||||
| part 'model/tag_response_dto.dart'; | part 'model/tag_response_dto.dart'; | ||||||
| part 'model/tag_type_enum.dart'; | part 'model/tag_type_enum.dart'; | ||||||
| part 'model/thumbnail_format.dart'; | part 'model/thumbnail_format.dart'; | ||||||
|   | |||||||
							
								
								
									
										167
									
								
								mobile/openapi/lib/api/asset_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										167
									
								
								mobile/openapi/lib/api/asset_api.dart
									
									
									
										generated
									
									
									
								
							| @@ -183,13 +183,13 @@ class AssetApi { | |||||||
|   /// Performs an HTTP 'DELETE /asset' operation and returns the [Response]. |   /// Performs an HTTP 'DELETE /asset' operation and returns the [Response]. | ||||||
|   /// Parameters: |   /// Parameters: | ||||||
|   /// |   /// | ||||||
|   /// * [DeleteAssetDto] deleteAssetDto (required): |   /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): | ||||||
|   Future<Response> deleteAssetWithHttpInfo(DeleteAssetDto deleteAssetDto,) async { |   Future<Response> deleteAssetsWithHttpInfo(AssetBulkDeleteDto assetBulkDeleteDto,) async { | ||||||
|     // ignore: prefer_const_declarations |     // ignore: prefer_const_declarations | ||||||
|     final path = r'/asset'; |     final path = r'/asset'; | ||||||
| 
 | 
 | ||||||
|     // ignore: prefer_final_locals |     // ignore: prefer_final_locals | ||||||
|     Object? postBody = deleteAssetDto; |     Object? postBody = assetBulkDeleteDto; | ||||||
| 
 | 
 | ||||||
|     final queryParams = <QueryParam>[]; |     final queryParams = <QueryParam>[]; | ||||||
|     final headerParams = <String, String>{}; |     final headerParams = <String, String>{}; | ||||||
| @@ -211,23 +211,12 @@ class AssetApi { | |||||||
| 
 | 
 | ||||||
|   /// Parameters: |   /// Parameters: | ||||||
|   /// |   /// | ||||||
|   /// * [DeleteAssetDto] deleteAssetDto (required): |   /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): | ||||||
|   Future<List<DeleteAssetResponseDto>?> deleteAsset(DeleteAssetDto deleteAssetDto,) async { |   Future<void> deleteAssets(AssetBulkDeleteDto assetBulkDeleteDto,) async { | ||||||
|     final response = await deleteAssetWithHttpInfo(deleteAssetDto,); |     final response = await deleteAssetsWithHttpInfo(assetBulkDeleteDto,); | ||||||
|     if (response.statusCode >= HttpStatus.badRequest) { |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); |       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||||
|     } |     } | ||||||
|     // When a remote server returns no body with a status of 204, we shall not decode it. |  | ||||||
|     // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" |  | ||||||
|     // FormatException when trying to decode an empty string. |  | ||||||
|     if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { |  | ||||||
|       final responseBody = await _decodeBodyBytes(response); |  | ||||||
|       return (await apiClient.deserializeAsync(responseBody, 'List<DeleteAssetResponseDto>') as List) |  | ||||||
|         .cast<DeleteAssetResponseDto>() |  | ||||||
|         .toList(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
|     return null; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Performs an HTTP 'POST /asset/download/archive' operation and returns the [Response]. |   /// Performs an HTTP 'POST /asset/download/archive' operation and returns the [Response]. | ||||||
| @@ -341,6 +330,39 @@ class AssetApi { | |||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// Performs an HTTP 'POST /asset/trash/empty' operation and returns the [Response]. | ||||||
|  |   Future<Response> emptyTrashWithHttpInfo() async { | ||||||
|  |     // ignore: prefer_const_declarations | ||||||
|  |     final path = r'/asset/trash/empty'; | ||||||
|  | 
 | ||||||
|  |     // ignore: prefer_final_locals | ||||||
|  |     Object? postBody; | ||||||
|  | 
 | ||||||
|  |     final queryParams = <QueryParam>[]; | ||||||
|  |     final headerParams = <String, String>{}; | ||||||
|  |     final formParams = <String, String>{}; | ||||||
|  | 
 | ||||||
|  |     const contentTypes = <String>[]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     return apiClient.invokeAPI( | ||||||
|  |       path, | ||||||
|  |       'POST', | ||||||
|  |       queryParams, | ||||||
|  |       postBody, | ||||||
|  |       headerParams, | ||||||
|  |       formParams, | ||||||
|  |       contentTypes.isEmpty ? null : contentTypes.first, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> emptyTrash() async { | ||||||
|  |     final response = await emptyTrashWithHttpInfo(); | ||||||
|  |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|  |       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   /// Get all AssetEntity belong to the user |   /// Get all AssetEntity belong to the user | ||||||
|   /// |   /// | ||||||
|   /// Note: This method returns the HTTP [Response]. |   /// Note: This method returns the HTTP [Response]. | ||||||
| @@ -549,7 +571,9 @@ class AssetApi { | |||||||
|   /// * [bool] isArchived: |   /// * [bool] isArchived: | ||||||
|   /// |   /// | ||||||
|   /// * [bool] isFavorite: |   /// * [bool] isFavorite: | ||||||
|   Future<Response> getAssetStatsWithHttpInfo({ bool? isArchived, bool? isFavorite, }) async { |   /// | ||||||
|  |   /// * [bool] isTrashed: | ||||||
|  |   Future<Response> getAssetStatsWithHttpInfo({ bool? isArchived, bool? isFavorite, bool? isTrashed, }) async { | ||||||
|     // ignore: prefer_const_declarations |     // ignore: prefer_const_declarations | ||||||
|     final path = r'/asset/statistics'; |     final path = r'/asset/statistics'; | ||||||
| 
 | 
 | ||||||
| @@ -566,6 +590,9 @@ class AssetApi { | |||||||
|     if (isFavorite != null) { |     if (isFavorite != null) { | ||||||
|       queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); |       queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); | ||||||
|     } |     } | ||||||
|  |     if (isTrashed != null) { | ||||||
|  |       queryParams.addAll(_queryParams('', 'isTrashed', isTrashed)); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     const contentTypes = <String>[]; |     const contentTypes = <String>[]; | ||||||
| 
 | 
 | ||||||
| @@ -586,8 +613,10 @@ class AssetApi { | |||||||
|   /// * [bool] isArchived: |   /// * [bool] isArchived: | ||||||
|   /// |   /// | ||||||
|   /// * [bool] isFavorite: |   /// * [bool] isFavorite: | ||||||
|   Future<AssetStatsResponseDto?> getAssetStats({ bool? isArchived, bool? isFavorite, }) async { |   /// | ||||||
|     final response = await getAssetStatsWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, ); |   /// * [bool] isTrashed: | ||||||
|  |   Future<AssetStatsResponseDto?> getAssetStats({ bool? isArchived, bool? isFavorite, bool? isTrashed, }) async { | ||||||
|  |     final response = await getAssetStatsWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, ); | ||||||
|     if (response.statusCode >= HttpStatus.badRequest) { |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); |       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||||
|     } |     } | ||||||
| @@ -681,8 +710,10 @@ class AssetApi { | |||||||
|   /// |   /// | ||||||
|   /// * [bool] isFavorite: |   /// * [bool] isFavorite: | ||||||
|   /// |   /// | ||||||
|  |   /// * [bool] isTrashed: | ||||||
|  |   /// | ||||||
|   /// * [String] key: |   /// * [String] key: | ||||||
|   Future<Response> getByTimeBucketWithHttpInfo(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, String? key, }) async { |   Future<Response> getByTimeBucketWithHttpInfo(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async { | ||||||
|     // ignore: prefer_const_declarations |     // ignore: prefer_const_declarations | ||||||
|     final path = r'/asset/time-bucket'; |     final path = r'/asset/time-bucket'; | ||||||
| 
 | 
 | ||||||
| @@ -708,6 +739,9 @@ class AssetApi { | |||||||
|     } |     } | ||||||
|     if (isFavorite != null) { |     if (isFavorite != null) { | ||||||
|       queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); |       queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); | ||||||
|  |     } | ||||||
|  |     if (isTrashed != null) { | ||||||
|  |       queryParams.addAll(_queryParams('', 'isTrashed', isTrashed)); | ||||||
|     } |     } | ||||||
|       queryParams.addAll(_queryParams('', 'timeBucket', timeBucket)); |       queryParams.addAll(_queryParams('', 'timeBucket', timeBucket)); | ||||||
|     if (key != null) { |     if (key != null) { | ||||||
| @@ -744,9 +778,11 @@ class AssetApi { | |||||||
|   /// |   /// | ||||||
|   /// * [bool] isFavorite: |   /// * [bool] isFavorite: | ||||||
|   /// |   /// | ||||||
|  |   /// * [bool] isTrashed: | ||||||
|  |   /// | ||||||
|   /// * [String] key: |   /// * [String] key: | ||||||
|   Future<List<AssetResponseDto>?> getByTimeBucket(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, String? key, }) async { |   Future<List<AssetResponseDto>?> getByTimeBucket(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async { | ||||||
|     final response = await getByTimeBucketWithHttpInfo(size, timeBucket,  userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, key: key, ); |     final response = await getByTimeBucketWithHttpInfo(size, timeBucket,  userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, ); | ||||||
|     if (response.statusCode >= HttpStatus.badRequest) { |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); |       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||||
|     } |     } | ||||||
| @@ -1107,8 +1143,10 @@ class AssetApi { | |||||||
|   /// |   /// | ||||||
|   /// * [bool] isFavorite: |   /// * [bool] isFavorite: | ||||||
|   /// |   /// | ||||||
|  |   /// * [bool] isTrashed: | ||||||
|  |   /// | ||||||
|   /// * [String] key: |   /// * [String] key: | ||||||
|   Future<Response> getTimeBucketsWithHttpInfo(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, String? key, }) async { |   Future<Response> getTimeBucketsWithHttpInfo(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async { | ||||||
|     // ignore: prefer_const_declarations |     // ignore: prefer_const_declarations | ||||||
|     final path = r'/asset/time-buckets'; |     final path = r'/asset/time-buckets'; | ||||||
| 
 | 
 | ||||||
| @@ -1135,6 +1173,9 @@ class AssetApi { | |||||||
|     if (isFavorite != null) { |     if (isFavorite != null) { | ||||||
|       queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); |       queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); | ||||||
|     } |     } | ||||||
|  |     if (isTrashed != null) { | ||||||
|  |       queryParams.addAll(_queryParams('', 'isTrashed', isTrashed)); | ||||||
|  |     } | ||||||
|     if (key != null) { |     if (key != null) { | ||||||
|       queryParams.addAll(_queryParams('', 'key', key)); |       queryParams.addAll(_queryParams('', 'key', key)); | ||||||
|     } |     } | ||||||
| @@ -1167,9 +1208,11 @@ class AssetApi { | |||||||
|   /// |   /// | ||||||
|   /// * [bool] isFavorite: |   /// * [bool] isFavorite: | ||||||
|   /// |   /// | ||||||
|  |   /// * [bool] isTrashed: | ||||||
|  |   /// | ||||||
|   /// * [String] key: |   /// * [String] key: | ||||||
|   Future<List<TimeBucketResponseDto>?> getTimeBuckets(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, String? key, }) async { |   Future<List<TimeBucketResponseDto>?> getTimeBuckets(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async { | ||||||
|     final response = await getTimeBucketsWithHttpInfo(size,  userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, key: key, ); |     final response = await getTimeBucketsWithHttpInfo(size,  userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, ); | ||||||
|     if (response.statusCode >= HttpStatus.badRequest) { |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); |       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||||
|     } |     } | ||||||
| @@ -1289,6 +1332,78 @@ class AssetApi { | |||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// Performs an HTTP 'POST /asset/restore' operation and returns the [Response]. | ||||||
|  |   /// Parameters: | ||||||
|  |   /// | ||||||
|  |   /// * [BulkIdsDto] bulkIdsDto (required): | ||||||
|  |   Future<Response> restoreAssetsWithHttpInfo(BulkIdsDto bulkIdsDto,) async { | ||||||
|  |     // ignore: prefer_const_declarations | ||||||
|  |     final path = r'/asset/restore'; | ||||||
|  | 
 | ||||||
|  |     // ignore: prefer_final_locals | ||||||
|  |     Object? postBody = bulkIdsDto; | ||||||
|  | 
 | ||||||
|  |     final queryParams = <QueryParam>[]; | ||||||
|  |     final headerParams = <String, String>{}; | ||||||
|  |     final formParams = <String, String>{}; | ||||||
|  | 
 | ||||||
|  |     const contentTypes = <String>['application/json']; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     return apiClient.invokeAPI( | ||||||
|  |       path, | ||||||
|  |       'POST', | ||||||
|  |       queryParams, | ||||||
|  |       postBody, | ||||||
|  |       headerParams, | ||||||
|  |       formParams, | ||||||
|  |       contentTypes.isEmpty ? null : contentTypes.first, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Parameters: | ||||||
|  |   /// | ||||||
|  |   /// * [BulkIdsDto] bulkIdsDto (required): | ||||||
|  |   Future<void> restoreAssets(BulkIdsDto bulkIdsDto,) async { | ||||||
|  |     final response = await restoreAssetsWithHttpInfo(bulkIdsDto,); | ||||||
|  |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|  |       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Performs an HTTP 'POST /asset/trash/restore' operation and returns the [Response]. | ||||||
|  |   Future<Response> restoreTrashWithHttpInfo() async { | ||||||
|  |     // ignore: prefer_const_declarations | ||||||
|  |     final path = r'/asset/trash/restore'; | ||||||
|  | 
 | ||||||
|  |     // ignore: prefer_final_locals | ||||||
|  |     Object? postBody; | ||||||
|  | 
 | ||||||
|  |     final queryParams = <QueryParam>[]; | ||||||
|  |     final headerParams = <String, String>{}; | ||||||
|  |     final formParams = <String, String>{}; | ||||||
|  | 
 | ||||||
|  |     const contentTypes = <String>[]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     return apiClient.invokeAPI( | ||||||
|  |       path, | ||||||
|  |       'POST', | ||||||
|  |       queryParams, | ||||||
|  |       postBody, | ||||||
|  |       headerParams, | ||||||
|  |       formParams, | ||||||
|  |       contentTypes.isEmpty ? null : contentTypes.first, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> restoreTrash() async { | ||||||
|  |     final response = await restoreTrashWithHttpInfo(); | ||||||
|  |     if (response.statusCode >= HttpStatus.badRequest) { | ||||||
|  |       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   /// Performs an HTTP 'POST /asset/jobs' operation and returns the [Response]. |   /// Performs an HTTP 'POST /asset/jobs' operation and returns the [Response]. | ||||||
|   /// Parameters: |   /// Parameters: | ||||||
|   /// |   /// | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							| @@ -199,6 +199,8 @@ class ApiClient { | |||||||
|           return AlbumResponseDto.fromJson(value); |           return AlbumResponseDto.fromJson(value); | ||||||
|         case 'AllJobStatusResponseDto': |         case 'AllJobStatusResponseDto': | ||||||
|           return AllJobStatusResponseDto.fromJson(value); |           return AllJobStatusResponseDto.fromJson(value); | ||||||
|  |         case 'AssetBulkDeleteDto': | ||||||
|  |           return AssetBulkDeleteDto.fromJson(value); | ||||||
|         case 'AssetBulkUpdateDto': |         case 'AssetBulkUpdateDto': | ||||||
|           return AssetBulkUpdateDto.fromJson(value); |           return AssetBulkUpdateDto.fromJson(value); | ||||||
|         case 'AssetBulkUploadCheckDto': |         case 'AssetBulkUploadCheckDto': | ||||||
| @@ -271,12 +273,6 @@ class ApiClient { | |||||||
|           return CuratedLocationsResponseDto.fromJson(value); |           return CuratedLocationsResponseDto.fromJson(value); | ||||||
|         case 'CuratedObjectsResponseDto': |         case 'CuratedObjectsResponseDto': | ||||||
|           return CuratedObjectsResponseDto.fromJson(value); |           return CuratedObjectsResponseDto.fromJson(value); | ||||||
|         case 'DeleteAssetDto': |  | ||||||
|           return DeleteAssetDto.fromJson(value); |  | ||||||
|         case 'DeleteAssetResponseDto': |  | ||||||
|           return DeleteAssetResponseDto.fromJson(value); |  | ||||||
|         case 'DeleteAssetStatus': |  | ||||||
|           return DeleteAssetStatusTypeTransformer().decode(value); |  | ||||||
|         case 'DownloadArchiveInfo': |         case 'DownloadArchiveInfo': | ||||||
|           return DownloadArchiveInfo.fromJson(value); |           return DownloadArchiveInfo.fromJson(value); | ||||||
|         case 'DownloadInfoDto': |         case 'DownloadInfoDto': | ||||||
| @@ -409,6 +405,8 @@ class ApiClient { | |||||||
|           return SystemConfigTemplateStorageOptionDto.fromJson(value); |           return SystemConfigTemplateStorageOptionDto.fromJson(value); | ||||||
|         case 'SystemConfigThumbnailDto': |         case 'SystemConfigThumbnailDto': | ||||||
|           return SystemConfigThumbnailDto.fromJson(value); |           return SystemConfigThumbnailDto.fromJson(value); | ||||||
|  |         case 'SystemConfigTrashDto': | ||||||
|  |           return SystemConfigTrashDto.fromJson(value); | ||||||
|         case 'TagResponseDto': |         case 'TagResponseDto': | ||||||
|           return TagResponseDto.fromJson(value); |           return TagResponseDto.fromJson(value); | ||||||
|         case 'TagTypeEnum': |         case 'TagTypeEnum': | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								mobile/openapi/lib/api_helper.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								mobile/openapi/lib/api_helper.dart
									
									
									
										generated
									
									
									
								
							| @@ -76,9 +76,6 @@ String parameterToString(dynamic value) { | |||||||
|   if (value is Colorspace) { |   if (value is Colorspace) { | ||||||
|     return ColorspaceTypeTransformer().encode(value).toString(); |     return ColorspaceTypeTransformer().encode(value).toString(); | ||||||
|   } |   } | ||||||
|   if (value is DeleteAssetStatus) { |  | ||||||
|     return DeleteAssetStatusTypeTransformer().encode(value).toString(); |  | ||||||
|   } |  | ||||||
|   if (value is EntityType) { |   if (value is EntityType) { | ||||||
|     return EntityTypeTypeTransformer().encode(value).toString(); |     return EntityTypeTypeTransformer().encode(value).toString(); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -10,40 +10,57 @@ | |||||||
| 
 | 
 | ||||||
| part of openapi.api; | part of openapi.api; | ||||||
| 
 | 
 | ||||||
| class DeleteAssetDto { | class AssetBulkDeleteDto { | ||||||
|   /// Returns a new [DeleteAssetDto] instance. |   /// Returns a new [AssetBulkDeleteDto] instance. | ||||||
|   DeleteAssetDto({ |   AssetBulkDeleteDto({ | ||||||
|  |     this.force, | ||||||
|     this.ids = const [], |     this.ids = const [], | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |   /// | ||||||
|  |   /// Please note: This property should have been non-nullable! Since the specification file | ||||||
|  |   /// does not include a default value (using the "default:" property), however, the generated | ||||||
|  |   /// source code must fall back to having a nullable type. | ||||||
|  |   /// Consider adding a "default:" property in the specification file to hide this note. | ||||||
|  |   /// | ||||||
|  |   bool? force; | ||||||
|  | 
 | ||||||
|   List<String> ids; |   List<String> ids; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   bool operator ==(Object other) => identical(this, other) || other is DeleteAssetDto && |   bool operator ==(Object other) => identical(this, other) || other is AssetBulkDeleteDto && | ||||||
|  |      other.force == force && | ||||||
|      other.ids == ids; |      other.ids == ids; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   int get hashCode => |   int get hashCode => | ||||||
|     // ignore: unnecessary_parenthesis |     // ignore: unnecessary_parenthesis | ||||||
|  |     (force == null ? 0 : force!.hashCode) + | ||||||
|     (ids.hashCode); |     (ids.hashCode); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'DeleteAssetDto[ids=$ids]'; |   String toString() => 'AssetBulkDeleteDto[force=$force, ids=$ids]'; | ||||||
| 
 | 
 | ||||||
|   Map<String, dynamic> toJson() { |   Map<String, dynamic> toJson() { | ||||||
|     final json = <String, dynamic>{}; |     final json = <String, dynamic>{}; | ||||||
|  |     if (this.force != null) { | ||||||
|  |       json[r'force'] = this.force; | ||||||
|  |     } else { | ||||||
|  |     //  json[r'force'] = null; | ||||||
|  |     } | ||||||
|       json[r'ids'] = this.ids; |       json[r'ids'] = this.ids; | ||||||
|     return json; |     return json; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Returns a new [DeleteAssetDto] instance and imports its values from |   /// Returns a new [AssetBulkDeleteDto] instance and imports its values from | ||||||
|   /// [value] if it's a [Map], null otherwise. |   /// [value] if it's a [Map], null otherwise. | ||||||
|   // ignore: prefer_constructors_over_static_methods |   // ignore: prefer_constructors_over_static_methods | ||||||
|   static DeleteAssetDto? fromJson(dynamic value) { |   static AssetBulkDeleteDto? fromJson(dynamic value) { | ||||||
|     if (value is Map) { |     if (value is Map) { | ||||||
|       final json = value.cast<String, dynamic>(); |       final json = value.cast<String, dynamic>(); | ||||||
| 
 | 
 | ||||||
|       return DeleteAssetDto( |       return AssetBulkDeleteDto( | ||||||
|  |         force: mapValueOfType<bool>(json, r'force'), | ||||||
|         ids: json[r'ids'] is List |         ids: json[r'ids'] is List | ||||||
|             ? (json[r'ids'] as List).cast<String>() |             ? (json[r'ids'] as List).cast<String>() | ||||||
|             : const [], |             : const [], | ||||||
| @@ -52,11 +69,11 @@ class DeleteAssetDto { | |||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static List<DeleteAssetDto> listFromJson(dynamic json, {bool growable = false,}) { |   static List<AssetBulkDeleteDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||||
|     final result = <DeleteAssetDto>[]; |     final result = <AssetBulkDeleteDto>[]; | ||||||
|     if (json is List && json.isNotEmpty) { |     if (json is List && json.isNotEmpty) { | ||||||
|       for (final row in json) { |       for (final row in json) { | ||||||
|         final value = DeleteAssetDto.fromJson(row); |         final value = AssetBulkDeleteDto.fromJson(row); | ||||||
|         if (value != null) { |         if (value != null) { | ||||||
|           result.add(value); |           result.add(value); | ||||||
|         } |         } | ||||||
| @@ -65,12 +82,12 @@ class DeleteAssetDto { | |||||||
|     return result.toList(growable: growable); |     return result.toList(growable: growable); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static Map<String, DeleteAssetDto> mapFromJson(dynamic json) { |   static Map<String, AssetBulkDeleteDto> mapFromJson(dynamic json) { | ||||||
|     final map = <String, DeleteAssetDto>{}; |     final map = <String, AssetBulkDeleteDto>{}; | ||||||
|     if (json is Map && json.isNotEmpty) { |     if (json is Map && json.isNotEmpty) { | ||||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments |       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||||
|       for (final entry in json.entries) { |       for (final entry in json.entries) { | ||||||
|         final value = DeleteAssetDto.fromJson(entry.value); |         final value = AssetBulkDeleteDto.fromJson(entry.value); | ||||||
|         if (value != null) { |         if (value != null) { | ||||||
|           map[entry.key] = value; |           map[entry.key] = value; | ||||||
|         } |         } | ||||||
| @@ -79,14 +96,14 @@ class DeleteAssetDto { | |||||||
|     return map; |     return map; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // maps a json object with a list of DeleteAssetDto-objects as value to a dart map |   // maps a json object with a list of AssetBulkDeleteDto-objects as value to a dart map | ||||||
|   static Map<String, List<DeleteAssetDto>> mapListFromJson(dynamic json, {bool growable = false,}) { |   static Map<String, List<AssetBulkDeleteDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||||
|     final map = <String, List<DeleteAssetDto>>{}; |     final map = <String, List<AssetBulkDeleteDto>>{}; | ||||||
|     if (json is Map && json.isNotEmpty) { |     if (json is Map && json.isNotEmpty) { | ||||||
|       // ignore: parameter_assignments |       // ignore: parameter_assignments | ||||||
|       json = json.cast<String, dynamic>(); |       json = json.cast<String, dynamic>(); | ||||||
|       for (final entry in json.entries) { |       for (final entry in json.entries) { | ||||||
|         map[entry.key] = DeleteAssetDto.listFromJson(entry.value, growable: growable,); |         map[entry.key] = AssetBulkDeleteDto.listFromJson(entry.value, growable: growable,); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     return map; |     return map; | ||||||
							
								
								
									
										10
									
								
								mobile/openapi/lib/model/asset_response_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/lib/model/asset_response_dto.dart
									
									
									
										generated
									
									
									
								
							| @@ -26,6 +26,7 @@ class AssetResponseDto { | |||||||
|     required this.isFavorite, |     required this.isFavorite, | ||||||
|     required this.isOffline, |     required this.isOffline, | ||||||
|     required this.isReadOnly, |     required this.isReadOnly, | ||||||
|  |     required this.isTrashed, | ||||||
|     required this.libraryId, |     required this.libraryId, | ||||||
|     this.livePhotoVideoId, |     this.livePhotoVideoId, | ||||||
|     required this.localDateTime, |     required this.localDateTime, | ||||||
| @@ -75,6 +76,8 @@ class AssetResponseDto { | |||||||
| 
 | 
 | ||||||
|   bool isReadOnly; |   bool isReadOnly; | ||||||
| 
 | 
 | ||||||
|  |   bool isTrashed; | ||||||
|  | 
 | ||||||
|   String libraryId; |   String libraryId; | ||||||
| 
 | 
 | ||||||
|   String? livePhotoVideoId; |   String? livePhotoVideoId; | ||||||
| @@ -131,6 +134,7 @@ class AssetResponseDto { | |||||||
|      other.isFavorite == isFavorite && |      other.isFavorite == isFavorite && | ||||||
|      other.isOffline == isOffline && |      other.isOffline == isOffline && | ||||||
|      other.isReadOnly == isReadOnly && |      other.isReadOnly == isReadOnly && | ||||||
|  |      other.isTrashed == isTrashed && | ||||||
|      other.libraryId == libraryId && |      other.libraryId == libraryId && | ||||||
|      other.livePhotoVideoId == livePhotoVideoId && |      other.livePhotoVideoId == livePhotoVideoId && | ||||||
|      other.localDateTime == localDateTime && |      other.localDateTime == localDateTime && | ||||||
| @@ -162,6 +166,7 @@ class AssetResponseDto { | |||||||
|     (isFavorite.hashCode) + |     (isFavorite.hashCode) + | ||||||
|     (isOffline.hashCode) + |     (isOffline.hashCode) + | ||||||
|     (isReadOnly.hashCode) + |     (isReadOnly.hashCode) + | ||||||
|  |     (isTrashed.hashCode) + | ||||||
|     (libraryId.hashCode) + |     (libraryId.hashCode) + | ||||||
|     (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + |     (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + | ||||||
|     (localDateTime.hashCode) + |     (localDateTime.hashCode) + | ||||||
| @@ -178,7 +183,7 @@ class AssetResponseDto { | |||||||
|     (updatedAt.hashCode); |     (updatedAt.hashCode); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isArchived=$isArchived, isExternal=$isExternal, isFavorite=$isFavorite, isOffline=$isOffline, isReadOnly=$isReadOnly, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]'; |   String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isArchived=$isArchived, isExternal=$isExternal, isFavorite=$isFavorite, isOffline=$isOffline, isReadOnly=$isReadOnly, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]'; | ||||||
| 
 | 
 | ||||||
|   Map<String, dynamic> toJson() { |   Map<String, dynamic> toJson() { | ||||||
|     final json = <String, dynamic>{}; |     final json = <String, dynamic>{}; | ||||||
| @@ -199,6 +204,7 @@ class AssetResponseDto { | |||||||
|       json[r'isFavorite'] = this.isFavorite; |       json[r'isFavorite'] = this.isFavorite; | ||||||
|       json[r'isOffline'] = this.isOffline; |       json[r'isOffline'] = this.isOffline; | ||||||
|       json[r'isReadOnly'] = this.isReadOnly; |       json[r'isReadOnly'] = this.isReadOnly; | ||||||
|  |       json[r'isTrashed'] = this.isTrashed; | ||||||
|       json[r'libraryId'] = this.libraryId; |       json[r'libraryId'] = this.libraryId; | ||||||
|     if (this.livePhotoVideoId != null) { |     if (this.livePhotoVideoId != null) { | ||||||
|       json[r'livePhotoVideoId'] = this.livePhotoVideoId; |       json[r'livePhotoVideoId'] = this.livePhotoVideoId; | ||||||
| @@ -253,6 +259,7 @@ class AssetResponseDto { | |||||||
|         isFavorite: mapValueOfType<bool>(json, r'isFavorite')!, |         isFavorite: mapValueOfType<bool>(json, r'isFavorite')!, | ||||||
|         isOffline: mapValueOfType<bool>(json, r'isOffline')!, |         isOffline: mapValueOfType<bool>(json, r'isOffline')!, | ||||||
|         isReadOnly: mapValueOfType<bool>(json, r'isReadOnly')!, |         isReadOnly: mapValueOfType<bool>(json, r'isReadOnly')!, | ||||||
|  |         isTrashed: mapValueOfType<bool>(json, r'isTrashed')!, | ||||||
|         libraryId: mapValueOfType<String>(json, r'libraryId')!, |         libraryId: mapValueOfType<String>(json, r'libraryId')!, | ||||||
|         livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'), |         livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'), | ||||||
|         localDateTime: mapDateTime(json, r'localDateTime', '')!, |         localDateTime: mapDateTime(json, r'localDateTime', '')!, | ||||||
| @@ -326,6 +333,7 @@ class AssetResponseDto { | |||||||
|     'isFavorite', |     'isFavorite', | ||||||
|     'isOffline', |     'isOffline', | ||||||
|     'isReadOnly', |     'isReadOnly', | ||||||
|  |     'isTrashed', | ||||||
|     'libraryId', |     'libraryId', | ||||||
|     'localDateTime', |     'localDateTime', | ||||||
|     'originalFileName', |     'originalFileName', | ||||||
|   | |||||||
							
								
								
									
										85
									
								
								mobile/openapi/lib/model/delete_asset_status.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										85
									
								
								mobile/openapi/lib/model/delete_asset_status.dart
									
									
									
										generated
									
									
									
								
							| @@ -1,85 +0,0 @@ | |||||||
| // |  | ||||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! |  | ||||||
| // |  | ||||||
| // @dart=2.12 |  | ||||||
| 
 |  | ||||||
| // ignore_for_file: unused_element, unused_import |  | ||||||
| // ignore_for_file: always_put_required_named_parameters_first |  | ||||||
| // ignore_for_file: constant_identifier_names |  | ||||||
| // ignore_for_file: lines_longer_than_80_chars |  | ||||||
| 
 |  | ||||||
| part of openapi.api; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DeleteAssetStatus { |  | ||||||
|   /// Instantiate a new enum with the provided [value]. |  | ||||||
|   const DeleteAssetStatus._(this.value); |  | ||||||
| 
 |  | ||||||
|   /// The underlying value of this enum member. |  | ||||||
|   final String value; |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   String toString() => value; |  | ||||||
| 
 |  | ||||||
|   String toJson() => value; |  | ||||||
| 
 |  | ||||||
|   static const SUCCESS = DeleteAssetStatus._(r'SUCCESS'); |  | ||||||
|   static const FAILED = DeleteAssetStatus._(r'FAILED'); |  | ||||||
| 
 |  | ||||||
|   /// List of all possible values in this [enum][DeleteAssetStatus]. |  | ||||||
|   static const values = <DeleteAssetStatus>[ |  | ||||||
|     SUCCESS, |  | ||||||
|     FAILED, |  | ||||||
|   ]; |  | ||||||
| 
 |  | ||||||
|   static DeleteAssetStatus? fromJson(dynamic value) => DeleteAssetStatusTypeTransformer().decode(value); |  | ||||||
| 
 |  | ||||||
|   static List<DeleteAssetStatus>? listFromJson(dynamic json, {bool growable = false,}) { |  | ||||||
|     final result = <DeleteAssetStatus>[]; |  | ||||||
|     if (json is List && json.isNotEmpty) { |  | ||||||
|       for (final row in json) { |  | ||||||
|         final value = DeleteAssetStatus.fromJson(row); |  | ||||||
|         if (value != null) { |  | ||||||
|           result.add(value); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return result.toList(growable: growable); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Transformation class that can [encode] an instance of [DeleteAssetStatus] to String, |  | ||||||
| /// and [decode] dynamic data back to [DeleteAssetStatus]. |  | ||||||
| class DeleteAssetStatusTypeTransformer { |  | ||||||
|   factory DeleteAssetStatusTypeTransformer() => _instance ??= const DeleteAssetStatusTypeTransformer._(); |  | ||||||
| 
 |  | ||||||
|   const DeleteAssetStatusTypeTransformer._(); |  | ||||||
| 
 |  | ||||||
|   String encode(DeleteAssetStatus data) => data.value; |  | ||||||
| 
 |  | ||||||
|   /// Decodes a [dynamic value][data] to a DeleteAssetStatus. |  | ||||||
|   /// |  | ||||||
|   /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, |  | ||||||
|   /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] |  | ||||||
|   /// cannot be decoded successfully, then an [UnimplementedError] is thrown. |  | ||||||
|   /// |  | ||||||
|   /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, |  | ||||||
|   /// and users are still using an old app with the old code. |  | ||||||
|   DeleteAssetStatus? decode(dynamic data, {bool allowNull = true}) { |  | ||||||
|     if (data != null) { |  | ||||||
|       switch (data) { |  | ||||||
|         case r'SUCCESS': return DeleteAssetStatus.SUCCESS; |  | ||||||
|         case r'FAILED': return DeleteAssetStatus.FAILED; |  | ||||||
|         default: |  | ||||||
|           if (!allowNull) { |  | ||||||
|             throw ArgumentError('Unknown enum value to decode: $data'); |  | ||||||
|           } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return null; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /// Singleton [DeleteAssetStatusTypeTransformer] instance. |  | ||||||
|   static DeleteAssetStatusTypeTransformer? _instance; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
							
								
								
									
										14
									
								
								mobile/openapi/lib/model/server_config_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								mobile/openapi/lib/model/server_config_dto.dart
									
									
									
										generated
									
									
									
								
							| @@ -16,6 +16,7 @@ class ServerConfigDto { | |||||||
|     required this.loginPageMessage, |     required this.loginPageMessage, | ||||||
|     required this.mapTileUrl, |     required this.mapTileUrl, | ||||||
|     required this.oauthButtonText, |     required this.oauthButtonText, | ||||||
|  |     required this.trashDays, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   String loginPageMessage; |   String loginPageMessage; | ||||||
| @@ -24,27 +25,32 @@ class ServerConfigDto { | |||||||
| 
 | 
 | ||||||
|   String oauthButtonText; |   String oauthButtonText; | ||||||
| 
 | 
 | ||||||
|  |   int trashDays; | ||||||
|  | 
 | ||||||
|   @override |   @override | ||||||
|   bool operator ==(Object other) => identical(this, other) || other is ServerConfigDto && |   bool operator ==(Object other) => identical(this, other) || other is ServerConfigDto && | ||||||
|      other.loginPageMessage == loginPageMessage && |      other.loginPageMessage == loginPageMessage && | ||||||
|      other.mapTileUrl == mapTileUrl && |      other.mapTileUrl == mapTileUrl && | ||||||
|      other.oauthButtonText == oauthButtonText; |      other.oauthButtonText == oauthButtonText && | ||||||
|  |      other.trashDays == trashDays; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   int get hashCode => |   int get hashCode => | ||||||
|     // ignore: unnecessary_parenthesis |     // ignore: unnecessary_parenthesis | ||||||
|     (loginPageMessage.hashCode) + |     (loginPageMessage.hashCode) + | ||||||
|     (mapTileUrl.hashCode) + |     (mapTileUrl.hashCode) + | ||||||
|     (oauthButtonText.hashCode); |     (oauthButtonText.hashCode) + | ||||||
|  |     (trashDays.hashCode); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'ServerConfigDto[loginPageMessage=$loginPageMessage, mapTileUrl=$mapTileUrl, oauthButtonText=$oauthButtonText]'; |   String toString() => 'ServerConfigDto[loginPageMessage=$loginPageMessage, mapTileUrl=$mapTileUrl, oauthButtonText=$oauthButtonText, trashDays=$trashDays]'; | ||||||
| 
 | 
 | ||||||
|   Map<String, dynamic> toJson() { |   Map<String, dynamic> toJson() { | ||||||
|     final json = <String, dynamic>{}; |     final json = <String, dynamic>{}; | ||||||
|       json[r'loginPageMessage'] = this.loginPageMessage; |       json[r'loginPageMessage'] = this.loginPageMessage; | ||||||
|       json[r'mapTileUrl'] = this.mapTileUrl; |       json[r'mapTileUrl'] = this.mapTileUrl; | ||||||
|       json[r'oauthButtonText'] = this.oauthButtonText; |       json[r'oauthButtonText'] = this.oauthButtonText; | ||||||
|  |       json[r'trashDays'] = this.trashDays; | ||||||
|     return json; |     return json; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @@ -59,6 +65,7 @@ class ServerConfigDto { | |||||||
|         loginPageMessage: mapValueOfType<String>(json, r'loginPageMessage')!, |         loginPageMessage: mapValueOfType<String>(json, r'loginPageMessage')!, | ||||||
|         mapTileUrl: mapValueOfType<String>(json, r'mapTileUrl')!, |         mapTileUrl: mapValueOfType<String>(json, r'mapTileUrl')!, | ||||||
|         oauthButtonText: mapValueOfType<String>(json, r'oauthButtonText')!, |         oauthButtonText: mapValueOfType<String>(json, r'oauthButtonText')!, | ||||||
|  |         trashDays: mapValueOfType<int>(json, r'trashDays')!, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
| @@ -109,6 +116,7 @@ class ServerConfigDto { | |||||||
|     'loginPageMessage', |     'loginPageMessage', | ||||||
|     'mapTileUrl', |     'mapTileUrl', | ||||||
|     'oauthButtonText', |     'oauthButtonText', | ||||||
|  |     'trashDays', | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								mobile/openapi/lib/model/server_features_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								mobile/openapi/lib/model/server_features_dto.dart
									
									
									
										generated
									
									
									
								
							| @@ -24,6 +24,7 @@ class ServerFeaturesDto { | |||||||
|     required this.search, |     required this.search, | ||||||
|     required this.sidecar, |     required this.sidecar, | ||||||
|     required this.tagImage, |     required this.tagImage, | ||||||
|  |     required this.trash, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   bool clipEncode; |   bool clipEncode; | ||||||
| @@ -48,6 +49,8 @@ class ServerFeaturesDto { | |||||||
| 
 | 
 | ||||||
|   bool tagImage; |   bool tagImage; | ||||||
| 
 | 
 | ||||||
|  |   bool trash; | ||||||
|  | 
 | ||||||
|   @override |   @override | ||||||
|   bool operator ==(Object other) => identical(this, other) || other is ServerFeaturesDto && |   bool operator ==(Object other) => identical(this, other) || other is ServerFeaturesDto && | ||||||
|      other.clipEncode == clipEncode && |      other.clipEncode == clipEncode && | ||||||
| @@ -60,7 +63,8 @@ class ServerFeaturesDto { | |||||||
|      other.reverseGeocoding == reverseGeocoding && |      other.reverseGeocoding == reverseGeocoding && | ||||||
|      other.search == search && |      other.search == search && | ||||||
|      other.sidecar == sidecar && |      other.sidecar == sidecar && | ||||||
|      other.tagImage == tagImage; |      other.tagImage == tagImage && | ||||||
|  |      other.trash == trash; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   int get hashCode => |   int get hashCode => | ||||||
| @@ -75,10 +79,11 @@ class ServerFeaturesDto { | |||||||
|     (reverseGeocoding.hashCode) + |     (reverseGeocoding.hashCode) + | ||||||
|     (search.hashCode) + |     (search.hashCode) + | ||||||
|     (sidecar.hashCode) + |     (sidecar.hashCode) + | ||||||
|     (tagImage.hashCode); |     (tagImage.hashCode) + | ||||||
|  |     (trash.hashCode); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'ServerFeaturesDto[clipEncode=$clipEncode, configFile=$configFile, facialRecognition=$facialRecognition, map=$map, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, search=$search, sidecar=$sidecar, tagImage=$tagImage]'; |   String toString() => 'ServerFeaturesDto[clipEncode=$clipEncode, configFile=$configFile, facialRecognition=$facialRecognition, map=$map, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, search=$search, sidecar=$sidecar, tagImage=$tagImage, trash=$trash]'; | ||||||
| 
 | 
 | ||||||
|   Map<String, dynamic> toJson() { |   Map<String, dynamic> toJson() { | ||||||
|     final json = <String, dynamic>{}; |     final json = <String, dynamic>{}; | ||||||
| @@ -93,6 +98,7 @@ class ServerFeaturesDto { | |||||||
|       json[r'search'] = this.search; |       json[r'search'] = this.search; | ||||||
|       json[r'sidecar'] = this.sidecar; |       json[r'sidecar'] = this.sidecar; | ||||||
|       json[r'tagImage'] = this.tagImage; |       json[r'tagImage'] = this.tagImage; | ||||||
|  |       json[r'trash'] = this.trash; | ||||||
|     return json; |     return json; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @@ -115,6 +121,7 @@ class ServerFeaturesDto { | |||||||
|         search: mapValueOfType<bool>(json, r'search')!, |         search: mapValueOfType<bool>(json, r'search')!, | ||||||
|         sidecar: mapValueOfType<bool>(json, r'sidecar')!, |         sidecar: mapValueOfType<bool>(json, r'sidecar')!, | ||||||
|         tagImage: mapValueOfType<bool>(json, r'tagImage')!, |         tagImage: mapValueOfType<bool>(json, r'tagImage')!, | ||||||
|  |         trash: mapValueOfType<bool>(json, r'trash')!, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
| @@ -173,6 +180,7 @@ class ServerFeaturesDto { | |||||||
|     'search', |     'search', | ||||||
|     'sidecar', |     'sidecar', | ||||||
|     'tagImage', |     'tagImage', | ||||||
|  |     'trash', | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								mobile/openapi/lib/model/system_config_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								mobile/openapi/lib/model/system_config_dto.dart
									
									
									
										generated
									
									
									
								
							| @@ -22,6 +22,7 @@ class SystemConfigDto { | |||||||
|     required this.reverseGeocoding, |     required this.reverseGeocoding, | ||||||
|     required this.storageTemplate, |     required this.storageTemplate, | ||||||
|     required this.thumbnail, |     required this.thumbnail, | ||||||
|  |     required this.trash, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   SystemConfigFFmpegDto ffmpeg; |   SystemConfigFFmpegDto ffmpeg; | ||||||
| @@ -42,6 +43,8 @@ class SystemConfigDto { | |||||||
| 
 | 
 | ||||||
|   SystemConfigThumbnailDto thumbnail; |   SystemConfigThumbnailDto thumbnail; | ||||||
| 
 | 
 | ||||||
|  |   SystemConfigTrashDto trash; | ||||||
|  | 
 | ||||||
|   @override |   @override | ||||||
|   bool operator ==(Object other) => identical(this, other) || other is SystemConfigDto && |   bool operator ==(Object other) => identical(this, other) || other is SystemConfigDto && | ||||||
|      other.ffmpeg == ffmpeg && |      other.ffmpeg == ffmpeg && | ||||||
| @@ -52,7 +55,8 @@ class SystemConfigDto { | |||||||
|      other.passwordLogin == passwordLogin && |      other.passwordLogin == passwordLogin && | ||||||
|      other.reverseGeocoding == reverseGeocoding && |      other.reverseGeocoding == reverseGeocoding && | ||||||
|      other.storageTemplate == storageTemplate && |      other.storageTemplate == storageTemplate && | ||||||
|      other.thumbnail == thumbnail; |      other.thumbnail == thumbnail && | ||||||
|  |      other.trash == trash; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   int get hashCode => |   int get hashCode => | ||||||
| @@ -65,10 +69,11 @@ class SystemConfigDto { | |||||||
|     (passwordLogin.hashCode) + |     (passwordLogin.hashCode) + | ||||||
|     (reverseGeocoding.hashCode) + |     (reverseGeocoding.hashCode) + | ||||||
|     (storageTemplate.hashCode) + |     (storageTemplate.hashCode) + | ||||||
|     (thumbnail.hashCode); |     (thumbnail.hashCode) + | ||||||
|  |     (trash.hashCode); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, machineLearning=$machineLearning, map=$map, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, storageTemplate=$storageTemplate, thumbnail=$thumbnail]'; |   String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, machineLearning=$machineLearning, map=$map, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, storageTemplate=$storageTemplate, thumbnail=$thumbnail, trash=$trash]'; | ||||||
| 
 | 
 | ||||||
|   Map<String, dynamic> toJson() { |   Map<String, dynamic> toJson() { | ||||||
|     final json = <String, dynamic>{}; |     final json = <String, dynamic>{}; | ||||||
| @@ -81,6 +86,7 @@ class SystemConfigDto { | |||||||
|       json[r'reverseGeocoding'] = this.reverseGeocoding; |       json[r'reverseGeocoding'] = this.reverseGeocoding; | ||||||
|       json[r'storageTemplate'] = this.storageTemplate; |       json[r'storageTemplate'] = this.storageTemplate; | ||||||
|       json[r'thumbnail'] = this.thumbnail; |       json[r'thumbnail'] = this.thumbnail; | ||||||
|  |       json[r'trash'] = this.trash; | ||||||
|     return json; |     return json; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @@ -101,6 +107,7 @@ class SystemConfigDto { | |||||||
|         reverseGeocoding: SystemConfigReverseGeocodingDto.fromJson(json[r'reverseGeocoding'])!, |         reverseGeocoding: SystemConfigReverseGeocodingDto.fromJson(json[r'reverseGeocoding'])!, | ||||||
|         storageTemplate: SystemConfigStorageTemplateDto.fromJson(json[r'storageTemplate'])!, |         storageTemplate: SystemConfigStorageTemplateDto.fromJson(json[r'storageTemplate'])!, | ||||||
|         thumbnail: SystemConfigThumbnailDto.fromJson(json[r'thumbnail'])!, |         thumbnail: SystemConfigThumbnailDto.fromJson(json[r'thumbnail'])!, | ||||||
|  |         trash: SystemConfigTrashDto.fromJson(json[r'trash'])!, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
| @@ -157,6 +164,7 @@ class SystemConfigDto { | |||||||
|     'reverseGeocoding', |     'reverseGeocoding', | ||||||
|     'storageTemplate', |     'storageTemplate', | ||||||
|     'thumbnail', |     'thumbnail', | ||||||
|  |     'trash', | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|   | |||||||
| @@ -10,58 +10,58 @@ | |||||||
| 
 | 
 | ||||||
| part of openapi.api; | part of openapi.api; | ||||||
| 
 | 
 | ||||||
| class DeleteAssetResponseDto { | class SystemConfigTrashDto { | ||||||
|   /// Returns a new [DeleteAssetResponseDto] instance. |   /// Returns a new [SystemConfigTrashDto] instance. | ||||||
|   DeleteAssetResponseDto({ |   SystemConfigTrashDto({ | ||||||
|     required this.id, |     required this.days, | ||||||
|     required this.status, |     required this.enabled, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   String id; |   int days; | ||||||
| 
 | 
 | ||||||
|   DeleteAssetStatus status; |   bool enabled; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   bool operator ==(Object other) => identical(this, other) || other is DeleteAssetResponseDto && |   bool operator ==(Object other) => identical(this, other) || other is SystemConfigTrashDto && | ||||||
|      other.id == id && |      other.days == days && | ||||||
|      other.status == status; |      other.enabled == enabled; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   int get hashCode => |   int get hashCode => | ||||||
|     // ignore: unnecessary_parenthesis |     // ignore: unnecessary_parenthesis | ||||||
|     (id.hashCode) + |     (days.hashCode) + | ||||||
|     (status.hashCode); |     (enabled.hashCode); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'DeleteAssetResponseDto[id=$id, status=$status]'; |   String toString() => 'SystemConfigTrashDto[days=$days, enabled=$enabled]'; | ||||||
| 
 | 
 | ||||||
|   Map<String, dynamic> toJson() { |   Map<String, dynamic> toJson() { | ||||||
|     final json = <String, dynamic>{}; |     final json = <String, dynamic>{}; | ||||||
|       json[r'id'] = this.id; |       json[r'days'] = this.days; | ||||||
|       json[r'status'] = this.status; |       json[r'enabled'] = this.enabled; | ||||||
|     return json; |     return json; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Returns a new [DeleteAssetResponseDto] instance and imports its values from |   /// Returns a new [SystemConfigTrashDto] instance and imports its values from | ||||||
|   /// [value] if it's a [Map], null otherwise. |   /// [value] if it's a [Map], null otherwise. | ||||||
|   // ignore: prefer_constructors_over_static_methods |   // ignore: prefer_constructors_over_static_methods | ||||||
|   static DeleteAssetResponseDto? fromJson(dynamic value) { |   static SystemConfigTrashDto? fromJson(dynamic value) { | ||||||
|     if (value is Map) { |     if (value is Map) { | ||||||
|       final json = value.cast<String, dynamic>(); |       final json = value.cast<String, dynamic>(); | ||||||
| 
 | 
 | ||||||
|       return DeleteAssetResponseDto( |       return SystemConfigTrashDto( | ||||||
|         id: mapValueOfType<String>(json, r'id')!, |         days: mapValueOfType<int>(json, r'days')!, | ||||||
|         status: DeleteAssetStatus.fromJson(json[r'status'])!, |         enabled: mapValueOfType<bool>(json, r'enabled')!, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static List<DeleteAssetResponseDto> listFromJson(dynamic json, {bool growable = false,}) { |   static List<SystemConfigTrashDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||||
|     final result = <DeleteAssetResponseDto>[]; |     final result = <SystemConfigTrashDto>[]; | ||||||
|     if (json is List && json.isNotEmpty) { |     if (json is List && json.isNotEmpty) { | ||||||
|       for (final row in json) { |       for (final row in json) { | ||||||
|         final value = DeleteAssetResponseDto.fromJson(row); |         final value = SystemConfigTrashDto.fromJson(row); | ||||||
|         if (value != null) { |         if (value != null) { | ||||||
|           result.add(value); |           result.add(value); | ||||||
|         } |         } | ||||||
| @@ -70,12 +70,12 @@ class DeleteAssetResponseDto { | |||||||
|     return result.toList(growable: growable); |     return result.toList(growable: growable); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static Map<String, DeleteAssetResponseDto> mapFromJson(dynamic json) { |   static Map<String, SystemConfigTrashDto> mapFromJson(dynamic json) { | ||||||
|     final map = <String, DeleteAssetResponseDto>{}; |     final map = <String, SystemConfigTrashDto>{}; | ||||||
|     if (json is Map && json.isNotEmpty) { |     if (json is Map && json.isNotEmpty) { | ||||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments |       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||||
|       for (final entry in json.entries) { |       for (final entry in json.entries) { | ||||||
|         final value = DeleteAssetResponseDto.fromJson(entry.value); |         final value = SystemConfigTrashDto.fromJson(entry.value); | ||||||
|         if (value != null) { |         if (value != null) { | ||||||
|           map[entry.key] = value; |           map[entry.key] = value; | ||||||
|         } |         } | ||||||
| @@ -84,14 +84,14 @@ class DeleteAssetResponseDto { | |||||||
|     return map; |     return map; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // maps a json object with a list of DeleteAssetResponseDto-objects as value to a dart map |   // maps a json object with a list of SystemConfigTrashDto-objects as value to a dart map | ||||||
|   static Map<String, List<DeleteAssetResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { |   static Map<String, List<SystemConfigTrashDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||||
|     final map = <String, List<DeleteAssetResponseDto>>{}; |     final map = <String, List<SystemConfigTrashDto>>{}; | ||||||
|     if (json is Map && json.isNotEmpty) { |     if (json is Map && json.isNotEmpty) { | ||||||
|       // ignore: parameter_assignments |       // ignore: parameter_assignments | ||||||
|       json = json.cast<String, dynamic>(); |       json = json.cast<String, dynamic>(); | ||||||
|       for (final entry in json.entries) { |       for (final entry in json.entries) { | ||||||
|         map[entry.key] = DeleteAssetResponseDto.listFromJson(entry.value, growable: growable,); |         map[entry.key] = SystemConfigTrashDto.listFromJson(entry.value, growable: growable,); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     return map; |     return map; | ||||||
| @@ -99,8 +99,8 @@ class DeleteAssetResponseDto { | |||||||
| 
 | 
 | ||||||
|   /// The list of required keys that must be present in a JSON. |   /// The list of required keys that must be present in a JSON. | ||||||
|   static const requiredKeys = <String>{ |   static const requiredKeys = <String>{ | ||||||
|     'id', |     'days', | ||||||
|     'status', |     'enabled', | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
							
								
								
									
										25
									
								
								mobile/openapi/test/asset_api_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										25
									
								
								mobile/openapi/test/asset_api_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -38,8 +38,8 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     //Future<List<DeleteAssetResponseDto>> deleteAsset(DeleteAssetDto deleteAssetDto) async |     //Future deleteAssets(AssetBulkDeleteDto assetBulkDeleteDto) async | ||||||
|     test('test deleteAsset', () async { |     test('test deleteAssets', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
| @@ -53,6 +53,11 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     //Future emptyTrash() async | ||||||
|  |     test('test emptyTrash', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     // Get all AssetEntity belong to the user |     // Get all AssetEntity belong to the user | ||||||
|     // |     // | ||||||
|     //Future<List<AssetResponseDto>> getAllAssets({ String userId, bool isFavorite, bool isArchived, num skip, DateTime updatedAfter, String ifNoneMatch }) async |     //Future<List<AssetResponseDto>> getAllAssets({ String userId, bool isFavorite, bool isArchived, num skip, DateTime updatedAfter, String ifNoneMatch }) async | ||||||
| @@ -72,7 +77,7 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     //Future<AssetStatsResponseDto> getAssetStats({ bool isArchived, bool isFavorite }) async |     //Future<AssetStatsResponseDto> getAssetStats({ bool isArchived, bool isFavorite, bool isTrashed }) async | ||||||
|     test('test getAssetStats', () async { |     test('test getAssetStats', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| @@ -82,7 +87,7 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     //Future<List<AssetResponseDto>> getByTimeBucket(TimeBucketSize size, String timeBucket, { String userId, String albumId, String personId, bool isArchived, bool isFavorite, String key }) async |     //Future<List<AssetResponseDto>> getByTimeBucket(TimeBucketSize size, String timeBucket, { String userId, String albumId, String personId, bool isArchived, bool isFavorite, bool isTrashed, String key }) async | ||||||
|     test('test getByTimeBucket', () async { |     test('test getByTimeBucket', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| @@ -117,7 +122,7 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     //Future<List<TimeBucketResponseDto>> getTimeBuckets(TimeBucketSize size, { String userId, String albumId, String personId, bool isArchived, bool isFavorite, String key }) async |     //Future<List<TimeBucketResponseDto>> getTimeBuckets(TimeBucketSize size, { String userId, String albumId, String personId, bool isArchived, bool isFavorite, bool isTrashed, String key }) async | ||||||
|     test('test getTimeBuckets', () async { |     test('test getTimeBuckets', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| @@ -134,6 +139,16 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     //Future restoreAssets(BulkIdsDto bulkIdsDto) async | ||||||
|  |     test('test restoreAssets', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     //Future restoreTrash() async | ||||||
|  |     test('test restoreTrash', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     //Future runAssetJobs(AssetJobsDto assetJobsDto) async |     //Future runAssetJobs(AssetJobsDto assetJobsDto) async | ||||||
|     test('test runAssetJobs', () async { |     test('test runAssetJobs', () async { | ||||||
|       // TODO |       // TODO | ||||||
|   | |||||||
| @@ -11,11 +11,16 @@ | |||||||
| import 'package:openapi/api.dart'; | import 'package:openapi/api.dart'; | ||||||
| import 'package:test/test.dart'; | import 'package:test/test.dart'; | ||||||
| 
 | 
 | ||||||
| // tests for DeleteAssetDto | // tests for AssetBulkDeleteDto | ||||||
| void main() { | void main() { | ||||||
|   // final instance = DeleteAssetDto(); |   // final instance = AssetBulkDeleteDto(); | ||||||
|  | 
 | ||||||
|  |   group('test AssetBulkDeleteDto', () { | ||||||
|  |     // bool force | ||||||
|  |     test('to test the property `force`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
| 
 | 
 | ||||||
|   group('test DeleteAssetDto', () { |  | ||||||
|     // List<String> ids (default value: const []) |     // List<String> ids (default value: const []) | ||||||
|     test('to test the property `ids`', () async { |     test('to test the property `ids`', () async { | ||||||
|       // TODO |       // TODO | ||||||
							
								
								
									
										5
									
								
								mobile/openapi/test/asset_response_dto_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								mobile/openapi/test/asset_response_dto_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -82,6 +82,11 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     // bool isTrashed | ||||||
|  |     test('to test the property `isTrashed`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     // String libraryId |     // String libraryId | ||||||
|     test('to test the property `libraryId`', () async { |     test('to test the property `libraryId`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								mobile/openapi/test/delete_asset_status_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								mobile/openapi/test/delete_asset_status_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -1,21 +0,0 @@ | |||||||
| // |  | ||||||
| // AUTO-GENERATED FILE, DO NOT MODIFY! |  | ||||||
| // |  | ||||||
| // @dart=2.12 |  | ||||||
| 
 |  | ||||||
| // ignore_for_file: unused_element, unused_import |  | ||||||
| // ignore_for_file: always_put_required_named_parameters_first |  | ||||||
| // ignore_for_file: constant_identifier_names |  | ||||||
| // ignore_for_file: lines_longer_than_80_chars |  | ||||||
| 
 |  | ||||||
| import 'package:openapi/api.dart'; |  | ||||||
| import 'package:test/test.dart'; |  | ||||||
| 
 |  | ||||||
| // tests for DeleteAssetStatus |  | ||||||
| void main() { |  | ||||||
| 
 |  | ||||||
|   group('test DeleteAssetStatus', () { |  | ||||||
| 
 |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
							
								
								
									
										5
									
								
								mobile/openapi/test/server_config_dto_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								mobile/openapi/test/server_config_dto_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -31,6 +31,11 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     // int trashDays | ||||||
|  |     test('to test the property `trashDays`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   | |||||||
| @@ -71,6 +71,11 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     // bool trash | ||||||
|  |     test('to test the property `trash`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								mobile/openapi/test/system_config_dto_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								mobile/openapi/test/system_config_dto_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -61,6 +61,11 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     // SystemConfigTrashDto trash | ||||||
|  |     test('to test the property `trash`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   | |||||||
| @@ -11,18 +11,18 @@ | |||||||
| import 'package:openapi/api.dart'; | import 'package:openapi/api.dart'; | ||||||
| import 'package:test/test.dart'; | import 'package:test/test.dart'; | ||||||
| 
 | 
 | ||||||
| // tests for DeleteAssetResponseDto | // tests for SystemConfigTrashDto | ||||||
| void main() { | void main() { | ||||||
|   // final instance = DeleteAssetResponseDto(); |   // final instance = SystemConfigTrashDto(); | ||||||
| 
 | 
 | ||||||
|   group('test DeleteAssetResponseDto', () { |   group('test SystemConfigTrashDto', () { | ||||||
|     // String id |     // int days | ||||||
|     test('to test the property `id`', () async { |     test('to test the property `days`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // DeleteAssetStatus status |     // bool enabled | ||||||
|     test('to test the property `status`', () async { |     test('to test the property `enabled`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
| @@ -24,6 +24,7 @@ void main() { | |||||||
|         fileName: '', |         fileName: '', | ||||||
|         isFavorite: false, |         isFavorite: false, | ||||||
|         isArchived: false, |         isArchived: false, | ||||||
|  |         isTrashed: false, | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -34,6 +34,7 @@ void main() { | |||||||
|       fileName: localId ?? remoteId ?? "", |       fileName: localId ?? remoteId ?? "", | ||||||
|       isFavorite: false, |       isFavorite: false, | ||||||
|       isArchived: false, |       isArchived: false, | ||||||
|  |       isTrashed: false, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -681,30 +681,20 @@ | |||||||
|     }, |     }, | ||||||
|     "/asset": { |     "/asset": { | ||||||
|       "delete": { |       "delete": { | ||||||
|         "operationId": "deleteAsset", |         "operationId": "deleteAssets", | ||||||
|         "parameters": [], |         "parameters": [], | ||||||
|         "requestBody": { |         "requestBody": { | ||||||
|           "content": { |           "content": { | ||||||
|             "application/json": { |             "application/json": { | ||||||
|               "schema": { |               "schema": { | ||||||
|                 "$ref": "#/components/schemas/DeleteAssetDto" |                 "$ref": "#/components/schemas/AssetBulkDeleteDto" | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           "required": true |           "required": true | ||||||
|         }, |         }, | ||||||
|         "responses": { |         "responses": { | ||||||
|           "200": { |           "204": { | ||||||
|             "content": { |  | ||||||
|               "application/json": { |  | ||||||
|                 "schema": { |  | ||||||
|                   "items": { |  | ||||||
|                     "$ref": "#/components/schemas/DeleteAssetResponseDto" |  | ||||||
|                   }, |  | ||||||
|                   "type": "array" |  | ||||||
|                 } |  | ||||||
|               } |  | ||||||
|             }, |  | ||||||
|             "description": "" |             "description": "" | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
| @@ -1568,6 +1558,41 @@ | |||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "/asset/restore": { | ||||||
|  |       "post": { | ||||||
|  |         "operationId": "restoreAssets", | ||||||
|  |         "parameters": [], | ||||||
|  |         "requestBody": { | ||||||
|  |           "content": { | ||||||
|  |             "application/json": { | ||||||
|  |               "schema": { | ||||||
|  |                 "$ref": "#/components/schemas/BulkIdsDto" | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           "required": true | ||||||
|  |         }, | ||||||
|  |         "responses": { | ||||||
|  |           "204": { | ||||||
|  |             "description": "" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "security": [ | ||||||
|  |           { | ||||||
|  |             "bearer": [] | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "cookie": [] | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "api_key": [] | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|  |         "tags": [ | ||||||
|  |           "Asset" | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "/asset/search": { |     "/asset/search": { | ||||||
|       "post": { |       "post": { | ||||||
|         "operationId": "searchAsset", |         "operationId": "searchAsset", | ||||||
| @@ -1667,6 +1692,14 @@ | |||||||
|             "schema": { |             "schema": { | ||||||
|               "type": "boolean" |               "type": "boolean" | ||||||
|             } |             } | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "name": "isTrashed", | ||||||
|  |             "required": false, | ||||||
|  |             "in": "query", | ||||||
|  |             "schema": { | ||||||
|  |               "type": "boolean" | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|         ], |         ], | ||||||
|         "responses": { |         "responses": { | ||||||
| @@ -1817,6 +1850,14 @@ | |||||||
|               "type": "boolean" |               "type": "boolean" | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|  |           { | ||||||
|  |             "name": "isTrashed", | ||||||
|  |             "required": false, | ||||||
|  |             "in": "query", | ||||||
|  |             "schema": { | ||||||
|  |               "type": "boolean" | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|           { |           { | ||||||
|             "name": "timeBucket", |             "name": "timeBucket", | ||||||
|             "required": true, |             "required": true, | ||||||
| @@ -1929,6 +1970,14 @@ | |||||||
|               "type": "boolean" |               "type": "boolean" | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|  |           { | ||||||
|  |             "name": "isTrashed", | ||||||
|  |             "required": false, | ||||||
|  |             "in": "query", | ||||||
|  |             "schema": { | ||||||
|  |               "type": "boolean" | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|           { |           { | ||||||
|             "name": "key", |             "name": "key", | ||||||
|             "required": false, |             "required": false, | ||||||
| @@ -1978,6 +2027,56 @@ | |||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "/asset/trash/empty": { | ||||||
|  |       "post": { | ||||||
|  |         "operationId": "emptyTrash", | ||||||
|  |         "parameters": [], | ||||||
|  |         "responses": { | ||||||
|  |           "204": { | ||||||
|  |             "description": "" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "security": [ | ||||||
|  |           { | ||||||
|  |             "bearer": [] | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "cookie": [] | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "api_key": [] | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|  |         "tags": [ | ||||||
|  |           "Asset" | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "/asset/trash/restore": { | ||||||
|  |       "post": { | ||||||
|  |         "operationId": "restoreTrash", | ||||||
|  |         "parameters": [], | ||||||
|  |         "responses": { | ||||||
|  |           "204": { | ||||||
|  |             "description": "" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "security": [ | ||||||
|  |           { | ||||||
|  |             "bearer": [] | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "cookie": [] | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "api_key": [] | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|  |         "tags": [ | ||||||
|  |           "Asset" | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "/asset/upload": { |     "/asset/upload": { | ||||||
|       "post": { |       "post": { | ||||||
|         "operationId": "uploadFile", |         "operationId": "uploadFile", | ||||||
| @@ -5398,6 +5497,24 @@ | |||||||
|         ], |         ], | ||||||
|         "type": "object" |         "type": "object" | ||||||
|       }, |       }, | ||||||
|  |       "AssetBulkDeleteDto": { | ||||||
|  |         "properties": { | ||||||
|  |           "force": { | ||||||
|  |             "type": "boolean" | ||||||
|  |           }, | ||||||
|  |           "ids": { | ||||||
|  |             "items": { | ||||||
|  |               "format": "uuid", | ||||||
|  |               "type": "string" | ||||||
|  |             }, | ||||||
|  |             "type": "array" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "required": [ | ||||||
|  |           "ids" | ||||||
|  |         ], | ||||||
|  |         "type": "object" | ||||||
|  |       }, | ||||||
|       "AssetBulkUpdateDto": { |       "AssetBulkUpdateDto": { | ||||||
|         "properties": { |         "properties": { | ||||||
|           "ids": { |           "ids": { | ||||||
| @@ -5616,6 +5733,9 @@ | |||||||
|           "isReadOnly": { |           "isReadOnly": { | ||||||
|             "type": "boolean" |             "type": "boolean" | ||||||
|           }, |           }, | ||||||
|  |           "isTrashed": { | ||||||
|  |             "type": "boolean" | ||||||
|  |           }, | ||||||
|           "libraryId": { |           "libraryId": { | ||||||
|             "type": "string" |             "type": "string" | ||||||
|           }, |           }, | ||||||
| @@ -5686,6 +5806,7 @@ | |||||||
|           "updatedAt", |           "updatedAt", | ||||||
|           "isFavorite", |           "isFavorite", | ||||||
|           "isArchived", |           "isArchived", | ||||||
|  |           "isTrashed", | ||||||
|           "localDateTime", |           "localDateTime", | ||||||
|           "isOffline", |           "isOffline", | ||||||
|           "isExternal", |           "isExternal", | ||||||
| @@ -6222,48 +6343,6 @@ | |||||||
|         ], |         ], | ||||||
|         "type": "object" |         "type": "object" | ||||||
|       }, |       }, | ||||||
|       "DeleteAssetDto": { |  | ||||||
|         "properties": { |  | ||||||
|           "ids": { |  | ||||||
|             "example": [ |  | ||||||
|               "bf973405-3f2a-48d2-a687-2ed4167164be", |  | ||||||
|               "dd41870b-5d00-46d2-924e-1d8489a0aa0f", |  | ||||||
|               "fad77c3f-deef-4e7e-9608-14c1aa4e559a" |  | ||||||
|             ], |  | ||||||
|             "items": { |  | ||||||
|               "type": "string" |  | ||||||
|             }, |  | ||||||
|             "title": "Array of asset IDs to delete", |  | ||||||
|             "type": "array" |  | ||||||
|           } |  | ||||||
|         }, |  | ||||||
|         "required": [ |  | ||||||
|           "ids" |  | ||||||
|         ], |  | ||||||
|         "type": "object" |  | ||||||
|       }, |  | ||||||
|       "DeleteAssetResponseDto": { |  | ||||||
|         "properties": { |  | ||||||
|           "id": { |  | ||||||
|             "type": "string" |  | ||||||
|           }, |  | ||||||
|           "status": { |  | ||||||
|             "$ref": "#/components/schemas/DeleteAssetStatus" |  | ||||||
|           } |  | ||||||
|         }, |  | ||||||
|         "required": [ |  | ||||||
|           "status", |  | ||||||
|           "id" |  | ||||||
|         ], |  | ||||||
|         "type": "object" |  | ||||||
|       }, |  | ||||||
|       "DeleteAssetStatus": { |  | ||||||
|         "enum": [ |  | ||||||
|           "SUCCESS", |  | ||||||
|           "FAILED" |  | ||||||
|         ], |  | ||||||
|         "type": "string" |  | ||||||
|       }, |  | ||||||
|       "DownloadArchiveInfo": { |       "DownloadArchiveInfo": { | ||||||
|         "properties": { |         "properties": { | ||||||
|           "assetIds": { |           "assetIds": { | ||||||
| @@ -7225,9 +7304,13 @@ | |||||||
|           }, |           }, | ||||||
|           "oauthButtonText": { |           "oauthButtonText": { | ||||||
|             "type": "string" |             "type": "string" | ||||||
|  |           }, | ||||||
|  |           "trashDays": { | ||||||
|  |             "type": "integer" | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         "required": [ |         "required": [ | ||||||
|  |           "trashDays", | ||||||
|           "oauthButtonText", |           "oauthButtonText", | ||||||
|           "loginPageMessage", |           "loginPageMessage", | ||||||
|           "mapTileUrl" |           "mapTileUrl" | ||||||
| @@ -7268,6 +7351,9 @@ | |||||||
|           }, |           }, | ||||||
|           "tagImage": { |           "tagImage": { | ||||||
|             "type": "boolean" |             "type": "boolean" | ||||||
|  |           }, | ||||||
|  |           "trash": { | ||||||
|  |             "type": "boolean" | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         "required": [ |         "required": [ | ||||||
| @@ -7275,6 +7361,7 @@ | |||||||
|           "configFile", |           "configFile", | ||||||
|           "facialRecognition", |           "facialRecognition", | ||||||
|           "map", |           "map", | ||||||
|  |           "trash", | ||||||
|           "reverseGeocoding", |           "reverseGeocoding", | ||||||
|           "oauth", |           "oauth", | ||||||
|           "oauthAutoLaunch", |           "oauthAutoLaunch", | ||||||
| @@ -7630,6 +7717,9 @@ | |||||||
|           }, |           }, | ||||||
|           "thumbnail": { |           "thumbnail": { | ||||||
|             "$ref": "#/components/schemas/SystemConfigThumbnailDto" |             "$ref": "#/components/schemas/SystemConfigThumbnailDto" | ||||||
|  |           }, | ||||||
|  |           "trash": { | ||||||
|  |             "$ref": "#/components/schemas/SystemConfigTrashDto" | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         "required": [ |         "required": [ | ||||||
| @@ -7641,7 +7731,8 @@ | |||||||
|           "reverseGeocoding", |           "reverseGeocoding", | ||||||
|           "storageTemplate", |           "storageTemplate", | ||||||
|           "job", |           "job", | ||||||
|           "thumbnail" |           "thumbnail", | ||||||
|  |           "trash" | ||||||
|         ], |         ], | ||||||
|         "type": "object" |         "type": "object" | ||||||
|       }, |       }, | ||||||
| @@ -7991,6 +8082,21 @@ | |||||||
|         ], |         ], | ||||||
|         "type": "object" |         "type": "object" | ||||||
|       }, |       }, | ||||||
|  |       "SystemConfigTrashDto": { | ||||||
|  |         "properties": { | ||||||
|  |           "days": { | ||||||
|  |             "type": "integer" | ||||||
|  |           }, | ||||||
|  |           "enabled": { | ||||||
|  |             "type": "boolean" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "required": [ | ||||||
|  |           "days", | ||||||
|  |           "enabled" | ||||||
|  |         ], | ||||||
|  |         "type": "object" | ||||||
|  |       }, | ||||||
|       "TagResponseDto": { |       "TagResponseDto": { | ||||||
|         "properties": { |         "properties": { | ||||||
|           "id": { |           "id": { | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ export enum Permission { | |||||||
|   ASSET_READ = 'asset.read', |   ASSET_READ = 'asset.read', | ||||||
|   ASSET_UPDATE = 'asset.update', |   ASSET_UPDATE = 'asset.update', | ||||||
|   ASSET_DELETE = 'asset.delete', |   ASSET_DELETE = 'asset.delete', | ||||||
|  |   ASSET_RESTORE = 'asset.restore', | ||||||
|   ASSET_SHARE = 'asset.share', |   ASSET_SHARE = 'asset.share', | ||||||
|   ASSET_VIEW = 'asset.view', |   ASSET_VIEW = 'asset.view', | ||||||
|   ASSET_DOWNLOAD = 'asset.download', |   ASSET_DOWNLOAD = 'asset.download', | ||||||
| @@ -128,6 +129,9 @@ export class AccessCore { | |||||||
|       case Permission.ASSET_DELETE: |       case Permission.ASSET_DELETE: | ||||||
|         return this.repository.asset.hasOwnerAccess(authUser.id, id); |         return this.repository.asset.hasOwnerAccess(authUser.id, id); | ||||||
|  |  | ||||||
|  |       case Permission.ASSET_RESTORE: | ||||||
|  |         return this.repository.asset.hasOwnerAccess(authUser.id, id); | ||||||
|  |  | ||||||
|       case Permission.ASSET_SHARE: |       case Permission.ASSET_SHARE: | ||||||
|         return ( |         return ( | ||||||
|           (await this.repository.asset.hasOwnerAccess(authUser.id, id)) || |           (await this.repository.asset.hasOwnerAccess(authUser.id, id)) || | ||||||
|   | |||||||
| @@ -6,10 +6,12 @@ export type AssetStats = Record<AssetType, number>; | |||||||
| export interface AssetStatsOptions { | export interface AssetStatsOptions { | ||||||
|   isFavorite?: boolean; |   isFavorite?: boolean; | ||||||
|   isArchived?: boolean; |   isArchived?: boolean; | ||||||
|  |   isTrashed?: boolean; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface AssetSearchOptions { | export interface AssetSearchOptions { | ||||||
|   isVisible?: boolean; |   isVisible?: boolean; | ||||||
|  |   trashedBefore?: Date; | ||||||
|   type?: AssetType; |   type?: AssetType; | ||||||
|   order?: 'ASC' | 'DESC'; |   order?: 'ASC' | 'DESC'; | ||||||
| } | } | ||||||
| @@ -58,6 +60,7 @@ export interface TimeBucketOptions { | |||||||
|   size: TimeBucketSize; |   size: TimeBucketSize; | ||||||
|   isArchived?: boolean; |   isArchived?: boolean; | ||||||
|   isFavorite?: boolean; |   isFavorite?: boolean; | ||||||
|  |   isTrashed?: boolean; | ||||||
|   albumId?: string; |   albumId?: string; | ||||||
|   personId?: string; |   personId?: string; | ||||||
|   userId?: string; |   userId?: string; | ||||||
| @@ -98,7 +101,8 @@ export interface IAssetRepository { | |||||||
|   getByDayOfYear(ownerId: string, monthDay: MonthDay): Promise<AssetEntity[]>; |   getByDayOfYear(ownerId: string, monthDay: MonthDay): Promise<AssetEntity[]>; | ||||||
|   getByChecksum(userId: string, checksum: Buffer): Promise<AssetEntity | null>; |   getByChecksum(userId: string, checksum: Buffer): Promise<AssetEntity | null>; | ||||||
|   getByAlbumId(pagination: PaginationOptions, albumId: string): Paginated<AssetEntity>; |   getByAlbumId(pagination: PaginationOptions, albumId: string): Paginated<AssetEntity>; | ||||||
|   getByUserId(pagination: PaginationOptions, userId: string): Paginated<AssetEntity>; |   getByUserId(pagination: PaginationOptions, userId: string, options?: AssetSearchOptions): Paginated<AssetEntity>; | ||||||
|  |   getById(id: string): Promise<AssetEntity | null>; | ||||||
|   getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated<AssetEntity>; |   getWithout(pagination: PaginationOptions, property: WithoutProperty): Paginated<AssetEntity>; | ||||||
|   getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated<AssetEntity>; |   getWith(pagination: PaginationOptions, property: WithProperty, libraryId?: string): Paginated<AssetEntity>; | ||||||
|   getRandom(userId: string, count: number): Promise<AssetEntity[]>; |   getRandom(userId: string, count: number): Promise<AssetEntity[]>; | ||||||
| @@ -110,12 +114,13 @@ export interface IAssetRepository { | |||||||
|   getAll(pagination: PaginationOptions, options?: AssetSearchOptions): Paginated<AssetEntity>; |   getAll(pagination: PaginationOptions, options?: AssetSearchOptions): Paginated<AssetEntity>; | ||||||
|   updateAll(ids: string[], options: Partial<AssetEntity>): Promise<void>; |   updateAll(ids: string[], options: Partial<AssetEntity>): Promise<void>; | ||||||
|   save(asset: Pick<AssetEntity, 'id'> & Partial<AssetEntity>): Promise<AssetEntity>; |   save(asset: Pick<AssetEntity, 'id'> & Partial<AssetEntity>): Promise<AssetEntity>; | ||||||
|  |   remove(asset: AssetEntity): Promise<void>; | ||||||
|  |   softDeleteAll(ids: string[]): Promise<void>; | ||||||
|  |   restoreAll(ids: string[]): Promise<void>; | ||||||
|   findLivePhotoMatch(options: LivePhotoSearchOptions): Promise<AssetEntity | null>; |   findLivePhotoMatch(options: LivePhotoSearchOptions): Promise<AssetEntity | null>; | ||||||
|   getMapMarkers(ownerId: string, options?: MapMarkerSearchOptions): Promise<MapMarker[]>; |   getMapMarkers(ownerId: string, options?: MapMarkerSearchOptions): Promise<MapMarker[]>; | ||||||
|   getStatistics(ownerId: string, options: AssetStatsOptions): Promise<AssetStats>; |   getStatistics(ownerId: string, options: AssetStatsOptions): Promise<AssetStats>; | ||||||
|   getTimeBuckets(options: TimeBucketOptions): Promise<TimeBucketItem[]>; |   getTimeBuckets(options: TimeBucketOptions): Promise<TimeBucketItem[]>; | ||||||
|   getByTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]>; |   getByTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]>; | ||||||
|   remove(asset: AssetEntity): Promise<AssetEntity>; |  | ||||||
|   getById(assetId: string): Promise<AssetEntity>; |  | ||||||
|   upsertExif(exif: Partial<ExifEntity>): Promise<void>; |   upsertExif(exif: Partial<ExifEntity>): Promise<void>; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,20 +1,23 @@ | |||||||
| import { AssetType } from '@app/infra/entities'; | import { AssetEntity, AssetType } from '@app/infra/entities'; | ||||||
| import { BadRequestException, UnauthorizedException } from '@nestjs/common'; | import { BadRequestException, UnauthorizedException } from '@nestjs/common'; | ||||||
| import { | import { | ||||||
|   IAccessRepositoryMock, |   IAccessRepositoryMock, | ||||||
|   assetStub, |   assetStub, | ||||||
|   authStub, |   authStub, | ||||||
|  |   faceStub, | ||||||
|   newAccessRepositoryMock, |   newAccessRepositoryMock, | ||||||
|   newAssetRepositoryMock, |   newAssetRepositoryMock, | ||||||
|   newCryptoRepositoryMock, |   newCryptoRepositoryMock, | ||||||
|   newJobRepositoryMock, |   newJobRepositoryMock, | ||||||
|   newStorageRepositoryMock, |   newStorageRepositoryMock, | ||||||
|  |   newSystemConfigRepositoryMock, | ||||||
| } from '@test'; | } from '@test'; | ||||||
| import { when } from 'jest-when'; | import { when } from 'jest-when'; | ||||||
| import { Readable } from 'stream'; | import { Readable } from 'stream'; | ||||||
| import { ICryptoRepository } from '../crypto'; | import { ICryptoRepository } from '../crypto'; | ||||||
| import { IJobRepository, JobName } from '../job'; | import { IJobRepository, JobItem, JobName } from '../job'; | ||||||
| import { IStorageRepository } from '../storage'; | import { IStorageRepository } from '../storage'; | ||||||
|  | import { ISystemConfigRepository } from '../system-config'; | ||||||
| import { AssetStats, IAssetRepository, TimeBucketSize } from './asset.repository'; | import { AssetStats, IAssetRepository, TimeBucketSize } from './asset.repository'; | ||||||
| import { AssetService, UploadFieldName } from './asset.service'; | import { AssetService, UploadFieldName } from './asset.service'; | ||||||
| import { AssetJobName, AssetStatsResponseDto, DownloadResponseDto } from './dto'; | import { AssetJobName, AssetStatsResponseDto, DownloadResponseDto } from './dto'; | ||||||
| @@ -150,6 +153,7 @@ describe(AssetService.name, () => { | |||||||
|   let cryptoMock: jest.Mocked<ICryptoRepository>; |   let cryptoMock: jest.Mocked<ICryptoRepository>; | ||||||
|   let jobMock: jest.Mocked<IJobRepository>; |   let jobMock: jest.Mocked<IJobRepository>; | ||||||
|   let storageMock: jest.Mocked<IStorageRepository>; |   let storageMock: jest.Mocked<IStorageRepository>; | ||||||
|  |   let configMock: jest.Mocked<ISystemConfigRepository>; | ||||||
|  |  | ||||||
|   it('should work', () => { |   it('should work', () => { | ||||||
|     expect(sut).toBeDefined(); |     expect(sut).toBeDefined(); | ||||||
| @@ -161,7 +165,15 @@ describe(AssetService.name, () => { | |||||||
|     cryptoMock = newCryptoRepositoryMock(); |     cryptoMock = newCryptoRepositoryMock(); | ||||||
|     jobMock = newJobRepositoryMock(); |     jobMock = newJobRepositoryMock(); | ||||||
|     storageMock = newStorageRepositoryMock(); |     storageMock = newStorageRepositoryMock(); | ||||||
|     sut = new AssetService(accessMock, assetMock, cryptoMock, jobMock, storageMock); |     configMock = newSystemConfigRepositoryMock(); | ||||||
|  |     sut = new AssetService(accessMock, assetMock, cryptoMock, jobMock, configMock, storageMock); | ||||||
|  |  | ||||||
|  |     when(assetMock.getById) | ||||||
|  |       .calledWith(assetStub.livePhotoStillAsset.id) | ||||||
|  |       .mockResolvedValue(assetStub.livePhotoStillAsset as AssetEntity); | ||||||
|  |     when(assetMock.getById) | ||||||
|  |       .calledWith(assetStub.livePhotoMotionAsset.id) | ||||||
|  |       .mockResolvedValue(assetStub.livePhotoMotionAsset as AssetEntity); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   describe('canUpload', () => { |   describe('canUpload', () => { | ||||||
| @@ -476,7 +488,9 @@ describe(AssetService.name, () => { | |||||||
|         downloadResponse, |         downloadResponse, | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
|       expect(assetMock.getByUserId).toHaveBeenCalledWith({ take: 2500, skip: 0 }, authStub.admin.id); |       expect(assetMock.getByUserId).toHaveBeenCalledWith({ take: 2500, skip: 0 }, authStub.admin.id, { | ||||||
|  |         isVisible: true, | ||||||
|  |       }); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     it('should split archives by size', async () => { |     it('should split archives by size', async () => { | ||||||
| @@ -596,6 +610,203 @@ describe(AssetService.name, () => { | |||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|  |   describe('deleteAll', () => { | ||||||
|  |     it('should required asset delete access for all ids', async () => { | ||||||
|  |       accessMock.asset.hasOwnerAccess.mockResolvedValue(false); | ||||||
|  |       await expect( | ||||||
|  |         sut.deleteAll(authStub.user1, { | ||||||
|  |           ids: ['asset-1'], | ||||||
|  |         }), | ||||||
|  |       ).rejects.toBeInstanceOf(BadRequestException); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('should force delete a batch of assets', async () => { | ||||||
|  |       accessMock.asset.hasOwnerAccess.mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |       await sut.deleteAll(authStub.user1, { ids: ['asset1', 'asset2'], force: true }); | ||||||
|  |  | ||||||
|  |       expect(jobMock.queue.mock.calls).toEqual([ | ||||||
|  |         [{ name: JobName.ASSET_DELETION, data: { id: 'asset1' } }], | ||||||
|  |         [{ name: JobName.ASSET_DELETION, data: { id: 'asset2' } }], | ||||||
|  |       ]); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('should soft delete a batch of assets', async () => { | ||||||
|  |       accessMock.asset.hasOwnerAccess.mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |       await sut.deleteAll(authStub.user1, { ids: ['asset1', 'asset2'], force: false }); | ||||||
|  |  | ||||||
|  |       expect(assetMock.softDeleteAll).toHaveBeenCalledWith(['asset1', 'asset2']); | ||||||
|  |       expect(jobMock.queue.mock.calls).toEqual([ | ||||||
|  |         [ | ||||||
|  |           { | ||||||
|  |             name: JobName.SEARCH_REMOVE_ASSET, | ||||||
|  |             data: { ids: ['asset1', 'asset2'] }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       ]); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   describe('restoreAll', () => { | ||||||
|  |     it('should required asset restore access for all ids', async () => { | ||||||
|  |       accessMock.asset.hasOwnerAccess.mockResolvedValue(false); | ||||||
|  |       await expect( | ||||||
|  |         sut.deleteAll(authStub.user1, { | ||||||
|  |           ids: ['asset-1'], | ||||||
|  |         }), | ||||||
|  |       ).rejects.toBeInstanceOf(BadRequestException); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('should restore a batch of assets', async () => { | ||||||
|  |       accessMock.asset.hasOwnerAccess.mockResolvedValue(true); | ||||||
|  |  | ||||||
|  |       await sut.restoreAll(authStub.user1, { ids: ['asset1', 'asset2'] }); | ||||||
|  |  | ||||||
|  |       expect(assetMock.restoreAll).toHaveBeenCalledWith(['asset1', 'asset2']); | ||||||
|  |       expect(jobMock.queue.mock.calls).toEqual([ | ||||||
|  |         [ | ||||||
|  |           { | ||||||
|  |             name: JobName.SEARCH_INDEX_ASSET, | ||||||
|  |             data: { ids: ['asset1', 'asset2'] }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       ]); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   describe('handleAssetDeletion', () => { | ||||||
|  |     beforeEach(() => { | ||||||
|  |       when(jobMock.queue) | ||||||
|  |         .calledWith( | ||||||
|  |           expect.objectContaining({ | ||||||
|  |             name: JobName.ASSET_DELETION, | ||||||
|  |           }), | ||||||
|  |         ) | ||||||
|  |         .mockImplementation(async (item: JobItem) => { | ||||||
|  |           const jobData = (item as { data?: any })?.data || {}; | ||||||
|  |           await sut.handleAssetDeletion(jobData); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('should remove faces', async () => { | ||||||
|  |       const assetWithFace = { ...(assetStub.image as AssetEntity), faces: [faceStub.face1, faceStub.mergeFace1] }; | ||||||
|  |  | ||||||
|  |       when(assetMock.getById).calledWith(assetWithFace.id).mockResolvedValue(assetWithFace); | ||||||
|  |  | ||||||
|  |       await sut.handleAssetDeletion({ id: assetWithFace.id }); | ||||||
|  |  | ||||||
|  |       expect(jobMock.queue.mock.calls).toEqual([ | ||||||
|  |         [ | ||||||
|  |           { | ||||||
|  |             name: JobName.SEARCH_REMOVE_FACE, | ||||||
|  |             data: { assetId: faceStub.face1.assetId, personId: faceStub.face1.personId }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         [ | ||||||
|  |           { | ||||||
|  |             name: JobName.SEARCH_REMOVE_FACE, | ||||||
|  |             data: { assetId: faceStub.mergeFace1.assetId, personId: faceStub.mergeFace1.personId }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         [{ name: JobName.SEARCH_REMOVE_ASSET, data: { ids: [assetWithFace.id] } }], | ||||||
|  |         [ | ||||||
|  |           { | ||||||
|  |             name: JobName.DELETE_FILES, | ||||||
|  |             data: { | ||||||
|  |               files: [ | ||||||
|  |                 assetWithFace.webpPath, | ||||||
|  |                 assetWithFace.resizePath, | ||||||
|  |                 assetWithFace.encodedVideoPath, | ||||||
|  |                 assetWithFace.sidecarPath, | ||||||
|  |                 assetWithFace.originalPath, | ||||||
|  |               ], | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       ]); | ||||||
|  |  | ||||||
|  |       expect(assetMock.remove).toHaveBeenCalledWith(assetWithFace); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('should not schedule delete-files job for readonly assets', async () => { | ||||||
|  |       when(assetMock.getById) | ||||||
|  |         .calledWith(assetStub.readOnly.id) | ||||||
|  |         .mockResolvedValue(assetStub.readOnly as AssetEntity); | ||||||
|  |  | ||||||
|  |       await sut.handleAssetDeletion({ id: assetStub.readOnly.id }); | ||||||
|  |  | ||||||
|  |       expect(jobMock.queue.mock.calls).toEqual([ | ||||||
|  |         [{ name: JobName.SEARCH_REMOVE_ASSET, data: { ids: [assetStub.readOnly.id] } }], | ||||||
|  |       ]); | ||||||
|  |  | ||||||
|  |       expect(assetMock.remove).toHaveBeenCalledWith(assetStub.readOnly); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('should not process assets from external library without fromExternal flag', async () => { | ||||||
|  |       when(assetMock.getById) | ||||||
|  |         .calledWith(assetStub.external.id) | ||||||
|  |         .mockResolvedValue(assetStub.external as AssetEntity); | ||||||
|  |  | ||||||
|  |       await sut.handleAssetDeletion({ id: assetStub.external.id }); | ||||||
|  |  | ||||||
|  |       expect(jobMock.queue).not.toBeCalled(); | ||||||
|  |       expect(assetMock.remove).not.toBeCalled(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('should process assets from external library with fromExternal flag', async () => { | ||||||
|  |       when(assetMock.getById) | ||||||
|  |         .calledWith(assetStub.external.id) | ||||||
|  |         .mockResolvedValue(assetStub.external as AssetEntity); | ||||||
|  |  | ||||||
|  |       await sut.handleAssetDeletion({ id: assetStub.external.id, fromExternal: true }); | ||||||
|  |  | ||||||
|  |       expect(assetMock.remove).toHaveBeenCalledWith(assetStub.external); | ||||||
|  |       expect(jobMock.queue.mock.calls).toEqual([ | ||||||
|  |         [{ name: JobName.SEARCH_REMOVE_ASSET, data: { ids: [assetStub.external.id] } }], | ||||||
|  |         [ | ||||||
|  |           { | ||||||
|  |             name: JobName.DELETE_FILES, | ||||||
|  |             data: { | ||||||
|  |               files: [ | ||||||
|  |                 assetStub.external.webpPath, | ||||||
|  |                 assetStub.external.resizePath, | ||||||
|  |                 assetStub.external.encodedVideoPath, | ||||||
|  |                 assetStub.external.sidecarPath, | ||||||
|  |               ], | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       ]); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('should delete a live photo', async () => { | ||||||
|  |       await sut.handleAssetDeletion({ id: assetStub.livePhotoStillAsset.id }); | ||||||
|  |  | ||||||
|  |       expect(jobMock.queue.mock.calls).toEqual([ | ||||||
|  |         [{ name: JobName.SEARCH_REMOVE_ASSET, data: { ids: [assetStub.livePhotoStillAsset.id] } }], | ||||||
|  |         [{ name: JobName.ASSET_DELETION, data: { id: assetStub.livePhotoMotionAsset.id } }], | ||||||
|  |         [{ name: JobName.SEARCH_REMOVE_ASSET, data: { ids: [assetStub.livePhotoMotionAsset.id] } }], | ||||||
|  |         [ | ||||||
|  |           { | ||||||
|  |             name: JobName.DELETE_FILES, | ||||||
|  |             data: { | ||||||
|  |               files: [undefined, undefined, undefined, undefined, 'fake_path/asset_1.mp4'], | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         [ | ||||||
|  |           { | ||||||
|  |             name: JobName.DELETE_FILES, | ||||||
|  |             data: { | ||||||
|  |               files: [undefined, undefined, undefined, undefined, 'fake_path/asset_1.jpeg'], | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       ]); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|   describe('run', () => { |   describe('run', () => { | ||||||
|     it('should run the refresh metadata job', async () => { |     it('should run the refresh metadata job', async () => { | ||||||
|       accessMock.asset.hasOwnerAccess.mockResolvedValue(true); |       accessMock.asset.hasOwnerAccess.mockResolvedValue(true); | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import { AssetEntity } from '@app/infra/entities'; | import { AssetEntity, LibraryType } from '@app/infra/entities'; | ||||||
| import { BadRequestException, Inject, Logger } from '@nestjs/common'; | import { BadRequestException, Inject, Logger } from '@nestjs/common'; | ||||||
| import _ from 'lodash'; | import _ from 'lodash'; | ||||||
|  | import { DateTime, Duration } from 'luxon'; | ||||||
| import { extname } from 'path'; | import { extname } from 'path'; | ||||||
| import sanitize from 'sanitize-filename'; | import sanitize from 'sanitize-filename'; | ||||||
| import { AccessCore, IAccessRepository, Permission } from '../access'; | import { AccessCore, IAccessRepository, Permission } from '../access'; | ||||||
| @@ -8,10 +9,12 @@ import { AuthUserDto } from '../auth'; | |||||||
| import { ICryptoRepository } from '../crypto'; | import { ICryptoRepository } from '../crypto'; | ||||||
| import { mimeTypes } from '../domain.constant'; | import { mimeTypes } from '../domain.constant'; | ||||||
| import { HumanReadableSize, usePagination } from '../domain.util'; | import { HumanReadableSize, usePagination } from '../domain.util'; | ||||||
| import { IJobRepository, JobName } from '../job'; | import { IAssetDeletionJob, IJobRepository, JOBS_ASSET_PAGINATION_SIZE, JobName } from '../job'; | ||||||
| import { IStorageRepository, ImmichReadStream, StorageCore, StorageFolder } from '../storage'; | import { IStorageRepository, ImmichReadStream, StorageCore, StorageFolder } from '../storage'; | ||||||
|  | import { ISystemConfigRepository, SystemConfigCore } from '../system-config'; | ||||||
| import { IAssetRepository } from './asset.repository'; | import { IAssetRepository } from './asset.repository'; | ||||||
| import { | import { | ||||||
|  |   AssetBulkDeleteDto, | ||||||
|   AssetBulkUpdateDto, |   AssetBulkUpdateDto, | ||||||
|   AssetIdsDto, |   AssetIdsDto, | ||||||
|   AssetJobName, |   AssetJobName, | ||||||
| @@ -24,11 +27,13 @@ import { | |||||||
|   MemoryLaneDto, |   MemoryLaneDto, | ||||||
|   TimeBucketAssetDto, |   TimeBucketAssetDto, | ||||||
|   TimeBucketDto, |   TimeBucketDto, | ||||||
|  |   TrashAction, | ||||||
|   UpdateAssetDto, |   UpdateAssetDto, | ||||||
|   mapStats, |   mapStats, | ||||||
| } from './dto'; | } from './dto'; | ||||||
| import { | import { | ||||||
|   AssetResponseDto, |   AssetResponseDto, | ||||||
|  |   BulkIdsDto, | ||||||
|   MapMarkerResponseDto, |   MapMarkerResponseDto, | ||||||
|   MemoryLaneResponseDto, |   MemoryLaneResponseDto, | ||||||
|   TimeBucketResponseDto, |   TimeBucketResponseDto, | ||||||
| @@ -57,6 +62,7 @@ export interface UploadFile { | |||||||
| export class AssetService { | export class AssetService { | ||||||
|   private logger = new Logger(AssetService.name); |   private logger = new Logger(AssetService.name); | ||||||
|   private access: AccessCore; |   private access: AccessCore; | ||||||
|  |   private configCore: SystemConfigCore; | ||||||
|   private storageCore: StorageCore; |   private storageCore: StorageCore; | ||||||
|  |  | ||||||
|   constructor( |   constructor( | ||||||
| @@ -64,10 +70,12 @@ export class AssetService { | |||||||
|     @Inject(IAssetRepository) private assetRepository: IAssetRepository, |     @Inject(IAssetRepository) private assetRepository: IAssetRepository, | ||||||
|     @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, |     @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, | ||||||
|     @Inject(IJobRepository) private jobRepository: IJobRepository, |     @Inject(IJobRepository) private jobRepository: IJobRepository, | ||||||
|  |     @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, | ||||||
|     @Inject(IStorageRepository) private storageRepository: IStorageRepository, |     @Inject(IStorageRepository) private storageRepository: IStorageRepository, | ||||||
|   ) { |   ) { | ||||||
|     this.access = new AccessCore(accessRepository); |     this.access = new AccessCore(accessRepository); | ||||||
|     this.storageCore = new StorageCore(storageRepository); |     this.storageCore = new StorageCore(storageRepository); | ||||||
|  |     this.configCore = new SystemConfigCore(configRepository); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   canUploadFile({ authUser, fieldName, file }: UploadRequest): true { |   canUploadFile({ authUser, fieldName, file }: UploadRequest): true { | ||||||
| @@ -274,7 +282,9 @@ export class AssetService { | |||||||
|     if (dto.userId) { |     if (dto.userId) { | ||||||
|       const userId = dto.userId; |       const userId = dto.userId; | ||||||
|       await this.access.requirePermission(authUser, Permission.TIMELINE_DOWNLOAD, userId); |       await this.access.requirePermission(authUser, Permission.TIMELINE_DOWNLOAD, userId); | ||||||
|       return usePagination(PAGINATION_SIZE, (pagination) => this.assetRepository.getByUserId(pagination, userId)); |       return usePagination(PAGINATION_SIZE, (pagination) => | ||||||
|  |         this.assetRepository.getByUserId(pagination, userId, { isVisible: true }), | ||||||
|  |       ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     throw new BadRequestException('assetIds, albumId, or userId is required'); |     throw new BadRequestException('assetIds, albumId, or userId is required'); | ||||||
| @@ -303,13 +313,119 @@ export class AssetService { | |||||||
|     return mapAsset(asset); |     return mapAsset(asset); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   async updateAll(authUser: AuthUserDto, dto: AssetBulkUpdateDto) { |   async updateAll(authUser: AuthUserDto, dto: AssetBulkUpdateDto): Promise<void> { | ||||||
|     const { ids, ...options } = dto; |     const { ids, ...options } = dto; | ||||||
|     await this.access.requirePermission(authUser, Permission.ASSET_UPDATE, ids); |     await this.access.requirePermission(authUser, Permission.ASSET_UPDATE, ids); | ||||||
|     await this.jobRepository.queue({ name: JobName.SEARCH_INDEX_ASSET, data: { ids } }); |     await this.jobRepository.queue({ name: JobName.SEARCH_INDEX_ASSET, data: { ids } }); | ||||||
|     await this.assetRepository.updateAll(ids, options); |     await this.assetRepository.updateAll(ids, options); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   async handleAssetDeletionCheck() { | ||||||
|  |     const config = await this.configCore.getConfig(); | ||||||
|  |     const trashedDays = config.trash.enabled ? config.trash.days : 0; | ||||||
|  |     const trashedBefore = DateTime.now() | ||||||
|  |       .minus(Duration.fromObject({ days: trashedDays })) | ||||||
|  |       .toJSDate(); | ||||||
|  |     const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => | ||||||
|  |       this.assetRepository.getAll(pagination, { trashedBefore }), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     for await (const assets of assetPagination) { | ||||||
|  |       for (const asset of assets) { | ||||||
|  |         await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.id } }); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async handleAssetDeletion(job: IAssetDeletionJob) { | ||||||
|  |     const { id, fromExternal } = job; | ||||||
|  |  | ||||||
|  |     const asset = await this.assetRepository.getById(id); | ||||||
|  |     if (!asset) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Ignore requests that are not from external library job but is for an external asset | ||||||
|  |     if (!fromExternal && (!asset.library || asset.library.type === LibraryType.EXTERNAL)) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (asset.faces) { | ||||||
|  |       await Promise.all( | ||||||
|  |         asset.faces.map(({ assetId, personId }) => | ||||||
|  |           this.jobRepository.queue({ name: JobName.SEARCH_REMOVE_FACE, data: { assetId, personId } }), | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     await this.assetRepository.remove(asset); | ||||||
|  |     await this.jobRepository.queue({ name: JobName.SEARCH_REMOVE_ASSET, data: { ids: [asset.id] } }); | ||||||
|  |  | ||||||
|  |     // TODO refactor this to use cascades | ||||||
|  |     if (asset.livePhotoVideoId) { | ||||||
|  |       await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.livePhotoVideoId } }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const files = [asset.webpPath, asset.resizePath, asset.encodedVideoPath, asset.sidecarPath]; | ||||||
|  |     if (!fromExternal) { | ||||||
|  |       files.push(asset.originalPath); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!asset.isReadOnly) { | ||||||
|  |       await this.jobRepository.queue({ name: JobName.DELETE_FILES, data: { files } }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async deleteAll(authUser: AuthUserDto, dto: AssetBulkDeleteDto): Promise<void> { | ||||||
|  |     const { ids, force } = dto; | ||||||
|  |  | ||||||
|  |     await this.access.requirePermission(authUser, Permission.ASSET_DELETE, ids); | ||||||
|  |  | ||||||
|  |     if (force) { | ||||||
|  |       for (const id of ids) { | ||||||
|  |         await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id } }); | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       await this.assetRepository.softDeleteAll(ids); | ||||||
|  |       await this.jobRepository.queue({ name: JobName.SEARCH_REMOVE_ASSET, data: { ids } }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async handleTrashAction(authUser: AuthUserDto, action: TrashAction): Promise<void> { | ||||||
|  |     const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => | ||||||
|  |       this.assetRepository.getByUserId(pagination, authUser.id, { trashedBefore: DateTime.now().toJSDate() }), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     if (action == TrashAction.RESTORE_ALL) { | ||||||
|  |       for await (const assets of assetPagination) { | ||||||
|  |         const ids = assets.map((a) => a.id); | ||||||
|  |         await this.assetRepository.restoreAll(ids); | ||||||
|  |         await this.jobRepository.queue({ name: JobName.SEARCH_INDEX_ASSET, data: { ids } }); | ||||||
|  |       } | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (action == TrashAction.EMPTY_ALL) { | ||||||
|  |       for await (const assets of assetPagination) { | ||||||
|  |         for (const asset of assets) { | ||||||
|  |           await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.id } }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async restoreAll(authUser: AuthUserDto, dto: BulkIdsDto): Promise<void> { | ||||||
|  |     const { ids } = dto; | ||||||
|  |     await this.access.requirePermission(authUser, Permission.ASSET_RESTORE, ids); | ||||||
|  |     await this.assetRepository.restoreAll(ids); | ||||||
|  |     await this.jobRepository.queue({ name: JobName.SEARCH_INDEX_ASSET, data: { ids } }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   async run(authUser: AuthUserDto, dto: AssetJobsDto) { |   async run(authUser: AuthUserDto, dto: AssetJobsDto) { | ||||||
|     await this.access.requirePermission(authUser, Permission.ASSET_UPDATE, dto.assetIds); |     await this.access.requirePermission(authUser, Permission.ASSET_UPDATE, dto.assetIds); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,6 +15,11 @@ export class AssetStatsDto { | |||||||
|   @Transform(toBoolean) |   @Transform(toBoolean) | ||||||
|   @Optional() |   @Optional() | ||||||
|   isFavorite?: boolean; |   isFavorite?: boolean; | ||||||
|  |  | ||||||
|  |   @IsBoolean() | ||||||
|  |   @Transform(toBoolean) | ||||||
|  |   @Optional() | ||||||
|  |   isTrashed?: boolean; | ||||||
| } | } | ||||||
|  |  | ||||||
| export class AssetStatsResponseDto { | export class AssetStatsResponseDto { | ||||||
|   | |||||||
| @@ -34,3 +34,14 @@ export class RandomAssetsDto { | |||||||
|   @Type(() => Number) |   @Type(() => Number) | ||||||
|   count?: number; |   count?: number; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export enum TrashAction { | ||||||
|  |   EMPTY_ALL = 'empty-all', | ||||||
|  |   RESTORE_ALL = 'restore-all', | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class AssetBulkDeleteDto extends BulkIdsDto { | ||||||
|  |   @Optional() | ||||||
|  |   @IsBoolean() | ||||||
|  |   force?: boolean; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -28,6 +28,11 @@ export class TimeBucketDto { | |||||||
|   @IsBoolean() |   @IsBoolean() | ||||||
|   @Transform(toBoolean) |   @Transform(toBoolean) | ||||||
|   isFavorite?: boolean; |   isFavorite?: boolean; | ||||||
|  |  | ||||||
|  |   @Optional() | ||||||
|  |   @IsBoolean() | ||||||
|  |   @Transform(toBoolean) | ||||||
|  |   isTrashed?: boolean; | ||||||
| } | } | ||||||
|  |  | ||||||
| export class TimeBucketAssetDto extends TimeBucketDto { | export class TimeBucketAssetDto extends TimeBucketDto { | ||||||
|   | |||||||
| @@ -26,6 +26,7 @@ export class AssetResponseDto { | |||||||
|   updatedAt!: Date; |   updatedAt!: Date; | ||||||
|   isFavorite!: boolean; |   isFavorite!: boolean; | ||||||
|   isArchived!: boolean; |   isArchived!: boolean; | ||||||
|  |   isTrashed!: boolean; | ||||||
|   localDateTime!: Date; |   localDateTime!: Date; | ||||||
|   isOffline!: boolean; |   isOffline!: boolean; | ||||||
|   isExternal!: boolean; |   isExternal!: boolean; | ||||||
| @@ -59,6 +60,7 @@ function _map(entity: AssetEntity, withExif: boolean): AssetResponseDto { | |||||||
|     updatedAt: entity.updatedAt, |     updatedAt: entity.updatedAt, | ||||||
|     isFavorite: entity.isFavorite, |     isFavorite: entity.isFavorite, | ||||||
|     isArchived: entity.isArchived, |     isArchived: entity.isArchived, | ||||||
|  |     isTrashed: !!entity.deletedAt, | ||||||
|     duration: entity.duration ?? '0:00:00.00000', |     duration: entity.duration ?? '0:00:00.00000', | ||||||
|     exifInfo: withExif ? (entity.exifInfo ? mapExif(entity.exifInfo) : undefined) : undefined, |     exifInfo: withExif ? (entity.exifInfo ? mapExif(entity.exifInfo) : undefined) : undefined, | ||||||
|     smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined, |     smartInfo: entity.smartInfo ? mapSmartInfo(entity.smartInfo) : undefined, | ||||||
|   | |||||||
| @@ -2,8 +2,10 @@ export const ICommunicationRepository = 'ICommunicationRepository'; | |||||||
|  |  | ||||||
| export enum CommunicationEvent { | export enum CommunicationEvent { | ||||||
|   UPLOAD_SUCCESS = 'on_upload_success', |   UPLOAD_SUCCESS = 'on_upload_success', | ||||||
|  |   CONFIG_UPDATE = 'on_config_update', | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface ICommunicationRepository { | export interface ICommunicationRepository { | ||||||
|   send(event: CommunicationEvent, userId: string, data: any): void; |   send(event: CommunicationEvent, userId: string, data: any): void; | ||||||
|  |   broadcast(event: CommunicationEvent, data: any): void; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -41,6 +41,10 @@ export enum JobName { | |||||||
|   USER_DELETION = 'user-deletion', |   USER_DELETION = 'user-deletion', | ||||||
|   USER_DELETE_CHECK = 'user-delete-check', |   USER_DELETE_CHECK = 'user-delete-check', | ||||||
|  |  | ||||||
|  |   // asset | ||||||
|  |   ASSET_DELETION = 'asset-deletion', | ||||||
|  |   ASSET_DELETION_CHECK = 'asset-deletion-check', | ||||||
|  |  | ||||||
|   // storage template |   // storage template | ||||||
|   STORAGE_TEMPLATE_MIGRATION = 'storage-template-migration', |   STORAGE_TEMPLATE_MIGRATION = 'storage-template-migration', | ||||||
|   STORAGE_TEMPLATE_MIGRATION_SINGLE = 'storage-template-migration-single', |   STORAGE_TEMPLATE_MIGRATION_SINGLE = 'storage-template-migration-single', | ||||||
| @@ -99,6 +103,8 @@ export const JOBS_ASSET_PAGINATION_SIZE = 1000; | |||||||
|  |  | ||||||
| export const JOBS_TO_QUEUE: Record<JobName, QueueName> = { | export const JOBS_TO_QUEUE: Record<JobName, QueueName> = { | ||||||
|   // misc |   // misc | ||||||
|  |   [JobName.ASSET_DELETION]: QueueName.BACKGROUND_TASK, | ||||||
|  |   [JobName.ASSET_DELETION_CHECK]: QueueName.BACKGROUND_TASK, | ||||||
|   [JobName.USER_DELETE_CHECK]: QueueName.BACKGROUND_TASK, |   [JobName.USER_DELETE_CHECK]: QueueName.BACKGROUND_TASK, | ||||||
|   [JobName.USER_DELETION]: QueueName.BACKGROUND_TASK, |   [JobName.USER_DELETION]: QueueName.BACKGROUND_TASK, | ||||||
|   [JobName.DELETE_FILES]: QueueName.BACKGROUND_TASK, |   [JobName.DELETE_FILES]: QueueName.BACKGROUND_TASK, | ||||||
|   | |||||||
| @@ -12,6 +12,10 @@ export interface IEntityJob extends IBaseJob { | |||||||
|   source?: 'upload'; |   source?: 'upload'; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export interface IAssetDeletionJob extends IEntityJob { | ||||||
|  |   fromExternal?: boolean; | ||||||
|  | } | ||||||
|  |  | ||||||
| export interface IOfflineLibraryFileJob extends IEntityJob { | export interface IOfflineLibraryFileJob extends IEntityJob { | ||||||
|   assetPath: string; |   assetPath: string; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import { JobName, QueueName } from './job.constants'; | import { JobName, QueueName } from './job.constants'; | ||||||
|  |  | ||||||
| import { | import { | ||||||
|  |   IAssetDeletionJob, | ||||||
|   IAssetFaceJob, |   IAssetFaceJob, | ||||||
|   IBaseJob, |   IBaseJob, | ||||||
|   IBulkEntityJob, |   IBulkEntityJob, | ||||||
| @@ -82,6 +83,8 @@ export type JobItem = | |||||||
|  |  | ||||||
|   // Asset Deletion |   // Asset Deletion | ||||||
|   | { name: JobName.PERSON_CLEANUP; data?: IBaseJob } |   | { name: JobName.PERSON_CLEANUP; data?: IBaseJob } | ||||||
|  |   | { name: JobName.ASSET_DELETION; data: IAssetDeletionJob } | ||||||
|  |   | { name: JobName.ASSET_DELETION_CHECK; data?: IBaseJob } | ||||||
|  |  | ||||||
|   // Library Managment |   // Library Managment | ||||||
|   | { name: JobName.LIBRARY_SCAN_ASSET; data: ILibraryFileJob } |   | { name: JobName.LIBRARY_SCAN_ASSET; data: ILibraryFileJob } | ||||||
|   | |||||||
| @@ -48,6 +48,7 @@ describe(JobService.name, () => { | |||||||
|       await sut.handleNightlyJobs(); |       await sut.handleNightlyJobs(); | ||||||
|  |  | ||||||
|       expect(jobMock.queue.mock.calls).toEqual([ |       expect(jobMock.queue.mock.calls).toEqual([ | ||||||
|  |         [{ name: JobName.ASSET_DELETION_CHECK }], | ||||||
|         [{ name: JobName.USER_DELETE_CHECK }], |         [{ name: JobName.USER_DELETE_CHECK }], | ||||||
|         [{ name: JobName.PERSON_CLEANUP }], |         [{ name: JobName.PERSON_CLEANUP }], | ||||||
|         [{ name: JobName.QUEUE_GENERATE_THUMBNAILS, data: { force: false } }], |         [{ name: JobName.QUEUE_GENERATE_THUMBNAILS, data: { force: false } }], | ||||||
|   | |||||||
| @@ -140,6 +140,7 @@ export class JobService { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   async handleNightlyJobs() { |   async handleNightlyJobs() { | ||||||
|  |     await this.jobRepository.queue({ name: JobName.ASSET_DELETION_CHECK }); | ||||||
|     await this.jobRepository.queue({ name: JobName.USER_DELETE_CHECK }); |     await this.jobRepository.queue({ name: JobName.USER_DELETE_CHECK }); | ||||||
|     await this.jobRepository.queue({ name: JobName.PERSON_CLEANUP }); |     await this.jobRepository.queue({ name: JobName.PERSON_CLEANUP }); | ||||||
|     await this.jobRepository.queue({ name: JobName.QUEUE_GENERATE_THUMBNAILS, data: { force: false } }); |     await this.jobRepository.queue({ name: JobName.QUEUE_GENERATE_THUMBNAILS, data: { force: false } }); | ||||||
|   | |||||||
| @@ -1182,23 +1182,8 @@ describe(LibraryService.name, () => { | |||||||
|       expect(jobMock.queue.mock.calls).toEqual([ |       expect(jobMock.queue.mock.calls).toEqual([ | ||||||
|         [ |         [ | ||||||
|           { |           { | ||||||
|             name: JobName.SEARCH_REMOVE_ASSET, |             name: JobName.ASSET_DELETION, | ||||||
|             data: { |             data: { id: assetStub.image1.id, fromExternal: true }, | ||||||
|               ids: [assetStub.image1.id], |  | ||||||
|             }, |  | ||||||
|           }, |  | ||||||
|         ], |  | ||||||
|         [ |  | ||||||
|           { |  | ||||||
|             name: JobName.DELETE_FILES, |  | ||||||
|             data: { |  | ||||||
|               files: [ |  | ||||||
|                 assetStub.image1.webpPath, |  | ||||||
|                 assetStub.image1.resizePath, |  | ||||||
|                 assetStub.image1.encodedVideoPath, |  | ||||||
|                 assetStub.image1.sidecarPath, |  | ||||||
|               ], |  | ||||||
|             }, |  | ||||||
|           }, |           }, | ||||||
|         ], |         ], | ||||||
|       ]); |       ]); | ||||||
|   | |||||||
| @@ -439,31 +439,17 @@ export class LibraryService { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private async deleteAssets(assetIds: string[]) { |   private async deleteAssets(assetIds: string[]) { | ||||||
|     // TODO: this should be refactored to a centralized asset deletion service |  | ||||||
|     for (const assetId of assetIds) { |     for (const assetId of assetIds) { | ||||||
|       const asset = await this.assetRepository.getById(assetId); |       const asset = await this.assetRepository.getById(assetId); | ||||||
|  |       if (!asset) { | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|       this.logger.debug(`Removing asset from library: ${asset.originalPath}`); |       this.logger.debug(`Removing asset from library: ${asset.originalPath}`); | ||||||
|  |  | ||||||
|       if (asset.faces) { |  | ||||||
|         await Promise.all( |  | ||||||
|           asset.faces.map(({ assetId, personId }) => |  | ||||||
|             this.jobRepository.queue({ name: JobName.SEARCH_REMOVE_FACE, data: { assetId, personId } }), |  | ||||||
|           ), |  | ||||||
|         ); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       await this.assetRepository.remove(asset); |  | ||||||
|       await this.jobRepository.queue({ name: JobName.SEARCH_REMOVE_ASSET, data: { ids: [asset.id] } }); |  | ||||||
|  |  | ||||||
|       await this.jobRepository.queue({ |       await this.jobRepository.queue({ | ||||||
|         name: JobName.DELETE_FILES, |         name: JobName.ASSET_DELETION, | ||||||
|         data: { files: [asset.webpPath, asset.resizePath, asset.encodedVideoPath, asset.sidecarPath] }, |         data: { id: asset.id, fromExternal: true }, | ||||||
|       }); |       }); | ||||||
|  |  | ||||||
|       // TODO refactor this to use cascades |  | ||||||
|       if (asset.livePhotoVideoId && !assetIds.includes(asset.livePhotoVideoId)) { |  | ||||||
|         assetIds.push(asset.livePhotoVideoId); |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -83,6 +83,8 @@ export class ServerConfigDto { | |||||||
|   oauthButtonText!: string; |   oauthButtonText!: string; | ||||||
|   loginPageMessage!: string; |   loginPageMessage!: string; | ||||||
|   mapTileUrl!: string; |   mapTileUrl!: string; | ||||||
|  |   @ApiProperty({ type: 'integer' }) | ||||||
|  |   trashDays!: number; | ||||||
| } | } | ||||||
|  |  | ||||||
| export class ServerFeaturesDto implements FeatureFlags { | export class ServerFeaturesDto implements FeatureFlags { | ||||||
| @@ -90,6 +92,7 @@ export class ServerFeaturesDto implements FeatureFlags { | |||||||
|   configFile!: boolean; |   configFile!: boolean; | ||||||
|   facialRecognition!: boolean; |   facialRecognition!: boolean; | ||||||
|   map!: boolean; |   map!: boolean; | ||||||
|  |   trash!: boolean; | ||||||
|   reverseGeocoding!: boolean; |   reverseGeocoding!: boolean; | ||||||
|   oauth!: boolean; |   oauth!: boolean; | ||||||
|   oauthAutoLaunch!: boolean; |   oauthAutoLaunch!: boolean; | ||||||
|   | |||||||
| @@ -159,6 +159,7 @@ describe(ServerInfoService.name, () => { | |||||||
|         sidecar: true, |         sidecar: true, | ||||||
|         tagImage: true, |         tagImage: true, | ||||||
|         configFile: false, |         configFile: false, | ||||||
|  |         trash: true, | ||||||
|       }); |       }); | ||||||
|       expect(configMock.load).toHaveBeenCalled(); |       expect(configMock.load).toHaveBeenCalled(); | ||||||
|     }); |     }); | ||||||
| @@ -169,6 +170,7 @@ describe(ServerInfoService.name, () => { | |||||||
|       await expect(sut.getConfig()).resolves.toEqual({ |       await expect(sut.getConfig()).resolves.toEqual({ | ||||||
|         loginPageMessage: '', |         loginPageMessage: '', | ||||||
|         oauthButtonText: 'Login with OAuth', |         oauthButtonText: 'Login with OAuth', | ||||||
|  |         trashDays: 30, | ||||||
|         mapTileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', |         mapTileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', | ||||||
|       }); |       }); | ||||||
|       expect(configMock.load).toHaveBeenCalled(); |       expect(configMock.load).toHaveBeenCalled(); | ||||||
|   | |||||||
| @@ -66,6 +66,7 @@ export class ServerInfoService { | |||||||
|     return { |     return { | ||||||
|       loginPageMessage, |       loginPageMessage, | ||||||
|       mapTileUrl: config.map.tileUrl, |       mapTileUrl: config.map.tileUrl, | ||||||
|  |       trashDays: config.trash.days, | ||||||
|       oauthButtonText: config.oauth.buttonText, |       oauthButtonText: config.oauth.buttonText, | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -3,4 +3,5 @@ export * from './system-config-oauth.dto'; | |||||||
| export * from './system-config-password-login.dto'; | export * from './system-config-password-login.dto'; | ||||||
| export * from './system-config-storage-template.dto'; | export * from './system-config-storage-template.dto'; | ||||||
| export * from './system-config-thumbnail.dto'; | export * from './system-config-thumbnail.dto'; | ||||||
|  | export * from './system-config-trash.dto'; | ||||||
| export * from './system-config.dto'; | export * from './system-config.dto'; | ||||||
|   | |||||||
| @@ -0,0 +1,14 @@ | |||||||
|  | import { ApiProperty } from '@nestjs/swagger'; | ||||||
|  | import { Type } from 'class-transformer'; | ||||||
|  | import { IsBoolean, IsInt, Min } from 'class-validator'; | ||||||
|  |  | ||||||
|  | export class SystemConfigTrashDto { | ||||||
|  |   @IsBoolean() | ||||||
|  |   enabled!: boolean; | ||||||
|  |  | ||||||
|  |   @IsInt() | ||||||
|  |   @Min(0) | ||||||
|  |   @Type(() => Number) | ||||||
|  |   @ApiProperty({ type: 'integer' }) | ||||||
|  |   days!: number; | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| import { SystemConfigThumbnailDto } from '@app/domain/system-config'; | import { SystemConfigThumbnailDto, SystemConfigTrashDto } from '@app/domain/system-config'; | ||||||
| import { SystemConfig } from '@app/infra/entities'; | import { SystemConfig } from '@app/infra/entities'; | ||||||
| import { Type } from 'class-transformer'; | import { Type } from 'class-transformer'; | ||||||
| import { IsObject, ValidateNested } from 'class-validator'; | import { IsObject, ValidateNested } from 'class-validator'; | ||||||
| @@ -56,6 +56,11 @@ export class SystemConfigDto implements SystemConfig { | |||||||
|   @ValidateNested() |   @ValidateNested() | ||||||
|   @IsObject() |   @IsObject() | ||||||
|   thumbnail!: SystemConfigThumbnailDto; |   thumbnail!: SystemConfigThumbnailDto; | ||||||
|  |  | ||||||
|  |   @Type(() => SystemConfigTrashDto) | ||||||
|  |   @ValidateNested() | ||||||
|  |   @IsObject() | ||||||
|  |   trash!: SystemConfigTrashDto; | ||||||
| } | } | ||||||
|  |  | ||||||
| export function mapConfig(config: SystemConfig): SystemConfigDto { | export function mapConfig(config: SystemConfig): SystemConfigDto { | ||||||
|   | |||||||
| @@ -102,17 +102,19 @@ export const defaults = Object.freeze<SystemConfig>({ | |||||||
|   passwordLogin: { |   passwordLogin: { | ||||||
|     enabled: true, |     enabled: true, | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   storageTemplate: { |   storageTemplate: { | ||||||
|     template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}', |     template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}', | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   thumbnail: { |   thumbnail: { | ||||||
|     webpSize: 250, |     webpSize: 250, | ||||||
|     jpegSize: 1440, |     jpegSize: 1440, | ||||||
|     quality: 80, |     quality: 80, | ||||||
|     colorspace: Colorspace.P3, |     colorspace: Colorspace.P3, | ||||||
|   }, |   }, | ||||||
|  |   trash: { | ||||||
|  |     enabled: true, | ||||||
|  |     days: 30, | ||||||
|  |   }, | ||||||
| }); | }); | ||||||
|  |  | ||||||
| export enum FeatureFlag { | export enum FeatureFlag { | ||||||
| @@ -127,6 +129,7 @@ export enum FeatureFlag { | |||||||
|   OAUTH_AUTO_LAUNCH = 'oauthAutoLaunch', |   OAUTH_AUTO_LAUNCH = 'oauthAutoLaunch', | ||||||
|   PASSWORD_LOGIN = 'passwordLogin', |   PASSWORD_LOGIN = 'passwordLogin', | ||||||
|   CONFIG_FILE = 'configFile', |   CONFIG_FILE = 'configFile', | ||||||
|  |   TRASH = 'trash', | ||||||
| } | } | ||||||
|  |  | ||||||
| export type FeatureFlags = Record<FeatureFlag, boolean>; | export type FeatureFlags = Record<FeatureFlag, boolean>; | ||||||
| @@ -186,6 +189,7 @@ export class SystemConfigCore { | |||||||
|       [FeatureFlag.REVERSE_GEOCODING]: config.reverseGeocoding.enabled, |       [FeatureFlag.REVERSE_GEOCODING]: config.reverseGeocoding.enabled, | ||||||
|       [FeatureFlag.SIDECAR]: true, |       [FeatureFlag.SIDECAR]: true, | ||||||
|       [FeatureFlag.SEARCH]: process.env.TYPESENSE_ENABLED !== 'false', |       [FeatureFlag.SEARCH]: process.env.TYPESENSE_ENABLED !== 'false', | ||||||
|  |       [FeatureFlag.TRASH]: config.trash.enabled, | ||||||
|  |  | ||||||
|       // TODO: use these instead of `POST oauth/config` |       // TODO: use these instead of `POST oauth/config` | ||||||
|       [FeatureFlag.OAUTH]: config.oauth.enabled, |       [FeatureFlag.OAUTH]: config.oauth.enabled, | ||||||
|   | |||||||
| @@ -12,7 +12,8 @@ import { | |||||||
|   VideoCodec, |   VideoCodec, | ||||||
| } from '@app/infra/entities'; | } from '@app/infra/entities'; | ||||||
| import { BadRequestException } from '@nestjs/common'; | import { BadRequestException } from '@nestjs/common'; | ||||||
| import { newJobRepositoryMock, newSystemConfigRepositoryMock } from '@test'; | import { newCommunicationRepositoryMock, newJobRepositoryMock, newSystemConfigRepositoryMock } from '@test'; | ||||||
|  | import { ICommunicationRepository } from '..'; | ||||||
| import { IJobRepository, JobName, QueueName } from '../job'; | import { IJobRepository, JobName, QueueName } from '../job'; | ||||||
| import { SystemConfigValidator, defaults } from './system-config.core'; | import { SystemConfigValidator, defaults } from './system-config.core'; | ||||||
| import { ISystemConfigRepository } from './system-config.repository'; | import { ISystemConfigRepository } from './system-config.repository'; | ||||||
| @@ -21,6 +22,7 @@ import { SystemConfigService } from './system-config.service'; | |||||||
| const updates: SystemConfigEntity[] = [ | const updates: SystemConfigEntity[] = [ | ||||||
|   { key: SystemConfigKey.FFMPEG_CRF, value: 30 }, |   { key: SystemConfigKey.FFMPEG_CRF, value: 30 }, | ||||||
|   { key: SystemConfigKey.OAUTH_AUTO_LAUNCH, value: true }, |   { key: SystemConfigKey.OAUTH_AUTO_LAUNCH, value: true }, | ||||||
|  |   { key: SystemConfigKey.TRASH_DAYS, value: 10 }, | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| const updatedConfig = Object.freeze<SystemConfig>({ | const updatedConfig = Object.freeze<SystemConfig>({ | ||||||
| @@ -110,18 +112,24 @@ const updatedConfig = Object.freeze<SystemConfig>({ | |||||||
|     quality: 80, |     quality: 80, | ||||||
|     colorspace: Colorspace.P3, |     colorspace: Colorspace.P3, | ||||||
|   }, |   }, | ||||||
|  |   trash: { | ||||||
|  |     enabled: true, | ||||||
|  |     days: 10, | ||||||
|  |   }, | ||||||
| }); | }); | ||||||
|  |  | ||||||
| describe(SystemConfigService.name, () => { | describe(SystemConfigService.name, () => { | ||||||
|   let sut: SystemConfigService; |   let sut: SystemConfigService; | ||||||
|   let configMock: jest.Mocked<ISystemConfigRepository>; |   let configMock: jest.Mocked<ISystemConfigRepository>; | ||||||
|  |   let communicationMock: jest.Mocked<ICommunicationRepository>; | ||||||
|   let jobMock: jest.Mocked<IJobRepository>; |   let jobMock: jest.Mocked<IJobRepository>; | ||||||
|  |  | ||||||
|   beforeEach(async () => { |   beforeEach(async () => { | ||||||
|     delete process.env.IMMICH_CONFIG_FILE; |     delete process.env.IMMICH_CONFIG_FILE; | ||||||
|     configMock = newSystemConfigRepositoryMock(); |     configMock = newSystemConfigRepositoryMock(); | ||||||
|  |     communicationMock = newCommunicationRepositoryMock(); | ||||||
|     jobMock = newJobRepositoryMock(); |     jobMock = newJobRepositoryMock(); | ||||||
|     sut = new SystemConfigService(configMock, jobMock); |     sut = new SystemConfigService(configMock, communicationMock, jobMock); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   it('should work', () => { |   it('should work', () => { | ||||||
| @@ -157,6 +165,7 @@ describe(SystemConfigService.name, () => { | |||||||
|       configMock.load.mockResolvedValue([ |       configMock.load.mockResolvedValue([ | ||||||
|         { key: SystemConfigKey.FFMPEG_CRF, value: 30 }, |         { key: SystemConfigKey.FFMPEG_CRF, value: 30 }, | ||||||
|         { key: SystemConfigKey.OAUTH_AUTO_LAUNCH, value: true }, |         { key: SystemConfigKey.OAUTH_AUTO_LAUNCH, value: true }, | ||||||
|  |         { key: SystemConfigKey.TRASH_DAYS, value: 10 }, | ||||||
|       ]); |       ]); | ||||||
|  |  | ||||||
|       await expect(sut.getConfig()).resolves.toEqual(updatedConfig); |       await expect(sut.getConfig()).resolves.toEqual(updatedConfig); | ||||||
| @@ -164,7 +173,7 @@ describe(SystemConfigService.name, () => { | |||||||
|  |  | ||||||
|     it('should load the config from a file', async () => { |     it('should load the config from a file', async () => { | ||||||
|       process.env.IMMICH_CONFIG_FILE = 'immich-config.json'; |       process.env.IMMICH_CONFIG_FILE = 'immich-config.json'; | ||||||
|       const partialConfig = { ffmpeg: { crf: 30 }, oauth: { autoLaunch: true } }; |       const partialConfig = { ffmpeg: { crf: 30 }, oauth: { autoLaunch: true }, trash: { days: 10 } }; | ||||||
|       configMock.readFile.mockResolvedValue(Buffer.from(JSON.stringify(partialConfig))); |       configMock.readFile.mockResolvedValue(Buffer.from(JSON.stringify(partialConfig))); | ||||||
|  |  | ||||||
|       await expect(sut.getConfig()).resolves.toEqual(updatedConfig); |       await expect(sut.getConfig()).resolves.toEqual(updatedConfig); | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import { Inject, Injectable } from '@nestjs/common'; | import { Inject, Injectable } from '@nestjs/common'; | ||||||
| import { ISystemConfigRepository } from '.'; | import { ISystemConfigRepository } from '.'; | ||||||
|  | import { CommunicationEvent, ICommunicationRepository } from '../communication'; | ||||||
| import { IJobRepository, JobName } from '../job'; | import { IJobRepository, JobName } from '../job'; | ||||||
| import { SystemConfigDto, mapConfig } from './dto/system-config.dto'; | import { SystemConfigDto, mapConfig } from './dto/system-config.dto'; | ||||||
| import { SystemConfigTemplateStorageOptionDto } from './response-dto/system-config-template-storage-option.dto'; | import { SystemConfigTemplateStorageOptionDto } from './response-dto/system-config-template-storage-option.dto'; | ||||||
| @@ -20,6 +21,7 @@ export class SystemConfigService { | |||||||
|   private core: SystemConfigCore; |   private core: SystemConfigCore; | ||||||
|   constructor( |   constructor( | ||||||
|     @Inject(ISystemConfigRepository) repository: ISystemConfigRepository, |     @Inject(ISystemConfigRepository) repository: ISystemConfigRepository, | ||||||
|  |     @Inject(ICommunicationRepository) private communicationRepository: ICommunicationRepository, | ||||||
|     @Inject(IJobRepository) private jobRepository: IJobRepository, |     @Inject(IJobRepository) private jobRepository: IJobRepository, | ||||||
|   ) { |   ) { | ||||||
|     this.core = new SystemConfigCore(repository); |     this.core = new SystemConfigCore(repository); | ||||||
| @@ -42,6 +44,7 @@ export class SystemConfigService { | |||||||
|   async updateConfig(dto: SystemConfigDto): Promise<SystemConfigDto> { |   async updateConfig(dto: SystemConfigDto): Promise<SystemConfigDto> { | ||||||
|     const config = await this.core.updateConfig(dto); |     const config = await this.core.updateConfig(dto); | ||||||
|     await this.jobRepository.queue({ name: JobName.SYSTEM_CONFIG_CHANGE }); |     await this.jobRepository.queue({ name: JobName.SYSTEM_CONFIG_CHANGE }); | ||||||
|  |     this.communicationRepository.broadcast(CommunicationEvent.CONFIG_UPDATE, {}); | ||||||
|     return mapConfig(config); |     return mapConfig(config); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,7 +23,6 @@ export interface AssetOwnerCheck extends AssetCheck { | |||||||
| export interface IAssetRepository { | export interface IAssetRepository { | ||||||
|   get(id: string): Promise<AssetEntity | null>; |   get(id: string): Promise<AssetEntity | null>; | ||||||
|   create(asset: AssetCreate): Promise<AssetEntity>; |   create(asset: AssetCreate): Promise<AssetEntity>; | ||||||
|   remove(asset: AssetEntity): Promise<void>; |  | ||||||
|   getAllByUserId(userId: string, dto: AssetSearchDto): Promise<AssetEntity[]>; |   getAllByUserId(userId: string, dto: AssetSearchDto): Promise<AssetEntity[]>; | ||||||
|   getAllByDeviceId(userId: string, deviceId: string): Promise<string[]>; |   getAllByDeviceId(userId: string, deviceId: string): Promise<string[]>; | ||||||
|   getById(assetId: string): Promise<AssetEntity>; |   getById(assetId: string): Promise<AssetEntity>; | ||||||
| @@ -111,6 +110,8 @@ export class AssetRepository implements IAssetRepository { | |||||||
|           person: true, |           person: true, | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|  |       // We are specifically asking for this asset. Return it even if it is soft deleted | ||||||
|  |       withDeleted: true, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -135,6 +136,7 @@ export class AssetRepository implements IAssetRepository { | |||||||
|       order: { |       order: { | ||||||
|         fileCreatedAt: 'DESC', |         fileCreatedAt: 'DESC', | ||||||
|       }, |       }, | ||||||
|  |       withDeleted: true, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -147,6 +149,7 @@ export class AssetRepository implements IAssetRepository { | |||||||
|         }, |         }, | ||||||
|         library: true, |         library: true, | ||||||
|       }, |       }, | ||||||
|  |       withDeleted: true, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -154,10 +157,6 @@ export class AssetRepository implements IAssetRepository { | |||||||
|     return this.assetRepository.save(asset); |     return this.assetRepository.save(asset); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   async remove(asset: AssetEntity): Promise<void> { |  | ||||||
|     await this.assetRepository.remove(asset); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Get assets by device's Id on the database |    * Get assets by device's Id on the database | ||||||
|    * @param ownerId |    * @param ownerId | ||||||
| @@ -194,6 +193,7 @@ export class AssetRepository implements IAssetRepository { | |||||||
|         ownerId, |         ownerId, | ||||||
|         checksum: In(checksums), |         checksum: In(checksums), | ||||||
|       }, |       }, | ||||||
|  |       withDeleted: true, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ import { AssetResponseDto, AuthUserDto } from '@app/domain'; | |||||||
| import { | import { | ||||||
|   Body, |   Body, | ||||||
|   Controller, |   Controller, | ||||||
|   Delete, |  | ||||||
|   Get, |   Get, | ||||||
|   HttpCode, |   HttpCode, | ||||||
|   HttpStatus, |   HttpStatus, | ||||||
| @@ -27,7 +26,6 @@ import { AssetSearchDto } from './dto/asset-search.dto'; | |||||||
| import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto'; | import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto'; | ||||||
| import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; | import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; | ||||||
| import { CreateAssetDto, ImportAssetDto } from './dto/create-asset.dto'; | import { CreateAssetDto, ImportAssetDto } from './dto/create-asset.dto'; | ||||||
| import { DeleteAssetDto } from './dto/delete-asset.dto'; |  | ||||||
| import { DeviceIdDto } from './dto/device-id.dto'; | import { DeviceIdDto } from './dto/device-id.dto'; | ||||||
| import { GetAssetThumbnailDto } from './dto/get-asset-thumbnail.dto'; | import { GetAssetThumbnailDto } from './dto/get-asset-thumbnail.dto'; | ||||||
| import { SearchAssetDto } from './dto/search-asset.dto'; | import { SearchAssetDto } from './dto/search-asset.dto'; | ||||||
| @@ -38,7 +36,6 @@ import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-a | |||||||
| import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto'; | import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto'; | ||||||
| import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto'; | import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto'; | ||||||
| import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto'; | import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto'; | ||||||
| import { DeleteAssetResponseDto } from './response-dto/delete-asset-response.dto'; |  | ||||||
|  |  | ||||||
| interface UploadFiles { | interface UploadFiles { | ||||||
|   assetData: ImmichFile[]; |   assetData: ImmichFile[]; | ||||||
| @@ -192,14 +189,6 @@ export class AssetController { | |||||||
|     return this.assetService.getAssetById(authUser, id); |     return this.assetService.getAssetById(authUser, id); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Delete('/') |  | ||||||
|   deleteAsset( |  | ||||||
|     @AuthUser() authUser: AuthUserDto, |  | ||||||
|     @Body(ValidationPipe) dto: DeleteAssetDto, |  | ||||||
|   ): Promise<DeleteAssetResponseDto[]> { |  | ||||||
|     return this.assetService.deleteAll(authUser, dto); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Check duplicated asset before uploading - for Web upload used |    * Check duplicated asset before uploading - for Web upload used | ||||||
|    */ |    */ | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ export class AssetCore { | |||||||
|       fileCreatedAt: dto.fileCreatedAt, |       fileCreatedAt: dto.fileCreatedAt, | ||||||
|       fileModifiedAt: dto.fileModifiedAt, |       fileModifiedAt: dto.fileModifiedAt, | ||||||
|       localDateTime: dto.fileCreatedAt, |       localDateTime: dto.fileCreatedAt, | ||||||
|  |       deletedAt: null, | ||||||
|  |  | ||||||
|       type: mimeTypes.assetType(file.originalPath), |       type: mimeTypes.assetType(file.originalPath), | ||||||
|       isFavorite: dto.isFavorite, |       isFavorite: dto.isFavorite, | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ import { | |||||||
|   assetStub, |   assetStub, | ||||||
|   authStub, |   authStub, | ||||||
|   fileStub, |   fileStub, | ||||||
|   libraryStub, |  | ||||||
|   newAccessRepositoryMock, |   newAccessRepositoryMock, | ||||||
|   newCryptoRepositoryMock, |   newCryptoRepositoryMock, | ||||||
|   newJobRepositoryMock, |   newJobRepositoryMock, | ||||||
| @@ -98,7 +97,6 @@ describe('AssetService', () => { | |||||||
|     assetRepositoryMock = { |     assetRepositoryMock = { | ||||||
|       get: jest.fn(), |       get: jest.fn(), | ||||||
|       create: jest.fn(), |       create: jest.fn(), | ||||||
|       remove: jest.fn(), |  | ||||||
|  |  | ||||||
|       getAllByUserId: jest.fn(), |       getAllByUserId: jest.fn(), | ||||||
|       getAllByDeviceId: jest.fn(), |       getAllByDeviceId: jest.fn(), | ||||||
| @@ -212,132 +210,6 @@ describe('AssetService', () => { | |||||||
|     expect(result).toEqual(assets.map((asset) => asset.deviceAssetId)); |     expect(result).toEqual(assets.map((asset) => asset.deviceAssetId)); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   describe('deleteAll', () => { |  | ||||||
|     it('should return failed status when an asset is missing', async () => { |  | ||||||
|       assetRepositoryMock.get.mockResolvedValue(null); |  | ||||||
|       accessMock.asset.hasOwnerAccess.mockResolvedValue(true); |  | ||||||
|  |  | ||||||
|       await expect(sut.deleteAll(authStub.user1, { ids: ['asset1'] })).resolves.toEqual([ |  | ||||||
|         { id: 'asset1', status: 'FAILED' }, |  | ||||||
|       ]); |  | ||||||
|  |  | ||||||
|       expect(jobMock.queue).not.toHaveBeenCalled(); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should return failed status a delete fails', async () => { |  | ||||||
|       assetRepositoryMock.get.mockResolvedValue({ |  | ||||||
|         id: 'asset1', |  | ||||||
|         library: libraryStub.uploadLibrary1, |  | ||||||
|       } as AssetEntity); |  | ||||||
|       assetRepositoryMock.remove.mockRejectedValue('delete failed'); |  | ||||||
|       accessMock.asset.hasOwnerAccess.mockResolvedValue(true); |  | ||||||
|  |  | ||||||
|       await expect(sut.deleteAll(authStub.user1, { ids: ['asset1'] })).resolves.toEqual([ |  | ||||||
|         { id: 'asset1', status: 'FAILED' }, |  | ||||||
|       ]); |  | ||||||
|  |  | ||||||
|       expect(jobMock.queue).not.toHaveBeenCalled(); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should delete a live photo', async () => { |  | ||||||
|       accessMock.asset.hasOwnerAccess.mockResolvedValue(true); |  | ||||||
|  |  | ||||||
|       await expect(sut.deleteAll(authStub.user1, { ids: [assetStub.livePhotoStillAsset.id] })).resolves.toEqual([ |  | ||||||
|         { id: assetStub.livePhotoStillAsset.id, status: 'SUCCESS' }, |  | ||||||
|         { id: assetStub.livePhotoMotionAsset.id, status: 'SUCCESS' }, |  | ||||||
|       ]); |  | ||||||
|  |  | ||||||
|       expect(jobMock.queue).toHaveBeenCalledWith({ |  | ||||||
|         name: JobName.DELETE_FILES, |  | ||||||
|         data: { |  | ||||||
|           files: [ |  | ||||||
|             'fake_path/asset_1.jpeg', |  | ||||||
|             undefined, |  | ||||||
|             undefined, |  | ||||||
|             undefined, |  | ||||||
|             undefined, |  | ||||||
|             'fake_path/asset_1.mp4', |  | ||||||
|             undefined, |  | ||||||
|             undefined, |  | ||||||
|             undefined, |  | ||||||
|             undefined, |  | ||||||
|           ], |  | ||||||
|         }, |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should delete a batch of assets', async () => { |  | ||||||
|       const asset1 = { |  | ||||||
|         id: 'asset1', |  | ||||||
|         originalPath: 'original-path-1', |  | ||||||
|         resizePath: 'resize-path-1', |  | ||||||
|         webpPath: 'web-path-1', |  | ||||||
|         library: libraryStub.uploadLibrary1, |  | ||||||
|       }; |  | ||||||
|  |  | ||||||
|       const asset2 = { |  | ||||||
|         id: 'asset2', |  | ||||||
|         originalPath: 'original-path-2', |  | ||||||
|         resizePath: 'resize-path-2', |  | ||||||
|         webpPath: 'web-path-2', |  | ||||||
|         encodedVideoPath: 'encoded-video-path-2', |  | ||||||
|         library: libraryStub.uploadLibrary1, |  | ||||||
|       }; |  | ||||||
|  |  | ||||||
|       // Can't be deleted since it's external |  | ||||||
|       const asset3 = { |  | ||||||
|         id: 'asset3', |  | ||||||
|         originalPath: 'original-path-3', |  | ||||||
|         resizePath: 'resize-path-3', |  | ||||||
|         webpPath: 'web-path-3', |  | ||||||
|         encodedVideoPath: 'encoded-video-path-2', |  | ||||||
|         library: libraryStub.externalLibrary1, |  | ||||||
|       }; |  | ||||||
|  |  | ||||||
|       when(assetRepositoryMock.get) |  | ||||||
|         .calledWith(asset1.id) |  | ||||||
|         .mockResolvedValue(asset1 as AssetEntity); |  | ||||||
|       when(assetRepositoryMock.get) |  | ||||||
|         .calledWith(asset2.id) |  | ||||||
|         .mockResolvedValue(asset2 as AssetEntity); |  | ||||||
|       when(assetRepositoryMock.get) |  | ||||||
|         .calledWith(asset3.id) |  | ||||||
|         .mockResolvedValue(asset3 as AssetEntity); |  | ||||||
|  |  | ||||||
|       accessMock.asset.hasOwnerAccess.mockResolvedValue(true); |  | ||||||
|  |  | ||||||
|       await expect(sut.deleteAll(authStub.user1, { ids: ['asset1', 'asset2', 'asset3'] })).resolves.toEqual([ |  | ||||||
|         { id: 'asset1', status: 'SUCCESS' }, |  | ||||||
|         { id: 'asset2', status: 'SUCCESS' }, |  | ||||||
|         { id: 'asset3', status: 'FAILED' }, |  | ||||||
|       ]); |  | ||||||
|  |  | ||||||
|       expect(jobMock.queue.mock.calls).toEqual([ |  | ||||||
|         [{ name: JobName.SEARCH_REMOVE_ASSET, data: { ids: ['asset1'] } }], |  | ||||||
|         [{ name: JobName.SEARCH_REMOVE_ASSET, data: { ids: ['asset2'] } }], |  | ||||||
|         [ |  | ||||||
|           { |  | ||||||
|             name: JobName.DELETE_FILES, |  | ||||||
|             data: { |  | ||||||
|               files: [ |  | ||||||
|                 'original-path-1', |  | ||||||
|                 'web-path-1', |  | ||||||
|                 'resize-path-1', |  | ||||||
|                 undefined, |  | ||||||
|                 undefined, |  | ||||||
|                 'original-path-2', |  | ||||||
|                 'web-path-2', |  | ||||||
|                 'resize-path-2', |  | ||||||
|                 'encoded-video-path-2', |  | ||||||
|                 undefined, |  | ||||||
|               ], |  | ||||||
|             }, |  | ||||||
|           }, |  | ||||||
|         ], |  | ||||||
|       ]); |  | ||||||
|     }); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   describe('bulkUploadCheck', () => { |   describe('bulkUploadCheck', () => { | ||||||
|     it('should accept hex and base64 checksums', async () => { |     it('should accept hex and base64 checksums', async () => { | ||||||
|       const file1 = Buffer.from('d2947b871a706081be194569951b7db246907957', 'hex'); |       const file1 = Buffer.from('d2947b871a706081be194569951b7db246907957', 'hex'); | ||||||
|   | |||||||
| @@ -37,7 +37,6 @@ import { AssetSearchDto } from './dto/asset-search.dto'; | |||||||
| import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto'; | import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto'; | ||||||
| import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; | import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; | ||||||
| import { CreateAssetDto, ImportAssetDto } from './dto/create-asset.dto'; | import { CreateAssetDto, ImportAssetDto } from './dto/create-asset.dto'; | ||||||
| import { DeleteAssetDto } from './dto/delete-asset.dto'; |  | ||||||
| import { GetAssetThumbnailDto, GetAssetThumbnailFormatEnum } from './dto/get-asset-thumbnail.dto'; | import { GetAssetThumbnailDto, GetAssetThumbnailFormatEnum } from './dto/get-asset-thumbnail.dto'; | ||||||
| import { SearchAssetDto } from './dto/search-asset.dto'; | import { SearchAssetDto } from './dto/search-asset.dto'; | ||||||
| import { SearchPropertiesDto } from './dto/search-properties.dto'; | import { SearchPropertiesDto } from './dto/search-properties.dto'; | ||||||
| @@ -52,7 +51,6 @@ import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-a | |||||||
| import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto'; | import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto'; | ||||||
| import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto'; | import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto'; | ||||||
| import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto'; | import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto'; | ||||||
| import { DeleteAssetResponseDto, DeleteAssetStatusEnum } from './response-dto/delete-asset-response.dto'; |  | ||||||
|  |  | ||||||
| @Injectable() | @Injectable() | ||||||
| export class AssetService { | export class AssetService { | ||||||
| @@ -246,66 +244,6 @@ export class AssetService { | |||||||
|     await this.sendFile(res, filepath); |     await this.sendFile(res, filepath); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public async deleteAll(authUser: AuthUserDto, dto: DeleteAssetDto): Promise<DeleteAssetResponseDto[]> { |  | ||||||
|     const deleteQueue: Array<string | null> = []; |  | ||||||
|     const result: DeleteAssetResponseDto[] = []; |  | ||||||
|  |  | ||||||
|     const ids = dto.ids.slice(); |  | ||||||
|     for (const id of ids) { |  | ||||||
|       const hasAccess = await this.access.hasPermission(authUser, Permission.ASSET_DELETE, id); |  | ||||||
|       if (!hasAccess) { |  | ||||||
|         result.push({ id, status: DeleteAssetStatusEnum.FAILED }); |  | ||||||
|         continue; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       const asset = await this._assetRepository.get(id); |  | ||||||
|       if (!asset || !asset.library || asset.library.type === LibraryType.EXTERNAL) { |  | ||||||
|         // We don't allow deletions assets belong to an external library |  | ||||||
|         result.push({ id, status: DeleteAssetStatusEnum.FAILED }); |  | ||||||
|         continue; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       try { |  | ||||||
|         if (asset.faces) { |  | ||||||
|           await Promise.all( |  | ||||||
|             asset.faces.map(({ assetId, personId }) => |  | ||||||
|               this.jobRepository.queue({ name: JobName.SEARCH_REMOVE_FACE, data: { assetId, personId } }), |  | ||||||
|             ), |  | ||||||
|           ); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         await this._assetRepository.remove(asset); |  | ||||||
|         await this.jobRepository.queue({ name: JobName.SEARCH_REMOVE_ASSET, data: { ids: [id] } }); |  | ||||||
|  |  | ||||||
|         result.push({ id, status: DeleteAssetStatusEnum.SUCCESS }); |  | ||||||
|  |  | ||||||
|         if (!asset.isReadOnly) { |  | ||||||
|           deleteQueue.push( |  | ||||||
|             asset.originalPath, |  | ||||||
|             asset.webpPath, |  | ||||||
|             asset.resizePath, |  | ||||||
|             asset.encodedVideoPath, |  | ||||||
|             asset.sidecarPath, |  | ||||||
|           ); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // TODO refactor this to use cascades |  | ||||||
|         if (asset.livePhotoVideoId && !ids.includes(asset.livePhotoVideoId)) { |  | ||||||
|           ids.push(asset.livePhotoVideoId); |  | ||||||
|         } |  | ||||||
|       } catch (error) { |  | ||||||
|         this.logger.error(`Error deleting asset ${id}`, error); |  | ||||||
|         result.push({ id, status: DeleteAssetStatusEnum.FAILED }); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (deleteQueue.length > 0) { |  | ||||||
|       await this.jobRepository.queue({ name: JobName.DELETE_FILES, data: { files: deleteQueue } }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return result; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   async getAssetSearchTerm(authUser: AuthUserDto): Promise<string[]> { |   async getAssetSearchTerm(authUser: AuthUserDto): Promise<string[]> { | ||||||
|     const possibleSearchTerm = new Set<string>(); |     const possibleSearchTerm = new Set<string>(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,17 +0,0 @@ | |||||||
| import { ApiProperty } from '@nestjs/swagger'; |  | ||||||
| import { IsNotEmpty } from 'class-validator'; |  | ||||||
|  |  | ||||||
| export class DeleteAssetDto { |  | ||||||
|   @IsNotEmpty() |  | ||||||
|   @ApiProperty({ |  | ||||||
|     isArray: true, |  | ||||||
|     type: String, |  | ||||||
|     title: 'Array of asset IDs to delete', |  | ||||||
|     example: [ |  | ||||||
|       'bf973405-3f2a-48d2-a687-2ed4167164be', |  | ||||||
|       'dd41870b-5d00-46d2-924e-1d8489a0aa0f', |  | ||||||
|       'fad77c3f-deef-4e7e-9608-14c1aa4e559a', |  | ||||||
|     ], |  | ||||||
|   }) |  | ||||||
|   ids!: string[]; |  | ||||||
| } |  | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| import { ApiProperty } from '@nestjs/swagger'; |  | ||||||
|  |  | ||||||
| export enum DeleteAssetStatusEnum { |  | ||||||
|   SUCCESS = 'SUCCESS', |  | ||||||
|   FAILED = 'FAILED', |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export class DeleteAssetResponseDto { |  | ||||||
|   id!: string; |  | ||||||
|  |  | ||||||
|   @ApiProperty({ type: 'string', enum: DeleteAssetStatusEnum, enumName: 'DeleteAssetStatus' }) |  | ||||||
|   status!: DeleteAssetStatusEnum; |  | ||||||
| } |  | ||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { | import { | ||||||
|  |   AssetBulkDeleteDto, | ||||||
|   AssetBulkUpdateDto, |   AssetBulkUpdateDto, | ||||||
|   AssetIdsDto, |   AssetIdsDto, | ||||||
|   AssetJobsDto, |   AssetJobsDto, | ||||||
| @@ -7,6 +8,7 @@ import { | |||||||
|   AssetStatsDto, |   AssetStatsDto, | ||||||
|   AssetStatsResponseDto, |   AssetStatsResponseDto, | ||||||
|   AuthUserDto, |   AuthUserDto, | ||||||
|  |   BulkIdsDto, | ||||||
|   DownloadInfoDto, |   DownloadInfoDto, | ||||||
|   DownloadResponseDto, |   DownloadResponseDto, | ||||||
|   MapMarkerDto, |   MapMarkerDto, | ||||||
| @@ -17,9 +19,22 @@ import { | |||||||
|   TimeBucketAssetDto, |   TimeBucketAssetDto, | ||||||
|   TimeBucketDto, |   TimeBucketDto, | ||||||
|   TimeBucketResponseDto, |   TimeBucketResponseDto, | ||||||
|  |   TrashAction, | ||||||
|   UpdateAssetDto as UpdateDto, |   UpdateAssetDto as UpdateDto, | ||||||
| } from '@app/domain'; | } from '@app/domain'; | ||||||
| import { Body, Controller, Get, HttpCode, HttpStatus, Param, Post, Put, Query, StreamableFile } from '@nestjs/common'; | import { | ||||||
|  |   Body, | ||||||
|  |   Controller, | ||||||
|  |   Delete, | ||||||
|  |   Get, | ||||||
|  |   HttpCode, | ||||||
|  |   HttpStatus, | ||||||
|  |   Param, | ||||||
|  |   Post, | ||||||
|  |   Put, | ||||||
|  |   Query, | ||||||
|  |   StreamableFile, | ||||||
|  | } from '@nestjs/common'; | ||||||
| import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; | import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; | ||||||
| import { AuthUser, Authenticated, SharedLinkRoute } from '../app.guard'; | import { AuthUser, Authenticated, SharedLinkRoute } from '../app.guard'; | ||||||
| import { UseValidation, asStreamableFile } from '../app.utils'; | import { UseValidation, asStreamableFile } from '../app.utils'; | ||||||
| @@ -98,6 +113,30 @@ export class AssetController { | |||||||
|     return this.service.updateAll(authUser, dto); |     return this.service.updateAll(authUser, dto); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   @Delete() | ||||||
|  |   @HttpCode(HttpStatus.NO_CONTENT) | ||||||
|  |   deleteAssets(@AuthUser() authUser: AuthUserDto, @Body() dto: AssetBulkDeleteDto): Promise<void> { | ||||||
|  |     return this.service.deleteAll(authUser, dto); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Post('restore') | ||||||
|  |   @HttpCode(HttpStatus.NO_CONTENT) | ||||||
|  |   restoreAssets(@AuthUser() authUser: AuthUserDto, @Body() dto: BulkIdsDto): Promise<void> { | ||||||
|  |     return this.service.restoreAll(authUser, dto); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Post('trash/empty') | ||||||
|  |   @HttpCode(HttpStatus.NO_CONTENT) | ||||||
|  |   emptyTrash(@AuthUser() authUser: AuthUserDto): Promise<void> { | ||||||
|  |     return this.service.handleTrashAction(authUser, TrashAction.EMPTY_ALL); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Post('trash/restore') | ||||||
|  |   @HttpCode(HttpStatus.NO_CONTENT) | ||||||
|  |   restoreTrash(@AuthUser() authUser: AuthUserDto): Promise<void> { | ||||||
|  |     return this.service.handleTrashAction(authUser, TrashAction.RESTORE_ALL); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   @Put(':id') |   @Put(':id') | ||||||
|   updateAsset( |   updateAsset( | ||||||
|     @AuthUser() authUser: AuthUserDto, |     @AuthUser() authUser: AuthUserDto, | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import { | import { | ||||||
|   Column, |   Column, | ||||||
|   CreateDateColumn, |   CreateDateColumn, | ||||||
|  |   DeleteDateColumn, | ||||||
|   Entity, |   Entity, | ||||||
|   Index, |   Index, | ||||||
|   JoinColumn, |   JoinColumn, | ||||||
| @@ -77,6 +78,9 @@ export class AssetEntity { | |||||||
|   @UpdateDateColumn({ type: 'timestamptz' }) |   @UpdateDateColumn({ type: 'timestamptz' }) | ||||||
|   updatedAt!: Date; |   updatedAt!: Date; | ||||||
|  |  | ||||||
|  |   @DeleteDateColumn({ type: 'timestamptz', nullable: true }) | ||||||
|  |   deletedAt!: Date | null; | ||||||
|  |  | ||||||
|   @Column({ type: 'timestamptz' }) |   @Column({ type: 'timestamptz' }) | ||||||
|   fileCreatedAt!: Date; |   fileCreatedAt!: Date; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -87,6 +87,9 @@ export enum SystemConfigKey { | |||||||
|   THUMBNAIL_JPEG_SIZE = 'thumbnail.jpegSize', |   THUMBNAIL_JPEG_SIZE = 'thumbnail.jpegSize', | ||||||
|   THUMBNAIL_QUALITY = 'thumbnail.quality', |   THUMBNAIL_QUALITY = 'thumbnail.quality', | ||||||
|   THUMBNAIL_COLORSPACE = 'thumbnail.colorspace', |   THUMBNAIL_COLORSPACE = 'thumbnail.colorspace', | ||||||
|  |  | ||||||
|  |   TRASH_ENABLED = 'trash.enabled', | ||||||
|  |   TRASH_DAYS = 'trash.days', | ||||||
| } | } | ||||||
|  |  | ||||||
| export enum TranscodePolicy { | export enum TranscodePolicy { | ||||||
| @@ -214,4 +217,8 @@ export interface SystemConfig { | |||||||
|     quality: number; |     quality: number; | ||||||
|     colorspace: Colorspace; |     colorspace: Colorspace; | ||||||
|   }; |   }; | ||||||
|  |   trash: { | ||||||
|  |     enabled: boolean; | ||||||
|  |     days: number; | ||||||
|  |   }; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,14 @@ | |||||||
|  | import { MigrationInterface, QueryRunner } from "typeorm"; | ||||||
|  |  | ||||||
|  | export class AddAssetDeletedAtColumn1694204416744 implements MigrationInterface { | ||||||
|  |     name = 'AddAssetDeletedAtColumn1694204416744' | ||||||
|  |  | ||||||
|  |     public async up(queryRunner: QueryRunner): Promise<void> { | ||||||
|  |         await queryRunner.query(`ALTER TABLE "assets" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public async down(queryRunner: QueryRunner): Promise<void> { | ||||||
|  |         await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "deletedAt"`); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -86,6 +86,7 @@ export class AccessRepository implements IAccessRepository { | |||||||
|           id: assetId, |           id: assetId, | ||||||
|           ownerId: userId, |           ownerId: userId, | ||||||
|         }, |         }, | ||||||
|  |         withDeleted: true, | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ import { | |||||||
| import { Injectable } from '@nestjs/common'; | import { Injectable } from '@nestjs/common'; | ||||||
| import { InjectRepository } from '@nestjs/typeorm'; | import { InjectRepository } from '@nestjs/typeorm'; | ||||||
| import { DateTime } from 'luxon'; | import { DateTime } from 'luxon'; | ||||||
| import { FindOptionsRelations, FindOptionsWhere, In, IsNull, Not, Repository } from 'typeorm'; | import { And, FindOptionsRelations, FindOptionsWhere, In, IsNull, LessThan, Not, Repository } from 'typeorm'; | ||||||
| import { AssetEntity, AssetType, ExifEntity } from '../entities'; | import { AssetEntity, AssetType, ExifEntity } from '../entities'; | ||||||
| import OptionalBetween from '../utils/optional-between.util'; | import OptionalBetween from '../utils/optional-between.util'; | ||||||
| import { paginate } from '../utils/pagination.util'; | import { paginate } from '../utils/pagination.util'; | ||||||
| @@ -109,6 +109,7 @@ export class AssetRepository implements IAssetRepository { | |||||||
|           person: true, |           person: true, | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|  |       withDeleted: true, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -130,15 +131,17 @@ export class AssetRepository implements IAssetRepository { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   getByUserId(pagination: PaginationOptions, userId: string): Paginated<AssetEntity> { |   getByUserId(pagination: PaginationOptions, userId: string, options: AssetSearchOptions = {}): Paginated<AssetEntity> { | ||||||
|     return paginate(this.repository, pagination, { |     return paginate(this.repository, pagination, { | ||||||
|       where: { |       where: { | ||||||
|         ownerId: userId, |         ownerId: userId, | ||||||
|         isVisible: true, |         isVisible: options.isVisible, | ||||||
|  |         deletedAt: options.trashedBefore ? And(Not(IsNull()), LessThan(options.trashedBefore)) : undefined, | ||||||
|       }, |       }, | ||||||
|       relations: { |       relations: { | ||||||
|         exifInfo: true, |         exifInfo: true, | ||||||
|       }, |       }, | ||||||
|  |       withDeleted: !!options.trashedBefore, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -154,32 +157,12 @@ export class AssetRepository implements IAssetRepository { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   getById(assetId: string): Promise<AssetEntity> { |  | ||||||
|     return this.repository.findOneOrFail({ |  | ||||||
|       where: { |  | ||||||
|         id: assetId, |  | ||||||
|       }, |  | ||||||
|       relations: { |  | ||||||
|         exifInfo: true, |  | ||||||
|         tags: true, |  | ||||||
|         sharedLinks: true, |  | ||||||
|         smartInfo: true, |  | ||||||
|         faces: { |  | ||||||
|           person: true, |  | ||||||
|         }, |  | ||||||
|       }, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   remove(asset: AssetEntity): Promise<AssetEntity> { |  | ||||||
|     return this.repository.remove(asset); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   getAll(pagination: PaginationOptions, options: AssetSearchOptions = {}): Paginated<AssetEntity> { |   getAll(pagination: PaginationOptions, options: AssetSearchOptions = {}): Paginated<AssetEntity> { | ||||||
|     return paginate(this.repository, pagination, { |     return paginate(this.repository, pagination, { | ||||||
|       where: { |       where: { | ||||||
|         isVisible: options.isVisible, |         isVisible: options.isVisible, | ||||||
|         type: options.type, |         type: options.type, | ||||||
|  |         deletedAt: options.trashedBefore ? And(Not(IsNull()), LessThan(options.trashedBefore)) : undefined, | ||||||
|       }, |       }, | ||||||
|       relations: { |       relations: { | ||||||
|         exifInfo: true, |         exifInfo: true, | ||||||
| @@ -189,6 +172,7 @@ export class AssetRepository implements IAssetRepository { | |||||||
|           person: true, |           person: true, | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|  |       withDeleted: !!options.trashedBefore, | ||||||
|       order: { |       order: { | ||||||
|         // Ensures correct order when paginating |         // Ensures correct order when paginating | ||||||
|         createdAt: options.order ?? 'ASC', |         createdAt: options.order ?? 'ASC', | ||||||
| @@ -196,10 +180,32 @@ export class AssetRepository implements IAssetRepository { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   getById(id: string): Promise<AssetEntity | null> { | ||||||
|  |     return this.repository.findOne({ | ||||||
|  |       where: { id }, | ||||||
|  |       relations: { | ||||||
|  |         faces: { | ||||||
|  |           person: true, | ||||||
|  |         }, | ||||||
|  |         library: true, | ||||||
|  |       }, | ||||||
|  |       // We are specifically asking for this asset. Return it even if it is soft deleted | ||||||
|  |       withDeleted: true, | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   async updateAll(ids: string[], options: Partial<AssetEntity>): Promise<void> { |   async updateAll(ids: string[], options: Partial<AssetEntity>): Promise<void> { | ||||||
|     await this.repository.update({ id: In(ids) }, options); |     await this.repository.update({ id: In(ids) }, options); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   async softDeleteAll(ids: string[]): Promise<void> { | ||||||
|  |     await this.repository.softDelete({ id: In(ids), isExternal: false }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async restoreAll(ids: string[]): Promise<void> { | ||||||
|  |     await this.repository.restore({ id: In(ids) }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   async save(asset: Partial<AssetEntity>): Promise<AssetEntity> { |   async save(asset: Partial<AssetEntity>): Promise<AssetEntity> { | ||||||
|     const { id } = await this.repository.save(asset); |     const { id } = await this.repository.save(asset); | ||||||
|     return this.repository.findOneOrFail({ |     return this.repository.findOneOrFail({ | ||||||
| @@ -213,9 +219,14 @@ export class AssetRepository implements IAssetRepository { | |||||||
|           person: true, |           person: true, | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|  |       withDeleted: true, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   async remove(asset: AssetEntity): Promise<void> { | ||||||
|  |     await this.repository.remove(asset); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   getByChecksum(userId: string, checksum: Buffer): Promise<AssetEntity | null> { |   getByChecksum(userId: string, checksum: Buffer): Promise<AssetEntity | null> { | ||||||
|     return this.repository.findOne({ where: { ownerId: userId, checksum } }); |     return this.repository.findOne({ where: { ownerId: userId, checksum } }); | ||||||
|   } |   } | ||||||
| @@ -424,7 +435,7 @@ export class AssetRepository implements IAssetRepository { | |||||||
|       .andWhere('asset.isVisible = true') |       .andWhere('asset.isVisible = true') | ||||||
|       .groupBy('asset.type'); |       .groupBy('asset.type'); | ||||||
|  |  | ||||||
|     const { isArchived, isFavorite } = options; |     const { isArchived, isFavorite, isTrashed } = options; | ||||||
|     if (isArchived !== undefined) { |     if (isArchived !== undefined) { | ||||||
|       builder = builder.andWhere(`asset.isArchived = :isArchived`, { isArchived }); |       builder = builder.andWhere(`asset.isArchived = :isArchived`, { isArchived }); | ||||||
|     } |     } | ||||||
| @@ -433,6 +444,10 @@ export class AssetRepository implements IAssetRepository { | |||||||
|       builder = builder.andWhere(`asset.isFavorite = :isFavorite`, { isFavorite }); |       builder = builder.andWhere(`asset.isFavorite = :isFavorite`, { isFavorite }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (isTrashed !== undefined) { | ||||||
|  |       builder = builder.withDeleted().andWhere(`asset.deletedAt is not null`); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const items = await builder.getRawMany(); |     const items = await builder.getRawMany(); | ||||||
|  |  | ||||||
|     const result: AssetStats = { |     const result: AssetStats = { | ||||||
| @@ -481,7 +496,7 @@ export class AssetRepository implements IAssetRepository { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private getBuilder(options: TimeBucketOptions) { |   private getBuilder(options: TimeBucketOptions) { | ||||||
|     const { isArchived, isFavorite, albumId, personId, userId } = options; |     const { isArchived, isFavorite, isTrashed, albumId, personId, userId } = options; | ||||||
|  |  | ||||||
|     let builder = this.repository |     let builder = this.repository | ||||||
|       .createQueryBuilder('asset') |       .createQueryBuilder('asset') | ||||||
| @@ -504,6 +519,10 @@ export class AssetRepository implements IAssetRepository { | |||||||
|       builder = builder.andWhere('asset.isFavorite = :isFavorite', { isFavorite }); |       builder = builder.andWhere('asset.isFavorite = :isFavorite', { isFavorite }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (isTrashed !== undefined) { | ||||||
|  |       builder = builder.andWhere(`asset.deletedAt ${isTrashed ? 'IS NOT NULL' : 'IS NULL'}`).withDeleted(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (personId !== undefined) { |     if (personId !== undefined) { | ||||||
|       builder = builder |       builder = builder | ||||||
|         .innerJoin('asset.faces', 'faces') |         .innerJoin('asset.faces', 'faces') | ||||||
|   | |||||||
| @@ -9,4 +9,8 @@ export class CommunicationRepository { | |||||||
|   send(event: CommunicationEvent, userId: string, data: any) { |   send(event: CommunicationEvent, userId: string, data: any) { | ||||||
|     this.ws.server.to(userId).emit(event, JSON.stringify(data)); |     this.ws.server.to(userId).emit(event, JSON.stringify(data)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   broadcast(event: CommunicationEvent, data: any) { | ||||||
|  |     this.ws.server.emit(event, data); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -67,6 +67,7 @@ export class PersonRepository implements IPersonRepository { | |||||||
|       .createQueryBuilder('person') |       .createQueryBuilder('person') | ||||||
|       .leftJoin('person.faces', 'face') |       .leftJoin('person.faces', 'face') | ||||||
|       .where('person.ownerId = :userId', { userId }) |       .where('person.ownerId = :userId', { userId }) | ||||||
|  |       .innerJoin('face.asset', 'asset') | ||||||
|       .orderBy('person.isHidden', 'ASC') |       .orderBy('person.isHidden', 'ASC') | ||||||
|       .addOrderBy("NULLIF(person.name, '') IS NULL", 'ASC') |       .addOrderBy("NULLIF(person.name, '') IS NULL", 'ASC') | ||||||
|       .addOrderBy('COUNT(face.assetId)', 'DESC') |       .addOrderBy('COUNT(face.assetId)', 'DESC') | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { | import { | ||||||
|  |   AssetService, | ||||||
|   AuditService, |   AuditService, | ||||||
|   IDeleteFilesJob, |   IDeleteFilesJob, | ||||||
|   JobName, |   JobName, | ||||||
| @@ -23,6 +24,7 @@ export class AppService { | |||||||
|  |  | ||||||
|   constructor( |   constructor( | ||||||
|     private jobService: JobService, |     private jobService: JobService, | ||||||
|  |     private assetService: AssetService, | ||||||
|     private mediaService: MediaService, |     private mediaService: MediaService, | ||||||
|     private metadataService: MetadataService, |     private metadataService: MetadataService, | ||||||
|     private personService: PersonService, |     private personService: PersonService, | ||||||
| @@ -38,6 +40,8 @@ export class AppService { | |||||||
|  |  | ||||||
|   async init() { |   async init() { | ||||||
|     await this.jobService.registerHandlers({ |     await this.jobService.registerHandlers({ | ||||||
|  |       [JobName.ASSET_DELETION]: (data) => this.assetService.handleAssetDeletion(data), | ||||||
|  |       [JobName.ASSET_DELETION_CHECK]: () => this.assetService.handleAssetDeletionCheck(), | ||||||
|       [JobName.DELETE_FILES]: (data: IDeleteFilesJob) => this.storageService.handleDeleteFiles(data), |       [JobName.DELETE_FILES]: (data: IDeleteFilesJob) => this.storageService.handleDeleteFiles(data), | ||||||
|       [JobName.CLEAN_OLD_AUDIT_LOGS]: () => this.auditService.handleCleanup(), |       [JobName.CLEAN_OLD_AUDIT_LOGS]: () => this.auditService.handleCleanup(), | ||||||
|       [JobName.USER_DELETE_CHECK]: () => this.userService.handleUserDeleteCheck(), |       [JobName.USER_DELETE_CHECK]: () => this.userService.handleUserDeleteCheck(), | ||||||
|   | |||||||
| @@ -92,6 +92,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => { | |||||||
|         search: false, |         search: false, | ||||||
|         sidecar: true, |         sidecar: true, | ||||||
|         tagImage: true, |         tagImage: true, | ||||||
|  |         trash: true, | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| @@ -104,6 +105,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => { | |||||||
|         loginPageMessage: '', |         loginPageMessage: '', | ||||||
|         oauthButtonText: 'Login with OAuth', |         oauthButtonText: 'Login with OAuth', | ||||||
|         mapTileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', |         mapTileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', | ||||||
|  |         trashDays: 30, | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								server/test/fixtures/asset.stub.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								server/test/fixtures/asset.stub.ts
									
									
									
									
										vendored
									
									
								
							| @@ -35,6 +35,7 @@ export const assetStub = { | |||||||
|     faces: [], |     faces: [], | ||||||
|     sidecarPath: null, |     sidecarPath: null, | ||||||
|     isReadOnly: false, |     isReadOnly: false, | ||||||
|  |     deletedAt: null, | ||||||
|     isOffline: false, |     isOffline: false, | ||||||
|     isExternal: false, |     isExternal: false, | ||||||
|     libraryId: 'library-id', |     libraryId: 'library-id', | ||||||
| @@ -77,6 +78,7 @@ export const assetStub = { | |||||||
|     exifInfo: { |     exifInfo: { | ||||||
|       fileSizeInByte: 123_000, |       fileSizeInByte: 123_000, | ||||||
|     } as ExifEntity, |     } as ExifEntity, | ||||||
|  |     deletedAt: null, | ||||||
|   }), |   }), | ||||||
|   noThumbhash: Object.freeze<AssetEntity>({ |   noThumbhash: Object.freeze<AssetEntity>({ | ||||||
|     id: 'asset-id', |     id: 'asset-id', | ||||||
| @@ -112,6 +114,7 @@ export const assetStub = { | |||||||
|     originalFileName: 'asset-id.ext', |     originalFileName: 'asset-id.ext', | ||||||
|     faces: [], |     faces: [], | ||||||
|     sidecarPath: null, |     sidecarPath: null, | ||||||
|  |     deletedAt: null, | ||||||
|   }), |   }), | ||||||
|   image: Object.freeze<AssetEntity>({ |   image: Object.freeze<AssetEntity>({ | ||||||
|     id: 'asset-id', |     id: 'asset-id', | ||||||
| @@ -146,6 +149,7 @@ export const assetStub = { | |||||||
|     sharedLinks: [], |     sharedLinks: [], | ||||||
|     originalFileName: 'asset-id.jpg', |     originalFileName: 'asset-id.jpg', | ||||||
|     faces: [], |     faces: [], | ||||||
|  |     deletedAt: null, | ||||||
|     sidecarPath: null, |     sidecarPath: null, | ||||||
|     exifInfo: { |     exifInfo: { | ||||||
|       fileSizeInByte: 5_000, |       fileSizeInByte: 5_000, | ||||||
| @@ -179,11 +183,12 @@ export const assetStub = { | |||||||
|     livePhotoVideoId: null, |     livePhotoVideoId: null, | ||||||
|     isOffline: false, |     isOffline: false, | ||||||
|     libraryId: 'library-id', |     libraryId: 'library-id', | ||||||
|     library: libraryStub.uploadLibrary1, |     library: libraryStub.externalLibrary1, | ||||||
|     tags: [], |     tags: [], | ||||||
|     sharedLinks: [], |     sharedLinks: [], | ||||||
|     originalFileName: 'asset-id.jpg', |     originalFileName: 'asset-id.jpg', | ||||||
|     faces: [], |     faces: [], | ||||||
|  |     deletedAt: null, | ||||||
|     sidecarPath: null, |     sidecarPath: null, | ||||||
|     exifInfo: { |     exifInfo: { | ||||||
|       fileSizeInByte: 5_000, |       fileSizeInByte: 5_000, | ||||||
| @@ -226,6 +231,7 @@ export const assetStub = { | |||||||
|     exifInfo: { |     exifInfo: { | ||||||
|       fileSizeInByte: 5_000, |       fileSizeInByte: 5_000, | ||||||
|     } as ExifEntity, |     } as ExifEntity, | ||||||
|  |     deletedAt: null, | ||||||
|   }), |   }), | ||||||
|   image1: Object.freeze<AssetEntity>({ |   image1: Object.freeze<AssetEntity>({ | ||||||
|     id: 'asset-id-1', |     id: 'asset-id-1', | ||||||
| @@ -244,6 +250,7 @@ export const assetStub = { | |||||||
|     encodedVideoPath: null, |     encodedVideoPath: null, | ||||||
|     createdAt: new Date('2023-02-23T05:06:29.716Z'), |     createdAt: new Date('2023-02-23T05:06:29.716Z'), | ||||||
|     updatedAt: new Date('2023-02-23T05:06:29.716Z'), |     updatedAt: new Date('2023-02-23T05:06:29.716Z'), | ||||||
|  |     deletedAt: null, | ||||||
|     localDateTime: new Date('2023-02-23T05:06:29.716Z'), |     localDateTime: new Date('2023-02-23T05:06:29.716Z'), | ||||||
|     isFavorite: true, |     isFavorite: true, | ||||||
|     isArchived: false, |     isArchived: false, | ||||||
| @@ -302,6 +309,7 @@ export const assetStub = { | |||||||
|     exifInfo: { |     exifInfo: { | ||||||
|       fileSizeInByte: 5_000, |       fileSizeInByte: 5_000, | ||||||
|     } as ExifEntity, |     } as ExifEntity, | ||||||
|  |     deletedAt: null, | ||||||
|   }), |   }), | ||||||
|   video: Object.freeze<AssetEntity>({ |   video: Object.freeze<AssetEntity>({ | ||||||
|     id: 'asset-id', |     id: 'asset-id', | ||||||
| @@ -340,6 +348,7 @@ export const assetStub = { | |||||||
|     exifInfo: { |     exifInfo: { | ||||||
|       fileSizeInByte: 100_000, |       fileSizeInByte: 100_000, | ||||||
|     } as ExifEntity, |     } as ExifEntity, | ||||||
|  |     deletedAt: null, | ||||||
|   }), |   }), | ||||||
|   livePhotoMotionAsset: Object.freeze({ |   livePhotoMotionAsset: Object.freeze({ | ||||||
|     id: 'live-photo-motion-asset', |     id: 'live-photo-motion-asset', | ||||||
| @@ -411,6 +420,7 @@ export const assetStub = { | |||||||
|       longitude: 100, |       longitude: 100, | ||||||
|       fileSizeInByte: 23_456, |       fileSizeInByte: 23_456, | ||||||
|     } as ExifEntity, |     } as ExifEntity, | ||||||
|  |     deletedAt: null, | ||||||
|   }), |   }), | ||||||
|   sidecar: Object.freeze<AssetEntity>({ |   sidecar: Object.freeze<AssetEntity>({ | ||||||
|     id: 'asset-id', |     id: 'asset-id', | ||||||
| @@ -446,5 +456,12 @@ export const assetStub = { | |||||||
|     originalFileName: 'asset-id.ext', |     originalFileName: 'asset-id.ext', | ||||||
|     faces: [], |     faces: [], | ||||||
|     sidecarPath: '/original/path.ext.xmp', |     sidecarPath: '/original/path.ext.xmp', | ||||||
|  |     deletedAt: null, | ||||||
|  |   }), | ||||||
|  |   readOnly: Object.freeze({ | ||||||
|  |     id: 'read-only-asset', | ||||||
|  |     isReadOnly: true, | ||||||
|  |     libraryId: 'library-id', | ||||||
|  |     library: libraryStub.uploadLibrary1, | ||||||
|   }), |   }), | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								server/test/fixtures/shared-link.stub.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								server/test/fixtures/shared-link.stub.ts
									
									
									
									
										vendored
									
									
								
							| @@ -69,6 +69,7 @@ const assetResponse: AssetResponseDto = { | |||||||
|   tags: [], |   tags: [], | ||||||
|   people: [], |   people: [], | ||||||
|   checksum: 'ZmlsZSBoYXNo', |   checksum: 'ZmlsZSBoYXNo', | ||||||
|  |   isTrashed: false, | ||||||
|   libraryId: 'library-id', |   libraryId: 'library-id', | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -235,6 +236,7 @@ export const sharedLinkStub = { | |||||||
|           sharedLinks: [], |           sharedLinks: [], | ||||||
|           faces: [], |           faces: [], | ||||||
|           sidecarPath: null, |           sidecarPath: null, | ||||||
|  |           deletedAt: null, | ||||||
|         }, |         }, | ||||||
|       ], |       ], | ||||||
|     }, |     }, | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ export const newAssetRepositoryMock = (): jest.Mocked<IAssetRepository> => { | |||||||
|     getByIds: jest.fn().mockResolvedValue([]), |     getByIds: jest.fn().mockResolvedValue([]), | ||||||
|     getByAlbumId: jest.fn(), |     getByAlbumId: jest.fn(), | ||||||
|     getByUserId: jest.fn(), |     getByUserId: jest.fn(), | ||||||
|  |     getById: jest.fn(), | ||||||
|     getWithout: jest.fn(), |     getWithout: jest.fn(), | ||||||
|     getByChecksum: jest.fn(), |     getByChecksum: jest.fn(), | ||||||
|     getWith: jest.fn(), |     getWith: jest.fn(), | ||||||
| @@ -18,15 +19,16 @@ export const newAssetRepositoryMock = (): jest.Mocked<IAssetRepository> => { | |||||||
|     getAll: jest.fn().mockResolvedValue({ items: [], hasNextPage: false }), |     getAll: jest.fn().mockResolvedValue({ items: [], hasNextPage: false }), | ||||||
|     updateAll: jest.fn(), |     updateAll: jest.fn(), | ||||||
|     getByLibraryId: jest.fn(), |     getByLibraryId: jest.fn(), | ||||||
|     getById: jest.fn(), |  | ||||||
|     getByLibraryIdAndOriginalPath: jest.fn(), |     getByLibraryIdAndOriginalPath: jest.fn(), | ||||||
|     deleteAll: jest.fn(), |     deleteAll: jest.fn(), | ||||||
|     save: jest.fn(), |     save: jest.fn(), | ||||||
|  |     remove: jest.fn(), | ||||||
|     findLivePhotoMatch: jest.fn(), |     findLivePhotoMatch: jest.fn(), | ||||||
|     getMapMarkers: jest.fn(), |     getMapMarkers: jest.fn(), | ||||||
|     getStatistics: jest.fn(), |     getStatistics: jest.fn(), | ||||||
|     getByTimeBucket: jest.fn(), |     getByTimeBucket: jest.fn(), | ||||||
|     getTimeBuckets: jest.fn(), |     getTimeBuckets: jest.fn(), | ||||||
|     remove: jest.fn(), |     restoreAll: jest.fn(), | ||||||
|  |     softDeleteAll: jest.fn(), | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -3,5 +3,6 @@ import { ICommunicationRepository } from '@app/domain'; | |||||||
| export const newCommunicationRepositoryMock = (): jest.Mocked<ICommunicationRepository> => { | export const newCommunicationRepositoryMock = (): jest.Mocked<ICommunicationRepository> => { | ||||||
|   return { |   return { | ||||||
|     send: jest.fn(), |     send: jest.fn(), | ||||||
|  |     broadcast: jest.fn(), | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										437
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										437
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							| @@ -356,6 +356,25 @@ export interface AllJobStatusResponseDto { | |||||||
|      */ |      */ | ||||||
|     'videoConversion': JobStatusDto; |     'videoConversion': JobStatusDto; | ||||||
| } | } | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * @export | ||||||
|  |  * @interface AssetBulkDeleteDto | ||||||
|  |  */ | ||||||
|  | export interface AssetBulkDeleteDto { | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof AssetBulkDeleteDto | ||||||
|  |      */ | ||||||
|  |     'force'?: boolean; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {Array<string>} | ||||||
|  |      * @memberof AssetBulkDeleteDto | ||||||
|  |      */ | ||||||
|  |     'ids': Array<string>; | ||||||
|  | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
| @@ -657,6 +676,12 @@ export interface AssetResponseDto { | |||||||
|      * @memberof AssetResponseDto |      * @memberof AssetResponseDto | ||||||
|      */ |      */ | ||||||
|     'isReadOnly': boolean; |     'isReadOnly': boolean; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof AssetResponseDto | ||||||
|  |      */ | ||||||
|  |     'isTrashed': boolean; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {string} |      * @type {string} | ||||||
| @@ -1357,54 +1382,6 @@ export interface CuratedObjectsResponseDto { | |||||||
|      */ |      */ | ||||||
|     'resizePath': string; |     'resizePath': string; | ||||||
| } | } | ||||||
| /** |  | ||||||
|  *  |  | ||||||
|  * @export |  | ||||||
|  * @interface DeleteAssetDto |  | ||||||
|  */ |  | ||||||
| export interface DeleteAssetDto { |  | ||||||
|     /** |  | ||||||
|      *  |  | ||||||
|      * @type {Array<string>} |  | ||||||
|      * @memberof DeleteAssetDto |  | ||||||
|      */ |  | ||||||
|     'ids': Array<string>; |  | ||||||
| } |  | ||||||
| /** |  | ||||||
|  *  |  | ||||||
|  * @export |  | ||||||
|  * @interface DeleteAssetResponseDto |  | ||||||
|  */ |  | ||||||
| export interface DeleteAssetResponseDto { |  | ||||||
|     /** |  | ||||||
|      *  |  | ||||||
|      * @type {string} |  | ||||||
|      * @memberof DeleteAssetResponseDto |  | ||||||
|      */ |  | ||||||
|     'id': string; |  | ||||||
|     /** |  | ||||||
|      *  |  | ||||||
|      * @type {DeleteAssetStatus} |  | ||||||
|      * @memberof DeleteAssetResponseDto |  | ||||||
|      */ |  | ||||||
|     'status': DeleteAssetStatus; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  *  |  | ||||||
|  * @export |  | ||||||
|  * @enum {string} |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| export const DeleteAssetStatus = { |  | ||||||
|     Success: 'SUCCESS', |  | ||||||
|     Failed: 'FAILED' |  | ||||||
| } as const; |  | ||||||
| 
 |  | ||||||
| export type DeleteAssetStatus = typeof DeleteAssetStatus[keyof typeof DeleteAssetStatus]; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
| @@ -2623,6 +2600,12 @@ export interface ServerConfigDto { | |||||||
|      * @memberof ServerConfigDto |      * @memberof ServerConfigDto | ||||||
|      */ |      */ | ||||||
|     'oauthButtonText': string; |     'oauthButtonText': string; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {number} | ||||||
|  |      * @memberof ServerConfigDto | ||||||
|  |      */ | ||||||
|  |     'trashDays': number; | ||||||
| } | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
| @@ -2696,6 +2679,12 @@ export interface ServerFeaturesDto { | |||||||
|      * @memberof ServerFeaturesDto |      * @memberof ServerFeaturesDto | ||||||
|      */ |      */ | ||||||
|     'tagImage': boolean; |     'tagImage': boolean; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof ServerFeaturesDto | ||||||
|  |      */ | ||||||
|  |     'trash': boolean; | ||||||
| } | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
| @@ -3139,6 +3128,12 @@ export interface SystemConfigDto { | |||||||
|      * @memberof SystemConfigDto |      * @memberof SystemConfigDto | ||||||
|      */ |      */ | ||||||
|     'thumbnail': SystemConfigThumbnailDto; |     'thumbnail': SystemConfigThumbnailDto; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {SystemConfigTrashDto} | ||||||
|  |      * @memberof SystemConfigDto | ||||||
|  |      */ | ||||||
|  |     'trash': SystemConfigTrashDto; | ||||||
| } | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
| @@ -3594,6 +3589,25 @@ export interface SystemConfigThumbnailDto { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * @export | ||||||
|  |  * @interface SystemConfigTrashDto | ||||||
|  |  */ | ||||||
|  | export interface SystemConfigTrashDto { | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {number} | ||||||
|  |      * @memberof SystemConfigTrashDto | ||||||
|  |      */ | ||||||
|  |     'days': number; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof SystemConfigTrashDto | ||||||
|  |      */ | ||||||
|  |     'enabled': boolean; | ||||||
|  | } | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
| @@ -5682,13 +5696,13 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {DeleteAssetDto} deleteAssetDto  |          * @param {AssetBulkDeleteDto} assetBulkDeleteDto  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         deleteAsset: async (deleteAssetDto: DeleteAssetDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { |         deleteAssets: async (assetBulkDeleteDto: AssetBulkDeleteDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|             // verify required parameter 'deleteAssetDto' is not null or undefined
 |             // verify required parameter 'assetBulkDeleteDto' is not null or undefined
 | ||||||
|             assertParamExists('deleteAsset', 'deleteAssetDto', deleteAssetDto) |             assertParamExists('deleteAssets', 'assetBulkDeleteDto', assetBulkDeleteDto) | ||||||
|             const localVarPath = `/asset`; |             const localVarPath = `/asset`; | ||||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 |             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); |             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||||
| @@ -5717,7 +5731,7 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; |             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; |             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||||
|             localVarRequestOptions.data = serializeDataIfNeeded(deleteAssetDto, localVarRequestOptions, configuration) |             localVarRequestOptions.data = serializeDataIfNeeded(assetBulkDeleteDto, localVarRequestOptions, configuration) | ||||||
| 
 | 
 | ||||||
|             return { |             return { | ||||||
|                 url: toPathString(localVarUrlObj), |                 url: toPathString(localVarUrlObj), | ||||||
| @@ -5811,6 +5825,44 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|      |      | ||||||
|  |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
|  |             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||||
|  |             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||||
|  | 
 | ||||||
|  |             return { | ||||||
|  |                 url: toPathString(localVarUrlObj), | ||||||
|  |                 options: localVarRequestOptions, | ||||||
|  |             }; | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         emptyTrash: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|  |             const localVarPath = `/asset/trash/empty`; | ||||||
|  |             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||||
|  |             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||||
|  |             let baseOptions; | ||||||
|  |             if (configuration) { | ||||||
|  |                 baseOptions = configuration.baseOptions; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; | ||||||
|  |             const localVarHeaderParameter = {} as any; | ||||||
|  |             const localVarQueryParameter = {} as any; | ||||||
|  | 
 | ||||||
|  |             // authentication cookie required
 | ||||||
|  | 
 | ||||||
|  |             // authentication api_key required
 | ||||||
|  |             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||||
|  | 
 | ||||||
|  |             // authentication bearer required
 | ||||||
|  |             // http bearer authentication required
 | ||||||
|  |             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |      | ||||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
|             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; |             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||||
|             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; |             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||||
| @@ -5979,10 +6031,11 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|          *  |          *  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getAssetStats: async (isArchived?: boolean, isFavorite?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { |         getAssetStats: async (isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|             const localVarPath = `/asset/statistics`; |             const localVarPath = `/asset/statistics`; | ||||||
|             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 |             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||||
|             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); |             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||||
| @@ -6012,6 +6065,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|                 localVarQueryParameter['isFavorite'] = isFavorite; |                 localVarQueryParameter['isFavorite'] = isFavorite; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             if (isTrashed !== undefined) { | ||||||
|  |                 localVarQueryParameter['isTrashed'] = isTrashed; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|      |      | ||||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
| @@ -6084,11 +6141,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|          * @param {string} [personId]  |          * @param {string} [personId]  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {string} [key]  |          * @param {string} [key]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getByTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { |         getByTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|             // verify required parameter 'size' is not null or undefined
 |             // verify required parameter 'size' is not null or undefined
 | ||||||
|             assertParamExists('getByTimeBucket', 'size', size) |             assertParamExists('getByTimeBucket', 'size', size) | ||||||
|             // verify required parameter 'timeBucket' is not null or undefined
 |             // verify required parameter 'timeBucket' is not null or undefined
 | ||||||
| @@ -6138,6 +6196,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|                 localVarQueryParameter['isFavorite'] = isFavorite; |                 localVarQueryParameter['isFavorite'] = isFavorite; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             if (isTrashed !== undefined) { | ||||||
|  |                 localVarQueryParameter['isTrashed'] = isTrashed; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             if (timeBucket !== undefined) { |             if (timeBucket !== undefined) { | ||||||
|                 localVarQueryParameter['timeBucket'] = timeBucket; |                 localVarQueryParameter['timeBucket'] = timeBucket; | ||||||
|             } |             } | ||||||
| @@ -6447,11 +6509,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|          * @param {string} [personId]  |          * @param {string} [personId]  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {string} [key]  |          * @param {string} [key]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { |         getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|             // verify required parameter 'size' is not null or undefined
 |             // verify required parameter 'size' is not null or undefined
 | ||||||
|             assertParamExists('getTimeBuckets', 'size', size) |             assertParamExists('getTimeBuckets', 'size', size) | ||||||
|             const localVarPath = `/asset/time-buckets`; |             const localVarPath = `/asset/time-buckets`; | ||||||
| @@ -6499,6 +6562,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|                 localVarQueryParameter['isFavorite'] = isFavorite; |                 localVarQueryParameter['isFavorite'] = isFavorite; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             if (isTrashed !== undefined) { | ||||||
|  |                 localVarQueryParameter['isTrashed'] = isTrashed; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             if (key !== undefined) { |             if (key !== undefined) { | ||||||
|                 localVarQueryParameter['key'] = key; |                 localVarQueryParameter['key'] = key; | ||||||
|             } |             } | ||||||
| @@ -6600,6 +6667,88 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | |||||||
|                 options: localVarRequestOptions, |                 options: localVarRequestOptions, | ||||||
|             }; |             }; | ||||||
|         }, |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {BulkIdsDto} bulkIdsDto  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         restoreAssets: async (bulkIdsDto: BulkIdsDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|  |             // verify required parameter 'bulkIdsDto' is not null or undefined
 | ||||||
|  |             assertParamExists('restoreAssets', 'bulkIdsDto', bulkIdsDto) | ||||||
|  |             const localVarPath = `/asset/restore`; | ||||||
|  |             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||||
|  |             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||||
|  |             let baseOptions; | ||||||
|  |             if (configuration) { | ||||||
|  |                 baseOptions = configuration.baseOptions; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; | ||||||
|  |             const localVarHeaderParameter = {} as any; | ||||||
|  |             const localVarQueryParameter = {} as any; | ||||||
|  | 
 | ||||||
|  |             // authentication cookie required
 | ||||||
|  | 
 | ||||||
|  |             // authentication api_key required
 | ||||||
|  |             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||||
|  | 
 | ||||||
|  |             // authentication bearer required
 | ||||||
|  |             // http bearer authentication required
 | ||||||
|  |             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  |             localVarHeaderParameter['Content-Type'] = 'application/json'; | ||||||
|  | 
 | ||||||
|  |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
|  |             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||||
|  |             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||||
|  |             localVarRequestOptions.data = serializeDataIfNeeded(bulkIdsDto, localVarRequestOptions, configuration) | ||||||
|  | 
 | ||||||
|  |             return { | ||||||
|  |                 url: toPathString(localVarUrlObj), | ||||||
|  |                 options: localVarRequestOptions, | ||||||
|  |             }; | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         restoreTrash: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||||
|  |             const localVarPath = `/asset/trash/restore`; | ||||||
|  |             // use dummy base URL string because the URL constructor only accepts absolute URLs.
 | ||||||
|  |             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); | ||||||
|  |             let baseOptions; | ||||||
|  |             if (configuration) { | ||||||
|  |                 baseOptions = configuration.baseOptions; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; | ||||||
|  |             const localVarHeaderParameter = {} as any; | ||||||
|  |             const localVarQueryParameter = {} as any; | ||||||
|  | 
 | ||||||
|  |             // authentication cookie required
 | ||||||
|  | 
 | ||||||
|  |             // authentication api_key required
 | ||||||
|  |             await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) | ||||||
|  | 
 | ||||||
|  |             // authentication bearer required
 | ||||||
|  |             // http bearer authentication required
 | ||||||
|  |             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  |             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||||
|  |             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; | ||||||
|  |             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; | ||||||
|  | 
 | ||||||
|  |             return { | ||||||
|  |                 url: toPathString(localVarUrlObj), | ||||||
|  |                 options: localVarRequestOptions, | ||||||
|  |             }; | ||||||
|  |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {AssetJobsDto} assetJobsDto  |          * @param {AssetJobsDto} assetJobsDto  | ||||||
| @@ -7014,12 +7163,12 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {DeleteAssetDto} deleteAssetDto  |          * @param {AssetBulkDeleteDto} assetBulkDeleteDto  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         async deleteAsset(deleteAssetDto: DeleteAssetDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<DeleteAssetResponseDto>>> { |         async deleteAssets(assetBulkDeleteDto: AssetBulkDeleteDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { | ||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAsset(deleteAssetDto, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAssets(assetBulkDeleteDto, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
| @@ -7044,6 +7193,15 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.downloadFile(id, key, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.downloadFile(id, key, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         async emptyTrash(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { | ||||||
|  |             const localVarAxiosArgs = await localVarAxiosParamCreator.emptyTrash(options); | ||||||
|  |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|  |         }, | ||||||
|         /** |         /** | ||||||
|          * Get all AssetEntity belong to the user |          * Get all AssetEntity belong to the user | ||||||
|          * @param {string} [userId]  |          * @param {string} [userId]  | ||||||
| @@ -7083,11 +7241,12 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|          *  |          *  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         async getAssetStats(isArchived?: boolean, isFavorite?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetStatsResponseDto>> { |         async getAssetStats(isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetStatsResponseDto>> { | ||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetStats(isArchived, isFavorite, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetStats(isArchived, isFavorite, isTrashed, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
| @@ -7111,12 +7270,13 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|          * @param {string} [personId]  |          * @param {string} [personId]  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {string} [key]  |          * @param {string} [key]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         async getByTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> { |         async getByTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> { | ||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, key, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, key, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
| @@ -7190,12 +7350,13 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|          * @param {string} [personId]  |          * @param {string} [personId]  | ||||||
|          * @param {boolean} [isArchived]  |          * @param {boolean} [isArchived]  | ||||||
|          * @param {boolean} [isFavorite]  |          * @param {boolean} [isFavorite]  | ||||||
|  |          * @param {boolean} [isTrashed]  | ||||||
|          * @param {string} [key]  |          * @param {string} [key]  | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<TimeBucketResponseDto>>> { |         async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<TimeBucketResponseDto>>> { | ||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, key, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, key, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
| @@ -7218,6 +7379,25 @@ export const AssetApiFp = function(configuration?: Configuration) { | |||||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.importFile(importAssetDto, options); |             const localVarAxiosArgs = await localVarAxiosParamCreator.importFile(importAssetDto, options); | ||||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|         }, |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {BulkIdsDto} bulkIdsDto  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         async restoreAssets(bulkIdsDto: BulkIdsDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { | ||||||
|  |             const localVarAxiosArgs = await localVarAxiosParamCreator.restoreAssets(bulkIdsDto, options); | ||||||
|  |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         async restoreTrash(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { | ||||||
|  |             const localVarAxiosArgs = await localVarAxiosParamCreator.restoreTrash(options); | ||||||
|  |             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||||
|  |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {AssetJobsDto} assetJobsDto  |          * @param {AssetJobsDto} assetJobsDto  | ||||||
| @@ -7336,12 +7516,12 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {AssetApiDeleteAssetRequest} requestParameters Request parameters. |          * @param {AssetApiDeleteAssetsRequest} requestParameters Request parameters. | ||||||
|          * @param {*} [options] Override http request option. |          * @param {*} [options] Override http request option. | ||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         deleteAsset(requestParameters: AssetApiDeleteAssetRequest, options?: AxiosRequestConfig): AxiosPromise<Array<DeleteAssetResponseDto>> { |         deleteAssets(requestParameters: AssetApiDeleteAssetsRequest, options?: AxiosRequestConfig): AxiosPromise<void> { | ||||||
|             return localVarFp.deleteAsset(requestParameters.deleteAssetDto, options).then((request) => request(axios, basePath)); |             return localVarFp.deleteAssets(requestParameters.assetBulkDeleteDto, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
| @@ -7361,6 +7541,14 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|         downloadFile(requestParameters: AssetApiDownloadFileRequest, options?: AxiosRequestConfig): AxiosPromise<File> { |         downloadFile(requestParameters: AssetApiDownloadFileRequest, options?: AxiosRequestConfig): AxiosPromise<File> { | ||||||
|             return localVarFp.downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(axios, basePath)); |             return localVarFp.downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         emptyTrash(options?: AxiosRequestConfig): AxiosPromise<void> { | ||||||
|  |             return localVarFp.emptyTrash(options).then((request) => request(axios, basePath)); | ||||||
|  |         }, | ||||||
|         /** |         /** | ||||||
|          * Get all AssetEntity belong to the user |          * Get all AssetEntity belong to the user | ||||||
|          * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. |          * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. | ||||||
| @@ -7394,7 +7582,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<AssetStatsResponseDto> { |         getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<AssetStatsResponseDto> { | ||||||
|             return localVarFp.getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, options).then((request) => request(axios, basePath)); |             return localVarFp.getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
| @@ -7412,7 +7600,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> { |         getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> { | ||||||
|             return localVarFp.getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(axios, basePath)); |             return localVarFp.getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
| @@ -7473,7 +7661,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|          * @throws {RequiredError} |          * @throws {RequiredError} | ||||||
|          */ |          */ | ||||||
|         getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig): AxiosPromise<Array<TimeBucketResponseDto>> { |         getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig): AxiosPromise<Array<TimeBucketResponseDto>> { | ||||||
|             return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(axios, basePath)); |             return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|         /** |         /** | ||||||
|          * Get all asset of a device that are in the database, ID only. |          * Get all asset of a device that are in the database, ID only. | ||||||
| @@ -7493,6 +7681,23 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | |||||||
|         importFile(requestParameters: AssetApiImportFileRequest, options?: AxiosRequestConfig): AxiosPromise<AssetFileUploadResponseDto> { |         importFile(requestParameters: AssetApiImportFileRequest, options?: AxiosRequestConfig): AxiosPromise<AssetFileUploadResponseDto> { | ||||||
|             return localVarFp.importFile(requestParameters.importAssetDto, options).then((request) => request(axios, basePath)); |             return localVarFp.importFile(requestParameters.importAssetDto, options).then((request) => request(axios, basePath)); | ||||||
|         }, |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {AssetApiRestoreAssetsRequest} requestParameters Request parameters. | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         restoreAssets(requestParameters: AssetApiRestoreAssetsRequest, options?: AxiosRequestConfig): AxiosPromise<void> { | ||||||
|  |             return localVarFp.restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(axios, basePath)); | ||||||
|  |         }, | ||||||
|  |         /** | ||||||
|  |          *  | ||||||
|  |          * @param {*} [options] Override http request option. | ||||||
|  |          * @throws {RequiredError} | ||||||
|  |          */ | ||||||
|  |         restoreTrash(options?: AxiosRequestConfig): AxiosPromise<void> { | ||||||
|  |             return localVarFp.restoreTrash(options).then((request) => request(axios, basePath)); | ||||||
|  |         }, | ||||||
|         /** |         /** | ||||||
|          *  |          *  | ||||||
|          * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. |          * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. | ||||||
| @@ -7600,17 +7805,17 @@ export interface AssetApiCheckExistingAssetsRequest { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Request parameters for deleteAsset operation in AssetApi. |  * Request parameters for deleteAssets operation in AssetApi. | ||||||
|  * @export |  * @export | ||||||
|  * @interface AssetApiDeleteAssetRequest |  * @interface AssetApiDeleteAssetsRequest | ||||||
|  */ |  */ | ||||||
| export interface AssetApiDeleteAssetRequest { | export interface AssetApiDeleteAssetsRequest { | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {DeleteAssetDto} |      * @type {AssetBulkDeleteDto} | ||||||
|      * @memberof AssetApiDeleteAsset |      * @memberof AssetApiDeleteAssets | ||||||
|      */ |      */ | ||||||
|     readonly deleteAssetDto: DeleteAssetDto |     readonly assetBulkDeleteDto: AssetBulkDeleteDto | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @@ -7744,6 +7949,13 @@ export interface AssetApiGetAssetStatsRequest { | |||||||
|      * @memberof AssetApiGetAssetStats |      * @memberof AssetApiGetAssetStats | ||||||
|      */ |      */ | ||||||
|     readonly isFavorite?: boolean |     readonly isFavorite?: boolean | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof AssetApiGetAssetStats | ||||||
|  |      */ | ||||||
|  |     readonly isTrashed?: boolean | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @@ -7829,6 +8041,13 @@ export interface AssetApiGetByTimeBucketRequest { | |||||||
|      */ |      */ | ||||||
|     readonly isFavorite?: boolean |     readonly isFavorite?: boolean | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof AssetApiGetByTimeBucket | ||||||
|  |      */ | ||||||
|  |     readonly isTrashed?: boolean | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {string} |      * @type {string} | ||||||
| @@ -7976,6 +8195,13 @@ export interface AssetApiGetTimeBucketsRequest { | |||||||
|      */ |      */ | ||||||
|     readonly isFavorite?: boolean |     readonly isFavorite?: boolean | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof AssetApiGetTimeBuckets | ||||||
|  |      */ | ||||||
|  |     readonly isTrashed?: boolean | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {string} |      * @type {string} | ||||||
| @@ -8012,6 +8238,20 @@ export interface AssetApiImportFileRequest { | |||||||
|     readonly importAssetDto: ImportAssetDto |     readonly importAssetDto: ImportAssetDto | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Request parameters for restoreAssets operation in AssetApi. | ||||||
|  |  * @export | ||||||
|  |  * @interface AssetApiRestoreAssetsRequest | ||||||
|  |  */ | ||||||
|  | export interface AssetApiRestoreAssetsRequest { | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {BulkIdsDto} | ||||||
|  |      * @memberof AssetApiRestoreAssets | ||||||
|  |      */ | ||||||
|  |     readonly bulkIdsDto: BulkIdsDto | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Request parameters for runAssetJobs operation in AssetApi. |  * Request parameters for runAssetJobs operation in AssetApi. | ||||||
|  * @export |  * @export | ||||||
| @@ -8271,13 +8511,13 @@ export class AssetApi extends BaseAPI { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @param {AssetApiDeleteAssetRequest} requestParameters Request parameters. |      * @param {AssetApiDeleteAssetsRequest} requestParameters Request parameters. | ||||||
|      * @param {*} [options] Override http request option. |      * @param {*} [options] Override http request option. | ||||||
|      * @throws {RequiredError} |      * @throws {RequiredError} | ||||||
|      * @memberof AssetApi |      * @memberof AssetApi | ||||||
|      */ |      */ | ||||||
|     public deleteAsset(requestParameters: AssetApiDeleteAssetRequest, options?: AxiosRequestConfig) { |     public deleteAssets(requestParameters: AssetApiDeleteAssetsRequest, options?: AxiosRequestConfig) { | ||||||
|         return AssetApiFp(this.configuration).deleteAsset(requestParameters.deleteAssetDto, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).deleteAssets(requestParameters.assetBulkDeleteDto, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @@ -8302,6 +8542,16 @@ export class AssetApi extends BaseAPI { | |||||||
|         return AssetApiFp(this.configuration).downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @param {*} [options] Override http request option. | ||||||
|  |      * @throws {RequiredError} | ||||||
|  |      * @memberof AssetApi | ||||||
|  |      */ | ||||||
|  |     public emptyTrash(options?: AxiosRequestConfig) { | ||||||
|  |         return AssetApiFp(this.configuration).emptyTrash(options).then((request) => request(this.axios, this.basePath)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Get all AssetEntity belong to the user |      * Get all AssetEntity belong to the user | ||||||
|      * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. |      * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. | ||||||
| @@ -8342,7 +8592,7 @@ export class AssetApi extends BaseAPI { | |||||||
|      * @memberof AssetApi |      * @memberof AssetApi | ||||||
|      */ |      */ | ||||||
|     public getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig) { |     public getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig) { | ||||||
|         return AssetApiFp(this.configuration).getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @@ -8364,7 +8614,7 @@ export class AssetApi extends BaseAPI { | |||||||
|      * @memberof AssetApi |      * @memberof AssetApi | ||||||
|      */ |      */ | ||||||
|     public getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig) { |     public getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig) { | ||||||
|         return AssetApiFp(this.configuration).getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @@ -8439,7 +8689,7 @@ export class AssetApi extends BaseAPI { | |||||||
|      * @memberof AssetApi |      * @memberof AssetApi | ||||||
|      */ |      */ | ||||||
|     public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig) { |     public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig) { | ||||||
|         return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @@ -8464,6 +8714,27 @@ export class AssetApi extends BaseAPI { | |||||||
|         return AssetApiFp(this.configuration).importFile(requestParameters.importAssetDto, options).then((request) => request(this.axios, this.basePath)); |         return AssetApiFp(this.configuration).importFile(requestParameters.importAssetDto, options).then((request) => request(this.axios, this.basePath)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @param {AssetApiRestoreAssetsRequest} requestParameters Request parameters. | ||||||
|  |      * @param {*} [options] Override http request option. | ||||||
|  |      * @throws {RequiredError} | ||||||
|  |      * @memberof AssetApi | ||||||
|  |      */ | ||||||
|  |     public restoreAssets(requestParameters: AssetApiRestoreAssetsRequest, options?: AxiosRequestConfig) { | ||||||
|  |         return AssetApiFp(this.configuration).restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(this.axios, this.basePath)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @param {*} [options] Override http request option. | ||||||
|  |      * @throws {RequiredError} | ||||||
|  |      * @memberof AssetApi | ||||||
|  |      */ | ||||||
|  |     public restoreTrash(options?: AxiosRequestConfig) { | ||||||
|  |         return AssetApiFp(this.configuration).restoreTrash(options).then((request) => request(this.axios, this.basePath)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. |      * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user