Merge branch 'main' of https://github.com/immich-app/immich into fix/library-scanning

This commit is contained in:
Jonathan Jogenfors 2023-10-09 15:17:16 +02:00
commit c111f11192
264 changed files with 7547 additions and 3366 deletions

View file

@ -13,20 +13,15 @@ jobs:
e2e-tests: e2e-tests:
name: Run end-to-end test suites name: Run end-to-end test suites
runs-on: ubuntu-latest runs-on: ubuntu-latest
defaults:
run:
working-directory: ./server
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
- name: Run npm install submodules: "recursive"
run: npm ci
- name: Run e2e tests - name: Run e2e tests
run: npm run test:e2e run: docker-compose -f ./docker/docker-compose.test.yml -p immich-test-e2e up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server-test --remove-orphans --build
if: ${{ !cancelled() }}
doc-tests: doc-tests:
name: Run documentation checks name: Run documentation checks
@ -223,15 +218,27 @@ jobs:
--health-retries 5 --health-retries 5
ports: ports:
- 5432:5432 - 5432:5432
defaults:
run:
working-directory: ./server
steps: steps:
- uses: actions/checkout@v4 - name: Checkout code
uses: actions/checkout@v4
- name: Install server dependencies - name: Install server dependencies
run: npm --prefix server ci run: npm ci
- name: Build the
run: npm run build
- name: Run existing migrations - name: Run existing migrations
run: npm --prefix server run typeorm:migrations:run run: npm run typeorm:migrations:run
- name: Generate new migrations - name: Generate new migrations
continue-on-error: true continue-on-error: true
run: npm --prefix server run typeorm:migrations:generate ./src/infra/migrations/TestMigration run: npm run typeorm:migrations:generate ./src/infra/migrations/TestMigration
- name: Find file changes - name: Find file changes
uses: tj-actions/verify-changed-files@v13.1 uses: tj-actions/verify-changed-files@v13.1
id: verify-changed-files id: verify-changed-files

3
.gitmodules vendored
View file

@ -1,3 +1,6 @@
[submodule "mobile/.isar"] [submodule "mobile/.isar"]
path = mobile/.isar path = mobile/.isar
url = https://github.com/isar/isar url = https://github.com/isar/isar
[submodule "server/test/assets"]
path = server/test/assets
url = https://github.com/immich-app/test-assets

View file

@ -20,7 +20,7 @@ pull-stage:
docker-compose -f ./docker/docker-compose.staging.yml pull docker-compose -f ./docker/docker-compose.staging.yml pull
test-e2e: test-e2e:
docker-compose -f ./docker/docker-compose.test.yml --env-file ./docker/.env.test -p immich-test-e2e up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server-test --remove-orphans --build docker-compose -f ./docker/docker-compose.test.yml -p immich-test-e2e up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server-test --remove-orphans --build
prod: prod:
docker-compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans docker-compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans

View file

@ -24,6 +24,7 @@
<a href="README_es_ES.md">Español</a> <a href="README_es_ES.md">Español</a>
<a href="README_fr_FR.md">Français</a> <a href="README_fr_FR.md">Français</a>
<a href="README_nl_NL.md">Nederlands</a> <a href="README_nl_NL.md">Nederlands</a>
<a href="README_ja_JP.md">日本語</a>
</p> </p>
## Disclaimer ## Disclaimer

View file

@ -24,6 +24,7 @@
<a href="README_ca_ES.md">Español</a> <a href="README_ca_ES.md">Español</a>
<a href="README_fr_FR.md">Français</a> <a href="README_fr_FR.md">Français</a>
<a href="README_nl_NL.md">Nederlands</a> <a href="README_nl_NL.md">Nederlands</a>
<a href="README_ja_JP.md">日本語</a>
</p> </p>
## Avís legal ## Avís legal

View file

@ -23,6 +23,7 @@
<a href="README_tr_TR.md">Türkçe</a> <a href="README_tr_TR.md">Türkçe</a>
<a href="README_ca_ES.md">Català</a> <a href="README_ca_ES.md">Català</a>
<a href="README_fr_FR.md">Français</a> <a href="README_fr_FR.md">Français</a>
<a href="README_ja_JP.md">日本語</a>
</p> </p>
## Descargo de responsabilidad ## Descargo de responsabilidad

View file

@ -24,6 +24,7 @@
<a href="README_es_ES.md">Español</a> <a href="README_es_ES.md">Español</a>
<a href="README_fr_FR.md">Français</a> <a href="README_fr_FR.md">Français</a>
<a href="README_nl_NL.md">Nederlands</a> <a href="README_nl_NL.md">Nederlands</a>
<a href="README_ja_JP.md">日本語</a>
</p> </p>
## Clause de non-responsabilité ## Clause de non-responsabilité

111
README_ja_JP.md Normal file
View file

@ -0,0 +1,111 @@
<p align="center">
<br/>
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: MIT"></a>
<a href="https://discord.gg/D8JsnBEuKb">
<img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" atl="Discord"/>
</a>
<br/>
<br/>
</p>
<p align="center">
<img src="design/immich-logo.svg" width="150" title="Login With Custom URL">
</p>
<h3 align="center">Immich - 高性能なセルフホスト 写真/ビデオバックアップソリューション</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Main Screenshot">
</a>
<br/>
<p align="center">
<a href="README_zh_CN.md">中文</a>
<a href="README_tr_TR.md">Türkçe</a>
<a href="README_ca_ES.md">Català</a>
<a href="README_es_ES.md">Español</a>
<a href="README_fr_FR.md">Français</a>
<a href="README_nl_NL.md">Nederlands</a>
</p>
## 免責事項
- ⚠️ このプロジェクトは **非常に活発に** 開発中です。
- ⚠️ バグの存在や変更が入ることも予想されます。
- ⚠️ **写真やビデオを保存する唯一の方法としてこのアプリを使用しないでください。**
- ⚠️ 大切な写真やビデオは、常に [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) のバックアッププランに従ってください!
## コンテンツ
- [公式ドキュメント](https://immich.app/docs)
- [ロードマップ](https://github.com/orgs/immich-app/projects/1)
- [デモ](#デモ)
- [機能](#機能)
- [紹介](https://immich.app/docs/overview/introduction)
- [インストール](https://immich.app/docs/install/requirements)
- [コントリビューションガイド](https://immich.app/docs/overview/support-the-project)
- [プロジェクトのサポート](#プロジェクトのサポート)
## ドキュメント
インストールガイドを含む主なドキュメントは、https://immich.app/ です。
## デモ
web デモは https://demo.immich.app からアクセスできます
モバイルアプリの場合、`Server Endpoint URL` には `https://demo.immich.app/api` を使用することができます
```bash title="Demo Credential"
The credential
email: demo@immich.app
password: demo
```
```
Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
```
# 機能
| 機能 | モバイル | Web |
| ------------------------------------------- | ------ | --- |
| ビデオや写真のアップロードと表示 | はい | はい |
| アプリを開いたとき自動バックアップ | はい | N/A |
| バックアップ用アルバム選択 | はい | N/A |
| 写真やビデオをローカルデバイスにダウンロード | はい | はい |
| マルチユーザー対応 | はい | はい |
| アルバムと共有アルバム | はい | はい |
| スクラブ可能/ドラッグ可能スクロールバ | はい | はい |
| 生のフォーマットに対応 | はい | はい |
| メタデータ表示EXIF、地図 | はい | はい |
| メタデータ、オブジェクト、フェース、CLIPによる検索 | はい | はい |
| 管理機能(ユーザー管理) | いいえ | はい |
| バックグラウンドバックアップ | はい | N/A |
| 仮想スクロール | はい | はい |
| OAuth サポート | はい | はい |
| API キー | N/A | はい |
| LivePhoto のバックアップと再生 | iOS | はい |
| ユーザー定義のストレージ構造 | はい | はい |
| 公開シェアリング | いいえ | はい |
| アーカイブとお気に入り | はい | はい |
| グローバルマップ | はい | はい |
| パートナー共有 | はい | はい |
| 思い出x 年前)顔認識とクラスタリング | はい | はい |
| 思い出x 年前) | はい | はい |
| オフラインサポート | はい | いいえ |
| 読み取り専用ギャラリー | はい | はい |
# プロジェクトのサポート
私はこのプロジェクトにコミットしてきました。ドキュメントを更新し、新しい機能を追加し、バグを修正し続けるつもりですが、私ひとりではできません。だから、続けるためのモチベーションをさらに高めてくれる皆さんの助けが必要なのです。
[selfhosted.show - In the episode 'The-organization-must-いいえt-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) のホストが言ったように、これはチームと私がやっていることの大規模な事業だ。そしていつの日か、フルタイムでこの仕事ができるようになりたいと思っています。
もし、あなたがこのプロジェクトに賛同し、このアプリを長く使い続けたいと思われるのであれば、以下のオプションから支援をご検討ください。
## 寄付
- GitHub スポンサー経由の[毎月の寄付](https://github.com/sponsors/alextran1502)
- GitHub スポンサー経由の[一回寄付](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502)
- [Librepay](https://liberapay.com/alex.tran1502/)
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX

View file

@ -24,6 +24,7 @@
<a href="README_es_ES.md">Español</a> <a href="README_es_ES.md">Español</a>
<a href="README_fr_FR.md">Français</a> <a href="README_fr_FR.md">Français</a>
<a href="README_nl_NL.md">Nederlands</a> <a href="README_nl_NL.md">Nederlands</a>
<a href="README_ja_JP.md">日本語</a>
</p> </p>
## Disclaimer ## Disclaimer

View file

@ -24,6 +24,7 @@
<a href="README_es_ES.md">Español</a> <a href="README_es_ES.md">Español</a>
<a href="README_fr_FR.md">Français</a> <a href="README_fr_FR.md">Français</a>
<a href="README_nl_NL.md">Nederlands</a> <a href="README_nl_NL.md">Nederlands</a>
<a href="README_ja_JP.md">日本語</a>
</p> </p>
## Feragatname ## Feragatname

View file

@ -28,6 +28,7 @@
<a href="README_es_ES.md">Español</a> <a href="README_es_ES.md">Español</a>
<a href="README_fr_FR.md">Français</a> <a href="README_fr_FR.md">Français</a>
<a href="README_nl_NL.md">Nederlands</a> <a href="README_nl_NL.md">Nederlands</a>
<a href="README_ja_JP.md">日本語</a>
</p> </p>

View file

@ -4,7 +4,7 @@
* Immich * Immich
* Immich API * Immich API
* *
* The version of the OpenAPI document: 1.79.1 * The version of the OpenAPI document: 1.81.1
* *
* *
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
@ -356,6 +356,25 @@ export interface AllJobStatusResponseDto {
*/ */
'videoConversion': JobStatusDto; 'videoConversion': JobStatusDto;
} }
/**
*
* @export
* @interface AssetBulkDeleteDto
*/
export interface AssetBulkDeleteDto {
/**
*
* @type {boolean}
* @memberof AssetBulkDeleteDto
*/
'force'?: boolean;
/**
*
* @type {Array<string>}
* @memberof AssetBulkDeleteDto
*/
'ids': Array<string>;
}
/** /**
* *
* @export * @export
@ -657,6 +676,12 @@ export interface AssetResponseDto {
* @memberof AssetResponseDto * @memberof AssetResponseDto
*/ */
'isReadOnly': boolean; 'isReadOnly': boolean;
/**
*
* @type {boolean}
* @memberof AssetResponseDto
*/
'isTrashed': boolean;
/** /**
* *
* @type {string} * @type {string}
@ -669,6 +694,12 @@ export interface AssetResponseDto {
* @memberof AssetResponseDto * @memberof AssetResponseDto
*/ */
'livePhotoVideoId'?: string | null; 'livePhotoVideoId'?: string | null;
/**
*
* @type {string}
* @memberof AssetResponseDto
*/
'localDateTime': string;
/** /**
* *
* @type {string} * @type {string}
@ -1351,54 +1382,6 @@ export interface CuratedObjectsResponseDto {
*/ */
'resizePath': string; 'resizePath': string;
} }
/**
*
* @export
* @interface DeleteAssetDto
*/
export interface DeleteAssetDto {
/**
*
* @type {Array<string>}
* @memberof DeleteAssetDto
*/
'ids': Array<string>;
}
/**
*
* @export
* @interface DeleteAssetResponseDto
*/
export interface DeleteAssetResponseDto {
/**
*
* @type {string}
* @memberof DeleteAssetResponseDto
*/
'id': string;
/**
*
* @type {DeleteAssetStatus}
* @memberof DeleteAssetResponseDto
*/
'status': DeleteAssetStatus;
}
/**
*
* @export
* @enum {string}
*/
export const DeleteAssetStatus = {
Success: 'SUCCESS',
Failed: 'FAILED'
} as const;
export type DeleteAssetStatus = typeof DeleteAssetStatus[keyof typeof DeleteAssetStatus];
/** /**
* *
* @export * @export
@ -2617,6 +2600,12 @@ export interface ServerConfigDto {
* @memberof ServerConfigDto * @memberof ServerConfigDto
*/ */
'oauthButtonText': string; 'oauthButtonText': string;
/**
*
* @type {number}
* @memberof ServerConfigDto
*/
'trashDays': number;
} }
/** /**
* *
@ -2690,6 +2679,12 @@ export interface ServerFeaturesDto {
* @memberof ServerFeaturesDto * @memberof ServerFeaturesDto
*/ */
'tagImage': boolean; 'tagImage': boolean;
/**
*
* @type {boolean}
* @memberof ServerFeaturesDto
*/
'trash': boolean;
} }
/** /**
* *
@ -3133,6 +3128,12 @@ export interface SystemConfigDto {
* @memberof SystemConfigDto * @memberof SystemConfigDto
*/ */
'thumbnail': SystemConfigThumbnailDto; 'thumbnail': SystemConfigThumbnailDto;
/**
*
* @type {SystemConfigTrashDto}
* @memberof SystemConfigDto
*/
'trash': SystemConfigTrashDto;
} }
/** /**
* *
@ -3542,6 +3543,12 @@ export interface SystemConfigTemplateStorageOptionDto {
* @memberof SystemConfigTemplateStorageOptionDto * @memberof SystemConfigTemplateStorageOptionDto
*/ */
'secondOptions': Array<string>; 'secondOptions': Array<string>;
/**
*
* @type {Array<string>}
* @memberof SystemConfigTemplateStorageOptionDto
*/
'weekOptions': Array<string>;
/** /**
* *
* @type {Array<string>} * @type {Array<string>}
@ -3582,6 +3589,25 @@ export interface SystemConfigThumbnailDto {
} }
/**
*
* @export
* @interface SystemConfigTrashDto
*/
export interface SystemConfigTrashDto {
/**
*
* @type {number}
* @memberof SystemConfigTrashDto
*/
'days': number;
/**
*
* @type {boolean}
* @memberof SystemConfigTrashDto
*/
'enabled': boolean;
}
/** /**
* *
* @export * @export
@ -5670,13 +5696,13 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
}, },
/** /**
* *
* @param {DeleteAssetDto} deleteAssetDto * @param {AssetBulkDeleteDto} assetBulkDeleteDto
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
deleteAsset: async (deleteAssetDto: DeleteAssetDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { deleteAssets: async (assetBulkDeleteDto: AssetBulkDeleteDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'deleteAssetDto' is not null or undefined // verify required parameter 'assetBulkDeleteDto' is not null or undefined
assertParamExists('deleteAsset', 'deleteAssetDto', deleteAssetDto) assertParamExists('deleteAssets', 'assetBulkDeleteDto', assetBulkDeleteDto)
const localVarPath = `/asset`; const localVarPath = `/asset`;
// use dummy base URL string because the URL constructor only accepts absolute URLs. // use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@ -5705,7 +5731,7 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
setSearchParams(localVarUrlObj, localVarQueryParameter); setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
localVarRequestOptions.data = serializeDataIfNeeded(deleteAssetDto, localVarRequestOptions, configuration) localVarRequestOptions.data = serializeDataIfNeeded(assetBulkDeleteDto, localVarRequestOptions, configuration)
return { return {
url: toPathString(localVarUrlObj), url: toPathString(localVarUrlObj),
@ -5799,6 +5825,44 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
emptyTrash: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/asset/trash/empty`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication cookie required
// authentication api_key required
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
// authentication bearer required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
setSearchParams(localVarUrlObj, localVarQueryParameter); setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
@ -5967,10 +6031,11 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* *
* @param {boolean} [isArchived] * @param {boolean} [isArchived]
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {boolean} [isTrashed]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getAssetStats: async (isArchived?: boolean, isFavorite?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { getAssetStats: async (isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/asset/statistics`; const localVarPath = `/asset/statistics`;
// use dummy base URL string because the URL constructor only accepts absolute URLs. // use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@ -6000,6 +6065,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
localVarQueryParameter['isFavorite'] = isFavorite; localVarQueryParameter['isFavorite'] = isFavorite;
} }
if (isTrashed !== undefined) {
localVarQueryParameter['isTrashed'] = isTrashed;
}
setSearchParams(localVarUrlObj, localVarQueryParameter); setSearchParams(localVarUrlObj, localVarQueryParameter);
@ -6072,11 +6141,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {string} [personId] * @param {string} [personId]
* @param {boolean} [isArchived] * @param {boolean} [isArchived]
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {boolean} [isTrashed]
* @param {string} [key] * @param {string} [key]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getByTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { getByTimeBucket: async (size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'size' is not null or undefined // verify required parameter 'size' is not null or undefined
assertParamExists('getByTimeBucket', 'size', size) assertParamExists('getByTimeBucket', 'size', size)
// verify required parameter 'timeBucket' is not null or undefined // verify required parameter 'timeBucket' is not null or undefined
@ -6126,6 +6196,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
localVarQueryParameter['isFavorite'] = isFavorite; localVarQueryParameter['isFavorite'] = isFavorite;
} }
if (isTrashed !== undefined) {
localVarQueryParameter['isTrashed'] = isTrashed;
}
if (timeBucket !== undefined) { if (timeBucket !== undefined) {
localVarQueryParameter['timeBucket'] = timeBucket; localVarQueryParameter['timeBucket'] = timeBucket;
} }
@ -6272,13 +6346,14 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
}, },
/** /**
* *
* @param {boolean} [isArchived]
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {string} [fileCreatedAfter] * @param {string} [fileCreatedAfter]
* @param {string} [fileCreatedBefore] * @param {string} [fileCreatedBefore]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getMapMarkers: async (isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { getMapMarkers: async (isArchived?: boolean, isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/asset/map-marker`; const localVarPath = `/asset/map-marker`;
// use dummy base URL string because the URL constructor only accepts absolute URLs. // use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@ -6300,6 +6375,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
// http bearer authentication required // http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration) await setBearerAuthToObject(localVarHeaderParameter, configuration)
if (isArchived !== undefined) {
localVarQueryParameter['isArchived'] = isArchived;
}
if (isFavorite !== undefined) { if (isFavorite !== undefined) {
localVarQueryParameter['isFavorite'] = isFavorite; localVarQueryParameter['isFavorite'] = isFavorite;
} }
@ -6329,13 +6408,16 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
}, },
/** /**
* *
* @param {string} timestamp Get pictures for +24 hours from this time going back x years * @param {number} day
* @param {number} month
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getMemoryLane: async (timestamp: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { getMemoryLane: async (day: number, month: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'timestamp' is not null or undefined // verify required parameter 'day' is not null or undefined
assertParamExists('getMemoryLane', 'timestamp', timestamp) assertParamExists('getMemoryLane', 'day', day)
// verify required parameter 'month' is not null or undefined
assertParamExists('getMemoryLane', 'month', month)
const localVarPath = `/asset/memory-lane`; const localVarPath = `/asset/memory-lane`;
// use dummy base URL string because the URL constructor only accepts absolute URLs. // use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@ -6357,10 +6439,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
// http bearer authentication required // http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration) await setBearerAuthToObject(localVarHeaderParameter, configuration)
if (timestamp !== undefined) { if (day !== undefined) {
localVarQueryParameter['timestamp'] = (timestamp as any instanceof Date) ? localVarQueryParameter['day'] = day;
(timestamp as any).toISOString() : }
timestamp;
if (month !== undefined) {
localVarQueryParameter['month'] = month;
} }
@ -6425,11 +6509,12 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {string} [personId] * @param {string} [personId]
* @param {boolean} [isArchived] * @param {boolean} [isArchived]
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {boolean} [isTrashed]
* @param {string} [key] * @param {string} [key]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { getTimeBuckets: async (size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'size' is not null or undefined // verify required parameter 'size' is not null or undefined
assertParamExists('getTimeBuckets', 'size', size) assertParamExists('getTimeBuckets', 'size', size)
const localVarPath = `/asset/time-buckets`; const localVarPath = `/asset/time-buckets`;
@ -6477,6 +6562,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
localVarQueryParameter['isFavorite'] = isFavorite; localVarQueryParameter['isFavorite'] = isFavorite;
} }
if (isTrashed !== undefined) {
localVarQueryParameter['isTrashed'] = isTrashed;
}
if (key !== undefined) { if (key !== undefined) {
localVarQueryParameter['key'] = key; localVarQueryParameter['key'] = key;
} }
@ -6578,6 +6667,88 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
options: localVarRequestOptions, options: localVarRequestOptions,
}; };
}, },
/**
*
* @param {BulkIdsDto} bulkIdsDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
restoreAssets: async (bulkIdsDto: BulkIdsDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'bulkIdsDto' is not null or undefined
assertParamExists('restoreAssets', 'bulkIdsDto', bulkIdsDto)
const localVarPath = `/asset/restore`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication cookie required
// authentication api_key required
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
// authentication bearer required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
localVarHeaderParameter['Content-Type'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
localVarRequestOptions.data = serializeDataIfNeeded(bulkIdsDto, localVarRequestOptions, configuration)
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
restoreTrash: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/asset/trash/restore`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication cookie required
// authentication api_key required
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
// authentication bearer required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/** /**
* *
* @param {AssetJobsDto} assetJobsDto * @param {AssetJobsDto} assetJobsDto
@ -6992,12 +7163,12 @@ export const AssetApiFp = function(configuration?: Configuration) {
}, },
/** /**
* *
* @param {DeleteAssetDto} deleteAssetDto * @param {AssetBulkDeleteDto} assetBulkDeleteDto
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async deleteAsset(deleteAssetDto: DeleteAssetDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<DeleteAssetResponseDto>>> { async deleteAssets(assetBulkDeleteDto: AssetBulkDeleteDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAsset(deleteAssetDto, options); const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAssets(assetBulkDeleteDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/** /**
@ -7022,6 +7193,15 @@ export const AssetApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.downloadFile(id, key, options); const localVarAxiosArgs = await localVarAxiosParamCreator.downloadFile(id, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async emptyTrash(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.emptyTrash(options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/** /**
* Get all AssetEntity belong to the user * Get all AssetEntity belong to the user
* @param {string} [userId] * @param {string} [userId]
@ -7061,11 +7241,12 @@ export const AssetApiFp = function(configuration?: Configuration) {
* *
* @param {boolean} [isArchived] * @param {boolean} [isArchived]
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {boolean} [isTrashed]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async getAssetStats(isArchived?: boolean, isFavorite?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetStatsResponseDto>> { async getAssetStats(isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetStatsResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetStats(isArchived, isFavorite, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getAssetStats(isArchived, isFavorite, isTrashed, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/** /**
@ -7089,12 +7270,13 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {string} [personId] * @param {string} [personId]
* @param {boolean} [isArchived] * @param {boolean} [isArchived]
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {boolean} [isTrashed]
* @param {string} [key] * @param {string} [key]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async getByTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> { async getByTimeBucket(size: TimeBucketSize, timeBucket: string, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, key, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/** /**
@ -7128,24 +7310,26 @@ export const AssetApiFp = function(configuration?: Configuration) {
}, },
/** /**
* *
* @param {boolean} [isArchived]
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {string} [fileCreatedAfter] * @param {string} [fileCreatedAfter]
* @param {string} [fileCreatedBefore] * @param {string} [fileCreatedBefore]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async getMapMarkers(isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<MapMarkerResponseDto>>> { async getMapMarkers(isArchived?: boolean, isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<MapMarkerResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getMapMarkers(isFavorite, fileCreatedAfter, fileCreatedBefore, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getMapMarkers(isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/** /**
* *
* @param {string} timestamp Get pictures for +24 hours from this time going back x years * @param {number} day
* @param {number} month
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async getMemoryLane(timestamp: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<MemoryLaneResponseDto>>> { async getMemoryLane(day: number, month: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<MemoryLaneResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getMemoryLane(timestamp, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getMemoryLane(day, month, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/** /**
@ -7166,12 +7350,13 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {string} [personId] * @param {string} [personId]
* @param {boolean} [isArchived] * @param {boolean} [isArchived]
* @param {boolean} [isFavorite] * @param {boolean} [isFavorite]
* @param {boolean} [isTrashed]
* @param {string} [key] * @param {string} [key]
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<TimeBucketResponseDto>>> { async getTimeBuckets(size: TimeBucketSize, userId?: string, albumId?: string, personId?: string, isArchived?: boolean, isFavorite?: boolean, isTrashed?: boolean, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<TimeBucketResponseDto>>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, key, options); const localVarAxiosArgs = await localVarAxiosParamCreator.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, key, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/** /**
@ -7194,6 +7379,25 @@ export const AssetApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.importFile(importAssetDto, options); const localVarAxiosArgs = await localVarAxiosParamCreator.importFile(importAssetDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
}, },
/**
*
* @param {BulkIdsDto} bulkIdsDto
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async restoreAssets(bulkIdsDto: BulkIdsDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.restoreAssets(bulkIdsDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async restoreTrash(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.restoreTrash(options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/** /**
* *
* @param {AssetJobsDto} assetJobsDto * @param {AssetJobsDto} assetJobsDto
@ -7312,12 +7516,12 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
}, },
/** /**
* *
* @param {AssetApiDeleteAssetRequest} requestParameters Request parameters. * @param {AssetApiDeleteAssetsRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
deleteAsset(requestParameters: AssetApiDeleteAssetRequest, options?: AxiosRequestConfig): AxiosPromise<Array<DeleteAssetResponseDto>> { deleteAssets(requestParameters: AssetApiDeleteAssetsRequest, options?: AxiosRequestConfig): AxiosPromise<void> {
return localVarFp.deleteAsset(requestParameters.deleteAssetDto, options).then((request) => request(axios, basePath)); return localVarFp.deleteAssets(requestParameters.assetBulkDeleteDto, options).then((request) => request(axios, basePath));
}, },
/** /**
* *
@ -7337,6 +7541,14 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
downloadFile(requestParameters: AssetApiDownloadFileRequest, options?: AxiosRequestConfig): AxiosPromise<File> { downloadFile(requestParameters: AssetApiDownloadFileRequest, options?: AxiosRequestConfig): AxiosPromise<File> {
return localVarFp.downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(axios, basePath)); return localVarFp.downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(axios, basePath));
}, },
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
emptyTrash(options?: AxiosRequestConfig): AxiosPromise<void> {
return localVarFp.emptyTrash(options).then((request) => request(axios, basePath));
},
/** /**
* Get all AssetEntity belong to the user * Get all AssetEntity belong to the user
* @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters.
@ -7370,7 +7582,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<AssetStatsResponseDto> { getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<AssetStatsResponseDto> {
return localVarFp.getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, options).then((request) => request(axios, basePath)); return localVarFp.getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, options).then((request) => request(axios, basePath));
}, },
/** /**
* *
@ -7388,7 +7600,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> { getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> {
return localVarFp.getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(axios, basePath)); return localVarFp.getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(axios, basePath));
}, },
/** /**
* *
@ -7422,7 +7634,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getMapMarkers(requestParameters: AssetApiGetMapMarkersRequest = {}, options?: AxiosRequestConfig): AxiosPromise<Array<MapMarkerResponseDto>> { getMapMarkers(requestParameters: AssetApiGetMapMarkersRequest = {}, options?: AxiosRequestConfig): AxiosPromise<Array<MapMarkerResponseDto>> {
return localVarFp.getMapMarkers(requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(axios, basePath)); return localVarFp.getMapMarkers(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(axios, basePath));
}, },
/** /**
* *
@ -7431,7 +7643,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @throws {RequiredError} * @throws {RequiredError}
*/ */
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.day, requestParameters.month, options).then((request) => request(axios, basePath));
}, },
/** /**
* *
@ -7449,7 +7661,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @throws {RequiredError} * @throws {RequiredError}
*/ */
getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig): AxiosPromise<Array<TimeBucketResponseDto>> { getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig): AxiosPromise<Array<TimeBucketResponseDto>> {
return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(axios, basePath)); return localVarFp.getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(axios, basePath));
}, },
/** /**
* Get all asset of a device that are in the database, ID only. * Get all asset of a device that are in the database, ID only.
@ -7469,6 +7681,23 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
importFile(requestParameters: AssetApiImportFileRequest, options?: AxiosRequestConfig): AxiosPromise<AssetFileUploadResponseDto> { importFile(requestParameters: AssetApiImportFileRequest, options?: AxiosRequestConfig): AxiosPromise<AssetFileUploadResponseDto> {
return localVarFp.importFile(requestParameters.importAssetDto, options).then((request) => request(axios, basePath)); return localVarFp.importFile(requestParameters.importAssetDto, options).then((request) => request(axios, basePath));
}, },
/**
*
* @param {AssetApiRestoreAssetsRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
restoreAssets(requestParameters: AssetApiRestoreAssetsRequest, options?: AxiosRequestConfig): AxiosPromise<void> {
return localVarFp.restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(axios, basePath));
},
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
restoreTrash(options?: AxiosRequestConfig): AxiosPromise<void> {
return localVarFp.restoreTrash(options).then((request) => request(axios, basePath));
},
/** /**
* *
* @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters.
@ -7576,17 +7805,17 @@ export interface AssetApiCheckExistingAssetsRequest {
} }
/** /**
* Request parameters for deleteAsset operation in AssetApi. * Request parameters for deleteAssets operation in AssetApi.
* @export * @export
* @interface AssetApiDeleteAssetRequest * @interface AssetApiDeleteAssetsRequest
*/ */
export interface AssetApiDeleteAssetRequest { export interface AssetApiDeleteAssetsRequest {
/** /**
* *
* @type {DeleteAssetDto} * @type {AssetBulkDeleteDto}
* @memberof AssetApiDeleteAsset * @memberof AssetApiDeleteAssets
*/ */
readonly deleteAssetDto: DeleteAssetDto readonly assetBulkDeleteDto: AssetBulkDeleteDto
} }
/** /**
@ -7720,6 +7949,13 @@ export interface AssetApiGetAssetStatsRequest {
* @memberof AssetApiGetAssetStats * @memberof AssetApiGetAssetStats
*/ */
readonly isFavorite?: boolean readonly isFavorite?: boolean
/**
*
* @type {boolean}
* @memberof AssetApiGetAssetStats
*/
readonly isTrashed?: boolean
} }
/** /**
@ -7805,6 +8041,13 @@ export interface AssetApiGetByTimeBucketRequest {
*/ */
readonly isFavorite?: boolean readonly isFavorite?: boolean
/**
*
* @type {boolean}
* @memberof AssetApiGetByTimeBucket
*/
readonly isTrashed?: boolean
/** /**
* *
* @type {string} * @type {string}
@ -7840,6 +8083,13 @@ export interface AssetApiGetDownloadInfoRequest {
* @interface AssetApiGetMapMarkersRequest * @interface AssetApiGetMapMarkersRequest
*/ */
export interface AssetApiGetMapMarkersRequest { export interface AssetApiGetMapMarkersRequest {
/**
*
* @type {boolean}
* @memberof AssetApiGetMapMarkers
*/
readonly isArchived?: boolean
/** /**
* *
* @type {boolean} * @type {boolean}
@ -7869,11 +8119,18 @@ export interface AssetApiGetMapMarkersRequest {
*/ */
export interface AssetApiGetMemoryLaneRequest { export interface AssetApiGetMemoryLaneRequest {
/** /**
* Get pictures for +24 hours from this time going back x years *
* @type {string} * @type {number}
* @memberof AssetApiGetMemoryLane * @memberof AssetApiGetMemoryLane
*/ */
readonly timestamp: string readonly day: number
/**
*
* @type {number}
* @memberof AssetApiGetMemoryLane
*/
readonly month: number
} }
/** /**
@ -7938,6 +8195,13 @@ export interface AssetApiGetTimeBucketsRequest {
*/ */
readonly isFavorite?: boolean readonly isFavorite?: boolean
/**
*
* @type {boolean}
* @memberof AssetApiGetTimeBuckets
*/
readonly isTrashed?: boolean
/** /**
* *
* @type {string} * @type {string}
@ -7974,6 +8238,20 @@ export interface AssetApiImportFileRequest {
readonly importAssetDto: ImportAssetDto readonly importAssetDto: ImportAssetDto
} }
/**
* Request parameters for restoreAssets operation in AssetApi.
* @export
* @interface AssetApiRestoreAssetsRequest
*/
export interface AssetApiRestoreAssetsRequest {
/**
*
* @type {BulkIdsDto}
* @memberof AssetApiRestoreAssets
*/
readonly bulkIdsDto: BulkIdsDto
}
/** /**
* Request parameters for runAssetJobs operation in AssetApi. * Request parameters for runAssetJobs operation in AssetApi.
* @export * @export
@ -8233,13 +8511,13 @@ export class AssetApi extends BaseAPI {
/** /**
* *
* @param {AssetApiDeleteAssetRequest} requestParameters Request parameters. * @param {AssetApiDeleteAssetsRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
* @memberof AssetApi * @memberof AssetApi
*/ */
public deleteAsset(requestParameters: AssetApiDeleteAssetRequest, options?: AxiosRequestConfig) { public deleteAssets(requestParameters: AssetApiDeleteAssetsRequest, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).deleteAsset(requestParameters.deleteAssetDto, options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).deleteAssets(requestParameters.assetBulkDeleteDto, options).then((request) => request(this.axios, this.basePath));
} }
/** /**
@ -8264,6 +8542,16 @@ export class AssetApi extends BaseAPI {
return AssetApiFp(this.configuration).downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).downloadFile(requestParameters.id, requestParameters.key, options).then((request) => request(this.axios, this.basePath));
} }
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof AssetApi
*/
public emptyTrash(options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).emptyTrash(options).then((request) => request(this.axios, this.basePath));
}
/** /**
* Get all AssetEntity belong to the user * Get all AssetEntity belong to the user
* @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters. * @param {AssetApiGetAllAssetsRequest} requestParameters Request parameters.
@ -8304,7 +8592,7 @@ export class AssetApi extends BaseAPI {
* @memberof AssetApi * @memberof AssetApi
*/ */
public getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig) { public getAssetStats(requestParameters: AssetApiGetAssetStatsRequest = {}, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).getAssetStats(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, options).then((request) => request(this.axios, this.basePath));
} }
/** /**
@ -8326,7 +8614,7 @@ export class AssetApi extends BaseAPI {
* @memberof AssetApi * @memberof AssetApi
*/ */
public getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig) { public getByTimeBucket(requestParameters: AssetApiGetByTimeBucketRequest, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).getByTimeBucket(requestParameters.size, requestParameters.timeBucket, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(this.axios, this.basePath));
} }
/** /**
@ -8368,7 +8656,7 @@ export class AssetApi extends BaseAPI {
* @memberof AssetApi * @memberof AssetApi
*/ */
public getMapMarkers(requestParameters: AssetApiGetMapMarkersRequest = {}, options?: AxiosRequestConfig) { public getMapMarkers(requestParameters: AssetApiGetMapMarkersRequest = {}, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).getMapMarkers(requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).getMapMarkers(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(this.axios, this.basePath));
} }
/** /**
@ -8379,7 +8667,7 @@ export class AssetApi extends BaseAPI {
* @memberof AssetApi * @memberof AssetApi
*/ */
public getMemoryLane(requestParameters: AssetApiGetMemoryLaneRequest, options?: AxiosRequestConfig) { public getMemoryLane(requestParameters: AssetApiGetMemoryLaneRequest, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).getMemoryLane(requestParameters.timestamp, options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).getMemoryLane(requestParameters.day, requestParameters.month, options).then((request) => request(this.axios, this.basePath));
} }
/** /**
@ -8401,7 +8689,7 @@ export class AssetApi extends BaseAPI {
* @memberof AssetApi * @memberof AssetApi
*/ */
public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig) { public getTimeBuckets(requestParameters: AssetApiGetTimeBucketsRequest, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.key, options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).getTimeBuckets(requestParameters.size, requestParameters.userId, requestParameters.albumId, requestParameters.personId, requestParameters.isArchived, requestParameters.isFavorite, requestParameters.isTrashed, requestParameters.key, options).then((request) => request(this.axios, this.basePath));
} }
/** /**
@ -8426,6 +8714,27 @@ export class AssetApi extends BaseAPI {
return AssetApiFp(this.configuration).importFile(requestParameters.importAssetDto, options).then((request) => request(this.axios, this.basePath)); return AssetApiFp(this.configuration).importFile(requestParameters.importAssetDto, options).then((request) => request(this.axios, this.basePath));
} }
/**
*
* @param {AssetApiRestoreAssetsRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof AssetApi
*/
public restoreAssets(requestParameters: AssetApiRestoreAssetsRequest, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).restoreAssets(requestParameters.bulkIdsDto, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof AssetApi
*/
public restoreTrash(options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).restoreTrash(options).then((request) => request(this.axios, this.basePath));
}
/** /**
* *
* @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters. * @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters.

View file

@ -4,7 +4,7 @@
* Immich * Immich
* Immich API * Immich API
* *
* The version of the OpenAPI document: 1.79.1 * The version of the OpenAPI document: 1.81.1
* *
* *
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -4,7 +4,7 @@
* Immich * Immich
* Immich API * Immich API
* *
* The version of the OpenAPI document: 1.79.1 * The version of the OpenAPI document: 1.81.1
* *
* *
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -4,7 +4,7 @@
* Immich * Immich
* Immich API * Immich API
* *
* The version of the OpenAPI document: 1.79.1 * The version of the OpenAPI document: 1.81.1
* *
* *
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -4,7 +4,7 @@
* Immich * Immich
* Immich API * Immich API
* *
* The version of the OpenAPI document: 1.79.1 * The version of the OpenAPI document: 1.81.1
* *
* *
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

View file

@ -1,16 +0,0 @@
# Database
DB_HOSTNAME=immich-database-test
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_DATABASE_NAME=e2e_test
# Redis
REDIS_HOSTNAME=immich-redis-test
# Upload File Config
UPLOAD_LOCATION=./upload
# WEB
VITE_SERVER_ENDPOINT=http://localhost:2283/api
TYPESENSE_ENABLED=false

View file

@ -11,8 +11,9 @@ services:
command: npm run start:debug immich command: npm run start:debug immich
volumes: volumes:
- ../server:/usr/src/app - ../server:/usr/src/app
- ${UPLOAD_LOCATION}:/usr/src/app/upload - ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
- /usr/src/app/node_modules - /usr/src/app/node_modules
- /etc/localtime:/etc/localtime:ro
ports: ports:
- 3001:3001 - 3001:3001
- 9230:9230 - 9230:9230
@ -25,25 +26,6 @@ services:
- database - database
- typesense - typesense
immich-machine-learning:
container_name: immich_machine_learning
image: immich-machine-learning-dev:latest
build:
context: ../machine-learning
dockerfile: Dockerfile
ports:
- 3003:3003
volumes:
- ../machine-learning:/usr/src/app
- model-cache:/cache
env_file:
- .env
environment:
- NODE_ENV=development
depends_on:
- database
restart: unless-stopped
immich-microservices: immich-microservices:
container_name: immich_microservices container_name: immich_microservices
image: immich-microservices:latest image: immich-microservices:latest
@ -57,8 +39,9 @@ services:
command: npm run start:debug microservices command: npm run start:debug microservices
volumes: volumes:
- ../server:/usr/src/app - ../server:/usr/src/app
- ${UPLOAD_LOCATION}:/usr/src/app/upload - ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
- /usr/src/app/node_modules - /usr/src/app/node_modules
- /etc/localtime:/etc/localtime:ro
env_file: env_file:
- .env - .env
ports: ports:
@ -94,6 +77,25 @@ services:
depends_on: depends_on:
- immich-server - immich-server
immich-machine-learning:
container_name: immich_machine_learning
image: immich-machine-learning-dev:latest
build:
context: ../machine-learning
dockerfile: Dockerfile
ports:
- 3003:3003
volumes:
- ../machine-learning:/usr/src/app
- model-cache:/cache
env_file:
- .env
environment:
- NODE_ENV=development
depends_on:
- database
restart: unless-stopped
typesense: typesense:
container_name: immich_typesense container_name: immich_typesense
image: typesense/typesense:0.24.1@sha256:9bcff2b829f12074426ca044b56160ca9d777a0c488303469143dd9f8259d4dd image: typesense/typesense:0.24.1@sha256:9bcff2b829f12074426ca044b56160ca9d777a0c488303469143dd9f8259d4dd
@ -103,7 +105,7 @@ services:
# remove this to get debug messages # remove this to get debug messages
- GLOG_minloglevel=1 - GLOG_minloglevel=1
volumes: volumes:
- tsdata:/data - ${UPLOAD_LOCATION}/typesense:/data
redis: redis:
container_name: immich_redis container_name: immich_redis
@ -119,7 +121,7 @@ services:
POSTGRES_USER: ${DB_USERNAME} POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME} POSTGRES_DB: ${DB_DATABASE_NAME}
volumes: volumes:
- pgdata:/var/lib/postgresql/data - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
ports: ports:
- 5432:5432 - 5432:5432
@ -141,6 +143,4 @@ services:
restart: unless-stopped restart: unless-stopped
volumes: volumes:
pgdata:
model-cache: model-cache:
tsdata:

View file

@ -10,6 +10,7 @@ services:
command: ["./start-server.sh"] command: ["./start-server.sh"]
volumes: volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload - ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
env_file: env_file:
- .env - .env
depends_on: depends_on:
@ -29,7 +30,7 @@ services:
env_file: env_file:
- .env - .env
restart: always restart: always
immich-microservices: immich-microservices:
container_name: immich_microservices container_name: immich_microservices
image: immich-microservices:latest image: immich-microservices:latest
@ -42,6 +43,7 @@ services:
command: ["./start-microservices.sh"] command: ["./start-microservices.sh"]
volumes: volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload - ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
env_file: env_file:
- .env - .env
depends_on: depends_on:

View file

@ -1,5 +1,7 @@
version: "3.8" version: "3.8"
# Compose file for dockerized end-to-end testing of the backend
services: services:
immich-server-test: immich-server-test:
image: immich-server-test image: immich-server-test
@ -8,39 +10,31 @@ services:
dockerfile: Dockerfile dockerfile: Dockerfile
target: builder target: builder
command: npm run test:e2e command: npm run test:e2e
expose:
- "3000"
volumes: volumes:
- ../server:/usr/src/app - ../server:/usr/src/app
- /usr/src/app/node_modules - /usr/src/app/node_modules
env_file:
- .env.test
environment: environment:
- NODE_ENV=development - DB_HOSTNAME=immich-database-test
- TYPESENSE_ENABLED=false - DB_USERNAME=postgres
- DB_PASSWORD=postgres
- DB_DATABASE_NAME=e2e_test
- IMMICH_RUN_ALL_TESTS=true
depends_on: depends_on:
- immich-redis-test
- immich-database-test - immich-database-test
networks: networks:
- immich-test-network - immich-test-network
immich-redis-test:
container_name: immich-redis-test
image: redis:6.2-alpine@sha256:70a7a5b641117670beae0d80658430853896b5ef269ccf00d1827427e3263fa3
networks:
- immich-test-network
immich-database-test: immich-database-test:
container_name: immich-database-test container_name: immich-database-test
image: postgres:14-alpine@sha256:28407a9961e76f2d285dc6991e8e48893503cc3836a4755bbc2d40bcc272a441 image: postgres:14-alpine@sha256:28407a9961e76f2d285dc6991e8e48893503cc3836a4755bbc2d40bcc272a441
env_file:
- .env.test
environment: environment:
POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_PASSWORD: postgres
POSTGRES_USER: ${DB_USERNAME} POSTGRES_USER: postgres
POSTGRES_DB: ${DB_DATABASE_NAME} POSTGRES_DB: e2e_test
volumes:
- /var/lib/postgresql/data
networks: networks:
- immich-test-network - immich-test-network
logging:
driver: none
networks: networks:
immich-test-network: immich-test-network:

View file

@ -4,9 +4,10 @@ services:
immich-server: immich-server:
container_name: immich_server container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release} image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
command: [ "start.sh", "immich" ] command: ["start.sh", "immich"]
volumes: volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload - ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
env_file: env_file:
- .env - .env
depends_on: depends_on:
@ -21,9 +22,10 @@ services:
# extends: # extends:
# file: hwaccel.yml # file: hwaccel.yml
# service: hwaccel # service: hwaccel
command: [ "start.sh", "microservices" ] command: ["start.sh", "microservices"]
volumes: volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload - ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
env_file: env_file:
- .env - .env
depends_on: depends_on:

View file

@ -19,7 +19,7 @@ Users can deploy a custom reverse proxy that forwards requests to Immich's rever
### Nginx example config ### Nginx example config
Below is an example config for nginx: Below is an example config for nginx. Make sure to include `client_max_body_size 50000M;` also in a `http` block in `/etc/nginx/nginx.conf`.
```nginx ```nginx
server { server {

View file

@ -0,0 +1,17 @@
# Testing
## Server
### Unit tests
Unit are run by calling `npm run test` from the `server` directory.
### End to end tests
The backend has an end-to-end test suite that can be called with `npm run test:e2e` from the `server` directory. This will set up a dummy database inside a temporary container and run the tests against it. Setup and teardown is automatically taken care of. That test, however, can not set up all prerequisites to parse file formats, as that is very complex and error-prone. As such, this test excludes some test cases like HEIC file imports. The test suite will also print a friendly warning to remind you that not all tests are being run.
Note that there is a bug in nodejs <20.8 that causes segmentation faults when running these tests. If you run into segfaults, ensure you are using at least version 20.8.
To perform a full e2e test, you need to run e2e tests inside docker. The easiest way to do that is to run `make test-e2e` in the root directory. This will build and start a docker-compose consisting of the server, microservices, and a postgres database. It will then perfom the tests and exit.
If you manually install the dependencies (see the DOCKERFILE) on your development machine, you can also run the full e2e tests manually by setting the `IMMICH_RUN_ALL_TESTS` environment value to true, i.e. `IMMICH_RUN_ALL_TESTS=true npm run test:e2e`.

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "machine-learning" name = "machine-learning"
version = "1.79.1" version = "1.81.1"
description = "" description = ""
authors = ["Hau Tran <alex.tran1502@gmail.com>"] authors = ["Hau Tran <alex.tran1502@gmail.com>"]
readme = "README.md" readme = "README.md"

View file

@ -35,8 +35,8 @@ platform :android do
task: 'bundle', task: 'bundle',
build_type: 'Release', build_type: 'Release',
properties: { properties: {
"android.injected.version.code" => 103, "android.injected.version.code" => 105,
"android.injected.version.name" => "1.79.1", "android.injected.version.name" => "1.81.1",
} }
) )
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')

View file

@ -163,7 +163,6 @@
"home_page_building_timeline": "Building the timeline", "home_page_building_timeline": "Building the timeline",
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping", "home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
"home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping",
"image_viewer_page_state_provider_download_error": "Download Error", "image_viewer_page_state_provider_download_error": "Download Error",
"image_viewer_page_state_provider_download_success": "Download Success", "image_viewer_page_state_provider_download_success": "Download Success",
"library_page_albums": "Albums", "library_page_albums": "Albums",
@ -312,6 +311,7 @@
"map_settings_dialog_title": "Map Settings", "map_settings_dialog_title": "Map Settings",
"map_settings_dark_mode": "Dark mode", "map_settings_dark_mode": "Dark mode",
"map_settings_only_show_favorites": "Show Favorite Only", "map_settings_only_show_favorites": "Show Favorite Only",
"map_settings_include_show_archived": "Include Archived",
"map_settings_only_relative_range": "Date range", "map_settings_only_relative_range": "Date range",
"map_settings_dialog_cancel": "Cancel", "map_settings_dialog_cancel": "Cancel",
"map_settings_dialog_save": "Save", "map_settings_dialog_save": "Save",
@ -321,5 +321,17 @@
"map_no_location_permission_title": "Location Permission denied", "map_no_location_permission_title": "Location Permission denied",
"map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?", "map_no_location_permission_content": "Location permission is needed to display assets from your current location. Do you want to allow it now?",
"map_location_dialog_cancel": "Cancel", "map_location_dialog_cancel": "Cancel",
"map_location_dialog_yes": "Yes" "map_location_dialog_yes": "Yes",
"trash_page_title": "Trash ({})",
"trash_page_info": "Backed up items will be permanently deleted after {} days",
"trash_page_no_assets": "No trashed assets",
"trash_page_delete": "Delete",
"trash_page_delete_all": "Delete All",
"trash_page_restore": "Restore",
"trash_page_restore_all": "Restore All",
"trash_page_select_btn": "Select",
"trash_page_select_assets_btn": "Select assets",
"trash_page_empty_trash_btn": "Empty trash",
"trash_page_empty_trash_dialog_ok": "Ok",
"trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Immich"
} }

View file

@ -169,4 +169,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 599d8aeb73728400c15364e734525722250a5382 PODFILE CHECKSUM: 599d8aeb73728400c15364e734525722250a5382
COCOAPODS: 1.12.1 COCOAPODS: 1.11.3

View file

@ -19,7 +19,7 @@ platform :ios do
desc "iOS Beta" desc "iOS Beta"
lane :beta do lane :beta do
increment_version_number( increment_version_number(
version_number: "1.79.1" version_number: "1.81.1"
) )
increment_build_number( increment_build_number(
build_number: latest_testflight_build_number + 1, build_number: latest_testflight_build_number + 1,

View file

@ -117,6 +117,7 @@ class SharingPage extends HookConsumerWidget {
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 12.0, left: 12.0,
right: 12.0, right: 12.0,
top: 24.0,
bottom: 12.0, bottom: 12.0,
), ),
child: Row( child: Row(

View file

@ -13,9 +13,11 @@ final archiveProvider = StreamProvider<RenderList>((ref) async* {
final query = ref final query = ref
.watch(dbProvider) .watch(dbProvider)
.assets .assets
.where()
.ownerIdEqualToAnyChecksum(user.isarId)
.filter() .filter()
.ownerIdEqualTo(user.isarId)
.isArchivedEqualTo(true) .isArchivedEqualTo(true)
.isTrashedEqualTo(false)
.sortByFileCreatedAt(); .sortByFileCreatedAt();
final settings = ref.watch(appSettingsServiceProvider); final settings = ref.watch(appSettingsServiceProvider);
final groupBy = final groupBy =

View file

@ -6,6 +6,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/show_controls.provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/show_controls.provider.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/video_player_controls_provider.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/video_player_controls_provider.dart';
@ -19,11 +20,14 @@ import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart'
import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.dart'; import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.dart';
import 'package:immich_mobile/modules/home/ui/upload_dialog.dart'; import 'package:immich_mobile/modules/home/ui/upload_dialog.dart';
import 'package:immich_mobile/shared/cache/original_image_provider.dart'; import 'package:immich_mobile/shared/cache/original_image_provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/models/store.dart';
import 'package:immich_mobile/modules/home/ui/delete_dialog.dart'; import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:immich_mobile/shared/ui/immich_image.dart'; import 'package:immich_mobile/shared/ui/immich_image.dart';
import 'package:immich_mobile/shared/ui/immich_toast.dart';
import 'package:immich_mobile/shared/ui/photo_view/photo_view_gallery.dart'; import 'package:immich_mobile/shared/ui/photo_view/photo_view_gallery.dart';
import 'package:immich_mobile/shared/ui/photo_view/src/photo_view_computed_scale.dart'; import 'package:immich_mobile/shared/ui/photo_view/src/photo_view_computed_scale.dart';
import 'package:immich_mobile/shared/ui/photo_view/src/photo_view_scale_state.dart'; import 'package:immich_mobile/shared/ui/photo_view/src/photo_view_scale_state.dart';
@ -67,6 +71,12 @@ class GalleryViewerPage extends HookConsumerWidget {
final header = {"Authorization": authToken}; final header = {"Authorization": authToken};
final currentIndex = useState(initialIndex); final currentIndex = useState(initialIndex);
final currentAsset = loadAsset(currentIndex.value); final currentAsset = loadAsset(currentIndex.value);
final isTrashEnabled =
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
final navStack = AutoRouter.of(context).stackData;
final isFromTrash = isTrashEnabled &&
navStack.length > 2 &&
navStack.elementAt(navStack.length - 2).name == TrashRoute.name;
Asset asset() => currentAsset; Asset asset() => currentAsset;
@ -161,25 +171,47 @@ class GalleryViewerPage extends HookConsumerWidget {
); );
} }
void handleDelete(Asset deleteAsset) { void handleDelete(Asset deleteAsset) async {
Future<bool> onDelete(bool force) async {
final isDeleted = await ref.read(assetProvider.notifier).deleteAssets(
{deleteAsset},
force: force,
);
if (isDeleted) {
if (totalAssets == 1) {
// Handle only one asset
AutoRouter.of(context).pop();
} else {
// Go to next page otherwise
controller.nextPage(
duration: const Duration(milliseconds: 100),
curve: Curves.fastLinearToSlowEaseIn,
);
}
}
return isDeleted;
}
// Asset is trashed
if (isTrashEnabled && !isFromTrash) {
final isDeleted = await onDelete(false);
// Can only trash assets stored in server. Local assets are always permanently removed for now
if (context.mounted && isDeleted && deleteAsset.isRemote) {
ImmichToast.show(
durationInSecond: 1,
context: context,
msg: 'Asset trashed',
gravity: ToastGravity.BOTTOM,
);
}
return;
}
// Asset is permanently removed
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext _) { builder: (BuildContext _) {
return DeleteDialog( return DeleteDialog(onDelete: () => onDelete(true));
onDelete: () {
if (totalAssets == 1) {
// Handle only one asset
AutoRouter.of(context).pop();
} else {
// Go to next page otherwise
controller.nextPage(
duration: const Duration(milliseconds: 100),
curve: Curves.fastLinearToSlowEaseIn,
);
}
ref.watch(assetProvider.notifier).deleteAssets({deleteAsset});
},
);
}, },
); );
} }

View file

@ -81,7 +81,9 @@ class BackupControllerPage extends HookConsumerWidget {
context: context, context: context,
msg: "Deleting ${assets.length} assets on the server...", msg: "Deleting ${assets.length} assets on the server...",
); );
await ref.read(assetProvider.notifier).deleteAssets(assets); await ref
.read(assetProvider.notifier)
.deleteAssets(assets, force: true);
ImmichToast.show( ImmichToast.show(
context: context, context: context,
msg: "Deleted ${assets.length} assets on the server. " msg: "Deleted ${assets.length} assets on the server. "

View file

@ -13,9 +13,11 @@ final favoriteAssetsProvider = StreamProvider<RenderList>((ref) async* {
final query = ref final query = ref
.watch(dbProvider) .watch(dbProvider)
.assets .assets
.where()
.ownerIdEqualToAnyChecksum(user.isarId)
.filter() .filter()
.ownerIdEqualTo(user.isarId)
.isFavoriteEqualTo(true) .isFavoriteEqualTo(true)
.isTrashedEqualTo(false)
.sortByFileCreatedAt(); .sortByFileCreatedAt();
final settings = ref.watch(appSettingsServiceProvider); final settings = ref.watch(appSettingsServiceProvider);
final groupBy = final groupBy =

View file

@ -142,7 +142,7 @@ class RenderList {
) async { ) async {
final List<RenderAssetGridElement> elements = []; final List<RenderAssetGridElement> elements = [];
const pageSize = 500; const pageSize = 50000;
const sectionSize = 60; // divides evenly by 2,3,4,5,6 const sectionSize = 60; // divides evenly by 2,3,4,5,6
if (groupBy == GroupAssetsBy.none) { if (groupBy == GroupAssetsBy.none) {

View file

@ -7,6 +7,7 @@ import 'package:immich_mobile/modules/album/ui/add_to_album_sliverlist.dart';
import 'package:immich_mobile/modules/home/ui/delete_dialog.dart'; import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
import 'package:immich_mobile/modules/home/ui/upload_dialog.dart'; import 'package:immich_mobile/modules/home/ui/upload_dialog.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:immich_mobile/shared/ui/drag_sheet.dart'; import 'package:immich_mobile/shared/ui/drag_sheet.dart';
import 'package:immich_mobile/shared/models/album.dart'; import 'package:immich_mobile/shared/models/album.dart';
@ -43,6 +44,8 @@ class ControlBottomAppBar extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
var isDarkMode = Theme.of(context).brightness == Brightness.dark; var isDarkMode = Theme.of(context).brightness == Brightness.dark;
var hasRemote = selectionAssetState == AssetState.remote; var hasRemote = selectionAssetState == AssetState.remote;
final trashEnabled =
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
Widget renderActionButtons() { Widget renderActionButtons() {
return Row( return Row(
@ -70,14 +73,20 @@ class ControlBottomAppBar extends ConsumerWidget {
iconData: Icons.delete_outline_rounded, iconData: Icons.delete_outline_rounded,
label: "control_bottom_app_bar_delete".tr(), label: "control_bottom_app_bar_delete".tr(),
onPressed: enabled onPressed: enabled
? () => showDialog( ? () {
context: context, if (!trashEnabled) {
builder: (BuildContext context) { showDialog(
return DeleteDialog( context: context,
onDelete: onDelete, builder: (BuildContext context) {
); return DeleteDialog(
}, onDelete: onDelete,
) );
},
);
} else {
onDelete();
}
}
: null, : null,
), ),
if (!hasRemote) if (!hasRemote)

View file

@ -9,6 +9,7 @@ import 'package:immich_mobile/modules/home/ui/profile_drawer/server_info_box.dar
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/providers/asset.provider.dart'; import 'package:immich_mobile/shared/providers/asset.provider.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:immich_mobile/shared/providers/websocket.provider.dart'; import 'package:immich_mobile/shared/providers/websocket.provider.dart';
class ProfileDrawer extends HookConsumerWidget { class ProfileDrawer extends HookConsumerWidget {
@ -16,6 +17,9 @@ class ProfileDrawer extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final trashEnabled =
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
buildSignOutButton() { buildSignOutButton() {
return ListTile( return ListTile(
leading: SizedBox( leading: SizedBox(
@ -91,6 +95,29 @@ class ProfileDrawer extends HookConsumerWidget {
); );
} }
buildTrashButton() {
return ListTile(
leading: SizedBox(
height: double.infinity,
child: Icon(
Icons.delete_rounded,
color: Theme.of(context).textTheme.labelMedium?.color,
size: 20,
),
),
title: Text(
"Trash",
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(fontWeight: FontWeight.bold),
).tr(),
onTap: () {
AutoRouter.of(context).push(const TrashRoute());
},
);
}
return Drawer( return Drawer(
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero, borderRadius: BorderRadius.zero,
@ -105,6 +132,7 @@ class ProfileDrawer extends HookConsumerWidget {
const ProfileDrawerHeader(), const ProfileDrawerHeader(),
buildSettingButton(), buildSettingButton(),
buildAppLogButton(), buildAppLogButton(),
if (trashEnabled) buildTrashButton(),
buildSignOutButton(), buildSignOutButton(),
], ],
), ),

View file

@ -43,6 +43,8 @@ class HomePage extends HookConsumerWidget {
final sharedAlbums = ref.watch(sharedAlbumProvider); final sharedAlbums = ref.watch(sharedAlbumProvider);
final albumService = ref.watch(albumServiceProvider); final albumService = ref.watch(albumServiceProvider);
final currentUser = ref.watch(currentUserProvider); final currentUser = ref.watch(currentUserProvider);
final trashEnabled =
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
final tipOneOpacity = useState(0.0); final tipOneOpacity = useState(0.0);
final refreshCount = useState(0); final refreshCount = useState(0);
@ -54,7 +56,7 @@ class HomePage extends HookConsumerWidget {
Future(() => ref.read(assetProvider.notifier).getAllAsset()); Future(() => ref.read(assetProvider.notifier).getAllAsset());
ref.read(albumProvider.notifier).getAllAlbums(); ref.read(albumProvider.notifier).getAllAlbums();
ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums(); ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums();
ref.read(serverInfoProvider.notifier).getServerVersion(); ref.read(serverInfoProvider.notifier).getServerInfo();
selectionEnabledHook.addListener(() { selectionEnabledHook.addListener(() {
multiselectEnabled.state = selectionEnabledHook.value; multiselectEnabled.state = selectionEnabledHook.value;
@ -139,30 +141,34 @@ class HomePage extends HookConsumerWidget {
void onDelete() async { void onDelete() async {
processing.value = true; processing.value = true;
try { try {
await ref.read(assetProvider.notifier).deleteAssets(selection.value); await ref
.read(assetProvider.notifier)
.deleteAssets(selection.value, force: !trashEnabled);
final hasRemote = selection.value.any((a) => a.isRemote);
final assetOrAssets = selection.value.length > 1 ? 'assets' : 'asset';
final trashOrRemoved =
!trashEnabled ? 'deleted permanently' : 'trashed';
if (hasRemote) {
ImmichToast.show(
context: context,
msg: '${selection.value.length} $assetOrAssets $trashOrRemoved',
gravity: ToastGravity.BOTTOM,
);
}
selectionEnabledHook.value = false; selectionEnabledHook.value = false;
} finally { } finally {
processing.value = false; processing.value = false;
} }
} }
void onUpload() async { void onUpload() {
processing.value = true; processing.value = true;
selectionEnabledHook.value = false;
try { try {
final Set<Asset> assets = selection.value; ref
if (assets.length > 30) { .read(manualUploadProvider.notifier)
ImmichToast.show( .uploadAssets(context, selection.value);
context: context,
msg: 'home_page_upload_err_limit'.tr(),
gravity: ToastGravity.BOTTOM,
);
} else {
processing.value = false;
selectionEnabledHook.value = false;
await ref
.read(manualUploadProvider.notifier)
.uploadAssets(context, assets);
}
} finally { } finally {
processing.value = false; processing.value = false;
} }

View file

@ -1,29 +1,33 @@
class MapState { class MapState {
final bool isDarkTheme; final bool isDarkTheme;
final bool showFavoriteOnly; final bool showFavoriteOnly;
final bool includeArchived;
final int relativeTime; final int relativeTime;
MapState({ MapState({
this.isDarkTheme = false, this.isDarkTheme = false,
this.showFavoriteOnly = false, this.showFavoriteOnly = false,
this.includeArchived = false,
this.relativeTime = 0, this.relativeTime = 0,
}); });
MapState copyWith({ MapState copyWith({
bool? isDarkTheme, bool? isDarkTheme,
bool? showFavoriteOnly, bool? showFavoriteOnly,
bool? includeArchived,
int? relativeTime, int? relativeTime,
}) { }) {
return MapState( return MapState(
isDarkTheme: isDarkTheme ?? this.isDarkTheme, isDarkTheme: isDarkTheme ?? this.isDarkTheme,
showFavoriteOnly: showFavoriteOnly ?? this.showFavoriteOnly, showFavoriteOnly: showFavoriteOnly ?? this.showFavoriteOnly,
includeArchived: includeArchived ?? this.includeArchived,
relativeTime: relativeTime ?? this.relativeTime, relativeTime: relativeTime ?? this.relativeTime,
); );
} }
@override @override
String toString() { String toString() {
return 'MapSettingsState(isDarkTheme: $isDarkTheme, showFavoriteOnly: $showFavoriteOnly, relativeTime: $relativeTime)'; return 'MapSettingsState(isDarkTheme: $isDarkTheme, showFavoriteOnly: $showFavoriteOnly, relativeTime: $relativeTime, includeArchived: $includeArchived)';
} }
@override @override
@ -33,13 +37,15 @@ class MapState {
return other is MapState && return other is MapState &&
other.isDarkTheme == isDarkTheme && other.isDarkTheme == isDarkTheme &&
other.showFavoriteOnly == showFavoriteOnly && other.showFavoriteOnly == showFavoriteOnly &&
other.relativeTime == relativeTime; other.relativeTime == relativeTime &&
other.includeArchived == includeArchived;
} }
@override @override
int get hashCode { int get hashCode {
return isDarkTheme.hashCode ^ return isDarkTheme.hashCode ^
showFavoriteOnly.hashCode ^ showFavoriteOnly.hashCode ^
relativeTime.hashCode; relativeTime.hashCode ^
includeArchived.hashCode;
} }
} }

View file

@ -10,6 +10,7 @@ final mapMarkersProvider =
final mapState = ref.read(mapStateNotifier); final mapState = ref.read(mapStateNotifier);
DateTime? fileCreatedAfter; DateTime? fileCreatedAfter;
bool? isFavorite; bool? isFavorite;
bool? isIncludeArchived;
if (mapState.relativeTime != 0) { if (mapState.relativeTime != 0) {
fileCreatedAfter = fileCreatedAfter =
@ -20,8 +21,13 @@ final mapMarkersProvider =
isFavorite = true; isFavorite = true;
} }
if (!mapState.includeArchived) {
isIncludeArchived = false;
}
final markers = await service.getMapMarkers( final markers = await service.getMapMarkers(
isFavorite: isFavorite, isFavorite: isFavorite,
withArchived: isIncludeArchived,
fileCreatedAfter: fileCreatedAfter, fileCreatedAfter: fileCreatedAfter,
); );

View file

@ -11,6 +11,8 @@ class MapStateNotifier extends StateNotifier<MapState> {
.getSetting<bool>(AppSettingsEnum.mapThemeMode), .getSetting<bool>(AppSettingsEnum.mapThemeMode),
showFavoriteOnly: appSettingsProvider showFavoriteOnly: appSettingsProvider
.getSetting<bool>(AppSettingsEnum.mapShowFavoriteOnly), .getSetting<bool>(AppSettingsEnum.mapShowFavoriteOnly),
includeArchived: appSettingsProvider
.getSetting<bool>(AppSettingsEnum.mapIncludeArchived),
relativeTime: appSettingsProvider relativeTime: appSettingsProvider
.getSetting<int>(AppSettingsEnum.mapRelativeDate), .getSetting<int>(AppSettingsEnum.mapRelativeDate),
), ),
@ -31,11 +33,19 @@ class MapStateNotifier extends StateNotifier<MapState> {
void switchFavoriteOnly(bool isFavoriteOnly) { void switchFavoriteOnly(bool isFavoriteOnly) {
appSettingsProvider.setSetting( appSettingsProvider.setSetting(
AppSettingsEnum.mapShowFavoriteOnly, AppSettingsEnum.mapShowFavoriteOnly,
appSettingsProvider, isFavoriteOnly,
); );
state = state.copyWith(showFavoriteOnly: isFavoriteOnly); state = state.copyWith(showFavoriteOnly: isFavoriteOnly);
} }
void switchIncludeArchived(bool isIncludeArchived) {
appSettingsProvider.setSetting(
AppSettingsEnum.mapIncludeArchived,
isIncludeArchived,
);
state = state.copyWith(includeArchived: isIncludeArchived);
}
void setRelativeTime(int relativeTime) { void setRelativeTime(int relativeTime) {
appSettingsProvider.setSetting( appSettingsProvider.setSetting(
AppSettingsEnum.mapRelativeDate, AppSettingsEnum.mapRelativeDate,

View file

@ -23,12 +23,14 @@ class MapSerivce {
Future<List<MapMarkerResponseDto>> getMapMarkers({ Future<List<MapMarkerResponseDto>> getMapMarkers({
bool? isFavorite, bool? isFavorite,
bool? withArchived,
DateTime? fileCreatedAfter, DateTime? fileCreatedAfter,
DateTime? fileCreatedBefore, DateTime? fileCreatedBefore,
}) async { }) async {
try { try {
final markers = await _apiService.assetApi.getMapMarkers( final markers = await _apiService.assetApi.getMapMarkers(
isFavorite: isFavorite, isFavorite: isFavorite,
isArchived: withArchived,
fileCreatedAfter: fileCreatedAfter, fileCreatedAfter: fileCreatedAfter,
fileCreatedBefore: fileCreatedBefore, fileCreatedBefore: fileCreatedBefore,
); );

View file

@ -13,6 +13,7 @@ class MapSettingsDialog extends HookConsumerWidget {
final mapSettings = ref.read(mapStateNotifier); final mapSettings = ref.read(mapStateNotifier);
final isDarkMode = useState(mapSettings.isDarkTheme); final isDarkMode = useState(mapSettings.isDarkTheme);
final showFavoriteOnly = useState(mapSettings.showFavoriteOnly); final showFavoriteOnly = useState(mapSettings.showFavoriteOnly);
final showIncludeArchived = useState(mapSettings.includeArchived);
final showRelativeDate = useState(mapSettings.relativeTime); final showRelativeDate = useState(mapSettings.relativeTime);
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
@ -48,6 +49,22 @@ class MapSettingsDialog extends HookConsumerWidget {
); );
} }
Widget buildIncludeArchivedSetting() {
return SwitchListTile.adaptive(
value: showIncludeArchived.value,
onChanged: (value) {
showIncludeArchived.value = value;
},
activeColor: theme.primaryColor,
dense: true,
title: Text(
"map_settings_include_show_archived".tr(),
style:
theme.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold),
),
);
}
Widget buildDateRangeSetting() { Widget buildDateRangeSetting() {
final now = DateTime.now(); final now = DateTime.now();
return DropdownMenu( return DropdownMenu(
@ -127,6 +144,8 @@ class MapSettingsDialog extends HookConsumerWidget {
mapSettingsNotifier.switchTheme(isDarkMode.value); mapSettingsNotifier.switchTheme(isDarkMode.value);
mapSettingsNotifier.switchFavoriteOnly(showFavoriteOnly.value); mapSettingsNotifier.switchFavoriteOnly(showFavoriteOnly.value);
mapSettingsNotifier.setRelativeTime(showRelativeDate.value); mapSettingsNotifier.setRelativeTime(showRelativeDate.value);
mapSettingsNotifier
.switchIncludeArchived(showIncludeArchived.value);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
style: TextButton.styleFrom( style: TextButton.styleFrom(
@ -166,6 +185,7 @@ class MapSettingsDialog extends HookConsumerWidget {
children: [ children: [
buildMapThemeSetting(), buildMapThemeSetting(),
buildFavoriteOnlySetting(), buildFavoriteOnlySetting(),
buildIncludeArchivedSetting(),
const SizedBox( const SizedBox(
height: 10, height: 10,
), ),

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/plugin_api.dart'; import 'package:flutter_map/plugin_api.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:immich_mobile/utils/color_filter_generator.dart'; import 'package:immich_mobile/utils/color_filter_generator.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -29,8 +30,9 @@ class MapThumbnail extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final tileLayer = TileLayer( final tileLayer = TileLayer(
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", urlTemplate: ref.watch(
subdomains: const ['a', 'b', 'c'], serverInfoProvider.select((v) => v.serverConfig.mapTileUrl),
),
); );
return SizedBox( return SizedBox(

View file

@ -20,6 +20,7 @@ import 'package:immich_mobile/modules/map/ui/map_page_bottom_sheet.dart';
import 'package:immich_mobile/modules/map/ui/map_page_app_bar.dart'; import 'package:immich_mobile/modules/map/ui/map_page_app_bar.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
import 'package:immich_mobile/shared/ui/immich_toast.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart';
import 'package:immich_mobile/utils/color_filter_generator.dart'; import 'package:immich_mobile/utils/color_filter_generator.dart';
@ -154,7 +155,8 @@ class MapPageState extends ConsumerState<MapPage> {
ref.listen(mapStateNotifier, (previous, next) { ref.listen(mapStateNotifier, (previous, next) {
bool shouldRefetch = bool shouldRefetch =
previous?.showFavoriteOnly != next.showFavoriteOnly || previous?.showFavoriteOnly != next.showFavoriteOnly ||
previous?.relativeTime != next.relativeTime; previous?.relativeTime != next.relativeTime ||
previous?.includeArchived != next.includeArchived;
if (shouldRefetch) { if (shouldRefetch) {
refetchMarkers.value = shouldRefetch; refetchMarkers.value = shouldRefetch;
ref.invalidate(mapMarkersProvider); ref.invalidate(mapMarkersProvider);
@ -358,8 +360,9 @@ class MapPageState extends ConsumerState<MapPage> {
} }
final tileLayer = TileLayer( final tileLayer = TileLayer(
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", urlTemplate: ref.watch(
subdomains: const ['a', 'b', 'c'], serverInfoProvider.select((v) => v.serverConfig.mapTileUrl),
),
maxNativeZoom: 19, maxNativeZoom: 19,
maxZoom: 19, maxZoom: 19,
); );

View file

@ -22,9 +22,9 @@ class MemoryService {
Future<List<Memory>?> getMemoryLane() async { Future<List<Memory>?> getMemoryLane() async {
try { try {
final now = DateTime.now(); final now = DateTime.now();
final beginningOfDate = DateTime(now.year, now.month, now.day);
final data = await _apiService.assetApi.getMemoryLane( final data = await _apiService.assetApi.getMemoryLane(
beginningOfDate, now.day,
now.month,
); );
if (data == null) { if (data == null) {

View file

@ -8,15 +8,21 @@ import 'package:immich_mobile/shared/models/store.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
class CuratedPlacesRow extends CuratedRow { class CuratedPlacesRow extends CuratedRow {
final bool isMapEnabled;
const CuratedPlacesRow({ const CuratedPlacesRow({
super.key, super.key,
required super.content, required super.content,
this.isMapEnabled = true,
super.imageSize, super.imageSize,
super.onTap, super.onTap,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Calculating the actual index of the content based on the whether map is enabled or not.
// If enabled, inject map as the first item in the list (index 0) and so the actual content will start from index 1
final int actualContentIndex = isMapEnabled ? 1 : 0;
Widget buildMapThumbnail() { Widget buildMapThumbnail() {
return GestureDetector( return GestureDetector(
onTap: () => AutoRouter.of(context).push( onTap: () => AutoRouter.of(context).push(
@ -75,6 +81,24 @@ class CuratedPlacesRow extends CuratedRow {
); );
} }
// Return empty thumbnail
if (!isMapEnabled && content.isEmpty) {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: SizedBox(
width: imageSize,
height: imageSize,
child: ThumbnailWithInfo(
textInfo: '',
onTap: () {},
),
),
),
);
}
return ListView.builder( return ListView.builder(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -82,11 +106,10 @@ class CuratedPlacesRow extends CuratedRow {
), ),
itemBuilder: (context, index) { itemBuilder: (context, index) {
// Injecting Map thumbnail as the first element // Injecting Map thumbnail as the first element
if (index == 0) { if (isMapEnabled && index == 0) {
return buildMapThumbnail(); return buildMapThumbnail();
} }
// The actual index is 1 less than the virutal index since we inject map into the first position final actualIndex = index - actualContentIndex;
final actualIndex = index - 1;
final object = content[actualIndex]; final object = content[actualIndex];
final thumbnailRequestUrl = final thumbnailRequestUrl =
'${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/${object.id}'; '${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/${object.id}';
@ -103,8 +126,7 @@ class CuratedPlacesRow extends CuratedRow {
), ),
); );
}, },
// Adding 1 to inject map thumbnail as first element itemCount: content.length + actualContentIndex,
itemCount: content.length + 1,
); );
} }
} }

View file

@ -14,6 +14,7 @@ import 'package:immich_mobile/modules/search/ui/person_name_edit_form.dart';
import 'package:immich_mobile/modules/search/ui/search_row_title.dart'; import 'package:immich_mobile/modules/search/ui/search_row_title.dart';
import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart'; import 'package:immich_mobile/modules/search/ui/search_suggestion_list.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
// ignore: must_be_immutable // ignore: must_be_immutable
@ -27,6 +28,8 @@ class SearchPage extends HookConsumerWidget {
final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled; final isSearchEnabled = ref.watch(searchPageStateProvider).isSearchEnabled;
final curatedLocation = ref.watch(getCuratedLocationProvider); final curatedLocation = ref.watch(getCuratedLocationProvider);
final curatedPeople = ref.watch(getCuratedPeopleProvider); final curatedPeople = ref.watch(getCuratedPeopleProvider);
final isMapEnabled =
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.map));
var isDarkTheme = Theme.of(context).brightness == Brightness.dark; var isDarkTheme = Theme.of(context).brightness == Brightness.dark;
double imageSize = math.min(MediaQuery.of(context).size.width / 3, 150); double imageSize = math.min(MediaQuery.of(context).size.width / 3, 150);
@ -107,6 +110,7 @@ class SearchPage extends HookConsumerWidget {
loading: () => const Center(child: ImmichLoadingIndicator()), loading: () => const Center(child: ImmichLoadingIndicator()),
error: (err, stack) => Center(child: Text('Error: $err')), error: (err, stack) => Center(child: Text('Error: $err')),
data: (locations) => CuratedPlacesRow( data: (locations) => CuratedPlacesRow(
isMapEnabled: isMapEnabled,
content: locations content: locations
.map( .map(
(o) => CuratedContent( (o) => CuratedContent(

View file

@ -48,6 +48,7 @@ enum AppSettingsEnum<T> {
preferRemoteImage<bool>(StoreKey.preferRemoteImage, null, false), preferRemoteImage<bool>(StoreKey.preferRemoteImage, null, false),
mapThemeMode<bool>(StoreKey.mapThemeMode, null, false), mapThemeMode<bool>(StoreKey.mapThemeMode, null, false),
mapShowFavoriteOnly<bool>(StoreKey.mapShowFavoriteOnly, null, false), mapShowFavoriteOnly<bool>(StoreKey.mapShowFavoriteOnly, null, false),
mapIncludeArchived<bool>(StoreKey.mapIncludeArchived, null, false),
mapRelativeDate<int>(StoreKey.mapRelativeDate, null, 0), mapRelativeDate<int>(StoreKey.mapRelativeDate, null, 0),
allowSelfSignedSSLCert<bool>(StoreKey.selfSignedCert, null, false), allowSelfSignedSSLCert<bool>(StoreKey.selfSignedCert, null, false),
; ;

View file

@ -0,0 +1,144 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart';
import 'package:immich_mobile/modules/trash/services/trash.service.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/models/exif_info.dart';
import 'package:immich_mobile/shared/providers/asset.provider.dart';
import 'package:immich_mobile/shared/providers/db.provider.dart';
import 'package:immich_mobile/shared/providers/user.provider.dart';
import 'package:isar/isar.dart';
import 'package:logging/logging.dart';
class TrashNotifier extends StateNotifier<bool> {
final Isar _db;
final Ref _ref;
final TrashService _trashService;
final _log = Logger('TrashNotifier');
TrashNotifier(
this._trashService,
this._db,
this._ref,
) : super(false);
Future<void> emptyTrash() async {
try {
final user = _ref.read(currentUserProvider);
if (user == null) {
return;
}
await _trashService.emptyTrash();
final dbIds = await _db.assets
.where()
.remoteIdIsNotNull()
.filter()
.ownerIdEqualTo(user.isarId)
.isTrashedEqualTo(true)
.idProperty()
.findAll();
await _db.writeTxn(() async {
await _db.exifInfos.deleteAll(dbIds);
await _db.assets.deleteAll(dbIds);
});
// Refresh assets in background
Future.delayed(
const Duration(seconds: 4),
() async => await _ref.read(assetProvider.notifier).getAllAsset(),
);
} catch (error, stack) {
_log.severe("Cannot empty trash ${error.toString()}", error, stack);
}
}
Future<bool> restoreAssets(Iterable<Asset> assetList) async {
try {
final result = await _trashService.restoreAssets(assetList);
if (result) {
final remoteAssets = assetList.where((a) => a.isRemote).toList();
final updatedAssets = remoteAssets.map((e) {
e.isTrashed = false;
return e;
}).toList();
await _db.writeTxn(() async {
await _db.assets.putAll(updatedAssets);
});
// Refresh assets in background
Future.delayed(
const Duration(seconds: 4),
() async => await _ref.read(assetProvider.notifier).getAllAsset(),
);
return true;
}
} catch (error, stack) {
_log.severe("Cannot restore trash ${error.toString()}", error, stack);
}
return false;
}
Future<void> restoreTrash() async {
try {
final user = _ref.read(currentUserProvider);
if (user == null) {
return;
}
await _trashService.restoreTrash();
final assets = await _db.assets
.where()
.remoteIdIsNotNull()
.filter()
.ownerIdEqualTo(user.isarId)
.isTrashedEqualTo(true)
.findAll();
final updatedAssets = assets.map((e) {
e.isTrashed = false;
return e;
}).toList();
await _db.writeTxn(() async {
await _db.assets.putAll(updatedAssets);
});
// Refresh assets in background
Future.delayed(
const Duration(seconds: 4),
() async => await _ref.read(assetProvider.notifier).getAllAsset(),
);
} catch (error, stack) {
_log.severe("Cannot restore trash ${error.toString()}", error, stack);
}
}
}
final trashProvider = StateNotifierProvider<TrashNotifier, bool>((ref) {
return TrashNotifier(
ref.watch(trashServiceProvider),
ref.watch(dbProvider),
ref,
);
});
final trashedAssetsProvider = StreamProvider<RenderList>((ref) async* {
final user = ref.read(currentUserProvider);
if (user == null) return;
final query = ref
.watch(dbProvider)
.assets
.filter()
.ownerIdEqualTo(user.isarId)
.isTrashedEqualTo(true)
.sortByFileCreatedAt();
const groupBy = GroupAssetsBy.none;
yield await RenderList.fromQuery(query, groupBy);
await for (final _ in query.watchLazy()) {
yield await RenderList.fromQuery(query, groupBy);
}
});

View file

@ -0,0 +1,48 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/api.provider.dart';
import 'package:immich_mobile/shared/services/api.service.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
final trashServiceProvider = Provider<TrashService>((ref) {
return TrashService(
ref.watch(apiServiceProvider),
);
});
class TrashService {
final _log = Logger("TrashService");
final ApiService _apiService;
TrashService(this._apiService);
Future<bool> restoreAssets(Iterable<Asset> assetList) async {
try {
List<String> remoteIds =
assetList.where((a) => a.isRemote).map((e) => e.remoteId!).toList();
await _apiService.assetApi.restoreAssets(BulkIdsDto(ids: remoteIds));
return true;
} catch (error, stack) {
_log.severe("Cannot restore assets ${error.toString()}", error, stack);
return false;
}
}
Future<void> emptyTrash() async {
try {
await _apiService.assetApi.emptyTrash();
} catch (error, stack) {
_log.severe("Cannot empty trash ${error.toString()}", error, stack);
}
}
Future<void> restoreTrash() async {
try {
await _apiService.assetApi.restoreTrash();
} catch (error, stack) {
_log.severe("Cannot restore trash ${error.toString()}", error, stack);
}
}
}

View file

@ -0,0 +1,276 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
import 'package:immich_mobile/modules/trash/providers/trashed_asset.provider.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/asset.provider.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:immich_mobile/shared/ui/confirm_dialog.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
import 'package:immich_mobile/shared/ui/immich_toast.dart';
class TrashPage extends HookConsumerWidget {
const TrashPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final trashedAssets = ref.watch(trashedAssetsProvider);
final trashDays =
ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays));
final selectionEnabledHook = useState(false);
final selection = useState(<Asset>{});
final processing = useState(false);
void selectionListener(
bool multiselect,
Set<Asset> selectedAssets,
) {
selectionEnabledHook.value = multiselect;
selection.value = selectedAssets;
}
onEmptyTrash() async {
processing.value = true;
await ref.read(trashProvider.notifier).emptyTrash();
processing.value = false;
selectionEnabledHook.value = false;
if (context.mounted) {
ImmichToast.show(
context: context,
msg: 'Emptied trash',
gravity: ToastGravity.BOTTOM,
);
}
}
handleEmptyTrash() async {
await showDialog(
context: context,
builder: (context) => ConfirmDialog(
onOk: () => onEmptyTrash(),
title: "trash_page_empty_trash_btn".tr(),
ok: "trash_page_empty_trash_dialog_ok".tr(),
content: "trash_page_empty_trash_dialog_content".tr(),
),
);
}
Future<void> onPermanentlyDelete() async {
processing.value = true;
try {
if (selection.value.isNotEmpty) {
await ref
.read(assetProvider.notifier)
.deleteAssets(selection.value, force: true);
final assetOrAssets = selection.value.length > 1 ? 'assets' : 'asset';
if (context.mounted) {
ImmichToast.show(
context: context,
msg:
'${selection.value.length} $assetOrAssets deleted permanently',
gravity: ToastGravity.BOTTOM,
);
}
}
} finally {
processing.value = false;
selectionEnabledHook.value = false;
}
}
handlePermanentDelete() async {
await showDialog(
context: context,
builder: (context) => DeleteDialog(
onDelete: () => onPermanentlyDelete(),
),
);
}
Future<void> handleRestoreAll() async {
processing.value = true;
await ref.read(trashProvider.notifier).restoreTrash();
processing.value = false;
selectionEnabledHook.value = false;
}
Future<void> handleRestore() async {
processing.value = true;
try {
if (selection.value.isNotEmpty) {
final result = await ref
.read(trashProvider.notifier)
.restoreAssets(selection.value);
final assetOrAssets = selection.value.length > 1 ? 'assets' : 'asset';
if (result && context.mounted) {
ImmichToast.show(
context: context,
msg:
'${selection.value.length} $assetOrAssets restored successfully',
gravity: ToastGravity.BOTTOM,
);
}
}
} finally {
processing.value = false;
selectionEnabledHook.value = false;
}
}
String getAppBarTitle(String count) {
if (selectionEnabledHook.value) {
return selection.value.isNotEmpty
? "${selection.value.length}"
: "trash_page_select_assets_btn".tr();
}
return 'trash_page_title'.tr(args: [count]);
}
AppBar buildAppBar(String count) {
return AppBar(
leading: IconButton(
onPressed: !selectionEnabledHook.value
? () => AutoRouter.of(context).pop()
: () {
selectionEnabledHook.value = false;
selection.value = {};
},
icon: !selectionEnabledHook.value
? const Icon(Icons.arrow_back_ios_rounded)
: const Icon(Icons.close_rounded),
),
centerTitle: !selectionEnabledHook.value,
automaticallyImplyLeading: false,
title: Text(getAppBarTitle(count)),
actions: <Widget>[
if (!selectionEnabledHook.value)
PopupMenuButton<void Function()>(
itemBuilder: (context) {
return [
PopupMenuItem(
value: () => selectionEnabledHook.value = true,
child: const Text('trash_page_select_btn').tr(),
),
PopupMenuItem(
value: handleEmptyTrash,
child: const Text('trash_page_empty_trash_btn').tr(),
),
];
},
onSelected: (fn) => fn(),
),
],
);
}
Widget buildBottomBar() {
return SafeArea(
child: Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: 64,
child: Container(
color: Theme.of(context).canvasColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextButton.icon(
icon: Icon(
Icons.delete_forever,
color: Colors.red[400],
),
label: Text(
selection.value.isEmpty
? 'trash_page_delete_all'.tr()
: 'trash_page_delete'.tr(),
style: TextStyle(
fontSize: 14,
color: Colors.red[400],
fontWeight: FontWeight.bold,
),
),
onPressed: processing.value
? null
: selection.value.isEmpty
? handleEmptyTrash
: handlePermanentDelete,
),
TextButton.icon(
icon: const Icon(
Icons.history_rounded,
),
label: Text(
selection.value.isEmpty
? 'trash_page_restore_all'.tr()
: 'trash_page_restore'.tr(),
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
onPressed: processing.value
? null
: selection.value.isEmpty
? handleRestoreAll
: handleRestore,
),
],
),
),
),
),
);
}
return trashedAssets.when(
loading: () => Scaffold(
appBar: buildAppBar("?"),
body: const Center(child: CircularProgressIndicator()),
),
error: (error, stackTrace) => Scaffold(
appBar: buildAppBar("!"),
body: Center(child: Text(error.toString())),
),
data: (data) => Scaffold(
appBar: buildAppBar(data.totalAssets.toString()),
body: data.isEmpty
? Center(
child: Text('trash_page_no_assets'.tr()),
)
: Stack(
children: [
SafeArea(
child: ImmichAssetGrid(
renderList: data,
listener: selectionListener,
selectionActive: selectionEnabledHook.value,
showMultiSelectIndicator: false,
topWidget: Padding(
padding: const EdgeInsets.only(
top: 24,
bottom: 24,
left: 12,
right: 12,
),
child: const Text(
"trash_page_info",
).tr(args: ["$trashDays"]),
),
),
),
if (selectionEnabledHook.value) buildBottomBar(),
if (processing.value)
const Center(child: ImmichLoadingIndicator()),
],
),
),
);
}
}

View file

@ -28,6 +28,7 @@ import 'package:immich_mobile/modules/login/views/change_password_page.dart';
import 'package:immich_mobile/modules/login/views/login_page.dart'; import 'package:immich_mobile/modules/login/views/login_page.dart';
import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart';
import 'package:immich_mobile/modules/onboarding/views/permission_onboarding_page.dart'; import 'package:immich_mobile/modules/onboarding/views/permission_onboarding_page.dart';
import 'package:immich_mobile/modules/trash/views/trash_page.dart';
import 'package:immich_mobile/modules/search/views/all_motion_videos_page.dart'; import 'package:immich_mobile/modules/search/views/all_motion_videos_page.dart';
import 'package:immich_mobile/modules/search/views/all_people_page.dart'; import 'package:immich_mobile/modules/search/views/all_people_page.dart';
import 'package:immich_mobile/modules/search/views/all_videos_page.dart'; import 'package:immich_mobile/modules/search/views/all_videos_page.dart';
@ -155,6 +156,7 @@ part 'router.gr.dart';
AutoRoute(page: MemoryPage, guards: [AuthGuard, DuplicateGuard]), AutoRoute(page: MemoryPage, guards: [AuthGuard, DuplicateGuard]),
AutoRoute(page: MapPage, guards: [AuthGuard, DuplicateGuard]), AutoRoute(page: MapPage, guards: [AuthGuard, DuplicateGuard]),
AutoRoute(page: AlbumOptionsPage, guards: [AuthGuard, DuplicateGuard]), AutoRoute(page: AlbumOptionsPage, guards: [AuthGuard, DuplicateGuard]),
AutoRoute(page: TrashPage, guards: [AuthGuard, DuplicateGuard]),
], ],
) )
class AppRouter extends _$AppRouter { class AppRouter extends _$AppRouter {

View file

@ -312,6 +312,12 @@ class _$AppRouter extends RootStackRouter {
), ),
); );
}, },
TrashRoute.name: (routeData) {
return MaterialPageX<dynamic>(
routeData: routeData,
child: const TrashPage(),
);
},
HomeRoute.name: (routeData) { HomeRoute.name: (routeData) {
return MaterialPageX<dynamic>( return MaterialPageX<dynamic>(
routeData: routeData, routeData: routeData,
@ -624,6 +630,14 @@ class _$AppRouter extends RootStackRouter {
duplicateGuard, duplicateGuard,
], ],
), ),
RouteConfig(
TrashRoute.name,
path: '/trash-page',
guards: [
authGuard,
duplicateGuard,
],
),
]; ];
} }
@ -1394,6 +1408,18 @@ class AlbumOptionsRouteArgs {
} }
} }
/// generated route for
/// [TrashPage]
class TrashRoute extends PageRouteInfo<void> {
const TrashRoute()
: super(
TrashRoute.name,
path: '/trash-page',
);
static const String name = 'TrashRoute';
}
/// generated route for /// generated route for
/// [HomePage] /// [HomePage]
class HomeRoute extends PageRouteInfo<void> { class HomeRoute extends PageRouteInfo<void> {

View file

@ -64,10 +64,10 @@ class TabNavigationObserver extends AutoRouterObserver {
} }
Store.put(StoreKey.currentUser, User.fromDto(userResponseDto)); Store.put(StoreKey.currentUser, User.fromDto(userResponseDto));
ref.read(serverInfoProvider.notifier).getServerVersion();
} catch (e) { } catch (e) {
debugPrint("Error refreshing user info $e"); debugPrint("Error refreshing user info $e");
} }
} }
ref.watch(serverInfoProvider.notifier).getServerVersion();
} }
} }

View file

@ -30,7 +30,8 @@ class Asset {
exifInfo = exifInfo =
remote.exifInfo != null ? ExifInfo.fromDto(remote.exifInfo!) : null, remote.exifInfo != null ? ExifInfo.fromDto(remote.exifInfo!) : null,
isFavorite = remote.isFavorite, isFavorite = remote.isFavorite,
isArchived = remote.isArchived; isArchived = remote.isArchived,
isTrashed = remote.isTrashed;
Asset.local(AssetEntity local, List<int> hash) Asset.local(AssetEntity local, List<int> hash)
: localId = local.id, : localId = local.id,
@ -45,6 +46,7 @@ class Asset {
updatedAt = local.modifiedDateTime, updatedAt = local.modifiedDateTime,
isFavorite = local.isFavorite, isFavorite = local.isFavorite,
isArchived = false, isArchived = false,
isTrashed = false,
fileCreatedAt = local.createDateTime { fileCreatedAt = local.createDateTime {
if (fileCreatedAt.year == 1970) { if (fileCreatedAt.year == 1970) {
fileCreatedAt = fileModifiedAt; fileCreatedAt = fileModifiedAt;
@ -74,6 +76,7 @@ class Asset {
this.exifInfo, this.exifInfo,
required this.isFavorite, required this.isFavorite,
required this.isArchived, required this.isArchived,
required this.isTrashed,
}); });
@ignore @ignore
@ -100,12 +103,6 @@ class Asset {
/// stores the raw SHA1 bytes as a base64 String /// stores the raw SHA1 bytes as a base64 String
/// because Isar cannot sort lists of byte arrays /// because Isar cannot sort lists of byte arrays
@Index(
unique: true,
replace: false,
type: IndexType.hash,
composite: [CompositeIndex("ownerId")],
)
String checksum; String checksum;
@Index(unique: false, replace: false, type: IndexType.hash) @Index(unique: false, replace: false, type: IndexType.hash)
@ -114,6 +111,11 @@ class Asset {
@Index(unique: false, replace: false, type: IndexType.hash) @Index(unique: false, replace: false, type: IndexType.hash)
String? localId; String? localId;
@Index(
unique: true,
replace: false,
composite: [CompositeIndex("checksum", type: IndexType.hash)],
)
int ownerId; int ownerId;
DateTime fileCreatedAt; DateTime fileCreatedAt;
@ -139,6 +141,8 @@ class Asset {
bool isArchived; bool isArchived;
bool isTrashed;
@ignore @ignore
ExifInfo? exifInfo; ExifInfo? exifInfo;
@ -195,7 +199,8 @@ class Asset {
livePhotoVideoId == other.livePhotoVideoId && livePhotoVideoId == other.livePhotoVideoId &&
isFavorite == other.isFavorite && isFavorite == other.isFavorite &&
isLocal == other.isLocal && isLocal == other.isLocal &&
isArchived == other.isArchived; isArchived == other.isArchived &&
isTrashed == other.isTrashed;
} }
@override @override
@ -217,7 +222,8 @@ class Asset {
livePhotoVideoId.hashCode ^ livePhotoVideoId.hashCode ^
isFavorite.hashCode ^ isFavorite.hashCode ^
isLocal.hashCode ^ isLocal.hashCode ^
isArchived.hashCode; isArchived.hashCode ^
isTrashed.hashCode;
/// Returns `true` if this [Asset] can updated with values from parameter [a] /// Returns `true` if this [Asset] can updated with values from parameter [a]
bool canUpdate(Asset a) { bool canUpdate(Asset a) {
@ -230,8 +236,9 @@ class Asset {
width == null && a.width != null || width == null && a.width != null ||
height == null && a.height != null || height == null && a.height != null ||
livePhotoVideoId == null && a.livePhotoVideoId != null || livePhotoVideoId == null && a.livePhotoVideoId != null ||
!isRemote && a.isRemote && isFavorite != a.isFavorite || isFavorite != a.isFavorite ||
!isRemote && a.isRemote && isArchived != a.isArchived; isArchived != a.isArchived ||
isTrashed != a.isTrashed;
} }
/// Returns a new [Asset] with values from this and merged & updated with [a] /// Returns a new [Asset] with values from this and merged & updated with [a]
@ -262,6 +269,7 @@ class Asset {
livePhotoVideoId: livePhotoVideoId, livePhotoVideoId: livePhotoVideoId,
isFavorite: isFavorite, isFavorite: isFavorite,
isArchived: isArchived, isArchived: isArchived,
isTrashed: isTrashed,
); );
} }
} else { } else {
@ -276,6 +284,7 @@ class Asset {
// isFavorite + isArchived are not set by device-only assets // isFavorite + isArchived are not set by device-only assets
isFavorite: a.isFavorite, isFavorite: a.isFavorite,
isArchived: a.isArchived, isArchived: a.isArchived,
isTrashed: a.isTrashed,
exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo, exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo,
); );
} else { } else {
@ -307,6 +316,7 @@ class Asset {
String? livePhotoVideoId, String? livePhotoVideoId,
bool? isFavorite, bool? isFavorite,
bool? isArchived, bool? isArchived,
bool? isTrashed,
ExifInfo? exifInfo, ExifInfo? exifInfo,
}) => }) =>
Asset( Asset(
@ -326,6 +336,7 @@ class Asset {
livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId,
isFavorite: isFavorite ?? this.isFavorite, isFavorite: isFavorite ?? this.isFavorite,
isArchived: isArchived ?? this.isArchived, isArchived: isArchived ?? this.isArchived,
isTrashed: isTrashed ?? this.isTrashed,
exifInfo: exifInfo ?? this.exifInfo, exifInfo: exifInfo ?? this.exifInfo,
); );
@ -379,7 +390,8 @@ class Asset {
"storage": "$storage", "storage": "$storage",
"width": ${width ?? "N/A"}, "width": ${width ?? "N/A"},
"height": ${height ?? "N/A"}, "height": ${height ?? "N/A"},
"isArchived": $isArchived "isArchived": $isArchived,
"isTrashed": $isTrashed,
}"""; }""";
} }
} }

View file

@ -57,39 +57,44 @@ const AssetSchema = CollectionSchema(
name: r'isFavorite', name: r'isFavorite',
type: IsarType.bool, type: IsarType.bool,
), ),
r'livePhotoVideoId': PropertySchema( r'isTrashed': PropertySchema(
id: 8, id: 8,
name: r'isTrashed',
type: IsarType.bool,
),
r'livePhotoVideoId': PropertySchema(
id: 9,
name: r'livePhotoVideoId', name: r'livePhotoVideoId',
type: IsarType.string, type: IsarType.string,
), ),
r'localId': PropertySchema( r'localId': PropertySchema(
id: 9, id: 10,
name: r'localId', name: r'localId',
type: IsarType.string, type: IsarType.string,
), ),
r'ownerId': PropertySchema( r'ownerId': PropertySchema(
id: 10, id: 11,
name: r'ownerId', name: r'ownerId',
type: IsarType.long, type: IsarType.long,
), ),
r'remoteId': PropertySchema( r'remoteId': PropertySchema(
id: 11, id: 12,
name: r'remoteId', name: r'remoteId',
type: IsarType.string, type: IsarType.string,
), ),
r'type': PropertySchema( r'type': PropertySchema(
id: 12, id: 13,
name: r'type', name: r'type',
type: IsarType.byte, type: IsarType.byte,
enumMap: _AssettypeEnumValueMap, enumMap: _AssettypeEnumValueMap,
), ),
r'updatedAt': PropertySchema( r'updatedAt': PropertySchema(
id: 13, id: 14,
name: r'updatedAt', name: r'updatedAt',
type: IsarType.dateTime, type: IsarType.dateTime,
), ),
r'width': PropertySchema( r'width': PropertySchema(
id: 14, id: 15,
name: r'width', name: r'width',
type: IsarType.int, type: IsarType.int,
) )
@ -100,24 +105,6 @@ const AssetSchema = CollectionSchema(
deserializeProp: _assetDeserializeProp, deserializeProp: _assetDeserializeProp,
idName: r'id', idName: r'id',
indexes: { indexes: {
r'checksum_ownerId': IndexSchema(
id: 5611361749756160119,
name: r'checksum_ownerId',
unique: true,
replace: false,
properties: [
IndexPropertySchema(
name: r'checksum',
type: IndexType.hash,
caseSensitive: true,
),
IndexPropertySchema(
name: r'ownerId',
type: IndexType.value,
caseSensitive: false,
)
],
),
r'remoteId': IndexSchema( r'remoteId': IndexSchema(
id: 6301175856541681032, id: 6301175856541681032,
name: r'remoteId', name: r'remoteId',
@ -143,6 +130,24 @@ const AssetSchema = CollectionSchema(
caseSensitive: true, caseSensitive: true,
) )
], ],
),
r'ownerId_checksum': IndexSchema(
id: -3295822444433175883,
name: r'ownerId_checksum',
unique: true,
replace: false,
properties: [
IndexPropertySchema(
name: r'ownerId',
type: IndexType.value,
caseSensitive: false,
),
IndexPropertySchema(
name: r'checksum',
type: IndexType.hash,
caseSensitive: true,
)
],
) )
}, },
links: {}, links: {},
@ -196,13 +201,14 @@ void _assetSerialize(
writer.writeInt(offsets[5], object.height); writer.writeInt(offsets[5], object.height);
writer.writeBool(offsets[6], object.isArchived); writer.writeBool(offsets[6], object.isArchived);
writer.writeBool(offsets[7], object.isFavorite); writer.writeBool(offsets[7], object.isFavorite);
writer.writeString(offsets[8], object.livePhotoVideoId); writer.writeBool(offsets[8], object.isTrashed);
writer.writeString(offsets[9], object.localId); writer.writeString(offsets[9], object.livePhotoVideoId);
writer.writeLong(offsets[10], object.ownerId); writer.writeString(offsets[10], object.localId);
writer.writeString(offsets[11], object.remoteId); writer.writeLong(offsets[11], object.ownerId);
writer.writeByte(offsets[12], object.type.index); writer.writeString(offsets[12], object.remoteId);
writer.writeDateTime(offsets[13], object.updatedAt); writer.writeByte(offsets[13], object.type.index);
writer.writeInt(offsets[14], object.width); writer.writeDateTime(offsets[14], object.updatedAt);
writer.writeInt(offsets[15], object.width);
} }
Asset _assetDeserialize( Asset _assetDeserialize(
@ -221,14 +227,15 @@ Asset _assetDeserialize(
id: id, id: id,
isArchived: reader.readBool(offsets[6]), isArchived: reader.readBool(offsets[6]),
isFavorite: reader.readBool(offsets[7]), isFavorite: reader.readBool(offsets[7]),
livePhotoVideoId: reader.readStringOrNull(offsets[8]), isTrashed: reader.readBool(offsets[8]),
localId: reader.readStringOrNull(offsets[9]), livePhotoVideoId: reader.readStringOrNull(offsets[9]),
ownerId: reader.readLong(offsets[10]), localId: reader.readStringOrNull(offsets[10]),
remoteId: reader.readStringOrNull(offsets[11]), ownerId: reader.readLong(offsets[11]),
type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[12])] ?? remoteId: reader.readStringOrNull(offsets[12]),
type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[13])] ??
AssetType.other, AssetType.other,
updatedAt: reader.readDateTime(offsets[13]), updatedAt: reader.readDateTime(offsets[14]),
width: reader.readIntOrNull(offsets[14]), width: reader.readIntOrNull(offsets[15]),
); );
return object; return object;
} }
@ -257,19 +264,21 @@ P _assetDeserializeProp<P>(
case 7: case 7:
return (reader.readBool(offset)) as P; return (reader.readBool(offset)) as P;
case 8: case 8:
return (reader.readStringOrNull(offset)) as P; return (reader.readBool(offset)) as P;
case 9: case 9:
return (reader.readStringOrNull(offset)) as P; return (reader.readStringOrNull(offset)) as P;
case 10: case 10:
return (reader.readLong(offset)) as P;
case 11:
return (reader.readStringOrNull(offset)) as P; return (reader.readStringOrNull(offset)) as P;
case 11:
return (reader.readLong(offset)) as P;
case 12: case 12:
return (reader.readStringOrNull(offset)) as P;
case 13:
return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ?? return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ??
AssetType.other) as P; AssetType.other) as P;
case 13:
return (reader.readDateTime(offset)) as P;
case 14: case 14:
return (reader.readDateTime(offset)) as P;
case 15:
return (reader.readIntOrNull(offset)) as P; return (reader.readIntOrNull(offset)) as P;
default: default:
throw IsarError('Unknown property with id $propertyId'); throw IsarError('Unknown property with id $propertyId');
@ -302,89 +311,89 @@ void _assetAttach(IsarCollection<dynamic> col, Id id, Asset object) {
} }
extension AssetByIndex on IsarCollection<Asset> { extension AssetByIndex on IsarCollection<Asset> {
Future<Asset?> getByChecksumOwnerId(String checksum, int ownerId) { Future<Asset?> getByOwnerIdChecksum(int ownerId, String checksum) {
return getByIndex(r'checksum_ownerId', [checksum, ownerId]); return getByIndex(r'ownerId_checksum', [ownerId, checksum]);
} }
Asset? getByChecksumOwnerIdSync(String checksum, int ownerId) { Asset? getByOwnerIdChecksumSync(int ownerId, String checksum) {
return getByIndexSync(r'checksum_ownerId', [checksum, ownerId]); return getByIndexSync(r'ownerId_checksum', [ownerId, checksum]);
} }
Future<bool> deleteByChecksumOwnerId(String checksum, int ownerId) { Future<bool> deleteByOwnerIdChecksum(int ownerId, String checksum) {
return deleteByIndex(r'checksum_ownerId', [checksum, ownerId]); return deleteByIndex(r'ownerId_checksum', [ownerId, checksum]);
} }
bool deleteByChecksumOwnerIdSync(String checksum, int ownerId) { bool deleteByOwnerIdChecksumSync(int ownerId, String checksum) {
return deleteByIndexSync(r'checksum_ownerId', [checksum, ownerId]); return deleteByIndexSync(r'ownerId_checksum', [ownerId, checksum]);
} }
Future<List<Asset?>> getAllByChecksumOwnerId( Future<List<Asset?>> getAllByOwnerIdChecksum(
List<String> checksumValues, List<int> ownerIdValues) { List<int> ownerIdValues, List<String> checksumValues) {
final len = checksumValues.length; final len = ownerIdValues.length;
assert(ownerIdValues.length == len, assert(checksumValues.length == len,
'All index values must have the same length'); 'All index values must have the same length');
final values = <List<dynamic>>[]; final values = <List<dynamic>>[];
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
values.add([checksumValues[i], ownerIdValues[i]]); values.add([ownerIdValues[i], checksumValues[i]]);
} }
return getAllByIndex(r'checksum_ownerId', values); return getAllByIndex(r'ownerId_checksum', values);
} }
List<Asset?> getAllByChecksumOwnerIdSync( List<Asset?> getAllByOwnerIdChecksumSync(
List<String> checksumValues, List<int> ownerIdValues) { List<int> ownerIdValues, List<String> checksumValues) {
final len = checksumValues.length; final len = ownerIdValues.length;
assert(ownerIdValues.length == len, assert(checksumValues.length == len,
'All index values must have the same length'); 'All index values must have the same length');
final values = <List<dynamic>>[]; final values = <List<dynamic>>[];
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
values.add([checksumValues[i], ownerIdValues[i]]); values.add([ownerIdValues[i], checksumValues[i]]);
} }
return getAllByIndexSync(r'checksum_ownerId', values); return getAllByIndexSync(r'ownerId_checksum', values);
} }
Future<int> deleteAllByChecksumOwnerId( Future<int> deleteAllByOwnerIdChecksum(
List<String> checksumValues, List<int> ownerIdValues) { List<int> ownerIdValues, List<String> checksumValues) {
final len = checksumValues.length; final len = ownerIdValues.length;
assert(ownerIdValues.length == len, assert(checksumValues.length == len,
'All index values must have the same length'); 'All index values must have the same length');
final values = <List<dynamic>>[]; final values = <List<dynamic>>[];
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
values.add([checksumValues[i], ownerIdValues[i]]); values.add([ownerIdValues[i], checksumValues[i]]);
} }
return deleteAllByIndex(r'checksum_ownerId', values); return deleteAllByIndex(r'ownerId_checksum', values);
} }
int deleteAllByChecksumOwnerIdSync( int deleteAllByOwnerIdChecksumSync(
List<String> checksumValues, List<int> ownerIdValues) { List<int> ownerIdValues, List<String> checksumValues) {
final len = checksumValues.length; final len = ownerIdValues.length;
assert(ownerIdValues.length == len, assert(checksumValues.length == len,
'All index values must have the same length'); 'All index values must have the same length');
final values = <List<dynamic>>[]; final values = <List<dynamic>>[];
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
values.add([checksumValues[i], ownerIdValues[i]]); values.add([ownerIdValues[i], checksumValues[i]]);
} }
return deleteAllByIndexSync(r'checksum_ownerId', values); return deleteAllByIndexSync(r'ownerId_checksum', values);
} }
Future<Id> putByChecksumOwnerId(Asset object) { Future<Id> putByOwnerIdChecksum(Asset object) {
return putByIndex(r'checksum_ownerId', object); return putByIndex(r'ownerId_checksum', object);
} }
Id putByChecksumOwnerIdSync(Asset object, {bool saveLinks = true}) { Id putByOwnerIdChecksumSync(Asset object, {bool saveLinks = true}) {
return putByIndexSync(r'checksum_ownerId', object, saveLinks: saveLinks); return putByIndexSync(r'ownerId_checksum', object, saveLinks: saveLinks);
} }
Future<List<Id>> putAllByChecksumOwnerId(List<Asset> objects) { Future<List<Id>> putAllByOwnerIdChecksum(List<Asset> objects) {
return putAllByIndex(r'checksum_ownerId', objects); return putAllByIndex(r'ownerId_checksum', objects);
} }
List<Id> putAllByChecksumOwnerIdSync(List<Asset> objects, List<Id> putAllByOwnerIdChecksumSync(List<Asset> objects,
{bool saveLinks = true}) { {bool saveLinks = true}) {
return putAllByIndexSync(r'checksum_ownerId', objects, return putAllByIndexSync(r'ownerId_checksum', objects,
saveLinks: saveLinks); saveLinks: saveLinks);
} }
} }
@ -463,145 +472,6 @@ extension AssetQueryWhere on QueryBuilder<Asset, Asset, QWhereClause> {
}); });
} }
QueryBuilder<Asset, Asset, QAfterWhereClause> checksumEqualToAnyOwnerId(
String checksum) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.equalTo(
indexName: r'checksum_ownerId',
value: [checksum],
));
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause> checksumNotEqualToAnyOwnerId(
String checksum) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'checksum_ownerId',
lower: [],
upper: [checksum],
includeUpper: false,
))
.addWhereClause(IndexWhereClause.between(
indexName: r'checksum_ownerId',
lower: [checksum],
includeLower: false,
upper: [],
));
} else {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'checksum_ownerId',
lower: [checksum],
includeLower: false,
upper: [],
))
.addWhereClause(IndexWhereClause.between(
indexName: r'checksum_ownerId',
lower: [],
upper: [checksum],
includeUpper: false,
));
}
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause> checksumOwnerIdEqualTo(
String checksum, int ownerId) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.equalTo(
indexName: r'checksum_ownerId',
value: [checksum, ownerId],
));
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause>
checksumEqualToOwnerIdNotEqualTo(String checksum, int ownerId) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'checksum_ownerId',
lower: [checksum],
upper: [checksum, ownerId],
includeUpper: false,
))
.addWhereClause(IndexWhereClause.between(
indexName: r'checksum_ownerId',
lower: [checksum, ownerId],
includeLower: false,
upper: [checksum],
));
} else {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'checksum_ownerId',
lower: [checksum, ownerId],
includeLower: false,
upper: [checksum],
))
.addWhereClause(IndexWhereClause.between(
indexName: r'checksum_ownerId',
lower: [checksum],
upper: [checksum, ownerId],
includeUpper: false,
));
}
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause>
checksumEqualToOwnerIdGreaterThan(
String checksum,
int ownerId, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'checksum_ownerId',
lower: [checksum, ownerId],
includeLower: include,
upper: [checksum],
));
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause> checksumEqualToOwnerIdLessThan(
String checksum,
int ownerId, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'checksum_ownerId',
lower: [checksum],
upper: [checksum, ownerId],
includeUpper: include,
));
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause> checksumEqualToOwnerIdBetween(
String checksum,
int lowerOwnerId,
int upperOwnerId, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'checksum_ownerId',
lower: [checksum, lowerOwnerId],
includeLower: includeLower,
upper: [checksum, upperOwnerId],
includeUpper: includeUpper,
));
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause> remoteIdIsNull() { QueryBuilder<Asset, Asset, QAfterWhereClause> remoteIdIsNull() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.equalTo( return query.addWhereClause(IndexWhereClause.equalTo(
@ -731,6 +601,141 @@ extension AssetQueryWhere on QueryBuilder<Asset, Asset, QWhereClause> {
} }
}); });
} }
QueryBuilder<Asset, Asset, QAfterWhereClause> ownerIdEqualToAnyChecksum(
int ownerId) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.equalTo(
indexName: r'ownerId_checksum',
value: [ownerId],
));
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause> ownerIdNotEqualToAnyChecksum(
int ownerId) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'ownerId_checksum',
lower: [],
upper: [ownerId],
includeUpper: false,
))
.addWhereClause(IndexWhereClause.between(
indexName: r'ownerId_checksum',
lower: [ownerId],
includeLower: false,
upper: [],
));
} else {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'ownerId_checksum',
lower: [ownerId],
includeLower: false,
upper: [],
))
.addWhereClause(IndexWhereClause.between(
indexName: r'ownerId_checksum',
lower: [],
upper: [ownerId],
includeUpper: false,
));
}
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause> ownerIdGreaterThanAnyChecksum(
int ownerId, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'ownerId_checksum',
lower: [ownerId],
includeLower: include,
upper: [],
));
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause> ownerIdLessThanAnyChecksum(
int ownerId, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'ownerId_checksum',
lower: [],
upper: [ownerId],
includeUpper: include,
));
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause> ownerIdBetweenAnyChecksum(
int lowerOwnerId,
int upperOwnerId, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'ownerId_checksum',
lower: [lowerOwnerId],
includeLower: includeLower,
upper: [upperOwnerId],
includeUpper: includeUpper,
));
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause> ownerIdChecksumEqualTo(
int ownerId, String checksum) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.equalTo(
indexName: r'ownerId_checksum',
value: [ownerId, checksum],
));
});
}
QueryBuilder<Asset, Asset, QAfterWhereClause>
ownerIdEqualToChecksumNotEqualTo(int ownerId, String checksum) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'ownerId_checksum',
lower: [ownerId],
upper: [ownerId, checksum],
includeUpper: false,
))
.addWhereClause(IndexWhereClause.between(
indexName: r'ownerId_checksum',
lower: [ownerId, checksum],
includeLower: false,
upper: [ownerId],
));
} else {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'ownerId_checksum',
lower: [ownerId, checksum],
includeLower: false,
upper: [ownerId],
))
.addWhereClause(IndexWhereClause.between(
indexName: r'ownerId_checksum',
lower: [ownerId],
upper: [ownerId, checksum],
includeUpper: false,
));
}
});
}
} }
extension AssetQueryFilter on QueryBuilder<Asset, Asset, QFilterCondition> { extension AssetQueryFilter on QueryBuilder<Asset, Asset, QFilterCondition> {
@ -1294,6 +1299,16 @@ extension AssetQueryFilter on QueryBuilder<Asset, Asset, QFilterCondition> {
}); });
} }
QueryBuilder<Asset, Asset, QAfterFilterCondition> isTrashedEqualTo(
bool value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'isTrashed',
value: value,
));
});
}
QueryBuilder<Asset, Asset, QAfterFilterCondition> livePhotoVideoIdIsNull() { QueryBuilder<Asset, Asset, QAfterFilterCondition> livePhotoVideoIdIsNull() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull( return query.addFilterCondition(const FilterCondition.isNull(
@ -2062,6 +2077,18 @@ extension AssetQuerySortBy on QueryBuilder<Asset, Asset, QSortBy> {
}); });
} }
QueryBuilder<Asset, Asset, QAfterSortBy> sortByIsTrashed() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isTrashed', Sort.asc);
});
}
QueryBuilder<Asset, Asset, QAfterSortBy> sortByIsTrashedDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isTrashed', Sort.desc);
});
}
QueryBuilder<Asset, Asset, QAfterSortBy> sortByLivePhotoVideoId() { QueryBuilder<Asset, Asset, QAfterSortBy> sortByLivePhotoVideoId() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'livePhotoVideoId', Sort.asc); return query.addSortBy(r'livePhotoVideoId', Sort.asc);
@ -2256,6 +2283,18 @@ extension AssetQuerySortThenBy on QueryBuilder<Asset, Asset, QSortThenBy> {
}); });
} }
QueryBuilder<Asset, Asset, QAfterSortBy> thenByIsTrashed() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isTrashed', Sort.asc);
});
}
QueryBuilder<Asset, Asset, QAfterSortBy> thenByIsTrashedDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isTrashed', Sort.desc);
});
}
QueryBuilder<Asset, Asset, QAfterSortBy> thenByLivePhotoVideoId() { QueryBuilder<Asset, Asset, QAfterSortBy> thenByLivePhotoVideoId() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'livePhotoVideoId', Sort.asc); return query.addSortBy(r'livePhotoVideoId', Sort.asc);
@ -2392,6 +2431,12 @@ extension AssetQueryWhereDistinct on QueryBuilder<Asset, Asset, QDistinct> {
}); });
} }
QueryBuilder<Asset, Asset, QDistinct> distinctByIsTrashed() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'isTrashed');
});
}
QueryBuilder<Asset, Asset, QDistinct> distinctByLivePhotoVideoId( QueryBuilder<Asset, Asset, QDistinct> distinctByLivePhotoVideoId(
{bool caseSensitive = true}) { {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
@ -2494,6 +2539,12 @@ extension AssetQueryProperty on QueryBuilder<Asset, Asset, QQueryProperty> {
}); });
} }
QueryBuilder<Asset, bool, QQueryOperations> isTrashedProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'isTrashed');
});
}
QueryBuilder<Asset, String?, QQueryOperations> livePhotoVideoIdProperty() { QueryBuilder<Asset, String?, QQueryOperations> livePhotoVideoIdProperty() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'livePhotoVideoId'); return query.addPropertyName(r'livePhotoVideoId');

View file

@ -2,22 +2,30 @@ import 'package:openapi/api.dart';
class ServerInfoState { class ServerInfoState {
final ServerVersionResponseDto serverVersion; final ServerVersionResponseDto serverVersion;
final ServerFeaturesDto serverFeatures;
final ServerConfigDto serverConfig;
final bool isVersionMismatch; final bool isVersionMismatch;
final String versionMismatchErrorMessage; final String versionMismatchErrorMessage;
ServerInfoState({ ServerInfoState({
required this.serverVersion, required this.serverVersion,
required this.serverFeatures,
required this.serverConfig,
required this.isVersionMismatch, required this.isVersionMismatch,
required this.versionMismatchErrorMessage, required this.versionMismatchErrorMessage,
}); });
ServerInfoState copyWith({ ServerInfoState copyWith({
ServerVersionResponseDto? serverVersion, ServerVersionResponseDto? serverVersion,
ServerFeaturesDto? serverFeatures,
ServerConfigDto? serverConfig,
bool? isVersionMismatch, bool? isVersionMismatch,
String? versionMismatchErrorMessage, String? versionMismatchErrorMessage,
}) { }) {
return ServerInfoState( return ServerInfoState(
serverVersion: serverVersion ?? this.serverVersion, serverVersion: serverVersion ?? this.serverVersion,
serverFeatures: serverFeatures ?? this.serverFeatures,
serverConfig: serverConfig ?? this.serverConfig,
isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch, isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch,
versionMismatchErrorMessage: versionMismatchErrorMessage:
versionMismatchErrorMessage ?? this.versionMismatchErrorMessage, versionMismatchErrorMessage ?? this.versionMismatchErrorMessage,
@ -26,7 +34,7 @@ class ServerInfoState {
@override @override
String toString() { String toString() {
return 'ServerInfoState( serverVersion: $serverVersion, isVersionMismatch: $isVersionMismatch, versionMismatchErrorMessage: $versionMismatchErrorMessage)'; return 'ServerInfoState( serverVersion: $serverVersion, serverFeatures: $serverFeatures, serverConfig: $serverConfig, isVersionMismatch: $isVersionMismatch, versionMismatchErrorMessage: $versionMismatchErrorMessage)';
} }
@override @override
@ -35,6 +43,8 @@ class ServerInfoState {
return other is ServerInfoState && return other is ServerInfoState &&
other.serverVersion == serverVersion && other.serverVersion == serverVersion &&
other.serverFeatures == serverFeatures &&
other.serverConfig == serverConfig &&
other.isVersionMismatch == isVersionMismatch && other.isVersionMismatch == isVersionMismatch &&
other.versionMismatchErrorMessage == versionMismatchErrorMessage; other.versionMismatchErrorMessage == versionMismatchErrorMessage;
} }
@ -42,6 +52,8 @@ class ServerInfoState {
@override @override
int get hashCode { int get hashCode {
return serverVersion.hashCode ^ return serverVersion.hashCode ^
serverFeatures.hashCode ^
serverConfig.hashCode ^
isVersionMismatch.hashCode ^ isVersionMismatch.hashCode ^
versionMismatchErrorMessage.hashCode; versionMismatchErrorMessage.hashCode;
} }

View file

@ -179,6 +179,7 @@ enum StoreKey<T> {
mapShowFavoriteOnly<bool>(118, type: bool), mapShowFavoriteOnly<bool>(118, type: bool),
mapRelativeDate<int>(119, type: int), mapRelativeDate<int>(119, type: int),
selfSignedCert<bool>(120, type: bool), selfSignedCert<bool>(120, type: bool),
mapIncludeArchived<bool>(121, type: bool),
; ;
const StoreKey( const StoreKey(

View file

@ -15,7 +15,6 @@ import 'package:immich_mobile/shared/services/user.service.dart';
import 'package:immich_mobile/utils/db.dart'; import 'package:immich_mobile/utils/db.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
class AssetNotifier extends StateNotifier<bool> { class AssetNotifier extends StateNotifier<bool> {
@ -92,23 +91,45 @@ class AssetNotifier extends StateNotifier<bool> {
await _syncService.syncNewAssetToDb(newAsset); await _syncService.syncNewAssetToDb(newAsset);
} }
Future<void> deleteAssets(Iterable<Asset> deleteAssets) async { Future<bool> deleteAssets(
Iterable<Asset> deleteAssets, {
bool? force = false,
}) async {
_deleteInProgress = true; _deleteInProgress = true;
state = true; state = true;
try { try {
final localDeleted = await _deleteLocalAssets(deleteAssets); final localDeleted = await _deleteLocalAssets(deleteAssets);
final remoteDeleted = await _deleteRemoteAssets(deleteAssets); final remoteDeleted = await _deleteRemoteAssets(deleteAssets, force);
if (localDeleted.isNotEmpty || remoteDeleted.isNotEmpty) { if (localDeleted.isNotEmpty || remoteDeleted.isNotEmpty) {
final dbIds = deleteAssets.map((e) => e.id).toList(); List<Asset>? assetsToUpdate;
// Local only assets are permanently deleted for now. So always remove them from db
final dbIds = deleteAssets
.where((a) => a.isLocal && !a.isRemote)
.map((e) => e.id)
.toList();
if (force == null || !force) {
assetsToUpdate = remoteDeleted.map((e) {
e.isTrashed = true;
return e;
}).toList();
} else {
// Add all remote assets to be deleted from isar as since they are permanently deleted
dbIds.addAll(remoteDeleted.map((e) => e.id));
}
await _db.writeTxn(() async { await _db.writeTxn(() async {
if (assetsToUpdate != null) {
await _db.assets.putAll(assetsToUpdate);
}
await _db.exifInfos.deleteAll(dbIds); await _db.exifInfos.deleteAll(dbIds);
await _db.assets.deleteAll(dbIds); await _db.assets.deleteAll(dbIds);
}); });
return true;
} }
} finally { } finally {
_deleteInProgress = false; _deleteInProgress = false;
state = false; state = false;
} }
return false;
} }
Future<List<String>> _deleteLocalAssets( Future<List<String>> _deleteLocalAssets(
@ -127,15 +148,14 @@ class AssetNotifier extends StateNotifier<bool> {
return []; return [];
} }
Future<Iterable<String>> _deleteRemoteAssets( Future<Iterable<Asset>> _deleteRemoteAssets(
Iterable<Asset> assetsToDelete, Iterable<Asset> assetsToDelete,
bool? force,
) async { ) async {
final Iterable<Asset> remote = assetsToDelete.where((e) => e.isRemote); final Iterable<Asset> remote = assetsToDelete.where((e) => e.isRemote);
final List<DeleteAssetResponseDto> deleteAssetResult =
await _assetService.deleteAssets(remote) ?? []; final isSuccess = await _assetService.deleteAssets(remote, force: force);
return deleteAssetResult return isSuccess ? remote : [];
.where((a) => a.status == DeleteAssetStatus.SUCCESS)
.map((a) => a.id);
} }
Future<void> toggleFavorite(List<Asset> assets, bool status) async { Future<void> toggleFavorite(List<Asset> assets, bool status) async {
@ -186,9 +206,11 @@ final assetsProvider =
final query = ref final query = ref
.watch(dbProvider) .watch(dbProvider)
.assets .assets
.where()
.ownerIdEqualToAnyChecksum(userId)
.filter() .filter()
.ownerIdEqualTo(userId)
.isArchivedEqualTo(false) .isArchivedEqualTo(false)
.isTrashedEqualTo(false)
.sortByFileCreatedAtDesc(); .sortByFileCreatedAtDesc();
final settings = ref.watch(appSettingsServiceProvider); final settings = ref.watch(appSettingsServiceProvider);
final groupBy = final groupBy =
@ -209,6 +231,7 @@ final remoteAssetsProvider =
.remoteIdIsNotNull() .remoteIdIsNotNull()
.filter() .filter()
.ownerIdEqualTo(userId) .ownerIdEqualTo(userId)
.isTrashedEqualTo(false)
.sortByFileCreatedAtDesc(); .sortByFileCreatedAtDesc();
final settings = ref.watch(appSettingsServiceProvider); final settings = ref.watch(appSettingsServiceProvider);
final groupBy = final groupBy =

View file

@ -15,6 +15,26 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
patch_: 0, patch_: 0,
minor: 0, minor: 0,
), ),
serverFeatures: ServerFeaturesDto(
clipEncode: true,
configFile: false,
facialRecognition: true,
map: true,
oauth: false,
oauthAutoLaunch: false,
passwordLogin: true,
search: true,
sidecar: true,
tagImage: true,
trash: true,
reverseGeocoding: true,
),
serverConfig: ServerConfigDto(
loginPageMessage: "",
mapTileUrl: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
oauthButtonText: "",
trashDays: 30,
),
isVersionMismatch: false, isVersionMismatch: false,
versionMismatchErrorMessage: "", versionMismatchErrorMessage: "",
), ),
@ -22,6 +42,12 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
final ServerInfoService _serverInfoService; final ServerInfoService _serverInfoService;
getServerInfo() {
getServerVersion();
getServerFeatures();
getServerConfig();
}
getServerVersion() async { getServerVersion() async {
ServerVersionResponseDto? serverVersion = ServerVersionResponseDto? serverVersion =
await _serverInfoService.getServerVersion(); await _serverInfoService.getServerVersion();
@ -66,6 +92,23 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
); );
} }
getServerFeatures() async {
ServerFeaturesDto? serverFeatures =
await _serverInfoService.getServerFeatures();
if (serverFeatures == null) {
return;
}
state = state.copyWith(serverFeatures: serverFeatures);
}
getServerConfig() async {
ServerConfigDto? serverConfig = await _serverInfoService.getServerConfig();
if (serverConfig == null) {
return;
}
state = state.copyWith(serverConfig: serverConfig);
}
Map<String, int> _getDetailVersion(String version) { Map<String, int> _getDetailVersion(String version) {
List<String> detail = version.split("."); List<String> detail = version.split(".");

View file

@ -6,6 +6,7 @@ import 'package:immich_mobile/modules/login/providers/authentication.provider.da
import 'package:immich_mobile/shared/models/asset.dart'; import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/models/store.dart';
import 'package:immich_mobile/shared/providers/asset.provider.dart'; import 'package:immich_mobile/shared/providers/asset.provider.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:openapi/api.dart'; import 'package:openapi/api.dart';
import 'package:socket_io_client/socket_io_client.dart'; import 'package:socket_io_client/socket_io_client.dart';
@ -92,6 +93,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
}); });
socket.on('on_upload_success', _handleOnUploadSuccess); socket.on('on_upload_success', _handleOnUploadSuccess);
socket.on('on_config_update', _handleOnConfigUpdate);
} catch (e) { } catch (e) {
debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}"); debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}");
} }
@ -126,6 +128,11 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset); ref.watch(assetProvider.notifier).onNewAssetUploaded(newAsset);
} }
} }
_handleOnConfigUpdate(dynamic data) {
ref.read(serverInfoProvider.notifier).getServerFeatures();
ref.read(serverInfoProvider.notifier).getServerConfig();
}
} }
final websocketProvider = final websocketProvider =

View file

@ -64,7 +64,9 @@ class AssetService {
Future<List<Asset>?> _getRemoteAssets(User user) async { Future<List<Asset>?> _getRemoteAssets(User user) async {
try { try {
final List<AssetResponseDto>? assets = final List<AssetResponseDto>? assets =
await _apiService.assetApi.getAllAssets(userId: user.id); await _apiService.assetApi.getAllAssets(
userId: user.id,
);
if (assets == null) { if (assets == null) {
return null; return null;
} else if (assets.isNotEmpty && assets.first.ownerId != user.id) { } else if (assets.isNotEmpty && assets.first.ownerId != user.id) {
@ -84,9 +86,10 @@ class AssetService {
} }
} }
Future<List<DeleteAssetResponseDto>?> deleteAssets( Future<bool> deleteAssets(
Iterable<Asset> deleteAssets, Iterable<Asset> deleteAssets, {
) async { bool? force = false,
}) async {
try { try {
final List<String> payload = []; final List<String> payload = [];
@ -94,12 +97,17 @@ class AssetService {
payload.add(asset.remoteId!); payload.add(asset.remoteId!);
} }
return await _apiService.assetApi await _apiService.assetApi.deleteAssets(
.deleteAsset(DeleteAssetDto(ids: payload)); AssetBulkDeleteDto(
ids: payload,
force: force,
),
);
return true;
} catch (error, stack) { } catch (error, stack) {
log.severe("Error deleteAssets ${error.toString()}", error, stack); log.severe("Error deleteAssets ${error.toString()}", error, stack);
return null;
} }
return false;
} }
/// Loads the exif information from the database. If there is none, loads /// Loads the exif information from the database. If there is none, loads

View file

@ -28,7 +28,25 @@ class ServerInfoService {
try { try {
return await _apiService.serverInfoApi.getServerVersion(); return await _apiService.serverInfoApi.getServerVersion();
} catch (e) { } catch (e) {
debugPrint("Error getting server info"); debugPrint("Error [getServerVersion] ${e.toString()}");
return null;
}
}
Future<ServerFeaturesDto?> getServerFeatures() async {
try {
return await _apiService.serverInfoApi.getServerFeatures();
} catch (e) {
debugPrint("Error [getServerFeatures] ${e.toString()}");
return null;
}
}
Future<ServerConfigDto?> getServerConfig() async {
try {
return await _apiService.serverInfoApi.getServerConfig();
} catch (e) {
debugPrint("Error [getServerConfig] ${e.toString()}");
return null; return null;
} }
} }

View file

@ -123,7 +123,7 @@ class SyncService {
/// Syncs a new asset to the db. Returns `true` if successful /// Syncs a new asset to the db. Returns `true` if successful
Future<bool> _syncNewAssetToDb(Asset a) async { Future<bool> _syncNewAssetToDb(Asset a) async {
final Asset? inDb = final Asset? inDb =
await _db.assets.getByChecksumOwnerId(a.checksum, a.ownerId); await _db.assets.getByOwnerIdChecksum(a.ownerId, a.checksum);
if (inDb != null) { if (inDb != null) {
// unify local/remote assets by replacing the // unify local/remote assets by replacing the
// local-only asset in the DB with a local&remote asset // local-only asset in the DB with a local&remote asset
@ -195,8 +195,8 @@ class SyncService {
return false; return false;
} }
final List<Asset> inDb = await _db.assets final List<Asset> inDb = await _db.assets
.filter() .where()
.ownerIdEqualTo(user.isarId) .ownerIdEqualToAnyChecksum(user.isarId)
.sortByChecksum() .sortByChecksum()
.findAll(); .findAll();
assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!"); assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!");
@ -638,9 +638,9 @@ class SyncService {
) async { ) async {
if (assets.isEmpty) return ([].cast<Asset>(), [].cast<Asset>()); if (assets.isEmpty) return ([].cast<Asset>(), [].cast<Asset>());
final List<Asset?> inDb = await _db.assets.getAllByChecksumOwnerId( final List<Asset?> inDb = await _db.assets.getAllByOwnerIdChecksum(
assets.map((a) => a.checksum).toList(growable: false),
assets.map((a) => a.ownerId).toInt64List(), assets.map((a) => a.ownerId).toInt64List(),
assets.map((a) => a.checksum).toList(growable: false),
); );
assert(inDb.length == assets.length); assert(inDb.length == assets.length);
final List<Asset> existing = [], toUpsert = []; final List<Asset> existing = [], toUpsert = [];
@ -683,9 +683,9 @@ class SyncService {
); );
// give details on the errors // give details on the errors
assets.sort(Asset.compareByOwnerChecksum); assets.sort(Asset.compareByOwnerChecksum);
final inDb = await _db.assets.getAllByChecksumOwnerId( final inDb = await _db.assets.getAllByOwnerIdChecksum(
assets.map((e) => e.checksum).toList(growable: false),
assets.map((e) => e.ownerId).toInt64List(), assets.map((e) => e.ownerId).toInt64List(),
assets.map((e) => e.checksum).toList(growable: false),
); );
for (int i = 0; i < assets.length; i++) { for (int i = 0; i < assets.length; i++) {
final Asset a = assets[i]; final Asset a = assets[i];

View file

@ -15,6 +15,7 @@ doc/AlbumCountResponseDto.md
doc/AlbumResponseDto.md doc/AlbumResponseDto.md
doc/AllJobStatusResponseDto.md doc/AllJobStatusResponseDto.md
doc/AssetApi.md doc/AssetApi.md
doc/AssetBulkDeleteDto.md
doc/AssetBulkUpdateDto.md doc/AssetBulkUpdateDto.md
doc/AssetBulkUploadCheckDto.md doc/AssetBulkUploadCheckDto.md
doc/AssetBulkUploadCheckItem.md doc/AssetBulkUploadCheckItem.md
@ -53,9 +54,6 @@ doc/CreateTagDto.md
doc/CreateUserDto.md doc/CreateUserDto.md
doc/CuratedLocationsResponseDto.md doc/CuratedLocationsResponseDto.md
doc/CuratedObjectsResponseDto.md doc/CuratedObjectsResponseDto.md
doc/DeleteAssetDto.md
doc/DeleteAssetResponseDto.md
doc/DeleteAssetStatus.md
doc/DownloadArchiveInfo.md doc/DownloadArchiveInfo.md
doc/DownloadInfoDto.md doc/DownloadInfoDto.md
doc/DownloadResponseDto.md doc/DownloadResponseDto.md
@ -131,6 +129,7 @@ doc/SystemConfigReverseGeocodingDto.md
doc/SystemConfigStorageTemplateDto.md doc/SystemConfigStorageTemplateDto.md
doc/SystemConfigTemplateStorageOptionDto.md doc/SystemConfigTemplateStorageOptionDto.md
doc/SystemConfigThumbnailDto.md doc/SystemConfigThumbnailDto.md
doc/SystemConfigTrashDto.md
doc/TagApi.md doc/TagApi.md
doc/TagResponseDto.md doc/TagResponseDto.md
doc/TagTypeEnum.md doc/TagTypeEnum.md
@ -186,6 +185,7 @@ lib/model/api_key_create_dto.dart
lib/model/api_key_create_response_dto.dart lib/model/api_key_create_response_dto.dart
lib/model/api_key_response_dto.dart lib/model/api_key_response_dto.dart
lib/model/api_key_update_dto.dart lib/model/api_key_update_dto.dart
lib/model/asset_bulk_delete_dto.dart
lib/model/asset_bulk_update_dto.dart lib/model/asset_bulk_update_dto.dart
lib/model/asset_bulk_upload_check_dto.dart lib/model/asset_bulk_upload_check_dto.dart
lib/model/asset_bulk_upload_check_item.dart lib/model/asset_bulk_upload_check_item.dart
@ -222,9 +222,6 @@ lib/model/create_tag_dto.dart
lib/model/create_user_dto.dart lib/model/create_user_dto.dart
lib/model/curated_locations_response_dto.dart lib/model/curated_locations_response_dto.dart
lib/model/curated_objects_response_dto.dart lib/model/curated_objects_response_dto.dart
lib/model/delete_asset_dto.dart
lib/model/delete_asset_response_dto.dart
lib/model/delete_asset_status.dart
lib/model/download_archive_info.dart lib/model/download_archive_info.dart
lib/model/download_info_dto.dart lib/model/download_info_dto.dart
lib/model/download_response_dto.dart lib/model/download_response_dto.dart
@ -291,6 +288,7 @@ lib/model/system_config_reverse_geocoding_dto.dart
lib/model/system_config_storage_template_dto.dart lib/model/system_config_storage_template_dto.dart
lib/model/system_config_template_storage_option_dto.dart lib/model/system_config_template_storage_option_dto.dart
lib/model/system_config_thumbnail_dto.dart lib/model/system_config_thumbnail_dto.dart
lib/model/system_config_trash_dto.dart
lib/model/tag_response_dto.dart lib/model/tag_response_dto.dart
lib/model/tag_type_enum.dart lib/model/tag_type_enum.dart
lib/model/thumbnail_format.dart lib/model/thumbnail_format.dart
@ -322,6 +320,7 @@ test/api_key_create_response_dto_test.dart
test/api_key_response_dto_test.dart test/api_key_response_dto_test.dart
test/api_key_update_dto_test.dart test/api_key_update_dto_test.dart
test/asset_api_test.dart test/asset_api_test.dart
test/asset_bulk_delete_dto_test.dart
test/asset_bulk_update_dto_test.dart test/asset_bulk_update_dto_test.dart
test/asset_bulk_upload_check_dto_test.dart test/asset_bulk_upload_check_dto_test.dart
test/asset_bulk_upload_check_item_test.dart test/asset_bulk_upload_check_item_test.dart
@ -360,9 +359,6 @@ test/create_tag_dto_test.dart
test/create_user_dto_test.dart test/create_user_dto_test.dart
test/curated_locations_response_dto_test.dart test/curated_locations_response_dto_test.dart
test/curated_objects_response_dto_test.dart test/curated_objects_response_dto_test.dart
test/delete_asset_dto_test.dart
test/delete_asset_response_dto_test.dart
test/delete_asset_status_test.dart
test/download_archive_info_test.dart test/download_archive_info_test.dart
test/download_info_dto_test.dart test/download_info_dto_test.dart
test/download_response_dto_test.dart test/download_response_dto_test.dart
@ -438,6 +434,7 @@ test/system_config_reverse_geocoding_dto_test.dart
test/system_config_storage_template_dto_test.dart test/system_config_storage_template_dto_test.dart
test/system_config_template_storage_option_dto_test.dart test/system_config_template_storage_option_dto_test.dart
test/system_config_thumbnail_dto_test.dart test/system_config_thumbnail_dto_test.dart
test/system_config_trash_dto_test.dart
test/tag_api_test.dart test/tag_api_test.dart
test/tag_response_dto_test.dart test/tag_response_dto_test.dart
test/tag_type_enum_test.dart test/tag_type_enum_test.dart

View file

@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.79.1 - API version: 1.81.1
- Build package: org.openapitools.codegen.languages.DartClientCodegen - Build package: org.openapitools.codegen.languages.DartClientCodegen
## Requirements ## Requirements
@ -90,9 +90,10 @@ Class | Method | HTTP request | Description
*AssetApi* | [**bulkUploadCheck**](doc//AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check | *AssetApi* | [**bulkUploadCheck**](doc//AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |
*AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check | *AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |
*AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist | *AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |
*AssetApi* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset | *AssetApi* | [**deleteAssets**](doc//AssetApi.md#deleteassets) | **DELETE** /asset |
*AssetApi* | [**downloadArchive**](doc//AssetApi.md#downloadarchive) | **POST** /asset/download/archive | *AssetApi* | [**downloadArchive**](doc//AssetApi.md#downloadarchive) | **POST** /asset/download/archive |
*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **POST** /asset/download/{id} | *AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **POST** /asset/download/{id} |
*AssetApi* | [**emptyTrash**](doc//AssetApi.md#emptytrash) | **POST** /asset/trash/empty |
*AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset | *AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |
*AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} | *AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |
*AssetApi* | [**getAssetSearchTerms**](doc//AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms | *AssetApi* | [**getAssetSearchTerms**](doc//AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |
@ -108,6 +109,8 @@ Class | Method | HTTP request | Description
*AssetApi* | [**getTimeBuckets**](doc//AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets | *AssetApi* | [**getTimeBuckets**](doc//AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets |
*AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} | *AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
*AssetApi* | [**importFile**](doc//AssetApi.md#importfile) | **POST** /asset/import | *AssetApi* | [**importFile**](doc//AssetApi.md#importfile) | **POST** /asset/import |
*AssetApi* | [**restoreAssets**](doc//AssetApi.md#restoreassets) | **POST** /asset/restore |
*AssetApi* | [**restoreTrash**](doc//AssetApi.md#restoretrash) | **POST** /asset/trash/restore |
*AssetApi* | [**runAssetJobs**](doc//AssetApi.md#runassetjobs) | **POST** /asset/jobs | *AssetApi* | [**runAssetJobs**](doc//AssetApi.md#runassetjobs) | **POST** /asset/jobs |
*AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search | *AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search |
*AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} | *AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} |
@ -201,6 +204,7 @@ Class | Method | HTTP request | Description
- [AlbumCountResponseDto](doc//AlbumCountResponseDto.md) - [AlbumCountResponseDto](doc//AlbumCountResponseDto.md)
- [AlbumResponseDto](doc//AlbumResponseDto.md) - [AlbumResponseDto](doc//AlbumResponseDto.md)
- [AllJobStatusResponseDto](doc//AllJobStatusResponseDto.md) - [AllJobStatusResponseDto](doc//AllJobStatusResponseDto.md)
- [AssetBulkDeleteDto](doc//AssetBulkDeleteDto.md)
- [AssetBulkUpdateDto](doc//AssetBulkUpdateDto.md) - [AssetBulkUpdateDto](doc//AssetBulkUpdateDto.md)
- [AssetBulkUploadCheckDto](doc//AssetBulkUploadCheckDto.md) - [AssetBulkUploadCheckDto](doc//AssetBulkUploadCheckDto.md)
- [AssetBulkUploadCheckItem](doc//AssetBulkUploadCheckItem.md) - [AssetBulkUploadCheckItem](doc//AssetBulkUploadCheckItem.md)
@ -237,9 +241,6 @@ Class | Method | HTTP request | Description
- [CreateUserDto](doc//CreateUserDto.md) - [CreateUserDto](doc//CreateUserDto.md)
- [CuratedLocationsResponseDto](doc//CuratedLocationsResponseDto.md) - [CuratedLocationsResponseDto](doc//CuratedLocationsResponseDto.md)
- [CuratedObjectsResponseDto](doc//CuratedObjectsResponseDto.md) - [CuratedObjectsResponseDto](doc//CuratedObjectsResponseDto.md)
- [DeleteAssetDto](doc//DeleteAssetDto.md)
- [DeleteAssetResponseDto](doc//DeleteAssetResponseDto.md)
- [DeleteAssetStatus](doc//DeleteAssetStatus.md)
- [DownloadArchiveInfo](doc//DownloadArchiveInfo.md) - [DownloadArchiveInfo](doc//DownloadArchiveInfo.md)
- [DownloadInfoDto](doc//DownloadInfoDto.md) - [DownloadInfoDto](doc//DownloadInfoDto.md)
- [DownloadResponseDto](doc//DownloadResponseDto.md) - [DownloadResponseDto](doc//DownloadResponseDto.md)
@ -306,6 +307,7 @@ Class | Method | HTTP request | Description
- [SystemConfigStorageTemplateDto](doc//SystemConfigStorageTemplateDto.md) - [SystemConfigStorageTemplateDto](doc//SystemConfigStorageTemplateDto.md)
- [SystemConfigTemplateStorageOptionDto](doc//SystemConfigTemplateStorageOptionDto.md) - [SystemConfigTemplateStorageOptionDto](doc//SystemConfigTemplateStorageOptionDto.md)
- [SystemConfigThumbnailDto](doc//SystemConfigThumbnailDto.md) - [SystemConfigThumbnailDto](doc//SystemConfigThumbnailDto.md)
- [SystemConfigTrashDto](doc//SystemConfigTrashDto.md)
- [TagResponseDto](doc//TagResponseDto.md) - [TagResponseDto](doc//TagResponseDto.md)
- [TagTypeEnum](doc//TagTypeEnum.md) - [TagTypeEnum](doc//TagTypeEnum.md)
- [ThumbnailFormat](doc//ThumbnailFormat.md) - [ThumbnailFormat](doc//ThumbnailFormat.md)

View file

@ -12,9 +12,10 @@ Method | HTTP request | Description
[**bulkUploadCheck**](AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check | [**bulkUploadCheck**](AssetApi.md#bulkuploadcheck) | **POST** /asset/bulk-upload-check |
[**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check | [**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |
[**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist | [**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |
[**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset | [**deleteAssets**](AssetApi.md#deleteassets) | **DELETE** /asset |
[**downloadArchive**](AssetApi.md#downloadarchive) | **POST** /asset/download/archive | [**downloadArchive**](AssetApi.md#downloadarchive) | **POST** /asset/download/archive |
[**downloadFile**](AssetApi.md#downloadfile) | **POST** /asset/download/{id} | [**downloadFile**](AssetApi.md#downloadfile) | **POST** /asset/download/{id} |
[**emptyTrash**](AssetApi.md#emptytrash) | **POST** /asset/trash/empty |
[**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset | [**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset |
[**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} | [**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |
[**getAssetSearchTerms**](AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms | [**getAssetSearchTerms**](AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |
@ -30,6 +31,8 @@ Method | HTTP request | Description
[**getTimeBuckets**](AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets | [**getTimeBuckets**](AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets |
[**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} | [**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
[**importFile**](AssetApi.md#importfile) | **POST** /asset/import | [**importFile**](AssetApi.md#importfile) | **POST** /asset/import |
[**restoreAssets**](AssetApi.md#restoreassets) | **POST** /asset/restore |
[**restoreTrash**](AssetApi.md#restoretrash) | **POST** /asset/trash/restore |
[**runAssetJobs**](AssetApi.md#runassetjobs) | **POST** /asset/jobs | [**runAssetJobs**](AssetApi.md#runassetjobs) | **POST** /asset/jobs |
[**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search | [**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search |
[**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{id} | [**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{id} |
@ -211,8 +214,8 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **deleteAsset** # **deleteAssets**
> List<DeleteAssetResponseDto> deleteAsset(deleteAssetDto) > deleteAssets(assetBulkDeleteDto)
@ -235,13 +238,12 @@ import 'package:openapi/api.dart';
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = AssetApi(); final api_instance = AssetApi();
final deleteAssetDto = DeleteAssetDto(); // DeleteAssetDto | final assetBulkDeleteDto = AssetBulkDeleteDto(); // AssetBulkDeleteDto |
try { try {
final result = api_instance.deleteAsset(deleteAssetDto); api_instance.deleteAssets(assetBulkDeleteDto);
print(result);
} catch (e) { } catch (e) {
print('Exception when calling AssetApi->deleteAsset: $e\n'); print('Exception when calling AssetApi->deleteAssets: $e\n');
} }
``` ```
@ -249,11 +251,11 @@ try {
Name | Type | Description | Notes Name | Type | Description | Notes
------------- | ------------- | ------------- | ------------- ------------- | ------------- | ------------- | -------------
**deleteAssetDto** | [**DeleteAssetDto**](DeleteAssetDto.md)| | **assetBulkDeleteDto** | [**AssetBulkDeleteDto**](AssetBulkDeleteDto.md)| |
### Return type ### Return type
[**List<DeleteAssetResponseDto>**](DeleteAssetResponseDto.md) void (empty response body)
### Authorization ### Authorization
@ -262,7 +264,7 @@ Name | Type | Description | Notes
### HTTP request headers ### HTTP request headers
- **Content-Type**: application/json - **Content-Type**: application/json
- **Accept**: application/json - **Accept**: Not defined
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
@ -380,6 +382,56 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **emptyTrash**
> emptyTrash()
### Example
```dart
import 'package:openapi/api.dart';
// TODO Configure API key authorization: cookie
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
// TODO Configure API key authorization: api_key
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
// TODO Configure HTTP Bearer authorization: bearer
// Case 1. Use String Token
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
// Case 2. Use Function which generate token.
// String yourTokenGeneratorFunction() { ... }
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = AssetApi();
try {
api_instance.emptyTrash();
} catch (e) {
print('Exception when calling AssetApi->emptyTrash: $e\n');
}
```
### Parameters
This endpoint does not need any parameter.
### Return type
void (empty response body)
### Authorization
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: Not defined
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **getAllAssets** # **getAllAssets**
> List<AssetResponseDto> getAllAssets(userId, isFavorite, isArchived, skip, updatedAfter, ifNoneMatch) > List<AssetResponseDto> getAllAssets(userId, isFavorite, isArchived, skip, updatedAfter, ifNoneMatch)
@ -558,7 +610,7 @@ This endpoint does not need any parameter.
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **getAssetStats** # **getAssetStats**
> AssetStatsResponseDto getAssetStats(isArchived, isFavorite) > AssetStatsResponseDto getAssetStats(isArchived, isFavorite, isTrashed)
@ -583,9 +635,10 @@ import 'package:openapi/api.dart';
final api_instance = AssetApi(); final api_instance = AssetApi();
final isArchived = true; // bool | final isArchived = true; // bool |
final isFavorite = true; // bool | final isFavorite = true; // bool |
final isTrashed = true; // bool |
try { try {
final result = api_instance.getAssetStats(isArchived, isFavorite); final result = api_instance.getAssetStats(isArchived, isFavorite, isTrashed);
print(result); print(result);
} catch (e) { } catch (e) {
print('Exception when calling AssetApi->getAssetStats: $e\n'); print('Exception when calling AssetApi->getAssetStats: $e\n');
@ -598,6 +651,7 @@ Name | Type | Description | Notes
------------- | ------------- | ------------- | ------------- ------------- | ------------- | ------------- | -------------
**isArchived** | **bool**| | [optional] **isArchived** | **bool**| | [optional]
**isFavorite** | **bool**| | [optional] **isFavorite** | **bool**| | [optional]
**isTrashed** | **bool**| | [optional]
### Return type ### Return type
@ -674,7 +728,7 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **getByTimeBucket** # **getByTimeBucket**
> List<AssetResponseDto> getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, key) > List<AssetResponseDto> getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, key)
@ -704,10 +758,11 @@ final albumId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final personId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | final personId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final isArchived = true; // bool | final isArchived = true; // bool |
final isFavorite = true; // bool | final isFavorite = true; // bool |
final isTrashed = true; // bool |
final key = key_example; // String | final key = key_example; // String |
try { try {
final result = api_instance.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, key); final result = api_instance.getByTimeBucket(size, timeBucket, userId, albumId, personId, isArchived, isFavorite, isTrashed, key);
print(result); print(result);
} catch (e) { } catch (e) {
print('Exception when calling AssetApi->getByTimeBucket: $e\n'); print('Exception when calling AssetApi->getByTimeBucket: $e\n');
@ -725,6 +780,7 @@ Name | Type | Description | Notes
**personId** | **String**| | [optional] **personId** | **String**| | [optional]
**isArchived** | **bool**| | [optional] **isArchived** | **bool**| | [optional]
**isFavorite** | **bool**| | [optional] **isFavorite** | **bool**| | [optional]
**isTrashed** | **bool**| | [optional]
**key** | **String**| | [optional] **key** | **String**| | [optional]
### Return type ### Return type
@ -902,7 +958,7 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **getMapMarkers** # **getMapMarkers**
> List<MapMarkerResponseDto> getMapMarkers(isFavorite, fileCreatedAfter, fileCreatedBefore) > List<MapMarkerResponseDto> getMapMarkers(isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore)
@ -925,12 +981,13 @@ import 'package:openapi/api.dart';
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = AssetApi(); final api_instance = AssetApi();
final isArchived = true; // bool |
final isFavorite = true; // bool | final isFavorite = true; // bool |
final fileCreatedAfter = 2013-10-20T19:20:30+01:00; // DateTime | final fileCreatedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final fileCreatedBefore = 2013-10-20T19:20:30+01:00; // DateTime | final fileCreatedBefore = 2013-10-20T19:20:30+01:00; // DateTime |
try { try {
final result = api_instance.getMapMarkers(isFavorite, fileCreatedAfter, fileCreatedBefore); final result = api_instance.getMapMarkers(isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore);
print(result); print(result);
} catch (e) { } catch (e) {
print('Exception when calling AssetApi->getMapMarkers: $e\n'); print('Exception when calling AssetApi->getMapMarkers: $e\n');
@ -941,6 +998,7 @@ try {
Name | Type | Description | Notes Name | Type | Description | Notes
------------- | ------------- | ------------- | ------------- ------------- | ------------- | ------------- | -------------
**isArchived** | **bool**| | [optional]
**isFavorite** | **bool**| | [optional] **isFavorite** | **bool**| | [optional]
**fileCreatedAfter** | **DateTime**| | [optional] **fileCreatedAfter** | **DateTime**| | [optional]
**fileCreatedBefore** | **DateTime**| | [optional] **fileCreatedBefore** | **DateTime**| | [optional]
@ -961,7 +1019,7 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **getMemoryLane** # **getMemoryLane**
> List<MemoryLaneResponseDto> getMemoryLane(timestamp) > List<MemoryLaneResponseDto> getMemoryLane(day, month)
@ -984,10 +1042,11 @@ import 'package:openapi/api.dart';
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction); //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = AssetApi(); final api_instance = AssetApi();
final timestamp = 2013-10-20T19:20:30+01:00; // DateTime | Get pictures for +24 hours from this time going back x years final day = 56; // int |
final month = 56; // int |
try { try {
final result = api_instance.getMemoryLane(timestamp); final result = api_instance.getMemoryLane(day, month);
print(result); print(result);
} catch (e) { } catch (e) {
print('Exception when calling AssetApi->getMemoryLane: $e\n'); print('Exception when calling AssetApi->getMemoryLane: $e\n');
@ -998,7 +1057,8 @@ try {
Name | Type | Description | Notes Name | Type | Description | Notes
------------- | ------------- | ------------- | ------------- ------------- | ------------- | ------------- | -------------
**timestamp** | **DateTime**| Get pictures for +24 hours from this time going back x years | **day** | **int**| |
**month** | **int**| |
### Return type ### Return type
@ -1071,7 +1131,7 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **getTimeBuckets** # **getTimeBuckets**
> List<TimeBucketResponseDto> getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, key) > List<TimeBucketResponseDto> getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, key)
@ -1100,10 +1160,11 @@ final albumId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final personId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | final personId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final isArchived = true; // bool | final isArchived = true; // bool |
final isFavorite = true; // bool | final isFavorite = true; // bool |
final isTrashed = true; // bool |
final key = key_example; // String | final key = key_example; // String |
try { try {
final result = api_instance.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, key); final result = api_instance.getTimeBuckets(size, userId, albumId, personId, isArchived, isFavorite, isTrashed, key);
print(result); print(result);
} catch (e) { } catch (e) {
print('Exception when calling AssetApi->getTimeBuckets: $e\n'); print('Exception when calling AssetApi->getTimeBuckets: $e\n');
@ -1120,6 +1181,7 @@ Name | Type | Description | Notes
**personId** | **String**| | [optional] **personId** | **String**| | [optional]
**isArchived** | **bool**| | [optional] **isArchived** | **bool**| | [optional]
**isFavorite** | **bool**| | [optional] **isFavorite** | **bool**| | [optional]
**isTrashed** | **bool**| | [optional]
**key** | **String**| | [optional] **key** | **String**| | [optional]
### Return type ### Return type
@ -1249,6 +1311,110 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **restoreAssets**
> restoreAssets(bulkIdsDto)
### Example
```dart
import 'package:openapi/api.dart';
// TODO Configure API key authorization: cookie
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
// TODO Configure API key authorization: api_key
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
// TODO Configure HTTP Bearer authorization: bearer
// Case 1. Use String Token
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
// Case 2. Use Function which generate token.
// String yourTokenGeneratorFunction() { ... }
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = AssetApi();
final bulkIdsDto = BulkIdsDto(); // BulkIdsDto |
try {
api_instance.restoreAssets(bulkIdsDto);
} catch (e) {
print('Exception when calling AssetApi->restoreAssets: $e\n');
}
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bulkIdsDto** | [**BulkIdsDto**](BulkIdsDto.md)| |
### Return type
void (empty response body)
### Authorization
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: Not defined
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **restoreTrash**
> restoreTrash()
### Example
```dart
import 'package:openapi/api.dart';
// TODO Configure API key authorization: cookie
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
// TODO Configure API key authorization: api_key
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
// TODO Configure HTTP Bearer authorization: bearer
// Case 1. Use String Token
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
// Case 2. Use Function which generate token.
// String yourTokenGeneratorFunction() { ... }
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = AssetApi();
try {
api_instance.restoreTrash();
} catch (e) {
print('Exception when calling AssetApi->restoreTrash: $e\n');
}
```
### Parameters
This endpoint does not need any parameter.
### Return type
void (empty response body)
### Authorization
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: Not defined
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **runAssetJobs** # **runAssetJobs**
> runAssetJobs(assetJobsDto) > runAssetJobs(assetJobsDto)

View file

@ -1,4 +1,4 @@
# openapi.model.DeleteAssetDto # openapi.model.AssetBulkDeleteDto
## Load the model package ## Load the model package
```dart ```dart
@ -8,6 +8,7 @@ import 'package:openapi/api.dart';
## Properties ## Properties
Name | Type | Description | Notes Name | Type | Description | Notes
------------ | ------------- | ------------- | ------------- ------------ | ------------- | ------------- | -------------
**force** | **bool** | | [optional]
**ids** | **List<String>** | | [default to const []] **ids** | **List<String>** | | [default to const []]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View file

@ -21,8 +21,10 @@ Name | Type | Description | Notes
**isFavorite** | **bool** | | **isFavorite** | **bool** | |
**isOffline** | **bool** | | **isOffline** | **bool** | |
**isReadOnly** | **bool** | | **isReadOnly** | **bool** | |
**isTrashed** | **bool** | |
**libraryId** | **String** | | **libraryId** | **String** | |
**livePhotoVideoId** | **String** | | [optional] **livePhotoVideoId** | **String** | | [optional]
**localDateTime** | [**DateTime**](DateTime.md) | |
**originalFileName** | **String** | | **originalFileName** | **String** | |
**originalPath** | **String** | | **originalPath** | **String** | |
**owner** | [**UserResponseDto**](UserResponseDto.md) | | [optional] **owner** | [**UserResponseDto**](UserResponseDto.md) | | [optional]

View file

@ -1,16 +0,0 @@
# openapi.model.DeleteAssetResponseDto
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | **String** | |
**status** | [**DeleteAssetStatus**](DeleteAssetStatus.md) | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View file

@ -11,6 +11,7 @@ Name | Type | Description | Notes
**loginPageMessage** | **String** | | **loginPageMessage** | **String** | |
**mapTileUrl** | **String** | | **mapTileUrl** | **String** | |
**oauthButtonText** | **String** | | **oauthButtonText** | **String** | |
**trashDays** | **int** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View file

@ -19,6 +19,7 @@ Name | Type | Description | Notes
**search** | **bool** | | **search** | **bool** | |
**sidecar** | **bool** | | **sidecar** | **bool** | |
**tagImage** | **bool** | | **tagImage** | **bool** | |
**trash** | **bool** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View file

@ -17,6 +17,7 @@ Name | Type | Description | Notes
**reverseGeocoding** | [**SystemConfigReverseGeocodingDto**](SystemConfigReverseGeocodingDto.md) | | **reverseGeocoding** | [**SystemConfigReverseGeocodingDto**](SystemConfigReverseGeocodingDto.md) | |
**storageTemplate** | [**SystemConfigStorageTemplateDto**](SystemConfigStorageTemplateDto.md) | | **storageTemplate** | [**SystemConfigStorageTemplateDto**](SystemConfigStorageTemplateDto.md) | |
**thumbnail** | [**SystemConfigThumbnailDto**](SystemConfigThumbnailDto.md) | | **thumbnail** | [**SystemConfigThumbnailDto**](SystemConfigThumbnailDto.md) | |
**trash** | [**SystemConfigTrashDto**](SystemConfigTrashDto.md) | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View file

@ -14,6 +14,7 @@ Name | Type | Description | Notes
**monthOptions** | **List<String>** | | [default to const []] **monthOptions** | **List<String>** | | [default to const []]
**presetOptions** | **List<String>** | | [default to const []] **presetOptions** | **List<String>** | | [default to const []]
**secondOptions** | **List<String>** | | [default to const []] **secondOptions** | **List<String>** | | [default to const []]
**weekOptions** | **List<String>** | | [default to const []]
**yearOptions** | **List<String>** | | [default to const []] **yearOptions** | **List<String>** | | [default to const []]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View file

@ -1,4 +1,4 @@
# openapi.model.DeleteAssetStatus # openapi.model.SystemConfigTrashDto
## Load the model package ## Load the model package
```dart ```dart
@ -8,6 +8,8 @@ import 'package:openapi/api.dart';
## Properties ## Properties
Name | Type | Description | Notes Name | Type | Description | Notes
------------ | ------------- | ------------- | ------------- ------------ | ------------- | ------------- | -------------
**days** | **int** | |
**enabled** | **bool** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View file

@ -54,6 +54,7 @@ part 'model/admin_signup_response_dto.dart';
part 'model/album_count_response_dto.dart'; part 'model/album_count_response_dto.dart';
part 'model/album_response_dto.dart'; part 'model/album_response_dto.dart';
part 'model/all_job_status_response_dto.dart'; part 'model/all_job_status_response_dto.dart';
part 'model/asset_bulk_delete_dto.dart';
part 'model/asset_bulk_update_dto.dart'; part 'model/asset_bulk_update_dto.dart';
part 'model/asset_bulk_upload_check_dto.dart'; part 'model/asset_bulk_upload_check_dto.dart';
part 'model/asset_bulk_upload_check_item.dart'; part 'model/asset_bulk_upload_check_item.dart';
@ -90,9 +91,6 @@ part 'model/create_tag_dto.dart';
part 'model/create_user_dto.dart'; part 'model/create_user_dto.dart';
part 'model/curated_locations_response_dto.dart'; part 'model/curated_locations_response_dto.dart';
part 'model/curated_objects_response_dto.dart'; part 'model/curated_objects_response_dto.dart';
part 'model/delete_asset_dto.dart';
part 'model/delete_asset_response_dto.dart';
part 'model/delete_asset_status.dart';
part 'model/download_archive_info.dart'; part 'model/download_archive_info.dart';
part 'model/download_info_dto.dart'; part 'model/download_info_dto.dart';
part 'model/download_response_dto.dart'; part 'model/download_response_dto.dart';
@ -159,6 +157,7 @@ part 'model/system_config_reverse_geocoding_dto.dart';
part 'model/system_config_storage_template_dto.dart'; part 'model/system_config_storage_template_dto.dart';
part 'model/system_config_template_storage_option_dto.dart'; part 'model/system_config_template_storage_option_dto.dart';
part 'model/system_config_thumbnail_dto.dart'; part 'model/system_config_thumbnail_dto.dart';
part 'model/system_config_trash_dto.dart';
part 'model/tag_response_dto.dart'; part 'model/tag_response_dto.dart';
part 'model/tag_type_enum.dart'; part 'model/tag_type_enum.dart';
part 'model/thumbnail_format.dart'; part 'model/thumbnail_format.dart';

View file

@ -183,13 +183,13 @@ class AssetApi {
/// Performs an HTTP 'DELETE /asset' operation and returns the [Response]. /// Performs an HTTP 'DELETE /asset' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [DeleteAssetDto] deleteAssetDto (required): /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required):
Future<Response> deleteAssetWithHttpInfo(DeleteAssetDto deleteAssetDto,) async { Future<Response> deleteAssetsWithHttpInfo(AssetBulkDeleteDto assetBulkDeleteDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/asset'; final path = r'/asset';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = deleteAssetDto; Object? postBody = assetBulkDeleteDto;
final queryParams = <QueryParam>[]; final queryParams = <QueryParam>[];
final headerParams = <String, String>{}; final headerParams = <String, String>{};
@ -211,23 +211,12 @@ class AssetApi {
/// Parameters: /// Parameters:
/// ///
/// * [DeleteAssetDto] deleteAssetDto (required): /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required):
Future<List<DeleteAssetResponseDto>?> deleteAsset(DeleteAssetDto deleteAssetDto,) async { Future<void> deleteAssets(AssetBulkDeleteDto assetBulkDeleteDto,) async {
final response = await deleteAssetWithHttpInfo(deleteAssetDto,); final response = await deleteAssetsWithHttpInfo(assetBulkDeleteDto,);
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
final responseBody = await _decodeBodyBytes(response);
return (await apiClient.deserializeAsync(responseBody, 'List<DeleteAssetResponseDto>') as List)
.cast<DeleteAssetResponseDto>()
.toList();
}
return null;
} }
/// Performs an HTTP 'POST /asset/download/archive' operation and returns the [Response]. /// Performs an HTTP 'POST /asset/download/archive' operation and returns the [Response].
@ -341,6 +330,39 @@ class AssetApi {
return null; return null;
} }
/// Performs an HTTP 'POST /asset/trash/empty' operation and returns the [Response].
Future<Response> emptyTrashWithHttpInfo() async {
// ignore: prefer_const_declarations
final path = r'/asset/trash/empty';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'POST',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
Future<void> emptyTrash() async {
final response = await emptyTrashWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
}
/// Get all AssetEntity belong to the user /// Get all AssetEntity belong to the user
/// ///
/// Note: This method returns the HTTP [Response]. /// Note: This method returns the HTTP [Response].
@ -549,7 +571,9 @@ class AssetApi {
/// * [bool] isArchived: /// * [bool] isArchived:
/// ///
/// * [bool] isFavorite: /// * [bool] isFavorite:
Future<Response> getAssetStatsWithHttpInfo({ bool? isArchived, bool? isFavorite, }) async { ///
/// * [bool] isTrashed:
Future<Response> getAssetStatsWithHttpInfo({ bool? isArchived, bool? isFavorite, bool? isTrashed, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/asset/statistics'; final path = r'/asset/statistics';
@ -566,6 +590,9 @@ class AssetApi {
if (isFavorite != null) { if (isFavorite != null) {
queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); queryParams.addAll(_queryParams('', 'isFavorite', isFavorite));
} }
if (isTrashed != null) {
queryParams.addAll(_queryParams('', 'isTrashed', isTrashed));
}
const contentTypes = <String>[]; const contentTypes = <String>[];
@ -586,8 +613,10 @@ class AssetApi {
/// * [bool] isArchived: /// * [bool] isArchived:
/// ///
/// * [bool] isFavorite: /// * [bool] isFavorite:
Future<AssetStatsResponseDto?> getAssetStats({ bool? isArchived, bool? isFavorite, }) async { ///
final response = await getAssetStatsWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, ); /// * [bool] isTrashed:
Future<AssetStatsResponseDto?> getAssetStats({ bool? isArchived, bool? isFavorite, bool? isTrashed, }) async {
final response = await getAssetStatsWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, );
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
@ -681,8 +710,10 @@ class AssetApi {
/// ///
/// * [bool] isFavorite: /// * [bool] isFavorite:
/// ///
/// * [bool] isTrashed:
///
/// * [String] key: /// * [String] key:
Future<Response> getByTimeBucketWithHttpInfo(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, String? key, }) async { Future<Response> getByTimeBucketWithHttpInfo(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/asset/time-bucket'; final path = r'/asset/time-bucket';
@ -708,6 +739,9 @@ class AssetApi {
} }
if (isFavorite != null) { if (isFavorite != null) {
queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); queryParams.addAll(_queryParams('', 'isFavorite', isFavorite));
}
if (isTrashed != null) {
queryParams.addAll(_queryParams('', 'isTrashed', isTrashed));
} }
queryParams.addAll(_queryParams('', 'timeBucket', timeBucket)); queryParams.addAll(_queryParams('', 'timeBucket', timeBucket));
if (key != null) { if (key != null) {
@ -744,9 +778,11 @@ class AssetApi {
/// ///
/// * [bool] isFavorite: /// * [bool] isFavorite:
/// ///
/// * [bool] isTrashed:
///
/// * [String] key: /// * [String] key:
Future<List<AssetResponseDto>?> getByTimeBucket(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, String? key, }) async { Future<List<AssetResponseDto>?> getByTimeBucket(TimeBucketSize size, String timeBucket, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async {
final response = await getByTimeBucketWithHttpInfo(size, timeBucket, userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, key: key, ); final response = await getByTimeBucketWithHttpInfo(size, timeBucket, userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, );
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
@ -909,12 +945,14 @@ class AssetApi {
/// Performs an HTTP 'GET /asset/map-marker' operation and returns the [Response]. /// Performs an HTTP 'GET /asset/map-marker' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [bool] isArchived:
///
/// * [bool] isFavorite: /// * [bool] isFavorite:
/// ///
/// * [DateTime] fileCreatedAfter: /// * [DateTime] fileCreatedAfter:
/// ///
/// * [DateTime] fileCreatedBefore: /// * [DateTime] fileCreatedBefore:
Future<Response> getMapMarkersWithHttpInfo({ bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, }) async { Future<Response> getMapMarkersWithHttpInfo({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/asset/map-marker'; final path = r'/asset/map-marker';
@ -925,6 +963,9 @@ class AssetApi {
final headerParams = <String, String>{}; final headerParams = <String, String>{};
final formParams = <String, String>{}; final formParams = <String, String>{};
if (isArchived != null) {
queryParams.addAll(_queryParams('', 'isArchived', isArchived));
}
if (isFavorite != null) { if (isFavorite != null) {
queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); queryParams.addAll(_queryParams('', 'isFavorite', isFavorite));
} }
@ -951,13 +992,15 @@ class AssetApi {
/// Parameters: /// Parameters:
/// ///
/// * [bool] isArchived:
///
/// * [bool] isFavorite: /// * [bool] isFavorite:
/// ///
/// * [DateTime] fileCreatedAfter: /// * [DateTime] fileCreatedAfter:
/// ///
/// * [DateTime] fileCreatedBefore: /// * [DateTime] fileCreatedBefore:
Future<List<MapMarkerResponseDto>?> getMapMarkers({ bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, }) async { Future<List<MapMarkerResponseDto>?> getMapMarkers({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, }) async {
final response = await getMapMarkersWithHttpInfo( isFavorite: isFavorite, fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, ); final response = await getMapMarkersWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, );
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
@ -977,9 +1020,10 @@ class AssetApi {
/// Performs an HTTP 'GET /asset/memory-lane' operation and returns the [Response]. /// Performs an HTTP 'GET /asset/memory-lane' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [DateTime] timestamp (required): /// * [int] day (required):
/// Get pictures for +24 hours from this time going back x years ///
Future<Response> getMemoryLaneWithHttpInfo(DateTime timestamp,) async { /// * [int] month (required):
Future<Response> getMemoryLaneWithHttpInfo(int day, int month,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/asset/memory-lane'; final path = r'/asset/memory-lane';
@ -990,7 +1034,8 @@ class AssetApi {
final headerParams = <String, String>{}; final headerParams = <String, String>{};
final formParams = <String, String>{}; final formParams = <String, String>{};
queryParams.addAll(_queryParams('', 'timestamp', timestamp)); queryParams.addAll(_queryParams('', 'day', day));
queryParams.addAll(_queryParams('', 'month', month));
const contentTypes = <String>[]; const contentTypes = <String>[];
@ -1008,10 +1053,11 @@ class AssetApi {
/// Parameters: /// Parameters:
/// ///
/// * [DateTime] timestamp (required): /// * [int] day (required):
/// Get pictures for +24 hours from this time going back x years ///
Future<List<MemoryLaneResponseDto>?> getMemoryLane(DateTime timestamp,) async { /// * [int] month (required):
final response = await getMemoryLaneWithHttpInfo(timestamp,); Future<List<MemoryLaneResponseDto>?> getMemoryLane(int day, int month,) async {
final response = await getMemoryLaneWithHttpInfo(day, month,);
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
@ -1097,8 +1143,10 @@ class AssetApi {
/// ///
/// * [bool] isFavorite: /// * [bool] isFavorite:
/// ///
/// * [bool] isTrashed:
///
/// * [String] key: /// * [String] key:
Future<Response> getTimeBucketsWithHttpInfo(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, String? key, }) async { Future<Response> getTimeBucketsWithHttpInfo(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/asset/time-buckets'; final path = r'/asset/time-buckets';
@ -1125,6 +1173,9 @@ class AssetApi {
if (isFavorite != null) { if (isFavorite != null) {
queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); queryParams.addAll(_queryParams('', 'isFavorite', isFavorite));
} }
if (isTrashed != null) {
queryParams.addAll(_queryParams('', 'isTrashed', isTrashed));
}
if (key != null) { if (key != null) {
queryParams.addAll(_queryParams('', 'key', key)); queryParams.addAll(_queryParams('', 'key', key));
} }
@ -1157,9 +1208,11 @@ class AssetApi {
/// ///
/// * [bool] isFavorite: /// * [bool] isFavorite:
/// ///
/// * [bool] isTrashed:
///
/// * [String] key: /// * [String] key:
Future<List<TimeBucketResponseDto>?> getTimeBuckets(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, String? key, }) async { Future<List<TimeBucketResponseDto>?> getTimeBuckets(TimeBucketSize size, { String? userId, String? albumId, String? personId, bool? isArchived, bool? isFavorite, bool? isTrashed, String? key, }) async {
final response = await getTimeBucketsWithHttpInfo(size, userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, key: key, ); final response = await getTimeBucketsWithHttpInfo(size, userId: userId, albumId: albumId, personId: personId, isArchived: isArchived, isFavorite: isFavorite, isTrashed: isTrashed, key: key, );
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
@ -1279,6 +1332,78 @@ class AssetApi {
return null; return null;
} }
/// Performs an HTTP 'POST /asset/restore' operation and returns the [Response].
/// Parameters:
///
/// * [BulkIdsDto] bulkIdsDto (required):
Future<Response> restoreAssetsWithHttpInfo(BulkIdsDto bulkIdsDto,) async {
// ignore: prefer_const_declarations
final path = r'/asset/restore';
// ignore: prefer_final_locals
Object? postBody = bulkIdsDto;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>['application/json'];
return apiClient.invokeAPI(
path,
'POST',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Parameters:
///
/// * [BulkIdsDto] bulkIdsDto (required):
Future<void> restoreAssets(BulkIdsDto bulkIdsDto,) async {
final response = await restoreAssetsWithHttpInfo(bulkIdsDto,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
}
/// Performs an HTTP 'POST /asset/trash/restore' operation and returns the [Response].
Future<Response> restoreTrashWithHttpInfo() async {
// ignore: prefer_const_declarations
final path = r'/asset/trash/restore';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'POST',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
Future<void> restoreTrash() async {
final response = await restoreTrashWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
}
/// Performs an HTTP 'POST /asset/jobs' operation and returns the [Response]. /// Performs an HTTP 'POST /asset/jobs' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///

View file

@ -199,6 +199,8 @@ class ApiClient {
return AlbumResponseDto.fromJson(value); return AlbumResponseDto.fromJson(value);
case 'AllJobStatusResponseDto': case 'AllJobStatusResponseDto':
return AllJobStatusResponseDto.fromJson(value); return AllJobStatusResponseDto.fromJson(value);
case 'AssetBulkDeleteDto':
return AssetBulkDeleteDto.fromJson(value);
case 'AssetBulkUpdateDto': case 'AssetBulkUpdateDto':
return AssetBulkUpdateDto.fromJson(value); return AssetBulkUpdateDto.fromJson(value);
case 'AssetBulkUploadCheckDto': case 'AssetBulkUploadCheckDto':
@ -271,12 +273,6 @@ class ApiClient {
return CuratedLocationsResponseDto.fromJson(value); return CuratedLocationsResponseDto.fromJson(value);
case 'CuratedObjectsResponseDto': case 'CuratedObjectsResponseDto':
return CuratedObjectsResponseDto.fromJson(value); return CuratedObjectsResponseDto.fromJson(value);
case 'DeleteAssetDto':
return DeleteAssetDto.fromJson(value);
case 'DeleteAssetResponseDto':
return DeleteAssetResponseDto.fromJson(value);
case 'DeleteAssetStatus':
return DeleteAssetStatusTypeTransformer().decode(value);
case 'DownloadArchiveInfo': case 'DownloadArchiveInfo':
return DownloadArchiveInfo.fromJson(value); return DownloadArchiveInfo.fromJson(value);
case 'DownloadInfoDto': case 'DownloadInfoDto':
@ -409,6 +405,8 @@ class ApiClient {
return SystemConfigTemplateStorageOptionDto.fromJson(value); return SystemConfigTemplateStorageOptionDto.fromJson(value);
case 'SystemConfigThumbnailDto': case 'SystemConfigThumbnailDto':
return SystemConfigThumbnailDto.fromJson(value); return SystemConfigThumbnailDto.fromJson(value);
case 'SystemConfigTrashDto':
return SystemConfigTrashDto.fromJson(value);
case 'TagResponseDto': case 'TagResponseDto':
return TagResponseDto.fromJson(value); return TagResponseDto.fromJson(value);
case 'TagTypeEnum': case 'TagTypeEnum':

View file

@ -76,9 +76,6 @@ String parameterToString(dynamic value) {
if (value is Colorspace) { if (value is Colorspace) {
return ColorspaceTypeTransformer().encode(value).toString(); return ColorspaceTypeTransformer().encode(value).toString();
} }
if (value is DeleteAssetStatus) {
return DeleteAssetStatusTypeTransformer().encode(value).toString();
}
if (value is EntityType) { if (value is EntityType) {
return EntityTypeTypeTransformer().encode(value).toString(); return EntityTypeTypeTransformer().encode(value).toString();
} }

View file

@ -10,40 +10,57 @@
part of openapi.api; part of openapi.api;
class DeleteAssetDto { class AssetBulkDeleteDto {
/// Returns a new [DeleteAssetDto] instance. /// Returns a new [AssetBulkDeleteDto] instance.
DeleteAssetDto({ AssetBulkDeleteDto({
this.force,
this.ids = const [], this.ids = const [],
}); });
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? force;
List<String> ids; List<String> ids;
@override @override
bool operator ==(Object other) => identical(this, other) || other is DeleteAssetDto && bool operator ==(Object other) => identical(this, other) || other is AssetBulkDeleteDto &&
other.force == force &&
other.ids == ids; other.ids == ids;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(force == null ? 0 : force!.hashCode) +
(ids.hashCode); (ids.hashCode);
@override @override
String toString() => 'DeleteAssetDto[ids=$ids]'; String toString() => 'AssetBulkDeleteDto[force=$force, ids=$ids]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
if (this.force != null) {
json[r'force'] = this.force;
} else {
// json[r'force'] = null;
}
json[r'ids'] = this.ids; json[r'ids'] = this.ids;
return json; return json;
} }
/// Returns a new [DeleteAssetDto] instance and imports its values from /// Returns a new [AssetBulkDeleteDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise. /// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods // ignore: prefer_constructors_over_static_methods
static DeleteAssetDto? fromJson(dynamic value) { static AssetBulkDeleteDto? fromJson(dynamic value) {
if (value is Map) { if (value is Map) {
final json = value.cast<String, dynamic>(); final json = value.cast<String, dynamic>();
return DeleteAssetDto( return AssetBulkDeleteDto(
force: mapValueOfType<bool>(json, r'force'),
ids: json[r'ids'] is List ids: json[r'ids'] is List
? (json[r'ids'] as List).cast<String>() ? (json[r'ids'] as List).cast<String>()
: const [], : const [],
@ -52,11 +69,11 @@ class DeleteAssetDto {
return null; return null;
} }
static List<DeleteAssetDto> listFromJson(dynamic json, {bool growable = false,}) { static List<AssetBulkDeleteDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <DeleteAssetDto>[]; final result = <AssetBulkDeleteDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
final value = DeleteAssetDto.fromJson(row); final value = AssetBulkDeleteDto.fromJson(row);
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@ -65,12 +82,12 @@ class DeleteAssetDto {
return result.toList(growable: growable); return result.toList(growable: growable);
} }
static Map<String, DeleteAssetDto> mapFromJson(dynamic json) { static Map<String, AssetBulkDeleteDto> mapFromJson(dynamic json) {
final map = <String, DeleteAssetDto>{}; final map = <String, AssetBulkDeleteDto>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) { for (final entry in json.entries) {
final value = DeleteAssetDto.fromJson(entry.value); final value = AssetBulkDeleteDto.fromJson(entry.value);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@ -79,14 +96,14 @@ class DeleteAssetDto {
return map; return map;
} }
// maps a json object with a list of DeleteAssetDto-objects as value to a dart map // maps a json object with a list of AssetBulkDeleteDto-objects as value to a dart map
static Map<String, List<DeleteAssetDto>> mapListFromJson(dynamic json, {bool growable = false,}) { static Map<String, List<AssetBulkDeleteDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<DeleteAssetDto>>{}; final map = <String, List<AssetBulkDeleteDto>>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments // ignore: parameter_assignments
json = json.cast<String, dynamic>(); json = json.cast<String, dynamic>();
for (final entry in json.entries) { for (final entry in json.entries) {
map[entry.key] = DeleteAssetDto.listFromJson(entry.value, growable: growable,); map[entry.key] = AssetBulkDeleteDto.listFromJson(entry.value, growable: growable,);
} }
} }
return map; return map;

View file

@ -26,8 +26,10 @@ class AssetResponseDto {
required this.isFavorite, required this.isFavorite,
required this.isOffline, required this.isOffline,
required this.isReadOnly, required this.isReadOnly,
required this.isTrashed,
required this.libraryId, required this.libraryId,
this.livePhotoVideoId, this.livePhotoVideoId,
required this.localDateTime,
required this.originalFileName, required this.originalFileName,
required this.originalPath, required this.originalPath,
this.owner, this.owner,
@ -74,10 +76,14 @@ class AssetResponseDto {
bool isReadOnly; bool isReadOnly;
bool isTrashed;
String libraryId; String libraryId;
String? livePhotoVideoId; String? livePhotoVideoId;
DateTime localDateTime;
String originalFileName; String originalFileName;
String originalPath; String originalPath;
@ -128,8 +134,10 @@ class AssetResponseDto {
other.isFavorite == isFavorite && other.isFavorite == isFavorite &&
other.isOffline == isOffline && other.isOffline == isOffline &&
other.isReadOnly == isReadOnly && other.isReadOnly == isReadOnly &&
other.isTrashed == isTrashed &&
other.libraryId == libraryId && other.libraryId == libraryId &&
other.livePhotoVideoId == livePhotoVideoId && other.livePhotoVideoId == livePhotoVideoId &&
other.localDateTime == localDateTime &&
other.originalFileName == originalFileName && other.originalFileName == originalFileName &&
other.originalPath == originalPath && other.originalPath == originalPath &&
other.owner == owner && other.owner == owner &&
@ -158,8 +166,10 @@ class AssetResponseDto {
(isFavorite.hashCode) + (isFavorite.hashCode) +
(isOffline.hashCode) + (isOffline.hashCode) +
(isReadOnly.hashCode) + (isReadOnly.hashCode) +
(isTrashed.hashCode) +
(libraryId.hashCode) + (libraryId.hashCode) +
(livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) +
(localDateTime.hashCode) +
(originalFileName.hashCode) + (originalFileName.hashCode) +
(originalPath.hashCode) + (originalPath.hashCode) +
(owner == null ? 0 : owner!.hashCode) + (owner == null ? 0 : owner!.hashCode) +
@ -173,7 +183,7 @@ class AssetResponseDto {
(updatedAt.hashCode); (updatedAt.hashCode);
@override @override
String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isArchived=$isArchived, isExternal=$isExternal, isFavorite=$isFavorite, isOffline=$isOffline, isReadOnly=$isReadOnly, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]'; String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isArchived=$isArchived, isExternal=$isExternal, isFavorite=$isFavorite, isOffline=$isOffline, isReadOnly=$isReadOnly, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -194,12 +204,14 @@ class AssetResponseDto {
json[r'isFavorite'] = this.isFavorite; json[r'isFavorite'] = this.isFavorite;
json[r'isOffline'] = this.isOffline; json[r'isOffline'] = this.isOffline;
json[r'isReadOnly'] = this.isReadOnly; json[r'isReadOnly'] = this.isReadOnly;
json[r'isTrashed'] = this.isTrashed;
json[r'libraryId'] = this.libraryId; json[r'libraryId'] = this.libraryId;
if (this.livePhotoVideoId != null) { if (this.livePhotoVideoId != null) {
json[r'livePhotoVideoId'] = this.livePhotoVideoId; json[r'livePhotoVideoId'] = this.livePhotoVideoId;
} else { } else {
// json[r'livePhotoVideoId'] = null; // json[r'livePhotoVideoId'] = null;
} }
json[r'localDateTime'] = this.localDateTime.toUtc().toIso8601String();
json[r'originalFileName'] = this.originalFileName; json[r'originalFileName'] = this.originalFileName;
json[r'originalPath'] = this.originalPath; json[r'originalPath'] = this.originalPath;
if (this.owner != null) { if (this.owner != null) {
@ -247,8 +259,10 @@ class AssetResponseDto {
isFavorite: mapValueOfType<bool>(json, r'isFavorite')!, isFavorite: mapValueOfType<bool>(json, r'isFavorite')!,
isOffline: mapValueOfType<bool>(json, r'isOffline')!, isOffline: mapValueOfType<bool>(json, r'isOffline')!,
isReadOnly: mapValueOfType<bool>(json, r'isReadOnly')!, isReadOnly: mapValueOfType<bool>(json, r'isReadOnly')!,
isTrashed: mapValueOfType<bool>(json, r'isTrashed')!,
libraryId: mapValueOfType<String>(json, r'libraryId')!, libraryId: mapValueOfType<String>(json, r'libraryId')!,
livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'), livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'),
localDateTime: mapDateTime(json, r'localDateTime', '')!,
originalFileName: mapValueOfType<String>(json, r'originalFileName')!, originalFileName: mapValueOfType<String>(json, r'originalFileName')!,
originalPath: mapValueOfType<String>(json, r'originalPath')!, originalPath: mapValueOfType<String>(json, r'originalPath')!,
owner: UserResponseDto.fromJson(json[r'owner']), owner: UserResponseDto.fromJson(json[r'owner']),
@ -319,7 +333,9 @@ class AssetResponseDto {
'isFavorite', 'isFavorite',
'isOffline', 'isOffline',
'isReadOnly', 'isReadOnly',
'isTrashed',
'libraryId', 'libraryId',
'localDateTime',
'originalFileName', 'originalFileName',
'originalPath', 'originalPath',
'ownerId', 'ownerId',

View file

@ -1,85 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class DeleteAssetStatus {
/// Instantiate a new enum with the provided [value].
const DeleteAssetStatus._(this.value);
/// The underlying value of this enum member.
final String value;
@override
String toString() => value;
String toJson() => value;
static const SUCCESS = DeleteAssetStatus._(r'SUCCESS');
static const FAILED = DeleteAssetStatus._(r'FAILED');
/// List of all possible values in this [enum][DeleteAssetStatus].
static const values = <DeleteAssetStatus>[
SUCCESS,
FAILED,
];
static DeleteAssetStatus? fromJson(dynamic value) => DeleteAssetStatusTypeTransformer().decode(value);
static List<DeleteAssetStatus>? listFromJson(dynamic json, {bool growable = false,}) {
final result = <DeleteAssetStatus>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = DeleteAssetStatus.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
}
/// Transformation class that can [encode] an instance of [DeleteAssetStatus] to String,
/// and [decode] dynamic data back to [DeleteAssetStatus].
class DeleteAssetStatusTypeTransformer {
factory DeleteAssetStatusTypeTransformer() => _instance ??= const DeleteAssetStatusTypeTransformer._();
const DeleteAssetStatusTypeTransformer._();
String encode(DeleteAssetStatus data) => data.value;
/// Decodes a [dynamic value][data] to a DeleteAssetStatus.
///
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
///
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
/// and users are still using an old app with the old code.
DeleteAssetStatus? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'SUCCESS': return DeleteAssetStatus.SUCCESS;
case r'FAILED': return DeleteAssetStatus.FAILED;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
}
}
}
return null;
}
/// Singleton [DeleteAssetStatusTypeTransformer] instance.
static DeleteAssetStatusTypeTransformer? _instance;
}

View file

@ -16,6 +16,7 @@ class ServerConfigDto {
required this.loginPageMessage, required this.loginPageMessage,
required this.mapTileUrl, required this.mapTileUrl,
required this.oauthButtonText, required this.oauthButtonText,
required this.trashDays,
}); });
String loginPageMessage; String loginPageMessage;
@ -24,27 +25,32 @@ class ServerConfigDto {
String oauthButtonText; String oauthButtonText;
int trashDays;
@override @override
bool operator ==(Object other) => identical(this, other) || other is ServerConfigDto && bool operator ==(Object other) => identical(this, other) || other is ServerConfigDto &&
other.loginPageMessage == loginPageMessage && other.loginPageMessage == loginPageMessage &&
other.mapTileUrl == mapTileUrl && other.mapTileUrl == mapTileUrl &&
other.oauthButtonText == oauthButtonText; other.oauthButtonText == oauthButtonText &&
other.trashDays == trashDays;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(loginPageMessage.hashCode) + (loginPageMessage.hashCode) +
(mapTileUrl.hashCode) + (mapTileUrl.hashCode) +
(oauthButtonText.hashCode); (oauthButtonText.hashCode) +
(trashDays.hashCode);
@override @override
String toString() => 'ServerConfigDto[loginPageMessage=$loginPageMessage, mapTileUrl=$mapTileUrl, oauthButtonText=$oauthButtonText]'; String toString() => 'ServerConfigDto[loginPageMessage=$loginPageMessage, mapTileUrl=$mapTileUrl, oauthButtonText=$oauthButtonText, trashDays=$trashDays]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
json[r'loginPageMessage'] = this.loginPageMessage; json[r'loginPageMessage'] = this.loginPageMessage;
json[r'mapTileUrl'] = this.mapTileUrl; json[r'mapTileUrl'] = this.mapTileUrl;
json[r'oauthButtonText'] = this.oauthButtonText; json[r'oauthButtonText'] = this.oauthButtonText;
json[r'trashDays'] = this.trashDays;
return json; return json;
} }
@ -59,6 +65,7 @@ class ServerConfigDto {
loginPageMessage: mapValueOfType<String>(json, r'loginPageMessage')!, loginPageMessage: mapValueOfType<String>(json, r'loginPageMessage')!,
mapTileUrl: mapValueOfType<String>(json, r'mapTileUrl')!, mapTileUrl: mapValueOfType<String>(json, r'mapTileUrl')!,
oauthButtonText: mapValueOfType<String>(json, r'oauthButtonText')!, oauthButtonText: mapValueOfType<String>(json, r'oauthButtonText')!,
trashDays: mapValueOfType<int>(json, r'trashDays')!,
); );
} }
return null; return null;
@ -109,6 +116,7 @@ class ServerConfigDto {
'loginPageMessage', 'loginPageMessage',
'mapTileUrl', 'mapTileUrl',
'oauthButtonText', 'oauthButtonText',
'trashDays',
}; };
} }

View file

@ -24,6 +24,7 @@ class ServerFeaturesDto {
required this.search, required this.search,
required this.sidecar, required this.sidecar,
required this.tagImage, required this.tagImage,
required this.trash,
}); });
bool clipEncode; bool clipEncode;
@ -48,6 +49,8 @@ class ServerFeaturesDto {
bool tagImage; bool tagImage;
bool trash;
@override @override
bool operator ==(Object other) => identical(this, other) || other is ServerFeaturesDto && bool operator ==(Object other) => identical(this, other) || other is ServerFeaturesDto &&
other.clipEncode == clipEncode && other.clipEncode == clipEncode &&
@ -60,7 +63,8 @@ class ServerFeaturesDto {
other.reverseGeocoding == reverseGeocoding && other.reverseGeocoding == reverseGeocoding &&
other.search == search && other.search == search &&
other.sidecar == sidecar && other.sidecar == sidecar &&
other.tagImage == tagImage; other.tagImage == tagImage &&
other.trash == trash;
@override @override
int get hashCode => int get hashCode =>
@ -75,10 +79,11 @@ class ServerFeaturesDto {
(reverseGeocoding.hashCode) + (reverseGeocoding.hashCode) +
(search.hashCode) + (search.hashCode) +
(sidecar.hashCode) + (sidecar.hashCode) +
(tagImage.hashCode); (tagImage.hashCode) +
(trash.hashCode);
@override @override
String toString() => 'ServerFeaturesDto[clipEncode=$clipEncode, configFile=$configFile, facialRecognition=$facialRecognition, map=$map, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, search=$search, sidecar=$sidecar, tagImage=$tagImage]'; String toString() => 'ServerFeaturesDto[clipEncode=$clipEncode, configFile=$configFile, facialRecognition=$facialRecognition, map=$map, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, search=$search, sidecar=$sidecar, tagImage=$tagImage, trash=$trash]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -93,6 +98,7 @@ class ServerFeaturesDto {
json[r'search'] = this.search; json[r'search'] = this.search;
json[r'sidecar'] = this.sidecar; json[r'sidecar'] = this.sidecar;
json[r'tagImage'] = this.tagImage; json[r'tagImage'] = this.tagImage;
json[r'trash'] = this.trash;
return json; return json;
} }
@ -115,6 +121,7 @@ class ServerFeaturesDto {
search: mapValueOfType<bool>(json, r'search')!, search: mapValueOfType<bool>(json, r'search')!,
sidecar: mapValueOfType<bool>(json, r'sidecar')!, sidecar: mapValueOfType<bool>(json, r'sidecar')!,
tagImage: mapValueOfType<bool>(json, r'tagImage')!, tagImage: mapValueOfType<bool>(json, r'tagImage')!,
trash: mapValueOfType<bool>(json, r'trash')!,
); );
} }
return null; return null;
@ -173,6 +180,7 @@ class ServerFeaturesDto {
'search', 'search',
'sidecar', 'sidecar',
'tagImage', 'tagImage',
'trash',
}; };
} }

View file

@ -22,6 +22,7 @@ class SystemConfigDto {
required this.reverseGeocoding, required this.reverseGeocoding,
required this.storageTemplate, required this.storageTemplate,
required this.thumbnail, required this.thumbnail,
required this.trash,
}); });
SystemConfigFFmpegDto ffmpeg; SystemConfigFFmpegDto ffmpeg;
@ -42,6 +43,8 @@ class SystemConfigDto {
SystemConfigThumbnailDto thumbnail; SystemConfigThumbnailDto thumbnail;
SystemConfigTrashDto trash;
@override @override
bool operator ==(Object other) => identical(this, other) || other is SystemConfigDto && bool operator ==(Object other) => identical(this, other) || other is SystemConfigDto &&
other.ffmpeg == ffmpeg && other.ffmpeg == ffmpeg &&
@ -52,7 +55,8 @@ class SystemConfigDto {
other.passwordLogin == passwordLogin && other.passwordLogin == passwordLogin &&
other.reverseGeocoding == reverseGeocoding && other.reverseGeocoding == reverseGeocoding &&
other.storageTemplate == storageTemplate && other.storageTemplate == storageTemplate &&
other.thumbnail == thumbnail; other.thumbnail == thumbnail &&
other.trash == trash;
@override @override
int get hashCode => int get hashCode =>
@ -65,10 +69,11 @@ class SystemConfigDto {
(passwordLogin.hashCode) + (passwordLogin.hashCode) +
(reverseGeocoding.hashCode) + (reverseGeocoding.hashCode) +
(storageTemplate.hashCode) + (storageTemplate.hashCode) +
(thumbnail.hashCode); (thumbnail.hashCode) +
(trash.hashCode);
@override @override
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, machineLearning=$machineLearning, map=$map, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, storageTemplate=$storageTemplate, thumbnail=$thumbnail]'; String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, machineLearning=$machineLearning, map=$map, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, storageTemplate=$storageTemplate, thumbnail=$thumbnail, trash=$trash]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -81,6 +86,7 @@ class SystemConfigDto {
json[r'reverseGeocoding'] = this.reverseGeocoding; json[r'reverseGeocoding'] = this.reverseGeocoding;
json[r'storageTemplate'] = this.storageTemplate; json[r'storageTemplate'] = this.storageTemplate;
json[r'thumbnail'] = this.thumbnail; json[r'thumbnail'] = this.thumbnail;
json[r'trash'] = this.trash;
return json; return json;
} }
@ -101,6 +107,7 @@ class SystemConfigDto {
reverseGeocoding: SystemConfigReverseGeocodingDto.fromJson(json[r'reverseGeocoding'])!, reverseGeocoding: SystemConfigReverseGeocodingDto.fromJson(json[r'reverseGeocoding'])!,
storageTemplate: SystemConfigStorageTemplateDto.fromJson(json[r'storageTemplate'])!, storageTemplate: SystemConfigStorageTemplateDto.fromJson(json[r'storageTemplate'])!,
thumbnail: SystemConfigThumbnailDto.fromJson(json[r'thumbnail'])!, thumbnail: SystemConfigThumbnailDto.fromJson(json[r'thumbnail'])!,
trash: SystemConfigTrashDto.fromJson(json[r'trash'])!,
); );
} }
return null; return null;
@ -157,6 +164,7 @@ class SystemConfigDto {
'reverseGeocoding', 'reverseGeocoding',
'storageTemplate', 'storageTemplate',
'thumbnail', 'thumbnail',
'trash',
}; };
} }

View file

@ -19,6 +19,7 @@ class SystemConfigTemplateStorageOptionDto {
this.monthOptions = const [], this.monthOptions = const [],
this.presetOptions = const [], this.presetOptions = const [],
this.secondOptions = const [], this.secondOptions = const [],
this.weekOptions = const [],
this.yearOptions = const [], this.yearOptions = const [],
}); });
@ -34,6 +35,8 @@ class SystemConfigTemplateStorageOptionDto {
List<String> secondOptions; List<String> secondOptions;
List<String> weekOptions;
List<String> yearOptions; List<String> yearOptions;
@override @override
@ -44,6 +47,7 @@ class SystemConfigTemplateStorageOptionDto {
other.monthOptions == monthOptions && other.monthOptions == monthOptions &&
other.presetOptions == presetOptions && other.presetOptions == presetOptions &&
other.secondOptions == secondOptions && other.secondOptions == secondOptions &&
other.weekOptions == weekOptions &&
other.yearOptions == yearOptions; other.yearOptions == yearOptions;
@override @override
@ -55,10 +59,11 @@ class SystemConfigTemplateStorageOptionDto {
(monthOptions.hashCode) + (monthOptions.hashCode) +
(presetOptions.hashCode) + (presetOptions.hashCode) +
(secondOptions.hashCode) + (secondOptions.hashCode) +
(weekOptions.hashCode) +
(yearOptions.hashCode); (yearOptions.hashCode);
@override @override
String toString() => 'SystemConfigTemplateStorageOptionDto[dayOptions=$dayOptions, hourOptions=$hourOptions, minuteOptions=$minuteOptions, monthOptions=$monthOptions, presetOptions=$presetOptions, secondOptions=$secondOptions, yearOptions=$yearOptions]'; String toString() => 'SystemConfigTemplateStorageOptionDto[dayOptions=$dayOptions, hourOptions=$hourOptions, minuteOptions=$minuteOptions, monthOptions=$monthOptions, presetOptions=$presetOptions, secondOptions=$secondOptions, weekOptions=$weekOptions, yearOptions=$yearOptions]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -68,6 +73,7 @@ class SystemConfigTemplateStorageOptionDto {
json[r'monthOptions'] = this.monthOptions; json[r'monthOptions'] = this.monthOptions;
json[r'presetOptions'] = this.presetOptions; json[r'presetOptions'] = this.presetOptions;
json[r'secondOptions'] = this.secondOptions; json[r'secondOptions'] = this.secondOptions;
json[r'weekOptions'] = this.weekOptions;
json[r'yearOptions'] = this.yearOptions; json[r'yearOptions'] = this.yearOptions;
return json; return json;
} }
@ -98,6 +104,9 @@ class SystemConfigTemplateStorageOptionDto {
secondOptions: json[r'secondOptions'] is List secondOptions: json[r'secondOptions'] is List
? (json[r'secondOptions'] as List).cast<String>() ? (json[r'secondOptions'] as List).cast<String>()
: const [], : const [],
weekOptions: json[r'weekOptions'] is List
? (json[r'weekOptions'] as List).cast<String>()
: const [],
yearOptions: json[r'yearOptions'] is List yearOptions: json[r'yearOptions'] is List
? (json[r'yearOptions'] as List).cast<String>() ? (json[r'yearOptions'] as List).cast<String>()
: const [], : const [],
@ -154,6 +163,7 @@ class SystemConfigTemplateStorageOptionDto {
'monthOptions', 'monthOptions',
'presetOptions', 'presetOptions',
'secondOptions', 'secondOptions',
'weekOptions',
'yearOptions', 'yearOptions',
}; };
} }

View file

@ -10,58 +10,58 @@
part of openapi.api; part of openapi.api;
class DeleteAssetResponseDto { class SystemConfigTrashDto {
/// Returns a new [DeleteAssetResponseDto] instance. /// Returns a new [SystemConfigTrashDto] instance.
DeleteAssetResponseDto({ SystemConfigTrashDto({
required this.id, required this.days,
required this.status, required this.enabled,
}); });
String id; int days;
DeleteAssetStatus status; bool enabled;
@override @override
bool operator ==(Object other) => identical(this, other) || other is DeleteAssetResponseDto && bool operator ==(Object other) => identical(this, other) || other is SystemConfigTrashDto &&
other.id == id && other.days == days &&
other.status == status; other.enabled == enabled;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(id.hashCode) + (days.hashCode) +
(status.hashCode); (enabled.hashCode);
@override @override
String toString() => 'DeleteAssetResponseDto[id=$id, status=$status]'; String toString() => 'SystemConfigTrashDto[days=$days, enabled=$enabled]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
json[r'id'] = this.id; json[r'days'] = this.days;
json[r'status'] = this.status; json[r'enabled'] = this.enabled;
return json; return json;
} }
/// Returns a new [DeleteAssetResponseDto] instance and imports its values from /// Returns a new [SystemConfigTrashDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise. /// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods // ignore: prefer_constructors_over_static_methods
static DeleteAssetResponseDto? fromJson(dynamic value) { static SystemConfigTrashDto? fromJson(dynamic value) {
if (value is Map) { if (value is Map) {
final json = value.cast<String, dynamic>(); final json = value.cast<String, dynamic>();
return DeleteAssetResponseDto( return SystemConfigTrashDto(
id: mapValueOfType<String>(json, r'id')!, days: mapValueOfType<int>(json, r'days')!,
status: DeleteAssetStatus.fromJson(json[r'status'])!, enabled: mapValueOfType<bool>(json, r'enabled')!,
); );
} }
return null; return null;
} }
static List<DeleteAssetResponseDto> listFromJson(dynamic json, {bool growable = false,}) { static List<SystemConfigTrashDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <DeleteAssetResponseDto>[]; final result = <SystemConfigTrashDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
final value = DeleteAssetResponseDto.fromJson(row); final value = SystemConfigTrashDto.fromJson(row);
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@ -70,12 +70,12 @@ class DeleteAssetResponseDto {
return result.toList(growable: growable); return result.toList(growable: growable);
} }
static Map<String, DeleteAssetResponseDto> mapFromJson(dynamic json) { static Map<String, SystemConfigTrashDto> mapFromJson(dynamic json) {
final map = <String, DeleteAssetResponseDto>{}; final map = <String, SystemConfigTrashDto>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) { for (final entry in json.entries) {
final value = DeleteAssetResponseDto.fromJson(entry.value); final value = SystemConfigTrashDto.fromJson(entry.value);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@ -84,14 +84,14 @@ class DeleteAssetResponseDto {
return map; return map;
} }
// maps a json object with a list of DeleteAssetResponseDto-objects as value to a dart map // maps a json object with a list of SystemConfigTrashDto-objects as value to a dart map
static Map<String, List<DeleteAssetResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { static Map<String, List<SystemConfigTrashDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<DeleteAssetResponseDto>>{}; final map = <String, List<SystemConfigTrashDto>>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments // ignore: parameter_assignments
json = json.cast<String, dynamic>(); json = json.cast<String, dynamic>();
for (final entry in json.entries) { for (final entry in json.entries) {
map[entry.key] = DeleteAssetResponseDto.listFromJson(entry.value, growable: growable,); map[entry.key] = SystemConfigTrashDto.listFromJson(entry.value, growable: growable,);
} }
} }
return map; return map;
@ -99,8 +99,8 @@ class DeleteAssetResponseDto {
/// The list of required keys that must be present in a JSON. /// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{ static const requiredKeys = <String>{
'id', 'days',
'status', 'enabled',
}; };
} }

View file

@ -38,8 +38,8 @@ void main() {
// TODO // TODO
}); });
//Future<List<DeleteAssetResponseDto>> deleteAsset(DeleteAssetDto deleteAssetDto) async //Future deleteAssets(AssetBulkDeleteDto assetBulkDeleteDto) async
test('test deleteAsset', () async { test('test deleteAssets', () async {
// TODO // TODO
}); });
@ -53,6 +53,11 @@ void main() {
// TODO // TODO
}); });
//Future emptyTrash() async
test('test emptyTrash', () async {
// TODO
});
// Get all AssetEntity belong to the user // Get all AssetEntity belong to the user
// //
//Future<List<AssetResponseDto>> getAllAssets({ String userId, bool isFavorite, bool isArchived, num skip, DateTime updatedAfter, String ifNoneMatch }) async //Future<List<AssetResponseDto>> getAllAssets({ String userId, bool isFavorite, bool isArchived, num skip, DateTime updatedAfter, String ifNoneMatch }) async
@ -72,7 +77,7 @@ void main() {
// TODO // TODO
}); });
//Future<AssetStatsResponseDto> getAssetStats({ bool isArchived, bool isFavorite }) async //Future<AssetStatsResponseDto> getAssetStats({ bool isArchived, bool isFavorite, bool isTrashed }) async
test('test getAssetStats', () async { test('test getAssetStats', () async {
// TODO // TODO
}); });
@ -82,7 +87,7 @@ void main() {
// TODO // TODO
}); });
//Future<List<AssetResponseDto>> getByTimeBucket(TimeBucketSize size, String timeBucket, { String userId, String albumId, String personId, bool isArchived, bool isFavorite, String key }) async //Future<List<AssetResponseDto>> getByTimeBucket(TimeBucketSize size, String timeBucket, { String userId, String albumId, String personId, bool isArchived, bool isFavorite, bool isTrashed, String key }) async
test('test getByTimeBucket', () async { test('test getByTimeBucket', () async {
// TODO // TODO
}); });
@ -102,12 +107,12 @@ void main() {
// TODO // TODO
}); });
//Future<List<MapMarkerResponseDto>> getMapMarkers({ bool isFavorite, DateTime fileCreatedAfter, DateTime fileCreatedBefore }) async //Future<List<MapMarkerResponseDto>> getMapMarkers({ bool isArchived, bool isFavorite, DateTime fileCreatedAfter, DateTime fileCreatedBefore }) async
test('test getMapMarkers', () async { test('test getMapMarkers', () async {
// TODO // TODO
}); });
//Future<List<MemoryLaneResponseDto>> getMemoryLane(DateTime timestamp) async //Future<List<MemoryLaneResponseDto>> getMemoryLane(int day, int month) async
test('test getMemoryLane', () async { test('test getMemoryLane', () async {
// TODO // TODO
}); });
@ -117,7 +122,7 @@ void main() {
// TODO // TODO
}); });
//Future<List<TimeBucketResponseDto>> getTimeBuckets(TimeBucketSize size, { String userId, String albumId, String personId, bool isArchived, bool isFavorite, String key }) async //Future<List<TimeBucketResponseDto>> getTimeBuckets(TimeBucketSize size, { String userId, String albumId, String personId, bool isArchived, bool isFavorite, bool isTrashed, String key }) async
test('test getTimeBuckets', () async { test('test getTimeBuckets', () async {
// TODO // TODO
}); });
@ -134,6 +139,16 @@ void main() {
// TODO // TODO
}); });
//Future restoreAssets(BulkIdsDto bulkIdsDto) async
test('test restoreAssets', () async {
// TODO
});
//Future restoreTrash() async
test('test restoreTrash', () async {
// TODO
});
//Future runAssetJobs(AssetJobsDto assetJobsDto) async //Future runAssetJobs(AssetJobsDto assetJobsDto) async
test('test runAssetJobs', () async { test('test runAssetJobs', () async {
// TODO // TODO

View file

@ -11,11 +11,16 @@
import 'package:openapi/api.dart'; import 'package:openapi/api.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
// tests for DeleteAssetDto // tests for AssetBulkDeleteDto
void main() { void main() {
// final instance = DeleteAssetDto(); // final instance = AssetBulkDeleteDto();
group('test AssetBulkDeleteDto', () {
// bool force
test('to test the property `force`', () async {
// TODO
});
group('test DeleteAssetDto', () {
// List<String> ids (default value: const []) // List<String> ids (default value: const [])
test('to test the property `ids`', () async { test('to test the property `ids`', () async {
// TODO // TODO

View file

@ -82,6 +82,11 @@ void main() {
// TODO // TODO
}); });
// bool isTrashed
test('to test the property `isTrashed`', () async {
// TODO
});
// String libraryId // String libraryId
test('to test the property `libraryId`', () async { test('to test the property `libraryId`', () async {
// TODO // TODO
@ -92,6 +97,11 @@ void main() {
// TODO // TODO
}); });
// DateTime localDateTime
test('to test the property `localDateTime`', () async {
// TODO
});
// String originalFileName // String originalFileName
test('to test the property `originalFileName`', () async { test('to test the property `originalFileName`', () async {
// TODO // TODO

View file

@ -1,21 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
import 'package:openapi/api.dart';
import 'package:test/test.dart';
// tests for DeleteAssetStatus
void main() {
group('test DeleteAssetStatus', () {
});
}

View file

@ -31,6 +31,11 @@ void main() {
// TODO // TODO
}); });
// int trashDays
test('to test the property `trashDays`', () async {
// TODO
});
}); });

View file

@ -71,6 +71,11 @@ void main() {
// TODO // TODO
}); });
// bool trash
test('to test the property `trash`', () async {
// TODO
});
}); });

View file

@ -61,6 +61,11 @@ void main() {
// TODO // TODO
}); });
// SystemConfigTrashDto trash
test('to test the property `trash`', () async {
// TODO
});
}); });

View file

@ -46,6 +46,11 @@ void main() {
// TODO // TODO
}); });
// List<String> weekOptions (default value: const [])
test('to test the property `weekOptions`', () async {
// TODO
});
// List<String> yearOptions (default value: const []) // List<String> yearOptions (default value: const [])
test('to test the property `yearOptions`', () async { test('to test the property `yearOptions`', () async {
// TODO // TODO

View file

@ -11,18 +11,18 @@
import 'package:openapi/api.dart'; import 'package:openapi/api.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
// tests for DeleteAssetResponseDto // tests for SystemConfigTrashDto
void main() { void main() {
// final instance = DeleteAssetResponseDto(); // final instance = SystemConfigTrashDto();
group('test DeleteAssetResponseDto', () { group('test SystemConfigTrashDto', () {
// String id // int days
test('to test the property `id`', () async { test('to test the property `days`', () async {
// TODO // TODO
}); });
// DeleteAssetStatus status // bool enabled
test('to test the property `status`', () async { test('to test the property `enabled`', () async {
// TODO // TODO
}); });

View file

@ -2,7 +2,7 @@ name: immich_mobile
description: Immich - selfhosted backup media file on mobile phone description: Immich - selfhosted backup media file on mobile phone
publish_to: "none" publish_to: "none"
version: 1.79.1+103 version: 1.81.1+105
isar_version: &isar_version 3.1.0+1 isar_version: &isar_version 3.1.0+1
environment: environment:

View file

@ -24,6 +24,7 @@ void main() {
fileName: '', fileName: '',
isFavorite: false, isFavorite: false,
isArchived: false, isArchived: false,
isTrashed: false,
), ),
); );
} }

View file

@ -34,6 +34,7 @@ void main() {
fileName: localId ?? remoteId ?? "", fileName: localId ?? remoteId ?? "",
isFavorite: false, isFavorite: false,
isArchived: false, isArchived: false,
isTrashed: false,
); );
} }

View file

@ -1,4 +1,4 @@
FROM node:18-bookworm@sha256:c85dc4392f44f5de1d0d72dd20a088a542734445f99bed7aa8ac895c706d370d as builder FROM node:20.8-bookworm as builder
WORKDIR /usr/src/app WORKDIR /usr/src/app
@ -29,7 +29,7 @@ FROM builder as prod
RUN npm run build RUN npm run build
RUN npm prune --omit=dev --omit=optional RUN npm prune --omit=dev --omit=optional
FROM node:18-bookworm-slim@sha256:a0cca98f2896135d4c0386922211c1f90f98f27a58b8f2c07850d0fbe1c2104e FROM node:20.8-bookworm
ENV NODE_ENV=production ENV NODE_ENV=production

Some files were not shown because too many files have changed in this diff Show more