http checking

This commit is contained in:
2025-12-21 03:46:37 +07:00
parent d3adf07c3f
commit 6bc35fc0bb

View File

@@ -29,9 +29,43 @@ export function LobbyPage() {
const [showAddGame, setShowAddGame] = useState(false)
const [gameTitle, setGameTitle] = useState('')
const [gameUrl, setGameUrl] = useState('')
const [gameUrlError, setGameUrlError] = useState<string | null>(null)
const [gameGenre, setGameGenre] = useState('')
const [isAddingGame, setIsAddingGame] = useState(false)
const validateUrl = (url: string): boolean => {
if (!url.trim()) return true // Empty is ok, will be caught by required check
try {
const parsed = new URL(url.trim())
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
return false
}
// Check that hostname has at least one dot (domain.tld)
const hostname = parsed.hostname
if (!hostname || !hostname.includes('.')) {
return false
}
// Check that TLD is valid (2-6 letters only, like com, ru, org, online)
const parts = hostname.split('.')
const tld = parts[parts.length - 1].toLowerCase()
if (tld.length < 2 || tld.length > 6 || !/^[a-z]+$/.test(tld)) {
return false
}
return true
} catch {
return false
}
}
const handleGameUrlChange = (value: string) => {
setGameUrl(value)
if (value.trim() && !validateUrl(value)) {
setGameUrlError('Введите корректную ссылку (например: https://store.steampowered.com/...)')
} else {
setGameUrlError(null)
}
}
// Moderation
const [moderatingGameId, setModeratingGameId] = useState<number | null>(null)
@@ -141,7 +175,7 @@ export function LobbyPage() {
}
const handleAddGame = async () => {
if (!id || !gameTitle.trim() || !gameUrl.trim()) return
if (!id || !gameTitle.trim() || !gameUrl.trim() || !validateUrl(gameUrl)) return
setIsAddingGame(true)
try {
@@ -152,6 +186,7 @@ export function LobbyPage() {
})
setGameTitle('')
setGameUrl('')
setGameUrlError(null)
setGameGenre('')
setShowAddGame(false)
await loadData()
@@ -1696,9 +1731,10 @@ export function LobbyPage() {
onChange={(e) => setGameTitle(e.target.value)}
/>
<Input
placeholder="Ссылка для скачивания"
placeholder="Ссылка для скачивания (https://...)"
value={gameUrl}
onChange={(e) => setGameUrl(e.target.value)}
onChange={(e) => handleGameUrlChange(e.target.value)}
error={gameUrlError || undefined}
/>
<Input
placeholder="Жанр (необязательно)"
@@ -1709,11 +1745,11 @@ export function LobbyPage() {
<NeonButton
onClick={handleAddGame}
isLoading={isAddingGame}
disabled={!gameTitle || !gameUrl}
disabled={!gameTitle || !gameUrl || !!gameUrlError}
>
{isOrganizer ? 'Добавить' : 'Предложить'}
</NeonButton>
<NeonButton variant="outline" onClick={() => setShowAddGame(false)}>
<NeonButton variant="outline" onClick={() => { setShowAddGame(false); setGameUrlError(null) }}>
Отмена
</NeonButton>
</div>