Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support except and restrict in push!() and run()` #244

Merged
merged 3 commits into from
Nov 28, 2023
Merged

Conversation

hhaensel
Copy link
Member

Currently, it is not possible to address single clients by push! or run commands.

To remedy this situation I propose to

  • modify existing keyword except to support not only UInt, but also Vetor{UInt} to exclude more than one client
  • remove WebSocket as a possible value for except and rather supply the id as it is done in WebThreads
  • add a keyword restrict to restrict messages to a client or list of clients

There will be a corresponding PR in Genie.

@hhaensel
Copy link
Member Author

hhaensel commented Nov 27, 2023

The current implementation for downloads is the following; in order to add the client info you add the keyword :addclient to the @on macro, e.g.

ui() = btn("Download", nocaps = true, icon = "download", @on(:click, :download_txt, :addclient))

@event download_txt begin                                                                                                                                                                                                                                                 
    download_text(__model__, "Hello world"; client = event["_client"])
end

@hhaensel
Copy link
Member Author

We could, however, always add the client info to the events.

@PGimenez
Copy link
Member

If I understand correctly, this would allow us to trigger downloads from the backend. Right?

One user recently asked about this on Discord, and I came up with a MWE (link) that does the following:

  • Add a download link to the page
  • Add a JS watcher to the link URL that clicks the link when the URL changes
  • Change the link URL from a reactive handler to trigger the download without user click

I believe this PR would make all the added JS unnecessary

@essenciary
Copy link
Member

"Trigger download from backend" -> that's simply a redirect isn't it? To the path/route that triggers the download...

@essenciary
Copy link
Member

But this PR really is not about the downloads, is it? It is about having a synchronised session (multiple clients using the same channel - a valid use case ex for collaborating on a dashboard, usage in GenieBuilder, etc) and being able to selectively send updates to just some of these clients. I think it's a great feature.

@hhaensel
Copy link
Member Author

Actually, it's both.
I had triggering downloads from the backend as use case to develop the extension. But it is of general interest for the use cases, @essenciary described. That's why I kept it very general and yet minimal invasive.

@hhaensel
Copy link
Member Author

"Trigger download from backend" -> that's simply a redirect isn't it? To the path/route that triggers the download...

I don't register a route to download the content. I add an anchor element with a data-url that contains data from a data buffer. The data is sent beforehand with a push! command to the field 'download' and then converted to a data buffer by Uint8Array.from(this.__download__). Then the anchor element is clicked by a script to trigger the download. It doesn't need more than the above example to make personalised downloads available.

@hhaensel
Copy link
Member Author

@hhaensel
Copy link
Member Author

@PGimenez @essenciary Do you think we should add client info per default, or should we keep it as optin by appending :addclient to the @on macro. Or should we just append _addclient to the eventkey

# 1. current implementation
ui() = btn("Download", nocaps = true, icon = "download", @on(:click, :download_txt, :addclient))

# 2. alternative implementation
ui() = btn("Download", nocaps = true, icon = "download", @on(:click, :download_txt_addclient))

# 3. always add client info (no change to current implementation)
ui() = btn("Download", nocaps = true, icon = "download", @on(:click, :download_txt))

@essenciary
Copy link
Member

essenciary commented Nov 28, 2023

"Trigger download from backend" -> that's simply a redirect isn't it? To the path/route that triggers the download...

I don't register a route to download the content. I add an anchor element with a data-url that contains data from a data buffer. The data is sent beforehand with a push! command to the field 'download' and then converted to a data buffer by Uint8Array.from(this.__download__). Then the anchor element is clicked by a script to trigger the download. It doesn't need more than the above example to make personalised downloads available.

OK - but why?

Literally all we had to do for that was to have a route /__/download?file=abc -- and open it in a new tab.

Am I missing something?

@hhaensel
Copy link
Member Author

"Trigger download from backend" -> that's simply a redirect isn't it? To the path/route that triggers the download...

I don't register a route to download the content. I add an anchor element with a data-url that contains data from a data buffer. The data is sent beforehand with a push! command to the field 'download' and then converted to a data buffer by Uint8Array.from(this.__download__). Then the anchor element is clicked by a script to trigger the download. It doesn't need more than the above example to make personalised downloads available.

OK - but why?

Literally all we had to do for that was to have a route /__/download?file=abc -- and open it in a new tab.

Am I missing something?

Yes, very often I have files that are generated on the fly, so I don't want to register and deregister a routed just for downloading a file.

Please have a look ath the Download Demo. It downloads a text file with the current content of the text field or a xlsx file with the current content of the table.

If I register a route, how could I prevent other people from addressing it? This way the information is only available to the person that requested the download.

@hhaensel
Copy link
Member Author

One of my use cases is that users calculate an optimised mixture and store the result on their computer. The file never exists on the server.

@essenciary
Copy link
Member

We don't need to register/deregister the route, there can always be a download utility there via Stipple.Downloads (eg on init of the module). Then the file name can be encrypted/hashed.

I did check the demo and hence my concern - it's not easy to follow and understand and it's quite complex. Hence my comment: is that the best/simplest way to achieve the objective? The code will need to be maintained for years to come.

@essenciary
Copy link
Member

OK, I understand - for files that don't exist on the server, that use case makes sense.
Also - is this for distinct plugin? If it's for a plugin (vs Stipple core) then I'm less concerned with complexity as it's optional addon.

src/Stipple.jl Outdated

Pushes data payloads over to the frontend by broadcasting the `vals` through the `channel`.
"""
function Base.push!(app::M, vals::Pair{Symbol,T};
channel::String = Genie.config.webchannels_default_route,
except::Union{Genie.WebChannels.HTTP.WebSockets.WebSocket,Nothing,UInt} = nothing)::Bool where {T,M<:ReactiveModel}
channel::String = getchannel(model),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is model here correct? Where is it coming from?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it should be app

Copy link
Member Author

@hhaensel hhaensel Nov 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had Genie.config.webchannels_default_route before and that generated errors, whenever I used it. I don't think that we still need the default route here, it's from the early days. Do you agree?
If yes, I'll fix that to be app
Or should we rename app to model? Or all to app?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - app is fine here, it's a local var. I prefer doing the minimum amount of changes so the PR is easier to understand in the future. So we just need to fix the bug with the incorrect variable name.

@hhaensel
Copy link
Member Author

It's our choice to include it in Stipple or to leave it as optional plugin.

The current PR is the basis for it to work, but there will be other uses as well, as you mentioned.

I did compared different implementations of js download. This seemed to me the most general one that can be adapted by users if they like. I don't think it will need lots of maintenance.

@essenciary
Copy link
Member

Yes, the changes in the PR are pretty light and easy to follow (the demo is more complex). Same for the PR in Genie.jl
We can merge both PRs in my opinion.

@hhaensel hhaensel merged commit cfe1025 into master Nov 28, 2023
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants