Просмотр исходного кода

Merge branch 'support/apply-nextjs-2' into imprv/103895-gridEdit

kaori 3 лет назад
Родитель
Сommit
c66544defc

+ 15 - 21
packages/app/src/components/InAppNotification/InAppNotificationPage.tsx

@@ -3,31 +3,30 @@ import React, {
 } from 'react';
 
 import { useTranslation } from 'next-i18next';
-import PropTypes from 'prop-types';
-import AppContainer from '~/client/services/AppContainer';
-import { withUnstatedContainers } from '../UnstatedUtils';
-import InAppNotificationList from './InAppNotificationList';
+
+import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
+import { InAppNotificationStatuses } from '~/interfaces/in-app-notification';
+import { useShowPageLimitationXL } from '~/stores/context';
+import loggerFactory from '~/utils/logger';
+
 import { useSWRxInAppNotifications, useSWRxInAppNotificationStatus } from '../../stores/in-app-notification';
-import PaginationWrapper from '../PaginationWrapper';
 import CustomNavAndContents from '../CustomNavigation/CustomNavAndContents';
-import { InAppNotificationStatuses } from '~/interfaces/in-app-notification';
-import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
+import PaginationWrapper from '../PaginationWrapper';
 
-import loggerFactory from '~/utils/logger';
+import InAppNotificationList from './InAppNotificationList';
 
-const logger = loggerFactory('growi:InAppNotificationPage');
 
+const logger = loggerFactory('growi:InAppNotificationPage');
 
-type Props = {
-  appContainer: AppContainer
-}
 
-const InAppNotificationPageBody: FC<Props> = (props) => {
-  const { appContainer } = props;
-  const limit = appContainer.config.pageLimitationXL;
+export const InAppNotificationPage: FC = () => {
   const { t } = useTranslation();
   const { mutate } = useSWRxInAppNotificationStatus();
 
+  const { data: showPageLimitationXL } = useShowPageLimitationXL();
+
+  const limit = showPageLimitationXL != null ? showPageLimitationXL : 20;
+
   const updateNotificationStatus = useCallback(async() => {
     try {
       await apiv3Post('/in-app-notification/read');
@@ -143,9 +142,4 @@ const InAppNotificationPageBody: FC<Props> = (props) => {
   );
 };
 
-const InAppNotificationPage = withUnstatedContainers(InAppNotificationPageBody, [AppContainer]);
-export default InAppNotificationPage;
-
-InAppNotificationPageBody.propTypes = {
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-};
+InAppNotificationPage.displayName = 'InAppNotificationPage';

+ 2 - 2
packages/app/src/components/Page.tsx

@@ -169,7 +169,7 @@ class PageSubstance extends React.Component<PageSubstanceProps> {
     // const DrawioModal = dynamic(() => import('./PageEditor/DrawioModal'), { ssr: false });
     const GridEditModal = dynamic(() => import('./PageEditor/GridEditModal'), { ssr: false });
     // const HandsontableModal = dynamic(() => import('./PageEditor/HandsontableModal'), { ssr: false });
-    // const LinkEditModal = dynamic(() => import('./PageEditor/LinkEditModal'), { ssr: false });
+    const LinkEditModal = dynamic(() => import('./PageEditor/LinkEditModal'), { ssr: false });
 
     return (
       <div className={`mb-5 ${isMobile ? 'page-mobile' : ''}`}>
@@ -181,7 +181,7 @@ class PageSubstance extends React.Component<PageSubstanceProps> {
         { !isGuestUser && (
           <>
             <GridEditModal ref={this.gridEditModal} />
-            {/* <LinkEditModal ref={this.LinkEditModal} /> */}
+            <LinkEditModal ref={this.linkEditModal} />
             {/* <HandsontableModal ref={this.handsontableModal} onSave={this.saveHandlerForHandsontableModal} /> */}
             {/* <DrawioModal ref={this.drawioModal} onSave={this.saveHandlerForDrawioModal} /> */}
           </>

+ 4 - 3
packages/app/src/components/PageEditor/CodeMirrorEditor.jsx

@@ -23,7 +23,7 @@ import EmojiPickerHelper from './EmojiPickerHelper';
 import GridEditModal from './GridEditModal';
 import geu from './GridEditorUtil';
 // import HandsontableModal from './HandsontableModal';
-// import LinkEditModal from './LinkEditModal';
+import LinkEditModal from './LinkEditModal';
 import mdu from './MarkdownDrawioUtil';
 import markdownLinkUtil from './MarkdownLinkUtil';
 import markdownListUtil from './MarkdownListUtil';
@@ -795,7 +795,7 @@ class CodeMirrorEditor extends AbstractEditor {
   }
 
   showLinkEditHandler() {
-    // this.linkEditModal.current.show(markdownLinkUtil.getMarkdownLink(this.getCodeMirror()));
+    this.linkEditModal.current.show(markdownLinkUtil.getMarkdownLink(this.getCodeMirror()));
   }
 
   showHandsonTableHandler() {
@@ -1060,10 +1060,11 @@ class CodeMirrorEditor extends AbstractEditor {
           ref={this.gridEditModal}
           onSave={(grid) => { return geu.replaceGridWithHtmlWithEditor(this.getCodeMirror(), grid) }}
         />
-        {/* <LinkEditModal
+        <LinkEditModal
           ref={this.linkEditModal}
           onSave={(linkTexts) => { return markdownLinkUtil.replaceFocusedMarkdownLinkWithEditor(this.getCodeMirror(), linkText) }}
         />
+        {/*
         <HandsontableModal
           ref={this.handsontableModal}
           onSave={(table) => { return mtu.replaceFocusedMarkdownTableWithEditor(this.getCodeMirror(), table) }}

+ 4 - 2
packages/app/src/components/PageEditor/LinkEditModal.jsx

@@ -24,6 +24,8 @@ import SearchTypeahead from '../SearchTypeahead';
 
 import Preview from './Preview';
 
+import styles from './LinkEditPreview.module.scss';
+
 
 class LinkEditModal extends React.PureComponent {
 
@@ -302,13 +304,13 @@ class LinkEditModal extends React.PureComponent {
                 autoFocus
               />
               <div className="d-none d-sm-block input-group-append">
-                <button type="button" id="preview-btn" className="btn btn-info btn-page-preview">
+                <button type="button" id="preview-btn" className={`btn btn-info btn-page-preview ${styles['btn-page-preview']}`}>
                   <PagePreviewIcon />
                 </button>
                 <Popover trigger="focus" placement="right" isOpen={this.state.isPreviewOpen} target="preview-btn" toggle={this.toggleIsPreviewOpen}>
                   <PopoverBody>
                     {this.state.markdown != null && pagePath != null
-                    && <div className="linkedit-preview">
+                    && <div className={`linkedit-preview ${styles['linkedit-preview']}`}>
                       <Preview markdown={this.state.markdown} pagePath={pagePath} />
                     </div>
                     }

+ 0 - 0
packages/app/src/styles/_linkedit-preview.scss → packages/app/src/components/PageEditor/LinkEditPreview.module.scss


+ 13 - 0
packages/app/src/components/Theme/utils/ThemeInjector.tsx

@@ -1,6 +1,8 @@
 
 import React from 'react';
 
+import { useIsomorphicLayoutEffect } from 'usehooks-ts';
+
 type Props = {
   children: JSX.Element,
   className: string,
@@ -9,6 +11,17 @@ type Props = {
 
 export const ThemeInjector = ({ children, className: themeClassName, bgImageNode }: Props): JSX.Element => {
   const className = `${children.props.className ?? ''} ${themeClassName}`;
+
+  // add class name to <body>
+  useIsomorphicLayoutEffect(() => {
+    document.body.classList.add(themeClassName);
+
+    // clean up
+    return () => {
+      document.body.classList.remove(themeClassName);
+    };
+  });
+
   return React.cloneElement(children, { className }, [
     <div key="grw-bg-image-wrapper" className="grw-bg-image-wrapper">{bgImageNode}</div>,
     children.props.children,

+ 52 - 17
packages/app/src/pages/me.page.tsx → packages/app/src/pages/me/[[...path]].page.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useMemo } from 'react';
 
 import {
   IUser, IUserHasId,
@@ -12,27 +12,25 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import dynamic from 'next/dynamic';
 import { useRouter } from 'next/router';
 
+import { BasicLayout } from '~/components/Layout/BasicLayout';
 import { CrowiRequest } from '~/interfaces/crowi-request';
 import { ISidebarConfig } from '~/interfaces/sidebar-config';
 import { IUserUISettings } from '~/interfaces/user-ui-settings';
 import { UserUISettingsModel } from '~/server/models/user-ui-settings';
-import {
-  usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed, useCurrentSidebarContents, useCurrentProductNavWidth,
-} from '~/stores/ui';
-import loggerFactory from '~/utils/logger';
-
-
-import { BasicLayout } from '../components/Layout/BasicLayout';
 import {
   useCurrentUser,
   useIsSearchServiceConfigured, useIsSearchServiceReachable,
   useCsrfToken, useIsSearchScopeChildrenAsDefault,
-  useRegistrationWhiteList,
-} from '../stores/context';
+  useRegistrationWhiteList, useShowPageLimitationXL,
+} from '~/stores/context';
+import {
+  usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed, useCurrentSidebarContents, useCurrentProductNavWidth,
+} from '~/stores/ui';
+import loggerFactory from '~/utils/logger';
 
 import {
   CommonProps, getNextI18NextConfig, getServerSideCommonProps, useCustomTitle,
-} from './utils/commons';
+} from '../utils/commons';
 
 
 const logger = loggerFactory('growi:pages:me');
@@ -44,16 +42,55 @@ type Props = CommonProps & {
   isSearchScopeChildrenAsDefault: boolean,
   userUISettings?: IUserUISettings
   sidebarConfig: ISidebarConfig,
+  showPageLimitationXL: number,
 
   // config
   registrationWhiteList: string[],
 };
 
+const PersonalSettings = dynamic(() => import('~/components/Me/PersonalSettings'), { ssr: false });
+const MyDraftList = dynamic(() => import('~/components/MyDraftList/MyDraftList'), { ssr: false });
+const InAppNotificationPage = dynamic(
+  () => import('~/components/InAppNotification/InAppNotificationPage').then(mod => mod.InAppNotificationPage), { ssr: false },
+);
+
 const MePage: NextPage<Props> = (props: Props) => {
+  const router = useRouter();
+  const { t } = useTranslation();
+  const { path } = router.query;
+  const pagePathKeys: string[] = Array.isArray(path) ? path : ['personal-settings'];
+
+  const mePagesMap = useMemo(() => {
+    return {
+      'personal-settings': {
+        title: t('User Settings'),
+        component: <PersonalSettings />,
+      },
+      drafts: {
+        title: t('My Drafts'),
+        component: <MyDraftList />,
+      },
+      'all-in-app-notifications': {
+        title: t('in_app_notification.notification_list'),
+        component: <InAppNotificationPage />,
+      },
+    };
+  }, [t]);
+
+  const getTargetPageToRender = (pagesMap, keys): {title: string, component: JSX.Element} => {
+    return keys.reduce((pagesMap, key) => {
+      return pagesMap[key];
+    }, pagesMap);
+  };
+
+  const targetPage = getTargetPageToRender(mePagesMap, pagePathKeys);
+
   useCurrentUser(props.currentUser ?? null);
 
   useRegistrationWhiteList(props.registrationWhiteList);
 
+  useShowPageLimitationXL(props.showPageLimitationXL);
+
   // commons
   useCsrfToken(props.csrfToken);
 
@@ -68,9 +105,6 @@ const MePage: NextPage<Props> = (props: Props) => {
   useIsSearchServiceConfigured(props.isSearchServiceConfigured);
   useIsSearchServiceReachable(props.isSearchServiceReachable);
   useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);
-  const { t } = useTranslation();
-
-  const PersonalSettings = dynamic(() => import('~/components/Me/PersonalSettings'), { ssr: false });
 
   return (
     <>
@@ -78,7 +112,7 @@ const MePage: NextPage<Props> = (props: Props) => {
 
         <header className="py-3">
           <div className="container-fluid">
-            <h1 className="title">{t('User Settings')}</h1>
+            <h1 className="title">{ targetPage.title }</h1>
           </div>
         </header>
 
@@ -86,7 +120,7 @@ const MePage: NextPage<Props> = (props: Props) => {
 
         <div id="main" className='main'>
           <div id="content-main" className="content-main grw-container-convertible">
-            <PersonalSettings />
+            {targetPage.component}
           </div>
         </div>
 
@@ -120,6 +154,8 @@ async function injectServerConfigurations(context: GetServerSidePropsContext, pr
 
   props.registrationWhiteList = configManager.getConfig('crowi', 'security:registrationWhiteList');
 
+  props.showPageLimitationXL = crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL');
+
   props.sidebarConfig = {
     isSidebarDrawerMode: configManager.getConfig('crowi', 'customize:isSidebarDrawerMode'),
     isSidebarClosedAtDockMode: configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode'),
@@ -143,7 +179,6 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
 
   const result = await getServerSideCommonProps(context);
 
-
   // check for presence
   // see: https://github.com/vercel/next.js/issues/19271#issuecomment-730006862
   if (!('props' in result)) {

+ 5 - 5
packages/app/src/server/routes/index.js

@@ -207,13 +207,13 @@ module.exports = function(crowi, app) {
   // app.get('/tags'                     , loginRequired, tag.showPage);
   app.get('/tags', loginRequired, next.delegateToNext);
 
-  app.get('/me'                                 , loginRequiredStrictly, injectUserUISettings, next.delegateToNext);
+  app.get('/me/*'                                 , loginRequiredStrictly, injectUserUISettings, next.delegateToNext);
   // external-accounts
   // my in-app-notifications
-  app.get('/me/all-in-app-notifications'   , loginRequiredStrictly, allInAppNotifications.list);
-  app.get('/me/external-accounts'               , loginRequiredStrictly, injectUserUISettings, me.externalAccounts.list);
-  // my drafts
-  app.get('/me/drafts'                          , loginRequiredStrictly, injectUserUISettings, me.drafts.list);
+  // app.get('/me/all-in-app-notifications'   , loginRequiredStrictly, allInAppNotifications.list);
+  // app.get('/me/external-accounts'               , loginRequiredStrictly, injectUserUISettings, me.externalAccounts.list);
+  // // my drafts
+  // app.get('/me/drafts'                          , loginRequiredStrictly, injectUserUISettings, me.drafts.list);
 
   app.get('/attachment/:id([0-9a-z]{24})' , certifySharedFile , loginRequired, attachment.api.get);
   app.get('/attachment/profile/:id([0-9a-z]{24})' , loginRequired, attachment.api.get);

+ 4 - 0
packages/app/src/stores/context.tsx

@@ -255,6 +255,10 @@ export const useIsUploadableFile = (initialData?: boolean): SWRResponse<boolean,
   return useStaticSWR('isUploadableFile', initialData);
 };
 
+export const useShowPageLimitationXL = (initialData?: number): SWRResponse<number, Error> => {
+  return useStaticSWR('showPageLimitationXL', initialData);
+};
+
 /** **********************************************************
  *                     Computed contexts
  *********************************************************** */