feat: Introduce user-editable source ID field with automatic generation and editing state handling.

This commit is contained in:
kuekhaoyang
2026-02-16 23:35:44 +08:00
parent a9888353a3
commit ba615e8965
4 changed files with 53 additions and 16 deletions

View File

@@ -14,7 +14,7 @@ interface AddSourceModalProps {
}
export function AddSourceModal({ isOpen, onClose, onAdd, existingIds, initialValues }: AddSourceModalProps) {
const { name, setName, url, setUrl, error, handleSubmit } = useAddSourceForm({
const { name, setName, customId, setCustomId, url, setUrl, error, handleSubmit, isEditing } = useAddSourceForm({
isOpen,
existingIds,
onAdd,
@@ -53,6 +53,24 @@ export function AddSourceModal({ isOpen, onClose, onAdd, existingIds, initialVal
/>
</div>
<div>
<label htmlFor="source-id" className="block mb-2 font-medium text-[var(--text-color)]">
ID
</label>
<input
id="source-id"
type="text"
value={customId}
onChange={(e) => setCustomId(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ''))}
placeholder="自动生成,可手动修改"
disabled={isEditing}
className="w-full bg-[var(--glass-bg)] backdrop-blur-md border border-[var(--glass-border)] rounded-[var(--radius-2xl)] px-4 py-3 text-[var(--text-color)] placeholder:text-[var(--text-color-secondary)] focus:outline-none focus:border-[var(--accent-color)] focus:ring-4 focus:ring-[color-mix(in_srgb,var(--accent-color)_30%,transparent)] transition-all duration-[0.4s] disabled:opacity-50"
/>
<p className="mt-1 text-xs text-[var(--text-color-secondary)]">
</p>
</div>
<div>
<label htmlFor="source-url" className="block mb-2 font-medium text-[var(--text-color)]">

View File

@@ -15,8 +15,15 @@ interface UseAddSourceFormProps {
initialValues?: VideoSource | null;
}
function generateIdFromName(name: string): string {
const slug = name.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
return slug || `custom-${Date.now().toString(36)}`;
}
export function useAddSourceForm({ isOpen, existingIds, onAdd, onClose, initialValues }: UseAddSourceFormProps) {
const [name, setName] = useState('');
const [customId, setCustomId] = useState('');
const [idManuallyEdited, setIdManuallyEdited] = useState(false);
const [url, setUrl] = useState('');
const [error, setError] = useState('');
@@ -24,15 +31,31 @@ export function useAddSourceForm({ isOpen, existingIds, onAdd, onClose, initialV
if (isOpen) {
if (initialValues) {
setName(initialValues.name);
setCustomId(initialValues.id);
setUrl(initialValues.baseUrl);
setIdManuallyEdited(true);
} else {
setName('');
setCustomId('');
setUrl('');
setIdManuallyEdited(false);
}
setError('');
}
}, [isOpen, initialValues]);
const handleNameChange = (newName: string) => {
setName(newName);
if (!idManuallyEdited && !initialValues) {
setCustomId(generateIdFromName(newName));
}
};
const handleIdChange = (newId: string) => {
setIdManuallyEdited(true);
setCustomId(newId);
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setError('');
@@ -49,18 +72,11 @@ export function useAddSourceForm({ isOpen, existingIds, onAdd, onClose, initialV
return;
}
let id = initialValues?.id;
const id = customId.trim() || generateIdFromName(name);
// Only generate new ID if not editing or if name changed (optional, maybe keep ID stable?)
// For now, let's keep ID stable if editing, unless we want to allow re-generating ID.
// But if we re-generate ID, we lose history/preferences for that ID.
// So better to keep ID if editing.
if (!id) {
id = name.toLowerCase().replace(/[^a-z0-9]/g, '-');
if (existingIds.includes(id)) {
setError('此源名称已存在');
return;
}
if (!initialValues && existingIds.includes(id)) {
setError('此源 ID 已存在,请修改源 ID');
return;
}
const newSource: VideoSource = {
@@ -79,10 +95,13 @@ export function useAddSourceForm({ isOpen, existingIds, onAdd, onClose, initialV
return {
name,
setName,
setName: handleNameChange,
customId,
setCustomId: handleIdChange,
url,
setUrl,
error,
handleSubmit,
isEditing: !!initialValues,
};
}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "kvideo",
"version": "4.1.6",
"version": "4.1.7",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "kvideo",
"version": "4.1.6",
"version": "4.1.7",
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "kvideo",
"version": "4.1.6",
"version": "4.1.7",
"private": true,
"scripts": {
"dev": "next dev",