1
0
mirror of https://github.com/immich-app/immich.git synced 2024-12-25 10:43:13 +02:00

refactor(server,web): time buckets for main timeline, archived, and favorites (1) (#3537)

* refactor: time buckets

* feat(web): use new time bucket api

* feat(web): use asset grid in archive/favorites

* chore: open api

* chore: clean up uuid validation

* refactor(web): move memory lane to photos page

* Update web/src/routes/(user)/archive/+page.svelte

Co-authored-by: Sergey Kondrikov <sergey.kondrikov@gmail.com>

* fix: hide archived photos on main timeline

* fix: select exif info

---------

Co-authored-by: Sergey Kondrikov <sergey.kondrikov@gmail.com>
This commit is contained in:
Jason Rasmussen 2023-08-04 17:07:15 -04:00 committed by GitHub
parent e5bdf671b5
commit c6abef186c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 1132 additions and 1038 deletions

View File

@ -410,44 +410,6 @@ export const AssetBulkUploadCheckResultReasonEnum = {
export type AssetBulkUploadCheckResultReasonEnum = typeof AssetBulkUploadCheckResultReasonEnum[keyof typeof 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<AssetCountByTimeBucket>}
* @memberof AssetCountByTimeBucketResponseDto
*/
'buckets': Array<AssetCountByTimeBucket>;
/**
*
* @type {number}
* @memberof AssetCountByTimeBucketResponseDto
*/
'totalCount': number;
}
/** /**
* *
* @export * @export
@ -1286,58 +1248,6 @@ export interface ExifResponseDto {
*/ */
'timeZone'?: string | null; 'timeZone'?: string | null;
} }
/**
*
* @export
* @interface GetAssetByTimeBucketDto
*/
export interface GetAssetByTimeBucketDto {
/**
*
* @type {Array<string>}
* @memberof GetAssetByTimeBucketDto
*/
'timeBucket': Array<string>;
/**
*
* @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 * @export
@ -2850,18 +2760,37 @@ export const ThumbnailFormat = {
export type ThumbnailFormat = typeof ThumbnailFormat[keyof typeof 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 * @export
* @enum {string} * @enum {string}
*/ */
export const TimeGroupEnum = { export const TimeBucketSize = {
Day: 'day', Day: 'DAY',
Month: 'month' Month: 'MONTH'
} as const; } 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, options: localVarRequestOptions,
}; };
}, },
/**
*
* @param {GetAssetByTimeBucketDto} getAssetByTimeBucketDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getAssetByTimeBucket: async (getAssetByTimeBucketDto: GetAssetByTimeBucketDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// 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<RequestArgs> => {
// 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. * @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<RequestArgs> => {
// 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); setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
@ -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<RequestArgs> => {
// 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); setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
@ -5960,26 +5948,6 @@ export const AssetApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetById(id, key, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetById(id, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); 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<Array<AssetResponseDto>>> {
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<AssetCountByTimeBucketResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetCountByTimeBucket(getAssetCountByTimeBucketDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/** /**
* *
* @param {*} [options] Override http request option. * @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); const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetThumbnail(id, format, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); 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<Array<AssetResponseDto>>> {
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. * @param {*} [options] Override http request option.
@ -6066,6 +6050,21 @@ export const AssetApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.getMemoryLane(timestamp, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getMemoryLane(timestamp, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); 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<Array<TimeBucketResponseDto>>> {
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. * Get all asset of a device that are in the database, ID only.
* @param {string} deviceId * @param {string} deviceId
@ -6224,24 +6223,6 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
getAssetById(requestParameters: AssetApiGetAssetByIdRequest, options?: AxiosRequestConfig): AxiosPromise<AssetResponseDto> { getAssetById(requestParameters: AssetApiGetAssetByIdRequest, options?: AxiosRequestConfig): AxiosPromise<AssetResponseDto> {
return localVarFp.getAssetById(requestParameters.id, requestParameters.key, options).then((request) => request(axios, basePath)); 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<Array<AssetResponseDto>> {
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<AssetCountByTimeBucketResponseDto> {
return localVarFp.getAssetCountByTimeBucket(requestParameters.getAssetCountByTimeBucketDto, options).then((request) => request(axios, basePath));
},
/** /**
* *
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
@ -6268,6 +6249,15 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
getAssetThumbnail(requestParameters: AssetApiGetAssetThumbnailRequest, options?: AxiosRequestConfig): AxiosPromise<File> { getAssetThumbnail(requestParameters: AssetApiGetAssetThumbnailRequest, options?: AxiosRequestConfig): AxiosPromise<File> {
return localVarFp.getAssetThumbnail(requestParameters.id, requestParameters.format, requestParameters.key, options).then((request) => request(axios, basePath)); 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<Array<AssetResponseDto>> {
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. * @param {*} [options] Override http request option.
@ -6311,6 +6301,15 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
getMemoryLane(requestParameters: AssetApiGetMemoryLaneRequest, options?: AxiosRequestConfig): AxiosPromise<Array<MemoryLaneResponseDto>> { getMemoryLane(requestParameters: AssetApiGetMemoryLaneRequest, options?: AxiosRequestConfig): AxiosPromise<Array<MemoryLaneResponseDto>> {
return localVarFp.getMemoryLane(requestParameters.timestamp, options).then((request) => request(axios, basePath)); 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<Array<TimeBucketResponseDto>> {
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. * Get all asset of a device that are in the database, ID only.
* @param {AssetApiGetUserAssetsByDeviceIdRequest} requestParameters Request parameters. * @param {AssetApiGetUserAssetsByDeviceIdRequest} requestParameters Request parameters.
@ -6543,34 +6542,6 @@ export interface AssetApiGetAssetByIdRequest {
readonly key?: string 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. * Request parameters for getAssetStats operation in AssetApi.
* @export * @export
@ -6620,6 +6591,62 @@ export interface AssetApiGetAssetThumbnailRequest {
readonly key?: string 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. * Request parameters for getDownloadInfo operation in AssetApi.
* @export * @export
@ -6704,6 +6731,55 @@ export interface AssetApiGetMemoryLaneRequest {
readonly timestamp: string 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. * Request parameters for getUserAssetsByDeviceId operation in AssetApi.
* @export * @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)); 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. * @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)); 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. * @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)); 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. * Get all asset of a device that are in the database, ID only.
* @param {AssetApiGetUserAssetsByDeviceIdRequest} requestParameters Request parameters. * @param {AssetApiGetUserAssetsByDeviceIdRequest} requestParameters Request parameters.

View File

@ -19,8 +19,6 @@ doc/AssetBulkUploadCheckDto.md
doc/AssetBulkUploadCheckItem.md doc/AssetBulkUploadCheckItem.md
doc/AssetBulkUploadCheckResponseDto.md doc/AssetBulkUploadCheckResponseDto.md
doc/AssetBulkUploadCheckResult.md doc/AssetBulkUploadCheckResult.md
doc/AssetCountByTimeBucket.md
doc/AssetCountByTimeBucketResponseDto.md
doc/AssetFileUploadResponseDto.md doc/AssetFileUploadResponseDto.md
doc/AssetIdsDto.md doc/AssetIdsDto.md
doc/AssetIdsResponseDto.md doc/AssetIdsResponseDto.md
@ -49,8 +47,6 @@ doc/DeleteAssetStatus.md
doc/DownloadArchiveInfo.md doc/DownloadArchiveInfo.md
doc/DownloadResponseDto.md doc/DownloadResponseDto.md
doc/ExifResponseDto.md doc/ExifResponseDto.md
doc/GetAssetByTimeBucketDto.md
doc/GetAssetCountByTimeBucketDto.md
doc/ImportAssetDto.md doc/ImportAssetDto.md
doc/JobApi.md doc/JobApi.md
doc/JobCommand.md doc/JobCommand.md
@ -112,7 +108,8 @@ doc/TagApi.md
doc/TagResponseDto.md doc/TagResponseDto.md
doc/TagTypeEnum.md doc/TagTypeEnum.md
doc/ThumbnailFormat.md doc/ThumbnailFormat.md
doc/TimeGroupEnum.md doc/TimeBucketResponseDto.md
doc/TimeBucketSize.md
doc/TranscodeHWAccel.md doc/TranscodeHWAccel.md
doc/TranscodePolicy.md doc/TranscodePolicy.md
doc/UpdateAlbumDto.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_item.dart
lib/model/asset_bulk_upload_check_response_dto.dart lib/model/asset_bulk_upload_check_response_dto.dart
lib/model/asset_bulk_upload_check_result.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_file_upload_response_dto.dart
lib/model/asset_ids_dto.dart lib/model/asset_ids_dto.dart
lib/model/asset_ids_response_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_archive_info.dart
lib/model/download_response_dto.dart lib/model/download_response_dto.dart
lib/model/exif_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/import_asset_dto.dart
lib/model/job_command.dart lib/model/job_command.dart
lib/model/job_command_dto.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_response_dto.dart
lib/model/tag_type_enum.dart lib/model/tag_type_enum.dart
lib/model/thumbnail_format.dart lib/model/thumbnail_format.dart
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_hw_accel.dart
lib/model/transcode_policy.dart lib/model/transcode_policy.dart
lib/model/update_album_dto.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_item_test.dart
test/asset_bulk_upload_check_response_dto_test.dart test/asset_bulk_upload_check_response_dto_test.dart
test/asset_bulk_upload_check_result_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_file_upload_response_dto_test.dart
test/asset_ids_dto_test.dart test/asset_ids_dto_test.dart
test/asset_ids_response_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_archive_info_test.dart
test/download_response_dto_test.dart test/download_response_dto_test.dart
test/exif_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/import_asset_dto_test.dart
test/job_api_test.dart test/job_api_test.dart
test/job_command_dto_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_response_dto_test.dart
test/tag_type_enum_test.dart test/tag_type_enum_test.dart
test/thumbnail_format_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_hw_accel_test.dart
test/transcode_policy_test.dart test/transcode_policy_test.dart
test/update_album_dto_test.dart test/update_album_dto_test.dart

BIN
mobile/openapi/README.md generated

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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": { "/asset/curated-locations": {
"get": { "get": {
"operationId": "getCuratedLocations", "operationId": "getCuratedLocations",
@ -1697,19 +1655,68 @@
} }
}, },
"/asset/time-bucket": { "/asset/time-bucket": {
"post": { "get": {
"operationId": "getAssetByTimeBucket", "operationId": "getByTimeBucket",
"parameters": [], "parameters": [
"requestBody": { {
"content": { "name": "size",
"application/json": { "required": true,
"schema": { "in": "query",
"$ref": "#/components/schemas/GetAssetByTimeBucketDto" "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": { "responses": {
"200": { "200": {
"content": { "content": {
@ -1726,6 +1733,110 @@
} }
}, },
"security": [ "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": [] "bearer": []
}, },
@ -4787,39 +4898,6 @@
], ],
"type": "object" "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": { "AssetFileUploadResponseDto": {
"properties": { "properties": {
"duplicate": { "duplicate": {
@ -5554,53 +5632,6 @@
}, },
"type": "object" "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": { "ImportAssetDto": {
"properties": { "properties": {
"assetPath": { "assetPath": {
@ -6806,10 +6837,25 @@
], ],
"type": "string" "type": "string"
}, },
"TimeGroupEnum": { "TimeBucketResponseDto": {
"properties": {
"count": {
"type": "integer"
},
"timeBucket": {
"type": "string"
}
},
"required": [
"timeBucket",
"count"
],
"type": "object"
},
"TimeBucketSize": {
"enum": [ "enum": [
"day", "DAY",
"month" "MONTH"
], ],
"type": "string" "type": "string"
}, },

View File

@ -47,6 +47,23 @@ export enum WithProperty {
SIDECAR = 'sidecar', 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 const IAssetRepository = 'IAssetRepository';
export interface IAssetRepository { export interface IAssetRepository {
@ -64,4 +81,6 @@ export interface IAssetRepository {
findLivePhotoMatch(options: LivePhotoSearchOptions): Promise<AssetEntity | null>; findLivePhotoMatch(options: LivePhotoSearchOptions): Promise<AssetEntity | null>;
getMapMarkers(ownerId: string, options?: MapMarkerSearchOptions): Promise<MapMarker[]>; getMapMarkers(ownerId: string, options?: MapMarkerSearchOptions): Promise<MapMarker[]>;
getStatistics(ownerId: string, options: AssetStatsOptions): Promise<AssetStats>; getStatistics(ownerId: string, options: AssetStatsOptions): Promise<AssetStats>;
getTimeBuckets(userId: string, options: TimeBucketOptions): Promise<TimeBucketItem[]>;
getByTimeBucket(userId: string, timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]>;
} }

View File

@ -10,11 +10,20 @@ import { mimeTypes } from '../domain.constant';
import { HumanReadableSize, usePagination } from '../domain.util'; import { HumanReadableSize, usePagination } from '../domain.util';
import { ImmichReadStream, IStorageRepository, StorageCore, StorageFolder } from '../storage'; import { ImmichReadStream, IStorageRepository, StorageCore, StorageFolder } from '../storage';
import { IAssetRepository } from './asset.repository'; 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 { AssetStatsDto, mapStats } from './dto/asset-statistics.dto';
import { MapMarkerDto } from './dto/map-marker.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 { MemoryLaneResponseDto } from './response-dto/memory-lane-response.dto';
import { TimeBucketResponseDto } from './response-dto/time-bucket-response.dto';
export enum UploadFieldName { export enum UploadFieldName {
ASSET_DATA = 'assetData', ASSET_DATA = 'assetData',
@ -135,6 +144,21 @@ export class AssetService {
return Promise.all(requests).then((results) => results.filter((result) => result.assets.length > 0)); return Promise.all(requests).then((results) => results.filter((result) => result.assets.length > 0));
} }
async getTimeBuckets(authUser: AuthUserDto, dto: TimeBucketDto): Promise<TimeBucketResponseDto[]> {
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<AssetResponseDto[]> {
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<ImmichReadStream> { async downloadFile(authUser: AuthUserDto, id: string): Promise<ImmichReadStream> {
await this.access.requirePermission(authUser, Permission.ASSET_DOWNLOAD, id); await this.access.requirePermission(authUser, Permission.ASSET_DOWNLOAD, id);

View File

@ -3,3 +3,4 @@ export * from './asset-statistics.dto';
export * from './download.dto'; export * from './download.dto';
export * from './map-marker.dto'; export * from './map-marker.dto';
export * from './memory-lane.dto'; export * from './memory-lane.dto';
export * from './time-bucket.dto';

View File

@ -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;
}

View File

@ -3,3 +3,4 @@ export * from './asset-response.dto';
export * from './exif-response.dto'; export * from './exif-response.dto';
export * from './map-marker-response.dto'; export * from './map-marker-response.dto';
export * from './smart-info-response.dto'; export * from './smart-info-response.dto';
export * from './time-bucket-response.dto';

View File

@ -0,0 +1,9 @@
import { ApiProperty } from '@nestjs/swagger';
export class TimeBucketResponseDto {
@ApiProperty({ type: 'string' })
timeBucket!: string;
@ApiProperty({ type: 'integer' })
count!: number;
}

View File

@ -6,11 +6,8 @@ import { In } from 'typeorm/find-options/operator/In';
import { Repository } from 'typeorm/repository/Repository'; import { Repository } from 'typeorm/repository/Repository';
import { AssetSearchDto } from './dto/asset-search.dto'; import { AssetSearchDto } from './dto/asset-search.dto';
import { CheckExistingAssetsDto } from './dto/check-existing-assets.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 { SearchPropertiesDto } from './dto/search-properties.dto';
import { UpdateAssetDto } from './dto/update-asset.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 { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto';
import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto'; import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto';
@ -36,8 +33,6 @@ export interface IAssetRepository {
getLocationsByUserId(userId: string): Promise<CuratedLocationsResponseDto[]>; getLocationsByUserId(userId: string): Promise<CuratedLocationsResponseDto[]>;
getDetectedObjectsByUserId(userId: string): Promise<CuratedObjectsResponseDto[]>; getDetectedObjectsByUserId(userId: string): Promise<CuratedObjectsResponseDto[]>;
getSearchPropertiesByUserId(userId: string): Promise<SearchPropertiesDto[]>; getSearchPropertiesByUserId(userId: string): Promise<SearchPropertiesDto[]>;
getAssetCountByTimeBucket(userId: string, dto: GetAssetCountByTimeBucketDto): Promise<AssetCountByTimeBucket[]>;
getAssetByTimeBucket(userId: string, getAssetByTimeBucketDto: GetAssetByTimeBucketDto): Promise<AssetEntity[]>;
getAssetsByChecksums(userId: string, checksums: Buffer[]): Promise<AssetCheck[]>; getAssetsByChecksums(userId: string, checksums: Buffer[]): Promise<AssetCheck[]>;
getExistingAssets(userId: string, checkDuplicateAssetDto: CheckExistingAssetsDto): Promise<string[]>; getExistingAssets(userId: string, checkDuplicateAssetDto: CheckExistingAssetsDto): Promise<string[]>;
getByOriginalPath(originalPath: string): Promise<AssetOwnerCheck | null>; getByOriginalPath(originalPath: string): Promise<AssetOwnerCheck | null>;
@ -52,57 +47,6 @@ export class AssetRepository implements IAssetRepository {
@InjectRepository(ExifEntity) private exifRepository: Repository<ExifEntity>, @InjectRepository(ExifEntity) private exifRepository: Repository<ExifEntity>,
) {} ) {}
async getAssetByTimeBucket(userId: string, dto: GetAssetByTimeBucketDto): Promise<AssetEntity[]> {
// 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<AssetCountByTimeBucket[]> {
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<SearchPropertiesDto[]> { getSearchPropertiesByUserId(userId: string): Promise<SearchPropertiesDto[]> {
return this.assetRepository return this.assetRepository
.createQueryBuilder('asset') .createQueryBuilder('asset')

View File

@ -30,14 +30,11 @@ import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
import { CreateAssetDto, ImportAssetDto } from './dto/create-asset.dto'; import { CreateAssetDto, ImportAssetDto } from './dto/create-asset.dto';
import { DeleteAssetDto } from './dto/delete-asset.dto'; import { DeleteAssetDto } from './dto/delete-asset.dto';
import { DeviceIdDto } from './dto/device-id.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 { GetAssetThumbnailDto } from './dto/get-asset-thumbnail.dto';
import { SearchAssetDto } from './dto/search-asset.dto'; import { SearchAssetDto } from './dto/search-asset.dto';
import { ServeFileDto } from './dto/serve-file.dto'; import { ServeFileDto } from './dto/serve-file.dto';
import { UpdateAssetDto } from './dto/update-asset.dto'; import { UpdateAssetDto } from './dto/update-asset.dto';
import { AssetBulkUploadCheckResponseDto } from './response-dto/asset-check-response.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 { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto';
import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-asset-response.dto'; import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-asset-response.dto';
import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-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); return this.assetService.searchAsset(authUser, dto);
} }
@Post('/count-by-time-bucket')
@HttpCode(HttpStatus.OK)
getAssetCountByTimeBucket(
@AuthUser() authUser: AuthUserDto,
@Body(ValidationPipe) dto: GetAssetCountByTimeBucketDto,
): Promise<AssetCountByTimeBucketResponseDto> {
return this.assetService.getAssetCountByTimeBucket(authUser, dto);
}
/** /**
* Get all AssetEntity belong to the user * Get all AssetEntity belong to the user
*/ */
@ -189,15 +177,6 @@ export class AssetController {
return this.assetService.getAllAssets(authUser, dto); return this.assetService.getAllAssets(authUser, dto);
} }
@Post('/time-bucket')
@HttpCode(HttpStatus.OK)
getAssetByTimeBucket(
@AuthUser() authUser: AuthUserDto,
@Body(ValidationPipe) dto: GetAssetByTimeBucketDto,
): Promise<AssetResponseDto[]> {
return this.assetService.getAssetByTimeBucket(authUser, dto);
}
/** /**
* Get all asset of a device that are in the database, ID only. * Get all asset of a device that are in the database, ID only.
*/ */

View File

@ -16,9 +16,7 @@ import { QueryFailedError, Repository } from 'typeorm';
import { IAssetRepository } from './asset-repository'; import { IAssetRepository } from './asset-repository';
import { AssetService } from './asset.service'; import { AssetService } from './asset.service';
import { CreateAssetDto } from './dto/create-asset.dto'; 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 { 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 _getCreateAssetDto = (): CreateAssetDto => {
const createAssetDto = new CreateAssetDto(); const createAssetDto = new CreateAssetDto();
@ -83,18 +81,6 @@ const _getAssets = () => {
return [_getAsset_1(), _getAsset_2()]; 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', () => { describe('AssetService', () => {
let sut: AssetService; let sut: AssetService;
let a: Repository<AssetEntity>; // TO BE DELETED AFTER FINISHED REFACTORING let a: Repository<AssetEntity>; // TO BE DELETED AFTER FINISHED REFACTORING
@ -113,12 +99,10 @@ describe('AssetService', () => {
update: jest.fn(), update: jest.fn(),
getAllByUserId: jest.fn(), getAllByUserId: jest.fn(),
getAllByDeviceId: jest.fn(), getAllByDeviceId: jest.fn(),
getAssetCountByTimeBucket: jest.fn(),
getById: jest.fn(), getById: jest.fn(),
getDetectedObjectsByUserId: jest.fn(), getDetectedObjectsByUserId: jest.fn(),
getLocationsByUserId: jest.fn(), getLocationsByUserId: jest.fn(),
getSearchPropertiesByUserId: jest.fn(), getSearchPropertiesByUserId: jest.fn(),
getAssetByTimeBucket: jest.fn(),
getAssetsByChecksums: jest.fn(), getAssetsByChecksums: jest.fn(),
getExistingAssets: jest.fn(), getExistingAssets: jest.fn(),
getByOriginalPath: jest.fn(), getByOriginalPath: jest.fn(),
@ -221,21 +205,6 @@ describe('AssetService', () => {
expect(result).toEqual(assets.map((asset) => asset.deviceAssetId)); 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[]>(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', () => { describe('deleteAll', () => {
it('should return failed status when an asset is missing', async () => { it('should return failed status when an asset is missing', async () => {
assetRepositoryMock.get.mockResolvedValue(null); assetRepositoryMock.get.mockResolvedValue(null);

View File

@ -37,8 +37,6 @@ import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto';
import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto'; import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
import { CreateAssetDto, ImportAssetDto } from './dto/create-asset.dto'; import { CreateAssetDto, ImportAssetDto } from './dto/create-asset.dto';
import { DeleteAssetDto } from './dto/delete-asset.dto'; import { 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 { GetAssetThumbnailDto, GetAssetThumbnailFormatEnum } from './dto/get-asset-thumbnail.dto';
import { SearchAssetDto } from './dto/search-asset.dto'; import { SearchAssetDto } from './dto/search-asset.dto';
import { SearchPropertiesDto } from './dto/search-properties.dto'; import { SearchPropertiesDto } from './dto/search-properties.dto';
@ -49,10 +47,6 @@ import {
AssetRejectReason, AssetRejectReason,
AssetUploadAction, AssetUploadAction,
} from './response-dto/asset-check-response.dto'; } 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 { AssetFileUploadResponseDto } from './response-dto/asset-file-upload-response.dto';
import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-asset-response.dto'; import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-asset-response.dto';
import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-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)); return assets.map((asset) => mapAsset(asset));
} }
public async getAssetByTimeBucket(authUser: AuthUserDto, dto: GetAssetByTimeBucketDto): Promise<AssetResponseDto[]> {
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<AssetResponseDto> { public async getAssetById(authUser: AuthUserDto, assetId: string): Promise<AssetResponseDto> {
await this.access.requirePermission(authUser, Permission.ASSET_READ, assetId); await this.access.requirePermission(authUser, Permission.ASSET_READ, assetId);
@ -457,16 +444,6 @@ export class AssetService {
}; };
} }
async getAssetCountByTimeBucket(
authUser: AuthUserDto,
dto: GetAssetCountByTimeBucketDto,
): Promise<AssetCountByTimeBucketResponseDto> {
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) { getExifPermission(authUser: AuthUserDto) {
return !authUser.isPublicUser || authUser.isShowExif; return !authUser.isPublicUser || authUser.isShowExif;
} }

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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),
};
}

View File

@ -1,5 +1,6 @@
import { import {
AssetIdsDto, AssetIdsDto,
AssetResponseDto,
AssetService, AssetService,
AssetStatsDto, AssetStatsDto,
AssetStatsResponseDto, AssetStatsResponseDto,
@ -8,6 +9,9 @@ import {
DownloadResponseDto, DownloadResponseDto,
MapMarkerResponseDto, MapMarkerResponseDto,
MemoryLaneDto, MemoryLaneDto,
TimeBucketAssetDto,
TimeBucketDto,
TimeBucketResponseDto,
} from '@app/domain'; } from '@app/domain';
import { MapMarkerDto } from '@app/domain/asset/dto/map-marker.dto'; import { MapMarkerDto } from '@app/domain/asset/dto/map-marker.dto';
import { MemoryLaneResponseDto } from '@app/domain/asset/response-dto/memory-lane-response.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<AssetStatsResponseDto> { getAssetStats(@AuthUser() authUser: AuthUserDto, @Query() dto: AssetStatsDto): Promise<AssetStatsResponseDto> {
return this.service.getStatistics(authUser, dto); return this.service.getStatistics(authUser, dto);
} }
@Authenticated({ isShared: true })
@Get('time-buckets')
getTimeBuckets(@AuthUser() authUser: AuthUserDto, @Query() dto: TimeBucketDto): Promise<TimeBucketResponseDto[]> {
return this.service.getTimeBuckets(authUser, dto);
}
@Authenticated({ isShared: true })
@Get('time-bucket')
getByTimeBucket(@AuthUser() authUser: AuthUserDto, @Query() dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> {
return this.service.getByTimeBucket(authUser, dto);
}
} }

View File

@ -8,6 +8,9 @@ import {
MapMarkerSearchOptions, MapMarkerSearchOptions,
Paginated, Paginated,
PaginationOptions, PaginationOptions,
TimeBucketItem,
TimeBucketOptions,
TimeBucketSize,
WithoutProperty, WithoutProperty,
WithProperty, WithProperty,
} from '@app/domain'; } from '@app/domain';
@ -19,6 +22,11 @@ import { AssetEntity, AssetType } from '../entities';
import OptionalBetween from '../utils/optional-between.util'; import OptionalBetween from '../utils/optional-between.util';
import { paginate } from '../utils/pagination.util'; import { paginate } from '../utils/pagination.util';
const truncateMap: Record<TimeBucketSize, string> = {
[TimeBucketSize.DAY]: 'day',
[TimeBucketSize.MONTH]: 'month',
};
@Injectable() @Injectable()
export class AssetRepository implements IAssetRepository { export class AssetRepository implements IAssetRepository {
constructor(@InjectRepository(AssetEntity) private repository: Repository<AssetEntity>) {} constructor(@InjectRepository(AssetEntity) private repository: Repository<AssetEntity>) {}
@ -357,4 +365,47 @@ export class AssetRepository implements IAssetRepository {
return result; return result;
} }
getTimeBuckets(userId: string, options: TimeBucketOptions): Promise<TimeBucketItem[]> {
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<AssetEntity[]> {
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;
}
} }

View File

@ -10,14 +10,13 @@ export const newAssetRepositoryMock = (): jest.Mocked<IAssetRepository> => {
getWith: jest.fn(), getWith: jest.fn(),
getFirstAssetForAlbumId: jest.fn(), getFirstAssetForAlbumId: jest.fn(),
getLastUpdatedAssetForAlbumId: jest.fn(), getLastUpdatedAssetForAlbumId: jest.fn(),
getAll: jest.fn().mockResolvedValue({ getAll: jest.fn().mockResolvedValue({ items: [], hasNextPage: false }),
items: [],
hasNextPage: false,
}),
deleteAll: jest.fn(), deleteAll: jest.fn(),
save: jest.fn(), save: jest.fn(),
findLivePhotoMatch: jest.fn(), findLivePhotoMatch: jest.fn(),
getMapMarkers: jest.fn(), getMapMarkers: jest.fn(),
getStatistics: jest.fn(), getStatistics: jest.fn(),
getByTimeBucket: jest.fn(),
getTimeBuckets: jest.fn(),
}; };
}; };

View File

@ -410,44 +410,6 @@ export const AssetBulkUploadCheckResultReasonEnum = {
export type AssetBulkUploadCheckResultReasonEnum = typeof AssetBulkUploadCheckResultReasonEnum[keyof typeof 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<AssetCountByTimeBucket>}
* @memberof AssetCountByTimeBucketResponseDto
*/
'buckets': Array<AssetCountByTimeBucket>;
/**
*
* @type {number}
* @memberof AssetCountByTimeBucketResponseDto
*/
'totalCount': number;
}
/** /**
* *
* @export * @export
@ -1286,58 +1248,6 @@ export interface ExifResponseDto {
*/ */
'timeZone'?: string | null; 'timeZone'?: string | null;
} }
/**
*
* @export
* @interface GetAssetByTimeBucketDto
*/
export interface GetAssetByTimeBucketDto {
/**
*
* @type {Array<string>}
* @memberof GetAssetByTimeBucketDto
*/
'timeBucket': Array<string>;
/**
*
* @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 * @export
@ -2850,18 +2760,37 @@ export const ThumbnailFormat = {
export type ThumbnailFormat = typeof ThumbnailFormat[keyof typeof 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 * @export
* @enum {string} * @enum {string}
*/ */
export const TimeGroupEnum = { export const TimeBucketSize = {
Day: 'day', Day: 'DAY',
Month: 'month' Month: 'MONTH'
} as const; } 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, options: localVarRequestOptions,
}; };
}, },
/**
*
* @param {GetAssetByTimeBucketDto} getAssetByTimeBucketDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getAssetByTimeBucket: async (getAssetByTimeBucketDto: GetAssetByTimeBucketDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// 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<RequestArgs> => {
// 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. * @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<RequestArgs> => {
// 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); setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
@ -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<RequestArgs> => {
// 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); setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
@ -5969,26 +5957,6 @@ export const AssetApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetById(id, key, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetById(id, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); 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<Array<AssetResponseDto>>> {
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<AssetCountByTimeBucketResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetCountByTimeBucket(getAssetCountByTimeBucketDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/** /**
* *
* @param {*} [options] Override http request option. * @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); const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetThumbnail(id, format, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); 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<Array<AssetResponseDto>>> {
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. * @param {*} [options] Override http request option.
@ -6075,6 +6059,21 @@ export const AssetApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.getMemoryLane(timestamp, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getMemoryLane(timestamp, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); 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<Array<TimeBucketResponseDto>>> {
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. * Get all asset of a device that are in the database, ID only.
* @param {string} deviceId * @param {string} deviceId
@ -6242,24 +6241,6 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
getAssetById(id: string, key?: string, options?: any): AxiosPromise<AssetResponseDto> { getAssetById(id: string, key?: string, options?: any): AxiosPromise<AssetResponseDto> {
return localVarFp.getAssetById(id, key, options).then((request) => request(axios, basePath)); 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<Array<AssetResponseDto>> {
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<AssetCountByTimeBucketResponseDto> {
return localVarFp.getAssetCountByTimeBucket(getAssetCountByTimeBucketDto, options).then((request) => request(axios, basePath));
},
/** /**
* *
* @param {*} [options] Override http request option. * @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<File> { getAssetThumbnail(id: string, format?: ThumbnailFormat, key?: string, options?: any): AxiosPromise<File> {
return localVarFp.getAssetThumbnail(id, format, key, options).then((request) => request(axios, basePath)); 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<Array<AssetResponseDto>> {
return localVarFp.getByTimeBucket(size, timeBucket, userId, albumId, isArchived, isFavorite, key, options).then((request) => request(axios, basePath));
},
/** /**
* *
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
@ -6338,6 +6334,20 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
getMemoryLane(timestamp: string, options?: any): AxiosPromise<Array<MemoryLaneResponseDto>> { getMemoryLane(timestamp: string, options?: any): AxiosPromise<Array<MemoryLaneResponseDto>> {
return localVarFp.getMemoryLane(timestamp, options).then((request) => request(axios, basePath)); 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<Array<TimeBucketResponseDto>> {
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. * Get all asset of a device that are in the database, ID only.
* @param {string} deviceId * @param {string} deviceId
@ -6586,34 +6596,6 @@ export interface AssetApiGetAssetByIdRequest {
readonly key?: string 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. * Request parameters for getAssetStats operation in AssetApi.
* @export * @export
@ -6663,6 +6645,62 @@ export interface AssetApiGetAssetThumbnailRequest {
readonly key?: string 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. * Request parameters for getDownloadInfo operation in AssetApi.
* @export * @export
@ -6747,6 +6785,55 @@ export interface AssetApiGetMemoryLaneRequest {
readonly timestamp: string 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. * Request parameters for getUserAssetsByDeviceId operation in AssetApi.
* @export * @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)); 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. * @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)); 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. * @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)); 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. * Get all asset of a device that are in the database, ID only.
* @param {AssetApiGetUserAssetsByDeviceIdRequest} requestParameters Request parameters. * @param {AssetApiGetUserAssetsByDeviceIdRequest} requestParameters Request parameters.

View File

@ -3,7 +3,7 @@
import { AssetStore } from '$lib/stores/assets.store'; import { AssetStore } from '$lib/stores/assets.store';
import { locale } from '$lib/stores/preferences.store'; import { locale } from '$lib/stores/preferences.store';
import { openFileUploadDialog } from '$lib/utils/file-uploader'; 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 { createEventDispatcher, onMount } from 'svelte';
import { quintOut } from 'svelte/easing'; import { quintOut } from 'svelte/easing';
import { fly } from 'svelte/transition'; import { fly } from 'svelte/transition';
@ -13,7 +13,7 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const assetStore = new AssetStore({ timeGroup: TimeGroupEnum.Month }); const assetStore = new AssetStore({ size: TimeBucketSize.Month });
const assetInteractionStore = createAssetInteractionStore(); const assetInteractionStore = createAssetInteractionStore();
const { selectedAssets, assetsInAlbumState } = assetInteractionStore; const { selectedAssets, assetsInAlbumState } = assetInteractionStore;

View File

@ -7,6 +7,9 @@
export let hideNavbar = false; export let hideNavbar = false;
export let showUploadButton = false; export let showUploadButton = false;
export let title: string | undefined = undefined; export let title: string | undefined = undefined;
export let scrollbar = true;
$: scrollbarClass = scrollbar ? 'immich-scrollbar p-4 pb-8' : 'scrollbar-hidden pl-4';
</script> </script>
<header> <header>
@ -32,7 +35,7 @@
<slot name="buttons" /> <slot name="buttons" />
</div> </div>
<div class="immich-scrollbar absolute top-16 h-[calc(100%-theme(spacing.16))] w-full overflow-y-auto p-4 pb-8"> <div class="{scrollbarClass} absolute top-16 h-[calc(100%-theme(spacing.16))] w-full overflow-y-auto">
<slot /> <slot />
</div> </div>
</section> </section>

View File

@ -274,7 +274,7 @@
<!-- Right margin MUST be equal to the width of immich-scrubbable-scrollbar --> <!-- Right margin MUST be equal to the width of immich-scrubbable-scrollbar -->
<section <section
id="asset-grid" id="asset-grid"
class="scrollbar-hidden ml-4 mr-[60px] overflow-y-auto pb-4" class="scrollbar-hidden ml-4 mr-[60px] h-full overflow-y-auto pb-4"
bind:clientHeight={viewport.height} bind:clientHeight={viewport.height}
bind:clientWidth={viewport.width} bind:clientWidth={viewport.width}
bind:this={element} bind:this={element}

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import empty1Url from '$lib/assets/empty-1.svg'; import empty1Url from '$lib/assets/empty-1.svg';
export let actionHandler: undefined | (() => Promise<void>) = undefined; export let actionHandler: undefined | (() => unknown) = undefined;
export let text = ''; export let text = '';
export let alt = ''; export let alt = '';

View File

@ -1,4 +1,4 @@
import { api, AssetResponseDto, GetAssetCountByTimeBucketDto } from '@api'; import { api, AssetApiGetTimeBucketsRequest, AssetResponseDto } from '@api';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { handleError } from '../utils/handle-error'; import { handleError } from '../utils/handle-error';
@ -9,7 +9,7 @@ export enum BucketPosition {
Unknown = 'unknown', Unknown = 'unknown',
} }
export type AssetStoreOptions = GetAssetCountByTimeBucketDto; export type AssetStoreOptions = AssetApiGetTimeBucketsRequest;
export interface Viewport { export interface Viewport {
width: number; width: number;
@ -51,11 +51,9 @@ export class AssetStore {
subscribe = this.store$.subscribe; subscribe = this.store$.subscribe;
async init(viewport: Viewport) { async init(viewport: Viewport) {
const { data } = await api.assetApi.getAssetCountByTimeBucket({ const { data: buckets } = await api.assetApi.getTimeBuckets(this.options);
getAssetCountByTimeBucketDto: { ...this.options, withoutThumbs: true },
});
this.buckets = data.buckets.map((bucket) => { this.buckets = buckets.map((bucket) => {
const unwrappedWidth = (3 / 2) * bucket.count * THUMBNAIL_HEIGHT * (7 / 10); const unwrappedWidth = (3 / 2) * bucket.count * THUMBNAIL_HEIGHT * (7 / 10);
const rows = Math.ceil(unwrappedWidth / viewport.width); const rows = Math.ceil(unwrappedWidth / viewport.width);
const height = rows * THUMBNAIL_HEIGHT; const height = rows * THUMBNAIL_HEIGHT;
@ -101,14 +99,8 @@ export class AssetStore {
bucket.cancelToken = new AbortController(); bucket.cancelToken = new AbortController();
const { data: assets } = await api.assetApi.getAssetByTimeBucket( const { data: assets } = await api.assetApi.getByTimeBucket(
{ { ...this.options, timeBucket: bucketDate },
getAssetByTimeBucketDto: {
timeBucket: [bucketDate],
...this.options,
withoutThumbs: true,
},
},
{ signal: bucket.cancelToken.signal }, { signal: bucket.cancelToken.signal },
); );

View File

@ -6,70 +6,55 @@
import DeleteAssets from '$lib/components/photos-page/actions/delete-assets.svelte'; import DeleteAssets from '$lib/components/photos-page/actions/delete-assets.svelte';
import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte'; import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte';
import FavoriteAction from '$lib/components/photos-page/actions/favorite-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 AssetSelectContextMenu from '$lib/components/photos-page/asset-select-context-menu.svelte';
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte'; import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
import SelectAll from 'svelte-material-icons/SelectAll.svelte'; import { AssetStore } from '$lib/stores/assets.store';
import { archivedAsset } from '$lib/stores/archived-asset.store'; import { api, TimeBucketSize } from '@api';
import { handleError } from '$lib/utils/handle-error';
import { api, AssetResponseDto } from '@api';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import DotsVertical from 'svelte-material-icons/DotsVertical.svelte'; import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
import Plus from 'svelte-material-icons/Plus.svelte'; import Plus from 'svelte-material-icons/Plus.svelte';
import type { PageData } from './$types'; import type { PageData } from './$types';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
export let data: PageData; export let data: PageData;
let assetCount = 1;
let selectedAssets: Set<AssetResponseDto> = new Set(); const assetStore = new AssetStore({ size: TimeBucketSize.Month, isArchived: true });
$: isMultiSelectionMode = selectedAssets.size > 0; const assetInteractionStore = createAssetInteractionStore();
$: isAllFavorite = Array.from(selectedAssets).every((asset) => asset.isFavorite); const { isMultiSelectState, selectedAssets } = assetInteractionStore;
$: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite);
onMount(async () => { onMount(async () => {
try { const { data: stats } = await api.assetApi.getAssetStats({ isArchived: true });
const { data: assets } = await api.assetApi.getAllAssets({ assetCount = stats.total;
isArchived: true,
withoutThumbs: true,
});
$archivedAsset = assets;
} catch {
handleError(Error, 'Unable to load archived assets');
}
}); });
const onAssetDelete = (assetId: string) => {
$archivedAsset = $archivedAsset.filter((a) => a.id !== assetId);
};
const handleSelectAll = () => {
selectedAssets = new Set($archivedAsset);
};
</script> </script>
<UserPageLayout user={data.user} hideNavbar={isMultiSelectionMode} title={data.meta.title}> {#if $isMultiSelectState}
<!-- Empty Message --> <AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
{#if $archivedAsset.length === 0} <ArchiveAction unarchive onAssetArchive={(asset) => assetStore.removeAsset(asset.id)} />
<CreateSharedLink />
<SelectAllAssets {assetStore} {assetInteractionStore} />
<AssetSelectContextMenu icon={Plus} title="Add">
<AddToAlbum />
<AddToAlbum shared />
</AssetSelectContextMenu>
<DeleteAssets onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
<AssetSelectContextMenu icon={DotsVertical} title="Add">
<DownloadAction menuItem />
<FavoriteAction menuItem removeFavorite={isAllFavorite} />
</AssetSelectContextMenu>
</AssetSelectControlBar>
{/if}
<UserPageLayout user={data.user} hideNavbar={$isMultiSelectState} title={data.meta.title} scrollbar={!assetCount}>
{#if assetCount}
<AssetGrid {assetStore} {assetInteractionStore} />
{:else}
<EmptyPlaceholder text="Archive photos and videos to hide them from your Photos view" alt="Empty archive" /> <EmptyPlaceholder text="Archive photos and videos to hide them from your Photos view" alt="Empty archive" />
{/if} {/if}
<svelte:fragment slot="header">
{#if isMultiSelectionMode}
<AssetSelectControlBar assets={selectedAssets} clearSelect={() => (selectedAssets = new Set())}>
<ArchiveAction unarchive onAssetArchive={(asset) => onAssetDelete(asset.id)} />
<CircleIconButton title="Select all" logo={SelectAll} on:click={handleSelectAll} />
<CreateSharedLink />
<AssetSelectContextMenu icon={Plus} title="Add">
<AddToAlbum />
<AddToAlbum shared />
</AssetSelectContextMenu>
<DeleteAssets {onAssetDelete} />
<AssetSelectContextMenu icon={DotsVertical} title="Add">
<DownloadAction menuItem />
<FavoriteAction menuItem removeFavorite={isAllFavorite} />
</AssetSelectContextMenu>
</AssetSelectControlBar>
{/if}
</svelte:fragment>
<GalleryViewer assets={$archivedAsset} bind:selectedAssets viewFrom="archive-page" />
</UserPageLayout> </UserPageLayout>

View File

@ -6,60 +6,45 @@
import DeleteAssets from '$lib/components/photos-page/actions/delete-assets.svelte'; import DeleteAssets from '$lib/components/photos-page/actions/delete-assets.svelte';
import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte'; import DownloadAction from '$lib/components/photos-page/actions/download-action.svelte';
import FavoriteAction from '$lib/components/photos-page/actions/favorite-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 AssetSelectContextMenu from '$lib/components/photos-page/asset-select-context-menu.svelte';
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte'; import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
import { handleError } from '$lib/utils/handle-error'; import { AssetStore } from '$lib/stores/assets.store';
import { api, AssetResponseDto } from '@api'; import { api, TimeBucketSize } from '@api';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import DotsVertical from 'svelte-material-icons/DotsVertical.svelte'; import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
import Plus from 'svelte-material-icons/Plus.svelte'; import Plus from 'svelte-material-icons/Plus.svelte';
import Error from '../../+error.svelte';
import type { PageData } from './$types'; 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<AssetResponseDto> = new Set();
export let data: PageData; export let data: PageData;
let assetCount = 1;
$: isMultiSelectionMode = selectedAssets.size > 0; const assetStore = new AssetStore({ size: TimeBucketSize.Month, isFavorite: true });
$: isAllArchive = Array.from(selectedAssets).every((asset) => asset.isArchived); const assetInteractionStore = createAssetInteractionStore();
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
$: isAllArchive = Array.from($selectedAssets).every((asset) => asset.isArchived);
onMount(async () => { onMount(async () => {
try { const { data: stats } = await api.assetApi.getAssetStats({ isFavorite: true });
const { data: assets } = await api.assetApi.getAllAssets({ assetCount = stats.total;
isFavorite: true,
withoutThumbs: true,
});
favorites = assets;
} catch {
handleError(Error, 'Unable to load favorites');
}
}); });
const handleSelectAll = () => {
selectedAssets = new Set(favorites);
};
const onAssetDelete = (assetId: string) => {
favorites = favorites.filter((a) => a.id !== assetId);
};
</script> </script>
<!-- Multiselection mode app bar --> <!-- Multiselection mode app bar -->
{#if isMultiSelectionMode} {#if $isMultiSelectState}
<AssetSelectControlBar assets={selectedAssets} clearSelect={() => (selectedAssets = new Set())}> <AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
<FavoriteAction removeFavorite onAssetFavorite={(asset) => onAssetDelete(asset.id)} /> <FavoriteAction removeFavorite onAssetFavorite={(asset) => assetStore.removeAsset(asset.id)} />
<CreateSharedLink /> <CreateSharedLink />
<CircleIconButton title="Select all" logo={SelectAll} on:click={handleSelectAll} /> <SelectAllAssets {assetStore} {assetInteractionStore} />
<AssetSelectContextMenu icon={Plus} title="Add"> <AssetSelectContextMenu icon={Plus} title="Add">
<AddToAlbum /> <AddToAlbum />
<AddToAlbum shared /> <AddToAlbum shared />
</AssetSelectContextMenu> </AssetSelectContextMenu>
<DeleteAssets {onAssetDelete} /> <DeleteAssets onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
<AssetSelectContextMenu icon={DotsVertical} title="Menu"> <AssetSelectContextMenu icon={DotsVertical} title="Menu">
<DownloadAction menuItem /> <DownloadAction menuItem />
<ArchiveAction menuItem unarchive={isAllArchive} /> <ArchiveAction menuItem unarchive={isAllArchive} />
@ -67,13 +52,10 @@
</AssetSelectControlBar> </AssetSelectControlBar>
{/if} {/if}
<UserPageLayout user={data.user} hideNavbar={isMultiSelectionMode} title={data.meta.title}> <UserPageLayout user={data.user} hideNavbar={$isMultiSelectState} title={data.meta.title} scrollbar={!assetCount}>
<section> {#if assetCount}
<!-- Empty Message --> <AssetGrid {assetStore} {assetInteractionStore} />
{#if favorites.length === 0} {:else}
<EmptyPlaceholder text="Add favorites to quickly find your best pictures and videos" alt="Empty favorites" /> <EmptyPlaceholder text="Add favorites to quickly find your best pictures and videos" alt="Empty favorites" />
{/if} {/if}
<GalleryViewer assets={favorites} bind:selectedAssets viewFrom="favorites-page" />
</section>
</UserPageLayout> </UserPageLayout>

View File

@ -10,7 +10,7 @@
import { AppRoute } from '$lib/constants'; import { AppRoute } from '$lib/constants';
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store'; import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
import { AssetStore } from '$lib/stores/assets.store'; import { AssetStore } from '$lib/stores/assets.store';
import { TimeGroupEnum } from '@api'; import { TimeBucketSize } from '@api';
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte'; import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
import Plus from 'svelte-material-icons/Plus.svelte'; import Plus from 'svelte-material-icons/Plus.svelte';
@ -18,7 +18,7 @@
export let data: PageData; 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 assetInteractionStore = createAssetInteractionStore();
const { isMultiSelectState, selectedAssets } = assetInteractionStore; const { isMultiSelectState, selectedAssets } = assetInteractionStore;

View File

@ -15,8 +15,8 @@
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store'; import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
import { AssetStore } from '$lib/stores/assets.store'; import { AssetStore } from '$lib/stores/assets.store';
import { openFileUploadDialog } from '$lib/utils/file-uploader'; import { openFileUploadDialog } from '$lib/utils/file-uploader';
import { TimeGroupEnum, api } from '@api'; import { TimeBucketSize, api } from '@api';
import { onDestroy, onMount } from 'svelte'; import { onMount } from 'svelte';
import DotsVertical from 'svelte-material-icons/DotsVertical.svelte'; import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
import Plus from 'svelte-material-icons/Plus.svelte'; import Plus from 'svelte-material-icons/Plus.svelte';
import type { PageData } from './$types'; import type { PageData } from './$types';
@ -24,30 +24,22 @@
export let data: PageData; export let data: PageData;
let assetCount = 1; let assetCount = 1;
const assetStore = new AssetStore({ timeGroup: TimeGroupEnum.Month }); const assetStore = new AssetStore({ size: TimeBucketSize.Month, isArchived: false });
const assetInteractionStore = createAssetInteractionStore(); const assetInteractionStore = createAssetInteractionStore();
const { isMultiSelectState, selectedAssets } = assetInteractionStore; const { isMultiSelectState, selectedAssets } = assetInteractionStore;
$: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite);
onMount(async () => { onMount(async () => {
const { data: stats } = await api.assetApi.getAssetStats(); const { data: stats } = await api.assetApi.getAssetStats();
assetCount = stats.total; assetCount = stats.total;
}); });
onDestroy(() => {
assetInteractionStore.clearMultiselect();
});
$: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite);
const handleUpload = async () => {
openFileUploadDialog();
};
</script> </script>
<UserPageLayout user={data.user} hideNavbar={$isMultiSelectState} showUploadButton> <UserPageLayout user={data.user} hideNavbar={$isMultiSelectState} showUploadButton>
<svelte:fragment slot="header"> <svelte:fragment slot="header">
{#if $isMultiSelectState} {#if $isMultiSelectState}
<AssetSelectControlBar assets={$selectedAssets} clearSelect={assetInteractionStore.clearMultiselect}> <AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
<CreateSharedLink /> <CreateSharedLink />
<SelectAllAssets {assetStore} {assetInteractionStore} /> <SelectAllAssets {assetStore} {assetInteractionStore} />
<AssetSelectContextMenu icon={Plus} title="Add"> <AssetSelectContextMenu icon={Plus} title="Add">
@ -69,7 +61,7 @@
<MemoryLane /> <MemoryLane />
</AssetGrid> </AssetGrid>
{:else} {:else}
<EmptyPlaceholder text="CLICK TO UPLOAD YOUR FIRST PHOTO" actionHandler={handleUpload} /> <EmptyPlaceholder text="CLICK TO UPLOAD YOUR FIRST PHOTO" actionHandler={() => openFileUploadDialog()} />
{/if} {/if}
</svelte:fragment> </svelte:fragment>
</UserPageLayout> </UserPageLayout>