Преглед изворни кода

Display username hint on typing @ in comment editor

https://youtrack.weseek.co.jp/issue/GW-7773
- Create CommentMentionHelper
- Create function to show username hint when typing `@`
- Add isComment props in CommentEditor
- Initialize CommentMentionHelper when comment editor opened
- Show username hint from code mirror editor when typing `@`
I Komang Mudana пре 3 година
родитељ
комит
880851e2dd

+ 8 - 7
packages/app/src/components/PageComment/CommentEditor.jsx

@@ -1,27 +1,26 @@
 import React from 'react';
 import React from 'react';
-import PropTypes from 'prop-types';
 
 
+import { UserPicture } from '@growi/ui';
+import PropTypes from 'prop-types';
 import {
 import {
   Button,
   Button,
   TabContent, TabPane,
   TabContent, TabPane,
 } from 'reactstrap';
 } from 'reactstrap';
-
 import * as toastr from 'toastr';
 import * as toastr from 'toastr';
 
 
-import { UserPicture } from '@growi/ui';
 import AppContainer from '~/client/services/AppContainer';
 import AppContainer from '~/client/services/AppContainer';
-import PageContainer from '~/client/services/PageContainer';
 import CommentContainer from '~/client/services/CommentContainer';
 import CommentContainer from '~/client/services/CommentContainer';
 import EditorContainer from '~/client/services/EditorContainer';
 import EditorContainer from '~/client/services/EditorContainer';
+import PageContainer from '~/client/services/PageContainer';
 import GrowiRenderer from '~/client/util/GrowiRenderer';
 import GrowiRenderer from '~/client/util/GrowiRenderer';
 
 
-import { withUnstatedContainers } from '../UnstatedUtils';
+import { CustomNavTab } from '../CustomNavigation/CustomNav';
+import NotAvailableForGuest from '../NotAvailableForGuest';
 import Editor from '../PageEditor/Editor';
 import Editor from '../PageEditor/Editor';
 import { SlackNotification } from '../SlackNotification';
 import { SlackNotification } from '../SlackNotification';
+import { withUnstatedContainers } from '../UnstatedUtils';
 
 
 import CommentPreview from './CommentPreview';
 import CommentPreview from './CommentPreview';
-import NotAvailableForGuest from '../NotAvailableForGuest';
-import { CustomNavTab } from '../CustomNavigation/CustomNav';
 
 
 
 
 const navTabMapping = {
 const navTabMapping = {
@@ -314,6 +313,7 @@ class CommentEditor extends React.Component {
                 onChange={this.updateState}
                 onChange={this.updateState}
                 onUpload={this.uploadHandler}
                 onUpload={this.uploadHandler}
                 onCtrlEnter={this.ctrlEnterHandler}
                 onCtrlEnter={this.ctrlEnterHandler}
+                isComment
               />
               />
               {/*
               {/*
                 Note: <OptionsSelector /> is not optimized for ComentEditor in terms of responsive design.
                 Note: <OptionsSelector /> is not optimized for ComentEditor in terms of responsive design.
@@ -419,6 +419,7 @@ CommentEditor.propTypes = {
   commentCreator: PropTypes.string,
   commentCreator: PropTypes.string,
   onCancelButtonClicked: PropTypes.func,
   onCancelButtonClicked: PropTypes.func,
   onCommentButtonClicked: PropTypes.func,
   onCommentButtonClicked: PropTypes.func,
+  isComment:  PropTypes.bool,
 };
 };
 
 
 /**
 /**

+ 28 - 20
packages/app/src/components/PageEditor/CodeMirrorEditor.jsx

@@ -1,36 +1,35 @@
 import React from 'react';
 import React from 'react';
-import PropTypes from 'prop-types';
 
 
-import urljoin from 'url-join';
+import { createValidator } from '@growi/codemirror-textlint';
 import * as codemirror from 'codemirror';
 import * as codemirror from 'codemirror';
-import { Button } from 'reactstrap';
-
 import { JSHINT } from 'jshint';
 import { JSHINT } from 'jshint';
-
-import * as loadScript from 'simple-load-script';
 import * as loadCssSync from 'load-css-file';
 import * as loadCssSync from 'load-css-file';
+import PropTypes from 'prop-types';
+import { Button } from 'reactstrap';
+import * as loadScript from 'simple-load-script';
+import urljoin from 'url-join';
 
 
-import { createValidator } from '@growi/codemirror-textlint';
-import { UncontrolledCodeMirror } from '../UncontrolledCodeMirror';
 import InterceptorManager from '~/services/interceptor-manager';
 import InterceptorManager from '~/services/interceptor-manager';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
-import AbstractEditor from './AbstractEditor';
-import SimpleCheatsheet from './SimpleCheatsheet';
+import { UncontrolledCodeMirror } from '../UncontrolledCodeMirror';
 
 
-import pasteHelper from './PasteHelper';
+import AbstractEditor from './AbstractEditor';
+import CommentMentionHelper from './CommentMentionHelper';
+import DrawioModal from './DrawioModal';
+import EditorIcon from './EditorIcon';
 import EmojiAutoCompleteHelper from './EmojiAutoCompleteHelper';
 import EmojiAutoCompleteHelper from './EmojiAutoCompleteHelper';
-import PreventMarkdownListInterceptor from './PreventMarkdownListInterceptor';
-import MarkdownTableInterceptor from './MarkdownTableInterceptor';
-import mlu from './MarkdownLinkUtil';
-import mtu from './MarkdownTableUtil';
-import mdu from './MarkdownDrawioUtil';
-import geu from './GridEditorUtil';
 import GridEditModal from './GridEditModal';
 import GridEditModal from './GridEditModal';
-import LinkEditModal from './LinkEditModal';
+import geu from './GridEditorUtil';
 import HandsontableModal from './HandsontableModal';
 import HandsontableModal from './HandsontableModal';
-import EditorIcon from './EditorIcon';
-import DrawioModal from './DrawioModal';
+import LinkEditModal from './LinkEditModal';
+import mdu from './MarkdownDrawioUtil';
+import mlu from './MarkdownLinkUtil';
+import MarkdownTableInterceptor from './MarkdownTableInterceptor';
+import mtu from './MarkdownTableUtil';
+import pasteHelper from './PasteHelper';
+import PreventMarkdownListInterceptor from './PreventMarkdownListInterceptor';
+import SimpleCheatsheet from './SimpleCheatsheet';
 
 
 // Textlint
 // Textlint
 window.JSHINT = JSHINT;
 window.JSHINT = JSHINT;
@@ -191,6 +190,11 @@ export default class CodeMirrorEditor extends AbstractEditor {
 
 
     // fold drawio section
     // fold drawio section
     this.foldDrawioSection();
     this.foldDrawioSection();
+
+    // initialize commentMentionHelper if comment editor is opened
+    if (this.props.isComment) {
+      this.commentMentionHelper = new CommentMentionHelper(this.getCodeMirror());
+    }
   }
   }
 
 
   componentWillReceiveProps(nextProps) {
   componentWillReceiveProps(nextProps) {
@@ -572,6 +576,10 @@ export default class CodeMirrorEditor extends AbstractEditor {
     if (this.state.isEnabledEmojiAutoComplete) {
     if (this.state.isEnabledEmojiAutoComplete) {
       this.emojiAutoCompleteHelper.showHint(editor);
       this.emojiAutoCompleteHelper.showHint(editor);
     }
     }
+    // Show username hint on comment editor
+    if (this.props.isComment) {
+      this.commentMentionHelper.showUsernameHint();
+    }
   }
   }
 
 
   /**
   /**

+ 40 - 0
packages/app/src/components/PageEditor/CommentMentionHelper.tsx

@@ -0,0 +1,40 @@
+
+export default class CommentMentionHelper {
+
+  editor;
+
+  pattern: RegExp;
+
+  constructor(editor) {
+    this.editor = editor;
+    this.pattern = /@[A-Za-z0-9._-]*/;
+  }
+
+  showUsernameHint = () => {
+    const currentPos = this.editor.getCursor();
+    const sc = this.editor.getSearchCursor(this.pattern, currentPos, { multiline:  false });
+    if (sc.findPrevious()) {
+      const isMentioning = (currentPos.line === sc.to().line && currentPos.ch === sc.to().ch);
+      if (!isMentioning) {
+        return;
+      }
+    }
+    else {
+      return;
+    }
+
+    this.editor.showHint({
+      completeSingle: false,
+      hint: () => {
+        const mention = this.editor.getDoc().getRange(sc.from(), sc.to());
+        const username = mention.replace('@', '');
+        return {
+          list: ['username1', 'username2'],
+          from: sc.from(),
+          to: sc.to(),
+        };
+      },
+    });
+  }
+
+}