2
0
Эх сурвалжийг харах

Merge pull request #6106 from weseek/feat/gw7826-add-button-to-switch-content-width

feat: gw7826 add button to switch content width
Mudana-Grune 3 жил өмнө
parent
commit
b12340801e

+ 1 - 0
packages/app/resource/locales/en_US/translation.json

@@ -161,6 +161,7 @@
   "No bookmarks yet": "No bookmarks yet",
   "add_bookmark": "Add to Bookmarks",
   "remove_bookmark": "Remove from Bookmarks",
+  "wide_view": "Wide View",
   "Recent Created": "Recent Created",
   "Recent Changes": "Recent Changes",
   "Page Tree": "Page Tree",

+ 1 - 0
packages/app/resource/locales/ja_JP/translation.json

@@ -163,6 +163,7 @@
   "No bookmarks yet": "No bookmarks yet",
   "add_bookmark": "ブックマークに追加",
   "remove_bookmark": "ブックマークから削除",
+  "wide_view": "ワイドビュー",
   "Recent Created": "最新の作成",
   "Recent Changes": "最新の変更",
   "Page Tree": "ページツリー",

+ 1 - 0
packages/app/resource/locales/zh_CN/translation.json

@@ -169,6 +169,7 @@
   "No bookmarks yet": "暂无书签",
   "add_bookmark": "添加到书签",
   "remove_bookmark": "从书签中删除",
+  "wide_view": "视野开阔",
 	"Recent Created": "最新创建",
   "Recent Changes": "最新修改",
   "Page Tree": "页面树",

+ 9 - 0
packages/app/src/client/services/page-operation.ts

@@ -36,6 +36,15 @@ export const toggleBookmark = async(pageId: string, currentValue?: boolean): Pro
   }
 };
 
+export const toggleContentWidth = async(pageId: string, currentValue?: boolean): Promise<void> => {
+  try {
+    await apiv3Put('/page/content-width', { pageId, bool: !currentValue });
+  }
+  catch (err) {
+    toastError(err);
+  }
+};
+
 export const bookmark = async(pageId: string): Promise<void> => {
   try {
     await apiv3Put('/bookmarks', { pageId, bool: true });

+ 40 - 2
packages/app/src/components/Common/Dropdown/PageItemControl.tsx

@@ -37,10 +37,12 @@ type CommonProps = {
   onClickDuplicateMenuItem?: (pageId: string) => Promise<void> | void,
   onClickDeleteMenuItem?: (pageId: string, pageInfo: IPageInfoAll | undefined) => Promise<void> | void,
   onClickRevertMenuItem?: (pageId: string) => Promise<void> | void,
+  onClickSwitchContentWidthMenuItem?: (pageId: string, isContainerFluid?: boolean) => Promise<void>,
 
   additionalMenuItemRenderer?: React.FunctionComponent<AdditionalMenuItemsRendererProps>,
   isInstantRename?: boolean,
   alignRight?: boolean,
+  isContainerFluid?: boolean
 }
 
 
@@ -55,10 +57,16 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
   const {
     pageId, isLoading,
     pageInfo, isEnableActions, forceHideMenuItems,
-    onClickBookmarkMenuItem, onClickRenameMenuItem, onClickDuplicateMenuItem, onClickDeleteMenuItem, onClickRevertMenuItem,
+    onClickBookmarkMenuItem, onClickRenameMenuItem, onClickDuplicateMenuItem, onClickDeleteMenuItem, onClickRevertMenuItem, onClickSwitchContentWidthMenuItem,
     additionalMenuItemRenderer: AdditionalMenuItems, isInstantRename, alignRight,
   } = props;
 
+  const switchContentWidthHandler = useCallback(async() => {
+    if (!isIPageInfoForOperation(pageInfo) || onClickSwitchContentWidthMenuItem == null) {
+      return;
+    }
+    await onClickSwitchContentWidthMenuItem(pageId, pageInfo.isContainerFluid);
+  }, [onClickSwitchContentWidthMenuItem, pageId, pageInfo]);
 
   // eslint-disable-next-line react-hooks/rules-of-hooks
   const bookmarkItemClickedHandler = useCallback(async() => {
@@ -132,6 +140,17 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
           </DropdownItem>
         ) }
 
+        { isEnableActions && !pageInfo.isEmpty && isIPageInfoForOperation(pageInfo) && (
+          <DropdownItem
+            onClick={switchContentWidthHandler}
+            className="grw-page-control-dropdown-item"
+          >
+            <i className={`fa fa-fw ${!pageInfo.isContainerFluid ? 'fa-toggle-off' : 'fa-toggle-on'} grw-page-control-dropdown-icon`}>
+            </i>
+            { t('wide_view') }
+          </DropdownItem>
+        ) }
+
         {/* Bookmark */}
         { !forceHideMenuItems?.includes(MenuItemType.BOOKMARK) && isEnableActions && !pageInfo.isEmpty && isIPageInfoForOperation(pageInfo) && (
           <DropdownItem
@@ -224,7 +243,7 @@ export const PageItemControlSubstance = (props: PageItemControlSubstanceProps):
   const {
     pageId, pageInfo: presetPageInfo, fetchOnInit,
     children,
-    onClickBookmarkMenuItem, onClickRenameMenuItem, onClickDuplicateMenuItem, onClickDeleteMenuItem,
+    onClickBookmarkMenuItem, onClickRenameMenuItem, onClickDuplicateMenuItem, onClickDeleteMenuItem, onClickSwitchContentWidthMenuItem,
   } = props;
 
   const [isOpen, setIsOpen] = useState(false);
@@ -240,8 +259,26 @@ export const PageItemControlSubstance = (props: PageItemControlSubstanceProps):
     if (!isIPageInfoForOperation(presetPageInfo) && isOpen) {
       setShouldFetch(true);
     }
+    if (presetPageInfo?.isContainerFluid) {
+      if (!$('body').hasClass('growi-layout-fluid')) {
+        $('body').addClass('growi-layout-fluid');
+      }
+    }
+    else if ($('body').hasClass('growi-layout-fluid')) {
+      $('body').removeClass('growi-layout-fluid');
+    }
   }, [isOpen, presetPageInfo, shouldFetch]);
 
+  const switchContentWidthMenuItemHandler = useCallback(async(_pageId: string, _isContainerFluid: boolean) => {
+    if (onClickSwitchContentWidthMenuItem != null) {
+      await onClickSwitchContentWidthMenuItem(_pageId, _isContainerFluid);
+    }
+
+    if (shouldFetch) {
+      mutatePageInfo();
+    }
+  }, [mutatePageInfo, onClickSwitchContentWidthMenuItem, shouldFetch]);
+
   // mutate after handle event
   const bookmarkMenuItemClickHandler = useCallback(async(_pageId: string, _newValue: boolean) => {
     if (onClickBookmarkMenuItem != null) {
@@ -292,6 +329,7 @@ export const PageItemControlSubstance = (props: PageItemControlSubstanceProps):
         onClickRenameMenuItem={renameMenuItemClickHandler}
         onClickDuplicateMenuItem={duplicateMenuItemClickHandler}
         onClickDeleteMenuItem={deleteMenuItemClickHandler}
+        onClickSwitchContentWidthMenuItem={switchContentWidthMenuItemHandler}
       />
     </Dropdown>
   );

+ 16 - 3
packages/app/src/components/Navbar/SubNavButtons.tsx

@@ -1,6 +1,8 @@
 import React, { useCallback } from 'react';
 
-import { toggleBookmark, toggleLike, toggleSubscribe } from '~/client/services/page-operation';
+import {
+  toggleBookmark, toggleLike, toggleSubscribe, toggleContentWidth,
+} from '~/client/services/page-operation';
 import {
   IPageInfoAll, IPageToDeleteWithMeta, IPageToRenameWithMeta, isIPageInfoForEntity, isIPageInfoForOperation,
 } from '~/interfaces/page';
@@ -140,13 +142,23 @@ const SubNavButtonsSubstance = (props: SubNavButtonsSubstanceProps): JSX.Element
     onClickDeleteMenuItem(pageToDelete);
   }, [onClickDeleteMenuItem, pageId, pageInfo, path, revisionId]);
 
+  const switchContentWidthClickHandler = useCallback(async() => {
+    if (isGuestUser == null || isGuestUser) {
+      return;
+    }
+    if (!isIPageInfoForOperation(pageInfo)) {
+      return;
+    }
+    await toggleContentWidth(pageId, pageInfo.isContainerFluid);
+    mutatePageInfo();
+  }, [isGuestUser, mutatePageInfo, pageId, pageInfo]);
+
   if (!isIPageInfoForOperation(pageInfo)) {
     return <></>;
   }
 
-
   const {
-    sumOfLikers, sumOfSeenUsers, isLiked, bookmarkCount, isBookmarked,
+    sumOfLikers, sumOfSeenUsers, isLiked, bookmarkCount, isBookmarked, isContainerFluid,
   } = pageInfo;
 
   const forceHideMenuItemsWithBookmark = forceHideMenuItems ?? [];
@@ -192,6 +204,7 @@ const SubNavButtonsSubstance = (props: SubNavButtonsSubstanceProps): JSX.Element
           onClickRenameMenuItem={renameMenuItemClickHandler}
           onClickDuplicateMenuItem={duplicateMenuItemClickHandler}
           onClickDeleteMenuItem={deleteMenuItemClickHandler}
+          onClickSwitchContentWidthMenuItem={switchContentWidthClickHandler}
         />
       )}
     </div>

+ 6 - 1
packages/app/src/components/PageList/PageListItemL.tsx

@@ -14,7 +14,7 @@ import urljoin from 'url-join';
 
 
 import { ISelectable } from '~/client/interfaces/selectable-all';
-import { bookmark, unbookmark } from '~/client/services/page-operation';
+import { bookmark, unbookmark, toggleContentWidth } from '~/client/services/page-operation';
 import {
   IPageInfoAll, IPageInfoForEntity, IPageInfoForListing, IPageWithMeta, isIPageInfoForListing, isIPageInfoForEntity,
 } from '~/interfaces/page';
@@ -124,6 +124,10 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
     await bookmarkOperation(_pageId);
   };
 
+  const switchContentWidthMenuItemClickHandler = async(_pageId: string, _isContainerFluid: boolean): Promise<void> => {
+    await toggleContentWidth(_pageId, _isContainerFluid);
+  };
+
   const duplicateMenuItemClickHandler = useCallback(() => {
     const page = {
       pageId: pageData._id,
@@ -235,6 +239,7 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
                   onClickDuplicateMenuItem={duplicateMenuItemClickHandler}
                   onClickDeleteMenuItem={deleteMenuItemClickHandler}
                   onClickRevertMenuItem={revertMenuItemClickHandler}
+                  onClickSwitchContentWidthMenuItem={switchContentWidthMenuItemClickHandler}
                 />
               </div>
             </div>

+ 1 - 1
packages/app/src/interfaces/page.ts

@@ -53,6 +53,7 @@ export type IPageInfo = {
   isDeletable: boolean,
   isAbleToDeleteCompletely: boolean,
   isRevertible: boolean,
+  isContainerFluid?: boolean,
 }
 
 export type IPageInfoForEntity = IPageInfo & {
@@ -67,7 +68,6 @@ export type IPageInfoForOperation = IPageInfoForEntity & {
   isBookmarked?: boolean,
   isLiked?: boolean,
   subscriptionStatus?: SubscriptionStatusType,
-  isContainerFluid?: boolean,
 }
 
 export type IPageInfoForListing = IPageInfoForEntity & HasRevisionShortbody;

+ 17 - 0
packages/app/src/server/routes/apiv3/page.js

@@ -215,6 +215,10 @@ module.exports = (crowi) => {
     subscribeStatus: [
       query('pageId').isString(),
     ],
+    contentWidth: [
+      body('pageId').isString(),
+      body('bool').isBoolean(),
+    ],
   };
 
   /**
@@ -785,5 +789,18 @@ module.exports = (crowi) => {
     }
   });
 
+  router.put('/content-width', accessTokenParser, loginRequiredStrictly, csrf, validator.contentWidth, apiV3FormValidator, async(req, res) => {
+    const { pageId, bool: isContainerFluid } = req.body;
+
+    try {
+      const page = await Page.updateOne({ _id: pageId }, { $set: { isContainerFluid } });
+      return res.apiv3({ page });
+    }
+    catch (err) {
+      logger.error('update-content-width-failed', err);
+      return res.apiv3Err(err, 500);
+    }
+  });
+
   return router;
 };