From 8dfd206aa903ec683ec9eed1fa4870ae0e18ff57 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sat, 6 Feb 2021 13:36:56 +1100 Subject: [PATCH] Updates for using the bridge --- v2/internal/runtime/js/runtime/.npmignore | 1 - v2/internal/runtime/js/runtime/bridge.js | 211 ++++++++++++++++++ v2/internal/runtime/js/runtime/init.js | 20 +- v2/internal/runtime/js/runtime/main.js | 2 +- .../runtime/js/runtime/package-lock.json | 2 +- v2/internal/runtime/js/runtime/package.json | 2 +- v2/internal/runtime/js/runtime/system.js | 36 ++- 7 files changed, 254 insertions(+), 20 deletions(-) delete mode 100644 v2/internal/runtime/js/runtime/.npmignore create mode 100644 v2/internal/runtime/js/runtime/bridge.js diff --git a/v2/internal/runtime/js/runtime/.npmignore b/v2/internal/runtime/js/runtime/.npmignore deleted file mode 100644 index d0a1d1fad..000000000 --- a/v2/internal/runtime/js/runtime/.npmignore +++ /dev/null @@ -1 +0,0 @@ -bridge.js \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/bridge.js b/v2/internal/runtime/js/runtime/bridge.js new file mode 100644 index 000000000..5af08c8ef --- /dev/null +++ b/v2/internal/runtime/js/runtime/bridge.js @@ -0,0 +1,211 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The lightweight framework for web-like apps +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +function init() { + // Bridge object + window.wailsbridge = { + reconnectOverlay: null, + reconnectTimer: 300, + wsURL: 'ws://' + window.location.hostname + ':34115/bridge', + connectionState: null, + config: {}, + websocket: null, + callback: null, + overlayHTML: + '
', + overlayCSS: + '.wails-reconnect-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:linear-gradient(45deg,rgb(0 0 0 / 60%) 0%,rgb(4 0 255 / 60%) 100%);font-family:sans-serif;display:none;z-index:999999}.wails-reconnect-overlay-content{text-align:center;width:10em;position:relative;margin:5% auto 0;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAuCAMAAACPpbA7AAAAflBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAAAAAAAAAAAABAQEEBAQAAAAAAAAEBAQAAAADAwMAAAABAQEAAAAAAAAAAAAAAAAAAAACAgICAgIBAQEAAAAAAAAAAAAAAAAAAAACAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQWCC3waAAAAKXRSTlMALgUMIBk0+xEqJs70Xhb3lu3EjX2EZTlv5eHXvbarQj3cdmpXSqOeUDwaqNAAAAKCSURBVEjHjZTntqsgEIUPVVCwtxg1vfD+L3hHRe8K6snZf+KKn8OewvzsSSeXLruLnz+KHs0gr6DkT3xsRkU6VVn4Ha/UxLe1Z4y64i847sykPBh/AvQ7ry3eFN70oKrfcBJYvm/tQ1qxP4T3emXPeXAkvodPUvtdjbhk+Ft4c0hslTiXVOzxOJ15NWUblQhRsdu3E1AfCjj3Gdm18zSOsiH8Lk4TB480ksy62fiqNo4OpyU8O21l6+hyRtS6z8r1pHlmle5sR1/WXS6Mq2Nl+YeKt3vr+vdH/q4O68tzXuwkiZmngYb4R8Co1jh0+Ww2UTyWxBvtyxLO7QVjO3YOD/lWZpbXDGellFG2Mws58mMnjVZSn7p+XvZ6IF4nn02OJZV0aTO22arp/DgLPtrgpVoi6TPbZm4XQBjY159w02uO0BDdYsfrOEi0M2ulRXlCIPAOuN1NOVhi+riBR3dgwQplYsZRZJLXq23Mlo5njkbY0rZFu3oiNIYG2kqsbVz67OlNuZZIOlfxHDl0UpyRX86z/OYC/3qf1A1xTrMp/PWWM4ePzf8DDp1nesQRpcFk7BlwdzN08ZIALJpCaciQXO0f6k4dnuT/Ewg4l7qSTNzm2SykdHn6GJ12mWc6aCNj/g1cTXpB8YFfr0uVc96aFkkqiIiX4nO+salKwGtIkvfB+Ja8DxMeD3hIXP5mTOYPB4eVT0+32I5ykvPZjesnkGgIREgYnmLrPb0PdV3hoLup2TjcGBPM4mgsfF5BrawZR4/GpzYQzQfrUZCf0TCWYo2DqhdhTJBQ6j4xqmmLN5LjdRIY8LWExiFUsSrza/nmFBqw3I9tEZB9h0lIQSO9if8DkISDAj8CDawAAAAASUVORK5CYII=);background-repeat:no-repeat;background-position:center}.wails-reconnect-overlay-loadingspinner{pointer-events:none;width:2.5em;height:2.5em;border:.4em solid transparent;border-color:#fff #eee0 #fff #eee0;border-radius:50%;animation:loadingspin 1s linear infinite;margin:auto;padding:2.5em}@keyframes loadingspin{100%{transform:rotate(360deg); opacity}}', + log: function (message) { + // eslint-disable-next-line + console.log( + '%c wails bridge %c ' + message + ' ', + 'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem', + 'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem' + ); + } + }; +} + +// Adapted from webview - thanks zserge! +function injectCSS(css) { + const elem = document.createElement('style'); + elem.setAttribute('type', 'text/css'); + elem.appendChild(document.createTextNode(css)); + const head = document.head || document.getElementsByTagName('head')[0]; + head.appendChild(elem); +} + +// Creates a node in the Dom +/** + * @param {HTMLElement} parent + * @param {string} elementType + * @param {string} id + * @param {string|null} className + * @param {string|null} content + */ +function createNode(parent, elementType, id, className, content) { + const d = document.createElement(elementType); + if (id) { + d.id = id; + } + if (className) { + d.className = className; + } + if (content) { + d.innerHTML = content; + } + parent.appendChild(d); + return d; +} + +// Sets up the overlay +function setupOverlay() { + const body = document.body; + const wailsBridgeNode = createNode(body, 'div', 'wails-bridge', null, null); + wailsBridgeNode.innerHTML = window.wailsbridge.overlayHTML; + + // Inject the overlay CSS + injectCSS(window.wailsbridge.overlayCSS); +} + +// Start the Wails Bridge +function startBridge() { + // Setup the overlay + setupOverlay(); + + window.wailsbridge.websocket = null; + window.wailsbridge.connectTimer = null; + window.wailsbridge.reconnectOverlay = document.querySelector( + '.wails-reconnect-overlay' + ); + window.wailsbridge.connectionState = 'disconnected'; + + // Shows the overlay + function showReconnectOverlay() { + window.wailsbridge.reconnectOverlay.style.display = 'block'; + } + + // Hides the overlay + const hideReconnectOverlay = function () { + window.wailsbridge.reconnectOverlay.style.display = 'none'; + } + window.wailsbridge.hideReconnectOverlay = hideReconnectOverlay; + + // Adds a script to the Dom. + // Removes it if second parameter is true. + function addScript(script, remove) { + const s = document.createElement('script'); + s.setAttribute('type', 'text/javascript'); + s.textContent = script; + document.head.appendChild(s); + + // Remove internal messages from the DOM + if (remove) { + s.parentNode.removeChild(s); + } + } + + // Handles incoming websocket connections + function handleConnect() { + window.wailsbridge.log('Connected to backend'); + hideReconnectOverlay(); + clearInterval(window.wailsbridge.connectTimer); + window.wailsbridge.websocket.onclose = handleDisconnect; + window.wailsbridge.websocket.onmessage = handleMessage; + window.wailsbridge.connectionState = 'connected'; + } + + // Handles websocket disconnects + function handleDisconnect() { + window.wailsbridge.log('Disconnected from backend'); + window.wailsbridge.websocket = null; + window.wailsbridge.connectionState = 'disconnected'; + showReconnectOverlay(); + connect(); + } + + // Try to connect to the backend every 300ms (default value). + // Change this value in the main wailsbridge object. + function connect() { + window.wailsbridge.connectTimer = setInterval(function () { + if (window.wailsbridge.websocket == null) { + window.wailsbridge.websocket = new WebSocket(window.wailsbridge.wsURL); + window.wailsbridge.websocket.onopen = handleConnect; + window.wailsbridge.websocket.onerror = function (e) { + e.stopImmediatePropagation(); + e.stopPropagation(); + e.preventDefault(); + window.wailsbridge.websocket = null; + return false; + }; + } + }, window.wailsbridge.reconnectTimer); + } + + function handleMessage(message) { + // As a bridge we ignore js and css injections + switch (message.data[0]) { + // Wails library - inject! + case 'w': + addScript(message.data.slice(1)); + + // Now wails runtime is loaded, wails for the ready event + // and callback to the main app + window.wails.Events.On('wails:loaded', function () { + window.wailsbridge.log('Wails Ready'); + if (window.wailsbridge.callback) { + window.wailsbridge.log('Notifying application'); + window.wailsbridge.callback(window.wails); + } + }); + window.wailsbridge.log('Loaded Wails Runtime'); + break; + // Notifications + case 'n': + addScript(message.data.slice(1), true); + break; + // Binding + case 'b': + const binding = message.data.slice(1); + //log("Binding: " + binding) + window.wails._.NewBinding(binding); + break; + // Call back + case 'c': + const callbackData = message.data.slice(1); + window.wails._.Callback(callbackData); + break; + default: + window.wails.Log.Error('Unknown message type received: ' + message.data[0]); + } + } + + // Start by showing the overlay... + showReconnectOverlay(); + + // ...and attempt to connect + connect(); +} + +function InitBridge(callback) { + // Set up the bridge + init(); + + // Save the callback + window.wailsbridge.callback = callback; + + // Start Bridge + startBridge(); +} + +module.exports = { + InitBridge: InitBridge, +} diff --git a/v2/internal/runtime/js/runtime/init.js b/v2/internal/runtime/js/runtime/init.js index 8ade99048..89f470da7 100644 --- a/v2/internal/runtime/js/runtime/init.js +++ b/v2/internal/runtime/js/runtime/init.js @@ -9,13 +9,25 @@ The lightweight framework for web-like apps */ /* jshint esversion: 6 */ +import { InitBridge } from './bridge'; + /** - * Initialises the Wails runtime + * ready will execute the callback when Wails has loaded + * and initialised. * * @param {function} callback */ -function Init(callback) { - window.wails._.Init(callback); +function ready(callback) { + + // If window.wails exists, we are ready + if( window.wails ) { + return callback; + } + + // If not we need to setup the bridge + InitBridge(callback); } -module.exports = Init; \ No newline at end of file +module.exports = { + ready: ready, +}; \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/main.js b/v2/internal/runtime/js/runtime/main.js index c70f5aa38..add95ab5d 100644 --- a/v2/internal/runtime/js/runtime/main.js +++ b/v2/internal/runtime/js/runtime/main.js @@ -23,7 +23,7 @@ module.exports = { Browser: Browser, Dialog: Dialog, Events: Events, - Init: Init, + ready: Init.ready, Log: Log, System: System, Store: Store, diff --git a/v2/internal/runtime/js/runtime/package-lock.json b/v2/internal/runtime/js/runtime/package-lock.json index 3b214c246..3f3b1ae3d 100644 --- a/v2/internal/runtime/js/runtime/package-lock.json +++ b/v2/internal/runtime/js/runtime/package-lock.json @@ -1,6 +1,6 @@ { "name": "@wails/runtime", - "version": "1.2.13", + "version": "1.2.24", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/v2/internal/runtime/js/runtime/package.json b/v2/internal/runtime/js/runtime/package.json index 808093a3f..cd2baf4f2 100644 --- a/v2/internal/runtime/js/runtime/package.json +++ b/v2/internal/runtime/js/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@wails/runtime", - "version": "1.2.22", + "version": "1.3.6", "description": "Wails V2 Javascript runtime library", "main": "main.js", "types": "runtime.d.ts", diff --git a/v2/internal/runtime/js/runtime/system.js b/v2/internal/runtime/js/runtime/system.js index ff736a2b3..3e2163e10 100644 --- a/v2/internal/runtime/js/runtime/system.js +++ b/v2/internal/runtime/js/runtime/system.js @@ -13,7 +13,7 @@ const Events = require('./events'); /** * Registers an event listener that will be invoked when the user changes the - * desktop theme (light mode / dark mode). The callback receives a boolean which + * desktop theme (light mode / dark mode). The callback receives a booleanean which * indicates if dark mode is enabled. * * @export @@ -43,12 +43,12 @@ function DarkModeEnabled() { * Mac Title Bar Config * Check out https://github.com/lukakerr/NSWindowStyles for some examples of these settings * @typedef {Object} MacTitleBar - * @param {bool} TitleBarAppearsTransparent - NSWindow.titleBarAppearsTransparent - * @param {bool} HideTitle - NSWindow.hideTitle - * @param {bool} HideTitleBar - NSWindow.hideTitleBar - * @param {bool} FullSizeContent - Makes the webview portion of the window the full size of the window, even over the titlebar - * @param {bool} UseToolbar - Set true to add a blank toolbar to the window (makes the title bar larger) - * @param {bool} HideToolbarSeparator - Set true to remove the separator between the toolbar and the main content area + * @param {boolean} TitleBarAppearsTransparent - NSWindow.titleBarAppearsTransparent + * @param {boolean} HideTitle - NSWindow.hideTitle + * @param {boolean} HideTitleBar - NSWindow.hideTitleBar + * @param {boolean} FullSizeContent - Makes the webview portion of the window the full size of the window, even over the titlebar + * @param {boolean} UseToolbar - Set true to add a blank toolbar to the window (makes the title bar larger) + * @param {boolean} HideToolbarSeparator - Set true to remove the separator between the toolbar and the main content area * */ @@ -65,8 +65,8 @@ function DarkModeEnabled() { * @param {number} MinHeight - Window Minimum Height * @param {number} MaxWidth - Window Maximum Width * @param {number} MaxHeight - Window Maximum Height - * @param {bool} StartHidden - Start with window hidden - * @param {bool} DevTools - Enables the window devtools + * @param {boolean} StartHidden - Start with window hidden + * @param {boolean} DevTools - Enables the window devtools * @param {number} RBGA - The initial window colour. Convert to hex then it'll mean 0xRRGGBBAA * @param {MacAppConfig} [Mac] - Configuration when running on Mac * @param {LinuxAppConfig} [Linux] - Configuration when running on Linux @@ -88,11 +88,23 @@ function AppConfig() { return window.wails.System.AppConfig.get(); } +function LogLevel() { + return window.wails.System.LogLevel(); +} + +function Platform() { + return window.wails.System.Platform(); +} + +function AppType() { + return window.wails.System.AppType(); +} + module.exports = { OnThemeChange: OnThemeChange, DarkModeEnabled: DarkModeEnabled, - LogLevel: window.wails.System.LogLevel, - Platform: window.wails.System.Platform, - AppType: window.wails.System.AppType, + LogLevel: LogLevel, + Platform: Platform, + AppType: AppType, AppConfig: AppConfig, }; \ No newline at end of file