diff --git a/mkdocs-website/docs/en/changelog.md b/mkdocs-website/docs/en/changelog.md index 0d13c0bcd82..8d91cec6062 100644 --- a/mkdocs-website/docs/en/changelog.md +++ b/mkdocs-website/docs/en/changelog.md @@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New `InitialPosition` option to specify if the window should be centered or positioned at the given X/Y location by [leaanthony](https://github.com/leaanthony) in [#3885](https://github.com/wailsapp/wails/pull/3885) - Add `Path` & `Paths` methods to `application` package by [ansxuman](https://github.com/ansxuman) and [leaanthony](https://github.com/leaanthony) in [#3823](https://github.com/wailsapp/wails/pull/3823) - Added `GeneralAutofillEnabled` and `PasswordAutosaveEnabled` Windows options by [leaanthony](https://github.com/leaanthony) in [#3766](https://github.com/wailsapp/wails/pull/3766) +- Added the ability to retrieve the window calling a service method by [leaanthony](https://github.com/leaanthony) + in [#3888](https://github.com/wailsapp/wails/pull/3888) ### Changed - Asset embed to include `all:frontend/dist` to support frameworks that generate subfolders by @atterpac in [#3887](https://github.com/wailsapp/wails/pull/3887) diff --git a/mkdocs-website/docs/en/learn/bindings.md b/mkdocs-website/docs/en/learn/bindings.md index 82f32a026cf..2d680f5d01b 100644 --- a/mkdocs-website/docs/en/learn/bindings.md +++ b/mkdocs-website/docs/en/learn/bindings.md @@ -329,3 +329,50 @@ export class Person { ``` Using TypeScript bindings provides type safety and improved IDE support when working with the generated code in your frontend. + +### Using `context.Context` + +When defining service methods in Go, you can include `context.Context` as the first parameter. The runtime will automatically provide a context when the method is called from the frontend. + +The context provides several powerful features: + +1. **Cancellation Support**: Long-running operations can be cancelled from the frontend, which will raise an error through the Promise chain. + +2. **Window Information**: You can determine which window made the call using these context keys: + - `application.WindowNameKey` - Returns the name of the calling window + - `application.WindowIDKey` - Returns the ID of the calling window + +Here are some examples: + +```go +// Basic context usage with cancellation +func (s *MyService) LongRunningTask(ctx context.Context, input string) (string, error) { + select { + // Check if the context has been cancelled from the frontend + case <-ctx.Done(): + return "", ctx.Err() + default: + // Process task + return "completed", nil + } +} + +// Getting caller window information +func (s *MyService) WindowAwareMethod(ctx context.Context) (string, error) { + windowName := ctx.Value(application.WindowNameKey).(string) + windowID := ctx.Value(application.WindowIDKey).(string) + return fmt.Sprintf("Called from window: %s (ID: %s)", windowName, windowID), nil +} +``` + +From the frontend, these methods can be called normally. If you need to cancel a long-running operation, the Promise will be rejected with the cancellation error: + +```javascript +// Call the method +const promise = MyService.LongRunningTask("input"); + +// Cancel it later if needed +// This will cause the context to be cancelled in the Go method +promise.cancel(); +``` + diff --git a/v3/pkg/application/messageprocessor_call.go b/v3/pkg/application/messageprocessor_call.go index 714d766db7c..45a6fba0cad 100644 --- a/v3/pkg/application/messageprocessor_call.go +++ b/v3/pkg/application/messageprocessor_call.go @@ -7,8 +7,12 @@ import ( "net/http" ) +type contextKey string + const ( - CallBinding = 0 + CallBinding = 0 + WindowNameKey contextKey = "WindowName" + WindowIDKey contextKey = "WindowID" ) func (m *MessageProcessor) callErrorCallback(window Window, message string, callID *string, err error) { @@ -95,6 +99,12 @@ func (m *MessageProcessor) processCallMethod(method int, rw http.ResponseWriter, return } + // Set the context values for the window + if window != nil { + ctx = context.WithValue(ctx, WindowNameKey, window.Name()) + ctx = context.WithValue(ctx, WindowIDKey, window.ID()) + } + go func() { defer func() { cancel() @@ -123,7 +133,11 @@ func (m *MessageProcessor) processCallMethod(method int, rw http.ResponseWriter, var jsonArgs struct { Args json.RawMessage `json:"args"` } - params.ToStruct(&jsonArgs) + err = params.ToStruct(&jsonArgs) + if err != nil { + m.callErrorCallback(window, "Error parsing arguments: %s", callID, err) + return + } m.Info("Call Binding:", "method", boundMethod, "args", string(jsonArgs.Args), "result", result) }() m.ok(rw)