Compare commits

...

13 Commits

Author SHA1 Message Date
Alex The Bot
2872886e77 Version v1.70.0 2023-07-27 03:40:16 +00:00
martin
a21112e4ab fix: people in shared assets (#3431)
* fix: people in shared assets

* use empty array
2023-07-26 21:14:50 -05:00
Jason Rasmussen
f3edf43158 chore: log listen address (#3428) 2023-07-26 18:29:35 +00:00
martin
1c5926553a fix: dialog overflow when creating a user (#3422) 2023-07-25 09:29:40 -05:00
faupau
05fa3092bf fix(web): fixes previous pull request: set asset as profile image (#3415)
* set photoviewer 100% width, fixes transparent ede

* remove unnecessary class

* format fix

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-07-25 05:17:59 +00:00
martyfuhry
7d3ec8af37 fix(mobile): Memory lane now updates to the correct day if the app is resumed the next day (#3414)
* Adds todayProvider to memory lane

* Revert "Adds todayProvider to memory lane"

This reverts commit 67ae58b513.

* Invalidate memory provider on app resume
2023-07-24 11:39:10 -05:00
Mark Monteiro
8db008ef0b Remove unnecessary PG_DATA environement variable from docker-compose.yml (#3394)
* Remove unnecessary PG_DATA environement variable from docker-compose.yml

There is no need to set the PostgreSQL data directory to the default location, it just adds an additional unnecessary line to the docker-compose file.

In addition, the PG_DATA isn't even the correct environment variable name (it should be PGDATA, see: https://hub.docker.com/_/postgres/), so this environment variable was never doing anything to begin with.

* Update docker-compose.dev.yml

* Update docker-compose.prod.yml

* Update docker-compose.test.yml
2023-07-23 21:11:27 -05:00
Alex
e493e05e99 fix(server): better facial recognition order (#3386) 2023-07-23 21:10:56 -05:00
martin
b83e535010 feat(web): show available shortcuts (#3342)
* feat(web): show available shortcuts

* pr feeback

* feat: new shortcut for deselect

* fix: remove new shortcut

* responsive
2023-07-23 21:09:06 -05:00
Daniele Ricci
111372edc1 fix(cli): fix wording in usage guide (#3378) 2023-07-23 21:06:27 -05:00
Mark Monteiro
625a899f64 Update environment-variables.md (#3402)
Add documentation for the environment variables that enable Docker secrets support

Support for these variables was implemented in #1254 and #3282
2023-07-23 17:53:52 -05:00
Alex
aaf0496f74 chore(server): Update Immich CLI version (#3403) 2023-07-23 17:53:20 -05:00
Alex
4977926c88 post mobile release 2023-07-23 13:51:48 -05:00
41 changed files with 175 additions and 60 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -36,7 +36,7 @@ program
)
.addOption(new Option('-i, --ignore [paths...]', 'Paths to ignore').env('IMMICH_IGNORE_PATHS').default(false))
.addOption(new Option('--no-read-only', 'Import files without read-only protection, allowing Immich to manage them'))
.argument('[paths...]', 'One or more paths to assets to be uploaded')
.argument('[paths...]', 'One or more paths to assets to be imported')
.action((paths, options) => {
options.import = true;
options.excludePatterns = options.ignore;

View File

@@ -115,7 +115,6 @@ services:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
PG_DATA: /var/lib/postgresql/data
volumes:
- pgdata:/var/lib/postgresql/data
ports:

View File

@@ -85,7 +85,6 @@ services:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
PG_DATA: /var/lib/postgresql/data
volumes:
- pgdata:/var/lib/postgresql/data
restart: always

View File

@@ -37,7 +37,6 @@ services:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
PG_DATA: /var/lib/postgresql/data
volumes:
- /var/lib/postgresql/data
networks:

View File

@@ -69,7 +69,6 @@ services:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
PG_DATA: /var/lib/postgresql/data
volumes:
- pgdata:/var/lib/postgresql/data
restart: always

View File

@@ -184,3 +184,24 @@ Typesense URL example JSON before encoding:
| `MACHINE_LEARNING_CLASSIFICATION_MODEL` | Classification Model | `microsoft/resnet-50` | machine learning |
| `MACHINE_LEARNING_CACHE_FOLDER` | ML Cache Location | `/cache` | machine learning |
| `TRANSFORMERS_CACHE` | ML Transformers Cache Location | `/cache` | machine learning |
## Docker Secrets
The following variables support the use of [Docker secrets](https://docs.docker.com/engine/swarm/secrets/) for additional security.
To use any of these, replace the regular environment variable with the equivalent `_FILE` environment variable. The value of
the `_FILE` variable should be set to the path of a file containing the variable value.
| Regular Variable | Equivalent Docker Secrets '\_FILE' Variable |
| :----------------: | :-----------------------------------------: |
| `DB_HOSTNAME` | `DB_HOSTNAME_FILE`<sup>\*1</sup> |
| `DB_DATABASE_NAME` | `DB_DATABASE_NAME_FILE`<sup>\*1</sup> |
| `DB_USERNAME` | `DB_USERNAME_FILE`<sup>\*1</sup> |
| `DB_PASSWORD` | `DB_PASSWORD_FILE`<sup>\*1</sup> |
| `REDIS_PASSWORD` | `REDIS_PASSWORD_FILE`<sup>\*2</sup> |
\*1: See the [official documentation](https://github.com/docker-library/docs/tree/master/postgres#docker-secrets) for
details on how to use Docker Secrets in the Postgres image.
\*2: See [this comment](https://github.com/docker-library/redis/issues/46#issuecomment-335326234) for an example of how
to use use a Docker secret for the password in the Redis container.

View File

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

View File

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

View File

@@ -5,17 +5,17 @@
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000296">
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000283">
</testcase>
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="64.042552">
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="68.955404">
</testcase>
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="29.676557">
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="24.147531">
</testcase>

View File

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

View File

@@ -379,7 +379,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 97;
CURRENT_PROJECT_VERSION = 109;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -515,7 +515,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 97;
CURRENT_PROJECT_VERSION = 109;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -543,7 +543,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 97;
CURRENT_PROJECT_VERSION = 109;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;

View File

@@ -59,11 +59,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.57.0</string>
<string>1.69.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>97</string>
<string>109</string>
<key>FLTEnableImpeller</key>
<true />
<key>ITSAppUsesNonExemptEncryption</key>

View File

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

View File

@@ -5,32 +5,32 @@
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000407">
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000218">
</testcase>
<testcase classname="fastlane.lanes" name="1: increment_version_number" time="2.988375">
<testcase classname="fastlane.lanes" name="1: increment_version_number" time="2.314237">
</testcase>
<testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="45.42439">
<testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="5.025464">
</testcase>
<testcase classname="fastlane.lanes" name="3: increment_build_number" time="2.381359">
<testcase classname="fastlane.lanes" name="3: increment_build_number" time="1.890539">
</testcase>
<testcase classname="fastlane.lanes" name="4: build_app" time="94.653021">
<testcase classname="fastlane.lanes" name="4: build_app" time="101.284714">
</testcase>
<testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="58.237354">
<testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="59.342932">
</testcase>

View File

@@ -14,6 +14,7 @@ import 'package:immich_mobile/modules/backup/models/duplicated_asset.model.dart'
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/modules/backup/providers/ios_background_settings.provider.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/modules/memories/providers/memory.provider.dart';
import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart';
import 'package:immich_mobile/modules/settings/providers/notification_permission.provider.dart';
import 'package:immich_mobile/routing/router.dart';
@@ -156,6 +157,8 @@ class ImmichAppState extends ConsumerState<ImmichApp>
ref.read(iOSBackgroundSettingsProvider.notifier).refresh();
ref.invalidate(memoryFutureProvider);
break;
case AppLifecycleState.inactive:

View File

@@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.69.0
- API version: 1.70.0
- Build package: org.openapitools.codegen.languages.DartClientCodegen
## Requirements

View File

@@ -2,7 +2,7 @@ name: immich_mobile
description: Immich - selfhosted backup media file on mobile phone
publish_to: "none"
version: 1.69.0+92
version: 1.70.0+93
isar_version: &isar_version 3.1.0+1
environment:

View File

@@ -4439,7 +4439,7 @@
"info": {
"title": "Immich",
"description": "Immich API",
"version": "1.69.0",
"version": "1.70.0",
"contact": {}
},
"tags": [],

View File

@@ -1,12 +1,12 @@
{
"name": "immich",
"version": "1.69.0",
"version": "1.70.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "immich",
"version": "1.69.0",
"version": "1.70.0",
"license": "UNLICENSED",
"dependencies": {
"@babel/runtime": "^7.20.13",
@@ -33,7 +33,7 @@
"fluent-ffmpeg": "^2.1.2",
"handlebars": "^4.7.7",
"i18n-iso-countries": "^7.5.0",
"immich": "^0.39.0",
"immich": "^0.40.1",
"ioredis": "^5.3.1",
"joi": "^17.5.0",
"local-reverse-geocoder": "0.12.5",
@@ -7003,9 +7003,9 @@
}
},
"node_modules/immich": {
"version": "0.39.0",
"resolved": "https://registry.npmjs.org/immich/-/immich-0.39.0.tgz",
"integrity": "sha512-FoIj/ZV7QrjuBC7F6o6YZ8jqLZDJCZwrr80CxkzERPI7qX8YrSjR1GM4ocA/9oT7p7iA+dIxT//BF5MKNPkn4g==",
"version": "0.40.1",
"resolved": "https://registry.npmjs.org/immich/-/immich-0.40.1.tgz",
"integrity": "sha512-pU0Ua+FAsOiqrPC8NbSA521QW0k56Sw0GZ5rrPyqEMb2dcYPDOqEFcEk/1INqoQpPxy+CF9ZOCHNWxsEc7L1Rw==",
"dependencies": {
"axios": "^0.26.0",
"chalk": "^2.4.1",
@@ -17786,9 +17786,9 @@
"dev": true
},
"immich": {
"version": "0.39.0",
"resolved": "https://registry.npmjs.org/immich/-/immich-0.39.0.tgz",
"integrity": "sha512-FoIj/ZV7QrjuBC7F6o6YZ8jqLZDJCZwrr80CxkzERPI7qX8YrSjR1GM4ocA/9oT7p7iA+dIxT//BF5MKNPkn4g==",
"version": "0.40.1",
"resolved": "https://registry.npmjs.org/immich/-/immich-0.40.1.tgz",
"integrity": "sha512-pU0Ua+FAsOiqrPC8NbSA521QW0k56Sw0GZ5rrPyqEMb2dcYPDOqEFcEk/1INqoQpPxy+CF9ZOCHNWxsEc7L1Rw==",
"requires": {
"axios": "^0.26.0",
"chalk": "^2.4.1",

View File

@@ -1,6 +1,6 @@
{
"name": "immich",
"version": "1.69.0",
"version": "1.70.0",
"description": "",
"author": "",
"private": true,
@@ -63,7 +63,7 @@
"fluent-ffmpeg": "^2.1.2",
"handlebars": "^4.7.7",
"i18n-iso-countries": "^7.5.0",
"immich": "^0.39.0",
"immich": "^0.40.1",
"ioredis": "^5.3.1",
"joi": "^17.5.0",
"local-reverse-geocoder": "0.12.5",

View File

@@ -11,6 +11,7 @@ export interface AssetStatsOptions {
export interface AssetSearchOptions {
isVisible?: boolean;
type?: AssetType;
order?: 'ASC' | 'DESC';
}
export interface LivePhotoSearchOptions {

View File

@@ -29,7 +29,7 @@ export class FacialRecognitionService {
async handleQueueRecognizeFaces({ force }: IBaseJob) {
const assetPagination = usePagination(JOBS_ASSET_PAGINATION_SIZE, (pagination) => {
return force
? this.assetRepository.getAll(pagination)
? this.assetRepository.getAll(pagination, { order: 'DESC' })
: this.assetRepository.getWithout(pagination, WithoutProperty.FACES);
});

View File

@@ -207,12 +207,13 @@ export class AssetService {
const allowExif = this.getExifPermission(authUser);
const asset = await this._assetRepository.getById(assetId);
const data = allowExif ? mapAsset(asset) : mapAssetWithoutExif(asset);
if (allowExif) {
return mapAsset(asset);
} else {
return mapAssetWithoutExif(asset);
if (data.ownerId !== authUser.id) {
data.people = [];
}
return data;
}
public async updateAsset(authUser: AuthUserDto, assetId: string, dto: UpdateAssetDto): Promise<AssetResponseDto> {

View File

@@ -31,5 +31,5 @@ export async function bootstrap() {
const server = await app.listen(port);
server.requestTimeout = 30 * 60 * 1000;
logger.log(`Immich Server is listening on ${port} [v${SERVER_VERSION}] [${envName}] `);
logger.log(`Immich Server is listening on ${await app.getUrl()} [v${SERVER_VERSION}] [${envName}] `);
}

View File

@@ -116,7 +116,7 @@ export class AssetRepository implements IAssetRepository {
},
order: {
// Ensures correct order when paginating
createdAt: 'ASC',
createdAt: options.order ?? 'ASC',
},
});
}

View File

@@ -17,5 +17,5 @@ export async function bootstrap() {
await app.get(AppService).init();
await app.listen(port);
logger.log(`Immich Microservices is listening on ${port} [v${SERVER_VERSION}] [${envName}] `);
logger.log(`Immich Microservices is listening on ${await app.getUrl()} [v${SERVER_VERSION}] [${envName}] `);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -78,7 +78,7 @@
</script>
<div
class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
class="max-h-screen w-[500px] max-w-[95vw] overflow-y-scroll rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
>
<div class="flex flex-col place-content-center place-items-center gap-4 px-4">
<ImmichLogo class="text-center" height="100" width="100" />
@@ -128,7 +128,8 @@
{#if success}
<p class="ml-4 text-sm text-immich-primary">{success}</p>
{/if}
<div class="flex w-full p-4">
<div class="flex w-full gap-4 p-4">
<Button color="gray" fullwidth on:click={() => dispatch('cancel')}>Cancel</Button>
<Button type="submit" disabled={isCreatingUser} fullwidth>Create</Button>
</div>
</form>

View File

@@ -30,6 +30,7 @@
import { goto } from '$app/navigation';
import { browser } from '$app/environment';
import { isSearchEnabled } from '$lib/stores/search.store';
import ShowShortcuts from '../shared-components/show-shortcuts.svelte';
export let user: UserResponseDto | undefined = undefined;
export let isAlbumSelectionMode = false;
@@ -39,6 +40,7 @@
let viewportWidth = 0;
let assetGridElement: HTMLElement;
let bucketInfo: AssetCountByTimeBucketResponseDto;
let showShortcuts = false;
const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);
@@ -93,6 +95,9 @@
if (!$isViewingAssetStoreState) {
switch (event.key) {
case '?':
if (event.shiftKey) showShortcuts = !showShortcuts;
return;
case '/':
goto(AppRoute.EXPLORE);
return;
@@ -290,6 +295,10 @@
<svelte:window on:keydown={onKeyDown} on:keyup={onKeyUp} on:selectstart={onSelectStart} />
{#if showShortcuts}
<ShowShortcuts on:close={() => (showShortcuts = !showShortcuts)} />
{/if}
{#if bucketInfo && viewportHeight && $assetGridState.timelineHeight > viewportHeight}
<Scrollbar
scrollbarHeight={viewportHeight}

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { AssetResponseDto, api } from '@api';
import { createEventDispatcher } from 'svelte';
import { createEventDispatcher, onMount } from 'svelte';
import { notificationController, NotificationType } from './notification/notification';
import { handleError } from '$lib/utils/handle-error';
import domtoimage from 'dom-to-image';
@@ -13,6 +13,10 @@
const dispatch = createEventDispatcher();
let imgElement: HTMLDivElement;
onMount(() => {
imgElement.style.width = '100%';
});
const hasTransparentPixels = async (blob: Blob) => {
const img = new Image();
img.src = URL.createObjectURL(blob);

View File

@@ -0,0 +1,79 @@
<script lang="ts">
import Close from 'svelte-material-icons/Close.svelte';
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
import { createEventDispatcher } from 'svelte';
const shortcuts = {
general: [
{ key: ['←', '→'], action: 'Previous or next photo' },
{ key: ['Esc'], action: 'Back, close, or deselect' },
{ key: ['/'], action: 'Search your photos' },
],
actions: [
{ key: ['f'], action: 'Favorite or unfavorite photo' },
{ key: ['i'], action: 'Show or hide info' },
{ key: ['⇧', 'a'], action: 'Archive or unarchive photo' },
{ key: ['⇧', 'd'], action: 'Download' },
{ key: ['Space'], action: 'Play or pause video' },
{ key: ['Del'], action: 'Delete Asset' },
],
};
const dispatch = createEventDispatcher();
</script>
<div class="absolute z-[99999] h-full w-full">
<div class="flex h-full w-full place-content-center place-items-center overflow-hidden bg-black/50">
<div
class="w-[400px] max-w-[125vw] rounded-3xl border bg-immich-bg shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg md:w-[650px]"
>
<div class="relative px-4 pt-4">
<h1 class="px-4 py-4 font-medium text-immich-primary dark:text-immich-dark-primary">Keyboard Shortcuts</h1>
<div class="absolute inset-y-0 right-0 px-4 py-4">
<CircleIconButton logo={Close} on:click={() => dispatch('close')} />
</div>
</div>
<div class="grid grid-cols-1 gap-4 px-4 pb-4 md:grid-cols-2">
<div class="px-4 py-4">
<h2>General</h2>
<div class="text-sm">
{#each shortcuts.general as shortcut}
<div class="grid grid-cols-[20%_80%] items-center gap-4 pt-4 text-sm">
<div class="flex justify-self-end">
{#each shortcut.key as key}
<p
class="mr-1 flex items-center justify-center justify-self-end rounded-lg bg-immich-primary/25 p-2"
>
{key}
</p>
{/each}
</div>
<p class="mb-1 mt-1 flex">{shortcut.action}</p>
</div>
{/each}
</div>
</div>
<div class="px-4 py-4">
<h2>Actions</h2>
<div class="text-sm">
{#each shortcuts.actions as shortcut}
<div class="grid grid-cols-[20%_80%] items-center gap-4 pt-4 text-sm">
<div class="flex justify-self-end">
{#each shortcut.key as key}
<p
class="mr-1 flex items-center justify-center justify-self-end rounded-lg bg-immich-primary/25 p-2"
>
{key}
</p>
{/each}
</div>
<p class="mb-1 mt-1 flex">{shortcut.action}</p>
</div>
{/each}
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -110,7 +110,7 @@
<section>
{#if shouldShowCreateUserForm}
<FullScreenModal on:clickOutside={() => (shouldShowCreateUserForm = false)}>
<CreateUserForm on:user-created={onUserCreated} />
<CreateUserForm on:user-created={onUserCreated} on:cancel={() => (shouldShowCreateUserForm = false)} />
</FullScreenModal>
{/if}