feat+refactor: Funcionalidad de busqueda de hashtags

This commit is contained in:
2026-01-01 21:38:32 -03:00
parent 7de6125eb8
commit c815f0b291
2 changed files with 62 additions and 30 deletions

View File

@@ -18,12 +18,12 @@
import AvatarImage from '@/components/ui/avatar/avatar-image.svelte'; import AvatarImage from '@/components/ui/avatar/avatar-image.svelte';
import Label from '@/components/ui/label/label.svelte'; import Label from '@/components/ui/label/label.svelte';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import { busquedaHashtags } from '@/hooks/busquedaHashtags';
let search: string = $state(''); let search: string = $state('');
let open = $state(false); let open = $state(false);
let usuarios: UserResponseDto[] = $state([]); let usuarios: Promise<UserResponseDto[]> | undefined = $state();
let loading = $state(false);
let hashtags: Promise<any[]> | undefined = $state(); let hashtags: Promise<any[]> | undefined = $state();
@@ -33,18 +33,17 @@
open = !open; open = !open;
} }
} }
$inspect(usuarios, loading); // $inspect(usuarios, loading);
let timeoutId: number | undefined; let timeoutId: number | undefined;
function buscar() { function buscar() {
loading = true;
if (timeoutId) { if (timeoutId) {
clearTimeout(timeoutId); clearTimeout(timeoutId);
} }
timeoutId = setTimeout(async () => { timeoutId = setTimeout(async () => {
usuarios = await busquedaUsuarios(search); usuarios = busquedaUsuarios(search);
loading = false; hashtags = busquedaHashtags(search);
}, 200); }, 200);
return () => { return () => {
@@ -55,13 +54,14 @@
<svelte:document onkeydown={handleKeydown} /> <svelte:document onkeydown={handleKeydown} />
<InputGroup> <InputGroup class="group">
<InputGroupAddon align="inline-start"><Search /></InputGroupAddon> <InputGroupAddon align="inline-start"><Search /></InputGroupAddon>
<InputGroupInput <InputGroupInput
type="text" type="text"
placeholder="Buscar Usuario o Hashtag" placeholder="Buscar Usuario o Hashtag"
bind:value={search} bind:value={search}
oninput={() => (open = true)} oninput={() => (open = true)}
class="max-w-0 transition-[max-width] duration-1000 ease-out group-hover:max-w-xs focus:max-w-xs"
/> />
<InputGroupAddon align="inline-end" class="flex gap-0"> <InputGroupAddon align="inline-end" class="flex gap-0">
<Kbd>Ctrl</Kbd>+<Kbd>K</Kbd> <Kbd>Ctrl</Kbd>+<Kbd>K</Kbd>
@@ -75,36 +75,50 @@
oninput={buscar} oninput={buscar}
/> />
{#if search} {#if search}
<ul class="space-y-2"> <ul class="m-2 space-y-2">
<Label class="ms-3 text-sm font-medium text-muted-foreground">Usuarios</Label> <Label class="ms-3 text-sm font-medium text-muted-foreground">Usuarios</Label>
{#each usuarios as usuario} {#await usuarios}
<li class=" w-full cursor-pointer rounded-md hover:bg-accent"> <li>
<a <Spinner class="ms-2" />
href={resolve('/[perfil]', { perfil: usuario.username })}
class="flex items-center gap-2 px-3 py-2"
>
{#if usuario.imageUrl}
<img src={usuario.imageUrl} alt={usuario.username} class="h-5 w-5 rounded-full" />
{:else}
<User class="h-5 w-5" />
{/if}
<span>{usuario.username}</span>
</a>
</li> </li>
{/each} {:then usuariosr}
{#if usuariosr?.length == 0}
<li><p class="ms-2 text-sm">No se encontraron Usuarios</p></li>
{/if}
{#each usuariosr as usuario}
<li class=" w-full cursor-pointer rounded-md hover:bg-accent">
<a
href={resolve('/[perfil]', { perfil: usuario.username })}
class="flex items-center gap-2 px-3 py-2"
>
{#if usuario.imageUrl}
<img src={usuario.imageUrl} alt={usuario.username} class="h-5 w-5 rounded-full" />
{:else}
<User class="h-5 w-5" />
{/if}
<span>{usuario.username}</span>
</a>
</li>
{/each}
{/await}
<Label class="ms-3 text-sm font-medium text-muted-foreground">Hashtag</Label> <Label class="ms-3 text-sm font-medium text-muted-foreground">Hashtag</Label>
{#await hashtags} {#await hashtags}
<li class="flex items-center gap-2 px-3 py-2 text-muted-foreground"> <li>
<Spinner class="h-5 w-5" /> <Spinner class="ms-2" />
<span>Cargando …</span>
</li> </li>
{:then hashtagsResult} {:then hashtagsResult}
{#each hashtagsResult as hashtag} {#if hashtagsResult?.length == 0}
<li class="flex cursor-pointer items-center gap-2 rounded-md px-3 py-2 hover:bg-accent"> <li>
<Hash class="h-5 w-5" /> <p class="ms-2 text-sm">No se encontraron Hashtags</p>
<span>{hashtag.name}</span>
</li> </li>
{/if}
{#each hashtagsResult as hashtag}
<a href={resolve('/htag/[htag]', { htag: hashtag })}>
<li class="flex cursor-pointer items-center gap-2 rounded-md px-3 py-2 hover:bg-accent">
<Hash class="h-5 w-5" />
<span>{hashtag}</span>
</li>
</a>
{/each} {/each}
{/await} {/await}
</ul> </ul>

View File

@@ -0,0 +1,18 @@
import { apiBase } from '@/stores/url';
import { get } from 'svelte/store';
export async function busquedaHashtags(htag: string) {
if (!htag) return null;
try {
const req = await fetch(`${get(apiBase)}/api/htag?q=${htag}`, {
method: 'GET'
});
if (req.ok) {
let data = await req.json();
return data;
}
return [];
} catch {
return null;
}
}