/
dispatcher.go
183 lines (144 loc) · 4.69 KB
/
dispatcher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package app
import (
"context"
"net/url"
)
const (
dispatcherSize = 4096
)
// Dispatcher is the interface that describes an environment that synchronizes
// UI instructions and UI elements lifecycle.
type Dispatcher interface {
// Context returns the context associated with the root element.
Context() Context
// Executes the given dispatch operation on the UI goroutine.
Dispatch(d Dispatch)
// Emit executes the given function and notifies the source's parent
// components to update their state.
Emit(src UI, fn func())
// Handle registers the handler for the given action name. When an action
// occurs, the handler is executed on the UI goroutine.
Handle(actionName string, src UI, h ActionHandler)
// Post posts the given action. The action is then handled by handlers
// registered with Handle() and Context.Handle().
Post(a Action)
// Sets the state with the given value.
SetState(state string, v any, opts ...StateOption)
// Stores the specified state value into the given receiver. Panics when the
// receiver is not a pointer or nil.
GetState(state string, recv any)
// Deletes the given state.
DelState(state string)
// Creates an observer that observes changes for the specified state while
// the given element is mounted.
ObserveState(state string, elem UI) Observer
// Async launches the given function on a new goroutine.
//
// The difference versus just launching a goroutine is that it ensures that
// the asynchronous instructions are called before the dispatcher is closed.
//
// This is important during component prerendering since asynchronous
// operations need to complete before sending a pre-rendered page over HTTP.
Async(fn func())
// Wait waits for the asynchronous operations launched with Async() to
// complete.
Wait()
start(context.Context)
getCurrentPage() Page
getLocalStorage() BrowserStorage
getSessionStorage() BrowserStorage
isServerSide() bool
resolveStaticResource(string) string
removeComponentUpdate(Composer)
preventComponentUpdate(Composer)
}
// ClientDispatcher is the interface that describes a dispatcher that emulates a
// client environment.
type ClientDispatcher interface {
Dispatcher
// Consume executes all the remaining UI instructions.
Consume()
// ConsumeNext executes the next UI instructions.
ConsumeNext()
// Close consumes all the remaining UI instruction and releases allocated
// resources.
Close()
// Mounts the given component as root element.
Mount(UI)
// Triggers OnNav from the root component.
Nav(*url.URL)
// Triggers OnAppUpdate from the root component.
AppUpdate()
// Triggers OnAppInstallChange from the root component.
AppInstallChange()
// Triggers OnAppResize from the root component.
AppResize()
}
// NewClientTester creates a testing dispatcher that simulates a
// client environment. The given UI element is mounted upon creation.
func NewClientTester(n UI) ClientDispatcher {
e := &engine{
ActionHandlers: actionHandlers,
}
if IsClient {
e.LocalStorage = newJSStorage("localStorage")
e.LocalStorage.Clear()
e.SessionStorage = newJSStorage("sessionStorage")
e.SessionStorage.Clear()
}
e.init()
e.Mount(n)
e.Consume()
return e
}
// ServerDispatcher is the interface that describes a dispatcher that emulates a server environment.
type ServerDispatcher interface {
Dispatcher
// Consume executes all the remaining UI instructions.
Consume()
// ConsumeNext executes the next UI instructions.
ConsumeNext()
// Close consumes all the remaining UI instruction and releases allocated
// resources.
Close()
}
// NewServerTester creates a testing dispatcher that simulates a
// client environment.
func NewServerTester(n UI) ServerDispatcher {
e := &engine{
IsServerSide: true,
ActionHandlers: actionHandlers,
}
e.init()
e.Mount(n)
e.Consume()
return e
}
// Dispatch represents an operation executed on the UI goroutine.
type Dispatch struct {
Mode DispatchMode
Source UI
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
const (
// A dispatch mode where the dispatched operation is enqueued to be executed
// as soon as possible and its associated UI element is updated at the end
// of the current update cycle.
Update DispatchMode = iota
// A dispatch mode that schedules the dispatched operation to be executed
// after the current update frame.
Defer
// A dispatch mode where the dispatched operation is enqueued to be executed
// as soon as possible.
Next
)
// MsgHandler represents a handler to listen to messages sent with Context.Post.
type MsgHandler func(Context, any)