diff --git a/cypress/fixtures/chatbox.js b/cypress/fixtures/chatbox.js new file mode 100644 index 000000000..8c837b116 --- /dev/null +++ b/cypress/fixtures/chatbox.js @@ -0,0 +1,31 @@ +import { DEFAULT_FOLDER_ITEM } from './items'; +import { CURRENT_USER, MEMBERS } from './members'; + +export const ITEM_WITH_CHATBOX_MESSAGES = { + ...DEFAULT_FOLDER_ITEM, + id: 'adf09f5a-5688-11eb-ae93-0242ac130004', + path: 'adf09f5a_5688_11eb_ae93_0242ac130004', + name: 'item with chatbox messages', + chat: [ + { + body: 'message1', + chatId: 'adf09f5a-5688-11eb-ae93-0242ac130004', + createdAt: '2021-08-11T12:56:36.834Z', + creator: CURRENT_USER.id, + }, + { + body: 'message2', + chatId: 'adf09f5a-5688-11eb-ae93-0242ac130004', + createdAt: '2021-09-11T12:56:36.834Z', + creator: MEMBERS.BOB.id, + }, + ], +}; + +export const ITEM_WITHOUT_CHATBOX_MESSAGES = { + ...DEFAULT_FOLDER_ITEM, + id: 'bdf09f5a-5688-11eb-ae93-0242ac130001', + path: 'bdf09f5a_5688_11eb_ae93_0242ac130001', + name: 'item without chatbox messages', + chat: [], +}; diff --git a/cypress/integration/item/chatbox/chatbox.spec.js b/cypress/integration/item/chatbox/chatbox.spec.js new file mode 100644 index 000000000..9a1fdb7c0 --- /dev/null +++ b/cypress/integration/item/chatbox/chatbox.spec.js @@ -0,0 +1,98 @@ +import { WebSocket } from '@graasp/websockets/test/mock-client'; +import { buildItemPath } from '../../../../src/config/paths'; +import { + CHATBOX_ID, + CHATBOX_INPUT_BOX_ID, + ITEM_CHATBOX_BUTTON_ID, +} from '../../../../src/config/selectors'; +import { + ITEM_WITHOUT_CHATBOX_MESSAGES, + ITEM_WITH_CHATBOX_MESSAGES, +} from '../../../fixtures/chatbox'; +import { CURRENT_USER, MEMBERS } from '../../../fixtures/members'; +import { WEBSOCKETS_DELAY_TIME } from '../../../support/constants'; + +const openChatbox = () => { + cy.get(`#${ITEM_CHATBOX_BUTTON_ID}`).click(); + cy.wait('@getItemChat'); +}; + +describe('Chatbox Scenarios', () => { + let client; + + beforeEach(() => { + client = new WebSocket(); + }); + + it('Send messages in chatbox', () => { + const item = ITEM_WITH_CHATBOX_MESSAGES; + cy.visitAndMockWs(buildItemPath(item.id), { items: [item] }, client); + + // open chatbox + openChatbox(); + // check the chatbox displays the already saved messages + for (const msg of item.chat) { + cy.get(`#${CHATBOX_ID}`).should('contain', msg.body); + } + + // send message + const message = 'a new message'; + cy.get(`#${CHATBOX_ID} #${CHATBOX_INPUT_BOX_ID} input`).type(message); + cy.get(`#${CHATBOX_ID} #${CHATBOX_INPUT_BOX_ID} button`).click(); + cy.wait('@postItemChatMessage').then(({ request: { body } }) => { + expect(body.body).to.equal(message); + + // mock websocket response + client.receive({ + realm: 'notif', + type: 'update', + topic: 'chat/item', + channel: item.id, + body: { + kind: 'item', + op: 'publish', + message: { + creator: CURRENT_USER.id, + chatId: item.id, + body: message, + }, + }, + }); + cy.wait(WEBSOCKETS_DELAY_TIME); + + // check the new message is visible + cy.get(`#${CHATBOX_ID}`).should('contain', message); + }); + }); + + it('Receive messages in chatbox from websockets', () => { + const item = ITEM_WITHOUT_CHATBOX_MESSAGES; + cy.visitAndMockWs(buildItemPath(item.id), { items: [item] }, client); + + openChatbox(); + + // check websocket: the chatbox displays someone else's message + const bobMessage = 'a message from bob'; + cy.get(`#${CHATBOX_ID}`).then(() => { + client.receive({ + realm: 'notif', + type: 'update', + topic: 'chat/item', + channel: item.id, + body: { + kind: 'item', + op: 'publish', + message: { + creator: MEMBERS.BOB.id, + chatId: item.id, + body: bobMessage, + }, + }, + }); + cy.wait(WEBSOCKETS_DELAY_TIME); + + // check the new message is visible + cy.get(`#${CHATBOX_ID}`).should('contain', bobMessage); + }); + }); +}); diff --git a/cypress/integration/item/view/viewDocument.spec.js b/cypress/integration/item/view/viewDocument.spec.js index 0bad1af56..a33c6ce2d 100644 --- a/cypress/integration/item/view/viewDocument.spec.js +++ b/cypress/integration/item/view/viewDocument.spec.js @@ -2,7 +2,7 @@ import { buildItemPath } from '../../../../src/config/paths'; import { GRAASP_DOCUMENT_ITEM } from '../../../fixtures/documents'; import { expectDocumentViewScreenLayout } from './utils'; -describe('View Space', () => { +describe('View Document', () => { describe('Grid', () => { beforeEach(() => { cy.setUpApi({ diff --git a/cypress/integration/item/view/viewFolder.spec.js b/cypress/integration/item/view/viewFolder.spec.js index 8217372a8..5fc195e7f 100644 --- a/cypress/integration/item/view/viewFolder.spec.js +++ b/cypress/integration/item/view/viewFolder.spec.js @@ -21,7 +21,7 @@ import { GRAASP_LINK_ITEM } from '../../../fixtures/links'; import { REQUEST_FAILURE_TIME } from '../../../support/constants'; import { expectFolderViewScreenLayout } from './utils'; -describe('View Space', () => { +describe('View Folder', () => { describe('Grid', () => { beforeEach(() => { cy.setUpApi({ diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 0dc2ac020..a29fbbad9 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -43,6 +43,8 @@ import { mockGetItems, mockGetFlags, mockPostItemFlag, + mockGetItemChat, + mockPostItemChatMessage, } from './server'; import './commands/item'; import './commands/navigation'; @@ -76,6 +78,7 @@ Cypress.Commands.add( putItemLoginError = false, editMemberError = false, postItemFlagError = false, + getItemChatError = false, } = {}) => { const cachedItems = JSON.parse(JSON.stringify(items)); const cachedMembers = JSON.parse(JSON.stringify(members)); @@ -152,6 +155,10 @@ Cypress.Commands.add( mockGetPublicChildren(items); mockGetItems({ items, currentMember }); + + mockGetItemChat({ items }, getItemChatError); + + mockPostItemChatMessage(); }, ); @@ -168,3 +175,15 @@ Cypress.Commands.add('switchMode', (mode) => { break; } }); + +Cypress.Commands.add( + 'visitAndMockWs', + (visitRoute, sampleData, wsClientStub) => { + cy.setUpApi(sampleData); + cy.visit(visitRoute, { + onBeforeLoad: (win) => { + cy.stub(win, 'WebSocket', () => wsClientStub); + }, + }); + }, +); diff --git a/cypress/support/server.js b/cypress/support/server.js index d0df5d2b8..ff8b8b2ec 100644 --- a/cypress/support/server.js +++ b/cypress/support/server.js @@ -64,6 +64,8 @@ const { buildDeleteItemMembershipRoute, buildPostItemFlagRoute, GET_FLAGS_ROUTE, + buildGetItemChatRoute, + buildPostItemChatMessageRoute, } = API_ROUTES; const API_HOST = Cypress.env('API_HOST'); @@ -826,3 +828,40 @@ export const mockPostItemFlag = (items, shouldThrowError) => { }, ).as('postItemFlag'); }; + +export const mockGetItemChat = ({ items }, shouldThrowError) => { + cy.intercept( + { + method: DEFAULT_GET.method, + url: new RegExp(`${API_HOST}/${buildGetItemChatRoute(ID_FORMAT)}$`), + }, + ({ reply, url }) => { + if (shouldThrowError) { + return reply({ statusCode: StatusCodes.BAD_REQUEST }); + } + + const itemId = url.slice(API_HOST.length).split('/')[2]; + const item = items.find(({ id }) => itemId === id); + + return reply({ id: itemId, messages: item?.chat }); + }, + ).as('getItemChat'); +}; + +export const mockPostItemChatMessage = (shouldThrowError) => { + cy.intercept( + { + method: DEFAULT_POST.method, + url: new RegExp( + `${API_HOST}/${buildPostItemChatMessageRoute(ID_FORMAT)}$`, + ), + }, + ({ reply, body }) => { + if (shouldThrowError) { + return reply({ statusCode: StatusCodes.BAD_REQUEST }); + } + + return reply(body); + }, + ).as('postItemChatMessage'); +}; diff --git a/package.json b/package.json index d0e690635..931536332 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "license": "AGPL-3.0-only", "dependencies": { + "@graasp/chatbox": "git://github.com/graasp/graasp-chatbox.git#main", "@graasp/query-client": "git://github.com/graasp/graasp-query-client.git#main", "@graasp/ui": "git://github.com/graasp/graasp-ui.git#41/rollup", "@material-ui/core": "4.11.2", @@ -106,7 +107,7 @@ "npm-run-all": "4.1.5", "nyc": "15.1.0", "prettier": "2.2.1", - "pretty-quick": "3.1.0", + "pretty-quick": "3.1.1", "standard-version": "9.1.0", "typescript": "4.1.3", "wait-on": "5.3.0" diff --git a/src/components/common/Chatbox.js b/src/components/common/Chatbox.js new file mode 100644 index 000000000..da1937a02 --- /dev/null +++ b/src/components/common/Chatbox.js @@ -0,0 +1,53 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Typography } from '@material-ui/core'; +import { useTranslation } from 'react-i18next'; +import GraaspChatbox from '@graasp/chatbox'; +import { MUTATION_KEYS } from '@graasp/query-client'; +import { Map } from 'immutable'; +import { Loader } from '@graasp/ui'; +import { hooks, useMutation } from '../../config/queryClient'; +import { HEADER_HEIGHT } from '../../config/constants'; +import { CHATBOX_INPUT_BOX_ID, CHATBOX_ID } from '../../config/selectors'; + +const { useItemChat, useCurrentMember } = hooks; + +const Chatbox = ({ item }) => { + const { t } = useTranslation(); + const { data: chat, isLoading: isChatLoading } = useItemChat(item.get('id')); + const { data: currentMember, isLoadingCurrentMember } = useCurrentMember(); + const { mutate: sendMessage } = useMutation( + MUTATION_KEYS.POST_ITEM_CHAT_MESSAGE, + ); + + const renderChatbox = () => { + if (isChatLoading || isLoadingCurrentMember) { + return ; + } + + return ( + + ); + }; + + return ( + <> + {t('Comments')} + {renderChatbox()} + + ); +}; + +Chatbox.propTypes = { + item: PropTypes.instanceOf(Map).isRequired, +}; + +export default Chatbox; diff --git a/src/components/context/LayoutContext.js b/src/components/context/LayoutContext.js index afcb43ba3..24f6c1467 100644 --- a/src/components/context/LayoutContext.js +++ b/src/components/context/LayoutContext.js @@ -26,6 +26,7 @@ const LayoutContextProvider = ({ children }) => { const [isItemMetadataMenuOpen, setIsItemMetadataMenuOpen] = useState( isItemPanelOpen, ); + const [isChatboxMenuOpen, setIsChatboxMenuOpen] = useState(false); return ( { setIsItemSettingsOpen, isItemMetadataMenuOpen, setIsItemMetadataMenuOpen, + isChatboxMenuOpen, + setIsChatboxMenuOpen, }} > {children} diff --git a/src/components/item/ItemMain.js b/src/components/item/ItemMain.js index 326934ce7..a88c4894c 100644 --- a/src/components/item/ItemMain.js +++ b/src/components/item/ItemMain.js @@ -8,10 +8,10 @@ import ItemHeader from './header/ItemHeader'; import ItemPanel from './ItemPanel'; import { ITEM_MAIN_CLASS } from '../../config/selectors'; import { LayoutContext } from '../context/LayoutContext'; +import Chatbox from '../common/Chatbox'; +import ItemMetadataContent from './ItemMetadataContent'; const useStyles = makeStyles((theme) => ({ - root: {}, - menuButton: {}, hide: { display: 'none', }, @@ -49,23 +49,42 @@ const useStyles = makeStyles((theme) => ({ const ItemMain = ({ id, children, item }) => { const classes = useStyles(); - const { isItemMetadataMenuOpen, setIsItemMetadataMenuOpen } = useContext( - LayoutContext, - ); + const { + isItemMetadataMenuOpen, + setIsItemMetadataMenuOpen, + isChatboxMenuOpen, + setIsChatboxMenuOpen, + } = useContext(LayoutContext); const handleToggleMetadataMenu = () => { setIsItemMetadataMenuOpen(!isItemMetadataMenuOpen); + setIsChatboxMenuOpen(false); + }; + const handleToggleChatboxMenu = () => { + setIsChatboxMenuOpen(!isChatboxMenuOpen); + setIsItemMetadataMenuOpen(false); }; return (
- + {isChatboxMenuOpen && ( + + + + )} + + + +
- + {children}
diff --git a/src/components/item/ItemMetadataContent.js b/src/components/item/ItemMetadataContent.js new file mode 100644 index 000000000..502b95f3c --- /dev/null +++ b/src/components/item/ItemMetadataContent.js @@ -0,0 +1,106 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import { useTranslation } from 'react-i18next'; +import TableRow from '@material-ui/core/TableRow'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; +import { formatDate } from '../../utils/date'; +import { hooks } from '../../config/queryClient'; +import { getFileExtra, getS3FileExtra } from '../../utils/itemExtra'; +import { + ITEM_PANEL_NAME_ID, + ITEM_PANEL_TABLE_ID, +} from '../../config/selectors'; +import { ITEM_KEYS, ITEM_TYPES } from '../../enums'; + +const { useMember } = hooks; + +const useStyles = makeStyles((theme) => ({ + table: { + padding: theme.spacing(2), + }, + extra: { + wordBreak: 'break-all', + }, + name: { + wordBreak: 'break-word', + }, +})); + +const ItemMetadataContent = ({ item }) => { + const { t } = useTranslation(); + + const classes = useStyles(); + + const { data: creator } = useMember(item.get('creator')); + + let type = null; + let size = null; + if (item.get(ITEM_KEYS.TYPE) === ITEM_TYPES.S3_FILE) { + const extra = getS3FileExtra(item.get('extra')); + ({ contenttype: type, size } = extra); + } else if (item.get(ITEM_KEYS.TYPE) === ITEM_TYPES.FILE) { + const extra = getFileExtra(item.get('extra')); + ({ mimetype: type, size } = extra); + } else { + type = item.get(ITEM_KEYS.TYPE); + } + + return ( + <> + + {item.get(ITEM_KEYS.NAME)} + + + + + + + {t('Type')} + + {type} + + {size && ( + + + {t('Size')} + + {size} + + )} + + {t('Creator')} + {creator?.get('name')} + + + {t('Created At')} + + {formatDate(item.get('createdAt'))} + + + + {t('Updated At')} + + {formatDate(item.get('updatedAt'))} + + + +
+
+ + ); +}; + +ItemMetadataContent.propTypes = { + item: PropTypes.instanceOf(Map).isRequired, +}; + +export default ItemMetadataContent; diff --git a/src/components/item/ItemPanel.js b/src/components/item/ItemPanel.js index 009a6ab42..f842a5b8f 100644 --- a/src/components/item/ItemPanel.js +++ b/src/components/item/ItemPanel.js @@ -1,28 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import Drawer from '@material-ui/core/Drawer'; -import { Map } from 'immutable'; -import Table from '@material-ui/core/Table'; -import TableBody from '@material-ui/core/TableBody'; -import { useTranslation } from 'react-i18next'; -import TableCell from '@material-ui/core/TableCell'; -import TableContainer from '@material-ui/core/TableContainer'; -import CloseIcon from '@material-ui/icons/Close'; -import TableRow from '@material-ui/core/TableRow'; -import { IconButton, Toolbar, Typography } from '@material-ui/core'; +import { Toolbar } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; import { RIGHT_MENU_WIDTH } from '../../config/constants'; -import { ITEM_KEYS, ITEM_TYPES } from '../../enums'; -import { formatDate } from '../../utils/date'; -import { - ITEM_PANEL_ID, - ITEM_PANEL_NAME_ID, - ITEM_PANEL_TABLE_ID, -} from '../../config/selectors'; -import { getFileExtra, getS3FileExtra } from '../../utils/itemExtra'; -import { hooks } from '../../config/queryClient'; - -const { useMember } = hooks; +import { ITEM_PANEL_ID } from '../../config/selectors'; const useStyles = makeStyles((theme) => ({ drawer: { @@ -31,41 +13,22 @@ const useStyles = makeStyles((theme) => ({ }, drawerPaper: { width: RIGHT_MENU_WIDTH, + padding: theme.spacing(1), + }, + table: { padding: theme.spacing(2), }, extra: { wordBreak: 'break-all', }, - closeButton: { - position: 'absolute', - right: theme.spacing(1), - top: theme.spacing(1), - width: 50, - }, name: { wordBreak: 'break-word', }, })); -const ItemPanel = ({ item, open }) => { - const { t } = useTranslation(); - +const ItemPanel = ({ open, children }) => { const classes = useStyles(); - const { data: creator } = useMember(item.get('creator')); - - let type = null; - let size = null; - if (item.get(ITEM_KEYS.TYPE) === ITEM_TYPES.S3_FILE) { - const extra = getS3FileExtra(item.get('extra')); - ({ contenttype: type, size } = extra); - } else if (item.get(ITEM_KEYS.TYPE) === ITEM_TYPES.FILE) { - const extra = getFileExtra(item.get('extra')); - ({ mimetype: type, size } = extra); - } else { - type = item.get(ITEM_KEYS.TYPE); - } - return ( { open={open} > - - - - - {item.get('name')} - - - - - - - {t('Type')} - - {type} - - {size && ( - - - {t('Size')} - - {size} - - )} - - {t('Creator')} - {creator?.get('name')} - - - {t('Created At')} - - {formatDate(item.get('createdAt'))} - - - - {t('Updated At')} - - {formatDate(item.get('updatedAt'))} - - - -
-
+ {children}
); }; ItemPanel.propTypes = { - item: PropTypes.instanceOf(Map).isRequired, open: PropTypes.bool.isRequired, + children: PropTypes.element, +}; + +ItemPanel.defaultProps = { + children: null, }; export default ItemPanel; diff --git a/src/components/item/header/ItemHeader.js b/src/components/item/header/ItemHeader.js index dcc410450..5fed3456a 100644 --- a/src/components/item/header/ItemHeader.js +++ b/src/components/item/header/ItemHeader.js @@ -23,7 +23,7 @@ const useStyles = makeStyles((theme) => ({ }, })); -const ItemHeader = ({ onClick }) => { +const ItemHeader = ({ onClickMetadata, onClickChatbox }) => { const match = useRouteMatch(buildItemPath()); const itemId = match?.params?.itemId; const { data: item, isLoading: isItemLoading } = useItem(itemId); @@ -36,17 +36,23 @@ const ItemHeader = ({ onClick }) => { return (
- +
); }; ItemHeader.propTypes = { - onClick: PropTypes.func, + onClickMetadata: PropTypes.func, + onClickChatbox: PropTypes.func, }; ItemHeader.defaultProps = { - onClick: () => {}, + onClickMetadata: () => {}, + onClickChatbox: () => {}, }; export default ItemHeader; diff --git a/src/components/item/header/ItemHeaderActions.js b/src/components/item/header/ItemHeaderActions.js index bd9655c3d..779a43a76 100644 --- a/src/components/item/header/ItemHeaderActions.js +++ b/src/components/item/header/ItemHeaderActions.js @@ -6,6 +6,7 @@ import EditIcon from '@material-ui/icons/Edit'; import { Map } from 'immutable'; import InfoIcon from '@material-ui/icons/Info'; import Tooltip from '@material-ui/core/Tooltip'; +import ForumIcon from '@material-ui/icons/Forum'; import { useTranslation } from 'react-i18next'; import { makeStyles } from '@material-ui/core/styles'; import ModeButton from './ModeButton'; @@ -13,6 +14,7 @@ import { ITEM_TYPES } from '../../../enums'; import { LayoutContext } from '../../context/LayoutContext'; import { buildEditButtonId, + ITEM_CHATBOX_BUTTON_ID, ITEM_INFORMATION_BUTTON_ID, ITEM_INFORMATION_ICON_IS_OPEN_CLASS, } from '../../../config/selectors'; @@ -32,7 +34,7 @@ const useStyles = makeStyles((theme) => ({ display: 'flex', }, })); -const ItemHeaderActions = ({ onClick, item }) => { +const ItemHeaderActions = ({ onClickMetadata, onClickChatbox, item }) => { const classes = useStyles(); const { t } = useTranslation(); const { @@ -94,27 +96,34 @@ const ItemHeaderActions = ({ onClick, item }) => { {renderItemActions()} {renderTableActions()} {id && ( - - - + <> + + + + + + + )}
); }; ItemHeaderActions.propTypes = { - onClick: PropTypes.func, + onClickMetadata: PropTypes.func, + onClickChatbox: PropTypes.func, item: PropTypes.instanceOf(Map), }; ItemHeaderActions.defaultProps = { - onClick: () => {}, + onClickMetadata: () => {}, + onClickChatbox: () => {}, item: Map(), }; diff --git a/src/config/selectors.js b/src/config/selectors.js index 2dd27a368..b1a92349e 100644 --- a/src/config/selectors.js +++ b/src/config/selectors.js @@ -127,3 +127,6 @@ export const SHARE_ITEM_DIALOG_ID = 'shareItemDialog'; export const SHARE_ITEM_DIALOG_LINK_ID = 'shareItemDialogLink'; export const SHARE_ITEM_DIALOG_LINK_SELECT_ID = 'shareItemDialogLinkSelect'; export const ACCESS_INDICATION_ID = 'accessIndication'; +export const ITEM_CHATBOX_BUTTON_ID = 'itemChatboxButton'; +export const CHATBOX_ID = 'chatbox'; +export const CHATBOX_INPUT_BOX_ID = 'chatboxInputBox'; diff --git a/src/enums/itemKeys.js b/src/enums/itemKeys.js index 81d581547..ee01c7fd7 100644 --- a/src/enums/itemKeys.js +++ b/src/enums/itemKeys.js @@ -1,5 +1,6 @@ const ITEM_KEYS = { ID: 'id', + NAME: 'name', TYPE: 'type', }; diff --git a/yarn.lock b/yarn.lock index a41677cf6..ead067af5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -363,13 +363,13 @@ __metadata: linkType: hard "@babel/helpers@npm:^7.12.1, @babel/helpers@npm:^7.14.8, @babel/helpers@npm:^7.4.4": - version: 7.14.8 - resolution: "@babel/helpers@npm:7.14.8" + version: 7.15.3 + resolution: "@babel/helpers@npm:7.15.3" dependencies: "@babel/template": ^7.14.5 - "@babel/traverse": ^7.14.8 - "@babel/types": ^7.14.8 - checksum: 2f1358c19fc1ee744c183f81b499b73977da7d3d3f7a881d457b235754394a503e4717353f29364bd5feb7fa406b1edd1aab92b5ab0765dba945fb559eeb1c65 + "@babel/traverse": ^7.15.0 + "@babel/types": ^7.15.0 + checksum: cd70614d610b01189812c83b505b076dca0822df55ed6cd41232416f3a10ae9200a07315683942e0adbc1833481920c2fc7a23a08064ced5a8770259aa0ad707 languageName: node linkType: hard @@ -385,11 +385,11 @@ __metadata: linkType: hard "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.3, @babel/parser@npm:^7.14.5, @babel/parser@npm:^7.15.0, @babel/parser@npm:^7.4.5, @babel/parser@npm:^7.7.0": - version: 7.15.2 - resolution: "@babel/parser@npm:7.15.2" + version: 7.15.3 + resolution: "@babel/parser@npm:7.15.3" bin: parser: ./bin/babel-parser.js - checksum: bcfc88de73903d6f536c98af4028a63c46e6b89cd2a9a2d9b1f9f871647abe20f05e282b29275a48ca33ce627fdcd2a2f06ecb9881e2857de3864dcf8f424de2 + checksum: 4b9ba7e8ffe0a3d0dd8c61dee975c79863f7744177de677cb7d12f96549eb5c8b9ffc70ca2b1b2488b06e056da99a6273e2d7d68fc31f498d01483dfac149e13 languageName: node linkType: hard @@ -936,13 +936,13 @@ __metadata: linkType: hard "@babel/plugin-transform-block-scoping@npm:^7.12.1, @babel/plugin-transform-block-scoping@npm:^7.14.5, @babel/plugin-transform-block-scoping@npm:^7.4.4": - version: 7.14.5 - resolution: "@babel/plugin-transform-block-scoping@npm:7.14.5" + version: 7.15.3 + resolution: "@babel/plugin-transform-block-scoping@npm:7.15.3" dependencies: "@babel/helper-plugin-utils": ^7.14.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d317d636d0475317302e9c8b01cf9214fac3ff9353b23d0d16285f196f5c7b95b7864a8e8eaf51a3e1b650649203855f80a58b7a2caef4b0ee9793e7349a0ec5 + checksum: ee28f51711b5f6569a9bb86be5b2a5456f3e6e22e68488ee77f8082fae5563f45c858dc8323e0e51085d880db1be73e28dc5d108c8a855c831fb29310a01b549 languageName: node linkType: hard @@ -1715,12 +1715,12 @@ __metadata: linkType: hard "@babel/runtime-corejs3@npm:^7.10.2": - version: 7.14.9 - resolution: "@babel/runtime-corejs3@npm:7.14.9" + version: 7.15.3 + resolution: "@babel/runtime-corejs3@npm:7.15.3" dependencies: core-js-pure: ^3.16.0 regenerator-runtime: ^0.13.4 - checksum: c0df31a24dc3b4813f0c40139e3937c45d6db588724fd1a208817068b75db2de273864728c0cd0633a0af665cbc5fbc9c144f2edc606c27d718fcd2c615fe14e + checksum: c33952657d0bec71f46c7ea2595297f4fd61e68ebce812a7de4e3a9841d8ac6af61880049f72560fda9396a86de5b06e5ba6a6f11bd9cf688ec7f4b812730c8b languageName: node linkType: hard @@ -1742,12 +1742,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": - version: 7.14.8 - resolution: "@babel/runtime@npm:7.14.8" +"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": + version: 7.15.3 + resolution: "@babel/runtime@npm:7.15.3" dependencies: regenerator-runtime: ^0.13.4 - checksum: d2dd0ce51ddab78ac93928b04042425145d0dc8cc2b70150d47934f8703f55702eb0b2894f9bd47f66794ad04d8bb03a6a847d0138fbb7aa0b970b5ccd5cc8b7 + checksum: 2f0b8d2d4e36035ab1d84af0ec26aafa098536870f27c8e07de0a0e398f7a394fdea68a88165535ffb52ded6a68912bdc3450bdf91f229eb132e1c89470789f5 languageName: node linkType: hard @@ -1762,7 +1762,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.1.0, @babel/traverse@npm:^7.12.1, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.14.5, @babel/traverse@npm:^7.14.8, @babel/traverse@npm:^7.15.0, @babel/traverse@npm:^7.4.5, @babel/traverse@npm:^7.7.0": +"@babel/traverse@npm:^7.1.0, @babel/traverse@npm:^7.12.1, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.14.5, @babel/traverse@npm:^7.15.0, @babel/traverse@npm:^7.4.5, @babel/traverse@npm:^7.7.0": version: 7.15.0 resolution: "@babel/traverse@npm:7.15.0" dependencies: @@ -2129,9 +2129,27 @@ __metadata: languageName: node linkType: hard +"@graasp/chatbox@git://github.com/graasp/graasp-chatbox.git#main": + version: 0.1.0 + resolution: "@graasp/chatbox@git://github.com/graasp/graasp-chatbox.git#commit=e3b92925e63c217f9ca4e3a36208be906ebb40a7" + dependencies: + clsx: 1.1.1 + i18next: 20.4.0 + immutable: 4.0.0-rc.12 + react-i18next: 11.11.4 + peerDependencies: + "@material-ui/core": "*" + "@material-ui/icons": "*" + "@material-ui/lab": "*" + react: "*" + react-dom: "*" + checksum: 5ebbbc65eedca08764a81df81a1f6d459d1697e63f3b79f26bc4384bdbd7585358567a1e623c3d182a1f0c269a86020fccc713f95182a6d2b4dd77805051c1d8 + languageName: node + linkType: hard + "@graasp/query-client@git://github.com/graasp/graasp-query-client.git#main": version: 0.1.0 - resolution: "@graasp/query-client@git://github.com/graasp/graasp-query-client.git#commit=b7aa5e2e66d8e23152f55be894b1bfc6d0fd6fc1" + resolution: "@graasp/query-client@git://github.com/graasp/graasp-query-client.git#commit=4a7ce3121ad436b3ee3b6a60a782e1d020c2aefc" dependencies: http-status-codes: 2.1.4 immutable: 4.0.0-rc.12 @@ -2142,7 +2160,7 @@ __metadata: uuid: 8.3.2 peerDependencies: react: ^17.0.0 - checksum: 53b9ca38abb73890dc6f0e91c9e936f52131556b0ff21ead9bc0d8e4fee5497eeba7e933c97da7c01b20e6e7a606470351760a4f93da85af8dcd8f8bd7013549 + checksum: a979f1384931ef0bb91b7658ef822c5472ff2b44868bf68b5359cbe1b50d9982418b7599fae2f1a1b070a8ce2d3b66096ea8573b4c1abd5207913f0a9059651a languageName: node linkType: hard @@ -3143,12 +3161,12 @@ __metadata: linkType: hard "@types/jest@npm:*": - version: 26.0.24 - resolution: "@types/jest@npm:26.0.24" + version: 27.0.0 + resolution: "@types/jest@npm:27.0.0" dependencies: jest-diff: ^26.0.0 pretty-format: ^26.0.0 - checksum: ae39675412f08d884926254e9b12bfd2b5a4e4d204c94d3148cb942174a474930d0c60540133c968f22241d4712b7940c96cbc883096eb326a4d5b206fb78bd0 + checksum: bb894a1fcc0e244ecefdf2b43ed786c0354c416d00e433155bf68569d0d8a45b012b008fbe6673890903b785076b9a34f128c7095e1b25f4481f3d2a76c7495f languageName: node linkType: hard @@ -3174,9 +3192,9 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 16.4.13 - resolution: "@types/node@npm:16.4.13" - checksum: 071e86a1d196cf72928a7109e99769dffed93db481032b9ca2f52e6f3ced432e2a179849c85e587e67409202e1ed37bffd0c7a31d38adf35a385d327dfe8bd72 + version: 16.6.0 + resolution: "@types/node@npm:16.6.0" + checksum: 72ab4b9fb47a64657d83336093867aba5bcf2f36facf3c18e9aeff802c078e44fa9f54025b02f2837eb0f2b19c1f551a5201fb4222766454c439de481dbf83d0 languageName: node linkType: hard @@ -3246,13 +3264,13 @@ __metadata: linkType: hard "@types/react@npm:*": - version: 17.0.16 - resolution: "@types/react@npm:17.0.16" + version: 17.0.17 + resolution: "@types/react@npm:17.0.17" dependencies: "@types/prop-types": "*" "@types/scheduler": "*" csstype: ^3.0.2 - checksum: cba60aeb284d4024feaca1364557fda72011e984b6b08f6e06a7cdc65e267eabc42d59c541ddbbb81720ab195b6df95a3535540abadd5c0e2cce26b16759dbaa + checksum: 3f2ae9ab6f7d5c7b480cd5962fe996f0682173036a8437cfde0122bc8594e0e1482864b766dbf611daef40ed65a9fc73ec9510e12511d68c064f309f25219f67 languageName: node linkType: hard @@ -5765,9 +5783,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30000981, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001125, caniuse-lite@npm:^1.0.30001248": - version: 1.0.30001249 - resolution: "caniuse-lite@npm:1.0.30001249" - checksum: d6a7b78e21258b27e238e997a70c3257aa02121a2e05f7636399a967503816af02fb0b11029f2f6e2e363d5267cf4366ca60f6fb33e206dfa6020836e4452a86 + version: 1.0.30001251 + resolution: "caniuse-lite@npm:1.0.30001251" + checksum: 918e1b1662c26c11291206146bc305d7fd1ca351aa9231c2e21cb1526d87b444830e2d8dc54416ebb8ecf7e0addea12d66a1c41493476229987e5e6922f0c14b languageName: node linkType: hard @@ -8074,9 +8092,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.3.564, electron-to-chromium@npm:^1.3.793": - version: 1.3.801 - resolution: "electron-to-chromium@npm:1.3.801" - checksum: 17ad684db86b0028172f4b755d0b3aef964c9f6f36ebf3b78c3d32dea9a5094998d00795c91aa3b14fa16e5fbfe496421b8df462763cd8614cd21c1ba68280ae + version: 1.3.803 + resolution: "electron-to-chromium@npm:1.3.803" + checksum: 9a10b5cd155be6eeea8f34607fd2dc1b9bbc00964e5b6d9d1717b47c96f2104c95f01f05322a490a08583c1b6f096c686f23449e853862d3a4080843f1708bd2 languageName: node linkType: hard @@ -10110,6 +10128,7 @@ fsevents@^1.2.7: "@commitlint/config-conventional": 11.0.0 "@cypress/code-coverage": 3.9.2 "@cypress/instrument-cra": 1.4.0 + "@graasp/chatbox": "git://github.com/graasp/graasp-chatbox.git#main" "@graasp/query-client": "git://github.com/graasp/graasp-query-client.git#main" "@graasp/ui": "git://github.com/graasp/graasp-ui.git#41/rollup" "@graasp/websockets": "git://github.com/graasp/graasp-websockets.git#master" @@ -10153,7 +10172,7 @@ fsevents@^1.2.7: npm-run-all: 4.1.5 nyc: 15.1.0 prettier: 2.2.1 - pretty-quick: 3.1.0 + pretty-quick: 3.1.1 prop-types: 15.7.2 qs: 6.10.1 react: ^17.0.1 @@ -10549,6 +10568,15 @@ fsevents@^1.2.7: languageName: node linkType: hard +"html-parse-stringify@npm:^3.0.1": + version: 3.0.1 + resolution: "html-parse-stringify@npm:3.0.1" + dependencies: + void-elements: 3.1.0 + checksum: 334fdebd4b5c355dba8e95284cead6f62bf865a2359da2759b039db58c805646350016d2017875718bc3c4b9bf81a0d11be5ee0cf4774a3a5a7b97cde21cfd67 + languageName: node + linkType: hard + "html-webpack-plugin@npm:4.5.0": version: 4.5.0 resolution: "html-webpack-plugin@npm:4.5.0" @@ -10768,6 +10796,15 @@ fsevents@^1.2.7: languageName: node linkType: hard +"i18next@npm:20.4.0": + version: 20.4.0 + resolution: "i18next@npm:20.4.0" + dependencies: + "@babel/runtime": ^7.12.0 + checksum: e2e3697132317f241b6290d4d0b549b0e445a9b400666505f549d62d38cd2f8347fb511e79c6f88f59021f0c9976c6f18cfe345fa3f223c110a3b0072a256f66 + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -11144,9 +11181,11 @@ fsevents@^1.2.7: linkType: hard "is-bigint@npm:^1.0.1": - version: 1.0.3 - resolution: "is-bigint@npm:1.0.3" - checksum: d52ab4fa6491a315b0c1d09f91cc538c242dd134d2ace3751341f3f38c036d37b5d6b6153b927b57bb78ed9f839415144a966c9141132ed870c65368cba0bf9c + version: 1.0.4 + resolution: "is-bigint@npm:1.0.4" + dependencies: + has-bigints: ^1.0.1 + checksum: c56edfe09b1154f8668e53ebe8252b6f185ee852a50f9b41e8d921cb2bed425652049fbe438723f6cb48a63ca1aa051e948e7e401e093477c99c84eba244f666 languageName: node linkType: hard @@ -13957,11 +13996,11 @@ fsevents@^1.2.7: linkType: hard "nanoid@npm:^3.1.23": - version: 3.1.23 - resolution: "nanoid@npm:3.1.23" + version: 3.1.25 + resolution: "nanoid@npm:3.1.25" bin: nanoid: bin/nanoid.cjs - checksum: 8fa8dc3283a4fa159700a36cb22f61197547c8155831cb74f1b9c51fbc29ea80c136fd91001468d147a31d3a77f884958aec6c1beabac903c89780acacca9327 + checksum: e2353828c7d8fde65265e9c981380102e2021f292038a93fd27288bad390339833286e8cbc7531abe1cb2c6b317e55f38b895dcb775151637bb487388558e0ff languageName: node linkType: hard @@ -14141,9 +14180,9 @@ fsevents@^1.2.7: linkType: hard "node-releases@npm:^1.1.61, node-releases@npm:^1.1.73": - version: 1.1.73 - resolution: "node-releases@npm:1.1.73" - checksum: 44a6caec3330538a669c156fa84833725ae92b317585b106e08ab292c14da09f30cb913c10f1a7402180a51b10074832d4e045b6c3512d74c37d86b41a69e63b + version: 1.1.74 + resolution: "node-releases@npm:1.1.74" + checksum: 3dde058c30f34bda66e11821a3d6a110deb5dd3abe8b5113cf88d88344f02c7a3b4599e92fd6b1f67fff4df6c70edc23543d3138033c0f32514401438d58a933 languageName: node linkType: hard @@ -16107,9 +16146,9 @@ fsevents@^1.2.7: languageName: node linkType: hard -"pretty-quick@npm:3.1.0": - version: 3.1.0 - resolution: "pretty-quick@npm:3.1.0" +"pretty-quick@npm:3.1.1": + version: 3.1.1 + resolution: "pretty-quick@npm:3.1.1" dependencies: chalk: ^3.0.0 execa: ^4.0.0 @@ -16121,7 +16160,7 @@ fsevents@^1.2.7: prettier: ">=2.0.0" bin: pretty-quick: bin/pretty-quick.js - checksum: a0f6823ff3807c9ac15555509eafcd869c603851ba84b605d5cf77cf1c90e4ffe27ac96c2405c5ac173bb0159e76664c2979c72e47c58aa9974a5ab684692335 + checksum: d535500aca06fe09232a86344c7693a322ce4c8601c0e3eafab901529f814600f81424137b09a7ee6061df01941e0895cacf48669e86d13522519849cb113a90 languageName: node linkType: hard @@ -16592,6 +16631,19 @@ fsevents@^1.2.7: languageName: node linkType: hard +"react-i18next@npm:11.11.4": + version: 11.11.4 + resolution: "react-i18next@npm:11.11.4" + dependencies: + "@babel/runtime": ^7.14.5 + html-parse-stringify: ^3.0.1 + peerDependencies: + i18next: ">= 19.0.0" + react: ">= 16.8.0" + checksum: 5630bfe1b39e6ce893fcccfba62cd42db92d130c0d00f181ec3bc68884ee0db3c698d0b6320d05dc783e5e32c14f7e547bb8aa034788a5f317563ad32f29edf9 + languageName: node + linkType: hard + "react-i18next@npm:11.8.5": version: 11.8.5 resolution: "react-i18next@npm:11.8.5" @@ -19077,8 +19129,8 @@ resolve@^2.0.0-next.3: linkType: hard "tar@npm:^6.0.2, tar@npm:^6.1.0": - version: 6.1.7 - resolution: "tar@npm:6.1.7" + version: 6.1.8 + resolution: "tar@npm:6.1.8" dependencies: chownr: ^2.0.0 fs-minipass: ^2.0.0 @@ -19086,7 +19138,7 @@ resolve@^2.0.0-next.3: minizlib: ^2.1.1 mkdirp: ^1.0.3 yallist: ^4.0.0 - checksum: c54c069341140ffed769be0517b6152799b109c6bed448573294169154be328c5e4982fcd1dea54ed429dcee8450e4fd301514318078c5e5515c0ddf811e8bdf + checksum: f5aa41340d3415ef6f19ed0ee620db1f7cb9ea3f5ea7bfef5ea199bdb39e978d11f31d347231193e0d9262f81de3e358aa3dda6ed0c1909f22a8ce3e3a743dad languageName: node linkType: hard @@ -19473,9 +19525,9 @@ resolve@^2.0.0-next.3: linkType: hard "tslib@npm:^2.0.3": - version: 2.3.0 - resolution: "tslib@npm:2.3.0" - checksum: 8869694c26e4a7b56d449662fd54a4f9ba872c889d991202c74462bd99f10e61d5bd63199566c4284c0f742277736292a969642cc7b590f98727a7cae9529122 + version: 2.3.1 + resolution: "tslib@npm:2.3.1" + checksum: de17a98d4614481f7fcb5cd53ffc1aaf8654313be0291e1bfaee4b4bb31a20494b7d218ff2e15017883e8ea9626599b3b0e0229c18383ba9dce89da2adf15cb9 languageName: node linkType: hard @@ -20098,6 +20150,13 @@ typescript@4.1.3: languageName: node linkType: hard +"void-elements@npm:3.1.0": + version: 3.1.0 + resolution: "void-elements@npm:3.1.0" + checksum: 0390f818107fa8fce55bb0a5c3f661056001c1d5a2a48c28d582d4d847347c2ab5b7f8272314cac58acf62345126b6b09bea623a185935f6b1c3bbce0dfd7f7f + languageName: node + linkType: hard + "void-elements@npm:^2.0.1": version: 2.0.1 resolution: "void-elements@npm:2.0.1"