mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-21 02:40:14 +01:00
✨ feat: Add CallButton component and integrate with SendButton for improved messaging functionality
This commit is contained in:
parent
52a6de2aa7
commit
12d7028a18
5 changed files with 115 additions and 20 deletions
40
client/src/components/Chat/Input/CallButton.tsx
Normal file
40
client/src/components/Chat/Input/CallButton.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React, { forwardRef } from 'react';
|
||||||
|
import { TooltipAnchor } from '~/components/ui';
|
||||||
|
import { SendIcon } from '~/components/svg';
|
||||||
|
import { useLocalize } from '~/hooks';
|
||||||
|
import { cn } from '~/utils';
|
||||||
|
|
||||||
|
const Button = React.memo(
|
||||||
|
forwardRef((props: { disabled: boolean }) => {
|
||||||
|
const localize = useLocalize();
|
||||||
|
return (
|
||||||
|
<TooltipAnchor
|
||||||
|
description={localize('com_nav_call_mode')}
|
||||||
|
render={
|
||||||
|
<button
|
||||||
|
aria-label={localize('com_nav_send_message')}
|
||||||
|
id="call-button"
|
||||||
|
disabled={props.disabled}
|
||||||
|
className={cn(
|
||||||
|
'rounded-full bg-text-primary p-2 text-text-primary outline-offset-4 transition-all duration-200 disabled:cursor-not-allowed disabled:text-text-secondary disabled:opacity-10',
|
||||||
|
)}
|
||||||
|
data-testid="call-button"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<span className="" data-state="closed">
|
||||||
|
<SendIcon size={24} />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
></TooltipAnchor>
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const CallButton = React.memo(
|
||||||
|
forwardRef((props: { disabled: boolean }) => {
|
||||||
|
return <Button disabled={props.disabled} />;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export default CallButton;
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useWatch } from 'react-hook-form';
|
||||||
import { memo, useRef, useMemo, useEffect, useState } from 'react';
|
import { memo, useRef, useMemo, useEffect, useState } from 'react';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
import {
|
import {
|
||||||
|
|
@ -31,6 +32,7 @@ import AudioRecorder from './AudioRecorder';
|
||||||
import { mainTextareaId } from '~/common';
|
import { mainTextareaId } from '~/common';
|
||||||
import CollapseChat from './CollapseChat';
|
import CollapseChat from './CollapseChat';
|
||||||
import StreamAudio from './StreamAudio';
|
import StreamAudio from './StreamAudio';
|
||||||
|
import CallButton from './CallButton';
|
||||||
import StopButton from './StopButton';
|
import StopButton from './StopButton';
|
||||||
import SendButton from './SendButton';
|
import SendButton from './SendButton';
|
||||||
import Mention from './Mention';
|
import Mention from './Mention';
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,71 @@
|
||||||
import React, { forwardRef } from 'react';
|
import React, { forwardRef } from 'react';
|
||||||
import { useWatch } from 'react-hook-form';
|
import { useWatch } from 'react-hook-form';
|
||||||
import type { Control } from 'react-hook-form';
|
import type { Control } from 'react-hook-form';
|
||||||
import { TooltipAnchor } from '~/components/ui';
|
import { TooltipAnchor, SendIcon, CallIcon } from '~/components';
|
||||||
import { SendIcon } from '~/components/svg';
|
|
||||||
import { useLocalize } from '~/hooks';
|
import { useLocalize } from '~/hooks';
|
||||||
import { cn } from '~/utils';
|
import { cn } from '~/utils';
|
||||||
|
|
||||||
type SendButtonProps = {
|
type ButtonProps = {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
control: Control<{ text: string }>;
|
control: Control<{ text: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SubmitButton = React.memo(
|
const ActionButton = forwardRef(
|
||||||
forwardRef((props: { disabled: boolean }, ref: React.ForwardedRef<HTMLButtonElement>) => {
|
(
|
||||||
const localize = useLocalize();
|
props: {
|
||||||
|
disabled: boolean;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
tooltip: string;
|
||||||
|
testId: string;
|
||||||
|
},
|
||||||
|
ref: React.ForwardedRef<HTMLButtonElement>,
|
||||||
|
) => {
|
||||||
return (
|
return (
|
||||||
<TooltipAnchor
|
<TooltipAnchor
|
||||||
description={localize('com_nav_send_message')}
|
description={props.tooltip}
|
||||||
render={
|
render={
|
||||||
<button
|
<button
|
||||||
ref={ref}
|
ref={ref}
|
||||||
aria-label={localize('com_nav_send_message')}
|
aria-label={props.tooltip}
|
||||||
id="send-button"
|
id="action-button"
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded-full bg-text-primary p-2 text-text-primary outline-offset-4 transition-all duration-200 disabled:cursor-not-allowed disabled:text-text-secondary disabled:opacity-10',
|
'rounded-full bg-text-primary p-2 text-text-primary outline-offset-4',
|
||||||
|
'transition-all duration-200',
|
||||||
|
'disabled:cursor-not-allowed disabled:text-text-secondary disabled:opacity-10',
|
||||||
)}
|
)}
|
||||||
data-testid="send-button"
|
data-testid={props.testId}
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
<span className="" data-state="closed">
|
<span className="" data-state="closed">
|
||||||
<SendIcon size={24} />
|
{props.icon}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
></TooltipAnchor>
|
/>
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const SendButton = React.memo(
|
const SendButton = forwardRef((props: ButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
|
||||||
forwardRef((props: SendButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
|
const localize = useLocalize();
|
||||||
const data = useWatch({ control: props.control });
|
const { text } = useWatch({ control: props.control });
|
||||||
return <SubmitButton ref={ref} disabled={props.disabled || !data.text} />;
|
|
||||||
}),
|
const buttonProps = text
|
||||||
);
|
? {
|
||||||
|
icon: <SendIcon size={24} />,
|
||||||
|
tooltip: localize('com_nav_send_message'),
|
||||||
|
testId: 'send-button',
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
icon: <CallIcon size={24} />,
|
||||||
|
tooltip: localize('com_nav_call'),
|
||||||
|
testId: 'call-button',
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ActionButton ref={ref} disabled={props.disabled} {...buttonProps} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
SendButton.displayName = 'SendButton';
|
||||||
|
|
||||||
export default SendButton;
|
export default SendButton;
|
||||||
|
|
|
||||||
30
client/src/components/svg/CallIcon.tsx
Normal file
30
client/src/components/svg/CallIcon.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { cn } from '~/utils';
|
||||||
|
|
||||||
|
export default function CallIcon({ size = 24, className = '' }) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox={'0 0 24 24'}
|
||||||
|
fill="none"
|
||||||
|
className={cn('text-white dark:text-black', className)}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M9.5 4C8.67157 4 8 4.67157 8 5.5V18.5C8 19.3284 8.67157 20 9.5 20C10.3284 20 11 19.3284 11 18.5V5.5C11 4.67157 10.3284 4 9.5 4Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
d="M13 8.5C13 7.67157 13.6716 7 14.5 7C15.3284 7 16 7.67157 16 8.5V15.5C16 16.3284 15.3284 17 14.5 17C13.6716 17 13 16.3284 13 15.5V8.5Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
d="M4.5 9C3.67157 9 3 9.67157 3 10.5V13.5C3 14.3284 3.67157 15 4.5 15C5.32843 15 6 14.3284 6 13.5V10.5C6 9.67157 5.32843 9 4.5 9Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
d="M19.5 9C18.6716 9 18 9.67157 18 10.5V13.5C18 14.3284 18.6716 15 19.5 15C20.3284 15 21 14.3284 21 13.5V10.5C21 9.67157 20.3284 9 19.5 9Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -56,3 +56,4 @@ export { default as SpeechIcon } from './SpeechIcon';
|
||||||
export { default as SaveIcon } from './SaveIcon';
|
export { default as SaveIcon } from './SaveIcon';
|
||||||
export { default as CircleHelpIcon } from './CircleHelpIcon';
|
export { default as CircleHelpIcon } from './CircleHelpIcon';
|
||||||
export { default as BedrockIcon } from './BedrockIcon';
|
export { default as BedrockIcon } from './BedrockIcon';
|
||||||
|
export { default as CallIcon } from './CallIcon';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue