Sfoglia il codice sorgente

Merge branch 'dev/7.0.x' into support/apply-colors-v7

Yuki Takei 2 anni fa
parent
commit
37d22f8c6a

+ 36 - 1
CHANGELOG.md

@@ -1,6 +1,6 @@
 # Changelog
 
-## [Unreleased](https://github.com/weseek/growi/compare/v6.1.12...HEAD)
+## [Unreleased](https://github.com/weseek/growi/compare/v6.2.0...HEAD)
 
 *Please do not manually update this file. We've automated the process.*
 
@@ -23,6 +23,41 @@
 - fix: Do not work img tag if use style property (#7988) @jam411
 - fix: "Searching..." label appearing unnecessarily (#7990) @yuki-takei
 
+## [v6.2.0](https://github.com/weseek/growi/compare/v6.1.12...v6.2.0) - 2023-09-14
+
+### 💎 Features
+
+- feat: Presentation preview and support Marp  (#8029) @reiji-h
+
+### 🚀 Improvement
+
+- imprv: Able to customize users homepage deletion (#7921) @yuki-takei
+- imprv: Search behavior (#8069) @yuki-takei
+- imprv: Add CSP style-src for Safari and Content-Disposition of attachment (#8049) @ykanematsu
+- imprv: Correct update message (#8040) @reiji-h
+- imprv: Add installed date to questionnaire answer (#7971) @TatsuyaIse
+- imprv: Show modal when you delete plugin (#7875) @soumaeda
+- imprv: i18n resetting password mail body (#8058) @meiri-k
+- imprv: Create Japanese ejs files (#7957) @meiri-k
+- imprv: Clean up old toastr (#7949) @jam411
+- imprv: Persist the installed date in the Config collection (#7936) @TatsuyaIse
+
+### 🐛 Bug Fixes
+
+- fix: Pages can be created under a non-existent user page (#7974) @miya
+- fix: Pages can be created under a non-existent user page (During attachment upload) (#8001) @miya
+- fix: Type safe implementation for objects imported from ElasticsearchClient (#7862) @miya
+- fix: Consider an empty page when renaming and duplicating (#7979) @yuki-takei
+- fix: Remove redundant toastSuccess for pasted attachments (#8044) @jam411
+- fix: Fixing swagger for tag update api (#8010) @miya
+- fix: Modification of links in the docs (#8004) @miya
+
+### 🧰 Maintenance
+
+- support: Omit core-js v2 (#7944) @yuki-takei
+- support: Improve build settings (#7919) @yuki-takei
+- support: Url to join to the slack team (#8073) @WNomunomu
+
 ## [v6.1.12](https://github.com/weseek/growi/compare/v6.1.11...v6.1.12) - 2023-08-14
 
 ### 🐛 Bug Fixes

+ 4 - 0
apps/app/src/components/PageEditor/PageEditor.tsx

@@ -19,6 +19,7 @@ import { toastError, toastSuccess } from '~/client/util/toastr';
 import { OptionsToSave } from '~/interfaces/page-operation';
 import { SocketEventName } from '~/interfaces/websocket';
 import {
+  useDefaultIndentSize,
   useCurrentPathname, useIsEnabledAttachTitleHeader,
   useIsEditable, useIsUploadableFile, useIsUploadableImage, useIsIndentSizeForced,
 } from '~/stores/context';
@@ -103,6 +104,7 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
   const { data: isSlackEnabled } = useIsSlackEnabled();
   const { data: isIndentSizeForced } = useIsIndentSizeForced();
   const { data: currentIndentSize, mutate: mutateCurrentIndentSize } = useCurrentIndentSize();
+  const { data: defaultIndentSize } = useDefaultIndentSize();
   const { data: isUploadableFile } = useIsUploadableFile();
   const { data: isUploadableImage } = useIsUploadableImage();
   const { data: conflictDiffModalStatus, close: closeConflictDiffModal } = useConflictDiffModal();
@@ -518,6 +520,7 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
   useEffect(() => {
     // do nothing if the indent size fixed
     if (isIndentSizeForced == null || isIndentSizeForced) {
+      mutateCurrentIndentSize(undefined);
       return;
     }
 
@@ -572,6 +575,7 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
         <CodeMirrorEditorMain
           onChange={markdownChangedHandler}
           onSave={saveWithShortcut}
+          indentSize={currentIndentSize ?? defaultIndentSize}
         />
       </div>
       <div className="page-editor-preview-container flex-expand-vert d-none d-lg-flex">

+ 46 - 46
apps/app/src/components/Sidebar/PersonalDropdown.tsx

@@ -49,59 +49,59 @@ export const PersonalDropdown = (): JSX.Element => {
           data-testid="personal-dropdown-button"
           aria-expanded="false"
         >
-          <UserPicture user={currentUser} noLink noTooltip /><span className="ms-1 d-none d-lg-inline-block">&nbsp;{currentUser.name}</span>
+          <UserPicture user={currentUser} noLink noTooltip />
         </button>
 
         {/* Menu */}
-        <div className="dropdown-menu" data-testid="personal-dropdown-menu">
-
-          <div className="px-4 pt-3 pb-2 text-center">
+        <ul className="dropdown-menu" data-testid="personal-dropdown-menu">
+          <li className="px-4 pt-3 pb-2">
             <UserPicture user={currentUser} size="lg" noLink noTooltip />
-
-            <h5 className="mt-2">
-              {currentUser.name}
-            </h5>
-
-            <div className="my-2">
-              <i className="icon-user icon-fw"></i>{currentUser.username}<br />
-              <i className="icon-envelope icon-fw"></i><span className="grw-email-sm">{currentUser.email}</span>
+            <h5>{currentUser.name}</h5>
+            <div className="d-flex align-items-center">
+              <i className="icon-user icon-fw"></i>{currentUser.username}
             </div>
-
-            <div className="btn-group mt-2" role="group">
-              <Link
-                href={pagePathUtils.userHomepagePath(currentUser)}
-                className="btn btn-sm btn-outline-secondary col"
-                data-testid="grw-personal-dropdown-menu-user-home"
-              >
-                <i className="icon-fw icon-home"></i>{t('personal_dropdown.home')}
-              </Link>
-              <Link
-                href="/me"
-                className="btn btn-sm btn-outline-secondary col"
-                data-testid="grw-personal-dropdown-menu-user-settings"
-              >
-                <i className="icon-fw icon-wrench"></i>{t('personal_dropdown.settings')}
-              </Link>
+            <div className="d-flex align-items-center">
+              <i className="icon-envelope icon-fw"></i><span className="grw-email-sm">{currentUser.email}</span>
             </div>
-          </div>
-
-          <div className="dropdown-divider"></div>
-
-          <button
-            data-testid="grw-proactive-questionnaire-modal-toggle-btn"
-            type="button"
-            className="dropdown-item"
-            onClick={() => setQuestionnaireModalOpen(true)}
-          >
-            <i className="icon-fw icon-pencil"></i>{t('personal_dropdown.feedback')}
-          </button>
-
-          <div className="dropdown-divider"></div>
+          </li>
+
+          <li className="dropdown-divider"></li>
+
+          <li>
+            <Link
+              href={pagePathUtils.userHomepagePath(currentUser)}
+              className="dropdown-item"
+              data-testid="grw-personal-dropdown-menu-user-home"
+            >
+              <i className="icon-fw icon-home"></i>{t('personal_dropdown.home')}
+            </Link>
+          </li>
+          <li>
+            <Link
+              href="/me"
+              className="dropdown-item"
+              data-testid="grw-personal-dropdown-menu-user-settings"
+            >
+              <i className="icon-fw icon-wrench"></i>{t('personal_dropdown.settings')}
+            </Link>
+          </li>
+          <li>
+            <button
+              data-testid="grw-proactive-questionnaire-modal-toggle-btn"
+              type="button"
+              className="dropdown-item"
+              onClick={() => setQuestionnaireModalOpen(true)}
+            >
+              <i className="icon-fw icon-pencil"></i>{t('personal_dropdown.feedback')}
+            </button>
+          </li>
+          <li>
+            <button type="button" className="dropdown-item" onClick={logoutHandler}>
+              <i className="icon-fw icon-power"></i>{t('Sign out')}
+            </button>
+          </li>
+        </ul>
 
-          <button type="button" className="dropdown-item" onClick={logoutHandler}>
-            <i className="icon-fw icon-power"></i>{t('Sign out')}
-          </button>
-        </div>
       </div>
 
       <ProactiveQuestionnaireModal isOpen={isQuestionnaireModalOpen} onClose={() => setQuestionnaireModalOpen(false)} />

+ 6 - 12
apps/app/src/components/Sidebar/SidebarNav.tsx

@@ -115,26 +115,20 @@ export const SidebarNav: FC<Props> = (props: Props) => {
       </div>
 
       <div className="grw-sidebar-nav-primary-container" data-vrt-blackout-sidebar-nav>
-        {/* eslint-disable max-len */}
         <PrimaryItem contents={SidebarContentsType.TREE} label="Page Tree" iconName="format_list_bulleted" onItemSelected={onItemSelected} />
         <PrimaryItem contents={SidebarContentsType.CUSTOM} label="Custom Sidebar" iconName="code" onItemSelected={onItemSelected} />
         <PrimaryItem contents={SidebarContentsType.RECENT} label="Recent Changes" iconName="update" onItemSelected={onItemSelected} />
-        {/* <PrimaryItem id="tag" label="Tags" iconName="icon-tag" /> */}
-        {/* <PrimaryItem id="favorite" label="Favorite" iconName="fa fa-bookmark-o" /> */}
-        <PrimaryItem contents={SidebarContentsType.TAG} label="Tags" iconName="local_offer" onItemSelected={onItemSelected} />
-        {/* <PrimaryItem id="favorite" label="Favorite" iconName="icon-star" /> */}
-        {/* eslint-enable max-len */}
         <PrimaryItem contents={SidebarContentsType.BOOKMARKS} label="Bookmarks" iconName="bookmark" onItemSelected={onItemSelected} />
+        <PrimaryItem contents={SidebarContentsType.TAG} label="Tags" iconName="local_offer" onItemSelected={onItemSelected} />
+        <InAppNotificationDropdown />
       </div>
       <div className="grw-sidebar-nav-secondary-container">
-        <AppearanceModeDropdown isAuthenticated={isAuthenticated} />
+        {/* TODO: This setting will be consolidated in "Settings" on My Page, so delete it from here. */}
+        {/* <AppearanceModeDropdown isAuthenticated={isAuthenticated} /> */}
         <PersonalDropdown />
-        <InAppNotificationDropdown />
-
-        {isAdmin && <SecondaryItem label="Admin" iconName="settings" href="/admin" />}
-        {/* <SecondaryItem label="Draft" iconName="file_copy" href="/me/drafts" /> */}
         <SecondaryItem label="Help" iconName="help" href={growiCloudUri != null ? 'https://growi.cloud/help/' : 'https://docs.growi.org'} isBlank />
-        <SecondaryItem label="Trash" iconName="delete" href="/trash" />
+        {isAdmin && <SecondaryItem label="Admin" iconName="settings" href="/admin" />}
+        <SecondaryItem label="Trash" href="/trash" iconName="delete" />
       </div>
     </div>
   );

+ 28 - 2
packages/editor/src/components/CodeMirrorEditor/CodeMirrorEditor.tsx

@@ -1,7 +1,10 @@
 import {
-  forwardRef, useMemo, useRef,
+  forwardRef, useMemo, useRef, useEffect,
 } from 'react';
 
+import { defaultKeymap } from '@codemirror/commands';
+import { indentUnit } from '@codemirror/language';
+import { keymap } from '@codemirror/view';
 import type { ReactCodeMirrorProps } from '@uiw/react-codemirror';
 
 import { GlobalCodeMirrorEditorKey } from '../../consts';
@@ -21,12 +24,14 @@ const CodeMirrorEditorContainer = forwardRef<HTMLDivElement>((props, ref) => {
 type Props = {
   editorKey: string | GlobalCodeMirrorEditorKey,
   onChange?: (value: string) => void,
+  indentSize?: number,
 }
 
 export const CodeMirrorEditor = (props: Props): JSX.Element => {
   const {
     editorKey,
     onChange,
+    indentSize,
   } = props;
 
   const containerRef = useRef(null);
@@ -36,7 +41,28 @@ export const CodeMirrorEditor = (props: Props): JSX.Element => {
       onChange,
     };
   }, [onChange]);
-  useCodeMirrorEditorIsolated(editorKey, containerRef.current, cmProps);
+  const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(editorKey, containerRef.current, cmProps);
+
+  useEffect(() => {
+    const extension = keymap.of([
+      ...defaultKeymap,
+    ]);
+
+    const cleanupFunction = codeMirrorEditor?.appendExtensions?.(extension);
+    return cleanupFunction;
+
+  }, [codeMirrorEditor]);
+
+  useEffect(() => {
+    if (indentSize == null) {
+      return;
+    }
+    const extension = indentUnit.of(' '.repeat(indentSize));
+
+    const cleanupFunction = codeMirrorEditor?.appendExtensions?.(extension);
+    return cleanupFunction;
+
+  }, [codeMirrorEditor, indentSize]);
 
   return (
     <div className="flex-expand-vert">

+ 3 - 1
packages/editor/src/components/CodeMirrorEditorMain.tsx

@@ -17,11 +17,12 @@ const additionalExtensions: Extension[] = [
 type Props = {
   onChange?: (value: string) => void,
   onSave?: () => void,
+  indentSize?: number,
 }
 
 export const CodeMirrorEditorMain = (props: Props): JSX.Element => {
   const {
-    onSave, onChange,
+    onSave, onChange, indentSize,
   } = props;
 
   const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(GlobalCodeMirrorEditorKey.MAIN);
@@ -60,6 +61,7 @@ export const CodeMirrorEditorMain = (props: Props): JSX.Element => {
     <CodeMirrorEditor
       editorKey={GlobalCodeMirrorEditorKey.MAIN}
       onChange={onChange}
+      indentSize={indentSize}
     />
   );
 };

+ 12 - 2
packages/editor/src/services/codemirror-editor/use-codemirror-editor/use-codemirror-editor.ts

@@ -1,9 +1,10 @@
 import { useMemo } from 'react';
 
+import { indentWithTab } from '@codemirror/commands';
 import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
 import { languages } from '@codemirror/language-data';
 import { EditorState, type Extension } from '@codemirror/state';
-import { EditorView } from '@codemirror/view';
+import { keymap, EditorView } from '@codemirror/view';
 import { useCodeMirror, type UseCodeMirror } from '@uiw/react-codemirror';
 import deepmerge from 'ts-deepmerge';
 
@@ -28,6 +29,7 @@ export type UseCodeMirrorEditor = {
 
 const defaultExtensions: Extension[] = [
   markdown({ base: markdownLanguage, codeLanguages: languages }),
+  keymap.of([indentWithTab]),
 ];
 
 export const useCodeMirrorEditor = (props?: UseCodeMirror): UseCodeMirrorEditor => {
@@ -35,7 +37,15 @@ export const useCodeMirrorEditor = (props?: UseCodeMirror): UseCodeMirrorEditor
   const mergedProps = useMemo<UseCodeMirror>(() => {
     return deepmerge(
       props ?? {},
-      { extensions: defaultExtensions },
+      {
+        extensions: defaultExtensions,
+        // Reset settings of react-codemirror.
+        // The extension defined first will be used, so it must be disabled here.
+        indentWithTab: false,
+        basicSetup: {
+          defaultKeymap: false,
+        },
+      },
     );
   }, [props]);
 

+ 3 - 3
packages/presentation/package.json

@@ -31,15 +31,15 @@
     "@marp-team/marp-core": "^3.6.0",
     "@types/reveal.js": "^4.4.1",
     "eslint-plugin-regex": "^1.8.0",
-    "reveal.js": "^4.4.0",
+    "hast-util-sanitize": "^4.1.0",
+    "hast-util-select": "^5.0.5",
     "mdast-util-frontmatter": "^1.0.0",
     "mdast-util-gfm": "^2.0.1",
     "mdast-util-to-markdown": "^1.3.0",
-    "hast-util-sanitize": "^4.1.0",
-    "hast-util-select": "^5.0.5",
     "react-markdown": "^8.0.7",
     "remark-frontmatter": "^4.0.1",
     "remark-stringify": "^10.0.0",
+    "reveal.js": "^4.4.0",
     "unified": "^10.1.2",
     "unist-util-find-after": "^4.0.0",
     "unist-util-visit": "^4.0.0"