package com.weEat.modules import org.scalajs.dom.raw.Event import scala.concurrent.duration._ import scala.concurrent.{ExecutionContext,Future,Promise} import scala.scalajs.js.timers.{clearTimeout, setTimeout} import mhtml.Rx import mhtml.future.syntax._ import scala.util.Try import cats.implicits._ import mhtml.implicits.cats._ import cats.Traverse._ import scala.xml.{Node,Elem,UnprefixedAttribute} import com.weEat.modules._ import com.weEat.util.MHtmlHelpers._ case class SearchBar[T]( searchFn: (String) => Future[T], minLength: Int = 3, delay: FiniteDuration = 500 milliseconds )(implicit val ec: ExecutionContext = ExecutionContext.global) extends Module { // TODO: Refactor these out into utility classes def setTimeoutResult[T](delay: FiniteDuration)(body: => T) = { val promise = Promise[T]() setTimeout(delay) { promise.success(body) } promise.future } implicit class RxRxFlattener[T](outer: Rx[Rx[T]]) { def flatten = outer.flatMap(identity) } private val (_render, _inputTerm) = ().value() val render = _render val searchTerm = _inputTerm.map[Option[String]](Some(_)) .keepIf(_.map(_.length >= minLength).getOrElse(false))(None) .dropRepeats.impure.sharing // Nested futures give us a hellish type web to unweave val result: Rx[Option[Try[T]]] = searchTerm.flatMap({ term => term.map({ termAtStart => setTimeoutResult(delay) { searchTerm.map({ termAfterDelay => if (Some(termAtStart) == termAfterDelay) searchFn(termAtStart).toRx else Rx(None) }).flatten }.toRx.map( _.map( _.sequence[Rx, Option[Try[T]]].map( _.flatSequence[Option, T] ) ) ).map(_.flatSequence[Rx, Try[T]]).flatten }).flatSequence[Rx, Try[T]] }).impure.sharing }