--!strict --[[ Constructs a new state object which can listen for updates on another state object. FIXME: enabling strict types here causes free types to leak ]] local PubTypes = require "../PubTypes" local Types = require "../Types" local External = require "../External" type Set = { [T]: any } local class = {} local CLASS_METATABLE = { __index = class } -- Table used to hold Observer objects in memory. local strongRefs: Set = {} --[[ Called when the watched state changes value. ]] function class:update(): boolean for _, callback in pairs(self._changeListeners) do External.doTaskImmediate(callback) end return false end --[[ Adds a change listener. When the watched state changes value, the listener will be fired. Returns a function which, when called, will disconnect the change listener. As long as there is at least one active change listener, this Observer will be held in memory, preventing GC, so disconnecting is important. ]] function class:onChange(callback: () -> ()): () -> () local uniqueIdentifier = {} self._numChangeListeners += 1 self._changeListeners[uniqueIdentifier] = callback -- disallow gc (this is important to make sure changes are received) strongRefs[self] = true local disconnected = false return function() if disconnected then return end disconnected = true self._changeListeners[uniqueIdentifier] = nil self._numChangeListeners -= 1 if self._numChangeListeners == 0 then -- allow gc if all listeners are disconnected strongRefs[self] = nil end end end --[[ Similar to `class:onChange()`, however it runs the provided callback immediately. ]] function class:onBind(callback: () -> ()): () -> () External.doTaskImmediate(callback) return self:onChange(callback) end local function Observer(watchedState: PubTypes.Value): Types.Observer local self = setmetatable({ type = "State", kind = "Observer", dependencySet = { [watchedState] = true }, dependentSet = {}, _changeListeners = {}, _numChangeListeners = 0, }, CLASS_METATABLE) -- add this object to the watched state's dependent set watchedState.dependentSet[self] = true return self end return Observer