Skip to content

Commit

Permalink
fix(chat): Application attachments (Issues #2015, #2018, #2019, #2024) (
Browse files Browse the repository at this point in the history
  • Loading branch information
Derikyan authored Sep 4, 2024
1 parent d05efbe commit 05a74cf
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 58 deletions.
24 changes: 15 additions & 9 deletions apps/chat/src/components/Chat/ChatInput/ChatInputMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ export const ChatInputMessage = ({
const canAttachLinks = useAppSelector(
ConversationsSelectors.selectCanAttachLink,
);
const maximumAttachmentsAmount = useAppSelector(
ConversationsSelectors.selectMaximumAttachmentsAmount,
);
const selectedFiles = useAppSelector(FilesSelectors.selectSelectedFiles);
const selectedFolders = useAppSelector(FilesSelectors.selectSelectedFolders);
const isUploadingFilePresent = useAppSelector(
Expand Down Expand Up @@ -168,6 +171,10 @@ export const ChatInputMessage = ({
isConversationNameInvalid ||
isConversationPathInvalid;

const canAttach =
(canAttachFiles || canAttachFolders || canAttachLinks) &&
!!maximumAttachmentsAmount;

const handleChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
const value = e.target.value;
Expand Down Expand Up @@ -386,14 +393,13 @@ export const ChatInputMessage = ({
return t('Please type a message');
};

const paddingLeftClass =
canAttachFiles || canAttachFolders || canAttachLinks
? isOverlay
? 'pl-11'
: 'pl-12'
: isOverlay
? 'pl-3'
: 'pl-4';
const paddingLeftClass = canAttach
? isOverlay
? 'pl-11'
: 'pl-12'
: isOverlay
? 'pl-3'
: 'pl-4';

return (
<div
Expand Down Expand Up @@ -435,7 +441,7 @@ export const ChatInputMessage = ({
isLoading={isLoading}
isSendDisabled={isSendDisabled}
/>
{(canAttachFiles || canAttachFolders || canAttachLinks) && (
{canAttach && (
<>
<div className="absolute left-4 top-[calc(50%_-_12px)] cursor-pointer rounded disabled:cursor-not-allowed">
<AttachButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function ReviewApplicationDialogView() {
<ModelIcon
entity={application}
entityId={application.id}
size={15}
size={60}
/>
)}
</div>
Expand Down
27 changes: 24 additions & 3 deletions apps/chat/src/components/Common/ApplicationDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ const safeStringify = (

const getItemLabel = (item: string) => item;

const attachmentTypeRegex = new RegExp(
'^([a-zA-Z0-9!*\\-.+]+|\\*)\\/([a-zA-Z0-9!*\\-.+]+|\\*)$',
);

const ApplicationDialogView: React.FC<Props> = ({
onClose,
isEdit,
Expand Down Expand Up @@ -182,6 +186,17 @@ const ApplicationDialogView: React.FC<Props> = ({
[handleDelete, setIsDeleteModalOpen],
);

const handleAttachmentTypesError = useCallback(() => {
setError('inputAttachmentTypes', {
type: 'manual',
message: t(`Please match the MIME format.`) || '',
});
}, [setError, t]);

const handleClearAttachmentTypesError = useCallback(() => {
clearErrors('inputAttachmentTypes');
}, [clearErrors]);

const handleAttachmentTypesChange = useCallback(
(selectedItems: string[]) => {
setInputAttachmentTypes(selectedItems);
Expand Down Expand Up @@ -493,17 +508,23 @@ const ApplicationDialogView: React.FC<Props> = ({
onChangeSelectedItems={handleAttachmentTypesChange}
placeholder={t('Enter one or more attachment types') || ''}
className={classNames(
'flex items-start py-1 pl-0 md:order-3 md:max-w-full',
'flex items-start py-1 pl-0 md:max-w-full',
inputClassName,
)}
hasDeleteAll
itemHeight="31"
validationRegExp={attachmentTypeRegex}
handleError={handleAttachmentTypesError}
handleClearError={handleClearAttachmentTypesError}
hideSuggestions
itemHeightClassName="h-[31px]"
{...restField}
/>
)}
/>
{errors.inputAttachmentTypes && (
<span>{errors.inputAttachmentTypes.message}</span>
<span className="text-xxs text-error peer-invalid:peer-[.submitted]:mb-1">
{errors.inputAttachmentTypes.message}
</span>
)}
</div>

Expand Down
106 changes: 61 additions & 45 deletions apps/chat/src/components/Common/MultipleComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,12 @@ interface Props<T> {
getItemValue: (item: T) => string;
onChangeSelectedItems: (value: T[]) => void;
hasDeleteAll?: boolean;
itemHeight?: string;
itemHeightClassName?: string;
className?: string;
validationRegExp?: RegExp;
handleError?: () => void;
handleClearError?: () => void;
hideSuggestions?: boolean;
}

export function MultipleComboBox<T>({
Expand All @@ -84,22 +88,22 @@ export function MultipleComboBox<T>({
selectedItemRow,
disabled,
hasDeleteAll = false,
itemHeight,
itemHeightClassName,
getItemLabel,
getItemValue,
onChangeSelectedItems,
className,
validationRegExp,
handleError,
handleClearError,
hideSuggestions,
}: Props<T>) {
const { t } = useTranslation(Translation.Common);
const [inputValue, setInputValue] = useState<string | undefined>('');
const [floatingWidth, setFloatingWidth] = useState(0);

const inputRef = useRef<HTMLInputElement>(null);

const itemStyle = itemHeight
? { height: `${itemHeight}` }
: { height: '23px' };

const { x, y, refs, strategy, update } = useFloating({
placement: 'bottom-start',
strategy: 'fixed',
Expand Down Expand Up @@ -195,13 +199,23 @@ export function MultipleComboBox<T>({
return;
}

if (
validationRegExp &&
typeof newSelectedItem === 'string' &&
!validationRegExp.test(newSelectedItem)
) {
handleError?.();
return;
}

addSelectedItem(newSelectedItem);
onChangeSelectedItems([...(selectedItems ?? []), newSelectedItem]);
setInputValue('');

break;

case useCombobox.stateChangeTypes.InputChange:
handleClearError?.();
setInputValue(newInputValue);

break;
Expand Down Expand Up @@ -251,8 +265,8 @@ export function MultipleComboBox<T>({
<span
className={classNames(
'flex items-center justify-between gap-2 rounded bg-accent-primary-alpha px-2 py-1.5',
itemHeightClassName ? itemHeightClassName : 'h-[23px]',
)}
style={itemStyle}
{...getSelectedItemProps({
selectedItem: selectedItemForRender,
index,
Expand Down Expand Up @@ -300,44 +314,46 @@ export function MultipleComboBox<T>({
/>
</div>

<ul
className={classNames(
'z-10 max-h-80 overflow-auto rounded bg-layer-3',
!isOpen && 'hidden',
)}
{...getMenuProps(
{ ref: refs.floating as RefObject<HTMLUListElement> },
{ suppressRefError: true },
)}
style={{
position: strategy,
top: y ?? '',
left: x ?? '',
width: `${floatingWidth}px`,
}}
>
{displayedItems?.length > 0
? displayedItems.map((item, index) => (
<li
className={classNames(
'group flex min-h-[31px] w-full cursor-pointer flex-col justify-center whitespace-break-spaces break-words px-3 text-xs',
highlightedIndex === index && 'bg-accent-primary-alpha',
selectedItem === item && 'bg-accent-primary-alpha',
)}
key={`${getItemValue(item)}${index}`}
{...getItemProps({ item, index })}
>
{itemRow
? createElement(itemRow, { item })
: getItemLabel(item)}
</li>
))
: !!inputValue?.length && (
<li className="px-3 py-2">
{notFoundPlaceholder || t('No available items')}
</li>
)}
</ul>
{!hideSuggestions && (
<ul
className={classNames(
'z-10 max-h-80 overflow-auto rounded bg-layer-3',
!isOpen && 'hidden',
)}
{...getMenuProps(
{ ref: refs.floating as RefObject<HTMLUListElement> },
{ suppressRefError: true },
)}
style={{
position: strategy,
top: y ?? '',
left: x ?? '',
width: `${floatingWidth}px`,
}}
>
{displayedItems?.length > 0
? displayedItems.map((item, index) => (
<li
className={classNames(
'group flex min-h-[31px] w-full cursor-pointer flex-col justify-center whitespace-break-spaces break-words px-3 text-xs',
highlightedIndex === index && 'bg-accent-primary-alpha',
selectedItem === item && 'bg-accent-primary-alpha',
)}
key={`${getItemValue(item)}${index}`}
{...getItemProps({ item, index })}
>
{itemRow
? createElement(itemRow, { item })
: getItemLabel(item)}
</li>
))
: !!inputValue?.length && (
<li className="px-3 py-2">
{notFoundPlaceholder || t('No available items')}
</li>
)}
</ul>
)}
</div>
{hasDeleteAll && selectedItems.length > 0 ? (
<span
Expand Down

0 comments on commit 05a74cf

Please sign in to comment.