diff --git a/src/app/api/getFinalityProviders.ts b/src/app/api/getFinalityProviders.ts
index 6d444d4e..2dd30538 100644
--- a/src/app/api/getFinalityProviders.ts
+++ b/src/app/api/getFinalityProviders.ts
@@ -19,6 +19,7 @@ interface FinalityProvidersAPIResponse {
interface FinalityProviderAPI {
description: DescriptionAPI;
+ state: "active" | "standby";
commission: string;
btc_pk: string;
active_tvl: number;
@@ -35,14 +36,28 @@ interface DescriptionAPI {
details: string;
}
-export const getFinalityProviders = async (
- key: string,
-): Promise => {
+export const getFinalityProviders = async ({
+ key,
+ pk,
+ sortBy,
+ order,
+ name,
+}: {
+ key: string;
+ name?: string;
+ sortBy?: string;
+ order?: "asc" | "desc";
+ pk?: string;
+}): Promise => {
// const limit = 100;
// const reverse = false;
const params = {
pagination_key: encode(key),
+ finality_provider_pk: pk,
+ sort_by: sortBy,
+ order,
+ name,
// "pagination_reverse": reverse,
// "pagination_limit": limit,
};
@@ -70,6 +85,7 @@ export const getFinalityProviders = async (
securityContact: fp.description.security_contact,
details: fp.description.details,
},
+ state: fp.state,
commission: fp.commission,
btcPk: fp.btc_pk,
activeTVLSat: fp.active_tvl,
diff --git a/src/app/components/Staking/FinalityProviders/FinalityProvider.tsx b/src/app/components/Staking/FinalityProviders/FinalityProvider.tsx
index 74fb92ed..425c1295 100644
--- a/src/app/components/Staking/FinalityProviders/FinalityProvider.tsx
+++ b/src/app/components/Staking/FinalityProviders/FinalityProvider.tsx
@@ -13,6 +13,7 @@ import { maxDecimals } from "@/utils/maxDecimals";
interface FinalityProviderProps {
moniker: string;
pkHex: string;
+ state: "active" | "standby";
stakeSat: number;
commission: string;
onClick: () => void;
@@ -22,6 +23,7 @@ interface FinalityProviderProps {
export const FinalityProvider: React.FC = ({
moniker,
+ state = "active",
pkHex,
stakeSat,
commission,
@@ -127,6 +129,9 @@ export const FinalityProvider: React.FC = ({
className="tooltip-wrap"
/>
+
+ Status: {state}
+
);
diff --git a/src/app/components/Staking/FinalityProviders/FinalityProviderSearch.tsx b/src/app/components/Staking/FinalityProviders/FinalityProviderSearch.tsx
index 65f3f9b2..941e07d2 100644
--- a/src/app/components/Staking/FinalityProviders/FinalityProviderSearch.tsx
+++ b/src/app/components/Staking/FinalityProviders/FinalityProviderSearch.tsx
@@ -1,23 +1,17 @@
-import { useDebounce } from "@uidotdev/usehooks";
-import React, { useEffect, useState } from "react";
+import React from "react";
import { FiSearch } from "react-icons/fi";
interface FinalityProviderSearchProps {
+ searchValue: string;
onSearch: (searchTerm: string) => void;
}
export const FinalityProviderSearch: React.FC = ({
+ searchValue,
onSearch,
}) => {
- const [searchTerm, setSearchTerm] = useState("");
- const debouncedSearchTerm = useDebounce(searchTerm, 300);
-
- useEffect(() => {
- onSearch(debouncedSearchTerm);
- }, [debouncedSearchTerm, onSearch]);
-
const handleSearch = (e: React.ChangeEvent) => {
- setSearchTerm(e.target.value);
+ onSearch(e.target.value);
};
return (
@@ -29,7 +23,7 @@ export const FinalityProviderSearch: React.FC = ({
diff --git a/src/app/components/Staking/FinalityProviders/FinalityProviders.tsx b/src/app/components/Staking/FinalityProviders/FinalityProviders.tsx
index 58d4339b..bbfcb0cc 100644
--- a/src/app/components/Staking/FinalityProviders/FinalityProviders.tsx
+++ b/src/app/components/Staking/FinalityProviders/FinalityProviders.tsx
@@ -1,102 +1,44 @@
-import { useInfiniteQuery } from "@tanstack/react-query";
import { useEffect } from "react";
import InfiniteScroll from "react-infinite-scroll-component";
-import {
- PaginatedFinalityProviders,
- getFinalityProviders,
-} from "@/app/api/getFinalityProviders";
import {
LoadingTableList,
LoadingView,
} from "@/app/components/Loading/Loading";
-import { useError } from "@/app/context/Error/ErrorContext";
-import { useFinalityProvidersData } from "@/app/hooks/finalityProviders/useFinalityProvidersData";
-import { ErrorState } from "@/app/types/errors";
-import { FinalityProvidersProps } from "@/app/types/finalityProviders";
+import { useFinalityProviderService } from "@/app/hooks/services/useFinalityProviderService";
+import type { FinalityProvider as FinalityProviderInterface } from "@/app/types/finalityProviders";
import { FinalityProvider } from "./FinalityProvider";
import { FinalityProviderSearch } from "./FinalityProviderSearch";
+export interface FinalityProvidersProps {
+ onFinalityProvidersLoad: (data: FinalityProviderInterface[]) => void;
+ selectedFinalityProvider: FinalityProviderInterface | undefined;
+ onFinalityProviderChange: (btcPkHex: string) => void;
+}
+
export const FinalityProviders: React.FC = ({
selectedFinalityProvider,
onFinalityProviderChange,
onFinalityProvidersLoad,
}) => {
- const { isErrorOpen, showError, handleError } = useError();
const {
- data: fps,
- fetchNextPage: finalityProvidersFetchNext,
- hasNextPage: finalityProvidersHasNext,
- isFetchingNextPage: finalityProvidersIsFetchingMore,
- error: finalityProvidersError,
- isError: hasFinalityProvidersError,
- refetch: refetchFinalityProvidersData,
- isRefetchError: isRefetchFinalityProvidersError,
- } = useInfiniteQuery({
- queryKey: ["finality providers"],
- queryFn: ({ pageParam = "" }) => getFinalityProviders(pageParam),
- getNextPageParam: (lastPage) =>
- lastPage?.pagination?.next_key !== ""
- ? lastPage?.pagination?.next_key
- : null,
- initialPageParam: "",
- refetchInterval: 60000, // 1 minute
- select: (data) => {
- const flattenedData = data.pages.reduce(
- (acc, page) => {
- acc.finalityProviders.push(...page.finalityProviders);
- acc.pagination = page.pagination;
- return acc;
- },
- { finalityProviders: [], pagination: { next_key: "" } },
- );
- return flattenedData;
- },
- retry: (failureCount) => {
- return !isErrorOpen && failureCount <= 3;
- },
- });
-
- useEffect(() => {
- fps?.finalityProviders && onFinalityProvidersLoad(fps.finalityProviders);
- }, [fps, onFinalityProvidersLoad]);
+ isLoading,
+ finalityProviders,
+ searchValue,
+ hasNextPage,
+ fetchNextPage,
+ handleSearch,
+ handleSort,
+ } = useFinalityProviderService();
useEffect(() => {
- if (
- finalityProvidersHasNext &&
- finalityProvidersFetchNext &&
- !finalityProvidersIsFetchingMore
- ) {
- finalityProvidersFetchNext();
+ if (finalityProviders) {
+ onFinalityProvidersLoad(finalityProviders);
}
- }, [
- finalityProvidersHasNext,
- finalityProvidersFetchNext,
- finalityProvidersIsFetchingMore,
- ]);
-
- useEffect(() => {
- handleError({
- error: finalityProvidersError,
- hasError: hasFinalityProvidersError,
- errorState: ErrorState.SERVER_ERROR,
- refetchFunction: refetchFinalityProvidersData,
- });
- }, [
- hasFinalityProvidersError,
- isRefetchFinalityProvidersError,
- finalityProvidersError,
- refetchFinalityProvidersData,
- showError,
- handleError,
- ]);
-
- const { handleSearch, filteredProviders } = useFinalityProvidersData(
- fps?.finalityProviders,
- );
+ }, [finalityProviders, onFinalityProvidersLoad]);
- if (!fps?.finalityProviders?.length) {
+ if (!finalityProviders?.length) {
return ;
}
@@ -106,13 +48,23 @@ export const FinalityProviders: React.FC = ({
Step-1: Select a finality provider
-
+
-
Finality Provider
-
BTC PK
-
Total Delegation
-
Commission
+
handleSort("name")}>
+ Finality Provider
+
+
BTC PK
+
handleSort("active_tvl")}>
+ Total Delegation
+
+
handleSort("commission")}>
+ Commission
+
+
Status
= ({
>
: null}
+ dataLength={finalityProviders?.length || 0}
+ next={fetchNextPage}
+ hasMore={hasNextPage}
+ loader={isLoading ?
: null}
scrollableTarget="finality-providers"
>
- {filteredProviders?.map((fp) => (
+ {finalityProviders?.map((fp) => (
+ getFinalityProviders({ key: pageParam, pk, sortBy, order, name }),
+ getNextPageParam: (lastPage) =>
+ lastPage?.pagination?.next_key !== ""
+ ? lastPage?.pagination?.next_key
+ : null,
+ initialPageParam: "",
+ refetchInterval: ONE_MINUTE,
+ placeholderData: (prev) => prev,
+ select: (data) => {
+ const flattenedData = data.pages.reduce(
+ (acc, page) => {
+ acc.finalityProviders.push(...page.finalityProviders);
+ acc.pagination = page.pagination;
+ return acc;
+ },
+ { finalityProviders: [], pagination: { next_key: "" } },
+ );
+ return flattenedData;
+ },
+ retry: (failureCount) => {
+ return !isErrorOpen && failureCount <= 3;
+ },
+ });
+
+ useEffect(() => {
+ handleError({
+ error: query.error,
+ hasError: query.isError,
+ errorState: ErrorState.SERVER_ERROR,
+ refetchFunction: query.refetch,
+ });
+ }, [query.isError, query.error, query.refetch, handleError]);
+
+ return query;
+}
diff --git a/src/app/hooks/services/useFinalityProviderService.ts b/src/app/hooks/services/useFinalityProviderService.ts
new file mode 100644
index 00000000..16b7ff6b
--- /dev/null
+++ b/src/app/hooks/services/useFinalityProviderService.ts
@@ -0,0 +1,56 @@
+import { useDebounce } from "@uidotdev/usehooks";
+import { useCallback, useState } from "react";
+
+import { useFinalityProviders } from "@/app/hooks/api/useFinalityProviders";
+
+interface SortState {
+ field?: string;
+ direction?: "asc" | "desc";
+}
+
+const SORT_DIRECTIONS = {
+ undefined: "desc",
+ desc: "asc",
+ asc: undefined,
+} as const;
+
+export function useFinalityProviderService() {
+ const [searchValue, setSearchValue] = useState("");
+ const [sortState, setSortState] = useState({});
+ const name = useDebounce(searchValue, 300);
+
+ const { data, hasNextPage, isFetchingNextPage, fetchNextPage } =
+ useFinalityProviders({
+ sortBy: sortState.field,
+ order: sortState.direction,
+ name,
+ });
+
+ const handleSearch = useCallback((searchTerm: string) => {
+ setSearchValue(searchTerm);
+ }, []);
+
+ const handleSort = useCallback((sortField: string) => {
+ setSortState(({ field, direction }) =>
+ field === sortField
+ ? {
+ field: SORT_DIRECTIONS[`${direction}`] ? field : undefined,
+ direction: SORT_DIRECTIONS[`${direction}`],
+ }
+ : {
+ field: sortField,
+ direction: "desc",
+ },
+ );
+ }, []);
+
+ return {
+ searchValue,
+ finalityProviders: data?.finalityProviders,
+ hasNextPage,
+ isLoading: isFetchingNextPage,
+ fetchNextPage,
+ handleSearch,
+ handleSort,
+ };
+}
diff --git a/src/app/types/finalityProviders.ts b/src/app/types/finalityProviders.ts
index c1bb1b3a..244e3d0c 100644
--- a/src/app/types/finalityProviders.ts
+++ b/src/app/types/finalityProviders.ts
@@ -1,7 +1,6 @@
-import { Dispatch, SetStateAction } from "react";
-
export interface FinalityProvider {
description: Description;
+ state: "active" | "standby";
commission: string;
btcPk: string;
activeTVLSat: number;
@@ -17,11 +16,3 @@ export interface Description {
securityContact: string;
details: string;
}
-
-export interface FinalityProvidersProps {
- onFinalityProvidersLoad: Dispatch<
- SetStateAction
- >;
- selectedFinalityProvider: FinalityProvider | undefined;
- onFinalityProviderChange: (btcPkHex: string) => void;
-}
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 6db2fdb4..b6d5373d 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -23,7 +23,7 @@ const config: Config = {
},
gridTemplateColumns: {
stakingFinalityProvidersMobile: "2fr 1fr",
- stakingFinalityProvidersDesktop: "2fr 1.5fr 2fr 0.75fr",
+ stakingFinalityProvidersDesktop: "2fr 1.5fr 2fr 0.75fr 0.75fr",
},
},
},