- Updated the VPN configuration commands and handlers to eliminate the device limit parameter, simplifying the configuration process. - Adjusted related API documentation to reflect the removal of device limit management, clarifying that this setting is now handled directly in the 3x-ui by node administrators. - Enhanced the overall codebase by removing unnecessary device limit references across various components, ensuring a cleaner and more maintainable code structure.
89 lines
3.4 KiB
TypeScript
89 lines
3.4 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
import { useMutation, useQuery } from '@tanstack/react-query'
|
|
import { useNavigate } from '@tanstack/react-router'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { QRCodeSVG } from 'qrcode.react'
|
|
import { Send } from 'lucide-react'
|
|
import { toast } from '@/shared/ui/toast-store'
|
|
import { Button } from '@/shared/ui/button'
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/shared/ui/dialog'
|
|
import { applyAuthResponse } from '@/features/auth/api'
|
|
import { createLoginRequest, getLoginRequestStatus } from './api'
|
|
import type { TelegramLoginStatus } from '@/shared/api/types'
|
|
|
|
const TERMINAL: TelegramLoginStatus[] = ['Rejected', 'Expired', 'Consumed']
|
|
|
|
export function TelegramLoginButton() {
|
|
const { t } = useTranslation()
|
|
const navigate = useNavigate()
|
|
const [open, setOpen] = useState(false)
|
|
const [requestId, setRequestId] = useState<string | null>(null)
|
|
const [deepLink, setDeepLink] = useState<string | null>(null)
|
|
|
|
const startMutation = useMutation({
|
|
mutationFn: createLoginRequest,
|
|
onSuccess: (data) => {
|
|
setRequestId(data.requestId)
|
|
setDeepLink(data.deepLink)
|
|
setOpen(true)
|
|
},
|
|
onError: () => toast.error(t('auth.genericError')),
|
|
})
|
|
|
|
const statusQuery = useQuery({
|
|
queryKey: ['telegram-login-status', requestId],
|
|
queryFn: () => getLoginRequestStatus(requestId!),
|
|
enabled: open && !!requestId,
|
|
refetchInterval: (query) => {
|
|
const status = query.state.data?.status
|
|
return status && (status === 'Approved' || TERMINAL.includes(status)) ? false : 2000
|
|
},
|
|
})
|
|
|
|
const status = statusQuery.data?.status
|
|
|
|
useEffect(() => {
|
|
if (status !== 'Approved' || !statusQuery.data?.accessToken || !statusQuery.data.user) return
|
|
applyAuthResponse({
|
|
accessToken: statusQuery.data.accessToken,
|
|
expiresAt: statusQuery.data.expiresAt!,
|
|
user: statusQuery.data.user,
|
|
})
|
|
setOpen(false)
|
|
void navigate({ to: '/dashboard' })
|
|
}, [status, statusQuery.data, navigate])
|
|
|
|
return (
|
|
<>
|
|
<Button type="button" variant="outline" className="w-full" onClick={() => startMutation.mutate()} disabled={startMutation.isPending}>
|
|
<Send className="h-4 w-4" />
|
|
{t('auth.loginViaTelegram')}
|
|
</Button>
|
|
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>{t('auth.loginViaTelegram')}</DialogTitle>
|
|
</DialogHeader>
|
|
<div className="flex flex-col items-center gap-4">
|
|
{deepLink && (
|
|
<>
|
|
<QRCodeSVG value={deepLink} size={200} />
|
|
<a href={deepLink} target="_blank" rel="noreferrer" className="text-sm text-primary hover:underline">
|
|
{deepLink}
|
|
</a>
|
|
<p className="text-center text-sm text-muted-foreground">{t('auth.telegramQrHint')}</p>
|
|
</>
|
|
)}
|
|
{!deepLink && <p className="text-sm text-muted-foreground">{t('auth.telegramBotNotConfigured')}</p>}
|
|
|
|
{status === 'Pending' && <p className="text-sm text-muted-foreground">{t('auth.waitingForConfirmation')}</p>}
|
|
{status === 'Rejected' && <p className="text-sm text-red-500">{t('auth.telegramLoginRejected')}</p>}
|
|
{status === 'Expired' && <p className="text-sm text-red-500">{t('auth.telegramLoginExpired')}</p>}
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
)
|
|
}
|