feat: add AddUrlDialog component and keyboard shortcut for adding URLs
Some checks failed
Frontend CI / lint (push) Failing after 5m6s
Frontend CI / build (push) Has been skipped

This commit is contained in:
JSC
2025-07-06 16:57:41 +02:00
parent 44c9c204f6
commit 3fad1d773e
4 changed files with 357 additions and 1 deletions

View File

@@ -0,0 +1,146 @@
import { useState, useEffect } from 'react'
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import { Label } from '@/components/ui/label'
import { toast } from 'sonner'
import { apiService } from '@/services/api'
interface AddUrlDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
}
export function AddUrlDialog({ open, onOpenChange }: AddUrlDialogProps) {
const [url, setUrl] = useState('')
const [loading, setLoading] = useState(false)
// Reset form when dialog opens
useEffect(() => {
if (open) {
setUrl('')
setLoading(false)
}
}, [open])
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!url.trim()) {
toast.error('Please enter a URL')
return
}
if (!isValidUrl(url.trim())) {
toast.error('Please enter a valid URL')
return
}
setLoading(true)
try {
const response = await apiService.post('/api/stream/add-url', { url: url.trim() })
const data = await response.json()
if (response.ok) {
toast.success('URL added to processing queue!')
onOpenChange(false)
} else {
if (response.status === 409) {
toast.error('This URL is already in the system')
} else {
toast.error(data.error || 'Failed to add URL')
}
}
} catch (error) {
console.error('Error adding URL:', error)
toast.error('Failed to add URL. Please try again.')
} finally {
setLoading(false)
}
}
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !loading) {
handleSubmit(e as any)
}
if (e.key === 'Escape') {
onOpenChange(false)
}
}
const isValidUrl = (string: string) => {
try {
const url = new URL(string)
return url.protocol === 'http:' || url.protocol === 'https:'
} catch {
return false
}
}
const getSupportedServices = () => {
return [
'YouTube',
'SoundCloud',
'Dailymotion',
'Spotify',
'Vimeo',
'Twitch',
'and many more...'
]
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Add Media URL</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="url">Media URL</Label>
<Input
id="url"
type="url"
placeholder="https://www.youtube.com/watch?v=..."
value={url}
onChange={(e) => setUrl(e.target.value)}
onKeyDown={handleKeyDown}
disabled={loading}
autoFocus
/>
<p className="text-sm text-muted-foreground">
Supported services: {getSupportedServices().join(', ')}
</p>
</div>
<div className="flex justify-end space-x-2">
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
disabled={loading}
>
Cancel
</Button>
<Button type="submit" disabled={loading || !url.trim()}>
{loading ? 'Adding...' : 'Add to Queue'}
</Button>
</div>
</form>
<div className="text-xs text-muted-foreground mt-4 p-3 bg-muted rounded">
<p className="font-medium mb-1">How it works:</p>
<ul className="list-disc list-inside space-y-1">
<li>Paste any supported media URL</li>
<li>Audio will be extracted automatically</li>
<li>New sound will be added to your soundboard</li>
<li>Processing may take a few moments</li>
<li>Check the streams page to monitor progress</li>
</ul>
</div>
</DialogContent>
</Dialog>
)
}