diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 5f2c988fa..30f768336 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -33,7 +33,7 @@ jobs: run: tsc --noEmit - name: Build App - run: NODE_OPTIONS=--max-old-space-size=4096 yarn build:test + run: NODE_OPTIONS=--max-old-space-size=8192 yarn build:test shell: bash env: VITE_PORT: ${{ vars.VITE_PORT }} diff --git a/cypress/fixtures/items.ts b/cypress/fixtures/items.ts index 82da12d75..eec32792e 100644 --- a/cypress/fixtures/items.ts +++ b/cypress/fixtures/items.ts @@ -686,6 +686,7 @@ export const PUBLISHED_ITEM: ItemForTest = { item, createdAt: new Date(), creator: MEMBERS.ANNA, + totalViews: 0, }, memberships: [ { @@ -896,6 +897,7 @@ export const PUBLISHED_ITEMS_WITH_CC_LICENSE: ItemForTest[] = [ item, createdAt: new Date(), creator: MEMBERS.ANNA, + totalViews: 0, }, memberships: [ { @@ -934,6 +936,7 @@ export const PUBLISHED_ITEMS_WITH_CC_LICENSE: ItemForTest[] = [ item, createdAt: new Date(), creator: MEMBERS.ANNA, + totalViews: 0, }, memberships: [ { @@ -972,6 +975,7 @@ export const PUBLISHED_ITEMS_WITH_CC_LICENSE: ItemForTest[] = [ item, createdAt: new Date(), creator: MEMBERS.ANNA, + totalViews: 0, }, memberships: [ { @@ -1010,6 +1014,7 @@ export const PUBLISHED_ITEMS_WITH_CC_LICENSE: ItemForTest[] = [ item, createdAt: new Date(), creator: MEMBERS.ANNA, + totalViews: 0, }, memberships: [ { diff --git a/package.json b/package.json index ab00b7900..133684b19 100644 --- a/package.json +++ b/package.json @@ -17,14 +17,14 @@ "@emotion/react": "11.11.1", "@emotion/styled": "11.11.0", "@graasp/chatbox": "2.0.1", - "@graasp/query-client": "1.11.0", - "@graasp/sdk": "1.8.0", + "@graasp/query-client": "1.11.1", + "@graasp/sdk": "1.9.2", "@graasp/translations": "1.19.2", - "@graasp/ui": "3.6.0", - "@mui/icons-material": "5.14.13", + "@graasp/ui": "3.6.1", + "@mui/icons-material": "5.14.14", "@mui/lab": "5.0.0-alpha.148", - "@mui/material": "5.14.13", - "@sentry/react": "7.73.0", + "@mui/material": "5.14.14", + "@sentry/react": "7.74.1", "@uppy/core": "3.3.1", "@uppy/dashboard": "3.4.2", "@uppy/drag-drop": "3.0.2", @@ -38,11 +38,12 @@ "ag-grid-react": "29.3.5", "axios": "1.5.0", "date-fns": "2.30.0", - "filesize": "10.0.12", - "http-status-codes": "2.2.0", + "filesize": "10.1.0", + "http-status-codes": "2.3.0", "immutable": "4.3.4", "katex": "0.16.9", "lodash.groupby": "4.6.0", + "lodash.isequal": "4.5.0", "lodash.partition": "4.6.0", "lodash.truncate": "4.4.2", "papaparse": "5.4.1", @@ -52,13 +53,13 @@ "react-csv": "2.2.2", "react-dom": "18.2.0", "react-ga4": "2.1.0", - "react-i18next": "13.2.0", + "react-i18next": "13.3.1", "react-image-crop": "9.1.1", "react-qr-code": "2.0.12", "react-query": "3.39.3", "react-quill": "2.0.0", - "react-router": "6.15.0", - "react-router-dom": "6.15.0", + "react-router": "6.17.0", + "react-router-dom": "6.17.0", "react-toastify": "9.1.3", "short-uuid": "4.2.2", "stylis": "4.3.0", @@ -104,30 +105,31 @@ "devDependencies": { "@commitlint/cli": "17.8.1", "@commitlint/config-conventional": "17.8.1", - "@cypress/code-coverage": "3.11.0", - "@testing-library/jest-dom": "^6.0.0", + "@cypress/code-coverage": "3.12.4", + "@testing-library/jest-dom": "^6.1.4", "@testing-library/react": "^14.0.0", - "@testing-library/user-event": "^14.4.3", + "@testing-library/user-event": "^14.5.1", "@trivago/prettier-plugin-sort-imports": "4.2.0", - "@types/jest": "29.5.4", + "@types/jest": "29.5.6", + "@types/lodash.isequal": "4.5.7", "@types/lodash.partition": "4.6.8", "@types/lodash.truncate": "4.4.8", - "@types/node": "18.17.12", - "@types/papaparse": "5.3.8", - "@types/qs": "6.9.7", + "@types/node": "20.8.7", + "@types/papaparse": "5.3.10", + "@types/qs": "6.9.9", "@types/react": "18.2.31", - "@types/react-csv": "1.1.3", - "@types/react-dom": "18.2.7", - "@types/uuid": "9.0.2", - "@types/validator": "13.11.1", - "@typescript-eslint/eslint-plugin": "6.5.0", - "@typescript-eslint/parser": "6.5.0", - "@vitejs/plugin-react": "4.0.4", - "concurrently": "8.2.1", - "cypress": "13.3.0", + "@types/react-csv": "1.1.7", + "@types/react-dom": "18.2.14", + "@types/uuid": "9.0.6", + "@types/validator": "13.11.5", + "@typescript-eslint/eslint-plugin": "6.8.0", + "@typescript-eslint/parser": "6.8.0", + "@vitejs/plugin-react": "4.1.0", + "concurrently": "8.2.2", + "cypress": "13.3.2", "cypress-localstorage-commands": "2.2.4", "env-cmd": "10.1.0", - "eslint": "^8.47.0", + "eslint": "^8.51.0", "eslint-config-airbnb": "19.0.4", "eslint-config-prettier": "9.0.0", "eslint-import-resolver-typescript": "3.6.1", @@ -137,10 +139,10 @@ "eslint-plugin-react-hooks": "4.6.0", "husky": "8.0.3", "nyc": "15.1.0", - "prettier": "3.0.2", + "prettier": "3.0.3", "rollup-plugin-visualizer": "5.9.2", "typescript": "5.2.2", - "vite": "4.4.9", + "vite": "4.5.0", "vite-plugin-checker": "0.6.2", "vite-plugin-istanbul": "5.0.0" }, diff --git a/src/components/RecycleBinScreen.tsx b/src/components/RecycleBinScreen.tsx index d1eeb29fe..35742b2f1 100644 --- a/src/components/RecycleBinScreen.tsx +++ b/src/components/RecycleBinScreen.tsx @@ -2,8 +2,6 @@ import { Box } from '@mui/material'; import { Loader } from '@graasp/ui'; -import { List } from 'immutable'; - import { useBuilderTranslation } from '../config/i18n'; import { hooks } from '../config/queryClient'; import { @@ -67,7 +65,7 @@ const RecycleBinLoadableContent = (): JSX.Element => { id={RECYCLED_ITEMS_ID} clickable={false} title={translateBuilder(BUILDER.RECYCLE_BIN_TITLE)} - items={recycledItems ?? List()} + items={recycledItems} actions={RowActions} ToolbarActions={ToolbarActions} showThumbnails={false} diff --git a/src/components/SharedItems.tsx b/src/components/SharedItems.tsx index a28476076..dd700fb69 100644 --- a/src/components/SharedItems.tsx +++ b/src/components/SharedItems.tsx @@ -2,8 +2,6 @@ import Box from '@mui/material/Box'; import { Loader } from '@graasp/ui'; -import { List } from 'immutable'; - import { BUILDER } from '@/langs/constants'; import { useBuilderTranslation } from '../config/i18n'; @@ -35,7 +33,7 @@ const SharedItemsLoadableContent = (): JSX.Element => { diff --git a/src/components/common/EditButton.tsx b/src/components/common/EditButton.tsx index 8c3dd6c9d..67022decd 100644 --- a/src/components/common/EditButton.tsx +++ b/src/components/common/EditButton.tsx @@ -1,11 +1,14 @@ import { useState } from 'react'; +import { Dialog } from '@mui/material'; + import { DiscriminatedItem, ItemType } from '@graasp/sdk'; import { EditButton as GraaspEditButton } from '@graasp/ui'; import { useBuilderTranslation } from '../../config/i18n'; import { EDIT_ITEM_BUTTON_CLASS, + EDIT_MODAL_ID, buildEditButtonId, } from '../../config/selectors'; import { BUILDER } from '../../langs/constants'; @@ -30,7 +33,7 @@ const EditButton = ({ item }: Props): JSX.Element => { }; const typeToFormComponent = (): EditModalContentType => { - switch (item?.type) { + switch (item.type) { case ItemType.DOCUMENT: return DocumentForm; case ItemType.LOCAL_FILE: @@ -50,12 +53,13 @@ const EditButton = ({ item }: Props): JSX.Element => { return ( <> - + + + , + ); const handleFavorite = () => { addFavorite.mutate(item.id); diff --git a/src/components/item/form/DocumentForm.tsx b/src/components/item/form/DocumentForm.tsx index 3c001e8e9..0405967bf 100644 --- a/src/components/item/form/DocumentForm.tsx +++ b/src/components/item/form/DocumentForm.tsx @@ -17,7 +17,7 @@ import { ITEM_FORM_DOCUMENT_TEXT_ID } from '../../../config/selectors'; import { BUILDER } from '../../../langs/constants'; import { buildDocumentExtra } from '../../../utils/itemExtra'; import type { EditModalContentPropType } from './EditModalWrapper'; -import BaseForm from './NameForm'; +import NameForm from './NameForm'; export const DocumentExtraForm = ({ documentItemId, @@ -138,7 +138,7 @@ const DocumentForm = ({ return ( <> - ) => void; updatedProperties: Partial; } - export type EditModalContentType = CT; type Props = { ComponentType: EditModalContentType; - open: boolean; item: DiscriminatedItem; setOpen: Dispatch; @@ -47,7 +44,6 @@ const EditModalWrapper = ({ item, setOpen, ComponentType, - open, }: Props): JSX.Element => { const { t: translateBuilder } = useBuilderTranslation(); const { t: translateCommon } = useCommonTranslation(); @@ -56,62 +52,52 @@ const EditModalWrapper = ({ // updated properties are separated from the original item // so only necessary properties are sent when editing - const [updatedProperties, setUpdatedItem] = useState< - Partial - >({}); - const [isConfirmButtonDisabled, setConfirmButtonDisabled] = useState(false); + const [updatedItem, setUpdatedItem] = useState(item); + + const onClose = () => { + setOpen(false); + }; const submit = () => { - if (isConfirmButtonDisabled) { - return; - } if ( !isItemValid({ ...item, - ...updatedProperties, + ...updatedItem, } as DiscriminatedItem) ) { toast.error(translateBuilder(BUILDER.EDIT_ITEM_ERROR_MESSAGE)); return; } - setConfirmButtonDisabled(true); // add id to changed properties - if (!item?.id) { notifier({ type: editItemRoutine.FAILURE, payload: { error: new Error(FAILURE_MESSAGES.UNEXPECTED_ERROR) }, }); } else { - editItem({ id: item.id, ...updatedProperties }); + editItem({ + id: updatedItem.id, + name: updatedItem.name, + description: updatedItem.description, + // only post extra if it has been changed + extra: !isEqual(item.extra, updatedItem.extra) + ? updatedItem.extra + : undefined, + }); } - setOpen(false); + onClose(); }; const setChanges = (payload: Partial) => { - setUpdatedItem({ ...updatedProperties, ...payload } as DiscriminatedItem); - }; - - const onClose = () => { - setOpen(false); - setUpdatedItem({}); - // schedule button disable state reset AFTER end of click event handling - // todo: factor out this logic to graasp-ui - setTimeout(() => setConfirmButtonDisabled(false), DOUBLE_CLICK_DELAY_MS); + setUpdatedItem({ ...updatedItem, ...payload } as DiscriminatedItem); }; return ( - + <> - {translateBuilder(BUILDER.EDIT_ITEM_MODAL_TITLE, { name: item.name })} + {translateBuilder(BUILDER.EDIT_ITEM_MODAL_TITLE)} @@ -130,13 +116,13 @@ const EditModalWrapper = ({ + ); }; export default EditModalWrapper; diff --git a/src/components/item/form/FileForm.tsx b/src/components/item/form/FileForm.tsx index 7c241bf68..58b65bd58 100644 --- a/src/components/item/form/FileForm.tsx +++ b/src/components/item/form/FileForm.tsx @@ -1,22 +1,15 @@ import { TextField } from '@mui/material'; -import { - FileItemProperties, - ItemType, - LocalFileItemType, - MimeTypes, - S3FileItemType, - getFileExtra, - getS3FileExtra, -} from '@graasp/sdk'; +import { FileItemProperties, ItemType, MimeTypes } from '@graasp/sdk'; import { useBuilderTranslation } from '@/config/i18n'; import { ITEM_FORM_IMAGE_ALT_TEXT_EDIT_FIELD_ID } from '@/config/selectors'; +import { getExtraFromPartial } from '@/utils/itemExtra'; import { BUILDER } from '../../../langs/constants'; import DescriptionForm from './DescriptionForm'; import { EditModalContentPropType } from './EditModalWrapper'; -import BaseItemForm from './NameForm'; +import NameForm from './NameForm'; const FileForm = (props: EditModalContentPropType): JSX.Element | null => { const { item, setChanges, updatedProperties } = props; @@ -27,54 +20,62 @@ const FileForm = (props: EditModalContentPropType): JSX.Element | null => { return null; } - const localFileExtra = - item.type === ItemType.LOCAL_FILE ? getFileExtra(item.extra) : undefined; - const s3FileExtra = - item.type === ItemType.S3_FILE ? getS3FileExtra(item.extra) : undefined; - const { mimetype, altText } = { - ...s3FileExtra, - ...localFileExtra, - }; + if ( + item && + (item.type === ItemType.LOCAL_FILE || item.type === ItemType.S3_FILE) && + (updatedProperties.type === ItemType.LOCAL_FILE || + updatedProperties.type === ItemType.S3_FILE) + ) { + const itemExtra = getExtraFromPartial(item); + const { mimetype, altText } = itemExtra; + const updatedExtra = getExtraFromPartial(updatedProperties); + return ( + <> + + {mimetype && MimeTypes.isImage(mimetype) && ( + { + const newExtra = { + ...itemExtra, + ...updatedExtra, + altText: e.target.value, + } as FileItemProperties; - return ( - <> - - {mimetype && MimeTypes.isImage(mimetype) && ( - - setChanges({ - extra: { - [item.type]: { ...updatedProperties, altText: e.target.value }, - }, - } as Partial) - } - // always shrink because setting name from defined app does not shrink automatically - InputLabelProps={{ shrink: true }} - sx={{ width: '50%', my: 1 }} - multiline + setChanges( + item.type === ItemType.S3_FILE + ? { + extra: { + [ItemType.S3_FILE]: newExtra, + }, + } + : { + extra: { [ItemType.LOCAL_FILE]: newExtra }, + }, + ); + }} + // always shrink because setting name from defined app does not shrink automatically + InputLabelProps={{ shrink: true }} + sx={{ width: '50%', my: 1 }} + multiline + /> + )} + - )} - - - ); + + ); + } + return null; }; export default FileForm; diff --git a/src/components/item/settings/FileSettings.tsx b/src/components/item/settings/FileSettings.tsx index f2b9cc478..9f71503f5 100644 --- a/src/components/item/settings/FileSettings.tsx +++ b/src/components/item/settings/FileSettings.tsx @@ -40,7 +40,7 @@ const FileSettings = ({ - +