From e3646361789de4eecefdc6e2f467f16704ec1448 Mon Sep 17 00:00:00 2001 From: ttang Date: Thu, 1 Feb 2024 15:46:01 +0800 Subject: [PATCH] feat: allowance ui --- apps/u3/.vscode/settings.json | 1 + apps/u3/package.json | 1 + .../components/social/farcaster/FCastTips.tsx | 254 ++++++++++++++---- apps/u3/src/components/ui/checkbox.tsx | 32 +++ 4 files changed, 238 insertions(+), 50 deletions(-) create mode 100644 apps/u3/src/components/ui/checkbox.tsx diff --git a/apps/u3/.vscode/settings.json b/apps/u3/.vscode/settings.json index c6f336cf..6fdb18f0 100644 --- a/apps/u3/.vscode/settings.json +++ b/apps/u3/.vscode/settings.json @@ -21,6 +21,7 @@ "unfollowed", "unpinup", "upsert", + "Upvote", "viem", "Warpcast", "Whatsnew", diff --git a/apps/u3/package.json b/apps/u3/package.json index 0608f99f..761378c0 100644 --- a/apps/u3/package.json +++ b/apps/u3/package.json @@ -20,6 +20,7 @@ "@noble/ed25519": "^2.0.0", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", diff --git a/apps/u3/src/components/social/farcaster/FCastTips.tsx b/apps/u3/src/components/social/farcaster/FCastTips.tsx index bfbb27ad..8fcfeaca 100644 --- a/apps/u3/src/components/social/farcaster/FCastTips.tsx +++ b/apps/u3/src/components/social/farcaster/FCastTips.tsx @@ -29,10 +29,9 @@ import { DegenABI, DegenAddress } from '@/services/social/abi/degen/contract'; import { FarCast } from '@/services/social/types'; import DegenTip from '@/components/common/icons/DegenTip'; import { useFarcasterCtx } from '@/contexts/social/FarcasterCtx'; -import { Input } from '@/components/ui/input'; -import ColorButton from '@/components/common/button/ColorButton'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { FARCASTER_NETWORK, FARCASTER_WEB_CLIENT } from '@/constants/farcaster'; +import { Checkbox } from '@/components/ui/checkbox'; export default function FCastTips({ userData, @@ -49,6 +48,7 @@ export default function FCastTips({ address: '', fname: '', }); + const { currFid, encryptedSigner } = useFarcasterCtx(); const [allowance, setAllowance] = useState('0'); const [loading, setLoading] = useState(false); @@ -59,6 +59,8 @@ export default function FCastTips({ const { data } = await getUserinfoWithFid(userData.fid); setUserInfo(data.data); setAllowance(allowanceData.data?.[0]?.tip_allowance || '0'); + setReplyTipAllowance(allowanceData.data?.[0]?.tip_allowance || '0'); + setReplyTipAmountTotal('0'); } catch (e) { console.error(e); } finally { @@ -66,12 +68,54 @@ export default function FCastTips({ } }, [userData, address]); + const directReply = useCallback(async () => { + const allowanceValue = getReplyTipAmount(); + try { + const castToReply = ( + await makeCastAdd( + { + text: `${allowanceValue} $DEGEN`, + embeds: [], + embedsDeprecated: [], + mentions: [], + mentionsPositions: [], + parentCastId: { + hash: Buffer.from(cast.hash.data), + fid: Number(cast.fid), + }, + }, + { fid: currFid, network: FARCASTER_NETWORK }, + encryptedSigner + ) + )._unsafeUnwrap(); + const r = await FARCASTER_WEB_CLIENT.submitMessage(castToReply); + if (r.isErr()) { + throw new Error(r.error.message); + } + setReplyTipAmount(allowanceValue); + setReplyTipTimes(Number(getReplyTipTimes()) - 1); + setReplyTipAmountTotal( + ( + Number(getReplyTipAmountTotal()) + Number(getReplyTipAmount()) + ).toString() + ); + toast.success('allowance tip posted'); + } catch (error) { + console.error(error); + toast.success('allowance tip failed'); + } + }, [currFid, encryptedSigner, cast]); + return ( <>
{ e.stopPropagation(); + const replyDirect = + getUseReplyTipDefault() && + Number(getReplyTipTimes()) > 0 && + Number(getReplyTipAmountTotal()) < Number(getReplyTipAllowance()); if (!isLoginU3) { loginU3(); return; @@ -80,6 +124,12 @@ export default function FCastTips({ openConnectModal(); return; } + + if (replyDirect) { + directReply(); + return; + } + loadUserinfo(); setOpenModal(true); }} @@ -195,9 +245,10 @@ function TipTransaction({ }); const network = useNetwork(); const [tipAmount, setTipAmount] = useState(tipsCount[1]); - const [allowanceValue, setAllowanceValue] = useState('0'); + const [allowanceValue, setAllowanceValue] = useState(''); const [transactionHash, setTransactionHash] = useState(''); const [tab, setTab] = useState('TabReply'); + const [count, setCount] = useState(0); const tipAction = useCallback(async () => { const left = result?.data?.formatted?.toString() || '0'; @@ -267,6 +318,9 @@ function TipTransaction({ if (r.isErr()) { throw new Error(r.error.message); } + setReplyTipAmount(allowanceValue); + setReplyTipAmountTotal(allowanceValue); + setReplyTipTimes(5); toast.success('allowance tip posted'); successCallback?.(); } catch (error) { @@ -276,11 +330,12 @@ function TipTransaction({ }, [allowanceValue, currFid, encryptedSigner]); useEffect(() => { - const tabExist = localStorage.getItem('tipTab'); - if (tabExist) { - setTab(tabExist); + if (Number(allowance) > 0) { + setTab('TabReply'); } - }, []); + }, [allowance]); + + const useAllowance = getUseReplyTipDefault(); const allowanceNum = Number.isNaN(Number(allowance)) ? 0 : Number(allowance); @@ -295,37 +350,90 @@ function TipTransaction({ }} className="h-60" > - - Tip via Reply - Tip via Transaction + + + Allowance + + + Token + -
-
-
Tip Allowance
-
: {allowance} $DEGEN
+
+
+ {tipsCount.map((item) => { + const isAllowance = allowanceNum >= item; + return ( +
{ + if (isAllowance) setAllowanceValue(`${item}`); + }} + > + ${item} +
+ ); + })}
-
- { - setAllowanceValue(e.target.value); - }} - /> - $DEGEN +
+ or +
+ { + setAllowanceValue(e.target.value); + }} + /> + $DEGEN +
- { allowanceAction(); }} > - Reply - + Tip by Reply & Upvote + +
+
+ { + if (v) { + setUseReplyTipDefault(); + } else { + setUseReplyTipDefault('false'); + } + setCount(count + 1); + }} + /> +

+ Use as default for next 5 tips +

@@ -337,42 +445,88 @@ function TipTransaction({
{ setTipAmount(item); }} > - {item} + ${item}
); })}
or - { - setTipAmount(Number(e.target.value)); - }} - /> - $DEGEN +
+ { + setTipAmount(Number(e.target.value)); + }} + /> + $DEGEN +
+
+
+ +
+
+

+ to @{fname} (0x{shortPubKey(address, { len: 4 })}) +

- -

- to @{fname} (0x{shortPubKey(address, { len: 4 })}) -

); } + +function setReplyTipAllowance(allowance: string) { + localStorage.setItem('tipAllowance', allowance); +} + +function getReplyTipAllowance() { + return localStorage.getItem('tipAllowance') || '0'; +} + +function setReplyTipAmount(num: string) { + localStorage.setItem('tipReplyAmount', num); +} + +function setReplyTipAmountTotal(num: string) { + localStorage.setItem('tipReplyAmountTotal', num); +} + +function getReplyTipAmountTotal() { + return localStorage.getItem('tipReplyAmountTotal') || '0'; +} + +function getReplyTipAmount() { + return localStorage.getItem('tipReplyAmount') || '0'; +} + +function setReplyTipTimes(times: number) { + localStorage.setItem('tipReplyTimes', times.toString()); +} + +function getReplyTipTimes() { + return localStorage.getItem('tipReplyTimes') || '0'; +} + +function setUseReplyTipDefault(value: string = 'true') { + localStorage.setItem('useReplyTipDefault', value); +} + +function getUseReplyTipDefault() { + return localStorage.getItem('useReplyTipDefault') === 'true'; +} diff --git a/apps/u3/src/components/ui/checkbox.tsx b/apps/u3/src/components/ui/checkbox.tsx new file mode 100644 index 00000000..45fdfc22 --- /dev/null +++ b/apps/u3/src/components/ui/checkbox.tsx @@ -0,0 +1,32 @@ +/* eslint-disable react/prop-types */ + +'use client'; + +import * as React from 'react'; +import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; +import { CheckIcon } from '@radix-ui/react-icons'; + +import { cn } from '@/lib/utils'; + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)); +Checkbox.displayName = CheckboxPrimitive.Root.displayName; + +export { Checkbox };