Yuki Takei 4 лет назад
Родитель
Сommit
ed8f5319b6
1 измененных файлов с 97 добавлено и 116 удалено
  1. 97 116
      packages/app/src/components/SavePageControls/GrantSelector.tsx

+ 97 - 116
packages/app/src/components/SavePageControls/GrantSelector.tsx

@@ -1,7 +1,6 @@
-import React from 'react';
+import React, { useCallback, useState } from 'react';
 
-import PropTypes from 'prop-types';
-import { withTranslation } from 'react-i18next';
+import { useTranslation } from 'react-i18next';
 import {
   UncontrolledDropdown,
   DropdownToggle, DropdownMenu, DropdownItem,
@@ -12,112 +11,109 @@ import {
 
 import AppContainer from '~/client/services/AppContainer';
 import { apiGet } from '~/client/util/apiv1-client';
+import { IUserGroupHasId } from '~/interfaces/user';
+import { useCurrentUser } from '~/stores/context';
 
 import { withUnstatedContainers } from '../UnstatedUtils';
 
+
+const AVAILABLE_GRANTS = [
+  {
+    grant: 1, iconClass: 'icon-people', btnStyleClass: 'outline-info', label: 'Public',
+  },
+  {
+    grant: 2, iconClass: 'icon-link', btnStyleClass: 'outline-teal', label: 'Anyone with the link',
+  },
+  // { grant: 3, iconClass: '', label: 'Specified users only' },
+  {
+    grant: 4, iconClass: 'icon-lock', btnStyleClass: 'outline-danger', label: 'Only me',
+  },
+  {
+    grant: 5, iconClass: 'icon-options', btnStyleClass: 'outline-purple', label: 'Only inside the group', reselectLabel: 'Reselect the group',
+  },
+];
+
+
+type Props = {
+  appContainer: AppContainer,
+
+  disabled?: boolean,
+  grant: number,
+  grantGroupId?: string,
+  grantGroupName?: string,
+
+  onUpdateGrant?: (args: { grant: number, grantGroupId?: string | null, grantGroupName?: string | null }) => void,
+}
+
 /**
  * Page grant select component
- *
- * @export
- * @class GrantSelector
- * @extends {React.Component}
  */
-class GrantSelector extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.availableGrants = [
-      {
-        grant: 1, iconClass: 'icon-people', btnStyleClass: 'outline-info', label: 'Public',
-      },
-      {
-        grant: 2, iconClass: 'icon-link', btnStyleClass: 'outline-teal', label: 'Anyone with the link',
-      },
-      // { grant: 3, iconClass: '', label: 'Specified users only' },
-      {
-        grant: 4, iconClass: 'icon-lock', btnStyleClass: 'outline-danger', label: 'Only me',
-      },
-      {
-        grant: 5, iconClass: 'icon-options', btnStyleClass: 'outline-purple', label: 'Only inside the group', reselectLabel: 'Reselect the group',
-      },
-    ];
-
-    this.state = {
-      userRelatedGroups: [],
-      isSelectGroupModalShown: false,
-    };
+const GrantSelector = (props: Props): JSX.Element => {
+  const { t } = useTranslation();
 
-    this.showSelectGroupModal = this.showSelectGroupModal.bind(this);
-    this.hideSelectGroupModal = this.hideSelectGroupModal.bind(this);
+  const {
+    disabled,
+    grantGroupName,
+    onUpdateGrant,
+  } = props;
 
-    this.changeGrantHandler = this.changeGrantHandler.bind(this);
-    this.groupListItemClickHandler = this.groupListItemClickHandler.bind(this);
-  }
 
-  showSelectGroupModal() {
-    this.retrieveUserGroupRelations();
-    this.setState({ isSelectGroupModalShown: true });
-  }
+  const [userRelatedGroups, setUserRelatedGroups] = useState<IUserGroupHasId[]>([]);
+  const [isSelectGroupModalShown, setIsSelectGroupModalShown] = useState(false);
 
-  hideSelectGroupModal() {
-    this.setState({ isSelectGroupModalShown: false });
-  }
+  const { data: currentUser } = useCurrentUser();
 
   /**
    * Retrieve user-group-relations data from backend
    */
-  retrieveUserGroupRelations() {
-    apiGet('/me/user-group-relations')
-      .then((res) => {
-        return res.userGroupRelations;
-      })
-      .then((userGroupRelations) => {
-        const userRelatedGroups = userGroupRelations.map((relation) => {
-          return relation.relatedGroup;
-        });
-        this.setState({ userRelatedGroups });
-      });
-  }
+  const retrieveUserGroupRelations = async() => {
+    const res = await apiGet('/me/user-group-relations') as any;
+    const userRelatedGroups = res.userGroupRelations.map((relation) => {
+      return relation.relatedGroup;
+    });
+    setUserRelatedGroups(userRelatedGroups);
+  };
+
+  const showSelectGroupModal = useCallback(() => {
+    retrieveUserGroupRelations();
+    setIsSelectGroupModalShown(true);
+  }, []);
 
   /**
    * change event handler for grant selector
    */
-  changeGrantHandler(grant) {
+  const changeGrantHandler = useCallback((grant: number) => {
     // select group
     if (grant === 5) {
-      this.showSelectGroupModal();
+      showSelectGroupModal();
       return;
     }
 
-    if (this.props.onUpdateGrant != null) {
-      this.props.onUpdateGrant({ grant, grantGroupId: null, grantGroupName: null });
+    if (onUpdateGrant != null) {
+      onUpdateGrant({ grant, grantGroupId: null, grantGroupName: null });
     }
-  }
+  }, [onUpdateGrant, showSelectGroupModal]);
 
-  groupListItemClickHandler(grantGroup) {
-    if (this.props.onUpdateGrant != null) {
-      this.props.onUpdateGrant({ grant: 5, grantGroupId: grantGroup._id, grantGroupName: grantGroup.name });
+  const groupListItemClickHandler = useCallback((grantGroup: IUserGroupHasId) => {
+    if (onUpdateGrant != null) {
+      onUpdateGrant({ grant: 5, grantGroupId: grantGroup._id, grantGroupName: grantGroup.name });
     }
 
     // hide modal
-    this.hideSelectGroupModal();
-  }
+    setIsSelectGroupModalShown(false);
+  }, [onUpdateGrant]);
 
   /**
    * Render grant selector DOM.
-   * @returns
-   * @memberof GrantSelector
    */
-  renderGrantSelector() {
-    const { t } = this.props;
-    const { grant: currentGrant, grantGroupId } = this.props;
+  const renderGrantSelector = useCallback(() => {
+    const { grant: currentGrant, grantGroupId } = props;
 
-    let dropdownToggleBtnColor = null;
-    let dropdownToggleLabelElm = null;
+    let dropdownToggleBtnColor;
+    let dropdownToggleLabelElm;
 
-    const dropdownMenuElems = this.availableGrants.map((opt) => {
-      const label = (opt.grant === 5 && grantGroupId != null)
+    const dropdownMenuElems = AVAILABLE_GRANTS.map((opt) => {
+      const label = ((opt.grant === 5 && opt.reselectLabel != null) && grantGroupId != null)
         ? opt.reselectLabel // when grantGroup is selected
         : opt.label;
 
@@ -134,7 +130,7 @@ class GrantSelector extends React.Component {
         dropdownToggleLabelElm = labelElm;
       }
 
-      return <DropdownItem key={opt.grant} onClick={() => this.changeGrantHandler(opt.grant)}>{labelElm}</DropdownItem>;
+      return <DropdownItem key={opt.grant} onClick={() => changeGrantHandler(opt.grant)}>{labelElm}</DropdownItem>;
     });
 
     // add specified group option
@@ -142,7 +138,7 @@ class GrantSelector extends React.Component {
       const labelElm = (
         <span>
           <i className="icon icon-fw icon-organization"></i>
-          <span className="label">{this.props.grantGroupName}</span>
+          <span className="label">{grantGroupName}</span>
         </span>
       );
 
@@ -155,7 +151,7 @@ class GrantSelector extends React.Component {
     return (
       <div className="form-group grw-grant-selector mb-0">
         <UncontrolledDropdown direction="up">
-          <DropdownToggle color={dropdownToggleBtnColor} caret className="d-flex justify-content-between align-items-center" disabled={this.props.disabled}>
+          <DropdownToggle color={dropdownToggleBtnColor} caret className="d-flex justify-content-between align-items-center" disabled={disabled}>
             {dropdownToggleLabelElm}
           </DropdownToggle>
           <DropdownMenu>
@@ -164,21 +160,20 @@ class GrantSelector extends React.Component {
         </UncontrolledDropdown>
       </div>
     );
-  }
+  }, [changeGrantHandler, disabled, grantGroupName, props, t]);
 
   /**
    * Render select grantgroup modal.
-   *
-   * @returns
-   * @memberof GrantSelector
    */
-  renderSelectGroupModal() {
-    const { t } = this.props;
+  const renderSelectGroupModal = useCallback(() => {
+    if (currentUser == null) {
+      return <></>;
+    }
 
     const generateGroupListItems = () => {
-      return this.state.userRelatedGroups.map((group) => {
+      return userRelatedGroups.map((group) => {
         return (
-          <button key={group._id} type="button" className="list-group-item list-group-item-action" onClick={() => { this.groupListItemClickHandler(group) }}>
+          <button key={group._id} type="button" className="list-group-item list-group-item-action" onClick={() => groupListItemClickHandler(group)}>
             <h5>{group.name}</h5>
             {/* TODO: Replace <div className="small">(TBD) List group members</div> */}
           </button>
@@ -186,13 +181,13 @@ class GrantSelector extends React.Component {
       });
     };
 
-    const content = this.state.userRelatedGroups.length === 0
+    const content = userRelatedGroups.length === 0
       ? (
         <div>
           <h4>{t('user_group.belonging_to_no_group')}</h4>
-          { this.props.appContainer.isAdmin
-            && <p><a href="/admin/user-groups"><i className="icon icon-fw icon-login"></i>{t('user_group.manage_user_groups')}</a></p>
-          }
+          { currentUser.admin && (
+            <p><a href="/admin/user-groups"><i className="icon icon-fw icon-login"></i>{t('user_group.manage_user_groups')}</a></p>
+          ) }
         </div>
       )
       : (
@@ -204,10 +199,10 @@ class GrantSelector extends React.Component {
     return (
       <Modal
         className="select-grant-group"
-        isOpen={this.state.isSelectGroupModalShown}
-        toggle={this.hideSelectGroupModal}
+        isOpen={isSelectGroupModalShown}
+        toggle={() => setIsSelectGroupModalShown(false)}
       >
-        <ModalHeader tag="h4" toggle={this.hideSelectGroupModal} className="bg-purple text-light">
+        <ModalHeader tag="h4" toggle={() => setIsSelectGroupModalShown(false)} className="bg-purple text-light">
           {t('user_group.select_group')}
         </ModalHeader>
         <ModalBody>
@@ -215,34 +210,20 @@ class GrantSelector extends React.Component {
         </ModalBody>
       </Modal>
     );
-  }
+  }, [currentUser, groupListItemClickHandler, isSelectGroupModalShown, t, userRelatedGroups]);
 
-  render() {
-    return (
-      <React.Fragment>
-        { this.renderGrantSelector() }
-        { !this.props.disabled && this.renderSelectGroupModal() }
-      </React.Fragment>
-    );
-  }
+  return (
+    <React.Fragment>
+      { renderGrantSelector() }
+      { !disabled && renderSelectGroupModal() }
+    </React.Fragment>
+  );
 
-}
+};
 
 /**
  * Wrapper component for using unstated
  */
 const GrantSelectorWrapper = withUnstatedContainers(GrantSelector, [AppContainer]);
 
-GrantSelector.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-
-  disabled: PropTypes.bool,
-  grant: PropTypes.number.isRequired,
-  grantGroupId: PropTypes.string,
-  grantGroupName: PropTypes.string,
-
-  onUpdateGrant: PropTypes.func,
-};
-
-export default withTranslation()(GrantSelectorWrapper);
+export default GrantSelectorWrapper;