From 89fd6b6fe821fa2da90d1980a6e85346feb91246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Wed, 22 Nov 2023 07:41:09 +0100 Subject: [PATCH 01/12] fix tests for versioned assets --- test/runtests.jl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 789c2bfc..d2d3d9fe 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,6 +3,8 @@ using Stipple.Genie.HTTPUtils.HTTP using Test +version = Genie.Assets.package_version(Stipple) + function string_get(x) String(HTTP.get(x, retries = 0, status_exception = false).body) end @@ -396,13 +398,13 @@ end @page("/", ui) payload = String(HTTP.payload(HTTP.get("http://127.0.0.1:$port"))) @test match(r"
", payload).match == p1 - @test contains(payload, """", payload).match == p1 - @test contains(payload, """", payload).match == p1 @test contains(payload, r"test \d+") - @test contains(payload, """", payload).match == p1 - @test contains(payload, """", payload).match != p1 - @test contains(payload, """test \d+") # route constant String @page("/", ui()) payload = String(HTTP.payload(HTTP.get("http://127.0.0.1:$port"))) @test match(r"
", payload).match != p1 - @test contains(payload, """test \d+") down() From 68323df7203fc591e2ec813a325d5dbcca4d386b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Wed, 22 Nov 2023 07:41:34 +0100 Subject: [PATCH 02/12] set version v0.27.21 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index f69df650..b7c78928 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.27.20" +version = "0.27.21" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" From b8acebf953545a857a773ee8922d877df6f96988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Wed, 22 Nov 2023 08:36:01 +0100 Subject: [PATCH 03/12] fix Genie compat --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b7c78928..1859299c 100644 --- a/Project.toml +++ b/Project.toml @@ -27,7 +27,7 @@ Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" DataFrames = "1" Dates = "1.6" FilePathsBase = "0.9" -Genie = "5.23.0" +Genie = "5.23.2" GenieSession = "1" GenieSessionFileSession = "1" JSON = "0.20, 0.21" From bf729b07b59ac0bdd8c2a5668d800dd005a3e54b Mon Sep 17 00:00:00 2001 From: hhaensel Date: Wed, 22 Nov 2023 10:46:12 +0100 Subject: [PATCH 04/12] Julia expression attributes in row, cell and column --- src/Layout.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Layout.jl b/src/Layout.jl index e52a02cf..e5354c02 100644 --- a/src/Layout.jl +++ b/src/Layout.jl @@ -174,7 +174,7 @@ function row(args...; col == -1 && size != -1 && (col = size) class = class isa Symbol ? Symbol("$class + ' row'") : join(push!(split(class), "row"), " ") - kwargs = flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...) + kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...)) Genie.Renderer.Html.div(args...; kwargs...) end @@ -204,7 +204,7 @@ function column(args...; col == -1 && size != -1 && (col = size) class = class isa Symbol ? Symbol("$class + ' column'") : join(push!(split(class), "column"), " ") - kwargs = flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...) + kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...)) Genie.Renderer.Html.div(args...; kwargs...) end @@ -247,7 +247,7 @@ function cell(args...; col == 0 && size != 0 && (col = size) class = class isa Symbol ? Symbol("$class + ' st-col'") : join(push!(split(class), "st-col"), " ") - kwargs = flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...) + kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...)) Genie.Renderer.Html.div(args...; kwargs...) end From 6f804ca1c79177a50c479431e9a9e5b842ce173d Mon Sep 17 00:00:00 2001 From: hhaensel Date: Wed, 22 Nov 2023 10:51:19 +0100 Subject: [PATCH 05/12] set version v0.27.22 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1859299c..c8546740 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.27.21" +version = "0.27.22" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" From 506fc44e7b8d746d0f1c8551162b61fc43f9cb15 Mon Sep 17 00:00:00 2001 From: hhaensel Date: Wed, 22 Nov 2023 11:31:50 +0100 Subject: [PATCH 06/12] modify htmldiv to support Julia expressions and flexgrid --- src/Layout.jl | 16 +++++++++++++++- src/Stipple.jl | 1 - 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Layout.jl b/src/Layout.jl index e5354c02..86151c16 100644 --- a/src/Layout.jl +++ b/src/Layout.jl @@ -8,7 +8,7 @@ module Layout using Genie, Stipple export layout, add_css, remove_css -export page, app, row, column, cell, container, flexgrid_kwargs +export page, app, row, column, cell, container, flexgrid_kwargs, htmldiv export theme const THEMES = Function[] @@ -252,6 +252,20 @@ function cell(args...; Genie.Renderer.Html.div(args...; kwargs...) end +function htmldiv(args...; + col::Union{Int,AbstractString,Symbol,Nothing} = -1, + xs::Union{Int,AbstractString,Symbol,Nothing} = -1, sm::Union{Int,AbstractString,Symbol,Nothing} = -1, md::Union{Int,AbstractString,Symbol,Nothing} = -1, + lg::Union{Int,AbstractString,Symbol,Nothing} = -1, xl::Union{Int,AbstractString,Symbol,Nothing} = -1, size::Union{Int,AbstractString,Symbol,Nothing} = -1, + class = "", kwargs...) + + # for backward compatibility with `size` kwarg + col == -1 && size != -1 && (col = size) + + kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...)) + + Genie.Renderer.Html.div(args...; kwargs...) +end + function sizetocol(size::Union{String,Int,Nothing,Symbol} = -1, tag::Symbol = :col) (size == -1 || size === nothing) && return "" out = ["col"] diff --git a/src/Stipple.jl b/src/Stipple.jl index 62b7ed31..eb19e474 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -75,7 +75,6 @@ using Logging, Mixers, Random, Reexport, Dates, Tables @reexport @using_except Genie: download import Genie.Router.download @reexport @using_except Genie.Renderers.Html: mark, div, time, view, render, Headers, menu -const htmldiv = Html.div export render, htmldiv, js_attr @reexport using JSON3 @reexport using StructTypes From 8467e58991d1c64f30d057c81bb1618e551ba6e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Thu, 23 Nov 2023 17:16:02 +0100 Subject: [PATCH 07/12] add debugging of incoming messages --- src/Stipple.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Stipple.jl b/src/Stipple.jl index eb19e474..54919a47 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -471,6 +471,8 @@ function init(::Type{M}; @error ex end + @debug payload + field = Symbol(payload["field"]) #check if field exists From 3c2a4aadba1808cddbf8ae29328a0fb172d2c05d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Mon, 27 Nov 2023 09:16:49 +0100 Subject: [PATCH 08/12] refactor broadcasting to support `except` and `restrict` kwargs --- src/Stipple.jl | 27 +++++++++++++++++---------- src/stipple/jsintegration.jl | 6 +++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/Stipple.jl b/src/Stipple.jl index 54919a47..7c8b5e2b 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -461,7 +461,7 @@ function init(::Type{M}; if ! Genie.Router.ischannel(Symbol(ch)) Genie.Router.channel(ch, named = Symbol(ch)) do payload = Genie.Requests.payload(:payload)["payload"] - client = transport == Genie.WebChannels ? Genie.Requests.wsclient() : Genie.Requests.wtclient() + client = transport == Genie.WebChannels ? Genie.WebChannels.id(Genie.Requests.wsclient()) : Genie.Requests.wtclient() try haskey(payload, "sesstoken") && ! isempty(payload["sesstoken"]) && @@ -608,15 +608,16 @@ const max_retry_times = 10 """ Base.push!(app::M, vals::Pair{Symbol,T}; channel::String, - except::Union{Genie.WebChannels.HTTP.WebSockets.WebSocket,Nothing,UInt}) where {T,M<:ReactiveModel} + except::Union{Nothing,UInt,Vector{UInt}}) where {T,M<:ReactiveModel} 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), + except::Union{Nothing,UInt,Vector{UInt}} = nothing, + restrict::Union{Nothing,UInt,Vector{UInt}} = nothing)::Bool where {T,M<:ReactiveModel} try - webtransport().broadcast(channel, json(Dict("key" => julia_to_vue(vals[1]), "value" => Stipple.render(vals[2], vals[1]))), except = except) + webtransport().broadcast(channel, json(Dict("key" => julia_to_vue(vals[1]), "value" => Stipple.render(vals[2], vals[1]))); except, restrict) catch ex @debug ex false @@ -625,13 +626,16 @@ end function Base.push!(model::M, vals::Pair{Symbol,Reactive{T}}; channel::String = getchannel(model), - except::Union{Genie.WebChannels.HTTP.WebSockets.WebSocket,Nothing,UInt} = nothing)::Bool where {T,M<:ReactiveModel} + except::Union{Nothing,UInt,Vector{UInt}} = nothing, + restrict::Union{Nothing,UInt,Vector{UInt}} = nothing)::Bool where {T,M<:ReactiveModel} v = vals[2].r_mode != JSFUNCTION ? vals[2][] : replace_jsfunction(vals[2][]) - push!(model, Symbol(julia_to_vue(vals[1])) => v; channel, except) + push!(model, Symbol(julia_to_vue(vals[1])) => v; channel, except, restrict) end function Base.push!(model::M; channel::String = getchannel(model), + except::Union{Nothing,UInt,Vector{UInt}} = nothing, + restrict::Union{Nothing,UInt,Vector{UInt}} = nothing, skip::Vector{Symbol} = Symbol[])::Bool where {M<:ReactiveModel} result = true @@ -639,15 +643,18 @@ function Base.push!(model::M; for field in fieldnames(M) (isprivate(field, model) || field in skip) && continue - push!(model, field => getproperty(model, field); channel) === false && (result = false) + push!(model, field => getproperty(model, field); channel, except, restrict) === false && (result = false) end result end -function Base.push!(model::M, field::Symbol; channel::String = getchannel(model))::Bool where {M<:ReactiveModel} +function Base.push!(model::M, field::Symbol; + channel::String = getchannel(model), + except::Union{Nothing,UInt,Vector{UInt}} = nothing, + restrict::Union{Nothing,UInt,Vector{UInt}} = nothing)::Bool where {M<:ReactiveModel} isprivate(field, model) && return false - push!(model, field => getproperty(model, field); channel) + push!(model, field => getproperty(model, field); channel, except, restrict) end @specialize diff --git a/src/stipple/jsintegration.jl b/src/stipple/jsintegration.jl index c019f6c3..ae3559d8 100644 --- a/src/stipple/jsintegration.jl +++ b/src/stipple/jsintegration.jl @@ -87,9 +87,9 @@ end Execute js code in the frontend. `context` can be `:model`, `:app` or `:console` """ -function Base.run(model::ReactiveModel, jscode::String; context = :model) - context ∈ (:model, :app) && return push!(model, Symbol("js_", context) => jsfunction(jscode); channel = getchannel(model)) - context == :console && push!(model, :js_model => jsfunction("console.log('$jscode')"); channel = getchannel(model)) +function Base.run(model::ReactiveModel, jscode::String; context = :model, kwargs...) + context ∈ (:model, :app) && return push!(model, Symbol("js_", context) => jsfunction(jscode); channel = getchannel(model), kwargs...) + context == :console && push!(model, :js_model => jsfunction("console.log('$jscode')"); channel = getchannel(model), kwargs...) nothing end From ecafe013c9fb6de4c7d5d2a3c583bc967a1624f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Mon, 27 Nov 2023 09:19:31 +0100 Subject: [PATCH 09/12] add client info to events on request --- assets/js/watchers.js | 3 ++- src/Elements.jl | 3 ++- src/Stipple.jl | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/assets/js/watchers.js b/assets/js/watchers.js index 2a467f1b..4cd55efb 100644 --- a/assets/js/watchers.js +++ b/assets/js/watchers.js @@ -81,8 +81,9 @@ const reviveMixin = { const eventMixin = { methods: { - handle_event: function (event_data, event_handler) { + handle_event: function (event_data, event_handler, mode) { console.debug('event: ' + JSON.stringify(event_data) + ":" + event_handler) + if (mode=='addclient') { event_data._addclient = true} Genie.WebChannels.sendMessageTo(window.CHANNEL, 'events', { 'event': { 'name': event_handler, diff --git a/src/Elements.jl b/src/Elements.jl index e4c9a7c7..31440780 100644 --- a/src/Elements.jl +++ b/src/Elements.jl @@ -359,6 +359,7 @@ Sometimes preprocessing of the events is necessary, e.g. to add or skip informat ``` """ macro on(arg, expr, preprocess = nothing) + preprocess isa QuoteNode && preprocess == :(:addclient) && (preprocess = "event._addclient = true") kw = Symbol("v-on:", arg isa String ? arg : arg isa QuoteNode ? arg.value : arg.head == :vect ? join(lstrip.(string.(arg.args), ':'), '.') : throw("Value '$arg' for `arg` not supported. `arg` should be of type Symbol, String, or Vector{Union{String, Symbol}}")) @@ -370,7 +371,7 @@ macro on(arg, expr, preprocess = nothing) :(replace("""function(event) { const preprocess = (event) => { """ * replace($preprocess, '"' => "\\\"") * """; return event } handle_event(preprocess(event), '$($(esc(expr)))') - }'""", '\n' => ';')) + }""", '\n' => ';')) end else esc_expr(expr) diff --git a/src/Stipple.jl b/src/Stipple.jl index 7c8b5e2b..7057e0b9 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -525,6 +525,13 @@ function init(::Type{M}; # form handler parameter & call event notifier handler = Symbol(get(event, "name", nothing)) event_info = get(event, "event", nothing) + + # add client id if requested + if event_info isa Dict && get(event_info, "_addclient", false) + client = transport == Genie.WebChannels ? Genie.WebChannels.id(Genie.Requests.wsclient()) : Genie.Requests.wtclient() + push!(event_info, "_client" => client) + end + isempty(methods(notify, (M, Val{handler}))) || notify(model, Val(handler)) isempty(methods(notify, (M, Val{handler}, Any))) || notify(model, Val(handler), event_info) LAST_ACTIVITY[Symbol(channel)] = now() From 0a43ff981cc5a682d19260b9cd50486c57396191 Mon Sep 17 00:00:00 2001 From: hhaensel Date: Tue, 28 Nov 2023 15:20:24 +0100 Subject: [PATCH 10/12] consistent naming of push! methods --- src/Stipple.jl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Stipple.jl b/src/Stipple.jl index 7057e0b9..7e19645f 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -620,7 +620,7 @@ const max_retry_times = 10 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 = getchannel(model), + channel::String = getchannel(app), except::Union{Nothing,UInt,Vector{UInt}} = nothing, restrict::Union{Nothing,UInt,Vector{UInt}} = nothing)::Bool where {T,M<:ReactiveModel} try @@ -631,16 +631,16 @@ function Base.push!(app::M, vals::Pair{Symbol,T}; end end -function Base.push!(model::M, vals::Pair{Symbol,Reactive{T}}; - channel::String = getchannel(model), +function Base.push!(app::M, vals::Pair{Symbol,Reactive{T}}; + channel::String = getchannel(app), except::Union{Nothing,UInt,Vector{UInt}} = nothing, restrict::Union{Nothing,UInt,Vector{UInt}} = nothing)::Bool where {T,M<:ReactiveModel} v = vals[2].r_mode != JSFUNCTION ? vals[2][] : replace_jsfunction(vals[2][]) - push!(model, Symbol(julia_to_vue(vals[1])) => v; channel, except, restrict) + push!(app, Symbol(julia_to_vue(vals[1])) => v; channel, except, restrict) end -function Base.push!(model::M; - channel::String = getchannel(model), +function Base.push!(app::M; + channel::String = getchannel(app), except::Union{Nothing,UInt,Vector{UInt}} = nothing, restrict::Union{Nothing,UInt,Vector{UInt}} = nothing, skip::Vector{Symbol} = Symbol[])::Bool where {M<:ReactiveModel} @@ -648,20 +648,20 @@ function Base.push!(model::M; result = true for field in fieldnames(M) - (isprivate(field, model) || field in skip) && continue + (isprivate(field, app) || field in skip) && continue - push!(model, field => getproperty(model, field); channel, except, restrict) === false && (result = false) + push!(app, field => getproperty(app, field); channel, except, restrict) === false && (result = false) end result end -function Base.push!(model::M, field::Symbol; - channel::String = getchannel(model), +function Base.push!(app::M, field::Symbol; + channel::String = getchannel(app), except::Union{Nothing,UInt,Vector{UInt}} = nothing, restrict::Union{Nothing,UInt,Vector{UInt}} = nothing)::Bool where {M<:ReactiveModel} - isprivate(field, model) && return false - push!(model, field => getproperty(model, field); channel, except, restrict) + isprivate(field, app) && return false + push!(app, field => getproperty(app, field); channel, except, restrict) end @specialize From 8477d548d3bf75fb5f31c93c7ea6414fc6685f65 Mon Sep 17 00:00:00 2001 From: hhaensel Date: Thu, 30 Nov 2023 16:01:19 +0100 Subject: [PATCH 11/12] fix merging of classes in flexgrid_kwargs --- src/Layout.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Layout.jl b/src/Layout.jl index 86151c16..22d4bbf6 100644 --- a/src/Layout.jl +++ b/src/Layout.jl @@ -115,8 +115,8 @@ function flexgrid_kwargs(; class = "", class! = nothing, symbol_class::Bool = tr # if either class is a Symbol or class! is not nothing. # So an argument of the form `class! = "'my-class' + 'your-class'` is supported # Furthermore Vectors are now supported - class isa Vector && (class = Symbol(join(js_attr.(class), " + "))) - class! isa Vector && (class! = join(js_attr.(class!), " + ")) + class isa Vector && (class = Symbol(join(js_attr.(class), " + ' ' + "))) + class! isa Vector && (class! = join(js_attr.(class!), " + ' ' + ")) classes = String[] if class isa Symbol @@ -173,7 +173,7 @@ function row(args...; # for backward compatibility with `size` kwarg col == -1 && size != -1 && (col = size) - class = class isa Symbol ? Symbol("$class + ' row'") : join(push!(split(class), "row"), " ") + class = class isa Symbol ? Symbol("$class + ' row'") : class isa Vector ? push!(class, "row") : join(push!(split(class), "row"), " ") kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...)) Genie.Renderer.Html.div(args...; kwargs...) From f8c7a42caf8ee1da6c773ed602ebc5f0588f15cc Mon Sep 17 00:00:00 2001 From: hhaensel Date: Thu, 30 Nov 2023 19:30:07 +0100 Subject: [PATCH 12/12] set version v0.27.23 and adapt Genie compat --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index c8546740..f1ed2902 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.27.22" +version = "0.27.23" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" @@ -27,7 +27,7 @@ Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" DataFrames = "1" Dates = "1.6" FilePathsBase = "0.9" -Genie = "5.23.2" +Genie = "5.23.3" GenieSession = "1" GenieSessionFileSession = "1" JSON = "0.20, 0.21"