Hide TCP VLESS tab by default
This commit is contained in:
parent
97e7a4a3bb
commit
081bedd637
@ -33,15 +33,40 @@ export type VlessQrCopy = {
|
|||||||
interface VlessQrCardProps {
|
interface VlessQrCardProps {
|
||||||
uuid: string | null | undefined
|
uuid: string | null | undefined
|
||||||
copy: VlessQrCopy
|
copy: VlessQrCopy
|
||||||
|
defaultTransport?: VlessTransport
|
||||||
|
visibleTransports?: VlessTransport[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_TRANSPORT: VlessTransport = 'xhttp'
|
||||||
|
const DEFAULT_VISIBLE_TRANSPORTS: VlessTransport[] = ['xhttp']
|
||||||
|
|
||||||
|
function normalizeVisibleTransports(transports?: VlessTransport[]): VlessTransport[] {
|
||||||
|
const visible = (transports ?? DEFAULT_VISIBLE_TRANSPORTS).filter(
|
||||||
|
(transport, index, values) => values.indexOf(transport) === index,
|
||||||
|
)
|
||||||
|
|
||||||
|
return visible.length > 0 ? visible : DEFAULT_VISIBLE_TRANSPORTS
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveInitialTransport(defaultTransport: VlessTransport | undefined, visibleTransports: VlessTransport[]): VlessTransport {
|
||||||
|
if (defaultTransport && visibleTransports.includes(defaultTransport)) {
|
||||||
|
return defaultTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
return visibleTransports[0] ?? DEFAULT_TRANSPORT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function VlessQrCard({
|
export default function VlessQrCard({
|
||||||
uuid,
|
uuid,
|
||||||
copy,
|
copy,
|
||||||
|
defaultTransport,
|
||||||
|
visibleTransports,
|
||||||
}: VlessQrCardProps) {
|
}: VlessQrCardProps) {
|
||||||
const { data: allNodes, error: nodesError } = useSWR<VlessNode[]>('user-center-agent-nodes', fetchAgentNodes)
|
const { data: allNodes, error: nodesError } = useSWR<VlessNode[]>('user-center-agent-nodes', fetchAgentNodes)
|
||||||
|
|
||||||
|
const transportOptions = useMemo(() => normalizeVisibleTransports(visibleTransports), [visibleTransports])
|
||||||
|
|
||||||
const nodes = useMemo(() => {
|
const nodes = useMemo(() => {
|
||||||
return (allNodes ?? []).filter((node) => {
|
return (allNodes ?? []).filter((node) => {
|
||||||
const name = (node.name || '').toLowerCase()
|
const name = (node.name || '').toLowerCase()
|
||||||
@ -53,7 +78,9 @@ export default function VlessQrCard({
|
|||||||
})
|
})
|
||||||
}, [allNodes])
|
}, [allNodes])
|
||||||
const [selectedNode, setSelectedNode] = useState<VlessNode | null>(null)
|
const [selectedNode, setSelectedNode] = useState<VlessNode | null>(null)
|
||||||
const [preferredTransport, setPreferredTransport] = useState<VlessTransport>('tcp')
|
const [preferredTransport, setPreferredTransport] = useState<VlessTransport>(() =>
|
||||||
|
resolveInitialTransport(defaultTransport, transportOptions),
|
||||||
|
)
|
||||||
const [isSelectorOpen, setIsSelectorOpen] = useState(false)
|
const [isSelectorOpen, setIsSelectorOpen] = useState(false)
|
||||||
|
|
||||||
const [qrDataUrl, setQrDataUrl] = useState<string | null>(null)
|
const [qrDataUrl, setQrDataUrl] = useState<string | null>(null)
|
||||||
@ -61,6 +88,16 @@ export default function VlessQrCard({
|
|||||||
const [generationError, setGenerationError] = useState<string | null>(null)
|
const [generationError, setGenerationError] = useState<string | null>(null)
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPreferredTransport((currentTransport) => {
|
||||||
|
if (transportOptions.includes(currentTransport)) {
|
||||||
|
return currentTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolveInitialTransport(defaultTransport, transportOptions)
|
||||||
|
})
|
||||||
|
}, [defaultTransport, transportOptions])
|
||||||
|
|
||||||
const rawNode = useMemo(() => {
|
const rawNode = useMemo(() => {
|
||||||
if (selectedNode) return selectedNode
|
if (selectedNode) return selectedNode
|
||||||
|
|
||||||
@ -238,7 +275,7 @@ export default function VlessQrCard({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{(['tcp', 'xhttp'] as const).map((transport) => (
|
{transportOptions.map((transport) => (
|
||||||
<button
|
<button
|
||||||
key={transport}
|
key={transport}
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@ -0,0 +1,53 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
|
import VlessQrCard, { type VlessQrCopy } from '../VlessQrCard'
|
||||||
|
|
||||||
|
vi.mock('next/image', () => ({
|
||||||
|
default: () => null,
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('qrcode', () => ({
|
||||||
|
toDataURL: vi.fn(() => Promise.resolve('data:image/png;base64,test')),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('swr', () => ({
|
||||||
|
default: vi.fn(() => ({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
name: 'JP-XHTTP.SVC.PLUS',
|
||||||
|
address: 'jp-xhttp.svc.plus',
|
||||||
|
port: 443,
|
||||||
|
xhttp_port: 443,
|
||||||
|
tcp_port: 1443,
|
||||||
|
uri_scheme_xhttp: 'vless://${UUID}@${DOMAIN}:443?type=xhttp&path=${PATH}#${TAG}',
|
||||||
|
uri_scheme_tcp: 'vless://${UUID}@${DOMAIN}:1443?type=tcp&flow=${FLOW}#${TAG}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: undefined,
|
||||||
|
})),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const copy: VlessQrCopy = {
|
||||||
|
label: 'VLESS QR',
|
||||||
|
description: 'Scan to import VLESS config.',
|
||||||
|
linkLabel: 'VLESS link',
|
||||||
|
linkHelper: 'Copy link helper',
|
||||||
|
copyLink: 'Copy link',
|
||||||
|
copied: 'Copied',
|
||||||
|
downloadQr: 'Download QR',
|
||||||
|
generating: 'Generating',
|
||||||
|
error: 'Failed to generate',
|
||||||
|
missingUuid: 'Missing UUID',
|
||||||
|
qrAlt: 'VLESS QR code',
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('VlessQrCard', () => {
|
||||||
|
it('only renders the XHTTP transport tab by default', () => {
|
||||||
|
render(<VlessQrCard uuid="11111111-1111-4111-8111-111111111111" copy={copy} />)
|
||||||
|
|
||||||
|
expect(screen.getByRole('button', { name: /xhttp/i })).toBeInTheDocument()
|
||||||
|
expect(screen.queryByRole('button', { name: /tcp/i })).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue
Block a user