UNPKG

@area2-ai/a2-react-keystroke-package

Version:

This package enables secure and efficient collection of user keystroke data through hooks, designed for both desktop and mobile platforms. The collected data is processed by **area2** servers to generate a neuroprofile, which reflects key cognitive, behav

1,366 lines (1,347 loc) 53.5 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var React = require('react'); var React__default = _interopDefault(React); var a2NodeKeystrokePackage = require('@area2-ai/a2-node-keystroke-package'); var axios = _interopDefault(require('axios')); var reactDeviceDetect = require('react-device-detect'); function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _objectDestructuringEmpty(t) { if (null == t) throw new TypeError("Cannot destructure " + t); } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } return t; } function _regeneratorRuntime() { _regeneratorRuntime = function () { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function (t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == typeof h && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function (t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(typeof e + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function (e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function () { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function (e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function (t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function (t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function (t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function (t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw Error("illegal catch attempt"); }, delegateYield: function (e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; } var Area2Context = /*#__PURE__*/React.createContext({}); var a2API = /*#__PURE__*/axios.create({ baseURL: "https://default.demo.area2-ai.com", headers: { "Content-Type": 'application/json' } }); var a2Actions = { "default": 'default', compare: 'a2_compare', summary: 'a2_summary', trends: 'a2_trends' }; var _excluded = ["keyArea", "keyTypes", "pressTimes", "qualityCheck", "releaseTimes", "sessionID", "startUnixTime", "textStructure", "timeZone"], _excluded2 = ["autocorrectLengths", "autocorrectTimes", "keyArea", "keyTypes", "predictionLengths", "predictionTimes", "pressTimes", "releaseTimes", "sessionID", "startUnixTime", "timeZone"]; /** * Formats keystroke data based on the platform type. * * @param platform - The platform type, either 'Desktop' or 'Mobile'. * @param typingData - The keystroke data collection, which can be either IKeystrokeCollection or IMobileKeystrokeCollection. * @returns The formatted keystroke data. */ var formatKeystrokeData = function formatKeystrokeData(platform, typingData) { var formattedBody; if (platform === 'Desktop') { var _keyArea = typingData.keyArea, _keyTypes = typingData.keyTypes, _pressTimes = typingData.pressTimes, qualityCheck = typingData.qualityCheck, _releaseTimes = typingData.releaseTimes, _sessionID = typingData.sessionID, _startUnixTime = typingData.startUnixTime, textStructure = typingData.textStructure, _timeZone = typingData.timeZone, _rest = _objectWithoutPropertiesLoose(typingData, _excluded); formattedBody = _extends({ 'key_area': _keyArea, 'key_type': _keyTypes, 'press_times': _pressTimes, 'quality_check': qualityCheck, 'release_times': _releaseTimes, 'session_id': _sessionID, 'startunixtime': _startUnixTime, 'text_structure': textStructure, 'timezone': _timeZone }, _rest); return formattedBody; } var autocorrectLengths = typingData.autocorrectLengths, autocorrectTimes = typingData.autocorrectTimes, keyArea = typingData.keyArea, keyTypes = typingData.keyTypes, predictionLengths = typingData.predictionLengths, predictionTimes = typingData.predictionTimes, pressTimes = typingData.pressTimes, releaseTimes = typingData.releaseTimes, sessionID = typingData.sessionID, startUnixTime = typingData.startUnixTime, timeZone = typingData.timeZone, rest = _objectWithoutPropertiesLoose(typingData, _excluded2); formattedBody = _extends({ 'autocorrect_lengths': autocorrectLengths, 'autocorrect_times': autocorrectTimes, 'prediction_lengths': predictionLengths, 'prediction_times': predictionTimes, 'key_area': keyArea, 'key_type': keyTypes, 'press_times': pressTimes, 'release_times': releaseTimes, 'session_id': sessionID, 'startunixtime': startUnixTime, 'timezone': timeZone }, rest); return formattedBody; }; /** * Asynchronously generates a reduced neuroprofile for a user based on their typing data. * * @param {string} userID - The unique identifier of the user for whom the neuroprofile is generated. * @param {string} token - A token used for authentication or authorization purposes. * @param {IKeystrokeCollection | IMobileKeystrokeCollection} typingData - The user's typing data, which can be either desktop or mobile keystroke collections. * @param {'Desktop' | 'Mobile'} platform - The platform type indicating whether the typing data is from a desktop or mobile device. * @param {'default' | 'compare' | 'summary' | 'trends'} a2Action - Action that determines the type of response to be received from the server. * @returns {Promise<NeuroprofileResponse>} A promise that resolves to a NeuroprofileResponse containing the neuroprofile data. */ var getReducedNeuroprofile = /*#__PURE__*/function () { var _ref = /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(userID, token, typingData, platform, a2Action) { var formattedBody, dataToSend, _yield$a2API$post, data, error, message, results, _err$response, err; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: formattedBody = formatKeystrokeData(platform, typingData); dataToSend = { 'a2_actions': [a2Actions[a2Action]], 'keystroke_data': _extends({}, formattedBody, { 'user_id': userID }) }; _context.prev = 2; _context.next = 5; return a2API.post('/get_reduced_neuroprofile', dataToSend, { headers: { Authorization: "Bearer " + token } }); case 5: _yield$a2API$post = _context.sent; data = _yield$a2API$post.data; if (!(data.status === 'error')) { _context.next = 10; break; } error = data.error, message = data.message; return _context.abrupt("return", { ok: false, error: error, message: message }); case 10: results = data.results; if (!(results != null && results["default"])) { _context.next = 13; break; } return _context.abrupt("return", { ok: true, neuroprofile: results["default"] }); case 13: if (!(results != null && results.a2_compare)) { _context.next = 15; break; } return _context.abrupt("return", { ok: true, neuroprofile: results.a2_compare }); case 15: if (!(results != null && results.a2_summary)) { _context.next = 17; break; } return _context.abrupt("return", { ok: true, neuroprofile: results.a2_summary }); case 17: return _context.abrupt("return", { ok: true, neuroprofile: results.a2_trends }); case 20: _context.prev = 20; _context.t0 = _context["catch"](2); err = _context.t0; console.error('Error when connecting to api: ', err.message); return _context.abrupt("return", { ok: false, message: "Error when connecting to api, reason: " + ((_err$response = err.response) == null ? void 0 : _err$response.data.message), error: err.message }); case 25: case "end": return _context.stop(); } }, _callee, null, [[2, 20]]); })); return function getReducedNeuroprofile(_x, _x2, _x3, _x4, _x5) { return _ref.apply(this, arguments); }; }(); var a2DevAccessAPI = /*#__PURE__*/axios.create({ baseURL: "https://developer.demo.area2-ai.com", headers: { "Content-Type": 'application/json' } }); /** * Validates the developer access key. * @param {string} devAccessKey - The developer access key to be validated. * @returns {Promise<CheckAccessKeyResponse>} - A promise that resolves to the response indicating whether the access key is valid. */ var validateDevAccessKey = /*#__PURE__*/function () { var _ref = /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(devAccessKey) { var _yield$a2DevAccessAPI, data, error, _err$response, err; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _context.prev = 0; _context.next = 3; return a2DevAccessAPI.get('/check-client-access-key', { headers: { Authorization: "Bearer " + devAccessKey } }); case 3: _yield$a2DevAccessAPI = _context.sent; data = _yield$a2DevAccessAPI.data; if (data.success) { _context.next = 8; break; } error = data.error; return _context.abrupt("return", { ok: false, error: error }); case 8: return _context.abrupt("return", { ok: data.success }); case 11: _context.prev = 11; _context.t0 = _context["catch"](0); err = _context.t0; console.error('Error: ', err.message); return _context.abrupt("return", { ok: false, error: ((_err$response = err.response) == null ? void 0 : _err$response.data.error) || err.message }); case 16: case "end": return _context.stop(); } }, _callee, null, [[0, 11]]); })); return function validateDevAccessKey(_x) { return _ref.apply(this, arguments); }; }(); var AREA2_INITIAL_STATE = { canAccess: false, desktopTextValue: '', androidTextValue: '', iOSTextValue: '' }; var Area2Provider = function Area2Provider(_ref) { var children = _ref.children, config = _ref.config; var _useReducer = React.useReducer(area2Reducer, AREA2_INITIAL_STATE), state = _useReducer[0], dispatch = _useReducer[1]; var keystrokeManagerRef = React.useRef(); var androidKeystrokeManagerRef = React.useRef(); var iosKeystrokeManagerRef = React.useRef(); var getKeystrokeManager = function getKeystrokeManager() { return keystrokeManagerRef.current; }; var getAndroidKeystrokeManager = function getAndroidKeystrokeManager() { return androidKeystrokeManagerRef.current; }; var getIosKeystrokeManager = function getIosKeystrokeManager() { return iosKeystrokeManagerRef.current; }; var validateAccessToken = React.useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee() { var apiKey, response; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: apiKey = config.apiKey; if (!(apiKey.length === 0)) { _context.next = 4; break; } dispatch({ type: '[A2 Auth] - Deny Access' }); return _context.abrupt("return"); case 4: _context.next = 6; return validateDevAccessKey(apiKey); case 6: response = _context.sent; if (response.ok) { _context.next = 11; break; } dispatch({ type: '[A2 Auth] - Deny Access' }); console.warn("Error validating access key: " + response.error); return _context.abrupt("return"); case 11: dispatch({ type: '[A2 Auth] - Allow Access' }); case 12: case "end": return _context.stop(); } }, _callee); })), [config]); var setDesktopTextValue = React.useCallback(function (value) { dispatch({ type: '[A2 Desktop] - Update text', payload: { newValue: value } }); }, []); var setIOSTextValue = React.useCallback(function (value) { dispatch({ type: '[A2 iOS] - Update text', payload: { newValue: value } }); }, []); var setAndroidTextValue = React.useCallback(function (value) { dispatch({ type: '[A2 Android] - Update text', payload: { newValue: value } }); }, []); React.useEffect(function () { validateAccessToken(); }, [validateAccessToken]); React.useEffect(function () { keystrokeManagerRef.current = new a2NodeKeystrokePackage.KeystrokeManager(); androidKeystrokeManagerRef.current = new a2NodeKeystrokePackage.AndroidKeystrokeManager(); iosKeystrokeManagerRef.current = new a2NodeKeystrokePackage.IosKeystrokeManager(); }, []); return React__default.createElement(Area2Context.Provider, { value: _extends({}, state, { //Methods getKeystrokeManager: getKeystrokeManager, getAndroidKeystrokeManager: getAndroidKeystrokeManager, getIosKeystrokeManager: getIosKeystrokeManager, setDesktopTextValue: setDesktopTextValue, setIOSTextValue: setIOSTextValue, setAndroidTextValue: setAndroidTextValue }) }, children); }; var area2Reducer = function area2Reducer(state, action) { switch (action.type) { case '[A2 Auth] - Deny Access': return _extends({}, state, { canAccess: false }); case '[A2 Auth] - Allow Access': return _extends({}, state, { canAccess: true }); case '[A2 Desktop] - Update text': return _extends({}, state, { desktopTextValue: action.payload.newValue }); case '[A2 iOS] - Update text': return _extends({}, state, { iOSTextValue: action.payload.newValue }); case '[A2 Android] - Update text': return _extends({}, state, { androidTextValue: action.payload.newValue }); default: return state; } }; var getBrowserInfo = function getBrowserInfo() { return reactDeviceDetect.browserName; }; var getOsInfo = function getOsInfo() { return reactDeviceDetect.osName; }; /** * Keystroke for android mobile browser * @returns {Object} - An object containing the text input, input change handler, keydown handler, keyup handler, paste handler, before input handler, key input handler, and getNeuroprofile function. */ var useMobileKeystrokeAndroid = function useMobileKeystrokeAndroid() { var _useContext = React.useContext(Area2Context), canAccess = _useContext.canAccess, getAndroidKeystrokeManager = _useContext.getAndroidKeystrokeManager, androidTextValue = _useContext.androidTextValue, setAndroidTextValue = _useContext.setAndroidTextValue; var _useState = React.useState(false), isSending = _useState[0], setIsSending = _useState[1]; var temporalTypingDataRef = React.useRef(null); var userTokenRef = React.useRef(''); var userUIDRef = React.useRef(''); /** * Handles the typing session when a submission is already in progress. * This function clears the desktop text value, updates the user credentials, * and processes the current typing session data by ending the session and resetting it. * * @param {string} userUID - The unique identifier of the user. * @param {string} userToken - A token used for authentication or authorization purposes. */ var handleTypingSessionWhileSending = function handleTypingSessionWhileSending(userUID, userToken) { setAndroidTextValue(""); userTokenRef.current = userToken; userUIDRef.current = userUID; if (temporalTypingDataRef.current === null) { temporalTypingDataRef.current = getAndroidKeystrokeManager().endTypingSession(); temporalTypingDataRef.current.appContext = getOsInfo() + " - " + getBrowserInfo(); } getAndroidKeystrokeManager().resetTypingData(); }; /** * This function sends the first typing session that was detected while waiting for a reply to the previous call to the server. * Its purpose is to keep the typing metrics on the server up to date. */ var updateTypingSession = function updateTypingSession() { if (!temporalTypingDataRef.current) { return; } getReducedNeuroprofile(userUIDRef.current, userTokenRef.current, temporalTypingDataRef.current, 'Mobile', "default"); temporalTypingDataRef.current = null; }; React.useEffect(function () { if (!isSending) { updateTypingSession(); } }, [isSending]); /** * Handles the before input event. * @param {string} currentValue - The current value of the input before the input event. */ var handleOnBeforeInput = React.useCallback(function (currentValue) { if (!canAccess) { return; } getAndroidKeystrokeManager().processBeforeInput(currentValue, androidTextValue); }, [canAccess, androidTextValue]); /** * Handles the paste event. * @param {ClipboardEvent<HTMLInputElement>} event - The paste event. */ var handlePaste = React.useCallback(function (event) { if (!canAccess) { return; } var pastedText = event.clipboardData.getData("text"); getAndroidKeystrokeManager().processPaste(pastedText); }, [canAccess]); /** * Handles the input change event * @param {ChangeEvent<HTMLInputElement>} event - The input change event */ var handleInputChange = function handleInputChange(event) { var newValue = event.target.value; if (!canAccess) { return; } setAndroidTextValue(newValue); }; /** * Handles the submission of typing data and retrieves the neuroprofile. * @param {string} userUID - The unique identifier of the user for whom the neuroprofile is generated. * @param {string} userToken - A token used for authentication or authorization purposes. * @param {'default' | 'compare' | 'summary' | 'trends'} [action] - Optional action that determines the type of response to be received from the server. * @returns {Promise<IKeystrokeResult | undefined>} - A promise that resolves to the keystroke result or undefined if the submission is skipped. */ var handleSubmit = React.useCallback(/*#__PURE__*/function () { var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(userUID, userToken, action) { var typingData, neuroProfileResp; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: if (!isSending) { _context.next = 3; break; } handleTypingSessionWhileSending(userUID, userToken); return _context.abrupt("return"); case 3: setAndroidTextValue(""); setIsSending(true); typingData = getAndroidKeystrokeManager().endTypingSession(); getAndroidKeystrokeManager().resetTypingData(); if (typingData.startUnixTime) { _context.next = 10; break; } setIsSending(false); return _context.abrupt("return", { error: 'Empty typing data', message: "Empty typing data for session: " + typingData.sessionID + ". Skipping..." }); case 10: typingData.appContext = getOsInfo() + " - " + getBrowserInfo(); if (!(!userToken || !userUID)) { _context.next = 15; break; } getAndroidKeystrokeManager().resetTypingData(); setIsSending(false); return _context.abrupt("return", { error: 'User credentials not found.', message: 'Skipping save...' }); case 15: _context.next = 17; return getReducedNeuroprofile(userUID, userToken, typingData, 'Mobile', action != null ? action : 'default'); case 17: neuroProfileResp = _context.sent; setIsSending(false); if (neuroProfileResp.ok) { _context.next = 21; break; } return _context.abrupt("return", { error: neuroProfileResp.error, message: neuroProfileResp.message }); case 21: return _context.abrupt("return", { data: neuroProfileResp.neuroprofile }); case 22: case "end": return _context.stop(); } }, _callee); })); return function (_x, _x2, _x3) { return _ref.apply(this, arguments); }; }(), [isSending]); /** * Handles the key input event. * @param {string} inputContent - The content of the key input. */ var handleKeyInput = React.useCallback(function (inputContent) { if (!canAccess) { return; } getAndroidKeystrokeManager().processKeyInput(inputContent); }, [canAccess]); /** * Handles the keydown event. * @param {HTMLInputElement} target - The target input element. */ var handleKeydown = React.useCallback(/*#__PURE__*/function () { var _ref2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2(target) { return _regeneratorRuntime().wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: if (canAccess) { _context2.next = 2; break; } return _context2.abrupt("return"); case 2: getAndroidKeystrokeManager().processKeydown(target); case 3: case "end": return _context2.stop(); } }, _callee2); })); return function (_x4) { return _ref2.apply(this, arguments); }; }(), [canAccess]); /** * Handles the keyup event. */ var handleKeyup = React.useCallback(function () { if (!canAccess) { return; } getAndroidKeystrokeManager().processKeyup(); }, [canAccess]); return { value: androidTextValue, handleInputChange: handleInputChange, handleKeydown: handleKeydown, handleKeyup: handleKeyup, handlePaste: handlePaste, handleKeyInput: handleKeyInput, handleOnBeforeInput: handleOnBeforeInput, getNeuroprofile: handleSubmit }; }; /** * Keystroke for ios mobile browser * @returns {Object} - An object containing the text input, input change handler, keydown handler, keyup handler, paste handler, before input handler, typing session status, and getNeuroprofile function. */ var useMobileKeystrokeIOS = function useMobileKeystrokeIOS() { var _useContext = React.useContext(Area2Context), canAccess = _useContext.canAccess, getIosKeystrokeManager = _useContext.getIosKeystrokeManager, iOSTextValue = _useContext.iOSTextValue, setIOSTextValue = _useContext.setIOSTextValue; var _useState = React.useState(false), isSending = _useState[0], setIsSending = _useState[1]; var temporalTypingDataRef = React.useRef(null); var userTokenRef = React.useRef(''); var userUIDRef = React.useRef(''); /** * Handles the typing session when a submission is already in progress. * This function clears the desktop text value, updates the user credentials, * and processes the current typing session data by ending the session and resetting it. * * @param {string} userUID - The unique identifier of the user. * @param {string} userToken - A token used for authentication or authorization purposes. */ var handleTypingSessionWhileSending = function handleTypingSessionWhileSending(userUID, userToken) { setIOSTextValue(""); userTokenRef.current = userToken; userUIDRef.current = userUID; if (temporalTypingDataRef.current === null) { temporalTypingDataRef.current = getIosKeystrokeManager().endTypingSession(); temporalTypingDataRef.current.appContext = getOsInfo() + " - " + getBrowserInfo(); } getIosKeystrokeManager().resetTypingData(); }; /** * This function sends the first typing session that was detected while waiting for a reply to the previous call to the server. * Its purpose is to keep the typing metrics on the server up to date. */ var updateTypingSession = function updateTypingSession() { if (!temporalTypingDataRef.current) { return; } getReducedNeuroprofile(userUIDRef.current, userTokenRef.current, temporalTypingDataRef.current, 'Mobile', "default"); temporalTypingDataRef.current = null; }; React.useEffect(function () { if (!isSending) { updateTypingSession(); } }, [isSending]); /** * Handles the before input event. * @param {number} value - The length of the content before the input event. */ var handleOnBeforeInput = React.useCallback(function (value) { if (!canAccess) { return; } getIosKeystrokeManager().setPrevContentLength = value; }, [canAccess]); /** * Handles the paste event. * @param {ClipboardEvent<HTMLInputElement>} event - The paste event. */ var handlePaste = React.useCallback(function (event) { if (!canAccess) { return; } var pastedText = event.clipboardData.getData("text"); getIosKeystrokeManager().processPaste(pastedText); }, [canAccess]); var processAutocorrection = function processAutocorrection() { if (!getIosKeystrokeManager()) { return; } getIosKeystrokeManager().processAutocorrection(iOSTextValue); }; /** * Checks for text prediction and processes it. * @param {string} newValue - The new value of the text input. */ var checkForPrediction = React.useCallback(function (newValue) { var textSnapshot = iOSTextValue; // Before it changes if (!canAccess) { return; } getIosKeystrokeManager().processPrediction(newValue, textSnapshot); }, [canAccess, iOSTextValue]); /** * Handles the input change event * @param {ChangeEvent<HTMLInputElement>} event - The input change event */ var handleInputChange = function handleInputChange(event) { var newValue = event.target.value; if (!canAccess) { return; } checkForPrediction(newValue); setIOSTextValue(newValue); }; /** * Handles the submission of typing data and retrieves the neuroprofile. * @param {string} userUID - The unique identifier of the user for whom the neuroprofile is generated. * @param {string} userToken - A token used for authentication or authorization purposes. * @param {'default' | 'compare' | 'summary' | 'trends'} [action] - Optional action that determines the type of response to be received from the server. * @returns {Promise<IKeystrokeResult | undefined>} - A promise that resolves to the keystroke result or undefined if the submission is skipped. */ var handleSubmit = React.useCallback(/*#__PURE__*/function () { var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(userUID, userToken, action) { var typingData, neuroProfileResp; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: if (!isSending) { _context.next = 3; break; } handleTypingSessionWhileSending(userUID, userToken); return _context.abrupt("return"); case 3: setIOSTextValue(""); setIsSending(true); typingData = getIosKeystrokeManager().endTypingSession(); getIosKeystrokeManager().resetTypingData(); if (typingData.startUnixTime) { _context.next = 10; break; } setIsSending(false); return _context.abrupt("return", { error: 'Empty typing data', message: "Empty typing data for session: " + typingData.sessionID + ". Skipping..." }); case 10: typingData.appContext = getOsInfo() + " - " + getBrowserInfo(); if (!(!userToken || !userUID)) { _context.next = 15; break; } getIosKeystrokeManager().resetTypingData(); setIsSending(false); return _context.abrupt("return", { error: 'User credentials not found.', message: 'Skipping save...' }); case 15: _context.next = 17; return getReducedNeuroprofile(userUID, userToken, typingData, 'Mobile', action != null ? action : 'default'); case 17: neuroProfileResp = _context.sent; setIsSending(false); if (neuroProfileResp.ok) { _context.next = 21; break; } return _context.abrupt("return", { error: neuroProfileResp.error, message: neuroProfileResp.message }); case 21: return _context.abrupt("return", { data: neuroProfileResp.neuroprofile }); case 22: case "end": return _context.stop(); } }, _callee); })); return function (_x, _x2, _x3) { return _ref.apply(this, arguments); }; }(), [isSending]); /** * Handles the keydown event. * @param {string} keyPressed - The key that was pressed. * @param {HTMLInputElement} target - The target input element. */ var handleKeydown = React.useCallback(function (keyPressed, target) { if (!canAccess) { return; } getIosKeystrokeManager().processKeydown(keyPressed, target); }, [canAccess]); /** * Handles the keyup event. * @param {string} key - The key that was released. */ var handleKeyup = React.useCallback(function (keyPressed) { if (!canAccess) { return; } getIosKeystrokeManager().processKeyup(keyPressed); }, [canAccess]); React.useEffect(function () { processAutocorrection(); }, [iOSTextValue]); return { value: iOSTextValue, handleInputChange: handleInputChange, handleKeydown: handleKeydown, handleKeyup: handleKeyup, handlePaste: handlePaste, handleOnBeforeInput: handleOnBeforeInput, getNeuroprofile: handleSubmit }; }; /** * Keystroke for desktop browsers * @returns {Object} - An object containing the text input, input change handler, keydown handler, keyup handler, typing session status, and getNeuroprofile function. */ var useKeystroke = function useKeystroke() { var _useContext = React.useContext(Area2Context), canAccess = _useContext.canAccess, getKeystrokeManager = _useContext.getKeystrokeManager, desktopTextValue = _useContext.desktopTextValue, setDesktopTextValue = _useContext.setDesktopTextValue; var _useState = React.useState(false), isSending = _useState[0], setIsSending = _useState[1]; var temporalTypingDataRef = React.useRef(null); var userTokenRef = React.useRef(''); var userUIDRef = React.useRef(''); var getIsTypingSessionActive = function getIsTypingSessionActive() { return getKeystrokeManager().getIsTypingSessionActive; }; var promptAccessWarning = function promptAccessWarning() { console.warn('You are not authorized to use the hook.'); console.log('Make sure to provide a valid access key.'); }; /** * Handles the typing session when a submission is already in progress. * This function clears the desktop text value, updates the user credentials, * and processes the current typing session data by ending the session and resetting it. * * @param {string} userUID - The unique identifier of the user. * @param {string} userToken - A token used for authentication or authorization purposes. */ var handleTypingSessionWhileSending = function handleTypingSessionWhileSending(userUID, userToken) { setDesktopTextValue(""); userTokenRef.current = userToken; userUIDRef.current = userUID; if (temporalTypingDataRef.current === null) { temporalTypingDataRef.current = getKeystrokeManager().endTypingSession(); temporalTypingDataRef.current.appContext = getOsInfo() + " - " + getBrowserInfo(); } getKeystrokeManager().resetTypingData(); }; /** * This function sends the first typing session that was detected while waiting for a reply to the previous call to the server. * Its purpose is to keep the typing metrics on the server up to date. */ var updateTypingSession = function updateTypingSession() { if (!temporalTypingDataRef.current) { return; } getReducedNeuroprofile(userUIDRef.current, userTokenRef.current, temporalTypingDataRef.current, 'Desktop', "default"); temporalTypingDataRef.current = null; }; React.useEffect(function () { if (!isSending) { updateTypingSession(); } }, [isSending]); /** * Handles the input change event * @param {ChangeEvent<HTMLInputElement>} event - The input change event */ var handleInputChange = function handleInputChange(event) { if (!canAccess) { promptAccessWarning(); return; } var newValue = event.target.value; getKeystrokeManager().processInputChange(newValue); setDesktopTextValue(newValue); }; /** * Handles the submission of typing data and retrieves the neuroprofile. * @param {string} userUID - The unique identifier of the user for whom the neuroprofile is generated. * @param {string} userToken - A token used for authentication or authorization purposes. * @param {'default' | 'compare' | 'summary' | 'trends'} [action] - Optional action that determines the type of response to be received from the server. * @returns {Promise<IKeystrokeResult | undefined>} - A promise that resolves to the keystroke result or undefined if the submission is skipped. */ var handleSubmit = React.useCallback(/*#__PURE__*/function () { var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(userUID, userToken, action) { var typingData, neuroProfileResp; return _regeneratorRuntime().wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: if (!isSending) { _context.next = 3; break; } handleTypingSessionWhileSending(userUID, userToken); return _context.abrupt("return"); case 3: setDesktopTextValue(""); setIsSending(true); typingData = getKeystrokeManager().endTypingSession(); getKeystrokeManager().resetTypingData(); if (typingData.startUnixTime) { _context.next = 11; break; } console.log("Empty typing data for session: " + typingData.sessionID + ". Skipping..."); setIsSending(false); return _context.abrupt("return", { error: 'Empty typing data', message: "Empty typing data for session: " + typingData.sessionID + ". Skipping..." }); case 11: typingData.appContext = getOsInfo() + " - " + getBrowserInfo(); if (!(!userToken || !userUID)) { _context.next = 17; break; } console.warn("User credentials not found. Skipping save... "); getKeystrokeManager().resetTypingData(); setIsSending(false); return _context.abrupt("return", { error: 'User credentials not found.', message: 'Skipping save...' }); case 17: _context.next = 19; return getReducedNeuroprofile(userUID, userToken, typingData, 'Desktop', action != null ? action : 'default'); case 19: neuroProfileResp = _context.sent; setIsSending(false); if (neuroProfileResp.ok) { _context.next = 25; break; } console.warn("" + neuroProfileResp.message); console.error("" + neuroProfileResp.error); return _context.abrupt("return", { error: neuroProfileResp.error, message: neuroProfileResp.message }); case 25: return _context.abrupt("return", { data: neuroProfileResp.neuroprofile }); case 26: case "end": return _context.stop(); } }, _callee); })); return function (_x, _x2, _x3) { return _ref.apply(this, arguments); }; }(), [isSending]); /** * Handles the keydown event. * @param {string} key - The key that was pressed. */ var handleKeydown = React.useCallback(function (key) { if (!canAccess) { promptAccessWarning(); return; } getKeystrokeManager().processKeydown(key); }, [canAccess]); /** * Handles the keyup event. * @param {string} key - The key that was released. */ var handleKeyup = React.useCallback(function (key) { if (!canAccess) { promptAccessWarning(); return; } getKeystrokeManager().processKeyup(key); }, [canAccess]); return { value: desktopTextValue, handleInputChange: handleInputChange, handleKeydown: handleKeydown, handleKeyup: handleKeyup, getIsTypingSessionActive: getIsTypingSessionActive, getNeuroprofile: handleSubmit }; }; /** * Component that renders an input field for Android mobile devices * with keystroke tracking and additional event handlers. */ var A2AndroidTextInput = function A2AndroidTextInput(_ref) { var rest = _extends({}, (_objectDestructuringEmpty(_ref), _ref)); var _useMobileKeystrokeAn = useMobileKeystrokeAndroid(), handleInputChange = _useMobileKeystrokeAn.handleInputChange, value = _useMobileKeystrokeAn.value, handleKeydown = _useMobileKeystrokeAn.handleKeydown, handleKeyup = _useMobileKeystrokeAn.handleKeyup, handlePaste = _useMobileKeystrokeAn.handlePaste, handleKeyInput = _useMobileKeystrokeAn.handleKeyInput, handleOnBeforeInput = _useMobileKeystrokeAn.handleOnBeforeInput; return React__default.createElement(React__default.Fragment, null, React__default.createElement("input", Object.assign({ type: "text", placeholder: "Using android mobile input", autoCapitalize: "sentences", onKeyDown: function onKeyDown(_ref2) { var currentTarget = _ref2.currentTarget; return handleKeydown(currentTarget); }, onKeyUp: handleKeyup, value: value, onChange: handleInputChange, onPaste: handlePaste, onInput: function onInput(_ref3) { var currentTarget = _ref3.currentTarget