Browser.tsx 6.96 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/**
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

17
import React, { useEffect } from 'react';
18
import { ApolloProvider } from '@apollo/client';
19
import { connect } from 'react-redux';
20
import { Link } from 'react-router-dom';
Andrey Azov's avatar
Andrey Azov committed
21

22 23
import useBrowserRouting from './hooks/useBrowserRouting';

24
import { client } from 'src/gql-client';
25 26 27
import analyticsTracking from 'src/services/analytics-service';
import * as urlFor from 'src/shared/helpers/urlHelper';
import { BreakpointWidth } from 'src/global/globalConfig';
28

29 30 31 32 33
import {
  parseEnsObjectId,
  buildFocusIdForUrl
} from 'src/shared/state/ens-object/ensObjectHelpers';

34 35 36
import { toggleTrackPanel } from 'src/content/app/browser/track-panel/trackPanelActions';
import { toggleDrawer } from './drawer/drawerActions';

37
import {
38
  getBrowserNavOpenState,
39
  getChrLocation,
40 41 42
  getBrowserActivated,
  getBrowserActiveGenomeId,
  getBrowserQueryParams,
Andrey Azov's avatar
Andrey Azov committed
43
  getBrowserActiveEnsObjectId,
44
  getBrowserActiveEnsObjectIds
45
} from './browserSelectors';
46

Imran Salam's avatar
Imran Salam committed
47 48
import { getIsTrackPanelOpened } from './track-panel/trackPanelSelectors';
import { getIsDrawerOpened } from './drawer/drawerSelectors';
49
import { getExampleEnsObjects } from 'src/shared/state/ens-object/ensObjectSelectors';
50
import { getBreakpointWidth } from 'src/global/globalSelectors';
51

52 53 54 55 56 57 58 59 60
import BrowserBar from './browser-bar/BrowserBar';
import BrowserImage from './browser-image/BrowserImage';
import BrowserNavBar from './browser-nav/BrowserNavBar';
import TrackPanel from './track-panel/TrackPanel';
import TrackPanelBar from './track-panel/track-panel-bar/TrackPanelBar';
import TrackPanelTabs from './track-panel/track-panel-tabs/TrackPanelTabs';
import BrowserAppBar from './browser-app-bar/BrowserAppBar';
import Drawer from './drawer/Drawer';
import { StandardAppLayout } from 'src/shared/components/layout';
Andrey Azov's avatar
Andrey Azov committed
61 62
import ErrorBoundary from 'src/shared/components/error-boundary/ErrorBoundary';
import { NewTechError } from 'src/shared/components/error-screen';
63
import BrowserInterstitial from './interstitial/BrowserInterstitial';
64

65
import { RootState } from 'src/store';
66
import { ChrLocation } from './browserState';
67
import { EnsObject } from 'src/shared/state/ens-object/ensObjectTypes';
68

69 70
import styles from './Browser.scss';

71
export type BrowserProps = {
Andrey Azov's avatar
Andrey Azov committed
72 73
  activeGenomeId: string | null;
  activeEnsObjectId: string | null;
74
  browserActivated: boolean;
75
  browserNavOpenState: boolean;
76
  browserQueryParams: { [key: string]: string };
Andrey Azov's avatar
Andrey Azov committed
77
  chrLocation: ChrLocation | null;
Imran Salam's avatar
Imran Salam committed
78 79
  isDrawerOpened: boolean;
  isTrackPanelOpened: boolean;
Andrey Azov's avatar
Andrey Azov committed
80
  exampleEnsObjects: EnsObject[];
81 82
  viewportWidth: BreakpointWidth;
  toggleTrackPanel: (isOpen: boolean) => void;
Imran Salam's avatar
Imran Salam committed
83
  toggleDrawer: (isDrawerOpened: boolean) => void;
84
};
85

86
export const Browser = (props: BrowserProps) => {
87
  const { changeGenomeId } = useBrowserRouting();
Andrey Azov's avatar
Andrey Azov committed
88

89
  const { isDrawerOpened } = props;
90

91
  useEffect(() => {
92
    const { activeGenomeId } = props;
93 94 95
    if (!activeGenomeId) {
      return;
    }
96

97
    analyticsTracking.setSpeciesDimension(activeGenomeId);
Andrey Azov's avatar
Andrey Azov committed
98
  }, [props.activeGenomeId]);
99

100 101
  const onSidebarToggle = () => {
    props.toggleTrackPanel(!props.isTrackPanelOpened); // FIXME
102
  };
103

104 105
  const toggleDrawer = () => {
    props.toggleDrawer(!props.isDrawerOpened);
106 107
  };

108
  const shouldShowNavBar =
109
    props.browserActivated && props.browserNavOpenState && !isDrawerOpened;
110

111
  const mainContent = (
112
    <>
113 114 115 116 117 118
      {shouldShowNavBar && <BrowserNavBar />}
      <BrowserImage />
    </>
  );

  return (
119
    <ApolloProvider client={client}>
120
      <div className={styles.genomeBrowser}>
121
        <BrowserAppBar onSpeciesSelect={changeGenomeId} />
122
        {props.activeGenomeId && props.browserQueryParams.focus ? (
123 124 125 126 127 128 129 130 131 132 133 134 135 136
          <StandardAppLayout
            mainContent={mainContent}
            sidebarContent={<TrackPanel />}
            sidebarNavigation={<TrackPanelTabs />}
            sidebarToolstripContent={<TrackPanelBar />}
            onSidebarToggle={onSidebarToggle}
            topbarContent={<BrowserBar />}
            isSidebarOpen={props.isTrackPanelOpened}
            isDrawerOpen={props.isDrawerOpened}
            drawerContent={<Drawer />}
            onDrawerClose={toggleDrawer}
            viewportWidth={props.viewportWidth}
          />
        ) : (
137
          <BrowserInterstitial />
138 139 140
        )}
      </div>
    </ApolloProvider>
141
  );
Andrey Azov's avatar
Andrey Azov committed
142 143
};

144
export const ExampleObjectLinks = (props: BrowserProps) => {
Andrey Azov's avatar
Andrey Azov committed
145
  const { activeGenomeId } = props;
146

Andrey Azov's avatar
Andrey Azov committed
147 148 149
  if (!activeGenomeId) {
    return null;
  }
150

Andrey Azov's avatar
Andrey Azov committed
151
  const links = props.exampleEnsObjects.map((exampleObject: EnsObject) => {
152 153
    const parsedEnsObjectId = parseEnsObjectId(exampleObject.object_id);
    const focusId = buildFocusIdForUrl(parsedEnsObjectId);
Andrey Azov's avatar
Andrey Azov committed
154 155
    const path = urlFor.browser({
      genomeId: activeGenomeId,
156
      focus: focusId
Andrey Azov's avatar
Andrey Azov committed
157 158 159
    });

    return (
160
      <div key={exampleObject.object_id} className={styles.exampleLink}>
161 162 163
        <Link to={path} replace>
          Example {exampleObject.type}
        </Link>
Andrey Azov's avatar
Andrey Azov committed
164 165 166 167
      </div>
    );
  });

168 169
  return (
    <div>
170
      <div className={styles.exampleLinks__emptyTopbar} />
171 172 173
      <div className={styles.exampleLinks}>{links}</div>
    </div>
  );
174
};
175

Andrey Azov's avatar
Andrey Azov committed
176 177 178 179 180 181 182
const mapStateToProps = (state: RootState) => {
  const activeGenomeId = getBrowserActiveGenomeId(state);
  return {
    activeGenomeId,
    activeEnsObjectId: getBrowserActiveEnsObjectId(state),
    allActiveEnsObjectIds: getBrowserActiveEnsObjectIds(state),
    browserActivated: getBrowserActivated(state),
183
    browserNavOpenState: getBrowserNavOpenState(state),
Andrey Azov's avatar
Andrey Azov committed
184 185 186 187
    browserQueryParams: getBrowserQueryParams(state),
    chrLocation: getChrLocation(state),
    isDrawerOpened: getIsDrawerOpened(state),
    isTrackPanelOpened: getIsTrackPanelOpened(state),
188
    exampleEnsObjects: getExampleEnsObjects(state),
Andrey Azov's avatar
Andrey Azov committed
189 190 191
    viewportWidth: getBreakpointWidth(state)
  };
};
192

193
const mapDispatchToProps = {
194
  toggleDrawer,
195
  toggleTrackPanel
196 197
};

Andrey Azov's avatar
Andrey Azov committed
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
const ReduxConnectedBrowser = connect(
  mapStateToProps,
  mapDispatchToProps
)(Browser);

const WasmLoadingBrowserContainer = () => {
  useEffect(() => {
    /* eslint-disable */
    // @ts-ignore ensembl-genome-browser does not have typescript definitions
    import('ensembl-genome-browser');
    /* eslint-enable */
  });

  return <ReduxConnectedBrowser />;
};

const ErrorWrappedBrowser = () => {
  // if an error happens during loading of the browser,
  // we will be able to show custom error string
  return (
    <ErrorBoundary fallbackComponent={NewTechError}>
      <WasmLoadingBrowserContainer />
    </ErrorBoundary>
  );
};

export default ErrorWrappedBrowser;