diff --git a/configs/webpack/dev.js b/configs/webpack/dev.js
index 0d027ff..2af8ef9 100644
--- a/configs/webpack/dev.js
+++ b/configs/webpack/dev.js
@@ -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
diff --git a/configs/webpack/prod.js b/configs/webpack/prod.js
index 3e6e069..2d9a3ad 100644
--- a/configs/webpack/prod.js
+++ b/configs/webpack/prod.js
@@ -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'),
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..8885e25
--- /dev/null
+++ b/src/index.ts
@@ -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()
+}
diff --git a/src/index.tsx b/src/index.tsx
deleted file mode 100644
index 5f24aa7..0000000
--- a/src/index.tsx
+++ /dev/null
@@ -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(
-
-
- ,
- 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(
-
-
- ,
- rootEl
- )
- })
-}
diff --git a/src/lib/jsBridge.ts b/src/lib/jsBridge.ts
new file mode 100644
index 0000000..36a24b1
--- /dev/null
+++ b/src/lib/jsBridge.ts
@@ -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'
+}
diff --git a/src/render.tsx b/src/render.tsx
new file mode 100644
index 0000000..0bf0971
--- /dev/null
+++ b/src/render.tsx
@@ -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(
+
+
+ ,
+ rootEl
+ )
+
+ if (module.hot) {
+ module.hot.accept('./components/App', () => {
+ const NewApp = require('./components/App').default
+ render(
+
+
+ ,
+ rootEl
+ )
+ })
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
index e63aa60..d284554 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -4,7 +4,6 @@
"sourceMap": true,
"noImplicitAny": false,
"noUnusedLocals": true,
- "noUnusedParameters": true,
"module": "commonjs",
"target": "es5",
"jsx": "react",
diff --git a/tslint.json b/tslint.json
index 503326f..cebb2eb 100644
--- a/tslint.json
+++ b/tslint.json
@@ -2,6 +2,7 @@
"extends": "tslint-config-standard",
"rules": {
"indent": [true, "spaces", 4],
- "ter-indent": [true, 4]
+ "ter-indent": [true, 4],
+ "no-empty": false
}
}