summaryrefslogtreecommitdiffstats
path: root/browser/components/asrouter/content-src/components/ASRouterAdmin/ImpressionsSection.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/asrouter/content-src/components/ASRouterAdmin/ImpressionsSection.jsx')
-rw-r--r--browser/components/asrouter/content-src/components/ASRouterAdmin/ImpressionsSection.jsx146
1 files changed, 146 insertions, 0 deletions
diff --git a/browser/components/asrouter/content-src/components/ASRouterAdmin/ImpressionsSection.jsx b/browser/components/asrouter/content-src/components/ASRouterAdmin/ImpressionsSection.jsx
new file mode 100644
index 0000000000..87174cb6d9
--- /dev/null
+++ b/browser/components/asrouter/content-src/components/ASRouterAdmin/ImpressionsSection.jsx
@@ -0,0 +1,146 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import { ASRouterUtils } from "../../asrouter-utils";
+import React, {
+ useState,
+ useMemo,
+ useCallback,
+ useEffect,
+ useRef,
+} from "react";
+
+const stringify = json => JSON.stringify(json, null, 2);
+
+export const ImpressionsSection = ({
+ messageImpressions,
+ groupImpressions,
+ screenImpressions,
+}) => {
+ const handleSaveMessageImpressions = useCallback(newImpressions => {
+ ASRouterUtils.editState("messageImpressions", newImpressions);
+ }, []);
+ const handleSaveGroupImpressions = useCallback(newImpressions => {
+ ASRouterUtils.editState("groupImpressions", newImpressions);
+ }, []);
+ const handleSaveScreenImpressions = useCallback(newImpressions => {
+ ASRouterUtils.editState("screenImpressions", newImpressions);
+ }, []);
+
+ const handleResetMessageImpressions = useCallback(() => {
+ ASRouterUtils.sendMessage({ type: "RESET_MESSAGE_STATE" });
+ }, []);
+ const handleResetGroupImpressions = useCallback(() => {
+ ASRouterUtils.sendMessage({ type: "RESET_GROUPS_STATE" });
+ }, []);
+ const handleResetScreenImpressions = useCallback(() => {
+ ASRouterUtils.sendMessage({ type: "RESET_SCREEN_IMPRESSIONS" });
+ }, []);
+
+ return (
+ <div className="impressions-section">
+ <ImpressionsItem
+ impressions={messageImpressions}
+ label="Message Impressions"
+ description="Message impressions are stored in an object, where each key is a message ID and each value is an array of timestamps. They are cleaned up when a message with that ID stops existing in ASRouter state (such as at the end of an experiment)."
+ onSave={handleSaveMessageImpressions}
+ onReset={handleResetMessageImpressions}
+ />
+ <ImpressionsItem
+ impressions={groupImpressions}
+ label="Group Impressions"
+ description="Group impressions are stored in an object, where each key is a group ID and each value is an array of timestamps. They are never cleaned up."
+ onSave={handleSaveGroupImpressions}
+ onReset={handleResetGroupImpressions}
+ />
+ <ImpressionsItem
+ impressions={screenImpressions}
+ label="Screen Impressions"
+ description="Screen impressions are stored in an object, where each key is a screen ID and each value is the most recent timestamp that screen was shown. They are never cleaned up."
+ onSave={handleSaveScreenImpressions}
+ onReset={handleResetScreenImpressions}
+ />
+ </div>
+ );
+};
+
+const ImpressionsItem = ({
+ impressions,
+ label,
+ description,
+ validator,
+ onSave,
+ onReset,
+}) => {
+ const [json, setJson] = useState(stringify(impressions));
+
+ const modified = useRef(false);
+
+ const isValidJson = useCallback(
+ text => {
+ try {
+ JSON.parse(text);
+ return validator ? validator(text) : true;
+ } catch (e) {
+ return false;
+ }
+ },
+ [validator]
+ );
+
+ const jsonIsInvalid = useMemo(() => !isValidJson(json), [json, isValidJson]);
+
+ const handleChange = useCallback(e => {
+ setJson(e.target.value);
+ modified.current = true;
+ }, []);
+ const handleSave = useCallback(() => {
+ if (jsonIsInvalid) {
+ return;
+ }
+ const newImpressions = JSON.parse(json);
+ modified.current = false;
+ onSave(newImpressions);
+ }, [json, jsonIsInvalid, onSave]);
+ const handleReset = useCallback(() => {
+ modified.current = false;
+ onReset();
+ }, [onReset]);
+
+ useEffect(() => {
+ if (!modified.current) {
+ setJson(stringify(impressions));
+ }
+ }, [impressions]);
+
+ return (
+ <div className="impressions-item">
+ <span className="impressions-category">{label}</span>
+ {description ? (
+ <p className="impressions-description">{description}</p>
+ ) : null}
+ <div className="impressions-inner-box">
+ <div className="impressions-buttons">
+ <button
+ className="button primary"
+ disabled={jsonIsInvalid}
+ onClick={handleSave}
+ >
+ Save
+ </button>
+ <button className="button reset" onClick={handleReset}>
+ Reset
+ </button>
+ </div>
+ <div className="impressions-editor">
+ <textarea
+ className="general-textarea"
+ value={json}
+ onChange={handleChange}
+ />
+ </div>
+ </div>
+ </div>
+ );
+};