Skip to content

Commit 1063954

Browse files
authored
Merge pull request #1920 from cprussin/add-guide-content
feat(staking): add guides & faqs & a ton of mobile fixes
2 parents 9773cfb + 9da84ef commit 1063954

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2098
-205
lines changed

apps/staking/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@solana/web3.js": "^1.95.2",
3737
"clsx": "^2.1.1",
3838
"dnum": "^2.13.1",
39+
"framer-motion": "^11.3.8",
3940
"next": "^14.2.5",
4041
"pino": "^9.3.2",
4142
"proxycheck-ts": "^0.0.11",

apps/staking/src/components/AccountSummary/index.tsx

Lines changed: 132 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { InformationCircleIcon } from "@heroicons/react/24/outline";
12
import Image from "next/image";
23
import { type ComponentProps, type ReactNode, useCallback } from "react";
34
import {
@@ -50,7 +51,7 @@ export const AccountSummary = ({
5051
alt=""
5152
className="absolute -right-40 hidden h-full object-cover object-right [mask-image:linear-gradient(to_right,_transparent,_black_50%)] md:block"
5253
/>
53-
<div className="relative flex flex-col items-start justify-between gap-8 sm:px-6 sm:py-10 md:gap-16 md:px-12 md:py-20 xl:flex-row xl:items-center">
54+
<div className="relative flex flex-row items-center justify-between gap-8 sm:px-6 sm:py-10 md:gap-16 lg:px-12 lg:py-20">
5455
<div>
5556
<div className="mb-2 inline-block border border-neutral-600/50 bg-neutral-900 px-4 py-1 text-xs text-neutral-400 sm:mb-4">
5657
Total Balance
@@ -114,24 +115,42 @@ export const AccountSummary = ({
114115
transfer={api.deposit}
115116
enableWithZeroMax
116117
/>
118+
<WithdrawButton
119+
api={api}
120+
max={availableToWithdraw}
121+
className="xl:hidden"
122+
/>
123+
{api.type === ApiStateType.Loaded ? (
124+
<DialogTrigger>
125+
<Button variant="secondary" className="xl:hidden">
126+
Claim
127+
</Button>
128+
{availableRewards > 0n ? (
129+
<ClaimDialog
130+
expiringRewards={expiringRewards}
131+
availableRewards={availableRewards}
132+
api={api}
133+
/>
134+
) : (
135+
<ModalDialog title="No Rewards" closeButtonText="Ok">
136+
<p>You have no rewards available to be claimed</p>
137+
</ModalDialog>
138+
)}
139+
</DialogTrigger>
140+
) : (
141+
<Button variant="secondary" isDisabled={true} className="lg:hidden">
142+
Claim
143+
</Button>
144+
)}
117145
</div>
118146
</div>
119-
<div className="flex w-full flex-row items-stretch gap-4 xl:w-auto">
147+
<div className="hidden w-auto items-stretch gap-4 xl:flex">
120148
<BalanceCategory
121149
name="Unlocked & Unstaked"
122150
amount={availableToWithdraw}
123151
description="The amount of unlocked tokens that are not staked in either program"
124152
action={
125-
<TransferButton
126-
size="small"
127-
variant="secondary"
128-
actionDescription="Move funds from your account back to your wallet"
129-
actionName="Withdraw"
130-
max={availableToWithdraw}
131-
{...(api.type === ApiStateType.Loaded && {
132-
transfer: api.withdraw,
133-
})}
134-
/>
153+
<WithdrawButton api={api} max={availableToWithdraw} size="small" />
135154
}
136155
/>
137156
<BalanceCategory
@@ -140,7 +159,12 @@ export const AccountSummary = ({
140159
description="Rewards you have earned from OIS"
141160
action={
142161
api.type === ApiStateType.Loaded ? (
143-
<ClaimButton isDisabled={availableRewards === 0n} api={api} />
162+
<ClaimButton
163+
size="small"
164+
variant="secondary"
165+
isDisabled={availableRewards === 0n}
166+
api={api}
167+
/>
144168
) : (
145169
<Button size="small" variant="secondary" isDisabled={true}>
146170
Claim
@@ -163,6 +187,33 @@ export const AccountSummary = ({
163187
</section>
164188
);
165189

190+
type WithdrawButtonProps = Omit<
191+
ComponentProps<typeof TransferButton>,
192+
"variant" | "actionDescription" | "actionName" | "transfer"
193+
> & {
194+
api: States[ApiStateType.Loaded] | States[ApiStateType.LoadedNoStakeAccount];
195+
};
196+
197+
const WithdrawButton = ({ api, ...props }: WithdrawButtonProps) => (
198+
<TransferButton
199+
variant="secondary"
200+
actionDescription="Move funds from your account back to your wallet"
201+
actionName="Withdraw"
202+
{...(api.type === ApiStateType.Loaded && {
203+
transfer: api.withdraw,
204+
})}
205+
{...props}
206+
>
207+
<div className="mb-4 flex max-w-96 flex-row gap-2 border border-neutral-600/50 bg-pythpurple-400/20 p-4">
208+
<InformationCircleIcon className="size-8 flex-none" />
209+
<div className="text-sm">
210+
You can only withdraw tokens that are unlocked and not staked in either
211+
OIS or Pyth Governance
212+
</div>
213+
</div>
214+
</TransferButton>
215+
);
216+
166217
type BalanceCategoryProps = {
167218
name: string;
168219
amount: bigint;
@@ -178,23 +229,88 @@ const BalanceCategory = ({
178229
action,
179230
warning,
180231
}: BalanceCategoryProps) => (
181-
<div className="flex w-full flex-col justify-between border border-neutral-600/50 bg-pythpurple-800/60 p-6 backdrop-blur xl:w-96">
232+
<div className="flex w-full flex-col justify-between border border-neutral-600/50 bg-pythpurple-800/60 p-4 backdrop-blur sm:p-6 xl:w-80 2xl:w-96">
182233
<div>
183234
<div className="mb-4 inline-block border border-neutral-600/50 bg-neutral-900 px-4 py-1 text-xs text-neutral-400">
184235
{name}
185236
</div>
186237
<div>
187238
<Tokens className="text-xl font-light">{amount}</Tokens>
188239
</div>
189-
<p className="mt-4 max-w-xs text-sm text-neutral-500">{description}</p>
240+
<p className="mt-4 text-sm text-neutral-500">{description}</p>
190241
</div>
191242
<div className="mt-4 flex flex-row items-center gap-4">
192243
{action}
193-
{warning && <p className="max-w-xs text-xs text-red-600">{warning}</p>}
244+
{warning && <p className="text-xs text-red-600">{warning}</p>}
194245
</div>
195246
</div>
196247
);
197248

249+
type ClaimDialogProps = {
250+
availableRewards: bigint;
251+
expiringRewards: Date | undefined;
252+
api: States[ApiStateType.Loaded];
253+
};
254+
255+
const ClaimDialog = ({
256+
api,
257+
expiringRewards,
258+
availableRewards,
259+
}: ClaimDialogProps) => {
260+
const { state, execute } = useAsync(api.claim);
261+
262+
const doClaim = useCallback(() => {
263+
execute().catch(() => {
264+
/* TODO figure out a better UI treatment for when claim fails */
265+
});
266+
}, [execute]);
267+
268+
return (
269+
<ModalDialog title="Claim">
270+
{({ close }) => (
271+
<>
272+
<p className="mb-4">
273+
Claim your <Tokens>{availableRewards}</Tokens> rewards
274+
</p>
275+
{expiringRewards && (
276+
<div className="mb-4 flex max-w-96 flex-row gap-2 border border-neutral-600/50 bg-pythpurple-400/20 p-4">
277+
<InformationCircleIcon className="size-8 flex-none" />
278+
<div className="text-sm">
279+
Rewards expire one year from the epoch in which they were
280+
earned. You have rewards expiring on{" "}
281+
{expiringRewards.toLocaleDateString()}.
282+
</div>
283+
</div>
284+
)}
285+
{state.type === StateType.Error && (
286+
<p className="mt-8 text-red-600">
287+
Uh oh, an error occurred! Please try again
288+
</p>
289+
)}
290+
<div className="mt-14 flex flex-col gap-8 sm:flex-row sm:justify-between">
291+
<Button
292+
variant="secondary"
293+
className="w-full sm:w-auto"
294+
size="noshrink"
295+
onPress={close}
296+
>
297+
Cancel
298+
</Button>
299+
<Button
300+
className="w-full sm:w-auto"
301+
size="noshrink"
302+
isLoading={state.type === StateType.Running}
303+
onPress={doClaim}
304+
>
305+
Claim
306+
</Button>
307+
</div>
308+
</>
309+
)}
310+
</ModalDialog>
311+
);
312+
};
313+
198314
type ClaimButtonProps = Omit<
199315
ComponentProps<typeof Button>,
200316
"onClick" | "disabled" | "loading"
@@ -213,8 +329,6 @@ const ClaimButton = ({ api, ...props }: ClaimButtonProps) => {
213329

214330
return (
215331
<Button
216-
size="small"
217-
variant="secondary"
218332
onPress={doClaim}
219333
isDisabled={state.type !== StateType.Base}
220334
isLoading={state.type === StateType.Running}

0 commit comments

Comments
 (0)