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

Merge remote-tracking branch 'origin/support/dev-in-container' into support/share-link-for-outside-for-merge

itizawa 5 лет назад
Родитель
Сommit
03689d294d
100 измененных файлов с 470 добавлено и 474 удалено
  1. 39 0
      .devcontainer/Dockerfile
  2. 38 0
      .devcontainer/devcontainer.json
  3. 83 0
      .devcontainer/docker-compose.yml
  4. 1 1
      .github/workflows/release-rc.yml
  5. 1 1
      .github/workflows/release.yml
  6. 1 0
      .gitignore
  7. 0 21
      .vscode/extensions.json
  8. 18 1
      CHANGES.md
  9. 5 3
      config/env.dev.js
  10. 7 7
      package.json
  11. 8 2
      public/images/icons/editor/bold.svg
  12. 11 2
      resource/locales/ja/translation.json
  13. 4 3
      src/client/js/app.jsx
  14. 2 4
      src/client/js/components/Admin/AdminHome/AdminHome.jsx
  15. 2 4
      src/client/js/components/Admin/AdminHome/InstalledPluginTable.jsx
  16. 2 4
      src/client/js/components/Admin/AdminHome/SystemInfomationTable.jsx
  17. 2 4
      src/client/js/components/Admin/App/AppSetting.jsx
  18. 2 4
      src/client/js/components/Admin/App/AppSettingsPage.jsx
  19. 2 4
      src/client/js/components/Admin/App/AwsSetting.jsx
  20. 2 4
      src/client/js/components/Admin/App/MailSetting.jsx
  21. 2 4
      src/client/js/components/Admin/App/PluginSetting.jsx
  22. 2 4
      src/client/js/components/Admin/App/SiteUrlSetting.jsx
  23. 69 82
      src/client/js/components/Admin/Common/AdminNavigation.jsx
  24. 2 4
      src/client/js/components/Admin/Customize/Customize.jsx
  25. 2 4
      src/client/js/components/Admin/Customize/CustomizeCssSetting.jsx
  26. 2 4
      src/client/js/components/Admin/Customize/CustomizeFunctionSetting.jsx
  27. 2 4
      src/client/js/components/Admin/Customize/CustomizeHeaderSetting.jsx
  28. 2 4
      src/client/js/components/Admin/Customize/CustomizeHighlightSetting.jsx
  29. 2 4
      src/client/js/components/Admin/Customize/CustomizeLayoutOptions.jsx
  30. 2 4
      src/client/js/components/Admin/Customize/CustomizeLayoutSetting.jsx
  31. 2 4
      src/client/js/components/Admin/Customize/CustomizeScriptSetting.jsx
  32. 2 4
      src/client/js/components/Admin/Customize/CustomizeThemeOptions.jsx
  33. 2 4
      src/client/js/components/Admin/Customize/CustomizeTitle.jsx
  34. 2 4
      src/client/js/components/Admin/ElasticsearchManagement/ElasticsearchManagement.jsx
  35. 2 4
      src/client/js/components/Admin/ElasticsearchManagement/NormalizeIndicesControls.jsx
  36. 2 4
      src/client/js/components/Admin/ElasticsearchManagement/RebuildIndexControls.jsx
  37. 2 4
      src/client/js/components/Admin/ElasticsearchManagement/ReconnectControls.jsx
  38. 2 4
      src/client/js/components/Admin/ElasticsearchManagement/StatusTable.jsx
  39. 2 4
      src/client/js/components/Admin/ExportArchiveData/ArchiveFilesTable.jsx
  40. 3 5
      src/client/js/components/Admin/ExportArchiveData/ArchiveFilesTableMenu.jsx
  41. 2 4
      src/client/js/components/Admin/ExportArchiveData/SelectCollectionsModal.jsx
  42. 2 4
      src/client/js/components/Admin/ExportArchiveDataPage.jsx
  43. 2 4
      src/client/js/components/Admin/FullTextSearchManagement.jsx
  44. 2 4
      src/client/js/components/Admin/ImportData/GrowiArchive/ErrorViewer.jsx
  45. 2 4
      src/client/js/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx
  46. 2 4
      src/client/js/components/Admin/ImportData/GrowiArchive/ImportForm.jsx
  47. 2 4
      src/client/js/components/Admin/ImportData/GrowiArchive/UploadForm.jsx
  48. 2 4
      src/client/js/components/Admin/ImportData/GrowiArchiveSection.jsx
  49. 2 4
      src/client/js/components/Admin/ImportDataPage.jsx
  50. 2 4
      src/client/js/components/Admin/ManageExternalAccount.jsx
  51. 2 4
      src/client/js/components/Admin/MarkdownSetting/LineBreakForm.jsx
  52. 2 4
      src/client/js/components/Admin/MarkdownSetting/MarkDownSetting.jsx
  53. 2 4
      src/client/js/components/Admin/MarkdownSetting/PresentationForm.jsx
  54. 2 4
      src/client/js/components/Admin/MarkdownSetting/WhiteListInput.jsx
  55. 2 4
      src/client/js/components/Admin/MarkdownSetting/XssForm.jsx
  56. 2 4
      src/client/js/components/Admin/Notification/GlobalNotification.jsx
  57. 2 4
      src/client/js/components/Admin/Notification/GlobalNotificationList.jsx
  58. 2 4
      src/client/js/components/Admin/Notification/ManageGlobalNotification.jsx
  59. 2 4
      src/client/js/components/Admin/Notification/NotificationSetting.jsx
  60. 2 4
      src/client/js/components/Admin/Notification/SlackAppConfiguration.jsx
  61. 2 4
      src/client/js/components/Admin/Notification/UserNotificationRow.jsx
  62. 2 4
      src/client/js/components/Admin/Notification/UserTriggerNotification.jsx
  63. 5 6
      src/client/js/components/Admin/Security/BasicSecuritySetting.jsx
  64. 2 4
      src/client/js/components/Admin/Security/FacebookSecuritySetting.jsx
  65. 5 6
      src/client/js/components/Admin/Security/GitHubSecuritySetting.jsx
  66. 5 4
      src/client/js/components/Admin/Security/GoogleSecuritySetting.jsx
  67. 2 4
      src/client/js/components/Admin/Security/LdapAuthTest.jsx
  68. 2 4
      src/client/js/components/Admin/Security/LdapAuthTestModal.jsx
  69. 2 4
      src/client/js/components/Admin/Security/LdapSecuritySetting.jsx
  70. 2 4
      src/client/js/components/Admin/Security/LocalSecuritySetting.jsx
  71. 2 4
      src/client/js/components/Admin/Security/OidcSecuritySetting.jsx
  72. 2 4
      src/client/js/components/Admin/Security/SamlSecuritySetting.jsx
  73. 2 4
      src/client/js/components/Admin/Security/SecurityManagement.jsx
  74. 2 4
      src/client/js/components/Admin/Security/SecuritySetting.jsx
  75. 5 6
      src/client/js/components/Admin/Security/TwitterSecuritySetting.jsx
  76. 2 4
      src/client/js/components/Admin/UserGroup/UserGroupCreateForm.jsx
  77. 2 4
      src/client/js/components/Admin/UserGroup/UserGroupDeleteModal.jsx
  78. 2 4
      src/client/js/components/Admin/UserGroup/UserGroupPage.jsx
  79. 2 4
      src/client/js/components/Admin/UserGroup/UserGroupTable.jsx
  80. 2 4
      src/client/js/components/Admin/UserGroupDetail/UserGroupDetailPage.jsx
  81. 2 4
      src/client/js/components/Admin/UserGroupDetail/UserGroupEditForm.jsx
  82. 2 4
      src/client/js/components/Admin/UserGroupDetail/UserGroupPageList.jsx
  83. 2 4
      src/client/js/components/Admin/UserGroupDetail/UserGroupUserFormByInput.jsx
  84. 2 4
      src/client/js/components/Admin/UserGroupDetail/UserGroupUserModal.jsx
  85. 2 4
      src/client/js/components/Admin/UserGroupDetail/UserGroupUserTable.jsx
  86. 2 4
      src/client/js/components/Admin/UserManagement.jsx
  87. 2 4
      src/client/js/components/Admin/Users/ExternalAccountTable.jsx
  88. 2 4
      src/client/js/components/Admin/Users/GiveAdminButton.jsx
  89. 2 4
      src/client/js/components/Admin/Users/InviteUserControl.jsx
  90. 2 4
      src/client/js/components/Admin/Users/PasswordResetModal.jsx
  91. 2 4
      src/client/js/components/Admin/Users/RemoveAdminButton.jsx
  92. 2 4
      src/client/js/components/Admin/Users/StatusActivateButton.jsx
  93. 2 4
      src/client/js/components/Admin/Users/StatusSuspendedButton.jsx
  94. 2 4
      src/client/js/components/Admin/Users/UserInviteModal.jsx
  95. 2 4
      src/client/js/components/Admin/Users/UserMenu.jsx
  96. 2 4
      src/client/js/components/Admin/Users/UserRemoveButton.jsx
  97. 2 4
      src/client/js/components/Admin/Users/UserTable.jsx
  98. 2 4
      src/client/js/components/CreateTemplateModal.jsx
  99. 2 4
      src/client/js/components/EmptyTrashModal.jsx
  100. 2 4
      src/client/js/components/LikeButton.jsx

+ 39 - 0
.devcontainer/Dockerfile

@@ -0,0 +1,39 @@
+#-------------------------------------------------------------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
+#-------------------------------------------------------------------------------------------------------------
+
+FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-12
+
+# The node image includes a non-root user with sudo access. Use the 
+# "remoteUser" property in devcontainer.json to use it. On Linux, update 
+# these values to ensure the container user's UID/GID matches your local values.
+# See https://aka.ms/vscode-remote/containers/non-root-user for details.
+ARG USERNAME=node
+ARG USER_UID=1000
+ARG USER_GID=$USER_UID
+
+# [Optional] Update UID/GID if needed
+RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \
+        groupmod --gid $USER_GID $USERNAME \
+        && usermod --uid $USER_UID --gid $USER_GID $USERNAME \
+        && chown -R $USER_UID:$USER_GID /home/$USERNAME /workspace; \
+    fi
+
+# *************************************************************
+# * Uncomment this section to use RUN instructions to install *
+# * any needed dependencies after executing "apt-get update". *
+# * See https://docs.docker.com/engine/reference/builder/#run *
+# *************************************************************
+# ENV DEBIAN_FRONTEND=noninteractive
+# RUN apt-get update \
+#    && apt-get -y install --no-install-recommends <your-package-list-here> \
+#    #
+#    # Clean up
+#    && apt-get autoremove -y \
+#    && apt-get clean -y \
+#    && rm -rf /var/lib/apt/lists/*
+# ENV DEBIAN_FRONTEND=dialog
+
+# Uncomment to default to non-root user
+# USER $USER_UID

+ 38 - 0
.devcontainer/devcontainer.json

@@ -0,0 +1,38 @@
+// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
+// https://github.com/microsoft/vscode-dev-containers/tree/v0.117.1/containers/javascript-node-12-mongo
+// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml.
+{
+	"name": "GROWI-Dev",
+	"dockerComposeFile": "docker-compose.yml",
+	"service": "node",
+	"workspaceFolder": "/workspace/growi",
+
+	// Set *default* container specific settings.json values on container create.
+	"settings": {
+		"terminal.integrated.shell.linux": "/bin/bash"
+	},
+
+	// Add the IDs of extensions you want installed when the container is created.
+	"extensions": [
+		"dbaeumer.vscode-eslint",
+		"eamodio.gitlens",
+		"msjsdiag.debugger-for-chrome",
+		"firefox-devtools.vscode-firefox-debug",
+		"editorconfig.editorconfig",
+		"esbenp.prettier-vscode",
+		"shinnn.stylelint",
+		"hex-ci.stylelint-plus",
+	],
+
+	// Uncomment the next line if you want start specific services in your Docker Compose config.
+	// "runServices": [],
+
+	// Uncomment the line below if you want to keep your containers running after VS Code shuts down.
+	// "shutdownAction": "none",
+
+	// Use 'postCreateCommand' to run commands after the container is created.
+	// "postCreateCommand": "yarn install",
+
+	// Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
+	"remoteUser": "node"
+}

+ 83 - 0
.devcontainer/docker-compose.yml

@@ -0,0 +1,83 @@
+#-------------------------------------------------------------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
+#-------------------------------------------------------------------------------------------------------------
+
+version: '3'
+services:
+  node:
+    # Uncomment the next line to use a non-root user for all processes. You can also
+    # simply use the "remoteUser" property in devcontainer.json if you just want VS Code
+    # and its sub-processes (terminals, tasks, debugging) to execute as the user. On Linux,
+    # you may need to update USER_UID and USER_GID in .devcontainer/Dockerfile to match your
+    # user if not 1000. See https://aka.ms/vscode-remote/containers/non-root for details.
+    user: node
+
+    build:
+      context: .
+      dockerfile: Dockerfile
+
+    volumes:
+      - ..:/workspace/growi:cached
+      - ../../growi-docker-compose:/workspace/growi-docker-compose:cached
+      - ../../node_modules:/workspace/node_modules:cached
+
+
+    # Overrides default command so things don't shut down after the process ends.
+    command: sleep infinity
+
+    links:
+      - mongo
+      - elasticsearch
+      - hackmd
+
+  mongo:
+    image: mongo:3.6
+    restart: unless-stopped
+    ports:
+      - 27017:27017
+    volumes:
+      - /data/db
+
+  # This container requires '../../growi-docker-compose' repository
+  #   cloned from https://github.com/weseek/growi-docker-compose.git
+  elasticsearch:
+    build:
+      context: ../../growi-docker-compose/elasticsearch
+      dockerfile: ./Dockerfile
+    restart: unless-stopped
+    ports:
+      - 9200:9200
+    environment:
+      - bootstrap.memory_lock=true
+      - "ES_JAVA_OPTS=-Xms256m -Xmx256m"
+    ulimits:
+      memlock:
+        soft: -1
+        hard: -1
+    volumes:
+      - /usr/share/elasticsearch/data
+      - ../../growi-docker-compose/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
+
+  elasticsearch-head:
+    image: tobias74/elasticsearch-head:6
+    restart: unless-stopped
+    ports:
+      - 9100:9100
+
+  # This container requires '../../growi-docker-compose' repository
+  #   cloned from https://github.com/weseek/growi-docker-compose.git
+  hackmd:
+    build:
+      context: ../../growi-docker-compose/hackmd
+    restart: unless-stopped
+    environment:
+      - GROWI_URI=http://localhost:3000
+      # define 'storage' option value
+      # see https://github.com/sequelize/cli/blob/7160d0/src/helpers/config-helper.js#L192
+      - CMD_DB_URL=sqlite://dummyhost/hackmd/sqlite/codimd.db
+      - CMD_CSP_ENABLE=false
+    ports:
+      - 3010:3000
+    volumes:
+      - /files/sqlite

+ 1 - 1
.github/workflows/release-rc.yml

@@ -31,7 +31,7 @@ jobs:
 
     - name: Build Docker Image
       run: |
-        CACHE_REF=weseek/growi-cache:3
+        CACHE_REF=weseek/growi-cache:4
         docker buildx build \
           --tag growi \
           --platform linux/amd64 \

+ 1 - 1
.github/workflows/release.yml

@@ -88,7 +88,7 @@ jobs:
 
     - name: Build Docker Image
       run: |
-        CACHE_REF=weseek/growi-cache:3${{ env.SUFFIX }}
+        CACHE_REF=weseek/growi-cache:4${{ env.SUFFIX }}
         docker buildx build \
           --tag growi${{ env.SUFFIX }} \
           --build-arg flavor=${{ matrix.flavor }} \

+ 1 - 0
.gitignore

@@ -40,3 +40,4 @@ package-lock.json
 # IDE, dev #
 .idea
 *.orig
+*.code-workspace

+ 0 - 21
.vscode/extensions.json

@@ -1,21 +0,0 @@
-{
-	// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
-	// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
-
-	// List of extensions which should be recommended for users of this workspace.
-	"recommendations": [
-    "msjsdiag.debugger-for-chrome",
-    "hbenl.vscode-firefox-debug",
-    "editorconfig.editorconfig",
-    "dbaeumer.vscode-eslint",
-    "eg2.vscode-npm-script",
-    "christian-kohler.npm-intellisense",
-    "esbenp.prettier-vscode",
-    "shinnn.stylelint",
-    "hex-ci.stylelint-plus",
-	],
-	// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
-	"unwantedRecommendations": [
-    "hookyqr.beautify",
-	]
-}

+ 18 - 1
CHANGES.md

@@ -1,6 +1,23 @@
 # CHANGES
 
-## v4.0.3-RC
+## v4.0.5
+
+* 
+
+## v4.0.4
+
+* Feature: Drawer/Dock mode selector
+* Improvement: Admin pages navigation
+* Improvement: Ensure not to avoid session management even when accessing to healthcheck
+* Support: Refactor unstated utils
+* Support: Upgrade libs
+    * connect-mongo
+    * connect-redis
+    * mongoose
+    * mongoose-gridfs
+    * mongoose-paginate-v2
+
+## v4.0.3
 
 * Feature: Copy page path dropdown with Append params switch
 * Improvement: Truncate overflowed user browsing history

+ 5 - 3
config/env.dev.js

@@ -2,10 +2,12 @@ module.exports = {
   NODE_ENV: 'development',
   FILE_UPLOAD: 'mongodb',
   // MONGO_GRIDFS_TOTAL_LIMIT: 10485760,   // 10MB
-  // MATHJAX: 1,
+  MATHJAX: 1,
   // NO_CDN: true,
-  ELASTICSEARCH_URI: 'http://localhost:9200/growi',
-  HACKMD_URI: 'http://localhost:3010',
+  MONGO_URI: 'mongodb://mongo:27017/growi',
+  // REDIS_URI: 'http://redis:6379',
+  ELASTICSEARCH_URI: 'http://elasticsearch:9200/growi',
+  HACKMD_URI: 'http://hackmd:3010',
   // DRAWIO_URI: 'http://localhost:8080/?offline=1&https=0',
   PLUGIN_NAMES_TOBE_LOADED: [
     // 'growi-plugin-lsx',

+ 7 - 7
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "4.0.3-RC",
+  "version": "4.0.5-RC",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",
@@ -67,7 +67,6 @@
   "dependencies": {
     "//": [
       "check-node-version: see https://github.com/parshap/check-node-version/issues/35",
-      "mongoose: somehow GlobalNotificationSetting CRUD does not work with mongoose v5.6.0",
       "openid-client: Node.js 12 or higher is required for openid-client@3 and above."
     ],
     "@google-cloud/storage": "^3.3.0",
@@ -83,8 +82,8 @@
     "bunyan-format": "^0.2.1",
     "check-node-version": "^4.0.2",
     "connect-flash": "~0.1.1",
-    "connect-mongo": "^3.0.0",
-    "connect-redis": "^3.3.0",
+    "connect-mongo": "^3.2.0",
+    "connect-redis": "^4.0.4",
     "cookie-parser": "^1.4.3",
     "cross-env": "^7.0.0",
     "csrf": "^3.1.0",
@@ -115,9 +114,9 @@
     "migrate-mongo": "^7.0.1",
     "mkdirp": "^1.0.3",
     "module-alias": "^2.0.6",
-    "mongoose": "5.4.4",
-    "mongoose-gridfs": "^1.2.2",
-    "mongoose-paginate-v2": "^1.3.2",
+    "mongoose": "5.9.18",
+    "mongoose-gridfs": "^1.2.42",
+    "mongoose-paginate-v2": "^1.3.9",
     "mongoose-unique-validator": "^2.0.3",
     "multer": "~1.4.0",
     "multer-autoreap": "^1.0.3",
@@ -136,6 +135,7 @@
     "passport-twitter": "^1.0.4",
     "react-card-flip": "^1.0.10",
     "react-image-crop": "^8.3.0",
+    "redis": "^3.0.2",
     "rimraf": "^3.0.0",
     "slack-node": "^0.1.8",
     "socket.io": "^2.0.3",

+ 8 - 2
public/images/icons/editor/bold.svg

@@ -61,8 +61,6 @@
   "No diff": "No diff",
   "Shrink versions that have no diffs": "Shrink versions that have no diffs",
   "User ID": "User ID",
-  "Home": "Home",
-  "Settings": "Settings",
   "User Information": "User information",
   "Basic Info": "Basic info",
   "Name": "Name",
@@ -133,6 +131,14 @@
   "Disassociate": "Disassociate",
   "Recent Created": "Recent Created",
   "Recent Changes": "Recent Changes",
+  "personal_dropdown": {
+    "home": "Home",
+    "settings": "Settings",
+    "color_mode": "Color mode",
+    "sidebar_mode": "Sidebar mode",
+    "sidebar_mode_editor": "Sidebar mode on Editor",
+    "use_os_settings": "Use OS settings"
+  },
   "form_validation": {
     "error_message": "Some values ​​are incorrect",
     "required": "%s is required",

+ 11 - 2
resource/locales/ja/translation.json

@@ -61,8 +61,6 @@
   "No diff": "差分なし",
   "Shrink versions that have no diffs": "差分のないバージョンをコンパクトに表示する",
   "User ID": "ユーザーID",
-  "Home": "ホーム",
-  "Settings": "設定",
   "User Information": "ユーザー情報",
   "Basic Info": "ユーザーの基本情報",
   "Name": "名前",
@@ -130,8 +128,19 @@
   "Deleted Pages": "削除済みページ",
   "Sign out": "ログアウト",
   "Disassociate": "連携解除",
+  "Color mode": "カラーモード",
+  "Sidebar mode": "サイドバーモード",
+  "Sidebar mode on Editor": "サイドバーモード(編集時)",
   "Recent Created": "最新の作成",
   "Recent Changes": "最新の変更",
+  "personal_dropdown": {
+    "home": "ホーム",
+    "settings": "設定",
+    "color_mode": "カラーモード",
+    "sidebar_mode": "サイドバーモード",
+    "sidebar_mode_editor": "サイドバーモード(編集時)",
+    "use_os_settings": "OS設定を利用する"
+  },
   "form_validation": {
     "error_message": "いくつかの値が設定されていません",
     "required": "%sに値を入力してください",

+ 4 - 3
src/client/js/app.jsx

@@ -28,7 +28,8 @@ import PageAttachment from './components/PageAttachment';
 import PageStatusAlert from './components/PageStatusAlert';
 import RecentCreated from './components/RecentCreated/RecentCreated';
 import MyDraftList from './components/MyDraftList/MyDraftList';
-import UserPictureList from './components/User/UserPictureList';
+import SeenUserList from './components/User/SeenUserList';
+import LikerList from './components/User/LikerList';
 import TableOfContents from './components/TableOfContents';
 
 import PersonalSettings from './components/Me/PersonalSettings';
@@ -94,8 +95,8 @@ if (pageContainer.state.pageId != null) {
     'page-share-management': <PageShareManagement />,
 
     'revision-toc': <TableOfContents />,
-    'seen-user-list': <UserPictureList userIds={pageContainer.state.seenUserIds} />,
-    'liker-list': <UserPictureList userIds={pageContainer.state.likerUserIds} />,
+    'seen-user-list': <SeenUserList />,
+    'liker-list': <LikerList />,
 
     'user-created-list': <RecentCreated />,
     'user-draft-list': <MyDraftList />,

+ 2 - 4
src/client/js/components/Admin/AdminHome/AdminHome.jsx

@@ -5,7 +5,7 @@ import loggerFactory from '@alias/logger';
 
 import { toastError } from '../../../util/apiNotification';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminHomeContainer from '../../../services/AdminHomeContainer';
 import SystemInfomationTable from './SystemInfomationTable';
@@ -69,9 +69,7 @@ class AdminHome extends React.Component {
 
 }
 
-const AdminHomeWrapper = (props) => {
-  return createSubscribedElement(AdminHome, props, [AppContainer, AdminHomeContainer]);
-};
+const AdminHomeWrapper = withUnstatedContainers(AdminHome, [AppContainer, AdminHomeContainer]);
 
 AdminHome.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/AdminHome/InstalledPluginTable.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminHomeContainer from '../../../services/AdminHomeContainer';
 
@@ -46,8 +46,6 @@ InstalledPluginTable.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const InstalledPluginTableWrapper = (props) => {
-  return createSubscribedElement(InstalledPluginTable, props, [AppContainer, AdminHomeContainer]);
-};
+const InstalledPluginTableWrapper = withUnstatedContainers(InstalledPluginTable, [AppContainer, AdminHomeContainer]);
 
 export default withTranslation()(InstalledPluginTableWrapper);

+ 2 - 4
src/client/js/components/Admin/AdminHome/SystemInfomationTable.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminHomeContainer from '../../../services/AdminHomeContainer';
 
@@ -46,8 +46,6 @@ SystemInformationTable.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const SystemInformationTableWrapper = (props) => {
-  return createSubscribedElement(SystemInformationTable, props, [AppContainer, AdminHomeContainer]);
-};
+const SystemInformationTableWrapper = withUnstatedContainers(SystemInformationTable, [AppContainer, AdminHomeContainer]);
 
 export default withTranslation()(SystemInformationTableWrapper);

+ 2 - 4
src/client/js/components/Admin/App/AppSetting.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -127,9 +127,7 @@ class AppSetting extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const AppSettingWrapper = (props) => {
-  return createSubscribedElement(AppSetting, props, [AppContainer, AdminAppContainer]);
-};
+const AppSettingWrapper = withUnstatedContainers(AppSetting, [AppContainer, AdminAppContainer]);
 
 AppSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/App/AppSettingsPage.jsx

@@ -3,7 +3,7 @@ import { withTranslation } from 'react-i18next';
 import PropTypes from 'prop-types';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -86,9 +86,7 @@ AppSettingsPage.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const AppSettingsPageWrapper = (props) => {
-  return createSubscribedElement(AppSettingsPage, props, [AppContainer, AdminAppContainer]);
-};
+const AppSettingsPageWrapper = withUnstatedContainers(AppSettingsPage, [AppContainer, AdminAppContainer]);
 
 
 export default withTranslation()(AppSettingsPageWrapper);

+ 2 - 4
src/client/js/components/Admin/App/AwsSetting.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -143,9 +143,7 @@ class AwsSetting extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const AwsSettingWrapper = (props) => {
-  return createSubscribedElement(AwsSetting, props, [AppContainer, AdminAppContainer]);
-};
+const AwsSettingWrapper = withUnstatedContainers(AwsSetting, [AppContainer, AdminAppContainer]);
 
 AwsSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/App/MailSetting.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -104,9 +104,7 @@ class MailSetting extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const MailSettingWrapper = (props) => {
-  return createSubscribedElement(MailSetting, props, [AppContainer, AdminAppContainer]);
-};
+const MailSettingWrapper = withUnstatedContainers(MailSetting, [AppContainer, AdminAppContainer]);
 
 MailSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/App/PluginSetting.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -68,9 +68,7 @@ class PluginSetting extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const PluginSettingWrapper = (props) => {
-  return createSubscribedElement(PluginSetting, props, [AppContainer, AdminAppContainer]);
-};
+const PluginSettingWrapper = withUnstatedContainers(PluginSetting, [AppContainer, AdminAppContainer]);
 
 PluginSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/App/SiteUrlSetting.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -94,9 +94,7 @@ class SiteUrlSetting extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const SiteUrlSettingWrapper = (props) => {
-  return createSubscribedElement(SiteUrlSetting, props, [AppContainer, AdminAppContainer]);
-};
+const SiteUrlSettingWrapper = withUnstatedContainers(SiteUrlSetting, [AppContainer, AdminAppContainer]);
 
 SiteUrlSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 69 - 82
src/client/js/components/Admin/Common/AdminNavigation.jsx

@@ -1,122 +1,109 @@
+/* eslint-disable no-multi-spaces */
+/* eslint-disable react/jsx-props-no-multi-spaces */
+
 import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import urljoin from 'url-join';
 
+
 const AdminNavigation = (props) => {
   const { t } = props;
   const pathname = window.location.pathname;
 
+  // eslint-disable-next-line react/prop-types
+  const MenuLabel = ({ menu }) => {
+    switch (menu) {
+      case 'app':           return <><i className="icon-fw icon-settings"></i>        { t('App Settings') }</>;
+      case 'security':      return <><i className="icon-fw icon-shield"></i>          { t('security_settings') }</>;
+      case 'markdown':      return <><i className="icon-fw icon-note"></i>            { t('Markdown Settings') }</>;
+      case 'customize':     return <><i className="icon-fw icon-wrench"></i>          { t('Customize') }</>;
+      case 'importer':      return <><i className="icon-fw icon-cloud-upload"></i>    { t('Import Data') }</>;
+      case 'export':        return <><i className="icon-fw icon-cloud-download"></i>  { t('Export Archive Data') }</>;
+      case 'notification':  return <><i className="icon-fw icon-bell"></i>            { t('Notification Settings') }</>;
+      case 'users':         return <><i className="icon-fw icon-user"></i>            { t('User_Management') }</>;
+      case 'user-groups':   return <><i className="icon-fw icon-people"></i>          { t('UserGroup Management') }</>;
+      case 'search':        return <><i className="icon-fw icon-magnifier"></i>       { t('Full Text Search Management') }</>;
+      default:              return <><i className="icon-fw icon-home"></i>            { t('Wiki Management Home Page') }</>;
+    }
+  };
+
+  const MenuLink = ({
+    // eslint-disable-next-line react/prop-types
+    menu, isRoot, isListGroupItems, isActive,
+  }) => {
+    const pageTransitionClassName = isListGroupItems
+      ? 'list-group-item list-group-item-action border-0 round-corner'
+      : 'dropdown-item px-3 py-2';
+
+    return (
+      <a
+        href={isRoot ? '/admin' : urljoin('/admin', menu)}
+        className={`${pageTransitionClassName} ${isActive && 'active'}`}
+      >
+        <MenuLabel menu={menu} />
+      </a>
+    );
+  };
+
   const isActiveMenu = (path) => {
     return (pathname.startsWith(urljoin('/admin', path)));
   };
 
   const getListGroupItemOrDropdownItemList = (isListGroupItems) => {
-    const pageTransitionClassName = isListGroupItems ? 'list-group-item list-group-item-action border-0 round-corner' : 'dropdown-item';
     return (
       <>
-        <a
-          href="/admin"
-          className={`${pageTransitionClassName} ${pathname === '/admin' && 'active'}`}
-        >
-          <i className="icon-fw icon-home"></i> {t('Wiki Management Home Page')}
-        </a>
-        <a
-          href="/admin/app"
-          className={`${pageTransitionClassName} ${isActiveMenu('/app') && 'active'}`}
-        >
-          <i className="icon-fw icon-settings"></i> {t('App Settings')}
-        </a>
-        <a
-          href="/admin/security"
-          className={`${pageTransitionClassName} ${isActiveMenu('/security') && 'active'}`}
-        >
-          <i className="icon-fw icon-shield"></i> {t('security_settings')}
-        </a>
-        <a
-          href="/admin/markdown"
-          className={`${pageTransitionClassName} ${isActiveMenu('/markdown') && 'active'}`}
-        >
-          <i className="icon-fw icon-note"></i> {t('Markdown Settings')}
-        </a>
-        <a
-          href="/admin/customize"
-          className={`${pageTransitionClassName} ${isActiveMenu('/customize') && 'active'}`}
-        >
-          <i className="icon-fw icon-wrench"></i> {t('Customize')}
-        </a>
-        <a
-          href="/admin/importer"
-          className={`${pageTransitionClassName} ${isActiveMenu('/importer') && 'active'}`}
-        >
-          <i className="icon-fw icon-cloud-upload"></i> {t('Import Data')}
-        </a>
-        <a
-          href="/admin/export"
-          className={`${pageTransitionClassName} ${isActiveMenu('/export') && 'active'}`}
-        >
-          <i className="icon-fw icon-cloud-download"></i> {t('Export Archive Data')}
-        </a>
-        <a
-          href="/admin/notification"
-          className={`${pageTransitionClassName} ${(isActiveMenu('/notification') || isActiveMenu('/global-notification')) && 'active'}`}
-        >
-          <i className="icon-fw icon-bell"></i> {t('Notification Settings')}
-        </a>
-        <a
-          href="/admin/users"
-          className={`${pageTransitionClassName} ${(isActiveMenu('/users')) && 'active'}`}
-        >
-          <i className="icon-fw icon-user"></i> {t('User_Management')}
-        </a>
-        <a
-          href="/admin/user-groups"
-          className={`${pageTransitionClassName} ${isActiveMenu('/user-group') && 'active'}`}
-        >
-          <i className="icon-fw icon-people"></i> {t('UserGroup Management')}
-        </a>
-        <a
-          href="/admin/search"
-          className={`${pageTransitionClassName} ${isActiveMenu('/search') && 'active'}`}
-        >
-          <i className="icon-fw icon-magnifier"></i> {t('Full Text Search Management')}
-        </a>
+        <MenuLink menu="home"         isListGroupItems isActive={pathname === '/admin'} isRoot />
+        <MenuLink menu="app"          isListGroupItems isActive={isActiveMenu('/app')} />
+        <MenuLink menu="security"     isListGroupItems isActive={isActiveMenu('/security')} />
+        <MenuLink menu="markdown"     isListGroupItems isActive={isActiveMenu('/markdown')} />
+        <MenuLink menu="customize"    isListGroupItems isActive={isActiveMenu('/customize')} />
+        <MenuLink menu="importer"     isListGroupItems isActive={isActiveMenu('/importer')} />
+        <MenuLink menu="export"       isListGroupItems isActive={isActiveMenu('/export')} />
+        <MenuLink menu="notification" isListGroupItems isActive={isActiveMenu('/notification') || isActiveMenu('/global-notification')} />
+        <MenuLink menu="users"        isListGroupItems isActive={isActiveMenu('/users')} />
+        <MenuLink menu="user-groups"  isListGroupItems isActive={isActiveMenu('/user-groups')} />
+        <MenuLink menu="search"       isListGroupItems isActive={isActiveMenu('/search')} />
       </>
     );
   };
 
   return (
     <div>
-      <div className="list-group admin-navigation d-none d-md-block">
+      {/* List group */}
+      <div className="list-group admin-navigation d-none d-lg-block">
         {getListGroupItemOrDropdownItemList(true)}
       </div>
-      <div className="dropdown d-block d-md-none">
+
+      {/* Dropdown */}
+      <div className="dropdown d-block d-lg-none mb-5">
         <button
-          className="btn btn-outline-secondary dropdown-toggle col-12 text-right"
+          className="btn btn-outline-primary btn-lg dropdown-toggle col-12 text-right"
           type="button"
           id="dropdown-admin-navigation"
           data-toggle="dropdown"
           aria-haspopup="true"
           aria-expanded="false"
         >
-          <span className="float-left"><i className="icon-fw icon-home"></i>
-            {pathname === '/admin' && t('Wiki Management Home Page')}
-            {pathname === '/admin/app' && t('App Settings')}
-            {pathname === '/admin/security' && t('security_settings')}
-            {pathname === '/admin/markdown' && t('Markdown Settings')}
-            {pathname === '/admin/customize' && t('Customize')}
-            {pathname === '/admin/importer' && t('Import Data')}
-            {pathname === '/admin/export' && t('Export Archive Data')}
-            {pathname === ('/admin/notification' || '/admin/global-notification') && t('Notification Settings')}
-            {pathname === '/admin/users' && t('User_Management')}
-            {pathname === '/admin/user-groups' && t('UserGroup Management')}
-            {pathname === '/admin/search' && t('Full Text Search Management')}
+          <span className="float-left">
+            {pathname === '/admin' &&         <MenuLabel menu="home" />}
+            {isActiveMenu('/app') &&          <MenuLabel menu="app" />}
+            {isActiveMenu('/security') &&     <MenuLabel menu="security" />}
+            {isActiveMenu('/markdown') &&     <MenuLabel menu="markdown" />}
+            {isActiveMenu('/customize') &&    <MenuLabel menu="customize" />}
+            {isActiveMenu('/importer') &&     <MenuLabel menu="importer" />}
+            {isActiveMenu('/export') &&       <MenuLabel menu="export" />}
+            {(isActiveMenu('/notification') || isActiveMenu('/global-notification')) && <MenuLabel menu="notification" />}
+            {isActiveMenu('/users') &&        <MenuLabel menu="users" />}
+            {isActiveMenu('/user-groups') &&  <MenuLabel menu="user-groups" />}
+            {isActiveMenu('/search') &&       <MenuLabel menu="search" />}
           </span>
         </button>
         <div className="dropdown-menu" aria-labelledby="dropdown-admin-navigation">
           {getListGroupItemOrDropdownItemList(false)}
         </div>
       </div>
+
     </div>
   );
 };

+ 2 - 4
src/client/js/components/Admin/Customize/Customize.jsx

@@ -6,7 +6,7 @@ import { withTranslation } from 'react-i18next';
 import AppContainer from '../../../services/AppContainer';
 import AdminCustomizeContainer from '../../../services/AdminCustomizeContainer';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastError } from '../../../util/apiNotification';
 
 import CustomizeLayoutSetting from './CustomizeLayoutSetting';
@@ -75,9 +75,7 @@ class Customize extends React.Component {
 
 }
 
-const CustomizeWrapper = (props) => {
-  return createSubscribedElement(Customize, props, [AppContainer, AdminCustomizeContainer]);
-};
+const CustomizeWrapper = withUnstatedContainers(Customize, [AppContainer, AdminCustomizeContainer]);
 
 Customize.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Customize/CustomizeCssSetting.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { Card, CardBody } from 'reactstrap';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -68,9 +68,7 @@ class CustomizeCssSetting extends React.Component {
 
 }
 
-const CustomizeCssSettingWrapper = (props) => {
-  return createSubscribedElement(CustomizeCssSetting, props, [AppContainer, AdminCustomizeContainer]);
-};
+const CustomizeCssSettingWrapper = withUnstatedContainers(CustomizeCssSetting, [AppContainer, AdminCustomizeContainer]);
 
 CustomizeCssSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Customize/CustomizeFunctionSetting.jsx

@@ -6,7 +6,7 @@ import {
   Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
 } from 'reactstrap';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -173,9 +173,7 @@ class CustomizeFunctionSetting extends React.Component {
 
 }
 
-const CustomizeFunctionSettingWrapper = (props) => {
-  return createSubscribedElement(CustomizeFunctionSetting, props, [AppContainer, AdminCustomizeContainer]);
-};
+const CustomizeFunctionSettingWrapper = withUnstatedContainers(CustomizeFunctionSetting, [AppContainer, AdminCustomizeContainer]);
 
 CustomizeFunctionSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Customize/CustomizeHeaderSetting.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { Card, CardBody } from 'reactstrap';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -78,9 +78,7 @@ class CustomizeHeaderSetting extends React.Component {
 
 }
 
-const CustomizeHeaderSettingWrapper = (props) => {
-  return createSubscribedElement(CustomizeHeaderSetting, props, [AppContainer, AdminCustomizeContainer]);
-};
+const CustomizeHeaderSettingWrapper = withUnstatedContainers(CustomizeHeaderSetting, [AppContainer, AdminCustomizeContainer]);
 
 CustomizeHeaderSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Customize/CustomizeHighlightSetting.jsx

@@ -6,7 +6,7 @@ import {
   Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
 } from 'reactstrap';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -145,9 +145,7 @@ class CustomizeHighlightSetting extends React.Component {
 
 }
 
-const CustomizeHighlightSettingWrapper = (props) => {
-  return createSubscribedElement(CustomizeHighlightSetting, props, [AppContainer, AdminCustomizeContainer]);
-};
+const CustomizeHighlightSettingWrapper = withUnstatedContainers(CustomizeHighlightSetting, [AppContainer, AdminCustomizeContainer]);
 
 CustomizeHighlightSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Customize/CustomizeLayoutOptions.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AdminCustomizeContainer from '../../../services/AdminCustomizeContainer';
 import AppContainer from '../../../services/AppContainer';
 
@@ -56,9 +56,7 @@ class CustomizeLayoutOptions extends React.Component {
 
 }
 
-const CustomizeLayoutOptionsWrapper = (props) => {
-  return createSubscribedElement(CustomizeLayoutOptions, props, [AppContainer, AdminCustomizeContainer]);
-};
+const CustomizeLayoutOptionsWrapper = withUnstatedContainers(CustomizeLayoutOptions, [AppContainer, AdminCustomizeContainer]);
 
 CustomizeLayoutOptions.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Customize/CustomizeLayoutSetting.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -68,9 +68,7 @@ class CustomizeLayoutSetting extends React.Component {
 
 }
 
-const CustomizeLayoutSettingWrapper = (props) => {
-  return createSubscribedElement(CustomizeLayoutSetting, props, [AppContainer, AdminCustomizeContainer]);
-};
+const CustomizeLayoutSettingWrapper = withUnstatedContainers(CustomizeLayoutSetting, [AppContainer, AdminCustomizeContainer]);
 
 CustomizeLayoutSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Customize/CustomizeScriptSetting.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { Card, CardBody } from 'reactstrap';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -109,9 +109,7 @@ class CustomizeScriptSetting extends React.Component {
 
 }
 
-const CustomizeScriptSettingWrapper = (props) => {
-  return createSubscribedElement(CustomizeScriptSetting, props, [AppContainer, AdminCustomizeContainer]);
-};
+const CustomizeScriptSettingWrapper = withUnstatedContainers(CustomizeScriptSetting, [AppContainer, AdminCustomizeContainer]);
 
 CustomizeScriptSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Customize/CustomizeThemeOptions.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 
 import AppContainer from '../../../services/AppContainer';
 import ThemeColorBox from './ThemeColorBox';
@@ -81,9 +81,7 @@ class CustomizeThemeOptions extends React.Component {
 
 }
 
-const CustomizeThemeOptionsWrapper = (props) => {
-  return createSubscribedElement(CustomizeThemeOptions, props, [AppContainer, AdminCustomizeContainer]);
-};
+const CustomizeThemeOptionsWrapper = withUnstatedContainers(CustomizeThemeOptions, [AppContainer, AdminCustomizeContainer]);
 
 CustomizeThemeOptions.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Customize/CustomizeTitle.jsx

@@ -7,7 +7,7 @@ import { Card, CardBody } from 'reactstrap';
 import AppContainer from '../../../services/AppContainer';
 import AdminCustomizeContainer from '../../../services/AdminCustomizeContainer';
 import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 class CustomizeTitle extends React.Component {
@@ -85,9 +85,7 @@ class CustomizeTitle extends React.Component {
 
 }
 
-const CustomizeTitleWrapper = (props) => {
-  return createSubscribedElement(CustomizeTitle, props, [AppContainer, AdminCustomizeContainer]);
-};
+const CustomizeTitleWrapper = withUnstatedContainers(CustomizeTitle, [AppContainer, AdminCustomizeContainer]);
 
 CustomizeTitle.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/ElasticsearchManagement/ElasticsearchManagement.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import WebsocketContainer from '../../../services/WebsocketContainer';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -224,9 +224,7 @@ class ElasticsearchManagement extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const ElasticsearchManagementWrapper = (props) => {
-  return createSubscribedElement(ElasticsearchManagement, props, [AppContainer, WebsocketContainer]);
-};
+const ElasticsearchManagementWrapper = withUnstatedContainers(ElasticsearchManagement, [AppContainer, WebsocketContainer]);
 
 ElasticsearchManagement.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/ElasticsearchManagement/NormalizeIndicesControls.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 
 class NormalizeIndicesControls extends React.PureComponent {
 
@@ -34,9 +34,7 @@ class NormalizeIndicesControls extends React.PureComponent {
 /**
  * Wrapper component for using unstated
  */
-const NormalizeIndicesControlsWrapper = (props) => {
-  return createSubscribedElement(NormalizeIndicesControls, props, []);
-};
+const NormalizeIndicesControlsWrapper = withUnstatedContainers(NormalizeIndicesControls, []);
 
 NormalizeIndicesControls.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/ElasticsearchManagement/RebuildIndexControls.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import WebsocketContainer from '../../../services/WebsocketContainer';
 
@@ -97,9 +97,7 @@ class RebuildIndexControls extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const RebuildIndexControlsWrapper = (props) => {
-  return createSubscribedElement(RebuildIndexControls, props, [AppContainer, WebsocketContainer]);
-};
+const RebuildIndexControlsWrapper = withUnstatedContainers(RebuildIndexControls, [AppContainer, WebsocketContainer]);
 
 RebuildIndexControls.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/ElasticsearchManagement/ReconnectControls.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 
 class ReconnectControls extends React.PureComponent {
 
@@ -33,9 +33,7 @@ class ReconnectControls extends React.PureComponent {
 /**
  * Wrapper component for using unstated
  */
-const ReconnectControlsWrapper = (props) => {
-  return createSubscribedElement(ReconnectControls, props, []);
-};
+const ReconnectControlsWrapper = withUnstatedContainers(ReconnectControls, []);
 
 ReconnectControls.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/ElasticsearchManagement/StatusTable.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 
 class StatusTable extends React.PureComponent {
 
@@ -163,9 +163,7 @@ class StatusTable extends React.PureComponent {
 /**
  * Wrapper component for using unstated
  */
-const StatusTableWrapper = (props) => {
-  return createSubscribedElement(StatusTable, props, []);
-};
+const StatusTableWrapper = withUnstatedContainers(StatusTable, []);
 
 StatusTable.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/ExportArchiveData/ArchiveFilesTable.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { format } from 'date-fns';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 
 import ArchiveFilesTableMenu from './ArchiveFilesTableMenu';
@@ -61,8 +61,6 @@ ArchiveFilesTable.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const ArchiveFilesTableWrapper = (props) => {
-  return createSubscribedElement(ArchiveFilesTable, props, [AppContainer]);
-};
+const ArchiveFilesTableWrapper = withUnstatedContainers(ArchiveFilesTable, [AppContainer]);
 
 export default withTranslation()(ArchiveFilesTableWrapper);

+ 3 - 5
src/client/js/components/Admin/ExportArchiveData/ArchiveFilesTableMenu.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 // import { toastSuccess, toastError } from '../../../util/apiNotification';
 
@@ -18,7 +18,7 @@ class ArchiveFilesTableMenu extends React.Component {
         </button>
         <ul className="dropdown-menu" role="menu">
           <li className="dropdown-header">{t('admin:export_management.export_menu')}</li>
-          <button type="button" className="dropdown-item" href={`/admin/export/${this.props.fileName}`}>
+          <button type="button" className="dropdown-item" onClick={() => { window.location.href = `/admin/export/${this.props.fileName}` }}>
             <i className="icon-cloud-download" /> {t('admin:export_management.download')}
           </button>
           <button type="button" className="dropdown-item" role="button" onClick={() => this.props.onZipFileStatRemove(this.props.fileName)}>
@@ -41,8 +41,6 @@ ArchiveFilesTableMenu.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const ArchiveFilesTableMenuWrapper = (props) => {
-  return createSubscribedElement(ArchiveFilesTableMenu, props, [AppContainer]);
-};
+const ArchiveFilesTableMenuWrapper = withUnstatedContainers(ArchiveFilesTableMenu, [AppContainer]);
 
 export default withTranslation()(ArchiveFilesTableMenuWrapper);

+ 2 - 4
src/client/js/components/Admin/ExportArchiveData/SelectCollectionsModal.jsx

@@ -6,7 +6,7 @@ import {
 } from 'reactstrap';
 import * as toastr from 'toastr';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 // import { toastSuccess, toastError } from '../../../util/apiNotification';
 
@@ -242,8 +242,6 @@ SelectCollectionsModal.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const SelectCollectionsModalWrapper = (props) => {
-  return createSubscribedElement(SelectCollectionsModal, props, [AppContainer]);
-};
+const SelectCollectionsModalWrapper = withUnstatedContainers(SelectCollectionsModal, [AppContainer]);
 
 export default withTranslation()(SelectCollectionsModalWrapper);

+ 2 - 4
src/client/js/components/Admin/ExportArchiveDataPage.jsx

@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
 import * as toastr from 'toastr';
 
 
-import { createSubscribedElement } from '../UnstatedUtils';
+import { withUnstatedContainers } from '../UnstatedUtils';
 // import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../services/AppContainer';
@@ -254,8 +254,6 @@ ExportArchiveDataPage.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const ExportArchiveDataPageWrapper = (props) => {
-  return createSubscribedElement(ExportArchiveDataPage, props, [AppContainer, WebsocketContainer]);
-};
+const ExportArchiveDataPageWrapper = withUnstatedContainers(ExportArchiveDataPage, [AppContainer, WebsocketContainer]);
 
 export default withTranslation()(ExportArchiveDataPageWrapper);

+ 2 - 4
src/client/js/components/Admin/FullTextSearchManagement.jsx

@@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../UnstatedUtils';
+import { withUnstatedContainers } from '../UnstatedUtils';
 import AppContainer from '../../services/AppContainer';
 
 import ElasticsearchManagement from './ElasticsearchManagement/ElasticsearchManagement';
@@ -23,9 +23,7 @@ class FullTextSearchManagement extends React.Component {
 
 }
 
-const FullTextSearchManagementWrapper = (props) => {
-  return createSubscribedElement(FullTextSearchManagement, props, [AppContainer]);
-};
+const FullTextSearchManagementWrapper = withUnstatedContainers(FullTextSearchManagement, [AppContainer]);
 
 FullTextSearchManagement.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/ImportData/GrowiArchive/ErrorViewer.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { Modal, ModalHeader, ModalBody } from 'reactstrap';
 
-import { createSubscribedElement } from '../../../UnstatedUtils';
+import { withUnstatedContainers } from '../../../UnstatedUtils';
 
 
 class ErrorViewer extends React.Component {
@@ -45,8 +45,6 @@ ErrorViewer.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const ErrorViewerWrapper = (props) => {
-  return createSubscribedElement(ErrorViewer, props, []);
-};
+const ErrorViewerWrapper = withUnstatedContainers(ErrorViewer, []);
 
 export default withTranslation()(ErrorViewerWrapper);

+ 2 - 4
src/client/js/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx

@@ -12,7 +12,7 @@ import {
 
 import GrowiArchiveImportOption from '@commons/models/admin/growi-archive-import-option';
 
-import { createSubscribedElement } from '../../../UnstatedUtils';
+import { withUnstatedContainers } from '../../../UnstatedUtils';
 import AppContainer from '../../../../services/AppContainer';
 // import { toastSuccess, toastError } from '../../../util/apiNotification';
 
@@ -236,8 +236,6 @@ ImportCollectionConfigurationModal.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const ImportCollectionConfigurationModalWrapper = (props) => {
-  return createSubscribedElement(ImportCollectionConfigurationModal, props, [AppContainer]);
-};
+const ImportCollectionConfigurationModalWrapper = withUnstatedContainers(ImportCollectionConfigurationModal, [AppContainer]);
 
 export default withTranslation()(ImportCollectionConfigurationModalWrapper);

+ 2 - 4
src/client/js/components/Admin/ImportData/GrowiArchive/ImportForm.jsx

@@ -6,7 +6,7 @@ import GrowiArchiveImportOption from '@commons/models/admin/growi-archive-import
 import ImportOptionForPages from '@commons/models/admin/import-option-for-pages';
 import ImportOptionForRevisions from '@commons/models/admin/import-option-for-revisions';
 
-import { createSubscribedElement } from '../../../UnstatedUtils';
+import { withUnstatedContainers } from '../../../UnstatedUtils';
 import AppContainer from '../../../../services/AppContainer';
 import WebsocketContainer from '../../../../services/WebsocketContainer';
 import { toastSuccess, toastError } from '../../../../util/apiNotification';
@@ -504,8 +504,6 @@ ImportForm.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const ImportFormWrapper = (props) => {
-  return createSubscribedElement(ImportForm, props, [AppContainer, WebsocketContainer]);
-};
+const ImportFormWrapper = withUnstatedContainers(ImportForm, [AppContainer, WebsocketContainer]);
 
 export default withTranslation()(ImportFormWrapper);

+ 2 - 4
src/client/js/components/Admin/ImportData/GrowiArchive/UploadForm.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../../UnstatedUtils';
+import { withUnstatedContainers } from '../../../UnstatedUtils';
 import AppContainer from '../../../../services/AppContainer';
 // import { toastSuccess, toastError } from '../../../util/apiNotification';
 
@@ -88,8 +88,6 @@ UploadForm.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const UploadFormWrapper = (props) => {
-  return createSubscribedElement(UploadForm, props, [AppContainer]);
-};
+const UploadFormWrapper = withUnstatedContainers(UploadForm, [AppContainer]);
 
 export default withTranslation()(UploadFormWrapper);

+ 2 - 4
src/client/js/components/Admin/ImportData/GrowiArchiveSection.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import * as toastr from 'toastr';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 // import { toastSuccess, toastError } from '../../../util/apiNotification';
 
@@ -113,8 +113,6 @@ GrowiArchiveSection.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const GrowiArchiveSectionWrapper = (props) => {
-  return createSubscribedElement(GrowiArchiveSection, props, [AppContainer]);
-};
+const GrowiArchiveSectionWrapper = withUnstatedContainers(GrowiArchiveSection, [AppContainer]);
 
 export default withTranslation()(GrowiArchiveSectionWrapper);

+ 2 - 4
src/client/js/components/Admin/ImportDataPage.jsx

@@ -3,7 +3,7 @@ import { withTranslation } from 'react-i18next';
 import PropTypes from 'prop-types';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../UnstatedUtils';
+import { withUnstatedContainers } from '../UnstatedUtils';
 import { toastSuccess, toastError } from '../../util/apiNotification';
 
 import AppContainer from '../../services/AppContainer';
@@ -339,9 +339,7 @@ ImportDataPage.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const ImportDataPageWrapper = (props) => {
-  return createSubscribedElement(ImportDataPage, props, [AppContainer]);
-};
+const ImportDataPageWrapper = withUnstatedContainers(ImportDataPage, [AppContainer]);
 
 
 export default withTranslation()(ImportDataPageWrapper);

+ 2 - 4
src/client/js/components/Admin/ManageExternalAccount.jsx

@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
 
 import PaginationWrapper from '../PaginationWrapper';
 
-import { createSubscribedElement } from '../UnstatedUtils';
+import { withUnstatedContainers } from '../UnstatedUtils';
 import AppContainer from '../../services/AppContainer';
 import AdminExternalAccountsContainer from '../../services/AdminExternalAccountsContainer';
 import ExternalAccountTable from './Users/ExternalAccountTable';
@@ -72,9 +72,7 @@ ManageExternalAccount.propTypes = {
   adminExternalAccountsContainer: PropTypes.instanceOf(AdminExternalAccountsContainer).isRequired,
 };
 
-const ManageExternalAccountWrapper = (props) => {
-  return createSubscribedElement(ManageExternalAccount, props, [AppContainer, AdminExternalAccountsContainer]);
-};
+const ManageExternalAccountWrapper = withUnstatedContainers(ManageExternalAccount, [AppContainer, AdminExternalAccountsContainer]);
 
 
 export default withTranslation()(ManageExternalAccountWrapper);

+ 2 - 4
src/client/js/components/Admin/MarkdownSetting/LineBreakForm.jsx

@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 
@@ -106,9 +106,7 @@ class LineBreakForm extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const LineBreakFormWrapper = (props) => {
-  return createSubscribedElement(LineBreakForm, props, [AppContainer, AdminMarkDownContainer]);
-};
+const LineBreakFormWrapper = withUnstatedContainers(LineBreakForm, [AppContainer, AdminMarkDownContainer]);
 
 LineBreakForm.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/MarkdownSetting/MarkDownSetting.jsx

@@ -5,7 +5,7 @@ import { withTranslation } from 'react-i18next';
 
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -63,9 +63,7 @@ class MarkdownSetting extends React.Component {
 
 }
 
-const MarkdownSettingWrapper = (props) => {
-  return createSubscribedElement(MarkdownSetting, props, [AppContainer, AdminMarkDownContainer]);
-};
+const MarkdownSettingWrapper = withUnstatedContainers(MarkdownSetting, [AppContainer, AdminMarkDownContainer]);
 
 MarkdownSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/MarkdownSetting/PresentationForm.jsx

@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -127,9 +127,7 @@ class PresentationForm extends React.Component {
 
 }
 
-const PresentationFormWrapper = (props) => {
-  return createSubscribedElement(PresentationForm, props, [AppContainer, AdminMarkDownContainer]);
-};
+const PresentationFormWrapper = withUnstatedContainers(PresentationForm, [AppContainer, AdminMarkDownContainer]);
 
 PresentationForm.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/MarkdownSetting/WhiteListInput.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { tags, attrs } from '../../../../../lib/service/xss/recommended-whitelist';
 
 import AppContainer from '../../../services/AppContainer';
@@ -75,9 +75,7 @@ class WhiteListInput extends React.Component {
 
 }
 
-const WhiteListWrapper = (props) => {
-  return createSubscribedElement(WhiteListInput, props, [AppContainer, AdminMarkDownContainer]);
-};
+const WhiteListWrapper = withUnstatedContainers(WhiteListInput, [AppContainer, AdminMarkDownContainer]);
 
 WhiteListInput.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/MarkdownSetting/XssForm.jsx

@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import { tags, attrs } from '../../../../../lib/service/xss/recommended-whitelist';
 
@@ -162,9 +162,7 @@ class XssForm extends React.Component {
 
 }
 
-const XssFormWrapper = (props) => {
-  return createSubscribedElement(XssForm, props, [AppContainer, AdminMarkDownContainer]);
-};
+const XssFormWrapper = withUnstatedContainers(XssForm, [AppContainer, AdminMarkDownContainer]);
 
 XssForm.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Notification/GlobalNotification.jsx

@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
 
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -127,9 +127,7 @@ class GlobalNotification extends React.Component {
 
 }
 
-const GlobalNotificationWrapper = (props) => {
-  return createSubscribedElement(GlobalNotification, props, [AppContainer, AdminNotificationContainer]);
-};
+const GlobalNotificationWrapper = withUnstatedContainers(GlobalNotification, [AppContainer, AdminNotificationContainer]);
 
 GlobalNotification.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Notification/GlobalNotificationList.jsx

@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
 import urljoin from 'url-join';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -171,9 +171,7 @@ class GlobalNotificationList extends React.Component {
 
 }
 
-const GlobalNotificationListWrapper = (props) => {
-  return createSubscribedElement(GlobalNotificationList, props, [AppContainer, AdminNotificationContainer]);
-};
+const GlobalNotificationListWrapper = withUnstatedContainers(GlobalNotificationList, [AppContainer, AdminNotificationContainer]);
 
 GlobalNotificationList.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Notification/ManageGlobalNotification.jsx

@@ -10,7 +10,7 @@ import { toastError } from '../../../util/apiNotification';
 import TriggerEventCheckBox from './TriggerEventCheckBox';
 import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
 import AppContainer from '../../../services/AppContainer';
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 
 const logger = loggerFactory('growi:manageGlobalNotification');
 
@@ -303,9 +303,7 @@ class ManageGlobalNotification extends React.Component {
 
 }
 
-const ManageGlobalNotificationWrapper = (props) => {
-  return createSubscribedElement(ManageGlobalNotification, props, [AppContainer]);
-};
+const ManageGlobalNotificationWrapper = withUnstatedContainers(ManageGlobalNotification, [AppContainer]);
 
 ManageGlobalNotification.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Notification/NotificationSetting.jsx

@@ -7,7 +7,7 @@ import {
 
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -104,9 +104,7 @@ class NotificationSetting extends React.Component {
 
 }
 
-const NotificationSettingWrapper = (props) => {
-  return createSubscribedElement(NotificationSetting, props, [AppContainer, AdminNotificationContainer]);
-};
+const NotificationSettingWrapper = withUnstatedContainers(NotificationSetting, [AppContainer, AdminNotificationContainer]);
 
 NotificationSetting.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Notification/SlackAppConfiguration.jsx

@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
 
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -170,9 +170,7 @@ class SlackAppConfiguration extends React.Component {
 
 }
 
-const SlackAppConfigurationWrapper = (props) => {
-  return createSubscribedElement(SlackAppConfiguration, props, [AppContainer, AdminNotificationContainer]);
-};
+const SlackAppConfigurationWrapper = withUnstatedContainers(SlackAppConfiguration, [AppContainer, AdminNotificationContainer]);
 
 SlackAppConfiguration.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Notification/UserNotificationRow.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 
 import AppContainer from '../../../services/AppContainer';
 import AdminNotificationContainer from '../../../services/AdminNotificationContainer';
@@ -36,9 +36,7 @@ class UserNotificationRow extends React.PureComponent {
 }
 
 
-const UserNotificationRowWrapper = (props) => {
-  return createSubscribedElement(UserNotificationRow, props, [AppContainer, AdminNotificationContainer]);
-};
+const UserNotificationRowWrapper = withUnstatedContainers(UserNotificationRow, [AppContainer, AdminNotificationContainer]);
 
 UserNotificationRow.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Notification/UserTriggerNotification.jsx

@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
 
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -145,9 +145,7 @@ class UserTriggerNotification extends React.Component {
 }
 
 
-const UserTriggerNotificationWrapper = (props) => {
-  return createSubscribedElement(UserTriggerNotification, props, [AppContainer, AdminNotificationContainer]);
-};
+const UserTriggerNotificationWrapper = withUnstatedContainers(UserTriggerNotification, [AppContainer, AdminNotificationContainer]);
 
 UserTriggerNotification.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 5 - 6
src/client/js/components/Admin/Security/BasicSecuritySetting.jsx

@@ -3,10 +3,9 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
-import AppContainer from '../../../services/AppContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminBasicSecurityContainer from '../../../services/AdminBasicSecurityContainer';
 
@@ -135,13 +134,13 @@ class BasicSecurityManagement extends React.Component {
 
 BasicSecurityManagement.propTypes = {
   t: PropTypes.func.isRequired, // i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   adminGeneralSecurityContainer: PropTypes.instanceOf(AdminGeneralSecurityContainer).isRequired,
   adminBasicSecurityContainer: PropTypes.instanceOf(AdminBasicSecurityContainer).isRequired,
 };
 
-const OidcSecurityManagementWrapper = (props) => {
-  return createSubscribedElement(BasicSecurityManagement, props, [AppContainer, AdminGeneralSecurityContainer, AdminBasicSecurityContainer]);
-};
+const OidcSecurityManagementWrapper = withUnstatedContainers(
+  BasicSecurityManagement,
+  [AdminGeneralSecurityContainer, AdminBasicSecurityContainer],
+);
 
 export default withTranslation()(OidcSecurityManagementWrapper);

+ 2 - 4
src/client/js/components/Admin/Security/FacebookSecuritySetting.jsx

@@ -3,7 +3,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 
 import AppContainer from '../../../services/AppContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
@@ -34,8 +34,6 @@ FacebookSecurityManagement.propTypes = {
   adminGeneralSecurityContainer: PropTypes.instanceOf(AdminGeneralSecurityContainer).isRequired,
 };
 
-const TwitterSecurityManagementWrapper = (props) => {
-  return createSubscribedElement(FacebookSecurityManagement, props, [AppContainer, AdminGeneralSecurityContainer]);
-};
+const TwitterSecurityManagementWrapper = withUnstatedContainers(FacebookSecurityManagement, [AppContainer, AdminGeneralSecurityContainer]);
 
 export default withTranslation()(TwitterSecurityManagementWrapper);

+ 5 - 6
src/client/js/components/Admin/Security/GitHubSecuritySetting.jsx

@@ -3,10 +3,9 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
-import AppContainer from '../../../services/AppContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminGitHubSecurityContainer from '../../../services/AdminGitHubSecurityContainer';
 
@@ -206,13 +205,13 @@ class GitHubSecurityManagement extends React.Component {
 
 GitHubSecurityManagement.propTypes = {
   t: PropTypes.func.isRequired, // i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   adminGeneralSecurityContainer: PropTypes.instanceOf(AdminGeneralSecurityContainer).isRequired,
   adminGitHubSecurityContainer: PropTypes.instanceOf(AdminGitHubSecurityContainer).isRequired,
 };
 
-const GitHubSecurityManagementWrapper = (props) => {
-  return createSubscribedElement(GitHubSecurityManagement, props, [AppContainer, AdminGeneralSecurityContainer, AdminGitHubSecurityContainer]);
-};
+const GitHubSecurityManagementWrapper = withUnstatedContainers(
+  GitHubSecurityManagement,
+  [AdminGeneralSecurityContainer, AdminGitHubSecurityContainer],
+);
 
 export default withTranslation()(GitHubSecurityManagementWrapper);

+ 5 - 4
src/client/js/components/Admin/Security/GoogleSecuritySetting.jsx

@@ -3,7 +3,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -218,8 +218,9 @@ GoogleSecurityManagement.propTypes = {
   adminGoogleSecurityContainer: PropTypes.instanceOf(AdminGoogleSecurityContainer).isRequired,
 };
 
-const GoogleSecurityManagementWrapper = (props) => {
-  return createSubscribedElement(GoogleSecurityManagement, props, [AppContainer, AdminGeneralSecurityContainer, AdminGoogleSecurityContainer]);
-};
+const GoogleSecurityManagementWrapper = withUnstatedContainers(
+  GoogleSecurityManagement,
+  [AppContainer, AdminGeneralSecurityContainer, AdminGoogleSecurityContainer],
+);
 
 export default withTranslation()(GoogleSecurityManagementWrapper);

+ 2 - 4
src/client/js/components/Admin/Security/LdapAuthTest.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import loggerFactory from '@alias/logger';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -139,8 +139,6 @@ LdapAuthTest.propTypes = {
   onChangePassword: PropTypes.func.isRequired,
 };
 
-const LdapAuthTestWrapper = (props) => {
-  return createSubscribedElement(LdapAuthTest, props, [AppContainer, AdminLdapSecurityContainer]);
-};
+const LdapAuthTestWrapper = withUnstatedContainers(LdapAuthTest, [AppContainer, AdminLdapSecurityContainer]);
 
 export default withTranslation()(LdapAuthTestWrapper);

+ 2 - 4
src/client/js/components/Admin/Security/LdapAuthTestModal.jsx

@@ -8,7 +8,7 @@ import {
   ModalBody,
 } from 'reactstrap';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 
 import AppContainer from '../../../services/AppContainer';
 import AdminLdapSecurityContainer from '../../../services/AdminLdapSecurityContainer';
@@ -74,8 +74,6 @@ LdapAuthTestModal.propTypes = {
   onClose: PropTypes.func.isRequired,
 };
 
-const LdapAuthTestModalWrapper = (props) => {
-  return createSubscribedElement(LdapAuthTestModal, props, [AppContainer, AdminLdapSecurityContainer]);
-};
+const LdapAuthTestModalWrapper = withUnstatedContainers(LdapAuthTestModal, [AppContainer, AdminLdapSecurityContainer]);
 
 export default withTranslation()(LdapAuthTestModalWrapper);

+ 2 - 4
src/client/js/components/Admin/Security/LdapSecuritySetting.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -453,8 +453,6 @@ LdapSecuritySetting.propTypes = {
   adminLdapSecurityContainer: PropTypes.instanceOf(AdminLdapSecurityContainer).isRequired,
 };
 
-const LdapSecuritySettingWrapper = (props) => {
-  return createSubscribedElement(LdapSecuritySetting, props, [AppContainer, AdminGeneralSecurityContainer, AdminLdapSecurityContainer]);
-};
+const LdapSecuritySettingWrapper = withUnstatedContainers(LdapSecuritySetting, [AppContainer, AdminGeneralSecurityContainer, AdminLdapSecurityContainer]);
 
 export default withTranslation()(LdapSecuritySettingWrapper);

+ 2 - 4
src/client/js/components/Admin/Security/LocalSecuritySetting.jsx

@@ -3,7 +3,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -183,8 +183,6 @@ LocalSecuritySetting.propTypes = {
   adminLocalSecurityContainer: PropTypes.instanceOf(AdminLocalSecurityContainer).isRequired,
 };
 
-const LocalSecuritySettingWrapper = (props) => {
-  return createSubscribedElement(LocalSecuritySetting, props, [AppContainer, AdminGeneralSecurityContainer, AdminLocalSecurityContainer]);
-};
+const LocalSecuritySettingWrapper = withUnstatedContainers(LocalSecuritySetting, [AppContainer, AdminGeneralSecurityContainer, AdminLocalSecurityContainer]);
 
 export default withTranslation()(LocalSecuritySettingWrapper);

+ 2 - 4
src/client/js/components/Admin/Security/OidcSecuritySetting.jsx

@@ -3,7 +3,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -347,8 +347,6 @@ OidcSecurityManagement.propTypes = {
   adminOidcSecurityContainer: PropTypes.instanceOf(AdminOidcSecurityContainer).isRequired,
 };
 
-const OidcSecurityManagementWrapper = (props) => {
-  return createSubscribedElement(OidcSecurityManagement, props, [AppContainer, AdminGeneralSecurityContainer, AdminOidcSecurityContainer]);
-};
+const OidcSecurityManagementWrapper = withUnstatedContainers(OidcSecurityManagement, [AppContainer, AdminGeneralSecurityContainer, AdminOidcSecurityContainer]);
 
 export default withTranslation()(OidcSecurityManagementWrapper);

+ 2 - 4
src/client/js/components/Admin/Security/SamlSecuritySetting.jsx

@@ -3,7 +3,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -546,8 +546,6 @@ SamlSecurityManagement.propTypes = {
   adminSamlSecurityContainer: PropTypes.instanceOf(AdminSamlSecurityContainer).isRequired,
 };
 
-const SamlSecurityManagementWrapper = (props) => {
-  return createSubscribedElement(SamlSecurityManagement, props, [AppContainer, AdminGeneralSecurityContainer, AdminSamlSecurityContainer]);
-};
+const SamlSecurityManagementWrapper = withUnstatedContainers(SamlSecurityManagement, [AppContainer, AdminGeneralSecurityContainer, AdminSamlSecurityContainer]);
 
 export default withTranslation()(SamlSecurityManagementWrapper);

+ 2 - 4
src/client/js/components/Admin/Security/SecurityManagement.jsx

@@ -5,7 +5,7 @@ import {
   TabContent, TabPane, Nav, NavItem, NavLink,
 } from 'reactstrap';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 
 import AppContainer from '../../../services/AppContainer';
 import LdapSecuritySetting from './LdapSecuritySetting';
@@ -185,8 +185,6 @@ SecurityManagement.propTypes = {
   csrf: PropTypes.string,
 };
 
-const SecurityManagementWrapper = (props) => {
-  return createSubscribedElement(SecurityManagement, props, [AppContainer]);
-};
+const SecurityManagementWrapper = withUnstatedContainers(SecurityManagement, [AppContainer]);
 
 export default withTranslation()(SecurityManagementWrapper);

+ 2 - 4
src/client/js/components/Admin/Security/SecuritySetting.jsx

@@ -3,7 +3,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
 import AppContainer from '../../../services/AppContainer';
@@ -224,8 +224,6 @@ SecuritySetting.propTypes = {
   adminGeneralSecurityContainer: PropTypes.instanceOf(AdminGeneralSecurityContainer).isRequired,
 };
 
-const SecuritySettingWrapper = (props) => {
-  return createSubscribedElement(SecuritySetting, props, [AppContainer, AdminGeneralSecurityContainer]);
-};
+const SecuritySettingWrapper = withUnstatedContainers(SecuritySetting, [AppContainer, AdminGeneralSecurityContainer]);
 
 export default withTranslation()(SecuritySettingWrapper);

+ 5 - 6
src/client/js/components/Admin/Security/TwitterSecuritySetting.jsx

@@ -3,10 +3,9 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
-import AppContainer from '../../../services/AppContainer';
 import AdminGeneralSecurityContainer from '../../../services/AdminGeneralSecurityContainer';
 import AdminTwitterSecurityContainer from '../../../services/AdminTwitterSecurityContainer';
 
@@ -214,13 +213,13 @@ class TwitterSecurityManagement extends React.Component {
 
 TwitterSecurityManagement.propTypes = {
   t: PropTypes.func.isRequired, // i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   adminGeneralSecurityContainer: PropTypes.instanceOf(AdminGeneralSecurityContainer).isRequired,
   adminTwitterSecurityContainer: PropTypes.instanceOf(AdminTwitterSecurityContainer).isRequired,
 };
 
-const TwitterSecurityManagementWrapper = (props) => {
-  return createSubscribedElement(TwitterSecurityManagement, props, [AppContainer, AdminGeneralSecurityContainer, AdminTwitterSecurityContainer]);
-};
+const TwitterSecurityManagementWrapper = withUnstatedContainers(
+  TwitterSecurityManagement,
+  [AdminGeneralSecurityContainer, AdminTwitterSecurityContainer],
+);
 
 export default withTranslation()(TwitterSecurityManagementWrapper);

+ 2 - 4
src/client/js/components/Admin/UserGroup/UserGroupCreateForm.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
@@ -105,9 +105,7 @@ class UserGroupCreateForm extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const UserGroupCreateFormWrapper = (props) => {
-  return createSubscribedElement(UserGroupCreateForm, props, [AppContainer]);
-};
+const UserGroupCreateFormWrapper = withUnstatedContainers(UserGroupCreateForm, [AppContainer]);
 
 UserGroupCreateForm.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/UserGroup/UserGroupDeleteModal.jsx

@@ -5,7 +5,7 @@ import {
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 
 /**
@@ -195,9 +195,7 @@ class UserGroupDeleteModal extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const UserGroupDeleteModalWrapper = (props) => {
-  return createSubscribedElement(UserGroupDeleteModal, props, [AppContainer]);
-};
+const UserGroupDeleteModalWrapper = withUnstatedContainers(UserGroupDeleteModal, [AppContainer]);
 
 UserGroupDeleteModal.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/UserGroup/UserGroupPage.jsx

@@ -6,7 +6,7 @@ import UserGroupTable from './UserGroupTable';
 import UserGroupCreateForm from './UserGroupCreateForm';
 import UserGroupDeleteModal from './UserGroupDeleteModal';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
@@ -175,9 +175,7 @@ class UserGroupPage extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const UserGroupPageWrapper = (props) => {
-  return createSubscribedElement(UserGroupPage, props, [AppContainer]);
-};
+const UserGroupPageWrapper = withUnstatedContainers(UserGroupPage, [AppContainer]);
 
 UserGroupPage.propTypes = {
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,

+ 2 - 4
src/client/js/components/Admin/UserGroup/UserGroupTable.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import dateFnsFormat from 'date-fns/format';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 
 class UserGroupTable extends React.Component {
@@ -115,9 +115,7 @@ class UserGroupTable extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const UserGroupTableWrapper = (props) => {
-  return createSubscribedElement(UserGroupTable, props, [AppContainer]);
-};
+const UserGroupTableWrapper = withUnstatedContainers(UserGroupTable, [AppContainer]);
 
 
 UserGroupTable.propTypes = {

+ 2 - 4
src/client/js/components/Admin/UserGroupDetail/UserGroupDetailPage.jsx

@@ -6,7 +6,7 @@ import UserGroupEditForm from './UserGroupEditForm';
 import UserGroupUserTable from './UserGroupUserTable';
 import UserGroupUserModal from './UserGroupUserModal';
 import UserGroupPageList from './UserGroupPageList';
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 
 class UserGroupDetailPage extends React.Component {
@@ -44,8 +44,6 @@ UserGroupDetailPage.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const UserGroupDetailPageWrapper = (props) => {
-  return createSubscribedElement(UserGroupDetailPage, props, [AppContainer]);
-};
+const UserGroupDetailPageWrapper = withUnstatedContainers(UserGroupDetailPage, [AppContainer]);
 
 export default withTranslation()(UserGroupDetailPageWrapper);

+ 2 - 4
src/client/js/components/Admin/UserGroupDetail/UserGroupEditForm.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import dateFnsFormat from 'date-fns/format';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminUserGroupDetailContainer from '../../../services/AdminUserGroupDetailContainer';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -106,8 +106,6 @@ UserGroupEditForm.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const UserGroupEditFormWrapper = (props) => {
-  return createSubscribedElement(UserGroupEditForm, props, [AppContainer, AdminUserGroupDetailContainer]);
-};
+const UserGroupEditFormWrapper = withUnstatedContainers(UserGroupEditForm, [AppContainer, AdminUserGroupDetailContainer]);
 
 export default withTranslation()(UserGroupEditFormWrapper);

+ 2 - 4
src/client/js/components/Admin/UserGroupDetail/UserGroupPageList.jsx

@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
 
 import Page from '../../PageList/Page';
 import PaginationWrapper from '../../PaginationWrapper';
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminUserGroupDetailContainer from '../../../services/AdminUserGroupDetailContainer';
 import { toastError } from '../../../util/apiNotification';
@@ -80,8 +80,6 @@ UserGroupPageList.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const UserGroupPageListWrapper = (props) => {
-  return createSubscribedElement(UserGroupPageList, props, [AppContainer, AdminUserGroupDetailContainer]);
-};
+const UserGroupPageListWrapper = withUnstatedContainers(UserGroupPageList, [AppContainer, AdminUserGroupDetailContainer]);
 
 export default withTranslation()(UserGroupPageListWrapper);

+ 2 - 4
src/client/js/components/Admin/UserGroupDetail/UserGroupUserFormByInput.jsx

@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
 
 import { AsyncTypeahead } from 'react-bootstrap-typeahead';
 import { debounce } from 'throttle-debounce';
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminUserGroupDetailContainer from '../../../services/AdminUserGroupDetailContainer';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -162,8 +162,6 @@ UserGroupUserFormByInput.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const UserGroupUserFormByInputWrapper = (props) => {
-  return createSubscribedElement(UserGroupUserFormByInput, props, [AppContainer, AdminUserGroupDetailContainer]);
-};
+const UserGroupUserFormByInputWrapper = withUnstatedContainers(UserGroupUserFormByInput, [AppContainer, AdminUserGroupDetailContainer]);
 
 export default withTranslation()(UserGroupUserFormByInputWrapper);

+ 2 - 4
src/client/js/components/Admin/UserGroupDetail/UserGroupUserModal.jsx

@@ -6,7 +6,7 @@ import {
 } from 'reactstrap';
 
 import UserGroupUserFormByInput from './UserGroupUserFormByInput';
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminUserGroupDetailContainer from '../../../services/AdminUserGroupDetailContainer';
 import RadioButtonForSerchUserOption from './RadioButtonForSerchUserOption';
@@ -84,8 +84,6 @@ UserGroupUserModal.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const UserGroupUserModalWrapper = (props) => {
-  return createSubscribedElement(UserGroupUserModal, props, [AppContainer, AdminUserGroupDetailContainer]);
-};
+const UserGroupUserModalWrapper = withUnstatedContainers(UserGroupUserModal, [AppContainer, AdminUserGroupDetailContainer]);
 
 export default withTranslation()(UserGroupUserModalWrapper);

+ 2 - 4
src/client/js/components/Admin/UserGroupDetail/UserGroupUserTable.jsx

@@ -4,7 +4,7 @@ import { withTranslation } from 'react-i18next';
 import dateFnsFormat from 'date-fns/format';
 
 import UserPicture from '../../User/UserPicture';
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminUserGroupDetailContainer from '../../../services/AdminUserGroupDetailContainer';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -118,8 +118,6 @@ UserGroupUserTable.propTypes = {
 /**
  * Wrapper component for using unstated
  */
-const UserGroupUserTableWrapper = (props) => {
-  return createSubscribedElement(UserGroupUserTable, props, [AppContainer, AdminUserGroupDetailContainer]);
-};
+const UserGroupUserTableWrapper = withUnstatedContainers(UserGroupUserTable, [AppContainer, AdminUserGroupDetailContainer]);
 
 export default withTranslation()(UserGroupUserTableWrapper);

+ 2 - 4
src/client/js/components/Admin/UserManagement.jsx

@@ -5,7 +5,7 @@ import { withTranslation } from 'react-i18next';
 import PaginationWrapper from '../PaginationWrapper';
 
 
-import { createSubscribedElement } from '../UnstatedUtils';
+import { withUnstatedContainers } from '../UnstatedUtils';
 import { toastError } from '../../util/apiNotification';
 
 import AppContainer from '../../services/AppContainer';
@@ -223,8 +223,6 @@ UserManagement.propTypes = {
   adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
 };
 
-const UserManagementWrapper = (props) => {
-  return createSubscribedElement(UserManagement, props, [AppContainer, AdminUsersContainer]);
-};
+const UserManagementWrapper = withUnstatedContainers(UserManagement, [AppContainer, AdminUsersContainer]);
 
 export default withTranslation()(UserManagementWrapper);

+ 2 - 4
src/client/js/components/Admin/Users/ExternalAccountTable.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import dateFnsFormat from 'date-fns/format';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminExternalAccountsContainer from '../../../services/AdminExternalAccountsContainer';
 
@@ -122,9 +122,7 @@ ExternalAccountTable.propTypes = {
   adminExternalAccountsContainer: PropTypes.instanceOf(AdminExternalAccountsContainer).isRequired,
 };
 
-const ExternalAccountTableWrapper = (props) => {
-  return createSubscribedElement(ExternalAccountTable, props, [AppContainer, AdminExternalAccountsContainer]);
-};
+const ExternalAccountTableWrapper = withUnstatedContainers(ExternalAccountTable, [AppContainer, AdminExternalAccountsContainer]);
 
 
 export default withTranslation()(ExternalAccountTableWrapper);

+ 2 - 4
src/client/js/components/Admin/Users/GiveAdminButton.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import AdminUsersContainer from '../../../services/AdminUsersContainer';
@@ -42,9 +42,7 @@ class GiveAdminButton extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const GiveAdminButtonWrapper = (props) => {
-  return createSubscribedElement(GiveAdminButton, props, [AppContainer, AdminUsersContainer]);
-};
+const GiveAdminButtonWrapper = withUnstatedContainers(GiveAdminButton, [AppContainer, AdminUsersContainer]);
 
 GiveAdminButton.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Users/InviteUserControl.jsx

@@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminUsersContainer from '../../../services/AdminUsersContainer';
 import UserInviteModal from './UserInviteModal';
@@ -24,9 +24,7 @@ class InviteUserControl extends React.Component {
 
 }
 
-const InviteUserControlWrapper = (props) => {
-  return createSubscribedElement(InviteUserControl, props, [AppContainer, AdminUsersContainer]);
-};
+const InviteUserControlWrapper = withUnstatedContainers(InviteUserControl, [AppContainer, AdminUsersContainer]);
 
 InviteUserControl.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Users/PasswordResetModal.jsx

@@ -6,7 +6,7 @@ import {
 } from 'reactstrap';
 
 import { toastError } from '../../../util/apiNotification';
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 
 class PasswordResetModal extends React.Component {
@@ -101,9 +101,7 @@ class PasswordResetModal extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const PasswordResetModalWrapper = (props) => {
-  return createSubscribedElement(PasswordResetModal, props, [AppContainer]);
-};
+const PasswordResetModalWrapper = withUnstatedContainers(PasswordResetModal, [AppContainer]);
 
 PasswordResetModal.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Users/RemoveAdminButton.jsx

@@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import AdminUsersContainer from '../../../services/AdminUsersContainer';
@@ -66,9 +66,7 @@ class RemoveAdminButton extends React.Component {
 /**
 * Wrapper component for using unstated
 */
-const RemoveAdminButtonWrapper = (props) => {
-  return createSubscribedElement(RemoveAdminButton, props, [AppContainer, AdminUsersContainer]);
-};
+const RemoveAdminButtonWrapper = withUnstatedContainers(RemoveAdminButton, [AppContainer, AdminUsersContainer]);
 
 RemoveAdminButton.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Users/StatusActivateButton.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminUsersContainer from '../../../services/AdminUsersContainer';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -42,9 +42,7 @@ class StatusActivateButton extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const StatusActivateFormWrapper = (props) => {
-  return createSubscribedElement(StatusActivateButton, props, [AppContainer, AdminUsersContainer]);
-};
+const StatusActivateFormWrapper = withUnstatedContainers(StatusActivateButton, [AppContainer, AdminUsersContainer]);
 
 StatusActivateButton.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Users/StatusSuspendedButton.jsx

@@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 import AdminUsersContainer from '../../../services/AdminUsersContainer';
@@ -65,9 +65,7 @@ class StatusSuspendedButton extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const StatusSuspendedFormWrapper = (props) => {
-  return createSubscribedElement(StatusSuspendedButton, props, [AppContainer, AdminUsersContainer]);
-};
+const StatusSuspendedFormWrapper = withUnstatedContainers(StatusSuspendedButton, [AppContainer, AdminUsersContainer]);
 
 StatusSuspendedButton.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Users/UserInviteModal.jsx

@@ -11,7 +11,7 @@ import {
 
 import { toastSuccess, toastError } from '../../../util/apiNotification';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminUsersContainer from '../../../services/AdminUsersContainer';
 
@@ -218,9 +218,7 @@ class UserInviteModal extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const UserInviteModalWrapper = (props) => {
-  return createSubscribedElement(UserInviteModal, props, [AppContainer, AdminUsersContainer]);
-};
+const UserInviteModalWrapper = withUnstatedContainers(UserInviteModal, [AppContainer, AdminUsersContainer]);
 
 
 UserInviteModal.propTypes = {

+ 2 - 4
src/client/js/components/Admin/Users/UserMenu.jsx

@@ -8,7 +8,7 @@ import RemoveUserButton from './UserRemoveButton';
 import RemoveAdminButton from './RemoveAdminButton';
 import GiveAdminButton from './GiveAdminButton';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminUsersContainer from '../../../services/AdminUsersContainer';
 
@@ -96,9 +96,7 @@ class UserMenu extends React.Component {
 
 }
 
-const UserMenuWrapper = (props) => {
-  return createSubscribedElement(UserMenu, props, [AppContainer, AdminUsersContainer]);
-};
+const UserMenuWrapper = withUnstatedContainers(UserMenu, [AppContainer, AdminUsersContainer]);
 
 UserMenu.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Users/UserRemoveButton.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminUsersContainer from '../../../services/AdminUsersContainer';
 import { toastSuccess, toastError } from '../../../util/apiNotification';
@@ -43,9 +43,7 @@ class UserRemoveButton extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const UserRemoveButtonWrapper = (props) => {
-  return createSubscribedElement(UserRemoveButton, props, [AppContainer, AdminUsersContainer]);
-};
+const UserRemoveButtonWrapper = withUnstatedContainers(UserRemoveButton, [AppContainer, AdminUsersContainer]);
 
 UserRemoveButton.propTypes = {
   t: PropTypes.func.isRequired, // i18next

+ 2 - 4
src/client/js/components/Admin/Users/UserTable.jsx

@@ -6,7 +6,7 @@ import dateFnsFormat from 'date-fns/format';
 import UserPicture from '../../User/UserPicture';
 import UserMenu from './UserMenu';
 
-import { createSubscribedElement } from '../../UnstatedUtils';
+import { withUnstatedContainers } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AdminUsersContainer from '../../../services/AdminUsersContainer';
 import SortIcons from './SortIcons';
@@ -221,8 +221,6 @@ UserTable.propTypes = {
 
 };
 
-const UserTableWrapper = (props) => {
-  return createSubscribedElement(UserTable, props, [AppContainer, AdminUsersContainer]);
-};
+const UserTableWrapper = withUnstatedContainers(UserTable, [AppContainer, AdminUsersContainer]);
 
 export default withTranslation()(UserTableWrapper);

+ 2 - 4
src/client/js/components/CreateTemplateModal.jsx

@@ -6,7 +6,7 @@ import { Modal, ModalHeader, ModalBody } from 'reactstrap';
 import { withTranslation } from 'react-i18next';
 import { pathUtils } from 'growi-commons';
 import urljoin from 'url-join';
-import { createSubscribedElement } from './UnstatedUtils';
+import { withUnstatedContainers } from './UnstatedUtils';
 
 import PageContainer from '../services/PageContainer';
 
@@ -70,9 +70,7 @@ const CreateTemplateModal = (props) => {
 /**
  * Wrapper component for using unstated
  */
-const CreateTemplateModalWrapper = (props) => {
-  return createSubscribedElement(CreateTemplateModal, props, [PageContainer]);
-};
+const CreateTemplateModalWrapper = withUnstatedContainers(CreateTemplateModal, [PageContainer]);
 
 
 CreateTemplateModal.propTypes = {

+ 2 - 4
src/client/js/components/EmptyTrashModal.jsx

@@ -6,7 +6,7 @@ import {
 } from 'reactstrap';
 
 import { withTranslation } from 'react-i18next';
-import { createSubscribedElement } from './UnstatedUtils';
+import { withUnstatedContainers } from './UnstatedUtils';
 
 import AppContainer from '../services/AppContainer';
 import ApiErrorMessage from './PageManagement/ApiErrorMessage';
@@ -56,9 +56,7 @@ const EmptyTrashModal = (props) => {
 /**
  * Wrapper component for using unstated
  */
-const EmptyTrashModalWrapper = (props) => {
-  return createSubscribedElement(EmptyTrashModal, props, [AppContainer]);
-};
+const EmptyTrashModalWrapper = withUnstatedContainers(EmptyTrashModal, [AppContainer]);
 
 
 EmptyTrashModal.propTypes = {

+ 2 - 4
src/client/js/components/LikeButton.jsx

@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 
 import { toastError } from '../util/apiNotification';
-import { createSubscribedElement } from './UnstatedUtils';
+import { withUnstatedContainers } from './UnstatedUtils';
 import AppContainer from '../services/AppContainer';
 
 class LikeButton extends React.Component {
@@ -56,9 +56,7 @@ class LikeButton extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const LikeButtonWrapper = (props) => {
-  return createSubscribedElement(LikeButton, props, [AppContainer]);
-};
+const LikeButtonWrapper = withUnstatedContainers(LikeButton, [AppContainer]);
 
 LikeButton.propTypes = {
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,

Некоторые файлы не были показаны из-за большого количества измененных файлов