import React, { useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import PropTypes from 'prop-types'; import { DropdownItem } from 'reactstrap'; import { withUnstatedContainers } from '../UnstatedUtils'; import EditorContainer from '~/client/services/EditorContainer'; import { EditorMode, useDrawerMode, useEditorMode, useIsDeviceSmallerThanMd, useIsAbleToShowPageManagement, useIsAbleToShowTagLabel, useIsAbleToShowPageEditorModeManager, useIsAbleToShowPageAuthors, } from '~/stores/ui'; import { usePageAccessoriesModal, PageAccessoriesModalContents, usePageDuplicateModal, usePageRenameModal, usePageDeleteModal, OnDeletedFunction, usePagePresentationModal, } from '~/stores/modal'; import { useSWRxPageChildren } from '~/stores/page-listing'; import { useCurrentCreatedAt, useCurrentUpdatedAt, useCurrentPageId, useRevisionId, useCurrentPagePath, useCreator, useRevisionAuthor, useCurrentUser, useIsGuestUser, useIsSharedUser, useShareLinkId, } from '~/stores/context'; import { useSWRTagsInfo } from '~/stores/page'; import { toastSuccess, toastError } from '~/client/util/apiNotification'; import { apiPost } from '~/client/util/apiv1-client'; import { IPageHasId } from '~/interfaces/page'; import HistoryIcon from '../Icons/HistoryIcon'; import AttachmentIcon from '../Icons/AttachmentIcon'; import ShareLinkIcon from '../Icons/ShareLinkIcon'; import { AdditionalMenuItemsRendererProps } from '../Common/Dropdown/PageItemControl'; import { SubNavButtons } from './SubNavButtons'; import PageEditorModeManager from './PageEditorModeManager'; import { GrowiSubNavigation } from './GrowiSubNavigation'; import PresentationIcon from '../Icons/PresentationIcon'; import CreateTemplateModal from '../CreateTemplateModal'; import { exportAsMarkdown } from '~/client/services/page-operation'; type AdditionalMenuItemsProps = AdditionalMenuItemsRendererProps & { pageId: string, revisionId: string, isLinkSharingDisabled?: boolean, onClickTemplateMenuItem: (isPageTemplateModalShown: boolean) => void, } const AdditionalMenuItems = (props: AdditionalMenuItemsProps): JSX.Element => { const { t } = useTranslation(); const { pageId, revisionId, isLinkSharingDisabled, onClickTemplateMenuItem, } = props; const openPageTemplateModalHandler = () => { onClickTemplateMenuItem(true); }; const { data: isGuestUser } = useIsGuestUser(); const { data: isSharedUser } = useIsSharedUser(); const { open: openPresentationModal } = usePagePresentationModal(); const { open: openAccessoriesModal } = usePageAccessoriesModal(); const hrefForPresentationModal = '?presentation=1'; return ( <> {/* Presentation */} openPresentationModal(hrefForPresentationModal)}> { t('Presentation Mode') } {/* Export markdown */} exportAsMarkdown(pageId, revisionId, 'md')}> {t('export_bulk.export_page_markdown')} {/* TODO: show Tooltip when menu is disabled refs: PageAccessoriesModalControl */} openAccessoriesModal(PageAccessoriesModalContents.PageHistory)} disabled={isGuestUser || isSharedUser} > {t('History')} openAccessoriesModal(PageAccessoriesModalContents.Attachment)} > {t('attachment_data')} openAccessoriesModal(PageAccessoriesModalContents.ShareLink)} disabled={isGuestUser || isSharedUser || isLinkSharingDisabled} > {t('share_links.share_link_management')} {/* Create template */} { t('template.option_label.create/edit') } ); }; const GrowiContextualSubNavigation = (props) => { const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd(); const { data: isDrawerMode } = useDrawerMode(); const { data: editorMode, mutate: mutateEditorMode } = useEditorMode(); const { data: createdAt } = useCurrentCreatedAt(); const { data: updatedAt } = useCurrentUpdatedAt(); const { data: pageId } = useCurrentPageId(); const { data: revisionId } = useRevisionId(); const { data: path } = useCurrentPagePath(); const { data: creator } = useCreator(); const { data: revisionAuthor } = useRevisionAuthor(); const { data: currentUser } = useCurrentUser(); const { data: isGuestUser } = useIsGuestUser(); const { data: isSharedUser } = useIsSharedUser(); const { data: shareLinkId } = useShareLinkId(); const { data: isAbleToShowPageManagement } = useIsAbleToShowPageManagement(); const { data: isAbleToShowTagLabel } = useIsAbleToShowTagLabel(); const { data: isAbleToShowPageEditorModeManager } = useIsAbleToShowPageEditorModeManager(); const { data: isAbleToShowPageAuthors } = useIsAbleToShowPageAuthors(); const { mutate: mutateChildren } = useSWRxPageChildren(path); const { mutate: mutateSWRTagsInfo, data: tagsInfoData } = useSWRTagsInfo(pageId); const { open: openDuplicateModal } = usePageDuplicateModal(); const { open: openRenameModal } = usePageRenameModal(); const { open: openDeleteModal } = usePageDeleteModal(); const [isPageTemplateModalShown, setIsPageTempleteModalShown] = useState(false); const { editorContainer, isCompactMode, isLinkSharingDisabled, } = props; const isViewMode = editorMode === EditorMode.View; const tagsUpdatedHandler = useCallback(async(newTags: string[]) => { // It will not be reflected in the DB until the page is refreshed if (editorMode === EditorMode.Editor) { return editorContainer.setState({ tags: newTags }); } try { const { tags } = await apiPost('/tags.update', { pageId, revisionId, tags: newTags }) as { tags }; // revalidate SWRTagsInfo mutateSWRTagsInfo(); // update editorContainer.state editorContainer.setState({ tags }); toastSuccess('updated tags successfully'); } catch (err) { toastError(err, 'fail to update tags'); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [pageId]); const duplicateItemClickedHandler = useCallback(async(pageId, path) => { openDuplicateModal(pageId, path); }, [openDuplicateModal]); const renameItemClickedHandler = useCallback(async(pageId, revisionId, path) => { openRenameModal(pageId, revisionId, path); }, [openRenameModal]); const onDeletedHandler: OnDeletedFunction = useCallback((pathOrPathsToDelete, isRecursively, isCompletely) => { if (typeof pathOrPathsToDelete !== 'string') { return; } mutateChildren(); const path = pathOrPathsToDelete; if (isCompletely) { // redirect to NotFound Page window.location.href = path; } else { window.location.reload(); } }, [mutateChildren]); const deleteItemClickedHandler = useCallback(async(pageToDelete, isAbleToDeleteCompletely) => { openDeleteModal([pageToDelete], onDeletedHandler, isAbleToDeleteCompletely); }, [onDeletedHandler, openDeleteModal]); const templateMenuItemClickHandler = useCallback(() => { setIsPageTempleteModalShown(true); }, []); const ControlComponents = useCallback(() => { function onPageEditorModeButtonClicked(viewType) { mutateEditorMode(viewType); } const className = `d-flex flex-column align-items-end justify-content-center ${isViewMode ? ' h-50' : ''}`; return ( <>
{ pageId != null && isViewMode && ( ( )} onClickDuplicateMenuItem={duplicateItemClickedHandler} onClickRenameMenuItem={renameItemClickedHandler} onClickDeleteMenuItem={deleteItemClickedHandler} /> ) }
{isAbleToShowPageEditorModeManager && ( )}
{currentUser != null && ( setIsPageTempleteModalShown(false)} /> )} ); }, [ pageId, revisionId, shareLinkId, editorMode, mutateEditorMode, isCompactMode, isLinkSharingDisabled, isDeviceSmallerThanMd, isGuestUser, isSharedUser, currentUser, isViewMode, isAbleToShowPageEditorModeManager, isAbleToShowPageManagement, duplicateItemClickedHandler, renameItemClickedHandler, deleteItemClickedHandler, path, templateMenuItemClickHandler, isPageTemplateModalShown, ]); if (path == null) { return <>; } const currentPage: Partial = { _id: pageId ?? undefined, path, revision: revisionId ?? undefined, creator: creator ?? undefined, lastUpdateUser: revisionAuthor, createdAt: createdAt ?? undefined, updatedAt: updatedAt ?? undefined, }; return ( ); }; /** * Wrapper component for using unstated */ const GrowiContextualSubNavigationWrapper = withUnstatedContainers(GrowiContextualSubNavigation, [EditorContainer]); GrowiContextualSubNavigation.propTypes = { editorContainer: PropTypes.instanceOf(EditorContainer).isRequired, isCompactMode: PropTypes.bool, isLinkSharingDisabled: PropTypes.bool, }; export default GrowiContextualSubNavigationWrapper;