feat(power): restrict power usage to wallet

This commit is contained in:
DIYgod 2026-04-30 14:23:23 +08:00
parent 88ad11a2ae
commit e1daec3013
25 changed files with 78 additions and 189 deletions

View File

@ -117,7 +117,7 @@ export const FeedForm: Component<{
isError: feedQuery.isError,
})
}
}, [feedQuery.isLoading])
}, [feedQuery.data?.feed.url, feedQuery.isError, feedQuery.isLoading, id, url])
return (
<div
@ -250,7 +250,7 @@ const FeedInnerForm = ({
useEffect(() => {
setClickOutSideToDismiss(!form.formState.isDirty)
}, [form.formState.isDirty])
}, [form.formState.isDirty, setClickOutSideToDismiss])
useEffect(() => {
if (subscription) {
@ -262,7 +262,7 @@ const FeedInnerForm = ({
form.setValue("hideFromTimeline", subscription.hideFromTimeline)
subscription?.title && form.setValue("title", subscription.title)
}
}, [subscription])
}, [form, subscription])
useEffect(() => {
if (
@ -272,7 +272,7 @@ const FeedInnerForm = ({
) {
form.setValue("view", `${analytics.view}`)
}
}, [analytics, subscription, defaultValues?.view])
}, [analytics, defaultValues?.view, form, subscription])
const followMutation = useMutation({
mutationFn: async (values: z.infer<typeof formSchema>) => {

View File

@ -31,6 +31,8 @@ import { useTOTPModalWrapper } from "~/modules/profile/hooks"
import { Balance } from "~/modules/wallet/balance"
import { useWallet, wallet as walletActions } from "~/queries/wallet"
const RSS3_CONVERSION_RATE = 0.043
export const WithdrawButton = () => {
const { t } = useTranslation("settings")
const { present } = useModalStack()
@ -54,18 +56,22 @@ const WithdrawModalContent = ({ dismiss }: { dismiss: () => void }) => {
const wallet = useWallet()
const cashablePowerTokenBigInt = [BigInt(wallet.data?.[0]!.cashablePowerToken || 0n), 18] as const
const cashablePowerTokenNumber = toNumber(cashablePowerTokenBigInt)
const walletAddress = wallet.data?.[0]?.address ?? "-"
const formSchema = z.object({
address: z.string().startsWith("0x").length(42),
amount: z.number().positive().max(cashablePowerTokenNumber),
toRss3: z.boolean().optional(),
toRss3: z.boolean(),
})
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
toRss3: true,
},
})
const rss3ConversionRate: number | null = null
const withdrawAmount = form.watch("amount")
const receiveAmount = Number.isFinite(withdrawAmount) ? withdrawAmount * RSS3_CONVERSION_RATE : 0
const mutation = useMutation({
mutationFn: async ({
@ -91,7 +97,7 @@ const WithdrawModalContent = ({ dismiss }: { dismiss: () => void }) => {
const present = useTOTPModalWrapper(mutation.mutateAsync, { force: true })
const onSubmit = (values: z.infer<typeof formSchema>) => {
present(values)
present({ ...values, toRss3: true })
}
useEffect(() => {
@ -126,6 +132,9 @@ const WithdrawModalContent = ({ dismiss }: { dismiss: () => void }) => {
</div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4 lg:w-96">
<div className="rounded-md border border-orange/20 bg-orange/10 p-3 text-xs leading-relaxed text-text-secondary">
{t("wallet.withdraw.gasFeeNotice", { address: walletAddress })}
</div>
<FormField
control={form.control}
name="address"
@ -161,7 +170,7 @@ const WithdrawModalContent = ({ dismiss }: { dismiss: () => void }) => {
<FormField
control={form.control}
name="toRss3"
render={({ field }) => (
render={() => (
<FormItem>
<div className="flex items-center gap-2">
<FormLabel className="flex items-center gap-1">
@ -173,7 +182,7 @@ const WithdrawModalContent = ({ dismiss }: { dismiss: () => void }) => {
<TooltipPortal>
<TooltipContent>
<span className="text-xs text-gray-500">
<span>1 POWER = {rss3ConversionRate ?? "-"} RSS3</span>
<span>1 POWER = {RSS3_CONVERSION_RATE} RSS3</span>
</span>
</TooltipContent>
</TooltipPortal>
@ -181,17 +190,15 @@ const WithdrawModalContent = ({ dismiss }: { dismiss: () => void }) => {
</FormLabel>
<FormControl className="!mt-0">
<span className="inline-flex">
<Switch checked={field.value} onCheckedChange={field.onChange} />
<Switch checked={true} disabled />
</span>
</FormControl>
</div>
{field.value && rss3ConversionRate !== null && (
<span className="text-xs text-gray-500">
{t("wallet.withdraw.receiveRSS3", {
amount: ((form.watch("amount") || 0) * rss3ConversionRate).toFixed(4),
})}
</span>
)}
<span className="text-xs text-gray-500">
{t("wallet.withdraw.receiveRSS3", {
amount: receiveAmount.toFixed(4),
})}
</span>
<FormMessage />
</FormItem>
)}

View File

@ -55,7 +55,7 @@ export function AddModalContent({
if (addRSSHubMutation.isSuccess) {
dismiss()
}
}, [addRSSHubMutation.isSuccess])
}, [addRSSHubMutation.isSuccess, dismiss])
useEffect(() => {
if (details.data?.instance.baseUrl) {
@ -64,12 +64,11 @@ export function AddModalContent({
accessKey: details.data.instance.accessKey || undefined,
})
}
}, [details.data])
}, [details.data, form])
const codes = [
`FOLLOW_OWNER_USER_ID=${me?.handle || me?.id} # User id or handle of your follow account`,
`FOLLOW_DESCRIPTION=${instance?.description || `${me?.name}'s instance`} # The description of your instance`,
`FOLLOW_PRICE=${instance?.price || 100} # The monthly price of your instance, set to 0 means free.`,
`FOLLOW_USER_LIMIT=${instance?.userLimit || 1000} # The user limit of your instance, set it to 0 or 1 can make your instance private, leaving it empty means no restriction`,
]

View File

@ -1,25 +1,10 @@
import { Button } from "@follow/components/ui/button/index.js"
import { Card, CardContent } from "@follow/components/ui/card/index.js"
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@follow/components/ui/form/index.jsx"
import { Input } from "@follow/components/ui/input/Input.js"
import { whoami } from "@follow/store/user/getters"
import type { RSSHubListItem } from "@follow-app/client-sdk"
import { zodResolver } from "@hookform/resolvers/zod"
import { useEffect } from "react"
import { useForm } from "react-hook-form"
import { Trans, useTranslation } from "react-i18next"
import { z } from "zod"
import { useTranslation } from "react-i18next"
import { useAuthQuery } from "~/hooks/common"
import { UserAvatar } from "~/modules/user/UserAvatar"
import { Queries } from "~/queries"
import { useSetRSSHubMutation } from "~/queries/rsshub"
import { useTOTPModalWrapper } from "../profile/hooks"
@ -34,35 +19,12 @@ export function SetModalContent({
const { t } = useTranslation("settings")
const setRSSHubMutation = useSetRSSHubMutation()
const preset = useTOTPModalWrapper(setRSSHubMutation.mutateAsync)
const details = useAuthQuery(Queries.rsshub.get({ id: instance.id }))
const hasPurchase = !!details.data?.purchase
const price = instance.ownerUserId === whoami()?.id ? 0 : instance.price
const formSchema = z.object({
months: z.coerce
.number()
.min(hasPurchase ? 0 : 1)
.max(12),
})
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
months: hasPurchase ? 0 : 1,
},
})
const months = form.watch("months")
const onSubmit = (data: z.infer<typeof formSchema>) => {
preset({ id: instance.id, durationInMonths: data.months })
}
useEffect(() => {
if (setRSSHubMutation.isSuccess) {
dismiss()
}
}, [setRSSHubMutation.isSuccess])
}, [setRSSHubMutation.isSuccess, dismiss])
return (
<div className="max-w-[550px] space-y-4 lg:min-w-[550px]">
@ -85,12 +47,6 @@ export function SetModalContent({
<td className="text-sm text-text-secondary">{t("rsshub.table.description")}</td>
<td className="line-clamp-2">{instance.description}</td>
</tr>
<tr>
<td className="text-sm text-text-secondary">{t("rsshub.table.price")}</td>
<td className="flex items-center gap-1">
{instance.price} <i className="i-mgc-power text-folo" />
</td>
</tr>
<tr>
<td className="text-sm text-text-secondary">{t("rsshub.table.userCount")}</td>
<td>{instance.userCount}</td>
@ -103,64 +59,15 @@ export function SetModalContent({
</table>
</CardContent>
</Card>
{details.data?.purchase && (
<div>
<div className="text-sm text-text-secondary">
{t("rsshub.useModal.purchase_expires_at")}
</div>
<div className="line-clamp-2">
{new Date(details.data.purchase.expiresAt).toLocaleString()}
</div>
</div>
)}
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
{price > 0 && (
<FormField
control={form.control}
name="months"
render={({ field }) => (
<FormItem className="flex flex-row items-center gap-4">
<FormLabel>{t("rsshub.useModal.months_label")}</FormLabel>
<FormControl className="!mt-0">
<div className="flex items-center gap-10">
<div className="space-x-2">
<Input
className="w-24"
type="number"
inputMode="numeric"
pattern="[0-9]*"
max={12}
min={hasPurchase ? 0 : 1}
{...field}
/>
<span className="text-sm text-text-secondary">
{t("rsshub.useModal.month")}
</span>
</div>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
<div className="flex items-center justify-end">
<Button type="submit" isLoading={setRSSHubMutation.isPending}>
{price ? (
<Trans
ns="settings"
i18nKey={"rsshub.useModal.useWith"}
components={{ Power: <i className="i-mgc-power ml-1 text-white" /> }}
values={{ amount: price * months }}
/>
) : (
t("rsshub.table.use")
)}
</Button>
</div>
</form>
</Form>
<div className="flex items-center justify-end">
<Button
type="button"
isLoading={setRSSHubMutation.isPending}
onClick={() => preset({ id: instance.id })}
>
{t("rsshub.table.use")}
</Button>
</div>
</div>
)
}

View File

@ -47,8 +47,12 @@ export const PaidBadge: Component<{
</TooltipTrigger>
<TooltipPortal>
<TooltipContent>
{paidLevel === SettingPaidLevels.FreeLimited && t("control.paid_badge.free_limited")}
{paidLevel === SettingPaidLevels.Basic && t("control.paid_badge.basic_or_higher")}
{paidLevel === SettingPaidLevels.FreeLimited && (
<span>{t("control.paid_badge.free_limited")}</span>
)}
{paidLevel === SettingPaidLevels.Basic && (
<span>{t("control.paid_badge.basic_or_higher")}</span>
)}
</TooltipContent>
</TooltipPortal>
</Tooltip>

View File

@ -139,7 +139,7 @@ export const ProfileButton: FC<ProfileButtonProps> = memo((props) => {
}}
icon={<i className="i-mgc-power-outline" />}
>
{t("user_button.power")}
{t("user_button.wallet")}
</DropdownMenuItem>
)}
<DropdownMenuItem

View File

@ -2,7 +2,7 @@ import { Logo } from "@follow/components/icons/logo.jsx"
import { Button } from "@follow/components/ui/button/index.js"
import { RSSHubLogo } from "@follow/components/ui/platform-icon/icons.js"
import { whoami } from "@follow/store/user/getters"
import { cn, formatNumber } from "@follow/utils/utils"
import { cn } from "@follow/utils/utils"
import type { RSSHubListItem } from "@follow-app/client-sdk"
import { memo, useCallback, useEffect } from "react"
import { useTranslation } from "react-i18next"
@ -110,7 +110,6 @@ const InstanceCard = memo(({ item }: { item: InstanceItem }) => {
)
const title = isOfficial ? "Folo Official" : ""
const price = isOfficial ? 0 : instance.price
const description = isOfficial ? "Folo Built-in RSSHub" : instance.description
const usersStat = isOfficial ? "*" : instance.userCount || 0
@ -166,11 +165,6 @@ const InstanceCard = memo(({ item }: { item: InstanceItem }) => {
<div className="flex items-center gap-1">{tags}</div>
</div>
</div>
<div className="text-right">
<div className="flex items-center gap-1 text-sm font-medium">
{formatNumber(price ?? 0)} <i className="i-mgc-power size-3 text-folo" />
</div>
</div>
</div>
<p className="mb-3 line-clamp-1 text-xs text-text-secondary">{description}</p>
@ -276,7 +270,7 @@ function List({ data }: { data?: RSSHubListItem[] }) {
// full load last
if (loadA === 1 && loadB === 1) {
return a.price - b.price
return 0
}
if (loadA === 1) {
return 1
@ -285,7 +279,7 @@ function List({ data }: { data?: RSSHubListItem[] }) {
return -1
}
return a.price - b.price || loadA - loadB
return loadA - loadB
}) || []),
]

View File

@ -108,7 +108,7 @@ export default ({ mode }) => {
host: true,
port: 2233,
watch: {
ignored: ["**/dist/**", "**/out/**", "**/public/**", ".git/**"],
ignored: ["**/dist/**", "**/out/**", "**/public/**", ".git/**", "**/.env", "**/.env.*"],
},
cors: true,
headers: {

View File

@ -7,7 +7,7 @@ import { MagicCard } from '~/components/ui/magic-card'
const tweetList = [
{
id: '1833056589135442345',
text: "Very nice news aggregation, and it gives 2 power token everyday, so far I just try move front end people I followed in, haven't done yet, will try move more rss subscribe.",
text: "Very nice news aggregation. So far I just try move front end people I followed in, haven't done yet, will try move more rss subscribe.",
name: '🦋 AnneInCoding',
screenName: '@anneincoding',
profileImageUrl:

View File

@ -49,6 +49,9 @@ export default defineConfig({
],
server: {
watch: {
ignored: ["**/.env", "**/.env.*"],
},
proxy: {
"/api": {
target: "https://api.follow.is",

View File

@ -241,7 +241,6 @@
"feed_form.feedback": "Feedback",
"feed_form.fill_default": "Fill",
"feed_form.follow": "Follow",
"feed_form.follow_with_fee": "Follow with {{fee}} Power",
"feed_form.followed": "🎉 Followed.",
"feed_form.hide_from_timeline": "Hide from Timeline",
"feed_form.hide_from_timeline_description": "Whether this subscription's entries are visible on your main view timeline.",
@ -504,9 +503,9 @@
"user_button.ai": "AI",
"user_button.download_desktop_app": "Download Desktop app",
"user_button.log_out": "Log out",
"user_button.power": "Power",
"user_button.preferences": "Preferences",
"user_button.profile": "Profile",
"user_button.wallet": "Wallet",
"user_profile.about": "About",
"user_profile.close": "Close",
"user_profile.created_lists": "Created Lists",

View File

@ -241,7 +241,6 @@
"feed_form.feedback": "Retour",
"feed_form.fill_default": "Remplir",
"feed_form.follow": "Suivre",
"feed_form.follow_with_fee": "Suivre avec {{fee}} Puissance",
"feed_form.followed": "🎉 Suivi.",
"feed_form.hide_from_timeline": "Masquer de la chronologie",
"feed_form.hide_from_timeline_description": "Si les entrées de cet abonnement sont visibles sur votre chronologie principale.",
@ -503,9 +502,9 @@
"user_button.ai": "IA",
"user_button.download_desktop_app": "Télécharger appli bureau",
"user_button.log_out": "Déconnexion",
"user_button.power": "Puissance",
"user_button.preferences": "Préférences",
"user_button.profile": "Profil",
"user_button.wallet": "Portefeuille",
"user_profile.about": "À propos",
"user_profile.close": "Fermer",
"user_profile.created_lists": "Listes créées",

View File

@ -241,7 +241,6 @@
"feed_form.feedback": "フィードバック",
"feed_form.fill_default": "入力する",
"feed_form.follow": "フォロー",
"feed_form.follow_with_fee": " {{fee}} Power で購読できます。",
"feed_form.followed": "🎉 フォローしました。",
"feed_form.hide_from_timeline": "タイムラインから非表示",
"feed_form.hide_from_timeline_description": "このサブスクリプションのエントリーがメインビューのタイムラインに表示されるかどうか。",
@ -504,9 +503,9 @@
"user_button.ai": "AI",
"user_button.download_desktop_app": "アプリをダウンロード",
"user_button.log_out": "ログアウト",
"user_button.power": "Power",
"user_button.preferences": "設定",
"user_button.profile": "プロフィール",
"user_button.wallet": "ウォレット",
"user_profile.about": "About",
"user_profile.close": "閉じる",
"user_profile.created_lists": "作成したリスト",

View File

@ -241,7 +241,6 @@
"feed_form.feedback": "反馈",
"feed_form.fill_default": "填充",
"feed_form.follow": "订阅",
"feed_form.follow_with_fee": "使用 {{fee}} Power 订阅",
"feed_form.followed": "🎉 订阅成功",
"feed_form.hide_from_timeline": "在时间线上隐藏",
"feed_form.hide_from_timeline_description": "开启后,此订阅将不再显示在主时间线中",
@ -504,9 +503,9 @@
"user_button.ai": "AI",
"user_button.download_desktop_app": "下载客户端",
"user_button.log_out": "登出",
"user_button.power": "Power",
"user_button.preferences": "设置",
"user_button.profile": "个人资料",
"user_button.wallet": "钱包",
"user_profile.about": "关于",
"user_profile.close": "关闭",
"user_profile.created_lists": "创建的列表",

View File

@ -241,7 +241,6 @@
"feed_form.feedback": "回饋",
"feed_form.fill_default": "填充",
"feed_form.follow": "跟隨",
"feed_form.follow_with_fee": "使用 {{fee}} Power 跟隨",
"feed_form.followed": "🎉 跟隨成功。",
"feed_form.hide_from_timeline": "從時間軸隱藏",
"feed_form.hide_from_timeline_description": "開啟後,此訂閱將不再顯示在主時間軸中",
@ -504,9 +503,9 @@
"user_button.ai": "AI",
"user_button.download_desktop_app": "下載桌面應用程式",
"user_button.log_out": "登出",
"user_button.power": "Power",
"user_button.preferences": "偏好設定",
"user_button.profile": "個人檔案",
"user_button.wallet": "錢包",
"user_profile.about": "關於",
"user_profile.close": "關閉",
"user_profile.created_lists": "已創建列表",

View File

@ -34,7 +34,7 @@
"invitation.earlyAccess": "Folo is currently requires an invitation code to use.",
"invitation.earlyAccessMessage": "😰 Sorry, Folo is currently requires an invitation code to use.",
"invitation.generateButton": "Generate new code",
"invitation.generateCost": "You can spend {{INVITATION_PRICE}} Power to generate an invitation code for your friends.",
"invitation.generateCost": "You can generate an invitation code for your friends.",
"invitation.getCodeMessage": "You can get an invitation code in the following ways:",
"invitation.title": "Invitation Code",
"login.backToWebApp": "Back To Web App",

View File

@ -34,7 +34,7 @@
"invitation.earlyAccess": "Folo nécessite actuellement un code d'invitation.",
"invitation.earlyAccessMessage": "😰 Désolé, Folo nécessite actuellement un code d'invitation pour être utilisé.",
"invitation.generateButton": "Générer un nouveau code",
"invitation.generateCost": "Vous pouvez dépenser {{INVITATION_PRICE}} Power pour générer un code d'invitation pour vos amis.",
"invitation.generateCost": "Vous pouvez générer un code d'invitation pour vos amis.",
"invitation.getCodeMessage": "Vous pouvez obtenir un code d'invitation des manières suivantes :",
"invitation.title": "Code d'invitation",
"login.backToWebApp": "Retour à l'application Web",

View File

@ -34,7 +34,7 @@
"invitation.earlyAccess": "現在、Folo はアーリーアクセス中で、利用には招待コードが必要です。",
"invitation.earlyAccessMessage": "😰 申し訳ありませんが、Folo は現在アーリーアクセス中で、招待コードが必要です。",
"invitation.generateButton": "新しいコードを生成",
"invitation.generateCost": "友達のために招待コードを生成するには、{{INVITATION_PRICE}} Power を消費できます。",
"invitation.generateCost": "友達のために招待コードを生成できます。",
"invitation.getCodeMessage": "以下の方法で招待コードを入手できます:",
"invitation.title": "招待コード",
"login.backToWebApp": "ウェブアプリに戻る",

View File

@ -34,7 +34,7 @@
"invitation.earlyAccess": "Folo 目前处于早期体验阶段,需要邀请码才能使用。",
"invitation.earlyAccessMessage": "😰 抱歉Folo 目前处于早期体验阶段,需要邀请码才能使用。",
"invitation.generateButton": "生成邀请码",
"invitation.generateCost": "花费 {{INVITATION_PRICE}} Power 生成一个邀请码给你的朋友。",
"invitation.generateCost": "你可以为你的朋友生成一个邀请码。",
"invitation.getCodeMessage": "通过以下方式获取:",
"invitation.title": "邀请码",
"login.backToWebApp": "返回网页版",

View File

@ -34,7 +34,7 @@
"invitation.earlyAccess": "Folo 目前處於搶先體驗階段,需要邀請碼才能使用。",
"invitation.earlyAccessMessage": "😰 抱歉Folo 目前處於搶先體驗階段,需要邀請碼才能使用。",
"invitation.generateButton": "產生新邀請碼",
"invitation.generateCost": "您可以花費 {{INVITATION_PRICE}} Power 您的朋友產生邀請碼。",
"invitation.generateCost": "您可以為朋友產生邀請碼。",
"invitation.getCodeMessage": "您可以通過以下方式獲取邀請碼:",
"invitation.title": "邀請碼",
"login.backToWebApp": "返回網頁應用程式",

View File

@ -530,14 +530,14 @@
"invitation.confirmModal.cancel": "Cancel",
"invitation.confirmModal.confirm": "Do you want to continue?",
"invitation.confirmModal.continue": "Continue",
"invitation.confirmModal.message": "Generating an invitation code will cost you {{INVITATION_PRICE}} <PowerIcon /> Power.",
"invitation.confirmModal.message": "Generating an invitation code will use one invitation quota.",
"invitation.confirmModal.title": "Confirm",
"invitation.created_at": "Created at",
"invitation.earlyAccess": "Folo is currently requires an invitation code to use.",
"invitation.earlyAccessMessage": "😰 Sorry, Folo is currently requires an invitation code to use.",
"invitation.generate": "Generate",
"invitation.generateButton": "Generate New Code",
"invitation.generateCost": "You can spend {{INVITATION_PRICE}} <PowerIcon /> Power to generate an invitation code for your friends.",
"invitation.generateCost": "You can generate an invitation code for your friends.",
"invitation.getCodeMessage": "You can get an invitation code through the following methods:",
"invitation.limitationMessage": "Based on your usage time, you can generate up to {{limitation}} invitation codes.",
"invitation.newInvitationSuccess": "🎉 New invitation generated, invite code is copied",
@ -737,7 +737,6 @@
"rsshub.table.limit_reached": "Limit Reached",
"rsshub.table.official": "Official",
"rsshub.table.owner": "Owner",
"rsshub.table.price": "Monthly Price",
"rsshub.table.private": "Private",
"rsshub.table.unavailable": "Unavailable",
"rsshub.table.unlimited": "Unlimited",
@ -746,11 +745,7 @@
"rsshub.table.userLimit": "User Limit",
"rsshub.table.yours": "Yours",
"rsshub.useModal.about": "About this Instance",
"rsshub.useModal.month": "month",
"rsshub.useModal.months_label": "The number of months you want to purchase",
"rsshub.useModal.purchase_expires_at": "You have purchased this Instance, and your purchase expires at",
"rsshub.useModal.title": "RSSHub Instance",
"rsshub.useModal.useWith": "Use with {{amount}} <Power />",
"spotlight.add_rule": "Add rule",
"spotlight.case_sensitive": "Case sensitive",
"spotlight.color": "Color",
@ -865,6 +860,7 @@
"wallet.withdraw.availableBalance": "You have <Balance></Balance> withdrawable Power in your wallet.",
"wallet.withdraw.button": "Withdraw",
"wallet.withdraw.error": "Withdrawal failed: {{error}}",
"wallet.withdraw.gasFeeNotice": "You are responsible for the Ethereum mainnet gas fee. Make sure this wallet address has enough ETH to submit one transaction before withdrawing: {{address}}.",
"wallet.withdraw.modalTitle": "Withdraw Power",
"wallet.withdraw.receiveRSS3": "You will receive {{amount}} RSS3",
"wallet.withdraw.submitButton": "Submit",

View File

@ -526,14 +526,14 @@
"invitation.confirmModal.cancel": "Annuler",
"invitation.confirmModal.confirm": "Voulez-vous continuer ?",
"invitation.confirmModal.continue": "Continuer",
"invitation.confirmModal.message": "Générer un code d'invitation vous coûtera {{INVITATION_PRICE}} <PowerIcon /> Puissance.",
"invitation.confirmModal.message": "Générer un code d'invitation utilisera un quota d'invitation.",
"invitation.confirmModal.title": "Confirmer",
"invitation.created_at": "Créé le",
"invitation.earlyAccess": "Folo nécessite actuellement un code d'invitation pour être utilisé.",
"invitation.earlyAccessMessage": "😰 Désolé, Folo nécessite actuellement un code d'invitation pour être utilisé.",
"invitation.generate": "Générer",
"invitation.generateButton": "Générer un nouveau code",
"invitation.generateCost": "Vous pouvez dépenser {{INVITATION_PRICE}} <PowerIcon /> Puissance pour générer un code d'invitation pour vos amis.",
"invitation.generateCost": "Vous pouvez générer un code d'invitation pour vos amis.",
"invitation.getCodeMessage": "Vous pouvez obtenir un code d'invitation via les méthodes suivantes :",
"invitation.limitationMessage": "En fonction de votre temps d'utilisation, vous pouvez générer jusqu'à {{limitation}} codes d'invitation.",
"invitation.newInvitationSuccess": "🎉 Nouvelle invitation générée, code copié",
@ -719,7 +719,6 @@
"rsshub.table.limit_reached": "Limite atteinte",
"rsshub.table.official": "Officiel",
"rsshub.table.owner": "Propriétaire",
"rsshub.table.price": "Prix mensuel",
"rsshub.table.private": "Privé",
"rsshub.table.unavailable": "Indisponible",
"rsshub.table.unlimited": "Illimité",
@ -728,11 +727,7 @@
"rsshub.table.userLimit": "Limite d'utilisateurs",
"rsshub.table.yours": "Le vôtre",
"rsshub.useModal.about": "À propos de cette instance",
"rsshub.useModal.month": "mois",
"rsshub.useModal.months_label": "Le nombre de mois que vous souhaitez acheter",
"rsshub.useModal.purchase_expires_at": "Vous avez acheté cette instance, et votre achat expire le",
"rsshub.useModal.title": "Instance RSSHub",
"rsshub.useModal.useWith": "Utiliser avec {{amount}} <Power />",
"subscription.actions.comingSoon": "Bientôt disponible",
"subscription.actions.current": "Plan actuel",
"subscription.actions.manage_error": "Une erreur s'est produite lors de l'ouverture de la gestion de l'abonnement.",
@ -830,6 +825,7 @@
"wallet.withdraw.availableBalance": "Vous avez <Balance></Balance> puissance retirable dans votre portefeuille.",
"wallet.withdraw.button": "Retirer",
"wallet.withdraw.error": "Retrait échoué : {{error}}",
"wallet.withdraw.gasFeeNotice": "Vous êtes responsable des gas fees du réseau principal Ethereum. Avant le retrait, assurez-vous que cette adresse de portefeuille dispose d'assez d'ETH pour soumettre une transaction : {{address}}.",
"wallet.withdraw.modalTitle": "Retirer de la puissance",
"wallet.withdraw.receiveRSS3": "Vous recevrez {{amount}} RSS3",
"wallet.withdraw.submitButton": "Soumettre",

View File

@ -526,14 +526,14 @@
"invitation.confirmModal.cancel": "キャンセル",
"invitation.confirmModal.confirm": "続けますか?",
"invitation.confirmModal.continue": "続行",
"invitation.confirmModal.message": "招待コードを生成するには、{{INVITATION_PRICE}} <PowerIcon /> Power が必要です。",
"invitation.confirmModal.message": "招待コードを生成すると招待枠を 1 つ使用します。",
"invitation.confirmModal.title": "確認",
"invitation.created_at": "作成日",
"invitation.earlyAccess": "現在、Folo は<strong>アーリーアクセス</strong>中で、招待コードが必要です。",
"invitation.earlyAccessMessage": "😰 申し訳ありません。Folo は現在アーリーアクセス中で、招待コードが必要です。",
"invitation.generate": "生成",
"invitation.generateButton": "新しいコードを生成",
"invitation.generateCost": "{{INVITATION_PRICE}} <PowerIcon /> Power を消費して、友達のために招待コードを生成できます。",
"invitation.generateCost": "友達のために招待コードを生成できます。",
"invitation.getCodeMessage": "以下の方法で招待コードを取得できます:",
"invitation.limitationMessage": "あなたの使用時間に応じて、最大 {{limitation}} 個の招待コードを生成できます。",
"invitation.newInvitationSuccess": "🎉 新しい招待コードが生成され、クリップボードにコピーされました",
@ -733,7 +733,6 @@
"rsshub.table.limit_reached": "制限に達しました",
"rsshub.table.official": "公式",
"rsshub.table.owner": "所有者",
"rsshub.table.price": "月額の費用",
"rsshub.table.private": "プライベート",
"rsshub.table.unavailable": "利用不可",
"rsshub.table.unlimited": "無制限",
@ -742,11 +741,7 @@
"rsshub.table.userLimit": "ユーザー制限",
"rsshub.table.yours": "あなたの",
"rsshub.useModal.about": "このインスタンスについて",
"rsshub.useModal.month": "月",
"rsshub.useModal.months_label": "購入したい月数",
"rsshub.useModal.purchase_expires_at": "このインスタンスを購入しました、利用期限は",
"rsshub.useModal.title": "RSSHub インスタンス",
"rsshub.useModal.useWith": "使用する {{amount}} <Power />",
"spotlight.add_rule": "ルールを追加",
"spotlight.case_sensitive": "大文字と小文字を区別",
"spotlight.color": "色",
@ -861,6 +856,7 @@
"wallet.withdraw.availableBalance": "引き出し可能な Power は<Balance></Balance>です。",
"wallet.withdraw.button": "引き出し",
"wallet.withdraw.error": "引き出しに失敗しました:{{error}}",
"wallet.withdraw.gasFeeNotice": "Ethereum メインネットの gas fee はご自身で支払う必要があります。引き出し前に、このウォレットアドレスに 1 回のトランザクションを送信できるだけの ETH があることを確認してください: {{address}}。",
"wallet.withdraw.modalTitle": "Power を引き出す",
"wallet.withdraw.receiveRSS3": "{{amount}} RSS3を受け取ります",
"wallet.withdraw.submitButton": "送信",

View File

@ -530,14 +530,14 @@
"invitation.confirmModal.cancel": "取消",
"invitation.confirmModal.confirm": "确认继续?",
"invitation.confirmModal.continue": "继续",
"invitation.confirmModal.message": "生成邀请码将花费 {{INVITATION_PRICE}} <PowerIcon>Power</PowerIcon>。",
"invitation.confirmModal.message": "生成邀请码将消耗一个邀请码额度。",
"invitation.confirmModal.title": "确认",
"invitation.created_at": "创建于",
"invitation.earlyAccess": "Folo 目前处于<strong>早期开发</strong>状态,需要邀请码才能使用。",
"invitation.earlyAccessMessage": "😰 抱歉Folo 目前处于抢先体验阶段,需要邀请码才能使用。",
"invitation.generate": "生成",
"invitation.generateButton": "生成邀请码",
"invitation.generateCost": "你可以花费 {{INVITATION_PRICE}} <PowerIcon /> Power 为你的朋友生成邀请码。",
"invitation.generateCost": "你可以为你的朋友生成一个邀请码。",
"invitation.getCodeMessage": "通过以下方式获取邀请码:",
"invitation.limitationMessage": "根据你的使用时间,你可以生成最多 {{limitation}} 个邀请码。",
"invitation.newInvitationSuccess": "🎉 邀请码已生成,已复制到剪贴板",
@ -737,7 +737,6 @@
"rsshub.table.limit_reached": "达到限制",
"rsshub.table.official": "官方",
"rsshub.table.owner": "所有者",
"rsshub.table.price": "月度价格",
"rsshub.table.private": "私有",
"rsshub.table.unavailable": "不可用",
"rsshub.table.unlimited": "无限制",
@ -746,11 +745,7 @@
"rsshub.table.userLimit": "用户限制",
"rsshub.table.yours": "你的",
"rsshub.useModal.about": "关于此实例",
"rsshub.useModal.month": "个月",
"rsshub.useModal.months_label": "你想购买的月份数量",
"rsshub.useModal.purchase_expires_at": "你已购买此实例,到期时间为",
"rsshub.useModal.title": "RSSHub 实例",
"rsshub.useModal.useWith": "使用 {{amount}} <Power />",
"spotlight.add_rule": "添加规则",
"spotlight.case_sensitive": "区分大小写",
"spotlight.color": "颜色",
@ -865,6 +860,7 @@
"wallet.withdraw.availableBalance": "钱包中有 <Balance></Balance> Power 可提现。",
"wallet.withdraw.button": "提现",
"wallet.withdraw.error": "提现失败:{{error}}",
"wallet.withdraw.gasFeeNotice": "你需要自行支付以太坊主网 gas fee。提现前请确保这个钱包地址有足够 ETH 发起一笔交易:{{address}}。",
"wallet.withdraw.modalTitle": "提现 Power",
"wallet.withdraw.receiveRSS3": "你将收到 {{amount}} RSS3",
"wallet.withdraw.submitButton": "提交",

View File

@ -526,14 +526,14 @@
"invitation.confirmModal.cancel": "取消",
"invitation.confirmModal.confirm": "您想繼續嗎?",
"invitation.confirmModal.continue": "繼續",
"invitation.confirmModal.message": "產生邀請碼將會花費您 {{INVITATION_PRICE}} <PowerIcon>Power</PowerIcon>。",
"invitation.confirmModal.message": "產生邀請碼將使用一個邀請碼額度。",
"invitation.confirmModal.title": "確認",
"invitation.created_at": "建立者:",
"invitation.earlyAccess": "Folo 目前處於<strong>早期開發</strong>狀態,需要邀請碼才能使用。",
"invitation.earlyAccessMessage": "😰 抱歉,關注目前處於搶先體驗階段,需要邀請碼才能使用。",
"invitation.generate": "產生",
"invitation.generateButton": "產生邀請碼",
"invitation.generateCost": "您可以花費 {{INVITATION_PRICE}} <PowerIcon>Power</PowerIcon> 您的朋友產生邀請碼。",
"invitation.generateCost": "您可以為朋友產生邀請碼。",
"invitation.getCodeMessage": "您可以通過以下方式獲取邀請碼:",
"invitation.limitationMessage": "基於您的使用時間,您最多可以產生 {{limitation}} 個邀請碼。",
"invitation.newInvitationSuccess": "🎉 邀請碼已產生,邀請碼已複製",
@ -719,7 +719,6 @@
"rsshub.table.limit_reached": "達到限制",
"rsshub.table.official": "官方",
"rsshub.table.owner": "建立者",
"rsshub.table.price": "每月價格",
"rsshub.table.private": "私人",
"rsshub.table.unavailable": "不可用",
"rsshub.table.unlimited": "無限制",
@ -728,11 +727,7 @@
"rsshub.table.userLimit": "使用者限制",
"rsshub.table.yours": "你的",
"rsshub.useModal.about": "關於此實例伺服器",
"rsshub.useModal.month": "個月",
"rsshub.useModal.months_label": "你想購買的月份數量",
"rsshub.useModal.purchase_expires_at": "你已購買此實例伺服器,到期時間為",
"rsshub.useModal.title": "RSSHub 實例伺服器",
"rsshub.useModal.useWith": "使用 {{amount}} <Power />",
"subscription.actions.comingSoon": "即將推出",
"subscription.actions.current": "目前方案",
"subscription.actions.manage_error": "開啟訂閱管理時發生問題。",
@ -830,6 +825,7 @@
"wallet.withdraw.availableBalance": "錢包中有 <Balance></Balance> Power 可提領。",
"wallet.withdraw.button": "提領",
"wallet.withdraw.error": "提領失敗:{{error}}",
"wallet.withdraw.gasFeeNotice": "你需要自行支付以太坊主網 gas fee。提領前請確保這個錢包地址有足夠 ETH 發起一筆交易:{{address}}。",
"wallet.withdraw.modalTitle": "提領 Power",
"wallet.withdraw.receiveRSS3": "你將收到 {{amount}} RSS3",
"wallet.withdraw.submitButton": "送出",