Add: jsbridge supported

This commit is contained in:
jas0ncn 2018-09-02 16:01:18 +08:00
parent 6d90543192
commit 9d5268107e
8 changed files with 137 additions and 32 deletions

View File

@ -9,7 +9,7 @@ module.exports = merge(commonConfig, {
'react-hot-loader/patch', // activate HMR for React
'webpack-dev-server/client?http://localhost:8080',// bundle the client for webpack-dev-server and connect to the provided endpoint
'webpack/hot/only-dev-server', // bundle the client for hot reloading, only- means to only hot reload for successful updates
'./index.tsx' // the entry point of our app
'./index.ts' // the entry point of our app
],
devServer: {
hot: true, // enable HMR on the server

View File

@ -6,7 +6,7 @@ const commonConfig = require('./common')
module.exports = merge(commonConfig, {
mode: 'production',
entry: './index.tsx',
entry: './index.ts',
output: {
filename: 'js/bundle.[hash].min.js',
path: resolve(__dirname, '../../dist'),

12
src/index.ts Normal file
View File

@ -0,0 +1,12 @@
import renderApp from './render'
import { isClashX, setupJsBridge } from './lib/jsBridge'
/**
* Global entry
* Will check if need setup jsbridge
*/
if (isClashX()) {
setupJsBridge(renderApp)
} else {
renderApp()
}

View File

@ -1,28 +0,0 @@
import * as React from 'react'
import { render } from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import App from './components/App'
const rootEl = document.getElementById('root')
render(
<AppContainer>
<App />
</AppContainer>,
rootEl
)
// Hot Module Replacement API
declare let module: { hot: any }
if (module.hot) {
module.hot.accept('./components/App', () => {
const NewApp = require('./components/App').default
render(
<AppContainer>
<NewApp />
</AppContainer>,
rootEl
)
})
}

91
src/lib/jsBridge.ts Normal file
View File

@ -0,0 +1,91 @@
/**
* For support ClashX runtime
*
* Clash Dashboard will use jsbridge to
* communicate with ClashX
*
* Before React app rendered, jsbridge
* should be checked if initialized,
* and also should checked if it's
* ClashX runtime
*
* @author jas0ncn
*/
/**
* declare javascript bridge API
*/
export interface JsBridge {
/**
* Register a javascript bridge event handle
*/
registerHandler: (eventName: string, callback: (data: any, responseCallback: (param: any) => void) => void) => void
/**
* Call a native handle
*/
callHandler: (handleName: string, data: any, responseCallback: (responseData: any) => void) => void
/**
* Who knows
*/
disableJavscriptAlertBoxSafetyTimeout: () => void
}
declare global {
interface Window {
/**
* Global jsbridge instance
*/
WebViewJavascriptBridge?: JsBridge | null
/**
* Global jsbridge init callback
*/
WVJBCallbacks?: Function[]
}
}
/**
* setup a jsbridge before app render
* @param {Function} cb callback when jsbridge initialized
* @see https://github.com/marcuswestin/WebViewJavascriptBridge
*/
export function setupJsBridge (callback = jsBridge => {}) {
/**
* You need check if inClashX first
*/
if (!isClashX()) {
return callback(null)
}
if (window.WebViewJavascriptBridge) {
return callback(window.WebViewJavascriptBridge)
}
// setup callback
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback)
}
window.WVJBCallbacks = [callback]
const WVJBIframe = document.createElement('iframe')
WVJBIframe.style.display = 'none'
WVJBIframe.src = 'https://__bridge_loaded__'
document.documentElement.appendChild(WVJBIframe)
setTimeout(() => document.documentElement.removeChild(WVJBIframe), 0)
}
/**
* Check if perched in ClashX Runtime
*/
export function isClashX () {
return navigator.userAgent === 'ClashX Runtime'
}

30
src/render.tsx Normal file
View File

@ -0,0 +1,30 @@
import * as React from 'react'
import { render } from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import App from './components/App'
const rootEl = document.getElementById('root')
// Hot Module Replacement API
declare let module: { hot: any }
export default function renderApp () {
render(
<AppContainer>
<App />
</AppContainer>,
rootEl
)
if (module.hot) {
module.hot.accept('./components/App', () => {
const NewApp = require('./components/App').default
render(
<AppContainer>
<NewApp />
</AppContainer>,
rootEl
)
})
}
}

View File

@ -4,7 +4,6 @@
"sourceMap": true,
"noImplicitAny": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"module": "commonjs",
"target": "es5",
"jsx": "react",

View File

@ -2,6 +2,7 @@
"extends": "tslint-config-standard",
"rules": {
"indent": [true, "spaces", 4],
"ter-indent": [true, 4]
"ter-indent": [true, 4],
"no-empty": false
}
}