From 87ef2f69a785fc470a1edc7adf0fb877be4c4a9e Mon Sep 17 00:00:00 2001 From: radostin04 Date: Sun, 26 Apr 2026 15:45:43 +0300 Subject: [PATCH 1/2] search: add search page Adds a basic search page that fetches and displays results from Algolia. --- app/routes/search.tsx | 93 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/app/routes/search.tsx b/app/routes/search.tsx index e69de29..08de776 100644 --- a/app/routes/search.tsx +++ b/app/routes/search.tsx @@ -0,0 +1,93 @@ +import { useState, type ChangeEventHandler, type KeyboardEventHandler } from "react"; +import AppCard from "~/components/app_card"; +import type { Application, CollectionOverview } from "~/types/AppstoreApi"; +import type { Route } from "../routes/+types/search"; + +export default function Search({ params }: Route.ComponentProps) { + const searchTagFilters = params.type === "watchapps" ? "(watchapp,companion-app)" : "watchface"; + + const [searchText, setSearchText] = useState(); + const [searchResults, setSearchResults] = useState([]); + + const [searchDebounceTimeout, setSearchDebounceTimeout] = useState(); + + const searchTextChangeHandler: ChangeEventHandler = (event) => { + clearTimeout(searchDebounceTimeout); + setSearchText(event.target.value); + + setSearchDebounceTimeout(setTimeout(sendSearchRequest, 200)); + }; + + const searchTextKeydownHandler: KeyboardEventHandler = (event) => { + switch (event.key) { + case "Enter": + forceStartSearch(); + break; + } + }; + + const forceStartSearch = () => { + clearTimeout(searchDebounceTimeout); + sendSearchRequest(); + }; + + const sendSearchRequest = () => { + if (!searchText) return; + + fetch(`https://7683ow76eq-dsn.algolia.net/1/indexes/rebble-appstore-production/query`, { + method: "POST", + headers: { + "x-algolia-api-key": "252f4938082b8693a8a9fc0157d1d24f", + "x-algolia-application-id": "7683OW76EQ", + }, + body: JSON.stringify({ + params: new URLSearchParams({ + query: searchText, + tagFilters: searchTagFilters, + hitsPerPage: "999", + page: "0", + }).toString(), + }), + }).then(async (res) => { + if (!res.ok) console.error(await res.text()); + const results = await res.json(); + setSearchResults(results.hits); + }); + }; + + return ( +
+ + +
+ {searchResults.map((application) => { + return ; + })} +
+ + +
+ ); +} From 5929a77595332836560ac62e5f72e04e5ca76907 Mon Sep 17 00:00:00 2001 From: radostin04 Date: Sun, 26 Apr 2026 15:46:16 +0300 Subject: [PATCH 2/2] app_card: Implement beginnings of horizontal layout (emulating legacy app search) --- app/components/app_card.tsx | 17 +++++++++++++---- app/routes/watchapps.tsx | 7 +++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/app/components/app_card.tsx b/app/components/app_card.tsx index 300d7b1..b871d47 100644 --- a/app/components/app_card.tsx +++ b/app/components/app_card.tsx @@ -1,13 +1,13 @@ import { NavLink } from 'react-router'; import Heart from "../icons/heart.svg?react"; -import type { ApplicationInfo } from "~/types/Application"; +import type { Application } from '~/types/AppstoreApi'; -export default function AppCard({ info }: { info: ApplicationInfo }) { +export default function AppCard({ info, horizontal }: { info: Application, horizontal?: true }) { const applicationLink = (applicationId: string) => { return `/application/${applicationId}`; }; return ( -
+
@@ -35,7 +35,8 @@ export default function AppCard({ info }: { info: ApplicationInfo }) { } .app-card a .info { text-align: center; - max-width: 100% + max-width: 100%; + flex-grow: 999; } .app-card a .info .title { font-weight: 600; @@ -48,6 +49,14 @@ export default function AppCard({ info }: { info: ApplicationInfo }) { .app-card a .info .hearts { color: var(--as-fg-muted) } + + .app-card.horizontal a { + flex-direction: row; + } + + .app-card.horizontal a .info { + text-align: left; + } `}
); diff --git a/app/routes/watchapps.tsx b/app/routes/watchapps.tsx index fdec8d7..5a320dc 100644 --- a/app/routes/watchapps.tsx +++ b/app/routes/watchapps.tsx @@ -5,6 +5,9 @@ import { mergeMeta } from "../utils/meta" import type { Route } from './+types/watchapps'; import type { CollectionOverview } from '~/types/AppstoreApi'; +/** + * Runs before page load and populates loaderData + */ export async function loader({ params }: Route.LoaderArgs) { const res = await fetch(`https://appstore-api.rebble.io/api/v1/home/apps`); const watchapps = await res.json(); @@ -65,6 +68,10 @@ export default function Watchapps({
{collection.application_ids.slice(0, 6).map((applicationId) => { const application = applicationById(applicationId); + if(!application) { + console.warn(`Application ${applicationId} was in collection.application_ids but as not found in known applications!`) + return <> + } return })}