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

refactor(web): search box #7397

Merged
merged 10 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 16 additions & 20 deletions server/src/domain/search/search.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,26 +195,22 @@ export class SearchService {
}

async getSearchSuggestions(auth: AuthDto, dto: SearchSuggestionRequestDto): Promise<string[]> {
if (dto.type === SearchSuggestionType.COUNTRY) {
return this.metadataRepository.getCountries(auth.user.id);
}

if (dto.type === SearchSuggestionType.STATE) {
return this.metadataRepository.getStates(auth.user.id, dto.country);
}

if (dto.type === SearchSuggestionType.CITY) {
return this.metadataRepository.getCities(auth.user.id, dto.country, dto.state);
}

if (dto.type === SearchSuggestionType.CAMERA_MAKE) {
return this.metadataRepository.getCameraMakes(auth.user.id, dto.model);
}

if (dto.type === SearchSuggestionType.CAMERA_MODEL) {
return this.metadataRepository.getCameraModels(auth.user.id, dto.make);
switch (dto.type) {
case SearchSuggestionType.COUNTRY: {
return this.metadataRepository.getCountries(auth.user.id);
}
case SearchSuggestionType.STATE: {
return this.metadataRepository.getStates(auth.user.id, dto.country);
}
case SearchSuggestionType.CITY: {
return this.metadataRepository.getCities(auth.user.id, dto.country, dto.state);
}
case SearchSuggestionType.CAMERA_MAKE: {
return this.metadataRepository.getCameraMakes(auth.user.id, dto.model);
}
case SearchSuggestionType.CAMERA_MODEL: {
return this.metadataRepository.getCameraModels(auth.user.id, dto.make);
}
}

return [];
}
}
4 changes: 4 additions & 0 deletions web/src/lib/components/shared-components/combobox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
label: string;
value: string;
};

export function toComboBoxOptions(items: string[]) {
return items.map<ComboBoxOption>((item) => ({ label: item, value: item }));
}
</script>

<script lang="ts">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<script lang="ts" context="module">
export interface SearchCameraFilter {
make?: string;
model?: string;
}
</script>

<script lang="ts">
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);
Comment on lines +19 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will have to be updated when #7382 is merged since it's calling an async function without error handling. I see the same in other files below


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">
<p class="immich-form-label">CAMERA</p>

<div class="grid grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-5 mt-1">
<div class="w-full">
<label class="text-sm text-black dark:text-white" for="search-camera-make">Make</label>
<Combobox
id="search-camera-make"
options={toComboBoxOptions(makes)}
selectedOption={makeFilter ? { label: makeFilter, value: makeFilter } : undefined}
on:select={({ detail }) => (filters.make = detail?.value)}
placeholder="Search camera make..."
/>
</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={toComboBoxOptions(models)}
selectedOption={modelFilter ? { label: modelFilter, value: modelFilter } : undefined}
on:select={({ detail }) => (filters.model = detail?.value)}
placeholder="Search camera model..."
/>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<script lang="ts" context="module">
export interface SearchDateFilter {
takenBefore?: string;
takenAfter?: string;
}
</script>

<script lang="ts">
export let filters: SearchDateFilter;
</script>

<div id="date-range-selection" class="grid grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-5">
<label class="immich-form-label" for="start-date">
<span>START DATE</span>
<input
class="immich-form-input w-full mt-1 hover:cursor-pointer"
type="date"
id="start-date"
name="start-date"
max={filters.takenBefore}
bind:value={filters.takenAfter}
/>
</label>

<label class="immich-form-label" for="end-date">
<span>END DATE</span>
<input
class="immich-form-input w-full mt-1 hover:cursor-pointer"
type="date"
id="end-date"
name="end-date"
placeholder=""
min={filters.takenAfter}
bind:value={filters.takenBefore}
/>
</label>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script lang="ts" context="module">
export interface SearchDisplayFilters {
isNotInAlbum?: boolean;
isArchive?: boolean;
isFavorite?: boolean;
}
</script>

<script lang="ts">
export let filters: SearchDisplayFilters;
</script>

<div id="display-options-selection" class="text-sm">
<p class="immich-form-label">DISPLAY OPTIONS</p>

<div class="flex flex-wrap gap-x-5 gap-y-2 mt-1">
<label class="flex items-center gap-2">
<input type="checkbox" class="size-5 flex-shrink-0" bind:checked={filters.isNotInAlbum} />
<span class="pt-1">Not in any album</span>
</label>

<label class="flex items-center gap-2">
<input type="checkbox" class="size-5 flex-shrink-0" bind:checked={filters.isArchive} />
<span class="pt-1">Archive</span>
</label>

<label class="flex items-center gap-2">
<input type="checkbox" class="size-5 flex-shrink-0" bind:checked={filters.isFavorite} />
<span class="pt-1">Favorite</span>
</label>
</div>
</div>