Skip to content

Commit

Permalink
specific search
Browse files Browse the repository at this point in the history
  • Loading branch information
Robonau authored and Robonau committed Sep 18, 2024
1 parent 2184cfc commit 5c1b3e2
Show file tree
Hide file tree
Showing 6 changed files with 376 additions and 17 deletions.
5 changes: 2 additions & 3 deletions src/lib/components/Search.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@
</script>

<div
class="max-w-[16rem]
overflow-hidden
transition-all duration-500 ease-in-out
class="relative max-w-[16rem] overflow-hidden transition-all duration-500 ease-in-out
{searchElementHidden ? 'max-w-[0rem] opacity-0 sm:max-w-[1rem]' : ''}"
>
<input
Expand All @@ -63,6 +61,7 @@
on:change={handelChange}
on:keydown={handelEscapeInput}
/>
<slot />
</div>
<TooltipIconButton
on:click={handelSearch}
Expand Down
15 changes: 12 additions & 3 deletions src/lib/gql/Queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,23 @@ export const getCategory = graphql(
lastReadAt
id
}
chapters {
totalCount
}
artist
author
description
genre
status
source {
displayName
id
}
trackRecords {
nodes {
...TrackRecordTypeFragment
}
}
chapters {
totalCount
}
}
}
}
Expand Down
157 changes: 147 additions & 10 deletions src/routes/(app)/(library)/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@
import { selected, selectMode, type MangaType } from './LibraryStores';
import { onMount } from 'svelte';
import { AppBarData } from '$lib/MountTitleAction';
import { gridValues, HelpDoSelect, HelpSelectAll } from '$lib/util';
import {
errortoast,
gridValues,
HelpDoSelect,
HelpSelectAll
} from '$lib/util';
import IconWrapper from '$lib/components/IconWrapper.svelte';
import { parseQuery, type ANO, type parsedQueryType } from './queryParse';
import { getCategories, getCategory } from '$lib/gql/Queries';
import { getContextClient, queryStore } from '@urql/svelte';
Expand Down Expand Up @@ -48,6 +53,61 @@
const query = queryParam('q', ssp.string(), { pushHistory: false });
const tab = queryParam('tab', ssp.number(), { pushHistory: false });
$: [err, parsedQuery] = parseQuery($query);
$: if (err !== null) {
errortoast('Invalid Query', err);
}
$: validateParsedQuery(parsedQuery);
function validateParsedQuery(query: parsedQueryType) {
if (query === null) return;
if (query.length === 0) return;
if (query.length < 2) return;
const anos = query.slice(1) as ANO[];
validateANOs(anos);
}
function validateANOs(anos: ANO[]) {
anos.forEach((e) => {
switch (e.type) {
case 'and':
case 'not':
return validateToBaseData(e.base, e.value.replaceAll('_', ' '));
case 'or':
return validateANOs(e.value);
}
});
}
function validateToBaseData(base: string, compare: string) {
switch (base) {
case 't':
case 'title':
case 'd':
case 'description':
case 'g':
case 'genre':
case 'a':
case 'artist':
case 'au':
case 'author':
case 's':
case 'source':
case 'st':
case 'status':
case '|':
return;
default:
errortoast(
'Invalid Base',
`base: ${base} in string ${base}:${compare}`
);
}
}
$: mangas = queryStore({
client,
query: getCategory,
Expand Down Expand Up @@ -96,11 +156,11 @@
if (!ele.inLibrary) return false;
if ($Meta.ignoreFiltersWhenSearching) {
if (
$query !== '' &&
$query !== null &&
ele.title.toLowerCase().includes($query.toLowerCase())
)
parsedQuery !== null &&
specificSearch(ele, parsedQuery).findIndex((e) => e === false) === -1
) {
return true;
}
}
if ($Meta.Downloaded === 1 && ele.downloadCount === 0) return false;
Expand All @@ -115,15 +175,92 @@
return false;
if (
$query !== '' &&
$query !== null &&
!ele.title.toLowerCase().includes($query.toLowerCase())
parsedQuery !== null &&
specificSearch(ele, parsedQuery.slice(1) as ANO[]).findIndex(
(e) => e === false
) !== -1
)
return false;
return true;
});
function OrSearch(manga: MangaType, query: ANO[]): boolean {
const ind = query.findIndex((e) => e.value === '|');
if (ind === -1)
return specificSearch(manga, query).findIndex((e) => e === false) === -1;
const before = query.slice(0, ind);
const after = query.slice(ind + 1);
return (
specificSearch(manga, before).findIndex((e) => e === false) === -1 ||
OrSearch(manga, after)
);
}
function specificSearch(manga: MangaType, query: ANO[]): boolean[] {
const comparason = query.flatMap((e) => {
switch (e.type) {
case 'and':
return compareToBaseData(manga, e.base, e.value.replaceAll('_', ' '));
case 'not':
return !compareToBaseData(
manga,
e.base,
e.value.replaceAll('_', ' ')
);
case 'or':
return OrSearch(manga, e.value);
}
});
return comparason; //-1 === comparason.findIndex((e) => e === false);
}
function compareToBaseData(
manga: MangaType,
base: string,
compare: string
): boolean {
// base strings: title, description, genre, artist, author, source
switch (base) {
case 't':
case 'title':
return manga.title.toLowerCase().includes(compare.toLowerCase());
case 'd':
case 'description':
return (
manga.description?.toLowerCase().includes(compare.toLowerCase()) ??
false
);
case 'g':
case 'genre':
return manga.genre.some((e) =>
e.toLowerCase().includes(compare.toLowerCase())
);
case 'a':
case 'artist':
return (
manga.artist?.toLowerCase().includes(compare.toLowerCase()) ?? false
);
case 'au':
case 'author':
return (
manga.author?.toLowerCase().includes(compare.toLowerCase()) ?? false
);
case 's':
case 'source':
return (
manga.source?.displayName
?.toLowerCase()
.includes(compare.toLowerCase()) ?? false
);
case 'st':
case 'status':
return manga.status.toLowerCase().includes(compare.toLowerCase());
default:
return true;
}
}
function shuffle<T>(array: T[]) {
var tmp,
current,
Expand Down Expand Up @@ -229,7 +366,7 @@
<svelte:fragment slot="panel">
{#if $mangas.fetching}
<div class="yoy m-2 grid gap-2 {gridValues}">
{#each new Array(orderedCategories.find((e) => e.id === $tab ?? 0)?.mangas.totalCount ?? 10) as _}
{#each new Array(orderedCategories.find((e) => e.id === $tab)?.mangas.totalCount ?? 10) as _}
<div class="aspect-cover w-full">
<div
class="placeholder h-full animate-pulse
Expand Down
64 changes: 64 additions & 0 deletions src/routes/(app)/(library)/LibrarySearchHelp.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<!--
Copyright (c) 2024 Contributors to the Suwayomi project
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->

<script lang="ts">
import ModalTemplate from '$lib/components/ModalTemplate.svelte';
</script>

<ModalTemplate title="Library Specific Search Help">
<p class="whitespace-pre-wrap">
{@html `<h2>Basic Info:</h2>specific search is based on the style of searching that booru sites often support
regular title search and specific search is separated by an "@"
specific search does not respect Escape Characters.
you can't use "@" in search at all
you cant use "{", "}" or ":" in the specific search
<h2>Searching:</h2>
@ title:isekai title:life
title contains both isekai and life
@ -title:isekai
title does not contain isekai
@ {title:isekai title:life | title:space}
title contains either isekai AND life, OR space
<h2>Metatags:</h2>
title:isekai
sorthand t:isekai
search for manga with isekai in the title
description:space
sorthand d:space
search for manga with space in the description
genre:action
sorthand g:action
search for manga with action in the genres
this can be a partial match so "actio" would also work
artist:Kwon
sorthand a:Kwon
search for manga with Kwon in the artist name
author:Ingijagga
sorthand au:Ingijagga
search for manga with Ingijagga in the author name
source:mangadex
sorthand s:mangadex
search for manga with mangadex in the source name
status:completed
sorthand st:completed
search for manga with completed in the status
`}
</p>
</ModalTemplate>
27 changes: 26 additions & 1 deletion src/routes/(app)/(library)/libraryActions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import { getContextClient } from '@urql/svelte';
import { ConditionalChaptersOfGivenManga } from '$lib/gql/Queries';
import { enqueueChapterDownloads, updateMangas } from '$lib/gql/Mutations';
import Tooltip from '$lib/components/Tooltip.svelte';
import LibrarySearchHelp from './LibrarySearchHelp.svelte';
const client = getContextClient();
Expand Down Expand Up @@ -121,6 +123,15 @@
client.mutation(enqueueChapterDownloads, { ids }).toPromise()
);
}
function handelHelp() {
modalStore.trigger({
type: 'component',
backdropClasses: '!p-0',
component: {
ref: LibrarySearchHelp
}
});
}
</script>

<div class="flex h-full max-w-full">
Expand Down Expand Up @@ -192,7 +203,21 @@
name="mdi:{$selectMode ? 'select-multiple' : 'flip-to-front'}"
tip="Select Mode"
/>
<Search />
<Search>
<Tooltip
tip="Help"
class="absolute right-0 top-1/2 -translate-y-1/2"
tipclass="z-70"
placement="left"
>
<button
on:click={handelHelp}
class="variant-ghost-primary btn z-[60] w-0 p-3 leading-[0] opacity-20 hover:opacity-100"
>
?
</button>
</Tooltip>
</Search>
<TooltipIconButton
on:click={handelFilter}
name="mdi:filter"
Expand Down
Loading

0 comments on commit 5c1b3e2

Please sign in to comment.