UNPKG

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
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;