From efd7354eaa4bbe8bbe2e6df71a357b5ea40cb103 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 7 Dec 2021 16:56:52 -0800 Subject: [PATCH 1/3] Add a websocket string binding --- README.md | 22 ++++++++++++++- data/binding/webstring.go | 59 +++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 ++ 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 data/binding/webstring.go diff --git a/README.md b/README.md index 4f3d17c8..c008d8a3 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,27 @@ h := widget.NewHexWidget() h.Set(0xf) ``` -## Validation +## Data Binding + +Community contributed data sources for binding. + +`import fyne.io/x/fyne/data/binding` + +### WebString + +A `WebSocketString` binding creates a data binding of type `String` to the specified web socket URL. +It is also `Closable` so you should be sure to call `Close()` once you are completed using it. + +```go +s, err := binding.NewWebSocketString("wss://demo.piesocket.com/v3/channel_1?api_key=oCdCMcMPQpbvNjUIzqtvF1d2X2okWpDQj4AwARJuAgtjhzKxVEjQU6IdCjwm¬ify_self") +l := widget.NewLabelWithData(s) +``` + +The code above uses a test web sockets server from "PieSocket", you can run the code above +and go to [their test page](https://www.piesocket.com/websocket-tester) to send messages. +The widget will automatically update to the latest data sent through the socket. + +## Data Validation Community contributed validators. diff --git a/data/binding/webstring.go b/data/binding/webstring.go new file mode 100644 index 00000000..7e8cf6b4 --- /dev/null +++ b/data/binding/webstring.go @@ -0,0 +1,59 @@ +// Package binding provides extended sources of data binding. +package binding + +import ( + "io" + "log" + "net/http" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/data/binding" + + "github.com/gorilla/websocket" +) + +type StringCloser interface { + binding.String + io.Closer +} + +type webSocketString struct { + binding.String + conn *websocket.Conn +} + +// NewWebSocketString returns a `String` binding to a web socket server specified as `url`. +// The resulting string will be set to the content of the latest message sent through the socket. +// You should also call `Close()` on the binding once you are done to free the connection. +func NewWebSocketString(url string) (StringCloser, error) { + s := binding.NewString() + sock, _, err := websocket.DefaultDialer.Dial(url, http.Header{}) + if err != nil { + return nil, err + } + + go func() { + for { + _, p, err := sock.ReadMessage() + if err != nil { + log.Println(err) + return + } + + err = s.Set(string(p)) + if err != nil { + fyne.LogError("Failed to set string from web socket", err) + } + } + }() + ret := &webSocketString{String: s, conn: sock} + return ret, nil +} + +func (s *webSocketString) Close() error { + if s.conn == nil { + return nil + } + + return s.conn.Close() +} diff --git a/go.mod b/go.mod index d5e2eae8..6e7aaa6c 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.14 require ( fyne.io/fyne/v2 v2.1.0 + github.com/gorilla/websocket v1.4.2 github.com/stretchr/testify v1.7.0 github.com/wagslane/go-password-validator v0.3.0 ) diff --git a/go.sum b/go.sum index 4fe8b3ac..83584dc4 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,8 @@ github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc= github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= From 2614fcb33590233578568a81c0ed25352ab6ff3e Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 7 Dec 2021 17:08:53 -0800 Subject: [PATCH 2/3] Add missing comment --- data/binding/webstring.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/binding/webstring.go b/data/binding/webstring.go index 7e8cf6b4..3a8f9b47 100644 --- a/data/binding/webstring.go +++ b/data/binding/webstring.go @@ -12,6 +12,8 @@ import ( "github.com/gorilla/websocket" ) +// StringCloser is an extension of the String interface that allows resources to be freed +// using the standard `Close()` method. type StringCloser interface { binding.String io.Closer From 92eb23bc4d18a3a2fc35a6b946de3e1219cc1d82 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 8 Dec 2021 13:40:11 -0800 Subject: [PATCH 3/3] PR feedback --- README.md | 3 ++- data/binding/webstring.go | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c008d8a3..a5cdf46a 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,8 @@ Community contributed data sources for binding. ### WebString -A `WebSocketString` binding creates a data binding of type `String` to the specified web socket URL. +A `WebSocketString` binding creates a `String` data binding to the specified web socket URL. +Each time a message is read the value will be converted to a `string` and set on the binding. It is also `Closable` so you should be sure to call `Close()` once you are completed using it. ```go diff --git a/data/binding/webstring.go b/data/binding/webstring.go index 3a8f9b47..8c08a67e 100644 --- a/data/binding/webstring.go +++ b/data/binding/webstring.go @@ -29,14 +29,14 @@ type webSocketString struct { // You should also call `Close()` on the binding once you are done to free the connection. func NewWebSocketString(url string) (StringCloser, error) { s := binding.NewString() - sock, _, err := websocket.DefaultDialer.Dial(url, http.Header{}) + conn, _, err := websocket.DefaultDialer.Dial(url, http.Header{}) if err != nil { return nil, err } go func() { for { - _, p, err := sock.ReadMessage() + _, p, err := conn.ReadMessage() if err != nil { log.Println(err) return @@ -48,7 +48,7 @@ func NewWebSocketString(url string) (StringCloser, error) { } } }() - ret := &webSocketString{String: s, conn: sock} + ret := &webSocketString{String: s, conn: conn} return ret, nil }