Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(chat): Fix recipient of the shared code app can not upload new files in the code editor (Issues #3040, #2968, #2971) #3061

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
5732c8f
feat: add possibility to upload files for shared code app
denys-kolomiitsev Jan 30, 2025
b5bd796
Merge branch 'development' into fix/3040-upload-files-to-the-shared-f…
denys-kolomiitsev Jan 30, 2025
6c7ddfb
fix: resolve comments
denys-kolomiitsev Jan 30, 2025
26e4fbe
Merge branch 'fix/3040-upload-files-to-the-shared-folder' of https://…
denys-kolomiitsev Jan 30, 2025
1d397db
Merge branch 'development' into fix/3040-upload-files-to-the-shared-f…
denys-kolomiitsev Jan 30, 2025
efcd871
fix: resolve comments
denys-kolomiitsev Jan 31, 2025
21e7795
fix: fix file upload to the app source folder
denys-kolomiitsev Jan 31, 2025
704b728
fix: refactor SelectFolderModal
denys-kolomiitsev Jan 31, 2025
1ea7243
Merge branch 'development' of https://github.com/epam/ai-dial-chat in…
denys-kolomiitsev Jan 31, 2025
fcf59a6
fix: fix after merge
denys-kolomiitsev Jan 31, 2025
551a41f
Merge branch 'development' into fix/3040-upload-files-to-the-shared-f…
denys-kolomiitsev Feb 3, 2025
39c8ceb
fix: resolve comments
denys-kolomiitsev Feb 3, 2025
14d70eb
Merge branch 'fix/3040-upload-files-to-the-shared-folder' of https://…
denys-kolomiitsev Feb 3, 2025
7ca67ca
Merge branch 'development' into fix/3040-upload-files-to-the-shared-f…
denys-kolomiitsev Feb 3, 2025
c19ccab
fix: fix tests
denys-kolomiitsev Feb 3, 2025
317a888
Merge branch 'fix/3040-upload-files-to-the-shared-folder' of https://…
denys-kolomiitsev Feb 3, 2025
a7881a3
fix: hide footer buttons for 'shared with me'
denys-kolomiitsev Feb 3, 2025
aac8210
fix: fix typo
denys-kolomiitsev Feb 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 2 additions & 19 deletions apps/chat/src/components/Chat/TalkTo/TalkToCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
getApplicationNextStatus,
getApplicationSimpleStatus,
getModelShortDescription,
getPlayerCaption,
isApplicationStatusUpdating,
isExecutableApp,
} from '@/src/utils/app/application';
Expand All @@ -24,10 +25,7 @@ import { isMyApplication } from '@/src/utils/app/id';
import { canWriteSharedWithMe } from '@/src/utils/app/share';
import { PseudoModel, isPseudoModel } from '@/src/utils/server/api';

import {
ApplicationStatus,
SimpleApplicationStatus,
} from '@/src/types/applications';
import { SimpleApplicationStatus } from '@/src/types/applications';
import { Conversation } from '@/src/types/chat';
import { FeatureType } from '@/src/types/common';
import { DisplayMenuItemProps } from '@/src/types/menu';
Expand Down Expand Up @@ -62,21 +60,6 @@ import ShareIcon from '../../Common/ShareIcon';
import IconUserUnshare from '@/public/images/icons/unshare-user.svg';
import { Feature } from '@epam/ai-dial-shared';

const getPlayerCaption = (entity: DialAIEntityModel) => {
switch (entity.functionStatus) {
case ApplicationStatus.DEPLOYED:
return 'Undeploy';
case ApplicationStatus.UNDEPLOYED:
case ApplicationStatus.FAILED:
return 'Deploy';
case ApplicationStatus.UNDEPLOYING:
return 'Undeploying';
case ApplicationStatus.DEPLOYING:
default:
return 'Deploying';
}
};

interface ApplicationCardProps {
entity: DialAIEntityModel;
conversation: Conversation;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useCallback } from 'react';
import { useCallback, useMemo } from 'react';
import { useFormContext } from 'react-hook-form';

import classNames from 'classnames';

import { useTranslation } from '@/src/hooks/useTranslation';

import { constructPath } from '@/src/utils/app/file';
import { splitEntityId } from '@/src/utils/app/folders';
import { getIdWithoutRootPathSegments } from '@/src/utils/app/id';

import { Translation } from '@/src/types/translation';
Expand Down Expand Up @@ -36,6 +37,11 @@ export const CodeAppExampleLink = ({

const dispatch = useAppDispatch();

const bucket = useMemo(() => {
const { bucket } = splitEntityId(folderId);
return bucket;
}, [folderId]);

const handleClick = useCallback(() => {
const example = CODE_APPS_EXAMPLES[exampleType];
Object.entries(example.files).forEach(([newFileName, content]) => {
Expand All @@ -48,6 +54,7 @@ export const CodeAppExampleLink = ({
relativePath: getIdWithoutRootPathSegments(folderId),
id: constructPath(folderId, newFileName),
name: newFileName,
bucket,
denys-kolomiitsev marked this conversation as resolved.
Show resolved Hide resolved
}),
);
}
Expand Down Expand Up @@ -83,7 +90,7 @@ export const CodeAppExampleLink = ({
}
});
}
}, [exampleType, fileNames, dispatch, folderId, setValue, getValues]);
}, [exampleType, fileNames, dispatch, folderId, bucket, getValues, setValue]);

return (
<span
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,11 +313,20 @@ export const CodeEditor = ({ sourcesFolderId }: Props) => {
rootFolders: [],
};
}, [files, folders, sourcesFolderId]);

const rootFileNames = useMemo(
() => rootFiles.map((f) => f.name),
[rootFiles],
);

const { bucket, parentPath } = useMemo(() => {
if (sourcesFolderId) {
const { bucket, parentPath } = splitEntityId(sourcesFolderId);
return { bucket, parentPath };
}
return { bucket: undefined, parentPath: undefined };
}, [sourcesFolderId]);

useEffect(() => {
if (sourcesFolderId) {
dispatch(CodeEditorActions.initCodeEditor({ sourcesFolderId }));
Expand All @@ -342,6 +351,11 @@ export const CodeEditor = ({ sourcesFolderId }: Props) => {
[dispatch, openedFoldersIds],
);

const openUploadDialog = useCallback(() => {
setUploadFolderId(sourcesFolderId);
dispatch(FilesActions.getFolders({ id: parentPath }));
}, [dispatch, parentPath, sourcesFolderId]);

const handleUploadFiles = useCallback(
(
selectedFiles: Required<Pick<DialFile, 'fileContent' | 'id' | 'name'>>[],
Expand All @@ -354,11 +368,12 @@ export const CodeEditor = ({ sourcesFolderId }: Props) => {
id: file.id,
relativePath: folderPath,
name: file.name,
bucket,
}),
);
});
},
[dispatch],
[bucket, dispatch],
);

const handleDeleteFile = useCallback(
Expand Down Expand Up @@ -395,7 +410,6 @@ export const CodeEditor = ({ sourcesFolderId }: Props) => {
const handleUploadEmptyFile = useCallback(
(fileName: string) => {
if (fileName && sourcesFolderId) {
const { bucket } = splitEntityId(sourcesFolderId);
dispatch(
FilesActions.uploadFile({
fileContent: new File([''], fileName, {
Expand All @@ -411,7 +425,7 @@ export const CodeEditor = ({ sourcesFolderId }: Props) => {
setNewFileName('');
}
},
[dispatch, sourcesFolderId],
[bucket, dispatch, sourcesFolderId],
);

const handleToggleFolder = useCallback(
Expand Down Expand Up @@ -597,7 +611,7 @@ export const CodeEditor = ({ sourcesFolderId }: Props) => {
<Tooltip tooltip={t('Upload file')}>
<button
type="button"
onClick={() => setUploadFolderId(sourcesFolderId)}
onClick={openUploadDialog}
className="text-secondary hover:text-accent-primary"
>
<IconUpload size={18} />
Expand Down Expand Up @@ -652,6 +666,7 @@ export const CodeEditor = ({ sourcesFolderId }: Props) => {
onUploadFiles={handleUploadFiles}
onClose={() => setUploadFolderId(undefined)}
maximumAttachmentsAmount={Number.MAX_SAFE_INTEGER}
rootFolderId={sourcesFolderId}
/>
)}
<ConfirmDialog
Expand Down
36 changes: 24 additions & 12 deletions apps/chat/src/components/Common/FolderContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
hasInvalidNameInPath,
isEntityNameInvalid,
} from '@/src/utils/app/common';
import { canEditSharedFolderOrParent } from '@/src/utils/app/folders';
import { isEntityIdExternal, isMyEntity } from '@/src/utils/app/id';
import { isEntityIdPublic } from '@/src/utils/app/publications';

Expand All @@ -26,6 +27,7 @@ import { FolderInterface } from '@/src/types/folder';
import { DisplayMenuItemProps } from '@/src/types/menu';
import { Translation } from '@/src/types/translation';

import { FilesSelectors } from '@/src/store/files/files.reducers';
import { useAppSelector } from '@/src/store/hooks';
import { SettingsSelectors } from '@/src/store/settings/settings.reducers';

Expand Down Expand Up @@ -79,11 +81,23 @@ export const FolderContextMenu = ({
SettingsSelectors.isSharingEnabled(state, featureType),
);

const folders = useAppSelector(FilesSelectors.selectFolders);

const isExternal = isEntityIdExternal(folder);
const isNameInvalid = isEntityNameInvalid(folder.name);
const isInvalidPath = hasInvalidNameInPath(folder.folderId);
const disableAll = isNameInvalid || isInvalidPath;

const canEditShared = useMemo(() => {
return canEditSharedFolderOrParent(folders, folder.folderId);
}, [folder.folderId, folders]);

const isMyFolder = useMemo(() => {
return isMyEntity(folder, featureType);
}, [featureType, folder]);

const isMyOrCanEdit = isMyFolder || canEditShared;

const menuItems: DisplayMenuItemProps[] = useMemo(
() => [
{
Expand All @@ -95,15 +109,15 @@ export const FolderContextMenu = ({
},
{
name: t('Upload'),
display: !!onUpload && !isExternal,
display: !!onUpload && isMyOrCanEdit,
dataQa: 'upload',
Icon: IconUpload,
onClick: onUpload,
disabled: disableAll,
},
{
name: t('Rename'),
display: (!!onRename && !isExternal) || !!folder.temporary,
display: !!onRename && (!isExternal || !!folder.temporary),
dataQa: 'rename',
Icon: IconPencilMinus,
onClick: onRename,
Expand Down Expand Up @@ -166,23 +180,20 @@ export const FolderContextMenu = ({
{
name: t('Delete'),
display:
(!!onDelete && isMyEntity(folder, featureType)) || !!folder.temporary,
dataQa: 'delete',
Icon: IconTrashX,
onClick: onDelete,
},
{
name: t('Delete'),
display: !!onDelete && !!folder.sharedWithMe,
!!onDelete &&
(isMyEntity(folder, featureType) ||
!!folder.temporary ||
!!folder.sharedWithMe),
dataQa: 'delete',
Icon: IconTrashX,
onClick: onDelete,
},

{
name: t('Add new folder'),
display:
(!!onAddFolder && !isExternal) ||
!!additionalItemData?.isChangePathFolder,
!!onAddFolder &&
(isMyOrCanEdit || !!additionalItemData?.isChangePathFolder),
dataQa: 'new-folder',
Icon: IconFolderPlus,
onClick: onAddFolder,
Expand All @@ -197,6 +208,7 @@ export const FolderContextMenu = ({
onSelect,
featureType,
onUpload,
isMyOrCanEdit,
disableAll,
onRename,
folder,
Expand Down
4 changes: 2 additions & 2 deletions apps/chat/src/components/Files/FileItemContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { MouseEvent, MouseEventHandler, useMemo } from 'react';

import { useTranslation } from '@/src/hooks/useTranslation';

import { isCurrentFolderOrParentSharedWithMeAndCanEdit } from '@/src/utils/app/folders';
import { canEditSharedFolderOrParent } from '@/src/utils/app/folders';
import { isMyEntity } from '@/src/utils/app/id';

import { FeatureType } from '@/src/types/common';
Expand Down Expand Up @@ -118,7 +118,7 @@ export function FileItemContextMenu({
display:
isMyEntity(file, FeatureType.File) ||
!!file.sharedWithMe ||
isCurrentFolderOrParentSharedWithMeAndCanEdit(folders, file.folderId),
canEditSharedFolderOrParent(folders, file.folderId),

Icon: IconTrashX,
onClick: onDelete,
Expand Down
33 changes: 26 additions & 7 deletions apps/chat/src/components/Files/PreUploadModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ import {
notAllowedSymbols,
prepareFileName,
} from '@/src/utils/app/file';
import { getParentAndCurrentFoldersById } from '@/src/utils/app/folders';
import { getFileRootId } from '@/src/utils/app/id';
import {
getParentAndCurrentFoldersById,
splitEntityId,
} from '@/src/utils/app/folders';
import { getFileRootId, isMyBucket } from '@/src/utils/app/id';

import { DialFile } from '@/src/types/files';
import { ModalState } from '@/src/types/modal';
Expand All @@ -33,6 +36,7 @@ import { FilesActions, FilesSelectors } from '@/src/store/files/files.reducers';
import { useAppDispatch, useAppSelector } from '@/src/store/hooks';

import { OUTSIDE_PRESS_AND_MOUSE_EVENT } from '@/src/constants/modal';
import { SHARED_WITH_ME_SECTION_NAME } from '@/src/constants/sections';

import Modal from '@/src/components/Common/Modal';

Expand All @@ -52,6 +56,7 @@ interface Props {
) => void;
uploadFolderId?: string;
customUploadButtonLabel?: string;
rootFolderId?: string;
}

const bytesInMb = 1_048_576;
Expand All @@ -66,6 +71,7 @@ export const PreUploadDialog = ({
onUploadFiles,
uploadFolderId,
customUploadButtonLabel,
rootFolderId,
}: Props) => {
const dispatch = useAppDispatch();
const { t } = useTranslation(Translation.Chat);
Expand All @@ -82,12 +88,20 @@ export const PreUploadDialog = ({
const [isChangeFolderModalOpened, setIsChangeFolderModalOpened] =
useState(false);
const [selectedFolderId, setSelectedFolderId] = useState(
uploadFolderId || getFileRootId(),
uploadFolderId || rootFolderId || getFileRootId(),
);

const headingId = useId();
const descriptionId = useId();

const { bucket, name: rootFolderName } = useMemo(
() =>
rootFolderId
? splitEntityId(rootFolderId)
: { bucket: undefined, name: undefined },
[rootFolderId],
);

const folderPath = useMemo(() => {
return (
getParentAndCurrentFoldersById(folders, selectedFolderId)
Expand Down Expand Up @@ -156,7 +170,7 @@ export const PreUploadDialog = ({
return {
fileContent: file,
id: constructPath(
getFileRootId(),
getFileRootId(bucket),
folderPath,
prepareFileName(file.name),
),
Expand All @@ -169,7 +183,7 @@ export const PreUploadDialog = ({
uploadInputRef.current.value = '';
}
},
[allowedTypes, folderPath, t],
[allowedTypes, bucket, folderPath, t],
);

const handleUpload = useCallback(() => {
Expand Down Expand Up @@ -400,7 +414,12 @@ export const PreUploadDialog = ({
data-qa="change-path-container"
>
<span className="truncate" data-qa="path">
{constructPath(t('All files'), folderPath)}
{!bucket || isMyBucket(bucket)
? constructPath(t('All files'), folderPath ?? rootFolderName)
: constructPath(
t(SHARED_WITH_ME_SECTION_NAME),
folderPath ?? rootFolderName,
)}
</span>
<span
className="cursor-pointer text-accent-primary"
Expand Down Expand Up @@ -490,7 +509,7 @@ export const PreUploadDialog = ({
<SelectFolderModal
isOpen={isChangeFolderModalOpened}
initialSelectedFolderId={selectedFolderId}
rootFolderId={getFileRootId()}
rootFolderId={rootFolderId ?? getFileRootId(bucket)}
IlyaBondar marked this conversation as resolved.
Show resolved Hide resolved
onClose={(folderId) => {
if (folderId) {
setSelectedFolderId(folderId);
Expand Down
Loading