http checking
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user