diff --git a/cli/src/api/open-api/api.ts b/cli/src/api/open-api/api.ts index 4e86e88387..63739dbd8b 100644 --- a/cli/src/api/open-api/api.ts +++ b/cli/src/api/open-api/api.ts @@ -410,44 +410,6 @@ export const AssetBulkUploadCheckResultReasonEnum = { export type AssetBulkUploadCheckResultReasonEnum = typeof AssetBulkUploadCheckResultReasonEnum[keyof typeof AssetBulkUploadCheckResultReasonEnum]; -/** - * - * @export - * @interface AssetCountByTimeBucket - */ -export interface AssetCountByTimeBucket { - /** - * - * @type {number} - * @memberof AssetCountByTimeBucket - */ - 'count': number; - /** - * - * @type {string} - * @memberof AssetCountByTimeBucket - */ - 'timeBucket': string; -} -/** - * - * @export - * @interface AssetCountByTimeBucketResponseDto - */ -export interface AssetCountByTimeBucketResponseDto { - /** - * - * @type {Array} - * @memberof AssetCountByTimeBucketResponseDto - */ - 'buckets': Array; - /** - * - * @type {number} - * @memberof AssetCountByTimeBucketResponseDto - */ - 'totalCount': number; -} /** * * @export @@ -1286,58 +1248,6 @@ export interface ExifResponseDto { */ 'timeZone'?: string | null; } -/** - * - * @export - * @interface GetAssetByTimeBucketDto - */ -export interface GetAssetByTimeBucketDto { - /** - * - * @type {Array} - * @memberof GetAssetByTimeBucketDto - */ - 'timeBucket': Array; - /** - * - * @type {string} - * @memberof GetAssetByTimeBucketDto - */ - 'userId'?: string; - /** - * Include assets without thumbnails - * @type {boolean} - * @memberof GetAssetByTimeBucketDto - */ - 'withoutThumbs'?: boolean; -} -/** - * - * @export - * @interface GetAssetCountByTimeBucketDto - */ -export interface GetAssetCountByTimeBucketDto { - /** - * - * @type {TimeGroupEnum} - * @memberof GetAssetCountByTimeBucketDto - */ - 'timeGroup': TimeGroupEnum; - /** - * - * @type {string} - * @memberof GetAssetCountByTimeBucketDto - */ - 'userId'?: string; - /** - * Include assets without thumbnails - * @type {boolean} - * @memberof GetAssetCountByTimeBucketDto - */ - 'withoutThumbs'?: boolean; -} - - /** * * @export @@ -2850,18 +2760,37 @@ export const ThumbnailFormat = { export type ThumbnailFormat = typeof ThumbnailFormat[keyof typeof ThumbnailFormat]; +/** + * + * @export + * @interface TimeBucketResponseDto + */ +export interface TimeBucketResponseDto { + /** + * + * @type {number} + * @memberof TimeBucketResponseDto + */ + 'count': number; + /** + * + * @type {string} + * @memberof TimeBucketResponseDto + */ + 'timeBucket': string; +} /** * * @export * @enum {string} */ -export const TimeGroupEnum = { - Day: 'day', - Month: 'month' +export const TimeBucketSize = { + Day: 'DAY', + Month: 'MONTH' } as const; -export type TimeGroupEnum = typeof TimeGroupEnum[keyof typeof TimeGroupEnum]; +export type TimeBucketSize = typeof TimeBucketSize[keyof typeof TimeBucketSize]; /** @@ -5038,94 +4967,6 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration options: localVarRequestOptions, }; }, - /** - * - * @param {GetAssetByTimeBucketDto} getAssetByTimeBucketDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetByTimeBucket: async (getAssetByTimeBucketDto: GetAssetByTimeBucketDto, options: AxiosRequestConfig = {}): Promise => { - // verify required parameter 'getAssetByTimeBucketDto' is not null or undefined - assertParamExists('getAssetByTimeBucket', 'getAssetByTimeBucketDto', getAssetByTimeBucketDto) - const localVarPath = `/asset/time-bucket`; - // 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(getAssetByTimeBucketDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {GetAssetCountByTimeBucketDto} getAssetCountByTimeBucketDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetCountByTimeBucket: async (getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto, options: AxiosRequestConfig = {}): Promise => { - // verify required parameter 'getAssetCountByTimeBucketDto' is not null or undefined - assertParamExists('getAssetCountByTimeBucket', 'getAssetCountByTimeBucketDto', getAssetCountByTimeBucketDto) - const localVarPath = `/asset/count-by-time-bucket`; - // 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(getAssetCountByTimeBucketDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, /** * * @param {*} [options] Override http request option. @@ -5255,6 +5096,83 @@ 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 {TimeBucketSize} size + * @param {string} timeBucket + * @param {string} [userId] + * @param {string} [albumId] + * @param {boolean} [isArchived] + * @param {boolean} [isFavorite] + * @param {string} [key] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getByTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'size' is not null or undefined + assertParamExists('getByTimeBucket', 'size', size) + // verify required parameter 'timeBucket' is not null or undefined + assertParamExists('getByTimeBucket', 'timeBucket', timeBucket) + const localVarPath = `/asset/time-bucket`; + // 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: 'GET', ...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) + + if (size !== undefined) { + localVarQueryParameter['size'] = size; + } + + if (userId !== undefined) { + localVarQueryParameter['userId'] = userId; + } + + if (albumId !== undefined) { + localVarQueryParameter['albumId'] = albumId; + } + + if (isArchived !== undefined) { + localVarQueryParameter['isArchived'] = isArchived; + } + + if (isFavorite !== undefined) { + localVarQueryParameter['isFavorite'] = isFavorite; + } + + if (timeBucket !== undefined) { + localVarQueryParameter['timeBucket'] = timeBucket; + } + + if (key !== undefined) { + localVarQueryParameter['key'] = key; + } + + + setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; @@ -5498,6 +5416,76 @@ 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 {TimeBucketSize} size + * @param {string} [userId] + * @param {string} [albumId] + * @param {boolean} [isArchived] + * @param {boolean} [isFavorite] + * @param {string} [key] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'size' is not null or undefined + assertParamExists('getTimeBuckets', 'size', size) + const localVarPath = `/asset/time-buckets`; + // 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: 'GET', ...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) + + if (size !== undefined) { + localVarQueryParameter['size'] = size; + } + + if (userId !== undefined) { + localVarQueryParameter['userId'] = userId; + } + + if (albumId !== undefined) { + localVarQueryParameter['albumId'] = albumId; + } + + if (isArchived !== undefined) { + localVarQueryParameter['isArchived'] = isArchived; + } + + if (isFavorite !== undefined) { + localVarQueryParameter['isFavorite'] = isFavorite; + } + + if (key !== undefined) { + localVarQueryParameter['key'] = key; + } + + + setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; @@ -5960,26 +5948,6 @@ export const AssetApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetById(id, key, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, - /** - * - * @param {GetAssetByTimeBucketDto} getAssetByTimeBucketDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAssetByTimeBucket(getAssetByTimeBucketDto: GetAssetByTimeBucketDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetByTimeBucket(getAssetByTimeBucketDto, options); - return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); - }, - /** - * - * @param {GetAssetCountByTimeBucketDto} getAssetCountByTimeBucketDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAssetCountByTimeBucket(getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetCountByTimeBucket(getAssetCountByTimeBucketDto, options); - return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); - }, /** * * @param {*} [options] Override http request option. @@ -6012,6 +5980,22 @@ export const AssetApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetThumbnail(id, format, key, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @param {TimeBucketSize} size + * @param {string} timeBucket + * @param {string} [userId] + * @param {string} [albumId] + * @param {boolean} [isArchived] + * @param {boolean} [isFavorite] + * @param {string} [key] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getByTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getByTimeBucket(size, timeBucket, userId, albumId, isArchived, isFavorite, key, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @param {*} [options] Override http request option. @@ -6066,6 +6050,21 @@ export const AssetApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.getMemoryLane(timestamp, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @param {TimeBucketSize} size + * @param {string} [userId] + * @param {string} [albumId] + * @param {boolean} [isArchived] + * @param {boolean} [isFavorite] + * @param {string} [key] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, isArchived, isFavorite, key, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * Get all asset of a device that are in the database, ID only. * @param {string} deviceId @@ -6224,24 +6223,6 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath getAssetById(requestParameters: AssetApiGetAssetByIdRequest, options?: AxiosRequestConfig): AxiosPromise { return localVarFp.getAssetById(requestParameters.id, requestParameters.key, options).then((request) => request(axios, basePath)); }, - /** - * - * @param {AssetApiGetAssetByTimeBucketRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetByTimeBucket(requestParameters: AssetApiGetAssetByTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise> { - return localVarFp.getAssetByTimeBucket(requestParameters.getAssetByTimeBucketDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {AssetApiGetAssetCountByTimeBucketRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetCountByTimeBucket(requestParameters: AssetApiGetAssetCountByTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise { - return localVarFp.getAssetCountByTimeBucket(requestParameters.getAssetCountByTimeBucketDto, options).then((request) => request(axios, basePath)); - }, /** * * @param {*} [options] Override http request option. @@ -6268,6 +6249,15 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath getAssetThumbnail(requestParameters: AssetApiGetAssetThumbnailRequest, options?: AxiosRequestConfig): AxiosPromise { return localVarFp.getAssetThumbnail(requestParameters.id, requestParameters.format, requestParameters.key, options).then((request) => request(axios, basePath)); }, + /** + * + * @param {AssetApiGetByTimeBucketRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise> { + return localVarFp.getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(axios, basePath)); + }, /** * * @param {*} [options] Override http request option. @@ -6311,6 +6301,15 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath getMemoryLane(requestParameters: AssetApiGetMemoryLaneRequest, options?: AxiosRequestConfig): AxiosPromise> { return localVarFp.getMemoryLane(requestParameters.timestamp, options).then((request) => request(axios, basePath)); }, + /** + * + * @param {AssetApiGetTimeBucketsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig): AxiosPromise> { + return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(axios, basePath)); + }, /** * Get all asset of a device that are in the database, ID only. * @param {AssetApiGetUserAssetsByDeviceIdRequest} requestParameters Request parameters. @@ -6543,34 +6542,6 @@ export interface AssetApiGetAssetByIdRequest { readonly key?: string } -/** - * Request parameters for getAssetByTimeBucket operation in AssetApi. - * @export - * @interface AssetApiGetAssetByTimeBucketRequest - */ -export interface AssetApiGetAssetByTimeBucketRequest { - /** - * - * @type {GetAssetByTimeBucketDto} - * @memberof AssetApiGetAssetByTimeBucket - */ - readonly getAssetByTimeBucketDto: GetAssetByTimeBucketDto -} - -/** - * Request parameters for getAssetCountByTimeBucket operation in AssetApi. - * @export - * @interface AssetApiGetAssetCountByTimeBucketRequest - */ -export interface AssetApiGetAssetCountByTimeBucketRequest { - /** - * - * @type {GetAssetCountByTimeBucketDto} - * @memberof AssetApiGetAssetCountByTimeBucket - */ - readonly getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto -} - /** * Request parameters for getAssetStats operation in AssetApi. * @export @@ -6620,6 +6591,62 @@ export interface AssetApiGetAssetThumbnailRequest { readonly key?: string } +/** + * Request parameters for getByTimeBucket operation in AssetApi. + * @export + * @interface AssetApiGetByTimeBucketRequest + */ +export interface AssetApiGetByTimeBucketRequest { + /** + * + * @type {TimeBucketSize} + * @memberof AssetApiGetByTimeBucket + */ + readonly size: TimeBucketSize + + /** + * + * @type {string} + * @memberof AssetApiGetByTimeBucket + */ + readonly timeBucket: string + + /** + * + * @type {string} + * @memberof AssetApiGetByTimeBucket + */ + readonly userId?: string + + /** + * + * @type {string} + * @memberof AssetApiGetByTimeBucket + */ + readonly albumId?: string + + /** + * + * @type {boolean} + * @memberof AssetApiGetByTimeBucket + */ + readonly isArchived?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiGetByTimeBucket + */ + readonly isFavorite?: boolean + + /** + * + * @type {string} + * @memberof AssetApiGetByTimeBucket + */ + readonly key?: string +} + /** * Request parameters for getDownloadInfo operation in AssetApi. * @export @@ -6704,6 +6731,55 @@ export interface AssetApiGetMemoryLaneRequest { readonly timestamp: string } +/** + * Request parameters for getTimeBuckets operation in AssetApi. + * @export + * @interface AssetApiGetTimeBucketsRequest + */ +export interface AssetApiGetTimeBucketsRequest { + /** + * + * @type {TimeBucketSize} + * @memberof AssetApiGetTimeBuckets + */ + readonly size: TimeBucketSize + + /** + * + * @type {string} + * @memberof AssetApiGetTimeBuckets + */ + readonly userId?: string + + /** + * + * @type {string} + * @memberof AssetApiGetTimeBuckets + */ + readonly albumId?: string + + /** + * + * @type {boolean} + * @memberof AssetApiGetTimeBuckets + */ + readonly isArchived?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiGetTimeBuckets + */ + readonly isFavorite?: boolean + + /** + * + * @type {string} + * @memberof AssetApiGetTimeBuckets + */ + readonly key?: string +} + /** * Request parameters for getUserAssetsByDeviceId operation in AssetApi. * @export @@ -6995,28 +7071,6 @@ export class AssetApi extends BaseAPI { return AssetApiFp(this.configuration).getAssetById(requestParameters.id, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); } - /** - * - * @param {AssetApiGetAssetByTimeBucketRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getAssetByTimeBucket(requestParameters: AssetApiGetAssetByTimeBucketRequest, options?: AxiosRequestConfig) { - return AssetApiFp(this.configuration).getAssetByTimeBucket(requestParameters.getAssetByTimeBucketDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiGetAssetCountByTimeBucketRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getAssetCountByTimeBucket(requestParameters: AssetApiGetAssetCountByTimeBucketRequest, options?: AxiosRequestConfig) { - return AssetApiFp(this.configuration).getAssetCountByTimeBucket(requestParameters.getAssetCountByTimeBucketDto, options).then((request) => request(this.axios, this.basePath)); - } - /** * * @param {*} [options] Override http request option. @@ -7049,6 +7103,17 @@ export class AssetApi extends BaseAPI { return AssetApiFp(this.configuration).getAssetThumbnail(requestParameters.id, requestParameters.format, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @param {AssetApiGetByTimeBucketRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AssetApi + */ + public getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig) { + return AssetApiFp(this.configuration).getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); + } + /** * * @param {*} [options] Override http request option. @@ -7102,6 +7167,17 @@ export class AssetApi extends BaseAPI { return AssetApiFp(this.configuration).getMemoryLane(requestParameters.timestamp, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @param {AssetApiGetTimeBucketsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AssetApi + */ + public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig) { + return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); + } + /** * Get all asset of a device that are in the database, ID only. * @param {AssetApiGetUserAssetsByDeviceIdRequest} requestParameters Request parameters. diff --git a/mobile/openapi/.openapi-generator/FILES b/mobile/openapi/.openapi-generator/FILES index ed58e2bfe1..279bf838c6 100644 --- a/mobile/openapi/.openapi-generator/FILES +++ b/mobile/openapi/.openapi-generator/FILES @@ -19,8 +19,6 @@ doc/AssetBulkUploadCheckDto.md doc/AssetBulkUploadCheckItem.md doc/AssetBulkUploadCheckResponseDto.md doc/AssetBulkUploadCheckResult.md -doc/AssetCountByTimeBucket.md -doc/AssetCountByTimeBucketResponseDto.md doc/AssetFileUploadResponseDto.md doc/AssetIdsDto.md doc/AssetIdsResponseDto.md @@ -49,8 +47,6 @@ doc/DeleteAssetStatus.md doc/DownloadArchiveInfo.md doc/DownloadResponseDto.md doc/ExifResponseDto.md -doc/GetAssetByTimeBucketDto.md -doc/GetAssetCountByTimeBucketDto.md doc/ImportAssetDto.md doc/JobApi.md doc/JobCommand.md @@ -112,7 +108,8 @@ doc/TagApi.md doc/TagResponseDto.md doc/TagTypeEnum.md doc/ThumbnailFormat.md -doc/TimeGroupEnum.md +doc/TimeBucketResponseDto.md +doc/TimeBucketSize.md doc/TranscodeHWAccel.md doc/TranscodePolicy.md doc/UpdateAlbumDto.md @@ -162,8 +159,6 @@ lib/model/asset_bulk_upload_check_dto.dart lib/model/asset_bulk_upload_check_item.dart lib/model/asset_bulk_upload_check_response_dto.dart lib/model/asset_bulk_upload_check_result.dart -lib/model/asset_count_by_time_bucket.dart -lib/model/asset_count_by_time_bucket_response_dto.dart lib/model/asset_file_upload_response_dto.dart lib/model/asset_ids_dto.dart lib/model/asset_ids_response_dto.dart @@ -191,8 +186,6 @@ lib/model/delete_asset_status.dart lib/model/download_archive_info.dart lib/model/download_response_dto.dart lib/model/exif_response_dto.dart -lib/model/get_asset_by_time_bucket_dto.dart -lib/model/get_asset_count_by_time_bucket_dto.dart lib/model/import_asset_dto.dart lib/model/job_command.dart lib/model/job_command_dto.dart @@ -245,7 +238,8 @@ lib/model/system_config_template_storage_option_dto.dart lib/model/tag_response_dto.dart lib/model/tag_type_enum.dart lib/model/thumbnail_format.dart -lib/model/time_group_enum.dart +lib/model/time_bucket_response_dto.dart +lib/model/time_bucket_size.dart lib/model/transcode_hw_accel.dart lib/model/transcode_policy.dart lib/model/update_album_dto.dart @@ -274,8 +268,6 @@ test/asset_bulk_upload_check_dto_test.dart test/asset_bulk_upload_check_item_test.dart test/asset_bulk_upload_check_response_dto_test.dart test/asset_bulk_upload_check_result_test.dart -test/asset_count_by_time_bucket_response_dto_test.dart -test/asset_count_by_time_bucket_test.dart test/asset_file_upload_response_dto_test.dart test/asset_ids_dto_test.dart test/asset_ids_response_dto_test.dart @@ -304,8 +296,6 @@ test/delete_asset_status_test.dart test/download_archive_info_test.dart test/download_response_dto_test.dart test/exif_response_dto_test.dart -test/get_asset_by_time_bucket_dto_test.dart -test/get_asset_count_by_time_bucket_dto_test.dart test/import_asset_dto_test.dart test/job_api_test.dart test/job_command_dto_test.dart @@ -367,7 +357,8 @@ test/tag_api_test.dart test/tag_response_dto_test.dart test/tag_type_enum_test.dart test/thumbnail_format_test.dart -test/time_group_enum_test.dart +test/time_bucket_response_dto_test.dart +test/time_bucket_size_test.dart test/transcode_hw_accel_test.dart test/transcode_policy_test.dart test/update_album_dto_test.dart diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 7e2721348a..5114c85bdb 100644 Binary files a/mobile/openapi/README.md and b/mobile/openapi/README.md differ diff --git a/mobile/openapi/doc/AssetApi.md b/mobile/openapi/doc/AssetApi.md index 8f6912676c..b86097feab 100644 Binary files a/mobile/openapi/doc/AssetApi.md and b/mobile/openapi/doc/AssetApi.md differ diff --git a/mobile/openapi/doc/AssetCountByTimeBucketResponseDto.md b/mobile/openapi/doc/AssetCountByTimeBucketResponseDto.md deleted file mode 100644 index b8c7978084..0000000000 Binary files a/mobile/openapi/doc/AssetCountByTimeBucketResponseDto.md and /dev/null differ diff --git a/mobile/openapi/doc/GetAssetByTimeBucketDto.md b/mobile/openapi/doc/GetAssetByTimeBucketDto.md deleted file mode 100644 index da314c71fc..0000000000 Binary files a/mobile/openapi/doc/GetAssetByTimeBucketDto.md and /dev/null differ diff --git a/mobile/openapi/doc/GetAssetCountByTimeBucketDto.md b/mobile/openapi/doc/GetAssetCountByTimeBucketDto.md deleted file mode 100644 index 5ba4078539..0000000000 Binary files a/mobile/openapi/doc/GetAssetCountByTimeBucketDto.md and /dev/null differ diff --git a/mobile/openapi/doc/AssetCountByTimeBucket.md b/mobile/openapi/doc/TimeBucketResponseDto.md similarity index 91% rename from mobile/openapi/doc/AssetCountByTimeBucket.md rename to mobile/openapi/doc/TimeBucketResponseDto.md index 334e00bb7b..83eed89f5d 100644 Binary files a/mobile/openapi/doc/AssetCountByTimeBucket.md and b/mobile/openapi/doc/TimeBucketResponseDto.md differ diff --git a/mobile/openapi/doc/TimeGroupEnum.md b/mobile/openapi/doc/TimeBucketSize.md similarity index 91% rename from mobile/openapi/doc/TimeGroupEnum.md rename to mobile/openapi/doc/TimeBucketSize.md index d037b9f356..e89e68d74f 100644 Binary files a/mobile/openapi/doc/TimeGroupEnum.md and b/mobile/openapi/doc/TimeBucketSize.md differ diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 35310b4a60..d567aaedc6 100644 Binary files a/mobile/openapi/lib/api.dart and b/mobile/openapi/lib/api.dart differ diff --git a/mobile/openapi/lib/api/asset_api.dart b/mobile/openapi/lib/api/asset_api.dart index b4ac396d33..a82c8ffc46 100644 Binary files a/mobile/openapi/lib/api/asset_api.dart and b/mobile/openapi/lib/api/asset_api.dart differ diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index e5b7aaa875..a3145739ac 100644 Binary files a/mobile/openapi/lib/api_client.dart and b/mobile/openapi/lib/api_client.dart differ diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart index c41cea12b6..a6dccf564c 100644 Binary files a/mobile/openapi/lib/api_helper.dart and b/mobile/openapi/lib/api_helper.dart differ diff --git a/mobile/openapi/lib/model/asset_count_by_time_bucket_response_dto.dart b/mobile/openapi/lib/model/asset_count_by_time_bucket_response_dto.dart deleted file mode 100644 index 0375649b72..0000000000 Binary files a/mobile/openapi/lib/model/asset_count_by_time_bucket_response_dto.dart and /dev/null differ diff --git a/mobile/openapi/lib/model/get_asset_by_time_bucket_dto.dart b/mobile/openapi/lib/model/get_asset_by_time_bucket_dto.dart deleted file mode 100644 index e294097457..0000000000 Binary files a/mobile/openapi/lib/model/get_asset_by_time_bucket_dto.dart and /dev/null differ diff --git a/mobile/openapi/lib/model/get_asset_count_by_time_bucket_dto.dart b/mobile/openapi/lib/model/get_asset_count_by_time_bucket_dto.dart deleted file mode 100644 index e42d0d514c..0000000000 Binary files a/mobile/openapi/lib/model/get_asset_count_by_time_bucket_dto.dart and /dev/null differ diff --git a/mobile/openapi/lib/model/asset_count_by_time_bucket.dart b/mobile/openapi/lib/model/time_bucket_response_dto.dart similarity index 63% rename from mobile/openapi/lib/model/asset_count_by_time_bucket.dart rename to mobile/openapi/lib/model/time_bucket_response_dto.dart index 7dba56d82f..3731cd1bc1 100644 Binary files a/mobile/openapi/lib/model/asset_count_by_time_bucket.dart and b/mobile/openapi/lib/model/time_bucket_response_dto.dart differ diff --git a/mobile/openapi/lib/model/time_group_enum.dart b/mobile/openapi/lib/model/time_bucket_size.dart similarity index 51% rename from mobile/openapi/lib/model/time_group_enum.dart rename to mobile/openapi/lib/model/time_bucket_size.dart index c25e80dc99..c75789d75b 100644 Binary files a/mobile/openapi/lib/model/time_group_enum.dart and b/mobile/openapi/lib/model/time_bucket_size.dart differ diff --git a/mobile/openapi/test/asset_api_test.dart b/mobile/openapi/test/asset_api_test.dart index e09bb4d0f5..c2e9a5a788 100644 Binary files a/mobile/openapi/test/asset_api_test.dart and b/mobile/openapi/test/asset_api_test.dart differ diff --git a/mobile/openapi/test/asset_count_by_time_bucket_response_dto_test.dart b/mobile/openapi/test/asset_count_by_time_bucket_response_dto_test.dart deleted file mode 100644 index 3df5ba0d31..0000000000 Binary files a/mobile/openapi/test/asset_count_by_time_bucket_response_dto_test.dart and /dev/null differ diff --git a/mobile/openapi/test/get_asset_by_time_bucket_dto_test.dart b/mobile/openapi/test/get_asset_by_time_bucket_dto_test.dart deleted file mode 100644 index e6021df73c..0000000000 Binary files a/mobile/openapi/test/get_asset_by_time_bucket_dto_test.dart and /dev/null differ diff --git a/mobile/openapi/test/get_asset_count_by_time_bucket_dto_test.dart b/mobile/openapi/test/get_asset_count_by_time_bucket_dto_test.dart deleted file mode 100644 index ca0f586a6b..0000000000 Binary files a/mobile/openapi/test/get_asset_count_by_time_bucket_dto_test.dart and /dev/null differ diff --git a/mobile/openapi/test/asset_count_by_time_bucket_test.dart b/mobile/openapi/test/time_bucket_response_dto_test.dart similarity index 81% rename from mobile/openapi/test/asset_count_by_time_bucket_test.dart rename to mobile/openapi/test/time_bucket_response_dto_test.dart index 7dbe5bc4be..9a09f2017d 100644 Binary files a/mobile/openapi/test/asset_count_by_time_bucket_test.dart and b/mobile/openapi/test/time_bucket_response_dto_test.dart differ diff --git a/mobile/openapi/test/time_group_enum_test.dart b/mobile/openapi/test/time_bucket_size_test.dart similarity index 84% rename from mobile/openapi/test/time_group_enum_test.dart rename to mobile/openapi/test/time_bucket_size_test.dart index 3c23d32333..8f0ca35b99 100644 Binary files a/mobile/openapi/test/time_group_enum_test.dart and b/mobile/openapi/test/time_bucket_size_test.dart differ diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 09091b9191..7c83acb84d 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -948,48 +948,6 @@ ] } }, - "/asset/count-by-time-bucket": { - "post": { - "operationId": "getAssetCountByTimeBucket", - "parameters": [], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GetAssetCountByTimeBucketDto" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AssetCountByTimeBucketResponseDto" - } - } - }, - "description": "" - } - }, - "security": [ - { - "bearer": [] - }, - { - "cookie": [] - }, - { - "api_key": [] - } - ], - "tags": [ - "Asset" - ] - } - }, "/asset/curated-locations": { "get": { "operationId": "getCuratedLocations", @@ -1697,19 +1655,68 @@ } }, "/asset/time-bucket": { - "post": { - "operationId": "getAssetByTimeBucket", - "parameters": [], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GetAssetByTimeBucketDto" - } + "get": { + "operationId": "getByTimeBucket", + "parameters": [ + { + "name": "size", + "required": true, + "in": "query", + "schema": { + "$ref": "#/components/schemas/TimeBucketSize" } }, - "required": true - }, + { + "name": "userId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "albumId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "isArchived", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isFavorite", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "timeBucket", + "required": true, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "key", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "content": { @@ -1726,6 +1733,110 @@ } }, "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + }, + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Asset" + ] + } + }, + "/asset/time-buckets": { + "get": { + "operationId": "getTimeBuckets", + "parameters": [ + { + "name": "size", + "required": true, + "in": "query", + "schema": { + "$ref": "#/components/schemas/TimeBucketSize" + } + }, + { + "name": "userId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "albumId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "isArchived", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isFavorite", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "key", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/TimeBucketResponseDto" + }, + "type": "array" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + }, { "bearer": [] }, @@ -4787,39 +4898,6 @@ ], "type": "object" }, - "AssetCountByTimeBucket": { - "properties": { - "count": { - "type": "integer" - }, - "timeBucket": { - "type": "string" - } - }, - "required": [ - "timeBucket", - "count" - ], - "type": "object" - }, - "AssetCountByTimeBucketResponseDto": { - "properties": { - "buckets": { - "items": { - "$ref": "#/components/schemas/AssetCountByTimeBucket" - }, - "type": "array" - }, - "totalCount": { - "type": "integer" - } - }, - "required": [ - "totalCount", - "buckets" - ], - "type": "object" - }, "AssetFileUploadResponseDto": { "properties": { "duplicate": { @@ -5554,53 +5632,6 @@ }, "type": "object" }, - "GetAssetByTimeBucketDto": { - "properties": { - "timeBucket": { - "example": [ - "2015-06-01T00:00:00.000Z", - "2016-02-01T00:00:00.000Z", - "2016-03-01T00:00:00.000Z" - ], - "items": { - "type": "string" - }, - "title": "Array of date time buckets", - "type": "array" - }, - "userId": { - "format": "uuid", - "type": "string" - }, - "withoutThumbs": { - "description": "Include assets without thumbnails", - "type": "boolean" - } - }, - "required": [ - "timeBucket" - ], - "type": "object" - }, - "GetAssetCountByTimeBucketDto": { - "properties": { - "timeGroup": { - "$ref": "#/components/schemas/TimeGroupEnum" - }, - "userId": { - "format": "uuid", - "type": "string" - }, - "withoutThumbs": { - "description": "Include assets without thumbnails", - "type": "boolean" - } - }, - "required": [ - "timeGroup" - ], - "type": "object" - }, "ImportAssetDto": { "properties": { "assetPath": { @@ -6806,10 +6837,25 @@ ], "type": "string" }, - "TimeGroupEnum": { + "TimeBucketResponseDto": { + "properties": { + "count": { + "type": "integer" + }, + "timeBucket": { + "type": "string" + } + }, + "required": [ + "timeBucket", + "count" + ], + "type": "object" + }, + "TimeBucketSize": { "enum": [ - "day", - "month" + "DAY", + "MONTH" ], "type": "string" }, diff --git a/server/src/domain/asset/asset.repository.ts b/server/src/domain/asset/asset.repository.ts index 3687ec9cc2..6bc5d1f038 100644 --- a/server/src/domain/asset/asset.repository.ts +++ b/server/src/domain/asset/asset.repository.ts @@ -47,6 +47,23 @@ export enum WithProperty { SIDECAR = 'sidecar', } +export enum TimeBucketSize { + DAY = 'DAY', + MONTH = 'MONTH', +} + +export interface TimeBucketOptions { + size: TimeBucketSize; + isArchived?: boolean; + isFavorite?: boolean; + albumId?: string; +} + +export interface TimeBucketItem { + timeBucket: string; + count: number; +} + export const IAssetRepository = 'IAssetRepository'; export interface IAssetRepository { @@ -64,4 +81,6 @@ export interface IAssetRepository { findLivePhotoMatch(options: LivePhotoSearchOptions): Promise; getMapMarkers(ownerId: string, options?: MapMarkerSearchOptions): Promise; getStatistics(ownerId: string, options: AssetStatsOptions): Promise; + getTimeBuckets(userId: string, options: TimeBucketOptions): Promise; + getByTimeBucket(userId: string, timeBucket: string, options: TimeBucketOptions): Promise; } diff --git a/server/src/domain/asset/asset.service.ts b/server/src/domain/asset/asset.service.ts index cb2701ea12..560a0d85f9 100644 --- a/server/src/domain/asset/asset.service.ts +++ b/server/src/domain/asset/asset.service.ts @@ -10,11 +10,20 @@ import { mimeTypes } from '../domain.constant'; import { HumanReadableSize, usePagination } from '../domain.util'; import { ImmichReadStream, IStorageRepository, StorageCore, StorageFolder } from '../storage'; import { IAssetRepository } from './asset.repository'; -import { AssetIdsDto, DownloadArchiveInfo, DownloadDto, DownloadResponseDto, MemoryLaneDto } from './dto'; +import { + AssetIdsDto, + DownloadArchiveInfo, + DownloadDto, + DownloadResponseDto, + MemoryLaneDto, + TimeBucketAssetDto, + TimeBucketDto, +} from './dto'; import { AssetStatsDto, mapStats } from './dto/asset-statistics.dto'; import { MapMarkerDto } from './dto/map-marker.dto'; -import { mapAsset, MapMarkerResponseDto } from './response-dto'; +import { AssetResponseDto, mapAsset, MapMarkerResponseDto } from './response-dto'; import { MemoryLaneResponseDto } from './response-dto/memory-lane-response.dto'; +import { TimeBucketResponseDto } from './response-dto/time-bucket-response.dto'; export enum UploadFieldName { ASSET_DATA = 'assetData', @@ -135,6 +144,21 @@ export class AssetService { return Promise.all(requests).then((results) => results.filter((result) => result.assets.length > 0)); } + async getTimeBuckets(authUser: AuthUserDto, dto: TimeBucketDto): Promise { + const { userId, ...options } = dto; + const targetId = userId || authUser.id; + await this.access.requirePermission(authUser, Permission.LIBRARY_READ, [targetId]); + return this.assetRepository.getTimeBuckets(targetId, options); + } + + async getByTimeBucket(authUser: AuthUserDto, dto: TimeBucketAssetDto): Promise { + const { userId, timeBucket, ...options } = dto; + const targetId = userId || authUser.id; + await this.access.requirePermission(authUser, Permission.LIBRARY_READ, [targetId]); + const assets = await this.assetRepository.getByTimeBucket(targetId, timeBucket, options); + return assets.map(mapAsset); + } + async downloadFile(authUser: AuthUserDto, id: string): Promise { await this.access.requirePermission(authUser, Permission.ASSET_DOWNLOAD, id); diff --git a/server/src/domain/asset/dto/index.ts b/server/src/domain/asset/dto/index.ts index f22534d35e..8e9440f027 100644 --- a/server/src/domain/asset/dto/index.ts +++ b/server/src/domain/asset/dto/index.ts @@ -3,3 +3,4 @@ export * from './asset-statistics.dto'; export * from './download.dto'; export * from './map-marker.dto'; export * from './memory-lane.dto'; +export * from './time-bucket.dto'; diff --git a/server/src/domain/asset/dto/time-bucket.dto.ts b/server/src/domain/asset/dto/time-bucket.dto.ts new file mode 100644 index 0000000000..c42366f39a --- /dev/null +++ b/server/src/domain/asset/dto/time-bucket.dto.ts @@ -0,0 +1,34 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { toBoolean, ValidateUUID } from '../../domain.util'; +import { TimeBucketSize } from '../asset.repository'; + +export class TimeBucketDto { + @IsNotEmpty() + @IsEnum(TimeBucketSize) + @ApiProperty({ enum: TimeBucketSize, enumName: 'TimeBucketSize' }) + size!: TimeBucketSize; + + @ValidateUUID({ optional: true }) + userId?: string; + + @ValidateUUID({ optional: true }) + albumId?: string; + + @IsOptional() + @IsBoolean() + @Transform(toBoolean) + isArchived?: boolean; + + @IsOptional() + @IsBoolean() + @Transform(toBoolean) + isFavorite?: boolean; +} + +export class TimeBucketAssetDto extends TimeBucketDto { + @IsString() + @IsNotEmpty() + timeBucket!: string; +} diff --git a/server/src/domain/asset/response-dto/index.ts b/server/src/domain/asset/response-dto/index.ts index b82249d2ba..7ed99db130 100644 --- a/server/src/domain/asset/response-dto/index.ts +++ b/server/src/domain/asset/response-dto/index.ts @@ -3,3 +3,4 @@ export * from './asset-response.dto'; export * from './exif-response.dto'; export * from './map-marker-response.dto'; export * from './smart-info-response.dto'; +export * from './time-bucket-response.dto'; diff --git a/server/src/domain/asset/response-dto/time-bucket-response.dto.ts b/server/src/domain/asset/response-dto/time-bucket-response.dto.ts new file mode 100644 index 0000000000..e143dde464 --- /dev/null +++ b/server/src/domain/asset/response-dto/time-bucket-response.dto.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class TimeBucketResponseDto { + @ApiProperty({ type: 'string' }) + timeBucket!: string; + + @ApiProperty({ type: 'integer' }) + count!: number; +} diff --git a/server/src/immich/api-v1/asset/asset-repository.ts b/server/src/immich/api-v1/asset/asset-repository.ts index 22c25d6ef4..ce85bd369b 100644 --- a/server/src/immich/api-v1/asset/asset-repository.ts +++ b/server/src/immich/api-v1/asset/asset-repository.ts @@ -6,11 +6,8 @@ import { In } from 'typeorm/find-options/operator/In'; import { Repository } from 'typeorm/repository/Repository'; import { AssetSearchDto } from './dto/asset-search.dto'; import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; -import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto'; -import { GetAssetCountByTimeBucketDto, TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto'; import { SearchPropertiesDto } from './dto/search-properties.dto'; import { UpdateAssetDto } from './dto/update-asset.dto'; -import { AssetCountByTimeBucket } from './response-dto/asset-count-by-time-group-response.dto'; import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto'; import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto'; @@ -36,8 +33,6 @@ export interface IAssetRepository { getLocationsByUserId(userId: string): Promise; getDetectedObjectsByUserId(userId: string): Promise; getSearchPropertiesByUserId(userId: string): Promise; - getAssetCountByTimeBucket(userId: string, dto: GetAssetCountByTimeBucketDto): Promise; - getAssetByTimeBucket(userId: string, getAssetByTimeBucketDto: GetAssetByTimeBucketDto): Promise; getAssetsByChecksums(userId: string, checksums: Buffer[]): Promise; getExistingAssets(userId: string, checkDuplicateAssetDto: CheckExistingAssetsDto): Promise; getByOriginalPath(originalPath: string): Promise; @@ -52,57 +47,6 @@ export class AssetRepository implements IAssetRepository { @InjectRepository(ExifEntity) private exifRepository: Repository, ) {} - async getAssetByTimeBucket(userId: string, dto: GetAssetByTimeBucketDto): Promise { - // Get asset entity from a list of time buckets - let builder = this.assetRepository - .createQueryBuilder('asset') - .leftJoinAndSelect('asset.exifInfo', 'exifInfo') - .where('asset.ownerId = :userId', { userId: userId }) - .andWhere(`date_trunc('month', "fileCreatedAt") IN (:...buckets)`, { - buckets: [...dto.timeBucket], - }) - .andWhere('asset.isVisible = true') - .andWhere('asset.isArchived = false') - .orderBy('asset.fileCreatedAt', 'DESC'); - - if (!dto.withoutThumbs) { - builder = builder.andWhere('asset.resizePath is not NULL'); - } - - return builder.getMany(); - } - - async getAssetCountByTimeBucket( - userId: string, - dto: GetAssetCountByTimeBucketDto, - ): Promise { - const builder = this.assetRepository - .createQueryBuilder('asset') - .select(`COUNT(asset.id)::int`, 'count') - .where('"ownerId" = :userId', { userId: userId }) - .andWhere('asset.isVisible = true') - .andWhere('asset.isArchived = false'); - - // Using a parameter for this doesn't work https://github.com/typeorm/typeorm/issues/7308 - if (dto.timeGroup === TimeGroupEnum.Month) { - builder - .addSelect(`date_trunc('month', "fileCreatedAt")`, 'timeBucket') - .groupBy(`date_trunc('month', "fileCreatedAt")`) - .orderBy(`date_trunc('month', "fileCreatedAt")`, 'DESC'); - } else if (dto.timeGroup === TimeGroupEnum.Day) { - builder - .addSelect(`date_trunc('day', "fileCreatedAt")`, 'timeBucket') - .groupBy(`date_trunc('day', "fileCreatedAt")`) - .orderBy(`date_trunc('day', "fileCreatedAt")`, 'DESC'); - } - - if (!dto.withoutThumbs) { - builder.andWhere('asset.resizePath is not NULL'); - } - - return builder.getRawMany(); - } - getSearchPropertiesByUserId(userId: string): Promise { return this.assetRepository .createQueryBuilder('asset') diff --git a/server/src/immich/api-v1/asset/asset.controller.ts b/server/src/immich/api-v1/asset/asset.controller.ts index 1e22bf3baa..44973a7af3 100644 --- a/server/src/immich/api-v1/asset/asset.controller.ts +++ b/server/src/immich/api-v1/asset/asset.controller.ts @@ -30,14 +30,11 @@ import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; import { CreateAssetDto, ImportAssetDto } from './dto/create-asset.dto'; import { DeleteAssetDto } from './dto/delete-asset.dto'; import { DeviceIdDto } from './dto/device-id.dto'; -import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto'; -import { GetAssetCountByTimeBucketDto } from './dto/get-asset-count-by-time-bucket.dto'; import { GetAssetThumbnailDto } from './dto/get-asset-thumbnail.dto'; import { SearchAssetDto } from './dto/search-asset.dto'; import { ServeFileDto } from './dto/serve-file.dto'; import { UpdateAssetDto } from './dto/update-asset.dto'; import { AssetBulkUploadCheckResponseDto } from './response-dto/asset-check-response.dto'; -import { AssetCountByTimeBucketResponseDto } from './response-dto/asset-count-by-time-group-response.dto'; import { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto'; import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-asset-response.dto'; import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto'; @@ -163,15 +160,6 @@ export class AssetController { return this.assetService.searchAsset(authUser, dto); } - @Post('/count-by-time-bucket') - @HttpCode(HttpStatus.OK) - getAssetCountByTimeBucket( - @AuthUser() authUser: AuthUserDto, - @Body(ValidationPipe) dto: GetAssetCountByTimeBucketDto, - ): Promise { - return this.assetService.getAssetCountByTimeBucket(authUser, dto); - } - /** * Get all AssetEntity belong to the user */ @@ -189,15 +177,6 @@ export class AssetController { return this.assetService.getAllAssets(authUser, dto); } - @Post('/time-bucket') - @HttpCode(HttpStatus.OK) - getAssetByTimeBucket( - @AuthUser() authUser: AuthUserDto, - @Body(ValidationPipe) dto: GetAssetByTimeBucketDto, - ): Promise { - return this.assetService.getAssetByTimeBucket(authUser, dto); - } - /** * Get all asset of a device that are in the database, ID only. */ diff --git a/server/src/immich/api-v1/asset/asset.service.spec.ts b/server/src/immich/api-v1/asset/asset.service.spec.ts index bf7441b4ad..3b8f9a1b9d 100644 --- a/server/src/immich/api-v1/asset/asset.service.spec.ts +++ b/server/src/immich/api-v1/asset/asset.service.spec.ts @@ -16,9 +16,7 @@ import { QueryFailedError, Repository } from 'typeorm'; import { IAssetRepository } from './asset-repository'; import { AssetService } from './asset.service'; import { CreateAssetDto } from './dto/create-asset.dto'; -import { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto'; import { AssetRejectReason, AssetUploadAction } from './response-dto/asset-check-response.dto'; -import { AssetCountByTimeBucket } from './response-dto/asset-count-by-time-group-response.dto'; const _getCreateAssetDto = (): CreateAssetDto => { const createAssetDto = new CreateAssetDto(); @@ -83,18 +81,6 @@ const _getAssets = () => { return [_getAsset_1(), _getAsset_2()]; }; -const _getAssetCountByTimeBucket = (): AssetCountByTimeBucket[] => { - const result1 = new AssetCountByTimeBucket(); - result1.count = 2; - result1.timeBucket = '2022-06-01T00:00:00.000Z'; - - const result2 = new AssetCountByTimeBucket(); - result1.count = 5; - result1.timeBucket = '2022-07-01T00:00:00.000Z'; - - return [result1, result2]; -}; - describe('AssetService', () => { let sut: AssetService; let a: Repository; // TO BE DELETED AFTER FINISHED REFACTORING @@ -113,12 +99,10 @@ describe('AssetService', () => { update: jest.fn(), getAllByUserId: jest.fn(), getAllByDeviceId: jest.fn(), - getAssetCountByTimeBucket: jest.fn(), getById: jest.fn(), getDetectedObjectsByUserId: jest.fn(), getLocationsByUserId: jest.fn(), getSearchPropertiesByUserId: jest.fn(), - getAssetByTimeBucket: jest.fn(), getAssetsByChecksums: jest.fn(), getExistingAssets: jest.fn(), getByOriginalPath: jest.fn(), @@ -221,21 +205,6 @@ describe('AssetService', () => { expect(result).toEqual(assets.map((asset) => asset.deviceAssetId)); }); - it('get assets count by time bucket', async () => { - const assetCountByTimeBucket = _getAssetCountByTimeBucket(); - - assetRepositoryMock.getAssetCountByTimeBucket.mockImplementation(() => - Promise.resolve(assetCountByTimeBucket), - ); - - const result = await sut.getAssetCountByTimeBucket(authStub.user1, { - timeGroup: TimeGroupEnum.Month, - }); - - expect(result.totalCount).toEqual(assetCountByTimeBucket.reduce((a, b) => a + b.count, 0)); - expect(result.buckets.length).toEqual(2); - }); - describe('deleteAll', () => { it('should return failed status when an asset is missing', async () => { assetRepositoryMock.get.mockResolvedValue(null); diff --git a/server/src/immich/api-v1/asset/asset.service.ts b/server/src/immich/api-v1/asset/asset.service.ts index 7fe2bb80ee..b397b2688b 100644 --- a/server/src/immich/api-v1/asset/asset.service.ts +++ b/server/src/immich/api-v1/asset/asset.service.ts @@ -37,8 +37,6 @@ import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto'; import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; import { CreateAssetDto, ImportAssetDto } from './dto/create-asset.dto'; import { DeleteAssetDto } from './dto/delete-asset.dto'; -import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto'; -import { GetAssetCountByTimeBucketDto } from './dto/get-asset-count-by-time-bucket.dto'; import { GetAssetThumbnailDto, GetAssetThumbnailFormatEnum } from './dto/get-asset-thumbnail.dto'; import { SearchAssetDto } from './dto/search-asset.dto'; import { SearchPropertiesDto } from './dto/search-properties.dto'; @@ -49,10 +47,6 @@ import { AssetRejectReason, AssetUploadAction, } from './response-dto/asset-check-response.dto'; -import { - AssetCountByTimeBucketResponseDto, - mapAssetCountByTimeBucket, -} from './response-dto/asset-count-by-time-group-response.dto'; import { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto'; import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-asset-response.dto'; import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto'; @@ -195,13 +189,6 @@ export class AssetService { return assets.map((asset) => mapAsset(asset)); } - public async getAssetByTimeBucket(authUser: AuthUserDto, dto: GetAssetByTimeBucketDto): Promise { - const userId = dto.userId || authUser.id; - await this.access.requirePermission(authUser, Permission.LIBRARY_READ, userId); - const assets = await this._assetRepository.getAssetByTimeBucket(userId, dto); - return assets.map((asset) => mapAsset(asset)); - } - public async getAssetById(authUser: AuthUserDto, assetId: string): Promise { await this.access.requirePermission(authUser, Permission.ASSET_READ, assetId); @@ -457,16 +444,6 @@ export class AssetService { }; } - async getAssetCountByTimeBucket( - authUser: AuthUserDto, - dto: GetAssetCountByTimeBucketDto, - ): Promise { - const userId = dto.userId || authUser.id; - await this.access.requirePermission(authUser, Permission.LIBRARY_READ, userId); - const result = await this._assetRepository.getAssetCountByTimeBucket(userId, dto); - return mapAssetCountByTimeBucket(result); - } - getExifPermission(authUser: AuthUserDto) { return !authUser.isPublicUser || authUser.isShowExif; } diff --git a/server/src/immich/api-v1/asset/dto/get-asset-by-time-bucket.dto.ts b/server/src/immich/api-v1/asset/dto/get-asset-by-time-bucket.dto.ts deleted file mode 100644 index ddf2d2aa51..0000000000 --- a/server/src/immich/api-v1/asset/dto/get-asset-by-time-bucket.dto.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { toBoolean } from '@app/domain'; -import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsBoolean, IsNotEmpty, IsOptional, IsUUID } from 'class-validator'; - -export class GetAssetByTimeBucketDto { - @IsNotEmpty() - @ApiProperty({ - isArray: true, - type: String, - title: 'Array of date time buckets', - example: ['2015-06-01T00:00:00.000Z', '2016-02-01T00:00:00.000Z', '2016-03-01T00:00:00.000Z'], - }) - timeBucket!: string[]; - - @IsOptional() - @IsUUID('4') - @ApiProperty({ format: 'uuid' }) - userId?: string; - - /** - * Include assets without thumbnails - */ - @IsOptional() - @IsBoolean() - @Transform(toBoolean) - withoutThumbs?: boolean; -} diff --git a/server/src/immich/api-v1/asset/dto/get-asset-count-by-time-bucket.dto.ts b/server/src/immich/api-v1/asset/dto/get-asset-count-by-time-bucket.dto.ts deleted file mode 100644 index f1f564a3b5..0000000000 --- a/server/src/immich/api-v1/asset/dto/get-asset-count-by-time-bucket.dto.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { toBoolean } from '@app/domain'; -import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsBoolean, IsNotEmpty, IsOptional, IsUUID } from 'class-validator'; - -export enum TimeGroupEnum { - Day = 'day', - Month = 'month', -} - -export class GetAssetCountByTimeBucketDto { - @IsNotEmpty() - @ApiProperty({ - type: String, - enum: TimeGroupEnum, - enumName: 'TimeGroupEnum', - }) - timeGroup!: TimeGroupEnum; - - @IsOptional() - @IsUUID('4') - @ApiProperty({ format: 'uuid' }) - userId?: string; - - /** - * Include assets without thumbnails - */ - @IsOptional() - @IsBoolean() - @Transform(toBoolean) - withoutThumbs?: boolean; -} diff --git a/server/src/immich/api-v1/asset/response-dto/asset-count-by-time-group-response.dto.ts b/server/src/immich/api-v1/asset/response-dto/asset-count-by-time-group-response.dto.ts deleted file mode 100644 index 97579d72c3..0000000000 --- a/server/src/immich/api-v1/asset/response-dto/asset-count-by-time-group-response.dto.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; - -export class AssetCountByTimeBucket { - @ApiProperty({ type: 'string' }) - timeBucket!: string; - - @ApiProperty({ type: 'integer' }) - count!: number; -} - -export class AssetCountByTimeBucketResponseDto { - buckets!: AssetCountByTimeBucket[]; - - @ApiProperty({ type: 'integer' }) - totalCount!: number; -} - -export function mapAssetCountByTimeBucket(result: AssetCountByTimeBucket[]): AssetCountByTimeBucketResponseDto { - return { - buckets: result, - totalCount: result.map((group) => group.count).reduce((a, b) => a + b, 0), - }; -} diff --git a/server/src/immich/controllers/asset.controller.ts b/server/src/immich/controllers/asset.controller.ts index 5c4e19ccf2..ba3de02cc5 100644 --- a/server/src/immich/controllers/asset.controller.ts +++ b/server/src/immich/controllers/asset.controller.ts @@ -1,5 +1,6 @@ import { AssetIdsDto, + AssetResponseDto, AssetService, AssetStatsDto, AssetStatsResponseDto, @@ -8,6 +9,9 @@ import { DownloadResponseDto, MapMarkerResponseDto, MemoryLaneDto, + TimeBucketAssetDto, + TimeBucketDto, + TimeBucketResponseDto, } from '@app/domain'; import { MapMarkerDto } from '@app/domain/asset/dto/map-marker.dto'; import { MemoryLaneResponseDto } from '@app/domain/asset/response-dto/memory-lane-response.dto'; @@ -60,4 +64,16 @@ export class AssetController { getAssetStats(@AuthUser() authUser: AuthUserDto, @Query() dto: AssetStatsDto): Promise { return this.service.getStatistics(authUser, dto); } + + @Authenticated({ isShared: true }) + @Get('time-buckets') + getTimeBuckets(@AuthUser() authUser: AuthUserDto, @Query() dto: TimeBucketDto): Promise { + return this.service.getTimeBuckets(authUser, dto); + } + + @Authenticated({ isShared: true }) + @Get('time-bucket') + getByTimeBucket(@AuthUser() authUser: AuthUserDto, @Query() dto: TimeBucketAssetDto): Promise { + return this.service.getByTimeBucket(authUser, dto); + } } diff --git a/server/src/infra/repositories/asset.repository.ts b/server/src/infra/repositories/asset.repository.ts index 82fd0abc31..e5f997f56c 100644 --- a/server/src/infra/repositories/asset.repository.ts +++ b/server/src/infra/repositories/asset.repository.ts @@ -8,6 +8,9 @@ import { MapMarkerSearchOptions, Paginated, PaginationOptions, + TimeBucketItem, + TimeBucketOptions, + TimeBucketSize, WithoutProperty, WithProperty, } from '@app/domain'; @@ -19,6 +22,11 @@ import { AssetEntity, AssetType } from '../entities'; import OptionalBetween from '../utils/optional-between.util'; import { paginate } from '../utils/pagination.util'; +const truncateMap: Record = { + [TimeBucketSize.DAY]: 'day', + [TimeBucketSize.MONTH]: 'month', +}; + @Injectable() export class AssetRepository implements IAssetRepository { constructor(@InjectRepository(AssetEntity) private repository: Repository) {} @@ -357,4 +365,47 @@ export class AssetRepository implements IAssetRepository { return result; } + + getTimeBuckets(userId: string, options: TimeBucketOptions): Promise { + const truncateValue = truncateMap[options.size]; + + return this.getBuilder(userId, options) + .select(`COUNT(asset.id)::int`, 'count') + .addSelect(`date_trunc('${truncateValue}', "fileCreatedAt")`, 'timeBucket') + .groupBy(`date_trunc('${truncateValue}', "fileCreatedAt")`) + .orderBy(`date_trunc('${truncateValue}', "fileCreatedAt")`, 'DESC') + .getRawMany(); + } + + getByTimeBucket(userId: string, timeBucket: string, options: TimeBucketOptions): Promise { + const truncateValue = truncateMap[options.size]; + return this.getBuilder(userId, options) + .andWhere(`date_trunc('${truncateValue}', "fileCreatedAt") = :timeBucket`, { timeBucket }) + .orderBy('asset.fileCreatedAt', 'DESC') + .getMany(); + } + + private getBuilder(userId: string, options: TimeBucketOptions) { + const { isArchived, isFavorite, albumId } = options; + + let builder = this.repository + .createQueryBuilder('asset') + .where('asset.ownerId = :userId', { userId }) + .andWhere('asset.isVisible = true') + .leftJoinAndSelect('asset.exifInfo', 'exifInfo'); + + if (albumId) { + builder = builder.leftJoin('asset.albums', 'album').andWhere('album.id = :albumId', { albumId }); + } + + if (isArchived != undefined) { + builder = builder.andWhere('asset.isArchived = :isArchived', { isArchived }); + } + + if (isFavorite !== undefined) { + builder = builder.andWhere('asset.isFavorite = :isFavorite', { isFavorite }); + } + + return builder; + } } diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index 5eb69a9e5a..7f1eac8319 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -10,14 +10,13 @@ export const newAssetRepositoryMock = (): jest.Mocked => { getWith: jest.fn(), getFirstAssetForAlbumId: jest.fn(), getLastUpdatedAssetForAlbumId: jest.fn(), - getAll: jest.fn().mockResolvedValue({ - items: [], - hasNextPage: false, - }), + getAll: jest.fn().mockResolvedValue({ items: [], hasNextPage: false }), deleteAll: jest.fn(), save: jest.fn(), findLivePhotoMatch: jest.fn(), getMapMarkers: jest.fn(), getStatistics: jest.fn(), + getByTimeBucket: jest.fn(), + getTimeBuckets: jest.fn(), }; }; diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index 2d330f6f27..55549dba1b 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -410,44 +410,6 @@ export const AssetBulkUploadCheckResultReasonEnum = { export type AssetBulkUploadCheckResultReasonEnum = typeof AssetBulkUploadCheckResultReasonEnum[keyof typeof AssetBulkUploadCheckResultReasonEnum]; -/** - * - * @export - * @interface AssetCountByTimeBucket - */ -export interface AssetCountByTimeBucket { - /** - * - * @type {number} - * @memberof AssetCountByTimeBucket - */ - 'count': number; - /** - * - * @type {string} - * @memberof AssetCountByTimeBucket - */ - 'timeBucket': string; -} -/** - * - * @export - * @interface AssetCountByTimeBucketResponseDto - */ -export interface AssetCountByTimeBucketResponseDto { - /** - * - * @type {Array} - * @memberof AssetCountByTimeBucketResponseDto - */ - 'buckets': Array; - /** - * - * @type {number} - * @memberof AssetCountByTimeBucketResponseDto - */ - 'totalCount': number; -} /** * * @export @@ -1286,58 +1248,6 @@ export interface ExifResponseDto { */ 'timeZone'?: string | null; } -/** - * - * @export - * @interface GetAssetByTimeBucketDto - */ -export interface GetAssetByTimeBucketDto { - /** - * - * @type {Array} - * @memberof GetAssetByTimeBucketDto - */ - 'timeBucket': Array; - /** - * - * @type {string} - * @memberof GetAssetByTimeBucketDto - */ - 'userId'?: string; - /** - * Include assets without thumbnails - * @type {boolean} - * @memberof GetAssetByTimeBucketDto - */ - 'withoutThumbs'?: boolean; -} -/** - * - * @export - * @interface GetAssetCountByTimeBucketDto - */ -export interface GetAssetCountByTimeBucketDto { - /** - * - * @type {TimeGroupEnum} - * @memberof GetAssetCountByTimeBucketDto - */ - 'timeGroup': TimeGroupEnum; - /** - * - * @type {string} - * @memberof GetAssetCountByTimeBucketDto - */ - 'userId'?: string; - /** - * Include assets without thumbnails - * @type {boolean} - * @memberof GetAssetCountByTimeBucketDto - */ - 'withoutThumbs'?: boolean; -} - - /** * * @export @@ -2850,18 +2760,37 @@ export const ThumbnailFormat = { export type ThumbnailFormat = typeof ThumbnailFormat[keyof typeof ThumbnailFormat]; +/** + * + * @export + * @interface TimeBucketResponseDto + */ +export interface TimeBucketResponseDto { + /** + * + * @type {number} + * @memberof TimeBucketResponseDto + */ + 'count': number; + /** + * + * @type {string} + * @memberof TimeBucketResponseDto + */ + 'timeBucket': string; +} /** * * @export * @enum {string} */ -export const TimeGroupEnum = { - Day: 'day', - Month: 'month' +export const TimeBucketSize = { + Day: 'DAY', + Month: 'MONTH' } as const; -export type TimeGroupEnum = typeof TimeGroupEnum[keyof typeof TimeGroupEnum]; +export type TimeBucketSize = typeof TimeBucketSize[keyof typeof TimeBucketSize]; /** @@ -5047,94 +4976,6 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration options: localVarRequestOptions, }; }, - /** - * - * @param {GetAssetByTimeBucketDto} getAssetByTimeBucketDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetByTimeBucket: async (getAssetByTimeBucketDto: GetAssetByTimeBucketDto, options: AxiosRequestConfig = {}): Promise => { - // verify required parameter 'getAssetByTimeBucketDto' is not null or undefined - assertParamExists('getAssetByTimeBucket', 'getAssetByTimeBucketDto', getAssetByTimeBucketDto) - const localVarPath = `/asset/time-bucket`; - // 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(getAssetByTimeBucketDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * - * @param {GetAssetCountByTimeBucketDto} getAssetCountByTimeBucketDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetCountByTimeBucket: async (getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto, options: AxiosRequestConfig = {}): Promise => { - // verify required parameter 'getAssetCountByTimeBucketDto' is not null or undefined - assertParamExists('getAssetCountByTimeBucket', 'getAssetCountByTimeBucketDto', getAssetCountByTimeBucketDto) - const localVarPath = `/asset/count-by-time-bucket`; - // 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(getAssetCountByTimeBucketDto, localVarRequestOptions, configuration) - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, /** * * @param {*} [options] Override http request option. @@ -5264,6 +5105,83 @@ 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 {TimeBucketSize} size + * @param {string} timeBucket + * @param {string} [userId] + * @param {string} [albumId] + * @param {boolean} [isArchived] + * @param {boolean} [isFavorite] + * @param {string} [key] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getByTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'size' is not null or undefined + assertParamExists('getByTimeBucket', 'size', size) + // verify required parameter 'timeBucket' is not null or undefined + assertParamExists('getByTimeBucket', 'timeBucket', timeBucket) + const localVarPath = `/asset/time-bucket`; + // 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: 'GET', ...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) + + if (size !== undefined) { + localVarQueryParameter['size'] = size; + } + + if (userId !== undefined) { + localVarQueryParameter['userId'] = userId; + } + + if (albumId !== undefined) { + localVarQueryParameter['albumId'] = albumId; + } + + if (isArchived !== undefined) { + localVarQueryParameter['isArchived'] = isArchived; + } + + if (isFavorite !== undefined) { + localVarQueryParameter['isFavorite'] = isFavorite; + } + + if (timeBucket !== undefined) { + localVarQueryParameter['timeBucket'] = timeBucket; + } + + if (key !== undefined) { + localVarQueryParameter['key'] = key; + } + + + setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; @@ -5507,6 +5425,76 @@ 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 {TimeBucketSize} size + * @param {string} [userId] + * @param {string} [albumId] + * @param {boolean} [isArchived] + * @param {boolean} [isFavorite] + * @param {string} [key] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'size' is not null or undefined + assertParamExists('getTimeBuckets', 'size', size) + const localVarPath = `/asset/time-buckets`; + // 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: 'GET', ...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) + + if (size !== undefined) { + localVarQueryParameter['size'] = size; + } + + if (userId !== undefined) { + localVarQueryParameter['userId'] = userId; + } + + if (albumId !== undefined) { + localVarQueryParameter['albumId'] = albumId; + } + + if (isArchived !== undefined) { + localVarQueryParameter['isArchived'] = isArchived; + } + + if (isFavorite !== undefined) { + localVarQueryParameter['isFavorite'] = isFavorite; + } + + if (key !== undefined) { + localVarQueryParameter['key'] = key; + } + + + setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; @@ -5969,26 +5957,6 @@ export const AssetApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetById(id, key, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, - /** - * - * @param {GetAssetByTimeBucketDto} getAssetByTimeBucketDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAssetByTimeBucket(getAssetByTimeBucketDto: GetAssetByTimeBucketDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetByTimeBucket(getAssetByTimeBucketDto, options); - return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); - }, - /** - * - * @param {GetAssetCountByTimeBucketDto} getAssetCountByTimeBucketDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async getAssetCountByTimeBucket(getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetCountByTimeBucket(getAssetCountByTimeBucketDto, options); - return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); - }, /** * * @param {*} [options] Override http request option. @@ -6021,6 +5989,22 @@ export const AssetApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetThumbnail(id, format, key, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @param {TimeBucketSize} size + * @param {string} timeBucket + * @param {string} [userId] + * @param {string} [albumId] + * @param {boolean} [isArchived] + * @param {boolean} [isFavorite] + * @param {string} [key] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getByTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getByTimeBucket(size, timeBucket, userId, albumId, isArchived, isFavorite, key, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @param {*} [options] Override http request option. @@ -6075,6 +6059,21 @@ export const AssetApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.getMemoryLane(timestamp, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @param {TimeBucketSize} size + * @param {string} [userId] + * @param {string} [albumId] + * @param {boolean} [isArchived] + * @param {boolean} [isFavorite] + * @param {string} [key] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, isArchived, isFavorite, key, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * Get all asset of a device that are in the database, ID only. * @param {string} deviceId @@ -6242,24 +6241,6 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath getAssetById(id: string, key?: string, options?: any): AxiosPromise { return localVarFp.getAssetById(id, key, options).then((request) => request(axios, basePath)); }, - /** - * - * @param {GetAssetByTimeBucketDto} getAssetByTimeBucketDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetByTimeBucket(getAssetByTimeBucketDto: GetAssetByTimeBucketDto, options?: any): AxiosPromise> { - return localVarFp.getAssetByTimeBucket(getAssetByTimeBucketDto, options).then((request) => request(axios, basePath)); - }, - /** - * - * @param {GetAssetCountByTimeBucketDto} getAssetCountByTimeBucketDto - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - getAssetCountByTimeBucket(getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto, options?: any): AxiosPromise { - return localVarFp.getAssetCountByTimeBucket(getAssetCountByTimeBucketDto, options).then((request) => request(axios, basePath)); - }, /** * * @param {*} [options] Override http request option. @@ -6289,6 +6270,21 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath getAssetThumbnail(id: string, format?: ThumbnailFormat, key?: string, options?: any): AxiosPromise { return localVarFp.getAssetThumbnail(id, format, key, options).then((request) => request(axios, basePath)); }, + /** + * + * @param {TimeBucketSize} size + * @param {string} timeBucket + * @param {string} [userId] + * @param {string} [albumId] + * @param {boolean} [isArchived] + * @param {boolean} [isFavorite] + * @param {string} [key] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getByTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: any): AxiosPromise> { + return localVarFp.getByTimeBucket(size, timeBucket, userId, albumId, isArchived, isFavorite, key, options).then((request) => request(axios, basePath)); + }, /** * * @param {*} [options] Override http request option. @@ -6338,6 +6334,20 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath getMemoryLane(timestamp: string, options?: any): AxiosPromise> { return localVarFp.getMemoryLane(timestamp, options).then((request) => request(axios, basePath)); }, + /** + * + * @param {TimeBucketSize} size + * @param {string} [userId] + * @param {string} [albumId] + * @param {boolean} [isArchived] + * @param {boolean} [isFavorite] + * @param {string} [key] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: any): AxiosPromise> { + return localVarFp.getTimeBuckets(size, userId, albumId, isArchived, isFavorite, key, options).then((request) => request(axios, basePath)); + }, /** * Get all asset of a device that are in the database, ID only. * @param {string} deviceId @@ -6586,34 +6596,6 @@ export interface AssetApiGetAssetByIdRequest { readonly key?: string } -/** - * Request parameters for getAssetByTimeBucket operation in AssetApi. - * @export - * @interface AssetApiGetAssetByTimeBucketRequest - */ -export interface AssetApiGetAssetByTimeBucketRequest { - /** - * - * @type {GetAssetByTimeBucketDto} - * @memberof AssetApiGetAssetByTimeBucket - */ - readonly getAssetByTimeBucketDto: GetAssetByTimeBucketDto -} - -/** - * Request parameters for getAssetCountByTimeBucket operation in AssetApi. - * @export - * @interface AssetApiGetAssetCountByTimeBucketRequest - */ -export interface AssetApiGetAssetCountByTimeBucketRequest { - /** - * - * @type {GetAssetCountByTimeBucketDto} - * @memberof AssetApiGetAssetCountByTimeBucket - */ - readonly getAssetCountByTimeBucketDto: GetAssetCountByTimeBucketDto -} - /** * Request parameters for getAssetStats operation in AssetApi. * @export @@ -6663,6 +6645,62 @@ export interface AssetApiGetAssetThumbnailRequest { readonly key?: string } +/** + * Request parameters for getByTimeBucket operation in AssetApi. + * @export + * @interface AssetApiGetByTimeBucketRequest + */ +export interface AssetApiGetByTimeBucketRequest { + /** + * + * @type {TimeBucketSize} + * @memberof AssetApiGetByTimeBucket + */ + readonly size: TimeBucketSize + + /** + * + * @type {string} + * @memberof AssetApiGetByTimeBucket + */ + readonly timeBucket: string + + /** + * + * @type {string} + * @memberof AssetApiGetByTimeBucket + */ + readonly userId?: string + + /** + * + * @type {string} + * @memberof AssetApiGetByTimeBucket + */ + readonly albumId?: string + + /** + * + * @type {boolean} + * @memberof AssetApiGetByTimeBucket + */ + readonly isArchived?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiGetByTimeBucket + */ + readonly isFavorite?: boolean + + /** + * + * @type {string} + * @memberof AssetApiGetByTimeBucket + */ + readonly key?: string +} + /** * Request parameters for getDownloadInfo operation in AssetApi. * @export @@ -6747,6 +6785,55 @@ export interface AssetApiGetMemoryLaneRequest { readonly timestamp: string } +/** + * Request parameters for getTimeBuckets operation in AssetApi. + * @export + * @interface AssetApiGetTimeBucketsRequest + */ +export interface AssetApiGetTimeBucketsRequest { + /** + * + * @type {TimeBucketSize} + * @memberof AssetApiGetTimeBuckets + */ + readonly size: TimeBucketSize + + /** + * + * @type {string} + * @memberof AssetApiGetTimeBuckets + */ + readonly userId?: string + + /** + * + * @type {string} + * @memberof AssetApiGetTimeBuckets + */ + readonly albumId?: string + + /** + * + * @type {boolean} + * @memberof AssetApiGetTimeBuckets + */ + readonly isArchived?: boolean + + /** + * + * @type {boolean} + * @memberof AssetApiGetTimeBuckets + */ + readonly isFavorite?: boolean + + /** + * + * @type {string} + * @memberof AssetApiGetTimeBuckets + */ + readonly key?: string +} + /** * Request parameters for getUserAssetsByDeviceId operation in AssetApi. * @export @@ -7038,28 +7125,6 @@ export class AssetApi extends BaseAPI { return AssetApiFp(this.configuration).getAssetById(requestParameters.id, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); } - /** - * - * @param {AssetApiGetAssetByTimeBucketRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getAssetByTimeBucket(requestParameters: AssetApiGetAssetByTimeBucketRequest, options?: AxiosRequestConfig) { - return AssetApiFp(this.configuration).getAssetByTimeBucket(requestParameters.getAssetByTimeBucketDto, options).then((request) => request(this.axios, this.basePath)); - } - - /** - * - * @param {AssetApiGetAssetCountByTimeBucketRequest} requestParameters Request parameters. - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof AssetApi - */ - public getAssetCountByTimeBucket(requestParameters: AssetApiGetAssetCountByTimeBucketRequest, options?: AxiosRequestConfig) { - return AssetApiFp(this.configuration).getAssetCountByTimeBucket(requestParameters.getAssetCountByTimeBucketDto, options).then((request) => request(this.axios, this.basePath)); - } - /** * * @param {*} [options] Override http request option. @@ -7092,6 +7157,17 @@ export class AssetApi extends BaseAPI { return AssetApiFp(this.configuration).getAssetThumbnail(requestParameters.id, requestParameters.format, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @param {AssetApiGetByTimeBucketRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AssetApi + */ + public getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig) { + return AssetApiFp(this.configuration).getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); + } + /** * * @param {*} [options] Override http request option. @@ -7145,6 +7221,17 @@ export class AssetApi extends BaseAPI { return AssetApiFp(this.configuration).getMemoryLane(requestParameters.timestamp, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @param {AssetApiGetTimeBucketsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AssetApi + */ + public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig) { + return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); + } + /** * Get all asset of a device that are in the database, ID only. * @param {AssetApiGetUserAssetsByDeviceIdRequest} requestParameters Request parameters. diff --git a/web/src/lib/components/album-page/asset-selection.svelte b/web/src/lib/components/album-page/asset-selection.svelte index 2cac754950..2035256942 100644 --- a/web/src/lib/components/album-page/asset-selection.svelte +++ b/web/src/lib/components/album-page/asset-selection.svelte @@ -3,7 +3,7 @@ import { AssetStore } from '$lib/stores/assets.store'; import { locale } from '$lib/stores/preferences.store'; import { openFileUploadDialog } from '$lib/utils/file-uploader'; - import { TimeGroupEnum, type AssetResponseDto } from '@api'; + import { TimeBucketSize, type AssetResponseDto } from '@api'; import { createEventDispatcher, onMount } from 'svelte'; import { quintOut } from 'svelte/easing'; import { fly } from 'svelte/transition'; @@ -13,7 +13,7 @@ const dispatch = createEventDispatcher(); - const assetStore = new AssetStore({ timeGroup: TimeGroupEnum.Month }); + const assetStore = new AssetStore({ size: TimeBucketSize.Month }); const assetInteractionStore = createAssetInteractionStore(); const { selectedAssets, assetsInAlbumState } = assetInteractionStore; diff --git a/web/src/lib/components/layouts/user-page-layout.svelte b/web/src/lib/components/layouts/user-page-layout.svelte index 8ce2ddef78..bc7d97d9b7 100644 --- a/web/src/lib/components/layouts/user-page-layout.svelte +++ b/web/src/lib/components/layouts/user-page-layout.svelte @@ -7,6 +7,9 @@ export let hideNavbar = false; export let showUploadButton = false; export let title: string | undefined = undefined; + export let scrollbar = true; + + $: scrollbarClass = scrollbar ? 'immich-scrollbar p-4 pb-8' : 'scrollbar-hidden pl-4';
@@ -32,7 +35,7 @@ -
+
diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index 4369ecfda7..85800471cb 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -274,7 +274,7 @@
import empty1Url from '$lib/assets/empty-1.svg'; - export let actionHandler: undefined | (() => Promise) = undefined; + export let actionHandler: undefined | (() => unknown) = undefined; export let text = ''; export let alt = ''; diff --git a/web/src/lib/stores/assets.store.ts b/web/src/lib/stores/assets.store.ts index 4480128207..2e0f06d9cb 100644 --- a/web/src/lib/stores/assets.store.ts +++ b/web/src/lib/stores/assets.store.ts @@ -1,4 +1,4 @@ -import { api, AssetResponseDto, GetAssetCountByTimeBucketDto } from '@api'; +import { api, AssetApiGetTimeBucketsRequest, AssetResponseDto } from '@api'; import { writable } from 'svelte/store'; import { handleError } from '../utils/handle-error'; @@ -9,7 +9,7 @@ export enum BucketPosition { Unknown = 'unknown', } -export type AssetStoreOptions = GetAssetCountByTimeBucketDto; +export type AssetStoreOptions = AssetApiGetTimeBucketsRequest; export interface Viewport { width: number; @@ -51,11 +51,9 @@ export class AssetStore { subscribe = this.store$.subscribe; async init(viewport: Viewport) { - const { data } = await api.assetApi.getAssetCountByTimeBucket({ - getAssetCountByTimeBucketDto: { ...this.options, withoutThumbs: true }, - }); + const { data: buckets } = await api.assetApi.getTimeBuckets(this.options); - this.buckets = data.buckets.map((bucket) => { + this.buckets = buckets.map((bucket) => { const unwrappedWidth = (3 / 2) * bucket.count * THUMBNAIL_HEIGHT * (7 / 10); const rows = Math.ceil(unwrappedWidth / viewport.width); const height = rows * THUMBNAIL_HEIGHT; @@ -101,14 +99,8 @@ export class AssetStore { bucket.cancelToken = new AbortController(); - const { data: assets } = await api.assetApi.getAssetByTimeBucket( - { - getAssetByTimeBucketDto: { - timeBucket: [bucketDate], - ...this.options, - withoutThumbs: true, - }, - }, + const { data: assets } = await api.assetApi.getByTimeBucket( + { ...this.options, timeBucket: bucketDate }, { signal: bucket.cancelToken.signal }, ); diff --git a/web/src/routes/(user)/archive/+page.svelte b/web/src/routes/(user)/archive/+page.svelte index 25bc1f2ff9..13a472e050 100644 --- a/web/src/routes/(user)/archive/+page.svelte +++ b/web/src/routes/(user)/archive/+page.svelte @@ -6,70 +6,55 @@ import DeleteAssets from '$lib/components/photos-page/actions/delete-assets.svelte'; import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte'; import FavoriteAction from '$lib/components/photos-page/actions/favorite-action.svelte'; + import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte'; + import AssetGrid from '$lib/components/photos-page/asset-grid.svelte'; import AssetSelectContextMenu from '$lib/components/photos-page/asset-select-context-menu.svelte'; import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; - import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte'; - import SelectAll from 'svelte-material-icons/SelectAll.svelte'; - import { archivedAsset } from '$lib/stores/archived-asset.store'; - import { handleError } from '$lib/utils/handle-error'; - import { api, AssetResponseDto } from '@api'; + import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store'; + import { AssetStore } from '$lib/stores/assets.store'; + import { api, TimeBucketSize } from '@api'; import { onMount } from 'svelte'; import DotsVertical from 'svelte-material-icons/DotsVertical.svelte'; import Plus from 'svelte-material-icons/Plus.svelte'; import type { PageData } from './$types'; - import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; export let data: PageData; + let assetCount = 1; - let selectedAssets: Set = new Set(); - $: isMultiSelectionMode = selectedAssets.size > 0; - $: isAllFavorite = Array.from(selectedAssets).every((asset) => asset.isFavorite); + const assetStore = new AssetStore({ size: TimeBucketSize.Month, isArchived: true }); + const assetInteractionStore = createAssetInteractionStore(); + const { isMultiSelectState, selectedAssets } = assetInteractionStore; + + $: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite); onMount(async () => { - try { - const { data: assets } = await api.assetApi.getAllAssets({ - isArchived: true, - withoutThumbs: true, - }); - $archivedAsset = assets; - } catch { - handleError(Error, 'Unable to load archived assets'); - } + const { data: stats } = await api.assetApi.getAssetStats({ isArchived: true }); + assetCount = stats.total; }); - - const onAssetDelete = (assetId: string) => { - $archivedAsset = $archivedAsset.filter((a) => a.id !== assetId); - }; - const handleSelectAll = () => { - selectedAssets = new Set($archivedAsset); - }; - - - {#if $archivedAsset.length === 0} +{#if $isMultiSelectState} + assetInteractionStore.clearMultiselect()}> + assetStore.removeAsset(asset.id)} /> + + + + + + + assetStore.removeAsset(assetId)} /> + + + + + +{/if} + + + {#if assetCount} + + {:else} {/if} - - - {#if isMultiSelectionMode} - (selectedAssets = new Set())}> - onAssetDelete(asset.id)} /> - - - - - - - - - - - - - {/if} - - - diff --git a/web/src/routes/(user)/favorites/+page.svelte b/web/src/routes/(user)/favorites/+page.svelte index 3a397ceb6e..959ab7f81c 100644 --- a/web/src/routes/(user)/favorites/+page.svelte +++ b/web/src/routes/(user)/favorites/+page.svelte @@ -6,60 +6,45 @@ import DeleteAssets from '$lib/components/photos-page/actions/delete-assets.svelte'; import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte'; import FavoriteAction from '$lib/components/photos-page/actions/favorite-action.svelte'; + import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte'; + import AssetGrid from '$lib/components/photos-page/asset-grid.svelte'; import AssetSelectContextMenu from '$lib/components/photos-page/asset-select-context-menu.svelte'; import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; - import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte'; - import { handleError } from '$lib/utils/handle-error'; - import { api, AssetResponseDto } from '@api'; + import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store'; + import { AssetStore } from '$lib/stores/assets.store'; + import { api, TimeBucketSize } from '@api'; import { onMount } from 'svelte'; import DotsVertical from 'svelte-material-icons/DotsVertical.svelte'; import Plus from 'svelte-material-icons/Plus.svelte'; - import Error from '../../+error.svelte'; import type { PageData } from './$types'; - import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; - import SelectAll from 'svelte-material-icons/SelectAll.svelte'; - - let favorites: AssetResponseDto[] = []; - let selectedAssets: Set = new Set(); export let data: PageData; + let assetCount = 1; - $: isMultiSelectionMode = selectedAssets.size > 0; - $: isAllArchive = Array.from(selectedAssets).every((asset) => asset.isArchived); + const assetStore = new AssetStore({ size: TimeBucketSize.Month, isFavorite: true }); + const assetInteractionStore = createAssetInteractionStore(); + const { isMultiSelectState, selectedAssets } = assetInteractionStore; + + $: isAllArchive = Array.from($selectedAssets).every((asset) => asset.isArchived); onMount(async () => { - try { - const { data: assets } = await api.assetApi.getAllAssets({ - isFavorite: true, - withoutThumbs: true, - }); - favorites = assets; - } catch { - handleError(Error, 'Unable to load favorites'); - } + const { data: stats } = await api.assetApi.getAssetStats({ isFavorite: true }); + assetCount = stats.total; }); - - const handleSelectAll = () => { - selectedAssets = new Set(favorites); - }; - - const onAssetDelete = (assetId: string) => { - favorites = favorites.filter((a) => a.id !== assetId); - }; -{#if isMultiSelectionMode} - (selectedAssets = new Set())}> - onAssetDelete(asset.id)} /> +{#if $isMultiSelectState} + assetInteractionStore.clearMultiselect()}> + assetStore.removeAsset(asset.id)} /> - + - + assetStore.removeAsset(assetId)} /> @@ -67,13 +52,10 @@ {/if} - -
- - {#if favorites.length === 0} - - {/if} - - -
+ + {#if assetCount} + + {:else} + + {/if} diff --git a/web/src/routes/(user)/partners/[userId]/+page.svelte b/web/src/routes/(user)/partners/[userId]/+page.svelte index f09a36d1ab..c7fd51f2a8 100644 --- a/web/src/routes/(user)/partners/[userId]/+page.svelte +++ b/web/src/routes/(user)/partners/[userId]/+page.svelte @@ -10,7 +10,7 @@ import { AppRoute } from '$lib/constants'; import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store'; import { AssetStore } from '$lib/stores/assets.store'; - import { TimeGroupEnum } from '@api'; + import { TimeBucketSize } from '@api'; import { onDestroy } from 'svelte'; import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte'; import Plus from 'svelte-material-icons/Plus.svelte'; @@ -18,7 +18,7 @@ export let data: PageData; - const assetStore = new AssetStore({ timeGroup: TimeGroupEnum.Month, userId: data.partner.id }); + const assetStore = new AssetStore({ size: TimeBucketSize.Month, userId: data.partner.id }); const assetInteractionStore = createAssetInteractionStore(); const { isMultiSelectState, selectedAssets } = assetInteractionStore; diff --git a/web/src/routes/(user)/photos/+page.svelte b/web/src/routes/(user)/photos/+page.svelte index 3fe74c27d8..eb2f82e4f2 100644 --- a/web/src/routes/(user)/photos/+page.svelte +++ b/web/src/routes/(user)/photos/+page.svelte @@ -15,8 +15,8 @@ import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store'; import { AssetStore } from '$lib/stores/assets.store'; import { openFileUploadDialog } from '$lib/utils/file-uploader'; - import { TimeGroupEnum, api } from '@api'; - import { onDestroy, onMount } from 'svelte'; + import { TimeBucketSize, api } from '@api'; + import { onMount } from 'svelte'; import DotsVertical from 'svelte-material-icons/DotsVertical.svelte'; import Plus from 'svelte-material-icons/Plus.svelte'; import type { PageData } from './$types'; @@ -24,30 +24,22 @@ export let data: PageData; let assetCount = 1; - const assetStore = new AssetStore({ timeGroup: TimeGroupEnum.Month }); + const assetStore = new AssetStore({ size: TimeBucketSize.Month, isArchived: false }); const assetInteractionStore = createAssetInteractionStore(); const { isMultiSelectState, selectedAssets } = assetInteractionStore; + $: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite); + onMount(async () => { const { data: stats } = await api.assetApi.getAssetStats(); assetCount = stats.total; }); - - onDestroy(() => { - assetInteractionStore.clearMultiselect(); - }); - - $: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite); - - const handleUpload = async () => { - openFileUploadDialog(); - }; {#if $isMultiSelectState} - + assetInteractionStore.clearMultiselect()}> @@ -69,7 +61,7 @@ {:else} - + openFileUploadDialog()} /> {/if}