Compare commits

..

11 Commits

Author SHA1 Message Date
Jason Rasmussen
e39de5e8cf chore: remove unused file 2026-03-23 12:18:15 -04:00
Yaros
0e93aa74cf fix(mobile): add keys to people list (#27112)
mobile(fix): add keys to people list
2026-03-23 10:50:56 -05:00
Yaros
e95ad9d2eb fix(mobile): option padding on search dropdowns (#27154)
* fix(mobile): option padding on search dropdowns

* chore: prevent height fill up screen and block the bottom menu entry

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2026-03-23 15:03:07 +00:00
Nicolas-micuda-becker
b98a227bbd fix(web): update upload summary when removing items (#27035) (#27139) 2026-03-23 10:02:09 -05:00
Michel Heusschen
2dd785e3e2 fix(web): restore duplicate viewer arrow key navigation (#27176) 2026-03-23 10:01:15 -05:00
Daniel Dietzler
7e754125cd fix: download original stale cache when edited (#27195) 2026-03-23 10:00:32 -05:00
Yaros
e2eb03d3a4 fix(mobile): star rating always defaults to 0 (#27157) 2026-03-23 09:56:27 -05:00
Yaros
bf065a834f fix(mobile): no results before applying filter (#27155) 2026-03-23 09:41:13 -05:00
Daniel Dietzler
db79173b5b chore: vite 8 (#26913) 2026-03-23 15:39:46 +01:00
Yaros
33666ccd21 fix(mobile): view similar photos from search (#27149)
* fix(mobile): view similar photos from search

* clean up

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2026-03-23 09:36:42 -05:00
bo0tzz
be93b9040c feat: consolidate auto-close workflows (#27172) 2026-03-23 11:22:44 +01:00
334 changed files with 9194 additions and 8294 deletions

143
.github/workflows/auto-close.yml vendored Normal file
View File

@@ -0,0 +1,143 @@
name: Auto-close PRs
on:
pull_request_target: # zizmor: ignore[dangerous-triggers]
types: [opened, edited, labeled]
permissions: {}
jobs:
parse_template:
runs-on: ubuntu-latest
if: ${{ github.event.action != 'labeled' && github.event.pull_request.head.repo.fork == true }}
permissions:
contents: read
outputs:
uses_template: ${{ steps.check.outputs.uses_template }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: .github/pull_request_template.md
sparse-checkout-cone-mode: false
persist-credentials: false
- name: Check required sections
id: check
env:
BODY: ${{ github.event.pull_request.body }}
run: |
OK=true
while IFS= read -r header; do
printf '%s\n' "$BODY" | grep -qF "$header" || OK=false
done < <(sed '/<!--/,/-->/d' .github/pull_request_template.md | grep "^## ")
echo "uses_template=$OK" >> "$GITHUB_OUTPUT"
close_template:
runs-on: ubuntu-latest
needs: parse_template
if: ${{ needs.parse_template.outputs.uses_template == 'false' && github.event.pull_request.state != 'closed' }}
permissions:
pull-requests: write
steps:
- name: Comment and close
env:
GH_TOKEN: ${{ github.token }}
NODE_ID: ${{ github.event.pull_request.node_id }}
run: |
gh api graphql \
-f prId="$NODE_ID" \
-f body="This PR has been automatically closed as the description doesn't follow our template. After you edit it to match the template, the PR will automatically be reopened." \
-f query='
mutation CommentAndClosePR($prId: ID!, $body: String!) {
addComment(input: {
subjectId: $prId,
body: $body
}) {
__typename
}
closePullRequest(input: {
pullRequestId: $prId
}) {
__typename
}
}'
- name: Add label
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: gh pr edit "$PR_NUMBER" --add-label "auto-closed:template"
close_llm:
runs-on: ubuntu-latest
if: ${{ github.event.action == 'labeled' && github.event.label.name == 'auto-closed:llm' }}
permissions:
pull-requests: write
steps:
- name: Comment and close
env:
GH_TOKEN: ${{ github.token }}
NODE_ID: ${{ github.event.pull_request.node_id }}
run: |
gh api graphql \
-f prId="$NODE_ID" \
-f body="Thank you for your interest in contributing to Immich! Unfortunately this PR looks like it was generated using an LLM. As noted in our [CONTRIBUTING.md](https://github.com/immich-app/immich/blob/main/CONTRIBUTING.md#use-of-generative-ai), we request that you don't use LLMs to generate PRs as those are not a good use of maintainer time." \
-f query='
mutation CommentAndClosePR($prId: ID!, $body: String!) {
addComment(input: {
subjectId: $prId,
body: $body
}) {
__typename
}
closePullRequest(input: {
pullRequestId: $prId
}) {
__typename
}
}'
reopen:
runs-on: ubuntu-latest
needs: parse_template
if: >-
${{
needs.parse_template.outputs.uses_template == 'true'
&& github.event.pull_request.state == 'closed'
&& contains(github.event.pull_request.labels.*.name, 'auto-closed:template')
}}
permissions:
pull-requests: write
steps:
- name: Remove template label
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: gh pr edit "$PR_NUMBER" --remove-label "auto-closed:template" || true
- name: Check for remaining auto-closed labels
id: check_labels
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
REMAINING=$(gh pr view "$PR_NUMBER" --json labels \
--jq '[.labels[].name | select(startswith("auto-closed:"))] | length')
echo "remaining=$REMAINING" >> "$GITHUB_OUTPUT"
- name: Reopen PR
if: ${{ steps.check_labels.outputs.remaining == '0' }}
env:
GH_TOKEN: ${{ github.token }}
NODE_ID: ${{ github.event.pull_request.node_id }}
run: |
gh api graphql \
-f prId="$NODE_ID" \
-f query='
mutation ReopenPR($prId: ID!) {
reopenPullRequest(input: {
pullRequestId: $prId
}) {
__typename
}
}'

View File

@@ -1,97 +0,0 @@
name: Check PR Template
on:
pull_request_target: # zizmor: ignore[dangerous-triggers]
types: [opened, edited]
permissions: {}
env:
LABEL_ID: 'LA_kwDOGyI-8M8AAAACcAeOfg' # auto-closed:template
jobs:
parse:
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.head.repo.fork == true }}
permissions:
contents: read
outputs:
uses_template: ${{ steps.check.outputs.uses_template }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
sparse-checkout: .github/pull_request_template.md
sparse-checkout-cone-mode: false
persist-credentials: false
- name: Check required sections
id: check
env:
BODY: ${{ github.event.pull_request.body }}
run: |
OK=true
while IFS= read -r header; do
printf '%s\n' "$BODY" | grep -qF "$header" || OK=false
done < <(sed '/<!--/,/-->/d' .github/pull_request_template.md | grep "^## ")
echo "uses_template=$OK" >> "$GITHUB_OUTPUT"
act:
runs-on: ubuntu-latest
needs: parse
permissions:
pull-requests: write
steps:
- name: Close PR
if: ${{ needs.parse.outputs.uses_template == 'false' && github.event.pull_request.state != 'closed' }}
env:
GH_TOKEN: ${{ github.token }}
NODE_ID: ${{ github.event.pull_request.node_id }}
run: |
gh api graphql \
-f prId="$NODE_ID" \
-f labelId="$LABEL_ID" \
-f body="This PR has been automatically closed as the description doesn't follow our template. After you edit it to match the template, the PR will automatically be reopened." \
-f query='
mutation CommentAndClosePR($prId: ID!, $body: String!, $labelId: ID!) {
addComment(input: {
subjectId: $prId,
body: $body
}) {
__typename
}
closePullRequest(input: {
pullRequestId: $prId
}) {
__typename
}
addLabelsToLabelable(input: {
labelableId: $prId,
labelIds: [$labelId]
}) {
__typename
}
}'
- name: Reopen PR (sections now present, PR was auto-closed)
if: ${{ needs.parse.outputs.uses_template == 'true' && github.event.pull_request.state == 'closed' && contains(github.event.pull_request.labels.*.node_id, env.LABEL_ID) }}
env:
GH_TOKEN: ${{ github.token }}
NODE_ID: ${{ github.event.pull_request.node_id }}
run: |
gh api graphql \
-f prId="$NODE_ID" \
-f labelId="$LABEL_ID" \
-f query='
mutation ReopenPR($prId: ID!, $labelId: ID!) {
reopenPullRequest(input: {
pullRequestId: $prId
}) {
__typename
}
removeLabelsFromLabelable(input: {
labelableId: $prId,
labelIds: [$labelId]
}) {
__typename
}
}'

View File

@@ -1,38 +0,0 @@
name: Close LLM-generated PRs
on:
pull_request_target:
types: [labeled]
permissions: {}
jobs:
comment_and_close:
runs-on: ubuntu-latest
if: ${{ github.event.label.name == 'llm-generated' }}
permissions:
pull-requests: write
steps:
- name: Comment and close
env:
GH_TOKEN: ${{ github.token }}
NODE_ID: ${{ github.event.pull_request.node_id }}
run: |
gh api graphql \
-f prId="$NODE_ID" \
-f body="Thank you for your interest in contributing to Immich! Unfortunately this PR looks like it was generated using an LLM. As noted in our [CONTRIBUTING.md](https://github.com/immich-app/immich/blob/main/CONTRIBUTING.md#use-of-generative-ai), we request that you don't use LLMs to generate PRs as those are not a good use of maintainer time." \
-f query='
mutation CommentAndClosePR($prId: ID!, $body: String!) {
addComment(input: {
subjectId: $prId,
body: $body
}) {
__typename
}
closePullRequest(input: {
pullRequestId: $prId
}) {
__typename
}
}'

View File

@@ -35,8 +35,7 @@
"prettier-plugin-organize-imports": "^4.0.0",
"typescript": "^5.3.3",
"typescript-eslint": "^8.28.0",
"vite": "^7.0.0",
"vite-tsconfig-paths": "^6.0.0",
"vite": "^8.0.0",
"vitest": "^4.0.0",
"vitest-fetch-mock": "^0.4.0",
"yaml": "^2.3.1"

View File

@@ -4,7 +4,7 @@ import path from 'node:path';
import { setTimeout as sleep } from 'node:timers/promises';
import { describe, expect, it, MockedFunction, vi } from 'vitest';
import { AssetRejectReason, AssetUploadAction, checkBulkUpload, defaults, getSupportedMediaTypes } from '@immich/sdk';
import { Action, checkBulkUpload, defaults, getSupportedMediaTypes, Reason } from '@immich/sdk';
import createFetchMock from 'vitest-fetch-mock';
import {
@@ -120,7 +120,7 @@ describe('checkForDuplicates', () => {
vi.mocked(checkBulkUpload).mockResolvedValue({
results: [
{
action: AssetUploadAction.Accept,
action: Action.Accept,
id: testFilePath,
},
],
@@ -144,10 +144,10 @@ describe('checkForDuplicates', () => {
vi.mocked(checkBulkUpload).mockResolvedValue({
results: [
{
action: AssetUploadAction.Reject,
action: Action.Reject,
id: testFilePath,
assetId: 'fc5621b1-86f6-44a1-9905-403e607df9f5',
reason: AssetRejectReason.Duplicate,
reason: Reason.Duplicate,
},
],
});
@@ -167,7 +167,7 @@ describe('checkForDuplicates', () => {
vi.mocked(checkBulkUpload).mockResolvedValue({
results: [
{
action: AssetUploadAction.Accept,
action: Action.Accept,
id: testFilePath,
},
],
@@ -187,7 +187,7 @@ describe('checkForDuplicates', () => {
mocked.mockResolvedValue({
results: [
{
action: AssetUploadAction.Accept,
action: Action.Accept,
id: testFilePath,
},
],

View File

@@ -1,9 +1,9 @@
import {
Action,
AssetBulkUploadCheckItem,
AssetBulkUploadCheckResult,
AssetMediaResponseDto,
AssetMediaStatus,
AssetUploadAction,
Permission,
addAssetsToAlbum,
checkBulkUpload,
@@ -234,7 +234,7 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas
const results = response.results as AssetBulkUploadCheckResults;
for (const { id: filepath, assetId, action } of results) {
if (action === AssetUploadAction.Accept) {
if (action === Action.Accept) {
newFiles.push(filepath);
} else {
// rejects are always duplicates

View File

@@ -1,10 +1,12 @@
import { defineConfig, UserConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
resolve: { alias: { src: '/src' } },
resolve: {
alias: { src: '/src' },
tsconfigPaths: true,
},
build: {
rollupOptions: {
rolldownOptions: {
input: 'src/index.ts',
output: {
dir: 'dist',
@@ -16,7 +18,6 @@ export default defineConfig({
// bundle everything except for Node built-ins
noExternal: /^(?!node:).*$/,
},
plugins: [tsconfigPaths()],
test: {
name: 'cli:unit',
globals: true,

View File

@@ -95,8 +95,8 @@ describe('/asset', () => {
utils.createAsset(user1.accessToken),
utils.createAsset(user1.accessToken, {
isFavorite: true,
fileCreatedAt: yesterday.toUTC().toISO(),
fileModifiedAt: yesterday.toUTC().toISO(),
fileCreatedAt: yesterday.toISO(),
fileModifiedAt: yesterday.toISO(),
assetData: { filename: 'example.mp4' },
}),
utils.createAsset(user1.accessToken),
@@ -435,8 +435,7 @@ describe('/asset', () => {
it('should require access', async () => {
const { status, body } = await request(app)
.put(`/assets/${user2Assets[0].id}`)
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({});
.set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorDto.noPermission);
});

View File

@@ -110,7 +110,7 @@ describe('/libraries', () => {
});
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[importPaths] Array must have unique items']));
expect(body).toEqual(errorDto.badRequest(["All importPaths's elements must be unique"]));
});
it('should not create an external library with duplicate exclusion patterns', async () => {
@@ -125,7 +125,7 @@ describe('/libraries', () => {
});
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[exclusionPatterns] Array must have unique items']));
expect(body).toEqual(errorDto.badRequest(["All exclusionPatterns's elements must be unique"]));
});
});
@@ -157,7 +157,7 @@ describe('/libraries', () => {
.send({ name: '' });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[name] Too small: expected string to have >=1 characters']));
expect(body).toEqual(errorDto.badRequest(['name should not be empty']));
});
it('should change the import paths', async () => {
@@ -181,7 +181,7 @@ describe('/libraries', () => {
.send({ importPaths: [''] });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[importPaths] Array items must not be empty']));
expect(body).toEqual(errorDto.badRequest(['each value in importPaths should not be empty']));
});
it('should reject duplicate import paths', async () => {
@@ -191,7 +191,7 @@ describe('/libraries', () => {
.send({ importPaths: ['/path', '/path'] });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[importPaths] Array must have unique items']));
expect(body).toEqual(errorDto.badRequest(["All importPaths's elements must be unique"]));
});
it('should change the exclusion pattern', async () => {
@@ -215,7 +215,7 @@ describe('/libraries', () => {
.send({ exclusionPatterns: ['**/*.jpg', '**/*.jpg'] });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[exclusionPatterns] Array must have unique items']));
expect(body).toEqual(errorDto.badRequest(["All exclusionPatterns's elements must be unique"]));
});
it('should reject an empty exclusion pattern', async () => {
@@ -225,7 +225,7 @@ describe('/libraries', () => {
.send({ exclusionPatterns: [''] });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[exclusionPatterns] Array items must not be empty']));
expect(body).toEqual(errorDto.badRequest(['each value in exclusionPatterns should not be empty']));
});
});

View File

@@ -109,7 +109,7 @@ describe('/map', () => {
.get('/map/reverse-geocode?lon=123')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[lat] Invalid input: expected number, received NaN']));
expect(body).toEqual(errorDto.badRequest(['lat must be a number between -90 and 90']));
});
it('should throw an error if a lat is not a number', async () => {
@@ -117,7 +117,7 @@ describe('/map', () => {
.get('/map/reverse-geocode?lat=abc&lon=123.456')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[lat] Invalid input: expected number, received NaN']));
expect(body).toEqual(errorDto.badRequest(['lat must be a number between -90 and 90']));
});
it('should throw an error if a lat is out of range', async () => {
@@ -125,7 +125,7 @@ describe('/map', () => {
.get('/map/reverse-geocode?lat=91&lon=123.456')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[lat] Too big: expected number to be <=90']));
expect(body).toEqual(errorDto.badRequest(['lat must be a number between -90 and 90']));
});
it('should throw an error if a lon is not provided', async () => {
@@ -133,7 +133,7 @@ describe('/map', () => {
.get('/map/reverse-geocode?lat=75')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[lon] Invalid input: expected number, received NaN']));
expect(body).toEqual(errorDto.badRequest(['lon must be a number between -180 and 180']));
});
const reverseGeocodeTestCases = [

View File

@@ -101,7 +101,7 @@ describe(`/oauth`, () => {
it(`should throw an error if a redirect uri is not provided`, async () => {
const { status, body } = await request(app).post('/oauth/authorize').send({});
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[redirectUri] Invalid input: expected string, received undefined']));
expect(body).toEqual(errorDto.badRequest(['redirectUri must be a string', 'redirectUri should not be empty']));
});
it('should return a redirect uri', async () => {
@@ -123,13 +123,13 @@ describe(`/oauth`, () => {
it(`should throw an error if a url is not provided`, async () => {
const { status, body } = await request(app).post('/oauth/callback').send({});
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[url] Invalid input: expected string, received undefined']));
expect(body).toEqual(errorDto.badRequest(['url must be a string', 'url should not be empty']));
});
it(`should throw an error if the url is empty`, async () => {
const { status, body } = await request(app).post('/oauth/callback').send({ url: '' });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[url] Too small: expected string to have >=1 characters']));
expect(body).toEqual(errorDto.badRequest(['url should not be empty']));
});
it(`should throw an error if the state is not provided`, async () => {

View File

@@ -309,7 +309,7 @@ describe('/tags', () => {
.get(`/tags/${uuidDto.invalid}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[id] Invalid UUID']));
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
});
it('should get tag details', async () => {
@@ -427,7 +427,7 @@ describe('/tags', () => {
.delete(`/tags/${uuidDto.invalid}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[id] Invalid UUID']));
expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
});
it('should delete a tag', async () => {

View File

@@ -287,8 +287,7 @@ describe('/admin/users', () => {
it('should delete user', async () => {
const { status, body } = await request(app)
.delete(`/admin/users/${userToDelete.userId}`)
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({});
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({

View File

@@ -178,9 +178,7 @@ describe('/users', () => {
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(
errorDto.badRequest(['[download.archiveSize] Invalid input: expected int, received number']),
);
expect(body).toEqual(errorDto.badRequest(['download.archiveSize must be an integer number']));
});
it('should update download archive size', async () => {
@@ -206,9 +204,7 @@ describe('/users', () => {
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(
errorDto.badRequest(['[download.includeEmbeddedVideos] Invalid input: expected boolean, received number']),
);
expect(body).toEqual(errorDto.badRequest(['download.includeEmbeddedVideos must be a boolean value']));
});
it('should update download include embedded videos', async () => {

View File

@@ -0,0 +1,51 @@
import { AssetMediaResponseDto, LoginResponseDto, updateAssets } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import crypto from 'node:crypto';
import { asBearerAuth, utils } from 'src/utils';
test.describe('Duplicates Utility', () => {
let admin: LoginResponseDto;
let firstAsset: AssetMediaResponseDto;
let secondAsset: AssetMediaResponseDto;
test.beforeAll(async () => {
utils.initSdk();
await utils.resetDatabase();
admin = await utils.adminSetup();
});
test.beforeEach(async ({ context }) => {
[firstAsset, secondAsset] = await Promise.all([
utils.createAsset(admin.accessToken, { deviceAssetId: 'duplicate-a' }),
utils.createAsset(admin.accessToken, { deviceAssetId: 'duplicate-b' }),
]);
await updateAssets(
{
assetBulkUpdateDto: {
ids: [firstAsset.id, secondAsset.id],
duplicateId: crypto.randomUUID(),
},
},
{ headers: asBearerAuth(admin.accessToken) },
);
await utils.setAuthCookies(context, admin.accessToken);
});
test('navigates with arrow keys between duplicate preview assets', async ({ page }) => {
await page.goto('/utilities/duplicates');
await page.getByRole('button', { name: 'View' }).first().click();
await page.waitForSelector('#immich-asset-viewer');
const getViewedAssetId = () => new URL(page.url()).pathname.split('/').at(-1) ?? '';
const initialAssetId = getViewedAssetId();
expect([firstAsset.id, secondAsset.id]).toContain(initialAssetId);
await page.keyboard.press('ArrowRight');
await expect.poll(getViewedAssetId).not.toBe(initialAssetId);
await page.keyboard.press('ArrowLeft');
await expect.poll(getViewedAssetId).toBe(initialAssetId);
});
});

View File

@@ -77,7 +77,7 @@ export function generateAsset(
latitude: hasGPS ? faker.location.latitude() : null,
longitude: hasGPS ? faker.location.longitude() : null,
visibility: AssetVisibility.Timeline,
stack: undefined,
stack: null,
fileSizeInByte: faker.number.int({ min: 510, max: 5_000_000 }),
checksum: faker.string.alphanumeric({ length: 5 }),
};

View File

@@ -52,7 +52,7 @@ export type MockTimelineAsset = {
latitude: number | null;
longitude: number | null;
visibility: AssetVisibility;
stack: undefined;
stack: null;
checksum: string;
fileSizeInByte: number;
};

View File

@@ -69,7 +69,7 @@ export const createMockStackAsset = (ownerId: string): AssetResponseDto => {
tags: [],
people: [],
unassignedFaces: [],
stack: undefined,
stack: null,
isOffline: false,
hasMetadata: true,
duplicateId: null,

View File

@@ -80,14 +80,12 @@ Future<void> _processCloudIdMappingsInBatches(
AssetMetadataBulkUpsertItemDto(
assetId: mapping.remoteAssetId,
key: kMobileMetadataKey,
value: Map<String, Object>.from(
RemoteAssetMobileAppMetadata(
cloudId: mapping.localAsset.cloudId,
createdAt: mapping.localAsset.createdAt.toIso8601String(),
adjustmentTime: mapping.localAsset.adjustmentTime?.toIso8601String(),
latitude: mapping.localAsset.latitude?.toString(),
longitude: mapping.localAsset.longitude?.toString(),
).toJson(),
value: RemoteAssetMobileAppMetadata(
cloudId: mapping.localAsset.cloudId,
createdAt: mapping.localAsset.createdAt.toIso8601String(),
adjustmentTime: mapping.localAsset.adjustmentTime?.toIso8601String(),
latitude: mapping.localAsset.latitude?.toString(),
longitude: mapping.localAsset.longitude?.toString(),
),
),
);

View File

@@ -79,6 +79,7 @@ class _DriftPeopleCollectionPageState extends ConsumerState<DriftPeopleCollectio
final person = people[index];
return Column(
key: ValueKey(person.id),
children: [
GestureDetector(
onTap: () {
@@ -88,6 +89,7 @@ class _DriftPeopleCollectionPageState extends ConsumerState<DriftPeopleCollectio
shape: const CircleBorder(side: BorderSide.none),
elevation: 3,
child: CircleAvatar(
key: ValueKey('avatar-${person.id}'),
maxRadius: isTablet ? 100 / 2 : 96 / 2,
backgroundImage: RemoteImageProvider(url: getFaceThumbnailUrl(person.id)),
),

View File

@@ -69,6 +69,7 @@ class DriftSearchPage extends HookConsumerWidget {
);
final previousFilter = useState<SearchFilter?>(null);
final hasRequestedSearch = useState<bool>(false);
final dateInputFilter = useState<DateFilterInputModel?>(null);
final peopleCurrentFilterWidget = useState<Widget?>(null);
@@ -91,9 +92,11 @@ class DriftSearchPage extends HookConsumerWidget {
if (filter.isEmpty) {
previousFilter.value = null;
hasRequestedSearch.value = false;
return;
}
hasRequestedSearch.value = true;
unawaited(ref.read(paginatedSearchProvider.notifier).search(filter));
previousFilter.value = filter;
}
@@ -107,6 +110,8 @@ class DriftSearchPage extends HookConsumerWidget {
searchPreFilter() {
if (preFilter != null) {
Future.delayed(Duration.zero, () {
filter.value = preFilter;
textSearchController.clear();
searchFilter(preFilter);
if (preFilter.location.city != null) {
@@ -719,7 +724,7 @@ class DriftSearchPage extends HookConsumerWidget {
),
),
),
if (filter.value.isEmpty)
if (!hasRequestedSearch.value)
const _SearchSuggestions()
else
_SearchResultGrid(onScrollEnd: loadMoreSearchResults),

View File

@@ -24,20 +24,22 @@ class SimilarPhotosActionButton extends ConsumerWidget {
}
ref.invalidate(assetViewerProvider);
ref
.read(searchPreFilterProvider.notifier)
.setFilter(
SearchFilter(
assetId: assetId,
people: {},
location: SearchLocationFilter(),
camera: SearchCameraFilter(),
date: SearchDateFilter(),
display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false),
rating: SearchRatingFilter(),
mediaType: AssetType.image,
),
);
ref.invalidate(paginatedSearchProvider);
ref.read(searchPreFilterProvider.notifier)
..clear()
..setFilter(
SearchFilter(
assetId: assetId,
people: {},
location: SearchLocationFilter(),
camera: SearchCameraFilter(),
date: SearchDateFilter(),
display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false),
rating: SearchRatingFilter(),
mediaType: AssetType.image,
),
);
unawaited(context.navigateTo(const DriftSearchRoute()));
}

View File

@@ -39,6 +39,16 @@ class _RatingBarState extends State<RatingBar> {
_currentRating = widget.initialRating;
}
@override
void didUpdateWidget(covariant RatingBar oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.initialRating != widget.initialRating && _currentRating != widget.initialRating) {
setState(() {
_currentRating = widget.initialRating;
});
}
}
void _updateRating(Offset localPosition, bool isRTL, {bool isTap = false}) {
final totalWidth = widget.itemCount * widget.itemSize + (widget.itemCount - 1) * widget.starPadding;
double dx = localPosition.dx;

View File

@@ -97,7 +97,7 @@ class AlbumApiRepository extends ApiRepository {
for (final result in response) {
if (result.success) {
added.add(result.id);
} else if (result.error == BulkIdErrorReason.duplicate) {
} else if (result.error == BulkIdResponseDtoErrorEnum.duplicate) {
duplicates.add(result.id);
}
}

View File

@@ -5,13 +5,13 @@ dynamic upgradeDto(dynamic value, String targetType) {
case 'UserPreferencesResponseDto':
if (value is Map) {
addDefault(value, 'download.includeEmbeddedVideos', false);
addDefault(value, 'folders', FoldersResponse(enabled: false, sidebarWeb: false).toJson());
addDefault(value, 'memories', MemoriesResponse(enabled: true, duration: 5).toJson());
addDefault(value, 'ratings', RatingsResponse(enabled: false).toJson());
addDefault(value, 'people', PeopleResponse(enabled: true, sidebarWeb: false).toJson());
addDefault(value, 'tags', TagsResponse(enabled: false, sidebarWeb: false).toJson());
addDefault(value, 'sharedLinks', SharedLinksResponse(enabled: true, sidebarWeb: false).toJson());
addDefault(value, 'cast', CastResponse(gCastEnabled: false).toJson());
addDefault(value, 'folders', FoldersResponse().toJson());
addDefault(value, 'memories', MemoriesResponse().toJson());
addDefault(value, 'ratings', RatingsResponse().toJson());
addDefault(value, 'people', PeopleResponse().toJson());
addDefault(value, 'tags', TagsResponse().toJson());
addDefault(value, 'sharedLinks', SharedLinksResponse().toJson());
addDefault(value, 'cast', CastResponse().toJson());
addDefault(value, 'albums', {'defaultAssetOrder': 'desc'});
}
break;

View File

@@ -16,9 +16,15 @@ class SearchDropdown<T> extends StatelessWidget {
final Widget? label;
final Widget? leadingIcon;
static const WidgetStatePropertyAll<EdgeInsetsGeometry> _optionPadding = WidgetStatePropertyAll<EdgeInsetsGeometry>(
EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0),
);
@override
Widget build(BuildContext context) {
final menuStyle = const MenuStyle(
final mediaQuery = MediaQuery.of(context);
final maxMenuHeight = mediaQuery.size.height * 0.5 - mediaQuery.viewPadding.bottom;
const menuStyle = MenuStyle(
shape: WidgetStatePropertyAll<OutlinedBorder>(
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))),
),
@@ -26,11 +32,26 @@ class SearchDropdown<T> extends StatelessWidget {
return LayoutBuilder(
builder: (context, constraints) {
final styledEntries = dropdownMenuEntries
.map(
(entry) => DropdownMenuEntry<T>(
value: entry.value,
label: entry.label,
labelWidget: entry.labelWidget,
enabled: entry.enabled,
leadingIcon: entry.leadingIcon,
trailingIcon: entry.trailingIcon,
style: (entry.style ?? const ButtonStyle()).copyWith(padding: _optionPadding),
),
)
.toList(growable: false);
return DropdownMenu(
controller: controller,
leadingIcon: leadingIcon,
width: constraints.maxWidth,
dropdownMenuEntries: dropdownMenuEntries,
menuHeight: maxMenuHeight,
dropdownMenuEntries: styledEntries,
label: label,
menuStyle: menuStyle,
trailingIcon: const Icon(Icons.arrow_drop_down_rounded),

View File

@@ -370,7 +370,6 @@ Class | Method | HTTP request | Description
- [AssetFaceUpdateItem](doc//AssetFaceUpdateItem.md)
- [AssetFaceWithoutPersonResponseDto](doc//AssetFaceWithoutPersonResponseDto.md)
- [AssetFullSyncDto](doc//AssetFullSyncDto.md)
- [AssetIdErrorReason](doc//AssetIdErrorReason.md)
- [AssetIdsDto](doc//AssetIdsDto.md)
- [AssetIdsResponseDto](doc//AssetIdsResponseDto.md)
- [AssetJobName](doc//AssetJobName.md)
@@ -388,12 +387,10 @@ Class | Method | HTTP request | Description
- [AssetMetadataUpsertItemDto](doc//AssetMetadataUpsertItemDto.md)
- [AssetOcrResponseDto](doc//AssetOcrResponseDto.md)
- [AssetOrder](doc//AssetOrder.md)
- [AssetRejectReason](doc//AssetRejectReason.md)
- [AssetResponseDto](doc//AssetResponseDto.md)
- [AssetStackResponseDto](doc//AssetStackResponseDto.md)
- [AssetStatsResponseDto](doc//AssetStatsResponseDto.md)
- [AssetTypeEnum](doc//AssetTypeEnum.md)
- [AssetUploadAction](doc//AssetUploadAction.md)
- [AssetVisibility](doc//AssetVisibility.md)
- [AudioCodec](doc//AudioCodec.md)
- [AuthStatusResponseDto](doc//AuthStatusResponseDto.md)
@@ -440,6 +437,7 @@ Class | Method | HTTP request | Description
- [LibraryResponseDto](doc//LibraryResponseDto.md)
- [LibraryStatsResponseDto](doc//LibraryStatsResponseDto.md)
- [LicenseKeyDto](doc//LicenseKeyDto.md)
- [LicenseResponseDto](doc//LicenseResponseDto.md)
- [LogLevel](doc//LogLevel.md)
- [LoginCredentialDto](doc//LoginCredentialDto.md)
- [LoginResponseDto](doc//LoginResponseDto.md)
@@ -503,10 +501,6 @@ Class | Method | HTTP request | Description
- [PluginActionResponseDto](doc//PluginActionResponseDto.md)
- [PluginContextType](doc//PluginContextType.md)
- [PluginFilterResponseDto](doc//PluginFilterResponseDto.md)
- [PluginJsonSchema](doc//PluginJsonSchema.md)
- [PluginJsonSchemaProperty](doc//PluginJsonSchemaProperty.md)
- [PluginJsonSchemaPropertyAdditionalProperties](doc//PluginJsonSchemaPropertyAdditionalProperties.md)
- [PluginJsonSchemaType](doc//PluginJsonSchemaType.md)
- [PluginResponseDto](doc//PluginResponseDto.md)
- [PluginTriggerResponseDto](doc//PluginTriggerResponseDto.md)
- [PluginTriggerType](doc//PluginTriggerType.md)

View File

@@ -109,7 +109,6 @@ part 'model/asset_face_update_dto.dart';
part 'model/asset_face_update_item.dart';
part 'model/asset_face_without_person_response_dto.dart';
part 'model/asset_full_sync_dto.dart';
part 'model/asset_id_error_reason.dart';
part 'model/asset_ids_dto.dart';
part 'model/asset_ids_response_dto.dart';
part 'model/asset_job_name.dart';
@@ -127,12 +126,10 @@ part 'model/asset_metadata_upsert_dto.dart';
part 'model/asset_metadata_upsert_item_dto.dart';
part 'model/asset_ocr_response_dto.dart';
part 'model/asset_order.dart';
part 'model/asset_reject_reason.dart';
part 'model/asset_response_dto.dart';
part 'model/asset_stack_response_dto.dart';
part 'model/asset_stats_response_dto.dart';
part 'model/asset_type_enum.dart';
part 'model/asset_upload_action.dart';
part 'model/asset_visibility.dart';
part 'model/audio_codec.dart';
part 'model/auth_status_response_dto.dart';
@@ -179,6 +176,7 @@ part 'model/job_settings_dto.dart';
part 'model/library_response_dto.dart';
part 'model/library_stats_response_dto.dart';
part 'model/license_key_dto.dart';
part 'model/license_response_dto.dart';
part 'model/log_level.dart';
part 'model/login_credential_dto.dart';
part 'model/login_response_dto.dart';
@@ -242,10 +240,6 @@ part 'model/places_response_dto.dart';
part 'model/plugin_action_response_dto.dart';
part 'model/plugin_context_type.dart';
part 'model/plugin_filter_response_dto.dart';
part 'model/plugin_json_schema.dart';
part 'model/plugin_json_schema_property.dart';
part 'model/plugin_json_schema_property_additional_properties.dart';
part 'model/plugin_json_schema_type.dart';
part 'model/plugin_response_dto.dart';
part 'model/plugin_trigger_response_dto.dart';
part 'model/plugin_trigger_type.dart';

View File

@@ -136,8 +136,10 @@ class ActivitiesApi {
/// Asset ID (if activity is for an asset)
///
/// * [ReactionLevel] level:
/// Filter by activity level
///
/// * [ReactionType] type:
/// Filter by activity type
///
/// * [String] userId:
/// Filter by user ID
@@ -193,8 +195,10 @@ class ActivitiesApi {
/// Asset ID (if activity is for an asset)
///
/// * [ReactionLevel] level:
/// Filter by activity level
///
/// * [ReactionType] type:
/// Filter by activity type
///
/// * [String] userId:
/// Filter by user ID

View File

@@ -864,6 +864,7 @@ class AssetsApi {
/// Filter by trash status
///
/// * [AssetVisibility] visibility:
/// Filter by visibility
Future<Response> getAssetStatisticsWithHttpInfo({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets/statistics';
@@ -912,6 +913,7 @@ class AssetsApi {
/// Filter by trash status
///
/// * [AssetVisibility] visibility:
/// Filter by visibility
Future<AssetStatsResponseDto?> getAssetStatistics({ bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async {
final response = await getAssetStatisticsWithHttpInfo( isFavorite: isFavorite, isTrashed: isTrashed, visibility: visibility, );
if (response.statusCode >= HttpStatus.badRequest) {
@@ -1590,6 +1592,7 @@ class AssetsApi {
/// Sidecar file data
///
/// * [AssetVisibility] visibility:
/// Asset visibility
Future<Response> uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/assets';
@@ -1728,6 +1731,7 @@ class AssetsApi {
/// Sidecar file data
///
/// * [AssetVisibility] visibility:
/// Asset visibility
Future<AssetMediaResponseDto?> uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, List<AssetMetadataUpsertItemDto>? metadata, MultipartFile? sidecarData, AssetVisibility? visibility, }) async {
final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, metadata: metadata, sidecarData: sidecarData, visibility: visibility, );
if (response.statusCode >= HttpStatus.badRequest) {
@@ -1759,6 +1763,7 @@ class AssetsApi {
/// * [String] key:
///
/// * [AssetMediaSize] size:
/// Asset media size
///
/// * [String] slug:
Future<Response> viewAssetWithHttpInfo(String id, { bool? edited, String? key, AssetMediaSize? size, String? slug, }) async {
@@ -1814,6 +1819,7 @@ class AssetsApi {
/// * [String] key:
///
/// * [AssetMediaSize] size:
/// Asset media size
///
/// * [String] slug:
Future<MultipartFile?> viewAsset(String id, { bool? edited, String? key, AssetMediaSize? size, String? slug, }) async {

View File

@@ -218,7 +218,6 @@ class DatabaseBackupsAdminApi {
/// Parameters:
///
/// * [MultipartFile] file:
/// Database backup file
Future<Response> uploadDatabaseBackupWithHttpInfo({ MultipartFile? file, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/admin/database-backups/upload';
@@ -261,7 +260,6 @@ class DatabaseBackupsAdminApi {
/// Parameters:
///
/// * [MultipartFile] file:
/// Database backup file
Future<void> uploadDatabaseBackup({ MultipartFile? file, }) async {
final response = await uploadDatabaseBackupWithHttpInfo( file: file, );
if (response.statusCode >= HttpStatus.badRequest) {

View File

@@ -520,6 +520,7 @@ class DeprecatedApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
///
/// * [QueueCommandDto] queueCommandDto (required):
Future<Response> runQueueCommandLegacyWithHttpInfo(QueueName name, QueueCommandDto queueCommandDto,) async {
@@ -555,6 +556,7 @@ class DeprecatedApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
///
/// * [QueueCommandDto] queueCommandDto (required):
Future<QueueResponseLegacyDto?> runQueueCommandLegacy(QueueName name, QueueCommandDto queueCommandDto,) async {

View File

@@ -121,6 +121,7 @@ class JobsApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
///
/// * [QueueCommandDto] queueCommandDto (required):
Future<Response> runQueueCommandLegacyWithHttpInfo(QueueName name, QueueCommandDto queueCommandDto,) async {
@@ -156,6 +157,7 @@ class JobsApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
///
/// * [QueueCommandDto] queueCommandDto (required):
Future<QueueResponseLegacyDto?> runQueueCommandLegacy(QueueName name, QueueCommandDto queueCommandDto,) async {

View File

@@ -260,11 +260,13 @@ class MemoriesApi {
/// Include trashed memories
///
/// * [MemorySearchOrder] order:
/// Sort order
///
/// * [int] size:
/// Number of memories to return
///
/// * [MemoryType] type:
/// Memory type
Future<Response> memoriesStatisticsWithHttpInfo({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? size, MemoryType? type, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/memories/statistics';
@@ -325,11 +327,13 @@ class MemoriesApi {
/// Include trashed memories
///
/// * [MemorySearchOrder] order:
/// Sort order
///
/// * [int] size:
/// Number of memories to return
///
/// * [MemoryType] type:
/// Memory type
Future<MemoryStatisticsResponseDto?> memoriesStatistics({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? size, MemoryType? type, }) async {
final response = await memoriesStatisticsWithHttpInfo( for_: for_, isSaved: isSaved, isTrashed: isTrashed, order: order, size: size, type: type, );
if (response.statusCode >= HttpStatus.badRequest) {
@@ -427,11 +431,13 @@ class MemoriesApi {
/// Include trashed memories
///
/// * [MemorySearchOrder] order:
/// Sort order
///
/// * [int] size:
/// Number of memories to return
///
/// * [MemoryType] type:
/// Memory type
Future<Response> searchMemoriesWithHttpInfo({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? size, MemoryType? type, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/memories';
@@ -492,11 +498,13 @@ class MemoriesApi {
/// Include trashed memories
///
/// * [MemorySearchOrder] order:
/// Sort order
///
/// * [int] size:
/// Number of memories to return
///
/// * [MemoryType] type:
/// Memory type
Future<List<MemoryResponseDto>?> searchMemories({ DateTime? for_, bool? isSaved, bool? isTrashed, MemorySearchOrder? order, int? size, MemoryType? type, }) async {
final response = await searchMemoriesWithHttpInfo( for_: for_, isSaved: isSaved, isTrashed: isTrashed, order: order, size: size, type: type, );
if (response.statusCode >= HttpStatus.badRequest) {

View File

@@ -182,8 +182,10 @@ class NotificationsApi {
/// Filter by notification ID
///
/// * [NotificationLevel] level:
/// Filter by notification level
///
/// * [NotificationType] type:
/// Filter by notification type
///
/// * [bool] unread:
/// Filter by unread status
@@ -235,8 +237,10 @@ class NotificationsApi {
/// Filter by notification ID
///
/// * [NotificationLevel] level:
/// Filter by notification level
///
/// * [NotificationType] type:
/// Filter by notification type
///
/// * [bool] unread:
/// Filter by unread status

View File

@@ -138,6 +138,7 @@ class PartnersApi {
/// Parameters:
///
/// * [PartnerDirection] direction (required):
/// Partner direction
Future<Response> getPartnersWithHttpInfo(PartnerDirection direction,) async {
// ignore: prefer_const_declarations
final apiPath = r'/partners';
@@ -172,6 +173,7 @@ class PartnersApi {
/// Parameters:
///
/// * [PartnerDirection] direction (required):
/// Partner direction
Future<List<PartnerResponseDto>?> getPartners(PartnerDirection direction,) async {
final response = await getPartnersWithHttpInfo(direction,);
if (response.statusCode >= HttpStatus.badRequest) {

View File

@@ -25,6 +25,7 @@ class QueuesApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
///
/// * [QueueDeleteDto] queueDeleteDto (required):
Future<Response> emptyQueueWithHttpInfo(QueueName name, QueueDeleteDto queueDeleteDto,) async {
@@ -60,6 +61,7 @@ class QueuesApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
///
/// * [QueueDeleteDto] queueDeleteDto (required):
Future<void> emptyQueue(QueueName name, QueueDeleteDto queueDeleteDto,) async {
@@ -78,6 +80,7 @@ class QueuesApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
Future<Response> getQueueWithHttpInfo(QueueName name,) async {
// ignore: prefer_const_declarations
final apiPath = r'/queues/{name}'
@@ -111,6 +114,7 @@ class QueuesApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
Future<QueueResponseDto?> getQueue(QueueName name,) async {
final response = await getQueueWithHttpInfo(name,);
if (response.statusCode >= HttpStatus.badRequest) {
@@ -135,6 +139,7 @@ class QueuesApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
///
/// * [List<QueueJobStatus>] status:
/// Filter jobs by status
@@ -175,6 +180,7 @@ class QueuesApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
///
/// * [List<QueueJobStatus>] status:
/// Filter jobs by status
@@ -256,6 +262,7 @@ class QueuesApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
///
/// * [QueueUpdateDto] queueUpdateDto (required):
Future<Response> updateQueueWithHttpInfo(QueueName name, QueueUpdateDto queueUpdateDto,) async {
@@ -291,6 +298,7 @@ class QueuesApi {
/// Parameters:
///
/// * [QueueName] name (required):
/// Queue name
///
/// * [QueueUpdateDto] queueUpdateDto (required):
Future<QueueResponseDto?> updateQueue(QueueName name, QueueUpdateDto queueUpdateDto,) async {

View File

@@ -127,6 +127,7 @@ class SearchApi {
/// Parameters:
///
/// * [SearchSuggestionType] type (required):
/// Suggestion type
///
/// * [String] country:
/// Filter by country
@@ -197,6 +198,7 @@ class SearchApi {
/// Parameters:
///
/// * [SearchSuggestionType] type (required):
/// Suggestion type
///
/// * [String] country:
/// Filter by country
@@ -432,6 +434,7 @@ class SearchApi {
/// Filter by trash date (before)
///
/// * [AssetTypeEnum] type:
/// Asset type filter
///
/// * [DateTime] updatedAfter:
/// Filter by update date (after)
@@ -440,6 +443,7 @@ class SearchApi {
/// Filter by update date (before)
///
/// * [AssetVisibility] visibility:
/// Filter by visibility
///
/// * [bool] withDeleted:
/// Include deleted assets
@@ -653,6 +657,7 @@ class SearchApi {
/// Filter by trash date (before)
///
/// * [AssetTypeEnum] type:
/// Asset type filter
///
/// * [DateTime] updatedAfter:
/// Filter by update date (after)
@@ -661,6 +666,7 @@ class SearchApi {
/// Filter by update date (before)
///
/// * [AssetVisibility] visibility:
/// Filter by visibility
///
/// * [bool] withDeleted:
/// Include deleted assets

View File

@@ -281,7 +281,7 @@ class ServerApi {
/// Get product key
///
/// Retrieve information about whether the server currently has a product key registered.
Future<UserLicense?> getServerLicense() async {
Future<LicenseResponseDto?> getServerLicense() async {
final response = await getServerLicenseWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@@ -290,7 +290,7 @@ class ServerApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserLicense',) as UserLicense;
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'LicenseResponseDto',) as LicenseResponseDto;
}
return null;
@@ -724,7 +724,7 @@ class ServerApi {
/// Parameters:
///
/// * [LicenseKeyDto] licenseKeyDto (required):
Future<UserLicense?> setServerLicense(LicenseKeyDto licenseKeyDto,) async {
Future<LicenseResponseDto?> setServerLicense(LicenseKeyDto licenseKeyDto,) async {
final response = await setServerLicenseWithHttpInfo(licenseKeyDto,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@@ -733,7 +733,7 @@ class ServerApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserLicense',) as UserLicense;
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'LicenseResponseDto',) as LicenseResponseDto;
}
return null;

View File

@@ -25,7 +25,7 @@ class TimelineApi {
/// Parameters:
///
/// * [String] timeBucket (required):
/// Time bucket identifier in YYYY-MM-DD format
/// Time bucket identifier in YYYY-MM-DD format (e.g., \"2024-01-01\" for January 2024)
///
/// * [String] albumId:
/// Filter assets belonging to a specific album
@@ -142,7 +142,7 @@ class TimelineApi {
/// Parameters:
///
/// * [String] timeBucket (required):
/// Time bucket identifier in YYYY-MM-DD format
/// Time bucket identifier in YYYY-MM-DD format (e.g., \"2024-01-01\" for January 2024)
///
/// * [String] albumId:
/// Filter assets belonging to a specific album

View File

@@ -324,6 +324,7 @@ class UsersAdminApi {
/// Filter by trash status
///
/// * [AssetVisibility] visibility:
/// Filter by visibility
Future<Response> getUserStatisticsAdminWithHttpInfo(String id, { bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/admin/users/{id}/statistics'
@@ -375,6 +376,7 @@ class UsersAdminApi {
/// Filter by trash status
///
/// * [AssetVisibility] visibility:
/// Filter by visibility
Future<AssetStatsResponseDto?> getUserStatisticsAdmin(String id, { bool? isFavorite, bool? isTrashed, AssetVisibility? visibility, }) async {
final response = await getUserStatisticsAdminWithHttpInfo(id, isFavorite: isFavorite, isTrashed: isTrashed, visibility: visibility, );
if (response.statusCode >= HttpStatus.badRequest) {

View File

@@ -447,7 +447,7 @@ class UsersApi {
/// Retrieve user product key
///
/// Retrieve information about whether the current user has a registered product key.
Future<UserLicense?> getUserLicense() async {
Future<LicenseResponseDto?> getUserLicense() async {
final response = await getUserLicenseWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@@ -456,7 +456,7 @@ class UsersApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserLicense',) as UserLicense;
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'LicenseResponseDto',) as LicenseResponseDto;
}
return null;
@@ -602,7 +602,7 @@ class UsersApi {
/// Parameters:
///
/// * [LicenseKeyDto] licenseKeyDto (required):
Future<UserLicense?> setUserLicense(LicenseKeyDto licenseKeyDto,) async {
Future<LicenseResponseDto?> setUserLicense(LicenseKeyDto licenseKeyDto,) async {
final response = await setUserLicenseWithHttpInfo(licenseKeyDto,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@@ -611,7 +611,7 @@ class UsersApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserLicense',) as UserLicense;
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'LicenseResponseDto',) as LicenseResponseDto;
}
return null;

View File

@@ -264,8 +264,6 @@ class ApiClient {
return AssetFaceWithoutPersonResponseDto.fromJson(value);
case 'AssetFullSyncDto':
return AssetFullSyncDto.fromJson(value);
case 'AssetIdErrorReason':
return AssetIdErrorReasonTypeTransformer().decode(value);
case 'AssetIdsDto':
return AssetIdsDto.fromJson(value);
case 'AssetIdsResponseDto':
@@ -300,8 +298,6 @@ class ApiClient {
return AssetOcrResponseDto.fromJson(value);
case 'AssetOrder':
return AssetOrderTypeTransformer().decode(value);
case 'AssetRejectReason':
return AssetRejectReasonTypeTransformer().decode(value);
case 'AssetResponseDto':
return AssetResponseDto.fromJson(value);
case 'AssetStackResponseDto':
@@ -310,8 +306,6 @@ class ApiClient {
return AssetStatsResponseDto.fromJson(value);
case 'AssetTypeEnum':
return AssetTypeEnumTypeTransformer().decode(value);
case 'AssetUploadAction':
return AssetUploadActionTypeTransformer().decode(value);
case 'AssetVisibility':
return AssetVisibilityTypeTransformer().decode(value);
case 'AudioCodec':
@@ -404,6 +398,8 @@ class ApiClient {
return LibraryStatsResponseDto.fromJson(value);
case 'LicenseKeyDto':
return LicenseKeyDto.fromJson(value);
case 'LicenseResponseDto':
return LicenseResponseDto.fromJson(value);
case 'LogLevel':
return LogLevelTypeTransformer().decode(value);
case 'LoginCredentialDto':
@@ -530,14 +526,6 @@ class ApiClient {
return PluginContextTypeTypeTransformer().decode(value);
case 'PluginFilterResponseDto':
return PluginFilterResponseDto.fromJson(value);
case 'PluginJsonSchema':
return PluginJsonSchema.fromJson(value);
case 'PluginJsonSchemaProperty':
return PluginJsonSchemaProperty.fromJson(value);
case 'PluginJsonSchemaPropertyAdditionalProperties':
return PluginJsonSchemaPropertyAdditionalProperties.fromJson(value);
case 'PluginJsonSchemaType':
return PluginJsonSchemaTypeTypeTransformer().decode(value);
case 'PluginResponseDto':
return PluginResponseDto.fromJson(value);
case 'PluginTriggerResponseDto':

View File

@@ -61,9 +61,6 @@ String parameterToString(dynamic value) {
if (value is AssetEditAction) {
return AssetEditActionTypeTransformer().encode(value).toString();
}
if (value is AssetIdErrorReason) {
return AssetIdErrorReasonTypeTransformer().encode(value).toString();
}
if (value is AssetJobName) {
return AssetJobNameTypeTransformer().encode(value).toString();
}
@@ -76,15 +73,9 @@ String parameterToString(dynamic value) {
if (value is AssetOrder) {
return AssetOrderTypeTransformer().encode(value).toString();
}
if (value is AssetRejectReason) {
return AssetRejectReasonTypeTransformer().encode(value).toString();
}
if (value is AssetTypeEnum) {
return AssetTypeEnumTypeTransformer().encode(value).toString();
}
if (value is AssetUploadAction) {
return AssetUploadActionTypeTransformer().encode(value).toString();
}
if (value is AssetVisibility) {
return AssetVisibilityTypeTransformer().encode(value).toString();
}
@@ -142,9 +133,6 @@ String parameterToString(dynamic value) {
if (value is PluginContextType) {
return PluginContextTypeTypeTransformer().encode(value).toString();
}
if (value is PluginJsonSchemaType) {
return PluginJsonSchemaTypeTypeTransformer().encode(value).toString();
}
if (value is PluginTriggerType) {
return PluginTriggerTypeTypeTransformer().encode(value).toString();
}

View File

@@ -40,6 +40,7 @@ class ActivityCreateDto {
///
String? comment;
/// Activity type (like or comment)
ReactionType type;
@override

View File

@@ -33,6 +33,7 @@ class ActivityResponseDto {
/// Activity ID
String id;
/// Activity type
ReactionType type;
UserResponseDto user;
@@ -71,9 +72,7 @@ class ActivityResponseDto {
} else {
// json[r'comment'] = null;
}
json[r'createdAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
? this.createdAt.millisecondsSinceEpoch
: this.createdAt.toUtc().toIso8601String();
json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
json[r'id'] = this.id;
json[r'type'] = this.type;
json[r'user'] = this.user;
@@ -91,7 +90,7 @@ class ActivityResponseDto {
return ActivityResponseDto(
assetId: mapValueOfType<String>(json, r'assetId'),
comment: mapValueOfType<String>(json, r'comment'),
createdAt: mapDateTime(json, r'createdAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
createdAt: mapDateTime(json, r'createdAt', r'')!,
id: mapValueOfType<String>(json, r'id')!,
type: ReactionType.fromJson(json[r'type'])!,
user: UserResponseDto.fromJson(json[r'user'])!,

View File

@@ -18,15 +18,9 @@ class ActivityStatisticsResponseDto {
});
/// Number of comments
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int comments;
/// Number of likes
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int likes;
@override

View File

@@ -43,9 +43,6 @@ class AlbumResponseDto {
List<AlbumUserResponseDto> albumUsers;
/// Number of assets
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int assetCount;
List<AssetResponseDto> assets;
@@ -85,6 +82,7 @@ class AlbumResponseDto {
///
DateTime? lastModifiedAssetTimestamp;
/// Asset sort order
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated

View File

@@ -19,21 +19,12 @@ class AlbumStatisticsResponseDto {
});
/// Number of non-shared albums
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int notShared;
/// Number of owned albums
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int owned;
/// Number of shared albums
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int shared;
@override

View File

@@ -13,17 +13,12 @@ part of openapi.api;
class AlbumUserAddDto {
/// Returns a new [AlbumUserAddDto] instance.
AlbumUserAddDto({
this.role,
this.role = AlbumUserRole.editor,
required this.userId,
});
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
AlbumUserRole? role;
/// Album user role
AlbumUserRole role;
/// User ID
String userId;
@@ -36,7 +31,7 @@ class AlbumUserAddDto {
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(role == null ? 0 : role!.hashCode) +
(role.hashCode) +
(userId.hashCode);
@override
@@ -44,11 +39,7 @@ class AlbumUserAddDto {
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
if (this.role != null) {
json[r'role'] = this.role;
} else {
// json[r'role'] = null;
}
json[r'userId'] = this.userId;
return json;
}
@@ -62,7 +53,7 @@ class AlbumUserAddDto {
final json = value.cast<String, dynamic>();
return AlbumUserAddDto(
role: AlbumUserRole.fromJson(json[r'role']),
role: AlbumUserRole.fromJson(json[r'role']) ?? AlbumUserRole.editor,
userId: mapValueOfType<String>(json, r'userId')!,
);
}

View File

@@ -17,6 +17,7 @@ class AlbumUserCreateDto {
required this.userId,
});
/// Album user role
AlbumUserRole role;
/// User ID

View File

@@ -17,6 +17,7 @@ class AlbumUserResponseDto {
required this.user,
});
/// Album user role
AlbumUserRole role;
UserResponseDto user;

View File

@@ -17,6 +17,7 @@ class AlbumsAddAssetsResponseDto {
required this.success,
});
/// Error reason
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated

View File

@@ -13,9 +13,10 @@ part of openapi.api;
class AlbumsResponse {
/// Returns a new [AlbumsResponse] instance.
AlbumsResponse({
required this.defaultAssetOrder,
this.defaultAssetOrder = AssetOrder.desc,
});
/// Default asset order for albums
AssetOrder defaultAssetOrder;
@override

View File

@@ -16,6 +16,7 @@ class AlbumsUpdate {
this.defaultAssetOrder,
});
/// Default asset order for albums
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated

View File

@@ -57,15 +57,11 @@ class APIKeyResponseDto {
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'createdAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
? this.createdAt.millisecondsSinceEpoch
: this.createdAt.toUtc().toIso8601String();
json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
json[r'id'] = this.id;
json[r'name'] = this.name;
json[r'permissions'] = this.permissions;
json[r'updatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
? this.updatedAt.millisecondsSinceEpoch
: this.updatedAt.toUtc().toIso8601String();
json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
return json;
}
@@ -78,11 +74,11 @@ class APIKeyResponseDto {
final json = value.cast<String, dynamic>();
return APIKeyResponseDto(
createdAt: mapDateTime(json, r'createdAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
createdAt: mapDateTime(json, r'createdAt', r'')!,
id: mapValueOfType<String>(json, r'id')!,
name: mapValueOfType<String>(json, r'name')!,
permissions: Permission.listFromJson(json[r'permissions']),
updatedAt: mapDateTime(json, r'updatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
);
}
return null;

View File

@@ -70,9 +70,6 @@ class AssetBulkUpdateDto {
/// Latitude coordinate
///
/// Minimum value: -90
/// Maximum value: 90
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
@@ -82,9 +79,6 @@ class AssetBulkUpdateDto {
/// Longitude coordinate
///
/// Minimum value: -180
/// Maximum value: 180
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
@@ -96,7 +90,7 @@ class AssetBulkUpdateDto {
///
/// Minimum value: -1
/// Maximum value: 5
int? rating;
num? rating;
/// Time zone (IANA timezone)
///
@@ -107,6 +101,7 @@ class AssetBulkUpdateDto {
///
String? timeZone;
/// Asset visibility
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
@@ -222,7 +217,9 @@ class AssetBulkUpdateDto {
isFavorite: mapValueOfType<bool>(json, r'isFavorite'),
latitude: num.parse('${json[r'latitude']}'),
longitude: num.parse('${json[r'longitude']}'),
rating: mapValueOfType<int>(json, r'rating'),
rating: json[r'rating'] == null
? null
: num.parse('${json[r'rating']}'),
timeZone: mapValueOfType<String>(json, r'timeZone'),
visibility: AssetVisibility.fromJson(json[r'visibility']),
);

View File

@@ -20,7 +20,8 @@ class AssetBulkUploadCheckResult {
this.reason,
});
AssetUploadAction action;
/// Upload action
AssetBulkUploadCheckResultActionEnum action;
/// Existing asset ID if duplicate
///
@@ -43,13 +44,8 @@ class AssetBulkUploadCheckResult {
///
bool? isTrashed;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
AssetRejectReason? reason;
/// Rejection reason if rejected
AssetBulkUploadCheckResultReasonEnum? reason;
@override
bool operator ==(Object other) => identical(this, other) || other is AssetBulkUploadCheckResult &&
@@ -102,11 +98,11 @@ class AssetBulkUploadCheckResult {
final json = value.cast<String, dynamic>();
return AssetBulkUploadCheckResult(
action: AssetUploadAction.fromJson(json[r'action'])!,
action: AssetBulkUploadCheckResultActionEnum.fromJson(json[r'action'])!,
assetId: mapValueOfType<String>(json, r'assetId'),
id: mapValueOfType<String>(json, r'id')!,
isTrashed: mapValueOfType<bool>(json, r'isTrashed'),
reason: AssetRejectReason.fromJson(json[r'reason']),
reason: AssetBulkUploadCheckResultReasonEnum.fromJson(json[r'reason']),
);
}
return null;
@@ -159,3 +155,151 @@ class AssetBulkUploadCheckResult {
};
}
/// Upload action
class AssetBulkUploadCheckResultActionEnum {
/// Instantiate a new enum with the provided [value].
const AssetBulkUploadCheckResultActionEnum._(this.value);
/// The underlying value of this enum member.
final String value;
@override
String toString() => value;
String toJson() => value;
static const accept = AssetBulkUploadCheckResultActionEnum._(r'accept');
static const reject = AssetBulkUploadCheckResultActionEnum._(r'reject');
/// List of all possible values in this [enum][AssetBulkUploadCheckResultActionEnum].
static const values = <AssetBulkUploadCheckResultActionEnum>[
accept,
reject,
];
static AssetBulkUploadCheckResultActionEnum? fromJson(dynamic value) => AssetBulkUploadCheckResultActionEnumTypeTransformer().decode(value);
static List<AssetBulkUploadCheckResultActionEnum> listFromJson(dynamic json, {bool growable = false,}) {
final result = <AssetBulkUploadCheckResultActionEnum>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = AssetBulkUploadCheckResultActionEnum.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
}
/// Transformation class that can [encode] an instance of [AssetBulkUploadCheckResultActionEnum] to String,
/// and [decode] dynamic data back to [AssetBulkUploadCheckResultActionEnum].
class AssetBulkUploadCheckResultActionEnumTypeTransformer {
factory AssetBulkUploadCheckResultActionEnumTypeTransformer() => _instance ??= const AssetBulkUploadCheckResultActionEnumTypeTransformer._();
const AssetBulkUploadCheckResultActionEnumTypeTransformer._();
String encode(AssetBulkUploadCheckResultActionEnum data) => data.value;
/// Decodes a [dynamic value][data] to a AssetBulkUploadCheckResultActionEnum.
///
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
///
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
/// and users are still using an old app with the old code.
AssetBulkUploadCheckResultActionEnum? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'accept': return AssetBulkUploadCheckResultActionEnum.accept;
case r'reject': return AssetBulkUploadCheckResultActionEnum.reject;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
}
}
}
return null;
}
/// Singleton [AssetBulkUploadCheckResultActionEnumTypeTransformer] instance.
static AssetBulkUploadCheckResultActionEnumTypeTransformer? _instance;
}
/// Rejection reason if rejected
class AssetBulkUploadCheckResultReasonEnum {
/// Instantiate a new enum with the provided [value].
const AssetBulkUploadCheckResultReasonEnum._(this.value);
/// The underlying value of this enum member.
final String value;
@override
String toString() => value;
String toJson() => value;
static const duplicate = AssetBulkUploadCheckResultReasonEnum._(r'duplicate');
static const unsupportedFormat = AssetBulkUploadCheckResultReasonEnum._(r'unsupported-format');
/// List of all possible values in this [enum][AssetBulkUploadCheckResultReasonEnum].
static const values = <AssetBulkUploadCheckResultReasonEnum>[
duplicate,
unsupportedFormat,
];
static AssetBulkUploadCheckResultReasonEnum? fromJson(dynamic value) => AssetBulkUploadCheckResultReasonEnumTypeTransformer().decode(value);
static List<AssetBulkUploadCheckResultReasonEnum> listFromJson(dynamic json, {bool growable = false,}) {
final result = <AssetBulkUploadCheckResultReasonEnum>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = AssetBulkUploadCheckResultReasonEnum.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
}
/// Transformation class that can [encode] an instance of [AssetBulkUploadCheckResultReasonEnum] to String,
/// and [decode] dynamic data back to [AssetBulkUploadCheckResultReasonEnum].
class AssetBulkUploadCheckResultReasonEnumTypeTransformer {
factory AssetBulkUploadCheckResultReasonEnumTypeTransformer() => _instance ??= const AssetBulkUploadCheckResultReasonEnumTypeTransformer._();
const AssetBulkUploadCheckResultReasonEnumTypeTransformer._();
String encode(AssetBulkUploadCheckResultReasonEnum data) => data.value;
/// Decodes a [dynamic value][data] to a AssetBulkUploadCheckResultReasonEnum.
///
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
///
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
/// and users are still using an old app with the old code.
AssetBulkUploadCheckResultReasonEnum? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'duplicate': return AssetBulkUploadCheckResultReasonEnum.duplicate;
case r'unsupported-format': return AssetBulkUploadCheckResultReasonEnum.unsupportedFormat;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
}
}
}
return null;
}
/// Singleton [AssetBulkUploadCheckResultReasonEnumTypeTransformer] instance.
static AssetBulkUploadCheckResultReasonEnumTypeTransformer? _instance;
}

View File

@@ -39,9 +39,7 @@ class AssetDeltaSyncDto {
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'updatedAfter'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
? this.updatedAfter.millisecondsSinceEpoch
: this.updatedAfter.toUtc().toIso8601String();
json[r'updatedAfter'] = this.updatedAfter.toUtc().toIso8601String();
json[r'userIds'] = this.userIds;
return json;
}
@@ -55,7 +53,7 @@ class AssetDeltaSyncDto {
final json = value.cast<String, dynamic>();
return AssetDeltaSyncDto(
updatedAfter: mapDateTime(json, r'updatedAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
updatedAfter: mapDateTime(json, r'updatedAfter', r'')!,
userIds: json[r'userIds'] is Iterable
? (json[r'userIds'] as Iterable).cast<String>().toList(growable: false)
: const [],

View File

@@ -24,6 +24,7 @@ class AssetDeltaSyncResponseDto {
/// Whether full sync is needed
bool needsFullSync;
/// Upserted assets
List<AssetResponseDto> upserted;
@override

View File

@@ -17,6 +17,7 @@ class AssetEditActionItemDto {
required this.parameters,
});
/// Type of edit action to perform
AssetEditAction action;
AssetEditActionItemDtoParameters parameters;

View File

@@ -44,6 +44,7 @@ class AssetEditActionItemDtoParameters {
/// Rotation angle in degrees
num angle;
/// Axis to mirror along
MirrorAxis axis;
@override

View File

@@ -18,9 +18,9 @@ class AssetEditActionItemResponseDto {
required this.parameters,
});
/// Type of edit action to perform
AssetEditAction action;
/// Asset edit ID
String id;
AssetEditActionItemDtoParameters parameters;

View File

@@ -27,42 +27,24 @@ class AssetFaceCreateDto {
String assetId;
/// Face bounding box height
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int height;
/// Image height in pixels
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int imageHeight;
/// Image width in pixels
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int imageWidth;
/// Person ID
String personId;
/// Face bounding box width
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int width;
/// Face bounding box X coordinate
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int x;
/// Face bounding box Y coordinate
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int y;
@override

View File

@@ -25,46 +25,30 @@ class AssetFaceResponseDto {
});
/// Bounding box X1 coordinate
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int boundingBoxX1;
/// Bounding box X2 coordinate
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int boundingBoxX2;
/// Bounding box Y1 coordinate
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int boundingBoxY1;
/// Bounding box Y2 coordinate
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int boundingBoxY2;
/// Face ID
String id;
/// Image height in pixels
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int imageHeight;
/// Image width in pixels
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int imageWidth;
/// Person associated with face
PersonResponseDto? person;
/// Face detection source type
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated

View File

@@ -24,44 +24,27 @@ class AssetFaceWithoutPersonResponseDto {
});
/// Bounding box X1 coordinate
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int boundingBoxX1;
/// Bounding box X2 coordinate
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int boundingBoxX2;
/// Bounding box Y1 coordinate
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int boundingBoxY1;
/// Bounding box Y2 coordinate
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int boundingBoxY2;
/// Face ID
String id;
/// Image height in pixels
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int imageHeight;
/// Image width in pixels
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int imageWidth;
/// Face detection source type
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated

View File

@@ -31,7 +31,6 @@ class AssetFullSyncDto {
/// Maximum number of assets to return
///
/// Minimum value: 1
/// Maximum value: 9007199254740991
int limit;
/// Sync assets updated until this date
@@ -72,9 +71,7 @@ class AssetFullSyncDto {
// json[r'lastId'] = null;
}
json[r'limit'] = this.limit;
json[r'updatedUntil'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
? this.updatedUntil.millisecondsSinceEpoch
: this.updatedUntil.toUtc().toIso8601String();
json[r'updatedUntil'] = this.updatedUntil.toUtc().toIso8601String();
if (this.userId != null) {
json[r'userId'] = this.userId;
} else {
@@ -94,7 +91,7 @@ class AssetFullSyncDto {
return AssetFullSyncDto(
lastId: mapValueOfType<String>(json, r'lastId'),
limit: mapValueOfType<int>(json, r'limit')!,
updatedUntil: mapDateTime(json, r'updatedUntil', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
updatedUntil: mapDateTime(json, r'updatedUntil', r'')!,
userId: mapValueOfType<String>(json, r'userId'),
);
}

View File

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

View File

@@ -21,13 +21,8 @@ class AssetIdsResponseDto {
/// Asset ID
String assetId;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
AssetIdErrorReason? error;
/// Error reason if failed
AssetIdsResponseDtoErrorEnum? error;
/// Whether operation succeeded
bool success;
@@ -70,7 +65,7 @@ class AssetIdsResponseDto {
return AssetIdsResponseDto(
assetId: mapValueOfType<String>(json, r'assetId')!,
error: AssetIdErrorReason.fromJson(json[r'error']),
error: AssetIdsResponseDtoErrorEnum.fromJson(json[r'error']),
success: mapValueOfType<bool>(json, r'success')!,
);
}
@@ -124,3 +119,80 @@ class AssetIdsResponseDto {
};
}
/// Error reason if failed
class AssetIdsResponseDtoErrorEnum {
/// Instantiate a new enum with the provided [value].
const AssetIdsResponseDtoErrorEnum._(this.value);
/// The underlying value of this enum member.
final String value;
@override
String toString() => value;
String toJson() => value;
static const duplicate = AssetIdsResponseDtoErrorEnum._(r'duplicate');
static const noPermission = AssetIdsResponseDtoErrorEnum._(r'no_permission');
static const notFound = AssetIdsResponseDtoErrorEnum._(r'not_found');
/// List of all possible values in this [enum][AssetIdsResponseDtoErrorEnum].
static const values = <AssetIdsResponseDtoErrorEnum>[
duplicate,
noPermission,
notFound,
];
static AssetIdsResponseDtoErrorEnum? fromJson(dynamic value) => AssetIdsResponseDtoErrorEnumTypeTransformer().decode(value);
static List<AssetIdsResponseDtoErrorEnum> listFromJson(dynamic json, {bool growable = false,}) {
final result = <AssetIdsResponseDtoErrorEnum>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = AssetIdsResponseDtoErrorEnum.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
}
/// Transformation class that can [encode] an instance of [AssetIdsResponseDtoErrorEnum] to String,
/// and [decode] dynamic data back to [AssetIdsResponseDtoErrorEnum].
class AssetIdsResponseDtoErrorEnumTypeTransformer {
factory AssetIdsResponseDtoErrorEnumTypeTransformer() => _instance ??= const AssetIdsResponseDtoErrorEnumTypeTransformer._();
const AssetIdsResponseDtoErrorEnumTypeTransformer._();
String encode(AssetIdsResponseDtoErrorEnum data) => data.value;
/// Decodes a [dynamic value][data] to a AssetIdsResponseDtoErrorEnum.
///
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
///
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
/// and users are still using an old app with the old code.
AssetIdsResponseDtoErrorEnum? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'duplicate': return AssetIdsResponseDtoErrorEnum.duplicate;
case r'no_permission': return AssetIdsResponseDtoErrorEnum.noPermission;
case r'not_found': return AssetIdsResponseDtoErrorEnum.notFound;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
}
}
}
return null;
}
/// Singleton [AssetIdsResponseDtoErrorEnumTypeTransformer] instance.
static AssetIdsResponseDtoErrorEnumTypeTransformer? _instance;
}

View File

@@ -20,6 +20,7 @@ class AssetJobsDto {
/// Asset IDs
List<String> assetIds;
/// Job name
AssetJobName name;
@override

View File

@@ -20,6 +20,7 @@ class AssetMediaResponseDto {
/// Asset media ID
String id;
/// Upload status
AssetMediaStatus status;
@override

View File

@@ -10,7 +10,7 @@
part of openapi.api;
/// Asset media size
class AssetMediaSize {
/// Instantiate a new enum with the provided [value].
const AssetMediaSize._(this.value);

View File

@@ -16,7 +16,7 @@ class AssetMetadataBulkResponseDto {
required this.assetId,
required this.key,
required this.updatedAt,
this.value = const {},
required this.value,
});
/// Asset ID
@@ -29,14 +29,14 @@ class AssetMetadataBulkResponseDto {
DateTime updatedAt;
/// Metadata value (object)
Map<String, Object> value;
Object value;
@override
bool operator ==(Object other) => identical(this, other) || other is AssetMetadataBulkResponseDto &&
other.assetId == assetId &&
other.key == key &&
other.updatedAt == updatedAt &&
_deepEquality.equals(other.value, value);
other.value == value;
@override
int get hashCode =>
@@ -53,9 +53,7 @@ class AssetMetadataBulkResponseDto {
final json = <String, dynamic>{};
json[r'assetId'] = this.assetId;
json[r'key'] = this.key;
json[r'updatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
? this.updatedAt.millisecondsSinceEpoch
: this.updatedAt.toUtc().toIso8601String();
json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
json[r'value'] = this.value;
return json;
}
@@ -71,8 +69,8 @@ class AssetMetadataBulkResponseDto {
return AssetMetadataBulkResponseDto(
assetId: mapValueOfType<String>(json, r'assetId')!,
key: mapValueOfType<String>(json, r'key')!,
updatedAt: mapDateTime(json, r'updatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
value: mapCastOfType<String, Object>(json, r'value')!,
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
value: mapValueOfType<Object>(json, r'value')!,
);
}
return null;

View File

@@ -15,7 +15,7 @@ class AssetMetadataBulkUpsertItemDto {
AssetMetadataBulkUpsertItemDto({
required this.assetId,
required this.key,
this.value = const {},
required this.value,
});
/// Asset ID
@@ -25,13 +25,13 @@ class AssetMetadataBulkUpsertItemDto {
String key;
/// Metadata value (object)
Map<String, Object> value;
Object value;
@override
bool operator ==(Object other) => identical(this, other) || other is AssetMetadataBulkUpsertItemDto &&
other.assetId == assetId &&
other.key == key &&
_deepEquality.equals(other.value, value);
other.value == value;
@override
int get hashCode =>
@@ -62,7 +62,7 @@ class AssetMetadataBulkUpsertItemDto {
return AssetMetadataBulkUpsertItemDto(
assetId: mapValueOfType<String>(json, r'assetId')!,
key: mapValueOfType<String>(json, r'key')!,
value: mapCastOfType<String, Object>(json, r'value')!,
value: mapValueOfType<Object>(json, r'value')!,
);
}
return null;

View File

@@ -15,7 +15,7 @@ class AssetMetadataResponseDto {
AssetMetadataResponseDto({
required this.key,
required this.updatedAt,
this.value = const {},
required this.value,
});
/// Metadata key
@@ -25,13 +25,13 @@ class AssetMetadataResponseDto {
DateTime updatedAt;
/// Metadata value (object)
Map<String, Object> value;
Object value;
@override
bool operator ==(Object other) => identical(this, other) || other is AssetMetadataResponseDto &&
other.key == key &&
other.updatedAt == updatedAt &&
_deepEquality.equals(other.value, value);
other.value == value;
@override
int get hashCode =>
@@ -46,9 +46,7 @@ class AssetMetadataResponseDto {
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'key'] = this.key;
json[r'updatedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
? this.updatedAt.millisecondsSinceEpoch
: this.updatedAt.toUtc().toIso8601String();
json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
json[r'value'] = this.value;
return json;
}
@@ -63,8 +61,8 @@ class AssetMetadataResponseDto {
return AssetMetadataResponseDto(
key: mapValueOfType<String>(json, r'key')!,
updatedAt: mapDateTime(json, r'updatedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
value: mapCastOfType<String, Object>(json, r'value')!,
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
value: mapValueOfType<Object>(json, r'value')!,
);
}
return null;

View File

@@ -14,19 +14,19 @@ class AssetMetadataUpsertItemDto {
/// Returns a new [AssetMetadataUpsertItemDto] instance.
AssetMetadataUpsertItemDto({
required this.key,
this.value = const {},
required this.value,
});
/// Metadata key
String key;
/// Metadata value (object)
Map<String, Object> value;
Object value;
@override
bool operator ==(Object other) => identical(this, other) || other is AssetMetadataUpsertItemDto &&
other.key == key &&
_deepEquality.equals(other.value, value);
other.value == value;
@override
int get hashCode =>
@@ -54,7 +54,7 @@ class AssetMetadataUpsertItemDto {
return AssetMetadataUpsertItemDto(
key: mapValueOfType<String>(json, r'key')!,
value: mapCastOfType<String, Object>(json, r'value')!,
value: mapValueOfType<Object>(json, r'value')!,
);
}
return null;

View File

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

View File

@@ -86,8 +86,6 @@ class AssetResponseDto {
bool hasMetadata;
/// Asset height
///
/// Minimum value: 0
num? height;
/// Asset ID
@@ -161,6 +159,7 @@ class AssetResponseDto {
/// Thumbhash for thumbnail generation (base64) also used as the c query param for thumbnail cache busting.
String? thumbhash;
/// Asset type
AssetTypeEnum type;
List<AssetFaceWithoutPersonResponseDto> unassignedFaces;
@@ -168,11 +167,10 @@ class AssetResponseDto {
/// The UTC timestamp when the asset record was last updated in the database. This is automatically maintained by the database and reflects when any field in the asset was last modified.
DateTime updatedAt;
/// Asset visibility
AssetVisibility visibility;
/// Asset width
///
/// Minimum value: 0
num? width;
@override

View File

@@ -19,9 +19,6 @@ class AssetStackResponseDto {
});
/// Number of assets in stack
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int assetCount;
/// Stack ID

View File

@@ -19,21 +19,12 @@ class AssetStatsResponseDto {
});
/// Number of images
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int images;
/// Total number of assets
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int total;
/// Number of videos
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int videos;
@override

View File

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

View File

@@ -16,6 +16,7 @@ class AvatarUpdate {
this.color,
});
/// Avatar color
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated

View File

@@ -18,13 +18,8 @@ class BulkIdResponseDto {
required this.success,
});
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
BulkIdErrorReason? error;
/// Error reason if failed
BulkIdResponseDtoErrorEnum? error;
/// ID
String id;
@@ -69,7 +64,7 @@ class BulkIdResponseDto {
final json = value.cast<String, dynamic>();
return BulkIdResponseDto(
error: BulkIdErrorReason.fromJson(json[r'error']),
error: BulkIdResponseDtoErrorEnum.fromJson(json[r'error']),
id: mapValueOfType<String>(json, r'id')!,
success: mapValueOfType<bool>(json, r'success')!,
);
@@ -124,3 +119,83 @@ class BulkIdResponseDto {
};
}
/// Error reason if failed
class BulkIdResponseDtoErrorEnum {
/// Instantiate a new enum with the provided [value].
const BulkIdResponseDtoErrorEnum._(this.value);
/// The underlying value of this enum member.
final String value;
@override
String toString() => value;
String toJson() => value;
static const duplicate = BulkIdResponseDtoErrorEnum._(r'duplicate');
static const noPermission = BulkIdResponseDtoErrorEnum._(r'no_permission');
static const notFound = BulkIdResponseDtoErrorEnum._(r'not_found');
static const unknown = BulkIdResponseDtoErrorEnum._(r'unknown');
/// List of all possible values in this [enum][BulkIdResponseDtoErrorEnum].
static const values = <BulkIdResponseDtoErrorEnum>[
duplicate,
noPermission,
notFound,
unknown,
];
static BulkIdResponseDtoErrorEnum? fromJson(dynamic value) => BulkIdResponseDtoErrorEnumTypeTransformer().decode(value);
static List<BulkIdResponseDtoErrorEnum> listFromJson(dynamic json, {bool growable = false,}) {
final result = <BulkIdResponseDtoErrorEnum>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = BulkIdResponseDtoErrorEnum.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
}
/// Transformation class that can [encode] an instance of [BulkIdResponseDtoErrorEnum] to String,
/// and [decode] dynamic data back to [BulkIdResponseDtoErrorEnum].
class BulkIdResponseDtoErrorEnumTypeTransformer {
factory BulkIdResponseDtoErrorEnumTypeTransformer() => _instance ??= const BulkIdResponseDtoErrorEnumTypeTransformer._();
const BulkIdResponseDtoErrorEnumTypeTransformer._();
String encode(BulkIdResponseDtoErrorEnum data) => data.value;
/// Decodes a [dynamic value][data] to a BulkIdResponseDtoErrorEnum.
///
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
///
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
/// and users are still using an old app with the old code.
BulkIdResponseDtoErrorEnum? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'duplicate': return BulkIdResponseDtoErrorEnum.duplicate;
case r'no_permission': return BulkIdResponseDtoErrorEnum.noPermission;
case r'not_found': return BulkIdResponseDtoErrorEnum.notFound;
case r'unknown': return BulkIdResponseDtoErrorEnum.unknown;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
}
}
}
return null;
}
/// Singleton [BulkIdResponseDtoErrorEnumTypeTransformer] instance.
static BulkIdResponseDtoErrorEnumTypeTransformer? _instance;
}

View File

@@ -13,7 +13,7 @@ part of openapi.api;
class CastResponse {
/// Returns a new [CastResponse] instance.
CastResponse({
required this.gCastEnabled,
this.gCastEnabled = false,
});
/// Whether Google Cast is enabled

View File

@@ -18,9 +18,6 @@ class ContributorCountResponseDto {
});
/// Number of assets contributed
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int assetCount;
/// User ID

View File

@@ -13,17 +13,17 @@ part of openapi.api;
class CreateLibraryDto {
/// Returns a new [CreateLibraryDto] instance.
CreateLibraryDto({
this.exclusionPatterns = const [],
this.importPaths = const [],
this.exclusionPatterns = const {},
this.importPaths = const {},
this.name,
required this.ownerId,
});
/// Exclusion patterns (max 128)
List<String> exclusionPatterns;
Set<String> exclusionPatterns;
/// Import paths (max 128)
List<String> importPaths;
Set<String> importPaths;
/// Library name
///
@@ -57,8 +57,8 @@ class CreateLibraryDto {
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'exclusionPatterns'] = this.exclusionPatterns;
json[r'importPaths'] = this.importPaths;
json[r'exclusionPatterns'] = this.exclusionPatterns.toList(growable: false);
json[r'importPaths'] = this.importPaths.toList(growable: false);
if (this.name != null) {
json[r'name'] = this.name;
} else {
@@ -78,11 +78,11 @@ class CreateLibraryDto {
return CreateLibraryDto(
exclusionPatterns: json[r'exclusionPatterns'] is Iterable
? (json[r'exclusionPatterns'] as Iterable).cast<String>().toList(growable: false)
: const [],
? (json[r'exclusionPatterns'] as Iterable).cast<String>().toSet()
: const {},
importPaths: json[r'importPaths'] is Iterable
? (json[r'importPaths'] as Iterable).cast<String>().toList(growable: false)
: const [],
? (json[r'importPaths'] as Iterable).cast<String>().toSet()
: const {},
name: mapValueOfType<String>(json, r'name'),
ownerId: mapValueOfType<String>(json, r'ownerId')!,
);

View File

@@ -45,9 +45,7 @@ class CreateProfileImageResponseDto {
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'profileChangedAt'] = _isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')
? this.profileChangedAt.millisecondsSinceEpoch
: this.profileChangedAt.toUtc().toIso8601String();
json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String();
json[r'profileImagePath'] = this.profileImagePath;
json[r'userId'] = this.userId;
return json;
@@ -62,7 +60,7 @@ class CreateProfileImageResponseDto {
final json = value.cast<String, dynamic>();
return CreateProfileImageResponseDto(
profileChangedAt: mapDateTime(json, r'profileChangedAt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/')!,
profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!,
profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
userId: mapValueOfType<String>(json, r'userId')!,
);

View File

@@ -16,7 +16,6 @@ class DatabaseBackupDeleteDto {
this.backups = const [],
});
/// Backup filenames to delete
List<String> backups;
@override

View File

@@ -17,10 +17,8 @@ class DatabaseBackupDto {
required this.filesize,
});
/// Backup filename
String filename;
/// Backup file size
num filesize;
@override

View File

@@ -16,7 +16,6 @@ class DatabaseBackupListResponseDto {
this.backups = const [],
});
/// List of backups
List<DatabaseBackupDto> backups;
@override

View File

@@ -21,9 +21,6 @@ class DownloadArchiveInfo {
List<String> assetIds;
/// Archive size in bytes
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int size;
@override

View File

@@ -31,7 +31,6 @@ class DownloadInfoDto {
/// Archive size limit in bytes
///
/// Minimum value: 1
/// Maximum value: 9007199254740991
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated

View File

@@ -14,13 +14,10 @@ class DownloadResponse {
/// Returns a new [DownloadResponse] instance.
DownloadResponse({
required this.archiveSize,
required this.includeEmbeddedVideos,
this.includeEmbeddedVideos = false,
});
/// Maximum archive size in bytes
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int archiveSize;
/// Whether to include embedded videos in downloads

View File

@@ -21,9 +21,6 @@ class DownloadResponseDto {
List<DownloadArchiveInfo> archives;
/// Total size in bytes
///
/// Minimum value: -9007199254740991
/// Maximum value: 9007199254740991
int totalSize;
@override

View File

@@ -20,7 +20,6 @@ class DownloadUpdate {
/// Maximum archive size in bytes
///
/// Minimum value: 1
/// Maximum value: 9007199254740991
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated

View File

@@ -50,13 +50,9 @@ class ExifResponseDto {
String? description;
/// Image height in pixels
///
/// Minimum value: 0
num? exifImageHeight;
/// Image width in pixels
///
/// Minimum value: 0
num? exifImageWidth;
/// Exposure time
@@ -66,9 +62,6 @@ class ExifResponseDto {
num? fNumber;
/// File size in bytes
///
/// Minimum value: 0
/// Maximum value: 9007199254740991
int? fileSizeInByte;
/// Focal length in mm

View File

@@ -32,7 +32,6 @@ class FacialRecognitionConfig {
/// Minimum number of faces required for recognition
///
/// Minimum value: 1
/// Maximum value: 9007199254740991
int minFaces;
/// Minimum confidence score for face detection

View File

@@ -13,8 +13,8 @@ part of openapi.api;
class FoldersResponse {
/// Returns a new [FoldersResponse] instance.
FoldersResponse({
required this.enabled,
required this.sidebarWeb,
this.enabled = false,
this.sidebarWeb = false,
});
/// Whether folders are enabled

View File

@@ -16,6 +16,7 @@ class JobCreateDto {
required this.name,
});
/// Job name
ManualJobName name;
@override

View File

@@ -19,7 +19,6 @@ class JobSettingsDto {
/// Concurrency
///
/// Minimum value: 1
/// Maximum value: 9007199254740991
int concurrency;
@override

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