mirror of
https://github.com/immich-app/immich.git
synced 2025-12-06 21:01:24 -08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dddc6b449 | ||
|
|
88ac3c2016 | ||
|
|
17eaeb695e | ||
|
|
f80f867976 | ||
|
|
d15c443d9b | ||
|
|
07b874edda | ||
|
|
df27460f1c |
@@ -7,11 +7,7 @@ Immich uses the [OpenAPI](https://swagger.io/specification/) standard to generat
|
||||
OpenAPI is used to generate the client (Typescript, Dart) SDK. `openapi-generator-cli` can be installed [here](https://openapi-generator.tech/docs/installation/). The generated SDK is based on the `immich-openapi-specs.json` file, which is autogenerated by the server **when running in development mode**. The `immich-openapi-specs.json` file can be modified with `@nestjs/swagger` decorators used or referenced by controller endpoints. See the [NestJS OpenAPI docs](https://docs.nestjs.com/openapi/types-and-parameters) for more info. When you add a new endpoint or modify an existing one, you must run the server in development mode and run the command below to update the client SDK.
|
||||
|
||||
```bash
|
||||
npm run api:generate # Run from the `server/` directory
|
||||
make open-api
|
||||
```
|
||||
|
||||
You can find the generated client SDK in the `web/src/api` for Typescript SDK and `mobile/openapi` for Dart SDK.
|
||||
|
||||
:::tip
|
||||
This can also be run via `make open-api` from the project root directory (not in the `server` folder)
|
||||
:::
|
||||
You can find the generated client SDK in the `open-api/typescript-sdk/client` for Typescript SDK and `mobile/openapi` for Dart SDK.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "machine-learning"
|
||||
version = "1.93.1"
|
||||
version = "1.93.2"
|
||||
description = ""
|
||||
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
||||
readme = "README.md"
|
||||
|
||||
@@ -62,7 +62,7 @@ fi
|
||||
if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then
|
||||
echo "Pumping Server: $CURRENT_SERVER => $NEXT_SERVER"
|
||||
npm --prefix server version $SERVER_PUMP
|
||||
npm --prefix server run api:generate
|
||||
make open-api
|
||||
poetry --directory machine-learning version $SERVER_PUMP
|
||||
fi
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ platform :android do
|
||||
task: 'bundle',
|
||||
build_type: 'Release',
|
||||
properties: {
|
||||
"android.injected.version.code" => 118,
|
||||
"android.injected.version.name" => "1.93.1",
|
||||
"android.injected.version.code" => 119,
|
||||
"android.injected.version.name" => "1.93.2",
|
||||
}
|
||||
)
|
||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||
|
||||
@@ -19,7 +19,7 @@ platform :ios do
|
||||
desc "iOS Beta"
|
||||
lane :beta do
|
||||
increment_version_number(
|
||||
version_number: "1.93.1"
|
||||
version_number: "1.93.2"
|
||||
)
|
||||
increment_build_number(
|
||||
build_number: latest_testflight_build_number + 1,
|
||||
|
||||
@@ -197,7 +197,7 @@ class ControlBottomAppBar extends ConsumerWidget {
|
||||
label: "control_bottom_app_bar_edit_location".tr(),
|
||||
onPressed: enabled ? onEditLocation : null,
|
||||
),
|
||||
if (!hasLocal &&
|
||||
if (!selectionAssetState.hasLocal &&
|
||||
selectionAssetState.selectedCount > 1 &&
|
||||
onStack != null)
|
||||
ControlBoxButton(
|
||||
@@ -211,7 +211,7 @@ class ControlBottomAppBar extends ConsumerWidget {
|
||||
label: 'album_viewer_appbar_share_remove'.tr(),
|
||||
onPressed: enabled ? onRemoveFromAlbum : null,
|
||||
),
|
||||
if (hasLocal)
|
||||
if (selectionAssetState.hasLocal)
|
||||
ControlBoxButton(
|
||||
iconData: Icons.backup_outlined,
|
||||
label: "Upload",
|
||||
|
||||
2
mobile/openapi/README.md
generated
2
mobile/openapi/README.md
generated
@@ -3,7 +3,7 @@ Immich API
|
||||
|
||||
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
||||
|
||||
- API version: 1.93.0
|
||||
- API version: 1.93.2
|
||||
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -2,7 +2,7 @@ name: immich_mobile
|
||||
description: Immich - selfhosted backup media file on mobile phone
|
||||
|
||||
publish_to: "none"
|
||||
version: 1.93.1+118
|
||||
version: 1.93.2+119
|
||||
isar_version: &isar_version 3.1.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@@ -6221,7 +6221,7 @@
|
||||
"info": {
|
||||
"title": "Immich",
|
||||
"description": "Immich API",
|
||||
"version": "1.93.0",
|
||||
"version": "1.93.2",
|
||||
"contact": {}
|
||||
},
|
||||
"tags": [],
|
||||
|
||||
2
open-api/typescript-sdk/client/api.ts
generated
2
open-api/typescript-sdk/client/api.ts
generated
@@ -4,7 +4,7 @@
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.93.0
|
||||
* The version of the OpenAPI document: 1.93.2
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
|
||||
2
open-api/typescript-sdk/client/base.ts
generated
2
open-api/typescript-sdk/client/base.ts
generated
@@ -4,7 +4,7 @@
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.93.0
|
||||
* The version of the OpenAPI document: 1.93.2
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
|
||||
2
open-api/typescript-sdk/client/common.ts
generated
2
open-api/typescript-sdk/client/common.ts
generated
@@ -4,7 +4,7 @@
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.93.0
|
||||
* The version of the OpenAPI document: 1.93.2
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
|
||||
2
open-api/typescript-sdk/client/configuration.ts
generated
2
open-api/typescript-sdk/client/configuration.ts
generated
@@ -4,7 +4,7 @@
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.93.0
|
||||
* The version of the OpenAPI document: 1.93.2
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
|
||||
2
open-api/typescript-sdk/client/index.ts
generated
2
open-api/typescript-sdk/client/index.ts
generated
@@ -4,7 +4,7 @@
|
||||
* Immich
|
||||
* Immich API
|
||||
*
|
||||
* The version of the OpenAPI document: 1.93.0
|
||||
* The version of the OpenAPI document: 1.93.2
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
|
||||
4
server/package-lock.json
generated
4
server/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "immich",
|
||||
"version": "1.93.1",
|
||||
"version": "1.93.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "immich",
|
||||
"version": "1.93.1",
|
||||
"version": "1.93.2",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.22.11",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "immich",
|
||||
"version": "1.93.1",
|
||||
"version": "1.93.2",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
|
||||
@@ -1014,8 +1014,6 @@ describe(PersonService.name, () => {
|
||||
oldPersonId: personStub.mergePerson.id,
|
||||
});
|
||||
|
||||
expect(personMock.update).not.toHaveBeenCalled();
|
||||
|
||||
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
||||
});
|
||||
|
||||
@@ -1040,7 +1038,6 @@ describe(PersonService.name, () => {
|
||||
name: personStub.primaryPerson.name,
|
||||
});
|
||||
|
||||
expect(personMock.delete).toHaveBeenCalledWith([personStub.primaryPerson]);
|
||||
expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1']));
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import { fly } from 'svelte/transition';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Slider from '$lib/components/elements/slider.svelte';
|
||||
|
||||
export let title: string;
|
||||
export let subtitle = '';
|
||||
@@ -11,6 +10,7 @@
|
||||
export let isEdited = false;
|
||||
|
||||
const dispatch = createEventDispatcher<{ toggle: boolean }>();
|
||||
const onToggle = (event: Event) => dispatch('toggle', (event.target as HTMLInputElement).checked);
|
||||
</script>
|
||||
|
||||
<div class="flex place-items-center justify-between">
|
||||
@@ -31,5 +31,67 @@
|
||||
|
||||
<p class="text-sm dark:text-immich-dark-fg">{subtitle}</p>
|
||||
</div>
|
||||
<Slider bind:checked {disabled} on:toggle={() => dispatch('toggle', checked)} />
|
||||
|
||||
<label class="relative inline-block h-[10px] w-[36px] flex-none">
|
||||
<input
|
||||
class="disabled::cursor-not-allowed h-0 w-0 opacity-0"
|
||||
type="checkbox"
|
||||
bind:checked
|
||||
on:click={onToggle}
|
||||
{disabled}
|
||||
/>
|
||||
|
||||
{#if disabled}
|
||||
<span class="slider slider-disabled cursor-not-allowed" />
|
||||
{:else}
|
||||
<span class="slider slider-enabled cursor-pointer" />
|
||||
{/if}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.slider {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: 0.4s;
|
||||
transition: 0.4s;
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: -4px;
|
||||
background-color: gray;
|
||||
-webkit-transition: 0.4s;
|
||||
transition: 0.4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(18px);
|
||||
-ms-transform: translateX(18px);
|
||||
transform: translateX(18px);
|
||||
background-color: #4250af;
|
||||
}
|
||||
|
||||
input:checked + .slider-disabled {
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
input:checked + .slider-enabled {
|
||||
background-color: #adcbfa;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { mdiLink, mdiShareCircle } from '@mdi/js';
|
||||
import { mdiCheck, mdiLink, mdiShareCircle } from '@mdi/js';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
|
||||
export let album: AlbumResponseDto;
|
||||
@@ -60,28 +60,25 @@
|
||||
</span>
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="immich-scrollbar max-h-[300px] overflow-y-auto">
|
||||
{#if selectedUsers.length > 0}
|
||||
<div class="mb-2 flex place-items-center gap-4 overflow-x-auto px-5 py-2">
|
||||
<p class="font-medium">To</p>
|
||||
{#if selectedUsers.length > 0}
|
||||
<div class="mb-2 flex flex-wrap place-items-center gap-4 overflow-x-auto px-5 py-2 sticky">
|
||||
<p class="font-medium">To</p>
|
||||
|
||||
{#each selectedUsers as user}
|
||||
{#key user.id}
|
||||
<button
|
||||
on:click={() => handleUnselect(user)}
|
||||
class="flex place-items-center gap-1 rounded-full border border-gray-400 p-1 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
>
|
||||
<UserAvatar {user} size="sm" />
|
||||
<p class="text-xs font-medium">{user.name}</p>
|
||||
</button>
|
||||
{/key}
|
||||
{/each}
|
||||
<div class="flex place-content-end mr-0 ml-auto p-5">
|
||||
<Button size="sm" rounded="lg" on:click={() => dispatch('select', selectedUsers)}>Add</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#each selectedUsers as user}
|
||||
{#key user.id}
|
||||
<button
|
||||
on:click={() => handleUnselect(user)}
|
||||
class="flex place-items-center gap-1 rounded-full border border-gray-500 p-2 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
>
|
||||
<UserAvatar {user} size="sm" />
|
||||
<p class="text-xs font-medium">{user.name}</p>
|
||||
</button>
|
||||
{/key}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="immich-scrollbar max-h-[500px] overflow-y-auto">
|
||||
{#if users.length > 0}
|
||||
<p class="px-5 text-xs font-medium">SUGGESTIONS</p>
|
||||
|
||||
@@ -92,10 +89,11 @@
|
||||
class="flex w-full place-items-center gap-4 px-5 py-4 transition-all hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
>
|
||||
{#if selectedUsers.includes(user)}
|
||||
<span
|
||||
class="flex h-12 w-12 place-content-center place-items-center rounded-full border bg-immich-primary text-3xl text-white dark:border-immich-dark-gray dark:bg-immich-dark-primary dark:text-immich-dark-bg"
|
||||
>✓</span
|
||||
<div
|
||||
class="flex h-10 w-10 items-center justify-center rounded-full border bg-immich-primary text-3xl text-white dark:border-immich-dark-gray dark:bg-immich-dark-primary dark:text-immich-dark-bg"
|
||||
>
|
||||
<Icon path={mdiCheck} size={24} />
|
||||
</div>
|
||||
{:else}
|
||||
<UserAvatar {user} size="md" />
|
||||
{/if}
|
||||
@@ -118,7 +116,20 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if users.length > 0}
|
||||
<div class="p-3">
|
||||
<Button
|
||||
size="sm"
|
||||
fullwidth
|
||||
rounded="full"
|
||||
disabled={!selectedUsers.length}
|
||||
on:click={() => dispatch('select', selectedUsers)}>Add</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<hr />
|
||||
|
||||
<div id="shared-buttons" class="my-4 flex place-content-center place-items-center justify-around">
|
||||
<button
|
||||
class="flex flex-col place-content-center place-items-center gap-2 hover:cursor-pointer"
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let checked = false;
|
||||
export let disabled = false;
|
||||
|
||||
const dispatch = createEventDispatcher<{ toggle: boolean }>();
|
||||
const onToggle = (event: Event) => dispatch('toggle', (event.target as HTMLInputElement).checked);
|
||||
</script>
|
||||
|
||||
<label class="relative inline-block h-[10px] w-[36px] flex-none">
|
||||
<input
|
||||
class="disabled::cursor-not-allowed h-0 w-0 opacity-0"
|
||||
type="checkbox"
|
||||
bind:checked
|
||||
on:click={onToggle}
|
||||
{disabled}
|
||||
/>
|
||||
|
||||
{#if disabled}
|
||||
<span class="slider slider-disabled cursor-not-allowed" />
|
||||
{:else}
|
||||
<span class="slider slider-enabled cursor-pointer" />
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<style>
|
||||
.slider {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: 0.4s;
|
||||
transition: 0.4s;
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: -4px;
|
||||
background-color: gray;
|
||||
-webkit-transition: 0.4s;
|
||||
transition: 0.4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(18px);
|
||||
-ms-transform: translateX(18px);
|
||||
transform: translateX(18px);
|
||||
background-color: #4250af;
|
||||
}
|
||||
|
||||
input:checked + .slider-disabled {
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
input:checked + .slider-enabled {
|
||||
background-color: #adcbfa;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user