Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

global-input-react

Package Overview
Dependencies
Maintainers
1
Versions
185
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

global-input-react - npm Package Compare versions

Comparing version 4.1.0 to 4.1.1

dist/__tests__/mobile-app-and-device-app-communicate.test.js

518

dist/globalinput.js

@@ -6,3 +6,3 @@ "use strict";

});
exports.displayQRCode = exports.reducer = exports.startConnect = exports.invokeOnChange = exports.getExposed = exports.getFlags = exports.initialState = void 0;
exports.displayQRCode = exports.reducer = exports.startConnect = exports.sendValue = exports.setOnchange = exports.mobileData = exports.initialState = void 0;

@@ -39,3 +39,9 @@ var _react = _interopRequireDefault(require("react"));

errorMessage: null,
field: null
field: null,
isLoading: true,
isReady: false,
isError: false,
isDisconnected: false,
isConnected: false,
initData: null
};

@@ -50,2 +56,3 @@ exports.initialState = initialState;

clients: [],
client: null,
mobileConfig: null,

@@ -56,3 +63,19 @@ sendInitData: () => {},

};
exports.mobileData = mobileData;
const getMobileDataState = () => {
return {
isLoading: mobileData.mobileState === MobileState.INITIALIZING,
isReady: mobileData.mobileState === MobileState.WAITING_FOR_MOBILE,
isError: mobileData.mobileState === MobileState.ERROR,
isDisconnected: mobileData.mobileState === MobileState.DISCONNECTED,
isConnected: mobileData.mobileState === MobileState.MOBILE_CONNECTED,
initData: mobileData.mobileConfig && mobileData.mobileConfig.initData
};
};
const setOnchange = onchange => mobileData.onchange = onchange;
exports.setOnchange = setOnchange;
const sendValue = (fieldId, valueToSet) => {

@@ -75,125 +98,4 @@ if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {

const closeConnection = () => {
if (mobileData.session) {
mobileData.session.disconnect();
mobileData.session = null;
mobileData.mobileState = MobileState.DISCONNECTED;
}
};
exports.sendValue = sendValue;
const setOnchange = onchange => mobileData.onchange = onchange;
const getFlags = () => {
return {
isLoading: mobileData.mobileState === MobileState.INITIALIZING,
isReady: mobileData.mobileState === MobileState.WAITING_FOR_MOBILE,
isError: mobileData.mobileState === MobileState.ERROR,
isDisconnected: mobileData.mobileState === MobileState.DISCONNECTED,
isConnected: mobileData.mobileState === MobileState.MOBILE_CONNECTED
};
};
exports.getFlags = getFlags;
const getExposed = () => {
return {
initData: mobileData.mobileConfig && mobileData.mobileConfig.initData,
sendValue,
sendInitData: mobileData.sendInitData,
setOnchange,
disconnect: mobileData.disconnect
};
};
exports.getExposed = getExposed;
const invokeOnChange = field => {
if (field && mobileData.onchange) {
const {
initData,
sendValue,
sendInitData
} = getExposed();
mobileData.onchange({
field,
initData,
sendInitData,
sendValue
});
}
};
exports.invokeOnChange = invokeOnChange;
const isValidInitData = initData => initData && initData.form && initData.form.fields && initData.form.fields.length;
const buildMessageHandlers = (dispatch, initData) => {
if (typeof initData === 'function') {
initData = initData();
}
if (!isValidInitData(initData)) {
console.warn("will not send empty form");
return {};
}
;
const fields = [];
const values = [];
const fieldSetters = [];
const formFields = initData.form.fields.map((f, index) => {
if (!f) {
throw `The form contains a null field:${index} in ${initData.form.title}`;
}
const field = {
id: f.id,
label: f.label,
value: f.value
};
fields.push(field);
values.push(f.value);
const s = value => dispatch({
type: ACTION_TYPES.SEND_FIELD,
value,
fields,
values,
index
});
fieldSetters.push(s);
if (f.type === 'info') {
return f;
}
if (f.operations && f.operations.onInput) {
return f;
}
return { ...f,
operations: {
onInput: value => dispatch({
type: ACTION_TYPES.RECEIVED_FIELD,
values,
fields,
value,
index
})
}
};
});
return {
setters: fieldSetters,
fields,
values,
initData: { ...initData,
form: { ...initData.form,
fields: formFields
}
}
};
};
const startConnect = (dispatch, configData) => {

@@ -222,6 +124,6 @@ if (typeof configData === 'function') {

initData,
onRegistered: next => {
next();
const connectionCode = mobileData.session.buildInputCodeData(); //console.log("encrypted one-time session code [[" + connectionCode + "]]");
onRegistered: connectionCode => {
//console.log("encrypted one-time session code [[" + connectionCode + "]]");
mobileData.mobileState = MobileState.WAITING_FOR_MOBILE;
options && options.onRegistered && options.onRegistered(connectionCode);
dispatch({

@@ -231,8 +133,7 @@ type: ACTION_TYPES.REGISTERED,

});
if (options && options.onRegistered) {
options.onRegistered();
}
},
onRegisterFailed: errorMessage => {
closeConnection();
mobileData.mobileState = MobileState.ERROR;
options && options.onRegisterFailed && options.onRegisterFailed();
dispatch({

@@ -242,22 +143,25 @@ type: ACTION_TYPES.REGISTER_FAILED,

});
if (options && options.onRegisterFailed) {
options.onRegisterFailed();
}
},
onSenderConnected: (client, clients) => {
mobileData.mobileState = MobileState.MOBILE_CONNECTED;
mobileData.clients = clients;
mobileData.client = client;
dispatch({
type: ACTION_TYPES.SENDER_CONNECTED,
clients,
client
type: ACTION_TYPES.SENDER_CONNECTED
});
},
onSenderDisconnected: (client, clients) => {
mobileData.clients = clients;
mobileData.client = client;
closeConnection();
mobileData.mobileState = MobileState.INITIALIZING;
mobileData.session = (0, _globalInputMessage.createMessageConnector)();
mobileData.session.connect(mobileData.mobileConfig);
dispatch({
type: ACTION_TYPES.SENDER_DISCONNECTED,
client,
clients
type: ACTION_TYPES.SENDER_DISCONNECTED
});
},
onError: errorMessage => {
closeConnection();
mobileData.mobileState = MobileState.ERROR;
dispatch({

@@ -276,10 +180,19 @@ type: ACTION_TYPES.CONNECTION_ERROR,

if (mobileData.mobileState === MobileState.MOBILE_CONNECTED) {
const sendInitData = (mobileConfig, setters, fields, values) => {
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
console.error("sendInitData:requires MOBILE_CONNECTED");
}
mobileData.setters = setters;
mobileData.fields = fields;
mobileData.values = values;
mobileData.mobileConfig = mobileConfig;
mobileData.session.sendInitData(mobileConfig.initData);
dispatch({
type: ACTION_TYPES.SEND_INIT_DATA,
setters,
fields,
values,
mobileConfig
type: ACTION_TYPES.SEND_INIT_DATA
});
};
if (mobileData.mobileState === MobileState.MOBILE_CONNECTED) {
sendInitData(mobileConfig, setters, fields, values);
return;

@@ -301,9 +214,3 @@ }

mobileConfig.initData = initData;
dispatch({
type: ACTION_TYPES.SEND_INIT_DATA,
setters,
fields,
values,
mobileConfig
});
sendInitData(mobileConfig, setters, fields, values);
};

@@ -319,20 +226,2 @@

closeConnection();
dispatch({
type: ACTION_TYPES.START_CONNECT,
mobileConfig,
setters,
fields,
values
});
};
exports.startConnect = startConnect;
const processStartConnect = (state, action) => {
const {
mobileConfig,
setters,
fields,
values
} = action;
mobileData.mobileState = MobileState.INITIALIZING;

@@ -345,163 +234,117 @@ mobileData.setters = setters;

mobileData.session.connect(mobileConfig);
return { ...state,
errorMessage: '',
field: null,
isLoading: true,
isReady: false,
isError: false,
isDisconnected: false,
isConnected: false,
senders: []
};
dispatch({
type: ACTION_TYPES.START_CONNECT
});
};
const processSendInitData = (state, action) => {
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
console.error("sendInitData:requires MOBILE_CONNECTED");
return state;
exports.startConnect = startConnect;
const closeConnection = () => {
if (mobileData.session) {
mobileData.session.disconnect();
mobileData.session = null;
}
const {
mobileConfig,
setters,
fields,
values
} = action;
mobileData.setters = setters;
mobileData.fields = fields;
mobileData.values = values;
mobileData.mobileConfig = mobileConfig;
mobileData.session.sendInitData(mobileConfig.initData);
return { ...state,
field: null
};
mobileData.mobileState = MobileState.DISCONNECTED;
};
const processRegistered = (state, action) => {
const {
connectionCode
} = action;
mobileData.mobileState = MobileState.WAITING_FOR_MOBILE;
return { ...state,
connectionCode,
isLoading: false,
isReady: true,
isError: false,
isDisconnected: false,
isConnected: false
};
};
const isValidInitData = initData => initData && initData.form && initData.form.fields && initData.form.fields.length;
const processError = (state, action) => {
const {
errorMessage
} = action;
mobileData.mobileState = MobileState.ERROR;
closeConnection();
return { ...state,
errorMessage,
isError: true,
isLoading: false,
isReady: false,
isDisconnected: true,
isConnected: false,
field: null
};
};
const buildMessageHandlers = (dispatch, initData) => {
if (typeof initData === 'function') {
initData = initData();
}
const processSenderConnected = (state, action) => {
const {
clients
} = action;
mobileData.mobileState = MobileState.MOBILE_CONNECTED;
return { ...state,
clients,
isConnected: true,
isLoading: false,
isReady: false,
isError: false,
isDisconnected: false
};
};
if (!isValidInitData(initData)) {
console.warn("will not send empty form");
return {};
}
const processSenderDisconnected = (state, action) => {
closeConnection();
mobileData.mobileState = MobileState.INITIALIZING;
mobileData.session = (0, _globalInputMessage.createMessageConnector)();
mobileData.session.connect(mobileData.mobileConfig);
return { ...state,
errorMessage: '',
field: null,
isLoading: true,
isReady: false,
isError: false,
isDisconnected: false,
isConnected: false,
senders: []
};
};
;
const fields = [];
const values = [];
const fieldSetters = [];
const formFields = initData.form.fields.map((f, index) => {
if (!f) {
throw `The form contains a null field:${index} in ${initData.form.title}`;
}
const processReceivedField = (state, action) => {
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
console.error("RECEIVED_FIELD:requires isConnected:" + mobileData.mobileState);
return state;
}
const field = {
id: f.id,
label: f.label,
value: f.value
};
fields.push(field);
values.push(f.value);
const {
values,
fields,
value,
index
} = action;
const s = value => {
if (mobileData.fields !== fields) {
console.error("SEND_FIELD:fields array is expected to stay unchanged");
return;
}
if (mobileData.fields !== fields) {
console.error("RECEIVED_FIELD:fields array is expected to stay unchanged");
return state;
}
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
console.error("SEND_FIELD:requires isConnected:" + mobileData.mobileState);
return state;
}
values[index] = value;
fields[index].value = value;
const field = { ...fields[index],
value
};
return { ...state,
field
};
};
values[index] = value;
fields[index].value = value;
const processSendField = (state, action) => {
const {
values,
fields,
index,
value
} = action;
if (fields[index].id) {
mobileData.session.sendValue(fields[index].id, value);
} else {
mobileData.session.sendValue(null, value, index);
}
if (mobileData.fields !== fields) {
console.error("SEND_FIELD:fields array is expected to stay unchanged");
state;
}
dispatch({
type: ACTION_TYPES.SEND_FIELD
});
};
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
console.error("SEND_FIELD:requires isConnected:" + mobileData.mobileState);
return state;
}
fieldSetters.push(s);
values[index] = value;
fields[index].value = value;
mobileData.session.sendInputMessage(value, index);
return { ...state
};
};
if (f.type === 'info') {
return f;
}
const processClose = (state, action) => {
mobileData.mobileState = MobileState.DISCONNECTED;
closeConnection();
return { ...state,
isError: false,
isLoading: false,
isReady: false,
isDisconnected: true,
isConnected: false,
field: null
if (f.operations && f.operations.onInput) {
return f;
}
return { ...f,
operations: {
onInput: value => {
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
console.error("RECEIVED_FIELD:requires isConnected:" + mobileData.mobileState);
return;
}
if (mobileData.fields !== fields) {
console.error("RECEIVED_FIELD:fields array is expected to stay unchanged");
return;
}
values[index] = value;
fields[index].value = value;
const field = { ...fields[index],
value
};
dispatch({
type: ACTION_TYPES.RECEIVED_FIELD,
field
});
}
}
};
});
return {
setters: fieldSetters,
fields,
values,
initData: { ...initData,
form: { ...initData.form,
fields: formFields
}
}
};

@@ -513,36 +356,41 @@ };

case ACTION_TYPES.START_CONNECT:
return processStartConnect(state, action);
case ACTION_TYPES.SEND_INIT_DATA:
return processSendInitData(state, action);
state = { ...state,
errorMessage: '',
field: null
};
break;
case ACTION_TYPES.REGISTERED:
return processRegistered(state, action);
state = { ...state,
errorMessage: '',
field: null,
connectionCode: action.connectionCode
};
break;
case ACTION_TYPES.RECEIVED_FIELD:
state = { ...state,
field: action.field
};
break;
case ACTION_TYPES.CONNECTION_ERROR:
return processError(state, action);
case ACTION_TYPES.REGISTER_FAILED:
return processError(state, action);
state = { ...state,
errorMessage: action.errorMessage
};
break;
case ACTION_TYPES.SENDER_CONNECTED:
return processSenderConnected(state, action);
case ACTION_TYPES.SENDER_DISCONNECTED:
return processSenderDisconnected(state, action);
case ACTION_TYPES.RECEIVED_FIELD:
return processReceivedField(state, action);
case ACTION_TYPES.SEND_FIELD:
return processSendField(state, action);
case ACTION_TYPES.CLOSE:
return processClose(state, action);
default:
return state;
}
;
return { ...state,
...getMobileDataState()
};
};

@@ -549,0 +397,0 @@

@@ -7,4 +7,3 @@ "use strict";

var _exportNames = {
useGlobalInputApp: true,
MobileState: true
useGlobalInputApp: true
};

@@ -17,8 +16,2 @@ Object.defineProperty(exports, "useGlobalInputApp", {

});
Object.defineProperty(exports, "MobileState", {
enumerable: true,
get: function () {
return _useGlobalInputApp.MobileState;
}
});

@@ -30,2 +23,3 @@ var _globalInputMessage = require("global-input-message");

if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _globalInputMessage[key]) return;
Object.defineProperty(exports, key, {

@@ -39,6 +33,4 @@ enumerable: true,

var _useGlobalInputApp = _interopRequireWildcard(require("./useGlobalInputApp"));
var _useGlobalInputApp = _interopRequireDefault(require("./useGlobalInputApp"));
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -10,3 +10,3 @@ "use strict";

var _globalinput = require("./globalinput");
var globalInput = _interopRequireWildcard(require("./globalinput"));

@@ -21,5 +21,3 @@ function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

errorMessage,
field
}, dispatch] = (0, _react.useReducer)(_globalinput.reducer, _globalinput.initialState);
const {
field,
isLoading,

@@ -29,9 +27,19 @@ isReady,

isDisconnected,
isConnected
} = (0, _globalinput.getFlags)();
isConnected,
initData
}, dispatch] = (0, _react.useReducer)(globalInput.reducer, globalInput.initialState);
(0, _react.useEffect)(() => {
(0, _globalinput.startConnect)(dispatch, configData);
globalInput.startConnect(dispatch, configData);
}, dependencies ? dependencies : []); //default connect once for the component
(0, _react.useEffect)(() => (0, _globalinput.invokeOnChange)(field), [field]);
(0, _react.useEffect)(() => {
if (field && globalInput.mobileData.onchange) {
globalInput.mobileData.onchange({
field,
initData,
sendInitData: globalInput.mobileData.sendInitData,
sendValue: globalInput.sendValue
});
}
}, [field]);
const ConnectQR = (0, _react.useCallback)(({

@@ -42,18 +50,13 @@ level,

children
}) => (0, _globalinput.displayQRCode)({
level,
size,
container,
connectionCode,
isReady,
isLoading,
children
}), [connectionCode, isReady, isLoading]);
const {
initData,
sendValue,
sendInitData,
setOnchange,
disconnect
} = (0, _globalinput.getExposed)();
}) => {
globalInput.displayQRCode({
level,
size,
container,
connectionCode,
isReady,
isLoading,
children
});
}, [connectionCode, isReady, isLoading]);
return {

@@ -70,6 +73,6 @@ ConnectQR,

initData,
sendValue,
sendInitData,
setOnchange,
disconnect
sendValue: globalInput.sendValue,
sendInitData: globalInput.mobileData.sendInitData,
disconnect: globalInput.mobileData.disconnect,
setOnchange: globalInput.setOnchange
};

@@ -76,0 +79,0 @@ };

@@ -7,12 +7,10 @@

type ConfigDataCreator = () => ConfigData;
export function useGlobalInputApp(configData?: ConfigData | ConfigDataCreator, dependencies?: ReadonlyArray<any>): GlobalInputData;
export function useGlobalInputApp(configData: ConfigData | (() => ConfigData), dependencies?: ReadonlyArray<any>): GlobalInputData;
type OnchangeFunction = (evt: FieldChanged) => void;
interface ConfigData {
initData?: InitData | InitDataCreator;
initData: InitData | (() => InitData);
onchange?: OnchangeFunction;
options?: ConnectOptions;
session?: object;
}

@@ -26,21 +24,21 @@ interface ConnectOptions {

interface FieldChanged {
field: FormField;
initData:InitData;
sendInitData:SetInitDataFunction;
sendValue:SetFieldValueByIdFunction
field: FormField;
initData: InitData;
sendInitData: SendInitDataFunction;
sendValue: SendValueFunction
}
type InitDataCreator = () => InitData;
type SendValueFunction = (fieldId: string, valueToSet: FieldValue) => void;
type SendInitDataFunction = (initData: InitData, options?: ConnectOptions) => void;
type SetFieldValueByIdFunction = (fieldId: string, valueToSet: FieldValue) => void;
type SetInitDataFunction = (initData: InitData, options?: ConnectOptions) => void;
type ConnectQRProps = {
size: number,
level: "L" | "M" | "Q" | "H"
level: "L" | "M" | "Q" | "H",
container?:React.FC
};
interface GlobalInputData {
ConnectQR: FunctionComponent<ConnectQRProps>,
ConnectQR: FunctionComponent<ConnectQRProps>,
connectionCode: string;
field: FormField;
errorMessage: string;
field: FormField;
errorMessage: string;
isLoading: boolean;

@@ -51,7 +49,7 @@ isReady: boolean;

isConnected: boolean;
initData:InitData;
sendValue:SetFieldValueByIdFunction;
sendInitData:SetInitDataFunction;
initData: InitData;
sendValue: SendValueFunction;
sendInitData: SendInitDataFunction;
setOnchange: (onchange: OnchangeFunction) => void;
disconnect: () => void;
disconnect: () => void;
}

@@ -58,0 +56,0 @@

{
"name": "global-input-react",
"version": "4.1.0",
"version": "4.1.1",
"description": "global input react component",

@@ -44,33 +44,32 @@ "repository": {

"devDependencies": {
"@babel/cli": "^7.11.6",
"@babel/core": "^7.11.6",
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/preset-env": "^7.11.5",
"@babel/preset-react": "^7.10.4",
"@babel/register": "^7.11.5",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.0.4",
"@babel/cli": "^7.12.1",
"@babel/core": "^7.12.3",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/preset-env": "^7.12.1",
"@babel/preset-react": "^7.12.1",
"@babel/register": "^7.12.1",
"@testing-library/jest-dom": "^5.11.5",
"@testing-library/react": "^11.1.0",
"@testing-library/react-hooks": "^3.4.2",
"@testing-library/user-event": "^12.1.7",
"babel-jest": "^26.5.0",
"@testing-library/user-event": "^12.1.10",
"babel-jest": "^26.6.1",
"babel-loader": "^8.1.0",
"eslint": "^7.10.0",
"eslint": "^7.12.1",
"eslint-config-airbnb": "^18.2.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-react": "^7.21.2",
"eslint-plugin-react-hooks": "^4.1.2",
"jest-canvas-mock": "^2.3.0",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"jest-dom": "^4.0.0",
"jest": "^26.5.0",
"jest": "^26.6.1",
"prettier": "^2.1.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-test-renderer": "^16.13.1",
"webpack": "^4.44.2"
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-test-renderer": "^17.0.1",
"webpack": "^5.2.1"
},
"dependencies": {
"global-input-message": "^1.8.8",
"global-input-message": "^2.0.3",
"qrcode.react": "^1.0.0"
}
}

@@ -0,11 +1,10 @@

### Global Input React
This React module allows you to introduce a mobile interoperability into React applications on smart devices like smart TVs, set-top boxes, game consoles, and devices in IoT, so that users can use their mobiles to operate on them. It allows you to define mobile interfaces and receive mobile events from within your device applications, while keeping the mobile app as a general and universal mobile app that works across all types of device applications with different business logic: meaning that there is no need to switch to different mobile app for operating on different devices and no need to develop different mobile apps for different business or device applications. It also allows you to enrich your device applications with a set of mobile functionalities like [mobile encryption](https://globalinput.co.uk/global-input-app/mobile-content-encryption), [mobile authentication](https://globalinput.co.uk/global-input-app/mobile-authentication), [mobile input & control](https://globalinput.co.uk/global-input-app/mobile-input-control), [second screen experience](https://globalinput.co.uk/global-input-app/second-screen-experience), [mobile secure storage](https://globalinput.co.uk/global-input-app/mobile-personal-storage), [mobile encryption & signing](https://globalinput.co.uk/global-input-app/mobile-content-encryption), and [mobile content transfer](https://globalinput.co.uk/global-input-app/mobile-content-transfer). The communication between a mobile app and a device application is often established through scanning an Encrypted QR Code that contains a set of communication parameters that includes one-time-use encryption key for starting an end-to-end encryption process.
This React library allows you to introduce a mobile interoperability into your React applications on smart devices like smart TVs, set-top boxes, game consoles, and devices in IoT, so that users can use their mobiles to operate on them. It allows you to define mobile interfaces and receive mobile events from within your device applications, while keeping the mobile app as a general and universal mobile app that works across all types of device applications with different business logic: meaning that there is no need to switch to different mobile app for operating on different devices and no need to develop different mobile apps for different business or device applications. It also allows you to enrich your device applications with a set of mobile functionalities like [mobile encryption](https://globalinput.co.uk/global-input-app/mobile-content-encryption), [mobile authentication](https://globalinput.co.uk/global-input-app/mobile-authentication), [mobile input & control](https://globalinput.co.uk/global-input-app/mobile-input-control), [second screen experience](https://globalinput.co.uk/global-input-app/second-screen-experience), [mobile secure storage](https://globalinput.co.uk/global-input-app/mobile-personal-storage), [mobile encryption & signing](https://globalinput.co.uk/global-input-app/mobile-content-encryption), and [mobile content transfer](https://globalinput.co.uk/global-input-app/mobile-content-transfer). The communication between a mobile app and a device application is often established through scanning an Encrypted QR Code that contains a set of communication parameters that includes one-time-use encryption key for starting an end-to-end encryption process.
This module is particularly useful in the current new normal established by the COVID-19 pandemic, where businesses require visiting customers to communicate accurately with customer representatives while enforcing the rules of wearing masks and social distancing. Thanks to this library, you will be able to establish an instant and secure communication right within the business software you are using, allowing your customers to collaborate effectively, securely and safely. For example, you may provide one-click subscriptions through user mobiles by leveraging the [mobile secure storage](https://globalinput.co.uk/global-input-app/mobile-authentication). Alternative, you do not even have to collect users' personal data thanks to the ability to request data on-demand from the mobile app at the point of service, freeing yourself from the pains of privacy regulations. You may also choose to allow your customers to encrypt their own data using their mobiles, giving users full control over the security and privacy of their personal data.
This library is particularly useful in the new normal established by the current COVID-19 pandemic, where businesses require visiting customers to communicate accurately with customer representatives while enforcing the rules of wearing masks and social distancing. Thanks to this library, you will be able to establish an instant and secure communication right within your business software, allowing your customers to collaborate effectively, securely and safely. For example, you may provide one-click subscriptions through user mobiles by leveraging the [mobile secure storage](https://globalinput.co.uk/global-input-app/mobile-authentication). Alternative, you do not even have to collect users' personal data thanks to the ability to request data on-demand from the mobile app at the point of service, freeing yourself from the pains of privacy regulations. You may also choose to allow your customers to encrypt their own data using their mobiles, giving users full control over the security and privacy of their personal data.
## Setup
```shell
npm i global-input-react
npm install global-input-react
```

@@ -18,41 +17,43 @@ ## Usage

```
The React hook ```useGlobalInputApp``` accepts a data object that defines a mobile interface for the connected mobile app to present to the user upon connection.
Then, ```useGlobalInputApp()``` function (which is a React hook) can be called with a parameter, defining a mobile user interface. For example, the following code displays a login screen on the user's mobile screen when connected to your application:
For example, if you would like to display a login screen on the connected user's mobile screen:
```JavaScript
const usernameField={
id: 'username',
label: 'Username',
const usernameField = {
id: 'username',
label: 'Username',
};
const passwordField={
id:'password',
label: 'Password'
const passwordField = {
id:'password',
label: 'Password'
};
const loginButton={
id: 'login',
label: 'Sign In',
type: 'button'
const loginButton = {
id: 'login',
label: 'Sign In',
type: 'button'
};
const mobile=useGlobalInputApp({initData:{
form:{
title: 'Sign In',
fields: [usernameField,passwordField,loginButton]
}
const mobile = useGlobalInputApp( {initData : {
form: {
title: 'Sign In',
fields: [usernameField,passwordField,loginButton]
}
}});
```
In the above code, the ```initData``` parameter holds data describing the mobile user interface. It specifies a ```form``` with a set of fields that are ```usernameField```,```passwordField```, and ```loginButton```.
The ```initData``` contains a ```form``` with a set of fields: ```usernameField```,```passwordField```, and ```loginButton```.
The ```mobile``` object returned by the ```useGlobalInputApp``` hook contains a React component called ```ConnectQR```, which allows you to display an encrypted QR Code for an user to scan to connect to your application.
The object returned by ```useGlobalInputApp()``` function is stored into the ```mobile``` variable. It contains a set of data items and functions for receiving or sending data to the connected mobile app. To begin with, you can place the following code into the rendering part of your code:
```JavaScript
<mobile.ConnectQR/>
```
When a mobile app has connected to your application, the QR code will disappear, and the connected mobile app will presents user with the form that you have defined in the ```initData``` parameter above. When the user interacts with any of the fields in the form on the mobile screen, your application will receive a ```mobile.field``` object with enclosing ```id``` and ```value``` attributes. The ```id``` attribute tells you which form field that the user has interacted with, and the ```value``` attribute tells you the current value of the field after the user has interacted with it. Instead of implement logic to monitor the changes in the ```mobile.field``` object, you can simply pass your callback function into the ```mobile.setOnchange()``` function to receive those mobile events:
It displays an encrypted QR Code for mobile apps to scan to connect to your application. It contains the content of ```mobile.connectionCode```, which holds an encrypted string value. When decrypted, it provides information on how to connect to your application, including a one-time-use encryption key for initiating an end-to-end encryption process between your application and a mobile app.
When connected to your application, the mobile app displays a ```form``` specified in ```initData```. Also, when the user interacts with elements in the ```form```, your application can receive mobile input events through ```mobile.field``` variable, with ```mobile.field.id``` identifying the element that the user has interacted with, and with ```mobile.field.value``` holding the value that the user has entered. Actually, you can simply pass your callback function into the ```mobile.setOnchange()``` function to receive those mobile input events instead of implementing the logic for monitoring the changes in the ```mobile.field``` variable:
```JavaScript
mobile.setOnchange(({field})=>{
const {id, value}=field;
switch(id){
mobile.setOnchange( ( {field} ) => {
const {id, value} = field;
switch(id) {
case usernameField.id:

@@ -70,159 +71,185 @@ setUsername(value);

```
In the above code, ```setUsername``` and ```setPassword``` are coming from the [State Hook](https://reactjs.org/docs/hooks-state.html) used for maintaining state in a functional component:
In the above code, You can implement the ```setUsername()``` and ```setPassword()``` functions to store the user entries, and the ```login()``` function to call an authentication mechanism:
```JavaScript
import React, { useState } from 'react';
...
const [username, setUsername]=useState('');
const [password, setPassword]=useState('');
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
```
The ```login()``` function is where you can implement actual authentication logic. Using this approach, you can turn a simple password-based authentication into a one-click mobile authentication [mobile authentication](https://globalinput.co.uk/global-input-app/mobile-authentication).
When the user enters content in your application directly instead of operating on the connected mobile app, you may like to send the updated value to the mobile app to keep the remote and local values in sync. You can do so by calling the ```mobile.sendValue()``` function:
You can use ```mobile.sendValue()``` to send values to the connected mobile app. It accepts two parameters: the first parameter is for providing the id of the target element in the form, and the second parameter is for providing value to be sent:
```JavaScript
Username:
<input type="text" value={username} onChange={event=>{
<input onChange = { (event) => {
setUsername(event.target.value);
mobile.sendValue(usernameField.id,event.target.value);
}/>
```
The code snippet ```setUsername(event.target.value)``` is for updating the local state, while ```mobile.sendValue(usernameField.id,event.target.value)``` is for updating the remote form element identified with ```usernameField.id``` on the mobile app with the value ```event.target.value```.
Similarly, we can do the same for the password field:
```JavaScript
Password:
<input type="password" value={username} onChange={event=>{
mobile.sendValue(usernameField.id, event.target.value);
} value={username} type="text"/>
<input onChange={ (event) => {
setPassword(event.target.value);
mobile.sendValue(passwordField.id,event.target.value);
}/>
mobile.sendValue(passwordField.id, event.target.value);
} value={username} type="password"/>
```
Using this approach, you can turn a simple password-based authentication into a one-click mobile authentication or implement a password-less authentication or add an extra security layer to the existing authentication mechanism without affecting the usability of your application.
With the above code in place, the content can be updated on both devices while keeping the local and remote values in sync.
When ```mobile.sendInitData()``` function is called with a ```InitData``` parameter, the connected mobile app will switch to the user interface specified:
In order to switch to a different mobile user interface responding to some events, you can can pass an user interface data to the ```mobile.sendInitData()``` function:
```JavaScript
const login=(username,password)=>{
const initData={
};
mobile.sendInitData(initData:{
form: {
title: "Welcome " +username,
fields: [{type: "info", value: "Test Completed",}]
}
});
const infoField = {
type: "info",
value: "Test Completed"
};
const login = (username,password) => {
mobile.sendInitData( {
form: {
title: "Welcome " + username,
fields: [infoField]
}
});
}
```
Alternatively, you can replace the current React component with a new React component that in turn replace the current ```useGlobalInputApp``` with a new one.
Another way is to place another instance of ```useGlobalInputApp``` in a React component, then render the component when you need to replace mobile user interface.
## Advanced Concept
Calling ```useGlobalInputApp``` hook may start an initialisation process if the library is not initialized yet. During the initialization phase, the ```<mobile.ConnectQR/>``` component displays a loading symbol, and the ```mobile.isLoading``` variable is set to true until this phase is completed. After that, if no mobile has yet connected to the application, it enters the "Ready" phase, meaning that it is ready to accept connection. In this phase, the ```mobile.isReady``` variable is set to true, and ```<mobile.ConnectQR/>``` displays an encrypted QR Code. The encrypted QR code is simply an QR code generated from an encrypted content in the ```mobile.connectionCode``` variable. It provides a set of parameters for establishing communication with your application, including an encryption key for initiating an end-to-end encryption process. In order to increase the security further, a brand new encryption key is generated for each session. When a mobile app is connected to your application, the component ```<mobile.ConnectQR/>``` returns null, and the ```mobile.isConnected``` variable is set to true. By levegaring these variables, you can control what to display in different phases:
When ```useGlobalInputApp``` is invoked for the first time, the module will start to initialize itself. In this phase, ```mobile.isLoading``` is set to true, and ```<mobile.ConnectQR/>``` displays a loading symbol. After the initialization is completed, if the application is ready to accept connection, ```mobile.isReady``` is set to true, and ```<mobile.ConnectQR/>``` displays an encrypted QR Code. When a mobile app has connected to your application, ```mobile.isConnected``` is set to true, and ```<mobile.ConnectQR/>``` displays nothing. Those variables are useful if you would like to control what to display during different phases:
```JavaScript
{mobile.isConnected && (<>
<h1>Mobile Connected</h1>
<div>Please operate on your mobile to provide your crendetial!</div>
<div>Please operate on your mobile to provide your credential!</div>
</>)}
```
If you would like to disconnect and disable the ability to allow mobile apps to connect to your application responding to some events, you can call ```mobile.disconnect()```. This brings the library into the "disconnected" phase, and set the ```mobile.isDisconnected``` variable to true. If there is a connection error, it enters the "error" phase, and set the ```mobile.isError``` variable to true, while the ```mobile.errorMessage``` variable contains the error message.
The (Typescript Declaration file)[https://github.com/global-input/global-input-react/blob/master/index.d.ts] contains more information on the structure of the data used and functions that are exposed by the library.
Note that you can use your own mobile app as the universal mobile app instead of using the [Global Input App](https://globalinput.co.uk), you can find the information how to do that from the code inside the integration tests included in the [test project](https://github.com/global-input/test-global-input-app-libs/blob/master/src/test-global-input-react/mobile-and-device-app.test.tsx).
For an element in a ```form```, ```type``` attribute defines how to process/display the data contained in it. For example, if it is set to ```button```, the mobile app display a ```Button```:
```JavaScript
const loginButton = {
id: 'login',
label: 'Sign In',
type: 'button'
};
```
## More about Form Element
The default value of the ```type``` attribute is "text". In this case, it display either a text input or a ```textarea```, depending on the value of ```nLines```, which represents how many number of lines is visible:
When you go through the previous example, you might have noticed that the ```type``` attribute of a form element defines what component the mobile app uses to process the data contained in it. For example, if it is set to ```button```, the mobile app uses the ```Button``` UI component to process the data:
```JavaScript
const loginButton={
id: 'login',
label: 'Sign In',
type: 'button'
};
const contentField = {
id: 'content',
label: 'Content',
type: "text",
nLines:5,
value:"This is a content in the text box"
};
```
As a result, it displays a button on the mobile screen.
If the ```value``` attribute is set, it will be sent along with the form to pre-populate the the field when being displayed on the mobile screen.
If the ```type``` attribute is missing, it takes its default value, which is "text". In this case, it display either a text field or text box (textarea) depending the values of another attribute, called ```nLines```, which takes ```1``` as its the default value. The ```nLines``` specifies the number of lines visible in the text field/box.
For example, when you would like to display a content with a text box with fix visible text rows:
## Mobile Encryption
If you set the value of ```type``` of element in a ```form``` to ```"encrypt"```, the connected mobile app encrypts the ```value``` of the element and send back the result to your application:
```JavaScript
const contentField={
id: 'content',
label: 'Content',
type: "text",
nLines:5
const encryptField = {
id: 'content',
label: 'Content',
type: "encrypt",
value: contentToEncrypt
};
```
You can also send the actual value with the form element to pre-populate the text box:
In a similar way, setting ```type``` to ```"decrypt"``` will lead to decryption:
```JavaScript
const contentField={
const decryptField = {
id: 'content',
label: 'Content',
type: "text",
nLines:5,
value:"This is a content in the text box"
type: "decrypt",
value: contentToDecrypt
};
```
## Mobile Encryption
To instruct the mobile app to encrypt the content and send the result back to your application, simply set the ```type``` attribute of the corresponding element to "encrypt".
```JavaScript
const contentField={
id: 'content',
label: 'Content',
type: "encrypt",
value:"Content that requires encryption"
};
const mobile=useGlobalInputApp({initData:{
id:"encrypting content",
form:{
title: 'Encrypt Content',
fields: [contentField]
}
}});
```
This prompts the user to encrypt the content specified in the value attribute, and the result will be sent back to your application. You can receive the content through your callback function:
## Customizing Form Elements & Styled Values.
The value attribute in an element can also be an object containing some styling information:
```JavaScript
mobile.setOnchange(({field})=>{
switch(field.id){
case contentField.id:
setEncryptedContent(field.value);
break;
const infoField = {
id: "title",
type: "info",
value: {
type: "text",
content: "This is a Text",
style: {
fontSize: 18,
marginTop: 20,
}
}
});
}
```
In the similar way, you can send an encrypted content to the mobile app for decryption by setting the type of an element to ```decrypt```. The following React component will ask the mobile user to decrypt the content passed in and passes the decrypted content to the ```onDecrypted()``` function:
You can display a multi-line text using an array for ```content```:
```JavaScript
import {useGlobalInputApp} from 'global-input-react';
export default ({contentToDecrypt,onDecrypted}) =>{
const contentField={
id: 'content',
label: 'Content',
type: "descrypt",
value:encryptedContent
};
const mobile=useGlobalInputApp({initData:{
form:{
title:"Mobile Decryption",
fields:[contentField,
{
type:"info",
value:"This content will be displayed when the decryption has completed and the connected application has received the result"
}]
}});
setOnFieldChanged(({field})=>{
switch(field.id){
case contentField.id:
onDecrypted(field.value);
}
});
return (<div>Please operate on your mobile</div>);
};
```
const informationField = {
id: "informationText",
type: "info",
value: {
type: "view",
style: {
borderColor: "#rgba(72,128,237,0.5)",
backgroundColor: "#rgba(72,128,237,0.5)",
borderWidth: 1,
marginTop: 5,
minWidth: "100%",
minHeight: 80,
},
content: [{
type: "text",
content: title,
style: {
fontSize: 18,
color: "white"
}
}, {
type: "text",
content: message,
style: {
fontSize: 14,
color: "white"
}
}]
}
}
````
Finally, the examples in the [website](https://globalinput.co.uk/), and tests in the [test project](https://github.com/global-input/test-global-input-app-libs) contain more information about various use cases that you can implement in your Typescript/JavaScript applications.
## On Mobile App Side
Although you can use [Global Input App](https://globalinput.co.uk/) to operate on your applications, You can certainly use this module to enable your own mobile app to have the ability to operate on various device applications that are powered with this module, assuming your mobile app is implemented using one of the JavaScript-based frameworks like [React Native](https://reactnative.dev/).
As discussed previously, in order to connect to a device application, your mobile app needs to obtain the value of ```connectionCode``` through scanning a QR Code. Then, you can pass it to the ```connect()``` function to connect to your device application as its second parameter:
```JavaScript
const mobileConnector = createMessageConnector();
const {initData} = await mobileConnector.connect( {
onInput:(inputMessage) => {
....
}
}, connectionCode);
```
In the above code, ```initData``` contains a ```form``` provided by the connected device application, while ```onInput()``` function is called whenever a message is received from the device application.
You can also send messages to the device application, responding to the events generated when the user interacts with form elements:
```JavaScript
const sendUsername = (username) => {
mobileConnector.sendValue(initData.form.fields[0].id, username);
}
```
There are two input parameters required for calling ```mobileConnector.sendValue()``` function: the first one identifies the target element that the value is being sent to, while the second parameter holds the value needs to be sent across.

@@ -7,51 +7,70 @@

const ACTION_TYPES = {
START_CONNECT:1,
SEND_INIT_DATA:2,
REGISTERED:3,
REGISTER_FAILED:4,
SENDER_CONNECTED:5,
SENDER_DISCONNECTED:6,
CONNECTION_ERROR:7,
RECEIVED_FIELD:8,
SEND_FIELD:9,
CLOSE:10
const ACTION_TYPES = {
START_CONNECT: 1,
SEND_INIT_DATA: 2,
REGISTERED: 3,
REGISTER_FAILED: 4,
SENDER_CONNECTED: 5,
SENDER_DISCONNECTED: 6,
CONNECTION_ERROR: 7,
RECEIVED_FIELD: 8,
SEND_FIELD: 9,
CLOSE: 10
};
const MobileState = {
INITIALIZING:1,
DISCONNECTED:2,
ERROR:3,
WAITING_FOR_MOBILE:4,
MOBILE_CONNECTED:5
const MobileState = {
INITIALIZING: 1,
DISCONNECTED: 2,
ERROR: 3,
WAITING_FOR_MOBILE: 4,
MOBILE_CONNECTED: 5
};
export const initialState={
connectionCode:null,
errorMessage:null,
field:null
export const initialState = {
connectionCode: null,
errorMessage: null,
field: null,
isLoading: true,
isReady: false,
isError: false,
isDisconnected: false,
isConnected: false,
initData: null,
};
const mobileData={
session:null,
mobileState:MobileState.INITIALIZING,
fields:[],
values:[],
setters:[],
clients:[],
mobileConfig:null,
sendInitData:()=>{},
disconnect:()=>{},
onchange:()=>{}
export const mobileData = {
session: null,
mobileState: MobileState.INITIALIZING,
fields: [],
values: [],
setters: [],
clients: [],
client: null,
mobileConfig: null,
sendInitData: () => { },
disconnect: () => { },
onchange: () => { }
};
const sendValue = (fieldId, valueToSet) => {
if(mobileData.mobileState!==MobileState.MOBILE_CONNECTED){
const getMobileDataState = () => {
return {
isLoading: mobileData.mobileState === MobileState.INITIALIZING,
isReady: mobileData.mobileState === MobileState.WAITING_FOR_MOBILE,
isError: mobileData.mobileState === MobileState.ERROR,
isDisconnected: mobileData.mobileState === MobileState.DISCONNECTED,
isConnected: mobileData.mobileState === MobileState.MOBILE_CONNECTED,
initData: mobileData.mobileConfig && mobileData.mobileConfig.initData,
}
};
export const setOnchange = onchange => mobileData.onchange = onchange;
export const sendValue = (fieldId, valueToSet) => {
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
return;
}
if(mobileData.fields && mobileData.fields.length){
for(let [index,field] of mobileData.fields.entries()){
if(field.id===fieldId){
mobileData.setters[index](valueToSet);
}
if (mobileData.fields && mobileData.fields.length) {
for (let [index, field] of mobileData.fields.entries()) {
if (field.id === fieldId) {
mobileData.setters[index](valueToSet);
break;

@@ -63,73 +82,157 @@ }

const closeConnection =()=>{
if(mobileData.session){
mobileData.session.disconnect();
mobileData.session=null;
mobileData.mobileState=MobileState.DISCONNECTED;
}
};
export const startConnect = (dispatch, configData) => {
if (typeof configData === 'function') {
configData = configData();
}
if (!configData) {
return;
}
const options = configData.options;
const { setters, fields, values, initData } = buildMessageHandlers(dispatch, configData.initData);
if (!initData) {
return null;
}
const mobileConfig = {
initData,
onRegistered: (connectionCode) => {
//console.log("encrypted one-time session code [[" + connectionCode + "]]");
mobileData.mobileState = MobileState.WAITING_FOR_MOBILE;
options && options.onRegistered && options.onRegistered(connectionCode);
dispatch({ type: ACTION_TYPES.REGISTERED, connectionCode });
},
onRegisterFailed: errorMessage => {
closeConnection();
mobileData.mobileState = MobileState.ERROR;
options && options.onRegisterFailed && options.onRegisterFailed();
dispatch({ type: ACTION_TYPES.REGISTER_FAILED, errorMessage });
const setOnchange=onchange => mobileData.onchange=onchange;
export const getFlags = ()=>{
return {
isLoading: mobileData.mobileState===MobileState.INITIALIZING,
isReady: mobileData.mobileState===MobileState.WAITING_FOR_MOBILE,
isError: mobileData.mobileState===MobileState.ERROR,
isDisconnected: mobileData.mobileState===MobileState.DISCONNECTED,
isConnected: mobileData.mobileState===MobileState.MOBILE_CONNECTED
},
onSenderConnected: (client, clients) => {
mobileData.mobileState = MobileState.MOBILE_CONNECTED;
mobileData.clients = clients;
mobileData.client = client;
dispatch({ type: ACTION_TYPES.SENDER_CONNECTED });
},
onSenderDisconnected: (client, clients) => {
mobileData.clients = clients;
mobileData.client = client;
closeConnection();
mobileData.mobileState = MobileState.INITIALIZING;
mobileData.session = createMessageConnector();
mobileData.session.connect(mobileData.mobileConfig);
dispatch({ type: ACTION_TYPES.SENDER_DISCONNECTED });
},
onError: errorMessage => {
closeConnection();
mobileData.mobileState = MobileState.ERROR;
dispatch({ type: ACTION_TYPES.CONNECTION_ERROR, errorMessage });
},
url: options && options.url,
apikey: options && options.apikey,
securityGroup: options && options.securityGroup,
aes: options && options.aes,
onInput: options && options.onInput,
onInputPermissionResult: options && options.onInputPermissionResult
};
};
export const getExposed = () => {
return {
initData:mobileData.mobileConfig && mobileData.mobileConfig.initData,
sendValue,
sendInitData:mobileData.sendInitData,
setOnchange,
disconnect:mobileData.disconnect
const sendInitData = (mobileConfig, setters, fields, values) => {
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
console.error("sendInitData:requires MOBILE_CONNECTED");
}
mobileData.setters = setters;
mobileData.fields = fields;
mobileData.values = values;
mobileData.mobileConfig = mobileConfig;
mobileData.session.sendInitData(mobileConfig.initData);
dispatch({ type: ACTION_TYPES.SEND_INIT_DATA });
}
if (mobileData.mobileState === MobileState.MOBILE_CONNECTED) {
sendInitData(mobileConfig, setters, fields, values);
return;
}
mobileData.sendInitData = (initDataToSet) => {
if (!initDataToSet) {
return null;
}
const { setters, fields, values, initData } = buildMessageHandlers(dispatch, initDataToSet);
const mobileConfig = mobileData.mobileConfig;
mobileConfig.initData = initData;
sendInitData(mobileConfig, setters, fields, values);
};
mobileData.disconnect = () => {
closeConnection();
dispatch({ type: ACTION_TYPES.CLOSE });
};
closeConnection();
mobileData.mobileState = MobileState.INITIALIZING;
mobileData.setters = setters;
mobileData.fields = fields;
mobileData.values = values;
mobileData.mobileConfig = mobileConfig;
mobileData.session = createMessageConnector();
mobileData.session.connect(mobileConfig);
dispatch({ type: ACTION_TYPES.START_CONNECT });
};
export const invokeOnChange=(field)=>{
if(field && mobileData.onchange){
const {initData,sendValue,sendInitData}=getExposed();
mobileData.onchange({
field,
initData,
sendInitData,
sendValue});
const closeConnection = () => {
if (mobileData.session) {
mobileData.session.disconnect();
mobileData.session = null;
}
}
mobileData.mobileState = MobileState.DISCONNECTED;
};
const isValidInitData=initData => initData && initData.form && initData.form.fields && initData.form.fields.length;
const buildMessageHandlers=(dispatch, initData)=>{
if(typeof initData ==='function'){
initData=initData();
const isValidInitData = initData => initData && initData.form && initData.form.fields && initData.form.fields.length;
const buildMessageHandlers = (dispatch, initData) => {
if (typeof initData === 'function') {
initData = initData();
}
if(!isValidInitData(initData)){
if (!isValidInitData(initData)) {
console.warn("will not send empty form");
return {};
};
const fields=[];
const values=[];
const fieldSetters=[];
const formFields=initData.form.fields.map((f,index)=>{
if(!f){
throw `The form contains a null field:${index} in ${initData.form.title}`;
const fields = [];
const values = [];
const fieldSetters = [];
const formFields = initData.form.fields.map((f, index) => {
if (!f) {
throw `The form contains a null field:${index} in ${initData.form.title}`;
}
const field={id:f.id,label:f.label,value:f.value};
const field = { id: f.id, label: f.label, value: f.value };
fields.push(field);
values.push(f.value);
const s= (value)=>dispatch({type:ACTION_TYPES.SEND_FIELD,value,fields,values,index});
values.push(f.value);
const s = (value) => {
if (mobileData.fields !== fields) {
console.error("SEND_FIELD:fields array is expected to stay unchanged");
return;
}
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
console.error("SEND_FIELD:requires isConnected:" + mobileData.mobileState);
return state;
}
values[index] = value;
fields[index].value = value;
if (fields[index].id) {
mobileData.session.sendValue(fields[index].id, value);
}
else {
mobileData.session.sendValue(null, value, index);
}
dispatch({ type: ACTION_TYPES.SEND_FIELD });
}
fieldSetters.push(s);
if(f.type==='info'){
if (f.type === 'info') {
return f;
}
if(f.operations && f.operations.onInput){
if (f.operations && f.operations.onInput) {
return f;

@@ -139,86 +242,32 @@ }

...f,
operations:{
onInput:value => dispatch({type:ACTION_TYPES.RECEIVED_FIELD,values,fields,value,index})
}
}
});
return {
setters:fieldSetters,
fields,
values,
initData:{
...initData,
form:{...initData.form,
fields:formFields
operations: {
onInput: value => {
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
console.error("RECEIVED_FIELD:requires isConnected:" + mobileData.mobileState);
return;
}
if (mobileData.fields !== fields) {
console.error("RECEIVED_FIELD:fields array is expected to stay unchanged");
return;
}
values[index] = value;
fields[index].value = value;
const field = { ...fields[index], value };
dispatch({ type: ACTION_TYPES.RECEIVED_FIELD, field });
}
};
};
export const startConnect = (dispatch,configData) => {
if(typeof configData ==='function'){
configData=configData();
}
if(!configData){
return;
}
const options=configData.options;
const {setters,fields,values,initData}=buildMessageHandlers(dispatch, configData.initData);
if(!initData){
return null;
}
const mobileConfig={
initData,
onRegistered: next => {
next();
const connectionCode = mobileData.session.buildInputCodeData();
//console.log("encrypted one-time session code [[" + connectionCode + "]]");
dispatch({type:ACTION_TYPES.REGISTERED,connectionCode});
if(options && options.onRegistered){
options.onRegistered();
}
},
onRegisterFailed:errorMessage => {
dispatch({type:ACTION_TYPES.REGISTER_FAILED,errorMessage});
if(options && options.onRegisterFailed){
options.onRegisterFailed();
}
});
return {
setters: fieldSetters,
fields,
values,
initData: {
...initData,
form: {
...initData.form,
fields: formFields
}
},
onSenderConnected:(client, clients) => {
dispatch({type:ACTION_TYPES.SENDER_CONNECTED, clients,client});
},
onSenderDisconnected:(client, clients) => {
dispatch({type:ACTION_TYPES.SENDER_DISCONNECTED,client, clients});
},
onError:errorMessage => {
dispatch({type:ACTION_TYPES.CONNECTION_ERROR,errorMessage});
},
url : options && options.url,
apikey: options && options.apikey,
securityGroup: options && options.securityGroup,
aes:options && options.aes,
onInput:options && options.onInput,
onInputPermissionResult:options && options.onInputPermissionResult
}
};
if(mobileData.mobileState===MobileState.MOBILE_CONNECTED){
dispatch({type:ACTION_TYPES.SEND_INIT_DATA,setters,fields,values,mobileConfig});
return;
}
mobileData.sendInitData=(initDataToSet)=>{
if(!initDataToSet){
return null;
}
const {setters,fields,values,initData}=buildMessageHandlers(dispatch, initDataToSet);
const mobileConfig=mobileData.mobileConfig;
mobileConfig.initData=initData;
dispatch({type:ACTION_TYPES.SEND_INIT_DATA, setters,fields,values,mobileConfig});
};
mobileData.disconnect=()=>{
closeConnection();
dispatch({type:ACTION_TYPES.CLOSE});
};
closeConnection();
dispatch({type:ACTION_TYPES.START_CONNECT,mobileConfig,setters,fields,values});
};

@@ -228,240 +277,102 @@

const processStartConnect = (state, action)=>{
const {mobileConfig,setters,fields,values}=action;
mobileData.mobileState=MobileState.INITIALIZING;
mobileData.setters=setters;
mobileData.fields=fields;
mobileData.values=values;
mobileData.mobileConfig=mobileConfig;
mobileData.session=createMessageConnector();
mobileData.session.connect(mobileConfig);
return {...state,
errorMessage:'',
field:null,
isLoading:true,
isReady:false,
isError:false,
isDisconnected:false,
isConnected:false,
senders: []
};
}
const processSendInitData = (state, action)=>{
if (mobileData.mobileState!==MobileState.MOBILE_CONNECTED) {
console.error("sendInitData:requires MOBILE_CONNECTED");
return state;
}
const {mobileConfig,setters,fields,values}=action;
mobileData.setters=setters;
mobileData.fields=fields;
mobileData.values=values;
mobileData.mobileConfig=mobileConfig;
mobileData.session.sendInitData(mobileConfig.initData);
return {...state,
field:null
};
}
const processRegistered = (state, action)=>{
const {connectionCode}=action;
mobileData.mobileState=MobileState.WAITING_FOR_MOBILE;
return {...state,
connectionCode,
isLoading:false,
isReady:true,
isError:false,
isDisconnected:false,
isConnected:false,
};
};
const processError=(state, action)=>{
const {errorMessage}=action;
mobileData.mobileState=MobileState.ERROR;
closeConnection();
return {...state,
errorMessage,
isError:true,
isLoading:false,
isReady:false,
isDisconnected:true,
isConnected:false,
field:null
};
};
const processSenderConnected = (state, action) => {
const {clients}=action;
mobileData.mobileState=MobileState.MOBILE_CONNECTED;
return {...state,
clients,
isConnected:true,
isLoading:false,
isReady: false,
isError:false,
isDisconnected:false
};
}
const processSenderDisconnected = (state, action) => {
closeConnection();
mobileData.mobileState=MobileState.INITIALIZING;
mobileData.session=createMessageConnector();
mobileData.session.connect(mobileData.mobileConfig);
return {...state,
errorMessage:'',
field:null,
isLoading:true,
isReady:false,
isError:false,
isDisconnected:false,
isConnected:false,
senders: []
};
};
const processReceivedField = (state, action)=>{
if(mobileData.mobileState!==MobileState.MOBILE_CONNECTED){
console.error("RECEIVED_FIELD:requires isConnected:"+mobileData.mobileState);
return state;
}
const {values,fields,value,index}=action;
if(mobileData.fields!==fields){
console.error("RECEIVED_FIELD:fields array is expected to stay unchanged");
return state;
}
values[index]=value;
fields[index].value=value;
const field={...fields[index],value};
return {...state,field};
};
const processSendField=(state, action)=>{
const { values, fields, index, value } = action;
if (mobileData.fields !== fields) {
console.error("SEND_FIELD:fields array is expected to stay unchanged");
state;
}
if(mobileData.mobileState!==MobileState.MOBILE_CONNECTED){
console.error("SEND_FIELD:requires isConnected:"+mobileData.mobileState);
return state;
}
values[index] = value;
fields[index].value = value;
mobileData.session.sendInputMessage(value, index);
return { ...state };
};
const processClose=(state, action)=>{
mobileData.mobileState=MobileState.DISCONNECTED;
closeConnection();
return {...state,
isError:false,
isLoading:false,
isReady:false,
isDisconnected:true,
isConnected:false,
field:null
};
}
export const reducer = (state, action)=>{
switch(action.type){
export const reducer = (state, action) => {
switch (action.type) {
case ACTION_TYPES.START_CONNECT:
return processStartConnect(state, action);
case ACTION_TYPES.SEND_INIT_DATA:
return processSendInitData(state, action);
case ACTION_TYPES.SEND_INIT_DATA:
state = { ...state, errorMessage: '', field: null };
break;
case ACTION_TYPES.REGISTERED:
return processRegistered(state, action);
state = { ...state, errorMessage: '', field: null, connectionCode: action.connectionCode };
break;
case ACTION_TYPES.RECEIVED_FIELD:
state = { ...state, field: action.field };
break;
case ACTION_TYPES.CONNECTION_ERROR:
return processError(state, action);
case ACTION_TYPES.REGISTER_FAILED:
return processError(state, action);
state = { ...state, errorMessage: action.errorMessage };
break;
case ACTION_TYPES.SENDER_CONNECTED:
return processSenderConnected(state,action);
case ACTION_TYPES.SENDER_DISCONNECTED:
return processSenderDisconnected(state,action);
case ACTION_TYPES.RECEIVED_FIELD:
return processReceivedField(state,action);
case ACTION_TYPES.SENDER_DISCONNECTED:
case ACTION_TYPES.SEND_FIELD:
return processSendField(state,action);
case ACTION_TYPES.CLOSE:
return processClose(state,action);
default:
return state;
};
default:
};
return { ...state, ...getMobileDataState() };
};
const getDefaultQRCodeSize=()=>{
if(!window){
return 400;
const getDefaultQRCodeSize = () => {
if (!window) {
return 400;
}
let size = window.innerWidth-10;
return size>400?400:size;
let size = window.innerWidth - 10;
return size > 400 ? 400 : size;
};
const DefaultQRCodeContainer=({children})=>(
const DefaultQRCodeContainer = ({ children }) => (
<div style={styles.barcode}>
{children}
</div>
{children}
</div>
);
const DefaultLabelContainer=({children})=>(
const DefaultLabelContainer = ({ children }) => (
<div style={styles.label}>
{children}
</div>
{children}
</div>
);
export const displayQRCode=({
connectionCode,
level='H',
isReady=false,
isLoading=false,
size=getDefaultQRCodeSize(),
container=DefaultQRCodeContainer,
children=(<DefaultLabelContainer> Scan with <a href="https://globalinput.co.uk/global-input-app/get-app" target="_blank"> Global Input App</a></DefaultLabelContainer>)
})=>{
if(isReady){
if(connectionCode){
return container({children:(
export const displayQRCode = ({
connectionCode,
level = 'H',
isReady = false,
isLoading = false,
size = getDefaultQRCodeSize(),
container = DefaultQRCodeContainer,
children = (<DefaultLabelContainer> Scan with <a href="https://globalinput.co.uk/global-input-app/get-app" target="_blank"> Global Input App</a></DefaultLabelContainer>)
}) => {
if (isReady) {
if (connectionCode) {
return container({
children: (
<>
<QRCode value={connectionCode} level={level} size={size}/>
{children}
<QRCode value={connectionCode} level={level} size={size} />
{children}
</>
)});
}
else{
console.log("connectionCode is not set yet");
}
}
else if(isLoading){
return container({children:(
)
});
}
else {
console.log("connectionCode is not set yet");
}
}
else if (isLoading) {
return container({
children: (
<>
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50">
<path fill="#C779D0" d="M25,5A20.14,20.14,0,0,1,45,22.88a2.51,2.51,0,0,0,2.49,2.26h0A2.52,2.52,0,0,0,50,22.33a25.14,25.14,0,0,0-50,0,2.52,2.52,0,0,0,2.5,2.81h0A2.51,2.51,0,0,0,5,22.88,20.14,20.14,0,0,1,25,5Z">
<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.5s" repeatCount="indefinite"/>
</path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50">
<path fill="#C779D0" d="M25,5A20.14,20.14,0,0,1,45,22.88a2.51,2.51,0,0,0,2.49,2.26h0A2.52,2.52,0,0,0,50,22.33a25.14,25.14,0,0,0-50,0,2.52,2.52,0,0,0,2.5,2.81h0A2.51,2.51,0,0,0,5,22.88,20.14,20.14,0,0,1,25,5Z">
<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.5s" repeatCount="indefinite" />
</path>
</svg>
</>
)});
}
return null;
)
});
}
return null;
}
const styles={
barcode:{
backgroundColor:"white",
padding:20,
display:"flex",
flexDirection:"column",
justifyContent:"flex-start",
alignItems:"center"
const styles = {
barcode: {
backgroundColor: "white",
padding: 20,
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
alignItems: "center"
},
label:{
paddingTop:20,
color:"#A9C8E6", //#4880ED
label: {
paddingTop: 20,
color: "#A9C8E6", //#4880ED
}

@@ -468,0 +379,0 @@ }

export * from "global-input-message";
export * from "global-input-message";
import useGlobalInputApp, {MobileState} from './useGlobalInputApp';
import useGlobalInputApp from './useGlobalInputApp';
export {useGlobalInputApp,MobileState};
export { useGlobalInputApp };
import React, { useReducer, useEffect, useRef, useMemo, useCallback } from "react";
import {reducer,
initialState,startConnect,
getFlags,invokeOnChange,getExposed,displayQRCode} from './globalinput';
import * as globalInput from './globalinput';
export default (configData, dependencies) => {
const [{
connectionCode,
errorMessage,
field
}, dispatch] = useReducer(reducer, initialState);
const {isLoading,isReady,isError,isDisconnected,isConnected}=getFlags();
errorMessage,
field,
isLoading,
isReady,
isError,
isDisconnected,
isConnected,
initData,
}, dispatch] = useReducer(globalInput.reducer, globalInput.initialState);
useEffect(() => {
startConnect(dispatch, configData);
globalInput.startConnect(dispatch, configData);
}, dependencies ? dependencies : []); //default connect once for the component
useEffect(()=>invokeOnChange(field),[field]);
const ConnectQR=useCallback(({level,size,container,children})=>displayQRCode({level,size,container,connectionCode,isReady,isLoading,children}),[connectionCode,isReady,isLoading]);
const {initData,sendValue,sendInitData,setOnchange,disconnect}=getExposed();
useEffect(() => {
if (field && globalInput.mobileData.onchange) {
globalInput.mobileData.onchange({
field,
initData,
sendInitData: globalInput.mobileData.sendInitData,
sendValue: globalInput.sendValue
});
}
}, [field]);
const ConnectQR = useCallback(({ level, size, container, children }) => {
globalInput.displayQRCode({ level, size, container, connectionCode, isReady, isLoading, children });
}, [connectionCode, isReady, isLoading]);
return {
ConnectQR,
connectionCode,
field,
field,
errorMessage,

@@ -40,9 +50,8 @@ isLoading,

initData,
sendValue,
sendInitData,
setOnchange,
disconnect
sendValue: globalInput.sendValue,
sendInitData: globalInput.mobileData.sendInitData,
disconnect: globalInput.mobileData.disconnect,
setOnchange: globalInput.setOnchange,
};
};

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc