From e02d5ee7351a531e7f7bff84bede3482ab8a0ea2 Mon Sep 17 00:00:00 2001 From: Songkeys Date: Sat, 29 Jun 2024 01:29:41 +0800 Subject: [PATCH] feat: tipping --- icons/mgc/power_outline.svg | 2 +- .../src/components/ui/wallet/balance.tsx | 31 ++-- src/renderer/src/hono.ts | 6 +- .../src/hooks/biz/useEntryActions.tsx | 4 +- .../my-wallet-section/claim-daily-reward.tsx | 2 +- .../my-wallet-section/create-wallet.tsx | 2 +- src/renderer/src/modules/wallet/hooks.ts | 4 +- src/renderer/src/modules/wallet/tip-modal.tsx | 143 ++++++++++++++++-- src/renderer/src/styles/tailwind-extend.css | 2 +- 9 files changed, 162 insertions(+), 34 deletions(-) diff --git a/icons/mgc/power_outline.svg b/icons/mgc/power_outline.svg index 1e79cb3f8..54b8780d6 100644 --- a/icons/mgc/power_outline.svg +++ b/icons/mgc/power_outline.svg @@ -1,3 +1,3 @@ - + diff --git a/src/renderer/src/components/ui/wallet/balance.tsx b/src/renderer/src/components/ui/wallet/balance.tsx index 7713d0199..d8ab97ab3 100644 --- a/src/renderer/src/components/ui/wallet/balance.tsx +++ b/src/renderer/src/components/ui/wallet/balance.tsx @@ -8,30 +8,39 @@ export const Balance = ({ className, precision = 2, withSuffix = false, + withTooltip = true, }: { /** The token balance in wei. */ - children: bigint + children: bigint | string className?: string precision?: number withSuffix?: boolean + withTooltip?: boolean }) => { - const from = [BigInt(children), 18] as const - const formatted = format(from, { digits: precision, trailingZeros: true }) - const formattedFull = format(from, { digits: 18, trailingZeros: true }) + const n = [BigInt(children), 18] as const + const formatted = format(n, { digits: precision, trailingZeros: true }) + const formattedFull = format(n, { digits: 18, trailingZeros: true }) + + const Content = ( +
+ {formatted} + {" "} + {withSuffix && POWER} +
+ ) + + if (!withTooltip) return Content + return ( - - - {formatted} - {" "} - {withSuffix && $POWER} - + + {Content}
{formattedFull} {" "} - $POWER + POWER
diff --git a/src/renderer/src/hono.ts b/src/renderer/src/hono.ts index a4e22f6b4..c83156266 100644 --- a/src/renderer/src/hono.ts +++ b/src/renderer/src/hono.ts @@ -41,7 +41,7 @@ declare const routes: hono_hono_base.HonoBase { nextFrame(openTipModal) }, @@ -239,6 +236,7 @@ export const useEntryActions = ({ collect, entry, read, + openTipModal, uncollect, unread, view, diff --git a/src/renderer/src/modules/settings/tabs/wallet/my-wallet-section/claim-daily-reward.tsx b/src/renderer/src/modules/settings/tabs/wallet/my-wallet-section/claim-daily-reward.tsx index 7ccf43d85..07a01bd2e 100644 --- a/src/renderer/src/modules/settings/tabs/wallet/my-wallet-section/claim-daily-reward.tsx +++ b/src/renderer/src/modules/settings/tabs/wallet/my-wallet-section/claim-daily-reward.tsx @@ -56,7 +56,7 @@ export const ClaimDailyReward = () => { {canClaim ? - "Claim your daily reward (2.00 PowerTokens) now!" : + "Claim your daily reward (2.00 POWER) now!" : `You can claim your daily reward in ${hour}:${minute}:${second}.`} diff --git a/src/renderer/src/modules/settings/tabs/wallet/my-wallet-section/create-wallet.tsx b/src/renderer/src/modules/settings/tabs/wallet/my-wallet-section/create-wallet.tsx index ef6ad893f..52b119f2e 100644 --- a/src/renderer/src/modules/settings/tabs/wallet/my-wallet-section/create-wallet.tsx +++ b/src/renderer/src/modules/settings/tabs/wallet/my-wallet-section/create-wallet.tsx @@ -17,7 +17,7 @@ export const CreateWallet = () => {

Create a free wallet to receive {" "} - PowerTokens + $POWER , which can be used to reward creators and also get rewarded for your content contributions. diff --git a/src/renderer/src/modules/wallet/hooks.ts b/src/renderer/src/modules/wallet/hooks.ts index a0c17fb5c..2d610b407 100644 --- a/src/renderer/src/modules/wallet/hooks.ts +++ b/src/renderer/src/modules/wallet/hooks.ts @@ -8,9 +8,9 @@ export const useTipModal = ({ userId, feedId }: { userId?: string, feedId?: stri const { present } = useModalStack() return useCallback(() => { - if (!userId) { + if (!userId && !feedId) { // this should not happen unless there is a bug in the code - toast.error("You must have a user to tip.") + toast.error("Invalid user id or feed id") return } diff --git a/src/renderer/src/modules/wallet/tip-modal.tsx b/src/renderer/src/modules/wallet/tip-modal.tsx index f3d6b0c85..db2c27822 100644 --- a/src/renderer/src/modules/wallet/tip-modal.tsx +++ b/src/renderer/src/modules/wallet/tip-modal.tsx @@ -1,17 +1,50 @@ +import { useUser } from "@renderer/atoms/user" import { StyledButton } from "@renderer/components/ui/button" +import { Divider } from "@renderer/components/ui/divider" +import { Input } from "@renderer/components/ui/input" import { LoadingCircle } from "@renderer/components/ui/loading" -import { useWalletTipMutation, useWalletTransactions } from "@renderer/queries/wallet" +import { useCurrentModal } from "@renderer/components/ui/modal" +import { Balance } from "@renderer/components/ui/wallet/balance" +import { nextFrame } from "@renderer/lib/dom" +import { cn } from "@renderer/lib/utils" +import { useWallet, useWalletTipMutation, useWalletTransactions } from "@renderer/queries/wallet" +import { from, subtract, toNumber } from "dnum" import type { FC } from "react" +import { useState } from "react" + +import { useSettingModal } from "../settings/modal/hooks" + +const DEFAULT_RECOMMENDED_TIP = 0.1 export const TipModalContent: FC<{ userId: string feedId?: string }> = ({ userId, feedId }) => { - const transationsQuery = useWalletTransactions({ toUserId: userId, toFeedId: feedId }) + const user = useUser() + const myWallet = useWallet({ userId: user.id }) + const myWalletData = myWallet.data?.[0] + + const balanceBigInt = BigInt(myWalletData?.powerToken ?? 0) + const balanceNumber = toNumber(from(balanceBigInt, 18), { digits: 2, trailingZeros: true }) + + const transactionsQuery = useWalletTransactions({ toUserId: userId, toFeedId: feedId }) const tipMutation = useWalletTipMutation() - if (transationsQuery.isPending) { + const [amount, setAmount] = useState(DEFAULT_RECOMMENDED_TIP > balanceNumber ? balanceNumber : DEFAULT_RECOMMENDED_TIP) + + const amountBigInt = from(amount, 18)[0] + const remainingBalance = subtract(balanceBigInt, amountBigInt)[0] + + const wrongNumberRange = amountBigInt > balanceBigInt || amountBigInt <= BigInt(0) + + const [showConfirm, setShowConfirm] = useState(false) + + const { dismiss } = useCurrentModal() + + const settingModalPresent = useSettingModal() + + if (transactionsQuery.isPending || myWallet.isPending) { return (

@@ -19,24 +52,112 @@ export const TipModalContent: FC<{ ) } - if (transationsQuery.error) { - return
Failed to load transactions history.
+ if (!myWalletData) { + return ( +
+

+ You don't have a wallet yet. Please create a wallet to tip. +

+
+ nextFrame(() => settingModalPresent("wallet"))} + > + Create For Free + +
+
+ ) + } + + // if (transactionsQuery.error) { + // return
Failed to load transactions history.
+ // } + + if (tipMutation.isSuccess) { + return ( +
+

+ Tip sent successfully! Thank you for your support. +

+

+ {amountBigInt} + {" "} + has been sent to the author. +

+ +
+ dismiss()} + > + OK + +
+
+ ) } return ( -
- {/* // TODO: */} -
+
+

+ ⭐ Tip to show your support! Your tip will be added to the author's wallet. +

+ +
+
Amount
+ setAmount(Number(e.target.value))} + /> + + + +
+
+
Balance
+ {balanceBigInt} +
+
+
Tipping
+ {amountBigInt} +
+
+
Remaining
+ {remainingBalance} +
+
+
+ +
tipMutation.mutate({ userId, feedId, amount: "0" })} + onClick={() => { + if (tipMutation.isPending) return + if (!showConfirm) { + setShowConfirm(true) + return + } + tipMutation.mutate({ userId, feedId, amount: amountBigInt.toString() }) + setShowConfirm(false) + }} variant={tipMutation.isSuccess ? "plain" : "primary"} > {tipMutation.isSuccess && ( )} - Claim + {showConfirm ? ( +
+ Confirm Tip + {amountBigInt} +
+ ) : "Tip Now"}
diff --git a/src/renderer/src/styles/tailwind-extend.css b/src/renderer/src/styles/tailwind-extend.css index e67addf67..b02202c99 100644 --- a/src/renderer/src/styles/tailwind-extend.css +++ b/src/renderer/src/styles/tailwind-extend.css @@ -51,7 +51,7 @@ } .shadow-modal { - @apply shadow-2xl shadow-stone-300 dark:shadow-stone-800; + @apply shadow-2xl shadow-stone-300 dark:shadow-stone-900; } /* Utils */ .no-animation {