Browse Source

feat: Added working global SensorLogger

master
Denis Thiessen 6 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. 56
      src/core/log/SensorLogger.jsx
  8. 4
      src/index.css
  9. 7
      src/index.js
  10. 22
      src/pages/TestPage.jsx

43
package-lock.json

@ -21,7 +21,8 @@
"react-i18next": "^14.1.1",
"react-router-dom": "^6.23.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
"web-vitals": "^2.1.4",
"zustand": "^4.5.2"
}
},
"node_modules/@adobe/css-tools": {
@ -8843,6 +8844,11 @@
"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": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
@ -17496,6 +17502,14 @@
"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": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -18589,6 +18603,33 @@
"funding": {
"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-router-dom": "^6.23.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
"web-vitals": "^2.1.4",
"zustand": "^4.5.2"
},
"scripts": {
"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 { 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 TestPage = React.lazy(() => import("./pages/TestPage"));
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...
/*
@ -70,18 +42,15 @@ function App() {
wait(200);
var logRoutePath = (path) => {
SensorLogger.pushToVisitLog(path);
};
return (
<GeistProvider>
<React.Suspense fallback="loading">
<RouteTracker />
<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>
</React.Suspense>
<CssBaseline />

2
src/components/webpage_container/StudySite.jsx

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

5
src/core/audio/AudioHandler.jsx

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

56
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

@ -16,4 +16,8 @@ code {
.heatmap-canvas {
pointer-events: none;
}
canvas {
opacity: 0;
}

7
src/index.js

@ -5,10 +5,11 @@ import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from "react-router-dom";
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'));
root.render(

22
src/pages/TestPage.jsx

@ -1,18 +1,16 @@
import React from "react";
import { getTranslation } from "../core/i18n/I18NHandler";
import WebpageBanner from "../components/webpage_container/WebpageBanner";
import { SensorLogger } from "../core/log/SensorLogger";
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