-
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: Zooming into a Var should not require an Owner (#119)
- Loading branch information
Showing
4 changed files
with
780 additions
and
0 deletions.
There are no files selected for viewing
58 changes: 58 additions & 0 deletions
58
src/main/scala/com/raquo/airstream/state/PureDerivedVar.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.raquo.airstream.state | ||
|
||
import com.raquo.airstream.core.AirstreamError.VarError | ||
import com.raquo.airstream.core.{AirstreamError, Transaction} | ||
import com.raquo.airstream.ownership.Owner | ||
|
||
import scala.util.{Failure, Success, Try} | ||
|
||
// #nc Update comments | ||
|
||
/** DerivedVar has the same Var contract as SourceVar, but instead of maintaining its own state | ||
* it is essentially a lens on the underlying SourceVar. | ||
* | ||
* This Var is active for as long as its signal has listeners. | ||
* Being a StrictSignal, it already starts out with a subscription owned by `owner`, | ||
* but even if owner kills its subscriptions, this Var's signal might have other listeners. | ||
*/ | ||
class PureDerivedVar[A, B]( | ||
parent: Var[A], | ||
zoomIn: A => B, | ||
zoomOut: (A, B) => A, | ||
displayNameSuffix: String | ||
) extends Var[B] { | ||
|
||
override private[state] def underlyingVar: SourceVar[_] = parent.underlyingVar | ||
|
||
private[this] val _varSignal = new PureDerivedVarSignal(parent, zoomIn, displayName) | ||
|
||
// #Note this getCurrentValue implementation is different from SourceVar | ||
// - SourceVar's getCurrentValue looks at an internal currentValue variable | ||
// - That currentValue gets updated immediately before the signal (in an already existing transaction) | ||
// - I hope this doesn't introduce weird transaction related timing glitches | ||
// - But even if it does, I think keeping derived var's current value consistent with its signal value | ||
// is more important, otherwise it would be madness if the derived var was accessed after its owner | ||
// was killed | ||
override private[state] def getCurrentValue: Try[B] = signal.tryNow() | ||
|
||
override private[state] def setCurrentValue(value: Try[B], transaction: Transaction): Unit = { | ||
// #nc Unlike the old DerivedVar, we don't check `_varSignal.isStarted` before updating the parent. | ||
// - Is that "natural" because we don't have an explicit "owner" here, or is that a change in semantics? | ||
parent.tryNow() match { | ||
case Success(parentValue) => | ||
// This can update the parent without causing an infinite loop because | ||
// the parent updates this derived var's signal, it does not call | ||
// setCurrentValue on this var directly. | ||
val nextValue = value.map(zoomOut(parentValue, _)) | ||
// println(s">> parent.setCurrentValue($nextValue)") | ||
parent.setCurrentValue(nextValue, transaction) | ||
|
||
case Failure(err) => | ||
AirstreamError.sendUnhandledError(VarError(s"Unable to zoom out of derived var when the parent var is failed.", cause = Some(err))) | ||
} | ||
} | ||
|
||
override val signal: StrictSignal[B] = _varSignal | ||
|
||
override protected def defaultDisplayName: String = parent.displayName + displayNameSuffix | ||
} |
33 changes: 33 additions & 0 deletions
33
src/main/scala/com/raquo/airstream/state/PureDerivedVarSignal.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.raquo.airstream.state | ||
|
||
import com.raquo.airstream.core.{Protected, Signal} | ||
import com.raquo.airstream.misc.MapSignal | ||
|
||
import scala.util.Try | ||
|
||
class PureDerivedVarSignal[I, O]( | ||
parent: Var[I], | ||
zoomIn: I => O, | ||
parentDisplayName: => String | ||
) extends MapSignal[I, O](parent.signal, project = zoomIn, recover = None) with StrictSignal[O] { self => | ||
|
||
// Note that even if owner kills subscription, this signal might remain due to other listeners | ||
// override protected[state] def isStarted: Boolean = super.isStarted | ||
|
||
override protected def defaultDisplayName: String = parentDisplayName + ".signal" | ||
|
||
override def tryNow(): Try[O] = { | ||
val newParentLastUpdateId = Protected.lastUpdateId(parent.signal) | ||
if (newParentLastUpdateId != _parentLastUpdateId) { | ||
// This branch can only run if !isStarted | ||
val nextValue = currentValueFromParent() | ||
updateCurrentValueFromParent(nextValue, newParentLastUpdateId) | ||
nextValue | ||
} else { | ||
super.tryNow() | ||
} | ||
} | ||
|
||
override protected[state] def updateCurrentValueFromParent(nextValue: Try[O], nextParentLastUpdateId: Int): Unit = | ||
super.updateCurrentValueFromParent(nextValue, nextParentLastUpdateId) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.