huridocs/uwazi

View on GitHub
app/react/App/Menu.tsx

Summary

Maintainability
A
1 hr
Test Coverage
B
86%
import React from 'react';
import { useLocation } from 'react-router-dom';
import { bindActionCreators, Dispatch } from 'redux';
import { connect, ConnectedProps } from 'react-redux';
import { fromJS } from 'immutable';
import { wrapDispatch } from 'app/Multireducer';
import { NeedAuthorization } from 'app/Auth';
import { I18NLink, I18NMenu, t, Translate } from 'app/I18N';
import { processFilters, encodeSearch } from 'app/Library/actions/libraryActions';
import { showSemanticSearch as showSemanticSearchAction } from 'app/SemanticSearch/actions/actions';
import { FeatureToggleSemanticSearch } from 'app/SemanticSearch/components/FeatureToggleSemanticSearch';
import { libraryViewInfo } from 'app/App/libraryViewInfo';
import { Icon } from 'UI';
import { actions } from 'app/BasicReducer';
import { IStore } from 'app/istore';
import { searchParamsFromLocationSearch } from 'app/utils/routeHelpers';
import { DropdownMenu } from './DropdownMenu';

interface MenuProps {
  className: string;
  defaultLibraryView: any;
  toggleMobileMenu: (visible: boolean) => void;
}

const mapStateToProps = (state: IStore) => {
  const { user, settings, library } = state;
  return {
    user,
    librarySearch: library.search,
    libraryFilters: library.filters,
    links: settings.collection.get('links'),
    defaultLibraryView: settings.collection.get('defaultLibraryView'),
    privateInstance: settings.collection.get('private'),
  };
};

const mapDispatchToProps = (dispatch: Dispatch<{}>) =>
  bindActionCreators(
    {
      showSemanticSearch: showSemanticSearchAction,
      setSidePanelView: actions.set.bind(null, 'library.sidepanel.view'),
    },
    wrapDispatch(dispatch, 'library')
  );

const connector = connect(mapStateToProps, mapDispatchToProps);

type mappedProps = ConnectedProps<typeof connector> & MenuProps;

const MenuComponent = ({
  librarySearch,
  libraryFilters,
  user,
  className,
  toggleMobileMenu,
  setSidePanelView,
  showSemanticSearch,
  links = fromJS([]),
  defaultLibraryView = 'cards',
  privateInstance,
}: mappedProps) => {
  const hideMobileMenu = () => toggleMobileMenu(false);

  const getLink = (
    to: string,
    text: string,
    icon: string,
    linkClassName?: string,
    onclick?: (args: any) => any
    // eslint-disable-next-line max-params
  ) => (
    <I18NLink
      to={to}
      onClick={(args: any) => {
        if (onclick) onclick(args);
        hideMobileMenu();
      }}
      className={`menuNav-btn btn btn-default ${linkClassName}`}
      activeclassname="active-link"
      aria-label={t('System', text, null, false)}
    >
      <Icon icon={icon} />
      <span className="tab-link-label">
        <Translate>{text}</Translate>
      </span>
    </I18NLink>
  );

  const libraryUrl = () => {
    // eslint-disable-next-line react-hooks/rules-of-hooks
    const location = useLocation();
    const searchParams = searchParamsFromLocationSearch(location, 'q') || {};
    const searchTerm = searchParams.searchTerm || '';
    const newParams = processFilters(librarySearch, libraryFilters.toJS());
    newParams.searchTerm = searchTerm;

    // @ts-ignore
    return `/${libraryViewInfo[defaultLibraryView].url}/${encodeSearch(newParams)}`;
  };

  const currentUser = user.toJS();

  const navLinks = links
    .map((link, index) => {
      if (link === undefined) {
        return null;
      }
      const type = link.get('type') || 'link';

      if (type === 'link') {
        const url = link.get('url') || '/';
        if (url.startsWith('http')) {
          return (
            <li key={link.get('_id')} className="menuNav-item">
              <a href={url} className="btn menuNav-btn" target="_blank" rel="noreferrer">
                {t('Menu', link.get('title'))}
              </a>
            </li>
          );
        }
        return (
          <li key={link.get('_id')} className="menuNav-item">
            <I18NLink
              to={url}
              className="btn menuNav-btn"
              activeclassname="active-link"
              onClick={hideMobileMenu}
            >
              {t('Menu', link.get('title'))}
            </I18NLink>
          </li>
        );
      }

      return (
        <DropdownMenu
          link={fromJS(link.toJS())}
          position={index!}
          key={index}
          hideMobileMenu={hideMobileMenu}
        />
      );
    })
    .filter(v => v !== null)
    .toArray();

  const setLibraryView = () => {
    setSidePanelView('library');
  };
  return (
    <ul className={className}>
      <li className="menuItems">
        <ul className="menuNav-list">{navLinks}</ul>
      </li>
      <I18NMenu />
      <li className="menuActions mobile-menuActions">
        <ul className="menuNav-list">
          <FeatureToggleSemanticSearch>
            <li className="menuNav-item semantic-search">
              <button
                type="button"
                onClick={showSemanticSearch}
                className="menuNav-btn btn btn-default"
                aria-label={t('System', 'Semantic search', null, false)}
              >
                <Icon icon="flask" />
                <span className="tab-link-tooltip">
                  <Translate>Semantic search</Translate>
                </span>
              </button>
            </li>
          </FeatureToggleSemanticSearch>
          {(!privateInstance || (privateInstance === true && currentUser._id)) && (
            <li className="menuNav-item">
              {getLink(
                libraryUrl(),
                'Library',
                //@ts-ignore
                libraryViewInfo[defaultLibraryView].icon,
                'public-documents',
                setLibraryView
              )}
            </li>
          )}
          <NeedAuthorization roles={['admin', 'editor', 'collaborator']}>
            <li className="menuNav-item only-desktop">
              {getLink('/settings/account', 'Settings', 'cog', 'settings-section')}
            </li>
          </NeedAuthorization>
          <NeedAuthorization roles={['admin', 'editor', 'collaborator']}>
            <li className="menuNav-item only-mobile">
              {getLink('/settings', 'Settings', 'cog', 'settings-section')}
            </li>
          </NeedAuthorization>
          <NeedAuthorization roles={['admin', 'editor', 'collaborator']}>
            <li className="menuNav-item only-mobile">
              <a href="/logout" className="menuNav-btn btn btn-default">
                <Icon icon="power-off" />
                <span className="tab-link-label">
                  <Translate>Logout</Translate>
                </span>
              </a>
            </li>
          </NeedAuthorization>
          {(() => {
            if (!currentUser._id) {
              return <li className="menuNav-item">{getLink('/login', 'Sign in', 'power-off')}</li>;
            }
            return null;
          })()}
        </ul>
      </li>
    </ul>
  );
};

const container = connector(MenuComponent);
export { container as Menu };