ai-voicebot/client/src/NameSetter.tsx

162 lines
5.0 KiB
TypeScript

import React, { useState, KeyboardEvent, useRef } from "react";
import { Input, Button, Box, Typography, Tooltip, Dialog, DialogTitle, DialogContent, DialogActions } from "@mui/material";
import { Session } from "./GlobalContext";
interface NameSetterProps {
session: Session;
sendJsonMessage: (message: any) => void;
onNameSet?: () => void;
initialName?: string;
initialPassword?: string;
}
const NameSetter: React.FC<NameSetterProps> = ({
session,
sendJsonMessage,
onNameSet,
initialName = "",
initialPassword = "",
}) => {
const [editName, setEditName] = useState<string>(initialName);
const [editPassword, setEditPassword] = useState<string>(initialPassword);
const [showDialog, setShowDialog] = useState<boolean>(!session.name);
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
const nameInputRef = useRef<HTMLInputElement>(null);
const passwordInputRef = useRef<HTMLInputElement>(null);
const setName = (name: string) => {
setIsSubmitting(true);
sendJsonMessage({
type: "set_name",
data: { name, password: editPassword ? editPassword : undefined },
});
if (onNameSet) {
onNameSet();
}
setShowDialog(false);
setIsSubmitting(false);
setEditName("");
setEditPassword("");
};
const handleNameKeyDown = (event: KeyboardEvent<HTMLInputElement>): void => {
if (event.key === "Enter") {
event.preventDefault();
if (passwordInputRef.current) {
passwordInputRef.current.focus();
}
}
};
const handlePasswordKeyDown = (event: KeyboardEvent<HTMLInputElement>): void => {
if (event.key === "Enter") {
event.preventDefault();
handleSubmit();
}
};
const handleSubmit = () => {
const newName = editName.trim();
if (!newName || (session?.name && session.name === newName)) {
return;
}
setName(newName);
};
const handleOpenDialog = () => {
setEditName(session.name || "");
setEditPassword("");
setShowDialog(true);
// Focus the name input when dialog opens
setTimeout(() => {
if (nameInputRef.current) {
nameInputRef.current.focus();
}
}, 100);
};
const handleCloseDialog = () => {
setShowDialog(false);
setEditName("");
setEditPassword("");
};
const hasNameChanged = editName.trim() !== (session.name || "");
const canSubmit = editName.trim() && hasNameChanged && !isSubmitting;
return (
<Box sx={{ gap: 1, display: "flex", flexDirection: "column", alignItems: "flex-start" }}>
{session.name && !showDialog && (
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<Typography>You are logged in as: {session.name}</Typography>
<Button variant="outlined" size="small" onClick={handleOpenDialog}>
Change Name
</Button>
</Box>
)}
{/* Dialog for name change */}
<Dialog open={showDialog} onClose={handleCloseDialog} maxWidth="sm" fullWidth>
<DialogTitle>
{session.name ? "Change Your Name" : "Enter Your Name"}
</DialogTitle>
<DialogContent>
<Box sx={{ display: "flex", flexDirection: "column", gap: 2, pt: 1 }}>
<Typography variant="body2" color="text.secondary">
{session.name
? "Enter a new name to change your current name."
: "Enter your name to join the lobby."
}
</Typography>
<Typography variant="caption" color="text.secondary">
You can optionally set a password to reserve this name; supply it again to takeover the name from another client.
</Typography>
<Input
inputRef={nameInputRef}
type="text"
value={editName}
onChange={(e): void => {
setEditName(e.target.value);
}}
onKeyDown={handleNameKeyDown}
placeholder="Your name"
fullWidth
autoFocus
/>
<Input
inputRef={passwordInputRef}
type="password"
value={editPassword}
onChange={(e): void => setEditPassword(e.target.value)}
onKeyDown={handlePasswordKeyDown}
placeholder="Optional password"
fullWidth
/>
<Tooltip title="Optional: choose a short password to reserve this name. Keep it secret.">
<span />
</Tooltip>
</Box>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseDialog} disabled={isSubmitting}>
Cancel
</Button>
<Button
variant="contained"
onClick={handleSubmit}
disabled={!canSubmit}
color={hasNameChanged ? "primary" : "inherit"}
>
{isSubmitting ? "Changing..." : (session.name ? "Change Name" : "Join")}
</Button>
</DialogActions>
</Dialog>
</Box>
);
};
export default NameSetter;