import React, { useEffect, useState, useMemo, useCallback, } from 'react'; import path from 'path'; import { Origin } from '@growi/core'; import { pagePathUtils, pathUtils } from '@growi/core/dist/utils'; import { normalizePath } from '@growi/core/dist/utils/path-utils'; import { format } from 'date-fns/format'; import { useAtomValue } from 'jotai'; import { useTranslation } from 'next-i18next'; import { Modal, ModalHeader, ModalBody, UncontrolledButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem, } from 'reactstrap'; import { debounce } from 'throttle-debounce'; import { useCreateTemplatePage } from '~/client/services/create-page'; import { useCreatePage } from '~/client/services/create-page/use-create-page'; import { useToastrOnError } from '~/client/services/use-toastr-on-error'; import { useCurrentUser } from '~/states/global'; import { isSearchServiceReachableAtom } from '~/states/server-configurations'; import { usePageCreateModalStatus, usePageCreateModalActions } from '~/states/ui/modal/page-create'; import PagePathAutoComplete from './PagePathAutoComplete'; import styles from './PageCreateModal.module.scss'; const { isCreatablePage, isUsersHomepage, } = pagePathUtils; const PageCreateModal: React.FC = () => { const { t } = useTranslation(); const currentUser = useCurrentUser(); const { isOpened, path: pathname = '' } = usePageCreateModalStatus(); const { close: closeCreateModal } = usePageCreateModalActions(); const { create } = useCreatePage(); const { createTemplate } = useCreateTemplatePage(); const isReachable = useAtomValue(isSearchServiceReachableAtom); // Memoize computed values const userHomepagePath = useMemo(() => pagePathUtils.userHomepagePath(currentUser), [currentUser]); const isCreatable = useMemo(() => isCreatablePage(pathname) || isUsersHomepage(pathname), [pathname]); const pageNameInputInitialValue = useMemo(() => (isCreatable ? pathUtils.addTrailingSlash(pathname) : '/'), [isCreatable, pathname]); const now = useMemo(() => format(new Date(), 'yyyy/MM/dd'), []); const todaysParentPath = useMemo( () => [userHomepagePath, t('create_page_dropdown.todays.memo', { ns: 'commons' }), now].join('/'), [userHomepagePath, t, now], ); const [todayInput, setTodayInput] = useState(''); const [pageNameInput, setPageNameInput] = useState(pageNameInputInitialValue); const [template, setTemplate] = useState(null); const [isMatchedWithUserHomepagePath, setIsMatchedWithUserHomepagePath] = useState(false); const checkIsUsersHomepageDebounce = useMemo(() => { return debounce(1000, (input: string) => { setIsMatchedWithUserHomepagePath(isUsersHomepage(input)); }); }, []); useEffect(() => { if (isOpened) { checkIsUsersHomepageDebounce(pageNameInput); } }, [isOpened, checkIsUsersHomepageDebounce, pageNameInput]); const transitBySubmitEvent = useCallback((e, transitHandler) => { // prevent page transition by submit e.preventDefault(); transitHandler(); }, []); /** * change todayInput * @param {string} value */ const onChangeTodayInputHandler = useCallback((value) => { setTodayInput(value); }, []); /** * change template * @param {string} value */ const onChangeTemplateHandler = useCallback((value) => { setTemplate(value); }, []); /** * access today page */ const createTodayPage = useCallback(async() => { const joinedPath = [todaysParentPath, todayInput].join('/'); return create( { path: joinedPath, parentPath: todaysParentPath, wip: true, origin: Origin.View, }, { onTerminated: closeCreateModal }, ); }, [closeCreateModal, create, todayInput, todaysParentPath]); /** * access input page */ const createInputPage = useCallback(async() => { const targetPath = normalizePath(pageNameInput); const parentPath = path.dirname(targetPath); return create( { path: targetPath, parentPath, wip: true, origin: Origin.View, }, { onTerminated: closeCreateModal }, ); }, [closeCreateModal, create, pageNameInput]); /** * access template page */ const createTemplatePage = useCallback(async() => { const label = (template === 'children') ? '_template' : '__template'; await createTemplate?.(label); closeCreateModal(); }, [closeCreateModal, createTemplate, template]); const createTodaysMemoWithToastr = useToastrOnError(createTodayPage); const createInputPageWithToastr = useToastrOnError(createInputPage); const createTemplateWithToastr = useToastrOnError(createTemplatePage); const renderCreateTodayForm = useMemo(() => { if (!isOpened) { return <>; } return (

{t('create_page_dropdown.todays.desc', { ns: 'commons' })}

{todaysParentPath}/
{ transitBySubmitEvent(e, createTodaysMemoWithToastr) }}> onChangeTodayInputHandler(e.target.value)} />
); }, [isOpened, todaysParentPath, todayInput, t, onChangeTodayInputHandler, transitBySubmitEvent, createTodaysMemoWithToastr]); const renderInputPageForm = useMemo(() => { if (!isOpened) { return <>; } return (

{t('Create under')}

{isReachable ? ( setPageNameInput(value)} autoFocus /> ) : (
{ transitBySubmitEvent(e, createInputPageWithToastr) }}> setPageNameInput(e.target.value)} required />
)}
{ isMatchedWithUserHomepagePath && (

Error: Cannot create page under /user page directory.

) }
); }, [isOpened, isReachable, pageNameInputInitialValue, createInputPageWithToastr, pageNameInput, isMatchedWithUserHomepagePath, t, transitBySubmitEvent]); const renderTemplatePageForm = useMemo(() => { if (!isOpened) { return <>; } return (

{t('template.modal_label.Create template under')}
{pathname}

{template == null && t('template.option_label.select')} {template === 'children' && t('template.children.label')} {template === 'descendants' && t('template.descendants.label')} onChangeTemplateHandler('children')}> {t('template.children.label')} (_template)
- {t('template.children.desc')}
onChangeTemplateHandler('descendants')}> {t('template.descendants.label')} (__template)
- {t('template.descendants.desc')}
); }, [isOpened, pathname, template, onChangeTemplateHandler, createTemplateWithToastr, t]); return ( closeCreateModal()} data-testid="page-create-modal" className={`grw-create-page ${styles['grw-create-page']}`} autoFocus={false} > closeCreateModal()}> {t('New Page')} {renderCreateTodayForm} {renderInputPageForm} {renderTemplatePageForm} ); }; export default PageCreateModal;