diff --git a/client/src/components/app.js b/client/src/components/app.js index 57442ee4..59375e18 100644 --- a/client/src/components/app.js +++ b/client/src/components/app.js @@ -10,6 +10,7 @@ import Legend from "./continuousLegend"; import Graph from "./graph/graph"; import MenuBar from "./menubar"; import Autosave from "./autosave"; +import TermsOfServicePrompt from "./termsPrompt"; import actions from "../actions"; @@ -93,6 +94,7 @@ class App extends React.Component { {loading ? null : } {loading ? null : } {loading ? null : } + {loading ? null : } diff --git a/client/src/components/framework/toasters.js b/client/src/components/framework/toasters.js index 52964d29..8466ead4 100644 --- a/client/src/components/framework/toasters.js +++ b/client/src/components/framework/toasters.js @@ -7,6 +7,11 @@ const ToastTopCenter = Toaster.create({ position: Position.TOP }); +const ToastBottomCenter = Toaster.create({ + className: "recipe-toaster", + position: Position.BOTTOM +}); + /* A "user" error - eg, bad input */ @@ -46,3 +51,11 @@ export const postAsyncFailureToast = message => timeout: 10000, intent: Intent.WARNING }); + +export const termsOfServiceToast = (message, _onDismiss) => + ToastBottomCenter.show({ + message, + timeout: 0, + intent: Intent.PRIMARY, + onDismiss: _onDismiss + }); diff --git a/client/src/components/termsPrompt/index.js b/client/src/components/termsPrompt/index.js new file mode 100644 index 00000000..992403ed --- /dev/null +++ b/client/src/components/termsPrompt/index.js @@ -0,0 +1,76 @@ +import React from "react"; +import { connect } from "react-redux"; +import * as globals from "../../globals"; +import { termsOfServiceToast } from "../framework/toasters"; + +const TosDismissedKey = "cxg.tosDismissed"; + +function storageGet(key, defaultValue = null) { + try { + const val = window.localStorage.getItem(key); + if (val === null) return defaultValue; + return val; + } catch (e) { + return defaultValue; + } +} + +function storageSet(key, value) { + try { + window.localStorage.setItem(key, value); + } catch { + return; + } +} + +@connect(state => ({ + tosURL: state.config?.parameters?.about_legal_tos +})) +class TermsPrompt extends React.PureComponent { + constructor(props) { + super(props); + this.state = { + hasDismissed: storageGet(TosDismissedKey, false) + }; + } + + componentDidMount() { + const { hasDismissed } = this.state; + const { tosURL } = this.props; + if (!hasDismissed && tosURL) { + this.popTermsToast(); + } + } + + onTermsToastDismissed = () => { + this.setState({ hasDismissed: "yes" }); + storageSet(TosDismissedKey, "yes"); + }; + + popTermsToast() { + const { tosURL } = this.props; + termsOfServiceToast( + + By using our site, you are agreeing to our{" "} + + Terms of Service + + , + this.onTermsToastDismissed + ); + } + + render() { + return null; + } +} + +export default TermsPrompt;