Skip to content

Commit

Permalink
Allow changing dashboard theme on the fly
Browse files Browse the repository at this point in the history
  • Loading branch information
WiNloSt committed Jan 31, 2024
1 parent 1f4a7db commit 95de2dc
Showing 1 changed file with 58 additions and 59 deletions.
117 changes: 58 additions & 59 deletions frontend/src/metabase/dashboard/hoc/DashboardControls.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable react/prop-types */
import { Component } from "react";
import _ from "underscore";

import { connect } from "react-redux";
import { replace } from "react-router-redux";
Expand All @@ -23,15 +24,6 @@ export const DashboardControls = ComposedComponent =>
(ComposedComponent.displayName || ComposedComponent.name) +
"]";

state = {
isFullscreen: false,
theme: null,

refreshPeriod: null,

hideParameters: null,
};

UNSAFE_componentWillMount() {
if (screenfull.enabled) {
document.addEventListener(
Expand All @@ -43,8 +35,7 @@ export const DashboardControls = ComposedComponent =>
}

componentDidUpdate() {
this.updateDashboardParams();
this._showNav(!this.state.isFullscreen);
this._showNav(!this.getUrlHashState().isFullscreen);
}

componentWillUnmount() {
Expand Down Expand Up @@ -72,44 +63,36 @@ export const DashboardControls = ComposedComponent =>
this.setHideParameters(options.hide_parameters);
};

updateDashboardParams = () => {
const { location, replace } = this.props;

const options = parseHashOptions(location.hash);
const setValue = (name, value) => {
if (value) {
options[name] = value;
} else {
delete options[name];
}
getUrlHashState() {
const { location } = this.props;
return {
isFullscreen: false,
refreshPeriod: null,
theme: null,
hideParameters: null,
...renameKeys(parseHashOptions(location.hash), {
fullscreen: "isFullscreen",
refresh: "refreshPeriod",
}),
};
setValue("refresh", this.state.refreshPeriod);
setValue("fullscreen", this.state.isFullscreen);
const normalizedTheme = options.theme ?? null;
if (normalizedTheme !== this.state.theme) {
this.setTheme(normalizedTheme);
}

delete options.night; // DEPRECATED: options.night

// Delete the "add card to dashboard" and "editing mode" parameters
// if they are present because we do not want to add the card again on
// page refresh. The parameters are already handled in DashboardApp
// before this method is called.
delete options.add;
delete options.edit;

let hash = stringifyHashOptions(options);
hash = hash ? "#" + hash : "";
}

if (hash !== location.hash) {
replace({
pathname: location.pathname,
search: location.search,
hash,
});
}
};
setUrlHash(options) {
const { location, replace } = this.props;
const newOptions = {
...parseHashOptions(location.hash),
...renameKeys(options, {
isFullscreen: "fullscreen",
refreshPeriod: "refresh",
}),
};
const hash = stringifyHashOptions(removeEmptyKeys(newOptions));
replace({
pathname: location.pathname,
search: location.search,
hash: hash ? "#" + hash : "",
});
}

setRefreshPeriod = refreshPeriod => {
this._clearRefreshInterval();
Expand All @@ -118,32 +101,32 @@ export const DashboardControls = ComposedComponent =>
this._tickRefreshClock,
TICK_PERIOD * 1000,
);
this.setState({ refreshPeriod });
this.setUrlHash({ refreshPeriod });
this.setRefreshElapsed(0);
MetabaseAnalytics.trackStructEvent(
"Dashboard",
"Set Refresh",
refreshPeriod,
);
} else {
this.setState({ refreshPeriod: null });
this.setUrlHash({ refreshPeriod: null });
this.setRefreshElapsed(null);
}
};

// Preserve existing behavior, while keeping state in a new `theme` key
setNightMode = isNightMode => {
const theme = isNightMode ? "night" : null;
this.setState({ theme });
this.setUrlHash({ theme });
};

setTheme = theme => {
this.setState({ theme });
this.setUrlHash({ theme });
};

setFullscreen = async (isFullscreen, browserFullscreen = true) => {
isFullscreen = !!isFullscreen;
if (isFullscreen !== this.state.isFullscreen) {
if (isFullscreen !== this.getUrlHashState().isFullscreen) {
if (screenfull.enabled && browserFullscreen) {
if (isFullscreen) {
try {
Expand All @@ -158,17 +141,17 @@ export const DashboardControls = ComposedComponent =>
await screenfull.exit();
}
}
this.setState({ isFullscreen });
this.setUrlHash({ isFullscreen });
}
};

setHideParameters = parameters => {
this.setState({ hideParameters: parameters });
this.setUrlHash({ hideParameters: parameters });
};

_tickRefreshClock = async () => {
this._refreshElapsed = (this._refreshElapsed || 0) + TICK_PERIOD;
const { refreshPeriod } = this.state;
const { refreshPeriod } = this.getUrlHashState();
if (refreshPeriod && this._refreshElapsed >= refreshPeriod) {
this._refreshElapsed = 0;
await this.props.fetchDashboard({
Expand Down Expand Up @@ -205,7 +188,7 @@ export const DashboardControls = ComposedComponent =>
}

_fullScreenChanged = () => {
this.setState({ isFullscreen: !!screenfull.isFullscreen });
this.setUrlHash({ isFullscreen: !!screenfull.isFullscreen });
};

setRefreshElapsedHook = hook => {
Expand All @@ -222,9 +205,9 @@ export const DashboardControls = ComposedComponent =>
return (
<ComposedComponent
{...this.props}
{...this.state}
isNightMode={this.state.theme === "night"}
hasNightModeToggle={this.state.theme !== "transparent"}
{...this.getUrlHashState()}
isNightMode={this.getUrlHashState().theme === "night"}
hasNightModeToggle={this.getUrlHashState().theme !== "transparent"}
setRefreshElapsedHook={this.setRefreshElapsedHook}
loadDashboardParams={this.loadDashboardParams}
onNightModeChange={this.setNightMode}
Expand All @@ -235,3 +218,19 @@ export const DashboardControls = ComposedComponent =>
}
},
);

function removeEmptyKeys(options) {
return Object.fromEntries(
Object.entries(options).filter(([_key, value]) => {
return value;
}),
);
}

function renameKeys(object, keysMap) {
return Object.fromEntries(
Object.entries(object).map(([key, value]) => {
return [keysMap[key] || key, value];
}),
);
}

0 comments on commit 95de2dc

Please sign in to comment.