diff --git a/packages/frontend/src/api/hooks/index.ts b/packages/frontend/src/api/hooks/index.ts
index e5966993..80070d13 100644
--- a/packages/frontend/src/api/hooks/index.ts
+++ b/packages/frontend/src/api/hooks/index.ts
@@ -8,6 +8,7 @@ export * from "./useCookieChoice";
export * from "./useIsMobile";
export * from "./useFeatureFlags";
export * from "./useFilters";
+export * from "./useFilterKeywordSearch";
export * from "./useGATracker";
export * from "./useGlobalSearch";
export * from "./useGlobalHooks";
diff --git a/packages/frontend/src/api/hooks/useFilterKeywordSearch.ts b/packages/frontend/src/api/hooks/useFilterKeywordSearch.ts
new file mode 100644
index 00000000..c30054d0
--- /dev/null
+++ b/packages/frontend/src/api/hooks/useFilterKeywordSearch.ts
@@ -0,0 +1,69 @@
+import { useMemo, useState } from "react";
+import { useGetFilterKeywordsQuery } from "src/api/services";
+import { TFilterKeyword, ESupportedFilters } from "src/api/types";
+
+interface IFilterKeywordSearch {
+ searchState: string;
+ keywordResults: TFilterKeyword[];
+ isFetchingKeywordResults: boolean;
+ setSearchState: (s: string) => void;
+}
+
+/**
+ * useFilterKeywordSearch.
+ * Pretty similar to useKeywordSearch, but instead of consuming the keywords endpoint,
+ * it queries the superfeed/filter_keywords endpoint.
+ * The main difference is that the results are grouped in categories (concept tags, coins, projects, etc).
+ * This hook is used in the UserFilter page.
+ * It is used to search for keywords, display them and add them to the view.
+ *
+ * @returns - The search state and results.
+ */
+export const useFilterKeywordSearch: () => IFilterKeywordSearch = () => {
+ const [searchState, setSearchState] = useState("");
+
+ const { data: keywordsData, isFetching: isFetchingKeywordResults } =
+ useGetFilterKeywordsQuery(
+ {
+ filter_text: searchState,
+ },
+ {
+ skip: searchState === "",
+ }
+ );
+
+ const keywordResults = useMemo(() => {
+ if (!keywordsData) return [];
+ return [
+ ...keywordsData.conceptTags.map((keyword) => ({
+ id: keyword.id,
+ name: keyword.name,
+ slug: keyword.tag.slug,
+ type: ESupportedFilters.ConceptTags,
+ })),
+ ...keywordsData.chains.map((keyword) => ({
+ id: keyword.id,
+ name: keyword.name,
+ slug: keyword.tag.slug,
+ type: ESupportedFilters.Chains,
+ })),
+ ...keywordsData.coins.map((keyword) => ({
+ id: keyword.id,
+ name: keyword.name,
+ slug: keyword.tag.slug,
+ type: ESupportedFilters.Coins,
+ })),
+ ].filter(
+ (item, index, self) =>
+ self.findIndex((innerItem) => innerItem.slug === item.slug) ===
+ index
+ );
+ }, [keywordsData]);
+
+ return {
+ searchState,
+ setSearchState,
+ keywordResults,
+ isFetchingKeywordResults,
+ };
+};
diff --git a/packages/frontend/src/api/services/superfeed/superfeedEndpoints.ts b/packages/frontend/src/api/services/superfeed/superfeedEndpoints.ts
index 9423979d..3be55488 100644
--- a/packages/frontend/src/api/services/superfeed/superfeedEndpoints.ts
+++ b/packages/frontend/src/api/services/superfeed/superfeedEndpoints.ts
@@ -9,6 +9,9 @@ import {
TGetSuperfeedFilterDataRawResponse,
TGetSuperfeedFilterDataResponse,
TGetSuperfeedFilterDataRequest,
+ TGetSuperfeedFilterKeywordsResponse,
+ TGetSuperfeedFilterKeywordsRequest,
+ TGetSuperfeedFilterKeywordsRawResponse,
} from "./types";
const { SUPERFEED } = CONFIG.API.DEFAULT.ROUTES;
@@ -89,8 +92,31 @@ export const superfeedApi = alphadayApi.injectEndpoints({
),
}),
}),
+ getFilterKeywords: builder.query<
+ TGetSuperfeedFilterKeywordsResponse,
+ TGetSuperfeedFilterKeywordsRequest
+ >({
+ query: (req) => {
+ const { ...reqParams } = req;
+ const params: string = queryString.stringify(reqParams);
+ const path = `${SUPERFEED.BASE}${SUPERFEED.FILTER_KEYWORDS}?${params}`;
+ Logger.debug("getSuperfeedFilterKeywords: querying", path);
+ return path;
+ },
+ transformResponse: (
+ r: TGetSuperfeedFilterKeywordsRawResponse
+ ): TGetSuperfeedFilterKeywordsResponse => ({
+ coins: r.coin_keywords,
+ conceptTags: r.concept_keywords,
+ chains: r.chain_keywords,
+ }),
+ }),
}),
overrideExisting: false,
});
-export const { useGetSuperfeedListQuery, useGetFilterDataQuery } = superfeedApi;
+export const {
+ useGetSuperfeedListQuery,
+ useGetFilterDataQuery,
+ useGetFilterKeywordsQuery,
+} = superfeedApi;
diff --git a/packages/frontend/src/api/services/superfeed/types.ts b/packages/frontend/src/api/services/superfeed/types.ts
index 1ba3aa87..98ab19c9 100644
--- a/packages/frontend/src/api/services/superfeed/types.ts
+++ b/packages/frontend/src/api/services/superfeed/types.ts
@@ -44,6 +44,16 @@ export type TTaggedFilterDatum = TBaseFilterItem & {
tags: TBaseFilterTag[];
};
+export type TRemoteFilterKeyword = {
+ id: number;
+ name: string;
+ tag: {
+ id: number;
+ name: string;
+ slug: string;
+ };
+};
+
/**
* Query types
*/
@@ -75,3 +85,17 @@ export type TGetSuperfeedFilterDataResponse = {
coins: TTaggedFilterDatum[];
chains: TTaggedFilterDatum[];
};
+
+export type TGetSuperfeedFilterKeywordsRequest = {
+ filter_text: string;
+};
+export type TGetSuperfeedFilterKeywordsRawResponse = {
+ concept_keywords: TRemoteFilterKeyword[];
+ coin_keywords: TRemoteFilterKeyword[];
+ chain_keywords: TRemoteFilterKeyword[];
+};
+export type TGetSuperfeedFilterKeywordsResponse = {
+ conceptTags: TRemoteFilterKeyword[];
+ coins: TRemoteFilterKeyword[];
+ chains: TRemoteFilterKeyword[];
+};
diff --git a/packages/frontend/src/api/types/superfeed.ts b/packages/frontend/src/api/types/superfeed.ts
index 859a83fa..79c0d911 100644
--- a/packages/frontend/src/api/types/superfeed.ts
+++ b/packages/frontend/src/api/types/superfeed.ts
@@ -42,6 +42,13 @@ export enum ETimeRange {
Last6Months = "last-6-months",
}
+export type TFilterKeyword = {
+ id: number;
+ name: string;
+ slug: string;
+ type: ESupportedFilters;
+};
+
export type TFeedMarketData = {
coin: {
name: string;
diff --git a/packages/frontend/src/config/backend.ts b/packages/frontend/src/config/backend.ts
index 1a947d06..862b6b37 100644
--- a/packages/frontend/src/config/backend.ts
+++ b/packages/frontend/src/config/backend.ts
@@ -123,6 +123,7 @@ const API_V0 = {
BASE: "superfeed",
DEFAULT: "/",
FILTER_DATA: "/filter_data/",
+ FILTER_KEYWORDS: "/filter_keywords/",
},
TVL: {
BASE: "tvl",
diff --git a/packages/frontend/src/mobile-components/FilterSearchBar.tsx b/packages/frontend/src/mobile-components/FilterSearchBar.tsx
index 16141eac..d117c88c 100644
--- a/packages/frontend/src/mobile-components/FilterSearchBar.tsx
+++ b/packages/frontend/src/mobile-components/FilterSearchBar.tsx
@@ -1,23 +1,22 @@
-import { FC } from "react";
import { SearchBar } from "@alphaday/ui-kit";
import { TBaseFilterItem } from "src/api/services";
import { Logger } from "src/api/utils/logging";
type TOption = TBaseFilterItem;
-interface FilterSearchBarProps {
+interface FilterSearchBarProps