From 583be8be4eebd51c361fb1f39141b08f75838f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Piaggio?= Date: Fri, 2 Apr 2021 16:36:28 -0300 Subject: [PATCH] New crystal with contexts. --- common/src/main/scala/explore/AppMain.scala | 33 +++++----- .../common/ConstraintSetObsQueries.scala | 14 ++--- .../scala/explore/common/ObsQueries.scala | 13 ++-- .../explore/common/TargetObsQueries.scala | 14 ++--- .../components/ConnectionsStatus.scala | 2 +- .../scala/explore/components/InputModal.scala | 2 +- .../components/UserSelectionForm.scala | 14 ++--- .../components/state/ConnectionManager.scala | 39 ++++++------ .../explore/components/state/IfLogged.scala | 30 ++++----- .../components/state/LogoutTracker.scala | 28 ++++----- .../explore/components/state/SSOManager.scala | 61 +++++++++---------- common/src/main/scala/explore/contexts.scala | 19 ++++++ .../explore/utils/ReactTableHelpers.scala | 4 +- explore/src/main/scala/explore/Routing.scala | 40 ++++++------ explore/src/main/scala/explore/SideTabs.scala | 2 +- explore/src/main/scala/explore/TopBar.scala | 19 +++++- .../constraints/ConstraintSetEditor.scala | 2 +- .../constraints/ConstraintsPanel.scala | 2 +- .../ConstraintSetObsList.scala | 44 ++++++------- .../explore/observationtree/ObsList.scala | 2 +- .../observationtree/TargetObsList.scala | 2 +- .../explore/observationtree/TreeComp.scala | 2 +- .../proposal/PartnerSplitsEditor.scala | 2 +- .../proposal/ProposalDetailsEditor.scala | 2 +- .../proposal/ProposalTabContents.scala | 2 +- .../tabs/ConstraintSetTabContents.scala | 29 +++++---- .../scala/explore/tabs/ObsTabContents.scala | 54 ++++++++-------- .../explore/tabs/TargetTabContents.scala | 29 ++++----- .../explore/targeteditor/AladinCell.scala | 57 ++++++++--------- .../targeteditor/AladinContainer.scala | 17 +++--- .../explore/targeteditor/CataloguesForm.scala | 2 +- .../explore/targeteditor/MagnitudeForm.scala | 2 +- .../scala/explore/targeteditor/RVInput.scala | 2 +- .../explore/targeteditor/SearchForm.scala | 2 +- .../explore/targeteditor/TargetBody.scala | 2 +- .../explore/targeteditor/TargetEditor.scala | 25 ++++---- project/Settings.scala | 2 +- 37 files changed, 315 insertions(+), 302 deletions(-) create mode 100644 common/src/main/scala/explore/contexts.scala diff --git a/common/src/main/scala/explore/AppMain.scala b/common/src/main/scala/explore/AppMain.scala index 6594578fce..4c2723701c 100644 --- a/common/src/main/scala/explore/AppMain.scala +++ b/common/src/main/scala/explore/AppMain.scala @@ -9,8 +9,6 @@ import cats.effect.IOApp import cats.syntax.all._ import clue.WebSocketReconnectionStrategy import clue.js.WebSocketJSBackend -import crystal.AppRootContext -import crystal.react.AppRoot import explore.components.ui.ExploreStyles import explore.model.AppConfig import explore.model.AppContext @@ -42,12 +40,14 @@ import scala.concurrent.duration._ import scala.scalajs.js import js.annotation._ - -object AppCtx extends AppRootContext[AppContextIO] +import japgolly.scalajs.react.Reusability +import crystal.react._ trait AppMain extends IOApp { LogLevelLogger.setLevel(LogLevelLogger.Level.INFO) + implicit val reuseContext: Reusability[AppContextIO] = Reusability.never + implicit val logger: Logger[IO] = LogLevelLogger.createForRoot[IO] implicit val gqlStreamingBackend: WebSocketJSBackend[IO] = WebSocketJSBackend[IO] @@ -136,19 +136,20 @@ trait AppMain extends IOApp { } (for { - _ <- utils.setupScheme[IO](Theme.Dark) - appConfig <- fetchConfig - _ <- logger.info(s"Git Commit: [${BuildInfo.gitHeadCommit.getOrElse("NONE")}]") - _ <- logger.info(s"Config: ${appConfig.show}") - ctx <- AppContext.from[IO](appConfig, reconnectionStrategy, pageUrl, IO.fromFuture) - r <- (ctx.sso.whoami, - setupDOM(), - showEnvironment(appConfig.environment), - AppCtx.initIn[IO](ctx) - ).parTupled - (vault, container, _, _) = r + _ <- utils.setupScheme[IO](Theme.Dark) + appConfig <- fetchConfig + _ <- logger.info(s"Git Commit: [${BuildInfo.gitHeadCommit.getOrElse("NONE")}]") + _ <- logger.info(s"Config: ${appConfig.show}") + ctx <- AppContext.from[IO](appConfig, reconnectionStrategy, pageUrl, IO.fromFuture) + r <- (ctx.sso.whoami, setupDOM(), showEnvironment(appConfig.environment)).parTupled + (vault, container, _) = r } yield { - val RootComponent = AppRoot[IO](initialModel(vault))(rootView => rootComponent(rootView)) + val RootComponent = + ContextProvider[IO](AppCtx, ctx)( + ContextProvider[IO](HelpCtx, HelpContext(none))( + StateProvider[IO](initialModel(vault))(rootView => rootComponent(rootView)) + ) + ) RootComponent().renderIntoDOM(container) diff --git a/common/src/main/scala/explore/common/ConstraintSetObsQueries.scala b/common/src/main/scala/explore/common/ConstraintSetObsQueries.scala index c020cbdd57..6029b6d3a2 100644 --- a/common/src/main/scala/explore/common/ConstraintSetObsQueries.scala +++ b/common/src/main/scala/explore/common/ConstraintSetObsQueries.scala @@ -18,7 +18,7 @@ import explore.model.AirMassRange import explore.model.ConstraintsSummary import explore.model.ObsSummary import explore.model.reusability._ -import japgolly.scalajs.react.Reusability +import japgolly.scalajs.react._ import japgolly.scalajs.react.vdom.html_<^._ import lucuma.core.model.ConstraintSet import lucuma.core.model.Observation @@ -197,14 +197,9 @@ object ConstraintSetObsQueries { implicit val constraintSetWithObsReusability: Reusability[ConstraintSetsWithObs] = Reusability.derive - type LiveQueryRenderer = - ( - View[ConstraintSetsWithObs] => VdomNode - ) => LiveQueryRenderMod[ObservationDB, ConstraintSetsObsQuery.Data, ConstraintSetsWithObs] - - val ConstraintSetObsLiveQuery: LiveQueryRenderer = - render => - AppCtx.runWithCtx { implicit appCtx => + val ConstraintSetObsLiveQuery = + ScalaFnComponent[View[ConstraintSetsWithObs] => VdomNode](render => + AppCtx.using { implicit appCtx => LiveQueryRenderMod[ObservationDB, ConstraintSetsObsQuery.Data, ConstraintSetsWithObs]( ConstraintSetsObsQuery.query(), ConstraintSetsObsQuery.Data.asConstraintSetsWithObs.get, @@ -214,5 +209,6 @@ object ConstraintSetObsQueries { ) )(render) } + ) } diff --git a/common/src/main/scala/explore/common/ObsQueries.scala b/common/src/main/scala/explore/common/ObsQueries.scala index ba89cec1bf..edf4e67141 100644 --- a/common/src/main/scala/explore/common/ObsQueries.scala +++ b/common/src/main/scala/explore/common/ObsQueries.scala @@ -14,6 +14,7 @@ import explore.data.KeyedIndexedList import explore.implicits._ import explore.model.ObsSummary import explore.model.reusability._ +import japgolly.scalajs.react._ import japgolly.scalajs.react.vdom.html_<^._ import lucuma.core.model.Observation import lucuma.ui.reusability._ @@ -74,14 +75,9 @@ object ObsQueries { """ } - type LiveQueryRenderer = - ( - View[ObservationList] => VdomNode - ) => LiveQueryRenderMod[ObservationDB, ProgramObservationsQuery.Data, ObservationList] - - val ObsLiveQuery: LiveQueryRenderer = - render => - AppCtx.runWithCtx { implicit appCtx => + val ObsLiveQuery = + ScalaFnComponent[View[ObservationList] => VdomNode](render => + AppCtx.using { implicit appCtx => LiveQueryRenderMod[ObservationDB, ProgramObservationsQuery.Data, ObservationList]( ProgramObservationsQuery.query(), ProgramObservationsQuery.Data.asObservationList.get, @@ -91,6 +87,7 @@ object ObsQueries { ) )(render) } + ) @GraphQL object ProgramCreateObservation extends GraphQLOperation[ObservationDB] { diff --git a/common/src/main/scala/explore/common/TargetObsQueries.scala b/common/src/main/scala/explore/common/TargetObsQueries.scala index 30df10ce1d..aa17d4bfa8 100644 --- a/common/src/main/scala/explore/common/TargetObsQueries.scala +++ b/common/src/main/scala/explore/common/TargetObsQueries.scala @@ -19,7 +19,7 @@ import io.circe.Decoder import io.circe.HCursor import io.circe.generic.semiauto._ import io.circe.refined._ -import japgolly.scalajs.react.Reusability +import japgolly.scalajs.react._ import japgolly.scalajs.react.vdom.html_<^._ import lucuma.core.model.Asterism import lucuma.core.model.Observation @@ -314,14 +314,9 @@ object TargetObsQueries { implicit val targetsWithObsReusability: Reusability[TargetsAndAsterismsWithObs] = Reusability.derive - type LiveQueryRenderer = - ( - View[TargetsAndAsterismsWithObs] => VdomNode - ) => LiveQueryRenderMod[ObservationDB, TargetsObsQuery.Data, TargetsAndAsterismsWithObs] - - val TargetObsLiveQuery: LiveQueryRenderer = - render => - AppCtx.runWithCtx { implicit appCtx => + val TargetObsLiveQuery = + ScalaFnComponent[View[TargetsAndAsterismsWithObs] => VdomNode](render => + AppCtx.using { implicit appCtx => LiveQueryRenderMod[ObservationDB, TargetsObsQuery.Data, TargetsAndAsterismsWithObs]( TargetsObsQuery.query(), TargetsObsQuery.Data.asTargetsWithObs.get, @@ -333,4 +328,5 @@ object TargetObsQueries { ) )(render) } + ) } diff --git a/common/src/main/scala/explore/components/ConnectionsStatus.scala b/common/src/main/scala/explore/components/ConnectionsStatus.scala index e80f14a738..05d20b30d9 100644 --- a/common/src/main/scala/explore/components/ConnectionsStatus.scala +++ b/common/src/main/scala/explore/components/ConnectionsStatus.scala @@ -54,7 +54,7 @@ object ConnectionsStatus { val component = ScalaComponent .builder[Props] .render(_ => - AppCtx.runWithCtx { ctx => + AppCtx.using { ctx => ctx.clients.ODBConnectionStatus(renderStatus("ODB")) } ) diff --git a/common/src/main/scala/explore/components/InputModal.scala b/common/src/main/scala/explore/components/InputModal.scala index d2824f3de0..19de18a14b 100644 --- a/common/src/main/scala/explore/components/InputModal.scala +++ b/common/src/main/scala/explore/components/InputModal.scala @@ -49,7 +49,7 @@ object InputModal { .builder[Props] .initialStateFromProps(p => State(p.initialValue.fold("")(_.value))) .renderPS { ($, props, state) => - AppCtx.runWithCtx { implicit appCtx => + AppCtx.using { implicit appCtx => val valueView = ViewF.fromState[IO]($).zoom(State.inputValue) val cleanInput = $.setStateL(State.inputValue)("") diff --git a/common/src/main/scala/explore/components/UserSelectionForm.scala b/common/src/main/scala/explore/components/UserSelectionForm.scala index d454cd9fe7..c396199a43 100644 --- a/common/src/main/scala/explore/components/UserSelectionForm.scala +++ b/common/src/main/scala/explore/components/UserSelectionForm.scala @@ -30,14 +30,10 @@ final case class UserSelectionForm( vault: View[Option[UserVault]], message: View[Option[NonEmptyString]] ) extends ReactProps[UserSelectionForm](UserSelectionForm.component) { - def guest: Callback = - AppCtx.runWithCtx { implicit ctx => - ctx.sso.guest.flatMap(v => vault.set(v.some)).runAsyncCB - } - def login: Callback = - AppCtx.runWithCtx { implicit ctx => - ctx.sso.redirectToLogin.runAsyncCB - } + def guest(implicit ctx: AppContextIO): Callback = + ctx.sso.guest.flatMap(v => vault.set(v.some)).runAsyncCB + def login(implicit ctx: AppContextIO): Callback = + ctx.sso.redirectToLogin.runAsyncCB def supportedOrcidBrowser: CallbackTo[(Boolean, Boolean)] = CallbackTo[(Boolean, Boolean)] { val browser = new UAParser(dom.window.navigator.userAgent).getBrowser() @@ -68,7 +64,7 @@ object UserSelectionForm { p.supportedOrcidBrowser.map(Function.tupled(State.apply _)) } .render_PS { (p, s) => - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => Modal( size = ModalSize.Large, clazz = ExploreStyles.LoginBox, diff --git a/common/src/main/scala/explore/components/state/ConnectionManager.scala b/common/src/main/scala/explore/components/state/ConnectionManager.scala index eb778a7dd0..b2e3585d65 100644 --- a/common/src/main/scala/explore/components/state/ConnectionManager.scala +++ b/common/src/main/scala/explore/components/state/ConnectionManager.scala @@ -8,7 +8,6 @@ import cats.syntax.all._ import crystal.react.implicits._ import eu.timepit.refined.cats._ import eu.timepit.refined.types.string.NonEmptyString -import explore.AppCtx import explore.implicits._ import explore.model.Clients import io.circe.Json @@ -22,7 +21,8 @@ import react.semanticui.sizes._ final case class ConnectionManager(ssoToken: NonEmptyString, onConnect: IO[Unit])( val render: () => VdomNode -) extends ReactProps[ConnectionManager](ConnectionManager.component) +)(implicit val ctx: AppContextIO) + extends ReactProps[ConnectionManager](ConnectionManager.component) object ConnectionManager { type Props = ConnectionManager @@ -55,24 +55,21 @@ object ConnectionManager { .builder[Props] .initialState(State()) .renderBackend[Backend] - .componentDidMount($ => - AppCtx.runWithCtx { implicit ctx => - $.backend.onMount(ctx.clients).runAsyncCB - } - ) - .componentDidUpdate($ => - AppCtx.runWithCtx { implicit ctx => - (Logger[IO].debug(s"[ConnectionManager] Token changed. Refreshing connections.") >> - $.backend.refresh(ctx.clients)) - .whenA($.prevProps.ssoToken =!= $.currentProps.ssoToken) - .runAsyncCB - } - ) - .componentWillUnmountConst( - AppCtx.runWithCtx { implicit ctx => - (Logger[IO].debug(s"[ConnectionManager] Terminating connections.") >> - ctx.clients.close()).runAsyncCB - } - ) + .componentDidMount { $ => + implicit val ctx = $.props.ctx + $.backend.onMount(ctx.clients).runAsyncCB + } + .componentDidUpdate { $ => + implicit val ctx = $.currentProps.ctx + (Logger[IO].debug(s"[ConnectionManager] Token changed. Refreshing connections.") >> + $.backend.refresh(ctx.clients)) + .whenA($.prevProps.ssoToken =!= $.currentProps.ssoToken) + .runAsyncCB + } + .componentWillUnmount { $ => + implicit val ctx = $.props.ctx + (Logger[IO].debug(s"[ConnectionManager] Terminating connections.") >> + ctx.clients.close()).runAsyncCB + } .build } diff --git a/common/src/main/scala/explore/components/state/IfLogged.scala b/common/src/main/scala/explore/components/state/IfLogged.scala index 3239a7f54c..b37e00af24 100644 --- a/common/src/main/scala/explore/components/state/IfLogged.scala +++ b/common/src/main/scala/explore/components/state/IfLogged.scala @@ -23,30 +23,30 @@ object IfLogged { type Props = IfLogged // Creates a "profile" for user preferences. - private def createUserPrefs(vault: UserVault): IO[Unit] = - AppCtx.flatMap(implicit ctx => - UserInsertMutation.execute(Input(vault.user.id.toString)).start.void - ) + private def createUserPrefs(vault: UserVault)(implicit ctx: AppContextIO): IO[Unit] = + UserInsertMutation.execute(Input(vault.user.id.toString)).start.void private val component = ScalaComponent .builder[IfLogged] .stateless .render_P { p => - val vaultView = p.view.zoom(RootModel.vault) - val messageView = p.view.zoom(RootModel.userSelectionMessage) + AppCtx.using { implicit ctx => + val vaultView = p.view.zoom(RootModel.vault) + val messageView = p.view.zoom(RootModel.userSelectionMessage) - vaultView.get.fold[VdomElement]( - UserSelectionForm(vaultView, messageView) - ) { vault => - React.Fragment( - SSOManager(vault.expiration, vaultView.set, messageView.set.compose(_.some)), - ConnectionManager(vault.token, onConnect = createUserPrefs(vault))(() => - LogoutTracker(vaultView.set, messageView.set.compose(_.some))(onLogout => - p.render(vault, onLogout) + vaultView.get.fold[VdomElement]( + UserSelectionForm(vaultView, messageView) + ) { vault => + React.Fragment( + SSOManager(vault.expiration, vaultView.set, messageView.set.compose(_.some)), + ConnectionManager(vault.token, onConnect = createUserPrefs(vault))(() => + LogoutTracker(vaultView.set, messageView.set.compose(_.some))(onLogout => + p.render(vault, onLogout) + ) ) ) - ) + } } } .build diff --git a/common/src/main/scala/explore/components/state/LogoutTracker.scala b/common/src/main/scala/explore/components/state/LogoutTracker.scala index 4c5a43d555..c24ee993fc 100644 --- a/common/src/main/scala/explore/components/state/LogoutTracker.scala +++ b/common/src/main/scala/explore/components/state/LogoutTracker.scala @@ -8,7 +8,6 @@ import cats.syntax.all._ import crystal.react.implicits._ import eu.timepit.refined.auto._ import eu.timepit.refined.types.string.NonEmptyString -import explore.AppCtx import explore.implicits._ import explore.model.UserVault import explore.utils.ExploreEvent @@ -21,7 +20,7 @@ import react.common.ReactProps final case class LogoutTracker( setVault: Option[UserVault] => IO[Unit], setMessage: NonEmptyString => IO[Unit] -)(val render: IO[Unit] => VdomNode) +)(val render: IO[Unit] => VdomNode)(implicit val ctx: AppContextIO) extends ReactProps[LogoutTracker](LogoutTracker.component) object LogoutTracker { @@ -42,19 +41,18 @@ object LogoutTracker { ) } .componentDidMount { $ => - AppCtx.runWithCtx { implicit ctx => - IO { - val bc = new BroadcastChannel[ExploreEvent]("explore") - bc.onmessage = (x: ExploreEvent) => - // This is coming from the js world, we can't match the type - (x.event match { - case ExploreEvent.Logout.event => - $.props.setVault(none) >> $.props.setMessage("You logged out in another instance") - case _ => IO.unit - }) - bc - }.flatMap(bc => $.modStateIn[IO](State.bc.set(bc.some))).runAsyncCB - } + implicit val ctx = $.props.ctx + IO { + val bc = new BroadcastChannel[ExploreEvent]("explore") + bc.onmessage = (x: ExploreEvent) => + // This is coming from the js world, we can't match the type + (x.event match { + case ExploreEvent.Logout.event => + $.props.setVault(none) >> $.props.setMessage("You logged out in another instance") + case _ => IO.unit + }) + bc + }.flatMap(bc => $.modStateIn[IO](State.bc.set(bc.some))).runAsyncCB } .componentWillUnmount($ => $.state.bc.map(bc => IO(bc.close()).attempt.void).orEmpty.runAsyncAndForgetCB diff --git a/common/src/main/scala/explore/components/state/SSOManager.scala b/common/src/main/scala/explore/components/state/SSOManager.scala index d211825472..bfb266d62c 100644 --- a/common/src/main/scala/explore/components/state/SSOManager.scala +++ b/common/src/main/scala/explore/components/state/SSOManager.scala @@ -8,7 +8,6 @@ import cats.syntax.all._ import crystal.react.implicits._ import eu.timepit.refined.auto._ import eu.timepit.refined.types.string.NonEmptyString -import explore.AppCtx import explore.implicits._ import explore.model.UserVault import japgolly.scalajs.react._ @@ -21,10 +20,11 @@ import react.common.ReactProps import java.time.Instant final case class SSOManager( - expiration: Instant, - setVault: Option[UserVault] => IO[Unit], - setMessage: NonEmptyString => IO[Unit] -) extends ReactProps[SSOManager](SSOManager.component) + expiration: Instant, + setVault: Option[UserVault] => IO[Unit], + setMessage: NonEmptyString => IO[Unit] +)(implicit val ctx: AppContextIO) + extends ReactProps[SSOManager](SSOManager.component) object SSOManager { type Props = SSOManager @@ -38,19 +38,17 @@ object SSOManager { final class Backend() { def tokenRefresher( - expiration: Instant, - setVault: Option[UserVault] => IO[Unit], - setMessage: NonEmptyString => IO[Unit] - ): IO[Unit] = - AppCtx.flatMap(implicit ctx => - for { - vaultOpt <- ctx.sso.refreshToken(expiration) - _ <- setVault(vaultOpt) - _ <- vaultOpt.fold(setMessage("Your session has expired"))(vault => - tokenRefresher(vault.expiration, setVault, setMessage) - ) - } yield () - ) + expiration: Instant, + setVault: Option[UserVault] => IO[Unit], + setMessage: NonEmptyString => IO[Unit] + )(implicit ctx: AppContextIO): IO[Unit] = + for { + vaultOpt <- ctx.sso.refreshToken(expiration) + _ <- setVault(vaultOpt) + _ <- vaultOpt.fold(setMessage("Your session has expired"))(vault => + tokenRefresher(vault.expiration, setVault, setMessage) + ) + } yield () // This is a "phantom" component. Doesn't render anything. def render(): VdomNode = React.Fragment() @@ -61,20 +59,19 @@ object SSOManager { .initialState(State(none)) .renderBackend[Backend] .componentDidMount { $ => - AppCtx.runWithCtx { implicit ctx => - $.backend - .tokenRefresher($.props.expiration, $.props.setVault, $.props.setMessage) - .runCancelable { - case Left(t) => - Logger[IO].error(t)("Error refreshing SSO token") >> - $.props.setVault(none) >> - $.props.setMessage("There was an error while checking the validity of your session") - case _ => IO.unit - } - .toIO - .flatMap(ct => $.modStateIn[IO](State.cancelToken.set(ct.some))) - .runAsyncCB - } + implicit val ctx = $.props.ctx + $.backend + .tokenRefresher($.props.expiration, $.props.setVault, $.props.setMessage) + .runCancelable { + case Left(t) => + Logger[IO].error(t)("Error refreshing SSO token") >> + $.props.setVault(none) >> + $.props.setMessage("There was an error while checking the validity of your session") + case _ => IO.unit + } + .toIO + .flatMap(ct => $.modStateIn[IO](State.cancelToken.set(ct.some))) + .runAsyncCB } .componentWillUnmount($ => // Setting vault to none is defensive. This component should actually unmount when vault is none. diff --git a/common/src/main/scala/explore/contexts.scala b/common/src/main/scala/explore/contexts.scala new file mode 100644 index 0000000000..9d7e61980a --- /dev/null +++ b/common/src/main/scala/explore/contexts.scala @@ -0,0 +1,19 @@ +// Copyright (c) 2016-2021 Association of Universities for Research in Astronomy, Inc. (AURA) +// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause + +package explore + +import cats.effect.IO +import crystal.react.Ctx +import japgolly.scalajs.react.Reusability +import monocle.macros.Lenses + +object AppCtx extends Ctx[IO, AppContextIO] + +@Lenses +case class HelpContext(msg: Option[String]) +object HelpContext { + implicit val helpContextReuse: Reusability[HelpContext] = Reusability.derive +} + +object HelpCtx extends Ctx[IO, HelpContext] diff --git a/common/src/main/scala/explore/utils/ReactTableHelpers.scala b/common/src/main/scala/explore/utils/ReactTableHelpers.scala index 420058cbb1..16a3e8b947 100644 --- a/common/src/main/scala/explore/utils/ReactTableHelpers.scala +++ b/common/src/main/scala/explore/utils/ReactTableHelpers.scala @@ -43,7 +43,7 @@ object ReactTableHelpers { ScalaComponent .builder[View[A]] .render_P { va => - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => FormInputEV(id = newId, value = va.zoom(lens), validFormat = validFormat, @@ -77,7 +77,7 @@ object ReactTableHelpers { .render_P { va => val excluded = excludeFn.fold(Set.empty[B])(_.apply(va)) - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => EnumViewSelect(id = newId, value = va.zoom(lens), exclude = excluded, diff --git a/explore/src/main/scala/explore/Routing.scala b/explore/src/main/scala/explore/Routing.scala index 8c068bd5fe..2173cf12d3 100644 --- a/explore/src/main/scala/explore/Routing.scala +++ b/explore/src/main/scala/explore/Routing.scala @@ -43,33 +43,39 @@ object Routing { private def targetTab(model: View[RootModel]): VdomElement = withSize { size => - TargetTabContents( - model.zoom(RootModel.userId), - model.zoom(RootModel.focused), - model.zoom(RootModel.searchingTarget), - model.zoom(RootModel.expandedIds), - size + AppCtx.using(implicit ctx => + TargetTabContents( + model.zoom(RootModel.userId), + model.zoom(RootModel.focused), + model.zoom(RootModel.searchingTarget), + model.zoom(RootModel.expandedIds), + size + ) ) } private def obsTab(model: View[RootModel]): VdomElement = withSize(size => - ObsTabContents(model.zoom(RootModel.userId), - model.zoom(RootModel.focused), - model.zoom(RootModel.searchingTarget), - size + AppCtx.using(implicit ctx => + ObsTabContents(model.zoom(RootModel.userId), + model.zoom(RootModel.focused), + model.zoom(RootModel.searchingTarget), + size + ) ) ) private def constraintSetTab(model: View[RootModel]): VdomElement = withSize(size => - ConstraintSetTabContents( - model.zoom(RootModel.userId), - model.zoom(RootModel.focused), - model.zoom( - RootModel.expandedIds.composeLens(ExpandedIds.constraintSetIds) - ), - size + AppCtx.using(implicit ctx => + ConstraintSetTabContents( + model.zoom(RootModel.userId), + model.zoom(RootModel.focused), + model.zoom( + RootModel.expandedIds.composeLens(ExpandedIds.constraintSetIds) + ), + size + ) ) ) diff --git a/explore/src/main/scala/explore/SideTabs.scala b/explore/src/main/scala/explore/SideTabs.scala index 1365f2eead..ee0a933b05 100644 --- a/explore/src/main/scala/explore/SideTabs.scala +++ b/explore/src/main/scala/explore/SideTabs.scala @@ -37,7 +37,7 @@ object SideTabs { .builder[Props] .stateless .render_P { p => - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => val tabsL = p.tabs.get.toNel val focus = p.tabs.get.focus diff --git a/explore/src/main/scala/explore/TopBar.scala b/explore/src/main/scala/explore/TopBar.scala index 5d2da6d67b..a2508bd1e6 100644 --- a/explore/src/main/scala/explore/TopBar.scala +++ b/explore/src/main/scala/explore/TopBar.scala @@ -64,7 +64,7 @@ object TopBar { val p = $.props val currentTheme = $.state.theme - AppCtx.runWithCtx { implicit appCtx => + AppCtx.using { implicit appCtx => val role = p.user.role def logout: IO[Unit] = @@ -75,14 +75,27 @@ object TopBar { ExploreStyles.MainHeader, Menu( attached = MenuAttached.Top, - // compact = true, borderless = true, tabular = MenuTabular.Right )( MenuItem( <.span( ExploreStyles.MainTitle, - "Explore" + "Explore", + HelpCtx.usingView { help => + val helpMsg = help.zoom(HelpContext.msg) + React.Fragment( + helpMsg.get, + react.semanticui.elements.button + .Button(onClick = + helpMsg + .mod(_.fold(" - Hello!".some)(_ => none)) + .runAsyncCB + )( + "Help" + ) + ) + } ) ), Item( diff --git a/explore/src/main/scala/explore/constraints/ConstraintSetEditor.scala b/explore/src/main/scala/explore/constraints/ConstraintSetEditor.scala index d5b2076c69..e4f1aea053 100644 --- a/explore/src/main/scala/explore/constraints/ConstraintSetEditor.scala +++ b/explore/src/main/scala/explore/constraints/ConstraintSetEditor.scala @@ -31,7 +31,7 @@ object ConstraintSetEditor { ScalaComponent .builder[Props] .render_P { props => - AppCtx.runWithCtx { implicit appCtx => + AppCtx.using { implicit appCtx => LiveQueryRenderMod[ObservationDB, ConstraintSetQuery.Data, Option[ConstraintSetModel]]( ConstraintSetQuery.query(props.csid), _.constraintSet, diff --git a/explore/src/main/scala/explore/constraints/ConstraintsPanel.scala b/explore/src/main/scala/explore/constraints/ConstraintsPanel.scala index f9ec6a5cf2..573fb3831f 100644 --- a/explore/src/main/scala/explore/constraints/ConstraintsPanel.scala +++ b/explore/src/main/scala/explore/constraints/ConstraintsPanel.scala @@ -97,7 +97,7 @@ object ConstraintsPanel { } class Backend($ : BackendScope[Props, State]) { - def render(props: Props, state: State) = AppCtx.runWithCtx { implicit appCtx => + def render(props: Props, state: State) = AppCtx.using { implicit appCtx => val stateView = ViewF.fromState[IO]($) val constraintSet = props.constraintSet diff --git a/explore/src/main/scala/explore/observationtree/ConstraintSetObsList.scala b/explore/src/main/scala/explore/observationtree/ConstraintSetObsList.scala index a1bc15be62..1bbf1bab61 100644 --- a/explore/src/main/scala/explore/observationtree/ConstraintSetObsList.scala +++ b/explore/src/main/scala/explore/observationtree/ConstraintSetObsList.scala @@ -55,7 +55,8 @@ final case class ConstraintSetObsList( constraintSetsWithObs: View[ConstraintSetsWithObs], focused: View[Option[Focused]], expandedIds: View[SortedSet[ConstraintSet.Id]] -) extends ReactProps[ConstraintSetObsList](ConstraintSetObsList.component) +)(implicit val ctx: AppContextIO) + extends ReactProps[ConstraintSetObsList](ConstraintSetObsList.component) with ViewCommon { override val obsBadgeLayout = ObsBadge.Layout.NameAndConf } @@ -257,7 +258,7 @@ object ConstraintSetObsList { expandedIds .mod(expanded => expanded.exists(_ === id).fold(expanded - id, expanded + id)) - def render(props: Props, state: State): VdomElement = AppCtx.runWithCtx { implicit ctx => + def render(props: Props, state: State): VdomElement = AppCtx.using { implicit ctx => val observations = props.constraintSetsWithObs.get.obs val obsByConstraintSet = observations.toList.groupBy(_.constraints.map(_.id)) @@ -459,28 +460,27 @@ object ConstraintSetObsList { .initialState(State()) .renderBackend[Backend] .componentDidMount { $ => - AppCtx.runWithCtx { implicit ctx => - val constraintSetsWithObs = $.props.constraintSetsWithObs.get - val expandedIds = $.props.expandedIds - - // expand constraint set with focused observation - val expandCs = $.props.focused.get - .collect { case FocusedObs(obsId) => - constraintSetsWithObs.obs - .getElement(obsId) - .flatMap(_.constraints.map(c => expandedIds.mod(_ + c.id))) - } - .flatten - .orEmpty + implicit val ctx = $.props.ctx + val constraintSetsWithObs = $.props.constraintSetsWithObs.get + val expandedIds = $.props.expandedIds + + // expand constraint set with focused observation + val expandCs = $.props.focused.get + .collect { case FocusedObs(obsId) => + constraintSetsWithObs.obs + .getElement(obsId) + .flatMap(_.constraints.map(c => expandedIds.mod(_ + c.id))) + } + .flatten + .orEmpty - // Remove contraint sets from expanded list which no longer exist. - val removeConstraintSets = - (expandedIds.get -- constraintSetsWithObs.constraintSets.toList.map(_.id)).toNes - .map(missingIds => expandedIds.mod(_ -- missingIds.toSortedSet)) - .orEmpty + // Remove contraint sets from expanded list which no longer exist. + val removeConstraintSets = + (expandedIds.get -- constraintSetsWithObs.constraintSets.toList.map(_.id)).toNes + .map(missingIds => expandedIds.mod(_ -- missingIds.toSortedSet)) + .orEmpty - (expandCs >> removeConstraintSets).runAsyncCB - } + (expandCs >> removeConstraintSets).runAsyncCB } .build } diff --git a/explore/src/main/scala/explore/observationtree/ObsList.scala b/explore/src/main/scala/explore/observationtree/ObsList.scala index 6f13b9de70..5604d107d8 100644 --- a/explore/src/main/scala/explore/observationtree/ObsList.scala +++ b/explore/src/main/scala/explore/observationtree/ObsList.scala @@ -156,7 +156,7 @@ object ObsList { } def render(props: Props) = - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => val focused = props.focused.get val observations = props.observations.get.toList val someSelected = focused.isDefined diff --git a/explore/src/main/scala/explore/observationtree/TargetObsList.scala b/explore/src/main/scala/explore/observationtree/TargetObsList.scala index 4c1f980622..93a1b9c744 100644 --- a/explore/src/main/scala/explore/observationtree/TargetObsList.scala +++ b/explore/src/main/scala/explore/observationtree/TargetObsList.scala @@ -493,7 +493,7 @@ object TargetObsList { .fold(expanded - id, expanded + id) } - def render(props: Props, state: State): VdomElement = AppCtx.runWithCtx { implicit ctx => + def render(props: Props, state: State): VdomElement = AppCtx.using { implicit ctx => val observations = props.aimsWithObs.get.observations val obsByAim = observations.toList.groupBy(_.pointingId) diff --git a/explore/src/main/scala/explore/observationtree/TreeComp.scala b/explore/src/main/scala/explore/observationtree/TreeComp.scala index 130e700638..77b0a94672 100644 --- a/explore/src/main/scala/explore/observationtree/TreeComp.scala +++ b/explore/src/main/scala/explore/observationtree/TreeComp.scala @@ -122,7 +122,7 @@ object TreeComp { } def render(props: Props[K, A], state: State[K, A]): VdomElement = - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => UndoRegion[KeyedIndexedTree[K, A]] { undoCtx => // println(state.tree) diff --git a/explore/src/main/scala/explore/proposal/PartnerSplitsEditor.scala b/explore/src/main/scala/explore/proposal/PartnerSplitsEditor.scala index fb5caca4bf..e1197e0945 100644 --- a/explore/src/main/scala/explore/proposal/PartnerSplitsEditor.scala +++ b/explore/src/main/scala/explore/proposal/PartnerSplitsEditor.scala @@ -92,7 +92,7 @@ object PartnerSplitsEditor { private def total(p: Props) = p.splits.get.map(_.percent.value.value).sum private def addsUpTo100(p: Props) = total(p) === 100 - def render(p: Props): VdomNode = AppCtx.runWithCtx { implicit ctx => + def render(p: Props): VdomNode = AppCtx.using { implicit ctx => def save = if (addsUpTo100(p)) p.onSave(p.splits.get) else Callback.empty Modal( diff --git a/explore/src/main/scala/explore/proposal/ProposalDetailsEditor.scala b/explore/src/main/scala/explore/proposal/ProposalDetailsEditor.scala index 8605041867..52f527d488 100644 --- a/explore/src/main/scala/explore/proposal/ProposalDetailsEditor.scala +++ b/explore/src/main/scala/explore/proposal/ProposalDetailsEditor.scala @@ -112,7 +112,7 @@ object ProposalDetailsEditor { class Backend($ : BackendScope[Props, State]) { def render(props: Props, state: State) = - AppCtx.runWithCtx { implicit appCtx => + AppCtx.using { implicit appCtx => val splitsZoom = ViewF.fromState[IO]($).zoom(State.splits) val details = props.proposalDetails diff --git a/explore/src/main/scala/explore/proposal/ProposalTabContents.scala b/explore/src/main/scala/explore/proposal/ProposalTabContents.scala index edc92faace..d7dd6b1f80 100644 --- a/explore/src/main/scala/explore/proposal/ProposalTabContents.scala +++ b/explore/src/main/scala/explore/proposal/ProposalTabContents.scala @@ -51,7 +51,7 @@ object ProposalTabContents { ) def render() = - AppCtx.runWithCtx { implicit appCtx => + AppCtx.using { implicit appCtx => val ref: SignallingRef[IO, ProposalDetails] = SignallingRef.in[SyncIO, IO, ProposalDetails](proposalDetails).unsafeRunSync() diff --git a/explore/src/main/scala/explore/tabs/ConstraintSetTabContents.scala b/explore/src/main/scala/explore/tabs/ConstraintSetTabContents.scala index 32e5691851..3e589ce3f0 100644 --- a/explore/src/main/scala/explore/tabs/ConstraintSetTabContents.scala +++ b/explore/src/main/scala/explore/tabs/ConstraintSetTabContents.scala @@ -41,11 +41,12 @@ import scala.collection.immutable.SortedSet import scala.concurrent.duration._ final case class ConstraintSetTabContents( - userId: ViewOpt[User.Id], - focused: View[Option[Focused]], - expandedIds: View[SortedSet[ConstraintSet.Id]], - size: ResizeDetector.Dimensions -) extends ReactProps[ConstraintSetTabContents](ConstraintSetTabContents.component) { + userId: ViewOpt[User.Id], + focused: View[Option[Focused]], + expandedIds: View[SortedSet[ConstraintSet.Id]], + size: ResizeDetector.Dimensions +)(implicit val ctx: AppContextIO) + extends ReactProps[ConstraintSetTabContents](ConstraintSetTabContents.component) { def isCsSelected: Boolean = focused.get.collect { case Focused.FocusedConstraintSet(_) => () }.isDefined @@ -57,13 +58,15 @@ object ConstraintSetTabContents { implicit val propsReuse: Reusability[Props] = Reusability.derive - def readWidthPreference($ : ComponentDidMount[Props, State, Unit]): Callback = - AppCtx.runWithCtx { implicit ctx => - (UserAreaWidths.queryWithDefault[IO]($.props.userId.get, - ResizableSection.ConstraintSetsTree, - Constants.InitialTreeWidth.toInt - ) >>= $.setStateLIn[IO](TwoPanelState.treeWidth)).runAsyncCB - } + def readWidthPreference( + $ : ComponentDidMount[Props, State, Unit] + ): Callback = { + implicit val ctx = $.props.ctx + (UserAreaWidths.queryWithDefault[IO]($.props.userId.get, + ResizableSection.ConstraintSetsTree, + Constants.InitialTreeWidth.toInt + ) >>= $.setStateLIn[IO](TwoPanelState.treeWidth)).runAsyncCB + } protected val component = ScalaComponent @@ -77,7 +80,7 @@ object ConstraintSetTabContents { } ) .renderPS { ($, props, state) => - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => val treeResize = (_: ReactEvent, d: ResizeCallbackData) => ($.setStateLIn[IO](TwoPanelState.treeWidth)(d.size.width) *> diff --git a/explore/src/main/scala/explore/tabs/ObsTabContents.scala b/explore/src/main/scala/explore/tabs/ObsTabContents.scala index 1fb03a27f1..ea3ab8a784 100644 --- a/explore/src/main/scala/explore/tabs/ObsTabContents.scala +++ b/explore/src/main/scala/explore/tabs/ObsTabContents.scala @@ -52,11 +52,12 @@ import react.semanticui.sizes._ import scala.concurrent.duration._ final case class ObsTabContents( - userId: ViewOpt[User.Id], - focused: View[Option[Focused]], - searching: View[Set[Target.Id]], - size: ResizeDetector.Dimensions -) extends ReactProps[ObsTabContents](ObsTabContents.component) { + userId: ViewOpt[User.Id], + focused: View[Option[Focused]], + searching: View[Set[Target.Id]], + size: ResizeDetector.Dimensions +)(implicit val ctx: AppContextIO) + extends ReactProps[ObsTabContents](ObsTabContents.component) { def isObsSelected: Boolean = focused.get.collect { case Focused.FocusedObs(_) => () }.isDefined } @@ -146,36 +147,31 @@ object ObsTabContents { implicit val stateReuse: Reusability[State] = Reusability.derive class Backend($ : BackendScope[Props, State]) { - def readTabPreference(userId: Option[User.Id]): Callback = - AppCtx.runWithCtx { implicit ctx => - ObsTabPreferencesQuery - .queryWithDefault[IO](userId, - GridLayoutSection.ObservationsLayout, - ResizableSection.ObservationsTree, - (Constants.InitialTreeWidth.toInt, defaultLayout) - ) - .runAsyncAndThenCB { - case Right((w, l)) => - $.modState((s: State) => State.panelsWidth.set(w)(s.updateLayouts(l))) - case Left(_) => Callback.empty - } - } + def readTabPreference(userId: Option[User.Id])(implicit ctx: AppContextIO): Callback = + ObsTabPreferencesQuery + .queryWithDefault[IO](userId, + GridLayoutSection.ObservationsLayout, + ResizableSection.ObservationsTree, + (Constants.InitialTreeWidth.toInt, defaultLayout) + ) + .runAsyncAndThenCB { + case Right((w, l)) => + $.modState((s: State) => State.panelsWidth.set(w)(s.updateLayouts(l))) + case Left(_) => Callback.empty + } - def readTargetPreferences(targetId: Target.Id): Callback = + def readTargetPreferences(targetId: Target.Id)(implicit ctx: AppContextIO): Callback = $.props.flatMap { p => p.userId.get.map { uid => - AppCtx - .runWithCtx { implicit ctx => - UserTargetPreferencesQuery - .queryWithDefault[IO](uid, targetId, Constants.InitialFov) - .flatMap(v => $.modStateIn[IO](State.fovAngle.set(v))) - .runAsyncAndForgetCB - } + UserTargetPreferencesQuery + .queryWithDefault[IO](uid, targetId, Constants.InitialFov) + .flatMap(v => $.modStateIn[IO](State.fovAngle.set(v))) + .runAsyncAndForgetCB }.getOrEmpty } def render(props: Props, state: State) = { - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => val treeResize = (_: ReactEvent, d: ResizeCallbackData) => $.setStateL(State.panelsWidth)(d.size.width) *> @@ -365,7 +361,7 @@ object ObsTabContents { } ) .renderBackend[Backend] - .componentDidMount($ => $.backend.readTabPreference($.props.userId.get)) + .componentDidMount($ => $.backend.readTabPreference($.props.userId.get)($.props.ctx)) .configure(Reusability.shouldComponentUpdate) .build diff --git a/explore/src/main/scala/explore/tabs/TargetTabContents.scala b/explore/src/main/scala/explore/tabs/TargetTabContents.scala index 7efb9bfb44..469cd02328 100644 --- a/explore/src/main/scala/explore/tabs/TargetTabContents.scala +++ b/explore/src/main/scala/explore/tabs/TargetTabContents.scala @@ -41,12 +41,13 @@ import react.semanticui.sizes._ import scala.concurrent.duration._ final case class TargetTabContents( - userId: ViewOpt[User.Id], - focused: View[Option[Focused]], - searching: View[Set[Target.Id]], - expandedIds: View[ExpandedIds], - size: ResizeDetector.Dimensions -) extends ReactProps[TargetTabContents](TargetTabContents.component) { + userId: ViewOpt[User.Id], + focused: View[Option[Focused]], + searching: View[Set[Target.Id]], + expandedIds: View[ExpandedIds], + size: ResizeDetector.Dimensions +)(implicit val ctx: AppContextIO) + extends ReactProps[TargetTabContents](TargetTabContents.component) { def isTargetSelected: Boolean = focused.get.collect { case Focused.FocusedTarget(_) => () }.isDefined @@ -58,13 +59,13 @@ object TargetTabContents { implicit val propsReuse: Reusability[Props] = Reusability.derive - def readWidthPreference($ : ComponentDidMount[Props, State, Unit]): Callback = - AppCtx.runWithCtx { implicit ctx => - (UserAreaWidths.queryWithDefault[IO]($.props.userId.get, - ResizableSection.TargetsTree, - Constants.InitialTreeWidth.toInt - ) >>= $.setStateLIn[IO](TwoPanelState.treeWidth)).runAsyncCB - } + def readWidthPreference($ : ComponentDidMount[Props, State, Unit]): Callback = { + implicit val ctx = $.props.ctx + (UserAreaWidths.queryWithDefault[IO]($.props.userId.get, + ResizableSection.TargetsTree, + Constants.InitialTreeWidth.toInt + ) >>= $.setStateLIn[IO](TwoPanelState.treeWidth)).runAsyncCB + } protected val component = ScalaComponent @@ -79,7 +80,7 @@ object TargetTabContents { } ) .renderPS { ($, props, state) => - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => val treeResize = (_: ReactEvent, d: ResizeCallbackData) => ($.setStateLIn[IO](TwoPanelState.treeWidth)(d.size.width) *> diff --git a/explore/src/main/scala/explore/targeteditor/AladinCell.scala b/explore/src/main/scala/explore/targeteditor/AladinCell.scala index 2ffde69d8d..b08ba29c0e 100644 --- a/explore/src/main/scala/explore/targeteditor/AladinCell.scala +++ b/explore/src/main/scala/explore/targeteditor/AladinCell.scala @@ -71,40 +71,41 @@ object AladinCell extends ModelOptics { .flatMapCB(_.backend.gotoRaDec(coords)) .toCallback - def render(props: Props, state: State) = React.Fragment( - <.div( - ExploreStyles.TargetAladinCell, - <.div( - ExploreStyles.AladinContainerColumn, - AladinRef - .withRef(aladinRef) { - AladinContainer( - props.target, - props.options.get, - $.setStateL(State.current)(_), - fov => - AppCtx.runWithCtx { implicit ctx => - $.setStateL(State.fov)(fov) *> UserTargetPreferencesUpsert - .updateFov[IO](props.uid, props.tid, fov.x) - .runAsyncAndForgetCB - .debounce(1.seconds) - } - ) - }, - AladinToolbar(state.fov, state.current), + def render(props: Props, state: State) = + AppCtx.using(implicit ctx => + React.Fragment( <.div( - ExploreStyles.AladinCenterButton, - Popup( - content = "Center on target", - position = PopupPosition.BottomLeft, - trigger = Button(size = Mini, icon = true, onClick = centerOnTarget)( - Icons.Bullseye.fitted(true).clazz(ExploreStyles.Accented) + ExploreStyles.TargetAladinCell, + <.div( + ExploreStyles.AladinContainerColumn, + AladinRef + .withRef(aladinRef) { + AladinContainer( + props.target, + props.options.get, + $.setStateL(State.current)(_), + fov => + $.setStateL(State.fov)(fov) *> UserTargetPreferencesUpsert + .updateFov[IO](props.uid, props.tid, fov.x) + .runAsyncAndForgetCB + .debounce(1.seconds) + ) + }, + AladinToolbar(state.fov, state.current), + <.div( + ExploreStyles.AladinCenterButton, + Popup( + content = "Center on target", + position = PopupPosition.BottomLeft, + trigger = Button(size = Mini, icon = true, onClick = centerOnTarget)( + Icons.Bullseye.fitted(true).clazz(ExploreStyles.Accented) + ) + ) ) ) ) ) ) - ) def newProps(currentProps: Props, nextProps: Props): Callback = gotoRaDec(nextProps.aladinCoords) diff --git a/explore/src/main/scala/explore/targeteditor/AladinContainer.scala b/explore/src/main/scala/explore/targeteditor/AladinContainer.scala index f112a5f90c..003c6f6f65 100644 --- a/explore/src/main/scala/explore/targeteditor/AladinContainer.scala +++ b/explore/src/main/scala/explore/targeteditor/AladinContainer.scala @@ -5,7 +5,6 @@ package explore.targeteditor import cats.syntax.all._ import crystal.react.implicits._ -import explore.AppCtx import explore.View import explore.components.ui.ExploreStyles import explore.implicits._ @@ -64,15 +63,11 @@ object AladinContainer { // Create a mutable reference private val aladinRef = Ref.toScalaComponent(AladinComp) - def setRa(ra: RightAscension): Callback = - AppCtx.runWithCtx { implicit ctx => - $.props >>= (_.target.zoom(Coordinates.rightAscension).set(ra).runAsyncCB) - } + def setRa(ra: RightAscension)(implicit ctx: AppContextIO): Callback = + $.props >>= (_.target.zoom(Coordinates.rightAscension).set(ra).runAsyncCB) - def setDec(dec: Declination): Callback = - AppCtx.runWithCtx { implicit ctx => - $.props >>= (_.target.zoom(Coordinates.declination).set(dec).runAsyncCB) - } + def setDec(dec: Declination)(implicit ctx: AppContextIO): Callback = + $.props >>= (_.target.zoom(Coordinates.declination).set(dec).runAsyncCB) val gotoRaDec = (coords: Coordinates) => aladinRef.get @@ -82,7 +77,9 @@ object AladinContainer { ) .toCallback - def searchAndGo(modify: ((String, RightAscension, Declination)) => Callback)(search: String) = + def searchAndGo( + modify: ((String, RightAscension, Declination)) => Callback + )(search: String)(implicit ctx: AppContextIO) = aladinRef.get .flatMapCB( _.backend diff --git a/explore/src/main/scala/explore/targeteditor/CataloguesForm.scala b/explore/src/main/scala/explore/targeteditor/CataloguesForm.scala index 7f776fc30e..5d4750988c 100644 --- a/explore/src/main/scala/explore/targeteditor/CataloguesForm.scala +++ b/explore/src/main/scala/explore/targeteditor/CataloguesForm.scala @@ -57,7 +57,7 @@ object CataloguesForm { ScalaComponent .builder[Props] .render { $ => - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => val optionsV = $.props.options val options = optionsV.get Form(size = Small)( diff --git a/explore/src/main/scala/explore/targeteditor/MagnitudeForm.scala b/explore/src/main/scala/explore/targeteditor/MagnitudeForm.scala index a394e07230..3af1e5ab95 100644 --- a/explore/src/main/scala/explore/targeteditor/MagnitudeForm.scala +++ b/explore/src/main/scala/explore/targeteditor/MagnitudeForm.scala @@ -74,7 +74,7 @@ object MagnitudeForm { val props = $.props val state = $.state - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => val newBandView: Option[View[MagnitudeBand]] = state.newBand.map(band => ViewF(band, diff --git a/explore/src/main/scala/explore/targeteditor/RVInput.scala b/explore/src/main/scala/explore/targeteditor/RVInput.scala index 438edcb908..8fecb61aef 100644 --- a/explore/src/main/scala/explore/targeteditor/RVInput.scala +++ b/explore/src/main/scala/explore/targeteditor/RVInput.scala @@ -93,7 +93,7 @@ object RVInput { class Backend($ : BackendScope[Props, State]) { def render(props: Props, state: State) = - AppCtx.runWithCtx { implicit ctx => + AppCtx.using { implicit ctx => val rvView = ViewF.fromState[IO]($).zoom(State.rvView) val input = state.rvView match { case RVView.Z => diff --git a/explore/src/main/scala/explore/targeteditor/SearchForm.scala b/explore/src/main/scala/explore/targeteditor/SearchForm.scala index 61b4e2aa4f..c2e5422ac9 100644 --- a/explore/src/main/scala/explore/targeteditor/SearchForm.scala +++ b/explore/src/main/scala/explore/targeteditor/SearchForm.scala @@ -66,7 +66,7 @@ object SearchForm { class Backend($ : BackendScope[Props, State]) { - def render(props: Props, state: State) = AppCtx.runWithCtx { implicit ctx => + def render(props: Props, state: State) = AppCtx.using { implicit ctx => val searchComplete: IO[Unit] = props.searching.mod(_ - props.id) val search: Callback = diff --git a/explore/src/main/scala/explore/targeteditor/TargetBody.scala b/explore/src/main/scala/explore/targeteditor/TargetBody.scala index 335b8d3b89..de48cefc27 100644 --- a/explore/src/main/scala/explore/targeteditor/TargetBody.scala +++ b/explore/src/main/scala/explore/targeteditor/TargetBody.scala @@ -72,7 +72,7 @@ object TargetBody { class Backend() { def render(props: Props) = - AppCtx.runWithCtx { implicit appCtx => + AppCtx.using { implicit appCtx => val target = props.target.get UndoRegion[TargetResult] { undoCtx => diff --git a/explore/src/main/scala/explore/targeteditor/TargetEditor.scala b/explore/src/main/scala/explore/targeteditor/TargetEditor.scala index 2cc2678e61..4f6a2ad948 100644 --- a/explore/src/main/scala/explore/targeteditor/TargetEditor.scala +++ b/explore/src/main/scala/explore/targeteditor/TargetEditor.scala @@ -25,10 +25,11 @@ import monocle.macros.Lenses import react.common._ final case class TargetEditor( - uid: User.Id, - tid: Target.Id, - searching: View[Set[Target.Id]] -) extends ReactProps[TargetEditor](TargetEditor.component) + uid: User.Id, + tid: Target.Id, + searching: View[Set[Target.Id]] +)(implicit val ctx: AppContextIO) + extends ReactProps[TargetEditor](TargetEditor.component) object TargetEditor { type Props = TargetEditor @@ -45,7 +46,7 @@ object TargetEditor { class Backend($ : BackendScope[Props, State]) { def render(props: Props) = - AppCtx.runWithCtx { implicit appCtx => + AppCtx.using { implicit appCtx => LiveQueryRenderMod[ObservationDB, TargetEditQuery.Data, Option[TargetEditQuery.Data.Target] @@ -73,14 +74,12 @@ object TargetEditor { .initialState(State(TargetVisualOptions.Default)) .renderBackend[Backend] .componentDidMount { $ => - val p = $.props - AppCtx - .runWithCtx { implicit ctx => - UserTargetPreferencesQuery - .queryWithDefault[IO](p.uid, p.tid, Constants.InitialFov) - .flatMap(v => $.modStateIn[IO](State.fovAngle.set(v))) - .runAsyncAndForgetCB - } + val p = $.props + implicit val ctx = p.ctx + UserTargetPreferencesQuery + .queryWithDefault[IO](p.uid, p.tid, Constants.InitialFov) + .flatMap(v => $.modStateIn[IO](State.fovAngle.set(v))) + .runAsyncAndForgetCB } .configure(Reusability.shouldComponentUpdate) .build diff --git a/project/Settings.scala b/project/Settings.scala index c583e62caa..e64be80bee 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -12,7 +12,7 @@ object Settings { val circe = "0.13.0" val circeGolden = "0.3.0" val clue = "0.11.3" - val crystal = "0.10.0" + val crystal = "0.11.0" val discipline = "1.1.4" val disciplineMUnit = "1.0.7" val geminiLocales = "0.5.1"