mirror of
https://github.com/immich-app/immich.git
synced 2025-01-21 00:52:43 -05:00
feat(web): search albums (#7322)
* feat: search albums * pr feedback * fix: comparison * pr feedback * simplify * chore: more compact album padding --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
parent
e3cccba78c
commit
75947ab6c2
6 changed files with 26 additions and 16 deletions
|
@ -1,12 +1,13 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { mdiClose, mdiMagnify } from '@mdi/js';
|
import { mdiClose, mdiMagnify } from '@mdi/js';
|
||||||
import Icon from '../elements/icon.svelte';
|
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';
|
||||||
|
|
||||||
export let name: string;
|
export let name: string;
|
||||||
export let isSearchingPeople: boolean;
|
export let isSearching: boolean;
|
||||||
|
export let placeholder: string;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ search: SearchOptions; reset: void }>();
|
const dispatch = createEventDispatcher<{ search: SearchOptions; reset: void }>();
|
||||||
|
|
||||||
|
@ -27,11 +28,11 @@
|
||||||
autofocus
|
autofocus
|
||||||
class="w-full gap-2 bg-gray-100 dark:bg-gray-700 dark:text-white"
|
class="w-full gap-2 bg-gray-100 dark:bg-gray-700 dark:text-white"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search names"
|
{placeholder}
|
||||||
bind:value={name}
|
bind:value={name}
|
||||||
on:input={() => dispatch('search', { force: false })}
|
on:input={() => dispatch('search', { force: false })}
|
||||||
/>
|
/>
|
||||||
{#if isSearchingPeople}
|
{#if isSearching}
|
||||||
<div class="flex place-items-center">
|
<div class="flex place-items-center">
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
</div>
|
</div>
|
|
@ -5,7 +5,7 @@
|
||||||
import { searchPerson, type PersonResponseDto } from '@immich/sdk';
|
import { searchPerson, type PersonResponseDto } from '@immich/sdk';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
import FaceThumbnail from './face-thumbnail.svelte';
|
import FaceThumbnail from './face-thumbnail.svelte';
|
||||||
import SearchBar from './search-bar.svelte';
|
import SearchBar from '../elements/search-bar.svelte';
|
||||||
|
|
||||||
export let screenHeight: number;
|
export let screenHeight: number;
|
||||||
export let people: PersonResponseDto[];
|
export let people: PersonResponseDto[];
|
||||||
|
@ -55,7 +55,8 @@
|
||||||
<div class=" w-40 sm:w-48 md:w-96 h-14 mb-8">
|
<div class=" w-40 sm:w-48 md:w-96 h-14 mb-8">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
bind:name
|
bind:name
|
||||||
{isSearchingPeople}
|
isSearching={isSearchingPeople}
|
||||||
|
placeholder="Search people"
|
||||||
on:reset={() => {
|
on:reset={() => {
|
||||||
people = peopleCopy;
|
people = peopleCopy;
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
export let scrollbar = true;
|
export let scrollbar = true;
|
||||||
export let admin = false;
|
export let admin = false;
|
||||||
|
|
||||||
$: scrollbarClass = scrollbar ? 'immich-scrollbar p-4 pb-8' : 'scrollbar-hidden';
|
$: scrollbarClass = scrollbar ? 'immich-scrollbar p-2 pb-8' : 'scrollbar-hidden';
|
||||||
$: hasTitleClass = title ? 'top-16 h-[calc(100%-theme(spacing.16))]' : 'top-0 h-full';
|
$: hasTitleClass = title ? 'top-16 h-[calc(100%-theme(spacing.16))]' : 'top-0 h-full';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -43,11 +43,13 @@
|
||||||
import { flip } from 'svelte/animate';
|
import { flip } from 'svelte/animate';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import { useAlbums } from './albums.bloc';
|
import { useAlbums } from './albums.bloc';
|
||||||
|
import SearchBar from '$lib/components/elements/search-bar.svelte';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
let shouldShowEditUserForm = false;
|
let shouldShowEditUserForm = false;
|
||||||
let selectedAlbum: AlbumResponseDto;
|
let selectedAlbum: AlbumResponseDto;
|
||||||
|
let searchAlbum = '';
|
||||||
|
|
||||||
let sortByOptions: Record<string, Sort> = {
|
let sortByOptions: Record<string, Sort> = {
|
||||||
albumTitle: {
|
albumTitle: {
|
||||||
|
@ -180,6 +182,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: albumsFiltered = $albums.filter((album) => album.albumName.toLowerCase().includes(searchAlbum.toLowerCase()));
|
||||||
|
|
||||||
const searchSort = (searched: string): Sort => {
|
const searchSort = (searched: string): Sort => {
|
||||||
for (const key in sortByOptions) {
|
for (const key in sortByOptions) {
|
||||||
if (sortByOptions[key].title === searched) {
|
if (sortByOptions[key].title === searched) {
|
||||||
|
@ -243,6 +247,9 @@
|
||||||
|
|
||||||
<UserPageLayout title={data.meta.title}>
|
<UserPageLayout title={data.meta.title}>
|
||||||
<div class="flex place-items-center gap-2" slot="buttons">
|
<div class="flex place-items-center gap-2" slot="buttons">
|
||||||
|
<div class="hidden lg:block lg:w-40 xl:w-60 2xl:w-80 h-10">
|
||||||
|
<SearchBar placeholder="Search albums" bind:name={searchAlbum} isSearching={false} />
|
||||||
|
</div>
|
||||||
<LinkButton on:click={handleCreateAlbum}>
|
<LinkButton on:click={handleCreateAlbum}>
|
||||||
<div class="flex place-items-center gap-2 text-sm">
|
<div class="flex place-items-center gap-2 text-sm">
|
||||||
<Icon path={mdiPlusBoxOutline} size="18" />
|
<Icon path={mdiPlusBoxOutline} size="18" />
|
||||||
|
@ -285,7 +292,7 @@
|
||||||
<!-- Album Card -->
|
<!-- Album Card -->
|
||||||
{#if $albumViewSettings.view === AlbumViewMode.Cover}
|
{#if $albumViewSettings.view === AlbumViewMode.Cover}
|
||||||
<div class="grid grid-cols-[repeat(auto-fill,minmax(14rem,1fr))]">
|
<div class="grid grid-cols-[repeat(auto-fill,minmax(14rem,1fr))]">
|
||||||
{#each $albums as album, index (album.id)}
|
{#each albumsFiltered as album, index (album.id)}
|
||||||
<a data-sveltekit-preload-data="hover" href="{AppRoute.ALBUMS}/{album.id}" animate:flip={{ duration: 200 }}>
|
<a data-sveltekit-preload-data="hover" href="{AppRoute.ALBUMS}/{album.id}" animate:flip={{ duration: 200 }}>
|
||||||
<AlbumCard
|
<AlbumCard
|
||||||
preload={index < 20}
|
preload={index < 20}
|
||||||
|
@ -296,7 +303,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{:else if $albumViewSettings.view === AlbumViewMode.List}
|
{:else if $albumViewSettings.view === AlbumViewMode.List}
|
||||||
<table class="mt-5 w-full text-left">
|
<table class="mt-2 w-full text-left">
|
||||||
<thead
|
<thead
|
||||||
class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary"
|
class="mb-4 flex h-12 w-full rounded-md border bg-gray-50 text-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-primary"
|
||||||
>
|
>
|
||||||
|
@ -310,7 +317,7 @@
|
||||||
<tbody
|
<tbody
|
||||||
class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg"
|
class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg"
|
||||||
>
|
>
|
||||||
{#each $albums as album (album.id)}
|
{#each albumsFiltered as album (album.id)}
|
||||||
<tr
|
<tr
|
||||||
class="flex h-[50px] w-full place-items-center border-[3px] border-transparent p-2 text-center odd:bg-immich-gray even:bg-immich-bg hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5"
|
class="flex h-[50px] w-full place-items-center border-[3px] border-transparent p-2 text-center odd:bg-immich-gray even:bg-immich-bg hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5"
|
||||||
on:click={() => goto(`${AppRoute.ALBUMS}/${album.id}`)}
|
on:click={() => goto(`${AppRoute.ALBUMS}/${album.id}`)}
|
||||||
|
|
|
@ -603,7 +603,7 @@
|
||||||
<input
|
<input
|
||||||
on:keydown={(e) => e.key === 'Enter' && titleInput.blur()}
|
on:keydown={(e) => e.key === 'Enter' && titleInput.blur()}
|
||||||
on:blur={handleUpdateName}
|
on:blur={handleUpdateName}
|
||||||
class="w-[99%] border-b-2 border-transparent text-6xl text-immich-primary outline-none transition-all dark:text-immich-dark-primary {isOwned
|
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-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"
|
: '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"
|
type="text"
|
||||||
|
@ -616,7 +616,7 @@
|
||||||
|
|
||||||
<!-- ALBUM SUMMARY -->
|
<!-- ALBUM SUMMARY -->
|
||||||
{#if album.assetCount > 0}
|
{#if album.assetCount > 0}
|
||||||
<span class="my-4 flex gap-2 text-sm font-medium text-gray-500" data-testid="album-details">
|
<span class="my-2 flex gap-2 text-sm font-medium text-gray-500" data-testid="album-details">
|
||||||
<p class="">{getDateRange()}</p>
|
<p class="">{getDateRange()}</p>
|
||||||
<p>·</p>
|
<p>·</p>
|
||||||
<p>{album.assetCount} items</p>
|
<p>{album.assetCount} items</p>
|
||||||
|
@ -625,7 +625,7 @@
|
||||||
|
|
||||||
<!-- ALBUM SHARING -->
|
<!-- ALBUM SHARING -->
|
||||||
{#if album.sharedUsers.length > 0 || (album.hasSharedLink && isOwned)}
|
{#if album.sharedUsers.length > 0 || (album.hasSharedLink && isOwned)}
|
||||||
<div class="my-6 flex gap-x-1">
|
<div class="my-3 flex gap-x-1">
|
||||||
<!-- link -->
|
<!-- link -->
|
||||||
{#if album.hasSharedLink && isOwned}
|
{#if album.hasSharedLink && isOwned}
|
||||||
<CircleIconButton
|
<CircleIconButton
|
||||||
|
@ -664,7 +664,7 @@
|
||||||
<!-- ALBUM DESCRIPTION -->
|
<!-- ALBUM DESCRIPTION -->
|
||||||
{#if isOwned}
|
{#if isOwned}
|
||||||
<textarea
|
<textarea
|
||||||
class="w-full 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"
|
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:this={textArea}
|
||||||
bind:value={description}
|
bind:value={description}
|
||||||
on:input={() => autoGrowHeight(textArea)}
|
on:input={() => autoGrowHeight(textArea)}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
import MergeSuggestionModal from '$lib/components/faces-page/merge-suggestion-modal.svelte';
|
import MergeSuggestionModal from '$lib/components/faces-page/merge-suggestion-modal.svelte';
|
||||||
import PeopleCard from '$lib/components/faces-page/people-card.svelte';
|
import PeopleCard from '$lib/components/faces-page/people-card.svelte';
|
||||||
import SearchBar from '$lib/components/faces-page/search-bar.svelte';
|
import SearchBar from '$lib/components/elements/search-bar.svelte';
|
||||||
import SetBirthDateModal from '$lib/components/faces-page/set-birth-date-modal.svelte';
|
import SetBirthDateModal from '$lib/components/faces-page/set-birth-date-modal.svelte';
|
||||||
import ShowHide from '$lib/components/faces-page/show-hide.svelte';
|
import ShowHide from '$lib/components/faces-page/show-hide.svelte';
|
||||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||||
|
@ -441,7 +441,8 @@
|
||||||
<div class="w-40 lg:w-80 h-10">
|
<div class="w-40 lg:w-80 h-10">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
bind:name={searchName}
|
bind:name={searchName}
|
||||||
{isSearchingPeople}
|
isSearching={isSearchingPeople}
|
||||||
|
placeholder="Search people"
|
||||||
on:reset={() => {
|
on:reset={() => {
|
||||||
searchedPeople = [];
|
searchedPeople = [];
|
||||||
}}
|
}}
|
||||||
|
|
Loading…
Add table
Reference in a new issue