mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04:00
feat(web): remember search context (#16614)
* Retain search context in LocalStorage. * Remove debug logging * Prettier * Added QueryType and VALID_QUERY_TYPES to $lib/constants * Prettier * Renamed VALID_QUERY_TYPES to fit the codestyle. Ran prettier * show current search type on search bar * fix: linting --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
2f8e89c7ec
commit
70a08707d2
@ -32,6 +32,7 @@
|
||||
let showFilter = $state(false);
|
||||
let isSearchSuggestions = $state(false);
|
||||
let selectedId: string | undefined = $state();
|
||||
let isFocus = $state(false);
|
||||
|
||||
const listboxId = generateId();
|
||||
|
||||
@ -98,7 +99,25 @@
|
||||
};
|
||||
|
||||
const onSubmit = () => {
|
||||
handlePromiseError(handleSearch({ query: value }));
|
||||
const searchType = getSearchType();
|
||||
let payload: SmartSearchDto | MetadataSearchDto = {} as SmartSearchDto | MetadataSearchDto;
|
||||
|
||||
switch (searchType) {
|
||||
case 'smart': {
|
||||
payload = { query: value } as SmartSearchDto;
|
||||
break;
|
||||
}
|
||||
case 'metadata': {
|
||||
payload = { originalFileName: value } as MetadataSearchDto;
|
||||
break;
|
||||
}
|
||||
case 'description': {
|
||||
payload = { description: value } as MetadataSearchDto;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handlePromiseError(handleSearch(payload));
|
||||
saveSearchTerm(value);
|
||||
};
|
||||
|
||||
@ -132,10 +151,12 @@
|
||||
|
||||
const openDropdown = () => {
|
||||
showSuggestions = true;
|
||||
isFocus = true;
|
||||
};
|
||||
|
||||
const closeDropdown = () => {
|
||||
showSuggestions = false;
|
||||
isFocus = false;
|
||||
searchHistoryBox?.clearSelection();
|
||||
};
|
||||
|
||||
@ -143,6 +164,26 @@
|
||||
event.preventDefault();
|
||||
onSubmit();
|
||||
};
|
||||
|
||||
function getSearchType(): 'smart' | 'metadata' | 'description' {
|
||||
const t = localStorage.getItem('searchQueryType');
|
||||
return t === 'smart' || t === 'description' ? t : 'metadata';
|
||||
}
|
||||
|
||||
function getSearchTypeText(): string {
|
||||
const searchType = getSearchType();
|
||||
switch (searchType) {
|
||||
case 'smart': {
|
||||
return $t('context');
|
||||
}
|
||||
case 'metadata': {
|
||||
return $t('filename');
|
||||
}
|
||||
case 'description': {
|
||||
return $t('description');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
@ -214,6 +255,21 @@
|
||||
<div class="absolute inset-y-0 {showClearIcon ? 'right-14' : 'right-2'} flex items-center pl-6 transition-all">
|
||||
<CircleIconButton title={$t('show_search_options')} icon={mdiTune} onclick={onFilterClick} size="20" />
|
||||
</div>
|
||||
|
||||
{#if isFocus}
|
||||
<div
|
||||
class="absolute inset-y-0 flex items-center"
|
||||
class:right-16={isFocus}
|
||||
class:right-28={isFocus && value.length > 0}
|
||||
>
|
||||
<p
|
||||
class="bg-immich-primary text-white dark:bg-immich-dark-primary/90 dark:text-black/75 rounded-full px-3 py-1 text-xs z-10"
|
||||
>
|
||||
{getSearchTypeText()}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if showClearIcon}
|
||||
<div class="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<CircleIconButton onclick={onClear} icon={mdiClose} title={$t('clear')} size="20" />
|
||||
|
@ -2,7 +2,7 @@
|
||||
import type { SearchLocationFilter } from './search-location-section.svelte';
|
||||
import type { SearchDisplayFilters } from './search-display-section.svelte';
|
||||
import type { SearchDateFilter } from './search-date-section.svelte';
|
||||
import { MediaType } from '$lib/constants';
|
||||
import { MediaType, QueryType, validQueryTypes } from '$lib/constants';
|
||||
|
||||
export type SearchFilter = {
|
||||
query: string;
|
||||
@ -55,9 +55,18 @@
|
||||
return value === null ? undefined : value;
|
||||
}
|
||||
|
||||
function storeQueryType(type: SearchFilter['queryType']) {
|
||||
localStorage.setItem('searchQueryType', type);
|
||||
}
|
||||
|
||||
function defaultQueryType(): QueryType {
|
||||
const storedQueryType = localStorage.getItem('searchQueryType') as QueryType;
|
||||
return validQueryTypes.has(storedQueryType) ? storedQueryType : QueryType.SMART;
|
||||
}
|
||||
|
||||
let filter: SearchFilter = $state({
|
||||
query: 'query' in searchQuery ? searchQuery.query : searchQuery.originalFileName || '',
|
||||
queryType: 'smart',
|
||||
queryType: defaultQueryType(),
|
||||
personIds: new SvelteSet('personIds' in searchQuery ? searchQuery.personIds : []),
|
||||
tagIds: new SvelteSet('tagIds' in searchQuery ? searchQuery.tagIds : []),
|
||||
location: {
|
||||
@ -90,7 +99,7 @@
|
||||
const resetForm = () => {
|
||||
filter = {
|
||||
query: '',
|
||||
queryType: 'smart',
|
||||
queryType: defaultQueryType(), // retain from localStorage or default
|
||||
personIds: new SvelteSet(),
|
||||
tagIds: new SvelteSet(),
|
||||
location: {},
|
||||
@ -142,8 +151,14 @@
|
||||
|
||||
const onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
storeQueryType(filter.queryType);
|
||||
search();
|
||||
};
|
||||
|
||||
// Will be called whenever queryType changes, not just onsubmit.
|
||||
$effect(() => {
|
||||
storeQueryType(filter.queryType);
|
||||
});
|
||||
</script>
|
||||
|
||||
<FullScreenModal icon={mdiTune} width="extra-wide" title={$t('search_options')} {onClose}>
|
||||
|
@ -119,6 +119,14 @@ export const fallbackLocale = {
|
||||
name: 'English (US)',
|
||||
};
|
||||
|
||||
export enum QueryType {
|
||||
SMART = 'smart',
|
||||
METADATA = 'metadata',
|
||||
DESCRIPTION = 'description',
|
||||
}
|
||||
|
||||
export const validQueryTypes = new Set([QueryType.SMART, QueryType.METADATA, QueryType.DESCRIPTION]);
|
||||
|
||||
export const locales = [
|
||||
{ code: 'af-ZA', name: 'Afrikaans (South Africa)' },
|
||||
{ code: 'sq-AL', name: 'Albanian (Albania)' },
|
||||
|
Loading…
x
Reference in New Issue
Block a user