Socket
Socket
Sign inDemoInstall

global-input-react

Package Overview
Dependencies
37
Maintainers
1
Versions
185
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 4.0.3 to 4.1.0

6

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

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

await waitForNextUpdate();
expect(result.current.mobileState).toBe(_index.MobileState.WAITING_FOR_MOBILE); //const {findByTestId}=render(<div>{connectionMessage}</div>); //display QR Code here
expect(result.current.isReady).toBe(true); //const {findByTestId}=render(<div>{connectionMessage}</div>); //display QR Code here
// const {code, level,size}=await getQRCodeValues({findByTestId}); //qrcode.react module is mocked

@@ -82,3 +82,3 @@

const contentSendByDevice = "send by device app";
result.current.setFieldValueById(initData.form.fields[0].id, contentSendByDevice);
result.current.sendValue(initData.form.fields[0].id, contentSendByDevice);
const inputMessageOnMobile = await input.get();

@@ -109,3 +109,3 @@ expect(inputMessageOnMobile.data.value).toEqual(contentSendByDevice);

fields = (0, _index.createWaitForFieldMessages)(initData2.form.fields);
result.current.setInitData(initData2);
result.current.sendInitData(initData2);
const initDataMessage = await input.get();

@@ -112,0 +112,0 @@ initDataMessage.initData && assertInitData(initDataMessage.initData, initData2);

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

});
exports.displayWhen = exports.displayWhen2 = exports.displayCode = exports.startConnect = exports.sendInitData = exports.setFieldValueById = exports.onFieldChanged = exports.setConnector = exports.closeConnection = exports.setOnFieldChanged = exports.getInitDataID = exports.getSetters = exports.getFields = exports.getValues = exports.initialData = void 0;
exports.displayQRCode = exports.reducer = exports.startConnect = exports.invokeOnChange = exports.getExposed = exports.getFlags = exports.initialState = void 0;

@@ -13,4 +13,2 @@ var _react = _interopRequireDefault(require("react"));

var _constants = require("./constants");
var _globalInputMessage = require("global-input-message");

@@ -20,67 +18,103 @@

const createInitialData = () => ({
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 initialState = {
connectionCode: null,
errorMessage: null,
field: null
};
exports.initialState = initialState;
const mobileData = {
session: null,
mobileState: MobileState.INITIALIZING,
fields: [],
values: [],
setters: [],
initData: null
});
const initialData = {
connector: null,
data: createInitialData(),
onFieldChanged: () => {}
clients: [],
mobileConfig: null,
sendInitData: () => {},
disconnect: () => {},
onchange: () => {}
};
exports.initialData = initialData;
const getValues = mobile => mobile.current.data && mobile.current.data.values;
const sendValue = (fieldId, valueToSet) => {
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
return;
}
exports.getValues = getValues;
if (mobileData.fields && mobileData.fields.length) {
for (let [index, field] of mobileData.fields.entries()) {
if (field.id === fieldId) {
mobileData.setters[index](valueToSet);
break;
}
}
const getFields = mobile => mobile.current.data && mobile.current.data.fields;
;
}
};
exports.getFields = getFields;
const getSetters = mobile => mobile.current.data && mobile.current.data.setters;
exports.getSetters = getSetters;
const getInitDataID = mobile => mobile.current.data && mobile.current.data.initData && mobile.current.data.initData.id;
exports.getInitDataID = getInitDataID;
const setOnFieldChanged = (mobile, onFieldChanged) => {
mobile.current.onFieldChanged = onFieldChanged;
const closeConnection = () => {
if (mobileData.session) {
mobileData.session.disconnect();
mobileData.session = null;
mobileData.mobileState = MobileState.DISCONNECTED;
}
};
exports.setOnFieldChanged = setOnFieldChanged;
const setOnchange = onchange => mobileData.onchange = onchange;
const closeConnection = mobile => {
if (mobile.current.connector) {
mobile.current.connector.disconnect();
mobile.current.connector = null;
mobile.current.data = createInitialData();
mobile.current.onFieldChanged = () => {};
}
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.closeConnection = closeConnection;
exports.getFlags = getFlags;
const setConnector = (mobile, connector) => {
mobile.current.connector = connector;
mobile.current.data = createInitialData();
mobile.current.onFieldChanged = () => {};
const getExposed = () => {
return {
initData: mobileData.mobileConfig && mobileData.mobileConfig.initData,
sendValue,
sendInitData: mobileData.sendInitData,
setOnchange,
disconnect: mobileData.disconnect
};
};
exports.setConnector = setConnector;
exports.getExposed = getExposed;
const onFieldChanged = (mobile, field, setFieldValueById, setInitData) => {
if (field && mobile.current.onFieldChanged) {
mobile.current.onFieldChanged({
const invokeOnChange = field => {
if (field && mobileData.onchange) {
const {
initData,
sendValue,
sendInitData
} = getExposed();
mobileData.onchange({
field,
values: getValues(mobile),
setFieldValueById,
setInitData,
initDataID: getInitDataID(mobile)
initData,
sendInitData,
sendValue
});

@@ -90,45 +124,76 @@ }

exports.onFieldChanged = onFieldChanged;
exports.invokeOnChange = invokeOnChange;
const setFieldValueById = (mobile, mobileState, fieldId, valueToSet) => {
if (mobileState !== _constants.MobileState.MOBILE_CONNECTED) {
return;
const isValidInitData = initData => initData && initData.form && initData.form.fields && initData.form.fields.length;
const buildMessageHandlers = (dispatch, initData) => {
if (typeof initData === 'function') {
initData = initData();
}
const {
setters,
fields
} = mobile.current.data;
if (!isValidInitData(initData)) {
console.warn("will not send empty form");
return {};
}
if (fields && fields.length) {
for (let [index, field] of fields.entries()) {
if (field.id === fieldId) {
setters[index](valueToSet);
break;
}
;
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);
exports.setFieldValueById = setFieldValueById;
const s = value => dispatch({
type: ACTION_TYPES.SEND_FIELD,
value,
fields,
values,
index
});
const sendInitData = (mobile, dispatch, initData) => {
const data = processInitData(mobile, dispatch, initData);
fieldSetters.push(s);
if (!data) {
return null;
}
if (f.type === 'info') {
return f;
}
mobile.current.data = data;
dispatch({
type: _constants.ACTION_TYPES.SEND_INIT_DATA
if (f.operations && f.operations.onInput) {
return f;
}
return { ...f,
operations: {
onInput: value => dispatch({
type: ACTION_TYPES.RECEIVED_FIELD,
values,
fields,
value,
index
})
}
};
});
mobile.current.connector.sendInitData(data.initData);
return {
setters: fieldSetters,
fields,
values,
initData: { ...initData,
form: { ...initData.form,
fields: formFields
}
}
};
};
exports.sendInitData = sendInitData;
const startConnect = (mobile, dispatch, configData) => {
const startConnect = (dispatch, configData) => {
if (typeof configData === 'function') {

@@ -143,5 +208,10 @@ configData = configData();

const options = configData.options;
const data = processInitData(mobile, dispatch, configData.initData);
const {
setters,
fields,
values,
initData
} = buildMessageHandlers(dispatch, configData.initData);
if (!data) {
if (!initData) {
return null;

@@ -151,9 +221,9 @@ }

const mobileConfig = {
initData: data.initData,
initData,
onRegistered: next => {
next();
const connectionCode = mobile.current.connector.buildInputCodeData();
console.log("getting[[" + connectionCode + "]]");
const connectionCode = mobileData.session.buildInputCodeData(); //console.log("encrypted one-time session code [[" + connectionCode + "]]");
dispatch({
type: _constants.ACTION_TYPES.REGISTERED,
type: ACTION_TYPES.REGISTERED,
connectionCode

@@ -168,6 +238,5 @@ });

dispatch({
type: _constants.ACTION_TYPES.REGISTER_FAILED,
type: ACTION_TYPES.REGISTER_FAILED,
errorMessage
});
closeConnection(mobile);

@@ -178,13 +247,14 @@ if (options && options.onRegisterFailed) {

},
onSenderConnected: (sender, senders) => {
onSenderConnected: (client, clients) => {
dispatch({
type: _constants.ACTION_TYPES.SENDER_CONNECTED,
senders,
sender
type: ACTION_TYPES.SENDER_CONNECTED,
clients,
client
});
},
onSenderDisconnected: (sender, senders) => {
closeConnection(mobile);
onSenderDisconnected: (client, clients) => {
dispatch({
type: _constants.ACTION_TYPES.SENDER_DISCONNECTED
type: ACTION_TYPES.SENDER_DISCONNECTED,
client,
clients
});

@@ -194,3 +264,3 @@ },

dispatch({
type: _constants.ACTION_TYPES.ON_CONNECTION_ERROR,
type: ACTION_TYPES.CONNECTION_ERROR,
errorMessage

@@ -207,20 +277,50 @@ });

if (configData.mobile && configData.mobile.connector) {
setConnector(mobile, configData.mobile.connector);
mobile.current.data = data;
mobile.current.onFieldChanged = configData.onFieldChanged;
if (mobileData.mobileState === MobileState.MOBILE_CONNECTED) {
dispatch({
type: _constants.ACTION_TYPES.ATTACH_CONNECT
type: ACTION_TYPES.SEND_INIT_DATA,
setters,
fields,
values,
mobileConfig
});
mobile.current.connector.sendInitData(data.initData);
} else {
closeConnection(mobile, configData);
mobile.current.data = data;
mobile.current.onFieldChanged = configData.onFieldChanged;
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: _constants.ACTION_TYPES.START_CONNECT
type: ACTION_TYPES.SEND_INIT_DATA,
setters,
fields,
values,
mobileConfig
});
mobile.current.connector = (0, _globalInputMessage.createMessageConnector)();
mobile.current.connector.connect(mobileConfig);
}
};
mobileData.disconnect = () => {
closeConnection();
dispatch({
type: ACTION_TYPES.CLOSE
});
};
closeConnection();
dispatch({
type: ACTION_TYPES.START_CONNECT,
mobileConfig,
setters,
fields,
values
});
};

@@ -230,158 +330,291 @@

const processInitData = (mobile, dispatch, initData) => {
if (typeof initData === 'function') {
initData = initData();
}
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 = (0, _globalInputMessage.createMessageConnector)();
mobileData.session.connect(mobileConfig);
return { ...state,
errorMessage: '',
field: null,
isLoading: true,
isReady: false,
isError: false,
isDisconnected: false,
isConnected: false,
senders: []
};
};
if (!isValidInitData(initData)) {
console.warn("will not send empty form");
return null;
const processSendInitData = (state, action) => {
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
console.error("sendInitData:requires MOBILE_CONNECTED");
return state;
}
;
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 {
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 field = {
id: f.id,
label: f.label,
value: f.value
};
fields.push(field);
values.push(f.value);
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 s = value => {
values[index] = value;
fields[index].value = value;
dispatch({
type: _constants.ACTION_TYPES.SEND_FIELD
});
mobile.current.connector.sendInputMessage(value, index);
};
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
};
};
fieldSetters.push(s);
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 (f.type === 'info') {
return f;
}
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: []
};
};
if (f.operations && f.operations.onInput) {
return f;
}
const processReceivedField = (state, action) => {
if (mobileData.mobileState !== MobileState.MOBILE_CONNECTED) {
console.error("RECEIVED_FIELD:requires isConnected:" + mobileData.mobileState);
return state;
}
return { ...f,
operations: {
onInput: value => {
values[index] = value;
fields[index].value = value;
const nf = { ...fields[index],
value
};
dispatch({
type: _constants.ACTION_TYPES.RECEIVED_FIELD,
field: nf
});
}
}
};
});
return {
setters: fieldSetters,
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,
initData: { ...initData,
form: { ...initData.form,
fields: formFields
}
}
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 isValidInitData = initData => initData && initData.form && initData.form.fields && initData.form.fields.length;
const processClose = (state, action) => {
mobileData.mobileState = MobileState.DISCONNECTED;
closeConnection();
return { ...state,
isError: false,
isLoading: false,
isReady: false,
isDisconnected: true,
isConnected: false,
field: null
};
};
const DefaultQRCodeContainer = ({
children
}) => /*#__PURE__*/_react.default.createElement("div", {
style: styles.barcode
}, children);
const reducer = (state, action) => {
switch (action.type) {
case ACTION_TYPES.START_CONNECT:
return processStartConnect(state, action);
const DefaultLabelContainer = ({
children
}) => /*#__PURE__*/_react.default.createElement("div", {
style: styles.label
}, children);
case ACTION_TYPES.SEND_INIT_DATA:
return processSendInitData(state, action);
const displayCode = (mobileState, connectionCode) => {
let qrCodeSize = window.innerWidth - 10;
case ACTION_TYPES.REGISTERED:
return processRegistered(state, action);
if (qrCodeSize > 400) {
qrCodeSize = 400;
}
case ACTION_TYPES.CONNECTION_ERROR:
return processError(state, action);
let QRCodeContainer = DefaultQRCodeContainer;
let LabelContainer = DefaultLabelContainer;
case ACTION_TYPES.REGISTER_FAILED:
return processError(state, action);
if (mobileState === _constants.MobileState.INITIALIZING) {
return /*#__PURE__*/_react.default.createElement(QRCodeContainer, null, /*#__PURE__*/_react.default.createElement("svg", {
xmlns: "http://www.w3.org/2000/svg",
width: "50",
height: "50",
viewBox: "0 0 50 50"
}, /*#__PURE__*/_react.default.createElement("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"
}, /*#__PURE__*/_react.default.createElement("animateTransform", {
attributeName: "transform",
type: "rotate",
from: "0 25 25",
to: "360 25 25",
dur: "0.5s",
repeatCount: "indefinite"
}))));
}
case ACTION_TYPES.SENDER_CONNECTED:
return processSenderConnected(state, action);
if (mobileState === _constants.MobileState.WAITING_FOR_MOBILE) {
if (!connectionCode) {
return /*#__PURE__*/_react.default.createElement(QRCodeContainer, null, "Empty connection code");
}
case ACTION_TYPES.SENDER_DISCONNECTED:
return processSenderDisconnected(state, action);
return /*#__PURE__*/_react.default.createElement(QRCodeContainer, null, /*#__PURE__*/_react.default.createElement(_qrcode.default, {
value: connectionCode,
level: "H",
size: qrCodeSize
}), /*#__PURE__*/_react.default.createElement(LabelContainer, null, "Scan with", /*#__PURE__*/_react.default.createElement("a", {
href: "https://globalinput.co.uk/global-input-app/get-app",
target: "_blank"
}, " Global Input App")));
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 null;
;
};
exports.displayCode = displayCode;
exports.reducer = reducer;
const displayWhen2 = (children, mobileState, st1, st2) => {
if (mobileState !== st1 && mobileState !== st2) {
return null;
const getDefaultQRCodeSize = () => {
if (!window) {
return 400;
}
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, children);
let size = window.innerWidth - 10;
return size > 400 ? 400 : size;
};
exports.displayWhen2 = displayWhen2;
const DefaultQRCodeContainer = ({
children
}) => /*#__PURE__*/_react.default.createElement("div", {
style: styles.barcode
}, children);
const displayWhen = (children, mobileState, st) => {
if (mobileState !== st) {
return null;
const DefaultLabelContainer = ({
children
}) => /*#__PURE__*/_react.default.createElement("div", {
style: styles.label
}, children);
const displayQRCode = ({
connectionCode,
level = 'H',
isReady = false,
isLoading = false,
size = getDefaultQRCodeSize(),
container = DefaultQRCodeContainer,
children = /*#__PURE__*/_react.default.createElement(DefaultLabelContainer, null, " Scan with ", /*#__PURE__*/_react.default.createElement("a", {
href: "https://globalinput.co.uk/global-input-app/get-app",
target: "_blank"
}, " Global Input App"))
}) => {
if (isReady) {
if (connectionCode) {
return container({
children: /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_qrcode.default, {
value: connectionCode,
level: level,
size: size
}), children)
});
} else {
console.log("connectionCode is not set yet");
}
} else if (isLoading) {
return container({
children: /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("svg", {
xmlns: "http://www.w3.org/2000/svg",
width: "50",
height: "50",
viewBox: "0 0 50 50"
}, /*#__PURE__*/_react.default.createElement("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"
}, /*#__PURE__*/_react.default.createElement("animateTransform", {
attributeName: "transform",
type: "rotate",
from: "0 25 25",
to: "360 25 25",
dur: "0.5s",
repeatCount: "indefinite"
}))))
});
}
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, children);
return null;
};
exports.displayWhen = displayWhen;
exports.displayQRCode = displayQRCode;
const styles = {

@@ -388,0 +621,0 @@ barcode: {

@@ -6,8 +6,2 @@ "use strict";

});
Object.defineProperty(exports, "MobileState", {
enumerable: true,
get: function () {
return _constants.MobileState;
}
});
exports.default = void 0;

@@ -17,8 +11,4 @@

var _constants = require("./constants");
var _globalinput = require("./globalinput");
var _reducer = _interopRequireWildcard(require("./reducer"));
var globalInput = _interopRequireWildcard(require("./globalinput"));
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

@@ -30,62 +20,54 @@

const [{
mobileState,
connectionCode,
field,
errorMessage
}, dispatch] = (0, _react.useReducer)(_reducer.default, _reducer.initialState);
const mobile = (0, _react.useRef)(globalInput.initialData);
errorMessage,
field
}, dispatch] = (0, _react.useReducer)(_globalinput.reducer, _globalinput.initialState);
const {
isLoading,
isReady,
isError,
isDisconnected,
isConnected
} = (0, _globalinput.getFlags)();
(0, _react.useEffect)(() => {
(0, _globalinput.startConnect)(dispatch, configData);
}, dependencies ? dependencies : []); //default connect once for the component
const setOnFieldChanged = onFieldChanged => {
globalInput.setOnFieldChanged(mobile, onFieldChanged);
};
const disconnect = () => {
globalInput.closeConnection(mobile);
};
const setFieldValueById = (0, _react.useCallback)((fieldId, valueToSet) => {
globalInput.setFieldValueById(mobile, mobileState, fieldId, valueToSet);
}, [mobile, mobileState]);
const setInitData = (0, _react.useCallback)(initData => {
globalInput.sendInitData(mobile, dispatch, initData);
}, [mobile, dispatch]);
(0, _react.useEffect)(() => {
globalInput.onFieldChanged(mobile, field, setFieldValueById, setInitData); //should only executed when the field is changed
}, [field]);
(0, _react.useEffect)(() => {
globalInput.startConnect(mobile, dispatch, configData);
}, dependencies ? dependencies : []);
const connectionMessage = (0, _react.useMemo)(() => globalInput.displayCode(mobileState, connectionCode), [connectionCode, mobileState]);
const WhenWaiting = (0, _react.useCallback)(({
(0, _react.useEffect)(() => (0, _globalinput.invokeOnChange)(field), [field]);
const ConnectQR = (0, _react.useCallback)(({
level,
size,
container,
children
}) => globalInput.displayWhen2(children, mobileState, _constants.MobileState.WAITING_FOR_MOBILE, _constants.MobileState.INITIALIZING), [mobileState === _constants.MobileState.WAITING_FOR_MOBILE || mobileState === _constants.MobileState.INITIALIZING]);
const WhenConnected = (0, _react.useCallback)(({
}) => (0, _globalinput.displayQRCode)({
level,
size,
container,
connectionCode,
isReady,
isLoading,
children
}) => globalInput.displayWhen(children, mobileState, _constants.MobileState.MOBILE_CONNECTED), [mobileState === _constants.MobileState.MOBILE_CONNECTED]);
const WhenDisconnected = (0, _react.useCallback)(({
children
}) => globalInput.displayWhen(children, mobileState, _constants.MobileState.DISCONNECTED), [mobileState === _constants.MobileState.DISCONNECTED]);
const WhenError = (0, _react.useCallback)(({
children
}) => globalInput.displayWhen(children, mobileState, _constants.MobileState.ERROR), [mobileState === _constants.MobileState.ERROR]);
}), [connectionCode, isReady, isLoading]);
const {
initData,
sendValue,
sendInitData,
setOnchange,
disconnect
} = (0, _globalinput.getExposed)();
return {
mobileState,
ConnectQR,
connectionCode,
field,
errorMessage,
initData: mobile.current.data && mobile.current.data.initData,
mobile: mobile.current,
disconnect,
setInitData,
connectionMessage,
values: globalInput.getValues(mobile),
fields: globalInput.getFields(mobile),
field,
setters: globalInput.getSetters(mobile),
setFieldValueById,
setOnFieldChanged,
WhenWaiting,
WhenConnected,
WhenDisconnected,
WhenError,
initDataID: globalInput.getInitDataID(mobile)
isLoading,
isReady,
isError,
isDisconnected,
isConnected,
initData,
sendValue,
sendInitData,
setOnchange,
disconnect
};

@@ -92,0 +74,0 @@ };

declare module 'global-input-react' {
declare module 'global-input-react' {
export * from 'global-input-message';
import {InitData,FormField,MessageReceiver,FieldValue} from 'global-input-message';
import { InitData, FormField, MessageReceiver, FieldValue } from 'global-input-message';
type ConfigDataCreator = () => ConfigData;
type ConfigDataCreator=()=>ConfigData;
export const MobileState:MobileState;
export enum MobileState {
INITIALIZING=1,
DISCONNECTED,
ERROR,
WAITING_FOR_MOBILE,
MOBILE_CONNECTED
}
export function useGlobalInputApp(configData?: ConfigData | ConfigDataCreator, dependencies?: ReadonlyArray<any>): GlobalInputData;
type OnchangeFunction = (evt: FieldChanged) => void;
export function useGlobalInputApp(configData?:ConfigData|ConfigDataCreator, dependencies?:ReadonlyArray<any>):GlobalInputData;
type OnFieldChangedFunction=(evt:FieldChanged)=>void;
interface ConfigData {
initData?:InitData|InitDataCreator;
onFieldChanged?:OnFieldChangedFunction;
options?:ConnectOptions;
initData?: InitData | InitDataCreator;
onchange?: OnchangeFunction;
options?: ConnectOptions;
session?: object;
}
interface ConnectOptions {
apikey?:string;
securityGroup?:string;
client?:string;
url?:string;
apikey?: string;
securityGroup?: string;
client?: string;
url?: string;
}
interface FieldChanged {
field:FormField;
values:FieldValue[];
setFieldValueById:SetFieldValueByIdFunction;
setInitData:SetInitDataFunction;
initDataID:string;
field: FormField;
initData:InitData;
sendInitData:SetInitDataFunction;
sendValue:SetFieldValueByIdFunction
}
type InitDataCreator=()=>InitData;
type InitDataCreator = () => InitData;
interface FormOperation{
onInput:(value:any) => void;
}
type SetFieldValueByIdFunction=(fieldId:string, valueToSet:FieldValue)=>void;
type SetInitDataFunction=(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"
};
interface GlobalInputData {
mobileState:1|2|3|4|5;
connectionCode:string;
errorMessage:string;
mobile:object;
disconnect:()=>void;
setInitData:SetInitDataFunction;
connectionMessage:React.FC<void>;
values:FieldValue[];
field:FormField;
fields:FormField[];
setters:((value:any)=>void)[];
WhenWaiting:WhenFunction;
WhenConnected:WhenFunction;
WhenDisconnected:WhenFunction;
WhenError:WhenFunction;
DisplayMobileConnect:WhenFunction;
setFieldValueById:SetFieldValueByIdFunction;
setOnFieldChanged:(onFieldChanged:OnFieldChangedFunction)=>void;
initDataID:string;
ConnectQR: FunctionComponent<ConnectQRProps>,
connectionCode: string;
field: FormField;
errorMessage: string;
isLoading: boolean;
isReady: boolean;
isError: boolean;
isDisconnected: boolean;
isConnected: boolean;
initData:InitData;
sendValue:SetFieldValueByIdFunction;
sendInitData:SetInitDataFunction;
setOnchange: (onchange: OnchangeFunction) => void;
disconnect: () => void;
}
type WhenFunction=(props:any)=>any;
export function generateRandomString(length?: number): string;
export function encrypt(content: string, password: string): string;
export function decrypt(content: string, password: string): string;
export function generateRandomString(length?:number):string;
export function encrypt(content:string, password:string):string;
export function decrypt(content:string, password:string):string;
}
{
"name": "global-input-react",
"version": "4.0.3",
"version": "4.1.0",
"description": "global input react component",

@@ -5,0 +5,0 @@ "repository": {

@@ -1,11 +0,7 @@

This React JS library allows you to introduce interoperability between your mobile app and your React applications. With this library, you will be able to receive mobile events from within your React applications, implementing interoperability logic in your device application instead of separating it across the two interacting applications. This mechanism offers a range of features that include
[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 Personal Storage](https://globalinput.co.uk/global-input-app/mobile-personal-storage),
[Mobile Encryption & Signing](https://globalinput.co.uk/global-input-app/mobile-content-encryption),
[Mobile Content Transfer](https://globalinput.co.uk/global-input-app/mobile-content-transfer).
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 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

@@ -16,73 +12,216 @@

```
## Usage
```JavaScript
The custom React hook ```useGlobalInputApp``` allows a React component specify a mobile user interface declaratively.
import {useGlobalInputApp} from 'global-input-react';
The following example application defines a login screen allowing user to use their mobile to carry out login operation using their mobile.
```
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.
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',
};
import {useGlobalInputApp} from 'global-input-react';
import React, {useState} from 'react';
const passwordField={
id:'password',
label: 'Password'
};
const initData={
form:{
title: 'Sign In',
id: '###username###@mycompany.com',
fields: [{
label: 'Username',
id: 'username'
},{
label: 'Password',
id: 'password',
},{
id: 'login',
label: 'Sign In',
type: 'button'
}]
}
};
export default ({login}){
const [username,setUser] = useState('');
const [password,setPassword] = useState('');
const {connectionMessage,setOnFieldChanged}=useGlobalInputApp({initData});
setOnFieldChanged(({field})=>{
const fds=initData.form.fields;
switch(field.id){
case fds[0].id: setUsername(field.value); break;
case fds[1].id: setPassword(field.value); break;
case fds[2].id: login(username,password);
}
});
return (
<>
{connectionMessage}
</>
);
};
const loginButton={
id: 'login',
label: 'Sign In',
type: 'button'
};
const mobile=useGlobalInputApp({initData:{
form:{
title: 'Sign In',
fields: [usernameField,passwordField,loginButton]
}
}});
```
The content of ```{connectionMessage}``` returned by the hook contains an encrypted QR Code that you can scan to connect to the application. Having connected, your mobile displays the user interface specified in the ```initData``` variable, allowing you to operate on the application. You can use ```setOnFieldChanged``` function to set your callback function to receive the form events through the ```field``` variable.
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``` specifies a form, in which ```id``` is used for autofill operation inside the connected mobile app through filtering the existing data in its encrypted storage. The form contain a set of fields, representing data that device application and the connected mobile need to collaborate on composing. The type of each field defines the related data operation. For example, if the type is "encrypt"/"decrypt", the mobile app initiates the encrypt/decrypt workflow inside the mobile app. This is useful when you would like to secure data stored on other devices or cloud.
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.
```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:
```JavaScript
mobile.setOnchange(({field})=>{
const {id, value}=field;
switch(id){
case usernameField.id:
setUsername(value);
break;
case passwordField.id:
setPassword(value);
break;
case loginButton.id:
login(username,password);
break;
}
});
```
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:
```JavaScript
import React, { useState } from 'react';
...
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).
Other values returned by the ```useGlobalInputApp``` function are listed in the table below:
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:
```JavaScript
Username:
<input type="text" value={username} 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=>{
setPassword(event.target.value);
mobile.sendValue(passwordField.id,event.target.value);
}/>
```
| Attributes | Description |
| ------ | ------ |
| connectionMessage | The variable containing the QR Code for the mobile app to scan to obtain the connection information securely|
|setFieldValueById | The function you can use interact with the form displayed on the connected mobile app.|
|field | The field that contains the id and value of the field sent by the mobile app |
| WhenWaiting | A container React component that you can use it to wrap content that you would like to display only when the application waiting for the user to connect |
| WhenConnected | A container React component that you can use it to wrap content that you would like to display only when a user has connected to the application |
| WhenDisconnected | A container React component that you can use it to wrap content that you would like to display only when a user has connected and then disconnected |
| WhenError | A container React component that you can use it to wrap content that you would like to display when there is an error, you can use {errorMessage} to find out what has happened, for example ```<WhenError>{errorMessage}</WhenError>``` |
| errorMessage | This value wil be populated when an error is raised |
With the above code in place, the content can be updated on both devices while keeping the local and remote values in sync.
## TypeScript support
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",}]
}
});
}
```
Alternatively, you can replace the current React component with a new React component that in turn replace the current ```useGlobalInputApp``` with a new one.
The type definition file is included within the module. You can obtain more information from [this](https://github.com/global-input/test-global-input-app-libs/blob/master/src/test-global-input-react/mobile-and-device-app.test.tsx) end-to-end test example on how to use this library within a TypeScript application.
## 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:
```JavaScript
{mobile.isConnected && (<>
<h1>Mobile Connected</h1>
<div>Please operate on your mobile to provide your crendetial!</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).
## More about Form Element
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'
};
```
As a result, it displays a button 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:
```JavaScript
const contentField={
id: 'content',
label: 'Content',
type: "text",
nLines:5
};
```
You can also send the actual value with the form element to pre-populate the text box:
```JavaScript
const contentField={
id: 'content',
label: 'Content',
type: "text",
nLines:5,
value:"This is a content in the text box"
};
```
## 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:
```JavaScript
mobile.setOnchange(({field})=>{
switch(field.id){
case contentField.id:
setEncryptedContent(field.value);
break;
}
});
```
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:
```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>);
};
```
import React from "react";
import QRCode from "qrcode.react";
import {ACTION_TYPES, MobileState} from './constants';
import { createMessageConnector } from 'global-input-message';
const createInitialData=()=>({
fields:[],
values:[],
setters:[],
initData:null
});
export const initialData={
connector:null,
data:createInitialData(),
onFieldChanged:()=>{},
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
};
export const getValues = mobile => mobile.current.data && mobile.current.data.values;
export const getFields = mobile => mobile.current.data && mobile.current.data.fields;
export const getSetters = mobile => mobile.current.data && mobile.current.data.setters;
export const getInitDataID = mobile => mobile.current.data && mobile.current.data.initData && mobile.current.data.initData.id;
export const setOnFieldChanged=(mobile,onFieldChanged)=>{
mobile.current.onFieldChanged=onFieldChanged;
}
export const closeConnection=(mobile)=>{
if(mobile.current.connector){
mobile.current.connector.disconnect();
mobile.current.connector=null;
mobile.current.data=createInitialData();
mobile.current.onFieldChanged=()=>{};
}
export const initialState={
connectionCode:null,
errorMessage:null,
field:null
};
export const setConnector=(mobile,connector)=>{
mobile.current.connector=connector;
mobile.current.data=createInitialData();
mobile.current.onFieldChanged=()=>{};
};
export const onFieldChanged=(mobile,field,setFieldValueById,setInitData)=>{
if(field && mobile.current.onFieldChanged){
mobile.current.onFieldChanged({
field,
values: getValues(mobile),
setFieldValueById,
setInitData,
initDataID:getInitDataID(mobile)
});
}
const mobileData={
session:null,
mobileState:MobileState.INITIALIZING,
fields:[],
values:[],
setters:[],
clients:[],
mobileConfig:null,
sendInitData:()=>{},
disconnect:()=>{},
onchange:()=>{}
};
export const setFieldValueById=(mobile,mobileState,fieldId, valueToSet)=>{
if(mobileState!==MobileState.MOBILE_CONNECTED){
const sendValue = (fieldId, valueToSet) => {
if(mobileData.mobileState!==MobileState.MOBILE_CONNECTED){
return;
}
const {setters,fields}=mobile.current.data;
if(fields && fields.length){
for(let [index,field] of fields.entries()){
}
if(mobileData.fields && mobileData.fields.length){
for(let [index,field] of mobileData.fields.entries()){
if(field.id===fieldId){
setters[index](valueToSet);
mobileData.setters[index](valueToSet);
break;

@@ -73,81 +60,52 @@ }

}
}
export const sendInitData=(mobile,dispatch,initData)=>{
const data=processInitData(mobile,dispatch, initData);
if(!data){
return null;
}
mobile.current.data=data;
dispatch({type:ACTION_TYPES.SEND_INIT_DATA});
mobile.current.connector.sendInitData(data.initData);
};
export const startConnect =(mobile,dispatch,configData) => {
if(typeof configData ==='function'){
configData=configData();
const closeConnection =()=>{
if(mobileData.session){
mobileData.session.disconnect();
mobileData.session=null;
mobileData.mobileState=MobileState.DISCONNECTED;
}
};
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
};
};
export const getExposed = () => {
return {
initData:mobileData.mobileConfig && mobileData.mobileConfig.initData,
sendValue,
sendInitData:mobileData.sendInitData,
setOnchange,
disconnect:mobileData.disconnect
}
if(!configData){
return;
}
const options=configData.options;
const data=processInitData(mobile,dispatch, configData.initData);
if(!data){
return null;
}
const mobileConfig={
initData:data.initData,
onRegistered: next => {
next();
const connectionCode = mobile.current.connector.buildInputCodeData();
console.log("getting[[" + connectionCode + "]]");
dispatch({type:ACTION_TYPES.REGISTERED,connectionCode});
if(options && options.onRegistered){
options.onRegistered();
}
},
onRegisterFailed:errorMessage => {
dispatch({type:ACTION_TYPES.REGISTER_FAILED,errorMessage});
closeConnection(mobile);
if(options && options.onRegisterFailed){
options.onRegisterFailed();
}
},
onSenderConnected:(sender, senders) => {
dispatch({type:ACTION_TYPES.SENDER_CONNECTED, senders,sender});
},
onSenderDisconnected:(sender, senders) => {
closeConnection(mobile);
dispatch({type:ACTION_TYPES.SENDER_DISCONNECTED});
},
onError:errorMessage => {
dispatch({type:ACTION_TYPES.ON_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(configData.mobile && configData.mobile.connector){
setConnector(mobile,configData.mobile.connector);
mobile.current.data=data;
mobile.current.onFieldChanged=configData.onFieldChanged;
dispatch({type:ACTION_TYPES.ATTACH_CONNECT});
mobile.current.connector.sendInitData(data.initData);
};
export const invokeOnChange=(field)=>{
if(field && mobileData.onchange){
const {initData,sendValue,sendInitData}=getExposed();
mobileData.onchange({
field,
initData,
sendInitData,
sendValue});
}
else{
closeConnection(mobile,configData);
mobile.current.data=data;
mobile.current.onFieldChanged=configData.onFieldChanged;
dispatch({type:ACTION_TYPES.START_CONNECT});
mobile.current.connector=createMessageConnector();
mobile.current.connector.connect(mobileConfig);
}
}
const processInitData=(mobile,dispatch, initData)=>{
const isValidInitData=initData => initData && initData.form && initData.form.fields && initData.form.fields.length;
const buildMessageHandlers=(dispatch, initData)=>{
if(typeof initData ==='function'){

@@ -158,3 +116,3 @@ initData=initData();

console.warn("will not send empty form");
return null;
return {};
};

@@ -170,9 +128,4 @@ const fields=[];

fields.push(field);
values.push(f.value);
const s= (value)=>{
values[index]=value;
fields[index].value=value;
dispatch({type:ACTION_TYPES.SEND_FIELD});
mobile.current.connector.sendInputMessage(value,index);
};
values.push(f.value);
const s= (value)=>dispatch({type:ACTION_TYPES.SEND_FIELD,value,fields,values,index});
fieldSetters.push(s);

@@ -182,3 +135,3 @@ if(f.type==='info'){

}
if(f.operations && f.operations.onInput){
if(f.operations && f.operations.onInput){
return f;

@@ -189,8 +142,3 @@ }

operations:{
onInput:value=>{
values[index]=value;
fields[index].value=value;
const nf={...fields[index],value};
dispatch({type:ACTION_TYPES.RECEIVED_FIELD,field:nf});
}
onInput:value => dispatch({type:ACTION_TYPES.RECEIVED_FIELD,values,fields,value,index})
}

@@ -211,4 +159,70 @@ }

};
const isValidInitData=initData => initData && initData.form && initData.form.fields && initData.form.fields.length;
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();
}
},
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});
};

@@ -218,2 +232,175 @@

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){
case ACTION_TYPES.START_CONNECT:
return processStartConnect(state, action);
case ACTION_TYPES.SEND_INIT_DATA:
return processSendInitData(state, action);
case ACTION_TYPES.REGISTERED:
return processRegistered(state, action);
case ACTION_TYPES.CONNECTION_ERROR:
return processError(state, action);
case ACTION_TYPES.REGISTER_FAILED:
return processError(state, action);
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;
};
};
const getDefaultQRCodeSize=()=>{
if(!window){
return 400;
}
let size = window.innerWidth-10;
return size>400?400:size;
};
const DefaultQRCodeContainer=({children})=>(

@@ -231,13 +418,29 @@ <div style={styles.barcode}>

export const displayCode = (mobileState,connectionCode) => {
let qrCodeSize = window.innerWidth-10;
if(qrCodeSize>400){
qrCodeSize=400;
}
let QRCodeContainer=DefaultQRCodeContainer;
let LabelContainer=DefaultLabelContainer;
if(mobileState===MobileState.INITIALIZING){
return (
<QRCodeContainer>
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50">
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}
</>
)});
}
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">

@@ -247,45 +450,11 @@ <animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.5s" repeatCount="indefinite"/>

</svg>
</QRCodeContainer>
);
}
if(mobileState===MobileState.WAITING_FOR_MOBILE){
if(!connectionCode){
return(
<QRCodeContainer>Empty connection code</QRCodeContainer>
);
}
return (
<QRCodeContainer>
<QRCode value={connectionCode} level='H' size={qrCodeSize} />
<LabelContainer>
Scan with
<a href="https://globalinput.co.uk/global-input-app/get-app" target="_blank"> Global Input App</a>
</LabelContainer>
</QRCodeContainer>
);
</>
)});
}
return null;
}
}
return null;
};
export const displayWhen2 = (children,mobileState,st1,st2)=>{
if(mobileState!==st1 && mobileState !== st2){
return null;
}
return (<React.Fragment>
{children}
</React.Fragment>);
};
export const displayWhen = (children,mobileState,st)=>{
if(mobileState!==st){
return null;
}
return (<React.Fragment>
{children}
</React.Fragment>);
};
const styles={

@@ -304,2 +473,4 @@ barcode:{

}
}
}

@@ -1,71 +0,47 @@

import React, { useReducer, useEffect, useRef,useMemo,useCallback} from "react";
import React, { useReducer, useEffect, useRef, useMemo, useCallback } from "react";
import {MobileState} from './constants';
import reducer,{initialState} from './reducer';
import * as globalInput from './globalinput';
export {MobileState};
import {reducer,
initialState,startConnect,
getFlags,invokeOnChange,getExposed,displayQRCode} from './globalinput';
export default (configData, dependencies)=>{
const [{
mobileState,
connectionCode,
field,
errorMessage
}, dispatch] = useReducer(reducer, initialState);
const mobile=useRef(globalInput.initialData);
const setOnFieldChanged=(onFieldChanged)=>{
globalInput.setOnFieldChanged(mobile,onFieldChanged);
};
const disconnect=()=>{
globalInput.closeConnection(mobile);
};
const setFieldValueById=useCallback((fieldId, valueToSet)=>{
globalInput.setFieldValueById(mobile,mobileState,fieldId, valueToSet);
},[mobile,mobileState]);
const setInitData= useCallback((initData)=>{
globalInput.sendInitData(mobile,dispatch,initData);
},[mobile,dispatch]);
useEffect(()=>{
globalInput.onFieldChanged(mobile,field,setFieldValueById,setInitData); //should only executed when the field is changed
},[field]);
useEffect(()=>{
globalInput.startConnect(mobile,dispatch,configData);
},dependencies?dependencies:[]);
export default (configData, dependencies) => {
const [{
connectionCode,
errorMessage,
field
}, dispatch] = useReducer(reducer, initialState);
const {isLoading,isReady,isError,isDisconnected,isConnected}=getFlags();
useEffect(() => {
startConnect(dispatch, configData);
}, dependencies ? dependencies : []); //default connect once for the component
useEffect(()=>invokeOnChange(field),[field]);
const connectionMessage=useMemo(()=>globalInput.displayCode(mobileState,connectionCode),[connectionCode,mobileState]);
const WhenWaiting = useCallback(({children})=>globalInput.displayWhen2(children,mobileState,MobileState.WAITING_FOR_MOBILE,MobileState.INITIALIZING),[mobileState===MobileState.WAITING_FOR_MOBILE || mobileState === MobileState.INITIALIZING]);
const WhenConnected = useCallback(({children})=>globalInput.displayWhen(children,mobileState,MobileState.MOBILE_CONNECTED),[mobileState===MobileState.MOBILE_CONNECTED]);
const WhenDisconnected = useCallback(({children})=>globalInput.displayWhen(children,mobileState,MobileState.DISCONNECTED),[mobileState===MobileState.DISCONNECTED]);
const WhenError = useCallback(({children})=>globalInput.displayWhen(children,mobileState,MobileState.ERROR),[mobileState===MobileState.ERROR]);
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();
return {
mobileState,
ConnectQR,
connectionCode,
field,
errorMessage,
initData:mobile.current.data && mobile.current.data.initData,
mobile:mobile.current,
disconnect,
setInitData,
connectionMessage,
values:globalInput.getValues(mobile),
fields:globalInput.getFields(mobile),
field,
setters:globalInput.getSetters(mobile),
setFieldValueById,
setOnFieldChanged,
WhenWaiting,
WhenConnected,
WhenDisconnected,
WhenError,
initDataID:globalInput.getInitDataID(mobile)
isLoading,
isReady,
isError,
isDisconnected,
isConnected,
initData,
sendValue,
sendInitData,
setOnchange,
disconnect
};
};

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

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc