Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(web): alignment of people in search box #7430

Merged
merged 13 commits into from
Feb 27, 2024
Prev Previous commit
Next Next commit
refactor camera filter
michelheusschen committed Feb 26, 2024
commit e485386dd6acbe3c7990599a4619cd8617ece3c4
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
<script lang="ts" context="module">
export interface SearchCameraFilter {
make?: string;
model?: string;
}
</script>

<script lang="ts">
import type { ComboBoxOption } from '../combobox.svelte';
import Combobox from '../combobox.svelte';
import type { SearchFilter } from './search-filter-box.svelte';
export let filteredCamera: SearchFilter['camera'];
export let suggestedMakes: ComboBoxOption[];
export let suggestedModels: ComboBoxOption[];
export let updateSuggestions: () => Promise<void>;
import { SearchSuggestionType, getSearchSuggestions } from '@immich/sdk';
import Combobox, { toComboBoxOptions } from '../combobox.svelte';
export let filters: SearchCameraFilter;
let makes: string[] = [];
let models: string[] = [];
$: makeFilter = filters.make;
$: modelFilter = filters.model;
$: updateMakes(modelFilter);
$: updateModels(makeFilter);
async function updateMakes(model?: string) {
makes = await getSearchSuggestions({
$type: SearchSuggestionType.CameraMake,
model,
});
}
async function updateModels(make?: string) {
models = await getSearchSuggestions({
$type: SearchSuggestionType.CameraModel,
make,
});
}
</script>

<div id="camera-selection">
@@ -17,21 +42,21 @@
<label class="text-sm text-black dark:text-white" for="search-camera-make">Make</label>
<Combobox
id="search-camera-make"
options={suggestedMakes}
bind:selectedOption={filteredCamera.make}
options={toComboBoxOptions(makes)}
selectedOption={makeFilter ? { label: makeFilter, value: makeFilter } : undefined}
on:select={({ detail }) => (filters.make = detail?.value)}
placeholder="Search camera make..."
on:click={updateSuggestions}
/>
</div>

<div class="w-full">
<label class="text-sm text-black dark:text-white" for="search-camera-model">Model</label>
<Combobox
id="search-camera-model"
options={suggestedModels}
bind:selectedOption={filteredCamera.model}
options={toComboBoxOptions(models)}
selectedOption={modelFilter ? { label: modelFilter, value: modelFilter } : undefined}
on:select={({ detail }) => (filters.model = detail?.value)}
placeholder="Search camera model..."
on:click={updateSuggestions}
/>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -10,23 +10,15 @@
export type SearchFilter = {
context?: string;
personIds: Set<string>;
location: SearchLocationFilter;
camera: {
make?: ComboBoxOption;
model?: ComboBoxOption;
};
camera: SearchCameraFilter;
date: {
takenAfter?: string;
takenBefore?: string;
};
isArchive?: boolean;
isFavorite?: boolean;
isNotInAlbum?: boolean;
mediaType: MediaType;
};
@@ -42,91 +34,51 @@
<script lang="ts">
import Button from '$lib/components/elements/buttons/button.svelte';
import { handleError } from '$lib/utils/handle-error';
import { AssetTypeEnum, type SmartSearchDto, type MetadataSearchDto, SearchSuggestionType } from '@immich/sdk';
import { getSearchSuggestions } from '@immich/sdk';
import { createEventDispatcher, onMount } from 'svelte';
import { AssetTypeEnum, type SmartSearchDto, type MetadataSearchDto } from '@immich/sdk';
import { createEventDispatcher } from 'svelte';
import { fly } from 'svelte/transition';
import { type ComboBoxOption } from '../combobox.svelte';
import SearchPeopleSection from './search-people-section.svelte';
import SearchLocationSection from './search-location-section.svelte';
import SearchCameraSection from './search-camera-section.svelte';
import SearchCameraSection, { type SearchCameraFilter } from './search-camera-section.svelte';
import SearchDateSection from './search-date-section.svelte';
import SearchMediaSection from './search-media-section.svelte';
import { parseUtcDate } from '$lib/utils/date-time';
type SearchSuggestion = {
make: ComboBoxOption[];
model: ComboBoxOption[];
};
export let searchQuery: MetadataSearchDto | SmartSearchDto;
let suggestions: SearchSuggestion = {
make: [],
model: [],
};
const parseOptionalDate = (dateString?: string) => (dateString ? parseUtcDate(dateString) : undefined);
const toStartOfDayDate = (dateString: string) => parseUtcDate(dateString)?.startOf('day').toISODate() || undefined;
const dispatch = createEventDispatcher<{ search: SmartSearchDto | MetadataSearchDto }>();
let filter: SearchFilter = {
context: undefined,
personIds: new Set(),
context: 'query' in searchQuery ? searchQuery.query : '',
personIds: new Set('personIds' in searchQuery ? searchQuery.personIds : []),
location: {
country: undefined,
state: undefined,
city: undefined,
country: searchQuery.country,
state: searchQuery.state,
city: searchQuery.city,
},
camera: {
make: undefined,
model: undefined,
make: searchQuery.make,
model: searchQuery.model,
},
date: {
takenAfter: undefined,
takenBefore: undefined,
takenAfter: searchQuery.takenAfter ? toStartOfDayDate(searchQuery.takenAfter) : undefined,
takenBefore: searchQuery.takenBefore ? toStartOfDayDate(searchQuery.takenBefore) : undefined,
},
isArchive: undefined,
isFavorite: undefined,
isNotInAlbum: undefined,
mediaType: MediaType.All,
isArchive: searchQuery.isArchived,
isFavorite: searchQuery.isFavorite,
isNotInAlbum: 'isNotInAlbum' in searchQuery ? searchQuery.isNotInAlbum : undefined,
mediaType:
searchQuery.type === AssetTypeEnum.Image
? MediaType.Image
: searchQuery.type === AssetTypeEnum.Video
? MediaType.Video
: MediaType.All,
};
const dispatch = createEventDispatcher<{ search: SmartSearchDto | MetadataSearchDto }>();
let filterBoxWidth = 0;
onMount(() => {
updateSuggestions();
populateExistingFilters();
});
const updateSuggestions = async () => {
let data: {
makes: ComboBoxOption[];
models: ComboBoxOption[];
};
try {
const makes = await getSearchSuggestions({
$type: SearchSuggestionType.CameraMake,
model: filter.camera.model?.value,
});
const models = await getSearchSuggestions({
$type: SearchSuggestionType.CameraModel,
make: filter.camera.make?.value,
});
data = {
makes: makes.map<ComboBoxOption>((item) => ({ label: item, value: item })),
models: models.map<ComboBoxOption>((item) => ({ label: item, value: item })),
};
} catch (error) {
handleError(error, 'Failed to get search suggestions');
return;
}
suggestions = {
...suggestions,
make: data.makes,
model: data.models,
};
};
const resetForm = () => {
filter = {
context: undefined,
@@ -151,9 +103,6 @@
};
};
const parseOptionalDate = (dateString?: string) => (dateString ? parseUtcDate(dateString) : undefined);
const toStartOfDayDate = (dateString: string) => parseUtcDate(dateString)?.startOf('day').toISODate() || undefined;
const search = async () => {
let type: AssetTypeEnum | undefined = undefined;
@@ -167,8 +116,8 @@
country: filter.location.country,
state: filter.location.state,
city: filter.location.city,
make: filter.camera.make?.value,
model: filter.camera.model?.value,
make: filter.camera.make,
model: filter.camera.model,
takenAfter: parseOptionalDate(filter.date.takenAfter)?.startOf('day').toISO() || undefined,
takenBefore: parseOptionalDate(filter.date.takenBefore)?.endOf('day').toISO() || undefined,
isArchived: filter.isArchive || undefined,
@@ -195,39 +144,6 @@
dispatch('search', payload);
};
function populateExistingFilters() {
if (searchQuery) {
const personIds = 'personIds' in searchQuery && searchQuery.personIds ? searchQuery.personIds : [];
filter = {
context: 'query' in searchQuery ? searchQuery.query : '',
personIds: new Set(personIds),
location: {
country: searchQuery.country,
state: searchQuery.state,
city: searchQuery.city,
},
camera: {
make: searchQuery.make ? { label: searchQuery.make, value: searchQuery.make } : undefined,
model: searchQuery.model ? { label: searchQuery.model, value: searchQuery.model } : undefined,
},
date: {
takenAfter: searchQuery.takenAfter ? toStartOfDayDate(searchQuery.takenAfter) : undefined,
takenBefore: searchQuery.takenBefore ? toStartOfDayDate(searchQuery.takenBefore) : undefined,
},
isArchive: searchQuery.isArchived,
isFavorite: searchQuery.isFavorite,
isNotInAlbum: 'isNotInAlbum' in searchQuery ? searchQuery.isNotInAlbum : undefined,
mediaType:
searchQuery.type === AssetTypeEnum.Image
? MediaType.Image
: searchQuery.type === AssetTypeEnum.Video
? MediaType.Video
: MediaType.All,
};
}
}
</script>

<div
@@ -264,12 +180,7 @@
<SearchLocationSection bind:filter={filter.location} />

<!-- CAMERA MODEL -->
<SearchCameraSection
suggestedMakes={suggestions.make}
suggestedModels={suggestions.model}
bind:filteredCamera={filter.camera}
{updateSuggestions}
/>
<SearchCameraSection bind:filters={filter.camera} />

<!-- DATE RANGE -->
<SearchDateSection bind:filteredDate={filter.date} />