Skip to content

Commit

Permalink
Merge pull request #1717: Move mobile display detection to Redux
Browse files Browse the repository at this point in the history
  • Loading branch information
victorlin authored Nov 10, 2023
2 parents 0ebd146 + f965ddf commit 477d27b
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 40 deletions.
1 change: 1 addition & 0 deletions src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ export const CHANGE_MEASUREMENTS_DISPLAY = "CHANGE_MEASUREMENTS_DISPLAY";
export const APPLY_MEASUREMENTS_FILTER = "APPLY_MEASUREMENTS_FILTER";
export const UPDATE_MEASUREMENTS_ERROR = "UPDATE_MEASUREMENTS_ERROR";
export const TOGGLE_SHOW_ALL_BRANCH_LABELS = "TOGGLE_SHOW_ALL_BRANCH_LABELS";
export const TOGGLE_MOBILE_DISPLAY = "TOGGLE_MOBILE_DISPLAY";
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import React from "react";
import { useSelector } from "react-redux";
import { FaInfoCircle } from "react-icons/fa";
import {StyledTooltip, HeaderIconContainer, HeaderContainer} from "./styles";
import { RootState } from "../../store";

export const AnnotatedHeader = ({title, tooltip}) => {
const mobile = useSelector((state: RootState) => state.general.mobileDisplay);

export const AnnotatedHeader = ({title, tooltip, mobile}) => {
return (
<HeaderContainer>
<span>{title}</span>
Expand Down
7 changes: 4 additions & 3 deletions src/components/controls/choose-explode-attr.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import CustomSelect from "./customSelect";
@connect((state) => ({
selected: state.controls.explodeAttr,
available: state.metadata.colorings,
showThisUI: !state.controls.showTreeToo
showThisUI: !state.controls.showTreeToo,
mobileDisplay: state.general.mobileDisplay
}))
class ChooseExplodeAttr extends React.Component {
constructor(props) {
Expand All @@ -34,7 +35,7 @@ class ChooseExplodeAttr extends React.Component {
}
render() {
if (!this.props.showThisUI) return null;
const { t, tooltip, mobile } = this.props;
const { t, tooltip } = this.props;
const selectOptions = this.gatherAttrs();
return (
<div style={{paddingTop: 10}}>
Expand All @@ -43,7 +44,7 @@ class ChooseExplodeAttr extends React.Component {
<ImLab style={{ marginRight: "5px" }}/>
{t("sidebar:Explode Tree By")}
</span>
{tooltip && !mobile && (
{tooltip && !this.props.mobileDisplay && (
<>
<SidebarIconContainer data-tip data-for="select-explode">
<FaInfoCircle/>
Expand Down
23 changes: 11 additions & 12 deletions src/components/controls/controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,68 +31,67 @@ type Props = {
mapOn: boolean
frequenciesOn: boolean
measurementsOn: boolean
mobileDisplay: boolean
}

function Controls({ treeOn, mapOn, frequenciesOn, measurementsOn, mobileDisplay }: Props) {
function Controls({ treeOn, mapOn, frequenciesOn, measurementsOn }: Props) {
const { t } = useTranslation();

return (
<ControlsContainer>
<ChooseDataset />

<AnnotatedHeader title={t("sidebar:Date Range")} tooltip={DateRangeInfo} mobile={mobileDisplay}/>
<AnnotatedHeader title={t("sidebar:Date Range")} tooltip={DateRangeInfo}/>
<DateRangeInputs />
<AnimationControls />

<AnnotatedHeader title={t("sidebar:Color By")} tooltip={ColorByInfo} mobile={mobileDisplay}/>
<AnnotatedHeader title={t("sidebar:Color By")} tooltip={ColorByInfo}/>
<ColorBy />

<AnnotatedHeader title={t("sidebar:Filter Data")} tooltip={FilterInfo} mobile={mobileDisplay}/>
<AnnotatedHeader title={t("sidebar:Filter Data")} tooltip={FilterInfo}/>
<FilterData measurementsOn={measurementsOn} />

{treeOn &&
<span>
<AnnotatedHeader title={t("sidebar:Tree Options")} tooltip={TreeOptionsInfo} mobile={mobileDisplay}/>
<AnnotatedHeader title={t("sidebar:Tree Options")} tooltip={TreeOptionsInfo}/>
<ChooseLayout />
<ChooseMetric />
<ChooseBranchLabelling />
<ChooseTipLabel />
<ChooseSecondTree />
<ChooseExplodeAttr tooltip={ExplodeTreeInfo} mobile={mobileDisplay} />
<ChooseExplodeAttr tooltip={ExplodeTreeInfo} />
<ToggleTangle />
</span>
}

{measurementsOn &&
<span style={{ marginTop: "10px" }}>
<AnnotatedHeader title={t("sidebar:Measurements Options")} tooltip={MeasurementsOptionsInfo} mobile={mobileDisplay}/>
<AnnotatedHeader title={t("sidebar:Measurements Options")} tooltip={MeasurementsOptionsInfo}/>
<MeasurementsOptions />
</span>
}

{mapOn &&
<span style={{ marginTop: "10px" }}>
<AnnotatedHeader title={t("sidebar:Map Options")} tooltip={MapOptionsInfo} mobile={mobileDisplay}/>
<AnnotatedHeader title={t("sidebar:Map Options")} tooltip={MapOptionsInfo}/>
<GeoResolution />
<TransmissionLines />
</span>
}

{frequenciesOn &&
<span style={{ marginTop: "10px" }}>
<AnnotatedHeader title={t("sidebar:Frequency Options")} tooltip={FrequencyInfo} mobile={mobileDisplay}/>
<AnnotatedHeader title={t("sidebar:Frequency Options")} tooltip={FrequencyInfo}/>
<NormalizeFrequencies />
</span>
}

<span style={{ marginTop: "10px" }}>
<AnnotatedHeader title={t("sidebar:Animation Options")} tooltip={AnimationOptionsInfo} mobile={mobileDisplay}/>
<AnnotatedHeader title={t("sidebar:Animation Options")} tooltip={AnimationOptionsInfo}/>
<AnimationOptions />
</span>

<span style={{ paddingTop: "10px" }} />
<AnnotatedHeader title={t("sidebar:Panel Options")} tooltip={PanelOptionsInfo} mobile={mobileDisplay}/>
<AnnotatedHeader title={t("sidebar:Panel Options")} tooltip={PanelOptionsInfo}/>
<PanelLayout />
<PanelToggles />
<Language />
Expand Down
9 changes: 8 additions & 1 deletion src/components/framework/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import PropTypes from 'prop-types';
import { connect } from "react-redux";
import _throttle from "lodash/throttle";
import { BROWSER_DIMENSIONS, CHANGE_PANEL_LAYOUT, TOGGLE_SIDEBAR } from "../../actions/types";
import { BROWSER_DIMENSIONS, CHANGE_PANEL_LAYOUT, TOGGLE_SIDEBAR, TOGGLE_MOBILE_DISPLAY } from "../../actions/types";
import { changePage } from "../../actions/navigation";
import { twoColumnBreakpoint, controlsHiddenWidth} from "../../util/globals";

Expand Down Expand Up @@ -62,6 +62,13 @@ class Monitor extends React.Component {
}
}

/* Determine if a mobile device is being used based on window width. */
if (oldBrowserDimensions.width > controlsHiddenWidth && newBrowserDimensions.width < controlsHiddenWidth) {
dispatch({ type: TOGGLE_MOBILE_DISPLAY, value: true });
} else if (oldBrowserDimensions.width < controlsHiddenWidth && newBrowserDimensions.width > controlsHiddenWidth) {
dispatch({ type: TOGGLE_MOBILE_DISPLAY, value: false });
}

/* if we are _not_ in narrative mode, then browser resizing may change between grid & full layouts automatically */
if (!this.props.displayNarrative && this.props.canTogglePanelLayout) {
if (oldBrowserDimensions.width < twoColumnBreakpoint && newBrowserDimensions.width >= twoColumnBreakpoint) {
Expand Down
26 changes: 7 additions & 19 deletions src/components/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import SidebarToggle from "../framework/sidebar-toggle";
import Info from "../info/info";
import Tree from "../tree";
import Map from "../map/map";
import { controlsHiddenWidth } from "../../util/globals";
import Footer from "../framework/footer";
import FinePrint from "../framework/fine-print";
import DownloadModal from "../download/downloadModal";
Expand Down Expand Up @@ -43,22 +42,14 @@ const Measurements = lazy(() => import("../measurements"));
sidebarOpen: state.controls.sidebarOpen,
showOnlyPanels: state.controls.showOnlyPanels,
treeName: state.tree.name,
secondTreeName: state.controls.showTreeToo
secondTreeName: state.controls.showTreeToo,
mobileDisplay: state.general.mobileDisplay
}))
class Main extends React.Component {
constructor(props) {
super(props);
/* window listener employed to toggle switch to mobile display.
NOTE: this used to toggle sidebar open boolean when that was stored
as state here, but his has since ben moved to redux state. The mobile
display should likewise be lifted to redux state */
const mql = window.matchMedia(`(min-width: ${controlsHiddenWidth}px)`);
mql.addListener(() => this.setState({
mobileDisplay: !this.state.mql.matches
}));

this.state = {
mql,
mobileDisplay: !mql.matches,
showSpinner: !(this.props.metadataLoaded && this.props.treeLoaded)
};
analyticsNewPage();
Expand Down Expand Up @@ -116,7 +107,7 @@ class Main extends React.Component {

/* for mobile narratives we use a custom component as the nesting of view components is different */
/* TODO - the breakpoint for `mobileDisplay` needs testing */
if (this.state.mobileDisplay && this.props.displayNarrative) {
if (this.props.mobileDisplay && this.props.displayNarrative) {
return (
<>
<AnimationController/>
Expand All @@ -132,7 +123,7 @@ class Main extends React.Component {
* (b) narrative display for non-mobile (i.e. display side-by-side)
*/
const {availableWidth, availableHeight, sidebarWidth, overlayStyles} =
calcStyles(this.props.browserDimensions, this.props.displayNarrative, this.props.sidebarOpen, this.state.mobileDisplay);
calcStyles(this.props.browserDimensions, this.props.displayNarrative, this.props.sidebarOpen, this.props.mobileDisplay);
const overlayHandler = () => {this.props.dispatch({type: TOGGLE_SIDEBAR, value: false});};
const {full, grid, chartEntropy, chartFrequencies} =
calcPanelDims(this.props.panelsToDisplay, this.props.displayNarrative, availableWidth, availableHeight);
Expand All @@ -148,17 +139,14 @@ class Main extends React.Component {
</ErrorBoundary>
<SidebarToggle
sidebarOpen={this.props.sidebarOpen}
mobileDisplay={this.state.mobileDisplay}
mobileDisplay={this.props.mobileDisplay}
handler={this.toggleSidebar}
/>
<Sidebar
sidebarOpen={this.props.sidebarOpen}
width={sidebarWidth}
height={availableHeight}
displayNarrative={this.props.displayNarrative}
panelsToDisplay={this.props.panelsToDisplay}
narrativeTitle={this.props.narrativeTitle}
mobileDisplay={this.state.mobileDisplay}
navBarHandler={this.toggleSidebar}
/>
<PanelsContainer width={availableWidth} height={availableHeight} left={this.props.sidebarOpen ? sidebarWidth : 0}>
Expand Down Expand Up @@ -223,7 +211,7 @@ class Main extends React.Component {
}
</PanelsContainer>
{/* overlay (used for mobile to open / close sidebar) */}
{this.state.mobileDisplay ?
{this.props.mobileDisplay ?
<div style={overlayStyles} onClick={overlayHandler} onTouchStart={overlayHandler}/> :
null
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import React from "react";
import { useSelector } from "react-redux";
import {ThemeProvider} from 'styled-components';
import Narrative from "../narrative";
import NavBar from "../navBar";
import Controls from "../controls/controls";
import { SidebarContainer, sidebarTheme } from "./styles";
import { narrativeNavBarHeight } from "../../util/globals";
import { RootState } from "../../store";


export const Sidebar = (
{sidebarOpen, width, height, displayNarrative, panelsToDisplay, narrativeTitle, mobileDisplay, navBarHandler}
{ width, height, displayNarrative, narrativeTitle, navBarHandler}
) => {
const sidebarOpen = useSelector((state: RootState) => state.controls.sidebarOpen);
const panelsToDisplay = useSelector((state: RootState) => state.controls.panelsToDisplay);

return (
<ThemeProvider theme={sidebarTheme}>
<SidebarContainer left={sidebarOpen ? 0 : -1 * width} width={width} height={height}>
<NavBar
sidebar
mobileDisplay={mobileDisplay}
toggleHandler={navBarHandler}
narrativeTitle={displayNarrative ? narrativeTitle : false}
width={width}
Expand All @@ -24,7 +28,6 @@ export const Sidebar = (
<Narrative height={height - narrativeNavBarHeight} width={width} />
) : (
<Controls
mobileDisplay={mobileDisplay}
treeOn={panelsToDisplay.includes("tree")}
mapOn={panelsToDisplay.includes("map")}
frequenciesOn={panelsToDisplay.includes("frequencies")}
Expand Down
12 changes: 11 additions & 1 deletion src/reducers/general.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import queryString from "query-string";
import * as types from "../actions/types";
import { chooseDisplayComponentFromURL } from "../actions/navigation";
import { hasExtension, getExtension } from "../util/extensions";
import { controlsHiddenWidth } from "../util/globals";

/* the store for cross-cutting state -- that is, state
not limited to <App>
Expand All @@ -20,13 +21,18 @@ const getFirstPageToDisplay = () => {
return chooseDisplayComponentFromURL(window.location.pathname);
};

function getInitialMobileState() {
return window.innerWidth < controlsHiddenWidth;
}


const general = (state = {
defaults,
displayComponent: getFirstPageToDisplay(),
errorMessage: undefined,
pathname: window.location.pathname, // keep a copy of what the app "thinks" the pathname is
language: query.lang ? query.lang : defaults.language
language: query.lang ? query.lang : defaults.language,
mobileDisplay: getInitialMobileState()
}, action) => {
switch (action.type) {
case types.PAGE_CHANGE: {
Expand Down Expand Up @@ -54,6 +60,10 @@ const general = (state = {
language: query.lang ? query.lang : defaultLanguage
});
}
case types.TOGGLE_MOBILE_DISPLAY:
return Object.assign({}, state, {
mobileDisplay: action.value
});
default:
return state;
}
Expand Down

0 comments on commit 477d27b

Please sign in to comment.