diff --git a/docs/web/app.wasm b/docs/web/app.wasm index 2c96142c1..f2f34c29e 100755 Binary files a/docs/web/app.wasm and b/docs/web/app.wasm differ diff --git a/docs/web/documents/reference.html b/docs/web/documents/reference.html index 5db26ab60..429d7e6b9 100644 --- a/docs/web/documents/reference.html +++ b/docs/web/documents/reference.html @@ -296,6 +296,11 @@

Index ▾

+
type Engine
+ + + +
type Environment
@@ -1413,7 +1418,7 @@

Index ▾

type UI
-
    func FilterUIElems(uis ...UI) []UI
+
    func FilterUIElems(v ...UI) []UI
    func Raw(v string) UI
@@ -1480,6 +1485,8 @@

Package files

engine.go + enginex.go + event.go html.go @@ -1693,7 +1700,7 @@

func func HTMLString +

func HTMLString @@ -1709,7 +1716,7 @@

func func HTMLStringWithIndent +

func HTMLStringWithIndent @@ -1813,7 +1820,7 @@

func func PrintHTML +

func PrintHTML @@ -1830,7 +1837,7 @@

func func PrintHTMLWithIndent +

func PrintHTMLWithIndent @@ -2059,7 +2066,7 @@

type type AppInstaller +

type AppInstaller @@ -2091,7 +2098,7 @@

type type AppUpdater +

type AppUpdater @@ -2228,7 +2235,7 @@

func type ClientDispatcher +

type ClientDispatcher @@ -2277,7 +2284,7 @@

type func NewClientTester +

func NewClientTester @@ -2296,7 +2303,7 @@

func type Compo +

type Compo @@ -2322,7 +2329,7 @@

type func (*Compo) JSValue +

func (*Compo) JSValue @@ -2337,7 +2344,7 @@

func (*Compo) func (*Compo) Kind +

func (*Compo) Kind @@ -2352,7 +2359,7 @@

func (*Compo) func (*Compo) Mounted +

func (*Compo) Mounted @@ -2367,7 +2374,7 @@

func (*Compo) func (*Compo) Render +

func (*Compo) Render @@ -2384,7 +2391,7 @@

func (*Compo) func (*Compo) ResizeContent +

func (*Compo) ResizeContent @@ -2400,7 +2407,7 @@

func (*Compo) func (*Compo) Update +

func (*Compo) Update @@ -2417,7 +2424,7 @@

func (*Compo) func (*Compo) ValueTo +

func (*Compo) ValueTo @@ -2442,7 +2449,7 @@

func (*Compo) type Composer +

type Composer @@ -2506,7 +2513,7 @@

type type Condition +

type Condition @@ -2538,7 +2545,7 @@

type func If +

func If @@ -2729,7 +2736,7 @@

type type Dismounter +

type Dismounter @@ -2761,7 +2768,7 @@

type type Dispatch +

type Dispatch @@ -2791,7 +2798,7 @@

type type DispatchMode +

type DispatchMode @@ -2832,7 +2839,7 @@

type type Dispatcher +

type Dispatcher @@ -2904,7 +2911,30 @@

type type Environment +

type Engine + + + +

+ +
type Engine interface {
+}
+ + + + + + + + + + + + + + + +

type Environment @@ -3285,7 +3315,7 @@

func type HTMLAbbr +

type HTMLAbbr @@ -3464,7 +3494,7 @@

type func Abbr +

func Abbr @@ -3482,7 +3512,7 @@

func type HTMLAddress +

type HTMLAddress @@ -3661,7 +3691,7 @@

type func Address +

func Address @@ -3679,7 +3709,7 @@

func type HTMLArea +

type HTMLArea @@ -3882,7 +3912,7 @@

type func Area +

func Area @@ -3900,7 +3930,7 @@

func type HTMLArticle +

type HTMLArticle @@ -4079,7 +4109,7 @@

type func Article +

func Article @@ -4097,7 +4127,7 @@

func type HTMLAside +

type HTMLAside @@ -4276,7 +4306,7 @@

type func Aside +

func Aside @@ -4294,7 +4324,7 @@

func type HTMLAudio +

type HTMLAudio @@ -4563,7 +4593,7 @@

type func Audio +

func Audio @@ -4581,7 +4611,7 @@

func type HTMLB +

type HTMLB @@ -4760,7 +4790,7 @@

type func B +

func B @@ -4778,7 +4808,7 @@

func type HTMLBase +

type HTMLBase @@ -4957,7 +4987,7 @@

type func Base +

func Base @@ -4975,7 +5005,7 @@

func type HTMLBdi +

type HTMLBdi @@ -5154,7 +5184,7 @@

type func Bdi +

func Bdi @@ -5172,7 +5202,7 @@

func type HTMLBdo +

type HTMLBdo @@ -5351,7 +5381,7 @@

type func Bdo +

func Bdo @@ -5369,7 +5399,7 @@

func type HTMLBlockquote +

type HTMLBlockquote @@ -5551,7 +5581,7 @@

type func Blockquote +

func Blockquote @@ -5569,7 +5599,7 @@

func type HTMLBody +

type HTMLBody @@ -5788,7 +5818,7 @@

type func Body +

func Body @@ -5806,7 +5836,7 @@

func type HTMLBr +

type HTMLBr @@ -5979,7 +6009,7 @@

type func Br +

func Br @@ -5997,7 +6027,7 @@

func type HTMLButton +

type HTMLButton @@ -6209,7 +6239,7 @@

type func Button +

func Button @@ -6227,7 +6257,7 @@

func type HTMLCanvas +

type HTMLCanvas @@ -6412,7 +6442,7 @@

type func Canvas +

func Canvas @@ -6430,7 +6460,7 @@

func type HTMLCaption +

type HTMLCaption @@ -6609,7 +6639,7 @@

type func Caption +

func Caption @@ -6627,7 +6657,7 @@

func type HTMLCite +

type HTMLCite @@ -6806,7 +6836,7 @@

type func Cite +

func Cite @@ -6824,7 +6854,7 @@

func type HTMLCode +

type HTMLCode @@ -7003,7 +7033,7 @@

type func Code +

func Code @@ -7021,7 +7051,7 @@

func type HTMLCol +

type HTMLCol @@ -7197,7 +7227,7 @@

type func Col +

func Col @@ -7215,7 +7245,7 @@

func type HTMLColGroup +

type HTMLColGroup @@ -7397,7 +7427,7 @@

type func ColGroup +

func ColGroup @@ -7415,7 +7445,7 @@

func type HTMLData +

type HTMLData @@ -7501,7 +7531,7 @@

type func Data +

func Data @@ -7519,7 +7549,7 @@

func type HTMLDataList +

type HTMLDataList @@ -7698,7 +7728,7 @@

type func DataList +

func DataList @@ -7716,7 +7746,7 @@

func type HTMLDd +

type HTMLDd @@ -7895,7 +7925,7 @@

type func Dd +

func Dd @@ -7913,7 +7943,7 @@

func type HTMLDel +

type HTMLDel @@ -8098,7 +8128,7 @@

type func Del +

func Del @@ -8116,7 +8146,7 @@

func type HTMLDetails +

type HTMLDetails @@ -8301,7 +8331,7 @@

type func Details +

func Details @@ -8319,7 +8349,7 @@

func type HTMLDfn +

type HTMLDfn @@ -8498,7 +8528,7 @@

type func Dfn +

func Dfn @@ -8516,7 +8546,7 @@

func type HTMLDialog +

type HTMLDialog @@ -8698,7 +8728,7 @@

type func Dialog +

func Dialog @@ -8716,7 +8746,7 @@

func type HTMLDiv +

type HTMLDiv @@ -8895,7 +8925,7 @@

type func Div +

func Div @@ -8913,7 +8943,7 @@

func type HTMLDl +

type HTMLDl @@ -9092,7 +9122,7 @@

type func Dl +

func Dl @@ -9110,7 +9140,7 @@

func type HTMLDt +

type HTMLDt @@ -9289,7 +9319,7 @@

type func Dt +

func Dt @@ -9307,7 +9337,7 @@

func type HTMLElem +

type HTMLElem @@ -9489,7 +9519,7 @@

type func Elem +

func Elem @@ -9507,7 +9537,7 @@

func type HTMLElemSelfClosing +

type HTMLElemSelfClosing @@ -9683,7 +9713,7 @@

type func ElemSelfClosing +

func ElemSelfClosing @@ -9701,7 +9731,7 @@

func type HTMLEm +

type HTMLEm @@ -9880,7 +9910,7 @@

type func Em +

func Em @@ -9898,7 +9928,7 @@

func type HTMLEmbed +

type HTMLEmbed @@ -10152,7 +10182,7 @@

type func Embed +

func Embed @@ -10170,7 +10200,7 @@

func type HTMLFieldSet +

type HTMLFieldSet @@ -10358,7 +10388,7 @@

type func FieldSet +

func FieldSet @@ -10376,7 +10406,7 @@

func type HTMLFigCaption +

type HTMLFigCaption @@ -10555,7 +10585,7 @@

type func FigCaption +

func FigCaption @@ -10573,7 +10603,7 @@

func type HTMLFigure +

type HTMLFigure @@ -10752,7 +10782,7 @@

type func Figure +

func Figure @@ -10770,7 +10800,7 @@

func type HTMLFooter +

type HTMLFooter @@ -10949,7 +10979,7 @@

type func Footer +

type HTMLForm @@ -11170,7 +11200,7 @@

type func Form +

func Form @@ -11188,7 +11218,7 @@

func type HTMLH1 +

type HTMLH1 @@ -11367,7 +11397,7 @@

type func H1 +

func H1 @@ -11385,7 +11415,7 @@

func type HTMLH2 +

type HTMLH2 @@ -11564,7 +11594,7 @@

type func H2 +

func H2 @@ -11582,7 +11612,7 @@

func type HTMLH3 +

type HTMLH3 @@ -11761,7 +11791,7 @@

type func H3 +

func H3 @@ -11779,7 +11809,7 @@

func type HTMLH4 +

type HTMLH4 @@ -11958,7 +11988,7 @@

type func H4 +

func H4 @@ -11976,7 +12006,7 @@

func type HTMLH5 +

type HTMLH5 @@ -12155,7 +12185,7 @@

type func H5 +

func H5 @@ -12173,7 +12203,7 @@

func type HTMLH6 +

type HTMLH6 @@ -12352,7 +12382,7 @@

type func H6 +

func H6 @@ -12370,7 +12400,7 @@

func type HTMLHead +

type HTMLHead @@ -12453,7 +12483,7 @@

type func Head +

type HTMLHeader @@ -12650,7 +12680,7 @@

type func Header +

type HTMLHr @@ -12841,7 +12871,7 @@

type func Hr +

func Hr @@ -12859,7 +12889,7 @@

func type HTMLHtml +

type HTMLHtml @@ -12937,7 +12967,7 @@

type func Html +

func Html @@ -12955,7 +12985,7 @@

func type HTMLI +

type HTMLI @@ -13134,7 +13164,7 @@

type func I +

func I @@ -13152,7 +13182,7 @@

func type HTMLIFrame +

type HTMLIFrame @@ -13367,7 +13397,7 @@

type func IFrame +

func IFrame @@ -13385,7 +13415,7 @@

func type HTMLImg +

type HTMLImg @@ -13657,7 +13687,7 @@

type func Img +

func Img @@ -13675,7 +13705,7 @@

func type HTMLInput +

type HTMLInput @@ -13944,7 +13974,7 @@

type func Input +

func Input @@ -13962,7 +13992,7 @@

func type HTMLIns +

type HTMLIns @@ -14141,7 +14171,7 @@

type func Ins +

func Ins @@ -14159,7 +14189,7 @@

func type HTMLKbd +

type HTMLKbd @@ -14338,7 +14368,7 @@

type func Kbd +

func Kbd @@ -14356,7 +14386,7 @@

func type HTMLLabel +

type HTMLLabel @@ -14541,7 +14571,7 @@

type func Label +

func Label @@ -14559,7 +14589,7 @@

func type HTMLLegend +

type HTMLLegend @@ -14738,7 +14768,7 @@

type func Legend +

func Legend @@ -14756,7 +14786,7 @@

func type HTMLLi +

type HTMLLi @@ -14938,7 +14968,7 @@

type func Li +

func Li @@ -14956,7 +14986,7 @@

func type HTMLLink +

type HTMLMain @@ -15350,7 +15380,7 @@

type func Main +

func Main @@ -15368,7 +15398,7 @@

func type HTMLMap +

type HTMLMap @@ -15550,7 +15580,7 @@

type func Map +

func Map @@ -15568,7 +15598,7 @@

func type HTMLMark +

type HTMLMark @@ -15747,7 +15777,7 @@

type func Mark +

func Mark @@ -15765,7 +15795,7 @@

func type HTMLMeta +

type HTMLMeta @@ -15857,7 +15887,7 @@

type func Meta +

func Meta @@ -15875,7 +15905,7 @@

func type HTMLMeter +

type HTMLMeter @@ -16075,7 +16105,7 @@

type func Meter +

func Meter @@ -16093,7 +16123,7 @@

func type HTMLNav +

type HTMLNav @@ -16272,7 +16302,7 @@

type func Nav +

type HTMLNoScript @@ -16373,7 +16403,7 @@

type func NoScript +

func NoScript @@ -16391,7 +16421,7 @@

func type HTMLObject +

type HTMLObject @@ -16660,7 +16690,7 @@

type func Object +

func Object @@ -16678,7 +16708,7 @@

func type HTMLOl +

type HTMLOl @@ -16866,7 +16896,7 @@

type func Ol +

func Ol @@ -16884,7 +16914,7 @@

func type HTMLOptGroup +

type HTMLOptGroup @@ -17069,7 +17099,7 @@

type func OptGroup +

func OptGroup @@ -17087,7 +17117,7 @@

func type HTMLOption +

type HTMLOption @@ -17278,7 +17308,7 @@

type func Option +

func Option @@ -17296,7 +17326,7 @@

func type HTMLOutput +

type HTMLOutput @@ -17484,7 +17514,7 @@

type func Output +

func Output @@ -17502,7 +17532,7 @@

func type HTMLP +

type HTMLP @@ -17681,7 +17711,7 @@

type func P +

func P @@ -17699,7 +17729,7 @@

func type HTMLParam +

type HTMLParam @@ -17878,7 +17908,7 @@

type func Param +

func Param @@ -17896,7 +17926,7 @@

func type HTMLPicture +

type HTMLPicture @@ -18075,7 +18105,7 @@

type func Picture +

func Picture @@ -18093,7 +18123,7 @@

func type HTMLPre +

type HTMLPre @@ -18272,7 +18302,7 @@

type func Pre +

func Pre @@ -18290,7 +18320,7 @@

func type HTMLProgress +

type HTMLProgress @@ -18475,7 +18505,7 @@

type func Progress +

func Progress @@ -18493,7 +18523,7 @@

func type HTMLQ +

type HTMLQ @@ -18675,7 +18705,7 @@

type func Q +

func Q @@ -18693,7 +18723,7 @@

func type HTMLRp +

type HTMLRp @@ -18872,7 +18902,7 @@

type func Rp +

func Rp @@ -18890,7 +18920,7 @@

func type HTMLRt +

type HTMLRt @@ -19069,7 +19099,7 @@

type func Rt +

func Rt @@ -19087,7 +19117,7 @@

func type HTMLRuby +

type HTMLRuby @@ -19266,7 +19296,7 @@

type func Ruby +

func Ruby @@ -19284,7 +19314,7 @@

func type HTMLS +

type HTMLS @@ -19463,7 +19493,7 @@

type func S +

func S @@ -19481,7 +19511,7 @@

func type HTMLSamp +

type HTMLSamp @@ -19660,7 +19690,7 @@

type func Samp +

func Samp @@ -19678,7 +19708,7 @@

func type HTMLScript +

type HTMLScript @@ -19782,7 +19812,7 @@

type func Script +

func Script @@ -19800,7 +19830,7 @@

func type HTMLSection +

type HTMLSection @@ -19979,7 +20009,7 @@

type func Section +

func Section @@ -19997,7 +20027,7 @@

func type HTMLSelect +

type HTMLSelect @@ -20197,7 +20227,7 @@

type func Select +

func Select @@ -20215,7 +20245,7 @@

func type HTMLSmall +

type HTMLSmall @@ -20394,7 +20424,7 @@

type func Small +

func Small @@ -20412,7 +20442,7 @@

func type HTMLSource +

type HTMLSource @@ -20600,7 +20630,7 @@

type func Source +

func Source @@ -20618,7 +20648,7 @@

func type HTMLSpan +

type HTMLSpan @@ -20797,7 +20827,7 @@

type func Span +

func Span @@ -20815,7 +20845,7 @@

func type HTMLStrong +

type HTMLStrong @@ -20994,7 +21024,7 @@

type func Strong +

func Strong @@ -21012,7 +21042,7 @@

func type HTMLStyle +

type HTMLStyle @@ -21200,7 +21230,7 @@

type func Style +

func Style @@ -21218,7 +21248,7 @@

func type HTMLSub +

type HTMLSub @@ -21397,7 +21427,7 @@

type func Sub +

func Sub @@ -21415,7 +21445,7 @@

func type HTMLSummary +

type HTMLSummary @@ -21594,7 +21624,7 @@

type func Summary +

func Summary @@ -21612,7 +21642,7 @@

func type HTMLSup +

type HTMLSup @@ -21791,7 +21821,7 @@

type func Sup +

func Sup @@ -21809,7 +21839,7 @@

func type HTMLTBody +

type HTMLTBody @@ -21988,7 +22018,7 @@

type func TBody +

func TBody @@ -22006,7 +22036,7 @@

func type HTMLTFoot +

type HTMLTFoot @@ -22185,7 +22215,7 @@

type func TFoot +

func TFoot @@ -22203,7 +22233,7 @@

func type HTMLTHead +

type HTMLTHead @@ -22382,7 +22412,7 @@

type func THead +

func THead @@ -22400,7 +22430,7 @@

func type HTMLTable +

type HTMLTable @@ -22579,7 +22609,7 @@

type func Table +

func Table @@ -22597,7 +22627,7 @@

func type HTMLTd +

type HTMLTd @@ -22785,7 +22815,7 @@

type func Td +

func Td @@ -22803,7 +22833,7 @@

func type HTMLTemplate +

type HTMLTemplate @@ -22886,7 +22916,7 @@

type func Template +

func Template @@ -22904,7 +22934,7 @@

func type HTMLTextarea +

type HTMLTextarea @@ -23119,7 +23149,7 @@

type func Textarea +

func Textarea @@ -23137,7 +23167,7 @@

func type HTMLTh +

type HTMLTh @@ -23331,7 +23361,7 @@

type func Th +

func Th @@ -23349,7 +23379,7 @@

func type HTMLTime +

type HTMLTime @@ -23531,7 +23561,7 @@

type func Time +

func Time @@ -23549,7 +23579,7 @@

func type HTMLTitle +

type HTMLTitle @@ -23632,7 +23662,7 @@

type func Title +

func Title @@ -23650,7 +23680,7 @@

func type HTMLTr +

type HTMLTr @@ -23829,7 +23859,7 @@

type func Tr +

func Tr @@ -23847,7 +23877,7 @@

func type HTMLU +

type HTMLU @@ -24026,7 +24056,7 @@

type func U +

func U @@ -24044,7 +24074,7 @@

func type HTMLUl +

type HTMLUl @@ -24223,7 +24253,7 @@

type func Ul +

func Ul @@ -24241,7 +24271,7 @@

func type HTMLVar +

type HTMLVar @@ -24420,7 +24450,7 @@

type func Var +

func Var @@ -24438,7 +24468,7 @@

func type HTMLVideo +

type HTMLVideo @@ -24716,7 +24746,7 @@

type func Video +

func Video @@ -24734,7 +24764,7 @@

func type HTMLWbr +

type HTMLWbr @@ -24913,7 +24943,7 @@

type func Wbr +

func Wbr @@ -25111,7 +25141,7 @@

func (*Handler) type Icon +

type Icon @@ -25157,7 +25187,7 @@

type type Initializer +

type Initializer @@ -25188,7 +25218,7 @@

type type Kind +

type Kind @@ -25233,7 +25263,7 @@

type func (Kind) String +

func (Kind) String @@ -25247,7 +25277,7 @@

func (Kind) type Mounter +

type Mounter @@ -25279,7 +25309,7 @@

type type MsgHandler +

type MsgHandler @@ -25304,7 +25334,7 @@

type type Navigator +

func (PreRenderedItem) type PreRenderer +

type PreRenderer @@ -25900,7 +25930,7 @@

type type RangeLoop +

type RangeLoop @@ -25936,7 +25966,7 @@

type func Range +

func Range @@ -25955,7 +25985,7 @@

func type Resizer +

type Resizer @@ -26094,7 +26124,7 @@

func type ServerDispatcher +

type ServerDispatcher @@ -26130,7 +26160,7 @@

type func NewServerTester +

func NewServerTester @@ -26492,7 +26522,7 @@

type type UI +

type UI @@ -26531,12 +26561,12 @@

type func FilterUIElems +

func FilterUIElems

-
func FilterUIElems(uis ...UI) []UI
+
func FilterUIElems(v ...UI) []UI

FilterUIElems returns a filtered version of the given UI elements where selector elements such as If and Range are interpreted and removed. It also @@ -26551,7 +26581,7 @@

func func Raw +

func Raw @@ -26570,7 +26600,7 @@

func func Text +

func Text @@ -26588,7 +26618,7 @@

func type Updater +

type Updater diff --git a/pkg/app/app.go b/pkg/app/app.go index a7509a87d..2397d696b 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -113,10 +113,10 @@ func RunWhenOnBrowser() { staticResourcesResolver := newClientStaticResourceResolver(Getenv("GOAPP_STATIC_RESOURCES_URL")) disp := engine{ - UpdateRate: engineUpdateRate, + FrameRate: engineUpdateRate, LocalStorage: newJSStorage("localStorage"), SessionStorage: newJSStorage("sessionStorage"), - ResolveStaticResources: staticResourcesResolver, + StaticResourceResolver: staticResourcesResolver, ActionHandlers: actionHandlers, } disp.Page = browserPage{dispatcher: &disp} diff --git a/pkg/app/component.go b/pkg/app/component.go index a6413c0ed..6ed0251bd 100644 --- a/pkg/app/component.go +++ b/pkg/app/component.go @@ -280,7 +280,7 @@ func (c *Compo) mount(d Dispatcher) error { Tag("kind", c.Kind()) } - if initializer, ok := c.self().(Initializer); ok && !d.runsInServer() { + if initializer, ok := c.self().(Initializer); ok && !d.isServerSide() { initializer.OnInit() } @@ -297,7 +297,7 @@ func (c *Compo) mount(d Dispatcher) error { root.setParent(c.this) c.root = root - if c.getDispatcher().runsInServer() { + if c.getDispatcher().isServerSide() { return nil } diff --git a/pkg/app/component_test.go b/pkg/app/component_test.go index 9c46560de..25c9f3a5f 100644 --- a/pkg/app/component_test.go +++ b/pkg/app/component_test.go @@ -381,7 +381,7 @@ func TestPreRenderer(t *testing.T) { d.PreRender() d.Consume() require.True(t, h.preRenderer) - require.Equal(t, "world", d.currentPage().Title()) + require.Equal(t, "world", d.getCurrentPage().Title()) } func TestNestedPreRenderer(t *testing.T) { @@ -393,7 +393,7 @@ func TestNestedPreRenderer(t *testing.T) { d.PreRender() d.Consume() require.True(t, h.preRenderer) - require.Equal(t, "world", d.currentPage().Title()) + require.Equal(t, "world", d.getCurrentPage().Title()) } func TestNestedInComponentPreRenderer(t *testing.T) { @@ -403,7 +403,7 @@ func TestNestedInComponentPreRenderer(t *testing.T) { d.PreRender() d.Consume() - require.Equal(t, "bar", d.currentPage().Title()) + require.Equal(t, "bar", d.getCurrentPage().Title()) } func TestUpdater(t *testing.T) { diff --git a/pkg/app/context.go b/pkg/app/context.go index 3c5444b0a..be4943822 100644 --- a/pkg/app/context.go +++ b/pkg/app/context.go @@ -157,6 +157,9 @@ type Context interface { // Returns the service to setup and display notifications. Notifications() NotificationService + + // Prevents the component that contains the context source to be updated. + PreventUpdate() } type uiContext struct { @@ -289,11 +292,11 @@ func (ctx uiContext) ResolveStaticResource(path string) string { } func (ctx uiContext) LocalStorage() BrowserStorage { - return ctx.Dispatcher().localStorage() + return ctx.Dispatcher().getLocalStorage() } func (ctx uiContext) SessionStorage() BrowserStorage { - return ctx.Dispatcher().sessionStorage() + return ctx.Dispatcher().getSessionStorage() } func (ctx uiContext) ScrollTo(id string) { @@ -367,6 +370,10 @@ func (ctx uiContext) Notifications() NotificationService { return NotificationService{dispatcher: ctx.Dispatcher()} } +func (ctx uiContext) PreventUpdate() { + ctx.Dispatcher().preventComponentUpdate(getComponent(ctx.src)) +} + func (ctx uiContext) cryptoKey() string { return strings.ReplaceAll(ctx.DeviceID(), "-", "") } @@ -377,7 +384,7 @@ func makeContext(src UI) Context { src: src, jsSrc: src.JSValue(), appUpdateAvailable: appUpdateAvailable, - page: src.getDispatcher().currentPage(), + page: src.getDispatcher().getCurrentPage(), disp: src.getDispatcher(), } } diff --git a/pkg/app/dispatcher.go b/pkg/app/dispatcher.go index 86ac7d247..6ee00fe6f 100644 --- a/pkg/app/dispatcher.go +++ b/pkg/app/dispatcher.go @@ -58,12 +58,12 @@ type Dispatcher interface { Wait() start(context.Context) - currentPage() Page - localStorage() BrowserStorage - sessionStorage() BrowserStorage - runsInServer() bool + getCurrentPage() Page + getLocalStorage() BrowserStorage + getSessionStorage() BrowserStorage + isServerSide() bool resolveStaticResource(string) string - removeFromUpdates(Composer) + preventComponentUpdate(Composer) } // ClientDispatcher is the interface that describes a dispatcher that emulates a @@ -131,7 +131,7 @@ type ServerDispatcher interface { // client environment. func NewServerTester(n UI) ServerDispatcher { e := &engine{ - RunsInServer: true, + IsServerSide: true, ActionHandlers: actionHandlers, } e.init() @@ -147,6 +147,13 @@ type Dispatch struct { Function func(Context) } +func (d Dispatch) do() { + if d.Source == nil || !d.Source.Mounted() || d.Function == nil { + return + } + d.Function(makeContext(d.Source)) +} + // DispatchMode represents how a dispatch is processed. type DispatchMode int diff --git a/pkg/app/dispatcher_test.go b/pkg/app/dispatcher_test.go index f771a7db7..ac6207e73 100644 --- a/pkg/app/dispatcher_test.go +++ b/pkg/app/dispatcher_test.go @@ -52,11 +52,11 @@ func testDispatcherAsyncWait(t *testing.T, d Dispatcher) { func TestDispatcherLocalStorage(t *testing.T) { d := NewClientTester(&hello{}) defer d.Close() - testBrowserStorage(t, d.localStorage()) + testBrowserStorage(t, d.getLocalStorage()) } func TestDispatcherSessionStorage(t *testing.T) { d := NewClientTester(&hello{}) defer d.Close() - testBrowserStorage(t, d.sessionStorage()) + testBrowserStorage(t, d.getSessionStorage()) } diff --git a/pkg/app/engine.go b/pkg/app/engine.go index 519ae23c7..a60ad57b3 100644 --- a/pkg/app/engine.go +++ b/pkg/app/engine.go @@ -3,28 +3,21 @@ package app import ( "context" "net/url" - "sort" "sync" "time" "github.com/maxence-charriere/go-app/v9/pkg/errors" ) -const ( - eventBufferSize = 4096 - updateBufferSize = 64 - deferBufferSize = 64 -) - type engine struct { - // The rate where component updates are performed (per seconds). - UpdateRate int + // The number of frame per seconds. + FrameRate int // The page. Page Page - // Reports whether the engine runs in a server. - RunsInServer bool + // Reports whether the engine runs on server-side. + IsServerSide bool // The storage use as local storage. LocalStorage BrowserStorage @@ -33,7 +26,7 @@ type engine struct { SessionStorage BrowserStorage // The function used to resolve static resource paths. - ResolveStaticResources func(string) string + StaticResourceResolver func(string) string // The body of the page. Body HTMLBody @@ -42,60 +35,65 @@ type engine struct { // executed asynchronously. ActionHandlers map[string]ActionHandler - initOnce sync.Once - startOnce sync.Once - closeOnce sync.Once - wait sync.WaitGroup + initOnce sync.Once + startOnce sync.Once + closeOnce sync.Once + wait sync.WaitGroup + componentUpdateMutex sync.RWMutex + + dispatches chan Dispatch + componentUpdates map[Composer]bool + deferables []Dispatch + actions actionManager + states *store + isFirstMount bool +} - isMountedOnce bool - dispatches chan Dispatch - updates map[Composer]struct{} - updateQueue []updateDescriptor - defers []Dispatch - actions actionManager - states *store +func (e *engine) Context() Context { + return makeContext(e.Body) } func (e *engine) Dispatch(d Dispatch) { if d.Source == nil { d.Source = e.Body } - if d.Function == nil { - d.Function = func(Context) {} - } e.dispatches <- d } func (e *engine) Emit(src UI, fn func()) { - if !src.Mounted() { - return - } - - if fn != nil { - fn() - } + e.Dispatch(Dispatch{ + Mode: Next, + Source: src, + Function: func(ctx Context) { + if fn != nil { + fn() + } - compoCount := 0 - for n := src; n != nil; n = n.getParent() { - compo, ok := n.(Composer) - if !ok { - continue - } + e.componentUpdateMutex.RLock() + compo := getComponent(src) + if canUpdate, ok := e.componentUpdates[compo]; ok && !canUpdate { + e.componentUpdateMutex.RUnlock() + return + } + e.componentUpdateMutex.RUnlock() - compoCount++ - if compoCount > 1 { - e.Dispatch(Dispatch{ - Source: compo, - Mode: Update, - }) - } - } + for c := compo; c != nil; c = getComponent(c.getParent()) { + e.addComponentUpdate(c) + } + }, + }) } func (e *engine) Handle(actionName string, src UI, h ActionHandler) { e.actions.handle(actionName, false, src, h) } +func (e *engine) Post(a Action) { + e.Async(func() { + e.actions.post(a) + }) +} + func (e *engine) SetState(state string, v any, opts ...StateOption) { e.states.Set(state, v, opts...) } @@ -112,12 +110,6 @@ func (e *engine) ObserveState(state string, elem UI) Observer { return e.states.Observe(state, elem) } -func (e *engine) Post(a Action) { - e.Async(func() { - e.actions.post(a) - }) -} - func (e *engine) Async(fn func()) { e.wait.Add(1) go func() { @@ -130,10 +122,6 @@ func (e *engine) Wait() { e.wait.Wait() } -func (e *engine) Context() Context { - return makeContext(e.Body) -} - func (e *engine) Consume() { for { e.Wait() @@ -143,8 +131,7 @@ func (e *engine) Consume() { e.handleDispatch(d) default: - e.updateComponents() - e.execDeferableEvents() + e.handleFrame() return } } @@ -152,15 +139,8 @@ func (e *engine) Consume() { func (e *engine) ConsumeNext() { e.Wait() - - select { - case d := <-e.dispatches: - e.handleDispatch(d) - e.updateComponents() - e.execDeferableEvents() - - default: - } + e.handleDispatch(<-e.dispatches) + e.handleFrame() } func (e *engine) Close() { @@ -170,8 +150,6 @@ func (e *engine) Close() { dismount(e.Body) e.Body = nil - close(e.dispatches) - e.states.Close() }) } @@ -186,45 +164,29 @@ func (e *engine) PreRender() { }) } -func (e *engine) Mount(n UI) { +func (e *engine) Mount(v UI) { e.Dispatch(Dispatch{ Mode: Update, Source: e.Body, Function: func(ctx Context) { - if !e.isMountedOnce { - if err := e.Body.(*htmlBody).replaceChildAt(0, n); err != nil { - panic(errors.New("mounting ui element failed"). - Tag("dispatches-count", len(e.dispatches)). - Tag("dispatches-capacity", cap(e.dispatches)). - Tag("updates-count", len(e.updates)). - Tag("updates-queue-len", len(e.updateQueue)). - Wrap(err)) + if e.isFirstMount { + if err := e.Body.(*htmlBody).replaceChildAt(0, v); err != nil { + panic(errors.New("mounting first ui element failed").Wrap(err)) } - e.isMountedOnce = true + e.isFirstMount = false return } - firstChild := e.Body.getChildren()[0] - if canUpdate(firstChild, n) { - if err := update(firstChild, n); err != nil { - panic(errors.New("mounting ui element failed"). - Tag("dispatches-count", len(e.dispatches)). - Tag("dispatches-capacity", cap(e.dispatches)). - Tag("updates-count", len(e.updates)). - Tag("updates-queue-len", len(e.updateQueue)). - Wrap(err)) + if firstChild := e.Body.getChildren()[0]; canUpdate(firstChild, v) { + if err := update(firstChild, v); err != nil { + panic(errors.New("mounting ui element failed").Wrap(err)) } return } - if err := e.Body.(*htmlBody).replaceChildAt(0, n); err != nil { - panic(errors.New("mounting ui element failed"). - Tag("dispatches-count", len(e.dispatches)). - Tag("dispatches-capacity", cap(e.dispatches)). - Tag("updates-count", len(e.updates)). - Tag("updates-queue-len", len(e.updateQueue)). - Wrap(err)) + if err := e.Body.(*htmlBody).replaceChildAt(0, v); err != nil { + panic(errors.New("mounting ui element failed").Wrap(err)) } }, }) @@ -276,14 +238,8 @@ func (e *engine) AppResize() { func (e *engine) init() { e.initOnce.Do(func() { - e.dispatches = make(chan Dispatch, eventBufferSize) - e.updates = make(map[Composer]struct{}) - e.updateQueue = make([]updateDescriptor, 0, updateBufferSize) - e.defers = make([]Dispatch, 0, deferBufferSize) - e.states = newStore(e) - - if e.UpdateRate <= 0 { - e.UpdateRate = 60 + if e.FrameRate <= 0 { + e.FrameRate = 60 } if e.Page == nil { @@ -299,8 +255,8 @@ func (e *engine) init() { e.SessionStorage = newMemoryStorage() } - if e.ResolveStaticResources == nil { - e.ResolveStaticResources = func(path string) string { + if e.StaticResourceResolver == nil { + e.StaticResourceResolver = func(path string) string { return path } } @@ -313,22 +269,70 @@ func (e *engine) init() { e.Body = body } + e.dispatches = make(chan Dispatch, 4096) + e.componentUpdates = make(map[Composer]bool) + e.deferables = make([]Dispatch, 32) + e.states = newStore(e) + e.isFirstMount = true + for actionName, handler := range e.ActionHandlers { e.actions.handle(actionName, true, e.Body, handler) } }) } +func (e *engine) getCurrentPage() Page { + return e.Page +} + +func (e *engine) getLocalStorage() BrowserStorage { + return e.LocalStorage +} + +func (e *engine) getSessionStorage() BrowserStorage { + return e.SessionStorage +} + +func (e *engine) isServerSide() bool { + return e.IsServerSide +} + +func (e *engine) resolveStaticResource(path string) string { + return e.StaticResourceResolver(path) +} + +func (e *engine) addComponentUpdate(c Composer) { + e.componentUpdateMutex.Lock() + defer e.componentUpdateMutex.Unlock() + + if c == nil || !c.Mounted() { + return + } + if _, isAdded := e.componentUpdates[c]; isAdded { + return + } + e.componentUpdates[c] = true +} + +func (e *engine) preventComponentUpdate(c Composer) { + e.componentUpdateMutex.Lock() + defer e.componentUpdateMutex.Unlock() + + e.componentUpdates[c] = false +} + +func (e *engine) addDeferable(d Dispatch) { + e.deferables = append(e.deferables, d) +} + func (e *engine) start(ctx context.Context) { e.startOnce.Do(func() { - updateInterval := time.Second / time.Duration(e.UpdateRate) - currentInterval := time.Duration(updateInterval) - - updates := time.NewTicker(currentInterval) - defer updates.Stop() + frameDuration := time.Second / time.Duration(e.FrameRate) + currentFrameDuration := frameDuration + frames := time.NewTicker(frameDuration) - cleanup := time.NewTicker(time.Minute) - defer cleanup.Stop() + cleanups := time.NewTicker(time.Minute) + defer cleanups.Stop() for { select { @@ -336,23 +340,20 @@ func (e *engine) start(ctx context.Context) { return case d := <-e.dispatches: - if currentInterval != updateInterval { - currentInterval = updateInterval - updates.Reset(currentInterval) + if currentFrameDuration != frameDuration { + currentFrameDuration = frameDuration + frames.Reset(currentFrameDuration) } - e.handleDispatch(d) - case <-updates.C: - e.updateComponents() - e.execDeferableEvents() - + case <-frames.C: + e.handleFrame() if len(e.dispatches) == 0 { - currentInterval = time.Hour - updates.Reset(currentInterval) + currentFrameDuration *= 2 + frames.Reset(currentFrameDuration) } - case <-cleanup.C: + case <-cleanups.C: e.actions.closeUnusedHandlers() e.states.Cleanup() } @@ -362,114 +363,49 @@ func (e *engine) start(ctx context.Context) { func (e *engine) handleDispatch(d Dispatch) { switch d.Mode { - case Next: - d.Function(makeContext(d.Source)) - case Update: - if d.Source.Mounted() { - d.Function(makeContext(d.Source)) - e.scheduleComponentUpdate(d.Source) - } + d.do() + e.addComponentUpdate(getComponent(d.Source)) case Defer: - if d.Source.Mounted() { - e.defers = append(e.defers, d) - } - } -} - -func (e *engine) scheduleComponentUpdate(n UI) { - if !n.Mounted() { - return - } - - c := nearestCompo(n) - if c == nil { - return - } + e.deferables = append(e.deferables, d) - if _, isScheduled := e.updates[c]; isScheduled { - return + case Next: + d.do() } - - e.updates[c] = struct{}{} - e.updateQueue = append(e.updateQueue, updateDescriptor{ - compo: c, - priority: compoPriority(c), - }) } -func (e *engine) updateComponents() { - if len(e.updates) == 0 { - return - } +func (e *engine) handleFrame() { + e.handleComponentUpdates() + e.handleDeferables() +} - sortUpdateDescriptors(e.updateQueue) - for _, ud := range e.updateQueue { - compo := ud.compo - if !compo.Mounted() { - e.removeFromUpdates(compo) - continue - } +func (e *engine) handleComponentUpdates() { + e.componentUpdateMutex.Lock() + defer e.componentUpdateMutex.Unlock() - if _, requiresUpdate := e.updates[compo]; !requiresUpdate { + for component, canUppdate := range e.componentUpdates { + if !component.Mounted() || !canUppdate { + delete(e.componentUpdates, component) continue } - if err := compo.updateRoot(); err != nil { + if err := component.updateRoot(); err != nil { panic(err) } - e.removeFromUpdates(compo) + delete(e.componentUpdates, component) } - - e.updateQueue = e.updateQueue[:0] } -func (e *engine) removeFromUpdates(c Composer) { - delete(e.updates, c) -} - -func (e *engine) execDeferableEvents() { - for _, d := range e.defers { - if d.Source.Mounted() { - d.Function(makeContext(d.Source)) - } +func (e *engine) handleDeferables() { + for i := range e.deferables { + e.deferables[i].do() + e.deferables[i] = Dispatch{} } - e.defers = e.defers[:0] -} - -func (e *engine) currentPage() Page { - return e.Page -} - -func (e *engine) localStorage() BrowserStorage { - return e.LocalStorage -} - -func (e *engine) sessionStorage() BrowserStorage { - return e.SessionStorage + e.deferables = e.deferables[:0] } -func (e *engine) runsInServer() bool { - return e.RunsInServer -} - -func (e *engine) resolveStaticResource(path string) string { - return e.ResolveStaticResources(path) -} - -type updateDescriptor struct { - compo Composer - priority int -} - -func sortUpdateDescriptors(d []updateDescriptor) { - sort.Slice(d, func(a, b int) bool { - return d[a].priority < d[b].priority - }) -} - -func nearestCompo(n UI) Composer { +func getComponent(n UI) Composer { for node := n; node != nil; node = node.getParent() { if c, isCompo := node.(Composer); isCompo { return c @@ -477,16 +413,3 @@ func nearestCompo(n UI) Composer { } return nil } - -func compoPriority(c Composer) int { - depth := 1 - for parent := c.getParent(); parent != nil; parent = parent.getParent() { - depth++ - } - return depth -} - -type msgHandler struct { - src UI - function MsgHandler -} diff --git a/pkg/app/engine_test.go b/pkg/app/engine_test.go index 23736317b..157b7d55a 100644 --- a/pkg/app/engine_test.go +++ b/pkg/app/engine_test.go @@ -12,16 +12,15 @@ func TestEngineInit(t *testing.T) { e.init() defer e.Close() - assert.NotZero(t, e.UpdateRate) + assert.NotZero(t, e.FrameRate) assert.NotNil(t, e.Page) assert.NotNil(t, e.LocalStorage) assert.NotNil(t, e.SessionStorage) - assert.NotNil(t, e.ResolveStaticResources) + assert.NotNil(t, e.StaticResourceResolver) assert.NotNil(t, e.Body) assert.NotNil(t, e.dispatches) - assert.NotNil(t, e.updates) - assert.NotNil(t, e.updateQueue) - assert.NotNil(t, e.defers) + assert.NotNil(t, e.componentUpdates) + assert.NotNil(t, e.deferables) } func TestEngineDispatch(t *testing.T) { @@ -36,7 +35,6 @@ func TestEngineDispatch(t *testing.T) { d := <-e.dispatches require.Equal(t, Update, d.Mode) require.Equal(t, e.Body, d.Source) - require.NotNil(t, d.Function) } func TestEngineEmit(t *testing.T) { @@ -48,8 +46,7 @@ func TestEngineEmit(t *testing.T) { e.Mount(foo) e.Consume() require.Empty(t, e.dispatches) - require.Empty(t, e.updates) - require.Empty(t, e.updateQueue) + require.Empty(t, e.componentUpdates) bar := foo.getChildren()[0].(*bar) @@ -57,10 +54,12 @@ func TestEngineEmit(t *testing.T) { e.Emit(bar, func() { emitted = true }) - require.True(t, emitted) + require.False(t, emitted) require.Len(t, e.dispatches, 1) - e.Emit(bar, nil) + e.Consume() + require.True(t, emitted) + require.Empty(t, e.dispatches) } func TestEngineHandleDispatch(t *testing.T) { @@ -80,7 +79,7 @@ func TestEngineHandleDispatch(t *testing.T) { Function: func(Context) { called = true }, }) require.True(t, called) - require.NotEmpty(t, e.updateQueue) + require.NotEmpty(t, e.componentUpdates) }) t.Run("defer", func(t *testing.T) { @@ -98,8 +97,8 @@ func TestEngineHandleDispatch(t *testing.T) { Source: bar, Function: func(Context) { called = true }, }) - require.Empty(t, e.updateQueue) - require.Len(t, e.defers, 1) + require.Empty(t, e.componentUpdates) + require.Len(t, e.deferables, 1) require.False(t, called) }) @@ -119,68 +118,49 @@ func TestEngineHandleDispatch(t *testing.T) { Function: func(Context) { called = true }, }) require.True(t, called) - require.Empty(t, e.updateQueue) + require.Empty(t, e.componentUpdates) }) } -func TestEngineScheduleComponentUpdate(t *testing.T) { +func TestEngineAddComponentUpdate(t *testing.T) { e := engine{} e.init() defer e.Close() h := &hello{} - e.scheduleComponentUpdate(h) - require.Empty(t, e.updates) - require.Empty(t, e.updateQueue) + e.addComponentUpdate(h) + require.Empty(t, e.componentUpdates) e.Mount(h) e.Consume() require.Empty(t, e.dispatches) - require.Empty(t, e.updates) - require.Empty(t, e.updateQueue) - - e.scheduleComponentUpdate(h) - require.Len(t, e.updates, 1) - require.Len(t, e.updateQueue, 1) - require.Equal(t, struct{}{}, e.updates[h]) - require.Equal(t, updateDescriptor{ - compo: h, - priority: 2, - }, e.updateQueue[0]) - - e.scheduleComponentUpdate(h) - require.Len(t, e.updates, 1) - require.Len(t, e.updateQueue, 1) + require.Empty(t, e.componentUpdates) + + e.addComponentUpdate(h) + require.Len(t, e.componentUpdates, 1) + require.True(t, e.componentUpdates[h]) + + e.addComponentUpdate(h) + require.Len(t, e.componentUpdates, 1) } -func TestEngineScheduleNestedComponentUpdate(t *testing.T) { +func TestPreventComponentUpdate(t *testing.T) { e := engine{} e.init() defer e.Close() h := &hello{} - div := Div().Body(h) - e.scheduleComponentUpdate(h) - require.Empty(t, e.updates) - require.Empty(t, e.updateQueue) - - e.Mount(div) + e.Mount(h) e.Consume() require.Empty(t, e.dispatches) - require.Empty(t, e.updates) - require.Empty(t, e.updateQueue) - - e.scheduleComponentUpdate(h) - require.Len(t, e.updates, 1) - require.Len(t, e.updateQueue, 1) - require.Equal(t, struct{}{}, e.updates[h]) - require.Equal(t, updateDescriptor{ - compo: h, - priority: 3, - }, e.updateQueue[0]) + require.Empty(t, e.componentUpdates) + + e.preventComponentUpdate(h) + require.Len(t, e.componentUpdates, 1) + require.False(t, e.componentUpdates[h]) } -func TestEngineUpdateCoponents(t *testing.T) { +func TestEngineHandleComponentUpdates(t *testing.T) { e := engine{} e.init() defer e.Close() @@ -189,22 +169,15 @@ func TestEngineUpdateCoponents(t *testing.T) { e.Mount(foo) e.Consume() require.Empty(t, e.dispatches) - require.Empty(t, e.updates) - require.Empty(t, e.updateQueue) + require.Empty(t, e.componentUpdates) bar := foo.root.(*bar) - e.scheduleComponentUpdate(foo) - e.scheduleComponentUpdate(bar) - require.Len(t, e.updates, 2) - require.Len(t, e.updateQueue, 2) + e.addComponentUpdate(foo) + e.addComponentUpdate(bar) + require.Len(t, e.componentUpdates, 2) - e.updateComponents() - require.Empty(t, e.updates) - require.Empty(t, e.updateQueue) - - e.updateComponents() - require.Empty(t, e.updates) - require.Empty(t, e.updateQueue) + e.handleComponentUpdates() + require.Empty(t, e.componentUpdates) } func TestEngineExecDeferableEvents(t *testing.T) { @@ -216,24 +189,22 @@ func TestEngineExecDeferableEvents(t *testing.T) { e.Mount(h) e.Consume() require.Empty(t, e.dispatches) - require.Empty(t, e.updates) - require.Empty(t, e.updateQueue) - require.Empty(t, e.defers) + require.Empty(t, e.componentUpdates) called := false - e.defers = append(e.defers, Dispatch{ + e.addDeferable(Dispatch{ Mode: Defer, Source: h, Function: func(Context) { called = true }, }) - require.Len(t, e.defers, 1) + require.Len(t, e.deferables, 1) - e.execDeferableEvents() + e.handleDeferables() require.True(t, called) - require.Empty(t, e.defers) + require.Empty(t, e.deferables) } func TestEngineHandlePost(t *testing.T) { @@ -277,70 +248,3 @@ func TestEngineHandlePost(t *testing.T) { require.True(t, isHandleBCalled) require.False(t, isHandleCCalled) } - -func TestSortUpdateDescriptors(t *testing.T) { - utests := []struct { - scenario string - in []updateDescriptor - out []updateDescriptor - }{ - { - scenario: "nil", - }, - { - scenario: "empty", - in: []updateDescriptor{}, - out: []updateDescriptor{}, - }, - { - scenario: "single value", - in: []updateDescriptor{ - {priority: 42}, - }, - out: []updateDescriptor{ - {priority: 42}, - }, - }, - { - scenario: "two values", - in: []updateDescriptor{ - {priority: 42}, - {priority: 21}, - }, - out: []updateDescriptor{ - {priority: 21}, - {priority: 42}, - }, - }, - { - scenario: "multiple values", - in: []updateDescriptor{ - {priority: 43}, - {priority: 2}, - {priority: 9}, - {priority: 36}, - {priority: 21}, - {priority: 198}, - {priority: 9}, - {priority: 1}, - }, - out: []updateDescriptor{ - {priority: 1}, - {priority: 2}, - {priority: 9}, - {priority: 9}, - {priority: 21}, - {priority: 36}, - {priority: 43}, - {priority: 198}, - }, - }, - } - - for _, u := range utests { - t.Run(u.scenario, func(t *testing.T) { - sortUpdateDescriptors(u.in) - require.Equal(t, u.out, u.in) - }) - } -} diff --git a/pkg/app/event.go b/pkg/app/event.go index 2a3577cca..9e2ca02f2 100644 --- a/pkg/app/event.go +++ b/pkg/app/event.go @@ -93,19 +93,14 @@ func (h eventHandler) Dismount() { func makeJSEventHandler(src UI, h EventHandler) Func { return FuncOf(func(this Value, args []Value) any { - src.getDispatcher().Dispatch(Dispatch{ - Mode: Update, - Source: src, - Function: func(ctx Context) { - ctx.Emit(func() { - event := Event{ - Value: args[0], - } - trackMousePosition(event) - h(ctx, event) - }) - }, + src.getDispatcher().Emit(src, func() { + event := Event{ + Value: args[0], + } + trackMousePosition(event) + h(makeContext(src), event) }) + return nil }) } diff --git a/pkg/app/http.go b/pkg/app/http.go index 6e51a224b..1b387fabf 100644 --- a/pkg/app/http.go +++ b/pkg/app/http.go @@ -632,8 +632,8 @@ func (h *Handler) servePage(w http.ResponseWriter, r *http.Request) { disp := engine{ Page: &page, - RunsInServer: true, - ResolveStaticResources: h.resolveStaticPath, + IsServerSide: true, + StaticResourceResolver: h.resolveStaticPath, ActionHandlers: actionHandlers, } body := h.Body().privateBody( @@ -656,7 +656,7 @@ func (h *Handler) servePage(w http.ResponseWriter, r *http.Request) { ) if err := mount(&disp, body); err != nil { panic(errors.New("mounting pre-rendering container failed"). - Tag("server-side", disp.runsInServer()). + Tag("server-side", disp.isServerSide()). Tag("body-type", reflect.TypeOf(disp.Body)). Wrap(err)) } diff --git a/pkg/app/state.go b/pkg/app/state.go index 79ad68117..145f766b5 100644 --- a/pkg/app/state.go +++ b/pkg/app/state.go @@ -263,7 +263,7 @@ func (s *store) Del(key string) { defer s.mutex.Unlock() delete(s.states, key) - s.disp.localStorage().Del(key) + s.disp.getLocalStorage().Del(key) } func (s *store) Observe(key string, elem UI) Observer { @@ -326,14 +326,14 @@ func (s *store) removeUnusedObservers() { func (s *store) getPersistent(key string, recv any) error { var state persistentState - s.disp.localStorage().Get(key, &state) + s.disp.getLocalStorage().Get(key, &state) if state.EncryptedValue == nil && state.Value == nil && state.ExpiresAt == (time.Time{}) { return nil } if state.isExpired(time.Now()) { - s.disp.localStorage().Del(key) + s.disp.getLocalStorage().Del(key) return nil } @@ -358,7 +358,7 @@ func (s *store) setPersistent(key string, encrypt bool, expiresAt time.Time, v a return err } - return s.disp.localStorage().Set(key, state) + return s.disp.getLocalStorage().Set(key, state) } func (s *store) expireExpiredValues() { @@ -372,7 +372,7 @@ func (s *store) expireExpiredValues() { } func (s *store) expire(key string, state State) State { - s.disp.localStorage().Del(key) + s.disp.getLocalStorage().Del(key) state.value = nil return state } diff --git a/pkg/app/state_test.go b/pkg/app/state_test.go index f42e13344..36065e572 100644 --- a/pkg/app/state_test.go +++ b/pkg/app/state_test.go @@ -117,7 +117,7 @@ func TestStorePersist(t *testing.T) { s.Set(key, 42, Persist) s.Get(key, &v) require.Equal(t, 42, v) - require.Equal(t, 1, d.localStorage().Len()) + require.Equal(t, 1, d.getLocalStorage().Len()) }) t.Run("value is not pesisted", func(t *testing.T) { @@ -139,7 +139,7 @@ func TestStorePersist(t *testing.T) { s.Get(key, &v) require.Equal(t, 21, v) - require.Equal(t, 1, d.localStorage().Len()) + require.Equal(t, 1, d.getLocalStorage().Len()) }) t.Run("value is observed from local storage", func(t *testing.T) { @@ -151,7 +151,7 @@ func TestStorePersist(t *testing.T) { s.Observe(key, Div()).Value(&v) require.Equal(t, 84, v) - require.Equal(t, 1, d.localStorage().Len()) + require.Equal(t, 1, d.getLocalStorage().Len()) }) t.Run("value is deleted", func(t *testing.T) { @@ -163,7 +163,7 @@ func TestStorePersist(t *testing.T) { require.Empty(t, s.states) s.Get(key, &v) require.Equal(t, 0, v) - require.Equal(t, 0, d.localStorage().Len()) + require.Equal(t, 0, d.getLocalStorage().Len()) }) } @@ -181,7 +181,7 @@ func TestStoreEncrypt(t *testing.T) { s.Set(key, 42, Persist, Encrypt) s.Get(key, &v) require.Equal(t, 42, v) - require.Equal(t, 2, d.localStorage().Len(), d.localStorage()) // Contain app ID. + require.Equal(t, 2, d.getLocalStorage().Len(), d.getLocalStorage()) // Contain app ID. }) t.Run("value is decrypted from local storage", func(t *testing.T) { @@ -192,7 +192,7 @@ func TestStoreEncrypt(t *testing.T) { s.Get(key, &v) require.Equal(t, 43, v) - require.Equal(t, 2, d.localStorage().Len()) // Contain app ID. + require.Equal(t, 2, d.getLocalStorage().Len()) // Contain app ID. }) } @@ -210,7 +210,7 @@ func TestStoreExpiresIn(t *testing.T) { s.Set(key, 42, Persist, ExpiresIn(time.Minute)) s.Get(key, &v) require.Equal(t, 42, v) - require.Equal(t, 1, d.localStorage().Len()) + require.Equal(t, 1, d.getLocalStorage().Len()) }) t.Run("get expired value", func(t *testing.T) { @@ -219,7 +219,7 @@ func TestStoreExpiresIn(t *testing.T) { s.Set(key, 21, Persist, ExpiresIn(-time.Minute)) s.Get(key, &v) require.Equal(t, 0, v) - require.Equal(t, 0, d.localStorage().Len()) + require.Equal(t, 0, d.getLocalStorage().Len()) }) t.Run("get persisted expired value", func(t *testing.T) { @@ -228,13 +228,13 @@ func TestStoreExpiresIn(t *testing.T) { s.Del(key) delete(s.states, key) - s.disp.localStorage().Set(key, persistentState{ + s.disp.getLocalStorage().Set(key, persistentState{ ExpiresAt: time.Now().Add(-time.Minute), }) s.Get(key, &v) require.Equal(t, 0, v) - require.Equal(t, 0, d.localStorage().Len()) + require.Equal(t, 0, d.getLocalStorage().Len()) }) t.Run("observe expired value", func(t *testing.T) { @@ -243,23 +243,23 @@ func TestStoreExpiresIn(t *testing.T) { s.Set(key, 21, Persist, ExpiresIn(-time.Minute)) s.Observe(key, Div()).Value(&v) require.Equal(t, 0, v) - require.Equal(t, 0, d.localStorage().Len()) + require.Equal(t, 0, d.getLocalStorage().Len()) }) t.Run("expire expired values", func(t *testing.T) { s.Set(key, 99, Persist, ExpiresIn(time.Minute)) require.Len(t, s.states, 1) - require.Equal(t, 1, d.localStorage().Len()) + require.Equal(t, 1, d.getLocalStorage().Len()) state := s.states[key] state.ExpiresAt = time.Now().Add(-time.Minute) s.states[key] = state require.True(t, state.isExpired(time.Now())) - require.Equal(t, 1, d.localStorage().Len()) + require.Equal(t, 1, d.getLocalStorage().Len()) s.expireExpiredValues() require.True(t, state.isExpired(time.Now())) - require.Equal(t, 0, d.localStorage().Len()) + require.Equal(t, 0, d.getLocalStorage().Len()) }) }