mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:27:09 -05:00 
			
		
		
		
	fix(web): User removal from option menu on the top in shared album (#12959)
* bug fix * added few more type hint * onMount removed, removed current user to user * user check removed and conflict in view mode resolved between option and share info modal * format fix --------- Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
		
							parent
							
								
									53358c768c
								
							
						
					
					
						commit
						1baa49edb7
					
				@ -1,7 +1,13 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import Icon from '$lib/components/elements/icon.svelte';
 | 
					  import Icon from '$lib/components/elements/icon.svelte';
 | 
				
			||||||
  import { updateAlbumInfo, type AlbumResponseDto, type UserResponseDto, AssetOrder } from '@immich/sdk';
 | 
					  import {
 | 
				
			||||||
  import { mdiArrowDownThin, mdiArrowUpThin, mdiPlus } from '@mdi/js';
 | 
					    updateAlbumInfo,
 | 
				
			||||||
 | 
					    removeUserFromAlbum,
 | 
				
			||||||
 | 
					    type AlbumResponseDto,
 | 
				
			||||||
 | 
					    type UserResponseDto,
 | 
				
			||||||
 | 
					    AssetOrder,
 | 
				
			||||||
 | 
					  } from '@immich/sdk';
 | 
				
			||||||
 | 
					  import { mdiArrowDownThin, mdiArrowUpThin, mdiPlus, mdiDotsVertical } from '@mdi/js';
 | 
				
			||||||
  import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
 | 
					  import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
 | 
				
			||||||
  import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
 | 
					  import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
 | 
				
			||||||
  import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
 | 
					  import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
 | 
				
			||||||
@ -10,14 +16,21 @@
 | 
				
			|||||||
  import { handleError } from '$lib/utils/handle-error';
 | 
					  import { handleError } from '$lib/utils/handle-error';
 | 
				
			||||||
  import { findKey } from 'lodash-es';
 | 
					  import { findKey } from 'lodash-es';
 | 
				
			||||||
  import { t } from 'svelte-i18n';
 | 
					  import { t } from 'svelte-i18n';
 | 
				
			||||||
 | 
					  import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
 | 
				
			||||||
 | 
					  import ConfirmDialog from '$lib/components/shared-components/dialog/confirm-dialog.svelte';
 | 
				
			||||||
 | 
					  import { notificationController, NotificationType } from '../shared-components/notification/notification';
 | 
				
			||||||
 | 
					  import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let album: AlbumResponseDto;
 | 
					  export let album: AlbumResponseDto;
 | 
				
			||||||
  export let order: AssetOrder | undefined;
 | 
					  export let order: AssetOrder | undefined;
 | 
				
			||||||
  export let user: UserResponseDto;
 | 
					  export let user: UserResponseDto; // Declare user as a prop
 | 
				
			||||||
  export let onChangeOrder: (order: AssetOrder) => void;
 | 
					  export let onChangeOrder: (order: AssetOrder) => void;
 | 
				
			||||||
  export let onClose: () => void;
 | 
					  export let onClose: () => void;
 | 
				
			||||||
  export let onToggleEnabledActivity: () => void;
 | 
					  export let onToggleEnabledActivity: () => void;
 | 
				
			||||||
  export let onShowSelectSharedUser: () => void;
 | 
					  export let onShowSelectSharedUser: () => void;
 | 
				
			||||||
 | 
					  export let onRemove: (userId: string) => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let selectedRemoveUser: UserResponseDto | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const options: Record<AssetOrder, RenderedOption> = {
 | 
					  const options: Record<AssetOrder, RenderedOption> = {
 | 
				
			||||||
    [AssetOrder.Asc]: { icon: mdiArrowUpThin, title: $t('oldest_first') },
 | 
					    [AssetOrder.Asc]: { icon: mdiArrowUpThin, title: $t('oldest_first') },
 | 
				
			||||||
@ -26,11 +39,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  $: selectedOption = order ? options[order] : options[AssetOrder.Desc];
 | 
					  $: selectedOption = order ? options[order] : options[AssetOrder.Desc];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleToggle = async (returnedOption: RenderedOption) => {
 | 
					  const handleToggle = async (returnedOption: RenderedOption): Promise<void> => {
 | 
				
			||||||
    if (selectedOption === returnedOption) {
 | 
					    if (selectedOption === returnedOption) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    let order = AssetOrder.Desc;
 | 
					    let order: AssetOrder = AssetOrder.Desc;
 | 
				
			||||||
    order = findKey(options, (option) => option === returnedOption) as AssetOrder;
 | 
					    order = findKey(options, (option) => option === returnedOption) as AssetOrder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
@ -45,8 +58,31 @@
 | 
				
			|||||||
      handleError(error, $t('errors.unable_to_save_album'));
 | 
					      handleError(error, $t('errors.unable_to_save_album'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleMenuRemove = (user: UserResponseDto): void => {
 | 
				
			||||||
 | 
					    selectedRemoveUser = user;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleRemoveUser = async (): Promise<void> => {
 | 
				
			||||||
 | 
					    if (!selectedRemoveUser) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await removeUserFromAlbum({ id: album.id, userId: selectedRemoveUser.id });
 | 
				
			||||||
 | 
					      onRemove(selectedRemoveUser.id);
 | 
				
			||||||
 | 
					      notificationController.show({
 | 
				
			||||||
 | 
					        type: NotificationType.Info,
 | 
				
			||||||
 | 
					        message: $t('album_user_removed', { values: { user: selectedRemoveUser.name } }),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      handleError(error, $t('errors.unable_to_remove_album_users'));
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					      selectedRemoveUser = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{#if !selectedRemoveUser}
 | 
				
			||||||
  <FullScreenModal title={$t('options')} {onClose}>
 | 
					  <FullScreenModal title={$t('options')} {onClose}>
 | 
				
			||||||
    <div class="items-center justify-center">
 | 
					    <div class="items-center justify-center">
 | 
				
			||||||
      <div class="py-2">
 | 
					      <div class="py-2">
 | 
				
			||||||
@ -77,6 +113,7 @@
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div>{$t('invite_people')}</div>
 | 
					            <div>{$t('invite_people')}</div>
 | 
				
			||||||
          </button>
 | 
					          </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <div class="flex items-center gap-2 py-2 mt-2">
 | 
					          <div class="flex items-center gap-2 py-2 mt-2">
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
              <UserAvatar {user} size="md" />
 | 
					              <UserAvatar {user} size="md" />
 | 
				
			||||||
@ -84,15 +121,33 @@
 | 
				
			|||||||
            <div class="w-full">{user.name}</div>
 | 
					            <div class="w-full">{user.name}</div>
 | 
				
			||||||
            <div>{$t('owner')}</div>
 | 
					            <div>{$t('owner')}</div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          {#each album.albumUsers as { user } (user.id)}
 | 
					          {#each album.albumUsers as { user } (user.id)}
 | 
				
			||||||
            <div class="flex items-center gap-2 py-2">
 | 
					            <div class="flex items-center gap-2 py-2">
 | 
				
			||||||
              <div>
 | 
					              <div>
 | 
				
			||||||
                <UserAvatar {user} size="md" />
 | 
					                <UserAvatar {user} size="md" />
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div class="w-full">{user.name}</div>
 | 
					              <div class="w-full">{user.name}</div>
 | 
				
			||||||
 | 
					              {#if user.id !== album.ownerId}
 | 
				
			||||||
 | 
					                <!-- Allow deletion for non-owners -->
 | 
				
			||||||
 | 
					                <ButtonContextMenu icon={mdiDotsVertical} size="20" title={$t('options')}>
 | 
				
			||||||
 | 
					                  <MenuOption onClick={() => handleMenuRemove(user)} text={$t('remove')} />
 | 
				
			||||||
 | 
					                </ButtonContextMenu>
 | 
				
			||||||
 | 
					              {/if}
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          {/each}
 | 
					          {/each}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </FullScreenModal>
 | 
					  </FullScreenModal>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{#if selectedRemoveUser}
 | 
				
			||||||
 | 
					  <ConfirmDialog
 | 
				
			||||||
 | 
					    title={$t('album_remove_user')}
 | 
				
			||||||
 | 
					    prompt={$t('album_remove_user_confirmation', { values: { user: selectedRemoveUser.name } })}
 | 
				
			||||||
 | 
					    confirmText={$t('remove_user')}
 | 
				
			||||||
 | 
					    onConfirm={handleRemoveUser}
 | 
				
			||||||
 | 
					    onCancel={() => (selectedRemoveUser = null)}
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
 | 
				
			|||||||
@ -345,7 +345,7 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleRemoveUser = async (userId: string) => {
 | 
					  const handleRemoveUser = async (userId: string, nextViewMode: ViewMode) => {
 | 
				
			||||||
    if (userId == 'me' || userId === $user.id) {
 | 
					    if (userId == 'me' || userId === $user.id) {
 | 
				
			||||||
      await goto(backUrl);
 | 
					      await goto(backUrl);
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
@ -353,7 +353,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await refreshAlbum();
 | 
					      await refreshAlbum();
 | 
				
			||||||
      viewMode = album.albumUsers.length > 0 ? ViewMode.VIEW_USERS : ViewMode.VIEW;
 | 
					
 | 
				
			||||||
 | 
					      // Dynamically set the view mode based on the passed argument
 | 
				
			||||||
 | 
					      viewMode = album.albumUsers.length > 0 ? nextViewMode : ViewMode.VIEW;
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
      handleError(error, $t('errors.error_deleting_shared_user'));
 | 
					      handleError(error, $t('errors.error_deleting_shared_user'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -730,7 +732,7 @@
 | 
				
			|||||||
  <ShareInfoModal
 | 
					  <ShareInfoModal
 | 
				
			||||||
    onClose={() => (viewMode = ViewMode.VIEW)}
 | 
					    onClose={() => (viewMode = ViewMode.VIEW)}
 | 
				
			||||||
    {album}
 | 
					    {album}
 | 
				
			||||||
    onRemove={handleRemoveUser}
 | 
					    onRemove={(userId) => handleRemoveUser(userId, ViewMode.VIEW_USERS)}
 | 
				
			||||||
    onRefreshAlbum={refreshAlbum}
 | 
					    onRefreshAlbum={refreshAlbum}
 | 
				
			||||||
  />
 | 
					  />
 | 
				
			||||||
{/if}
 | 
					{/if}
 | 
				
			||||||
@ -744,6 +746,7 @@
 | 
				
			|||||||
      albumOrder = order;
 | 
					      albumOrder = order;
 | 
				
			||||||
      await setModeToView();
 | 
					      await setModeToView();
 | 
				
			||||||
    }}
 | 
					    }}
 | 
				
			||||||
 | 
					    onRemove={(userId) => handleRemoveUser(userId, ViewMode.OPTIONS)}
 | 
				
			||||||
    onClose={() => (viewMode = ViewMode.VIEW)}
 | 
					    onClose={() => (viewMode = ViewMode.VIEW)}
 | 
				
			||||||
    onToggleEnabledActivity={handleToggleEnableActivity}
 | 
					    onToggleEnabledActivity={handleToggleEnableActivity}
 | 
				
			||||||
    onShowSelectSharedUser={() => (viewMode = ViewMode.SELECT_USERS)}
 | 
					    onShowSelectSharedUser={() => (viewMode = ViewMode.SELECT_USERS)}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user