in projects/deliberation_at_scale/packages/frontend/components/ChatInput.tsx [43:181]
export default function ChatInput(props: ChatInputProps) {
const { _ } = useLingui();
const { externalRoomId, roomId, participantId } = useRoom();
const defaultPlaceholder = _(msg`Tap to type`);
const { onSubmit, disabled = false, placeholder = defaultPlaceholder, helpAvailable} = props;
const theme = useTheme();
const [createHelpRequest] = useCreateHelpRequestMutation();
const [helpMenu, setHelpMenu] = useState(false);
const isMobile = useIsMobile();
const inputRef = useRef<HTMLInputElement>(null);
const [input, setInput] = useState('');
const handleSubmit = useCallback(async (e: FormEvent<HTMLFormElement>) => {
const validatedInput = input.trim();
const userInput: UserInput = { content: validatedInput };
e.preventDefault();
// guard: check if the content is valid
if (isEmpty(validatedInput)) {
return;
}
const hasBeenSent = await onSubmit(userInput);
// only clear when the message was sent out
if (hasBeenSent) {
setInput('');
}
}, [input, onSubmit, setInput]);
const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
setInput(e.target.value);
}, [setInput]);
const fetchHelp = useCallback(async (type: HelpRequestType) => {
await createHelpRequest({
variables: {
roomId: roomId,
participantId: participantId,
type,
externalRoomUrl: externalRoomId ?? '',
},
});
}, [roomId, participantId, externalRoomId, createHelpRequest]);
// focus the input when it is enabled
useEffect(() => {
if (disabled) {
return;
}
if (inputRef.current && !isMobile) {
inputRef.current.focus();
}
}, [inputRef, disabled, isMobile]);
return(
<motion.form
layoutId="chat-input"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ y: 100 }}
onSubmit={handleSubmit}
className={`relative flex gap-2 z-10 ${disabled ? 'grayscale cursor-not-allowed' : ''}`}
>
{helpAvailable && (
<div className="relative aspect-square group shrink-0 m-2"
onMouseLeave={function (): void {
setHelpMenu(false);
}}>
<motion.button
type="button"
className={`duration-300 w-[40px] h-[40px] peer peer rounded-full grow saturate-0 group-hover:saturate-100 transition-all text-white bg-red-300`}
whileTap={{ scale: (disabled ? 1: 0.9) }}
disabled={disabled}
onClick={() => {
setHelpMenu(!helpMenu);
}}
>
<FontAwesomeIcon icon={faQuestion} />
</motion.button>
{helpMenu && (
<motion.div className="absolute w-max max-w-[90vw] md:max-w-[350px] z-50 hover:opacity-100 transition-opacity -translate-y-full top-0 text-black flex flex-col gap-1 pb-4">
<div className="p-2 md:p-4 shadow-xl bg-white rounded-lg border">
<p className="pb-2 md:pb-4">Running into technical problems? Try refreshing the page first. If that doesn't work, you can call a facilitator.</p>
<Button className="p-2 shadow-xl" onClick={function (): void {
const promiseAdmin = fetchHelp(HelpRequestType.Facilitator);
toast.promise(
promiseAdmin,
{
loading: _(msg`Looking for facilitator`),
success: () => _(msg`Facilitator on the way!`),
error: (err) => _(msg`This just happened: ${err.toString()}`),
},
{
style: {
minWidth: '250px',
},
success: {
duration: 5000,
icon: '🤙',
},
loading: {
duration: 5000,
icon: '👀',
},
}
);
} }>
<FontAwesomeIcon icon={faHandsHelping} />
<Trans>Call a Facilitator</Trans>
</Button>
</div>
</motion.div>
)}
</div>
)}
<div className={`flex rounded-md border w-full bg-white focus-within:ring ${focusColorMap[theme]} ${hoverBorderMap[theme]}`}>
<input
ref={inputRef}
className={`py-3 px-4 outline-none rounded-md border-none w-full bg-none`}
placeholder={placeholder}
value={input}
onChange={handleChange}
disabled={disabled}
/>
<motion.button
type="submit"
className={`transition-colors duration-1000 rounded m-2 p-2 w-10 aspect-square shrink-0 text-white ${submitBgColorMap[theme]}`}
whileTap={{ scale: (disabled ? 1: 0.9) }}
disabled={disabled}
>
<FontAwesomeIcon icon={faPaperPlane} />
</motion.button>
</div>
</motion.form>
);
}