mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	fix(web): re-render albums (#7403)
* fix: re-render albums * fix: album description * fix: reactivity * fix album reactivity + components for title and description * only update AssetGrid when albumId changes * remove title and description bindings * remove console.log * chore: fix merge * pr feedback * pr feedback --------- Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									e4f32a045d
								
							
						
					
					
						commit
						84fe41df31
					
				
							
								
								
									
										45
									
								
								web/src/lib/components/album-page/album-description.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								web/src/lib/components/album-page/album-description.svelte
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					  import { autoGrowHeight } from '$lib/utils/autogrow';
 | 
				
			||||||
 | 
					  import { updateAlbumInfo } from '@immich/sdk';
 | 
				
			||||||
 | 
					  import { handleError } from '$lib/utils/handle-error';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  export let id: string;
 | 
				
			||||||
 | 
					  export let description: string;
 | 
				
			||||||
 | 
					  export let isOwned: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  $: newDescription = description;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleUpdateDescription = async () => {
 | 
				
			||||||
 | 
					    if (newDescription === description) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await updateAlbumInfo({
 | 
				
			||||||
 | 
					        id,
 | 
				
			||||||
 | 
					        updateAlbumDto: {
 | 
				
			||||||
 | 
					          description: newDescription,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      handleError(error, 'Error updating album description');
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    description = newDescription;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{#if isOwned}
 | 
				
			||||||
 | 
					  <textarea
 | 
				
			||||||
 | 
					    class="w-full mt-2 resize-none overflow-hidden text-black dark:text-white border-b-2 border-transparent border-gray-500 bg-transparent text-base outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:focus:border-immich-dark-primary hover:border-gray-400"
 | 
				
			||||||
 | 
					    bind:value={newDescription}
 | 
				
			||||||
 | 
					    on:input={(e) => autoGrowHeight(e.currentTarget)}
 | 
				
			||||||
 | 
					    on:focusout={handleUpdateDescription}
 | 
				
			||||||
 | 
					    use:autoGrowHeight
 | 
				
			||||||
 | 
					    placeholder="Add description"
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					{:else if description}
 | 
				
			||||||
 | 
					  <p class="break-words whitespace-pre-line w-full text-black dark:text-white text-base">
 | 
				
			||||||
 | 
					    {description}
 | 
				
			||||||
 | 
					  </p>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
							
								
								
									
										42
									
								
								web/src/lib/components/album-page/album-title.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								web/src/lib/components/album-page/album-title.svelte
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					  import { updateAlbumInfo } from '@immich/sdk';
 | 
				
			||||||
 | 
					  import { handleError } from '$lib/utils/handle-error';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  export let id: string;
 | 
				
			||||||
 | 
					  export let albumName: string;
 | 
				
			||||||
 | 
					  export let isOwned: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  $: newAlbumName = albumName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleUpdateName = async () => {
 | 
				
			||||||
 | 
					    if (newAlbumName === albumName) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await updateAlbumInfo({
 | 
				
			||||||
 | 
					        id,
 | 
				
			||||||
 | 
					        updateAlbumDto: {
 | 
				
			||||||
 | 
					          albumName: newAlbumName,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      handleError(error, 'Unable to update album name');
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    albumName = newAlbumName;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<input
 | 
				
			||||||
 | 
					  on:keydown={(e) => e.key === 'Enter' && e.currentTarget.blur()}
 | 
				
			||||||
 | 
					  on:blur={handleUpdateName}
 | 
				
			||||||
 | 
					  class="w-[99%] mb-2 border-b-2 border-transparent text-6xl text-immich-primary outline-none transition-all dark:text-immich-dark-primary {isOwned
 | 
				
			||||||
 | 
					    ? 'hover:border-gray-400'
 | 
				
			||||||
 | 
					    : 'hover:border-transparent'} bg-immich-bg focus:border-b-2 focus:border-immich-primary focus:outline-none dark:bg-immich-dark-bg dark:focus:border-immich-dark-primary dark:focus:bg-immich-dark-gray"
 | 
				
			||||||
 | 
					  type="text"
 | 
				
			||||||
 | 
					  bind:value={newAlbumName}
 | 
				
			||||||
 | 
					  disabled={!isOwned}
 | 
				
			||||||
 | 
					  title="Edit Title"
 | 
				
			||||||
 | 
					  placeholder="Add a title"
 | 
				
			||||||
 | 
					/>
 | 
				
			||||||
@ -11,7 +11,7 @@
 | 
				
			|||||||
  export let isEdited = false;
 | 
					  export let isEdited = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const dispatch = createEventDispatcher<{ toggle: boolean }>();
 | 
					  const dispatch = createEventDispatcher<{ toggle: boolean }>();
 | 
				
			||||||
  const onToggle = (event: Event) => dispatch('toggle', (event.target as HTMLInputElement).checked);
 | 
					  const onToggle = (ischecked: boolean) => dispatch('toggle', ischecked);
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="flex place-items-center justify-between">
 | 
					<div class="flex place-items-center justify-between">
 | 
				
			||||||
@ -34,5 +34,5 @@
 | 
				
			|||||||
    <slot />
 | 
					    <slot />
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <Slider bind:checked {disabled} on:click={onToggle} />
 | 
					  <Slider bind:checked {disabled} on:toggle={({ detail }) => onToggle(detail)} />
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
				
			|||||||
@ -41,7 +41,6 @@
 | 
				
			|||||||
  import { SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
 | 
					  import { SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
 | 
				
			||||||
  import { user } from '$lib/stores/user.store';
 | 
					  import { user } from '$lib/stores/user.store';
 | 
				
			||||||
  import { downloadArchive } from '$lib/utils/asset-utils';
 | 
					  import { downloadArchive } from '$lib/utils/asset-utils';
 | 
				
			||||||
  import { autoGrowHeight } from '$lib/utils/autogrow';
 | 
					 | 
				
			||||||
  import { clickOutside } from '$lib/utils/click-outside';
 | 
					  import { clickOutside } from '$lib/utils/click-outside';
 | 
				
			||||||
  import { getContextMenuPosition } from '$lib/utils/context-menu';
 | 
					  import { getContextMenuPosition } from '$lib/utils/context-menu';
 | 
				
			||||||
  import { openFileUploadDialog } from '$lib/utils/file-uploader';
 | 
					  import { openFileUploadDialog } from '$lib/utils/file-uploader';
 | 
				
			||||||
@ -71,17 +70,19 @@
 | 
				
			|||||||
    mdiPlus,
 | 
					    mdiPlus,
 | 
				
			||||||
    mdiShareVariantOutline,
 | 
					    mdiShareVariantOutline,
 | 
				
			||||||
  } from '@mdi/js';
 | 
					  } from '@mdi/js';
 | 
				
			||||||
  import { onMount } from 'svelte';
 | 
					 | 
				
			||||||
  import { fly } from 'svelte/transition';
 | 
					  import { fly } from 'svelte/transition';
 | 
				
			||||||
  import type { PageData } from './$types';
 | 
					  import type { PageData } from './$types';
 | 
				
			||||||
 | 
					  import AlbumTitle from '$lib/components/album-page/album-title.svelte';
 | 
				
			||||||
 | 
					  import AlbumDescription from '$lib/components/album-page/album-description.svelte';
 | 
				
			||||||
 | 
					  import { handlePromiseError } from '$lib/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let data: PageData;
 | 
					  export let data: PageData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let { isViewing: showAssetViewer, setAssetId } = assetViewingStore;
 | 
					  let { isViewing: showAssetViewer, setAssetId } = assetViewingStore;
 | 
				
			||||||
  let { slideshowState, slideshowShuffle } = slideshowStore;
 | 
					  let { slideshowState, slideshowShuffle } = slideshowStore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let album = data.album;
 | 
					  $: album = data.album;
 | 
				
			||||||
  let description = album.description;
 | 
					  $: albumId = album.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  $: {
 | 
					  $: {
 | 
				
			||||||
    if (!album.isActivityEnabled && $numberOfComments === 0) {
 | 
					    if (!album.isActivityEnabled && $numberOfComments === 0) {
 | 
				
			||||||
@ -103,9 +104,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  let backUrl: string = AppRoute.ALBUMS;
 | 
					  let backUrl: string = AppRoute.ALBUMS;
 | 
				
			||||||
  let viewMode = ViewMode.VIEW;
 | 
					  let viewMode = ViewMode.VIEW;
 | 
				
			||||||
  let titleInput: HTMLInputElement;
 | 
					 | 
				
			||||||
  let isCreatingSharedAlbum = false;
 | 
					  let isCreatingSharedAlbum = false;
 | 
				
			||||||
  let currentAlbumName = album.albumName;
 | 
					 | 
				
			||||||
  let contextMenuPosition: { x: number; y: number } = { x: 0, y: 0 };
 | 
					  let contextMenuPosition: { x: number; y: number } = { x: 0, y: 0 };
 | 
				
			||||||
  let isShowActivity = false;
 | 
					  let isShowActivity = false;
 | 
				
			||||||
  let isLiked: ActivityResponseDto | null = null;
 | 
					  let isLiked: ActivityResponseDto | null = null;
 | 
				
			||||||
@ -114,11 +113,11 @@
 | 
				
			|||||||
  let assetGridWidth: number;
 | 
					  let assetGridWidth: number;
 | 
				
			||||||
  let textArea: HTMLTextAreaElement;
 | 
					  let textArea: HTMLTextAreaElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const assetStore = new AssetStore({ albumId: album.id });
 | 
					  $: assetStore = new AssetStore({ albumId });
 | 
				
			||||||
  const assetInteractionStore = createAssetInteractionStore();
 | 
					  const assetInteractionStore = createAssetInteractionStore();
 | 
				
			||||||
  const { isMultiSelectState, selectedAssets } = assetInteractionStore;
 | 
					  const { isMultiSelectState, selectedAssets } = assetInteractionStore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const timelineStore = new AssetStore({ isArchived: false }, album.id);
 | 
					  $: timelineStore = new AssetStore({ isArchived: false }, albumId);
 | 
				
			||||||
  const timelineInteractionStore = createAssetInteractionStore();
 | 
					  const timelineInteractionStore = createAssetInteractionStore();
 | 
				
			||||||
  const { selectedAssets: timelineSelected } = timelineInteractionStore;
 | 
					  const { selectedAssets: timelineSelected } = timelineInteractionStore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -132,7 +131,7 @@
 | 
				
			|||||||
  $: showActivityStatus =
 | 
					  $: showActivityStatus =
 | 
				
			||||||
    album.sharedUsers.length > 0 && !$showAssetViewer && (album.isActivityEnabled || $numberOfComments > 0);
 | 
					    album.sharedUsers.length > 0 && !$showAssetViewer && (album.isActivityEnabled || $numberOfComments > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  $: afterNavigate(({ from }) => {
 | 
					  afterNavigate(({ from }) => {
 | 
				
			||||||
    assetViewingStore.showAssetViewer(false);
 | 
					    assetViewingStore.showAssetViewer(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let url: string | undefined = from?.url?.pathname;
 | 
					    let url: string | undefined = from?.url?.pathname;
 | 
				
			||||||
@ -218,11 +217,10 @@
 | 
				
			|||||||
    isShowActivity = !isShowActivity;
 | 
					    isShowActivity = !isShowActivity;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onMount(async () => {
 | 
					  $: if (album.sharedUsers.length > 0) {
 | 
				
			||||||
    if (album.sharedUsers.length > 0) {
 | 
					    handlePromiseError(getFavorite());
 | 
				
			||||||
      await Promise.all([getFavorite(), getNumberOfComments()]);
 | 
					    handlePromiseError(getNumberOfComments());
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleKeypress = (event: KeyboardEvent) => {
 | 
					  const handleKeypress = (event: KeyboardEvent) => {
 | 
				
			||||||
    if (event.target !== textArea) {
 | 
					    if (event.target !== textArea) {
 | 
				
			||||||
@ -416,42 +414,6 @@
 | 
				
			|||||||
      handleError(error, 'Unable to update album cover');
 | 
					      handleError(error, 'Unable to update album cover');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					 | 
				
			||||||
  const handleUpdateName = async () => {
 | 
					 | 
				
			||||||
    if (currentAlbumName === album.albumName) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      await updateAlbumInfo({
 | 
					 | 
				
			||||||
        id: album.id,
 | 
					 | 
				
			||||||
        updateAlbumDto: {
 | 
					 | 
				
			||||||
          albumName: album.albumName,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      currentAlbumName = album.albumName;
 | 
					 | 
				
			||||||
    } catch (error) {
 | 
					 | 
				
			||||||
      handleError(error, 'Unable to update album name');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const handleUpdateDescription = async () => {
 | 
					 | 
				
			||||||
    if (album.description === description) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      await updateAlbumInfo({
 | 
					 | 
				
			||||||
        id: album.id,
 | 
					 | 
				
			||||||
        updateAlbumDto: {
 | 
					 | 
				
			||||||
          description,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      album.description = description;
 | 
					 | 
				
			||||||
    } catch (error) {
 | 
					 | 
				
			||||||
      handleError(error, 'Error updating album description');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<svelte:window on:keydown={handleKeypress} />
 | 
					<svelte:window on:keydown={handleKeypress} />
 | 
				
			||||||
@ -576,134 +538,115 @@
 | 
				
			|||||||
      class="relative h-screen overflow-hidden bg-immich-bg px-6 pt-[var(--navbar-height)] dark:bg-immich-dark-bg"
 | 
					      class="relative h-screen overflow-hidden bg-immich-bg px-6 pt-[var(--navbar-height)] dark:bg-immich-dark-bg"
 | 
				
			||||||
      style={`width:${assetGridWidth}px`}
 | 
					      style={`width:${assetGridWidth}px`}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      {#if viewMode === ViewMode.SELECT_ASSETS}
 | 
					      <!-- Use key because AssetGrid can't deal with changing stores -->
 | 
				
			||||||
        <AssetGrid assetStore={timelineStore} assetInteractionStore={timelineInteractionStore} isSelectionMode={true} />
 | 
					      {#key albumId}
 | 
				
			||||||
      {:else}
 | 
					        {#if viewMode === ViewMode.SELECT_ASSETS}
 | 
				
			||||||
        <AssetGrid
 | 
					          <AssetGrid
 | 
				
			||||||
          {album}
 | 
					            assetStore={timelineStore}
 | 
				
			||||||
          {assetStore}
 | 
					            assetInteractionStore={timelineInteractionStore}
 | 
				
			||||||
          {assetInteractionStore}
 | 
					            isSelectionMode={true}
 | 
				
			||||||
          isShared={album.sharedUsers.length > 0}
 | 
					 | 
				
			||||||
          isSelectionMode={viewMode === ViewMode.SELECT_THUMBNAIL}
 | 
					 | 
				
			||||||
          singleSelect={viewMode === ViewMode.SELECT_THUMBNAIL}
 | 
					 | 
				
			||||||
          showArchiveIcon
 | 
					 | 
				
			||||||
          on:select={({ detail: asset }) => handleUpdateThumbnail(asset.id)}
 | 
					 | 
				
			||||||
          on:escape={handleEscape}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          {#if viewMode !== ViewMode.SELECT_THUMBNAIL}
 | 
					 | 
				
			||||||
            <!-- ALBUM TITLE -->
 | 
					 | 
				
			||||||
            <section class="pt-24">
 | 
					 | 
				
			||||||
              <input
 | 
					 | 
				
			||||||
                on:keydown={(e) => e.key === 'Enter' && titleInput.blur()}
 | 
					 | 
				
			||||||
                on:blur={handleUpdateName}
 | 
					 | 
				
			||||||
                class="w-[99%] mb-2 border-b-2 border-transparent text-6xl text-immich-primary outline-none transition-all dark:text-immich-dark-primary {isOwned
 | 
					 | 
				
			||||||
                  ? 'hover:border-gray-400'
 | 
					 | 
				
			||||||
                  : 'hover:border-transparent'} bg-immich-bg focus:border-b-2 focus:border-immich-primary focus:outline-none dark:bg-immich-dark-bg dark:focus:border-immich-dark-primary dark:focus:bg-immich-dark-gray"
 | 
					 | 
				
			||||||
                type="text"
 | 
					 | 
				
			||||||
                bind:value={album.albumName}
 | 
					 | 
				
			||||||
                disabled={!isOwned}
 | 
					 | 
				
			||||||
                bind:this={titleInput}
 | 
					 | 
				
			||||||
                title="Edit Title"
 | 
					 | 
				
			||||||
                placeholder="Add a title"
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              <!-- ALBUM SUMMARY -->
 | 
					 | 
				
			||||||
              {#if album.assetCount > 0}
 | 
					 | 
				
			||||||
                <span class="my-2 flex gap-2 text-sm font-medium text-gray-500" data-testid="album-details">
 | 
					 | 
				
			||||||
                  <p class="">{getDateRange()}</p>
 | 
					 | 
				
			||||||
                  <p>·</p>
 | 
					 | 
				
			||||||
                  <p>{album.assetCount} items</p>
 | 
					 | 
				
			||||||
                </span>
 | 
					 | 
				
			||||||
              {/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              <!-- ALBUM SHARING -->
 | 
					 | 
				
			||||||
              {#if album.sharedUsers.length > 0 || (album.hasSharedLink && isOwned)}
 | 
					 | 
				
			||||||
                <div class="my-3 flex gap-x-1">
 | 
					 | 
				
			||||||
                  <!-- link -->
 | 
					 | 
				
			||||||
                  {#if album.hasSharedLink && isOwned}
 | 
					 | 
				
			||||||
                    <CircleIconButton
 | 
					 | 
				
			||||||
                      backgroundColor="#d3d3d3"
 | 
					 | 
				
			||||||
                      forceDark
 | 
					 | 
				
			||||||
                      size="20"
 | 
					 | 
				
			||||||
                      icon={mdiLink}
 | 
					 | 
				
			||||||
                      on:click={() => (viewMode = ViewMode.LINK_SHARING)}
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                  {/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                  <!-- owner -->
 | 
					 | 
				
			||||||
                  <button on:click={() => (viewMode = ViewMode.VIEW_USERS)}>
 | 
					 | 
				
			||||||
                    <UserAvatar user={album.owner} size="md" />
 | 
					 | 
				
			||||||
                  </button>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                  <!-- users -->
 | 
					 | 
				
			||||||
                  {#each album.sharedUsers as user (user.id)}
 | 
					 | 
				
			||||||
                    <button on:click={() => (viewMode = ViewMode.VIEW_USERS)}>
 | 
					 | 
				
			||||||
                      <UserAvatar {user} size="md" />
 | 
					 | 
				
			||||||
                    </button>
 | 
					 | 
				
			||||||
                  {/each}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                  {#if isOwned}
 | 
					 | 
				
			||||||
                    <CircleIconButton
 | 
					 | 
				
			||||||
                      backgroundColor="#d3d3d3"
 | 
					 | 
				
			||||||
                      forceDark
 | 
					 | 
				
			||||||
                      size="20"
 | 
					 | 
				
			||||||
                      icon={mdiPlus}
 | 
					 | 
				
			||||||
                      on:click={() => (viewMode = ViewMode.SELECT_USERS)}
 | 
					 | 
				
			||||||
                      title="Add more users"
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                  {/if}
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
              {/if}
 | 
					 | 
				
			||||||
              <!-- ALBUM DESCRIPTION -->
 | 
					 | 
				
			||||||
              {#if isOwned}
 | 
					 | 
				
			||||||
                <textarea
 | 
					 | 
				
			||||||
                  class="w-full mt-2 resize-none overflow-hidden text-black dark:text-white border-b-2 border-transparent border-gray-500 bg-transparent text-base outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:focus:border-immich-dark-primary hover:border-gray-400"
 | 
					 | 
				
			||||||
                  bind:this={textArea}
 | 
					 | 
				
			||||||
                  bind:value={description}
 | 
					 | 
				
			||||||
                  on:input={() => autoGrowHeight(textArea)}
 | 
					 | 
				
			||||||
                  on:focusout={handleUpdateDescription}
 | 
					 | 
				
			||||||
                  use:autoGrowHeight
 | 
					 | 
				
			||||||
                  placeholder="Add description"
 | 
					 | 
				
			||||||
                />
 | 
					 | 
				
			||||||
              {:else if description}
 | 
					 | 
				
			||||||
                <p class="break-words whitespace-pre-line w-full text-black dark:text-white text-base">
 | 
					 | 
				
			||||||
                  {description}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
              {/if}
 | 
					 | 
				
			||||||
            </section>
 | 
					 | 
				
			||||||
          {/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          {#if album.assetCount === 0}
 | 
					 | 
				
			||||||
            <section id="empty-album" class=" mt-[200px] flex place-content-center place-items-center">
 | 
					 | 
				
			||||||
              <div class="w-[300px]">
 | 
					 | 
				
			||||||
                <p class="text-xs dark:text-immich-dark-fg">ADD PHOTOS</p>
 | 
					 | 
				
			||||||
                <button
 | 
					 | 
				
			||||||
                  on:click={() => (viewMode = ViewMode.SELECT_ASSETS)}
 | 
					 | 
				
			||||||
                  class="mt-5 flex w-full place-items-center gap-6 rounded-md border bg-immich-bg px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none dark:bg-immich-dark-gray dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                  <span class="text-text-immich-primary dark:text-immich-dark-primary"
 | 
					 | 
				
			||||||
                    ><Icon path={mdiPlus} size="24" />
 | 
					 | 
				
			||||||
                  </span>
 | 
					 | 
				
			||||||
                  <span class="text-lg">Select photos</span>
 | 
					 | 
				
			||||||
                </button>
 | 
					 | 
				
			||||||
              </div>
 | 
					 | 
				
			||||||
            </section>
 | 
					 | 
				
			||||||
          {/if}
 | 
					 | 
				
			||||||
        </AssetGrid>
 | 
					 | 
				
			||||||
      {/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      {#if showActivityStatus}
 | 
					 | 
				
			||||||
        <div class="absolute z-[2] bottom-0 right-0 mb-6 mr-6 justify-self-end">
 | 
					 | 
				
			||||||
          <ActivityStatus
 | 
					 | 
				
			||||||
            disabled={!album.isActivityEnabled}
 | 
					 | 
				
			||||||
            {isLiked}
 | 
					 | 
				
			||||||
            numberOfComments={$numberOfComments}
 | 
					 | 
				
			||||||
            {isShowActivity}
 | 
					 | 
				
			||||||
            on:favorite={handleFavorite}
 | 
					 | 
				
			||||||
            on:openActivityTab={handleOpenAndCloseActivityTab}
 | 
					 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        </div>
 | 
					        {:else}
 | 
				
			||||||
      {/if}
 | 
					          <AssetGrid
 | 
				
			||||||
 | 
					            {album}
 | 
				
			||||||
 | 
					            {assetStore}
 | 
				
			||||||
 | 
					            {assetInteractionStore}
 | 
				
			||||||
 | 
					            isShared={album.sharedUsers.length > 0}
 | 
				
			||||||
 | 
					            isSelectionMode={viewMode === ViewMode.SELECT_THUMBNAIL}
 | 
				
			||||||
 | 
					            singleSelect={viewMode === ViewMode.SELECT_THUMBNAIL}
 | 
				
			||||||
 | 
					            showArchiveIcon
 | 
				
			||||||
 | 
					            on:select={({ detail: asset }) => handleUpdateThumbnail(asset.id)}
 | 
				
			||||||
 | 
					            on:escape={handleEscape}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            {#if viewMode !== ViewMode.SELECT_THUMBNAIL}
 | 
				
			||||||
 | 
					              <!-- ALBUM TITLE -->
 | 
				
			||||||
 | 
					              <section class="pt-24">
 | 
				
			||||||
 | 
					                <AlbumTitle id={album.id} albumName={album.albumName} {isOwned} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- ALBUM SUMMARY -->
 | 
				
			||||||
 | 
					                {#if album.assetCount > 0}
 | 
				
			||||||
 | 
					                  <span class="my-2 flex gap-2 text-sm font-medium text-gray-500" data-testid="album-details">
 | 
				
			||||||
 | 
					                    <p class="">{getDateRange()}</p>
 | 
				
			||||||
 | 
					                    <p>·</p>
 | 
				
			||||||
 | 
					                    <p>{album.assetCount} items</p>
 | 
				
			||||||
 | 
					                  </span>
 | 
				
			||||||
 | 
					                {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- ALBUM SHARING -->
 | 
				
			||||||
 | 
					                {#if album.sharedUsers.length > 0 || (album.hasSharedLink && isOwned)}
 | 
				
			||||||
 | 
					                  <div class="my-3 flex gap-x-1">
 | 
				
			||||||
 | 
					                    <!-- link -->
 | 
				
			||||||
 | 
					                    {#if album.hasSharedLink && isOwned}
 | 
				
			||||||
 | 
					                      <CircleIconButton
 | 
				
			||||||
 | 
					                        backgroundColor="#d3d3d3"
 | 
				
			||||||
 | 
					                        forceDark
 | 
				
			||||||
 | 
					                        size="20"
 | 
				
			||||||
 | 
					                        icon={mdiLink}
 | 
				
			||||||
 | 
					                        on:click={() => (viewMode = ViewMode.LINK_SHARING)}
 | 
				
			||||||
 | 
					                      />
 | 
				
			||||||
 | 
					                    {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- owner -->
 | 
				
			||||||
 | 
					                    <button on:click={() => (viewMode = ViewMode.VIEW_USERS)}>
 | 
				
			||||||
 | 
					                      <UserAvatar user={album.owner} size="md" />
 | 
				
			||||||
 | 
					                    </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- users -->
 | 
				
			||||||
 | 
					                    {#each album.sharedUsers as user (user.id)}
 | 
				
			||||||
 | 
					                      <button on:click={() => (viewMode = ViewMode.VIEW_USERS)}>
 | 
				
			||||||
 | 
					                        <UserAvatar {user} size="md" />
 | 
				
			||||||
 | 
					                      </button>
 | 
				
			||||||
 | 
					                    {/each}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    {#if isOwned}
 | 
				
			||||||
 | 
					                      <CircleIconButton
 | 
				
			||||||
 | 
					                        backgroundColor="#d3d3d3"
 | 
				
			||||||
 | 
					                        forceDark
 | 
				
			||||||
 | 
					                        size="20"
 | 
				
			||||||
 | 
					                        icon={mdiPlus}
 | 
				
			||||||
 | 
					                        on:click={() => (viewMode = ViewMode.SELECT_USERS)}
 | 
				
			||||||
 | 
					                        title="Add more users"
 | 
				
			||||||
 | 
					                      />
 | 
				
			||||||
 | 
					                    {/if}
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                {/if}
 | 
				
			||||||
 | 
					                <!-- ALBUM DESCRIPTION -->
 | 
				
			||||||
 | 
					                <AlbumDescription id={album.id} description={album.description} {isOwned} />
 | 
				
			||||||
 | 
					              </section>
 | 
				
			||||||
 | 
					            {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {#if album.assetCount === 0}
 | 
				
			||||||
 | 
					              <section id="empty-album" class=" mt-[200px] flex place-content-center place-items-center">
 | 
				
			||||||
 | 
					                <div class="w-[300px]">
 | 
				
			||||||
 | 
					                  <p class="text-xs dark:text-immich-dark-fg">ADD PHOTOS</p>
 | 
				
			||||||
 | 
					                  <button
 | 
				
			||||||
 | 
					                    on:click={() => (viewMode = ViewMode.SELECT_ASSETS)}
 | 
				
			||||||
 | 
					                    class="mt-5 flex w-full place-items-center gap-6 rounded-md border bg-immich-bg px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none dark:bg-immich-dark-gray dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    <span class="text-text-immich-primary dark:text-immich-dark-primary"
 | 
				
			||||||
 | 
					                      ><Icon path={mdiPlus} size="24" />
 | 
				
			||||||
 | 
					                    </span>
 | 
				
			||||||
 | 
					                    <span class="text-lg">Select photos</span>
 | 
				
			||||||
 | 
					                  </button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </section>
 | 
				
			||||||
 | 
					            {/if}
 | 
				
			||||||
 | 
					          </AssetGrid>
 | 
				
			||||||
 | 
					        {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {#if showActivityStatus}
 | 
				
			||||||
 | 
					          <div class="absolute z-[2] bottom-0 right-0 mb-6 mr-6 justify-self-end">
 | 
				
			||||||
 | 
					            <ActivityStatus
 | 
				
			||||||
 | 
					              disabled={!album.isActivityEnabled}
 | 
				
			||||||
 | 
					              {isLiked}
 | 
				
			||||||
 | 
					              numberOfComments={$numberOfComments}
 | 
				
			||||||
 | 
					              {isShowActivity}
 | 
				
			||||||
 | 
					              on:favorite={handleFavorite}
 | 
				
			||||||
 | 
					              on:openActivityTab={handleOpenAndCloseActivityTab}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        {/if}
 | 
				
			||||||
 | 
					      {/key}
 | 
				
			||||||
    </main>
 | 
					    </main>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  {#if album.sharedUsers.length > 0 && album && isShowActivity && $user && !$showAssetViewer}
 | 
					  {#if album.sharedUsers.length > 0 && album && isShowActivity && $user && !$showAssetViewer}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user