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 (
-
+ >
);
};
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 = ({
-