diff --git a/Project.toml b/Project.toml index ef112e7d..b737a72a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.27.26" +version = "0.27.27" [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.6" +Genie = "5.23.7" GenieSession = "1" GenieSessionFileSession = "1" JSON = "0.20, 0.21" diff --git a/assets/js/watchers.js b/assets/js/watchers.js index 4cd55efb..fc07662c 100644 --- a/assets/js/watchers.js +++ b/assets/js/watchers.js @@ -82,6 +82,7 @@ const reviveMixin = { const eventMixin = { methods: { handle_event: function (event_data, event_handler, mode) { + if (event_data === undefined) { event_data = {} } console.debug('event: ' + JSON.stringify(event_data) + ":" + event_handler) if (mode=='addclient') { event_data._addclient = true} Genie.WebChannels.sendMessageTo(window.CHANNEL, 'events', { diff --git a/src/Elements.jl b/src/Elements.jl index 31440780..b94a23b0 100644 --- a/src/Elements.jl +++ b/src/Elements.jl @@ -102,6 +102,7 @@ function vue_integration(::Type{M}; } function app_ready() { + $vue_app_name.channel_ = window.CHANNEL; $vue_app_name.isready = true; Genie.Revivers.addReviver(window.$(vue_app_name).revive_jsfunction); $(transport == Genie.WebChannels && diff --git a/src/ModelStorage.jl b/src/ModelStorage.jl index 2463e3d2..17022228 100644 --- a/src/ModelStorage.jl +++ b/src/ModelStorage.jl @@ -8,12 +8,20 @@ import GenieSessionFileSession export init_from_storage +function model_id(::Type{M}) where M + Symbol(Stipple.routename(M)) +end + +function store(model::M) where M + GenieSession.set!(model_id(M), model) + nothing +end + function init_from_storage( t::Type{M}; channel::Union{Any,Nothing} = Stipple.channeldefault(t), kwargs...) where M - model_id = Symbol(Stipple.routename(M)) model = Stipple.init(M; channel, kwargs...) - stored_model = GenieSession.get(model_id, nothing) + stored_model = GenieSession.get(model_id(M), nothing) CM = Stipple.get_concrete_type(M) for f in fieldnames(CM) @@ -28,7 +36,7 @@ function init_from_storage( t::Type{M}; # register reactive handlers to automatically save model on session when model changes if f ∉ [Stipple.AUTOFIELDS...] on(field) do _ - GenieSession.set!(model_id, model) + GenieSession.set!(model_id(M), model) end end else diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index 89a83ef9..af976e9e 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -186,7 +186,7 @@ function delete_events(::Type{M}) where M end end nothing - end +end function delete_handlers!(m::Module) delete!(HANDLERS, m) @@ -421,7 +421,7 @@ Declares a reactive variable that is public and can be written to from the UI. end ``` """ -macro var"in" end +macro in end """ ```julia diff --git a/src/Stipple.jl b/src/Stipple.jl index 9253a8cd..23e4828d 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -397,12 +397,16 @@ function deletemode!(modes, fieldnames::Symbol...) end function init_storage() + ch = channelfactory() + LittleDict{Symbol, Expr}( CHANNELFIELDNAME => - :($(Stipple.CHANNELFIELDNAME)::$(Stipple.ChannelName) = Stipple.channelfactory()), - :modes__ => :(modes__::Stipple.LittleDict{Symbol, Int} = Stipple.LittleDict{Symbol, Int}()), + :($(Stipple.CHANNELFIELDNAME)::$(Stipple.ChannelName) = $ch), + :modes__ => :(modes__::Stipple.LittleDict{Symbol,Int} = Stipple.LittleDict{Symbol,Int}()), :isready => :(isready::Stipple.R{Bool} = false), - :isprocessing => :(isprocessing::Stipple.R{Bool} = false) + :isprocessing => :(isprocessing::Stipple.R{Bool} = false), + :channel_ => :(channel_::String = $ch), + :fileuploads => :(fileuploads::Stipple.R{Dict{AbstractString,AbstractString}} = Dict{AbstractString,AbstractString}()) ) end @@ -457,6 +461,9 @@ function init(t::Type{M}; setchannel(model, channel) end + # make sure we store the channel name in the model + Stipple.ModelStorage.Sessions.store(model) + # add a timer that checks if the model is outdated and if so prepare the model to be garbage collected LAST_ACTIVITY[Symbol(getchannel(model))] = now() @@ -667,6 +674,17 @@ function Base.push!(app::M, vals::Pair{Symbol,T}; channel::String = getchannel(app), except::Union{Nothing,UInt,Vector{UInt}} = nothing, restrict::Union{Nothing,UInt,Vector{UInt}} = nothing)::Bool where {T,M<:ReactiveModel} + try + _push!(vals, channel; except, restrict) + catch ex + @debug ex + false + end +end + +function _push!(vals::Pair{Symbol,T}, channel::String; + except::Union{Nothing,UInt,Vector{UInt}} = nothing, + restrict::Union{Nothing,UInt,Vector{UInt}} = nothing)::Bool where {T} try webtransport().broadcast(channel, json(Dict("key" => julia_to_vue(vals[1]), "value" => Stipple.render(vals[2], vals[1]))); except, restrict) catch ex diff --git a/src/stipple/reactivity.jl b/src/stipple/reactivity.jl index b4ce33be..c2fe41e0 100644 --- a/src/stipple/reactivity.jl +++ b/src/stipple/reactivity.jl @@ -171,13 +171,15 @@ function setchannel(m::M, value) where {M<:ReactiveModel} setfield!(m, CHANNELFIELDNAME, ChannelName(value)) end -const AUTOFIELDS = [:isready, :isprocessing] # not DRY but we need a reference to the auto-set fields +const AUTOFIELDS = [:isready, :isprocessing, :fileuploads] # not DRY but we need a reference to the auto-set fields @pour reactors begin modes__::LittleDict{Symbol, Int} = LittleDict(:modes__ => PRIVATE, :channel__ => PRIVATE) channel__::Stipple.ChannelName = Stipple.channelfactory() isready::Stipple.R{Bool} = false isprocessing::Stipple.R{Bool} = false + channel_::String = "" # not sure what this does if it's empty + fileuploads::Stipple.R{Dict{AbstractString,AbstractString}} = Dict{AbstractString,AbstractString}() end @mix Stipple.@with_kw mutable struct old_reactive