feat: daily reward display
This commit is contained in:
parent
b6cb042ed9
commit
78db8ad50f
|
|
@ -4,10 +4,9 @@ import { Trans, useTranslation } from "react-i18next"
|
|||
import { Button } from "~/components/ui/button"
|
||||
import { Tooltip, TooltipContent } from "~/components/ui/tooltip"
|
||||
import { DAILY_CLAIM_AMOUNT } from "~/constants"
|
||||
import { Level } from "~/modules/wallet/level"
|
||||
import { useClaimCheck, useClaimWalletDailyRewardMutation } from "~/queries/wallet"
|
||||
|
||||
export const ClaimDailyReward = ({ level }: { level: number }) => {
|
||||
export const ClaimDailyReward = () => {
|
||||
const mutation = useClaimWalletDailyRewardMutation()
|
||||
const { t } = useTranslation("settings")
|
||||
|
||||
|
|
@ -24,7 +23,6 @@ export const ClaimDailyReward = ({ level }: { level: number }) => {
|
|||
disabled={!canClaim}
|
||||
>
|
||||
{canClaim ? t("wallet.claim.button.claim") : t("wallet.claim.button.claimed")}
|
||||
<Level className="ml-3" level={level} hideIcon />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { DAILY_CLAIM_AMOUNT } from "~/constants"
|
|||
import { apiClient } from "~/lib/api-fetch"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { SettingSectionTitle } from "~/modules/settings/section"
|
||||
import { ActivityPoints } from "~/modules/wallet/activity-points"
|
||||
import { Balance } from "~/modules/wallet/balance"
|
||||
import { Level } from "~/modules/wallet/level"
|
||||
import { useWallet, wallet as walletActions } from "~/queries/wallet"
|
||||
|
|
@ -82,50 +83,79 @@ export const MyWalletSection = () => {
|
|||
className="p-1 opacity-0 duration-200 group-hover:opacity-100 [&_i]:size-2.5"
|
||||
/>
|
||||
</div>
|
||||
<SettingSectionTitle title={t("wallet.balance.level")} margin="compact" />
|
||||
<Level level={myWallet.level?.level || 0} />
|
||||
<SettingSectionTitle title={t("wallet.balance.title")} margin="compact" />
|
||||
<div className="mb-2 flex items-end justify-between">
|
||||
<div className="flex items-center gap-1">
|
||||
<Balance className="text-xl font-bold text-accent">
|
||||
{BigInt(myWallet.dailyPowerToken || 0n) + BigInt(myWallet.cashablePowerToken || 0n)}
|
||||
</Balance>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => refreshMutation.mutate()}
|
||||
disabled={refreshMutation.isPending}
|
||||
>
|
||||
<i
|
||||
className={cn(
|
||||
"i-mgc-refresh-2-cute-re",
|
||||
refreshMutation.isPending && "animate-spin",
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Balance className="text-xl font-bold text-accent">
|
||||
{BigInt(myWallet.powerToken || 0n)}
|
||||
</Balance>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => refreshMutation.mutate()}
|
||||
disabled={refreshMutation.isPending}
|
||||
>
|
||||
<i
|
||||
className={cn(
|
||||
"i-mgc-refresh-2-cute-re",
|
||||
refreshMutation.isPending && "animate-spin",
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
<Tooltip>
|
||||
<TooltipTrigger className="mt-1 block">
|
||||
<div className="flex flex-row items-center gap-x-2 text-xs">
|
||||
<span className="flex items-center gap-1 text-left">
|
||||
{t("wallet.balance.withdrawable")} <i className="i-mgc-question-cute-re" />
|
||||
</span>
|
||||
<Balance className="center text-[12px] font-medium">
|
||||
{myWallet.cashablePowerToken}
|
||||
</Balance>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPortal>
|
||||
<TooltipContent align="start" className="z-[999]">
|
||||
<p>{t("wallet.balance.withdrawableTooltip")}</p>
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<WithdrawButton />
|
||||
<ClaimDailyReward level={myWallet.level?.level || 0} />
|
||||
</div>
|
||||
</div>
|
||||
<Tooltip>
|
||||
<TooltipTrigger className="block">
|
||||
<div className="flex flex-row items-center gap-x-2 text-xs text-zinc-600 dark:text-neutral-400">
|
||||
<span className="flex items-center gap-1 text-left">
|
||||
{t("wallet.balance.withdrawable")} <i className="i-mingcute-question-line" />
|
||||
</span>
|
||||
|
||||
<Balance className="center text-[12px] font-medium">
|
||||
{myWallet.cashablePowerToken}
|
||||
</Balance>
|
||||
<SettingSectionTitle title={t("wallet.balance.dailyReward")} margin="compact" />
|
||||
<div className="my-1 text-sm">
|
||||
All active users on Follow are eligible for daily power rewards, which can be used for
|
||||
purchases and tipping on Follow.
|
||||
</div>
|
||||
<div className="my-1 text-sm">
|
||||
Based on your level and past activities, you can receive a{" "}
|
||||
<Balance className="align-top" withSuffix>
|
||||
{BigInt(myWallet.todayDailyPower || 0n)}
|
||||
</Balance>{" "}
|
||||
reward today.{" "}
|
||||
<a
|
||||
href="https://github.com/RSSNext/Follow/wiki/Power#daily-reward"
|
||||
target="_blank"
|
||||
className="underline"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
Learn more.
|
||||
</a>
|
||||
</div>
|
||||
<div className="my-2 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="space-y-1">
|
||||
<Level level={myWallet.level?.level || 0} />
|
||||
<ActivityPoints points={myWallet.level?.prevActivityPoints || 0} />
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPortal>
|
||||
<TooltipContent align="start" className="z-[999]">
|
||||
<p>{t("wallet.balance.withdrawableTooltip")}</p>
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</Tooltip>
|
||||
<i className="i-mgc-right-cute-li text-3xl" />
|
||||
<Balance withSuffix>{BigInt(myWallet.todayDailyPower || 0n)}</Balance>
|
||||
</div>
|
||||
<ClaimDailyReward />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -74,8 +74,7 @@ export const PowerRanking: Component = ({ className }) => {
|
|||
</TableCell>
|
||||
<TableCell className="py-2">
|
||||
<div className="flex items-center gap-1">
|
||||
<i className="i-mgc-power align-text-bottom text-accent" />
|
||||
<Balance>{row.powerToken}</Balance>
|
||||
<Balance withSuffix>{row.powerToken}</Balance>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="py-2">
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ import { cn } from "~/lib/utils"
|
|||
import { SettingActionItem, SettingDescription, SettingSwitch } from "./control"
|
||||
|
||||
export const SettingSectionTitle: FC<{
|
||||
title: string
|
||||
title: string | ReactNode
|
||||
|
||||
margin?: "compact" | "normal"
|
||||
}> = ({ title, margin }) => (
|
||||
<div
|
||||
className={cn(
|
||||
"shrink-0 text-sm font-medium capitalize text-gray-400 first:mt-0 dark:text-neutral-500",
|
||||
margin === "compact" ? "mb-2 mt-4" : "mb-4 mt-8",
|
||||
margin === "compact" ? "mb-2 mt-8" : "mb-4 mt-8",
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import { cn } from "~/lib/utils"
|
||||
|
||||
export const ActivityPoints = ({ points, className }: { points: number; className?: string }) => {
|
||||
return (
|
||||
<div className={cn("flex items-center gap-1", className)}>
|
||||
<i className="i-mgc-fire-cute-fi text-red-500" />
|
||||
<span>{points}</span>
|
||||
<sub className="-translate-y-px text-[0.6rem] font-normal">x{Math.floor(points / 10)}</sub>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -25,13 +25,9 @@ export const Balance = ({
|
|||
const formattedFull = format(n, { digits: 18, trailingZeros: true })
|
||||
|
||||
const Content = (
|
||||
<div className={cn("tabular-nums", className)}>
|
||||
{formatted}{" "}
|
||||
{withSuffix && (
|
||||
<span>
|
||||
Power <i className="i-mgc-power align-text-bottom text-accent" />
|
||||
</span>
|
||||
)}
|
||||
<div className={cn("inline-flex items-center gap-1 tabular-nums", className)}>
|
||||
{withSuffix && <i className="i-mgc-power align-text-bottom text-accent" />}
|
||||
<span>{formatted}</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ const TipModalContent_: FC<{
|
|||
<div className="flex w-[80vw] max-w-[350px] flex-col gap-5">
|
||||
<p className="text-sm text-theme-foreground/80">{t("tip_modal.tip_sent")}</p>
|
||||
<p>
|
||||
<Balance className="mr-1 inline-block text-sm" withSuffix>
|
||||
<Balance className="mr-1 text-sm" withSuffix>
|
||||
{amountBigInt}
|
||||
</Balance>{" "}
|
||||
{t("tip_modal.tip_amount_sent")}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#fff" fill-opacity=".01" d="M24 0v24H0V0z"/><path fill="#10161F" fill-rule="evenodd" d="M10.938 2.785c.592-.227 1.333-.18 1.913.347a8.094 8.094 0 0 1 2.276 3.533c.226-.188.492-.324.783-.39a1.767 1.767 0 0 1 1.787.626 18.24 18.24 0 0 1 2.197 3.42C20.52 11.612 21 13.07 21 14.502c0 1.778-.36 3.454-1.369 4.793-1.023 1.357-2.607 2.228-4.77 2.595-1.103.188-1.87-.817-1.71-1.747.249-1.44.074-2.068-.148-2.497-.131-.253-.302-.481-.55-.807l-.013-.017c-.235-.31-.523-.688-.795-1.166a.286.286 0 0 0-.11-.108c-1.056.844-1.497 1.606-1.658 2.246-.164.655-.07 1.318.19 2.018.192.513.094 1.066-.213 1.469a1.43 1.43 0 0 1-1.51.52c-2.341-.613-4.517-2.394-5.25-4.847-.758-2.54.11-5.486 3.251-8.209.868-.751 2.557-2.532 3.337-4.67a2.186 2.186 0 0 1 1.256-1.289" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 858 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#fff" fill-opacity=".01" d="M24 0v24H0V0z"/><path stroke="#10161F" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.343 17.657a15.965 15.965 0 0 0 4.981-4.63c.334-.473.5-.71.5-1.027 0-.317-.166-.554-.5-1.028a15.962 15.962 0 0 0-4.98-4.629"/></svg>
|
||||
|
After Width: | Height: | Size: 357 B |
|
|
@ -218,7 +218,8 @@
|
|||
"titles.profile": "Profile",
|
||||
"titles.shortcuts": "Shortcuts",
|
||||
"wallet.address.title": "Your Address",
|
||||
"wallet.balance.level": "Your Level",
|
||||
"wallet.balance.activePoints": "Active Points",
|
||||
"wallet.balance.dailyReward": "Your Daily Reward",
|
||||
"wallet.balance.title": "Your Balance",
|
||||
"wallet.balance.withdrawable": "Withdrawable",
|
||||
"wallet.balance.withdrawableTooltip": "Withdrawable Power includes both the tips you've received and the Power you've recharged.",
|
||||
|
|
|
|||
|
|
@ -4320,8 +4320,8 @@ declare const levels: drizzle_orm_pg_core.PgTableWithColumns<{
|
|||
baseColumn: never;
|
||||
generated: undefined;
|
||||
}, {}, {}>;
|
||||
preActivePoints: drizzle_orm_pg_core.PgColumn<{
|
||||
name: "pre_active_points";
|
||||
prevActivityPoints: drizzle_orm_pg_core.PgColumn<{
|
||||
name: "prev_activity_points";
|
||||
tableName: "levels";
|
||||
dataType: "number";
|
||||
columnType: "PgInteger";
|
||||
|
|
@ -4336,8 +4336,8 @@ declare const levels: drizzle_orm_pg_core.PgTableWithColumns<{
|
|||
baseColumn: never;
|
||||
generated: undefined;
|
||||
}, {}, {}>;
|
||||
activePoints: drizzle_orm_pg_core.PgColumn<{
|
||||
name: "active_points";
|
||||
activityPoints: drizzle_orm_pg_core.PgColumn<{
|
||||
name: "activity_points";
|
||||
tableName: "levels";
|
||||
dataType: "number";
|
||||
columnType: "PgInteger";
|
||||
|
|
@ -4407,8 +4407,8 @@ declare const levelsOpenAPISchema: zod.ZodObject<{
|
|||
address: zod.ZodString;
|
||||
rank: zod.ZodNullable<zod.ZodNumber>;
|
||||
level: zod.ZodNullable<zod.ZodNumber>;
|
||||
preActivePoints: zod.ZodNullable<zod.ZodNumber>;
|
||||
activePoints: zod.ZodNullable<zod.ZodNumber>;
|
||||
prevActivityPoints: zod.ZodNullable<zod.ZodNumber>;
|
||||
activityPoints: zod.ZodNullable<zod.ZodNumber>;
|
||||
statusUpdatedAt: zod.ZodNullable<zod.ZodString>;
|
||||
powerToken: zod.ZodString;
|
||||
userId: zod.ZodString;
|
||||
|
|
@ -4418,8 +4418,8 @@ declare const levelsOpenAPISchema: zod.ZodObject<{
|
|||
powerToken: string;
|
||||
rank: number | null;
|
||||
level: number | null;
|
||||
preActivePoints: number | null;
|
||||
activePoints: number | null;
|
||||
prevActivityPoints: number | null;
|
||||
activityPoints: number | null;
|
||||
statusUpdatedAt: string | null;
|
||||
}, {
|
||||
userId: string;
|
||||
|
|
@ -4427,8 +4427,8 @@ declare const levelsOpenAPISchema: zod.ZodObject<{
|
|||
powerToken: string;
|
||||
rank: number | null;
|
||||
level: number | null;
|
||||
preActivePoints: number | null;
|
||||
activePoints: number | null;
|
||||
prevActivityPoints: number | null;
|
||||
activityPoints: number | null;
|
||||
statusUpdatedAt: string | null;
|
||||
}>;
|
||||
declare const levelsRelations: drizzle_orm.Relations<"levels", {
|
||||
|
|
@ -5040,19 +5040,6 @@ declare const _routes: hono_hono_base.HonoBase<Env, {
|
|||
status: 200;
|
||||
};
|
||||
};
|
||||
"/wallets/transactions/claim_daily_ttl": {
|
||||
$get: {
|
||||
input: {};
|
||||
output: {
|
||||
code: 0;
|
||||
data: {
|
||||
ttl: number;
|
||||
};
|
||||
};
|
||||
outputFormat: "json" | "text";
|
||||
status: 200;
|
||||
};
|
||||
};
|
||||
"/wallets/transactions/withdraw": {
|
||||
$post: {
|
||||
input: {
|
||||
|
|
@ -5096,15 +5083,13 @@ declare const _routes: hono_hono_base.HonoBase<Env, {
|
|||
dailyPowerToken: string;
|
||||
cashablePowerToken: string;
|
||||
level: {
|
||||
userId: string;
|
||||
address: string;
|
||||
powerToken: string;
|
||||
rank: number | null;
|
||||
level: number | null;
|
||||
preActivePoints: number | null;
|
||||
activePoints: number | null;
|
||||
statusUpdatedAt: string | null;
|
||||
prevActivityPoints: number | null;
|
||||
activityPoints: number | null;
|
||||
} | null;
|
||||
todayDailyPower: string;
|
||||
tomorrowDailyPower: string;
|
||||
}[];
|
||||
};
|
||||
outputFormat: "json" | "text";
|
||||
|
|
@ -5149,8 +5134,8 @@ declare const _routes: hono_hono_base.HonoBase<Env, {
|
|||
powerToken: string;
|
||||
rank: number | null;
|
||||
level: number | null;
|
||||
preActivePoints: number | null;
|
||||
activePoints: number | null;
|
||||
prevActivityPoints: number | null;
|
||||
activityPoints: number | null;
|
||||
statusUpdatedAt: string | null;
|
||||
}[];
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue