1
0
mirror of https://github.com/immich-app/immich.git synced 2024-11-28 09:33:27 +02:00

fix(web): multiple improvements for people page (1) (#4717)

* fix(web): multiple improvements for people page

* feat: better responsive icons
This commit is contained in:
martin 2023-10-30 20:40:28 +01:00 committed by GitHub
parent 8dcd159bd6
commit 9a60578088
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 35 deletions

View File

@ -103,11 +103,7 @@ export class PersonRepository implements IPersonRepository {
return this.personRepository.findOne({ where: { id: personId } }); return this.personRepository.findOne({ where: { id: personId } });
} }
async getByName( getByName(userId: string, personName: string, { withHidden }: PersonNameSearchOptions): Promise<PersonEntity[]> {
userId: string,
personName: string,
{ withHidden }: PersonNameSearchOptions,
): Promise<PersonEntity[]> {
const queryBuilder = this.personRepository const queryBuilder = this.personRepository
.createQueryBuilder('person') .createQueryBuilder('person')
.leftJoin('person.faces', 'face') .leftJoin('person.faces', 'face')

View File

@ -10,10 +10,11 @@
import { NotificationType, notificationController } from '../shared-components/notification/notification'; import { NotificationType, notificationController } from '../shared-components/notification/notification';
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte'; import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
import { handleError } from '$lib/utils/handle-error'; import { handleError } from '$lib/utils/handle-error';
import { goto, invalidateAll } from '$app/navigation'; import { goto } from '$app/navigation';
import { AppRoute } from '$lib/constants'; import { AppRoute } from '$lib/constants';
import { mdiCallMerge, mdiMerge, mdiSwapHorizontal } from '@mdi/js'; import { mdiCallMerge, mdiMerge, mdiSwapHorizontal } from '@mdi/js';
import Icon from '$lib/components/elements/icon.svelte'; import Icon from '$lib/components/elements/icon.svelte';
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
export let person: PersonResponseDto; export let person: PersonResponseDto;
let people: PersonResponseDto[] = []; let people: PersonResponseDto[] = [];
@ -69,8 +70,6 @@
message: `Merged ${count} ${count === 1 ? 'person' : 'people'}`, message: `Merged ${count} ${count === 1 ? 'person' : 'people'}`,
type: NotificationType.Info, type: NotificationType.Info,
}); });
people = people.filter((person) => !results.some((result) => result.id === person.id && result.success === true));
await invalidateAll();
dispatch('merge'); dispatch('merge');
} catch (error) { } catch (error) {
handleError(error, 'Cannot merge faces'); handleError(error, 'Cannot merge faces');
@ -121,14 +120,18 @@
{/each} {/each}
{#if hasSelection} {#if hasSelection}
<span class="grid grid-cols-1" <div class="relative h-full">
><Icon path={mdiCallMerge} size={48} class="rotate-90 dark:text-white" /> <div class="flex flex-col h-full justify-between">
{#if selectedPeople.length === 1} <div class="flex h-full items-center justify-center">
<button class="flex justify-center" on:click={handleSwapPeople} <Icon path={mdiCallMerge} size={48} class="rotate-90 dark:text-white" />
><Icon path={mdiSwapHorizontal} size={24} class="dark:text-white" /> </div>
</button> {#if selectedPeople.length === 1}
{/if} <div class="absolute bottom-2">
</span> <CircleIconButton icon={mdiSwapHorizontal} size="24" on:click={handleSwapPeople} />
</div>
{/if}
</div>
</div>
{/if} {/if}
<FaceThumbnail {person} border circle selectable={false} thumbnailSize={180} /> <FaceThumbnail {person} border circle selectable={false} thumbnailSize={180} />
</div> </div>

View File

@ -22,7 +22,9 @@
OBJECTS = 'smartInfo.objects', OBJECTS = 'smartInfo.objects',
} }
const MAX_ITEMS = 12; let MAX_ITEMS: number;
let innerWidth: number;
let screenSize: number;
const getFieldItems = (items: SearchExploreResponseDto[], field: Field) => { const getFieldItems = (items: SearchExploreResponseDto[], field: Field) => {
const targetField = items.find((item) => item.fieldName === field); const targetField = items.find((item) => item.fieldName === field);
return targetField?.items || []; return targetField?.items || [];
@ -32,8 +34,16 @@
$: places = getFieldItems(data.items, Field.CITY); $: places = getFieldItems(data.items, Field.CITY);
$: people = data.response.people.slice(0, MAX_ITEMS); $: people = data.response.people.slice(0, MAX_ITEMS);
$: hasPeople = data.response.total > 0; $: hasPeople = data.response.total > 0;
$: {
if (innerWidth && screenSize) {
// Set the number of faces according to the screen size and the div size
MAX_ITEMS = screenSize < 768 ? Math.floor(innerWidth / 96) : Math.floor(innerWidth / 112);
}
}
</script> </script>
<svelte:window bind:innerWidth={screenSize} />
<UserPageLayout user={data.user} title={data.meta.title}> <UserPageLayout user={data.user} title={data.meta.title}>
{#if hasPeople} {#if hasPeople}
<div class="mb-6 mt-2"> <div class="mb-6 mt-2">
@ -45,19 +55,21 @@
draggable="false">View All</a draggable="false">View All</a
> >
</div> </div>
<div class="flex flex-row flex-wrap gap-4"> <div class="flex flex-row {MAX_ITEMS < 5 ? 'justify-center' : ''} flex-wrap gap-4" bind:offsetWidth={innerWidth}>
{#each people as person (person.id)} {#if MAX_ITEMS}
<a href="/people/{person.id}" class="w-24 text-center"> {#each people as person (person.id)}
<ImageThumbnail <a href="/people/{person.id}" class="w-20 md:w-24 text-center">
circle <ImageThumbnail
shadow circle
url={api.getPeopleThumbnailUrl(person.id)} shadow
altText={person.name} url={api.getPeopleThumbnailUrl(person.id)}
widthStyle="100%" altText={person.name}
/> widthStyle="100%"
<p class="mt-2 text-ellipsis text-sm font-medium dark:text-white">{person.name}</p> />
</a> <p class="mt-2 text-ellipsis text-sm font-medium dark:text-white">{person.name}</p>
{/each} </a>
{/each}
{/if}
</div> </div>
</div> </div>
{/if} {/if}

View File

@ -87,7 +87,7 @@
if ((people.length < 20 && name.startsWith(searchWord)) || name === '') { if ((people.length < 20 && name.startsWith(searchWord)) || name === '') {
return; return;
} }
const timeout = setTimeout(() => (isSearchingPeople = true), 300); const timeout = setTimeout(() => (isSearchingPeople = true), 100);
try { try {
const { data } = await api.searchApi.searchPerson({ name }); const { data } = await api.searchApi.searchPerson({ name });
people = data; people = data;
@ -156,6 +156,7 @@
personId: data.person.id, personId: data.person.id,
}); });
previousPersonId = data.person.id; previousPersonId = data.person.id;
name = data.person.name;
refreshAssetGrid = !refreshAssetGrid; refreshAssetGrid = !refreshAssetGrid;
} }
}); });
@ -265,7 +266,7 @@
if (viewMode === ViewMode.SUGGEST_MERGE) { if (viewMode === ViewMode.SUGGEST_MERGE) {
return; return;
} }
isSearchingPeople = false;
isEditingName = false; isEditingName = false;
}; };
@ -463,7 +464,7 @@
<div class="absolute z-[999] w-64 sm:w-96"> <div class="absolute z-[999] w-64 sm:w-96">
{#if isSearchingPeople} {#if isSearchingPeople}
<div <div
class="flex rounded-b-lg dark:border-immich-dark-gray place-items-center bg-gray-100 p-2 dark:bg-gray-700" class="flex border h-14 rounded-b-lg border-gray-400 dark:border-immich-dark-gray place-items-center bg-gray-200 p-2 dark:bg-gray-700"
> >
<div class="flex w-full place-items-center"> <div class="flex w-full place-items-center">
<LoadingSpinner /> <LoadingSpinner />
@ -472,8 +473,10 @@
{:else} {:else}
{#each suggestedPeople as person, index (person.id)} {#each suggestedPeople as person, index (person.id)}
<div <div
class="flex border-t dark:border-immich-dark-gray place-items-center bg-gray-100 p-2 dark:bg-gray-700 {index === class="flex border-t border-x border-gray-400 dark:border-immich-dark-gray h-14 place-items-center bg-gray-200 p-2 dark:bg-gray-700 hover:bg-gray-300 hover:dark:bg-[#232932] {index ===
suggestedPeople.length - 1 && 'rounded-b-lg'}" suggestedPeople.length - 1
? 'rounded-b-lg border-b'
: ''}"
> >
<button class="flex w-full place-items-center" on:click={() => handleSuggestPeople(person)}> <button class="flex w-full place-items-center" on:click={() => handleSuggestPeople(person)}>
<ImageThumbnail <ImageThumbnail