blockcity-js-sdk
Version:
BlockCity JS-SDK is a web development kit based on BlockCity for web developers.
837 lines (817 loc) • 35.3 kB
JavaScript
import { fetch } from 'whatwg-fetch'
import { getMobileOperatingSystem } from './src/util'
import { apis } from './src/config'
var methods = {};
var methodID = 0;
var needEncode = true;
window.formatJSON = function (params) {
let tmpObj = {};
let tmpArr = params.split('&');
for (let i = 0; i < tmpArr.length; i++) {
let paramArr = tmpArr[i].split('=');
paramArr[1] = decodeURIComponent(paramArr[1]);
if ((paramArr[1].split('{').length > 1) && (paramArr[1].split('}').length > 1)) {
paramArr[1] = JSON.parse(paramArr[1]);
}
tmpObj[paramArr[0]] = paramArr[1];
}
return tmpObj;
};
var nativeCallback = window.nativeCallback || function () { };
window.nativeCallback = function (mId) {
var args = Array.prototype.slice.call(arguments, 1);
typeof methods[mId] === 'function' && methods[mId].apply(this, args);
nativeCallback(...arguments);
};
const Blockcity = {
callBridge: function (schema, method, params, callback) {
var mId = methodID++;
var url_params = '';
methods[mId] = function () {
typeof callback === 'function' && callback.apply(this, arguments);
if (methods[mId]) {
try {
delete methods[mId];
} catch (err) {
methods[mId] = null;
}
}
};
if (typeof (params) !== 'object') {
params = {};
}
params._mId = mId;
for (var k in params) {
if (needEncode) {
url_params += ((url_params.indexOf('=') != -1) ? '&' : '') + encodeURIComponent(k) + '=' + encodeURIComponent(params[k]);
} else {
url_params += ((url_params.indexOf('=') != -1) ? '&' : '') + k + '=' + encodeURI(params[k]);
}
}
var url = schema + '://' + method + '?' + url_params;
var iFrame;
iFrame = document.createElement('iframe');
iFrame.setAttribute('src', url);
iFrame.setAttribute('style', 'display:none;');
iFrame.setAttribute('height', '0px');
iFrame.setAttribute('width', '0px');
iFrame.setAttribute('frameborder', '0');
document.body.appendChild(iFrame);
iFrame.parentNode.removeChild(iFrame);
iFrame = null;
},
callNative: function (method, params, callback) {
this.callBridge('native', method, params, callback);
},
compareVersion: function (lowestVersion, localVersion) {
let v1 = lowestVersion.split('.');
let v2 = localVersion.split('.');
return v1[0] * 1000000 + v1[1] * 1000 + parseInt(v1[2]) <= v2[0] * 1000000 + v2[1] * 1000 + parseInt(v2[2]);
},
closeWindow: function () {
this.callNative('exit');
},
qrScan: function (callback) {
callback = callback ? callback : function () {};
this.callNative('qrscan', {
}, function (result) {
callback && callback(result);
});
},
getDeviceInfo: function (callback) {
callback = callback ? callback : function () {};
this.callNative('deviceInfo', {
}, function (result) {
callback && callback(result);
});
},
choosePay: function (config) {
config = typeof (config) === 'object' ? config : {};
config.success = config.hasOwnProperty('success') ? config.success : function () {};
config.fail = config.hasOwnProperty('fail') ? config.fail : function () {};
config.cancel = config.hasOwnProperty('cancel') ? config.cancel : function () {};
if (!config.hasOwnProperty('tradeNo')) {
errorHandler('tradeNo is undefined');
return;
} else {
if (typeof config.tradeNo !== 'string') {
errorHandler('tradeNo is not a string');
return;
}
}
this.callNative('pay', {
tradeNo: config.tradeNo
}, function (tradeNo, status, isThird) {
if (isThird) {
this.callNative('finish', { tradeNo: tradeNo, status: status, isThird: isThird }, function () { });
}
switch (status) {
case 'success':
config.success && config.success();
break;
case 'timeout':
config.fail && config.fail('TIMEOUT');
break;
case 'failed':
config.fail && config.fail('FAILED');
break;
case 'close':
config.cancel && config.cancel();
break;
}
});
},
shareConfig: function (config) {
config = typeof (config) === 'object' ? config : {};
config.success = config.hasOwnProperty('success') ? config.success : function () {};
config.fail = config.hasOwnProperty('fail') ? config.fail : function () {};
if (!config.hasOwnProperty('title')) {
errorHandler('title is undefined');
return;
} else {
if (typeof config.title !== 'string') {
errorHandler('title is not a string');
return;
}
}
if (!config.hasOwnProperty('shareDes')) {
errorHandler('shareDes is undefined');
return;
} else {
if (typeof config.shareDes !== 'string') {
errorHandler('shareDes is not a string');
return;
}
}
if (!config.hasOwnProperty('url')) {
errorHandler('url is undefined');
return;
} else {
if (typeof config.url !== 'string') {
errorHandler('url is not a string');
return;
}
}
if (!config.hasOwnProperty('thumbUrl')) {
errorHandler('thumbUrl is undefined');
return;
} else {
if (typeof config.thumbUrl !== 'string') {
errorHandler('thumbUrl is not a string');
return;
}
}
let self = this;
let shareUrl = config.url;
function _shareConfig(jointUrl) {
self.callNative('shareConfig', {
title: config.title,
shareDes: config.shareDes,
url: jointUrl,
thumbUrl: config.thumbUrl
}, function (result) {
if (result === 'success') {
config.success && config.success(shareUrl);
} else {
config.fail && config.fail(result);
}
});
}
function _getMediatorUrl() {
return fetch('https://blockcity.gxb.io/api/app/config/share_domain?type=sdk')
.then(function (response) {
return response.json();
}).then(function (json) {
return json.data.share_domain;
}).catch(function (ex) {
return 'https://share.8ug2.cn/tmp/';
});
}
let timer = setTimeout(() => {
needEncode = false;
_getMediatorUrl().then(url => {
const jointUrl = url + encodeURIComponent(shareUrl)
_shareConfig(jointUrl)
})
}, 500);
this.callNative('deviceInfo', {}, function (result) {
clearTimeout(timer);
const deviceInfo = JSON.parse(result);
const appName = deviceInfo.appName || '';
const platform = deviceInfo.platform;
const version = deviceInfo.version;
if (appName !== 'blockcityglobal') {
if (!self.compareVersion(apis['local'][platform]['shareConfig'], version)) {
_getMediatorUrl().then(url => {
const jointUrl = url + encodeURIComponent(shareUrl)
_shareConfig(jointUrl)
})
return;
}
}
_shareConfig(shareUrl)
});
},
callShare: function (config) {
config = typeof (config) === 'object' ? config : {};
config.success = config.hasOwnProperty('success') ? config.success : function () {};
config.fail = config.hasOwnProperty('fail') ? config.fail : function () {};
if (config.hasOwnProperty('contentType') && config.contentType === 0) {
if (!config.hasOwnProperty('title')) {
errorHandler('title is undefined');
return;
} else {
if (typeof config.title !== 'string') {
errorHandler('title is not a string');
return;
}
}
} else {
if (!config.hasOwnProperty('title')) {
errorHandler('title is undefined');
return;
} else {
if (typeof config.title !== 'string') {
errorHandler('title is not a string');
return;
}
}
if (!config.hasOwnProperty('shareDes')) {
errorHandler('shareDes is undefined');
return;
} else {
if (typeof config.shareDes !== 'string') {
errorHandler('shareDes is not a string');
return;
}
}
if (!config.hasOwnProperty('url')) {
errorHandler('url is undefined');
return;
} else {
if (typeof config.url !== 'string') {
errorHandler('url is not a string');
return;
}
}
if (!config.hasOwnProperty('thumbUrl')) {
errorHandler('thumbUrl is undefined');
return;
} else {
if (typeof config.thumbUrl !== 'string') {
errorHandler('thumbUrl is not a string');
return;
}
}
}
let self = this;
let timer = setTimeout(() => {
this.callNative('share', {
contentType: config.contentType || 0,
sceneType: config.sceneType || 0,
title: config.title,
shareDes: config.shareDes,
url: config.url,
thumbUrl: config.thumbUrl
}, function (result) {
if (result === 'success') {
config.success && config.success(result);
} else {
config.fail && config.fail(result);
}
});
}, 500);
this.callNative('deviceInfo', {}, function (result) {
clearTimeout(timer);
const deviceInfo = JSON.parse(result);
const appName = deviceInfo.appName || '';
const platform = deviceInfo.platform;
const version = deviceInfo.version;
if (appName !== 'blockcityglobal') {
// local
if (self.compareVersion(apis['local'][platform]['callShare'], version)) {
self.callNative('share', {
contentType: config.contentType || 0,
sceneType: config.sceneType || 0,
title: config.title,
shareDes: config.shareDes,
url: config.url,
thumbUrl: config.thumbUrl
}, function (result) {
if (result === 'success') {
config.success && config.success(result);
} else {
config.fail && config.fail(result);
}
});
} else {
alert('The current version of the app is too low, please upgrade to the latest version.');
}
} else {
// global
if (self.compareVersion(apis['global'][platform]['callShare'], version)) {
self.callNative('share', {
contentType: config.contentType || 0,
sceneType: config.sceneType || 0,
title: config.title,
shareDes: config.shareDes,
url: config.url,
thumbUrl: config.thumbUrl
}, function (result) {
if (result === 'success') {
config.success && config.success(result);
} else {
config.fail && config.fail(result);
}
});
} else {
alert('The current version of the app is too low, please upgrade to the latest version.');
}
}
});
},
rotate: function (config) {
config = typeof (config) === 'object' ? config : {};
config.hiddenNav = config.hasOwnProperty('hiddenNav') ? config.hiddenNav : 1;
config.direction = config.hasOwnProperty('direction') ? config.direction : 0;
let self = this;
let timer = setTimeout(() => {
this.callNative('rotate', {
hiddenNav: config.hiddenNav,
direction: config.direction
});
}, 500);
this.callNative('deviceInfo', {}, function (result) {
clearTimeout(timer);
const deviceInfo = JSON.parse(result);
const appName = deviceInfo.appName || '';
const platform = deviceInfo.platform;
const version = deviceInfo.version;
if (appName !== 'blockcityglobal') {
// local
if (self.compareVersion(apis['local'][platform]['rotate'], version)) {
self.callNative('rotate', {
hiddenNav: config.hiddenNav,
direction: config.direction
});
} else {
alert('The current version of the app is too low, please upgrade to the latest version.');
}
} else {
// global
if (self.compareVersion(apis['global'][platform]['rotate'], version)) {
self.callNative('rotate', {
hiddenNav: config.hiddenNav,
direction: config.direction
});
} else {
alert('The current version of the app is too low, please upgrade to the latest version.');
}
}
});
},
getSysLanguage: function (callback) {
callback = callback ? callback : function () {};
let self = this;
let timer = setTimeout(() => {
this.callNative('currentLanguage', {
}, function (result) {
callback && callback(result);
});
}, 500);
this.callNative('deviceInfo', {}, function (result) {
clearTimeout(timer);
const deviceInfo = JSON.parse(result);
const appName = deviceInfo.appName || '';
const platform = deviceInfo.platform;
const version = deviceInfo.version;
if (appName !== 'blockcityglobal') {
// local
if (self.compareVersion(apis['local'][platform]['getSysLanguage'], version)) {
self.callNative('currentLanguage', {
}, function (result) {
callback && callback(result);
});
} else {
alert('The current version of the app is too low, please upgrade to the latest version.');
}
} else {
// global
if (self.compareVersion(apis['global'][platform]['getSysLanguage'], version)) {
self.callNative('currentLanguage', {
}, function (result) {
callback && callback(result);
});
} else {
alert('The current version of the app is too low, please upgrade to the latest version.');
}
}
});
},
callAuth: function (config) {
config = typeof (config) === 'object' ? config : {};
config.success = config.hasOwnProperty('success') ? config.success : function () {};
config.fail = config.hasOwnProperty('fail') ? config.fail : function () {};
config.cancel = config.hasOwnProperty('cancel') ? config.cancel : function () {};
if (!config.hasOwnProperty('authItem')) {
errorHandler('authItem is undefined');
return;
} else {
if (typeof config.authItem !== 'string') {
errorHandler('authItem is not a string');
return;
}
}
let self = this;
// 兼容 iOS2.0.0 无法获取deviceInfo
let timer = setTimeout(() => {
this.callNative('callContract', {
v: '1.0',
plugin: 'auth',
authItem: config.authItem
}, function (result) {
// 兼容 iOS2.0.0 返回的不是JSON对象
if (typeof result === 'string') {
result = JSON.stringify(formatJSON(result));
}
let returnParams = JSON.parse(result);
switch (parseInt(returnParams.code)) {
case 0:
config.cancel && config.cancel(result);
break;
case 1:
config.success && config.success(result);
break;
default:
config.fail && config.fail(result);
}
});
}, 500);
this.callNative('deviceInfo', {}, function (result) {
clearTimeout(timer);
const deviceInfo = JSON.parse(result);
const appName = deviceInfo.appName || '';
const platform = deviceInfo.platform;
const version = deviceInfo.version;
if (appName !== 'blockcityglobal') {
// local
if (self.compareVersion(apis['local'][platform]['callContract'], version)) {
if (!self.compareVersion('2.0.3', version)) {
if (platform === 'ios') {
self.callNative('callContract', {
v: '1.0',
plugin: 'auth',
authItem: config.authItem
}, function (result) {
// 兼容 iOS2.0.0 返回的不是JSON对象
if (typeof result === 'string') {
result = JSON.stringify(formatJSON(result));
}
let returnParams = JSON.parse(result);
switch (parseInt(returnParams.code)) {
case 0:
config.cancel && config.cancel(result);
break;
case 1:
config.success && config.success(result);
break;
default:
config.fail && config.fail(result);
}
});
} else {
// android2.0.3以下 返回的不是JSON对象
self.callNative('callContract', {
v: '1.0',
plugin: 'auth',
authItem: config.authItem
}, function (result) {
result = decodeURIComponent(JSON.stringify(result));
result = result.replace('"{', '{');
result = result.replace('}"', '}');
let returnParams = JSON.parse(result);
switch (parseInt(returnParams.code)) {
case 0:
config.cancel && config.cancel(result);
break;
case 1:
config.success && config.success(result);
break;
default:
config.fail && config.fail(result);
}
});
}
} else {
self.callNative('callContract', {
v: '1.0',
plugin: 'auth',
authItem: config.authItem
}, function (result) {
let returnParams = JSON.parse(result);
switch (parseInt(returnParams.code)) {
case 0:
config.cancel && config.cancel(result);
break;
case 1:
config.success && config.success(result);
break;
default:
config.fail && config.fail(result);
}
});
}
} else {
alert('The current version of the app is too low, please upgrade to the latest version.');
}
} else {
// global
if (self.compareVersion(apis['global'][platform]['callContract'], version)) {
self.callNative('callContract', {
v: '1.0',
plugin: 'auth',
authItem: config.authItem
}, function (result) {
let returnParams = JSON.parse(result);
switch (parseInt(returnParams.code)) {
case 0:
config.cancel && config.cancel(result);
break;
case 1:
config.success && config.success(result);
break;
default:
config.fail && config.fail(result);
}
});
} else {
alert('The current version of the app is too low, please upgrade to the latest version.');
}
}
});
},
callContract: function (config) {
config = typeof (config) === 'object' ? config : {};
config.success = config.hasOwnProperty('success') ? config.success : function () {};
config.fail = config.hasOwnProperty('fail') ? config.fail : function () {};
config.cancel = config.hasOwnProperty('cancel') ? config.cancel : function () {};
if (!config.hasOwnProperty('contractName')) {
errorHandler('contractName is undefined');
return;
} else {
if (typeof config.contractName !== 'string') {
errorHandler('contractName is not a string');
return;
}
}
if (!config.hasOwnProperty('amount')) {
config.amount = 0;
} else {
if (typeof config.amount !== 'object') {
if (config.amount !== 0) {
errorHandler('amount is not a object');
return;
}
} else {
if (!config.amount.hasOwnProperty('amount')) {
errorHandler('amount.amount is undefined');
return;
} else {
if (typeof config.amount.amount !== 'number') {
errorHandler('amount.amount is not a number');
return;
}
}
if (!config.amount.hasOwnProperty('asset_id')) {
errorHandler('amount.asset_id is undefined');
return;
} else {
if (typeof config.amount.asset_id !== 'string') {
errorHandler('amount.asset_id is not a string');
return;
}
}
}
}
if (!config.hasOwnProperty('methodName')) {
errorHandler('methodName is undefined');
return;
} else {
if (typeof config.methodName !== 'string') {
errorHandler('methodName is not a string');
return;
}
}
if (!config.hasOwnProperty('methodParams')) {
errorHandler('methodParams is undefined');
return;
} else {
if (typeof config.methodParams !== 'object') {
errorHandler('methodParams is not a object');
return;
}
}
if (!config.hasOwnProperty('specifiedAccount')) {
config.specifiedAccount = '';
} else {
if (typeof config.specifiedAccount !== 'string') {
errorHandler('specifiedAccount is not a string');
return;
}
}
let self = this;
let methodParams = JSON.stringify(config.methodParams);
let amount = JSON.stringify(config.amount);
// 兼容 iOS2.0.0 无法获取deviceInfo
let timer = setTimeout(() => {
const params = {
contract_name: config.contractName,
amount: amount,
method_name: config.methodName,
params: methodParams,
specified_account: config.specifiedAccount
}
if (!!config.type) {
params.type = config.type
}
this.callNative('callContract', params, function (result) {
// 兼容 iOS2.0.0 返回的不是JSON对象
if (typeof result === 'string') {
result = JSON.stringify(formatJSON(result));
}
let returnParams = JSON.parse(result);
switch (parseInt(returnParams.code)) {
case 0:
config.cancel && config.cancel(result);
break;
case 1:
config.success && config.success(result);
break;
default:
config.fail && config.fail(decodeURIComponent(result));
}
});
}, 500);
this.callNative('deviceInfo', {}, function (result) {
clearTimeout(timer);
const deviceInfo = JSON.parse(result);
const appName = deviceInfo.appName || '';
const platform = deviceInfo.platform;
const version = deviceInfo.version;
if (appName !== 'blockcityglobal') {
// local
if (self.compareVersion(apis['local'][platform]['callContract'], version)) {
if (!self.compareVersion('2.0.3', version)) {
if (platform === 'ios') {
self.callNative('callContract', {
contract_name: config.contractName,
amount: amount,
method_name: config.methodName,
params: methodParams,
specified_account: config.specifiedAccount
}, function (result) {
// 兼容 iOS2.0.0 返回的不是JSON对象
if (typeof result === 'string') {
result = JSON.stringify(formatJSON(result));
}
let returnParams = JSON.parse(result);
switch (parseInt(returnParams.code)) {
case 0:
config.cancel && config.cancel(result);
break;
case 1:
config.success && config.success(result);
break;
default:
config.fail && config.fail(decodeURIComponent(result));
}
});
} else {
// android2.0.3以下 返回的不是JSON对象
self.callNative('callContract', {
contract_name: config.contractName,
amount: amount,
method_name: config.methodName,
params: methodParams,
specified_account: config.specifiedAccount
}, function (result) {
result = decodeURIComponent(JSON.stringify(result));
result = result.replace('"{', '{');
result = result.replace('}"', '}');
let returnParams = JSON.parse(result);
switch (parseInt(returnParams.code)) {
case 0:
config.cancel && config.cancel(result);
break;
case 1:
config.success && config.success(result);
break;
default:
config.fail && config.fail(decodeURIComponent(result));
}
});
}
} else {
self.callNative('callContract', {
contract_name: config.contractName,
amount: amount,
method_name: config.methodName,
params: methodParams,
specified_account: config.specifiedAccount
}, function (result) {
let returnParams = JSON.parse(result);
switch (parseInt(returnParams.code)) {
case 0:
config.cancel && config.cancel(result);
break;
case 1:
config.success && config.success(result);
break;
default:
config.fail && config.fail(decodeURIComponent(result));
}
});
}
} else {
alert('The current version of the app is too low, please upgrade to the latest version.');
}
} else {
// global
if (self.compareVersion(apis['global'][platform]['callContract'], version)) {
self.callNative('callContract', {
contract_name: config.contractName,
amount: amount,
method_name: config.methodName,
params: methodParams,
specified_account: config.specifiedAccount
}, function (result) {
let returnParams = JSON.parse(result);
switch (parseInt(returnParams.code)) {
case 0:
config.cancel && config.cancel(result);
break;
case 1:
config.success && config.success(result);
break;
default:
config.fail && config.fail(decodeURIComponent(result));
}
});
} else {
alert('The current version of the app is too low, please upgrade to the latest version.');
}
}
});
},
checkJsApi: function ({ jsApiList = [], success = () => { }, fail = () => { } }) {
getDeviceInfoSingleInstance.then(info => {
info = JSON.parse(info)
const retMap = {}
const platform = info.platform
const version = info.version
let appName = info.appName === 'blockcityglobal' ? 'global' : 'local'
try {
jsApiList.forEach((api) => {
retMap[api] = this.compareVersion(apis[appName][platform][api], version)
})
} catch (err) {
throw err
}
success(retMap)
}).catch(err => {
fail(err)
})
}
};
var errorHandler = function (errMsg) {
console.error(JSON.stringify({'errMsg': errMsg}));
alert(JSON.stringify({'errMsg': errMsg}));
}
var getDeviceInfoSingleInstance = new Promise(function (resolve, reject) {
const expireTime = 3 * 1000
const startTime = new Date()
let interval = 0
// 若n秒后还没有返回,则继续获取
let timer = setInterval(fetchDeviceInfo, 1000)
function fetchDeviceInfo() {
const now = new Date()
interval = now - startTime
// hack for 2.0.0, which can not use getDeviceInfo
if (interval > expireTime) {
clearInterval(timer);
const fakeInfo = {
version: '2.0.0',
platform: getMobileOperatingSystem() || 'android',
appName: 'blockcity'
}
resolve(JSON.stringify(fakeInfo))
// reject(new Error('getDeviceInfo timeout'));
return;
}
Blockcity.getDeviceInfo((info) => {
clearInterval(timer)
resolve(info)
})
}
})
export default Blockcity;