mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:27:09 -05:00 
			
		
		
		
	feat(web): increase usage of CircleIconButton (#9256)
This commit is contained in:
		
							parent
							
								
									5b87abb021
								
							
						
					
					
						commit
						48b490f5e9
					
				@ -1,6 +1,5 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import Badge from '$lib/components/elements/badge.svelte';
 | 
					  import Badge from '$lib/components/elements/badge.svelte';
 | 
				
			||||||
  import Button from '$lib/components/elements/buttons/button.svelte';
 | 
					 | 
				
			||||||
  import Icon from '$lib/components/elements/icon.svelte';
 | 
					  import Icon from '$lib/components/elements/icon.svelte';
 | 
				
			||||||
  import { locale } from '$lib/stores/preferences.store';
 | 
					  import { locale } from '$lib/stores/preferences.store';
 | 
				
			||||||
  import { JobCommand, type JobCommandDto, type JobCountsDto, type QueueStatusDto } from '@immich/sdk';
 | 
					  import { JobCommand, type JobCommandDto, type JobCountsDto, type QueueStatusDto } from '@immich/sdk';
 | 
				
			||||||
@ -16,6 +15,7 @@
 | 
				
			|||||||
  import { createEventDispatcher } from 'svelte';
 | 
					  import { createEventDispatcher } from 'svelte';
 | 
				
			||||||
  import JobTileButton from './job-tile-button.svelte';
 | 
					  import JobTileButton from './job-tile-button.svelte';
 | 
				
			||||||
  import JobTileStatus from './job-tile-status.svelte';
 | 
					  import JobTileStatus from './job-tile-status.svelte';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let title: string;
 | 
					  export let title: string;
 | 
				
			||||||
  export let subtitle: string | undefined;
 | 
					  export let subtitle: string | undefined;
 | 
				
			||||||
@ -56,16 +56,19 @@
 | 
				
			|||||||
        <div class="flex gap-2">
 | 
					        <div class="flex gap-2">
 | 
				
			||||||
          {#if jobCounts.failed > 0}
 | 
					          {#if jobCounts.failed > 0}
 | 
				
			||||||
            <Badge color="primary">
 | 
					            <Badge color="primary">
 | 
				
			||||||
              <span class="text-sm">
 | 
					              <div class="flex flex-row gap-1">
 | 
				
			||||||
                {jobCounts.failed.toLocaleString($locale)} failed
 | 
					                <span class="text-sm">
 | 
				
			||||||
              </span>
 | 
					                  {jobCounts.failed.toLocaleString($locale)} failed
 | 
				
			||||||
              <Button
 | 
					                </span>
 | 
				
			||||||
                size="tiny"
 | 
					                <CircleIconButton
 | 
				
			||||||
                shadow={false}
 | 
					                  color="primary"
 | 
				
			||||||
                on:click={() => dispatch('command', { command: JobCommand.ClearFailed, force: false })}
 | 
					                  icon={mdiClose}
 | 
				
			||||||
              >
 | 
					                  title="Clear message"
 | 
				
			||||||
                <Icon path={mdiClose} size="18" />
 | 
					                  size="12"
 | 
				
			||||||
              </Button>
 | 
					                  padding="1"
 | 
				
			||||||
 | 
					                  on:click={() => dispatch('command', { command: JobCommand.ClearFailed, force: false })}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
            </Badge>
 | 
					            </Badge>
 | 
				
			||||||
          {/if}
 | 
					          {/if}
 | 
				
			||||||
          {#if jobCounts.delayed > 0}
 | 
					          {#if jobCounts.delayed > 0}
 | 
				
			||||||
 | 
				
			|||||||
@ -33,7 +33,7 @@
 | 
				
			|||||||
      data-testid="context-button-parent"
 | 
					      data-testid="context-button-parent"
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <CircleIconButton
 | 
					      <CircleIconButton
 | 
				
			||||||
        color="light"
 | 
					        color="opaque"
 | 
				
			||||||
        title="Show album options"
 | 
					        title="Show album options"
 | 
				
			||||||
        icon={mdiDotsVertical}
 | 
					        icon={mdiDotsVertical}
 | 
				
			||||||
        size="20"
 | 
					        size="20"
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,6 @@
 | 
				
			|||||||
  class="w-full h-14 flex p-4 text-white items-center justify-center rounded-full gap-4 bg-immich-dark-bg bg-opacity-60"
 | 
					  class="w-full h-14 flex p-4 text-white items-center justify-center rounded-full gap-4 bg-immich-dark-bg bg-opacity-60"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
  <button class={disabled ? 'cursor-not-allowed' : ''} on:click={() => dispatch('favorite')} {disabled}>
 | 
					  <button class={disabled ? 'cursor-not-allowed' : ''} on:click={() => dispatch('favorite')} {disabled}>
 | 
				
			||||||
    <!-- svelte-ignore missing-declaration -->
 | 
					 | 
				
			||||||
    <div class="items-center justify-center">
 | 
					    <div class="items-center justify-center">
 | 
				
			||||||
      <Icon path={isLiked ? mdiHeart : mdiHeartOutline} size={24} />
 | 
					      <Icon path={isLiked ? mdiHeart : mdiHeartOutline} size={24} />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
				
			|||||||
@ -160,12 +160,7 @@
 | 
				
			|||||||
      bind:clientHeight={activityHeight}
 | 
					      bind:clientHeight={activityHeight}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <div class="flex place-items-center gap-2">
 | 
					      <div class="flex place-items-center gap-2">
 | 
				
			||||||
        <button
 | 
					        <CircleIconButton on:click={() => dispatch('close')} icon={mdiClose} title="Close" />
 | 
				
			||||||
          class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
 | 
					 | 
				
			||||||
          on:click={() => dispatch('close')}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <Icon path={mdiClose} size="24" />
 | 
					 | 
				
			||||||
        </button>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <p class="text-lg text-immich-fg dark:text-immich-dark-fg">Activity</p>
 | 
					        <p class="text-lg text-immich-fg dark:text-immich-dark-fg">Activity</p>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
@ -193,10 +188,13 @@
 | 
				
			|||||||
                </a>
 | 
					                </a>
 | 
				
			||||||
              {/if}
 | 
					              {/if}
 | 
				
			||||||
              {#if reaction.user.id === user.id || albumOwnerId === user.id}
 | 
					              {#if reaction.user.id === user.id || albumOwnerId === user.id}
 | 
				
			||||||
                <div class="flex items-start w-fit pt-[5px]" title="Delete comment">
 | 
					                <div class="flex items-start w-fit pt-[5px]">
 | 
				
			||||||
                  <button on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}>
 | 
					                  <CircleIconButton
 | 
				
			||||||
                    <Icon path={mdiDotsVertical} />
 | 
					                    icon={mdiDotsVertical}
 | 
				
			||||||
                  </button>
 | 
					                    title="Comment options"
 | 
				
			||||||
 | 
					                    size="16"
 | 
				
			||||||
 | 
					                    on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
              {/if}
 | 
					              {/if}
 | 
				
			||||||
              <div>
 | 
					              <div>
 | 
				
			||||||
@ -242,10 +240,13 @@
 | 
				
			|||||||
                  </a>
 | 
					                  </a>
 | 
				
			||||||
                {/if}
 | 
					                {/if}
 | 
				
			||||||
                {#if reaction.user.id === user.id || albumOwnerId === user.id}
 | 
					                {#if reaction.user.id === user.id || albumOwnerId === user.id}
 | 
				
			||||||
                  <div class="flex items-start w-fit" title="Delete like">
 | 
					                  <div class="flex items-start w-fit">
 | 
				
			||||||
                    <button on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}>
 | 
					                    <CircleIconButton
 | 
				
			||||||
                      <Icon path={mdiDotsVertical} />
 | 
					                      icon={mdiDotsVertical}
 | 
				
			||||||
                    </button>
 | 
					                      title="Reaction options"
 | 
				
			||||||
 | 
					                      size="16"
 | 
				
			||||||
 | 
					                      on:click={() => (showDeleteReaction[index] ? '' : showOptionsMenu(index))}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
                  </div>
 | 
					                  </div>
 | 
				
			||||||
                {/if}
 | 
					                {/if}
 | 
				
			||||||
                <div>
 | 
					                <div>
 | 
				
			||||||
 | 
				
			|||||||
@ -169,13 +169,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<section class="relative p-2 dark:bg-immich-dark-bg dark:text-immich-dark-fg">
 | 
					<section class="relative p-2 dark:bg-immich-dark-bg dark:text-immich-dark-fg">
 | 
				
			||||||
  <div class="flex place-items-center gap-2">
 | 
					  <div class="flex place-items-center gap-2">
 | 
				
			||||||
    <button
 | 
					    <CircleIconButton icon={mdiClose} title="Close" on:click={() => dispatch('close')} />
 | 
				
			||||||
      class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
 | 
					 | 
				
			||||||
      on:click={() => dispatch('close')}
 | 
					 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
      <Icon path={mdiClose} size="24" />
 | 
					 | 
				
			||||||
    </button>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <p class="text-lg text-immich-fg dark:text-immich-dark-fg">Info</p>
 | 
					    <p class="text-lg text-immich-fg dark:text-immich-dark-fg">Info</p>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -401,9 +395,13 @@
 | 
				
			|||||||
          <p class="break-all flex place-items-center gap-2">
 | 
					          <p class="break-all flex place-items-center gap-2">
 | 
				
			||||||
            {asset.originalFileName}
 | 
					            {asset.originalFileName}
 | 
				
			||||||
            {#if isOwner}
 | 
					            {#if isOwner}
 | 
				
			||||||
              <button title="Show File Location" on:click={toggleAssetPath} class="-translate-y-[2px]">
 | 
					              <CircleIconButton
 | 
				
			||||||
                <Icon path={mdiInformationOutline} />
 | 
					                icon={mdiInformationOutline}
 | 
				
			||||||
              </button>
 | 
					                title="Show file location"
 | 
				
			||||||
 | 
					                size="16"
 | 
				
			||||||
 | 
					                padding="2"
 | 
				
			||||||
 | 
					                on:click={toggleAssetPath}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
            {/if}
 | 
					            {/if}
 | 
				
			||||||
          </p>
 | 
					          </p>
 | 
				
			||||||
          <div class="flex gap-2 text-sm">
 | 
					          <div class="flex gap-2 text-sm">
 | 
				
			||||||
 | 
				
			|||||||
@ -1,20 +1,23 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import { mdiClose, mdiMagnify } from '@mdi/js';
 | 
					  import { mdiClose, mdiMagnify } from '@mdi/js';
 | 
				
			||||||
  import Icon from './icon.svelte';
 | 
					 | 
				
			||||||
  import { createEventDispatcher } from 'svelte';
 | 
					  import { createEventDispatcher } from 'svelte';
 | 
				
			||||||
  import type { SearchOptions } from '$lib/utils/dipatch';
 | 
					  import type { SearchOptions } from '$lib/utils/dipatch';
 | 
				
			||||||
  import LoadingSpinner from '../shared-components/loading-spinner.svelte';
 | 
					  import LoadingSpinner from '../shared-components/loading-spinner.svelte';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let name: string;
 | 
					  export let name: string;
 | 
				
			||||||
  export let roundedBottom = true;
 | 
					  export let roundedBottom = true;
 | 
				
			||||||
  export let showLoadingSpinner: boolean;
 | 
					  export let showLoadingSpinner: boolean;
 | 
				
			||||||
  export let placeholder: string;
 | 
					  export let placeholder: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let inputRef: HTMLElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const dispatch = createEventDispatcher<{ search: SearchOptions; reset: void }>();
 | 
					  const dispatch = createEventDispatcher<{ search: SearchOptions; reset: void }>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const resetSearch = () => {
 | 
					  const resetSearch = () => {
 | 
				
			||||||
    name = '';
 | 
					    name = '';
 | 
				
			||||||
    dispatch('reset');
 | 
					    dispatch('reset');
 | 
				
			||||||
 | 
					    inputRef?.focus();
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleSearch = (event: KeyboardEvent) => {
 | 
					  const handleSearch = (event: KeyboardEvent) => {
 | 
				
			||||||
@ -29,16 +32,19 @@
 | 
				
			|||||||
    ? 'rounded-2xl'
 | 
					    ? 'rounded-2xl'
 | 
				
			||||||
    : 'rounded-t-lg'} bg-gray-200 p-2 dark:bg-immich-dark-gray gap-2 place-items-center h-full"
 | 
					    : 'rounded-t-lg'} bg-gray-200 p-2 dark:bg-immich-dark-gray gap-2 place-items-center h-full"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
  <button type="button" on:click={() => dispatch('search', { force: true })}>
 | 
					  <CircleIconButton
 | 
				
			||||||
    <div class="w-fit">
 | 
					    icon={mdiMagnify}
 | 
				
			||||||
      <Icon path={mdiMagnify} size="24" />
 | 
					    title="Search"
 | 
				
			||||||
    </div>
 | 
					    size="16"
 | 
				
			||||||
  </button>
 | 
					    padding="2"
 | 
				
			||||||
 | 
					    on:click={() => dispatch('search', { force: true })}
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
  <input
 | 
					  <input
 | 
				
			||||||
    class="w-full gap-2 bg-gray-200 dark:bg-immich-dark-gray dark:text-white"
 | 
					    class="w-full gap-2 bg-gray-200 dark:bg-immich-dark-gray dark:text-white"
 | 
				
			||||||
    type="text"
 | 
					    type="text"
 | 
				
			||||||
    {placeholder}
 | 
					    {placeholder}
 | 
				
			||||||
    bind:value={name}
 | 
					    bind:value={name}
 | 
				
			||||||
 | 
					    bind:this={inputRef}
 | 
				
			||||||
    on:keydown={handleSearch}
 | 
					    on:keydown={handleSearch}
 | 
				
			||||||
    on:input={() => dispatch('search', { force: false })}
 | 
					    on:input={() => dispatch('search', { force: false })}
 | 
				
			||||||
  />
 | 
					  />
 | 
				
			||||||
@ -48,8 +54,6 @@
 | 
				
			|||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  {/if}
 | 
					  {/if}
 | 
				
			||||||
  {#if name}
 | 
					  {#if name}
 | 
				
			||||||
    <button on:click={resetSearch}>
 | 
					    <CircleIconButton icon={mdiClose} title="Clear value" size="16" padding="2" on:click={resetSearch} />
 | 
				
			||||||
      <Icon path={mdiClose} />
 | 
					 | 
				
			||||||
    </button>
 | 
					 | 
				
			||||||
  {/if}
 | 
					  {/if}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
				
			|||||||
@ -9,9 +9,9 @@
 | 
				
			|||||||
  import { linear } from 'svelte/easing';
 | 
					  import { linear } from 'svelte/easing';
 | 
				
			||||||
  import { fly } from 'svelte/transition';
 | 
					  import { fly } from 'svelte/transition';
 | 
				
			||||||
  import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
 | 
					  import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
 | 
				
			||||||
  import Icon from '../elements/icon.svelte';
 | 
					 | 
				
			||||||
  import LoadingSpinner from '../shared-components/loading-spinner.svelte';
 | 
					  import LoadingSpinner from '../shared-components/loading-spinner.svelte';
 | 
				
			||||||
  import SearchPeople from '$lib/components/faces-page/people-search.svelte';
 | 
					  import SearchPeople from '$lib/components/faces-page/people-search.svelte';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let peopleWithFaces: AssetFaceResponseDto[];
 | 
					  export let peopleWithFaces: AssetFaceResponseDto[];
 | 
				
			||||||
  export let allPeople: PersonResponseDto[];
 | 
					  export let allPeople: PersonResponseDto[];
 | 
				
			||||||
@ -119,38 +119,19 @@
 | 
				
			|||||||
  <div class="flex place-items-center justify-between gap-2">
 | 
					  <div class="flex place-items-center justify-between gap-2">
 | 
				
			||||||
    {#if !searchFaces}
 | 
					    {#if !searchFaces}
 | 
				
			||||||
      <div class="flex items-center gap-2">
 | 
					      <div class="flex items-center gap-2">
 | 
				
			||||||
        <button
 | 
					        <CircleIconButton icon={mdiArrowLeftThin} title="Back" on:click={handleBackButton} />
 | 
				
			||||||
          class="flex place-content-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
 | 
					 | 
				
			||||||
          on:click={handleBackButton}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <div>
 | 
					 | 
				
			||||||
            <Icon path={mdiArrowLeftThin} size="24" />
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </button>
 | 
					 | 
				
			||||||
        <p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">Select face</p>
 | 
					        <p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">Select face</p>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="flex justify-end gap-2">
 | 
					      <div class="flex justify-end gap-2">
 | 
				
			||||||
        <button
 | 
					        <CircleIconButton
 | 
				
			||||||
          class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
 | 
					          icon={mdiMagnify}
 | 
				
			||||||
          title="Search existing person"
 | 
					          title="Search for existing person"
 | 
				
			||||||
          on:click={() => {
 | 
					          on:click={() => {
 | 
				
			||||||
            searchFaces = true;
 | 
					            searchFaces = true;
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
        >
 | 
					        />
 | 
				
			||||||
          <div>
 | 
					 | 
				
			||||||
            <Icon path={mdiMagnify} size="24" />
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </button>
 | 
					 | 
				
			||||||
        {#if !isShowLoadingNewPerson}
 | 
					        {#if !isShowLoadingNewPerson}
 | 
				
			||||||
          <button
 | 
					          <CircleIconButton icon={mdiPlus} title="Create new person" on:click={handleCreatePerson} />
 | 
				
			||||||
            class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
 | 
					 | 
				
			||||||
            on:click={handleCreatePerson}
 | 
					 | 
				
			||||||
            title="Create new person"
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            <div>
 | 
					 | 
				
			||||||
              <Icon path={mdiPlus} size="24" />
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </button>
 | 
					 | 
				
			||||||
        {:else}
 | 
					        {:else}
 | 
				
			||||||
          <div class="flex place-content-center place-items-center">
 | 
					          <div class="flex place-content-center place-items-center">
 | 
				
			||||||
            <LoadingSpinner />
 | 
					            <LoadingSpinner />
 | 
				
			||||||
@ -158,14 +139,7 @@
 | 
				
			|||||||
        {/if}
 | 
					        {/if}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    {:else}
 | 
					    {:else}
 | 
				
			||||||
      <button
 | 
					      <CircleIconButton icon={mdiArrowLeftThin} title="Back" on:click={handleBackButton} />
 | 
				
			||||||
        class="flex place-content-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
 | 
					 | 
				
			||||||
        on:click={handleBackButton}
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        <div>
 | 
					 | 
				
			||||||
          <Icon path={mdiArrowLeftThin} size="24" />
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </button>
 | 
					 | 
				
			||||||
      <div class="w-full flex">
 | 
					      <div class="w-full flex">
 | 
				
			||||||
        <SearchPeople
 | 
					        <SearchPeople
 | 
				
			||||||
          type="input"
 | 
					          type="input"
 | 
				
			||||||
@ -179,14 +153,7 @@
 | 
				
			|||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        {/if}
 | 
					        {/if}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <button
 | 
					      <CircleIconButton icon={mdiClose} title="Cancel search" on:click={() => (searchFaces = false)} />
 | 
				
			||||||
        class="flex place-content-center place-items-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
 | 
					 | 
				
			||||||
        on:click={() => (searchFaces = false)}
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        <div>
 | 
					 | 
				
			||||||
          <Icon path={mdiClose} size="24" />
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </button>
 | 
					 | 
				
			||||||
    {/if}
 | 
					    {/if}
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  <div class="px-4 py-4 text-sm">
 | 
					  <div class="px-4 py-4 text-sm">
 | 
				
			||||||
 | 
				
			|||||||
@ -74,7 +74,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  <div class="absolute right-2 top-2" class:hidden={!showVerticalDots}>
 | 
					  <div class="absolute right-2 top-2" class:hidden={!showVerticalDots}>
 | 
				
			||||||
    <CircleIconButton
 | 
					    <CircleIconButton
 | 
				
			||||||
      color="light"
 | 
					      color="opaque"
 | 
				
			||||||
      icon={mdiDotsVertical}
 | 
					      icon={mdiDotsVertical}
 | 
				
			||||||
      title="Show person options"
 | 
					      title="Show person options"
 | 
				
			||||||
      size="20"
 | 
					      size="20"
 | 
				
			||||||
 | 
				
			|||||||
@ -15,14 +15,14 @@
 | 
				
			|||||||
    type AssetFaceResponseDto,
 | 
					    type AssetFaceResponseDto,
 | 
				
			||||||
    type PersonResponseDto,
 | 
					    type PersonResponseDto,
 | 
				
			||||||
  } from '@immich/sdk';
 | 
					  } from '@immich/sdk';
 | 
				
			||||||
  import { mdiArrowLeftThin, mdiRestart } from '@mdi/js';
 | 
					  import { mdiArrowLeftThin, mdiMinus, mdiRestart } from '@mdi/js';
 | 
				
			||||||
  import { createEventDispatcher, onMount } from 'svelte';
 | 
					  import { createEventDispatcher, onMount } from 'svelte';
 | 
				
			||||||
  import { linear } from 'svelte/easing';
 | 
					  import { linear } from 'svelte/easing';
 | 
				
			||||||
  import { fly } from 'svelte/transition';
 | 
					  import { fly } from 'svelte/transition';
 | 
				
			||||||
  import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
 | 
					  import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
 | 
				
			||||||
  import Icon from '../elements/icon.svelte';
 | 
					 | 
				
			||||||
  import { NotificationType, notificationController } from '../shared-components/notification/notification';
 | 
					  import { NotificationType, notificationController } from '../shared-components/notification/notification';
 | 
				
			||||||
  import AssignFaceSidePanel from './assign-face-side-panel.svelte';
 | 
					  import AssignFaceSidePanel from './assign-face-side-panel.svelte';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let assetId: string;
 | 
					  export let assetId: string;
 | 
				
			||||||
  export let assetType: AssetTypeEnum;
 | 
					  export let assetType: AssetTypeEnum;
 | 
				
			||||||
@ -42,7 +42,7 @@
 | 
				
			|||||||
  let isShowLoadingPeople = false;
 | 
					  let isShowLoadingPeople = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // search people
 | 
					  // search people
 | 
				
			||||||
  let showSeletecFaces = false;
 | 
					  let showSelectedFaces = false;
 | 
				
			||||||
  let allPeople: PersonResponseDto[] = [];
 | 
					  let allPeople: PersonResponseDto[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // timers
 | 
					  // timers
 | 
				
			||||||
@ -159,21 +159,21 @@
 | 
				
			|||||||
    if (newFeaturePhoto && personToUpdate) {
 | 
					    if (newFeaturePhoto && personToUpdate) {
 | 
				
			||||||
      selectedPersonToCreate[personToUpdate.id] = newFeaturePhoto;
 | 
					      selectedPersonToCreate[personToUpdate.id] = newFeaturePhoto;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    showSeletecFaces = false;
 | 
					    showSelectedFaces = false;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleReassignFace = (person: PersonResponseDto | null) => {
 | 
					  const handleReassignFace = (person: PersonResponseDto | null) => {
 | 
				
			||||||
    const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id);
 | 
					    const personToUpdate = peopleWithFaces.find((face) => face.person?.id === editedPerson.id);
 | 
				
			||||||
    if (person && personToUpdate) {
 | 
					    if (person && personToUpdate) {
 | 
				
			||||||
      selectedPersonToReassign[personToUpdate.id] = person;
 | 
					      selectedPersonToReassign[personToUpdate.id] = person;
 | 
				
			||||||
      showSeletecFaces = false;
 | 
					      showSelectedFaces = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handlePersonPicker = (person: PersonResponseDto | null) => {
 | 
					  const handlePersonPicker = (person: PersonResponseDto | null) => {
 | 
				
			||||||
    if (person) {
 | 
					    if (person) {
 | 
				
			||||||
      editedPerson = person;
 | 
					      editedPerson = person;
 | 
				
			||||||
      showSeletecFaces = true;
 | 
					      showSelectedFaces = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
@ -184,14 +184,7 @@
 | 
				
			|||||||
>
 | 
					>
 | 
				
			||||||
  <div class="flex place-items-center justify-between gap-2">
 | 
					  <div class="flex place-items-center justify-between gap-2">
 | 
				
			||||||
    <div class="flex items-center gap-2">
 | 
					    <div class="flex items-center gap-2">
 | 
				
			||||||
      <button
 | 
					      <CircleIconButton icon={mdiArrowLeftThin} title="Back" on:click={handleBackButton} />
 | 
				
			||||||
        class="flex place-content-center rounded-full p-3 transition-colors hover:bg-gray-200 dark:text-immich-dark-fg dark:hover:bg-gray-900"
 | 
					 | 
				
			||||||
        on:click={handleBackButton}
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        <div>
 | 
					 | 
				
			||||||
          <Icon path={mdiArrowLeftThin} size="24" />
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </button>
 | 
					 | 
				
			||||||
      <p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">Edit faces</p>
 | 
					      <p class="flex text-lg text-immich-fg dark:text-immich-dark-fg">Edit faces</p>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    {#if !isShowLoadingDone}
 | 
					    {#if !isShowLoadingDone}
 | 
				
			||||||
@ -273,21 +266,27 @@
 | 
				
			|||||||
                  </p>
 | 
					                  </p>
 | 
				
			||||||
                {/if}
 | 
					                {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <div class="absolute -right-[5px] -top-[5px] h-[20px] w-[20px] rounded-full bg-blue-700">
 | 
					                <div class="absolute -right-[5px] -top-[5px] h-[20px] w-[20px] rounded-full">
 | 
				
			||||||
                  {#if selectedPersonToCreate[face.id] || selectedPersonToReassign[face.id]}
 | 
					                  {#if selectedPersonToCreate[face.id] || selectedPersonToReassign[face.id]}
 | 
				
			||||||
                    <button on:click={() => handleReset(face.id)} class="flex h-full w-full">
 | 
					                    <CircleIconButton
 | 
				
			||||||
                      <div class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform">
 | 
					                      color="primary"
 | 
				
			||||||
                        <div>
 | 
					                      icon={mdiRestart}
 | 
				
			||||||
                          <Icon path={mdiRestart} size={18} />
 | 
					                      title="Reset"
 | 
				
			||||||
                        </div>
 | 
					                      size="18"
 | 
				
			||||||
                      </div>
 | 
					                      padding="1"
 | 
				
			||||||
                    </button>
 | 
					                      class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
 | 
				
			||||||
 | 
					                      on:click={() => handleReset(face.id)}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
                  {:else}
 | 
					                  {:else}
 | 
				
			||||||
                    <button on:click={() => handlePersonPicker(face.person)} class="flex h-full w-full">
 | 
					                    <CircleIconButton
 | 
				
			||||||
                      <div
 | 
					                      color="primary"
 | 
				
			||||||
                        class="absolute left-1/2 top-1/2 h-[2px] w-[14px] translate-x-[-50%] translate-y-[-50%] transform bg-white"
 | 
					                      icon={mdiMinus}
 | 
				
			||||||
                      />
 | 
					                      title="Select new face"
 | 
				
			||||||
                    </button>
 | 
					                      size="18"
 | 
				
			||||||
 | 
					                      padding="1"
 | 
				
			||||||
 | 
					                      class="absolute left-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
 | 
				
			||||||
 | 
					                      on:click={() => handlePersonPicker(face.person)}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
                  {/if}
 | 
					                  {/if}
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
@ -299,14 +298,14 @@
 | 
				
			|||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{#if showSeletecFaces}
 | 
					{#if showSelectedFaces}
 | 
				
			||||||
  <AssignFaceSidePanel
 | 
					  <AssignFaceSidePanel
 | 
				
			||||||
    {peopleWithFaces}
 | 
					    {peopleWithFaces}
 | 
				
			||||||
    {allPeople}
 | 
					    {allPeople}
 | 
				
			||||||
    {editedPerson}
 | 
					    {editedPerson}
 | 
				
			||||||
    {assetType}
 | 
					    {assetType}
 | 
				
			||||||
    {assetId}
 | 
					    {assetId}
 | 
				
			||||||
    on:close={() => (showSeletecFaces = false)}
 | 
					    on:close={() => (showSelectedFaces = false)}
 | 
				
			||||||
    on:createPerson={(event) => handleCreatePerson(event.detail)}
 | 
					    on:createPerson={(event) => handleCreatePerson(event.detail)}
 | 
				
			||||||
    on:reassign={(event) => handleReassignFace(event.detail)}
 | 
					    on:reassign={(event) => handleReassignFace(event.detail)}
 | 
				
			||||||
  />
 | 
					  />
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@
 | 
				
			|||||||
  import { validate, type LibraryResponseDto } from '@immich/sdk';
 | 
					  import { validate, type LibraryResponseDto } from '@immich/sdk';
 | 
				
			||||||
  import type { ValidateLibraryImportPathResponseDto } from '@immich/sdk';
 | 
					  import type { ValidateLibraryImportPathResponseDto } from '@immich/sdk';
 | 
				
			||||||
  import { NotificationType, notificationController } from '../shared-components/notification/notification';
 | 
					  import { NotificationType, notificationController } from '../shared-components/notification/notification';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let library: LibraryResponseDto;
 | 
					  export let library: LibraryResponseDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -209,17 +210,17 @@
 | 
				
			|||||||
          </td>
 | 
					          </td>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <td class="w-4/5 text-ellipsis px-4 text-sm">{validatedPath.importPath}</td>
 | 
					          <td class="w-4/5 text-ellipsis px-4 text-sm">{validatedPath.importPath}</td>
 | 
				
			||||||
          <td class="w-1/5 text-ellipsis px-4 text-sm flex flex-row">
 | 
					          <td class="w-1/5 text-ellipsis flex justify-center">
 | 
				
			||||||
            <button
 | 
					            <CircleIconButton
 | 
				
			||||||
              type="button"
 | 
					              color="primary"
 | 
				
			||||||
 | 
					              icon={mdiPencilOutline}
 | 
				
			||||||
 | 
					              title="Edit import path"
 | 
				
			||||||
 | 
					              size="16"
 | 
				
			||||||
              on:click={() => {
 | 
					              on:click={() => {
 | 
				
			||||||
                editImportPath = listIndex;
 | 
					                editImportPath = listIndex;
 | 
				
			||||||
                editedImportPath = validatedPath.importPath;
 | 
					                editedImportPath = validatedPath.importPath;
 | 
				
			||||||
              }}
 | 
					              }}
 | 
				
			||||||
              class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
 | 
					            />
 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              <Icon path={mdiPencilOutline} size="16" />
 | 
					 | 
				
			||||||
            </button>
 | 
					 | 
				
			||||||
          </td>
 | 
					          </td>
 | 
				
			||||||
        </tr>
 | 
					        </tr>
 | 
				
			||||||
      {/each}
 | 
					      {/each}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import Icon from '$lib/components/elements/icon.svelte';
 | 
					 | 
				
			||||||
  import { LibraryType, type LibraryResponseDto } from '@immich/sdk';
 | 
					  import { LibraryType, type LibraryResponseDto } from '@immich/sdk';
 | 
				
			||||||
  import { mdiPencilOutline } from '@mdi/js';
 | 
					  import { mdiPencilOutline } from '@mdi/js';
 | 
				
			||||||
  import { createEventDispatcher, onMount } from 'svelte';
 | 
					  import { createEventDispatcher, onMount } from 'svelte';
 | 
				
			||||||
  import { handleError } from '../../utils/handle-error';
 | 
					  import { handleError } from '../../utils/handle-error';
 | 
				
			||||||
  import Button from '../elements/buttons/button.svelte';
 | 
					  import Button from '../elements/buttons/button.svelte';
 | 
				
			||||||
  import LibraryExclusionPatternForm from './library-exclusion-pattern-form.svelte';
 | 
					  import LibraryExclusionPatternForm from './library-exclusion-pattern-form.svelte';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let library: Partial<LibraryResponseDto>;
 | 
					  export let library: Partial<LibraryResponseDto>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -138,17 +138,17 @@
 | 
				
			|||||||
          }`}
 | 
					          }`}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <td class="w-3/4 text-ellipsis px-4 text-sm">{exclusionPattern}</td>
 | 
					          <td class="w-3/4 text-ellipsis px-4 text-sm">{exclusionPattern}</td>
 | 
				
			||||||
          <td class="w-1/4 text-ellipsis px-4 text-sm">
 | 
					          <td class="w-1/4 text-ellipsis flex justify-center">
 | 
				
			||||||
            <button
 | 
					            <CircleIconButton
 | 
				
			||||||
              type="button"
 | 
					              color="primary"
 | 
				
			||||||
 | 
					              icon={mdiPencilOutline}
 | 
				
			||||||
 | 
					              title="Edit exclusion pattern"
 | 
				
			||||||
 | 
					              size="16"
 | 
				
			||||||
              on:click={() => {
 | 
					              on:click={() => {
 | 
				
			||||||
                editExclusionPattern = listIndex;
 | 
					                editExclusionPattern = listIndex;
 | 
				
			||||||
                editedExclusionPattern = exclusionPattern;
 | 
					                editedExclusionPattern = exclusionPattern;
 | 
				
			||||||
              }}
 | 
					              }}
 | 
				
			||||||
              class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
 | 
					            />
 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              <Icon path={mdiPencilOutline} size="16" />
 | 
					 | 
				
			||||||
            </button>
 | 
					 | 
				
			||||||
          </td>
 | 
					          </td>
 | 
				
			||||||
        </tr>
 | 
					        </tr>
 | 
				
			||||||
      {/each}
 | 
					      {/each}
 | 
				
			||||||
 | 
				
			|||||||
@ -340,9 +340,12 @@
 | 
				
			|||||||
        class:opacity-0={galleryInView}
 | 
					        class:opacity-0={galleryInView}
 | 
				
			||||||
        class:opacity-100={!galleryInView}
 | 
					        class:opacity-100={!galleryInView}
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        <button on:click={() => memoryGallery.scrollIntoView({ behavior: 'smooth' })}>
 | 
					        <CircleIconButton
 | 
				
			||||||
          <CircleIconButton title="Show gallery" icon={mdiChevronDown} color="light" />
 | 
					          title="Show gallery"
 | 
				
			||||||
        </button>
 | 
					          icon={mdiChevronDown}
 | 
				
			||||||
 | 
					          color="light"
 | 
				
			||||||
 | 
					          on:click={() => memoryGallery.scrollIntoView({ behavior: 'smooth' })}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <IntersectionObserver
 | 
					      <IntersectionObserver
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@
 | 
				
			|||||||
  import UserAvatar from '../user-avatar.svelte';
 | 
					  import UserAvatar from '../user-avatar.svelte';
 | 
				
			||||||
  import AvatarSelector from './avatar-selector.svelte';
 | 
					  import AvatarSelector from './avatar-selector.svelte';
 | 
				
			||||||
  import FocusTrap from '$lib/components/shared-components/focus-trap.svelte';
 | 
					  import FocusTrap from '$lib/components/shared-components/focus-trap.svelte';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let isShowSelectAvatar = false;
 | 
					  let isShowSelectAvatar = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -61,15 +62,16 @@
 | 
				
			|||||||
        {#key $user}
 | 
					        {#key $user}
 | 
				
			||||||
          <UserAvatar user={$user} size="xl" />
 | 
					          <UserAvatar user={$user} size="xl" />
 | 
				
			||||||
        {/key}
 | 
					        {/key}
 | 
				
			||||||
        <div
 | 
					        <div class="absolute z-10 bottom-0 right-0 rounded-full w-6 h-6">
 | 
				
			||||||
          class="absolute z-10 bottom-0 right-0 rounded-full w-6 h-6 border dark:border-immich-dark-primary bg-immich-primary"
 | 
					          <CircleIconButton
 | 
				
			||||||
        >
 | 
					            color="primary"
 | 
				
			||||||
          <button
 | 
					            icon={mdiPencil}
 | 
				
			||||||
            class="flex items-center justify-center w-full h-full text-white"
 | 
					            title="Edit avatar"
 | 
				
			||||||
 | 
					            class="border"
 | 
				
			||||||
 | 
					            size="12"
 | 
				
			||||||
 | 
					            padding="2"
 | 
				
			||||||
            on:click={() => (isShowSelectAvatar = true)}
 | 
					            on:click={() => (isShowSelectAvatar = true)}
 | 
				
			||||||
          >
 | 
					          />
 | 
				
			||||||
            <Icon path={mdiPencil} />
 | 
					 | 
				
			||||||
          </button>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@
 | 
				
			|||||||
  } from '$lib/components/shared-components/notification/notification';
 | 
					  } from '$lib/components/shared-components/notification/notification';
 | 
				
			||||||
  import { onMount } from 'svelte';
 | 
					  import { onMount } from 'svelte';
 | 
				
			||||||
  import { mdiCloseCircleOutline, mdiInformationOutline, mdiWindowClose } from '@mdi/js';
 | 
					  import { mdiCloseCircleOutline, mdiInformationOutline, mdiWindowClose } from '@mdi/js';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let notification: Notification;
 | 
					  export let notification: Notification;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -78,9 +79,14 @@
 | 
				
			|||||||
        {notification.type.toString()}
 | 
					        {notification.type.toString()}
 | 
				
			||||||
      </h2>
 | 
					      </h2>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <button on:click|stopPropagation={discard}>
 | 
					    <CircleIconButton
 | 
				
			||||||
      <Icon path={mdiWindowClose} size="20" />
 | 
					      icon={mdiWindowClose}
 | 
				
			||||||
    </button>
 | 
					      title="Close"
 | 
				
			||||||
 | 
					      class="dark:text-immich-dark-gray"
 | 
				
			||||||
 | 
					      size="20"
 | 
				
			||||||
 | 
					      padding="2"
 | 
				
			||||||
 | 
					      on:click={discard}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <p class="whitespace-pre-wrap pl-[28px] pr-[16px] text-sm" data-testid="message">
 | 
					  <p class="whitespace-pre-wrap pl-[28px] pr-[16px] text-sm" data-testid="message">
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
  import Icon from '$lib/components/elements/icon.svelte';
 | 
					  import Icon from '$lib/components/elements/icon.svelte';
 | 
				
			||||||
  import { locale } from '$lib/stores/preferences.store';
 | 
					  import { locale } from '$lib/stores/preferences.store';
 | 
				
			||||||
  import type { SessionResponseDto } from '@immich/sdk';
 | 
					  import type { SessionResponseDto } from '@immich/sdk';
 | 
				
			||||||
@ -64,14 +65,14 @@
 | 
				
			|||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    {#if !device.current}
 | 
					    {#if !device.current}
 | 
				
			||||||
      <div class="flex flex-col justify-center text-sm">
 | 
					      <div>
 | 
				
			||||||
        <button
 | 
					        <CircleIconButton
 | 
				
			||||||
          on:click={() => dispatcher('delete')}
 | 
					          color="primary"
 | 
				
			||||||
          class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
 | 
					          icon={mdiTrashCanOutline}
 | 
				
			||||||
          title="Log out"
 | 
					          title="Log out"
 | 
				
			||||||
        >
 | 
					          size="16"
 | 
				
			||||||
          <Icon path={mdiTrashCanOutline} size="16" />
 | 
					          on:click={() => dispatcher('delete')}
 | 
				
			||||||
        </button>
 | 
					        />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    {/if}
 | 
					    {/if}
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import Icon from '$lib/components/elements/icon.svelte';
 | 
					 | 
				
			||||||
  import { locale } from '$lib/stores/preferences.store';
 | 
					  import { locale } from '$lib/stores/preferences.store';
 | 
				
			||||||
  import { createApiKey, deleteApiKey, getApiKeys, updateApiKey, type ApiKeyResponseDto } from '@immich/sdk';
 | 
					  import { createApiKey, deleteApiKey, getApiKeys, updateApiKey, type ApiKeyResponseDto } from '@immich/sdk';
 | 
				
			||||||
  import { mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js';
 | 
					  import { mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js';
 | 
				
			||||||
@ -10,6 +9,7 @@
 | 
				
			|||||||
  import APIKeySecret from '../forms/api-key-secret.svelte';
 | 
					  import APIKeySecret from '../forms/api-key-secret.svelte';
 | 
				
			||||||
  import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
 | 
					  import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
 | 
				
			||||||
  import { NotificationType, notificationController } from '../shared-components/notification/notification';
 | 
					  import { NotificationType, notificationController } from '../shared-components/notification/notification';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let keys: ApiKeyResponseDto[];
 | 
					  export let keys: ApiKeyResponseDto[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -143,19 +143,21 @@
 | 
				
			|||||||
                <td class="w-1/3 text-ellipsis px-4 text-sm"
 | 
					                <td class="w-1/3 text-ellipsis px-4 text-sm"
 | 
				
			||||||
                  >{new Date(key.createdAt).toLocaleDateString($locale, format)}
 | 
					                  >{new Date(key.createdAt).toLocaleDateString($locale, format)}
 | 
				
			||||||
                </td>
 | 
					                </td>
 | 
				
			||||||
                <td class="w-1/3 text-ellipsis px-4 text-sm">
 | 
					                <td class="flex flex-row flex-wrap justify-center gap-x-2 gap-y-1 w-1/3">
 | 
				
			||||||
                  <button
 | 
					                  <CircleIconButton
 | 
				
			||||||
 | 
					                    color="primary"
 | 
				
			||||||
 | 
					                    icon={mdiPencilOutline}
 | 
				
			||||||
 | 
					                    title="Edit key"
 | 
				
			||||||
 | 
					                    size="16"
 | 
				
			||||||
                    on:click={() => (editKey = key)}
 | 
					                    on:click={() => (editKey = key)}
 | 
				
			||||||
                    class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
 | 
					                  />
 | 
				
			||||||
                  >
 | 
					                  <CircleIconButton
 | 
				
			||||||
                    <Icon path={mdiPencilOutline} size="16" />
 | 
					                    color="primary"
 | 
				
			||||||
                  </button>
 | 
					                    icon={mdiTrashCanOutline}
 | 
				
			||||||
                  <button
 | 
					                    title="Delete key"
 | 
				
			||||||
 | 
					                    size="16"
 | 
				
			||||||
                    on:click={() => (deleteKey = key)}
 | 
					                    on:click={() => (deleteKey = key)}
 | 
				
			||||||
                    class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
 | 
					                  />
 | 
				
			||||||
                  >
 | 
					 | 
				
			||||||
                    <Icon path={mdiTrashCanOutline} size="16" />
 | 
					 | 
				
			||||||
                  </button>
 | 
					 | 
				
			||||||
                </td>
 | 
					                </td>
 | 
				
			||||||
              </tr>
 | 
					              </tr>
 | 
				
			||||||
            {/key}
 | 
					            {/key}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@
 | 
				
			|||||||
  import { page } from '$app/stores';
 | 
					  import { page } from '$app/stores';
 | 
				
			||||||
  import Icon from '$lib/components/elements/icon.svelte';
 | 
					  import Icon from '$lib/components/elements/icon.svelte';
 | 
				
			||||||
  import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
 | 
					  import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
  import { copyToClipboard } from '$lib/utils';
 | 
					  import { copyToClipboard } from '$lib/utils';
 | 
				
			||||||
  import { mdiCodeTags, mdiContentCopy, mdiMessage, mdiPartyPopper } from '@mdi/js';
 | 
					  import { mdiCodeTags, mdiContentCopy, mdiMessage, mdiPartyPopper } from '@mdi/js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -36,12 +37,12 @@
 | 
				
			|||||||
              🚨 Error - Something went wrong
 | 
					              🚨 Error - Something went wrong
 | 
				
			||||||
            </h1>
 | 
					            </h1>
 | 
				
			||||||
            <div class="flex justify-end">
 | 
					            <div class="flex justify-end">
 | 
				
			||||||
              <button
 | 
					              <CircleIconButton
 | 
				
			||||||
 | 
					                color="primary"
 | 
				
			||||||
 | 
					                icon={mdiContentCopy}
 | 
				
			||||||
 | 
					                title="Copy error"
 | 
				
			||||||
                on:click={() => handleCopy()}
 | 
					                on:click={() => handleCopy()}
 | 
				
			||||||
                class="rounded-full bg-immich-primary px-3 py-2 text-sm text-white shadow-md transition-colors hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-immich-dark-gray dark:hover:bg-immich-dark-primary/80"
 | 
					              />
 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                <Icon path={mdiContentCopy} size={24} />
 | 
					 | 
				
			||||||
              </button>
 | 
					 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -37,6 +37,7 @@
 | 
				
			|||||||
  import { fade, slide } from 'svelte/transition';
 | 
					  import { fade, slide } from 'svelte/transition';
 | 
				
			||||||
  import LinkButton from '../../../lib/components/elements/buttons/link-button.svelte';
 | 
					  import LinkButton from '../../../lib/components/elements/buttons/link-button.svelte';
 | 
				
			||||||
  import type { PageData } from './$types';
 | 
					  import type { PageData } from './$types';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let data: PageData;
 | 
					  export let data: PageData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -386,12 +387,13 @@
 | 
				
			|||||||
                {/if}
 | 
					                {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <td class=" text-ellipsis px-4 text-sm">
 | 
					                <td class=" text-ellipsis px-4 text-sm">
 | 
				
			||||||
                  <button
 | 
					                  <CircleIconButton
 | 
				
			||||||
                    class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
 | 
					                    color="primary"
 | 
				
			||||||
                    on:click|stopPropagation|preventDefault={(e) => showMenu(e, library, index)}
 | 
					                    icon={mdiDotsVertical}
 | 
				
			||||||
                  >
 | 
					                    title="Library options"
 | 
				
			||||||
                    <Icon path={mdiDotsVertical} size="16" />
 | 
					                    size="16"
 | 
				
			||||||
                  </button>
 | 
					                    on:click={(e) => showMenu(e, library, index)}
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                  {#if showContextMenu}
 | 
					                  {#if showContextMenu}
 | 
				
			||||||
                    <Portal target="body">
 | 
					                    <Portal target="body">
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user