mirror of
https://github.com/perfect-panel/ppanel-web.git
synced 2026-02-15 04:41:10 -05:00
🐛 fix(user): Update user subscribe display
This commit is contained in:
parent
7023875548
commit
3bb714d15c
21
CHANGELOG.md
21
CHANGELOG.md
@ -1,24 +1,23 @@
|
|||||||
<a name="readme-top"></a>
|
<a name="readme-top"></a>
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
# [1.0.0-beta.34](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.33...v1.0.0-beta.34) (2025-04-02)
|
# [1.0.0-beta.34](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.33...v1.0.0-beta.34) (2025-04-02)
|
||||||
|
|
||||||
|
|
||||||
### ✨ Features
|
### ✨ Features
|
||||||
|
|
||||||
* **admin**: Add application and rule management entries to localization files ([8b43e69](https://github.com/perfect-panel/ppanel-web/commit/8b43e69))
|
- **admin**: Add application and rule management entries to localization files ([8b43e69](https://github.com/perfect-panel/ppanel-web/commit/8b43e69))
|
||||||
* **api**: Add an interface to obtain user subscription details, update related type definitions and localized text ([cf5c39c](https://github.com/perfect-panel/ppanel-web/commit/cf5c39c))
|
- **api**: Add an interface to obtain user subscription details, update related type definitions and localized text ([cf5c39c](https://github.com/perfect-panel/ppanel-web/commit/cf5c39c))
|
||||||
* **user**: Integrate subscription list into user management, update request parameters and types ([8d49dac](https://github.com/perfect-panel/ppanel-web/commit/8d49dac))
|
- **user**: Integrate subscription list into user management, update request parameters and types ([8d49dac](https://github.com/perfect-panel/ppanel-web/commit/8d49dac))
|
||||||
|
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
* **admin**: Hidden versions and system upgrades ([64cd842](https://github.com/perfect-panel/ppanel-web/commit/64cd842))
|
- **admin**: Hidden versions and system upgrades ([64cd842](https://github.com/perfect-panel/ppanel-web/commit/64cd842))
|
||||||
* **admin**: Modify the label type in the rule form to a string array ([a7aa5fe](https://github.com/perfect-panel/ppanel-web/commit/a7aa5fe))
|
- **admin**: Modify the label type in the rule form to a string array ([a7aa5fe](https://github.com/perfect-panel/ppanel-web/commit/a7aa5fe))
|
||||||
* **node**: Handle potential null value for online users count ([fa2fb28](https://github.com/perfect-panel/ppanel-web/commit/fa2fb28))
|
- **node**: Handle potential null value for online users count ([fa2fb28](https://github.com/perfect-panel/ppanel-web/commit/fa2fb28))
|
||||||
* **subscribe**: Add value prop to field in subscription form for proper state management ([328838d](https://github.com/perfect-panel/ppanel-web/commit/328838d))
|
- **subscribe**: Add value prop to field in subscription form for proper state management ([328838d](https://github.com/perfect-panel/ppanel-web/commit/328838d))
|
||||||
* **subscribe**: Refactor discount calculations and default selection logic in subscription forms ([423b240](https://github.com/perfect-panel/ppanel-web/commit/423b240))
|
- **subscribe**: Refactor discount calculations and default selection logic in subscription forms ([423b240](https://github.com/perfect-panel/ppanel-web/commit/423b240))
|
||||||
* **subscribe**: Update default selection logic in subscription form to ensure proper state management ([ef15374](https://github.com/perfect-panel/ppanel-web/commit/ef15374))
|
- **subscribe**: Update default selection logic in subscription form to ensure proper state management ([ef15374](https://github.com/perfect-panel/ppanel-web/commit/ef15374))
|
||||||
|
|
||||||
<a name="readme-top"></a>
|
<a name="readme-top"></a>
|
||||||
|
|
||||||
|
|||||||
1
apps/admin/services/admin/typings.d.ts
vendored
1
apps/admin/services/admin/typings.d.ts
vendored
@ -1748,6 +1748,7 @@ declare namespace API {
|
|||||||
subscribe: Subscribe;
|
subscribe: Subscribe;
|
||||||
start_time: number;
|
start_time: number;
|
||||||
expire_time: number;
|
expire_time: number;
|
||||||
|
finished_at: number;
|
||||||
reset_time: number;
|
reset_time: number;
|
||||||
traffic: number;
|
traffic: number;
|
||||||
download: number;
|
download: number;
|
||||||
|
|||||||
@ -25,6 +25,24 @@ export async function getApplication(options?: { [key: string]: any }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check verification code POST /v1/common/check_verification_code */
|
||||||
|
export async function checkVerificationCode(
|
||||||
|
body: API.CheckVerificationCodeRequest,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
return request<API.Response & { data?: API.CheckVerificationCodeRespone }>(
|
||||||
|
'/v1/common/check_verification_code',
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
data: body,
|
||||||
|
...(options || {}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** Get verification code POST /v1/common/send_code */
|
/** Get verification code POST /v1/common/send_code */
|
||||||
export async function sendEmailCode(body: API.SendCodeRequest, options?: { [key: string]: any }) {
|
export async function sendEmailCode(body: API.SendCodeRequest, options?: { [key: string]: any }) {
|
||||||
return request<API.Response & { data?: API.SendCodeResponse }>('/v1/common/send_code', {
|
return request<API.Response & { data?: API.SendCodeResponse }>('/v1/common/send_code', {
|
||||||
|
|||||||
12
apps/admin/services/common/typings.d.ts
vendored
12
apps/admin/services/common/typings.d.ts
vendored
@ -121,6 +121,17 @@ declare namespace API {
|
|||||||
telephone: string;
|
telephone: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CheckVerificationCodeRequest = {
|
||||||
|
method: 'email' | 'mobile';
|
||||||
|
account: string;
|
||||||
|
code: string;
|
||||||
|
type: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CheckVerificationCodeRespone = {
|
||||||
|
status: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
type CloseOrderRequest = {
|
type CloseOrderRequest = {
|
||||||
orderNo: string;
|
orderNo: string;
|
||||||
};
|
};
|
||||||
@ -910,6 +921,7 @@ declare namespace API {
|
|||||||
subscribe: Subscribe;
|
subscribe: Subscribe;
|
||||||
start_time: number;
|
start_time: number;
|
||||||
expire_time: number;
|
expire_time: number;
|
||||||
|
finished_at: number;
|
||||||
reset_time: number;
|
reset_time: number;
|
||||||
traffic: number;
|
traffic: number;
|
||||||
download: number;
|
download: number;
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/componen
|
|||||||
import { Separator } from '@workspace/ui/components/separator';
|
import { Separator } from '@workspace/ui/components/separator';
|
||||||
import { Tabs, TabsList, TabsTrigger } from '@workspace/ui/components/tabs';
|
import { Tabs, TabsList, TabsTrigger } from '@workspace/ui/components/tabs';
|
||||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||||
|
import { cn } from '@workspace/ui/lib/utils';
|
||||||
import { differenceInDays, formatDate, isBrowser } from '@workspace/ui/utils';
|
import { differenceInDays, formatDate, isBrowser } from '@workspace/ui/utils';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
@ -88,6 +89,12 @@ export default function Content() {
|
|||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const statusWatermarks = {
|
||||||
|
2: t('finished'),
|
||||||
|
3: t('expired'),
|
||||||
|
4: t('deducted'),
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{userSubscribe.length ? (
|
{userSubscribe.length ? (
|
||||||
@ -159,209 +166,259 @@ export default function Content() {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{userSubscribe.map((item) => (
|
{userSubscribe.map((item) => {
|
||||||
<Card key={item.id}>
|
return (
|
||||||
<CardHeader className='flex flex-row flex-wrap items-center justify-between gap-2 space-y-0'>
|
<Card
|
||||||
<CardTitle className='font-medium'>
|
key={item.id}
|
||||||
{item.subscribe.name}
|
className={cn('relative', {
|
||||||
<p className='text-foreground/50 mt-1 text-sm'>{formatDate(item.start_time)}</p>
|
'relative opacity-80 grayscale': item.status === 3,
|
||||||
</CardTitle>
|
'relative hidden opacity-60 blur-[0.3px] grayscale': item.status === 4,
|
||||||
<div className='flex flex-wrap gap-2'>
|
})}
|
||||||
<AlertDialog>
|
>
|
||||||
<AlertDialogTrigger asChild>
|
{item.status >= 2 && (
|
||||||
<Button size='sm' variant='destructive'>
|
<div
|
||||||
{t('resetSubscription')}
|
className={cn(
|
||||||
</Button>
|
'pointer-events-none absolute left-0 top-0 z-10 h-full w-full overflow-hidden mix-blend-difference',
|
||||||
</AlertDialogTrigger>
|
{
|
||||||
<AlertDialogContent>
|
'text-destructive': item.status === 2,
|
||||||
<AlertDialogHeader>
|
'text-white': item.status === 3 || item.status === 4,
|
||||||
<AlertDialogTitle>{t('prompt')}</AlertDialogTitle>
|
},
|
||||||
<AlertDialogDescription>
|
)}
|
||||||
{t('confirmResetSubscription')}
|
style={{
|
||||||
</AlertDialogDescription>
|
filter: 'contrast(200%) brightness(150%) invert(0.2)',
|
||||||
</AlertDialogHeader>
|
}}
|
||||||
<AlertDialogFooter>
|
>
|
||||||
<AlertDialogCancel>{t('cancel')}</AlertDialogCancel>
|
<div className='absolute inset-0'>
|
||||||
<AlertDialogAction
|
{Array.from({ length: 16 }).map((_, i) => {
|
||||||
onClick={async () => {
|
const row = Math.floor(i / 4);
|
||||||
await resetUserSubscribeToken({
|
const col = i % 4;
|
||||||
user_subscribe_id: item.id,
|
// 计算位置百分比
|
||||||
});
|
const top = 10 + row * 25 + (col % 2 === 0 ? 5 : -5);
|
||||||
await refetch();
|
const left = 5 + col * 30 + (row % 2 === 0 ? 0 : 10);
|
||||||
toast.success(t('resetSuccess'));
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('confirm')}
|
|
||||||
</AlertDialogAction>
|
|
||||||
</AlertDialogFooter>
|
|
||||||
</AlertDialogContent>
|
|
||||||
</AlertDialog>
|
|
||||||
<ResetTraffic id={item.id} replacement={item.subscribe.replacement} />
|
|
||||||
<Renewal id={item.id} subscribe={item.subscribe} />
|
|
||||||
<Unsubscribe id={item.id} allowDeduction={item.subscribe.allow_deduction} />
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<ul className='grid grid-cols-2 gap-3 *:flex *:flex-col *:justify-between lg:grid-cols-4'>
|
|
||||||
<li>
|
|
||||||
<span className='text-muted-foreground'>{t('used')}</span>
|
|
||||||
<span className='text-2xl font-bold'>
|
|
||||||
<Display
|
|
||||||
type='traffic'
|
|
||||||
value={item.upload + item.download}
|
|
||||||
unlimited={!item.traffic}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span className='text-muted-foreground'>{t('totalTraffic')}</span>
|
|
||||||
<span className='text-2xl font-bold'>
|
|
||||||
<Display type='traffic' value={item.traffic} unlimited={!item.traffic} />
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span className='text-muted-foreground'>{t('nextResetDays')}</span>
|
|
||||||
<span className='text-2xl font-semibold'>
|
|
||||||
{item.reset_time
|
|
||||||
? differenceInDays(new Date(item.reset_time), new Date())
|
|
||||||
: t('noReset')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span className='text-muted-foreground'>{t('expirationDays')}</span>
|
|
||||||
<span className='text-2xl font-semibold'>
|
|
||||||
{}
|
|
||||||
{item.expire_time
|
|
||||||
? differenceInDays(new Date(item.expire_time), new Date()) || t('unknown')
|
|
||||||
: t('noLimit')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<Separator className='mt-4' />
|
|
||||||
<Accordion type='single' collapsible defaultValue='0' className='w-full'>
|
|
||||||
{getUserSubscribe(item.token, protocol)?.map((url, index) => (
|
|
||||||
<AccordionItem key={url} value={String(index)}>
|
|
||||||
<AccordionTrigger className='hover:no-underline'>
|
|
||||||
<div className='flex w-full flex-row items-center justify-between'>
|
|
||||||
<CardTitle className='text-sm font-medium'>
|
|
||||||
{t('subscriptionUrl')} {index + 1}
|
|
||||||
</CardTitle>
|
|
||||||
|
|
||||||
<CopyToClipboard
|
return (
|
||||||
text={url}
|
<span
|
||||||
onCopy={(text, result) => {
|
key={i}
|
||||||
if (result) {
|
className='absolute rotate-[-30deg] whitespace-nowrap text-lg font-black opacity-40'
|
||||||
toast.success(t('copySuccess'));
|
style={{
|
||||||
}
|
top: `${top}%`,
|
||||||
|
left: `${left}%`,
|
||||||
|
textShadow: '0px 0px 1px rgba(255,255,255,0.5)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
{statusWatermarks[item.status as keyof typeof statusWatermarks]}
|
||||||
className='text-primary hover:bg-accent mr-4 flex cursor-pointer rounded p-2 text-sm'
|
</span>
|
||||||
onClick={(e) => e.stopPropagation()}
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<CardHeader className='flex flex-row flex-wrap items-center justify-between gap-2 space-y-0'>
|
||||||
|
<CardTitle className='font-medium'>
|
||||||
|
{item.subscribe.name}
|
||||||
|
<p className='text-foreground/50 mt-1 text-sm'>{formatDate(item.start_time)}</p>
|
||||||
|
</CardTitle>
|
||||||
|
{item.status !== 4 && (
|
||||||
|
<div className='flex flex-wrap gap-2'>
|
||||||
|
<AlertDialog>
|
||||||
|
<AlertDialogTrigger asChild>
|
||||||
|
<Button size='sm' variant='destructive'>
|
||||||
|
{t('resetSubscription')}
|
||||||
|
</Button>
|
||||||
|
</AlertDialogTrigger>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>{t('prompt')}</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
{t('confirmResetSubscription')}
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>{t('cancel')}</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
onClick={async () => {
|
||||||
|
await resetUserSubscribeToken({
|
||||||
|
user_subscribe_id: item.id,
|
||||||
|
});
|
||||||
|
await refetch();
|
||||||
|
toast.success(t('resetSuccess'));
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Icon icon='uil:copy' className='mr-2 size-5' />
|
{t('confirm')}
|
||||||
{t('copy')}
|
</AlertDialogAction>
|
||||||
</span>
|
</AlertDialogFooter>
|
||||||
</CopyToClipboard>
|
</AlertDialogContent>
|
||||||
</div>
|
</AlertDialog>
|
||||||
</AccordionTrigger>
|
<ResetTraffic id={item.id} replacement={item.subscribe.replacement} />
|
||||||
<AccordionContent>
|
<Renewal id={item.id} subscribe={item.subscribe} />
|
||||||
<div className='grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6'>
|
|
||||||
{applications
|
|
||||||
?.filter((application) => {
|
|
||||||
const platformApps = application.platform?.[platform];
|
|
||||||
return platformApps && platformApps.length > 0;
|
|
||||||
})
|
|
||||||
.map((application) => {
|
|
||||||
const platformApps = application.platform?.[platform];
|
|
||||||
const app =
|
|
||||||
platformApps?.find((item) => item.is_default) || platformApps?.[0];
|
|
||||||
if (!app) return null;
|
|
||||||
|
|
||||||
const handleCopy = (text: string, result: boolean) => {
|
<Unsubscribe id={item.id} allowDeduction={item.subscribe.allow_deduction} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<ul className='grid grid-cols-2 gap-3 *:flex *:flex-col *:justify-between lg:grid-cols-4'>
|
||||||
|
<li>
|
||||||
|
<span className='text-muted-foreground'>{t('used')}</span>
|
||||||
|
<span className='text-2xl font-bold'>
|
||||||
|
<Display
|
||||||
|
type='traffic'
|
||||||
|
value={item.upload + item.download}
|
||||||
|
unlimited={!item.traffic}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className='text-muted-foreground'>{t('totalTraffic')}</span>
|
||||||
|
<span className='text-2xl font-bold'>
|
||||||
|
<Display type='traffic' value={item.traffic} unlimited={!item.traffic} />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className='text-muted-foreground'>{t('nextResetDays')}</span>
|
||||||
|
<span className='text-2xl font-semibold'>
|
||||||
|
{item.reset_time
|
||||||
|
? differenceInDays(new Date(item.reset_time), new Date())
|
||||||
|
: t('noReset')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className='text-muted-foreground'>{t('expirationDays')}</span>
|
||||||
|
<span className='text-2xl font-semibold'>
|
||||||
|
{}
|
||||||
|
{item.expire_time
|
||||||
|
? differenceInDays(new Date(item.expire_time), new Date()) || t('unknown')
|
||||||
|
: t('noLimit')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<Separator className='mt-4' />
|
||||||
|
<Accordion type='single' collapsible defaultValue='0' className='w-full'>
|
||||||
|
{getUserSubscribe(item.token, protocol)?.map((url, index) => (
|
||||||
|
<AccordionItem key={url} value={String(index)}>
|
||||||
|
<AccordionTrigger className='hover:no-underline'>
|
||||||
|
<div className='flex w-full flex-row items-center justify-between'>
|
||||||
|
<CardTitle className='text-sm font-medium'>
|
||||||
|
{t('subscriptionUrl')} {index + 1}
|
||||||
|
</CardTitle>
|
||||||
|
|
||||||
|
<CopyToClipboard
|
||||||
|
text={url}
|
||||||
|
onCopy={(text, result) => {
|
||||||
if (result) {
|
if (result) {
|
||||||
const href = getAppSubLink(application.subscribe_type, url);
|
toast.success(t('copySuccess'));
|
||||||
const showSuccessMessage = () => {
|
|
||||||
toast.success(
|
|
||||||
<>
|
|
||||||
<p>{t('copySuccess')}</p>
|
|
||||||
<br />
|
|
||||||
<p>{t('manualImportMessage')}</p>
|
|
||||||
</>,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isBrowser() && href) {
|
|
||||||
window.location.href = href;
|
|
||||||
const checkRedirect = setTimeout(() => {
|
|
||||||
if (window.location.href !== href) {
|
|
||||||
showSuccessMessage();
|
|
||||||
}
|
|
||||||
clearTimeout(checkRedirect);
|
|
||||||
}, 1000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
showSuccessMessage();
|
|
||||||
}
|
}
|
||||||
};
|
}}
|
||||||
|
>
|
||||||
return (
|
<span
|
||||||
<div
|
className='text-primary hover:bg-accent mr-4 flex cursor-pointer rounded p-2 text-sm'
|
||||||
key={application.name}
|
onClick={(e) => e.stopPropagation()}
|
||||||
className='text-muted-foreground flex size-full flex-col items-center justify-between gap-2 text-xs'
|
>
|
||||||
>
|
<Icon icon='uil:copy' className='mr-2 size-5' />
|
||||||
<span>{application.name}</span>
|
{t('copy')}
|
||||||
|
</span>
|
||||||
{application.icon && (
|
</CopyToClipboard>
|
||||||
<Image
|
|
||||||
src={application.icon}
|
|
||||||
alt={application.name}
|
|
||||||
width={64}
|
|
||||||
height={64}
|
|
||||||
className='p-1'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className='flex'>
|
|
||||||
<Button
|
|
||||||
size='sm'
|
|
||||||
variant='secondary'
|
|
||||||
className='rounded-r-none px-1.5'
|
|
||||||
asChild
|
|
||||||
>
|
|
||||||
<Link href={app.url}>{t('download')}</Link>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<CopyToClipboard
|
|
||||||
text={getAppSubLink(application.subscribe_type, url) || url}
|
|
||||||
onCopy={handleCopy}
|
|
||||||
>
|
|
||||||
<Button size='sm' className='rounded-l-none p-2'>
|
|
||||||
{t('import')}
|
|
||||||
</Button>
|
|
||||||
</CopyToClipboard>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<div className='text-muted-foreground hidden size-full flex-col items-center justify-between gap-2 text-sm lg:flex'>
|
|
||||||
<span>{t('qrCode')}</span>
|
|
||||||
<QRCodeCanvas
|
|
||||||
value={url}
|
|
||||||
size={80}
|
|
||||||
bgColor='transparent'
|
|
||||||
fgColor='rgb(59, 130, 246)'
|
|
||||||
/>
|
|
||||||
<span className='text-center'>{t('scanToSubscribe')}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AccordionTrigger>
|
||||||
</AccordionContent>
|
<AccordionContent>
|
||||||
</AccordionItem>
|
<div className='grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6'>
|
||||||
))}
|
{applications
|
||||||
</Accordion>
|
?.filter((application) => {
|
||||||
</CardContent>
|
const platformApps = application.platform?.[platform];
|
||||||
</Card>
|
return platformApps && platformApps.length > 0;
|
||||||
))}
|
})
|
||||||
|
.map((application) => {
|
||||||
|
const platformApps = application.platform?.[platform];
|
||||||
|
const app =
|
||||||
|
platformApps?.find((item) => item.is_default) ||
|
||||||
|
platformApps?.[0];
|
||||||
|
if (!app) return null;
|
||||||
|
|
||||||
|
const handleCopy = (text: string, result: boolean) => {
|
||||||
|
if (result) {
|
||||||
|
const href = getAppSubLink(application.subscribe_type, url);
|
||||||
|
const showSuccessMessage = () => {
|
||||||
|
toast.success(
|
||||||
|
<>
|
||||||
|
<p>{t('copySuccess')}</p>
|
||||||
|
<br />
|
||||||
|
<p>{t('manualImportMessage')}</p>
|
||||||
|
</>,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isBrowser() && href) {
|
||||||
|
window.location.href = href;
|
||||||
|
const checkRedirect = setTimeout(() => {
|
||||||
|
if (window.location.href !== href) {
|
||||||
|
showSuccessMessage();
|
||||||
|
}
|
||||||
|
clearTimeout(checkRedirect);
|
||||||
|
}, 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showSuccessMessage();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={application.name}
|
||||||
|
className='text-muted-foreground flex size-full flex-col items-center justify-between gap-2 text-xs'
|
||||||
|
>
|
||||||
|
<span>{application.name}</span>
|
||||||
|
|
||||||
|
{application.icon && (
|
||||||
|
<Image
|
||||||
|
src={application.icon}
|
||||||
|
alt={application.name}
|
||||||
|
width={64}
|
||||||
|
height={64}
|
||||||
|
className='p-1'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className='flex'>
|
||||||
|
<Button
|
||||||
|
size='sm'
|
||||||
|
variant='secondary'
|
||||||
|
className='rounded-r-none px-1.5'
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<Link href={app.url}>{t('download')}</Link>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<CopyToClipboard
|
||||||
|
text={getAppSubLink(application.subscribe_type, url) || url}
|
||||||
|
onCopy={handleCopy}
|
||||||
|
>
|
||||||
|
<Button size='sm' className='rounded-l-none p-2'>
|
||||||
|
{t('import')}
|
||||||
|
</Button>
|
||||||
|
</CopyToClipboard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<div className='text-muted-foreground hidden size-full flex-col items-center justify-between gap-2 text-sm lg:flex'>
|
||||||
|
<span>{t('qrCode')}</span>
|
||||||
|
<QRCodeCanvas
|
||||||
|
value={url}
|
||||||
|
size={80}
|
||||||
|
bgColor='transparent'
|
||||||
|
fgColor='rgb(59, 130, 246)'
|
||||||
|
/>
|
||||||
|
<span className='text-center'>{t('scanToSubscribe')}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
))}
|
||||||
|
</Accordion>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "kopírovat",
|
"copy": "kopírovat",
|
||||||
"copyFailure": "Kopírování selhalo, prosím zkopírujte ručně",
|
"copyFailure": "Kopírování selhalo, prosím zkopírujte ručně",
|
||||||
"copySuccess": "Kopírování úspěšné",
|
"copySuccess": "Kopírování úspěšné",
|
||||||
|
"deducted": "Zrušeno",
|
||||||
"download": "stáhnout",
|
"download": "stáhnout",
|
||||||
"expirationDays": "Doba platnosti/dny",
|
"expirationDays": "Doba platnosti/dny",
|
||||||
|
"expired": "Vypršelo",
|
||||||
|
"finished": "Provoz vyčerpán",
|
||||||
"import": "Importovat",
|
"import": "Importovat",
|
||||||
"latestAnnouncement": "Nejnovější oznámení",
|
"latestAnnouncement": "Nejnovější oznámení",
|
||||||
"manualImportMessage": "Tato aplikace momentálně nepodporuje spuštění, prosím importujte ručně, adresa předplatného byla automaticky zkopírována",
|
"manualImportMessage": "Tato aplikace momentálně nepodporuje spuštění, prosím importujte ručně, adresa předplatného byla automaticky zkopírována",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "Kopieren",
|
"copy": "Kopieren",
|
||||||
"copyFailure": "Kopieren fehlgeschlagen, bitte manuell kopieren",
|
"copyFailure": "Kopieren fehlgeschlagen, bitte manuell kopieren",
|
||||||
"copySuccess": "Kopieren erfolgreich",
|
"copySuccess": "Kopieren erfolgreich",
|
||||||
|
"deducted": "Storniert",
|
||||||
"download": "Herunterladen",
|
"download": "Herunterladen",
|
||||||
"expirationDays": "Ablaufzeit/Tage",
|
"expirationDays": "Ablaufzeit/Tage",
|
||||||
|
"expired": "Abgelaufen",
|
||||||
|
"finished": "Verkehr erschöpft",
|
||||||
"import": "Importieren",
|
"import": "Importieren",
|
||||||
"latestAnnouncement": "Neueste Ankündigung",
|
"latestAnnouncement": "Neueste Ankündigung",
|
||||||
"manualImportMessage": "Diese App unterstützt derzeit keine Aktivierung. Bitte manuell importieren, die Abonnementadresse wurde automatisch kopiert.",
|
"manualImportMessage": "Diese App unterstützt derzeit keine Aktivierung. Bitte manuell importieren, die Abonnementadresse wurde automatisch kopiert.",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
"copyFailure": "Copy failed, please copy manually",
|
"copyFailure": "Copy failed, please copy manually",
|
||||||
"copySuccess": "Copy successful",
|
"copySuccess": "Copy successful",
|
||||||
|
"deducted": "Canceled",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"expirationDays": "Expiration Days",
|
"expirationDays": "Expiration Days",
|
||||||
|
"expired": "Expired",
|
||||||
|
"finished": "Traffic exhausted",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
"latestAnnouncement": "Latest Announcement",
|
"latestAnnouncement": "Latest Announcement",
|
||||||
"manualImportMessage": "This app does not support activation. Please import manually. The subscription address has been copied.",
|
"manualImportMessage": "This app does not support activation. Please import manually. The subscription address has been copied.",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
"copyFailure": "Error al copiar, por favor copia manualmente",
|
"copyFailure": "Error al copiar, por favor copia manualmente",
|
||||||
"copySuccess": "Copia exitosa",
|
"copySuccess": "Copia exitosa",
|
||||||
|
"deducted": "Cancelado",
|
||||||
"download": "descargar",
|
"download": "descargar",
|
||||||
"expirationDays": "Días de vencimiento",
|
"expirationDays": "Días de vencimiento",
|
||||||
|
"expired": "Caducado",
|
||||||
|
"finished": "Tráfico agotado",
|
||||||
"import": "importar",
|
"import": "importar",
|
||||||
"latestAnnouncement": "Último anuncio",
|
"latestAnnouncement": "Último anuncio",
|
||||||
"manualImportMessage": "Esta aplicación no admite la activación por el momento. Por favor, importe manualmente. La dirección de suscripción ha sido copiada automáticamente.",
|
"manualImportMessage": "Esta aplicación no admite la activación por el momento. Por favor, importe manualmente. La dirección de suscripción ha sido copiada automáticamente.",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "copiar",
|
"copy": "copiar",
|
||||||
"copyFailure": "Error al copiar, por favor copia manualmente",
|
"copyFailure": "Error al copiar, por favor copia manualmente",
|
||||||
"copySuccess": "Copia exitosa",
|
"copySuccess": "Copia exitosa",
|
||||||
|
"deducted": "Cancelado",
|
||||||
"download": "descargar",
|
"download": "descargar",
|
||||||
"expirationDays": "Días de vencimiento",
|
"expirationDays": "Días de vencimiento",
|
||||||
|
"expired": "Expirado",
|
||||||
|
"finished": "Tráfico agotado",
|
||||||
"import": "Importar",
|
"import": "Importar",
|
||||||
"latestAnnouncement": "Último anuncio",
|
"latestAnnouncement": "Último anuncio",
|
||||||
"manualImportMessage": "Esta aplicación no admite la activación por el momento, por favor importe manualmente, la dirección de suscripción se ha copiado automáticamente",
|
"manualImportMessage": "Esta aplicación no admite la activación por el momento, por favor importe manualmente, la dirección de suscripción se ha copiado automáticamente",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "کپی",
|
"copy": "کپی",
|
||||||
"copyFailure": "کپی ناموفق بود، لطفاً به صورت دستی کپی کنید",
|
"copyFailure": "کپی ناموفق بود، لطفاً به صورت دستی کپی کنید",
|
||||||
"copySuccess": "کپی با موفقیت انجام شد",
|
"copySuccess": "کپی با موفقیت انجام شد",
|
||||||
|
"deducted": "لغو شده",
|
||||||
"download": "دانلود",
|
"download": "دانلود",
|
||||||
"expirationDays": "روزهای انقضا",
|
"expirationDays": "روزهای انقضا",
|
||||||
|
"expired": "منقضی شده",
|
||||||
|
"finished": "ترافیک تمام شده",
|
||||||
"import": "وارد کردن",
|
"import": "وارد کردن",
|
||||||
"latestAnnouncement": "آخرین اعلامیه",
|
"latestAnnouncement": "آخرین اعلامیه",
|
||||||
"manualImportMessage": "این برنامه از فعالسازی پشتیبانی نمیکند. لطفاً به صورت دستی وارد کنید. آدرس اشتراک کپی شده است.",
|
"manualImportMessage": "این برنامه از فعالسازی پشتیبانی نمیکند. لطفاً به صورت دستی وارد کنید. آدرس اشتراک کپی شده است.",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "kopioi",
|
"copy": "kopioi",
|
||||||
"copyFailure": "Kopiointi epäonnistui, kopioi manuaalisesti",
|
"copyFailure": "Kopiointi epäonnistui, kopioi manuaalisesti",
|
||||||
"copySuccess": "Kopiointi onnistui",
|
"copySuccess": "Kopiointi onnistui",
|
||||||
|
"deducted": "Peruutettu",
|
||||||
"download": "lataa",
|
"download": "lataa",
|
||||||
"expirationDays": "Vanhentumispäivät",
|
"expirationDays": "Vanhentumispäivät",
|
||||||
|
"expired": "Vanhentunut",
|
||||||
|
"finished": "Liikenne loppunut",
|
||||||
"import": "Tuo",
|
"import": "Tuo",
|
||||||
"latestAnnouncement": "Viimeisin ilmoitus",
|
"latestAnnouncement": "Viimeisin ilmoitus",
|
||||||
"manualImportMessage": "Tämä sovellus ei tue herätystä tällä hetkellä, tuo manuaalisesti, tilausosoite on kopioitu automaattisesti",
|
"manualImportMessage": "Tämä sovellus ei tue herätystä tällä hetkellä, tuo manuaalisesti, tilausosoite on kopioitu automaattisesti",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "Copier",
|
"copy": "Copier",
|
||||||
"copyFailure": "Échec de la copie, veuillez copier manuellement",
|
"copyFailure": "Échec de la copie, veuillez copier manuellement",
|
||||||
"copySuccess": "Copie réussie",
|
"copySuccess": "Copie réussie",
|
||||||
|
"deducted": "Annulé",
|
||||||
"download": "télécharger",
|
"download": "télécharger",
|
||||||
"expirationDays": "Date d'expiration/jours",
|
"expirationDays": "Date d'expiration/jours",
|
||||||
|
"expired": "Expiré",
|
||||||
|
"finished": "Trafic épuisé",
|
||||||
"import": "Importer",
|
"import": "Importer",
|
||||||
"latestAnnouncement": "Dernière annonce",
|
"latestAnnouncement": "Dernière annonce",
|
||||||
"manualImportMessage": "Cette application ne prend pas encore en charge l'activation, veuillez importer manuellement, l'adresse d'abonnement a été copiée automatiquement",
|
"manualImportMessage": "Cette application ne prend pas encore en charge l'activation, veuillez importer manuellement, l'adresse d'abonnement a été copiée automatiquement",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "प्रतिलिपि",
|
"copy": "प्रतिलिपि",
|
||||||
"copyFailure": "प्रतिलिपि बनाने में विफल, कृपया मैन्युअल रूप से प्रतिलिपि बनाएँ",
|
"copyFailure": "प्रतिलिपि बनाने में विफल, कृपया मैन्युअल रूप से प्रतिलिपि बनाएँ",
|
||||||
"copySuccess": "प्रतिलिपि सफल",
|
"copySuccess": "प्रतिलिपि सफल",
|
||||||
|
"deducted": "रद्द किया गया",
|
||||||
"download": "डाउनलोड",
|
"download": "डाउनलोड",
|
||||||
"expirationDays": "समाप्ति समय/दिन",
|
"expirationDays": "समाप्ति समय/दिन",
|
||||||
|
"expired": "समाप्त",
|
||||||
|
"finished": "ट्रैफ़िक समाप्त",
|
||||||
"import": "आयात",
|
"import": "आयात",
|
||||||
"latestAnnouncement": "नवीनतम घोषणा",
|
"latestAnnouncement": "नवीनतम घोषणा",
|
||||||
"manualImportMessage": "यह ऐप फिलहाल जागृत करने का समर्थन नहीं करता है, कृपया मैन्युअल रूप से आयात करें, सदस्यता पता स्वचालित रूप से कॉपी कर लिया गया है।",
|
"manualImportMessage": "यह ऐप फिलहाल जागृत करने का समर्थन नहीं करता है, कृपया मैन्युअल रूप से आयात करें, सदस्यता पता स्वचालित रूप से कॉपी कर लिया गया है।",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "Másolás",
|
"copy": "Másolás",
|
||||||
"copyFailure": "Másolás sikertelen, kérjük, másolja kézzel",
|
"copyFailure": "Másolás sikertelen, kérjük, másolja kézzel",
|
||||||
"copySuccess": "Sikeres másolás",
|
"copySuccess": "Sikeres másolás",
|
||||||
|
"deducted": "Törölve",
|
||||||
"download": "letöltés",
|
"download": "letöltés",
|
||||||
"expirationDays": "Lejárati idő/nap",
|
"expirationDays": "Lejárati idő/nap",
|
||||||
|
"expired": "Lejárt",
|
||||||
|
"finished": "Forgalom kimerült",
|
||||||
"import": "importálás",
|
"import": "importálás",
|
||||||
"latestAnnouncement": "Legújabb bejelentés",
|
"latestAnnouncement": "Legújabb bejelentés",
|
||||||
"manualImportMessage": "Ez az alkalmazás jelenleg nem támogatja az ébresztést, kérjük, importálja manuálisan, az előfizetési cím automatikusan másolva lett",
|
"manualImportMessage": "Ez az alkalmazás jelenleg nem támogatja az ébresztést, kérjük, importálja manuálisan, az előfizetési cím automatikusan másolva lett",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "コピー",
|
"copy": "コピー",
|
||||||
"copyFailure": "コピーに失敗しました。手動でコピーしてください",
|
"copyFailure": "コピーに失敗しました。手動でコピーしてください",
|
||||||
"copySuccess": "コピー成功",
|
"copySuccess": "コピー成功",
|
||||||
|
"deducted": "キャンセルされた",
|
||||||
"download": "ダウンロード",
|
"download": "ダウンロード",
|
||||||
"expirationDays": "有効期限/日",
|
"expirationDays": "有効期限/日",
|
||||||
|
"expired": "期限切れ",
|
||||||
|
"finished": "トラフィックが使い切られました",
|
||||||
"import": "インポート",
|
"import": "インポート",
|
||||||
"latestAnnouncement": "最新のお知らせ",
|
"latestAnnouncement": "最新のお知らせ",
|
||||||
"manualImportMessage": "このアプリは現在起動をサポートしていません。手動でインポートしてください。サブスクリプションアドレスは自動的にコピーされました。",
|
"manualImportMessage": "このアプリは現在起動をサポートしていません。手動でインポートしてください。サブスクリプションアドレスは自動的にコピーされました。",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "복사",
|
"copy": "복사",
|
||||||
"copyFailure": "복사 실패, 수동으로 복사하세요",
|
"copyFailure": "복사 실패, 수동으로 복사하세요",
|
||||||
"copySuccess": "복사 성공",
|
"copySuccess": "복사 성공",
|
||||||
|
"deducted": "취소됨",
|
||||||
"download": "다운로드",
|
"download": "다운로드",
|
||||||
"expirationDays": "만료일/일",
|
"expirationDays": "만료일/일",
|
||||||
|
"expired": "만료됨",
|
||||||
|
"finished": "트래픽 소진됨",
|
||||||
"import": "가져오기",
|
"import": "가져오기",
|
||||||
"latestAnnouncement": "최신 공지",
|
"latestAnnouncement": "최신 공지",
|
||||||
"manualImportMessage": "이 앱은 현재 호출을 지원하지 않습니다. 수동으로 가져오세요. 구독 주소가 자동으로 복사되었습니다.",
|
"manualImportMessage": "이 앱은 현재 호출을 지원하지 않습니다. 수동으로 가져오세요. 구독 주소가 자동으로 복사되었습니다.",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "kopier",
|
"copy": "kopier",
|
||||||
"copyFailure": "Kopiering mislyktes, vennligst kopier manuelt",
|
"copyFailure": "Kopiering mislyktes, vennligst kopier manuelt",
|
||||||
"copySuccess": "Kopiering vellykket",
|
"copySuccess": "Kopiering vellykket",
|
||||||
|
"deducted": "Avbrutt",
|
||||||
"download": "last ned",
|
"download": "last ned",
|
||||||
"expirationDays": "Utløpsdato/dager",
|
"expirationDays": "Utløpsdato/dager",
|
||||||
|
"expired": "Utløpt",
|
||||||
|
"finished": "Trafikk brukt opp",
|
||||||
"import": "Importer",
|
"import": "Importer",
|
||||||
"latestAnnouncement": "Siste kunngjøring",
|
"latestAnnouncement": "Siste kunngjøring",
|
||||||
"manualImportMessage": "Denne appen støtter foreløpig ikke oppstart, vennligst importer manuelt, abonnementsadressen er automatisk kopiert",
|
"manualImportMessage": "Denne appen støtter foreløpig ikke oppstart, vennligst importer manuelt, abonnementsadressen er automatisk kopiert",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "kopiuj",
|
"copy": "kopiuj",
|
||||||
"copyFailure": "Kopiowanie nie powiodło się, proszę skopiować ręcznie",
|
"copyFailure": "Kopiowanie nie powiodło się, proszę skopiować ręcznie",
|
||||||
"copySuccess": "Skopiowano pomyślnie",
|
"copySuccess": "Skopiowano pomyślnie",
|
||||||
|
"deducted": "Anulowane",
|
||||||
"download": "pobierz",
|
"download": "pobierz",
|
||||||
"expirationDays": "Czas wygaśnięcia/dni",
|
"expirationDays": "Czas wygaśnięcia/dni",
|
||||||
|
"expired": "Wygasło",
|
||||||
|
"finished": "Wykończony ruch",
|
||||||
"import": "Importuj",
|
"import": "Importuj",
|
||||||
"latestAnnouncement": "Najnowsze ogłoszenie",
|
"latestAnnouncement": "Najnowsze ogłoszenie",
|
||||||
"manualImportMessage": "Ta aplikacja tymczasowo nie obsługuje wywoływania, proszę zaimportować ręcznie, adres subskrypcji został automatycznie skopiowany",
|
"manualImportMessage": "Ta aplikacja tymczasowo nie obsługuje wywoływania, proszę zaimportować ręcznie, adres subskrypcji został automatycznie skopiowany",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
"copyFailure": "Falha ao copiar, por favor copie manualmente",
|
"copyFailure": "Falha ao copiar, por favor copie manualmente",
|
||||||
"copySuccess": "Cópia bem-sucedida",
|
"copySuccess": "Cópia bem-sucedida",
|
||||||
|
"deducted": "Cancelado",
|
||||||
"download": "baixar",
|
"download": "baixar",
|
||||||
"expirationDays": "Data de expiração/dias",
|
"expirationDays": "Data de expiração/dias",
|
||||||
|
"expired": "Expirado",
|
||||||
|
"finished": "Tráfego esgotado",
|
||||||
"import": "Importar",
|
"import": "Importar",
|
||||||
"latestAnnouncement": "Último Anúncio",
|
"latestAnnouncement": "Último Anúncio",
|
||||||
"manualImportMessage": "Este aplicativo não suporta ativação no momento, por favor, importe manualmente. O endereço de assinatura foi copiado automaticamente.",
|
"manualImportMessage": "Este aplicativo não suporta ativação no momento, por favor, importe manualmente. O endereço de assinatura foi copiado automaticamente.",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "Copiază",
|
"copy": "Copiază",
|
||||||
"copyFailure": "Copierea a eșuat, vă rugăm să copiați manual",
|
"copyFailure": "Copierea a eșuat, vă rugăm să copiați manual",
|
||||||
"copySuccess": "Copiere reușită",
|
"copySuccess": "Copiere reușită",
|
||||||
|
"deducted": "Anulat",
|
||||||
"download": "descărcare",
|
"download": "descărcare",
|
||||||
"expirationDays": "Zile până la expirare",
|
"expirationDays": "Zile până la expirare",
|
||||||
|
"expired": "Expirat",
|
||||||
|
"finished": "Trafic epuizat",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
"latestAnnouncement": "Ultimul anunț",
|
"latestAnnouncement": "Ultimul anunț",
|
||||||
"manualImportMessage": "Această aplicație nu suportă momentan activarea, vă rugăm să importați manual, adresa de abonament a fost copiată automat",
|
"manualImportMessage": "Această aplicație nu suportă momentan activarea, vă rugăm să importați manual, adresa de abonament a fost copiată automat",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "Копировать",
|
"copy": "Копировать",
|
||||||
"copyFailure": "Не удалось скопировать, пожалуйста, скопируйте вручную",
|
"copyFailure": "Не удалось скопировать, пожалуйста, скопируйте вручную",
|
||||||
"copySuccess": "Копирование успешно",
|
"copySuccess": "Копирование успешно",
|
||||||
|
"deducted": "Отменено",
|
||||||
"download": "скачать",
|
"download": "скачать",
|
||||||
"expirationDays": "Срок действия/дни",
|
"expirationDays": "Срок действия/дни",
|
||||||
|
"expired": "Истекло",
|
||||||
|
"finished": "Трафик исчерпан",
|
||||||
"import": "Импорт",
|
"import": "Импорт",
|
||||||
"latestAnnouncement": "Последнее объявление",
|
"latestAnnouncement": "Последнее объявление",
|
||||||
"manualImportMessage": "Это приложение временно не поддерживает вызов, пожалуйста, импортируйте вручную, адрес подписки уже скопирован",
|
"manualImportMessage": "Это приложение временно не поддерживает вызов, пожалуйста, импортируйте вручную, адрес подписки уже скопирован",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "คัดลอก",
|
"copy": "คัดลอก",
|
||||||
"copyFailure": "คัดลอกไม่สำเร็จ กรุณาคัดลอกด้วยตนเอง",
|
"copyFailure": "คัดลอกไม่สำเร็จ กรุณาคัดลอกด้วยตนเอง",
|
||||||
"copySuccess": "คัดลอกสำเร็จ",
|
"copySuccess": "คัดลอกสำเร็จ",
|
||||||
|
"deducted": "ยกเลิก",
|
||||||
"download": "ดาวน์โหลด",
|
"download": "ดาวน์โหลด",
|
||||||
"expirationDays": "วันหมดอายุ/วัน",
|
"expirationDays": "วันหมดอายุ/วัน",
|
||||||
|
"expired": "หมดอายุ",
|
||||||
|
"finished": "การใช้งานหมด",
|
||||||
"import": "นำเข้า",
|
"import": "นำเข้า",
|
||||||
"latestAnnouncement": "ประกาศล่าสุด",
|
"latestAnnouncement": "ประกาศล่าสุด",
|
||||||
"manualImportMessage": "แอปนี้ยังไม่รองรับการเปิดใช้งาน กรุณานำเข้าด้วยตนเอง ที่อยู่การสมัครสมาชิกได้ถูกคัดลอกอัตโนมัติแล้ว",
|
"manualImportMessage": "แอปนี้ยังไม่รองรับการเปิดใช้งาน กรุณานำเข้าด้วยตนเอง ที่อยู่การสมัครสมาชิกได้ถูกคัดลอกอัตโนมัติแล้ว",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "kopyala",
|
"copy": "kopyala",
|
||||||
"copyFailure": "Kopyalama başarısız oldu, lütfen elle kopyalayın",
|
"copyFailure": "Kopyalama başarısız oldu, lütfen elle kopyalayın",
|
||||||
"copySuccess": "Kopyalama başarılı",
|
"copySuccess": "Kopyalama başarılı",
|
||||||
|
"deducted": "İptal edildi",
|
||||||
"download": "indir",
|
"download": "indir",
|
||||||
"expirationDays": "Son Kullanma Süresi/Gün",
|
"expirationDays": "Son Kullanma Süresi/Gün",
|
||||||
|
"expired": "Süresi dolmuş",
|
||||||
|
"finished": "Trafik tükendi",
|
||||||
"import": "İçe Aktar",
|
"import": "İçe Aktar",
|
||||||
"latestAnnouncement": "Son Duyuru",
|
"latestAnnouncement": "Son Duyuru",
|
||||||
"manualImportMessage": "Bu uygulama şu anda uyandırmayı desteklemiyor, lütfen elle içe aktarın, abone adresi otomatik olarak kopyalandı",
|
"manualImportMessage": "Bu uygulama şu anda uyandırmayı desteklemiyor, lütfen elle içe aktarın, abone adresi otomatik olarak kopyalandı",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "Копіювати",
|
"copy": "Копіювати",
|
||||||
"copyFailure": "Не вдалося скопіювати, будь ласка, скопіюйте вручну",
|
"copyFailure": "Не вдалося скопіювати, будь ласка, скопіюйте вручну",
|
||||||
"copySuccess": "Скопійовано успішно",
|
"copySuccess": "Скопійовано успішно",
|
||||||
|
"deducted": "Скасовано",
|
||||||
"download": "завантажити",
|
"download": "завантажити",
|
||||||
"expirationDays": "Термін дії/дні",
|
"expirationDays": "Термін дії/дні",
|
||||||
|
"expired": "Термін закінчився",
|
||||||
|
"finished": "Трафік вичерпано",
|
||||||
"import": "Імпорт",
|
"import": "Імпорт",
|
||||||
"latestAnnouncement": "Останнє оголошення",
|
"latestAnnouncement": "Останнє оголошення",
|
||||||
"manualImportMessage": "Цей додаток тимчасово не підтримує виклик, будь ласка, імпортуйте вручну, адресу підписки вже скопійовано",
|
"manualImportMessage": "Цей додаток тимчасово не підтримує виклик, будь ласка, імпортуйте вручну, адресу підписки вже скопійовано",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "Sao chép",
|
"copy": "Sao chép",
|
||||||
"copyFailure": "Sao chép thất bại, vui lòng sao chép thủ công",
|
"copyFailure": "Sao chép thất bại, vui lòng sao chép thủ công",
|
||||||
"copySuccess": "Sao chép thành công",
|
"copySuccess": "Sao chép thành công",
|
||||||
|
"deducted": "Đã hủy",
|
||||||
"download": "tải xuống",
|
"download": "tải xuống",
|
||||||
"expirationDays": "Thời gian hết hạn/ngày",
|
"expirationDays": "Thời gian hết hạn/ngày",
|
||||||
|
"expired": "Hết hạn",
|
||||||
|
"finished": "Lưu lượng đã sử dụng",
|
||||||
"import": "Nhập khẩu",
|
"import": "Nhập khẩu",
|
||||||
"latestAnnouncement": "Thông báo mới nhất",
|
"latestAnnouncement": "Thông báo mới nhất",
|
||||||
"manualImportMessage": "Ứng dụng này hiện không hỗ trợ kích hoạt, vui lòng nhập thủ công, địa chỉ đăng ký đã được sao chép tự động",
|
"manualImportMessage": "Ứng dụng này hiện không hỗ trợ kích hoạt, vui lòng nhập thủ công, địa chỉ đăng ký đã được sao chép tự động",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "复制",
|
"copy": "复制",
|
||||||
"copyFailure": "复制失败,请手动复制",
|
"copyFailure": "复制失败,请手动复制",
|
||||||
"copySuccess": "复制成功",
|
"copySuccess": "复制成功",
|
||||||
|
"deducted": "已取消",
|
||||||
"download": "下载",
|
"download": "下载",
|
||||||
"expirationDays": "到期时间/天",
|
"expirationDays": "到期时间/天",
|
||||||
|
"expired": "已过期",
|
||||||
|
"finished": "流量已用尽",
|
||||||
"import": "导入",
|
"import": "导入",
|
||||||
"latestAnnouncement": "最新公告",
|
"latestAnnouncement": "最新公告",
|
||||||
"manualImportMessage": "该应用暂不支持唤起,请手动导入,已自动复制订阅地址",
|
"manualImportMessage": "该应用暂不支持唤起,请手动导入,已自动复制订阅地址",
|
||||||
|
|||||||
@ -5,8 +5,11 @@
|
|||||||
"copy": "複製",
|
"copy": "複製",
|
||||||
"copyFailure": "複製失敗,請手動複製",
|
"copyFailure": "複製失敗,請手動複製",
|
||||||
"copySuccess": "複製成功",
|
"copySuccess": "複製成功",
|
||||||
|
"deducted": "已取消",
|
||||||
"download": "下載",
|
"download": "下載",
|
||||||
"expirationDays": "到期時間/天",
|
"expirationDays": "到期時間/天",
|
||||||
|
"expired": "已過期",
|
||||||
|
"finished": "流量已用盡",
|
||||||
"import": "匯入",
|
"import": "匯入",
|
||||||
"latestAnnouncement": "最新公告",
|
"latestAnnouncement": "最新公告",
|
||||||
"manualImportMessage": "該應用暫不支援喚起,請手動導入,已自動複製訂閱地址",
|
"manualImportMessage": "該應用暫不支援喚起,請手動導入,已自動複製訂閱地址",
|
||||||
|
|||||||
@ -25,6 +25,24 @@ export async function getApplication(options?: { [key: string]: any }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check verification code POST /v1/common/check_verification_code */
|
||||||
|
export async function checkVerificationCode(
|
||||||
|
body: API.CheckVerificationCodeRequest,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
return request<API.Response & { data?: API.CheckVerificationCodeRespone }>(
|
||||||
|
'/v1/common/check_verification_code',
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
data: body,
|
||||||
|
...(options || {}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** Get verification code POST /v1/common/send_code */
|
/** Get verification code POST /v1/common/send_code */
|
||||||
export async function sendEmailCode(body: API.SendCodeRequest, options?: { [key: string]: any }) {
|
export async function sendEmailCode(body: API.SendCodeRequest, options?: { [key: string]: any }) {
|
||||||
return request<API.Response & { data?: API.SendCodeResponse }>('/v1/common/send_code', {
|
return request<API.Response & { data?: API.SendCodeResponse }>('/v1/common/send_code', {
|
||||||
|
|||||||
12
apps/user/services/common/typings.d.ts
vendored
12
apps/user/services/common/typings.d.ts
vendored
@ -121,6 +121,17 @@ declare namespace API {
|
|||||||
telephone: string;
|
telephone: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CheckVerificationCodeRequest = {
|
||||||
|
method: 'email' | 'mobile';
|
||||||
|
account: string;
|
||||||
|
code: string;
|
||||||
|
type: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CheckVerificationCodeRespone = {
|
||||||
|
status: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
type CloseOrderRequest = {
|
type CloseOrderRequest = {
|
||||||
orderNo: string;
|
orderNo: string;
|
||||||
};
|
};
|
||||||
@ -910,6 +921,7 @@ declare namespace API {
|
|||||||
subscribe: Subscribe;
|
subscribe: Subscribe;
|
||||||
start_time: number;
|
start_time: number;
|
||||||
expire_time: number;
|
expire_time: number;
|
||||||
|
finished_at: number;
|
||||||
reset_time: number;
|
reset_time: number;
|
||||||
traffic: number;
|
traffic: number;
|
||||||
download: number;
|
download: number;
|
||||||
|
|||||||
1
apps/user/services/user/typings.d.ts
vendored
1
apps/user/services/user/typings.d.ts
vendored
@ -1010,6 +1010,7 @@ declare namespace API {
|
|||||||
subscribe: Subscribe;
|
subscribe: Subscribe;
|
||||||
start_time: number;
|
start_time: number;
|
||||||
expire_time: number;
|
expire_time: number;
|
||||||
|
finished_at: number;
|
||||||
reset_time: number;
|
reset_time: number;
|
||||||
traffic: number;
|
traffic: number;
|
||||||
download: number;
|
download: number;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user