Skip to content

Commit

Permalink
Silo:OnDispatch()
Browse files Browse the repository at this point in the history
  • Loading branch information
ThoughtSpinnr committed Jan 17, 2024
1 parent 73620d0 commit b07e651
Showing 1 changed file with 53 additions and 24 deletions.
77 changes: 53 additions & 24 deletions modules/silo/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ function Silo.new<S, A>(defaultState: State<S>, modifiers: { [string]: Modifier<
self._Modifiers = {} :: { [string]: any }
self._Dispatching = false
self._Parent = self
self._Subscribers = {}
self._StateSubscribers = {}
self._DispatchSubscribers = {}

self.Actions = {}

Expand Down Expand Up @@ -167,6 +168,28 @@ function Silo.combine<S, A>(silos: { [string]: Silo<unknown, unknown> }, initial
return combinedSilo
end

function Silo:_subscribeInternal<T>(subscriberTbl: { T }, subscriber: T): () -> ()
if self._Dispatching then
error("cannot subscribe from within a modifier", 2)
end
if self._Parent ~= self then
error("can only subscribe on top-level silo", 2)
end
if table.find(subscriberTbl, subscriber) then
error("cannot subscribe same function more than once", 2)
end

table.insert(subscriberTbl, subscriber)

return function()
local index = table.find(subscriberTbl, subscriber)
if not index then
return
end
table.remove(subscriberTbl, index)
end
end

--[=[
Get the current state.
Expand Down Expand Up @@ -196,6 +219,11 @@ function Silo:Dispatch<A>(action: Action<A>)
error("can only dispatch from top-level silo", 2)
end

-- Notify dispatch subscribers of action dispatch:
for _, subscriber in self._DispatchSubscribers do
subscriber(action)
end

-- Find and invoke the modifier to modify current state:
self._Dispatching = true
local oldState = self._State
Expand All @@ -210,13 +238,33 @@ function Silo:Dispatch<A>(action: Action<A>)
if newState ~= oldState then
self._State = Util.DeepFreeze(newState)

-- Notify subscribers of state change:
for _, subscriber in self._Subscribers do
-- Notify state subscribers of state change:
for _, subscriber in self._StateSubscribers do
subscriber(newState, oldState)
end
end
end

--[=[
Subscribe a function to receive all action dispatches. Most useful for
replicating state between two identical silos running on client and
server without ever directly touching state.
Returns an unsubscribe function. Call the function to unsubscribe.
```lua
local unsubscribe = silo:OnDispatch(function(action)
-- Do something
end)
-- Later on, if desired, disconnect the subscription by calling unsubscribe:
unsubscribe()
```
]=]
function Silo:OnDispatch<A>(subscriber: (action: Action<A>) -> ()): () -> ()
return self:_subscribeInternal(self._DispatchSubscribers, subscriber)
end

--[=[
Subscribe a function to receive all state updates, including
initial state (subscriber is called immediately).
Expand All @@ -233,26 +281,7 @@ end
```
]=]
function Silo:Subscribe<S>(subscriber: (newState: State<S>, oldState: State<S>) -> ()): () -> ()
if self._Dispatching then
error("cannot subscribe from within a modifier", 2)
end
if self._Parent ~= self then
error("can only subscribe on top-level silo", 2)
end
if table.find(self._Subscribers, subscriber) then
error("cannot subscribe same function more than once", 2)
end

table.insert(self._Subscribers, subscriber)

-- Unsubscribe:
return function()
local index = table.find(self._Subscribers, subscriber)
if not index then
return
end
table.remove(self._Subscribers, index)
end
return self:_subscribeInternal(self._StateSubscribers, subscriber)
end

--[=[
Expand Down Expand Up @@ -326,7 +355,7 @@ function Silo:ResetToDefaultState()
if self._DefaultState ~= oldState then
self._State = Util.DeepFreeze(Util.DeepCopy(self._DefaultState))

for _, subscriber in self._Subscribers do
for _, subscriber in self._StateSubscribers do
subscriber(self._State, oldState)
end
end
Expand Down

0 comments on commit b07e651

Please sign in to comment.