diff --git a/package.json b/package.json index b450453ba..c02895c9e 100644 --- a/package.json +++ b/package.json @@ -190,8 +190,7 @@ "scratch-vm": "3.0.9", "shortid": "^2.2.14", "slugg": "^1.2.1", - "uuid": "^3.3.2", - "zepto": "^1.2.0" + "uuid": "^3.3.2" }, "optionalDependencies": { "printer": "^0.2.2" diff --git a/src/components/edu-stage/edu-stage.jsx b/src/components/edu-stage/edu-stage.jsx index fe4e3b801..93b8bb3a3 100644 --- a/src/components/edu-stage/edu-stage.jsx +++ b/src/components/edu-stage/edu-stage.jsx @@ -16,7 +16,7 @@ import { import { eduUrl } from '../../lib/routing' import { gamesKeyed } from '../../lib/edu/' import VideoPlayer from '../video-player/video-player.jsx' -import { guiTypePages, paEvent } from '../../lib/piano-analytics/main' +import { buildGuiPage, paEvent } from '../../lib/piano-analytics/main' import styles from './edu-stage.css' const EduStageComponent = (props) => { @@ -119,12 +119,16 @@ const EduStageComponent = (props) => { } const sendPaEvent = (props, clickName) => { - const pages = guiTypePages(props.gameId) - pages.push('Code') + const pages = buildGuiPage(props.gameId, false, props.activeTabIndex) - let target = `${props.slideIndex}/${props.slideCount}` - if (clickName == 'Weiter') { - target = !props.linkNextGame ? `${props.slideIndex + 2}/${props.slideCount}` : 'Nächstes Lernspiel' + // 1-based number of the slide shown after the click + let target = `${props.slideIndex + 1}/${props.slideCount}` + if (clickName == 'Zurück') { + target = `${props.slideIndex}/${props.slideCount}` + } else if (clickName == 'Weiter') { + target = !props.linkNextGame + ? `${props.slideIndex + 2}/${props.slideCount}` + : 'Nächstes Lernspiel' } paEvent.clickAction({ @@ -132,7 +136,7 @@ const sendPaEvent = (props, clickName) => { pageType: 'Spiele', chapter1: 'Tutorial', chapter2: clickName, - target + target, }) } @@ -150,6 +154,7 @@ EduStageComponent.propTypes = { slideIndex: PropTypes.number, toggleFullscreen: PropTypes.func.isRequired, finished: PropTypes.bool, + activeTabIndex: PropTypes.number, preVideo: PropTypes.string, postVideo: PropTypes.string, } @@ -162,6 +167,7 @@ const mapStateToProps = (state, ownProps) => { isEnabled: state.scratchGui.eduLayer.enabled, imageSrc: '', gameId: ownProps.match.params.eduId, + activeTabIndex: state.scratchGui.editorTab.activeTabIndex, caption: '', } diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index a52e27006..642375ff9 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -106,11 +106,15 @@ const GUIComponent = (props) => { const logClickEvent = (eventFn, eventName) => { paEvent.clickAction({ - pages: buildGuiPage(props.eduId, props.isNewProject, props.activeTabIndex), + pages: buildGuiPage( + props.eduId, + props.isNewProject, + props.activeTabIndex + ), pageType: 'Spiele', chapter1: 'Speichern', chapter2: eventName, - target: eventName + target: eventName, }) eventFn() } @@ -138,7 +142,12 @@ const GUIComponent = (props) => {

{saveProjectError}

@@ -172,14 +183,19 @@ const GUIComponent = (props) => { {(_, renderFileInput, handleClick) => ( - + + )} @@ -268,7 +284,14 @@ const GUIComponent = (props) => { />
- +
@@ -380,6 +403,7 @@ GUIComponent.propTypes = { onProjectNameChange: PropTypes.func.isRequired, saveProjectError: PropTypes.string, eduId: PropTypes.string, + isNewProject: PropTypes.bool, } GUIComponent.defaultProps = { basePath: '/', diff --git a/src/components/menu/menu.jsx b/src/components/menu/menu.jsx index a81e2079d..423eedc76 100644 --- a/src/components/menu/menu.jsx +++ b/src/components/menu/menu.jsx @@ -102,13 +102,9 @@ export const MenuComponent = (props) => { selectedTabClassName={tabClassNames.tabSelected} selectedTabPanelClassName={tabClassNames.tabPanelSelected} selectedIndex={props.selectedTab} - onSelect={(index) => { - paEvent.pageDisplay({ - pages: ['Menu', menuTabTitles[index]], - pageType: 'Hauptseite', - }) - return props.handleTabSelected(index) - }} + onSelect={(index) => + index !== props.selectedTab && props.handleTabSelected(index) + } > @@ -200,7 +196,7 @@ export const MenuComponent = (props) => { pageType: 'Hauptseite', chapter1: 'Exit', chapter2: 'Zur Maus-Seite', - target: "https://www.wdrmaus.de/" + target: 'https://www.wdrmaus.de/', }) }} > diff --git a/src/containers/app.jsx b/src/containers/app.jsx index 1b9033802..6e693bf38 100644 --- a/src/containers/app.jsx +++ b/src/containers/app.jsx @@ -13,7 +13,6 @@ import AppStateHOC, { history } from '../lib/app-state-hoc.jsx' import styles from '../css/index.css' import ErrorBoundaryHOC from '../lib/error-boundary-hoc.jsx' -import withTracking from '../lib/tracking-hoc.jsx' import localeDe from '../../translations/de.json' import storage, { s3userFile } from '../lib/storage' import { setUserId } from '../reducers/project' @@ -271,9 +270,7 @@ const ConnectedApp = connect( }) )(App) -const WrappedApp = ErrorBoundaryHOC('Top Level App')( - AppStateHOC(withTracking(ConnectedApp)) -) +const WrappedApp = ErrorBoundaryHOC('Top Level App')(AppStateHOC(ConnectedApp)) WrappedApp.setAppElement = ReactModal.setAppElement export default WrappedApp diff --git a/src/containers/content.jsx b/src/containers/content.jsx index 802680f07..88642cb7a 100644 --- a/src/containers/content.jsx +++ b/src/containers/content.jsx @@ -45,10 +45,20 @@ class Content extends React.Component { } componentDidMount() { - paEvent.pageDisplay({ - pages: [this.props.match.params.page], - pageType: 'Beitrag', - }) + this.logPageDisplay() + } + + componentDidUpdate(prevProps) { + if (prevProps.match.params.page !== this.props.match.params.page) { + this.logPageDisplay() + } + } + + logPageDisplay() { + const { page } = this.props.match.params + if (page in contentMap) { + paEvent.pageDisplay({ pages: [page], pageType: 'Beitrag' }) + } } render() { diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx index df66d90fa..5672fb41f 100644 --- a/src/containers/gui.jsx +++ b/src/containers/gui.jsx @@ -9,7 +9,6 @@ import { activateTab, BLOCKS_TAB_INDEX, COSTUMES_TAB_INDEX, - editorTabNames, SOUNDS_TAB_INDEX, } from '../reducers/editor-tab' @@ -24,7 +23,6 @@ import GUIComponent from '../components/gui/gui.jsx' import { toggleLayoutMode } from '../reducers/layout-mode' import { setProjectUnchanged } from '../reducers/project-changed' import { buildGuiPage, paEvent } from '../lib/piano-analytics/main' -import { menuTabTitles } from '../lib/piano-analytics/constants' class GUI extends React.Component { constructor(props) { @@ -48,14 +46,12 @@ class GUI extends React.Component { } } componentDidUpdate(prevProps) { - if (this.props.isNewProject && prevProps.fetchingProject) { - logPageDisplay(null, this.props.isNewProject) - } - - const eduId = this.props.match.params.eduId - const prevEduId = prevProps.match.params.eduId - if (eduId && eduId !== prevEduId) { - logPageDisplay(eduId, false) + if (prevProps.fetchingProject && !this.props.fetchingProject) { + logPageDisplay( + this.props.eduId, + this.props.isNewProject, + this.props.activeTabIndex + ) } if ( @@ -107,7 +103,6 @@ class GUI extends React.Component { loadingStateVisible, onSetUnchanged, // eslint-disable-line no-unused-vars projectData, // eslint-disable-line no-unused-vars - isNewProject, // eslint-disable-line no-unused-vars staticContext, // eslint-disable-line no-unused-vars dispatch, // eslint-disable-line no-unused-vars vm, @@ -155,7 +150,6 @@ const mapStateToProps = (state) => ({ layoutmode: state.scratchGui.layoutMode, saveProjectVisible: state.scratchGui.modals.saveProject, eduLayerActive: state.scratchGui.eduLayer.enabled, - // eduId: state.scratchGui.eduLayer.gameId, isNewProject: state.router.location.state && !!state.router.location.state.isNewProject, }) @@ -163,18 +157,13 @@ const mapStateToProps = (state) => ({ const logPageDisplay = (eduId, isNewProject, tab) => { const pages = buildGuiPage(eduId, isNewProject, tab) - paEvent.pageDisplay({ pages: pages, pageType: "Spiele" }) -} - -const onTabActivating = (eduId, isNewProject, tab) => { - logPageDisplay(eduId, isNewProject, tab) - return activateTab(tab) + paEvent.pageDisplay({ pages: pages, pageType: 'Spiele' }) } const mapDispatchToProps = (dispatch) => ({ closeSaveModal: () => dispatch(closeSaveProject()), onExtensionButtonClick: () => dispatch(openExtensionLibrary()), - onActivateTab: (eduId, isNewProject, tab) => dispatch(onTabActivating(eduId, isNewProject, tab)), + onActivateTab: (tab) => dispatch(activateTab(tab)), onActivateCostumesTab: () => dispatch(activateTab(COSTUMES_TAB_INDEX)), onActivateSoundsTab: () => dispatch(activateTab(SOUNDS_TAB_INDEX)), onLayoutModeClick: () => dispatch(toggleLayoutMode()), @@ -188,7 +177,14 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { ...ownProps, ...stateProps, ...dispatchProps, - onActivateTab: (tab) => dispatchProps.onActivateTab(stateProps.eduId, stateProps.isNewProject, tab), + eduId, + onActivateTab: (tab) => { + // react-tabs also fires onSelect for the already active tab + if (tab !== stateProps.activeTabIndex) { + logPageDisplay(eduId, stateProps.isNewProject, tab) + } + dispatchProps.onActivateTab(tab) + }, onActivateCostumesTab: () => dispatchProps.onActivateCostumesTab(), onActivateSoundsTab: () => dispatchProps.onActivateSoundsTab(), } diff --git a/src/containers/menu.jsx b/src/containers/menu.jsx index 052b6b570..a8bd034dd 100644 --- a/src/containers/menu.jsx +++ b/src/containers/menu.jsx @@ -7,6 +7,7 @@ import { MenuComponent } from '../components/menu/menu.jsx' import { MenuTabs } from '../lib/routing' import { games, examples, videos } from '../lib/edu' import { paEvent } from '../lib/piano-analytics/main.js' +import { menuTabTitles } from '../lib/piano-analytics/constants.js' const tabIdToTab = { 0: MenuTabs.edugames, @@ -46,12 +47,24 @@ class Menu extends React.Component { projects: [], videos: videos.map(Menu.mapVideoData), } - - paEvent.pageDisplay({ pages: ['Menu'], pageType: 'Hauptseite' }) } componentDidMount() { this.loadUserProjects() + this.logPageDisplay() + } + + componentDidUpdate(prevProps) { + if (prevProps.tab !== this.props.tab) { + this.logPageDisplay() + } + } + + logPageDisplay() { + paEvent.pageDisplay({ + pages: ['Menu', menuTabTitles[Menu.getTabId(this.props.tab)]], + pageType: 'Hauptseite', + }) } async loadUserProjects() { diff --git a/src/lib/piano-analytics/main.js b/src/lib/piano-analytics/main.js index 092d8acbd..fe1cdeb9a 100644 --- a/src/lib/piano-analytics/main.js +++ b/src/lib/piano-analytics/main.js @@ -1,16 +1,11 @@ import { pianoAnalytics } from 'piano-analytics-js' +import { editorTabNames } from '../../reducers/editor-tab' import { EVENTS, PROPERTIES, DEFAULT_PROPERTY_VALUES, menuTabTitles, } from './constants' -import { editorTabNames } from '../../reducers/editor-tab' - -pianoAnalytics.setConfigurations({ - site: 621455, - collectDomain: 'https://logs1414.xiti.com/', -}) export const paSetConfig = () => { let configurations @@ -18,7 +13,7 @@ export const paSetConfig = () => { if (process.env.BRANCH === 'production') { configurations = { site: 632700, - collectDomain: 'https://ama.wdr.de/', + collectDomain: 'https://lwqvhgk.pa-cd.com/', } } else { configurations = { @@ -29,18 +24,6 @@ export const paSetConfig = () => { pianoAnalytics.setConfigurations(configurations) } -export const guiTypePages = (gameId) => { - if (!gameId) { - return [menuTabTitles[1], 'New Project'] - } - - if (gameId.match(/beispiel(0|0\d{1})?$/gm)) { - return [menuTabTitles[2], `Beispiel ${gameId}`] - } else { - return [menuTabTitles[0], `Lernspiel ${gameId}`] - } -} - const pageLevelKeys = [ PROPERTIES.pageLevel1, PROPERTIES.pageLevel2, @@ -97,14 +80,17 @@ export const paEvent = { clickExit: (params) => clickEvent(EVENTS.clickExit, params), } +// eduId set: Lernspiel or Beispiel; otherwise a new or saved own project export const buildGuiPage = (eduId, isNewProject, activeTab) => { - let pages = [] - if (isNewProject) { + let pages + if (eduId) { + pages = eduId.startsWith('beispiel') + ? [menuTabTitles[2], `Beispiel ${eduId}`] + : [menuTabTitles[0], `Lernspiel ${eduId}`] + } else if (isNewProject) { pages = [menuTabTitles[1], 'New Project'] - } else if (eduId && eduId.match(/beispiel(0|0\d{1})?$/gm)) { - pages = [menuTabTitles[2], `Beispiel ${eduId}`] - } else if (eduId) { - pages = [menuTabTitles[0], `Lernspiel ${eduId}`] + } else { + pages = [menuTabTitles[1], 'Projekt'] } return [...pages, editorTabNames[activeTab || 0]] } diff --git a/src/lib/tracking-hoc.jsx b/src/lib/tracking-hoc.jsx deleted file mode 100644 index 4ebb4335f..000000000 --- a/src/lib/tracking-hoc.jsx +++ /dev/null @@ -1,120 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import zepto from 'zepto' -import { connect } from 'react-redux' - -import { Views } from '../lib/routing' - -const withTracking = (WrappedComponent) => { - if (!process.env.ENABLE_TRACKING) { - return WrappedComponent - } - - const baseGroups = [ - 'WDR', - 'Fernsehen', - 'Kinder', - 'Die Sendung mit der Maus', - 'Programmieren mit der Maus', - ] - - const extendZepto = ($) => { - $.ajaxSetup = (opts) => { - $.ajaxSettings = $.extend($.ajaxSettings, opts) - } - $.getScript = (url, success, error) => { - const script = document.createElement('script') - script.src = url - - const $script = $(script) - $script.bind('load', success) - $script.bind('error', error) - - $('body').append(script) - } - return $ - } - - class WDRTracking extends React.Component { - constructor(props) { - super(props) - this.tracker = null - - if (!window.trackingInserted) { - this.insertTracking() - } - } - - componentDidUpdate(prevProps) { - if ( - this.props.view !== prevProps.view || - this.props.params !== prevProps.params - ) { - this.updateTracking() - } - } - - insertTracking() { - window.trackingInserted = true - window.jQuery = extendZepto(zepto) - window.wdrWebtrekkBuildArr = [] - - const s = document.createElement('script') - s.src = 'https://www.wdr.de/themen/global/tracker/tracker.min.js' - s.async = true - s.addEventListener('load', () => { - this.tracker = window.wdrTrackerObject - this.updateTracking() - }) - document.body.appendChild(s) - } - - updateTracking() { - if (!this.tracker) { - if (!window.wdrTrackerObject) { - console.warn('WDR tracker object not found.') // eslint-disable-line - return - } - this.tracker = window.wdrTrackerObject - } - - window.wdrWebtrekkBuildArr = this.buildContentGroups() - this.tracker.update() - } - - buildContentGroups() { - switch (this.props.view) { - case Views.welcome: - return baseGroups.concat('Startscreen') - case Views.menu: - return baseGroups.concat('Menü') - case Views.edu: - return baseGroups.concat('Lernspiel', this.props.params.eduId) - case Views.project: - return baseGroups.concat('Projekt', this.props.params.projectId) - case Views.content: - return baseGroups.concat('Inhalte', this.props.params.page) - } - } - - render() { - return - } - } - const mapStateToProps = (state) => { - const result = state.router.result || {} - return { - view: result.view, - params: state.router.params, - } - } - - WDRTracking.propTypes = { - view: PropTypes.oneOf(Object.values(Views)), - params: PropTypes.objectOf(PropTypes.string), - } - - return connect(mapStateToProps)(WDRTracking) -} - -export default withTracking diff --git a/webpack.config.js b/webpack.config.js index 16bd4ebf9..c93a04f77 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -184,15 +184,6 @@ module.exports = { filename: 'static/ffmpeg-worker-mp4.[contenthash][ext]', }, }, - { - test: require.resolve('zepto'), - use: [ - { - loader: 'imports-loader', - options: 'this=>window', - }, - ], - }, ], }, optimization: { @@ -206,9 +197,6 @@ module.exports = { plugins: [ new webpack.DefinePlugin({ 'process.env.DEBUG': 'process.env.DEBUG', - 'process.env.ENABLE_TRACKING': JSON.stringify( - Boolean(branch === 'production') - ), 'process.env.BRANCH': JSON.stringify(branch), }), customHtmlPlugin({ diff --git a/yarn.lock b/yarn.lock index 5c3a60983..fc84ae090 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16530,11 +16530,6 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zepto@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/zepto/-/zepto-1.2.0.tgz#e127bd9e66fd846be5eab48c1394882f7c0e4f98" - integrity sha512-C1x6lfvBICFTQIMgbt3JqMOno3VOtkWat/xEakLTOurskYIHPmzJrzd1e8BnmtdDVJlGuk5D+FxyCA8MPmkIyA== - zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"