Skip to content

Commit

Permalink
Keep SSE connection while tab is inactive (#311)
Browse files Browse the repository at this point in the history
Fixes #246
Add better sse handles and docs
  • Loading branch information
delaneyj authored Dec 5, 2024
1 parent 093aeac commit df109f9
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 25 deletions.
16 changes: 8 additions & 8 deletions bundles/datastar.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions bundles/datastar.js.map

Large diffs are not rendered by default.

23 changes: 18 additions & 5 deletions library/src/plugins/official/backend/actions/sse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ const isWrongContent = (err: any) => `${err}`.includes(`text/event-stream`);
export type SSEArgs = {
method: METHOD;
headers?: Record<string, string>;
onlyRemote?: boolean;
includeLocal?: boolean;
openWhenHidden?: boolean;
};

export const ServerSentEvents: ActionPlugin = {
Expand All @@ -40,11 +41,22 @@ export const ServerSentEvents: ActionPlugin = {
fn: async (
ctx,
url: string,
args: SSEArgs = { method: "GET", headers: {}, onlyRemote: true },
args: SSEArgs,
) => {
const { el: { id: elId }, signals } = ctx;
const { headers: userHeaders, onlyRemote } = args;
const method = args.method.toUpperCase();
const {
method: methodAnyCase,
headers: userHeaders,
includeLocal,
openWhenHidden,
} = Object
.assign({
method: "GET",
headers: {},
includeLocal: false,
openWhenHidden: false,
}, args);
const method = methodAnyCase.toUpperCase();
try {
dispatchSSE(STARTED, { elId });
if (!!!url?.length) {
Expand All @@ -59,6 +71,7 @@ export const ServerSentEvents: ActionPlugin = {
const req: FetchEventSourceInit = {
method,
headers,
openWhenHidden,
onmessage: (evt) => {
if (!evt.event.startsWith(DATASTAR)) {
return;
Expand Down Expand Up @@ -101,7 +114,7 @@ export const ServerSentEvents: ActionPlugin = {
};

const urlInstance = new URL(url, window.location.origin);
const json = signals.JSON(false, onlyRemote);
const json = signals.JSON(false, !includeLocal);
if (method === "GET") {
const queryParams = new URLSearchParams(urlInstance.search);
queryParams.set(DATASTAR, json);
Expand Down
4 changes: 2 additions & 2 deletions sdk/dotnet/src/Consts.fs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions sdk/go/consts.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions sdk/php/src/Consts.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class Consts
{
public const DATASTAR_KEY = 'datastar';
public const VERSION = '0.21.0-beta1';
public const VERSION_CLIENT_BYTE_SIZE = 33186;
public const VERSION_CLIENT_BYTE_SIZE_GZIP = 12206;
public const VERSION_CLIENT_BYTE_SIZE = 33260;
public const VERSION_CLIENT_BYTE_SIZE_GZIP = 12223;

// The default duration for settling during merges. Allows for CSS transitions to complete.
public const DEFAULT_SETTLE_DURATION = 300;
Expand Down
11 changes: 10 additions & 1 deletion site/routes_examples_progress_bar.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package site

import (
"fmt"
"math/rand"
"net/http"
"time"
Expand All @@ -17,10 +18,18 @@ func setupExamplesProgressBar(examplesRouter chi.Router) error {
progress := 0

for progress < 100 {
progress = min(100, progress+rand.Intn(20)+1)
progress = min(100, progress+rand.Intn(10)+1)
sse.MergeFragmentTempl(progressBarView(progress))
sse.MergeFragments(
fmt.Sprintf("<title>%d%%</title>", progress),
datastar.WithSelector("title"),
)
time.Sleep(250 * time.Millisecond)
}
sse.MergeFragments(
"<title>Progress Bar</title>",
datastar.WithSelector("title"),
)
})

return nil
Expand Down
13 changes: 12 additions & 1 deletion site/static/md/examples/progress_bar.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,21 @@

<div
id="progress_bar"
data-on-load="sse('/examples/progress_bar/data')"
data-on-load="sse('/examples/progress_bar/data',{openWhenHidden:true})"
>
</div>
## Explanation

```html
<div
id="progress_bar"
data-on-load="sse('/examples/progress_bar/data',{openWhenHidden:true})"
>
</div>

```

This example shows how to implement an updating progress graphic. Since Datastar is using SSE this is very easy to implement. The server sends a progress value every 500 milliseconds, and the client updates the progress bar accordingly sending down a new SVG. After the progress is complete, the server a button to restart the job.

***Note:*** The `openWhenHidden` option is used to keep the connection open even when the progress bar is not visible. This is useful for when the user navigates away from the page and then returns. This will use more resources, so use it judiciously.
6 changes: 5 additions & 1 deletion site/static/md/reference/plugins_backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ Every request will be sent with a `{datastar: *}` object containing the current

The actions above take a second argument of options.

The `onlyRemoteSignals` option determines whether to only send remotely viewable signals values (defaults to `true`).
The `method` option is the HTTP method to use (defaults to `GET`).

The `includeLocal` option determines whether to only send remotely viewable signals values (defaults to `false`).

The `headers` option is an object containing headers to send with the request.

The `openWhenHidden` option determines whether to close the page is hidden (defaults to `false`). Useful for dashboards but will cause a drain on battery life and other resources.

```html
<div data-on-click="sse('/examples/click_to_edit/contact/1', {
onlyRemoteSignals: false,
Expand Down

0 comments on commit df109f9

Please sign in to comment.