Browse Source

feat: Added working global SensorLogger

master
Denis Thiessen 5 months ago
parent
commit
005fc11f39
  1. 43
      package-lock.json
  2. 3
      package.json
  3. 75
      src/App.js
  4. 2
      src/components/webpage_container/StudySite.jsx
  5. 5
      src/core/audio/AudioHandler.jsx
  6. 15
      src/core/log/RouteTracker.jsx
  7. 52
      src/core/log/SensorLogger.jsx
  8. 4
      src/index.css
  9. 7
      src/index.js
  10. 20
      src/pages/TestPage.jsx

43
package-lock.json

@ -21,7 +21,8 @@
"react-i18next": "^14.1.1", "react-i18next": "^14.1.1",
"react-router-dom": "^6.23.0", "react-router-dom": "^6.23.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
"web-vitals": "^2.1.4",
"zustand": "^4.5.2"
} }
}, },
"node_modules/@adobe/css-tools": { "node_modules/@adobe/css-tools": {
@ -8843,6 +8844,11 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
},
"node_modules/fs-extra": { "node_modules/fs-extra": {
"version": "10.1.0", "version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
@ -17496,6 +17502,14 @@
"requires-port": "^1.0.0" "requires-port": "^1.0.0"
} }
}, },
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -18589,6 +18603,33 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
},
"node_modules/zustand": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz",
"integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==",
"dependencies": {
"use-sync-external-store": "1.2.0"
},
"engines": {
"node": ">=12.7.0"
},
"peerDependencies": {
"@types/react": ">=16.8",
"immer": ">=9.0.6",
"react": ">=16.8"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
}
}
} }
} }
} }

3
package.json

@ -16,7 +16,8 @@
"react-i18next": "^14.1.1", "react-i18next": "^14.1.1",
"react-router-dom": "^6.23.0", "react-router-dom": "^6.23.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
"web-vitals": "^2.1.4",
"zustand": "^4.5.2"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",

75
src/App.js

@ -1,58 +1,30 @@
import React, {useEffect} from "react";
import React, { useEffect } from "react";
import { Route, Routes } from "react-router-dom"; import { Route, Routes } from "react-router-dom";
import { GeistProvider, CssBaseline } from '@geist-ui/core' import { GeistProvider, CssBaseline } from '@geist-ui/core'
import { SensorLogger } from "./core/log/SensorLogger";
import h337 from "heatmap.js";
import RouteTracker from "./core/log/RouteTracker";
import { createStore } from 'zustand/vanilla'
import { persist, createJSONStorage } from 'zustand/middleware'
const NoPageFound = React.lazy(() => import("./pages/NoPageFound")); const NoPageFound = React.lazy(() => import("./pages/NoPageFound"));
const TestPage = React.lazy(() => import("./pages/TestPage")); const TestPage = React.lazy(() => import("./pages/TestPage"));
const TestQuestionnaire = React.lazy(() => import("./pages/TestQuestionnaire")); const TestQuestionnaire = React.lazy(() => import("./pages/TestQuestionnaire"));
const mouseMode = true;
function App() {
useEffect(() => {
/*var heatmapInstance = h337.create({
container: document.body,
radius: 20
});
heatmapInstance.setDataMax(9999);
export const sensorLogState = createStore(
persist(
() => ({
sensorLog: {mouseLog: [], clickedElements: [], visitedSites: [], playedSonifications: []}
}),
{
name: 'sensor-storage', // name of the item in the storage (must be unique)
storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
},
),
)
var addData;
export const { getState, setState, subscribe, getInitialState } = sensorLogState;
if(mouseMode) {
const mouseDataFunc = function(ev) {
heatmapInstance.addData({
x: ev.layerX,
y: ev.layerY,
value: 1
});
};
addData = mouseDataFunc;
} else {
const touchDataFunc = function(ev) {
heatmapInstance.addData({
x: ev.layerX,
y: ev.layerY,
value: 1
});
};
addData = touchDataFunc;
}
if(mouseMode) {
document.body.addEventListener("mousemove", addData, false);
}else {
document.body.addEventListener("touchmove", addData, false);
document.body.addEventListener("touchstart", addData, false);
document.body.addEventListener("touchend", addData, false);
}*/
})
function App() {
// TODO FIX... // TODO FIX...
/* /*
@ -70,18 +42,15 @@ function App() {
wait(200); wait(200);
var logRoutePath = (path) => {
SensorLogger.pushToVisitLog(path);
};
return ( return (
<GeistProvider> <GeistProvider>
<React.Suspense fallback="loading"> <React.Suspense fallback="loading">
<RouteTracker />
<Routes> <Routes>
<Route path="/" action={logRoutePath("/")} element={<React.Suspense fallback={<>...</>}><TestPage /></React.Suspense>} />
<Route path="/info" action={logRoutePath("/info")} element={<React.Suspense fallback={<>...</>}><TestPage /></React.Suspense>} />
<Route path="/questionnaire/:id" action={logRoutePath("/questionnaire")} element={<React.Suspense fallback={<>...</>}><TestQuestionnaire /></React.Suspense>} />
<Route path="*" action={logRoutePath("*")} element={<React.Suspense fallback={<>...</>}><NoPageFound /></React.Suspense>} />
<Route path="/" element={<React.Suspense fallback={<>...</>}><TestPage /></React.Suspense>} />
<Route path="/info" element={<React.Suspense fallback={<>...</>}><TestPage /></React.Suspense>} />
<Route path="/questionnaire/:id" element={<React.Suspense fallback={<>...</>}><TestQuestionnaire /></React.Suspense>} />
<Route path="*" element={<React.Suspense fallback={<>...</>}><NoPageFound /></React.Suspense>} />
</Routes> </Routes>
</React.Suspense> </React.Suspense>
<CssBaseline /> <CssBaseline />

2
src/components/webpage_container/StudySite.jsx

@ -18,7 +18,7 @@ export function StudySite(props) {
heatmapInstance.addData({ heatmapInstance.addData({
x: ev.layerX, x: ev.layerX,
y: ev.layerY, y: ev.layerY,
value: 1
value: 1,
}); });
}; };

5
src/core/audio/AudioHandler.jsx

@ -1,22 +1,27 @@
import {Component} from 'react'; import {Component} from 'react';
import * as Tone from "tone"; import * as Tone from "tone";
import { pushToSonificationLog } from '../log/SensorLogger';
export default class AudioHandler extends Component { export default class AudioHandler extends Component {
playTabEarconSonification() { playTabEarconSonification() {
pushToSonificationLog("tab_earcon");
} }
playTabModelSonification() { playTabModelSonification() {
pushToSonificationLog("tab_model");
} }
playDetailEarconSonification() { playDetailEarconSonification() {
pushToSonificationLog("detail_earcon");
} }
playDetailModelSonification() { playDetailModelSonification() {
pushToSonificationLog("detail_model");
} }
setRotation(angle) { setRotation(angle) {

15
src/core/log/RouteTracker.jsx

@ -0,0 +1,15 @@
import React, { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { pushToVisitLog } from './SensorLogger';
const RouteTracker = () => {
const location = useLocation();
useEffect(() => {
pushToVisitLog({ pathname: location.pathname, timestamp: new Date() });
}, [location.pathname]);
return null;
};
export default RouteTracker;

52
src/core/log/SensorLogger.jsx

@ -1,33 +1,47 @@
import {Component} from 'react';
import { getState, setState } from '../../App';
export class SensorLogger extends Component {
static sensorLog = {mouseLog: [], orientationLog: [], clickedElements: [], visitedSites: [], playedSonifications: []};
static pushToMouseLog(logElement) {
this.sensorLog.mouseLog.push(logElement);
export function pushToMouseLog(logElement) {
const sensorLog = getState().sensorLog;
sensorLog.mouseLog.push(logElement);
setState({ sensorLog: sensorLog });
} }
static pushToOrientationLog(logElement) {
this.sensorLog.orientationLog.push(logElement);
export function pushToClickLog(logElement) {
// Filter out dummy body objects
if(logElement.target.localName === "body") {
return;
} }
static pushToClickLog(logElement) {
this.sensorLog.clickedElements.push(logElement);
const sensorLog = getState().sensorLog;
sensorLog.clickedElements.push(logElement);
setState({ sensorLog: sensorLog });
} }
static pushToVisitLog(logElement) {
this.sensorLog.visitedSites.push(logElement);
export function pushToVisitLog(logElement) {
const sensorLog = getState().sensorLog;
// Filter out double visit pushes...
const visitedSitesAmount = sensorLog.visitedSites.length;
if(visitedSitesAmount > 0) {
const lastVisitedElement = sensorLog.visitedSites[visitedSitesAmount - 1];
if(lastVisitedElement.path === logElement.path && logElement.timestamp - lastVisitedElement.timestamp < 500) {
return;
}
sensorLog.visitedSites.push(logElement);
setState({ sensorLog: sensorLog });
}
} }
static pushToSonificationLog(logElement) {
this.sensorLog.playedSonifications.push(logElement);
export function pushToSonificationLog(logElement) {
const sensorLog = getState().sensorLog;
sensorLog.playedSonifications.push(logElement);
setState({ sensorLog: sensorLog });
} }
static getSensorLog() {
return this.sensorLog;
export function getSensorLog() {
return getState().sensorLog;
} }
static resetSensorLog() {
this.sensorLog = {mouseLog: [], orientationLog: [], clickedElements: [], visitedSites: [], playedSonifications: []};
export function resetSensorLog() {
setState({ sensorLog: {mouseLog: [], clickedElements: [], visitedSites: [], playedSonifications: []}});
} }
}

4
src/index.css

@ -17,3 +17,7 @@ code {
.heatmap-canvas { .heatmap-canvas {
pointer-events: none; pointer-events: none;
} }
canvas {
opacity: 0;
}

7
src/index.js

@ -5,10 +5,11 @@ import App from './App';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
import "./core/i18n/i18n"; import "./core/i18n/i18n";
import { SensorLogger } from './core/log/SensorLogger';
import { pushToClickLog } from './core/log/SensorLogger';
document.onclick = function(event) {SensorLogger.pushToClickLog(event);};
window.addEventListener("deviceorientation", function(event) {SensorLogger.pushToOrientationLog(event);});
document.onclick = function(event) {
pushToClickLog(event);
};
const root = ReactDOM.createRoot(document.getElementById('root')); const root = ReactDOM.createRoot(document.getElementById('root'));
root.render( root.render(

20
src/pages/TestPage.jsx

@ -1,18 +1,16 @@
import React from "react"; import React from "react";
import { getTranslation } from "../core/i18n/I18NHandler"; import { getTranslation } from "../core/i18n/I18NHandler";
import WebpageBanner from "../components/webpage_container/WebpageBanner"; import WebpageBanner from "../components/webpage_container/WebpageBanner";
import { SensorLogger } from "../core/log/SensorLogger";
import { StudySite, getHeatmapData } from "../components/webpage_container/StudySite"; import { StudySite, getHeatmapData } from "../components/webpage_container/StudySite";
import { pushToMouseLog, getSensorLog } from "../core/log/SensorLogger";
export default class TestPage extends React.Component {
render() {
var clickFunction = function(){
SensorLogger.pushToMouseLog(getHeatmapData());
console.log(SensorLogger.getSensorLog());
function TestPage() {
var clickFunction = function() {
pushToMouseLog(getHeatmapData());
console.log(getSensorLog());
alert(JSON.stringify(getSensorLog()));
}; };
return (<StudySite><WebpageBanner translationKey="hello_world" /><p onMouseOver={clickFunction}>{getTranslation("hello_world")}</p><button>Teeest</button></StudySite>);
}
return (<StudySite><WebpageBanner translationKey="hello_world" /><p onClick={clickFunction}>{getTranslation("hello_world")}</p><button>Teeest</button></StudySite>);
} }
export default TestPage;
Loading…
Cancel
Save