0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-21 00:52:43 -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:
Pranav tiwari 2024-10-10 14:26:08 +05:30 committed by GitHub
parent 53358c768c
commit 1baa49edb7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 106 additions and 48 deletions

View file

@ -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,54 +58,96 @@
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>
<FullScreenModal title={$t('options')} {onClose}> {#if !selectedRemoveUser}
<div class="items-center justify-center"> <FullScreenModal title={$t('options')} {onClose}>
<div class="py-2"> <div class="items-center justify-center">
<h2 class="text-gray text-sm mb-2">{$t('settings').toUpperCase()}</h2> <div class="py-2">
<div class="grid p-2 gap-y-2"> <h2 class="text-gray text-sm mb-2">{$t('settings').toUpperCase()}</h2>
{#if order} <div class="grid p-2 gap-y-2">
<SettingDropdown {#if order}
title={$t('display_order')} <SettingDropdown
options={Object.values(options)} title={$t('display_order')}
selectedOption={options[order]} options={Object.values(options)}
onToggle={handleToggle} selectedOption={options[order]}
onToggle={handleToggle}
/>
{/if}
<SettingSwitch
title={$t('comments_and_likes')}
subtitle={$t('let_others_respond')}
checked={album.isActivityEnabled}
onToggle={onToggleEnabledActivity}
/> />
{/if}
<SettingSwitch
title={$t('comments_and_likes')}
subtitle={$t('let_others_respond')}
checked={album.isActivityEnabled}
onToggle={onToggleEnabledActivity}
/>
</div>
</div>
<div class="py-2">
<div class="text-gray text-sm mb-3">{$t('people').toUpperCase()}</div>
<div class="p-2">
<button type="button" class="flex items-center gap-2" on:click={onShowSelectSharedUser}>
<div class="rounded-full w-10 h-10 border border-gray-500 flex items-center justify-center">
<div><Icon path={mdiPlus} size="25" /></div>
</div>
<div>{$t('invite_people')}</div>
</button>
<div class="flex items-center gap-2 py-2 mt-2">
<div>
<UserAvatar {user} size="md" />
</div>
<div class="w-full">{user.name}</div>
<div>{$t('owner')}</div>
</div> </div>
{#each album.albumUsers as { user } (user.id)} </div>
<div class="flex items-center gap-2 py-2"> <div class="py-2">
<div class="text-gray text-sm mb-3">{$t('people').toUpperCase()}</div>
<div class="p-2">
<button type="button" class="flex items-center gap-2" on:click={onShowSelectSharedUser}>
<div class="rounded-full w-10 h-10 border border-gray-500 flex items-center justify-center">
<div><Icon path={mdiPlus} size="25" /></div>
</div>
<div>{$t('invite_people')}</div>
</button>
<div class="flex items-center gap-2 py-2 mt-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>
<div>{$t('owner')}</div>
</div> </div>
{/each}
{#each album.albumUsers as { user } (user.id)}
<div class="flex items-center gap-2 py-2">
<div>
<UserAvatar {user} size="md" />
</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>
{/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}

View file

@ -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)}