Skip to content

Commit

Permalink
Merge pull request #426 from TosiDrop/master
Browse files Browse the repository at this point in the history
Release 3.3.0
  • Loading branch information
reqlez authored Apr 8, 2023
2 parents 8060c90 + 5889345 commit 43d973c
Show file tree
Hide file tree
Showing 21 changed files with 372 additions and 89 deletions.
11 changes: 11 additions & 0 deletions client/src/components/Banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import useBanner from "src/hooks/useBanner";

export default function Banner() {
const { bannerText } = useBanner();

return bannerText ? (
<div className="background-see-through p-2.5 w-full text-center">
{bannerText}
</div>
) : null;
}
20 changes: 15 additions & 5 deletions client/src/components/Claim/RewardsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ interface Props {
isLoadingClaimReward: boolean;
selectAll: () => void;
poolInfo?: any;
maxTokenSelected: number;
selectRandomTokens: any;
}

export default function RewardsView({
Expand All @@ -22,6 +24,8 @@ export default function RewardsView({
isLoadingClaimReward,
selectAll,
poolInfo,
maxTokenSelected,
selectRandomTokens,
}: Props) {
if (claimableTokens.length > 0) {
return (
Expand Down Expand Up @@ -93,17 +97,23 @@ export default function RewardsView({
className={"background flex flex-row items-center p-5 rounded-2xl"}
>
<div>Selected {numberOfSelectedTokens} token</div>
<div className="ml-auto flex flex-row w-fit">
<div className="ml-auto flex flex-row w-fit gap-4">
<button
className="tosi-button py-2.5 px-5 rounded-lg"
onClick={selectAll}
>
{numberOfSelectedTokens === claimableTokens.length
? "Unselect All"
: "Select All"}
Unselect All
</button>

<button
className="tosi-button py-2.5 px-5 rounded-lg"
onClick={selectRandomTokens}
>
I'm feeling lucky
</button>

<button
className="tosi-button ml-5 py-2.5 px-5 rounded-lg flex flex-row items-center"
className="tosi-button py-2.5 px-5 rounded-lg flex flex-row items-center"
disabled={numberOfSelectedTokens === 0}
onClick={claimRewards}
>
Expand Down
3 changes: 3 additions & 0 deletions client/src/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import logoLight from "src/assets/tosidrop-light.png";
import { Blockchain, Themes } from "src/entities/common.entities";
import { toggleMenu, toggleTheme } from "src/reducers/globalSlice";
import { RootState } from "src/store";
import Banner from "../Banner";
import BlockchainSelector from "../BlockchainSelector";
import CardanoWalletSelector from "../WalletSelector/CardanoWalletSelector";
import ErgoWalletSelector from "../WalletSelector/ErgoWalletSelector";
Expand All @@ -32,6 +33,8 @@ function Header() {

return (
<>
<Banner></Banner>

{/* Web header */}
<div className="flex-row items-center w-full p-5 pb-0 hidden sm:flex">
<Link to="/">
Expand Down
23 changes: 23 additions & 0 deletions client/src/entities/dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { DeliveredReward } from "./common.entities";
import { MinswapTypes } from "./minswap";
import { VmTypes } from "./vm";
import { Assets, ClaimableToken, PoolInfo, VmPoolInfo } from "./vm.entities";

export interface GetRewardsDto {
Expand Down Expand Up @@ -89,4 +91,25 @@ export namespace Dto {
addressInBech32: string;
};
}

export interface GetVmSettings extends Base {
response: VmTypes.Settings;
}

export interface GetMinswapPriceInfoMap extends Base {
response: MinswapTypes.PriceInfoMap;
}

export interface PostBannerText extends Base {
body: {
text: string;
adminKey: string;
};
}

export interface GetBannerText extends Base {
response: {
text: string;
};
}
}
17 changes: 17 additions & 0 deletions client/src/entities/minswap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export namespace MinswapTypes {
export interface PriceInfoMap {
[key: string]: PriceInfo;
}

export interface PriceInfo {
base_id: string;
base_name: string;
base_symbol: string;
quote_id: string;
quote_name: string;
quote_symbol: string;
last_price: string;
base_volume: string;
quote_volume: string;
}
}
12 changes: 12 additions & 0 deletions client/src/entities/vm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export namespace VmTypes {
export interface Settings {
withdrawal_fee: number;
epoch: number;
switching_epoch: boolean;
frontend_version: string;
backend_version: string;
min_balance: number;
confirmations_required: number;
max_assets_in_request?: number;
}
}
142 changes: 77 additions & 65 deletions client/src/hooks/cardano/claim/useClaimReward.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import { useSelector } from "react-redux";

import { RootState } from "src/store";

import { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";

import {
InfoModalTypes,
ModalTypes,
PageRoute,
} from "src/entities/common.entities";
import { PageRoute } from "src/entities/common.entities";
import { ClaimableToken, VmPoolInfo } from "src/entities/vm.entities";
import useErrorHandler from "src/hooks/useErrorHandler";
import { showModal } from "src/reducers/globalSlice";
import useModal from "src/hooks/useModal";
import { getCustomRewards, getRewards } from "src/services/claim";
import { getStakeKey } from "src/services/common";
import { getSettings, getStakeKey } from "src/services/common";
import { RootState } from "src/store";
import { shuffleArray } from "src/utils";

export default function useClaimReward() {
const dispatch = useDispatch();
const navigate = useNavigate();
const { handleError } = useErrorHandler();
const { showInfoModal } = useModal();
const connectedWalletAddress = useSelector(
(state: RootState) => state.wallet.walletAddress
);
Expand All @@ -35,6 +29,8 @@ export default function useClaimReward() {
const [isClaimRewardLoading, setIsClaimRewardLoading] = useState(false);
const [stakeAddress, setStakeAddress] = useState<string>("");
const [numberOfSelectedTokens, setNumberOfSelectedTokens] = useState(0);
/** default max number of token to claim */
const [maxTokenSelected, setMaxTokenSelected] = useState(1000);

useEffect(() => {
setSearchAddress(isWrongNetwork ? "" : connectedWalletAddress);
Expand All @@ -51,81 +47,96 @@ export default function useClaimReward() {
);
}, [claimableTokens]);

const selectAllClaimableTokens = () => {
const handleTokenSelect = (position: number) => {
const updatedClaimableTokens = [...claimableTokens];
if (numberOfSelectedTokens < claimableTokens.length) {
updatedClaimableTokens.forEach((token) => (token.selected = true));
} else {
updatedClaimableTokens.forEach((token) => (token.selected = false));

if (
!updatedClaimableTokens[position].selected &&
numberOfSelectedTokens === maxTokenSelected
) {
showInfoModal(
`You have selected the maximum number of tokens to claim (${maxTokenSelected}).
Please deselect other tokens first`
);
return;
}
setClaimableTokens(updatedClaimableTokens);
};

const handleTokenSelect = (position: number) => {
const updatedClaimableTokens = [...claimableTokens];
updatedClaimableTokens[position].selected =
!updatedClaimableTokens[position].selected;
setClaimableTokens(updatedClaimableTokens);
};

const selectAll = () => {
const updatedClaimableTokens = [...claimableTokens];
if (numberOfSelectedTokens < claimableTokens.length) {
updatedClaimableTokens.forEach((token) => (token.selected = true));
} else {
updatedClaimableTokens.forEach((token) => (token.selected = false));
}
updatedClaimableTokens.forEach((_) => (_.selected = false));
// const limit = Math.min(maxTokenSelected, claimableTokens.length);
// if (numberOfSelectedTokens !== limit) {
// for (let i = 0; i < limit; i++) {
// updatedClaimableTokens[i].selected = true;
// }
// }
setClaimableTokens(updatedClaimableTokens);
};

const selectRandomTokens = () => {
const positions = shuffleArray([
...Array(claimableTokens.length).keys(),
]).slice(0, maxTokenSelected);

const updatedClaimableTokens = [...claimableTokens];
updatedClaimableTokens.forEach((token) => (token.selected = false));
positions.forEach(
(position) => (updatedClaimableTokens[position].selected = true)
);

setClaimableTokens(updatedClaimableTokens);
};

const checkRewards = async () => {
setIsCheckRewardLoading(true);
try {
/**
* check if the inserted address is cardano address, we want the stake address
* if it is cardano address, get the staking address
*/
let address = await getStakeKey(searchAddress);

address = address.staking_address;

setStakeAddress(address);
const getRewardsDto = await getRewards(address);
if (getRewardsDto == null) {

const [getRewardsResponse, vmSettings] = await Promise.all([
getRewards(address),
getSettings(),
]);

if (getRewardsResponse == null) {
throw new Error("Something went wrong when checking reward");
}
if (getRewardsDto.claimable_tokens.length !== 0) {
setClaimableTokens(
getRewardsDto.claimable_tokens
.map((token) => {
token.selected = false;
return token;
})
.sort((a, b) => {
if (a.premium === b.premium) {
if (a.ticker < b.ticker) {
return -1;
} else {
return 1;
}

if (getRewardsResponse.claimable_tokens.length === 0) {
showInfoModal("No rewards found for the account, yet.");
return;
}

if (vmSettings.max_assets_in_request) {
setMaxTokenSelected(vmSettings.max_assets_in_request);
}

setClaimableTokens(
getRewardsResponse.claimable_tokens
.map((token) => {
token.selected = false;
return token;
})
.sort((a, b) => {
if (a.premium === b.premium) {
if (a.ticker < b.ticker) {
return -1;
} else {
return a.premium ? -1 : 1;
return 1;
}
})
);
setPoolInfo(getRewardsDto.pool_info);
setIsCheckRewardLoading(false);
} else {
dispatch(
showModal({
modalType: ModalTypes.info,
details: {
text: "No rewards found for the account, yet.",
type: InfoModalTypes.info,
},
} else {
return a.premium ? -1 : 1;
}
})
);
}
);
setPoolInfo(getRewardsResponse.pool_info);
setIsCheckRewardLoading(false);
} catch (e: any) {
handleError(e);
} finally {
Expand Down Expand Up @@ -177,13 +188,14 @@ export default function useClaimReward() {
checkRewards,
claimRewards,
selectAll,
selectRandomTokens,
cancelClaim,
claimableTokens,
selectAllClaimableTokens,
handleTokenSelect,
numberOfSelectedTokens,
isCheckRewardLoading,
isClaimRewardLoading,
poolInfo,
maxTokenSelected,
};
}
21 changes: 21 additions & 0 deletions client/src/hooks/useBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useEffect, useState } from "react";
import { getBannerText } from "src/services/common";

export default function useBanner() {
const [bannerText, setBannerText] = useState("");

useEffect(() => {
(async () => {
const bannerText = await getBannerText();
try {
if (bannerText) {
setBannerText(bannerText);
}
} catch (e) {}
})();
}, []);

return {
bannerText,
};
}
Loading

0 comments on commit 43d973c

Please sign in to comment.