Skip to content

Commit

Permalink
feat: add new tab for publish item
Browse files Browse the repository at this point in the history
  • Loading branch information
louisewang1 committed Apr 14, 2022
1 parent d5c8b4a commit eef97dd
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 53 deletions.
40 changes: 40 additions & 0 deletions src/components/common/PublishButton.js
Original file line number Diff line number Diff line change
@@ -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 (
<Tooltip title={t('Share')}>
<IconButton
aria-label="share"
className={PUBLISH_ITEM_BUTTON_CLASS}
onClick={onClick}
id={buildPublishButtonId(itemId)}
>
{isItemPublishOpen ? <CloseIcon /> : <ExploreIcon fontSize="small" />}
</IconButton>
</Tooltip>
);
};

PublishButton.propTypes = {
itemId: PropTypes.string.isRequired,
};

export default PublishButton;
4 changes: 4 additions & 0 deletions src/components/context/LayoutContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -41,13 +42,16 @@ const LayoutContextProvider = ({ children }) => {
setIsItemSharingOpen,
isDashboardOpen,
setIsDashboardOpen,
isItemPublishOpen,
setIsItemPublishOpen,
}),
[
editingItemId,
isChatboxMenuOpen,
isItemMetadataMenuOpen,
isItemSettingsOpen,
isItemSharingOpen,
isItemPublishOpen,
isMainMenuOpen,
isDashboardOpen,
setIsDashboardOpen,
Expand Down
2 changes: 2 additions & 0 deletions src/components/item/header/ItemHeaderActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -87,6 +88,7 @@ const ItemHeaderActions = ({ onClickMetadata, onClickChatbox, item }) => {
</Tooltip>
)}
<ShareButton itemId={id} />
<PublishButton itemId={id} />
<PlayerViewButton itemId={id} />
<IconButton id={ITEM_CHATBOX_BUTTON_ID} onClick={onClickChatbox}>
<ForumIcon />
Expand Down
51 changes: 51 additions & 0 deletions src/components/item/publish/ItemPublishTab.js
Original file line number Diff line number Diff line change
@@ -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 <Loader />;
}

return (
<Container disableGutters className={classes.wrapper}>
<ItemPublishConfiguration item={item} edit={canEdit} />
</Container>
);
};
ItemPublishTab.propTypes = {
item: PropTypes.instanceOf(Map).isRequired,
memberships: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
};
export default ItemPublishTab;
78 changes: 48 additions & 30 deletions src/components/item/sharing/ItemPublishConfiguration.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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,
Expand Down Expand Up @@ -59,23 +65,30 @@ const useStyles = makeStyles((theme) => ({
},
}));

const ItemPublishConfiguration = ({
item,
edit,
tagValue,
itemTagValue,
publishedTag,
publicTag,
}) => {
const ItemPublishConfiguration = ({ item, edit }) => {
const { t } = useTranslation();
const classes = useStyles();
// current user
const { data: user } = useContext(CurrentUserContext);
// 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
Expand All @@ -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) {
Expand All @@ -114,10 +142,14 @@ const ItemPublishConfiguration = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ivByStatus]);

if (isLoading) {
if (isLoading || isTagsLoading || isItemTagsLoading) {
return <Loader />;
}

if (isError) {
return null;
}

const handleValidate = () => {
// prevent re-send request if the item is already successfully validated, or a validation is already pending
if (
Expand All @@ -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,
Expand Down Expand Up @@ -250,6 +284,10 @@ const ItemPublishConfiguration = ({
)}
</Typography>
<Button
disabled={
isDisabled ||
itemValidationStatus !== ITEM_VALIDATION_STATUSES.SUCCESS
}
variant="outlined"
onClick={publishItem}
color="primary"
Expand Down Expand Up @@ -280,29 +318,9 @@ const ItemPublishConfiguration = ({
);
};

// define types for propType only
const Tag = {
id: string,
name: string,
nested: string,
createdAt: string,
};

const ItemTag = {
id: string,
tagId: string,
itemPath: string,
creator: string,
createdAt: string,
};

ItemPublishConfiguration.propTypes = {
item: PropTypes.instanceOf(Map).isRequired,
edit: PropTypes.bool.isRequired,
tagValue: PropTypes.instanceOf(Tag).isRequired,
itemTagValue: PropTypes.instanceOf(ItemTag).isRequired,
publishedTag: PropTypes.instanceOf(Tag).isRequired,
publicTag: PropTypes.instanceOf(Tag).isRequired,
};

export default ItemPublishConfiguration;
23 changes: 0 additions & 23 deletions src/components/item/sharing/VisibilitySelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
SHARE_ITEM_VISIBILITY_SELECT_ID,
} from '../../../config/selectors';
import { CurrentUserContext } from '../../context/CurrentUserContext';
import ItemPublishConfiguration from './ItemPublishConfiguration';

const { DELETE_ITEM_TAG, POST_ITEM_TAG, PUT_ITEM_LOGIN } = MUTATION_KEYS;

Expand Down Expand Up @@ -59,17 +58,13 @@ const VisibilitySelect = ({ item, edit }) => {
const [itemTagValue, setItemTagValue] = useState(false);
const [tagValue, setTagValue] = useState(false);
const [isDisabled, setIsDisabled] = useState(false);
const [isPublishedSelected, setIsPublishedSelected] = useState(false);

// update state variables depending on fetch values
useEffect(() => {
if (tags && itemTags) {
const { tag, itemTag } = getVisibilityTagAndItemTag({ tags, itemTags });
setItemTagValue(itemTag);
setTagValue(tag);
if (tagValue?.name === SETTINGS.ITEM_PUBLISHED.name) {
setIsPublishedSelected(true);
}

// disable setting if any visiblity is set on any ancestor items
setIsDisabled(
Expand Down Expand Up @@ -163,10 +158,6 @@ const VisibilitySelect = ({ item, edit }) => {
deletePreviousTag();
break;
}
case SETTINGS.ITEM_PUBLISHED.name: {
setIsPublishedSelected(true);
break;
}
default:
break;
}
Expand Down Expand Up @@ -254,9 +245,6 @@ const VisibilitySelect = ({ item, edit }) => {
{t('Pseudonymized')}
</MenuItem>
<MenuItem value={SETTINGS.ITEM_PUBLIC.name}>{t('Public')}</MenuItem>
<MenuItem value={SETTINGS.ITEM_PUBLISHED.name}>
{t('Published')}
</MenuItem>
</Select>
)}
{renderVisiblityIndication()}
Expand All @@ -267,17 +255,6 @@ const VisibilitySelect = ({ item, edit }) => {
)}
</Typography>
)}
{(isPublishedSelected ||
tagValue?.name === SETTINGS.ITEM_PUBLISHED.name) && (
<ItemPublishConfiguration
item={item}
edit={edit}
tagValue={tagValue}
itemTagValue={itemTagValue}
publishedTag={getTagByName(tags, SETTINGS.ITEM_PUBLISHED.name)}
publicTag={getTagByName(tags, SETTINGS.ITEM_PUBLIC.name)}
/>
)}
</>
);
};
Expand Down
7 changes: 7 additions & 0 deletions src/components/main/ItemScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
import { PERMISSIONS_EDITION_ALLOWED } from '../../config/constants';
import { UppyContextProvider } from '../file/UppyContext';
import FileUploader from '../file/FileUploader';
import ItemPublishTab from '../item/publish/ItemPublishTab';

const { useItem, useItemMemberships, useCurrentMember, useItemLogin } = hooks;

Expand All @@ -40,6 +41,7 @@ const ItemScreen = () => {
setIsItemSettingsOpen,
setIsItemSharingOpen,
isItemSharingOpen,
isItemPublishOpen,
isDashboardOpen,
} = useContext(LayoutContext);
const { data: currentMember } = useContext(CurrentUserContext);
Expand Down Expand Up @@ -75,6 +77,11 @@ const ItemScreen = () => {
if (isDashboardOpen) {
return <GraaspAnalyzer item={item} />;
}
if (isItemPublishOpen) {
return (
<ItemPublishTab item={item} memberships={getMembership(memberships)} />
);
}
return (
<ItemContent
item={item}
Expand Down
2 changes: 2 additions & 0 deletions src/config/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const FAVORITE_ITEM_BUTTON_CLASS = 'favoriteButton';
export const PIN_ITEM_BUTTON_CLASS = 'pinButton';
export const HIDDEN_ITEM_BUTTON_CLASS = 'hideButton';
export const SHARE_ITEM_BUTTON_CLASS = 'itemMenuShareButton';
export const PUBLISH_ITEM_BUTTON_CLASS = 'publishItemButton';
export const RESTORE_ITEMS_BUTTON_CLASS = 'itemMenuRestoreButton';
export const SHARE_ITEM_EMAIL_INPUT_ID = 'shareItemModalEmailInput';
export const buildPermissionOptionId = (id) => `permission-${id}`;
Expand Down Expand Up @@ -125,6 +126,7 @@ export const buildItemsGridPaginationButtonSelected = (page) =>
export const ITEM_HEADER_ID = 'itemHeader';
export const buildRowDraggerId = (id) => `rowDragger-${id}`;
export const buildShareButtonId = (id) => `shareButton-${id}`;
export const buildPublishButtonId = (id) => `publishButton-${id}`;
export const buildDeleteButtonId = (id) => `deleteButton-${id}`;
export const buildItemMenuButtonId = (id) => `itemMenuButton-${id}`;
export const buildPlayerButtonId = (id) => `playerButton-${id}`;
Expand Down

0 comments on commit eef97dd

Please sign in to comment.