local V=require(script.Parent.Symbol)local P=require(script.Parent.createFragment)local m=require(script.Parent.createSignal)local O=require(script.Parent.PropMarkers.Children)local g=require(script.Parent.Component)local function B(V)return{value=V;onUpdate=m()}end local function l(V)local m=g:extend("\080\114\111\118\105\100\101\114")function m.init(m,P)m.contextEntry=B(P.value)m:__addContext(V.key,m.contextEntry)end function m.willUpdate(P,V)if V.value~=P.props.value then P.contextEntry.value=V.value end end function m.didUpdate(P,V)if V.value~=P.props.value then P.contextEntry.onUpdate:fire(P.props.value)end end function m.render(V)return P(V.props[O])end return m end local function y(V)local P=g:extend("\067\111\110\115\117\109\101\114")function P.validateProps(V)if type(V.render)~="\102\117\110\099\116\105\111\110"then return false,"\067\111\110\115\117\109\101\114 \101\120\112\101\099\116\115 \097 \096\114\101\110\100\101\114\096 \102\117\110\099\116\105\111\110"else return true end end function P.init(m,P)m.contextEntry=m:__getContext(V.key)end function P.render(P)local m if P.contextEntry~=nil then m=P.contextEntry.value else m=V.defaultValue end return P.props.render(m)end function P.didUpdate(V)if V.contextEntry~=nil then V.lastValue=V.contextEntry.value end end function P.didMount(V)if V.contextEntry~=nil then V.disconnect=V.contextEntry.onUpdate:subscribe(function(P)if P~=V.lastValue then V:setState({})end end)end end function P.willUnmount(V)if V.disconnect~=nil then V.disconnect()V.disconnect=nil end end return P end local c={}c.__index=c function c.new(P)return setmetatable({defaultValue=P,key=V.named("\067\111\110\116\101\120\116\075\101\121")},c)end function c.__tostring(V)return"\082\111\097\099\116\067\111\110\116\101\120\116"end local function L(V)local P=c.new(V)return{Provider=l(P);Consumer=y(P)}end return L
local Symbol = require(script.Parent.Symbol)
local createFragment = require(script.Parent.createFragment)
local createSignal = require(script.Parent.createSignal)
local Children = require(script.Parent.PropMarkers.Children)
local Component = require(script.Parent.Component)

--[[
	Construct the value that is assigned to Roact's context storage.
]]
local function createContextEntry(currentValue)
	return {
		value = currentValue,
		onUpdate = createSignal(),
	}
end

local function createProvider(context)
	local Provider = Component:extend("Provider")

	function Provider:init(props)
		self.contextEntry = createContextEntry(props.value)
		self:__addContext(context.key, self.contextEntry)
	end

	function Provider:willUpdate(nextProps)
		-- If the provided value changed, immediately update the context entry.
		--
		-- During this update, any components that are reachable will receive
		-- this updated value at the same time as any props and state updates
		-- that are being applied.
		if nextProps.value ~= self.props.value then
			self.contextEntry.value = nextProps.value
		end
	end

	function Provider:didUpdate(prevProps)
		-- If the provided value changed, after we've updated every reachable
		-- component, fire a signal to update the rest.
		--
		-- This signal will notify all context consumers. It's expected that
		-- they will compare the last context value they updated with and only
		-- trigger an update on themselves if this value is different.
		--
		-- This codepath will generally only update consumer components that has
		-- a component implementing shouldUpdate between them and the provider.
		if prevProps.value ~= self.props.value then
			self.contextEntry.onUpdate:fire(self.props.value)
		end
	end

	function Provider:render()
		return createFragment(self.props[Children])
	end

	return Provider
end

local function createConsumer(context)
	local Consumer = Component:extend("Consumer")

	function Consumer.validateProps(props)
		if type(props.render) ~= "function" then
			return false, "Consumer expects a `render` function"
		else
			return true
		end
	end

	function Consumer:init(_props)
		-- This value may be nil, which indicates that our consumer is not a
		-- descendant of a provider for this context item.
		self.contextEntry = self:__getContext(context.key)
	end

	function Consumer:render()
		-- Render using the latest available for this context item.
		--
		-- We don't store this value in state in order to have more fine-grained
		-- control over our update behavior.
		local value
		if self.contextEntry ~= nil then
			value = self.contextEntry.value
		else
			value = context.defaultValue
		end

		return self.props.render(value)
	end

	function Consumer:didUpdate()
		-- Store the value that we most recently updated with.
		--
		-- This value is compared in the contextEntry onUpdate hook below.
		if self.contextEntry ~= nil then
			self.lastValue = self.contextEntry.value
		end
	end

	function Consumer:didMount()
		if self.contextEntry ~= nil then
			-- When onUpdate is fired, a new value has been made available in
			-- this context entry, but we may have already updated in the same
			-- update cycle.
			--
			-- To avoid sending a redundant update, we compare the new value
			-- with the last value that we updated with (set in didUpdate) and
			-- only update if they differ. This may happen when an update from a
			-- provider was blocked by an intermediate component that returned
			-- false from shouldUpdate.
			self.disconnect = self.contextEntry.onUpdate:subscribe(function(newValue)
				if newValue ~= self.lastValue then
					-- Trigger a dummy state update.
					self:setState({})
				end
			end)
		end
	end

	function Consumer:willUnmount()
		if self.disconnect ~= nil then
			self.disconnect()
			self.disconnect = nil
		end
	end

	return Consumer
end

local Context = {}
Context.__index = Context

function Context.new(defaultValue)
	return setmetatable({
		defaultValue = defaultValue,
		key = Symbol.named("ContextKey"),
	}, Context)
end

function Context:__tostring()
	return "RoactContext"
end

local function createContext(defaultValue)
	local context = Context.new(defaultValue)

	return {
		Provider = createProvider(context),
		Consumer = createConsumer(context),
	}
end

return createContext
