Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2bad1d553 | ||
|
|
3e31ad51be | ||
|
|
fbeb4664f7 | ||
|
|
4ee8a30a5a | ||
|
|
6243bce46c | ||
|
|
98b72fdb9b | ||
|
|
5e901e4d21 | ||
|
|
66490d5db4 | ||
|
|
2b839088c7 | ||
|
|
28d3d3e679 | ||
|
|
2de30e34f4 | ||
|
|
2ff71b0d27 | ||
|
|
cdb45364c3 | ||
|
|
8ba338fbe1 | ||
|
|
ce84f9c755 | ||
|
|
d1e74a28d9 | ||
|
|
78a2a9e666 | ||
|
|
53f5643994 | ||
|
|
4ee634766d | ||
|
|
bab739efbd | ||
|
|
8568ec838a | ||
|
|
4cbb18aabc | ||
|
|
3fb60aca4f | ||
|
|
19bbdebdf7 | ||
|
|
bc66b1a556 | ||
|
|
4762fd83d4 | ||
|
|
c27c12d975 | ||
|
|
0abbd85134 | ||
|
|
af1f00dff9 | ||
|
|
35b4c9d375 | ||
|
|
74da15e20d | ||
|
|
efc7fdb669 | ||
|
|
a75f368d5b |
2
.github/workflows/build-mobile.yml
vendored
2
.github/workflows/build-mobile.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: "3.10.5"
|
||||
flutter-version: "3.13.0"
|
||||
cache: true
|
||||
|
||||
- name: Create the Keystore
|
||||
|
||||
2
.github/workflows/static_analysis.yml
vendored
2
.github/workflows/static_analysis.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: "3.10.5"
|
||||
flutter-version: "3.13.0"
|
||||
|
||||
- name: Install dependencies
|
||||
run: dart pub get
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -149,7 +149,7 @@ jobs:
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: "3.10.5"
|
||||
flutter-version: "3.13.0"
|
||||
- name: Run tests
|
||||
working-directory: ./mobile
|
||||
run: flutter test -j 1
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</p>
|
||||
<h3 align="center">Immich - 高性能的自托管照片和视频备份方案</h3>
|
||||
<p align="center">
|
||||
请注意: 此README不是由Immich团队维护, 这意味着它在某一时间点不会被更新,因为我们是依靠贡献者来更新的。感谢理解。
|
||||
请注意: 此 README 不是由 Immich 团队维护, 而是依靠贡献者来更新的,这意味着它可能并不会被及时更新。感谢理解。
|
||||
</p>
|
||||
<br/>
|
||||
<a href="https://immich.app">
|
||||
@@ -31,29 +31,31 @@
|
||||
|
||||
## 免责声明
|
||||
|
||||
- ⚠️ 本项目正在 **非常活跃** 的开发中。
|
||||
- ⚠️ 可能存在bug或者重大变更。
|
||||
- ⚠️ **不要把本软件作为你存储照片或视频的唯一方式!**
|
||||
- ⚠️ 本项目正在 **非常活跃** 地开发中。
|
||||
- ⚠️ 可能存在 bug 或者随时有重大变更。
|
||||
- ⚠️ **不要把本软件作为您存储照片或视频的唯一方式。**
|
||||
- ⚠️ 为了您宝贵的照片与视频,始终遵守 [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) 备份方案!
|
||||
|
||||
## 目录
|
||||
|
||||
- [官方文档](https://immich.app/docs/overview/introduction)
|
||||
- [官方文档](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)
|
||||
- [支持本项目](#support-the-project)
|
||||
- [已知问题](#known-issues)
|
||||
- [支持本项目](#支持本项目)
|
||||
|
||||
## 官方文档
|
||||
|
||||
你可以在 https://immich.app/ 找到包含安装手册的官方文档.
|
||||
您可以在 https://immich.app/ 找到官方文档(包含安装手册)。
|
||||
|
||||
## 示例
|
||||
|
||||
你可以在 https://demo.immich.app 访问示例.
|
||||
您可以在 https://demo.immich.app 访问示例。
|
||||
|
||||
在移动端, 你可以使用 `https://demo.immich.app/api`获取`服务终端链接`
|
||||
在移动端, 您可以使用 `https://demo.immich.app/api` 获取 `服务终端链接`
|
||||
|
||||
```bash title="示例认证信息"
|
||||
认证信息
|
||||
@@ -62,57 +64,52 @@
|
||||
```
|
||||
|
||||
```
|
||||
规格: 甲骨文免费虚拟机套餐-阿姆斯特丹 4核 2.4Ghz ARM64 CPU, 24GB RAM。
|
||||
规格: 甲骨文免费虚拟机套餐——阿姆斯特丹 4核 2.4Ghz ARM64 CPU, 24GB RAM。
|
||||
```
|
||||
|
||||
# 功能特性
|
||||
|
||||
| 功能特性 | 移动端 | 网页端 |
|
||||
| ------------------------------------------- | ------- | --- |
|
||||
| 上传并查看照片和视频 | 是 | 是 |
|
||||
| 软件运行时自动备份 | 是 | N/A |
|
||||
| 上传并查看照片和视频 | 是 | 是 |
|
||||
| 软件运行时自动备份 | 是 | N/A |
|
||||
| 选择需要备份的相册 | 是 | N/A |
|
||||
| 下载照片和视频到本地 | 是 | 是 |
|
||||
| 下载照片和视频到本地 | 是 | 是 |
|
||||
| 多用户支持 | 是 | 是 |
|
||||
| 相册 | 是 | 是 |
|
||||
| 共享相册 | 是 | 是 |
|
||||
| 可拖动的快速导航栏 | 是 | 是 |
|
||||
| 支持RAW格式 (HEIC, HEIF, DNG, Apple ProRaw) | 是 | 是 |
|
||||
| 元数据视图 (EXIF, 地图) | 是 | 是 |
|
||||
| 通过元数据、对象和标签进行搜索 | 是 | No |
|
||||
| 管理功能 (用户管理) | N/A | 是 |
|
||||
| 后台备份 | Android | N/A |
|
||||
| 元数据视图(EXIF, 地图) | 是 | 是 |
|
||||
| 通过元数据、对象和标签进行搜索 | 是 | 是 |
|
||||
| 管理功能(用户管理) | 否 | 是 |
|
||||
| 后台备份 | 是 | N/A |
|
||||
| 虚拟滚动 | 是 | 是 |
|
||||
| OAuth支持 | 是 | 是 |
|
||||
| 实时照片备份和查看 (仅iOS) | 是 | 是 |
|
||||
| OAuth 支持 | 是 | 是 |
|
||||
| API Keys|N/A|是|
|
||||
| 实况照片备份和查看 | 仅 iOS | 是 |
|
||||
|用户自定义存储结构|是|是|
|
||||
|公共分享|否|是|
|
||||
|归档与收藏功能|是|是|
|
||||
|全局地图|否|是|
|
||||
|好友分享|是|是|
|
||||
|人像识别与分组|是|是|
|
||||
|回忆(那年今日)|是|是|
|
||||
|离线支持|是|否|
|
||||
|只读相册|是|是|
|
||||
|
||||
# 支持本项目
|
||||
|
||||
我已经致力于本项目并且将我会持续更新文档、新增功能和修复问题。但是我不能一个人走下去,所以我需要你给予我走下去的动力。
|
||||
我已经致力于本项目并且将我会持续更新文档、新增功能和修复问题。但是独木不成林,我需要您给予我坚持下去的动力。
|
||||
|
||||
就像我主页里面 [selfhosted.show - In the episode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) 说的一样,这是我和团队的一项艰巨的任务。我希望某一天我能够全职开发本项目,在此我希望你们能够助我梦想成真。
|
||||
就像我在 [selfhosted.show - In the episode 'The-organization-must-not-be-name is a Hostile Actor'](https://selfhosted.show/79?t=1418) 节目里说的一样,这是我和团队的一项艰巨任务。并且我希望某一天我能够全职开发本项目,在此我请求您能够助我梦想成真。
|
||||
|
||||
如果你使用了本项目一段时间,并且觉得上面的话有道理,那么请你按照如下方式帮助我吧。
|
||||
如果您使用了本项目一段时间,并且觉得上面的话有道理,那么请您考虑通过下列任一方式支持我吧。
|
||||
|
||||
## 捐赠
|
||||
|
||||
- [按月捐赠](https://github.com/sponsors/alextran1502) via GitHub Sponsors
|
||||
- [一次捐赠](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via Github Sponsors
|
||||
|
||||
# 已知问题
|
||||
|
||||
## TensorFlow 构建问题
|
||||
|
||||
_这是一个针对于Proxmox的已知问题_
|
||||
|
||||
TensorFlow 不能运行在很旧的CPU架构上, 需要运行在AVX和AVX2指令集的CPU上。如果你在docker-compose的命令行中遇到了 `illegal instruction core dump`的错误, 通过如下命令检查你的CPU flag寄存器然后确保你能够看到`AVX`和`AVX2`的字样:
|
||||
|
||||
```bash
|
||||
more /proc/cpuinfo | grep flags
|
||||
```
|
||||
|
||||
如果你在Proxmox中运行虚拟机, 虚拟机中没有启用flag寄存器。
|
||||
|
||||
你需要在虚拟机的硬件面板中把CPU类型从`kvm64`改为`host`。
|
||||
|
||||
`Hardware > Processors > Edit > Advanced > Type (dropdown menu) > host`
|
||||
- 通过 GitHub Sponsors [按月捐赠](https://github.com/sponsors/alextran1502)
|
||||
- 通过 Github Sponsors [单次捐赠](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502)
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- 比特币: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||
|
||||
471
cli/src/api/open-api/api.ts
generated
471
cli/src/api/open-api/api.ts
generated
@@ -4,7 +4,7 @@
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.73.0
|
||||
* The version of the OpenAPI document: 1.74.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
@@ -344,6 +344,31 @@ export interface AllJobStatusResponseDto {
|
||||
*/
|
||||
'videoConversion': JobStatusDto;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface AssetBulkUpdateDto
|
||||
*/
|
||||
export interface AssetBulkUpdateDto {
|
||||
/**
|
||||
*
|
||||
* @type {Array<string>}
|
||||
* @memberof AssetBulkUpdateDto
|
||||
*/
|
||||
'ids': Array<string>;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof AssetBulkUpdateDto
|
||||
*/
|
||||
'isArchived'?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof AssetBulkUpdateDto
|
||||
*/
|
||||
'isFavorite'?: boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@@ -500,6 +525,42 @@ export const AssetIdsResponseDtoErrorEnum = {
|
||||
|
||||
export type AssetIdsResponseDtoErrorEnum = typeof AssetIdsResponseDtoErrorEnum[keyof typeof AssetIdsResponseDtoErrorEnum];
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @enum {string}
|
||||
*/
|
||||
|
||||
export const AssetJobName = {
|
||||
RegenerateThumbnail: 'regenerate-thumbnail',
|
||||
RefreshMetadata: 'refresh-metadata',
|
||||
TranscodeVideo: 'transcode-video'
|
||||
} as const;
|
||||
|
||||
export type AssetJobName = typeof AssetJobName[keyof typeof AssetJobName];
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface AssetJobsDto
|
||||
*/
|
||||
export interface AssetJobsDto {
|
||||
/**
|
||||
*
|
||||
* @type {Array<string>}
|
||||
* @memberof AssetJobsDto
|
||||
*/
|
||||
'assetIds': Array<string>;
|
||||
/**
|
||||
*
|
||||
* @type {AssetJobName}
|
||||
* @memberof AssetJobsDto
|
||||
*/
|
||||
'name': AssetJobName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@@ -1132,6 +1193,37 @@ export interface DownloadArchiveInfo {
|
||||
*/
|
||||
'size': number;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface DownloadInfoDto
|
||||
*/
|
||||
export interface DownloadInfoDto {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof DownloadInfoDto
|
||||
*/
|
||||
'albumId'?: string;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof DownloadInfoDto
|
||||
*/
|
||||
'archiveSize'?: number;
|
||||
/**
|
||||
*
|
||||
* @type {Array<string>}
|
||||
* @memberof DownloadInfoDto
|
||||
*/
|
||||
'assetIds'?: Array<string>;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof DownloadInfoDto
|
||||
*/
|
||||
'userId'?: string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@@ -1748,6 +1840,12 @@ export interface PeopleUpdateDto {
|
||||
* @interface PeopleUpdateItem
|
||||
*/
|
||||
export interface PeopleUpdateItem {
|
||||
/**
|
||||
* Person date of birth.
|
||||
* @type {string}
|
||||
* @memberof PeopleUpdateItem
|
||||
*/
|
||||
'birthDate'?: string | null;
|
||||
/**
|
||||
* Asset is used to get the feature face thumbnail.
|
||||
* @type {string}
|
||||
@@ -1779,6 +1877,12 @@ export interface PeopleUpdateItem {
|
||||
* @interface PersonResponseDto
|
||||
*/
|
||||
export interface PersonResponseDto {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PersonResponseDto
|
||||
*/
|
||||
'birthDate': string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -1810,6 +1914,12 @@ export interface PersonResponseDto {
|
||||
* @interface PersonUpdateDto
|
||||
*/
|
||||
export interface PersonUpdateDto {
|
||||
/**
|
||||
* Person date of birth.
|
||||
* @type {string}
|
||||
* @memberof PersonUpdateDto
|
||||
*/
|
||||
'birthDate'?: string | null;
|
||||
/**
|
||||
* Asset is used to get the feature face thumbnail.
|
||||
* @type {string}
|
||||
@@ -2031,6 +2141,43 @@ export interface SearchResponseDto {
|
||||
*/
|
||||
'assets': SearchAssetResponseDto;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface ServerFeaturesDto
|
||||
*/
|
||||
export interface ServerFeaturesDto {
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'machineLearning': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'oauth': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'oauthAutoLaunch': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'passwordLogin': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'search': boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@@ -2152,25 +2299,25 @@ export interface ServerStatsResponseDto {
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface ServerVersionReponseDto
|
||||
* @interface ServerVersionResponseDto
|
||||
*/
|
||||
export interface ServerVersionReponseDto {
|
||||
export interface ServerVersionResponseDto {
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof ServerVersionReponseDto
|
||||
* @memberof ServerVersionResponseDto
|
||||
*/
|
||||
'major': number;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof ServerVersionReponseDto
|
||||
* @memberof ServerVersionResponseDto
|
||||
*/
|
||||
'minor': number;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof ServerVersionReponseDto
|
||||
* @memberof ServerVersionResponseDto
|
||||
*/
|
||||
'patch': number;
|
||||
}
|
||||
@@ -4880,7 +5027,7 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||
downloadArchive: async (assetIdsDto: AssetIdsDto, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'assetIdsDto' is not null or undefined
|
||||
assertParamExists('downloadArchive', 'assetIdsDto', assetIdsDto)
|
||||
const localVarPath = `/asset/download`;
|
||||
const localVarPath = `/asset/download/archive`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
@@ -5379,16 +5526,15 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {Array<string>} [assetIds]
|
||||
* @param {string} [albumId]
|
||||
* @param {string} [userId]
|
||||
* @param {number} [archiveSize]
|
||||
* @param {DownloadInfoDto} downloadInfoDto
|
||||
* @param {string} [key]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getDownloadInfo: async (assetIds?: Array<string>, albumId?: string, userId?: string, archiveSize?: number, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/asset/download`;
|
||||
getDownloadInfo: async (downloadInfoDto: DownloadInfoDto, key?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'downloadInfoDto' is not null or undefined
|
||||
assertParamExists('getDownloadInfo', 'downloadInfoDto', downloadInfoDto)
|
||||
const localVarPath = `/asset/download/info`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
@@ -5396,7 +5542,7 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
@@ -5409,31 +5555,18 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
if (assetIds) {
|
||||
localVarQueryParameter['assetIds'] = assetIds;
|
||||
}
|
||||
|
||||
if (albumId !== undefined) {
|
||||
localVarQueryParameter['albumId'] = albumId;
|
||||
}
|
||||
|
||||
if (userId !== undefined) {
|
||||
localVarQueryParameter['userId'] = userId;
|
||||
}
|
||||
|
||||
if (archiveSize !== undefined) {
|
||||
localVarQueryParameter['archiveSize'] = archiveSize;
|
||||
}
|
||||
|
||||
if (key !== undefined) {
|
||||
localVarQueryParameter['key'] = key;
|
||||
}
|
||||
|
||||
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(downloadInfoDto, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
@@ -5705,6 +5838,50 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {AssetJobsDto} assetJobsDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
runAssetJobs: async (assetJobsDto: AssetJobsDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'assetJobsDto' is not null or undefined
|
||||
assertParamExists('runAssetJobs', 'assetJobsDto', assetJobsDto)
|
||||
const localVarPath = `/asset/jobs`;
|
||||
// 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(assetJobsDto, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {SearchAssetDto} searchAssetDto
|
||||
@@ -5854,6 +6031,50 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {AssetBulkUpdateDto} assetBulkUpdateDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
updateAssets: async (assetBulkUpdateDto: AssetBulkUpdateDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'assetBulkUpdateDto' is not null or undefined
|
||||
assertParamExists('updateAssets', 'assetBulkUpdateDto', assetBulkUpdateDto)
|
||||
const localVarPath = `/asset`;
|
||||
// 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: 'PUT', ...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(assetBulkUpdateDto, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {File} assetData
|
||||
@@ -6141,16 +6362,13 @@ export const AssetApiFp = function(configuration?: Configuration) {
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {Array<string>} [assetIds]
|
||||
* @param {string} [albumId]
|
||||
* @param {string} [userId]
|
||||
* @param {number} [archiveSize]
|
||||
* @param {DownloadInfoDto} downloadInfoDto
|
||||
* @param {string} [key]
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getDownloadInfo(assetIds?: Array<string>, albumId?: string, userId?: string, archiveSize?: number, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<DownloadResponseDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getDownloadInfo(assetIds, albumId, userId, archiveSize, key, options);
|
||||
async getDownloadInfo(downloadInfoDto: DownloadInfoDto, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<DownloadResponseDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getDownloadInfo(downloadInfoDto, key, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
@@ -6211,6 +6429,16 @@ export const AssetApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.importFile(importAssetDto, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {AssetJobsDto} assetJobsDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async runAssetJobs(assetJobsDto: AssetJobsDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.runAssetJobs(assetJobsDto, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {SearchAssetDto} searchAssetDto
|
||||
@@ -6245,6 +6473,16 @@ export const AssetApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.updateAsset(id, updateAssetDto, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {AssetBulkUpdateDto} assetBulkUpdateDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async updateAssets(assetBulkUpdateDto: AssetBulkUpdateDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.updateAssets(assetBulkUpdateDto, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {File} assetData
|
||||
@@ -6406,8 +6644,8 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getDownloadInfo(requestParameters: AssetApiGetDownloadInfoRequest = {}, options?: AxiosRequestConfig): AxiosPromise<DownloadResponseDto> {
|
||||
return localVarFp.getDownloadInfo(requestParameters.assetIds, requestParameters.albumId, requestParameters.userId, requestParameters.archiveSize, requestParameters.key, options).then((request) => request(axios, basePath));
|
||||
getDownloadInfo(requestParameters: AssetApiGetDownloadInfoRequest, options?: AxiosRequestConfig): AxiosPromise<DownloadResponseDto> {
|
||||
return localVarFp.getDownloadInfo(requestParameters.downloadInfoDto, requestParameters.key, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
@@ -6454,6 +6692,15 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
|
||||
importFile(requestParameters: AssetApiImportFileRequest, options?: AxiosRequestConfig): AxiosPromise<AssetFileUploadResponseDto> {
|
||||
return localVarFp.importFile(requestParameters.importAssetDto, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
runAssetJobs(requestParameters: AssetApiRunAssetJobsRequest, options?: AxiosRequestConfig): AxiosPromise<void> {
|
||||
return localVarFp.runAssetJobs(requestParameters.assetJobsDto, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {AssetApiSearchAssetRequest} requestParameters Request parameters.
|
||||
@@ -6481,6 +6728,15 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
|
||||
updateAsset(requestParameters: AssetApiUpdateAssetRequest, options?: AxiosRequestConfig): AxiosPromise<AssetResponseDto> {
|
||||
return localVarFp.updateAsset(requestParameters.id, requestParameters.updateAssetDto, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {AssetApiUpdateAssetsRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
updateAssets(requestParameters: AssetApiUpdateAssetsRequest, options?: AxiosRequestConfig): AxiosPromise<void> {
|
||||
return localVarFp.updateAssets(requestParameters.assetBulkUpdateDto, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {AssetApiUploadFileRequest} requestParameters Request parameters.
|
||||
@@ -6788,31 +7044,10 @@ export interface AssetApiGetByTimeBucketRequest {
|
||||
export interface AssetApiGetDownloadInfoRequest {
|
||||
/**
|
||||
*
|
||||
* @type {Array<string>}
|
||||
* @type {DownloadInfoDto}
|
||||
* @memberof AssetApiGetDownloadInfo
|
||||
*/
|
||||
readonly assetIds?: Array<string>
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AssetApiGetDownloadInfo
|
||||
*/
|
||||
readonly albumId?: string
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof AssetApiGetDownloadInfo
|
||||
*/
|
||||
readonly userId?: string
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof AssetApiGetDownloadInfo
|
||||
*/
|
||||
readonly archiveSize?: number
|
||||
readonly downloadInfoDto: DownloadInfoDto
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -6948,6 +7183,20 @@ export interface AssetApiImportFileRequest {
|
||||
readonly importAssetDto: ImportAssetDto
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for runAssetJobs operation in AssetApi.
|
||||
* @export
|
||||
* @interface AssetApiRunAssetJobsRequest
|
||||
*/
|
||||
export interface AssetApiRunAssetJobsRequest {
|
||||
/**
|
||||
*
|
||||
* @type {AssetJobsDto}
|
||||
* @memberof AssetApiRunAssetJobs
|
||||
*/
|
||||
readonly assetJobsDto: AssetJobsDto
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for searchAsset operation in AssetApi.
|
||||
* @export
|
||||
@@ -7018,6 +7267,20 @@ export interface AssetApiUpdateAssetRequest {
|
||||
readonly updateAssetDto: UpdateAssetDto
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for updateAssets operation in AssetApi.
|
||||
* @export
|
||||
* @interface AssetApiUpdateAssetsRequest
|
||||
*/
|
||||
export interface AssetApiUpdateAssetsRequest {
|
||||
/**
|
||||
*
|
||||
* @type {AssetBulkUpdateDto}
|
||||
* @memberof AssetApiUpdateAssets
|
||||
*/
|
||||
readonly assetBulkUpdateDto: AssetBulkUpdateDto
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for uploadFile operation in AssetApi.
|
||||
* @export
|
||||
@@ -7281,8 +7544,8 @@ export class AssetApi extends BaseAPI {
|
||||
* @throws {RequiredError}
|
||||
* @memberof AssetApi
|
||||
*/
|
||||
public getDownloadInfo(requestParameters: AssetApiGetDownloadInfoRequest = {}, options?: AxiosRequestConfig) {
|
||||
return AssetApiFp(this.configuration).getDownloadInfo(requestParameters.assetIds, requestParameters.albumId, requestParameters.userId, requestParameters.archiveSize, requestParameters.key, options).then((request) => request(this.axios, this.basePath));
|
||||
public getDownloadInfo(requestParameters: AssetApiGetDownloadInfoRequest, options?: AxiosRequestConfig) {
|
||||
return AssetApiFp(this.configuration).getDownloadInfo(requestParameters.downloadInfoDto, requestParameters.key, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7340,6 +7603,17 @@ export class AssetApi extends BaseAPI {
|
||||
return AssetApiFp(this.configuration).importFile(requestParameters.importAssetDto, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {AssetApiRunAssetJobsRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof AssetApi
|
||||
*/
|
||||
public runAssetJobs(requestParameters: AssetApiRunAssetJobsRequest, options?: AxiosRequestConfig) {
|
||||
return AssetApiFp(this.configuration).runAssetJobs(requestParameters.assetJobsDto, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {AssetApiSearchAssetRequest} requestParameters Request parameters.
|
||||
@@ -7373,6 +7647,17 @@ export class AssetApi extends BaseAPI {
|
||||
return AssetApiFp(this.configuration).updateAsset(requestParameters.id, requestParameters.updateAssetDto, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {AssetApiUpdateAssetsRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof AssetApi
|
||||
*/
|
||||
public updateAssets(requestParameters: AssetApiUpdateAssetsRequest, options?: AxiosRequestConfig) {
|
||||
return AssetApiFp(this.configuration).updateAssets(requestParameters.assetBulkUpdateDto, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {AssetApiUploadFileRequest} requestParameters Request parameters.
|
||||
@@ -10050,6 +10335,35 @@ export class SearchApi extends BaseAPI {
|
||||
*/
|
||||
export const ServerInfoApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getServerFeatures: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/server-info/features`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
|
||||
|
||||
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.
|
||||
@@ -10223,6 +10537,15 @@ export const ServerInfoApiAxiosParamCreator = function (configuration?: Configur
|
||||
export const ServerInfoApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = ServerInfoApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getServerFeatures(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ServerFeaturesDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getServerFeatures(options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
@@ -10237,7 +10560,7 @@ export const ServerInfoApiFp = function(configuration?: Configuration) {
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getServerVersion(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ServerVersionReponseDto>> {
|
||||
async getServerVersion(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ServerVersionResponseDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getServerVersion(options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
@@ -10278,6 +10601,14 @@ export const ServerInfoApiFp = function(configuration?: Configuration) {
|
||||
export const ServerInfoApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = ServerInfoApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getServerFeatures(options?: AxiosRequestConfig): AxiosPromise<ServerFeaturesDto> {
|
||||
return localVarFp.getServerFeatures(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
@@ -10291,7 +10622,7 @@ export const ServerInfoApiFactory = function (configuration?: Configuration, bas
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getServerVersion(options?: AxiosRequestConfig): AxiosPromise<ServerVersionReponseDto> {
|
||||
getServerVersion(options?: AxiosRequestConfig): AxiosPromise<ServerVersionResponseDto> {
|
||||
return localVarFp.getServerVersion(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
@@ -10328,6 +10659,16 @@ export const ServerInfoApiFactory = function (configuration?: Configuration, bas
|
||||
* @extends {BaseAPI}
|
||||
*/
|
||||
export class ServerInfoApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof ServerInfoApi
|
||||
*/
|
||||
public getServerFeatures(options?: AxiosRequestConfig) {
|
||||
return ServerInfoApiFp(this.configuration).getServerFeatures(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
|
||||
2
cli/src/api/open-api/base.ts
generated
2
cli/src/api/open-api/base.ts
generated
@@ -4,7 +4,7 @@
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.73.0
|
||||
* The version of the OpenAPI document: 1.74.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
|
||||
2
cli/src/api/open-api/common.ts
generated
2
cli/src/api/open-api/common.ts
generated
@@ -4,7 +4,7 @@
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.73.0
|
||||
* The version of the OpenAPI document: 1.74.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
|
||||
2
cli/src/api/open-api/configuration.ts
generated
2
cli/src/api/open-api/configuration.ts
generated
@@ -4,7 +4,7 @@
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.73.0
|
||||
* The version of the OpenAPI document: 1.74.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
|
||||
2
cli/src/api/open-api/index.ts
generated
2
cli/src/api/open-api/index.ts
generated
@@ -4,7 +4,7 @@
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.73.0
|
||||
* The version of the OpenAPI document: 1.74.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
|
||||
@@ -4,14 +4,14 @@ import { SessionService } from '../services/session.service';
|
||||
import { LoginError } from '../cores/errors/login-error';
|
||||
import { exit } from 'node:process';
|
||||
import os from 'os';
|
||||
import { ServerVersionReponseDto, UserResponseDto } from 'src/api/open-api';
|
||||
import { ServerVersionResponseDto, UserResponseDto } from 'src/api/open-api';
|
||||
|
||||
export abstract class BaseCommand {
|
||||
protected sessionService!: SessionService;
|
||||
protected immichApi!: ImmichApi;
|
||||
protected deviceId!: string;
|
||||
protected user!: UserResponseDto;
|
||||
protected serverVersion!: ServerVersionReponseDto;
|
||||
protected serverVersion!: ServerVersionResponseDto;
|
||||
|
||||
protected configDir;
|
||||
protected authPath;
|
||||
|
||||
@@ -100,8 +100,8 @@ services:
|
||||
environment:
|
||||
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
||||
- TYPESENSE_DATA_DIR=/data
|
||||
logging:
|
||||
driver: none
|
||||
# remove this to get debug messages
|
||||
- GLOG_minloglevel=1
|
||||
volumes:
|
||||
- tsdata:/data
|
||||
|
||||
|
||||
@@ -68,8 +68,8 @@ services:
|
||||
environment:
|
||||
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
||||
- TYPESENSE_DATA_DIR=/data
|
||||
logging:
|
||||
driver: none
|
||||
# remove this to get debug messages
|
||||
- GLOG_minloglevel=1
|
||||
volumes:
|
||||
- tsdata:/data
|
||||
restart: always
|
||||
|
||||
@@ -54,6 +54,8 @@ services:
|
||||
environment:
|
||||
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
||||
- TYPESENSE_DATA_DIR=/data
|
||||
# remove this to get debug messages
|
||||
- GLOG_minloglevel=1
|
||||
volumes:
|
||||
- tsdata:/data
|
||||
restart: always
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "machine-learning"
|
||||
version = "1.73.0"
|
||||
version = "1.74.0"
|
||||
description = ""
|
||||
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
||||
readme = "README.md"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"flutterSdkVersion": "3.10.5",
|
||||
"flutterSdkVersion": "3.13.0",
|
||||
"flavors": {}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ android {
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "app.alextran.immich"
|
||||
minSdkVersion 23
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
|
||||
@@ -35,8 +35,8 @@ platform :android do
|
||||
task: 'bundle',
|
||||
build_type: 'Release',
|
||||
properties: {
|
||||
"android.injected.version.code" => 96,
|
||||
"android.injected.version.name" => "1.73.0",
|
||||
"android.injected.version.code" => 97,
|
||||
"android.injected.version.name" => "1.74.0",
|
||||
}
|
||||
)
|
||||
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')
|
||||
|
||||
@@ -5,17 +5,17 @@
|
||||
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000239">
|
||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.00023">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="68.788432">
|
||||
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="67.877631">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="29.76592">
|
||||
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="23.895222">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
@@ -300,5 +300,6 @@
|
||||
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
||||
"version_announcement_overlay_text_2": "please take your time to visit the ",
|
||||
"version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
|
||||
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89"
|
||||
}
|
||||
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89",
|
||||
"translated_text_options": "Options"
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- path_provider_ios (0.0.1):
|
||||
- Flutter
|
||||
- permission_handler_apple (9.0.4):
|
||||
- permission_handler_apple (9.1.1):
|
||||
- Flutter
|
||||
- photo_manager (2.0.0):
|
||||
- Flutter
|
||||
@@ -53,7 +53,7 @@ PODS:
|
||||
- Flutter
|
||||
- video_player_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
- wakelock (0.0.1):
|
||||
- wakelock_plus (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
@@ -78,7 +78,7 @@ DEPENDENCIES:
|
||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
|
||||
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
||||
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
@@ -130,8 +130,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
video_player_avfoundation:
|
||||
:path: ".symlinks/plugins/video_player_avfoundation/ios"
|
||||
wakelock:
|
||||
:path: ".symlinks/plugins/wakelock/ios"
|
||||
wakelock_plus:
|
||||
:path: ".symlinks/plugins/wakelock_plus/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
|
||||
@@ -141,25 +141,25 @@ SPEC CHECKSUMS:
|
||||
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
||||
flutter_udid: 0848809dbed4c055175747ae6a45a8b4f6771e1c
|
||||
flutter_web_auth: c25208760459cec375a3c39f6a8759165ca0fa4d
|
||||
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
|
||||
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5
|
||||
integration_test: 13825b8a9334a850581300559b8839134b124670
|
||||
isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073
|
||||
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
||||
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
|
||||
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
|
||||
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
|
||||
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
||||
photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
|
||||
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028
|
||||
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
|
||||
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||
video_player_avfoundation: 81e49bb3d9fb63dccf9fa0f6d877dc3ddbeac126
|
||||
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
||||
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
|
||||
|
||||
PODFILE CHECKSUM: 599d8aeb73728400c15364e734525722250a5382
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1300;
|
||||
LastUpgradeCheck = 1430;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
97C146ED1CF9000F007C117D = {
|
||||
@@ -379,7 +379,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 110;
|
||||
CURRENT_PROJECT_VERSION = 113;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
@@ -515,7 +515,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 110;
|
||||
CURRENT_PROJECT_VERSION = 113;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
@@ -543,7 +543,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 110;
|
||||
CURRENT_PROJECT_VERSION = 113;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1300"
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -59,11 +59,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.70.0</string>
|
||||
<string>1.73.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>110</string>
|
||||
<string>113</string>
|
||||
<key>FLTEnableImpeller</key>
|
||||
<true />
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# The default execution directory of this script is the ci_scripts directory.
|
||||
cd $CI_WORKSPACE/mobile
|
||||
|
||||
@@ -19,7 +19,7 @@ platform :ios do
|
||||
desc "iOS Beta"
|
||||
lane :beta do
|
||||
increment_version_number(
|
||||
version_number: "1.73.0"
|
||||
version_number: "1.74.0"
|
||||
)
|
||||
increment_build_number(
|
||||
build_number: latest_testflight_build_number + 1,
|
||||
|
||||
@@ -5,32 +5,32 @@
|
||||
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000211">
|
||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000187">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="1: increment_version_number" time="2.108738">
|
||||
<testcase classname="fastlane.lanes" name="1: increment_version_number" time="2.403882">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="28.952846">
|
||||
<testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="5.068392">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="3: increment_build_number" time="1.821481">
|
||||
<testcase classname="fastlane.lanes" name="3: increment_build_number" time="1.988079">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="4: build_app" time="99.212621">
|
||||
<testcase classname="fastlane.lanes" name="4: build_app" time="96.47923">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="68.366701">
|
||||
<testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="57.517755">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
@@ -139,6 +139,10 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
||||
debugPrint("[APP STATE] detached");
|
||||
ref.read(appStateProvider.notifier).handleAppDetached();
|
||||
break;
|
||||
case AppLifecycleState.hidden:
|
||||
debugPrint("[APP STATE] hidden");
|
||||
ref.read(appStateProvider.notifier).handleAppHidden();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,16 @@ class SharedAlbumNotifier extends StateNotifier<List<Album>> {
|
||||
return _albumService.removeAssetFromAlbum(album, assets);
|
||||
}
|
||||
|
||||
Future<bool> removeUserFromAlbum(Album album, User user) async {
|
||||
final result = await _albumService.removeUserFromAlbum(album, user);
|
||||
|
||||
if (result && album.sharedUsers.isEmpty) {
|
||||
state = state.where((element) => element.id != album.id).toList();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_streamSub.cancel();
|
||||
|
||||
@@ -348,6 +348,26 @@ class AlbumService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> removeUserFromAlbum(
|
||||
Album album,
|
||||
User user,
|
||||
) async {
|
||||
try {
|
||||
await _apiService.albumApi.removeUserFromAlbum(
|
||||
album.remoteId!,
|
||||
user.id,
|
||||
);
|
||||
|
||||
album.sharedUsers.remove(user);
|
||||
await _db.writeTxn(() => album.sharedUsers.update(unlink: [user]));
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
debugPrint("Error removeUserFromAlbum ${e.toString()}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> changeTitleAlbum(
|
||||
Album album,
|
||||
String newAlbumTitle,
|
||||
|
||||
@@ -49,7 +49,7 @@ class AlbumThumbnailListTile extends StatelessWidget {
|
||||
type: ThumbnailFormat.JPEG,
|
||||
),
|
||||
httpHeaders: {
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}"
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
},
|
||||
cacheKey: getAlbumThumbNailCacheKey(album, type: ThumbnailFormat.JPEG),
|
||||
errorWidget: (context, url, error) =>
|
||||
@@ -105,9 +105,9 @@ class AlbumThumbnailListTile extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
),
|
||||
).tr()
|
||||
).tr(),
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -69,6 +69,11 @@ class AlbumTitleTextField extends ConsumerWidget {
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
hintText: 'share_add_title'.tr(),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 28,
|
||||
color: isDarkTheme ? Colors.grey[300] : Colors.grey[700],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
focusColor: Colors.grey[300],
|
||||
fillColor: isDarkTheme
|
||||
? const Color.fromARGB(255, 32, 33, 35)
|
||||
|
||||
@@ -39,7 +39,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
||||
final newAlbumTitle = ref.watch(albumViewerProvider).editTitleText;
|
||||
final isEditAlbum = ref.watch(albumViewerProvider).isEditAlbum;
|
||||
|
||||
void onDeleteAlbumPressed() async {
|
||||
deleteAlbum() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
|
||||
final bool success;
|
||||
@@ -65,6 +65,52 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
}
|
||||
|
||||
Future<void> showConfirmationDialog() async {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false, // user must tap button!
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Delete album'),
|
||||
content: const Text(
|
||||
'Are you sure you want to delete this album from your account?',
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, 'Cancel'),
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Confirm');
|
||||
deleteAlbum();
|
||||
},
|
||||
child: Text(
|
||||
'Confirm',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? Colors.red
|
||||
: Colors.red[300],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void onDeleteAlbumPressed() async {
|
||||
showConfirmationDialog();
|
||||
}
|
||||
|
||||
void onLeaveAlbumPressed() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
|
||||
@@ -152,43 +198,61 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
||||
}
|
||||
|
||||
void buildBottomSheet() {
|
||||
final ownerActions = [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.person_add_alt_rounded),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
onAddUsers!(album);
|
||||
},
|
||||
title: const Text(
|
||||
"album_viewer_page_share_add_users",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.settings_rounded),
|
||||
onTap: () =>
|
||||
AutoRouter.of(context).navigate(AlbumOptionsRoute(album: album)),
|
||||
title: const Text(
|
||||
"translated_text_options",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
),
|
||||
];
|
||||
|
||||
final commonActions = [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.add_photo_alternate_outlined),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
onAddPhotos!(album);
|
||||
},
|
||||
title: const Text(
|
||||
"share_add_photos",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
),
|
||||
];
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
isScrollControlled: false,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
buildBottomSheetActionButton(),
|
||||
if (selected.isEmpty && onAddPhotos != null)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.add_photo_alternate_outlined),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
onAddPhotos!(album);
|
||||
},
|
||||
title: const Text(
|
||||
"share_add_photos",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
),
|
||||
if (selected.isEmpty &&
|
||||
onAddPhotos != null &&
|
||||
userId == album.ownerId)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.person_add_alt_rounded),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
onAddUsers!(album);
|
||||
},
|
||||
title: const Text(
|
||||
"album_viewer_page_share_add_users",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
),
|
||||
],
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 24.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
buildBottomSheetActionButton(),
|
||||
if (selected.isEmpty && onAddPhotos != null) ...commonActions,
|
||||
if (selected.isEmpty &&
|
||||
onAddPhotos != null &&
|
||||
userId == album.ownerId)
|
||||
...ownerActions,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -217,6 +281,8 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
||||
toastType: ToastType.error,
|
||||
);
|
||||
}
|
||||
|
||||
titleFocusNode.unfocus();
|
||||
},
|
||||
icon: const Icon(Icons.check_rounded),
|
||||
splashRadius: 25,
|
||||
|
||||
@@ -84,6 +84,11 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
|
||||
: Colors.grey[200],
|
||||
filled: titleFocusNode.hasFocus,
|
||||
hintText: 'share_add_title'.tr(),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 28,
|
||||
color: isDarkTheme ? Colors.grey[300] : Colors.grey[700],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
205
mobile/lib/modules/album/views/album_options_part.dart
Normal file
205
mobile/lib/modules/album/views/album_options_part.dart
Normal file
@@ -0,0 +1,205 @@
|
||||
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/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
|
||||
class AlbumOptionsPage extends HookConsumerWidget {
|
||||
final Album album;
|
||||
|
||||
const AlbumOptionsPage({super.key, required this.album});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sharedUsers = useState(album.sharedUsers.toList());
|
||||
final owner = album.owner.value;
|
||||
final userId = ref.watch(authenticationProvider).userId;
|
||||
final isOwner = owner?.id == userId;
|
||||
|
||||
void showErrorMessage() {
|
||||
Navigator.pop(context);
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: "Error leaving/removing from album",
|
||||
toastType: ToastType.error,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
|
||||
void leaveAlbum() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
|
||||
try {
|
||||
final isSuccess =
|
||||
await ref.read(sharedAlbumProvider.notifier).leaveAlbum(album);
|
||||
|
||||
if (isSuccess) {
|
||||
AutoRouter.of(context)
|
||||
.navigate(const TabControllerRoute(children: [SharingRoute()]));
|
||||
} else {
|
||||
showErrorMessage();
|
||||
}
|
||||
} catch (_) {
|
||||
showErrorMessage();
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
}
|
||||
|
||||
void removeUserFromAlbum(User user) async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
|
||||
try {
|
||||
await ref
|
||||
.read(sharedAlbumProvider.notifier)
|
||||
.removeUserFromAlbum(album, user);
|
||||
album.sharedUsers.remove(user);
|
||||
sharedUsers.value = album.sharedUsers.toList();
|
||||
} catch (error) {
|
||||
showErrorMessage();
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
}
|
||||
|
||||
void handleUserClick(User user) {
|
||||
var actions = [];
|
||||
|
||||
if (user.id == userId) {
|
||||
actions = [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.exit_to_app_rounded),
|
||||
title: const Text("Leave album"),
|
||||
onTap: leaveAlbum,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
if (isOwner) {
|
||||
actions = [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.person_remove_rounded),
|
||||
title: const Text("Remove user from album"),
|
||||
onTap: () => removeUserFromAlbum(user),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
isScrollControlled: false,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 24.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [...actions],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
buildOwnerInfo() {
|
||||
return ListTile(
|
||||
leading: owner != null
|
||||
? UserCircleAvatar(
|
||||
user: owner,
|
||||
useRandomBackgroundColor: true,
|
||||
)
|
||||
: const SizedBox(),
|
||||
title: Text(
|
||||
album.owner.value?.firstName ?? "",
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
album.owner.value?.email ?? "",
|
||||
style: TextStyle(color: Colors.grey[500]),
|
||||
),
|
||||
trailing: const Text(
|
||||
"Owner",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
buildSharedUsersList() {
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: sharedUsers.value.length,
|
||||
itemBuilder: (context, index) {
|
||||
final user = sharedUsers.value[index];
|
||||
return ListTile(
|
||||
leading: UserCircleAvatar(
|
||||
user: user,
|
||||
useRandomBackgroundColor: true,
|
||||
radius: 22,
|
||||
),
|
||||
title: Text(
|
||||
user.firstName,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
user.email,
|
||||
style: TextStyle(color: Colors.grey[500]),
|
||||
),
|
||||
trailing: userId == user.id || isOwner
|
||||
? const Icon(Icons.more_horiz_rounded)
|
||||
: const SizedBox(),
|
||||
onTap: userId == user.id || isOwner
|
||||
? () => handleUserClick(user)
|
||||
: null,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
buildSectionTitle(String text) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(text, style: Theme.of(context).textTheme.bodySmall),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios_new_rounded),
|
||||
onPressed: () {
|
||||
AutoRouter.of(context).pop(null);
|
||||
},
|
||||
),
|
||||
centerTitle: true,
|
||||
title: Text("translated_text_options".tr()),
|
||||
),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildSectionTitle("PEOPLE"),
|
||||
buildOwnerInfo(),
|
||||
buildSharedUsersList(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
|
||||
class AlbumViewerPage extends HookConsumerWidget {
|
||||
@@ -116,7 +117,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
||||
|
||||
Widget buildControlButton(Album album) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 8, bottom: 8),
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 8, bottom: 16),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: ListView(
|
||||
@@ -141,7 +142,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
||||
|
||||
Widget buildTitle(Album album) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8, top: 16),
|
||||
padding: const EdgeInsets.only(left: 8, right: 8, top: 24),
|
||||
child: userId == album.ownerId && album.isRemote
|
||||
? AlbumViewerEditableTitle(
|
||||
album: album,
|
||||
@@ -172,7 +173,6 @@ class AlbumViewerPage extends HookConsumerWidget {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 16.0,
|
||||
top: 8.0,
|
||||
bottom: album.shared ? 0.0 : 8.0,
|
||||
),
|
||||
child: Text(
|
||||
@@ -180,7 +180,34 @@ class AlbumViewerPage extends HookConsumerWidget {
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSharedUserIconsRow(Album album) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
await AutoRouter.of(context).push(AlbumOptionsRoute(album: album));
|
||||
ref.invalidate(albumDetailProvider(album.id));
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 50,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: ((context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: UserCircleAvatar(
|
||||
user: album.sharedUsers.toList()[index],
|
||||
radius: 18,
|
||||
size: 36,
|
||||
useRandomBackgroundColor: true,
|
||||
),
|
||||
);
|
||||
}),
|
||||
itemCount: album.sharedUsers.length,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -193,33 +220,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
||||
children: [
|
||||
buildTitle(album),
|
||||
if (album.assets.isNotEmpty == true) buildAlbumDateRange(album),
|
||||
if (album.shared)
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: ((context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: CircleAvatar(
|
||||
backgroundColor: Colors.grey[300],
|
||||
radius: 18,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
child: Image.asset(
|
||||
'assets/immich-logo-no-outline.png',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
itemCount: album.sharedUsers.length,
|
||||
),
|
||||
),
|
||||
if (album.shared) buildSharedUserIconsRow(album),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,9 +73,12 @@ class AssetSelectionPage extends HookConsumerWidget {
|
||||
AutoRouter.of(context)
|
||||
.popForced<AssetSelectionPageResult>(payload);
|
||||
},
|
||||
child: const Text(
|
||||
child: Text(
|
||||
"share_add",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
).tr(),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -30,7 +30,8 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
final albumTitleTextFieldFocusNode = useFocusNode();
|
||||
final isAlbumTitleTextFieldFocus = useState(false);
|
||||
final isAlbumTitleEmpty = useState(true);
|
||||
final selectedAssets = useState<Set<Asset>>(initialAssets != null ? Set.from(initialAssets!) : const {});
|
||||
final selectedAssets = useState<Set<Asset>>(
|
||||
initialAssets != null ? Set.from(initialAssets!) : const {},);
|
||||
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
showSelectUserPage() async {
|
||||
@@ -248,8 +249,9 @@ class CreateAlbumPage extends HookConsumerWidget {
|
||||
: null,
|
||||
child: Text(
|
||||
'create_shared_album_page_create'.tr(),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -60,7 +60,7 @@ class LibraryPage extends HookConsumerWidget {
|
||||
Widget buildSortButton() {
|
||||
final options = [
|
||||
"library_page_sort_created".tr(),
|
||||
"library_page_sort_title".tr()
|
||||
"library_page_sort_title".tr(),
|
||||
];
|
||||
|
||||
return PopupMenuButton(
|
||||
@@ -87,7 +87,7 @@ class LibraryPage extends HookConsumerWidget {
|
||||
color: selected ? Theme.of(context).primaryColor : null,
|
||||
fontSize: 12.0,
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:immich_mobile/modules/album/providers/suggested_shared_users.pro
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
|
||||
class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
||||
final Album album;
|
||||
@@ -35,10 +36,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return CircleAvatar(
|
||||
backgroundImage:
|
||||
const AssetImage('assets/immich-logo-no-outline.png'),
|
||||
backgroundColor: Theme.of(context).primaryColor.withAlpha(50),
|
||||
return UserCircleAvatar(
|
||||
user: user,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -103,7 +102,7 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
||||
} else {
|
||||
sharedUsersList.value = {
|
||||
...sharedUsersList.value,
|
||||
users[index]
|
||||
users[index],
|
||||
};
|
||||
}
|
||||
},
|
||||
@@ -136,7 +135,7 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
||||
"share_add",
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
body: suggestedShareUsers.when(
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
|
||||
class SelectUserForSharingPage extends HookConsumerWidget {
|
||||
const SelectUserForSharingPage({Key? key, required this.assets})
|
||||
@@ -56,10 +57,8 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return CircleAvatar(
|
||||
backgroundImage:
|
||||
const AssetImage('assets/immich-logo-no-outline.png'),
|
||||
backgroundColor: Theme.of(context).primaryColor.withAlpha(50),
|
||||
return UserCircleAvatar(
|
||||
user: user,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -124,7 +123,7 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
||||
} else {
|
||||
sharedUsersList.value = {
|
||||
...sharedUsersList.value,
|
||||
users[index]
|
||||
users[index],
|
||||
};
|
||||
}
|
||||
},
|
||||
@@ -164,7 +163,7 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
||||
// color: Theme.of(context).primaryColor,
|
||||
),
|
||||
).tr(),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
body: suggestedShareUsers.when(
|
||||
|
||||
@@ -160,7 +160,7 @@ class SharingPage extends HookConsumerWidget {
|
||||
maxLines: 1,
|
||||
).tr(),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -91,7 +91,7 @@ class ArchivePage extends HookConsumerWidget {
|
||||
selectionEnabledHook.value = false;
|
||||
}
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -124,7 +124,7 @@ class ArchivePage extends HookConsumerWidget {
|
||||
),
|
||||
if (selectionEnabledHook.value) buildBottomBar(),
|
||||
if (processing.value)
|
||||
const Center(child: ImmichLoadingIndicator())
|
||||
const Center(child: ImmichLoadingIndicator()),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -16,16 +16,32 @@ class ExifBottomSheet extends HookConsumerWidget {
|
||||
|
||||
const ExifBottomSheet({Key? key, required this.asset}) : super(key: key);
|
||||
|
||||
bool get showMap =>
|
||||
bool get hasCoordinates =>
|
||||
asset.exifInfo?.latitude != null && asset.exifInfo?.longitude != null;
|
||||
|
||||
Future<Uri> _createCoordinatesUri(double latitude, double longitude) async {
|
||||
const zoomLevel = 5;
|
||||
String get formattedDateTime {
|
||||
final fileCreatedAt = asset.fileCreatedAt.toLocal();
|
||||
final date = DateFormat.yMMMEd().format(fileCreatedAt);
|
||||
final time = DateFormat.jm().format(fileCreatedAt);
|
||||
|
||||
return '$date • $time';
|
||||
}
|
||||
|
||||
Future<Uri?> _createCoordinatesUri() async {
|
||||
if (!hasCoordinates) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double latitude = asset.exifInfo!.latitude!;
|
||||
double longitude = asset.exifInfo!.longitude!;
|
||||
|
||||
const zoomLevel = 16;
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
Uri uri = Uri(
|
||||
scheme: 'geo',
|
||||
host: '$latitude,$longitude',
|
||||
queryParameters: {'z': '$zoomLevel', 'q': '$latitude,$longitude'},
|
||||
queryParameters: {'z': '$zoomLevel', 'q': formattedDateTime},
|
||||
);
|
||||
if (await canLaunchUrl(uri)) {
|
||||
return uri;
|
||||
@@ -33,16 +49,20 @@ class ExifBottomSheet extends HookConsumerWidget {
|
||||
} else if (Platform.isIOS) {
|
||||
var params = {
|
||||
'll': '$latitude,$longitude',
|
||||
'q': '$latitude, $longitude',
|
||||
'q': formattedDateTime,
|
||||
'z': '$zoomLevel',
|
||||
};
|
||||
Uri uri = Uri.https('maps.apple.com', '/', params);
|
||||
if (!await canLaunchUrl(uri)) {
|
||||
if (await canLaunchUrl(uri)) {
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
return Uri.https(
|
||||
'www.google.com',
|
||||
'/maps/place/$latitude,$longitude/@$latitude,$longitude,${zoomLevel}z',
|
||||
|
||||
return Uri(
|
||||
scheme: 'https',
|
||||
host: 'openstreetmap.org',
|
||||
queryParameters: {'mlat': '$latitude', 'mlon': '$longitude'},
|
||||
fragment: 'map=$zoomLevel/$latitude/$longitude',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -72,16 +92,14 @@ class ExifBottomSheet extends HookConsumerWidget {
|
||||
),
|
||||
zoom: 16.0,
|
||||
onTap: (tapPosition, latLong) async {
|
||||
if (exifInfo != null &&
|
||||
exifInfo.latitude != null &&
|
||||
exifInfo.longitude != null) {
|
||||
launchUrl(
|
||||
await _createCoordinatesUri(
|
||||
exifInfo.latitude!,
|
||||
exifInfo.longitude!,
|
||||
),
|
||||
);
|
||||
Uri? uri = await _createCoordinatesUri();
|
||||
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugPrint('Opening Map Uri: $uri');
|
||||
launchUrl(uri);
|
||||
},
|
||||
),
|
||||
nonRotatedChildren: [
|
||||
@@ -151,7 +169,7 @@ class ExifBottomSheet extends HookConsumerWidget {
|
||||
|
||||
buildLocation() {
|
||||
// Guard no lat/lng
|
||||
if (!showMap) {
|
||||
if (!hasCoordinates) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
@@ -199,7 +217,7 @@ class ExifBottomSheet extends HookConsumerWidget {
|
||||
Text(
|
||||
"${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo.longitude!.toStringAsFixed(4)}",
|
||||
style: const TextStyle(fontSize: 12),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -207,12 +225,8 @@ class ExifBottomSheet extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
buildDate() {
|
||||
final fileCreatedAt = asset.fileCreatedAt.toLocal();
|
||||
final date = DateFormat.yMMMEd().format(fileCreatedAt);
|
||||
final time = DateFormat.jm().format(fileCreatedAt);
|
||||
|
||||
return Text(
|
||||
'$date • $time',
|
||||
formattedDateTime,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
@@ -306,7 +320,7 @@ class ExifBottomSheet extends HookConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Flexible(
|
||||
flex: showMap ? 5 : 0,
|
||||
flex: hasCoordinates ? 5 : 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: buildLocation(),
|
||||
@@ -336,7 +350,7 @@ class ExifBottomSheet extends HookConsumerWidget {
|
||||
if (asset.isRemote) DescriptionInput(asset: asset),
|
||||
const SizedBox(height: 8.0),
|
||||
buildLocation(),
|
||||
SizedBox(height: showMap ? 16.0 : 0.0),
|
||||
SizedBox(height: hasCoordinates ? 16.0 : 0.0),
|
||||
buildDetail(),
|
||||
const SizedBox(height: 50),
|
||||
],
|
||||
|
||||
@@ -128,7 +128,7 @@ class TopControlAppBar extends HookConsumerWidget {
|
||||
if (asset.isLocal && !asset.isRemote) buildUploadButton(),
|
||||
if (asset.isRemote && !asset.isLocal) buildDownloadButton(),
|
||||
if (asset.isRemote) buildAddToAlbumButtom(),
|
||||
buildMoreInfoButton()
|
||||
buildMoreInfoButton(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import 'package:immich_mobile/shared/models/store.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class VideoViewerPage extends HookConsumerWidget {
|
||||
@@ -136,16 +136,16 @@ class _VideoPlayerState extends State<VideoPlayer> {
|
||||
videoPlayerController.addListener(() {
|
||||
if (videoPlayerController.value.isInitialized) {
|
||||
if (videoPlayerController.value.isPlaying) {
|
||||
Wakelock.enable();
|
||||
WakelockPlus.enable();
|
||||
widget.onPlaying?.call();
|
||||
} else if (!videoPlayerController.value.isPlaying) {
|
||||
Wakelock.disable();
|
||||
WakelockPlus.disable();
|
||||
widget.onPaused?.call();
|
||||
}
|
||||
|
||||
if (videoPlayerController.value.position ==
|
||||
videoPlayerController.value.duration) {
|
||||
Wakelock.disable();
|
||||
WakelockPlus.disable();
|
||||
widget.onVideoEnded();
|
||||
}
|
||||
}
|
||||
@@ -155,8 +155,8 @@ class _VideoPlayerState extends State<VideoPlayer> {
|
||||
Future<void> initializePlayer() async {
|
||||
try {
|
||||
videoPlayerController = widget.file == null
|
||||
? VideoPlayerController.network(
|
||||
widget.url!,
|
||||
? VideoPlayerController.networkUrl(
|
||||
Uri.parse(widget.url!),
|
||||
httpHeaders: {"Authorization": "Bearer ${widget.jwtToken}"},
|
||||
)
|
||||
: VideoPlayerController.file(widget.file!);
|
||||
@@ -210,8 +210,7 @@ class _VideoPlayerState extends State<VideoPlayer> {
|
||||
child: Center(
|
||||
child: Stack(
|
||||
children: [
|
||||
if (widget.placeholder != null)
|
||||
widget.placeholder!,
|
||||
if (widget.placeholder != null) widget.placeholder!,
|
||||
const Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
|
||||
@@ -90,7 +90,7 @@ class BackgroundService {
|
||||
requireUnmetered,
|
||||
requireCharging,
|
||||
triggerUpdateDelay,
|
||||
triggerMaxDelay
|
||||
triggerMaxDelay,
|
||||
],
|
||||
);
|
||||
return ok;
|
||||
|
||||
@@ -511,7 +511,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
state = state.copyWith(
|
||||
selectedAlbumsBackupAssetsIds: {
|
||||
...state.selectedAlbumsBackupAssetsIds,
|
||||
deviceAssetId
|
||||
deviceAssetId,
|
||||
},
|
||||
allAssetsInDatabase: [...state.allAssetsInDatabase, deviceAssetId],
|
||||
);
|
||||
|
||||
@@ -149,16 +149,30 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
|
||||
}
|
||||
|
||||
Future<bool> _startUpload(Iterable<Asset> allManualUploads) async {
|
||||
bool hasErrors = false;
|
||||
try {
|
||||
_backupProvider.updateBackupProgress(BackUpProgressEnum.manualInProgress);
|
||||
|
||||
if (ref.read(galleryPermissionNotifier.notifier).hasPermission) {
|
||||
await PhotoManager.clearFileCache();
|
||||
|
||||
Set<AssetEntity> allUploadAssets = allManualUploads
|
||||
.where((e) => e.isLocal && e.local != null)
|
||||
.map((e) => e.local!)
|
||||
.toSet();
|
||||
// We do not have 1:1 mapping of all AssetEntity fields to Asset. This results in cases
|
||||
// where platform specific fields such as `subtype` used to detect platform specific assets such as
|
||||
// LivePhoto in iOS is lost when we directly fetch the local asset from Asset using Asset.local
|
||||
List<AssetEntity?> allAssetsFromDevice = await Future.wait(
|
||||
allManualUploads
|
||||
// Filter local only assets
|
||||
.where((e) => e.isLocal && !e.isRemote)
|
||||
.map((e) => e.local!.obtainForNewProperties()),
|
||||
);
|
||||
|
||||
if (allAssetsFromDevice.length != allManualUploads.length) {
|
||||
_log.warning(
|
||||
'[_startUpload] Refreshed upload list -> ${allManualUploads.length - allAssetsFromDevice.length} asset will not be uploaded',
|
||||
);
|
||||
}
|
||||
|
||||
Set<AssetEntity> allUploadAssets = allAssetsFromDevice.nonNulls.toSet();
|
||||
|
||||
if (allUploadAssets.isEmpty) {
|
||||
debugPrint("[_startUpload] No Assets to upload - Abort Process");
|
||||
@@ -213,7 +227,7 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
|
||||
'[_startUpload] Manual Upload Completed - success: ${state.successfulUploads},'
|
||||
' failed: ${state.totalAssetsToUpload - state.successfulUploads}',
|
||||
);
|
||||
bool hasErrors = false;
|
||||
|
||||
// User cancelled upload
|
||||
if (!ok && state.cancelToken.isCancelled) {
|
||||
await _localNotificationService.showOrUpdateManualUploadStatus(
|
||||
@@ -237,32 +251,29 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
|
||||
presentBanner: true,
|
||||
);
|
||||
}
|
||||
|
||||
_backupProvider.updateBackupProgress(BackUpProgressEnum.idle);
|
||||
_handleAppInActivity();
|
||||
await _backupProvider.notifyBackgroundServiceCanRun();
|
||||
return !hasErrors;
|
||||
} else {
|
||||
openAppSettings();
|
||||
debugPrint("[_startUpload] Do not have permission to the gallery");
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("ERROR _startUpload: ${e.toString()}");
|
||||
hasErrors = true;
|
||||
} finally {
|
||||
_backupProvider.updateBackupProgress(BackUpProgressEnum.idle);
|
||||
_handleAppInActivity();
|
||||
await _localNotificationService.closeNotification(
|
||||
LocalNotificationService.manualUploadDetailedNotificationID,
|
||||
);
|
||||
await _backupProvider.notifyBackgroundServiceCanRun();
|
||||
}
|
||||
_backupProvider.updateBackupProgress(BackUpProgressEnum.idle);
|
||||
_handleAppInActivity();
|
||||
await _localNotificationService.closeNotification(
|
||||
LocalNotificationService.manualUploadDetailedNotificationID,
|
||||
);
|
||||
await _backupProvider.notifyBackgroundServiceCanRun();
|
||||
return false;
|
||||
return !hasErrors;
|
||||
}
|
||||
|
||||
void _handleAppInActivity() {
|
||||
final appState = ref.read(appStateProvider.notifier).getAppState();
|
||||
// The app is currently in background. Perform the necessary cleanups which
|
||||
// are on-hold for upload completion
|
||||
if (appState != AppStateEnum.active || appState != AppStateEnum.resumed) {
|
||||
if (appState != AppStateEnum.active && appState != AppStateEnum.resumed) {
|
||||
ref.read(appStateProvider.notifier).handleAppInactivity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ class AlbumInfoCard extends HookConsumerWidget {
|
||||
bottom: 10,
|
||||
right: 25,
|
||||
child: buildSelectedTextBox(),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -218,7 +218,7 @@ class AlbumInfoCard extends HookConsumerWidget {
|
||||
}),
|
||||
future: albumInfo.assetCount,
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -212,7 +212,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
|
||||
Text(
|
||||
" ${uploadProgress.toStringAsFixed(0)}%",
|
||||
style: const TextStyle(fontSize: 12),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -247,7 +247,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
||||
child: Wrap(
|
||||
children: [
|
||||
...buildSelectedAlbumNameChip(),
|
||||
...buildExcludedAlbumNameChip()
|
||||
...buildExcludedAlbumNameChip(),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -301,7 +301,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
||||
.watch(backupProvider)
|
||||
.availableAlbums
|
||||
.length
|
||||
.toString()
|
||||
.toString(),
|
||||
],
|
||||
),
|
||||
style: const TextStyle(
|
||||
|
||||
@@ -26,7 +26,7 @@ import 'package:immich_mobile/shared/ui/confirm_dialog.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
|
||||
class BackupControllerPage extends HookConsumerWidget {
|
||||
const BackupControllerPage({Key? key}) : super(key: key);
|
||||
@@ -114,7 +114,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
);
|
||||
return;
|
||||
}
|
||||
Wakelock.enable();
|
||||
WakelockPlus.enable();
|
||||
const limit = 100;
|
||||
final toDelete = await ref
|
||||
.read(backupVerificationServiceProvider)
|
||||
@@ -140,7 +140,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
Wakelock.disable();
|
||||
WakelockPlus.disable();
|
||||
checkInProgress.value = false;
|
||||
}
|
||||
}
|
||||
@@ -202,7 +202,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
child: const Text('backup_controller_page_storage_format').tr(
|
||||
args: [
|
||||
backupState.serverInfo.diskUse,
|
||||
backupState.serverInfo.diskSize
|
||||
backupState.serverInfo.diskSize,
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -256,7 +256,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -624,7 +624,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
style: TextStyle(fontSize: 12),
|
||||
).tr(),
|
||||
buildSelectedAlbumName(),
|
||||
buildExcludedAlbumName()
|
||||
buildExcludedAlbumName(),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -776,7 +776,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
||||
const Divider(),
|
||||
const CurrentUploadingAssetInfoBox(),
|
||||
if (!hasExclusiveAccess) buildBackgroundBackupInfo(),
|
||||
buildBackupButton()
|
||||
buildBackupButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -129,7 +129,7 @@ class FailedBackupStatusPage extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -83,7 +83,7 @@ class FavoritesPage extends HookConsumerWidget {
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
onTap: processing.value ? null : unfavorite,
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -108,7 +108,7 @@ class FavoritesPage extends HookConsumerWidget {
|
||||
selectionActive: selectionEnabledHook.value,
|
||||
listener: selectionListener,
|
||||
),
|
||||
if (selectionEnabledHook.value) buildBottomBar()
|
||||
if (selectionEnabledHook.value) buildBottomBar(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -57,7 +57,7 @@ class GroupDividerTitle extends ConsumerWidget {
|
||||
Icons.check_circle_outline_rounded,
|
||||
color: Colors.grey,
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -89,7 +89,7 @@ class ImmichAssetGrid extends HookConsumerWidget {
|
||||
perRow.value = 7 - scaleFactor.value.toInt();
|
||||
}
|
||||
};
|
||||
})
|
||||
}),
|
||||
},
|
||||
child: ImmichAssetGridView(
|
||||
onRefresh: onRefresh,
|
||||
|
||||
@@ -225,7 +225,7 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
||||
right: i + 1 == num ? 0.0 : widget.margin,
|
||||
),
|
||||
color: Colors.grey,
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -300,7 +300,13 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
||||
}
|
||||
|
||||
Text _labelBuilder(int pos) {
|
||||
final date = widget.renderList.elements[pos].date;
|
||||
final maxLength = widget.renderList.elements.length;
|
||||
if (pos < 0 || pos >= maxLength) {
|
||||
return const Text("");
|
||||
}
|
||||
|
||||
final date = widget.renderList.elements[pos % maxLength].date;
|
||||
|
||||
return Text(
|
||||
DateFormat.yMMMM().format(date),
|
||||
style: const TextStyle(
|
||||
@@ -335,7 +341,8 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
||||
itemBuilder: _itemBuilder,
|
||||
itemPositionsListener: _itemPositionsListener,
|
||||
itemScrollController: _itemScrollController,
|
||||
itemCount: widget.renderList.elements.length + (widget.topWidget != null ? 1 : 0),
|
||||
itemCount: widget.renderList.elements.length +
|
||||
(widget.topWidget != null ? 1 : 0),
|
||||
addRepaintBoundaries: true,
|
||||
);
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ class ControlBottomAppBar extends ConsumerWidget {
|
||||
if (hasRemote)
|
||||
const SliverToBoxAdapter(
|
||||
child: SizedBox(height: 200),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/user_circle_avatar.dart';
|
||||
import 'package:immich_mobile/shared/models/store.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
|
||||
@@ -29,7 +30,7 @@ class HomePageAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
||||
backupState.backgroundBackup || backupState.autoBackup;
|
||||
final ServerInfoState serverInfoState = ref.watch(serverInfoProvider);
|
||||
AuthenticationState authState = ref.watch(authenticationProvider);
|
||||
|
||||
final user = Store.get(StoreKey.currentUser);
|
||||
buildProfilePhoto() {
|
||||
if (authState.profileImagePath.isEmpty) {
|
||||
return IconButton(
|
||||
@@ -47,9 +48,10 @@ class HomePageAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
||||
onTap: () {
|
||||
Scaffold.of(context).openDrawer();
|
||||
},
|
||||
child: const UserCircleAvatar(
|
||||
child: UserCircleAvatar(
|
||||
radius: 18,
|
||||
size: 33,
|
||||
user: user,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ class ProfileDrawer extends HookConsumerWidget {
|
||||
buildSignOutButton(),
|
||||
],
|
||||
),
|
||||
const ServerInfoBox()
|
||||
const ServerInfoBox(),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -3,7 +3,8 @@ import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:immich_mobile/modules/home/providers/upload_profile_image.provider.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/user_circle_avatar.dart';
|
||||
import 'package:immich_mobile/shared/models/store.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
@@ -19,11 +20,13 @@ class ProfileDrawerHeader extends HookConsumerWidget {
|
||||
final uploadProfileImageStatus =
|
||||
ref.watch(uploadProfileImageProvider).status;
|
||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
final user = Store.get(StoreKey.currentUser);
|
||||
|
||||
buildUserProfileImage() {
|
||||
var userImage = const UserCircleAvatar(
|
||||
var userImage = UserCircleAvatar(
|
||||
radius: 35,
|
||||
size: 66,
|
||||
user: user,
|
||||
);
|
||||
|
||||
if (authState.profileImagePath.isEmpty) {
|
||||
@@ -153,7 +156,7 @@ class ProfileDrawerHeader extends HookConsumerWidget {
|
||||
Text(
|
||||
authState.userEmail,
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
import 'package:immich_mobile/shared/models/store.dart';
|
||||
import 'package:immich_mobile/shared/ui/transparent_image.dart';
|
||||
|
||||
class UserCircleAvatar extends ConsumerWidget {
|
||||
final double radius;
|
||||
final double size;
|
||||
const UserCircleAvatar({super.key, required this.radius, required this.size});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
AuthenticationState authState = ref.watch(authenticationProvider);
|
||||
|
||||
var profileImageUrl =
|
||||
'${Store.get(StoreKey.serverEndpoint)}/user/profile-image/${authState.userId}?d=${Random().nextInt(1024)}';
|
||||
return CircleAvatar(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
radius: radius,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: FadeInImage(
|
||||
fit: BoxFit.cover,
|
||||
placeholder: MemoryImage(kTransparentImage),
|
||||
width: size,
|
||||
height: size,
|
||||
image: NetworkImage(
|
||||
profileImageUrl,
|
||||
headers: {
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}"
|
||||
},
|
||||
),
|
||||
fadeInDuration: const Duration(milliseconds: 200),
|
||||
imageErrorBuilder: (context, error, stackTrace) =>
|
||||
Image.memory(kTransparentImage),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -221,7 +221,7 @@ class HomePage extends HookConsumerWidget {
|
||||
namedArgs: {
|
||||
"album": album.name,
|
||||
"added": result.successfullyAdded.toString(),
|
||||
"failed": result.alreadyInAlbum.length.toString()
|
||||
"failed": result.alreadyInAlbum.length.toString(),
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -323,7 +323,7 @@ class HomePage extends HookConsumerWidget {
|
||||
).tr(),
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -365,7 +365,7 @@ class HomePage extends HookConsumerWidget {
|
||||
enabled: !processing.value,
|
||||
selectionAssetState: selectionAssetState.value,
|
||||
),
|
||||
if (processing.value) const Center(child: ImmichLoadingIndicator())
|
||||
if (processing.value) const Center(child: ImmichLoadingIndicator()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -94,7 +94,7 @@ class ChangePasswordForm extends HookConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -110,7 +110,7 @@ class MemoryCard extends HookConsumerWidget {
|
||||
left: 18.0,
|
||||
bottom: 18.0,
|
||||
child: buildTitle(),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -153,6 +153,7 @@ class PermissionOnboardingPage extends HookConsumerWidget {
|
||||
child = buildRequestPermission();
|
||||
break;
|
||||
case PermissionStatus.granted:
|
||||
case PermissionStatus.provisional:
|
||||
child = buildPermissionGranted();
|
||||
break;
|
||||
case PermissionStatus.restricted:
|
||||
@@ -183,7 +184,7 @@ class PermissionOnboardingPage extends HookConsumerWidget {
|
||||
),
|
||||
TextButton(
|
||||
child: const Text('permission_onboarding_log_out').tr(),
|
||||
onPressed: () {
|
||||
onPressed: () {
|
||||
ref.read(authenticationProvider.notifier).logout();
|
||||
AutoRouter.of(context).replace(
|
||||
const LoginRoute(),
|
||||
|
||||
@@ -44,7 +44,7 @@ class PartnerPage extends HookConsumerWidget {
|
||||
Text("${u.firstName} ${u.lastName}"),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -151,7 +151,7 @@ class PartnerPage extends HookConsumerWidget {
|
||||
availableUsers.whenOrNull(data: (data) => addNewUsersHandler),
|
||||
icon: const Icon(Icons.person_add),
|
||||
tooltip: "partner_page_add_partner".tr(),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
body: buildUserList(partners),
|
||||
|
||||
@@ -50,7 +50,7 @@ class CuratedPeopleRow extends StatelessWidget {
|
||||
itemBuilder: (context, index) {
|
||||
final person = content[index];
|
||||
final headers = {
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}"
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
};
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 18.0),
|
||||
@@ -102,7 +102,7 @@ class CuratedPeopleRow extends StatelessWidget {
|
||||
fontSize: 13.0,
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -39,7 +39,7 @@ class SearchSuggestionList extends ConsumerWidget {
|
||||
color: Theme.of(context).primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -46,7 +46,7 @@ class ThumbnailWithInfo extends StatelessWidget {
|
||||
imageUrl: imageUrl!,
|
||||
httpHeaders: {
|
||||
"Authorization":
|
||||
"Bearer ${Store.get(StoreKey.accessToken)}"
|
||||
"Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
},
|
||||
errorWidget: (context, url, error) =>
|
||||
const Icon(Icons.image_not_supported_outlined),
|
||||
|
||||
@@ -56,7 +56,7 @@ class PersonResultPage extends HookConsumerWidget {
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
onTap: showEditNameDialog,
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -134,7 +134,7 @@ class PersonResultPage extends HookConsumerWidget {
|
||||
getFaceThumbnailUrl(personId),
|
||||
headers: {
|
||||
"Authorization":
|
||||
"Bearer ${isar_store.Store.get(isar_store.StoreKey.accessToken)}"
|
||||
"Bearer ${isar_store.Store.get(isar_store.StoreKey.accessToken)}",
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -42,7 +42,7 @@ class SettingsPage extends HookConsumerWidget {
|
||||
const AssetListSettings(),
|
||||
const NotificationSetting(),
|
||||
// const ExperimentalSettings(),
|
||||
const AdvancedSettings()
|
||||
const AdvancedSettings(),
|
||||
],
|
||||
).toList(),
|
||||
],
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/album/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/album/views/album_options_part.dart';
|
||||
import 'package:immich_mobile/modules/album/views/album_viewer_page.dart';
|
||||
import 'package:immich_mobile/modules/album/views/asset_selection_page.dart';
|
||||
import 'package:immich_mobile/modules/album/views/create_album_page.dart';
|
||||
@@ -74,7 +75,7 @@ part 'router.gr.dart';
|
||||
AutoRoute(page: HomePage, guards: [AuthGuard, DuplicateGuard]),
|
||||
AutoRoute(page: SearchPage, guards: [AuthGuard, DuplicateGuard]),
|
||||
AutoRoute(page: SharingPage, guards: [AuthGuard, DuplicateGuard]),
|
||||
AutoRoute(page: LibraryPage, guards: [AuthGuard, DuplicateGuard])
|
||||
AutoRoute(page: LibraryPage, guards: [AuthGuard, DuplicateGuard]),
|
||||
],
|
||||
transitionsBuilder: TransitionsBuilders.fadeIn,
|
||||
),
|
||||
@@ -152,6 +153,7 @@ part 'router.gr.dart';
|
||||
),
|
||||
AutoRoute(page: AllPeoplePage, guards: [AuthGuard, DuplicateGuard]),
|
||||
AutoRoute(page: MemoryPage, guards: [AuthGuard, DuplicateGuard]),
|
||||
AutoRoute(page: AlbumOptionsPage, guards: [AuthGuard, DuplicateGuard]),
|
||||
],
|
||||
)
|
||||
class AppRouter extends _$AppRouter {
|
||||
|
||||
@@ -296,6 +296,16 @@ class _$AppRouter extends RootStackRouter {
|
||||
),
|
||||
);
|
||||
},
|
||||
AlbumOptionsRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<AlbumOptionsRouteArgs>();
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: AlbumOptionsPage(
|
||||
key: args.key,
|
||||
album: args.album,
|
||||
),
|
||||
);
|
||||
},
|
||||
HomeRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
@@ -595,6 +605,14 @@ class _$AppRouter extends RootStackRouter {
|
||||
duplicateGuard,
|
||||
],
|
||||
),
|
||||
RouteConfig(
|
||||
AlbumOptionsRoute.name,
|
||||
path: '/album-options-page',
|
||||
guards: [
|
||||
authGuard,
|
||||
duplicateGuard,
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1319,6 +1337,40 @@ class MemoryRouteArgs {
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [AlbumOptionsPage]
|
||||
class AlbumOptionsRoute extends PageRouteInfo<AlbumOptionsRouteArgs> {
|
||||
AlbumOptionsRoute({
|
||||
Key? key,
|
||||
required Album album,
|
||||
}) : super(
|
||||
AlbumOptionsRoute.name,
|
||||
path: '/album-options-page',
|
||||
args: AlbumOptionsRouteArgs(
|
||||
key: key,
|
||||
album: album,
|
||||
),
|
||||
);
|
||||
|
||||
static const String name = 'AlbumOptionsRoute';
|
||||
}
|
||||
|
||||
class AlbumOptionsRouteArgs {
|
||||
const AlbumOptionsRouteArgs({
|
||||
this.key,
|
||||
required this.album,
|
||||
});
|
||||
|
||||
final Key? key;
|
||||
|
||||
final Album album;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlbumOptionsRouteArgs{key: $key, album: $album}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [HomePage]
|
||||
class HomeRoute extends PageRouteInfo<void> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class ServerInfoState {
|
||||
final ServerVersionReponseDto serverVersion;
|
||||
final ServerVersionResponseDto serverVersion;
|
||||
final bool isVersionMismatch;
|
||||
final String versionMismatchErrorMessage;
|
||||
|
||||
@@ -12,7 +12,7 @@ class ServerInfoState {
|
||||
});
|
||||
|
||||
ServerInfoState copyWith({
|
||||
ServerVersionReponseDto? serverVersion,
|
||||
ServerVersionResponseDto? serverVersion,
|
||||
bool? isVersionMismatch,
|
||||
String? versionMismatchErrorMessage,
|
||||
}) {
|
||||
|
||||
@@ -157,7 +157,7 @@ User _userDeserialize(
|
||||
isPartnerSharedBy: reader.readBoolOrNull(offsets[4]) ?? false,
|
||||
isPartnerSharedWith: reader.readBoolOrNull(offsets[5]) ?? false,
|
||||
lastName: reader.readString(offsets[6]),
|
||||
memoryEnabled: reader.readBoolOrNull(offsets[7]) ?? true,
|
||||
memoryEnabled: reader.readBoolOrNull(offsets[7]),
|
||||
profileImagePath: reader.readStringOrNull(offsets[8]) ?? '',
|
||||
updatedAt: reader.readDateTime(offsets[9]),
|
||||
);
|
||||
@@ -186,7 +186,7 @@ P _userDeserializeProp<P>(
|
||||
case 6:
|
||||
return (reader.readString(offset)) as P;
|
||||
case 7:
|
||||
return (reader.readBoolOrNull(offset) ?? true) as P;
|
||||
return (reader.readBoolOrNull(offset)) as P;
|
||||
case 8:
|
||||
return (reader.readStringOrNull(offset) ?? '') as P;
|
||||
case 9:
|
||||
@@ -979,8 +979,24 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNull(
|
||||
property: r'memoryEnabled',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(const FilterCondition.isNotNull(
|
||||
property: r'memoryEnabled',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<User, User, QAfterFilterCondition> memoryEnabledEqualTo(
|
||||
bool value) {
|
||||
bool? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'memoryEnabled',
|
||||
@@ -1661,7 +1677,7 @@ extension UserQueryProperty on QueryBuilder<User, User, QQueryProperty> {
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<User, bool, QQueryOperations> memoryEnabledProperty() {
|
||||
QueryBuilder<User, bool?, QQueryOperations> memoryEnabledProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'memoryEnabled');
|
||||
});
|
||||
|
||||
7
mobile/lib/shared/providers/admin_provider.dart
Normal file
7
mobile/lib/shared/providers/admin_provider.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/providers/user.provider.dart';
|
||||
|
||||
final isAdminProvider = Provider<bool>((ref) {
|
||||
final currentUser = ref.watch(currentUserProvider);
|
||||
return currentUser?.isAdmin ?? false; // Default to non-admin if no user
|
||||
});
|
||||
@@ -21,6 +21,7 @@ enum AppStateEnum {
|
||||
paused,
|
||||
resumed,
|
||||
detached,
|
||||
hidden,
|
||||
}
|
||||
|
||||
class AppStateNotiifer extends StateNotifier<AppStateEnum> {
|
||||
@@ -84,6 +85,10 @@ class AppStateNotiifer extends StateNotifier<AppStateEnum> {
|
||||
state = AppStateEnum.detached;
|
||||
ref.watch(manualUploadProvider.notifier).cancelBackup();
|
||||
}
|
||||
|
||||
void handleAppHidden() {
|
||||
state = AppStateEnum.hidden;
|
||||
}
|
||||
}
|
||||
|
||||
final appStateProvider =
|
||||
|
||||
@@ -10,7 +10,7 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
|
||||
ServerInfoNotifier(this._serverInfoService)
|
||||
: super(
|
||||
ServerInfoState(
|
||||
serverVersion: ServerVersionReponseDto(
|
||||
serverVersion: ServerVersionResponseDto(
|
||||
major: 0,
|
||||
patch_: 0,
|
||||
minor: 0,
|
||||
@@ -23,7 +23,7 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
|
||||
final ServerInfoService _serverInfoService;
|
||||
|
||||
getServerVersion() async {
|
||||
ServerVersionReponseDto? serverVersion =
|
||||
ServerVersionResponseDto? serverVersion =
|
||||
await _serverInfoService.getServerVersion();
|
||||
|
||||
if (serverVersion == null) {
|
||||
|
||||
@@ -102,7 +102,7 @@ class LocalNotificationService {
|
||||
cancelUploadActionID,
|
||||
'Cancel',
|
||||
showsUserInterface: true,
|
||||
)
|
||||
),
|
||||
]
|
||||
: null,
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ class ServerInfoService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<ServerVersionReponseDto?> getServerVersion() async {
|
||||
Future<ServerVersionResponseDto?> getServerVersion() async {
|
||||
try {
|
||||
return await _apiService.serverInfoApi.getServerVersion();
|
||||
} catch (e) {
|
||||
|
||||
@@ -16,7 +16,7 @@ class IgnorableChangeNotifier extends ChangeNotifier {
|
||||
if (_ignorableListeners == null) {
|
||||
AssertionError([
|
||||
'A $runtimeType was used after being disposed.',
|
||||
'Once you have called dispose() on a $runtimeType, it can no longer be used.'
|
||||
'Once you have called dispose() on a $runtimeType, it can no longer be used.',
|
||||
]);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -15,7 +15,7 @@ class ShareDialog extends StatelessWidget {
|
||||
margin: const EdgeInsets.only(top: 12),
|
||||
child: const Text('share_dialog_preparing')
|
||||
.tr(),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
75
mobile/lib/shared/ui/user_circle_avatar.dart
Normal file
75
mobile/lib/shared/ui/user_circle_avatar.dart
Normal file
@@ -0,0 +1,75 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/models/store.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/ui/transparent_image.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class UserCircleAvatar extends ConsumerWidget {
|
||||
final User user;
|
||||
double radius;
|
||||
double size;
|
||||
bool useRandomBackgroundColor;
|
||||
|
||||
UserCircleAvatar({
|
||||
super.key,
|
||||
this.radius = 22,
|
||||
this.size = 44,
|
||||
this.useRandomBackgroundColor = false,
|
||||
required this.user,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final randomColors = [
|
||||
Colors.red[200],
|
||||
Colors.blue[200],
|
||||
Colors.green[200],
|
||||
Colors.yellow[200],
|
||||
Colors.purple[200],
|
||||
Colors.orange[200],
|
||||
Colors.pink[200],
|
||||
Colors.teal[200],
|
||||
Colors.indigo[200],
|
||||
Colors.cyan[200],
|
||||
Colors.brown[200],
|
||||
];
|
||||
|
||||
final profileImageUrl =
|
||||
'${Store.get(StoreKey.serverEndpoint)}/user/profile-image/${user.id}?d=${Random().nextInt(1024)}';
|
||||
return CircleAvatar(
|
||||
backgroundColor: useRandomBackgroundColor
|
||||
? randomColors[Random().nextInt(randomColors.length)]
|
||||
: Theme.of(context).primaryColor,
|
||||
radius: radius,
|
||||
child: user.profileImagePath == ""
|
||||
? Text(
|
||||
user.firstName[0],
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
)
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: FadeInImage(
|
||||
fit: BoxFit.cover,
|
||||
placeholder: MemoryImage(kTransparentImage),
|
||||
width: size,
|
||||
height: size,
|
||||
image: NetworkImage(
|
||||
profileImageUrl,
|
||||
headers: {
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
},
|
||||
),
|
||||
fadeInDuration: const Duration(milliseconds: 200),
|
||||
imageErrorBuilder: (context, error, stackTrace) =>
|
||||
Image.memory(kTransparentImage),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ class AppLogDetailPage extends HookConsumerWidget {
|
||||
size: 16.0,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
@@ -106,7 +106,7 @@ class AppLogDetailPage extends HookConsumerWidget {
|
||||
size: 16.0,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
@@ -181,7 +181,7 @@ class AppLogDetailPage extends HookConsumerWidget {
|
||||
if (logMessage.context1 != null)
|
||||
buildLogContext1(logMessage.context1.toString()),
|
||||
if (logMessage.context2 != null)
|
||||
buildStackMessage(logMessage.context2.toString())
|
||||
buildStackMessage(logMessage.context2.toString()),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -148,7 +148,7 @@ class TabControllerPage extends HookConsumerWidget {
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -159,7 +159,7 @@ class TabControllerPage extends HookConsumerWidget {
|
||||
const HomeRoute(),
|
||||
SearchRoute(),
|
||||
const SharingRoute(),
|
||||
const LibraryRoute()
|
||||
const LibraryRoute(),
|
||||
],
|
||||
builder: (context, child, animation) {
|
||||
final tabsRouter = AutoTabsRouter.of(context);
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/providers/release_info.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/admin_provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class VersionAnnouncementOverlay extends HookConsumerWidget {
|
||||
@@ -12,6 +13,12 @@ class VersionAnnouncementOverlay extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final bool isAdmin = ref.watch(isAdminProvider);
|
||||
|
||||
if (!isAdmin) {
|
||||
return const SizedBox.shrink(); // Don't show anything for non-admins
|
||||
}
|
||||
|
||||
void goToReleaseNote() async {
|
||||
final Uri url =
|
||||
Uri.parse('https://github.com/immich-app/immich/releases/latest');
|
||||
@@ -92,7 +99,7 @@ class VersionAnnouncementOverlay extends HookConsumerWidget {
|
||||
text:
|
||||
"version_announcement_overlay_text_3"
|
||||
.tr(),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -119,7 +126,7 @@ class VersionAnnouncementOverlay extends HookConsumerWidget {
|
||||
),
|
||||
).tr(),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
21
mobile/openapi/.openapi-generator/FILES
generated
21
mobile/openapi/.openapi-generator/FILES
generated
@@ -15,6 +15,7 @@ doc/AlbumCountResponseDto.md
|
||||
doc/AlbumResponseDto.md
|
||||
doc/AllJobStatusResponseDto.md
|
||||
doc/AssetApi.md
|
||||
doc/AssetBulkUpdateDto.md
|
||||
doc/AssetBulkUploadCheckDto.md
|
||||
doc/AssetBulkUploadCheckItem.md
|
||||
doc/AssetBulkUploadCheckResponseDto.md
|
||||
@@ -22,6 +23,8 @@ doc/AssetBulkUploadCheckResult.md
|
||||
doc/AssetFileUploadResponseDto.md
|
||||
doc/AssetIdsDto.md
|
||||
doc/AssetIdsResponseDto.md
|
||||
doc/AssetJobName.md
|
||||
doc/AssetJobsDto.md
|
||||
doc/AssetResponseDto.md
|
||||
doc/AssetStatsResponseDto.md
|
||||
doc/AssetTypeEnum.md
|
||||
@@ -45,6 +48,7 @@ doc/DeleteAssetDto.md
|
||||
doc/DeleteAssetResponseDto.md
|
||||
doc/DeleteAssetStatus.md
|
||||
doc/DownloadArchiveInfo.md
|
||||
doc/DownloadInfoDto.md
|
||||
doc/DownloadResponseDto.md
|
||||
doc/ExifResponseDto.md
|
||||
doc/ImportAssetDto.md
|
||||
@@ -83,12 +87,13 @@ doc/SearchExploreResponseDto.md
|
||||
doc/SearchFacetCountResponseDto.md
|
||||
doc/SearchFacetResponseDto.md
|
||||
doc/SearchResponseDto.md
|
||||
doc/ServerFeaturesDto.md
|
||||
doc/ServerInfoApi.md
|
||||
doc/ServerInfoResponseDto.md
|
||||
doc/ServerMediaTypesResponseDto.md
|
||||
doc/ServerPingResponse.md
|
||||
doc/ServerStatsResponseDto.md
|
||||
doc/ServerVersionReponseDto.md
|
||||
doc/ServerVersionResponseDto.md
|
||||
doc/SharedLinkApi.md
|
||||
doc/SharedLinkCreateDto.md
|
||||
doc/SharedLinkEditDto.md
|
||||
@@ -157,6 +162,7 @@ lib/model/api_key_create_dto.dart
|
||||
lib/model/api_key_create_response_dto.dart
|
||||
lib/model/api_key_response_dto.dart
|
||||
lib/model/api_key_update_dto.dart
|
||||
lib/model/asset_bulk_update_dto.dart
|
||||
lib/model/asset_bulk_upload_check_dto.dart
|
||||
lib/model/asset_bulk_upload_check_item.dart
|
||||
lib/model/asset_bulk_upload_check_response_dto.dart
|
||||
@@ -164,6 +170,8 @@ lib/model/asset_bulk_upload_check_result.dart
|
||||
lib/model/asset_file_upload_response_dto.dart
|
||||
lib/model/asset_ids_dto.dart
|
||||
lib/model/asset_ids_response_dto.dart
|
||||
lib/model/asset_job_name.dart
|
||||
lib/model/asset_jobs_dto.dart
|
||||
lib/model/asset_response_dto.dart
|
||||
lib/model/asset_stats_response_dto.dart
|
||||
lib/model/asset_type_enum.dart
|
||||
@@ -186,6 +194,7 @@ 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_info_dto.dart
|
||||
lib/model/download_response_dto.dart
|
||||
lib/model/exif_response_dto.dart
|
||||
lib/model/import_asset_dto.dart
|
||||
@@ -219,11 +228,12 @@ lib/model/search_explore_response_dto.dart
|
||||
lib/model/search_facet_count_response_dto.dart
|
||||
lib/model/search_facet_response_dto.dart
|
||||
lib/model/search_response_dto.dart
|
||||
lib/model/server_features_dto.dart
|
||||
lib/model/server_info_response_dto.dart
|
||||
lib/model/server_media_types_response_dto.dart
|
||||
lib/model/server_ping_response.dart
|
||||
lib/model/server_stats_response_dto.dart
|
||||
lib/model/server_version_reponse_dto.dart
|
||||
lib/model/server_version_response_dto.dart
|
||||
lib/model/shared_link_create_dto.dart
|
||||
lib/model/shared_link_edit_dto.dart
|
||||
lib/model/shared_link_response_dto.dart
|
||||
@@ -268,6 +278,7 @@ test/api_key_create_response_dto_test.dart
|
||||
test/api_key_response_dto_test.dart
|
||||
test/api_key_update_dto_test.dart
|
||||
test/asset_api_test.dart
|
||||
test/asset_bulk_update_dto_test.dart
|
||||
test/asset_bulk_upload_check_dto_test.dart
|
||||
test/asset_bulk_upload_check_item_test.dart
|
||||
test/asset_bulk_upload_check_response_dto_test.dart
|
||||
@@ -275,6 +286,8 @@ test/asset_bulk_upload_check_result_test.dart
|
||||
test/asset_file_upload_response_dto_test.dart
|
||||
test/asset_ids_dto_test.dart
|
||||
test/asset_ids_response_dto_test.dart
|
||||
test/asset_job_name_test.dart
|
||||
test/asset_jobs_dto_test.dart
|
||||
test/asset_response_dto_test.dart
|
||||
test/asset_stats_response_dto_test.dart
|
||||
test/asset_type_enum_test.dart
|
||||
@@ -298,6 +311,7 @@ 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_info_dto_test.dart
|
||||
test/download_response_dto_test.dart
|
||||
test/exif_response_dto_test.dart
|
||||
test/import_asset_dto_test.dart
|
||||
@@ -336,12 +350,13 @@ test/search_explore_response_dto_test.dart
|
||||
test/search_facet_count_response_dto_test.dart
|
||||
test/search_facet_response_dto_test.dart
|
||||
test/search_response_dto_test.dart
|
||||
test/server_features_dto_test.dart
|
||||
test/server_info_api_test.dart
|
||||
test/server_info_response_dto_test.dart
|
||||
test/server_media_types_response_dto_test.dart
|
||||
test/server_ping_response_test.dart
|
||||
test/server_stats_response_dto_test.dart
|
||||
test/server_version_reponse_dto_test.dart
|
||||
test/server_version_response_dto_test.dart
|
||||
test/shared_link_api_test.dart
|
||||
test/shared_link_create_dto_test.dart
|
||||
test/shared_link_edit_dto_test.dart
|
||||
|
||||
16
mobile/openapi/README.md
generated
16
mobile/openapi/README.md
generated
@@ -3,7 +3,7 @@ Immich API
|
||||
|
||||
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
||||
|
||||
- API version: 1.73.0
|
||||
- API version: 1.74.0
|
||||
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||
|
||||
## Requirements
|
||||
@@ -91,7 +91,7 @@ Class | Method | HTTP request | Description
|
||||
*AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
||||
*AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
||||
*AssetApi* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset |
|
||||
*AssetApi* | [**downloadArchive**](doc//AssetApi.md#downloadarchive) | **POST** /asset/download |
|
||||
*AssetApi* | [**downloadArchive**](doc//AssetApi.md#downloadarchive) | **POST** /asset/download/archive |
|
||||
*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **POST** /asset/download/{id} |
|
||||
*AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |
|
||||
*AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |
|
||||
@@ -101,15 +101,17 @@ Class | Method | HTTP request | Description
|
||||
*AssetApi* | [**getByTimeBucket**](doc//AssetApi.md#getbytimebucket) | **GET** /asset/time-bucket |
|
||||
*AssetApi* | [**getCuratedLocations**](doc//AssetApi.md#getcuratedlocations) | **GET** /asset/curated-locations |
|
||||
*AssetApi* | [**getCuratedObjects**](doc//AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects |
|
||||
*AssetApi* | [**getDownloadInfo**](doc//AssetApi.md#getdownloadinfo) | **GET** /asset/download |
|
||||
*AssetApi* | [**getDownloadInfo**](doc//AssetApi.md#getdownloadinfo) | **POST** /asset/download/info |
|
||||
*AssetApi* | [**getMapMarkers**](doc//AssetApi.md#getmapmarkers) | **GET** /asset/map-marker |
|
||||
*AssetApi* | [**getMemoryLane**](doc//AssetApi.md#getmemorylane) | **GET** /asset/memory-lane |
|
||||
*AssetApi* | [**getTimeBuckets**](doc//AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets |
|
||||
*AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
||||
*AssetApi* | [**importFile**](doc//AssetApi.md#importfile) | **POST** /asset/import |
|
||||
*AssetApi* | [**runAssetJobs**](doc//AssetApi.md#runassetjobs) | **POST** /asset/jobs |
|
||||
*AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search |
|
||||
*AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} |
|
||||
*AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{id} |
|
||||
*AssetApi* | [**updateAssets**](doc//AssetApi.md#updateassets) | **PUT** /asset |
|
||||
*AssetApi* | [**uploadFile**](doc//AssetApi.md#uploadfile) | **POST** /asset/upload |
|
||||
*AuthenticationApi* | [**adminSignUp**](doc//AuthenticationApi.md#adminsignup) | **POST** /auth/admin-sign-up |
|
||||
*AuthenticationApi* | [**changePassword**](doc//AuthenticationApi.md#changepassword) | **POST** /auth/change-password |
|
||||
@@ -139,6 +141,7 @@ Class | Method | HTTP request | Description
|
||||
*SearchApi* | [**getExploreData**](doc//SearchApi.md#getexploredata) | **GET** /search/explore |
|
||||
*SearchApi* | [**getSearchConfig**](doc//SearchApi.md#getsearchconfig) | **GET** /search/config |
|
||||
*SearchApi* | [**search**](doc//SearchApi.md#search) | **GET** /search |
|
||||
*ServerInfoApi* | [**getServerFeatures**](doc//ServerInfoApi.md#getserverfeatures) | **GET** /server-info/features |
|
||||
*ServerInfoApi* | [**getServerInfo**](doc//ServerInfoApi.md#getserverinfo) | **GET** /server-info |
|
||||
*ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
|
||||
*ServerInfoApi* | [**getStats**](doc//ServerInfoApi.md#getstats) | **GET** /server-info/stats |
|
||||
@@ -187,6 +190,7 @@ Class | Method | HTTP request | Description
|
||||
- [AlbumCountResponseDto](doc//AlbumCountResponseDto.md)
|
||||
- [AlbumResponseDto](doc//AlbumResponseDto.md)
|
||||
- [AllJobStatusResponseDto](doc//AllJobStatusResponseDto.md)
|
||||
- [AssetBulkUpdateDto](doc//AssetBulkUpdateDto.md)
|
||||
- [AssetBulkUploadCheckDto](doc//AssetBulkUploadCheckDto.md)
|
||||
- [AssetBulkUploadCheckItem](doc//AssetBulkUploadCheckItem.md)
|
||||
- [AssetBulkUploadCheckResponseDto](doc//AssetBulkUploadCheckResponseDto.md)
|
||||
@@ -194,6 +198,8 @@ Class | Method | HTTP request | Description
|
||||
- [AssetFileUploadResponseDto](doc//AssetFileUploadResponseDto.md)
|
||||
- [AssetIdsDto](doc//AssetIdsDto.md)
|
||||
- [AssetIdsResponseDto](doc//AssetIdsResponseDto.md)
|
||||
- [AssetJobName](doc//AssetJobName.md)
|
||||
- [AssetJobsDto](doc//AssetJobsDto.md)
|
||||
- [AssetResponseDto](doc//AssetResponseDto.md)
|
||||
- [AssetStatsResponseDto](doc//AssetStatsResponseDto.md)
|
||||
- [AssetTypeEnum](doc//AssetTypeEnum.md)
|
||||
@@ -216,6 +222,7 @@ Class | Method | HTTP request | Description
|
||||
- [DeleteAssetResponseDto](doc//DeleteAssetResponseDto.md)
|
||||
- [DeleteAssetStatus](doc//DeleteAssetStatus.md)
|
||||
- [DownloadArchiveInfo](doc//DownloadArchiveInfo.md)
|
||||
- [DownloadInfoDto](doc//DownloadInfoDto.md)
|
||||
- [DownloadResponseDto](doc//DownloadResponseDto.md)
|
||||
- [ExifResponseDto](doc//ExifResponseDto.md)
|
||||
- [ImportAssetDto](doc//ImportAssetDto.md)
|
||||
@@ -249,11 +256,12 @@ Class | Method | HTTP request | Description
|
||||
- [SearchFacetCountResponseDto](doc//SearchFacetCountResponseDto.md)
|
||||
- [SearchFacetResponseDto](doc//SearchFacetResponseDto.md)
|
||||
- [SearchResponseDto](doc//SearchResponseDto.md)
|
||||
- [ServerFeaturesDto](doc//ServerFeaturesDto.md)
|
||||
- [ServerInfoResponseDto](doc//ServerInfoResponseDto.md)
|
||||
- [ServerMediaTypesResponseDto](doc//ServerMediaTypesResponseDto.md)
|
||||
- [ServerPingResponse](doc//ServerPingResponse.md)
|
||||
- [ServerStatsResponseDto](doc//ServerStatsResponseDto.md)
|
||||
- [ServerVersionReponseDto](doc//ServerVersionReponseDto.md)
|
||||
- [ServerVersionResponseDto](doc//ServerVersionResponseDto.md)
|
||||
- [SharedLinkCreateDto](doc//SharedLinkCreateDto.md)
|
||||
- [SharedLinkEditDto](doc//SharedLinkEditDto.md)
|
||||
- [SharedLinkResponseDto](doc//SharedLinkResponseDto.md)
|
||||
|
||||
130
mobile/openapi/doc/AssetApi.md
generated
130
mobile/openapi/doc/AssetApi.md
generated
@@ -13,7 +13,7 @@ Method | HTTP request | Description
|
||||
[**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check |
|
||||
[**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
||||
[**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset |
|
||||
[**downloadArchive**](AssetApi.md#downloadarchive) | **POST** /asset/download |
|
||||
[**downloadArchive**](AssetApi.md#downloadarchive) | **POST** /asset/download/archive |
|
||||
[**downloadFile**](AssetApi.md#downloadfile) | **POST** /asset/download/{id} |
|
||||
[**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset |
|
||||
[**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |
|
||||
@@ -23,15 +23,17 @@ Method | HTTP request | Description
|
||||
[**getByTimeBucket**](AssetApi.md#getbytimebucket) | **GET** /asset/time-bucket |
|
||||
[**getCuratedLocations**](AssetApi.md#getcuratedlocations) | **GET** /asset/curated-locations |
|
||||
[**getCuratedObjects**](AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects |
|
||||
[**getDownloadInfo**](AssetApi.md#getdownloadinfo) | **GET** /asset/download |
|
||||
[**getDownloadInfo**](AssetApi.md#getdownloadinfo) | **POST** /asset/download/info |
|
||||
[**getMapMarkers**](AssetApi.md#getmapmarkers) | **GET** /asset/map-marker |
|
||||
[**getMemoryLane**](AssetApi.md#getmemorylane) | **GET** /asset/memory-lane |
|
||||
[**getTimeBuckets**](AssetApi.md#gettimebuckets) | **GET** /asset/time-buckets |
|
||||
[**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
||||
[**importFile**](AssetApi.md#importfile) | **POST** /asset/import |
|
||||
[**runAssetJobs**](AssetApi.md#runassetjobs) | **POST** /asset/jobs |
|
||||
[**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search |
|
||||
[**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{id} |
|
||||
[**updateAsset**](AssetApi.md#updateasset) | **PUT** /asset/{id} |
|
||||
[**updateAssets**](AssetApi.md#updateassets) | **PUT** /asset |
|
||||
[**uploadFile**](AssetApi.md#uploadfile) | **POST** /asset/upload |
|
||||
|
||||
|
||||
@@ -842,7 +844,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)
|
||||
|
||||
# **getDownloadInfo**
|
||||
> DownloadResponseDto getDownloadInfo(assetIds, albumId, userId, archiveSize, key)
|
||||
> DownloadResponseDto getDownloadInfo(downloadInfoDto, key)
|
||||
|
||||
|
||||
|
||||
@@ -865,14 +867,11 @@ import 'package:openapi/api.dart';
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AssetApi();
|
||||
final assetIds = []; // List<String> |
|
||||
final albumId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final userId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final archiveSize = 8.14; // num |
|
||||
final downloadInfoDto = DownloadInfoDto(); // DownloadInfoDto |
|
||||
final key = key_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.getDownloadInfo(assetIds, albumId, userId, archiveSize, key);
|
||||
final result = api_instance.getDownloadInfo(downloadInfoDto, key);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->getDownloadInfo: $e\n');
|
||||
@@ -883,10 +882,7 @@ try {
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**assetIds** | [**List<String>**](String.md)| | [optional] [default to const []]
|
||||
**albumId** | **String**| | [optional]
|
||||
**userId** | **String**| | [optional]
|
||||
**archiveSize** | **num**| | [optional]
|
||||
**downloadInfoDto** | [**DownloadInfoDto**](DownloadInfoDto.md)| |
|
||||
**key** | **String**| | [optional]
|
||||
|
||||
### Return type
|
||||
@@ -899,7 +895,7 @@ Name | Type | Description | Notes
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[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)
|
||||
@@ -1197,6 +1193,60 @@ 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)
|
||||
|
||||
# **runAssetJobs**
|
||||
> runAssetJobs(assetJobsDto)
|
||||
|
||||
|
||||
|
||||
### 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 assetJobsDto = AssetJobsDto(); // AssetJobsDto |
|
||||
|
||||
try {
|
||||
api_instance.runAssetJobs(assetJobsDto);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->runAssetJobs: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**assetJobsDto** | [**AssetJobsDto**](AssetJobsDto.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)
|
||||
|
||||
# **searchAsset**
|
||||
> List<AssetResponseDto> searchAsset(searchAssetDto)
|
||||
|
||||
@@ -1372,6 +1422,60 @@ 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)
|
||||
|
||||
# **updateAssets**
|
||||
> updateAssets(assetBulkUpdateDto)
|
||||
|
||||
|
||||
|
||||
### 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 assetBulkUpdateDto = AssetBulkUpdateDto(); // AssetBulkUpdateDto |
|
||||
|
||||
try {
|
||||
api_instance.updateAssets(assetBulkUpdateDto);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->updateAssets: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**assetBulkUpdateDto** | [**AssetBulkUpdateDto**](AssetBulkUpdateDto.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)
|
||||
|
||||
# **uploadFile**
|
||||
> AssetFileUploadResponseDto uploadFile(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, key, duration, isArchived, isReadOnly, isVisible, livePhotoData, sidecarData)
|
||||
|
||||
|
||||
17
mobile/openapi/doc/AssetBulkUpdateDto.md
generated
Normal file
17
mobile/openapi/doc/AssetBulkUpdateDto.md
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
# openapi.model.AssetBulkUpdateDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**ids** | **List<String>** | | [default to const []]
|
||||
**isArchived** | **bool** | | [optional]
|
||||
**isFavorite** | **bool** | | [optional]
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
14
mobile/openapi/doc/AssetJobName.md
generated
Normal file
14
mobile/openapi/doc/AssetJobName.md
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
# openapi.model.AssetJobName
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
16
mobile/openapi/doc/AssetJobsDto.md
generated
Normal file
16
mobile/openapi/doc/AssetJobsDto.md
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
# openapi.model.AssetJobsDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**assetIds** | **List<String>** | | [default to const []]
|
||||
**name** | [**AssetJobName**](AssetJobName.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)
|
||||
|
||||
|
||||
18
mobile/openapi/doc/DownloadInfoDto.md
generated
Normal file
18
mobile/openapi/doc/DownloadInfoDto.md
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
# openapi.model.DownloadInfoDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**albumId** | **String** | | [optional]
|
||||
**archiveSize** | **int** | | [optional]
|
||||
**assetIds** | **List<String>** | | [optional] [default to const []]
|
||||
**userId** | **String** | | [optional]
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
1
mobile/openapi/doc/PeopleUpdateItem.md
generated
1
mobile/openapi/doc/PeopleUpdateItem.md
generated
@@ -8,6 +8,7 @@ import 'package:openapi/api.dart';
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**birthDate** | [**DateTime**](DateTime.md) | Person date of birth. | [optional]
|
||||
**featureFaceAssetId** | **String** | Asset is used to get the feature face thumbnail. | [optional]
|
||||
**id** | **String** | Person id. |
|
||||
**isHidden** | **bool** | Person visibility | [optional]
|
||||
|
||||
1
mobile/openapi/doc/PersonResponseDto.md
generated
1
mobile/openapi/doc/PersonResponseDto.md
generated
@@ -8,6 +8,7 @@ import 'package:openapi/api.dart';
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**birthDate** | [**DateTime**](DateTime.md) | |
|
||||
**id** | **String** | |
|
||||
**isHidden** | **bool** | |
|
||||
**name** | **String** | |
|
||||
|
||||
1
mobile/openapi/doc/PersonUpdateDto.md
generated
1
mobile/openapi/doc/PersonUpdateDto.md
generated
@@ -8,6 +8,7 @@ import 'package:openapi/api.dart';
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**birthDate** | [**DateTime**](DateTime.md) | Person date of birth. | [optional]
|
||||
**featureFaceAssetId** | **String** | Asset is used to get the feature face thumbnail. | [optional]
|
||||
**isHidden** | **bool** | Person visibility | [optional]
|
||||
**name** | **String** | Person name. | [optional]
|
||||
|
||||
19
mobile/openapi/doc/ServerFeaturesDto.md
generated
Normal file
19
mobile/openapi/doc/ServerFeaturesDto.md
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
# openapi.model.ServerFeaturesDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**machineLearning** | **bool** | |
|
||||
**oauth** | **bool** | |
|
||||
**oauthAutoLaunch** | **bool** | |
|
||||
**passwordLogin** | **bool** | |
|
||||
**search** | **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)
|
||||
|
||||
|
||||
42
mobile/openapi/doc/ServerInfoApi.md
generated
42
mobile/openapi/doc/ServerInfoApi.md
generated
@@ -9,6 +9,7 @@ All URIs are relative to */api*
|
||||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**getServerFeatures**](ServerInfoApi.md#getserverfeatures) | **GET** /server-info/features |
|
||||
[**getServerInfo**](ServerInfoApi.md#getserverinfo) | **GET** /server-info |
|
||||
[**getServerVersion**](ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
|
||||
[**getStats**](ServerInfoApi.md#getstats) | **GET** /server-info/stats |
|
||||
@@ -16,6 +17,43 @@ Method | HTTP request | Description
|
||||
[**pingServer**](ServerInfoApi.md#pingserver) | **GET** /server-info/ping |
|
||||
|
||||
|
||||
# **getServerFeatures**
|
||||
> ServerFeaturesDto getServerFeatures()
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final api_instance = ServerInfoApi();
|
||||
|
||||
try {
|
||||
final result = api_instance.getServerFeatures();
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling ServerInfoApi->getServerFeatures: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
This endpoint does not need any parameter.
|
||||
|
||||
### Return type
|
||||
|
||||
[**ServerFeaturesDto**](ServerFeaturesDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
No authorization required
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **Accept**: application/json
|
||||
|
||||
[[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)
|
||||
|
||||
# **getServerInfo**
|
||||
> ServerInfoResponseDto getServerInfo()
|
||||
|
||||
@@ -68,7 +106,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)
|
||||
|
||||
# **getServerVersion**
|
||||
> ServerVersionReponseDto getServerVersion()
|
||||
> ServerVersionResponseDto getServerVersion()
|
||||
|
||||
|
||||
|
||||
@@ -91,7 +129,7 @@ This endpoint does not need any parameter.
|
||||
|
||||
### Return type
|
||||
|
||||
[**ServerVersionReponseDto**](ServerVersionReponseDto.md)
|
||||
[**ServerVersionResponseDto**](ServerVersionResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user