diff --git a/src/components/common/PublishButton.js b/src/components/common/PublishButton.js
new file mode 100644
index 000000000..de1050e40
--- /dev/null
+++ b/src/components/common/PublishButton.js
@@ -0,0 +1,40 @@
+import React, { useContext } from 'react';
+import PropTypes from 'prop-types';
+import IconButton from '@material-ui/core/IconButton';
+import ExploreIcon from '@material-ui/icons/Explore';
+import { useTranslation } from 'react-i18next';
+import CloseIcon from '@material-ui/icons/Close';
+import Tooltip from '@material-ui/core/Tooltip';
+import {
+ buildPublishButtonId,
+ PUBLISH_ITEM_BUTTON_CLASS,
+} from '../../config/selectors';
+import { LayoutContext } from '../context/LayoutContext';
+
+const PublishButton = ({ itemId }) => {
+ const { t } = useTranslation();
+ const { setIsItemPublishOpen, isItemPublishOpen } = useContext(LayoutContext);
+
+ const onClick = () => {
+ setIsItemPublishOpen(!isItemPublishOpen);
+ };
+
+ return (
+
+
+ {isItemPublishOpen ? : }
+
+
+ );
+};
+
+PublishButton.propTypes = {
+ itemId: PropTypes.string.isRequired,
+};
+
+export default PublishButton;
diff --git a/src/components/context/LayoutContext.js b/src/components/context/LayoutContext.js
index 4b9a6888f..2c8afccdf 100644
--- a/src/components/context/LayoutContext.js
+++ b/src/components/context/LayoutContext.js
@@ -16,6 +16,7 @@ const LayoutContextProvider = ({ children }) => {
// todo: separate in item specific context
const [isItemSettingsOpen, setIsItemSettingsOpen] = useState(false);
const [isItemSharingOpen, setIsItemSharingOpen] = useState(false);
+ const [isItemPublishOpen, setIsItemPublishOpen] = useState(false);
const [isDashboardOpen, setIsDashboardOpen] = useState(false);
const [isMainMenuOpen, setIsMainMenuOpen] = useState(true);
@@ -41,6 +42,8 @@ const LayoutContextProvider = ({ children }) => {
setIsItemSharingOpen,
isDashboardOpen,
setIsDashboardOpen,
+ isItemPublishOpen,
+ setIsItemPublishOpen,
}),
[
editingItemId,
@@ -48,6 +51,7 @@ const LayoutContextProvider = ({ children }) => {
isItemMetadataMenuOpen,
isItemSettingsOpen,
isItemSharingOpen,
+ isItemPublishOpen,
isMainMenuOpen,
isDashboardOpen,
setIsDashboardOpen,
diff --git a/src/components/item/header/ItemHeaderActions.js b/src/components/item/header/ItemHeaderActions.js
index 00d5e2012..a8008a606 100644
--- a/src/components/item/header/ItemHeaderActions.js
+++ b/src/components/item/header/ItemHeaderActions.js
@@ -29,6 +29,7 @@ import {
import { hooks } from '../../../config/queryClient';
import { CurrentUserContext } from '../../context/CurrentUserContext';
import AnalyticsDashboardButton from '../../common/AnalyticsDashboardButton';
+import PublishButton from '../../common/PublishButton';
const useStyles = makeStyles((theme) => ({
root: {
@@ -87,6 +88,7 @@ const ItemHeaderActions = ({ onClickMetadata, onClickChatbox, item }) => {
)}
+
diff --git a/src/components/item/publish/ItemPublishTab.js b/src/components/item/publish/ItemPublishTab.js
new file mode 100644
index 000000000..9485092eb
--- /dev/null
+++ b/src/components/item/publish/ItemPublishTab.js
@@ -0,0 +1,51 @@
+import React, { useContext, useEffect } from 'react';
+import Container from '@material-ui/core/Container';
+import PropTypes from 'prop-types';
+import { Map } from 'immutable';
+import { Loader } from '@graasp/ui';
+import { makeStyles } from '@material-ui/core';
+import { isItemUpdateAllowedForUser } from '../../../utils/membership';
+import { LayoutContext } from '../../context/LayoutContext';
+import { CurrentUserContext } from '../../context/CurrentUserContext';
+import ItemPublishConfiguration from '../sharing/ItemPublishConfiguration';
+
+const useStyles = makeStyles((theme) => ({
+ wrapper: {
+ marginTop: theme.spacing(2),
+ },
+}));
+
+const ItemPublishTab = ({ item, memberships }) => {
+ const classes = useStyles();
+ const { data: currentMember, isLoadingCurrentMember } =
+ useContext(CurrentUserContext);
+ const { setIsItemPublishOpen } = useContext(LayoutContext);
+
+ const canEdit = isItemUpdateAllowedForUser({
+ memberships,
+ memberId: currentMember?.get('id'),
+ });
+
+ useEffect(
+ () => () => {
+ setIsItemPublishOpen(false);
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [],
+ );
+
+ if (isLoadingCurrentMember) {
+ return ;
+ }
+
+ return (
+
+
+
+ );
+};
+ItemPublishTab.propTypes = {
+ item: PropTypes.instanceOf(Map).isRequired,
+ memberships: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
+};
+export default ItemPublishTab;
diff --git a/src/components/item/sharing/ItemPublishConfiguration.js b/src/components/item/sharing/ItemPublishConfiguration.js
index 5e6bcfb9c..55f1ceff2 100644
--- a/src/components/item/sharing/ItemPublishConfiguration.js
+++ b/src/components/item/sharing/ItemPublishConfiguration.js
@@ -1,5 +1,5 @@
import React, { useContext, useState, useEffect } from 'react';
-import PropTypes, { string } from 'prop-types';
+import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router';
import { Loader } from '@graasp/ui';
@@ -26,10 +26,16 @@ import {
ITEM_VALIDATION_BUTTON_ID,
} from '../../../config/selectors';
import { getValidationStatusFromItemValidations } from '../../../utils/itemValidation';
+import {
+ getTagByName,
+ getVisibilityTagAndItemTag,
+} from '../../../utils/itemTag';
const { DELETE_ITEM_TAG, POST_ITEM_TAG, POST_ITEM_VALIDATION } = MUTATION_KEYS;
const { buildItemValidationAndReviewsKey } = DATA_KEYS;
const {
+ useTags,
+ useItemTags,
useItemValidationAndReviews,
useItemValidationStatuses,
useItemValidationReviewStatuses,
@@ -59,14 +65,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
-const ItemPublishConfiguration = ({
- item,
- edit,
- tagValue,
- itemTagValue,
- publishedTag,
- publicTag,
-}) => {
+const ItemPublishConfiguration = ({ item, edit }) => {
const { t } = useTranslation();
const classes = useStyles();
// current user
@@ -74,8 +73,22 @@ const ItemPublishConfiguration = ({
// current item
const { itemId } = useParams();
+ // item tags
const { mutate: deleteItemTag } = useMutation(DELETE_ITEM_TAG);
const { mutate: postItemTag } = useMutation(POST_ITEM_TAG);
+
+ const { data: tags, isLoading: isTagsLoading } = useTags();
+ const {
+ data: itemTags,
+ isLoading: isItemTagsLoading,
+ isError,
+ } = useItemTags(itemId);
+
+ const [itemTagValue, setItemTagValue] = useState(false);
+ const [tagValue, setTagValue] = useState(false);
+ const [isDisabled, setIsDisabled] = useState(false);
+
+ // item validation
const { mutate: validateItem } = useMutation(POST_ITEM_VALIDATION);
// get map of item validation and review statuses
@@ -101,6 +114,21 @@ const ItemPublishConfiguration = ({
const [itemValidationStatus, setItemValidationStatus] = useState(false);
+ // update state variables depending on fetch values
+ useEffect(() => {
+ if (tags && itemTags) {
+ const { tag, itemTag } = getVisibilityTagAndItemTag({ tags, itemTags });
+ setItemTagValue(itemTag);
+ setTagValue(tag);
+
+ // disable setting if any visiblity is set on any ancestor items
+ setIsDisabled(
+ tag && itemTag?.itemPath && itemTag?.itemPath !== item?.get('path'),
+ );
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [tags, itemTags, item]);
+
useEffect(() => {
// process when we fetch the item validation and review records
if (ivByStatus) {
@@ -114,10 +142,14 @@ const ItemPublishConfiguration = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ivByStatus]);
- if (isLoading) {
+ if (isLoading || isTagsLoading || isItemTagsLoading) {
return ;
}
+ if (isError) {
+ return null;
+ }
+
const handleValidate = () => {
// prevent re-send request if the item is already successfully validated, or a validation is already pending
if (
@@ -132,6 +164,8 @@ const ItemPublishConfiguration = ({
};
const publishItem = () => {
+ const publishedTag = getTagByName(tags, SETTINGS.ITEM_PUBLISHED.name);
+ const publicTag = getTagByName(tags, SETTINGS.ITEM_PUBLIC.name);
// post published tag
postItemTag({
id: itemId,
@@ -250,6 +284,10 @@ const ItemPublishConfiguration = ({
)}