Skip to content

Commit

Permalink
shuffle fp list
Browse files Browse the repository at this point in the history
- Refactor fp state management to shuffle the list on fetch, this update make sure when refetch it doesn't re-shuffle the list
  • Loading branch information
jeremy-babylonlabs committed Dec 3, 2024
1 parent 54ea2e9 commit 5d992ff
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 44 deletions.
7 changes: 4 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "simple-staking",
"version": "0.3.14",
"version": "0.3.15",
"private": true,
"scripts": {
"dev": "next dev",
Expand All @@ -25,6 +25,7 @@
"node": "22.3.0"
},
"dependencies": {
"@babylonlabs-io/btc-staking-ts": "0.3.0",
"@bitcoin-js/tiny-secp256k1-asmjs": "2.2.3",
"@bitcoinerlab/secp256k1": "^1.1.1",
"@keystonehq/animated-qr": "^0.8.6",
Expand All @@ -36,7 +37,6 @@
"@uidotdev/usehooks": "^2.4.1",
"axios": "^1.7.4",
"bitcoinjs-lib": "6.1.5",
"@babylonlabs-io/btc-staking-ts": "0.3.0",
"date-fns": "^3.6.0",
"decimal.js-light": "^2.5.1",
"framer-motion": "^11.1.9",
Expand Down
7 changes: 0 additions & 7 deletions src/app/api/getFinalityProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,9 @@ interface DescriptionAPI {

export const getFinalityProviders = async (
key: string,
sort: string,
): Promise<PaginatedFinalityProviders> => {
// const limit = 100;
// const reverse = false;

const params = {
pagination_key: encode(key),
sort,
// "pagination_reverse": reverse,
// "pagination_limit": limit,
};

const response = await apiWrapper(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ export const FinalityProviders: React.FC<FinalityProvidersProps> = ({
fetchNextPage: finalityProvidersFetchNext,
hasNextPage: finalityProvidersHasNext,
isFetchingNextPage: finalityProvidersIsFetchingMore,
isFetching: isFetchingFinalityProviders,
isFetched: isFetchedFinalityProviders,
error: finalityProvidersError,
isError: hasFinalityProvidersError,
refetch: refetchFinalityProvidersData,
isRefetchError: isRefetchFinalityProvidersError,
} = useInfiniteQuery({
queryKey: ["finality providers"],
queryFn: ({ pageParam = "" }) => getFinalityProviders(pageParam, "random"),
queryFn: ({ pageParam = "" }) => getFinalityProviders(pageParam),
getNextPageParam: (lastPage) =>
lastPage?.pagination?.next_key !== ""
? lastPage?.pagination?.next_key
Expand Down Expand Up @@ -92,8 +94,10 @@ export const FinalityProviders: React.FC<FinalityProvidersProps> = ({
handleError,
]);

const { handleSearch, filteredProviders } = useFinalityProvidersData(
const { handleSearch, filteredFinalityProviders } = useFinalityProvidersData(
fps?.finalityProviders,
isFetchingFinalityProviders,
isFetchedFinalityProviders,
);

if (!fps?.finalityProviders?.length) {
Expand All @@ -120,13 +124,13 @@ export const FinalityProviders: React.FC<FinalityProvidersProps> = ({
>
<InfiniteScroll
className="flex flex-col gap-4"
dataLength={filteredProviders?.length || 0}
dataLength={filteredFinalityProviders?.length || 0}
next={finalityProvidersFetchNext}
hasMore={finalityProvidersHasNext}
loader={finalityProvidersIsFetchingMore ? <LoadingTableList /> : null}
scrollableTarget="finality-providers"
>
{filteredProviders?.map((fp) => (
{filteredFinalityProviders?.map((fp) => (
<FinalityProvider
key={fp.btcPk}
moniker={fp.description?.moniker}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const UnbondingDisabledBanner: FC<UnbondingDisabledBannerProps> = () => {
</strong>{" "}
<span className="block text-xs md:text-sm md:inline">
To support a smooth transition of the {network} to Phase-2,
on-demand unbonding has been disabled until the phase-2 {network}
on-demand unbonding has been disabled until the phase-2 {network}{" "}
launch.
</span>
</p>
Expand Down
117 changes: 95 additions & 22 deletions src/app/hooks/finalityProviders/useFinalityProvidersData.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,106 @@
import { useCallback, useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";

import { FinalityProvider as FinalityProviderInterface } from "@/app/types/finalityProviders";
import { FinalityProvider } from "@/app/types/finalityProviders";

export const useFinalityProvidersData = (
initialProviders: FinalityProviderInterface[] | undefined,
initialProviders: FinalityProvider[] | undefined,
isRefetchingFinalityProviders: boolean,
isFetchedFinalityProviders: boolean,
) => {
const [filteredProviders, setFilteredProviders] = useState(initialProviders);
const finalityProvidersRef = useRef<FinalityProvider[] | undefined>(
undefined,
);
const [filteredFinalityProviders, setFilteredFinalityProviders] = useState<
FinalityProvider[] | undefined
>(undefined);
const [searchTerm, setSearchTerm] = useState("");

useEffect(() => {
setFilteredProviders(initialProviders);
}, [initialProviders]);

const handleSearch = useCallback(
(searchTerm: string) => {
const filtered = initialProviders?.filter(
(fp) =>
fp.description?.moniker
?.toLowerCase()
.includes(searchTerm.toLowerCase()) ||
fp.btcPk.toLowerCase().includes(searchTerm.toLowerCase()),
);
setFilteredProviders(filtered);
},
[initialProviders],
);
if (
!initialProviders ||
isRefetchingFinalityProviders ||
!isFetchedFinalityProviders
) {
return;
}

if (
!finalityProvidersRef.current ||
finalityProvidersRef.current.length === 0
) {
// on initial load, we shuffle the fp result
const shuffledProviders = [...initialProviders];
for (let i = shuffledProviders.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffledProviders[i], shuffledProviders[j]] = [
shuffledProviders[j],
shuffledProviders[i],
];
}
finalityProvidersRef.current = shuffledProviders;
} else {
const newProvidersMap = new Map<string, FinalityProvider>();
initialProviders.forEach((provider) => {
newProvidersMap.set(provider.btcPk, provider);
});

let hasChanges = false;
const updatedProviders = finalityProvidersRef.current.map((provider) => {
const newProvider = newProvidersMap.get(provider.btcPk);
if (newProvider) {
newProvidersMap.delete(provider.btcPk);
if (!shallowCompareFP(provider, newProvider)) {
hasChanges = true;
return newProvider;
}
}
return provider;
});

const newProviders = Array.from(newProvidersMap.values());
if (newProviders.length > 0 || hasChanges) {
finalityProvidersRef.current = [...updatedProviders, ...newProviders];
}
}
}, [initialProviders, isRefetchingFinalityProviders]);

useEffect(() => {
if (!finalityProvidersRef.current) {
setFilteredFinalityProviders(undefined);
return;
}

if (!searchTerm) {
setFilteredFinalityProviders(finalityProvidersRef.current);
return;
}

const searchTermLower = searchTerm.toLowerCase();
const filtered = finalityProvidersRef.current.filter(
(fp) =>
fp.description?.moniker?.toLowerCase().includes(searchTermLower) ||
fp.btcPk.toLowerCase().includes(searchTermLower),
);
setFilteredFinalityProviders(filtered);
}, [searchTerm, finalityProvidersRef.current]);

const handleSearch = (term: string) => {
setSearchTerm(term);
};

return {
filteredProviders,
setFilteredProviders,
finalityProviders: finalityProvidersRef.current,
filteredFinalityProviders,
handleSearch,
};
};

const shallowCompareFP = (objA: FinalityProvider, objB: FinalityProvider) => {
return (
objA.btcPk === objB.btcPk &&
objA.commission === objB.commission &&
objA.activeTVLSat === objB.activeTVLSat &&
objA.description?.moniker === objB.description?.moniker &&
objA.description?.website === objB.description?.website
);
};
6 changes: 1 addition & 5 deletions src/app/types/finalityProviders.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Dispatch, SetStateAction } from "react";

export interface FinalityProvider {
description: Description;
commission: string;
Expand All @@ -19,9 +17,7 @@ export interface Description {
}

export interface FinalityProvidersProps {
onFinalityProvidersLoad: Dispatch<
SetStateAction<FinalityProvider[] | undefined>
>;
onFinalityProvidersLoad: (finalityProviders: FinalityProvider[]) => void;
selectedFinalityProvider: FinalityProvider | undefined;
onFinalityProviderChange: (btcPkHex: string) => void;
}

0 comments on commit 5d992ff

Please sign in to comment.