mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 02:49:29 +08:00
Initial implementation of runtime over http
This commit is contained in:
parent
8534b32a9f
commit
14b201bb65
@ -6,16 +6,19 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
|
||||
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
const (
|
||||
runtimeJSPath = "/wails/runtime.js"
|
||||
ipcJSPath = "/wails/ipc.js"
|
||||
runtimePath = "/wails/runtime"
|
||||
)
|
||||
|
||||
type RuntimeAssets interface {
|
||||
@ -22,6 +23,10 @@ type RuntimeAssets interface {
|
||||
RuntimeDesktopJS() []byte
|
||||
}
|
||||
|
||||
type RuntimeHandler interface {
|
||||
HandleRuntimeCall(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
type AssetServer struct {
|
||||
handler http.Handler
|
||||
wsHandler http.Handler
|
||||
@ -34,6 +39,9 @@ type AssetServer struct {
|
||||
servingFromDisk bool
|
||||
appendSpinnerToBody bool
|
||||
|
||||
// Use http based runtime
|
||||
runtimeHandler RuntimeHandler
|
||||
|
||||
assetServerWebView
|
||||
}
|
||||
|
||||
@ -77,6 +85,10 @@ func NewAssetServerWithHandler(handler http.Handler, bindingsJSON string, servin
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *AssetServer) UseRuntimeHandler(handler RuntimeHandler) {
|
||||
d.runtimeHandler = handler
|
||||
}
|
||||
|
||||
func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
if isWebSocket(req) {
|
||||
// Forward WebSockets to the distinct websocket handler if it exists
|
||||
@ -122,6 +134,13 @@ func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
case runtimeJSPath:
|
||||
d.writeBlob(rw, path, d.runtimeJS)
|
||||
|
||||
case runtimePath:
|
||||
if d.runtimeHandler != nil {
|
||||
d.runtimeHandler.HandleRuntimeCall(rw, req)
|
||||
} else {
|
||||
d.handler.ServeHTTP(rw, req)
|
||||
}
|
||||
|
||||
case ipcJSPath:
|
||||
content := d.runtime.DesktopIPC()
|
||||
if d.ipcJS != nil {
|
||||
|
@ -5,6 +5,7 @@ go 1.19
|
||||
require (
|
||||
github.com/go-task/task/v3 v3.20.0
|
||||
github.com/jackmordaunt/icns/v2 v2.2.1
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/leaanthony/clir v1.6.0
|
||||
github.com/leaanthony/gosod v1.0.3
|
||||
github.com/leaanthony/winicon v1.0.0
|
||||
@ -33,6 +34,8 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mattn/go-zglob v0.0.4 // indirect
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
@ -26,6 +26,7 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78
|
||||
github.com/go-task/task/v3 v3.20.0 h1:pTavuhP+AiEpKLzh5I6Lja9Ux7ypYO5QMsEPTbhYEDc=
|
||||
github.com/go-task/task/v3 v3.20.0/go.mod h1:y7rWakbLR5gFElGgo6rA2dyr6vU/zNIDVfn3S4Of6OI=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
||||
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
|
||||
@ -36,6 +37,8 @@ github.com/jackmordaunt/icns/v2 v2.2.1 h1:MGklwYP2yohKn2Bw7XxlF69LZe98S1vUfl5OvA
|
||||
github.com/jackmordaunt/icns/v2 v2.2.1/go.mod h1:6aYIB9eSzyfHHMKqDf17Xrs1zetQPReAkiUSHzdw4cI=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
@ -74,6 +77,10 @@ github.com/mattn/go-zglob v0.0.4 h1:LQi2iOm0/fGgu80AioIJ/1j9w9Oh+9DZ39J4VAGzHQM=
|
||||
github.com/mattn/go-zglob v0.0.4/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
@ -107,6 +114,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
|
@ -10,46 +10,61 @@ The electron alternative for Go
|
||||
|
||||
/* jshint esversion: 9 */
|
||||
|
||||
const runtimeURL = window.location.origin + "/wails/runtime";
|
||||
|
||||
import {Call} from "./calls";
|
||||
import {invoke} from "./ipc";
|
||||
function runtimeCall(method, args) {
|
||||
let url = new URL(runtimeURL);
|
||||
url.searchParams.append("method", method);
|
||||
if (args) {
|
||||
for (let key in args) {
|
||||
url.searchParams.append(key, args[key]);
|
||||
}
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
reject(Error(response.statusText));
|
||||
})
|
||||
.then(data => resolve(data))
|
||||
.catch(error => reject(error));
|
||||
});
|
||||
}
|
||||
|
||||
export function newWindow(id) {
|
||||
let call = function(method, args) {
|
||||
if (id !== -1) {
|
||||
args["windowID"] = id;
|
||||
}
|
||||
return runtimeCall("window." + method, args);
|
||||
}
|
||||
return {
|
||||
// Reload: () => invoke('WR', id),
|
||||
// ReloadApp: () => invoke('WR', id),
|
||||
// SetSystemDefaultTheme: () => invoke('WASDT', id),
|
||||
// SetLightTheme: () => invoke('WALT', id),
|
||||
// SetDarkTheme: () => invoke('WADT', id),
|
||||
Center: () => invoke('Wc', id),
|
||||
SetTitle: (title) => invoke('WT' + title, id),
|
||||
Fullscreen: () => invoke('WF', id),
|
||||
UnFullscreen: () => invoke('Wf', id),
|
||||
SetSize: (width, height) => invoke('WS' + width + ',' + height, id),
|
||||
GetSize: () => {
|
||||
return Call(":wails:WindowGetSize")
|
||||
},
|
||||
SetMaxSize: (width, height) => invoke('WZ:' + width + ':' + height, id),
|
||||
SetMinSize: (width, height) => invoke('Wz:' + width + ':' + height, id),
|
||||
SetAlwaysOnTop: (b) => invoke('WATP:' + (b ? '1' : '0'), id),
|
||||
SetPosition: (x, y) => invoke('Wp:' + x + ':' + y, id),
|
||||
GetPosition: () => {
|
||||
return Call(":wails:WindowGetPos")
|
||||
},
|
||||
Hide: () => invoke('WH', id),
|
||||
Maximise: () => invoke('WM', id),
|
||||
Show: () => invoke('WS', id),
|
||||
ToggleMaximise: () => invoke('Wt', id),
|
||||
UnMaximise: () => invoke('WU', id),
|
||||
Minimise: () => invoke('Wm', id),
|
||||
UnMinimise: () => invoke('Wu', id),
|
||||
SetBackgroundColour: (R, G, B, A) =>
|
||||
invoke('Wr:' + JSON.stringify({
|
||||
r: R || 0,
|
||||
g: G || 0,
|
||||
b: B || 0,
|
||||
a: A || 255}, id)
|
||||
),
|
||||
// Reload: () => call('WR'),
|
||||
// ReloadApp: () => call('WR'),
|
||||
// SetSystemDefaultTheme: () => call('WASDT'),
|
||||
// SetLightTheme: () => call('WALT'),
|
||||
// SetDarkTheme: () => call('WADT'),
|
||||
Center: () => call('Center'),
|
||||
SetTitle: (title) => call('SetTitle', {title}),
|
||||
Fullscreen: () => call('Fullscreen'),
|
||||
UnFullscreen: () => call('UnFullscreen'),
|
||||
SetSize: (width, height) => call('SetSize', {width,height}),
|
||||
GetSize: () => { return call('GetSize') },
|
||||
SetMaxSize: (width, height) => call('SetMaxSize', {width,height}),
|
||||
SetMinSize: (width, height) => call('SetMinSize', {width,height}),
|
||||
SetAlwaysOnTop: (b) => call('SetAlwaysOnTop', {alwaysOnTop:b}),
|
||||
SetPosition: (x, y) => call('SetPosition', {x,y}),
|
||||
GetPosition: () => { return call('GetPosition') },
|
||||
Hide: () => call('Hide'),
|
||||
Maximise: () => call('Maximise'),
|
||||
Show: () => call('Show'),
|
||||
ToggleMaximise: () => call('ToggleMaximise'),
|
||||
UnMaximise: () => call('UnMaximise'),
|
||||
Minimise: () => call('Minimise'),
|
||||
UnMinimise: () => call('UnMinimise'),
|
||||
SetBackgroundColour: (r, g, b, a) => call('SetBackgroundColour', {R, G, B, A}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1 +1 @@
|
||||
(()=>{var o=null;(function(){let s=function(e){for(var n=window[e.shift()];n&&e.length;)n=n[e.shift()];return n},t=s(["chrome","webview","postMessage"]),i=s(["webkit","messageHandlers","external","postMessage"]);if(!t&&!i){console.error("Unsupported Platform");return}t&&(o=e=>window.chrome.webview.postMessage(e)),i&&(o=e=>window.webkit.messageHandlers.external.postMessage(e))})();function r(s,t){o(t&&t!==-1?"WINDOWID:"+t+":"+s:s)}})();
|
||||
(()=>{var o=null;(function(){let s=function(e){let n=window[e.shift()];for(;n&&e.length;)n=n[e.shift()];return n},t=s(["chrome","webview","postMessage"]),i=s(["webkit","messageHandlers","external","postMessage"]);if(!t&&!i){console.error("Unsupported Platform");return}t&&(o=e=>window.chrome.webview.postMessage(e)),i&&(o=e=>window.webkit.messageHandlers.external.postMessage(e))})();function l(s,t){o(t&&t!==-1?"WINDOWID:"+t+":"+s:s)}})();
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
(()=>{var u=null;(function(){let e=function(n){for(var r=window[n.shift()];r&&n.length;)r=r[n.shift()];return r},t=e(["chrome","webview","postMessage"]),o=e(["webkit","messageHandlers","external","postMessage"]);if(!t&&!o){console.error("Unsupported Platform");return}t&&(u=n=>window.chrome.webview.postMessage(n)),o&&(u=n=>window.webkit.messageHandlers.external.postMessage(n))})();function i(e,t){u(t&&t!==-1?"WINDOWID:"+t+":"+e:e)}var c={};function y(){var e=new Uint32Array(1);return window.crypto.getRandomValues(e)[0]}function W(){return Math.random()*9007199254740991}var d;window.crypto?d=y:d=W;function f(e,t,o){o==null&&(o=0);let n=window.wails.window.ID();return new Promise(function(r,l){var s;do s=e+"-"+d();while(c[s]);var w;o>0&&(w=setTimeout(function(){l(Error("Call to "+e+" timed out. Request ID: "+s))},o)),c[s]={timeoutHandle:w,reject:l,resolve:r};try{let m={name:e,args:t,callbackID:s,windowID:n};window.WailsInvoke("C"+JSON.stringify(m))}catch(m){console.error(m)}})}window.ObfuscatedCall=(e,t,o)=>(o==null&&(o=0),new Promise(function(n,r){var l;do l=e+"-"+d();while(c[l]);var s;o>0&&(s=setTimeout(function(){r(Error("Call to method "+e+" timed out. Request ID: "+l))},o)),c[l]={timeoutHandle:s,reject:r,resolve:n};try{let w={id:e,args:t,callbackID:l,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(w))}catch(w){console.error(w)}}));function p(e){let t;try{t=JSON.parse(e)}catch(r){let l=`Invalid JSON passed to callback: ${r.message}. Message: ${e}`;throw runtime.LogDebug(l),new Error(l)}let o=t.callbackid,n=c[o];if(!n){let r=`Callback '${o}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete c[o],t.error?n.reject(t.error):n.resolve(t.result)}var a={};function b(e){let t=e.name;if(a[t]){let o=a[t].slice();for(let n=0;n<a[t].length;n+=1){let r=a[t][n],l=e.data;r.Callback(l)&&o.splice(n,1)}o.length===0?S(t):a[t]=o}}function h(e){let t;try{t=JSON.parse(e)}catch{let n="Invalid JSON passed to Notify: "+e;throw new Error(n)}b(t)}function S(e){delete a[e],window.WailsInvoke("EX"+e)}window.go={};function v(e){try{e=JSON.parse(e)}catch(t){console.error(t)}window.go=window.go||{},Object.keys(e).forEach(t=>{window.go[t]=window.go[t]||{},Object.keys(e[t]).forEach(o=>{window.go[t][o]=window.go[t][o]||{},Object.keys(e[t][o]).forEach(n=>{window.go[t][o][n]=function(){let r=0;function l(){let s=[].slice.call(arguments);return f([t,o,n].join("."),s,r)}return l.setTimeout=function(s){r=s},l.getTimeout=function(){return r},l}()})})})}function g(e){return{Center:()=>i("Wc",e),SetTitle:t=>i("WT"+t,e),Fullscreen:()=>i("WF",e),UnFullscreen:()=>i("Wf",e),SetSize:(t,o)=>i("WS"+t+","+o,e),GetSize:()=>f(":wails:WindowGetSize"),SetMaxSize:(t,o)=>i("WZ:"+t+":"+o,e),SetMinSize:(t,o)=>i("Wz:"+t+":"+o,e),SetAlwaysOnTop:t=>i("WATP:"+(t?"1":"0"),e),SetPosition:(t,o)=>i("Wp:"+t+":"+o,e),GetPosition:()=>f(":wails:WindowGetPos"),Hide:()=>i("WH",e),Maximise:()=>i("WM",e),Show:()=>i("WS",e),ToggleMaximise:()=>i("Wt",e),UnMaximise:()=>i("WU",e),Minimise:()=>i("Wm",e),UnMinimise:()=>i("Wu",e),SetBackgroundColour:(t,o,n,r)=>i("Wr:"+JSON.stringify({r:t||0,g:o||0,b:n||0,a:r||255},e))}}window.wails={Callback:p,callbacks:c,EventsNotify:h,eventListeners:a,SetBindings:v};function x(e){return{Window:g(e),Show:()=>i("S"),Hide:()=>i("H"),Quit:()=>i("Q")}}window.runtime=x(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
||||
(()=>{var w=null;(function(){let o=function(n){let r=window[n.shift()];for(;r&&n.length;)r=r[n.shift()];return r},e=o(["chrome","webview","postMessage"]),t=o(["webkit","messageHandlers","external","postMessage"]);if(!e&&!t){console.error("Unsupported Platform");return}e&&(w=n=>window.chrome.webview.postMessage(n)),t&&(w=n=>window.webkit.messageHandlers.external.postMessage(n))})();function u(o,e){w(e&&e!==-1?"WINDOWID:"+e+":"+o:o)}var s={};function x(){let o=new Uint32Array(1);return window.crypto.getRandomValues(o)[0]}function g(){return Math.random()*9007199254740991}var f;window.crypto?f=x:f=g;function m(o,e,t){t==null&&(t=0);let n=window.wails.window.ID();return new Promise(function(r,i){let l;do l=o+"-"+f();while(s[l]);let a;t>0&&(a=setTimeout(function(){i(Error("Call to "+o+" timed out. Request ID: "+l))},t)),s[l]={timeoutHandle:a,reject:i,resolve:r};try{let d={name:o,args:e,callbackID:l,windowID:n};window.WailsInvoke("C"+JSON.stringify(d))}catch(d){console.error(d)}})}window.ObfuscatedCall=(o,e,t)=>(t==null&&(t=0),new Promise(function(n,r){let i;do i=o+"-"+f();while(s[i]);let l;t>0&&(l=setTimeout(function(){r(Error("Call to method "+o+" timed out. Request ID: "+i))},t)),s[i]={timeoutHandle:l,reject:r,resolve:n};try{let a={id:o,args:e,callbackID:i,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(a))}catch(a){console.error(a)}}));function h(o){let e;try{e=JSON.parse(o)}catch(r){let i=`Invalid JSON passed to callback: ${r.message}. Message: ${o}`;throw runtime.LogDebug(i),new Error(i)}let t=e.callbackid,n=s[t];if(!n){let r=`Callback '${t}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete s[t],e.error?n.reject(e.error):n.resolve(e.result)}var c={};function b(o){let e=o.name;if(c[e]){let t=c[e].slice();for(let n=0;n<c[e].length;n+=1){let r=c[e][n],i=o.data;r.Callback(i)&&t.splice(n,1)}t.length===0?k(e):c[e]=t}}function p(o){let e;try{e=JSON.parse(o)}catch{let n="Invalid JSON passed to Notify: "+o;throw new Error(n)}b(e)}function k(o){delete c[o],window.WailsInvoke("EX"+o)}window.go={};function S(o){try{o=JSON.parse(o)}catch(e){console.error(e)}window.go=window.go||{},Object.keys(o).forEach(e=>{window.go[e]=window.go[e]||{},Object.keys(o[e]).forEach(t=>{window.go[e][t]=window.go[e][t]||{},Object.keys(o[e][t]).forEach(n=>{window.go[e][t][n]=function(){let r=0;function i(){let l=[].slice.call(arguments);return m([e,t,n].join("."),l,r)}return i.setTimeout=function(l){r=l},i.getTimeout=function(){return r},i}()})})})}var v=window.location.origin+"/wails/runtime";function E(o,e){let t=new URL(v);if(t.searchParams.append("method",o),e)for(let n in e)t.searchParams.append(n,e[n]);return new Promise((n,r)=>{fetch(t).then(i=>{if(i.ok)return i.json();r(Error(i.statusText))}).then(i=>n(i)).catch(i=>r(i))})}function y(o){let e=function(t,n){return o!==-1&&(n.windowID=o),E("window."+t,n)};return{Center:()=>e("Center"),SetTitle:t=>e("SetTitle",{title:t}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(t,n)=>e("SetSize",{width:t,height:n}),GetSize:()=>e("GetSize"),SetMaxSize:(t,n)=>e("SetMaxSize",{width:t,height:n}),SetMinSize:(t,n)=>e("SetMinSize",{width:t,height:n}),SetAlwaysOnTop:t=>e("SetAlwaysOnTop",{alwaysOnTop:t}),SetPosition:(t,n)=>e("SetPosition",{x:t,y:n}),GetPosition:()=>e("GetPosition"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(t,n,r,i)=>e("SetBackgroundColour",{R,G,B,A})}}window.wails={Callback:h,callbacks:s,EventsNotify:p,eventListeners:c,SetBindings:S};function O(o){return{Window:y(o),Show:()=>u("S"),Hide:()=>u("H"),Quit:()=>u("Q")}}window.runtime=O(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
||||
|
@ -1 +1 @@
|
||||
(()=>{var u=null;(function(){let e=function(n){for(var r=window[n.shift()];r&&n.length;)r=r[n.shift()];return r},t=e(["chrome","webview","postMessage"]),o=e(["webkit","messageHandlers","external","postMessage"]);if(!t&&!o){console.error("Unsupported Platform");return}t&&(u=n=>window.chrome.webview.postMessage(n)),o&&(u=n=>window.webkit.messageHandlers.external.postMessage(n))})();function i(e,t){u(t&&t!==-1?"WINDOWID:"+t+":"+e:e)}var c={};function y(){var e=new Uint32Array(1);return window.crypto.getRandomValues(e)[0]}function W(){return Math.random()*9007199254740991}var d;window.crypto?d=y:d=W;function f(e,t,o){o==null&&(o=0);let n=window.wails.window.ID();return new Promise(function(r,l){var s;do s=e+"-"+d();while(c[s]);var w;o>0&&(w=setTimeout(function(){l(Error("Call to "+e+" timed out. Request ID: "+s))},o)),c[s]={timeoutHandle:w,reject:l,resolve:r};try{let m={name:e,args:t,callbackID:s,windowID:n};window.WailsInvoke("C"+JSON.stringify(m))}catch(m){console.error(m)}})}window.ObfuscatedCall=(e,t,o)=>(o==null&&(o=0),new Promise(function(n,r){var l;do l=e+"-"+d();while(c[l]);var s;o>0&&(s=setTimeout(function(){r(Error("Call to method "+e+" timed out. Request ID: "+l))},o)),c[l]={timeoutHandle:s,reject:r,resolve:n};try{let w={id:e,args:t,callbackID:l,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(w))}catch(w){console.error(w)}}));function p(e){let t;try{t=JSON.parse(e)}catch(r){let l=`Invalid JSON passed to callback: ${r.message}. Message: ${e}`;throw runtime.LogDebug(l),new Error(l)}let o=t.callbackid,n=c[o];if(!n){let r=`Callback '${o}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete c[o],t.error?n.reject(t.error):n.resolve(t.result)}var a={};function b(e){let t=e.name;if(a[t]){let o=a[t].slice();for(let n=0;n<a[t].length;n+=1){let r=a[t][n],l=e.data;r.Callback(l)&&o.splice(n,1)}o.length===0?S(t):a[t]=o}}function h(e){let t;try{t=JSON.parse(e)}catch{let n="Invalid JSON passed to Notify: "+e;throw new Error(n)}b(t)}function S(e){delete a[e],window.WailsInvoke("EX"+e)}window.go={};function v(e){try{e=JSON.parse(e)}catch(t){console.error(t)}window.go=window.go||{},Object.keys(e).forEach(t=>{window.go[t]=window.go[t]||{},Object.keys(e[t]).forEach(o=>{window.go[t][o]=window.go[t][o]||{},Object.keys(e[t][o]).forEach(n=>{window.go[t][o][n]=function(){let r=0;function l(){let s=[].slice.call(arguments);return f([t,o,n].join("."),s,r)}return l.setTimeout=function(s){r=s},l.getTimeout=function(){return r},l}()})})})}function g(e){return{Center:()=>i("Wc",e),SetTitle:t=>i("WT"+t,e),Fullscreen:()=>i("WF",e),UnFullscreen:()=>i("Wf",e),SetSize:(t,o)=>i("WS"+t+","+o,e),GetSize:()=>f(":wails:WindowGetSize"),SetMaxSize:(t,o)=>i("WZ:"+t+":"+o,e),SetMinSize:(t,o)=>i("Wz:"+t+":"+o,e),SetAlwaysOnTop:t=>i("WATP:"+(t?"1":"0"),e),SetPosition:(t,o)=>i("Wp:"+t+":"+o,e),GetPosition:()=>f(":wails:WindowGetPos"),Hide:()=>i("WH",e),Maximise:()=>i("WM",e),Show:()=>i("WS",e),ToggleMaximise:()=>i("Wt",e),UnMaximise:()=>i("WU",e),Minimise:()=>i("Wm",e),UnMinimise:()=>i("Wu",e),SetBackgroundColour:(t,o,n,r)=>i("Wr:"+JSON.stringify({r:t||0,g:o||0,b:n||0,a:r||255},e))}}window.wails={Callback:p,callbacks:c,EventsNotify:h,eventListeners:a,SetBindings:v};function x(e){return{Window:g(e),Show:()=>i("S"),Hide:()=>i("H"),Quit:()=>i("Q")}}window.runtime=x(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
||||
(()=>{var w=null;(function(){let o=function(n){let r=window[n.shift()];for(;r&&n.length;)r=r[n.shift()];return r},e=o(["chrome","webview","postMessage"]),t=o(["webkit","messageHandlers","external","postMessage"]);if(!e&&!t){console.error("Unsupported Platform");return}e&&(w=n=>window.chrome.webview.postMessage(n)),t&&(w=n=>window.webkit.messageHandlers.external.postMessage(n))})();function u(o,e){w(e&&e!==-1?"WINDOWID:"+e+":"+o:o)}var s={};function x(){let o=new Uint32Array(1);return window.crypto.getRandomValues(o)[0]}function g(){return Math.random()*9007199254740991}var f;window.crypto?f=x:f=g;function m(o,e,t){t==null&&(t=0);let n=window.wails.window.ID();return new Promise(function(r,i){let l;do l=o+"-"+f();while(s[l]);let a;t>0&&(a=setTimeout(function(){i(Error("Call to "+o+" timed out. Request ID: "+l))},t)),s[l]={timeoutHandle:a,reject:i,resolve:r};try{let d={name:o,args:e,callbackID:l,windowID:n};window.WailsInvoke("C"+JSON.stringify(d))}catch(d){console.error(d)}})}window.ObfuscatedCall=(o,e,t)=>(t==null&&(t=0),new Promise(function(n,r){let i;do i=o+"-"+f();while(s[i]);let l;t>0&&(l=setTimeout(function(){r(Error("Call to method "+o+" timed out. Request ID: "+i))},t)),s[i]={timeoutHandle:l,reject:r,resolve:n};try{let a={id:o,args:e,callbackID:i,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(a))}catch(a){console.error(a)}}));function h(o){let e;try{e=JSON.parse(o)}catch(r){let i=`Invalid JSON passed to callback: ${r.message}. Message: ${o}`;throw runtime.LogDebug(i),new Error(i)}let t=e.callbackid,n=s[t];if(!n){let r=`Callback '${t}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete s[t],e.error?n.reject(e.error):n.resolve(e.result)}var c={};function b(o){let e=o.name;if(c[e]){let t=c[e].slice();for(let n=0;n<c[e].length;n+=1){let r=c[e][n],i=o.data;r.Callback(i)&&t.splice(n,1)}t.length===0?k(e):c[e]=t}}function p(o){let e;try{e=JSON.parse(o)}catch{let n="Invalid JSON passed to Notify: "+o;throw new Error(n)}b(e)}function k(o){delete c[o],window.WailsInvoke("EX"+o)}window.go={};function S(o){try{o=JSON.parse(o)}catch(e){console.error(e)}window.go=window.go||{},Object.keys(o).forEach(e=>{window.go[e]=window.go[e]||{},Object.keys(o[e]).forEach(t=>{window.go[e][t]=window.go[e][t]||{},Object.keys(o[e][t]).forEach(n=>{window.go[e][t][n]=function(){let r=0;function i(){let l=[].slice.call(arguments);return m([e,t,n].join("."),l,r)}return i.setTimeout=function(l){r=l},i.getTimeout=function(){return r},i}()})})})}var v=window.location.origin+"/wails/runtime";function E(o,e){let t=new URL(v);if(t.searchParams.append("method",o),e)for(let n in e)t.searchParams.append(n,e[n]);return new Promise((n,r)=>{fetch(t).then(i=>{if(i.ok)return i.json();r(Error(i.statusText))}).then(i=>n(i)).catch(i=>r(i))})}function y(o){let e=function(t,n){return o!==-1&&(n.windowID=o),E("window."+t,n)};return{Center:()=>e("Center"),SetTitle:t=>e("SetTitle",{title:t}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(t,n)=>e("SetSize",{width:t,height:n}),GetSize:()=>e("GetSize"),SetMaxSize:(t,n)=>e("SetMaxSize",{width:t,height:n}),SetMinSize:(t,n)=>e("SetMinSize",{width:t,height:n}),SetAlwaysOnTop:t=>e("SetAlwaysOnTop",{alwaysOnTop:t}),SetPosition:(t,n)=>e("SetPosition",{x:t,y:n}),GetPosition:()=>e("GetPosition"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(t,n,r,i)=>e("SetBackgroundColour",{R,G,B,A})}}window.wails={Callback:h,callbacks:s,EventsNotify:p,eventListeners:c,SetBindings:S};function O(o){return{Window:y(o),Show:()=>u("S"),Hide:()=>u("H"),Quit:()=>u("Q")}}window.runtime=O(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
||||
|
@ -1 +1 @@
|
||||
(()=>{var u=null;(function(){let e=function(n){for(var r=window[n.shift()];r&&n.length;)r=r[n.shift()];return r},t=e(["chrome","webview","postMessage"]),o=e(["webkit","messageHandlers","external","postMessage"]);if(!t&&!o){console.error("Unsupported Platform");return}t&&(u=n=>window.chrome.webview.postMessage(n)),o&&(u=n=>window.webkit.messageHandlers.external.postMessage(n))})();function i(e,t){u(t&&t!==-1?"WINDOWID:"+t+":"+e:e)}var c={};function y(){var e=new Uint32Array(1);return window.crypto.getRandomValues(e)[0]}function W(){return Math.random()*9007199254740991}var d;window.crypto?d=y:d=W;function f(e,t,o){o==null&&(o=0);let n=window.wails.window.ID();return new Promise(function(r,l){var s;do s=e+"-"+d();while(c[s]);var w;o>0&&(w=setTimeout(function(){l(Error("Call to "+e+" timed out. Request ID: "+s))},o)),c[s]={timeoutHandle:w,reject:l,resolve:r};try{let m={name:e,args:t,callbackID:s,windowID:n};window.WailsInvoke("C"+JSON.stringify(m))}catch(m){console.error(m)}})}window.ObfuscatedCall=(e,t,o)=>(o==null&&(o=0),new Promise(function(n,r){var l;do l=e+"-"+d();while(c[l]);var s;o>0&&(s=setTimeout(function(){r(Error("Call to method "+e+" timed out. Request ID: "+l))},o)),c[l]={timeoutHandle:s,reject:r,resolve:n};try{let w={id:e,args:t,callbackID:l,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(w))}catch(w){console.error(w)}}));function p(e){let t;try{t=JSON.parse(e)}catch(r){let l=`Invalid JSON passed to callback: ${r.message}. Message: ${e}`;throw runtime.LogDebug(l),new Error(l)}let o=t.callbackid,n=c[o];if(!n){let r=`Callback '${o}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete c[o],t.error?n.reject(t.error):n.resolve(t.result)}var a={};function b(e){let t=e.name;if(a[t]){let o=a[t].slice();for(let n=0;n<a[t].length;n+=1){let r=a[t][n],l=e.data;r.Callback(l)&&o.splice(n,1)}o.length===0?S(t):a[t]=o}}function h(e){let t;try{t=JSON.parse(e)}catch{let n="Invalid JSON passed to Notify: "+e;throw new Error(n)}b(t)}function S(e){delete a[e],window.WailsInvoke("EX"+e)}window.go={};function v(e){try{e=JSON.parse(e)}catch(t){console.error(t)}window.go=window.go||{},Object.keys(e).forEach(t=>{window.go[t]=window.go[t]||{},Object.keys(e[t]).forEach(o=>{window.go[t][o]=window.go[t][o]||{},Object.keys(e[t][o]).forEach(n=>{window.go[t][o][n]=function(){let r=0;function l(){let s=[].slice.call(arguments);return f([t,o,n].join("."),s,r)}return l.setTimeout=function(s){r=s},l.getTimeout=function(){return r},l}()})})})}function g(e){return{Center:()=>i("Wc",e),SetTitle:t=>i("WT"+t,e),Fullscreen:()=>i("WF",e),UnFullscreen:()=>i("Wf",e),SetSize:(t,o)=>i("WS"+t+","+o,e),GetSize:()=>f(":wails:WindowGetSize"),SetMaxSize:(t,o)=>i("WZ:"+t+":"+o,e),SetMinSize:(t,o)=>i("Wz:"+t+":"+o,e),SetAlwaysOnTop:t=>i("WATP:"+(t?"1":"0"),e),SetPosition:(t,o)=>i("Wp:"+t+":"+o,e),GetPosition:()=>f(":wails:WindowGetPos"),Hide:()=>i("WH",e),Maximise:()=>i("WM",e),Show:()=>i("WS",e),ToggleMaximise:()=>i("Wt",e),UnMaximise:()=>i("WU",e),Minimise:()=>i("Wm",e),UnMinimise:()=>i("Wu",e),SetBackgroundColour:(t,o,n,r)=>i("Wr:"+JSON.stringify({r:t||0,g:o||0,b:n||0,a:r||255},e))}}window.wails={Callback:p,callbacks:c,EventsNotify:h,eventListeners:a,SetBindings:v};function x(e){return{Window:g(e),Show:()=>i("S"),Hide:()=>i("H"),Quit:()=>i("Q")}}window.runtime=x(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
||||
(()=>{var w=null;(function(){let o=function(n){let r=window[n.shift()];for(;r&&n.length;)r=r[n.shift()];return r},e=o(["chrome","webview","postMessage"]),t=o(["webkit","messageHandlers","external","postMessage"]);if(!e&&!t){console.error("Unsupported Platform");return}e&&(w=n=>window.chrome.webview.postMessage(n)),t&&(w=n=>window.webkit.messageHandlers.external.postMessage(n))})();function u(o,e){w(e&&e!==-1?"WINDOWID:"+e+":"+o:o)}var s={};function x(){let o=new Uint32Array(1);return window.crypto.getRandomValues(o)[0]}function g(){return Math.random()*9007199254740991}var f;window.crypto?f=x:f=g;function m(o,e,t){t==null&&(t=0);let n=window.wails.window.ID();return new Promise(function(r,i){let l;do l=o+"-"+f();while(s[l]);let a;t>0&&(a=setTimeout(function(){i(Error("Call to "+o+" timed out. Request ID: "+l))},t)),s[l]={timeoutHandle:a,reject:i,resolve:r};try{let d={name:o,args:e,callbackID:l,windowID:n};window.WailsInvoke("C"+JSON.stringify(d))}catch(d){console.error(d)}})}window.ObfuscatedCall=(o,e,t)=>(t==null&&(t=0),new Promise(function(n,r){let i;do i=o+"-"+f();while(s[i]);let l;t>0&&(l=setTimeout(function(){r(Error("Call to method "+o+" timed out. Request ID: "+i))},t)),s[i]={timeoutHandle:l,reject:r,resolve:n};try{let a={id:o,args:e,callbackID:i,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(a))}catch(a){console.error(a)}}));function h(o){let e;try{e=JSON.parse(o)}catch(r){let i=`Invalid JSON passed to callback: ${r.message}. Message: ${o}`;throw runtime.LogDebug(i),new Error(i)}let t=e.callbackid,n=s[t];if(!n){let r=`Callback '${t}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete s[t],e.error?n.reject(e.error):n.resolve(e.result)}var c={};function b(o){let e=o.name;if(c[e]){let t=c[e].slice();for(let n=0;n<c[e].length;n+=1){let r=c[e][n],i=o.data;r.Callback(i)&&t.splice(n,1)}t.length===0?k(e):c[e]=t}}function p(o){let e;try{e=JSON.parse(o)}catch{let n="Invalid JSON passed to Notify: "+o;throw new Error(n)}b(e)}function k(o){delete c[o],window.WailsInvoke("EX"+o)}window.go={};function S(o){try{o=JSON.parse(o)}catch(e){console.error(e)}window.go=window.go||{},Object.keys(o).forEach(e=>{window.go[e]=window.go[e]||{},Object.keys(o[e]).forEach(t=>{window.go[e][t]=window.go[e][t]||{},Object.keys(o[e][t]).forEach(n=>{window.go[e][t][n]=function(){let r=0;function i(){let l=[].slice.call(arguments);return m([e,t,n].join("."),l,r)}return i.setTimeout=function(l){r=l},i.getTimeout=function(){return r},i}()})})})}var v=window.location.origin+"/wails/runtime";function E(o,e){let t=new URL(v);if(t.searchParams.append("method",o),e)for(let n in e)t.searchParams.append(n,e[n]);return new Promise((n,r)=>{fetch(t).then(i=>{if(i.ok)return i.json();r(Error(i.statusText))}).then(i=>n(i)).catch(i=>r(i))})}function y(o){let e=function(t,n){return o!==-1&&(n.windowID=o),E("window."+t,n)};return{Center:()=>e("Center"),SetTitle:t=>e("SetTitle",{title:t}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(t,n)=>e("SetSize",{width:t,height:n}),GetSize:()=>e("GetSize"),SetMaxSize:(t,n)=>e("SetMaxSize",{width:t,height:n}),SetMinSize:(t,n)=>e("SetMinSize",{width:t,height:n}),SetAlwaysOnTop:t=>e("SetAlwaysOnTop",{alwaysOnTop:t}),SetPosition:(t,n)=>e("SetPosition",{x:t,y:n}),GetPosition:()=>e("GetPosition"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(t,n,r,i)=>e("SetBackgroundColour",{R,G,B,A})}}window.wails={Callback:h,callbacks:s,EventsNotify:p,eventListeners:c,SetBindings:S};function O(o){return{Window:y(o),Show:()=>u("S"),Hide:()=>u("H"),Quit:()=>u("Q")}}window.runtime=O(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
||||
|
@ -114,6 +114,13 @@ func (a *App) getSystemTrayID() uint {
|
||||
a.systemTrayID++
|
||||
return a.systemTrayID
|
||||
}
|
||||
|
||||
func (a *App) getWindowForID(id uint) *WebviewWindow {
|
||||
a.windowsLock.Lock()
|
||||
defer a.windowsLock.Unlock()
|
||||
return a.windows[id]
|
||||
}
|
||||
|
||||
func (a *App) On(eventType events.ApplicationEventType, callback func()) {
|
||||
eventID := uint(eventType)
|
||||
a.applicationEventListenersLock.Lock()
|
||||
|
@ -163,7 +163,7 @@ func newRole(role Role) *MenuItem {
|
||||
case ToggleDevTools:
|
||||
return newToggleDevToolsMenuItem()
|
||||
case ResetZoom:
|
||||
return newResetZoomMenuItem()
|
||||
return newZoomResetMenuItem()
|
||||
case ZoomIn:
|
||||
return newZoomInMenuItem()
|
||||
case ZoomOut:
|
||||
|
@ -564,14 +564,14 @@ func newToggleDevToolsMenuItem() *MenuItem {
|
||||
})
|
||||
}
|
||||
|
||||
func newResetZoomMenuItem() *MenuItem {
|
||||
func newZoomResetMenuItem() *MenuItem {
|
||||
// reset zoom menu item
|
||||
return newMenuItem("Actual Size").
|
||||
SetAccelerator("CmdOrCtrl+0").
|
||||
OnClick(func(ctx *Context) {
|
||||
currentWindow := globalApplication.CurrentWindow()
|
||||
if currentWindow != nil {
|
||||
currentWindow.ResetZoom()
|
||||
currentWindow.ZoomReset()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -2,7 +2,10 @@ package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type MessageProcessor struct {
|
||||
@ -15,46 +18,41 @@ func NewMessageProcessor(w *WebviewWindow) *MessageProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MessageProcessor) ProcessMessage(message string) {
|
||||
func (m *MessageProcessor) httpError(rw http.ResponseWriter, message string, args ...any) {
|
||||
m.Error(message, args...)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
rw.Write([]byte(message))
|
||||
}
|
||||
|
||||
// TODO: Implement calls to other windows
|
||||
// Check for prefix "WINDOWID"
|
||||
// If prefix exists, get window ID by parsing: "WINDOWID:12:MESSAGE"
|
||||
|
||||
if strings.HasPrefix(message, "WINDOWID") {
|
||||
m.Error("Window ID prefix not yet implemented")
|
||||
func (m *MessageProcessor) HandleRuntimeCall(rw http.ResponseWriter, r *http.Request) {
|
||||
m.Info("Processing runtime call")
|
||||
// Read "method" from query string
|
||||
method := r.URL.Query().Get("method")
|
||||
if method == "" {
|
||||
m.httpError(rw, "No method specified")
|
||||
return
|
||||
}
|
||||
|
||||
window := m.window
|
||||
|
||||
if message == "" {
|
||||
m.Error("Blank message received")
|
||||
splitMethod := strings.Split(method, ".")
|
||||
if len(splitMethod) != 2 {
|
||||
m.httpError(rw, "Invalid method format")
|
||||
return
|
||||
}
|
||||
m.Info("Processing message: %s", message)
|
||||
switch message[0] {
|
||||
//case 'L':
|
||||
// m.processLogMessage(message)
|
||||
//case 'E':
|
||||
// return m.processEventMessage(message)
|
||||
//case 'C':
|
||||
// return m.processCallMessage(message)
|
||||
//case 'c':
|
||||
// return m.processSecureCallMessage(message)
|
||||
case 'W':
|
||||
m.processWindowMessage(message, window)
|
||||
//case 'B':
|
||||
// return m.processBrowserMessage(message)
|
||||
case 'Q':
|
||||
globalApplication.Quit()
|
||||
case 'S':
|
||||
//globalApplication.Show()
|
||||
case 'H':
|
||||
//globalApplication.Hide()
|
||||
// Get the object
|
||||
object := splitMethod[0]
|
||||
// Get the method
|
||||
method = splitMethod[1]
|
||||
|
||||
switch object {
|
||||
case "window":
|
||||
m.processWindowMethod(method, rw, r)
|
||||
default:
|
||||
m.Error("Unknown message from front end:", message)
|
||||
m.httpError(rw, "Unknown runtime call: %s", object)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (m *MessageProcessor) ProcessMessage(message string) {
|
||||
m.Info("ProcessMessage from front end:", message)
|
||||
}
|
||||
|
||||
func (m *MessageProcessor) Error(message string, args ...any) {
|
||||
@ -64,3 +62,23 @@ func (m *MessageProcessor) Error(message string, args ...any) {
|
||||
func (m *MessageProcessor) Info(message string, args ...any) {
|
||||
fmt.Printf("[MessageProcessor] Info: "+message, args...)
|
||||
}
|
||||
|
||||
func (m *MessageProcessor) json(rw http.ResponseWriter, data any) {
|
||||
// convert data to json
|
||||
var jsonPayload = []byte("{}")
|
||||
var err error
|
||||
if data != nil {
|
||||
jsonPayload, err = jsoniter.Marshal(data)
|
||||
if err != nil {
|
||||
m.Error("Unable to convert data to JSON. Please report this to the Wails team! Error: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
_, err = rw.Write(jsonPayload)
|
||||
if err != nil {
|
||||
m.Error("Unable to write json payload. Please report this to the Wails team! Error: %s", err)
|
||||
return
|
||||
}
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
|
95
v3/pkg/application/messageprocessor_params.go
Normal file
95
v3/pkg/application/messageprocessor_params.go
Normal file
@ -0,0 +1,95 @@
|
||||
package application
|
||||
|
||||
import "strconv"
|
||||
|
||||
type QueryParams map[string][]string
|
||||
|
||||
func (qp QueryParams) String(key string) *string {
|
||||
if qp == nil {
|
||||
return nil
|
||||
}
|
||||
values := qp[key]
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &values[0]
|
||||
}
|
||||
|
||||
func (qp QueryParams) Int(key string) *int {
|
||||
val := qp.String(key)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
result, err := strconv.Atoi(*val)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
||||
func (qp QueryParams) UInt8(key string) *uint8 {
|
||||
val := qp.String(key)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
intResult, err := strconv.Atoi(*val)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if intResult < 0 {
|
||||
intResult = 0
|
||||
}
|
||||
if intResult > 255 {
|
||||
intResult = 255
|
||||
}
|
||||
|
||||
var result = uint8(intResult)
|
||||
|
||||
return &result
|
||||
}
|
||||
func (qp QueryParams) UInt(key string) *uint {
|
||||
val := qp.String(key)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
intResult, err := strconv.Atoi(*val)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if intResult < 0 {
|
||||
intResult = 0
|
||||
}
|
||||
if intResult > 255 {
|
||||
intResult = 255
|
||||
}
|
||||
|
||||
var result = uint(intResult)
|
||||
|
||||
return &result
|
||||
}
|
||||
|
||||
func (qp QueryParams) Bool(key string) *bool {
|
||||
val := qp.String(key)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
result, err := strconv.ParseBool(*val)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
||||
func (qp QueryParams) Float64(key string) *float64 {
|
||||
val := qp.String(key)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
result, err := strconv.ParseFloat(*val, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &result
|
||||
}
|
@ -1,95 +1,191 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"net/http"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/options"
|
||||
)
|
||||
|
||||
func (m *MessageProcessor) mustAtoI(input string) int {
|
||||
result, err := strconv.Atoi(input)
|
||||
if err != nil {
|
||||
m.Error("cannot convert %s to integer!", input)
|
||||
}
|
||||
return result
|
||||
}
|
||||
func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (m *MessageProcessor) processWindowMessage(message string, window *WebviewWindow) {
|
||||
if len(message) < 2 {
|
||||
m.Error("Invalid Window Message: " + message)
|
||||
params := QueryParams(r.URL.Query())
|
||||
|
||||
var targetWindow = m.window
|
||||
windowID := params.UInt("windowID")
|
||||
if windowID != nil {
|
||||
// Get window for ID
|
||||
targetWindow = globalApplication.getWindowForID(*windowID)
|
||||
if targetWindow == nil {
|
||||
m.Error("Window ID %s not found", *windowID)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch message[1] {
|
||||
case 'A':
|
||||
switch message[2:] {
|
||||
//case "SDT":
|
||||
// go window.WindowSetSystemDefaultTheme()
|
||||
//case "LT":
|
||||
// go window.SetLightTheme()
|
||||
//case "DT":
|
||||
// go window.SetDarkTheme()
|
||||
case "TP:0", "TP:1":
|
||||
if message[2:] == "TP:0" {
|
||||
go window.SetAlwaysOnTop(false)
|
||||
} else if message[2:] == "TP:1" {
|
||||
go window.SetAlwaysOnTop(true)
|
||||
}
|
||||
switch method {
|
||||
case "SetTitle":
|
||||
title := params.String("title")
|
||||
if title == nil {
|
||||
m.Error("SetTitle: title is required")
|
||||
return
|
||||
}
|
||||
case 'c':
|
||||
go window.Center()
|
||||
case 'T':
|
||||
title := message[2:]
|
||||
go window.SetTitle(title)
|
||||
case 'F':
|
||||
go window.Fullscreen()
|
||||
case 'f':
|
||||
go window.UnFullscreen()
|
||||
case 's':
|
||||
parts := strings.Split(message[3:], ":")
|
||||
w := m.mustAtoI(parts[0])
|
||||
h := m.mustAtoI(parts[1])
|
||||
go window.SetSize(w, h)
|
||||
case 'p':
|
||||
parts := strings.Split(message[3:], ":")
|
||||
x := m.mustAtoI(parts[0])
|
||||
y := m.mustAtoI(parts[1])
|
||||
go window.SetPosition(x, y)
|
||||
case 'H':
|
||||
go window.Hide()
|
||||
case 'S':
|
||||
go window.Show()
|
||||
//case 'R':
|
||||
// go window.ReloadApp()
|
||||
case 'r':
|
||||
var rgba options.RGBA
|
||||
err := json.Unmarshal([]byte(message[3:]), &rgba)
|
||||
if err != nil {
|
||||
m.Error("Invalid RGBA Message: %s", err.Error())
|
||||
targetWindow.SetTitle(*title)
|
||||
m.json(rw, nil)
|
||||
case "SetSize":
|
||||
width := params.Int("width")
|
||||
height := params.Int("height")
|
||||
if width == nil || height == nil {
|
||||
m.Error("Invalid SetSize message")
|
||||
return
|
||||
}
|
||||
go window.SetBackgroundColour(&rgba)
|
||||
case 'M':
|
||||
go window.Maximise()
|
||||
//case 't':
|
||||
// go window.ToggleMaximise()
|
||||
case 'U':
|
||||
go window.UnMaximise()
|
||||
case 'm':
|
||||
go window.Minimise()
|
||||
case 'u':
|
||||
go window.UnMinimise()
|
||||
case 'Z':
|
||||
parts := strings.Split(message[3:], ":")
|
||||
w := m.mustAtoI(parts[0])
|
||||
h := m.mustAtoI(parts[1])
|
||||
go window.SetMaxSize(w, h)
|
||||
case 'z':
|
||||
parts := strings.Split(message[3:], ":")
|
||||
w := m.mustAtoI(parts[0])
|
||||
h := m.mustAtoI(parts[1])
|
||||
go window.SetMinSize(w, h)
|
||||
targetWindow.SetSize(*width, *height)
|
||||
m.json(rw, nil)
|
||||
case "SetPosition":
|
||||
x := params.Int("x")
|
||||
y := params.Int("y")
|
||||
if x == nil || y == nil {
|
||||
m.Error("Invalid SetPosition message")
|
||||
return
|
||||
}
|
||||
targetWindow.SetPosition(*x, *y)
|
||||
m.json(rw, nil)
|
||||
case "Fullscreen":
|
||||
targetWindow.Fullscreen()
|
||||
m.json(rw, nil)
|
||||
case "UnFullscreen":
|
||||
targetWindow.UnFullscreen()
|
||||
m.json(rw, nil)
|
||||
case "Minimise":
|
||||
targetWindow.Minimize()
|
||||
m.json(rw, nil)
|
||||
case "UnMinimise":
|
||||
targetWindow.UnMinimise()
|
||||
m.json(rw, nil)
|
||||
case "Maximise":
|
||||
targetWindow.Maximise()
|
||||
m.json(rw, nil)
|
||||
case "UnMaximise":
|
||||
targetWindow.UnMaximise()
|
||||
m.json(rw, nil)
|
||||
case "Show":
|
||||
targetWindow.Show()
|
||||
m.json(rw, nil)
|
||||
case "Hide":
|
||||
targetWindow.Hide()
|
||||
m.json(rw, nil)
|
||||
case "Close":
|
||||
targetWindow.Close()
|
||||
m.json(rw, nil)
|
||||
case "Center":
|
||||
targetWindow.Center()
|
||||
m.json(rw, nil)
|
||||
case "Size":
|
||||
width, height := targetWindow.Size()
|
||||
m.json(rw, map[string]interface{}{
|
||||
"width": width,
|
||||
"height": height,
|
||||
})
|
||||
case "Position":
|
||||
x, y := targetWindow.Position()
|
||||
m.json(rw, map[string]interface{}{
|
||||
"x": x,
|
||||
"y": y,
|
||||
})
|
||||
case "SetBackgroundColour":
|
||||
r := params.UInt8("r")
|
||||
if r == nil {
|
||||
m.Error("Invalid SetBackgroundColour message: 'r' value required")
|
||||
return
|
||||
}
|
||||
g := params.UInt8("g")
|
||||
if g == nil {
|
||||
m.Error("Invalid SetBackgroundColour message: 'g' value required")
|
||||
return
|
||||
}
|
||||
b := params.UInt8("b")
|
||||
if b == nil {
|
||||
m.Error("Invalid SetBackgroundColour message: 'b' value required")
|
||||
return
|
||||
}
|
||||
a := params.UInt8("a")
|
||||
if a == nil {
|
||||
m.Error("Invalid SetBackgroundColour message: 'a' value required")
|
||||
return
|
||||
}
|
||||
targetWindow.SetBackgroundColour(&options.RGBA{
|
||||
Red: *r,
|
||||
Green: *g,
|
||||
Blue: *b,
|
||||
Alpha: *a,
|
||||
})
|
||||
m.json(rw, nil)
|
||||
case "SetAlwaysOnTop":
|
||||
alwaysOnTop := params.Bool("alwaysOnTop")
|
||||
if alwaysOnTop == nil {
|
||||
m.Error("Invalid SetAlwaysOnTop message: 'alwaysOnTop' value required")
|
||||
return
|
||||
}
|
||||
targetWindow.SetAlwaysOnTop(*alwaysOnTop)
|
||||
m.json(rw, nil)
|
||||
case "SetResizable":
|
||||
resizable := params.Bool("resizable")
|
||||
if resizable == nil {
|
||||
m.Error("Invalid SetResizable message: 'resizable' value required")
|
||||
return
|
||||
}
|
||||
targetWindow.SetResizable(*resizable)
|
||||
m.json(rw, nil)
|
||||
case "SetMinSize":
|
||||
width := params.Int("width")
|
||||
height := params.Int("height")
|
||||
if width == nil || height == nil {
|
||||
m.Error("Invalid SetMinSize message")
|
||||
return
|
||||
}
|
||||
targetWindow.SetMinSize(*width, *height)
|
||||
m.json(rw, nil)
|
||||
case "SetMaxSize":
|
||||
width := params.Int("width")
|
||||
height := params.Int("height")
|
||||
if width == nil || height == nil {
|
||||
m.Error("Invalid SetMaxSize message")
|
||||
return
|
||||
}
|
||||
targetWindow.SetMaxSize(*width, *height)
|
||||
m.json(rw, nil)
|
||||
case "Width":
|
||||
width := targetWindow.Width()
|
||||
m.json(rw, map[string]interface{}{
|
||||
"width": width,
|
||||
})
|
||||
case "Height":
|
||||
height := targetWindow.Height()
|
||||
m.json(rw, map[string]interface{}{
|
||||
"height": height,
|
||||
})
|
||||
case "ZoomIn":
|
||||
targetWindow.ZoomIn()
|
||||
m.json(rw, nil)
|
||||
case "ZoomOut":
|
||||
targetWindow.ZoomOut()
|
||||
m.json(rw, nil)
|
||||
case "ZoomReset":
|
||||
targetWindow.ZoomReset()
|
||||
m.json(rw, nil)
|
||||
case "GetZoom":
|
||||
zoomLevel := targetWindow.GetZoom()
|
||||
m.json(rw, map[string]interface{}{
|
||||
"zoomLevel": zoomLevel,
|
||||
})
|
||||
case "SetZoom":
|
||||
zoomLevel := params.Float64("zoomLevel")
|
||||
if zoomLevel == nil {
|
||||
m.Error("Invalid SetZoom message: invalid 'zoomLevel' value")
|
||||
return
|
||||
}
|
||||
targetWindow.SetZoom(*zoomLevel)
|
||||
m.json(rw, nil)
|
||||
default:
|
||||
m.Error("unknown Window message: %s", message)
|
||||
m.httpError(rw, "Unknown window method: %s", method)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,9 +34,11 @@ type (
|
||||
reload()
|
||||
forceReload()
|
||||
toggleDevTools()
|
||||
resetZoom()
|
||||
zoomReset()
|
||||
zoomIn()
|
||||
zoomOut()
|
||||
getZoom() float64
|
||||
setZoom(zoom float64)
|
||||
close()
|
||||
zoom()
|
||||
minimize()
|
||||
@ -112,6 +114,7 @@ func NewWindow(options *options.WebviewWindow) *WebviewWindow {
|
||||
}
|
||||
|
||||
result.messageProcessor = NewMessageProcessor(result)
|
||||
srv.UseRuntimeHandler(result.messageProcessor)
|
||||
|
||||
return result
|
||||
}
|
||||
@ -211,6 +214,21 @@ func (w *WebviewWindow) SetURL(s string) *WebviewWindow {
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) SetZoom(magnification float64) *WebviewWindow {
|
||||
w.options.Zoom = magnification
|
||||
if w.impl != nil {
|
||||
w.impl.setZoom(magnification)
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) GetZoom() float64 {
|
||||
if w.impl != nil {
|
||||
return w.impl.getZoom()
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) SetResizable(b bool) *WebviewWindow {
|
||||
w.options.DisableResize = !b
|
||||
if w.impl != nil {
|
||||
@ -449,9 +467,9 @@ func (w *WebviewWindow) ToggleDevTools() {
|
||||
w.impl.toggleDevTools()
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) ResetZoom() *WebviewWindow {
|
||||
func (w *WebviewWindow) ZoomReset() *WebviewWindow {
|
||||
if w.impl != nil {
|
||||
w.impl.resetZoom()
|
||||
w.impl.zoomReset()
|
||||
}
|
||||
return w
|
||||
|
||||
|
@ -251,8 +251,8 @@ void windowEnableDevTools(void* nsWindow) {
|
||||
});
|
||||
}
|
||||
|
||||
// windowResetZoom
|
||||
void windowResetZoom(void* nsWindow) {
|
||||
// windowZoomReset
|
||||
void windowZoomReset(void* nsWindow) {
|
||||
// Reset zoom on main thread
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Get window delegate
|
||||
@ -262,6 +262,24 @@ void windowResetZoom(void* nsWindow) {
|
||||
});
|
||||
}
|
||||
|
||||
// windowZoomSet
|
||||
void windowZoomSet(void* nsWindow, double zoom) {
|
||||
// Reset zoom on main thread
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Get window delegate
|
||||
WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[(WebviewWindow*)nsWindow delegate];
|
||||
// Reset zoom
|
||||
[delegate.webView setMagnification:zoom];
|
||||
});
|
||||
}
|
||||
|
||||
// windowZoomGet
|
||||
float windowZoomGet(void* nsWindow) {
|
||||
// Get zoom
|
||||
WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[(WebviewWindow*)nsWindow delegate];
|
||||
return [delegate.webView magnification];
|
||||
}
|
||||
|
||||
// windowZoomIn
|
||||
void windowZoomIn(void* nsWindow) {
|
||||
// Zoom in on main thread
|
||||
@ -762,6 +780,14 @@ type macosWebviewWindow struct {
|
||||
parent *WebviewWindow
|
||||
}
|
||||
|
||||
func (w *macosWebviewWindow) getZoom() float64 {
|
||||
return float64(C.windowZoomGet(w.nsWindow))
|
||||
}
|
||||
|
||||
func (w *macosWebviewWindow) setZoom(zoom float64) {
|
||||
C.windowZoomSet(w.nsWindow, C.double(zoom))
|
||||
}
|
||||
|
||||
func (w *macosWebviewWindow) setFrameless(frameless bool) {
|
||||
C.windowSetFrameless(w.nsWindow, C.bool(frameless))
|
||||
if frameless {
|
||||
@ -848,8 +874,8 @@ func (w *macosWebviewWindow) zoomOut() {
|
||||
C.windowZoomOut(w.nsWindow)
|
||||
}
|
||||
|
||||
func (w *macosWebviewWindow) resetZoom() {
|
||||
C.windowResetZoom(w.nsWindow)
|
||||
func (w *macosWebviewWindow) zoomReset() {
|
||||
C.windowZoomReset(w.nsWindow)
|
||||
}
|
||||
|
||||
func (w *macosWebviewWindow) toggleDevTools() {
|
||||
@ -1017,6 +1043,7 @@ func (w *macosWebviewWindow) run() {
|
||||
if w.parent.options.MaxWidth != 0 || w.parent.options.MaxHeight != 0 {
|
||||
w.setMaxSize(w.parent.options.MaxWidth, w.parent.options.MaxHeight)
|
||||
}
|
||||
//w.setZoom(w.parent.options.Zoom)
|
||||
w.enableDevTools()
|
||||
w.setBackgroundColour(w.parent.options.BackgroundColour)
|
||||
|
||||
|
@ -39,6 +39,7 @@ type WebviewWindow struct {
|
||||
FullscreenButtonEnabled bool
|
||||
Hidden bool
|
||||
EnableFraudulentWebsiteWarnings bool
|
||||
Zoom float64
|
||||
}
|
||||
|
||||
var WindowDefaults = &WebviewWindow{
|
||||
|
Loading…
Reference in New Issue
Block a user