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

Search: Support special characters (technically breaking) #713

Merged
merged 1 commit into from
Dec 8, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 28 additions & 18 deletions server/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ func (b *BaseRouter) addContentRoutes() {
exp := time.Hour * 24

// Search for content
content.GET("/search/multi/:query", cache.CachePage(b.ms, exp, func(c *gin.Context) {
if c.Param("query") == "" {
content.GET("/search/multi", cache.CachePage(b.ms, exp, func(c *gin.Context) {
query := c.Query("q")
if query == "" {
c.JSON(http.StatusBadRequest, ErrorResponse{Error: "a query was not provided"})
return
}
Expand All @@ -94,7 +95,7 @@ func (b *BaseRouter) addContentRoutes() {
}
pageNum = num
}
content, err := searchContent(c.Param("query"), pageNum)
content, err := searchContent(query, pageNum)
if err != nil {
c.JSON(http.StatusBadRequest, ErrorResponse{Error: err.Error()})
return
Expand All @@ -103,8 +104,9 @@ func (b *BaseRouter) addContentRoutes() {
}))

// Search for movies
content.GET("/search/movie/:query", cache.CachePage(b.ms, exp, func(c *gin.Context) {
if c.Param("query") == "" {
content.GET("/search/movie", cache.CachePage(b.ms, exp, func(c *gin.Context) {
query := c.Query("q")
if query == "" {
c.JSON(http.StatusBadRequest, ErrorResponse{Error: "a query was not provided"})
return
}
Expand All @@ -118,7 +120,7 @@ func (b *BaseRouter) addContentRoutes() {
}
pageNum = num
}
content, err := searchMovies(c.Param("query"), pageNum)
content, err := searchMovies(query, pageNum)
if err != nil {
c.JSON(http.StatusBadRequest, ErrorResponse{Error: err.Error()})
return
Expand All @@ -127,8 +129,9 @@ func (b *BaseRouter) addContentRoutes() {
}))

// Search for shows
content.GET("/search/tv/:query", cache.CachePage(b.ms, exp, func(c *gin.Context) {
if c.Param("query") == "" {
content.GET("/search/tv", cache.CachePage(b.ms, exp, func(c *gin.Context) {
query := c.Query("q")
if query == "" {
c.JSON(http.StatusBadRequest, ErrorResponse{Error: "a query was not provided"})
return
}
Expand All @@ -142,7 +145,7 @@ func (b *BaseRouter) addContentRoutes() {
}
pageNum = num
}
content, err := searchTv(c.Param("query"), pageNum)
content, err := searchTv(query, pageNum)
if err != nil {
c.JSON(http.StatusBadRequest, ErrorResponse{Error: err.Error()})
return
Expand All @@ -151,8 +154,9 @@ func (b *BaseRouter) addContentRoutes() {
}))

// Search for people
content.GET("/search/person/:query", cache.CachePage(b.ms, exp, func(c *gin.Context) {
if c.Param("query") == "" {
content.GET("/search/person", cache.CachePage(b.ms, exp, func(c *gin.Context) {
query := c.Query("q")
if query == "" {
c.JSON(http.StatusBadRequest, ErrorResponse{Error: "a query was not provided"})
return
}
Expand All @@ -166,7 +170,7 @@ func (b *BaseRouter) addContentRoutes() {
}
pageNum = num
}
content, err := searchPeople(c.Param("query"), pageNum)
content, err := searchPeople(query, pageNum)
if err != nil {
c.JSON(http.StatusBadRequest, ErrorResponse{Error: err.Error()})
return
Expand Down Expand Up @@ -389,12 +393,13 @@ func (b *BaseRouter) addGameRoutes() {
}

// Search for games
gamer.GET("/search/:query", cache.CachePage(b.ms, exp, func(c *gin.Context) {
if c.Param("query") == "" {
c.Status(400)
gamer.GET("/search", cache.CachePage(b.ms, exp, func(c *gin.Context) {
query := c.Query("q")
if query == "" {
c.JSON(http.StatusBadRequest, ErrorResponse{Error: "a query was not provided"})
return
}
games, err := igdb.Search(c.Param("query"))
games, err := igdb.Search(query)
if err != nil {
c.JSON(http.StatusBadRequest, ErrorResponse{Error: err.Error()})
return
Expand Down Expand Up @@ -946,9 +951,14 @@ func (b *BaseRouter) addUserRoutes() {
})

// Search users
u.GET("/search/:query", func(c *gin.Context) {
u.GET("/search", func(c *gin.Context) {
userId := c.MustGet("userId").(uint)
response, err := userSearch(b.db, userId, c.Param("query"))
query := c.Query("q")
if query == "" {
c.JSON(http.StatusBadRequest, ErrorResponse{Error: "a query was not provided"})
return
}
response, err := userSearch(b.db, userId, query)
if err != nil {
c.JSON(http.StatusForbidden, ErrorResponse{Error: err.Error()})
return
Expand Down
2 changes: 1 addition & 1 deletion src/lib/nav/DetailedMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
$: dve = $wlDetailedView;
</script>

<div class={`menu${$page.url?.pathname.startsWith("/search/") ? " on-search-page" : ""}`}>
<div class={`menu${$page.url?.pathname.startsWith("/search") ? " on-search-page" : ""}`}>
<div class="inner">
<h4 class="norm sm-caps">Shown Details</h4>
<button
Expand Down
2 changes: 1 addition & 1 deletion src/lib/poster/ExtraDetails.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
}
</script>

{#if ($page.url?.pathname === "/" || $page.url?.pathname.startsWith("/search/")) && details && dve && dve.length > 0}
{#if ($page.url?.pathname === "/" || $page.url?.pathname.startsWith("/search")) && details && dve && dve.length > 0}
<div class="extra-details">
<!--
The `if` statements can't be on their own line to look pretty
Expand Down
4 changes: 2 additions & 2 deletions src/routes/(app)/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
// Using autofocus seems to work. Disables after goto runs.
// https://github.com/sbondCo/Watcharr/issues/169
target.autofocus = true;
goto(`/search/${query}`).then(() => {
goto(`/search?q=${encodeURIComponent(query)}`).then(() => {
// Use mainSearchEl if nav not split, otherwise use ev target.
if (!document.body.classList.contains("split-nav")) {
mainSearchEl?.focus();
Expand Down Expand Up @@ -252,7 +252,7 @@
</div>
<div class="btns">
<!-- Detailed posters only supported on own watched list currently -->
{#if $page.url?.pathname === "/" || $page.url?.pathname.startsWith("/search/")}
{#if $page.url?.pathname === "/" || $page.url?.pathname.startsWith("/search")}
<button
class="plain other detailedView"
on:click={() => {
Expand Down
2 changes: 1 addition & 1 deletion src/routes/(app)/game/[id]/+page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { error } from "@sveltejs/kit";
import type { PageLoad } from "../../search/[query]/$types";
import type { PageLoad } from "./$types";

export const load = (async ({ params }) => {
const { id } = params;
Expand Down
2 changes: 1 addition & 1 deletion src/routes/(app)/movie/[id]/+page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { error } from "@sveltejs/kit";
import type { PageLoad } from "../../search/[query]/$types";
import type { PageLoad } from "./$types";

export const load = (async ({ params }) => {
const { id } = params;
Expand Down
2 changes: 1 addition & 1 deletion src/routes/(app)/person/[id]/+page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { error } from "@sveltejs/kit";
import type { PageLoad } from "../../search/[query]/$types";
import type { PageLoad } from "./$types";

export const load = (async ({ params }) => {
const { id } = params;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { searchQuery, serverFeatures, watchedList } from "@/store";
import PageError from "@/lib/PageError.svelte";
import Spinner from "@/lib/Spinner.svelte";
import axios, { type GenericAbortSignal } from "axios";
import axios from "axios";
import { getWatchedDependedProps, getPlayedDependedProps } from "@/lib/util/helpers";
import PersonPoster from "@/lib/poster/PersonPoster.svelte";
import type {
Expand Down Expand Up @@ -49,17 +49,19 @@
const infiniteScrollThreshold = 150;
let reqController = new AbortController();

$: query = data?.query;
$: searchQ = $searchQuery;
$: wList = $watchedList;

async function searchMovies(query: string, page: number) {
try {
const movies = await axios.get<MoviesSearchResponse>(
`/content/search/movie/${query}?page=${page}`,
{
signal: reqController.signal
}
);
const movies = await axios.get<MoviesSearchResponse>(`/content/search/movie`, {
params: {
q: encodeURIComponent(query),
page
},
signal: reqController.signal
});
return movies;
} catch (err) {
console.error("Movies search failed!", err);
Expand All @@ -69,12 +71,13 @@

async function searchTv(query: string, page: number) {
try {
const shows = await axios.get<ShowsSearchResponse>(
`/content/search/tv/${query}?page=${page}`,
{
signal: reqController.signal
}
);
const shows = await axios.get<ShowsSearchResponse>(`/content/search/tv`, {
params: {
q: encodeURIComponent(query),
page
},
signal: reqController.signal
});
return shows;
} catch (err) {
console.error("Tv search failed!", err);
Expand All @@ -84,12 +87,13 @@

async function searchPeople(query: string, page: number) {
try {
const people = await axios.get<PeopleSearchResponse>(
`/content/search/person/${query}?page=${page}`,
{
signal: reqController.signal
}
);
const people = await axios.get<PeopleSearchResponse>(`/content/search/person`, {
params: {
q: encodeURIComponent(query),
page
},
signal: reqController.signal
});
return people;
} catch (err) {
console.error("People search failed!", err);
Expand All @@ -99,7 +103,11 @@

async function searchMulti(query: string, page: number) {
try {
return await axios.get<ContentSearch>(`/content/search/multi/${query}?page=${page}`, {
return await axios.get<ContentSearch>(`/content/search/multi`, {
params: {
q: encodeURIComponent(query),
page
},
signal: reqController.signal
});
} catch (err) {
Expand All @@ -120,7 +128,11 @@
console.debug("game search is not enabled on this server");
return { data: [] };
}
const games = await axios.get<GameSearch[]>(`/game/search/${query}`, {
const games = await axios.get<GameSearch[]>(`/game/search`, {
params: {
q: encodeURIComponent(query),
page
},
signal: reqController.signal
});
return {
Expand Down Expand Up @@ -321,19 +333,19 @@
}

async function doCleanSearch() {
if (!data.slug) {
if (!query) {
console.error("doCleanSearch: No query to use.");
return;
}
console.debug("doCleanSearch()");
curPage = 0;
allSearchResults = [];
searchResults = [];
search(data.slug);
search(query);
}

async function searchUsers(query: string) {
return (await axios.get(`/user/search/${query}`)).data as PublicUser[];
return (await axios.get(`/user/search`, { params: { q: query } })).data as PublicUser[];
}

function setActiveSearchFilter(to: SearchFilterTypes) {
Expand All @@ -356,15 +368,15 @@
) {
console.log("reached end");
window.removeEventListener("scroll", infiniteScroll);
if (data.slug) await search(data.slug);
if (query) await search(query);
window.addEventListener("scroll", infiniteScroll);
console.debug(`Page: ${curPage} / ${maxContentPage}`);
}
}

onMount(() => {
if (!searchQ && data.slug) {
searchQuery.set(data.slug);
if (!searchQ && query) {
searchQuery.set(query);
}
doCleanSearch();

Expand All @@ -378,7 +390,7 @@
});

afterNavigate((e) => {
if (!e.from?.route?.id?.toLowerCase()?.includes("/search/")) {
if (!e.from?.route?.id?.toLowerCase()?.includes("/search")) {
// AfterNavigate will also be called when this page is mounted,
// but that won't work for us since the OnMount hook also runs
// a clean search, which can cause errors when both ran at same
Expand All @@ -388,7 +400,7 @@
// use that. The only alternative to only run this hook after a
// navigation on the search page (query change), seems to be
// checking the `from` property an making sure it's from the
// `/search/` route already.
// `/search` route already.
return;
}
console.log("Query changed (or just loaded first query), performing search");
Expand All @@ -405,15 +417,14 @@
</script>

<svelte:head>
<title>Search Results{data?.slug ? ` for '${data?.slug}'` : ""}</title>
<title>Search Results{query ? ` for '${query}'` : ""}</title>
</svelte:head>

<!-- <span style="position: sticky;top: 70px;">{curPage} / {maxContentPage}</span> -->

<div class="content">
<div class="inner">
{#if data.slug}
{#await searchUsers(data.slug) then results}
{#if query}
{#await searchUsers(query) then results}
{#if results?.length > 0}
<UsersList users={results} />
{/if}
Expand Down Expand Up @@ -496,7 +507,7 @@
error={contentSearchErr}
onRetry={() => {
contentSearchErr = undefined;
search(data.slug);
search(query);
}}
/>
</div>
Expand Down
5 changes: 5 additions & 0 deletions src/routes/(app)/search/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export async function load({ url }) {
return {
query: url.searchParams.get("q")
};
}
14 changes: 0 additions & 14 deletions src/routes/(app)/search/[query]/+page.ts

This file was deleted.

Loading