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

Merge branch 'master' into feat/106971-106972-update-pageaccessories-modal

ryoji-s 3 лет назад
Родитель
Сommit
2febf0fcb6
24 измененных файлов с 87 добавлено и 281 удалено
  1. 0 0
      packages/app/_obsolete/src/client/services/AppContainer.js
  2. 0 0
      packages/app/_obsolete/src/client/services/PageContainer.js
  3. 0 0
      packages/app/_obsolete/src/components/MyDraftList/Draft.tsx
  4. 0 0
      packages/app/_obsolete/src/components/MyDraftList/MyDraftList.jsx
  5. 0 2
      packages/app/public/static/locales/en_US/admin.json
  6. 4 0
      packages/app/public/static/locales/en_US/translation.json
  7. 0 2
      packages/app/public/static/locales/ja_JP/admin.json
  8. 4 0
      packages/app/public/static/locales/ja_JP/translation.json
  9. 0 2
      packages/app/public/static/locales/zh_CN/admin.json
  10. 4 0
      packages/app/public/static/locales/zh_CN/translation.json
  11. 2 4
      packages/app/src/components/Admin/Notification/ManageGlobalNotification.jsx
  12. 0 86
      packages/app/src/components/Admin/Users/RemoveAdminButton.jsx
  13. 0 85
      packages/app/src/components/Admin/Users/StatusSuspendedButton.jsx
  14. 3 11
      packages/app/src/components/ArchiveCreateModal.jsx
  15. 4 1
      packages/app/src/components/InAppNotification/PageNotification/PageModelNotification.tsx
  16. 6 3
      packages/app/src/components/InstallerForm.tsx
  17. 7 4
      packages/app/src/components/Navbar/GlobalSearch.tsx
  18. 24 16
      packages/app/src/components/PageEditor/ConflictDiffModal.tsx
  19. 12 17
      packages/app/src/components/PageStatusAlert.jsx
  20. 2 2
      packages/app/src/components/Sidebar/PageTree.tsx
  21. 5 1
      packages/app/src/components/Sidebar/Tag.tsx
  22. 9 5
      packages/app/src/components/TagList.tsx
  23. 0 38
      packages/app/src/components/User/LikerList.jsx
  24. 1 2
      packages/app/src/pages/utils/commons.ts

+ 0 - 0
packages/app/src/client/services/AppContainer.js → packages/app/_obsolete/src/client/services/AppContainer.js


+ 0 - 0
packages/app/src/client/services/PageContainer.js → packages/app/_obsolete/src/client/services/PageContainer.js


+ 0 - 0
packages/app/src/components/MyDraftList/Draft.tsx → packages/app/_obsolete/src/components/MyDraftList/Draft.tsx


+ 0 - 0
packages/app/src/components/MyDraftList/MyDraftList.jsx → packages/app/_obsolete/src/components/MyDraftList/MyDraftList.jsx


+ 0 - 2
packages/app/public/static/locales/en_US/admin.json

@@ -290,8 +290,6 @@
     "submit_bug_report": "<a href='https://github.com/weseek/growi/issues/new?assignees=&labels=bug&template=bug-report.md&title=Bug%3A' target='_blank' rel='noreferrer'>then submit your issue to GitHub.</a>"
     "submit_bug_report": "<a href='https://github.com/weseek/growi/issues/new?assignees=&labels=bug&template=bug-report.md&title=Bug%3A' target='_blank' rel='noreferrer'>then submit your issue to GitHub.</a>"
   },
   },
   "v5_page_migration": {
   "v5_page_migration": {
-    "page_tree_not_avaliable" : "Page tree feature is not available yet.",
-    "go_to_settings": "Go to settings to enable the feature",
     "migration_desc": "There are some pages with old v4 compatibility. To take advantage of new features such as page trees and easy renaming, please convert all your pages to v5 compatibility.",
     "migration_desc": "There are some pages with old v4 compatibility. To take advantage of new features such as page trees and easy renaming, please convert all your pages to v5 compatibility.",
     "migration_note": "Note: You will lose unique constraints from the page paths.",
     "migration_note": "Note: You will lose unique constraints from the page paths.",
     "upgrade_to_v5": "Convert to v5 compatibility",
     "upgrade_to_v5": "Convert to v5 compatibility",

+ 4 - 0
packages/app/public/static/locales/en_US/translation.json

@@ -863,5 +863,9 @@
   "footer": {
   "footer": {
     "bookmarks": "Bookmarks",
     "bookmarks": "Bookmarks",
     "recently_created": "Recently Created"
     "recently_created": "Recently Created"
+  },
+  "v5_page_migration": {
+    "page_tree_not_avaliable" : "Page tree feature is not available yet.",
+    "go_to_settings": "Go to settings to enable the feature"
   }
   }
 }
 }

+ 0 - 2
packages/app/public/static/locales/ja_JP/admin.json

@@ -322,8 +322,6 @@
     "submit_bug_report": "<a href='https://github.com/weseek/growi/issues/new?assignees=&labels=bug&template=bug-report.md&title=Bug%3A' target='_blank' rel='noreferrer'>次に GitHub で Issue を投稿してください。</a>"
     "submit_bug_report": "<a href='https://github.com/weseek/growi/issues/new?assignees=&labels=bug&template=bug-report.md&title=Bug%3A' target='_blank' rel='noreferrer'>次に GitHub で Issue を投稿してください。</a>"
   },
   },
   "v5_page_migration": {
   "v5_page_migration": {
-    "page_tree_not_avaliable" : "Page Tree 機能は現在使用できません。",
-    "go_to_settings": "設定する",
     "migration_desc": "公開されているページに 古い v4 互換形式のものが存在します。ページツリーや簡単なリネームなどの新機能を利用するには、全てのページを v5 互換形式に変換してください。",
     "migration_desc": "公開されているページに 古い v4 互換形式のものが存在します。ページツリーや簡単なリネームなどの新機能を利用するには、全てのページを v5 互換形式に変換してください。",
     "migration_note": "注意: ページパスからユニーク制約が失われます。",
     "migration_note": "注意: ページパスからユニーク制約が失われます。",
     "upgrade_to_v5": "v5 互換形式 へ変換",
     "upgrade_to_v5": "v5 互換形式 へ変換",

+ 4 - 0
packages/app/public/static/locales/ja_JP/translation.json

@@ -854,5 +854,9 @@
   "footer": {
   "footer": {
     "bookmarks": "ブックマーク",
     "bookmarks": "ブックマーク",
     "recently_created": "最近作成したページ"
     "recently_created": "最近作成したページ"
+  },
+  "v5_page_migration": {
+    "page_tree_not_avaliable" : "Page Tree 機能は現在使用できません。",
+    "go_to_settings": "設定する"
   }
   }
 }
 }

+ 0 - 2
packages/app/public/static/locales/zh_CN/admin.json

@@ -277,8 +277,6 @@
     "submit_bug_report": "<a href='https://github.com/weseek/growi/issues/new?assignees=&labels=bug&template=bug-report.md&title=Bug%3A' target='_blank' rel='noreferrer'>然后提交你的问题到GitHub。</a>"
     "submit_bug_report": "<a href='https://github.com/weseek/growi/issues/new?assignees=&labels=bug&template=bug-report.md&title=Bug%3A' target='_blank' rel='noreferrer'>然后提交你的问题到GitHub。</a>"
   },
   },
   "v5_page_migration": {
   "v5_page_migration": {
-    "page_tree_not_avaliable": "Page Tree 功能不可用",
-    "go_to_settings": "进入设置,启用该功能",
     "migration_desc": "有一些页面具有旧的v4兼容性。为了利用新的功能,如页面树和容易重命名,请将您的所有页面转换为v5兼容性。",
     "migration_desc": "有一些页面具有旧的v4兼容性。为了利用新的功能,如页面树和容易重命名,请将您的所有页面转换为v5兼容性。",
     "migration_note": "注意:你将失去页面路径的唯一约束。",
     "migration_note": "注意:你将失去页面路径的唯一约束。",
     "upgrade_to_v5": "转换为v5兼容性",
     "upgrade_to_v5": "转换为v5兼容性",

+ 4 - 0
packages/app/public/static/locales/zh_CN/translation.json

@@ -909,5 +909,9 @@
   "footer": {
   "footer": {
     "bookmarks": "书签",
     "bookmarks": "书签",
     "recently_created": "最近创建页面"
     "recently_created": "最近创建页面"
+  },
+  "v5_page_migration": {
+    "page_tree_not_avaliable": "Page Tree 功能不可用",
+    "go_to_settings": "进入设置,启用该功能"
   }
   }
 }
 }

+ 2 - 4
packages/app/src/components/Admin/Notification/ManageGlobalNotification.jsx

@@ -2,7 +2,6 @@ import React, { useCallback, useState } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
-import urljoin from 'url-join';
 
 
 import AdminNotificationContainer from '~/client/services/AdminNotificationContainer';
 import AdminNotificationContainer from '~/client/services/AdminNotificationContainer';
 import { toastError } from '~/client/util/apiNotification';
 import { toastError } from '~/client/util/apiNotification';
@@ -45,7 +44,7 @@ const ManageGlobalNotification = (props) => {
     }
     }
   }, [triggerEvents]);
   }, [triggerEvents]);
 
 
-  const submitHandler = useCallback(async() => {
+  const updateButtonClickedHandler = useCallback(async() => {
 
 
     const requestParams = {
     const requestParams = {
       triggerPath,
       triggerPath,
@@ -62,7 +61,6 @@ const ManageGlobalNotification = (props) => {
       else {
       else {
         await apiv3Post('/notification-setting/global-notification', requestParams);
         await apiv3Post('/notification-setting/global-notification', requestParams);
       }
       }
-      window.location.href = urljoin(window.location.origin, '/admin/notification#global-notification');
     }
     }
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
@@ -271,7 +269,7 @@ const ManageGlobalNotification = (props) => {
       </div>
       </div>
 
 
       <AdminUpdateButtonRow
       <AdminUpdateButtonRow
-        onClick={submitHandler}
+        onClick={updateButtonClickedHandler}
         disabled={adminNotificationContainer.state.retrieveError != null}
         disabled={adminNotificationContainer.state.retrieveError != null}
       />
       />
     </>
     </>

+ 0 - 86
packages/app/src/components/Admin/Users/RemoveAdminButton.jsx

@@ -1,86 +0,0 @@
-import React, { Fragment } from 'react';
-
-import PropTypes from 'prop-types';
-import { useTranslation } from 'next-i18next';
-
-import AdminUsersContainer from '~/client/services/AdminUsersContainer';
-import AppContainer from '~/client/services/AppContainer';
-import { toastSuccess, toastError } from '~/client/util/apiNotification';
-
-import { withUnstatedContainers } from '../../UnstatedUtils';
-
-class RemoveAdminButton extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.onClickRemoveAdminBtn = this.onClickRemoveAdminBtn.bind(this);
-  }
-
-  async onClickRemoveAdminBtn() {
-    const { t } = this.props;
-
-    try {
-      const username = await this.props.adminUsersContainer.removeUserAdmin(this.props.user._id);
-      toastSuccess(t('toaster.remove_user_admin', { username }));
-    }
-    catch (err) {
-      toastError(err);
-    }
-  }
-
-
-  renderRemoveAdminBtn() {
-    const { t } = this.props;
-
-    return (
-      <button className="dropdown-item" type="button" onClick={() => { this.onClickRemoveAdminBtn() }}>
-        <i className="icon-fw icon-user-unfollow"></i> {t('admin:user_management.user_table.remove_admin_access')}
-      </button>
-    );
-  }
-
-  renderRemoveAdminAlert() {
-    const { t } = this.props;
-
-    return (
-      <div className="px-4">
-        <i className="icon-fw icon-user-unfollow mb-2"></i>{t('admin:user_management.user_table.remove_admin_access')}
-        <p className="alert alert-danger">{t('admin:user_management.user_table.cannot_remove')}</p>
-      </div>
-    );
-  }
-
-  render() {
-    const { user } = this.props;
-    const { currentUsername } = this.props.appContainer;
-
-    return (
-      <Fragment>
-        {user.username !== currentUsername ? this.renderRemoveAdminBtn()
-          : this.renderRemoveAdminAlert()}
-      </Fragment>
-    );
-  }
-
-}
-
-const RemoveAdminButtonWrapperFC = (props) => {
-  const { t } = useTranslation();
-  return <RemoveAdminButton t={t} {...props} />;
-};
-
-/**
-* Wrapper component for using unstated
-*/
-const RemoveAdminButtonWrapper = withUnstatedContainers(RemoveAdminButtonWrapperFC, [AppContainer, AdminUsersContainer]);
-
-RemoveAdminButton.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
-
-  user: PropTypes.object.isRequired,
-};
-
-export default RemoveAdminButtonWrapper;

+ 0 - 85
packages/app/src/components/Admin/Users/StatusSuspendedButton.jsx

@@ -1,85 +0,0 @@
-import React, { Fragment } from 'react';
-
-import PropTypes from 'prop-types';
-import { useTranslation } from 'next-i18next';
-
-import AdminUsersContainer from '~/client/services/AdminUsersContainer';
-import AppContainer from '~/client/services/AppContainer';
-import { toastSuccess, toastError } from '~/client/util/apiNotification';
-
-import { withUnstatedContainers } from '../../UnstatedUtils';
-
-class StatusSuspendedButton extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.onClickDeactiveBtn = this.onClickDeactiveBtn.bind(this);
-  }
-
-  async onClickDeactiveBtn() {
-    const { t } = this.props;
-
-    try {
-      const username = await this.props.adminUsersContainer.deactivateUser(this.props.user._id);
-      toastSuccess(t('toaster.deactivate_user_success', { username }));
-    }
-    catch (err) {
-      toastError(err);
-    }
-  }
-
-  renderSuspendedBtn() {
-    const { t } = this.props;
-
-    return (
-      <button className="dropdown-item" type="button" onClick={() => { this.onClickDeactiveBtn() }}>
-        <i className="icon-fw icon-ban"></i> {t('admin:user_management.user_table.deactivate_account')}
-      </button>
-    );
-  }
-
-  renderSuspendedAlert() {
-    const { t } = this.props;
-
-    return (
-      <div className="px-4">
-        <i className="icon-fw icon-ban mb-2"></i>{t('admin:user_management.user_table.deactivate_account')}
-        <p className="alert alert-danger">{t('admin:user_management.user_table.your_own')}</p>
-      </div>
-    );
-  }
-
-  render() {
-    const { user } = this.props;
-    const { currentUsername } = this.props.appContainer;
-
-    return (
-      <Fragment>
-        {user.username !== currentUsername ? this.renderSuspendedBtn()
-          : this.renderSuspendedAlert()}
-      </Fragment>
-    );
-  }
-
-}
-
-const StatusSuspendedFormWrapperFC = (props) => {
-  const { t } = useTranslation();
-  return <StatusSuspendedButton t={t} {...props} />;
-};
-
-/**
- * Wrapper component for using unstated
- */
-const StatusSuspendedFormWrapper = withUnstatedContainers(StatusSuspendedFormWrapperFC, [AppContainer, AdminUsersContainer]);
-
-StatusSuspendedButton.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
-
-  user: PropTypes.object.isRequired,
-};
-
-export default StatusSuspendedFormWrapper;

+ 3 - 11
packages/app/src/components/ArchiveCreateModal.jsx

@@ -1,17 +1,14 @@
 import React, { useState, useCallback } from 'react';
 import React, { useState, useCallback } from 'react';
 
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 import {
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 } from 'reactstrap';
 
 
-import AppContainer from '~/client/services/AppContainer';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';
 import { apiv3Post } from '~/client/util/apiv3-client';
 import { apiv3Post } from '~/client/util/apiv3-client';
 
 
-import { withUnstatedContainers } from './UnstatedUtils';
-
 
 
 const ArchiveCreateModal = (props) => {
 const ArchiveCreateModal = (props) => {
   const { t } = useTranslation();
   const { t } = useTranslation();
@@ -235,7 +232,7 @@ const ArchiveCreateModal = (props) => {
 };
 };
 
 
 ArchiveCreateModal.propTypes = {
 ArchiveCreateModal.propTypes = {
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  // appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   isOpen: PropTypes.bool.isRequired,
   isOpen: PropTypes.bool.isRequired,
   onClose: PropTypes.func,
   onClose: PropTypes.func,
   path: PropTypes.string.isRequired,
   path: PropTypes.string.isRequired,
@@ -243,9 +240,4 @@ ArchiveCreateModal.propTypes = {
   errorMessage: PropTypes.string,
   errorMessage: PropTypes.string,
 };
 };
 
 
-/**
- * Wrapper component for using unstated
- */
-const ArchiveCreateModalWrapper = withUnstatedContainers(ArchiveCreateModal, [AppContainer]);
-
-export default ArchiveCreateModalWrapper;
+export default ArchiveCreateModal;

+ 4 - 1
packages/app/src/components/InAppNotification/PageNotification/PageModelNotification.tsx

@@ -4,6 +4,7 @@ import React, {
 
 
 import { HasObjectId } from '@growi/core';
 import { HasObjectId } from '@growi/core';
 import { PagePathLabel } from '@growi/ui';
 import { PagePathLabel } from '@growi/ui';
+import { useRouter } from 'next/router';
 
 
 import { IInAppNotificationOpenable } from '~/client/interfaces/in-app-notification-openable';
 import { IInAppNotificationOpenable } from '~/client/interfaces/in-app-notification-openable';
 import { IInAppNotification } from '~/interfaces/in-app-notification';
 import { IInAppNotification } from '~/interfaces/in-app-notification';
@@ -24,6 +25,8 @@ const PageModelNotification: ForwardRefRenderFunction<IInAppNotificationOpenable
     notification, actionMsg, actionIcon, actionUsers,
     notification, actionMsg, actionIcon, actionUsers,
   } = props;
   } = props;
 
 
+  const router = useRouter();
+
   const snapshot = parseSnapshot(notification.snapshot);
   const snapshot = parseSnapshot(notification.snapshot);
 
 
   // publish open()
   // publish open()
@@ -33,7 +36,7 @@ const PageModelNotification: ForwardRefRenderFunction<IInAppNotificationOpenable
         // jump to target page
         // jump to target page
         const targetPagePath = notification.target.path;
         const targetPagePath = notification.target.path;
         if (targetPagePath != null) {
         if (targetPagePath != null) {
-          window.location.href = targetPagePath;
+          router.push(targetPagePath);
         }
         }
       }
       }
     },
     },

+ 6 - 3
packages/app/src/components/InstallerForm.tsx

@@ -3,6 +3,7 @@ import {
 } from 'react';
 } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import { useRouter } from 'next/router';
 
 
 import { i18n as i18nConfig } from '^/config/next-i18next.config';
 import { i18n as i18nConfig } from '^/config/next-i18next.config';
 
 
@@ -12,6 +13,8 @@ import { apiv3Post } from '~/client/util/apiv3-client';
 const InstallerForm = memo((): JSX.Element => {
 const InstallerForm = memo((): JSX.Element => {
   const { t, i18n } = useTranslation();
   const { t, i18n } = useTranslation();
 
 
+  const router = useRouter();
+
   const [isValidUserName, setValidUserName] = useState(true);
   const [isValidUserName, setValidUserName] = useState(true);
   const [isSubmittingDisabled, setSubmittingDisabled] = useState(false);
   const [isSubmittingDisabled, setSubmittingDisabled] = useState(false);
   const [currentLocale, setCurrentLocale] = useState('en_US');
   const [currentLocale, setCurrentLocale] = useState('en_US');
@@ -70,7 +73,7 @@ const InstallerForm = memo((): JSX.Element => {
 
 
     try {
     try {
       await apiv3Post('/installer', data);
       await apiv3Post('/installer', data);
-      window.location.href = '/';
+      router.push('/');
     }
     }
     catch (errs) {
     catch (errs) {
       const err = errs[0];
       const err = errs[0];
@@ -78,12 +81,12 @@ const InstallerForm = memo((): JSX.Element => {
 
 
       if (code === 'failed_to_login_after_install') {
       if (code === 'failed_to_login_after_install') {
         toastError(t('installer.failed_to_login_after_install'));
         toastError(t('installer.failed_to_login_after_install'));
-        setTimeout(() => { window.location.href = '/login' }, 700); // Wait 700 ms to show toastr
+        setTimeout(() => { router.push('/login') }, 700); // Wait 700 ms to show toastr
       }
       }
 
 
       toastError(t('installer.failed_to_install'));
       toastError(t('installer.failed_to_install'));
     }
     }
-  }, [isSubmittingDisabled, t, currentLocale]);
+  }, [isSubmittingDisabled, currentLocale, router, t]);
 
 
   const hasErrorClass = isValidUserName ? '' : ' has-error';
   const hasErrorClass = isValidUserName ? '' : ' has-error';
   const unavailableUserId = isValidUserName
   const unavailableUserId = isValidUserName

+ 7 - 4
packages/app/src/components/Navbar/GlobalSearch.tsx

@@ -3,6 +3,7 @@ import React, { useState, useCallback, useRef } from 'react';
 import assert from 'assert';
 import assert from 'assert';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import { useRouter } from 'next/router';
 
 
 import { IFocusable } from '~/client/interfaces/focusable';
 import { IFocusable } from '~/client/interfaces/focusable';
 import { IPageWithSearchMeta } from '~/interfaces/search';
 import { IPageWithSearchMeta } from '~/interfaces/search';
@@ -25,6 +26,8 @@ export const GlobalSearch = (props: GlobalSearchProps): JSX.Element => {
 
 
   const { dropup } = props;
   const { dropup } = props;
 
 
+  const router = useRouter();
+
   const globalSearchFormRef = useRef<IFocusable>(null);
   const globalSearchFormRef = useRef<IFocusable>(null);
 
 
   useGlobalSearchFormRef(globalSearchFormRef);
   useGlobalSearchFormRef(globalSearchFormRef);
@@ -45,9 +48,9 @@ export const GlobalSearch = (props: GlobalSearchProps): JSX.Element => {
 
 
     // navigate to page
     // navigate to page
     if (page != null) {
     if (page != null) {
-      window.location.href = `/${page._id}`;
+      router.push(`/${page._id}`);
     }
     }
-  }, []);
+  }, [router]);
 
 
   const search = useCallback(() => {
   const search = useCallback(() => {
     const url = new URL(window.location.href);
     const url = new URL(window.location.href);
@@ -60,8 +63,8 @@ export const GlobalSearch = (props: GlobalSearchProps): JSX.Element => {
     }
     }
     url.searchParams.append('q', q);
     url.searchParams.append('q', q);
 
 
-    window.location.href = url.href;
-  }, [currentPagePath, isScopeChildren, text]);
+    router.push(url.href);
+  }, [currentPagePath, isScopeChildren, router, text]);
 
 
   const scopeLabel = isScopeChildren
   const scopeLabel = isScopeChildren
     ? t('header_search_box.label.This tree')
     ? t('header_search_box.label.This tree')

+ 24 - 16
packages/app/src/components/PageEditor/ConflictDiffModal.tsx

@@ -14,7 +14,6 @@ import { IUser } from '~/interfaces/user';
 import { useCurrentUser } from '~/stores/context';
 import { useCurrentUser } from '~/stores/context';
 import { useEditorMode } from '~/stores/ui';
 import { useEditorMode } from '~/stores/ui';
 
 
-import PageContainer from '../../client/services/PageContainer';
 import { IRevisionOnConflict } from '../../interfaces/revision';
 import { IRevisionOnConflict } from '../../interfaces/revision';
 import ExpandOrContractButton from '../ExpandOrContractButton';
 import ExpandOrContractButton from '../ExpandOrContractButton';
 import { UncontrolledCodeMirror } from '../UncontrolledCodeMirror';
 import { UncontrolledCodeMirror } from '../UncontrolledCodeMirror';
@@ -29,7 +28,7 @@ Object.keys(DMP).forEach((key) => { window[key] = DMP[key] });
 type ConflictDiffModalProps = {
 type ConflictDiffModalProps = {
   isOpen?: boolean;
   isOpen?: boolean;
   onClose?: (() => void);
   onClose?: (() => void);
-  pageContainer: PageContainer;
+  // pageContainer: PageContainer;
   markdownOnEdit: string;
   markdownOnEdit: string;
 };
 };
 
 
@@ -38,7 +37,7 @@ type IRevisionOnConflictWithStringDate = Omit<IRevisionOnConflict, 'createdAt'>
 }
 }
 
 
 const ConflictDiffModalCore = (props: ConflictDiffModalProps & { currentUser: IUser }): JSX.Element => {
 const ConflictDiffModalCore = (props: ConflictDiffModalProps & { currentUser: IUser }): JSX.Element => {
-  const { currentUser, pageContainer, onClose } = props;
+  const { currentUser, onClose } = props;
 
 
   const { data: editorMode } = useEditorMode();
   const { data: editorMode } = useEditorMode();
 
 
@@ -59,16 +58,25 @@ const ConflictDiffModalCore = (props: ConflictDiffModalProps & { currentUser: IU
     user: currentUser,
     user: currentUser,
   };
   };
   const origin: IRevisionOnConflictWithStringDate = {
   const origin: IRevisionOnConflictWithStringDate = {
-    revisionId: pageContainer.state.revisionId || '',
-    revisionBody: pageContainer.state.markdown || '',
-    createdAt: pageContainer.state.updatedAt || '',
-    user: pageContainer.state.revisionAuthor,
+    // revisionId: pageContainer.state.revisionId || '',
+    // revisionBody: pageContainer.state.markdown || '',
+    // createdAt: pageContainer.state.updatedAt || '',
+    // user: pageContainer.state.revisionAuthor,
+    revisionId:  '',
+    revisionBody: '',
+    createdAt: '',
+    user: {} as IUser,
   };
   };
   const latest: IRevisionOnConflictWithStringDate = {
   const latest: IRevisionOnConflictWithStringDate = {
-    revisionId: pageContainer.state.remoteRevisionId || '',
-    revisionBody: pageContainer.state.remoteRevisionBody || '',
-    createdAt: format(new Date(pageContainer.state.remoteRevisionUpdateAt || currentTime.toString()), 'yyyy/MM/dd HH:mm:ss'),
-    user: pageContainer.state.lastUpdateUser,
+    // revisionId: pageContainer.state.remoteRevisionId || '',
+    // revisionBody: pageContainer.state.remoteRevisionBody || '',
+    // createdAt: format(new Date(pageContainer.state.remoteRevisionUpdateAt || currentTime.toString()), 'yyyy/MM/dd HH:mm:ss'),
+    // user: pageContainer.state.lastUpdateUser,
+    revisionId: '',
+    revisionBody: '',
+    createdAt: format(new Date(''), 'yyyy/MM/dd HH:mm:ss'),
+    user: {} as IUser,
+
   };
   };
 
 
   useEffect(() => {
   useEffect(() => {
@@ -101,15 +109,15 @@ const ConflictDiffModalCore = (props: ConflictDiffModalProps & { currentUser: IU
     const codeMirrorVal = uncontrolledRef.current?.editor.doc.getValue();
     const codeMirrorVal = uncontrolledRef.current?.editor.doc.getValue();
 
 
     try {
     try {
-      await pageContainer.resolveConflict(codeMirrorVal, editorMode);
-      close();
-      pageContainer.showSuccessToastr();
+      // await pageContainer.resolveConflict(codeMirrorVal, editorMode);
+      // close();
+      // pageContainer.showSuccessToastr();
     }
     }
     catch (error) {
     catch (error) {
-      pageContainer.showErrorToastr(error);
+      // pageContainer.showErrorToastr(error);
     }
     }
 
 
-  }, [editorMode, close, pageContainer]);
+  }, [editorMode, close]);
 
 
   const resizeAndCloseButtons = useMemo(() => (
   const resizeAndCloseButtons = useMemo(() => (
     <div className="d-flex flex-nowrap">
     <div className="d-flex flex-nowrap">

+ 12 - 17
packages/app/src/components/PageStatusAlert.jsx

@@ -3,8 +3,8 @@ import React from 'react';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 
 
-import AppContainer from '~/client/services/AppContainer';
-import PageContainer from '~/client/services/PageContainer';
+// import AppContainer from '~/client/services/AppContainer';
+// import PageContainer from '~/client/services/PageContainer';
 import Username from '~/components/User/Username';
 import Username from '~/components/User/Username';
 
 
 import { withUnstatedContainers } from './UnstatedUtils';
 import { withUnstatedContainers } from './UnstatedUtils';
@@ -73,15 +73,15 @@ class PageStatusAlert extends React.Component {
   }
   }
 
 
   getContentsForUpdatedAlert() {
   getContentsForUpdatedAlert() {
-    const { t, appContainer, pageContainer } = this.props;
-    const pageEditor = appContainer.getComponentInstance('PageEditor');
+    const { t } = this.props;
+    // const pageEditor = appContainer.getComponentInstance('PageEditor');
 
 
-    let isConflictOnEdit = false;
+    const isConflictOnEdit = false;
 
 
-    if (pageEditor != null) {
-      const markdownOnEdit = pageEditor.getMarkdown();
-      isConflictOnEdit = markdownOnEdit !== pageContainer.state.markdown;
-    }
+    // if (pageEditor != null) {
+    //   const markdownOnEdit = pageEditor.getMarkdown();
+    //   isConflictOnEdit = markdownOnEdit !== pageContainer.state.markdown;
+    // }
 
 
     // TODO: re-impl with Next.js way
     // TODO: re-impl with Next.js way
     // const usernameComponentToString = ReactDOMServer.renderToString(<Username user={pageContainer.state.lastUpdateUser} />);
     // const usernameComponentToString = ReactDOMServer.renderToString(<Username user={pageContainer.state.lastUpdateUser} />);
@@ -165,8 +165,8 @@ class PageStatusAlert extends React.Component {
 PageStatusAlert.propTypes = {
 PageStatusAlert.propTypes = {
   t: PropTypes.func.isRequired, // i18next
   t: PropTypes.func.isRequired, // i18next
 
 
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
+  // appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  // pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
 };
 };
 
 
 const PageStatusAlertWrapperFC = (props) => {
 const PageStatusAlertWrapperFC = (props) => {
@@ -174,9 +174,4 @@ const PageStatusAlertWrapperFC = (props) => {
   return <PageStatusAlert t={t} {...props} />;
   return <PageStatusAlert t={t} {...props} />;
 };
 };
 
 
-/**
- * Wrapper component for using unstated
- */
-const PageStatusAlertWrapper = withUnstatedContainers(PageStatusAlertWrapperFC, [AppContainer, PageContainer]);
-
-export default PageStatusAlertWrapper;
+export default PageStatusAlertWrapperFC;

+ 2 - 2
packages/app/src/components/Sidebar/PageTree.tsx

@@ -43,8 +43,8 @@ const PageTree: FC = memo(() => {
           <h3 className="mb-0">{t('Page Tree')}</h3>
           <h3 className="mb-0">{t('Page Tree')}</h3>
         </div>
         </div>
         <div className="mt-5 mx-2 text-center">
         <div className="mt-5 mx-2 text-center">
-          <h3 className="text-gray">{t('admin:v5_page_migration.page_tree_not_avaliable')}</h3>
-          <a href="/admin">{t('admin:v5_page_migration.go_to_settings')}</a>
+          <h3 className="text-gray">{t('v5_page_migration.page_tree_not_avaliable')}</h3>
+          <a href="/admin">{t('v5_page_migration.go_to_settings')}</a>
         </div>
         </div>
       </>
       </>
     );
     );

+ 5 - 1
packages/app/src/components/Sidebar/Tag.tsx

@@ -1,6 +1,7 @@
 import React, { FC, useState, useCallback } from 'react';
 import React, { FC, useState, useCallback } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import { useRouter } from 'next/router';
 
 
 import { IDataTagCount } from '~/interfaces/tag';
 import { IDataTagCount } from '~/interfaces/tag';
 import { useSWRxTagsList } from '~/stores/tag';
 import { useSWRxTagsList } from '~/stores/tag';
@@ -13,6 +14,9 @@ const PAGING_LIMIT = 10;
 const TAG_CLOUD_LIMIT = 20;
 const TAG_CLOUD_LIMIT = 20;
 
 
 const Tag: FC = () => {
 const Tag: FC = () => {
+
+  const router = useRouter();
+
   const [activePage, setActivePage] = useState<number>(1);
   const [activePage, setActivePage] = useState<number>(1);
   const [offset, setOffset] = useState<number>(0);
   const [offset, setOffset] = useState<number>(0);
 
 
@@ -74,7 +78,7 @@ const Tag: FC = () => {
         <button
         <button
           className="btn btn-primary rounded px-4"
           className="btn btn-primary rounded px-4"
           type="button"
           type="button"
-          onClick={() => { window.location.href = '/tags' }}
+          onClick={() => router.push('/tags')}
         >
         >
           {t('Check All tags')}
           {t('Check All tags')}
         </button>
         </button>

+ 9 - 5
packages/app/src/components/TagList.tsx

@@ -3,6 +3,7 @@ import React, {
 } from 'react';
 } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import Link from 'next/link';
 
 
 import { IDataTagCount } from '~/interfaces/tag';
 import { IDataTagCount } from '~/interfaces/tag';
 
 
@@ -33,14 +34,17 @@ const TagList: FC<TagListProps> = (props:(TagListProps & typeof defaultProps)) =
       const tagListClasses: string = index === 0 ? 'list-group-item d-flex' : 'list-group-item d-flex border-top-0';
       const tagListClasses: string = index === 0 ? 'list-group-item d-flex' : 'list-group-item d-flex border-top-0';
 
 
       return (
       return (
-        <a
+        <Link
           key={tag._id}
           key={tag._id}
           href={`/_search?q=tag:${encodeURIComponent(tag.name)}`}
           href={`/_search?q=tag:${encodeURIComponent(tag.name)}`}
-          className={tagListClasses}
         >
         >
-          <div className="text-truncate list-tag-name">{tag.name}</div>
-          <div className="ml-4 my-auto py-1 px-2 list-tag-count badge badge-secondary text-white">{tag.count}</div>
-        </a>
+          <a
+            className={tagListClasses}
+          >
+            <div className="text-truncate list-tag-name">{tag.name}</div>
+            <div className="ml-4 my-auto py-1 px-2 list-tag-count badge badge-secondary text-white">{tag.count}</div>
+          </a>
+        </Link>
       );
       );
     });
     });
   }, []);
   }, []);

+ 0 - 38
packages/app/src/components/User/LikerList.jsx

@@ -1,38 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import UserPictureList from './UserPictureList';
-
-import { withUnstatedContainers } from '../UnstatedUtils';
-
-import PageContainer from '~/client/services/PageContainer';
-
-class LikerList extends React.Component {
-
-  render() {
-    const { pageContainer } = this.props;
-    return (
-      <div className="user-list-content text-truncate text-muted text-right">
-        <span className="text-info">
-          <span className="liker-user-count">{pageContainer.state.sumOfLikers}</span>
-          <i className="fa fa-fw fa-heart-o"></i>
-        </span>
-        <span className="mr-1">
-          <UserPictureList users={pageContainer.state.likerUsers} />
-        </span>
-      </div>
-    );
-  }
-
-}
-
-LikerList.propTypes = {
-  pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
-};
-
-/**
- * Wrapper component for using unstated
- */
-const LikerListWrapper = withUnstatedContainers(LikerList, [PageContainer]);
-
-export default (LikerListWrapper);

+ 1 - 2
packages/app/src/pages/utils/commons.ts

@@ -78,12 +78,11 @@ export const getNextI18NextConfig = async(
     ?? configManager.getConfig('crowi', 'app:globalLang') as Lang
     ?? configManager.getConfig('crowi', 'app:globalLang') as Lang
     ?? Lang.en_US;
     ?? Lang.en_US;
 
 
-  // TODO: Consider to not include translation as default or other architecture idea
-  // see: https://redmine.weseek.co.jp/issues/107092
   const namespaces = ['commons'];
   const namespaces = ['commons'];
   if (namespacesRequired != null) {
   if (namespacesRequired != null) {
     namespaces.push(...namespacesRequired);
     namespaces.push(...namespacesRequired);
   }
   }
+  // TODO: deprecate 'translation.json' in the future
   else {
   else {
     namespaces.push('translation');
     namespaces.push('translation');
   }
   }