From 4c75930508dd19476567683183bdcadbbd140bd3 Mon Sep 17 00:00:00 2001 From: Jan Konstant Date: Wed, 3 Nov 2021 16:24:36 +0100 Subject: [PATCH 1/3] reworked the matomo tracking feature --- examples/local_files/data-config.js | 5 + examples/local_files/index.html | 22 ++- vis/js/components/Backlink.js | 4 + vis/js/components/ContextLine.js | 10 +- vis/js/components/KnowledgeMap.js | 37 ++++- vis/js/headstart.js | 10 +- vis/js/intermediate.js | 54 +------ vis/js/reducers/index.js | 2 + vis/js/reducers/tracking.js | 17 +++ vis/js/templates/ListToggle.jsx | 13 +- vis/js/templates/ScaleToolbar.jsx | 15 +- vis/js/templates/ZoomedOutHeading.jsx | 6 + vis/js/templates/buttons/EditButton.jsx | 13 +- vis/js/templates/buttons/EmbedButton.jsx | 9 +- vis/js/templates/buttons/ShareButton.jsx | 52 ++++++- vis/js/templates/contextfeatures/Modifier.jsx | 9 +- .../templates/filtersort/FilterDropdown.jsx | 15 +- vis/js/templates/filtersort/SearchBox.jsx | 9 +- vis/js/templates/filtersort/SortDropdown.jsx | 14 +- vis/js/templates/listentry/Area.jsx | 33 +++- vis/js/templates/listentry/Link.jsx | 6 + vis/js/templates/listentry/PreviewIcons.jsx | 23 ++- vis/js/templates/listentry/PreviewImage.jsx | 10 +- vis/js/templates/listentry/Title.jsx | 10 +- vis/js/templates/modals/ViperEditModal.jsx | 7 + vis/js/utils/actionLogger.js | 142 ++---------------- vis/js/utils/useMatomo.js | 13 ++ 27 files changed, 341 insertions(+), 219 deletions(-) create mode 100644 vis/js/reducers/tracking.js create mode 100644 vis/js/utils/useMatomo.js diff --git a/examples/local_files/data-config.js b/examples/local_files/data-config.js index 96070589c..27fea47cd 100644 --- a/examples/local_files/data-config.js +++ b/examples/local_files/data-config.js @@ -17,6 +17,11 @@ var data_config = { show_context: false, create_title_from_context: false, + share_modal: true, + embed_modal: false, + + enable_mouseover_evaluation: false, + files: [{ title: "edu1", file: "./data/edu1.csv" diff --git a/examples/local_files/index.html b/examples/local_files/index.html index f864d8918..77b1c3bac 100644 --- a/examples/local_files/index.html +++ b/examples/local_files/index.html @@ -17,11 +17,31 @@ - + + + + + \ No newline at end of file diff --git a/vis/js/components/Backlink.js b/vis/js/components/Backlink.js index 67027ea8a..c41355460 100644 --- a/vis/js/components/Backlink.js +++ b/vis/js/components/Backlink.js @@ -6,6 +6,7 @@ import { STREAMGRAPH_MODE } from "../reducers/chartType"; import BacklinkTemplate from "../templates/Backlink"; import { createAnimationCallback } from "../utils/eventhandlers"; +import useMatomo from "../utils/useMatomo"; export const Backlink = ({ hidden = false, @@ -13,11 +14,14 @@ export const Backlink = ({ onClick, localization = {}, }) => { + const { trackEvent } = useMatomo(); + if (hidden) { return null; } const handleOnClick = () => { + trackEvent("Heading", "Zoom out", "Backlink"); if (onClick && typeof onClick === "function") { onClick(); } diff --git a/vis/js/components/ContextLine.js b/vis/js/components/ContextLine.js index 984017580..6afb59f39 100644 --- a/vis/js/components/ContextLine.js +++ b/vis/js/components/ContextLine.js @@ -17,6 +17,7 @@ import SearchLang from "../templates/contextfeatures/SearchLang"; import Timestamp from "../templates/contextfeatures/Timestamp"; import MetadataQuality from "../templates/contextfeatures/MetadataQuality"; import Modifier from "../templates/contextfeatures/Modifier"; +import { trackMatomoEvent } from "../utils/useMatomo"; const defined = (param) => param !== undefined && param !== null; @@ -104,9 +105,16 @@ class ContextLine extends React.Component { const text = documentTypes.join(", "); + const trackMouseOver = () => + trackMatomoEvent("Heading", "Hover document types", "Context line"); + return ( <> - + { const { data, areas, zoom, animation } = props; @@ -17,6 +18,7 @@ const KnowledgeMap = (props) => { const { handleDeselectPaper, handleSelectPaper } = props; const { hoveredBubble, bubbleOrder, changeBubbleOrder } = props; const { hoveredPaper, paperOrder, changePaperOrder } = props; + const { trackMouseOver } = props; // bubble section const handleAreaMouseOver = (area) => { @@ -24,24 +26,38 @@ const KnowledgeMap = (props) => { return; } + if (trackMouseOver) { + trackMatomoEvent("Knowledge map", "Hover bubble", "Bubble"); + } + changeBubbleOrder(area.area_uri); }; const handleOtherAreaZoomIn = (bubble) => { handleDeselectPaper(); handleZoomIn(bubble, true); + trackMatomoEvent("Knowledge map", "Zoom in", "Bubble"); }; const getBubbleZoomClickHandler = (bubble) => { if (zoomedBubbleUri === bubble.area_uri) { - return handleDeselectPaper; + if (!selectedPaperId) { + return undefined; + } + return () => { + handleDeselectPaper(); + trackMatomoEvent("Knowledge map", "Deselect paper", "Bubble"); + }; } if (zoom) { return () => handleOtherAreaZoomIn(bubble); } - return () => handleZoomIn(bubble); + return () => { + handleZoomIn(bubble); + trackMatomoEvent("Knowledge map", "Zoom in", "Bubble"); + }; }; const sortedAreas = sortAreasByIds(areas, bubbleOrder); @@ -50,7 +66,10 @@ const KnowledgeMap = (props) => { let onClick = undefined; let onMouseOver = () => changeBubbleOrder(null); if (zoom) { - onClick = handleZoomOut; + onClick = () => { + handleZoomOut(); + trackMatomoEvent("Knowledge map", "Zoom out", "Chart"); + }; onMouseOver = undefined; } @@ -65,7 +84,10 @@ const KnowledgeMap = (props) => { } let onDoubleClick = undefined; if (zoomedBubbleUri === bubble.area_uri) { - onDoubleClick = handleZoomOut; + onDoubleClick = () => { + handleZoomOut(); + trackMatomoEvent("Knowledge map", "Zoom out", "Bubble"); + }; } return { onClick, onMouseOver, onDoubleClick }; @@ -79,6 +101,7 @@ const KnowledgeMap = (props) => { handleZoomIn( areas.find((bubble) => bubble.area_uri === paper.area_uri) ); + trackMatomoEvent("Knowledge map", "Zoom in", "Bubble"); return; } @@ -90,10 +113,15 @@ const KnowledgeMap = (props) => { // bubble click event event.stopPropagation(); handleSelectPaper(paper); + trackMatomoEvent("Knowledge map", "Select paper", "Paper"); }; const handlePaperMouseOver = (newEnlargeFactor) => { changePaperOrder(paper.safe_id, newEnlargeFactor); + + if (trackMouseOver) { + trackMatomoEvent("Knowledge map", "Hover paper", "Paper"); + } }; const handlePaperMouseOut = () => { @@ -193,6 +221,7 @@ const mapStateToProps = (state) => ({ hoveredPaper: state.paperOrder.hoveredPaper, paperOrder: state.paperOrder.order, enlargeFactor: state.paperOrder.enlargeFactor, + trackMouseOver: state.tracking.trackMouseOver, }); export default connect( diff --git a/vis/js/headstart.js b/vis/js/headstart.js index 5390fd1b7..9128a0602 100644 --- a/vis/js/headstart.js +++ b/vis/js/headstart.js @@ -32,6 +32,7 @@ HeadstartFSM.prototype = { } }, + // TODO delete this completely recordAction: function(id, category, action, user, timestamp, additional_params, post_data) { if(!config.is_evaluation) { @@ -47,9 +48,6 @@ HeadstartFSM.prototype = { if (services.includes("log")) { this.recordActionLog(category, action, id, user, timestamp, additional_params, post_data); } - if (services.includes("matomo")) { - this.recordActionMatomo(category, action, id, user, timestamp, additional_params, post_data); - } if (services.includes("ga")) { this.recordActionGA(category, action, id, user, timestamp, additional_params, post_data); } @@ -83,12 +81,6 @@ HeadstartFSM.prototype = { }); }, - recordActionMatomo: function(category, action, id) { - if(typeof _paq !== "undefined") { - _paq.push(['trackEvent', category, action, id]); - } - }, - recordActionGA: function(category, action, id) { //gtag.js if(typeof gtag === "function") { diff --git a/vis/js/intermediate.js b/vis/js/intermediate.js index 50a33aec7..7c18f1f7e 100644 --- a/vis/js/intermediate.js +++ b/vis/js/intermediate.js @@ -31,12 +31,9 @@ import { sanitizeInputData } from "./utils/data"; * This class should ideally only talk to the mediator and redux */ class Intermediate { - constructor(rescaleCallback, recordActionCallback) { + constructor(rescaleCallback) { this.actionQueue = []; - this.recordActionCallback = recordActionCallback; - this.recordActionParams = {}; - const middleware = applyMiddleware( createFileChangeMiddleware(), createActionQueueMiddleware(this), @@ -44,10 +41,7 @@ class Intermediate { createRepeatedInitializeMiddleware(this), createChartTypeMiddleware(), createRescaleMiddleware(rescaleCallback), - createRecordActionMiddleware( - this.recordAction.bind(this), - this.recordActionParams - ) + createRecordActionMiddleware() ); this.store = createStore(rootReducer, middleware); @@ -68,14 +62,6 @@ class Intermediate { const { size, width, height } = getChartSize(config, context); const list = getListSize(config, context, size); - Object.assign(this.recordActionParams, { - title: config.title, - user: config.user_id, - localization: config.localization[config.language], - mouseoverEvaluation: config.enable_mouseover_evaluation, - scaleLabel: config.scale_label, - }); - const sanitizedMapData = sanitizeInputData(mapData); this.store.dispatch( @@ -124,38 +110,6 @@ class Intermediate { this.forceLayoutParams ); } - - /** - * Log an action using the mediator's function. - * - * @param {string} id usually some title, e.g. paper.title / default is the config.title - * @param {string} category some static key, such as "List" - * @param {string} action some static key, such as "show" - * @param {string} type some static key, such as "open_embed_modal" - * @param {object} timestamp optional object / default is null - * @param {string} additionalParams optional string / default is null - * @param {object} postData optional object / default is null - */ - recordAction( - id, - category, - action, - type, - timestamp = null, - additionalParams = null, - postData = null - ) { - this.recordActionCallback( - id, - category, - action, - this.recordActionParams.user, - type, - timestamp, - additionalParams, - postData - ); - } } /** @@ -309,11 +263,11 @@ function createFileChangeMiddleware() { }; } -function createRecordActionMiddleware(callback, params) { +function createRecordActionMiddleware() { return function ({ getState }) { return (next) => (action) => { const state = getState(); - logAction(action, state, callback, params); + logAction(action, state); return next(action); }; }; diff --git a/vis/js/reducers/index.js b/vis/js/reducers/index.js index 132b6abac..51cbb3520 100644 --- a/vis/js/reducers/index.js +++ b/vis/js/reducers/index.js @@ -28,6 +28,7 @@ import service from "./service"; import streamgraph from "./streamgraph"; import timespan from "./timespan"; import toolbar from "./toolbar"; +import tracking from "./tracking"; import zoom from "./zoom"; export default combineReducers({ @@ -61,5 +62,6 @@ export default combineReducers({ streamgraph, timespan, toolbar, + tracking, zoom, }); diff --git a/vis/js/reducers/tracking.js b/vis/js/reducers/tracking.js new file mode 100644 index 000000000..192da43ed --- /dev/null +++ b/vis/js/reducers/tracking.js @@ -0,0 +1,17 @@ +const tracking = (state = {}, action) => { + if (action.canceled) { + return state; + } + + switch (action.type) { + case "INITIALIZE": + return { + ...state, + trackMouseOver: action.configObject.enable_mouseover_evaluation, + }; + default: + return state; + } +}; + +export default tracking; diff --git a/vis/js/templates/ListToggle.jsx b/vis/js/templates/ListToggle.jsx index 15edb4cad..56ffe000c 100644 --- a/vis/js/templates/ListToggle.jsx +++ b/vis/js/templates/ListToggle.jsx @@ -1,13 +1,24 @@ import React from "react"; import { useLocalizationContext } from "../components/LocalizationProvider"; +import useMatomo from "../utils/useMatomo"; const ListToggle = ({ show, docsNumber, onClick }) => { const localization = useLocalizationContext(); + const { trackEvent } = useMatomo(); + + const handleClick = () => { + onClick(); + if (show) { + trackEvent("List controls", "Hide list", "List toggle"); + } else { + trackEvent("List controls", "Show list", "List toggle"); + } + }; return ( // html template starts here -
+
diff --git a/vis/js/templates/ScaleToolbar.jsx b/vis/js/templates/ScaleToolbar.jsx index 131e1b114..ac86cc26a 100644 --- a/vis/js/templates/ScaleToolbar.jsx +++ b/vis/js/templates/ScaleToolbar.jsx @@ -2,6 +2,7 @@ import React from "react"; import { DropdownButton, MenuItem } from "react-bootstrap"; import { useLocalizationContext } from "../components/LocalizationProvider"; +import useMatomo from "../utils/useMatomo"; const ScaleToolbar = ({ value, @@ -13,10 +14,16 @@ const ScaleToolbar = ({ onInfoClick, }) => { const localization = useLocalizationContext(); + const { trackEvent } = useMatomo(); + const handleScaleChange = (id) => { + onChange(id); + trackEvent("Added components", "Rescale map", labels[id]); + }; const handleInfoClick = (event) => { event.preventDefault(); onInfoClick(); + trackEvent("Added components", "Open more info modal", "Toolbar"); }; return ( @@ -36,7 +43,7 @@ const ScaleToolbar = ({ className="scale_item" key={key} eventKey={key} - onSelect={onChange} + onSelect={handleScaleChange} active={key === value} > {labels[key]} @@ -58,7 +65,11 @@ const ScaleToolbar = ({ {showCredit && (
created by{" "} - + { + const { trackEvent } = useMatomo(); + const handleInfoClick = (event) => { event.preventDefault(); + + trackEvent("Heading", "Open more info modal", "More info button"); + if (onInfoClick) { onInfoClick(); } diff --git a/vis/js/templates/buttons/EditButton.jsx b/vis/js/templates/buttons/EditButton.jsx index 0b5737e42..c761d55bf 100644 --- a/vis/js/templates/buttons/EditButton.jsx +++ b/vis/js/templates/buttons/EditButton.jsx @@ -1,15 +1,26 @@ import React from "react"; import { Button } from "react-bootstrap"; +import useMatomo from "../../utils/useMatomo"; const EditButton = ({ onClick, title }) => { + const { trackEvent } = useMatomo(); + const handleClick = () => { + onClick(); + trackEvent( + "Added components", + "Open Viper edit modal", + "Viper edit button" + ); + }; + return (
diff --git a/vis/js/templates/buttons/EmbedButton.jsx b/vis/js/templates/buttons/EmbedButton.jsx index 6185ea877..cd7e1aed6 100644 --- a/vis/js/templates/buttons/EmbedButton.jsx +++ b/vis/js/templates/buttons/EmbedButton.jsx @@ -1,9 +1,16 @@ import React from "react"; import { useLocalizationContext } from "../../components/LocalizationProvider"; +import useMatomo from "../../utils/useMatomo"; const EmbedButton = ({ onClick }) => { const localization = useLocalizationContext(); + const { trackEvent } = useMatomo(); + + const handleClick = () => { + trackEvent("Added components", "Open embed modal", "Embed button"); + onClick(); + }; return ( // html template starts here @@ -12,7 +19,7 @@ const EmbedButton = ({ onClick }) => { className="btn btn-primary" id="embedlink" title={localization.embed_button_title} - onClick={onClick} + onClick={handleClick} > diff --git a/vis/js/templates/buttons/ShareButton.jsx b/vis/js/templates/buttons/ShareButton.jsx index eae8fe592..0c6479f2e 100644 --- a/vis/js/templates/buttons/ShareButton.jsx +++ b/vis/js/templates/buttons/ShareButton.jsx @@ -3,16 +3,35 @@ import $ from "jquery"; import useOutsideClick from "../../utils/useOutsideClick"; import { useLocalizationContext } from "../../components/LocalizationProvider"; +import useMatomo from "../../utils/useMatomo"; const ShareButton = ({ twitterHashtags }) => { const localization = useLocalizationContext(); + const { trackEvent } = useMatomo(); + + const trackToggle = (opening) => { + if (opening) { + trackEvent("Added components", "Open share buttons", "Share button"); + } else { + trackEvent("Added components", "Close share buttons", "Share button"); + } + + return opening; + }; + + const trackShare = (type) => trackEvent("Added components", "Share", type); const [opened, setOpened] = useState(false); const handleClick = () => { - setOpened((prev) => !prev); + setOpened((prevOpened) => trackToggle(!prevOpened)); }; const handleOutsideClick = () => { - setOpened(false); + setOpened((prevOpened) => { + if (prevOpened) { + trackToggle(false); + } + return false; + }); }; const containerRef = useRef(null); @@ -42,18 +61,37 @@ const ShareButton = ({ twitterHashtags }) => { {opened && (
- - - - - diff --git a/vis/js/templates/contextfeatures/Modifier.jsx b/vis/js/templates/contextfeatures/Modifier.jsx index 18e0412f5..30ef5253e 100644 --- a/vis/js/templates/contextfeatures/Modifier.jsx +++ b/vis/js/templates/contextfeatures/Modifier.jsx @@ -3,11 +3,13 @@ import { connect } from "react-redux"; import { useLocalizationContext } from "../../components/LocalizationProvider"; import { STREAMGRAPH_MODE } from "../../reducers/chartType"; +import useMatomo from "../../utils/useMatomo"; import HoverPopover from "../HoverPopover"; const Modifier = ({ popoverContainer, modifier, isStreamgraph }) => { const localization = useLocalizationContext(); + const { trackEvent } = useMatomo(); if (modifier === "most-recent") { return ( @@ -19,9 +21,12 @@ const Modifier = ({ popoverContainer, modifier, isStreamgraph }) => { ); } + const trackMouseOver = () => + trackEvent("Heading", "Hover most relevant", "Context line"); + if (modifier === "most-relevant") { return ( - <> + { {localization.most_relevant_label} {" "} - + ); } diff --git a/vis/js/templates/filtersort/FilterDropdown.jsx b/vis/js/templates/filtersort/FilterDropdown.jsx index 802d01e92..705448fab 100644 --- a/vis/js/templates/filtersort/FilterDropdown.jsx +++ b/vis/js/templates/filtersort/FilterDropdown.jsx @@ -1,5 +1,6 @@ import React from "react"; import { DropdownButton, MenuItem } from "react-bootstrap"; +import useMatomo from "../../utils/useMatomo"; const FilterDropdown = ({ label, @@ -8,6 +9,18 @@ const FilterDropdown = ({ options, handleChange, }) => { + const { trackEvent } = useMatomo(); + const handleFilterChange = (id) => { + handleChange(id); + const selectedOption = options.find((o) => o.id === id); + const isOA = options.some((o) => o.label === "Open Access"); + trackEvent( + "List controls", + isOA ? "Filter open access" : "Filter document types", + selectedOption ? selectedOption.label : undefined + ); + }; + return (
{o.label} diff --git a/vis/js/templates/filtersort/SearchBox.jsx b/vis/js/templates/filtersort/SearchBox.jsx index ea2d3bdcf..5e52dce8b 100644 --- a/vis/js/templates/filtersort/SearchBox.jsx +++ b/vis/js/templates/filtersort/SearchBox.jsx @@ -2,6 +2,7 @@ import React from "react"; import { Glyphicon } from "react-bootstrap"; import debounce from "../../utils/debounce"; +import { trackMatomoEvent } from "../../utils/useMatomo"; // inspired by // https://medium.com/@justintulk/debouncing-reacts-controlled-textareas-w-redux-lodash-4383084ca090 @@ -31,6 +32,12 @@ class DebouncedSearchBox extends React.Component { render() { const { placeholder } = this.props; + + const handleClearSearch = () => { + this.handleChangeImmediately(""); + trackMatomoEvent("List controls", "Clear search", "Search box"); + }; + return ( // html template starts here
@@ -48,7 +55,7 @@ class DebouncedSearchBox extends React.Component { this.handleChangeImmediately("")} + onClick={handleClearSearch} /> )}
diff --git a/vis/js/templates/filtersort/SortDropdown.jsx b/vis/js/templates/filtersort/SortDropdown.jsx index d85797ec6..7c6a65c01 100644 --- a/vis/js/templates/filtersort/SortDropdown.jsx +++ b/vis/js/templates/filtersort/SortDropdown.jsx @@ -1,7 +1,19 @@ import React from "react"; import { DropdownButton, MenuItem } from "react-bootstrap"; +import useMatomo from "../../utils/useMatomo"; const SortDropdown = ({ label, value, valueLabel, options, handleChange }) => { + const { trackEvent } = useMatomo(); + const handleSortChange = (id) => { + handleChange(id); + const selectedOption = options.find((o) => o.id === id); + trackEvent( + "List controls", + "Sort list", + selectedOption ? selectedOption.label : undefined + ); + }; + return (
{ id={"sort_option_" + o.id} key={o.id} eventKey={o.id} - onSelect={handleChange} + onSelect={handleSortChange} active={o.id === value} > {o.label} diff --git a/vis/js/templates/listentry/Area.jsx b/vis/js/templates/listentry/Area.jsx index 3ee4faac6..9d441c1a5 100644 --- a/vis/js/templates/listentry/Area.jsx +++ b/vis/js/templates/listentry/Area.jsx @@ -1,16 +1,37 @@ import React from "react"; +import { connect } from "react-redux"; import { useLocalizationContext } from "../../components/LocalizationProvider"; +import useMatomo from "../../utils/useMatomo"; -const Area = ({ children, onClick, onMouseOver, onMouseOut }) => { +const Area = ({ + children, + onClick, + onMouseOver, + onMouseOut, + trackMouseOver, +}) => { const localization = useLocalizationContext(); + const { trackEvent } = useMatomo(); + + const handleMouseOver = () => { + onMouseOver(); + if (trackMouseOver) { + trackEvent("List paper", "Hover bubble", "Area name"); + } + }; + + const handleClick = () => { + onClick(); + trackEvent("List paper", "Zoom in", "Area name"); + }; return ( // html template starts here
{localization.area}:{" "} @@ -20,4 +41,8 @@ const Area = ({ children, onClick, onMouseOver, onMouseOut }) => { ); }; -export default Area; +const mapStateToProps = (state) => ({ + trackMouseOver: state.tracking.trackMouseOver, +}); + +export default connect(mapStateToProps)(Area); diff --git a/vis/js/templates/listentry/Link.jsx b/vis/js/templates/listentry/Link.jsx index 7a37f0107..5070bb778 100644 --- a/vis/js/templates/listentry/Link.jsx +++ b/vis/js/templates/listentry/Link.jsx @@ -1,9 +1,14 @@ import React from "react"; import { useLocalizationContext } from "../../components/LocalizationProvider"; +import useMatomo from "../../utils/useMatomo"; const Link = ({ address, isDoi }) => { const localization = useLocalizationContext(); + const { trackEvent } = useMatomo(); + + const trackClick = () => + trackEvent("List paper", "Open paper link", "Text link"); return ( // html template starts here @@ -16,6 +21,7 @@ const Link = ({ address, isDoi }) => { href={isDoi ? `https://dx.doi.org/${address}` : address} target="_blank" rel="noreferrer" + onClick={trackClick} > {address} diff --git a/vis/js/templates/listentry/PreviewIcons.jsx b/vis/js/templates/listentry/PreviewIcons.jsx index df10efa8e..47b26594e 100644 --- a/vis/js/templates/listentry/PreviewIcons.jsx +++ b/vis/js/templates/listentry/PreviewIcons.jsx @@ -1,16 +1,35 @@ import React from "react"; +import useMatomo from "../../utils/useMatomo"; const PreviewIcons = ({ link, onClickPDF }) => { + const { trackEvent } = useMatomo(); + + const trackLinkClick = () => { + trackEvent("List paper", "Open paper link", "Button link"); + }; + + const handlePDFClick = (event) => { + event.preventDefault(); + onClickPDF(); + trackEvent("List paper", "Show PDF preview", "PDF button"); + }; + return ( // html template starts here
{!!link && ( - + LINK )} {!!onClickPDF && ( - + PDF )} diff --git a/vis/js/templates/listentry/PreviewImage.jsx b/vis/js/templates/listentry/PreviewImage.jsx index 75d9efb47..f47dbf05f 100644 --- a/vis/js/templates/listentry/PreviewImage.jsx +++ b/vis/js/templates/listentry/PreviewImage.jsx @@ -3,8 +3,11 @@ import React from "react"; import defaultImage from "../../../images/preview_pdf.png"; import { isFileAvailable } from "../../utils/data"; +import useMatomo from "../../utils/useMatomo"; const PreviewImage = ({ imageURL = defaultImage, onClick }) => { + const { trackEvent } = useMatomo(); + if (imageURL !== defaultImage && !isFileAvailable(imageURL)) { return null; } @@ -19,9 +22,14 @@ const PreviewImage = ({ imageURL = defaultImage, onClick }) => { background: `url(${imageURL}) 0% 0% / 230px, 0% 0% / 298px`, }; + const handleClick = () => { + onClick(); + trackEvent("List paper", "Show PDF preview", "Preview image"); + }; + return ( // html template starts here -
+
Click here to open preview
diff --git a/vis/js/templates/listentry/Title.jsx b/vis/js/templates/listentry/Title.jsx index 48548d569..d4ca3c11c 100644 --- a/vis/js/templates/listentry/Title.jsx +++ b/vis/js/templates/listentry/Title.jsx @@ -1,11 +1,19 @@ import React from "react"; import Highlight from "../../components/Highlight"; +import useMatomo from "../../utils/useMatomo"; const Title = ({ children, onClick }) => { + const { trackEvent } = useMatomo(); + + const handleClick = () => { + onClick(); + trackEvent("List paper", "Select paper", "List title"); + }; + return ( // html template starts here -
+
{children} diff --git a/vis/js/templates/modals/ViperEditModal.jsx b/vis/js/templates/modals/ViperEditModal.jsx index 4f359bef7..c7e00185f 100644 --- a/vis/js/templates/modals/ViperEditModal.jsx +++ b/vis/js/templates/modals/ViperEditModal.jsx @@ -4,15 +4,22 @@ import { Modal } from "react-bootstrap"; import viperEditScreenshot from "../../../images/viper-project-screenshot.png"; import { useLocalizationContext } from "../../components/LocalizationProvider"; +import useMatomo from "../../utils/useMatomo"; const ViperEditModal = ({ open, onClose, acronym, title, objectID }) => { const localization = useLocalizationContext(); + const { trackEvent } = useMatomo(); const pageTitle = (acronym !== "" ? acronym + " - " : "") + title; const buttonLabel = localization.viper_button_desc_label.replace(/^

/, ""); const handleClick = (event) => { event.preventDefault(); + trackEvent( + "Added components", + "Open Viper edit outlink", + "Viper edit modal" + ); window.open(`https://www.openaire.eu/search/project?projectId=${objectID}`); }; diff --git a/vis/js/utils/actionLogger.js b/vis/js/utils/actionLogger.js index 054cb3aa6..18b37a329 100644 --- a/vis/js/utils/actionLogger.js +++ b/vis/js/utils/actionLogger.js @@ -1,139 +1,23 @@ +import { trackMatomoEvent } from "./useMatomo"; + /** - * If the action has to be logged, it logs it. + * Matomo logging function for events that depend purely on the state change. + * + * Events that are triggered by a user click are logged right in the components. * * @param {object} action redux action object - * @param {object} state redux state - * @param {Function} callback intermediate layer logger function - * @param {object} params config params + * @param {object} state redux state object */ -const logAction = (action, state, callback, params) => { +const logAction = (action, state) => { switch (action.type) { - case "ZOOM_IN": - return callback( - action.selectedAreaData.title, - "Bubble", - "zoomin", - "none" - ); - case "ZOOM_OUT": - return callback( - state.selectedBubble ? state.selectedBubble.title : "none", - "Bubble", - "zoomout", - "none" - ); - case "HOVER_BUBBLE": - if (params.mouseoverEvaluation) { - callback( - action.uri ? action.uri : state.bubbleOrder.hoveredBubble, - "Bubble", - action.uri ? "mouseover" : "mouseout", - "none" - ); - } - break; + case "INITIALIZE": + return trackMatomoEvent("Application", "Load"); case "RESIZE": - return callback(params.title, "Map", "resize", "resize_map"); - case "SHOW_LIST": - return callback(params.title, "List", "show", "none"); - case "TOGGLE_LIST": - return callback( - params.title, - "List", - state.list.show ? "hide" : "show", - "none" - ); - case "SORT": - return callback( - params.localization[action.id], - "List", - "sortBy", - "listsort", - null, - "sort_option=" + action.id - ); + return trackMatomoEvent("Application", "Resize window"); case "SEARCH": - return callback( - action.text, - "List", - "search", - "filter_list", - null, - "search_words=" + action.text - ); - case "FILTER": - return callback( - params.localization[action.id], - "List", - "filter", - "filter_list", - null, - "filter_param=" + action.id - ); - case "HOVER_PAPER": { - if (!params.mouseoverEvaluation) { - return; - } - if (!action.safeId) { - return; - } - const paper = state.data.list.find((p) => p.safe_id === action.safeId); - return callback( - paper.title, - "Paper", - "enlarge", - paper.bookmarked + " " + paper.recommended - ); - } - case "SELECT_PAPER": { - const paper = action.paper; - return callback( - paper.title, - "Paper", - "select", - paper.bookmarked + " " + paper.recommended - ); - } - case "DESELECT_PAPER": { - if (!state.selectedPaper || !state.selectedPaper.safeId) { - return; - } - const paper = state.data.list.find( - (p) => p.safe_id === state.selectedPaper.safeId - ); - if (!paper) { - return; - } - return callback( - paper.title, - "Paper", - "unselect", - paper.bookmarked + " " + paper.recommended - ); - } - case "INITIALIZE": - return callback(params.title, "Map", "start", "start_bubble"); - case "OPEN_INFO_MODAL": - return callback( - params.title, - "Map", - "open_info_modal", - "open_info_modal" - ); - case "OPEN_EMBED_MODAL": - return callback( - params.title, - "Map", - "open_embed_modal", - "open_embed_modal" - ); - case "SCALE": - return callback( - params.scaleLabel[action.value], - "Toolbar", - "select", - "toolbar_select" - ); + // TODO trackSiteSearch ? + // https://developer.matomo.org/guides/tracking-javascript-guide + return trackMatomoEvent("List controls", "Search", "Search box"); default: return; } diff --git a/vis/js/utils/useMatomo.js b/vis/js/utils/useMatomo.js new file mode 100644 index 000000000..b826fcd62 --- /dev/null +++ b/vis/js/utils/useMatomo.js @@ -0,0 +1,13 @@ +const useMatomo = () => { + const trackEvent = trackMatomoEvent; + + return { trackEvent }; +}; + +export default useMatomo; + +export const trackMatomoEvent = (category, action, name, value, dimensions) => { + if (typeof _paq !== "undefined") { + _paq.push(["trackEvent", category, action, name, value, dimensions]); + } +}; From cad7476a0bbba6f43ad87e7ad4439e1546387183 Mon Sep 17 00:00:00 2001 From: Jan Konstant Date: Wed, 10 Nov 2021 11:54:42 +0100 Subject: [PATCH 2/3] added citation button & vis footer tracking --- examples/project_website/base.html | 20 ++++++++++++++++++++ examples/project_website/pubmed.html | 20 ++++++++++++++++++++ vis/js/templates/buttons/CitationButton.jsx | 9 ++++++++- vis/js/templates/footers/CreatedBy.jsx | 16 +++++++++++++++- 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/examples/project_website/base.html b/examples/project_website/base.html index eef7e39c5..122a67f23 100644 --- a/examples/project_website/base.html +++ b/examples/project_website/base.html @@ -93,4 +93,24 @@ + + + + \ No newline at end of file diff --git a/examples/project_website/pubmed.html b/examples/project_website/pubmed.html index 6fb7a274e..f56c11460 100644 --- a/examples/project_website/pubmed.html +++ b/examples/project_website/pubmed.html @@ -115,4 +115,24 @@ + + + + \ No newline at end of file diff --git a/vis/js/templates/buttons/CitationButton.jsx b/vis/js/templates/buttons/CitationButton.jsx index d8bc93bcf..30abb211b 100644 --- a/vis/js/templates/buttons/CitationButton.jsx +++ b/vis/js/templates/buttons/CitationButton.jsx @@ -4,9 +4,16 @@ import { connect } from "react-redux"; import { openCitationModal } from "../../actions"; import { useLocalizationContext } from "../../components/LocalizationProvider"; import { STREAMGRAPH_MODE } from "../../reducers/chartType"; +import useMatomo from "../../utils/useMatomo"; const CitationButton = ({ isStreamgraph, onClick }) => { const localization = useLocalizationContext(); + const { trackEvent } = useMatomo(); + + const handleClick = () => { + onClick(); + trackEvent("Added components", "Open cite modal", "Cite button"); + }; return ( // html template starts here @@ -18,7 +25,7 @@ const CitationButton = ({ isStreamgraph, onClick }) => { ? localization.cite_title_sg : localization.cite_title_km } - onClick={onClick} + onClick={handleClick} > {" "} diff --git a/vis/js/templates/footers/CreatedBy.jsx b/vis/js/templates/footers/CreatedBy.jsx index e1f040d07..fe1107c8c 100644 --- a/vis/js/templates/footers/CreatedBy.jsx +++ b/vis/js/templates/footers/CreatedBy.jsx @@ -3,14 +3,28 @@ import React from "react"; import okmapsRoundLogo from "../../../images/okmaps-logo-round.svg"; import { getDateTimeFromTimestamp } from "../../utils/dates"; +import useMatomo from "../../utils/useMatomo"; const CreatedBy = ({ timestamp, faqsUrl }) => { const dateTime = getDateTimeFromTimestamp(timestamp); + const { trackEvent } = useMatomo(); + + const trackVisLink = () => + trackEvent( + "Added components", + "Open this visualization", + "Visualization link in footer" + ); return (

OKMaps round logo{" "} - + This visualization {" "} was created by{" "} From 03b7c64f199841c67ced9e61da71b5bd61a3a5af Mon Sep 17 00:00:00 2001 From: Jan Konstant Date: Wed, 10 Nov 2021 13:31:08 +0100 Subject: [PATCH 3/3] renamed some event categories --- vis/js/components/Backlink.js | 2 +- vis/js/components/ContextLine.js | 2 +- vis/js/templates/ZoomedOutHeading.jsx | 2 +- vis/js/templates/contextfeatures/Modifier.jsx | 2 +- vis/js/templates/listentry/Area.jsx | 4 ++-- vis/js/templates/listentry/Link.jsx | 2 +- vis/js/templates/listentry/PreviewIcons.jsx | 4 ++-- vis/js/templates/listentry/PreviewImage.jsx | 2 +- vis/js/templates/listentry/Title.jsx | 2 +- vis/js/utils/actionLogger.js | 4 ++-- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/vis/js/components/Backlink.js b/vis/js/components/Backlink.js index c41355460..834f9b815 100644 --- a/vis/js/components/Backlink.js +++ b/vis/js/components/Backlink.js @@ -21,7 +21,7 @@ export const Backlink = ({ } const handleOnClick = () => { - trackEvent("Heading", "Zoom out", "Backlink"); + trackEvent("Title & Context line", "Zoom out", "Backlink"); if (onClick && typeof onClick === "function") { onClick(); } diff --git a/vis/js/components/ContextLine.js b/vis/js/components/ContextLine.js index 8c7e2fc07..b6ae97b53 100644 --- a/vis/js/components/ContextLine.js +++ b/vis/js/components/ContextLine.js @@ -106,7 +106,7 @@ class ContextLine extends React.Component { const text = documentTypes.join(", "); const trackMouseOver = () => - trackMatomoEvent("Heading", "Hover document types", "Context line"); + trackMatomoEvent("Title & Context line", "Hover document types", "Context line"); return ( <> diff --git a/vis/js/templates/ZoomedOutHeading.jsx b/vis/js/templates/ZoomedOutHeading.jsx index e483d3a4b..c39c0523b 100644 --- a/vis/js/templates/ZoomedOutHeading.jsx +++ b/vis/js/templates/ZoomedOutHeading.jsx @@ -13,7 +13,7 @@ const ZoomedOutHeading = ({ const handleInfoClick = (event) => { event.preventDefault(); - trackEvent("Heading", "Open more info modal", "More info button"); + trackEvent("Title & Context line", "Open more info modal", "More info button"); if (onInfoClick) { onInfoClick(); diff --git a/vis/js/templates/contextfeatures/Modifier.jsx b/vis/js/templates/contextfeatures/Modifier.jsx index 30ef5253e..a1f2f4af2 100644 --- a/vis/js/templates/contextfeatures/Modifier.jsx +++ b/vis/js/templates/contextfeatures/Modifier.jsx @@ -22,7 +22,7 @@ const Modifier = ({ popoverContainer, modifier, isStreamgraph }) => { } const trackMouseOver = () => - trackEvent("Heading", "Hover most relevant", "Context line"); + trackEvent("Title & Context line", "Hover most relevant", "Context line"); if (modifier === "most-relevant") { return ( diff --git a/vis/js/templates/listentry/Area.jsx b/vis/js/templates/listentry/Area.jsx index 9d441c1a5..3edd6b0b0 100644 --- a/vis/js/templates/listentry/Area.jsx +++ b/vis/js/templates/listentry/Area.jsx @@ -17,13 +17,13 @@ const Area = ({ const handleMouseOver = () => { onMouseOver(); if (trackMouseOver) { - trackEvent("List paper", "Hover bubble", "Area name"); + trackEvent("List document", "Hover bubble", "Area name"); } }; const handleClick = () => { onClick(); - trackEvent("List paper", "Zoom in", "Area name"); + trackEvent("List document", "Zoom in", "Area name"); }; return ( diff --git a/vis/js/templates/listentry/Link.jsx b/vis/js/templates/listentry/Link.jsx index 5070bb778..36d3f55e7 100644 --- a/vis/js/templates/listentry/Link.jsx +++ b/vis/js/templates/listentry/Link.jsx @@ -8,7 +8,7 @@ const Link = ({ address, isDoi }) => { const { trackEvent } = useMatomo(); const trackClick = () => - trackEvent("List paper", "Open paper link", "Text link"); + trackEvent("List document", "Open paper link", "Text link"); return ( // html template starts here diff --git a/vis/js/templates/listentry/PreviewIcons.jsx b/vis/js/templates/listentry/PreviewIcons.jsx index 47b26594e..feeb1eb49 100644 --- a/vis/js/templates/listentry/PreviewIcons.jsx +++ b/vis/js/templates/listentry/PreviewIcons.jsx @@ -5,13 +5,13 @@ const PreviewIcons = ({ link, onClickPDF }) => { const { trackEvent } = useMatomo(); const trackLinkClick = () => { - trackEvent("List paper", "Open paper link", "Button link"); + trackEvent("List document", "Open paper link", "Button link"); }; const handlePDFClick = (event) => { event.preventDefault(); onClickPDF(); - trackEvent("List paper", "Show PDF preview", "PDF button"); + trackEvent("List document", "Show PDF preview", "PDF button"); }; return ( diff --git a/vis/js/templates/listentry/PreviewImage.jsx b/vis/js/templates/listentry/PreviewImage.jsx index f47dbf05f..50f8a9dcd 100644 --- a/vis/js/templates/listentry/PreviewImage.jsx +++ b/vis/js/templates/listentry/PreviewImage.jsx @@ -24,7 +24,7 @@ const PreviewImage = ({ imageURL = defaultImage, onClick }) => { const handleClick = () => { onClick(); - trackEvent("List paper", "Show PDF preview", "Preview image"); + trackEvent("List document", "Show PDF preview", "Preview image"); }; return ( diff --git a/vis/js/templates/listentry/Title.jsx b/vis/js/templates/listentry/Title.jsx index d4ca3c11c..e701bc508 100644 --- a/vis/js/templates/listentry/Title.jsx +++ b/vis/js/templates/listentry/Title.jsx @@ -8,7 +8,7 @@ const Title = ({ children, onClick }) => { const handleClick = () => { onClick(); - trackEvent("List paper", "Select paper", "List title"); + trackEvent("List document", "Select paper", "List title"); }; return ( diff --git a/vis/js/utils/actionLogger.js b/vis/js/utils/actionLogger.js index 18b37a329..628083b46 100644 --- a/vis/js/utils/actionLogger.js +++ b/vis/js/utils/actionLogger.js @@ -11,9 +11,9 @@ import { trackMatomoEvent } from "./useMatomo"; const logAction = (action, state) => { switch (action.type) { case "INITIALIZE": - return trackMatomoEvent("Application", "Load"); + return trackMatomoEvent("Headstart", "Load"); case "RESIZE": - return trackMatomoEvent("Application", "Resize window"); + return trackMatomoEvent("Headstart", "Resize window"); case "SEARCH": // TODO trackSiteSearch ? // https://developer.matomo.org/guides/tracking-javascript-guide