Browse Source

Added ability to fork a recipe history.

Thomas Flucke 2 years ago
parent
commit
a537c0f663

+ 30 - 21
webClient/src/main/scala/com/weEat/views/FoodSearch.scala

@@ -54,36 +54,45 @@ object FoodSearch extends View[Option[String]] {
           View.router.pushState(ViewPage(q = str))
       },
       PaginatedTable[FoodNodeId](Seq(
-        ("", 2, { (x) => div(
-          button(cls := "btn btn-light",
-            onClick --> { (_) =>
-              View.router.pushState(RecipeView.ViewPage(x._id))
+        ("", 2, { (food) =>
+          div(
+            Icon.view { (_) =>
+              View.router.pushState(RecipeView.ViewPage(food._id))
             },
-            "View"
-          ),
-          children <-- Signal.fromFuture(x.user).map({ (userOpt) =>
-            userOpt.map { (user) =>
+            children <-- Signal.fromFuture(food.user).optionMap({ (user) =>
               OAuthSignal.username.valueIs(Some(user.email))
-            }
-          }).shiftOption.optionMap({
-            case true => button(cls := "btn btn-light",
-              x.nodeType match {
-                case FoodNodeId.NodeType.RECIPE => onClick --> { (_) =>
-                  View.router.pushState(RecipeEdit.ViewPage(Some(x._id)))
+            }).shiftOption.optionMap({
+              case true => food.nodeType match {
+                case FoodNodeId.NodeType.RECIPE => Icon.edit { (_) =>
+                  View.router.pushState(RecipeEdit.ViewPage(Some(food._id)))
                 }
-                case FoodNodeId.NodeType.USDA => onClick --> {(_) =>
-                  val editor = USDAEditor(x.asInstanceOf[USDANode], false)
+                case FoodNodeId.NodeType.USDA => Icon.edit { (_) =>
+                  val editor = USDAEditor(food.asInstanceOf[USDANode], false)
                   Overlay.confirm(editor.render) { () =>
                     import com.weEat.Main.headers
-                    FoodController.update(x._id)(editor.getUSDANode())
+                    FoodController.update(food._id)(editor.getUSDANode())
+                  }
+                }
+              }
+              case false => Icon.placeholder
+            }).map(_.toSeq),
+            child <-- OAuthSignal.username.isDefined.switch(
+              food.nodeType match {
+                case FoodNodeId.NodeType.RECIPE => Icon.fork { (_) =>
+                  View.router.pushState(RecipeEdit.ViewPage(Some(food._id), true))
+                }
+                case FoodNodeId.NodeType.USDA => Icon.fork { (_) =>
+                  val editor = USDAEditor(food.asInstanceOf[USDANode], false)
+                  Overlay.confirm(editor.render) { () =>
+                    import com.weEat.Main.headers
+                    FoodController.add()(editor.getUSDANode())
                   }
                 }
               },
-              "Edit"
+              Icon.placeholder
             )
-            case false => span()
-          }).map(_.toSeq)
-        ) }),
+          )
+        }),
         ("Name", 3, { (x) => span(x.name)}),
         ("Type", 3, { (x) => span(x.nodeType.toString)}),
         ("User", 2, { (x) => span(futureChild <-- x.user.map { _.email })}),

+ 52 - 23
webClient/src/main/scala/com/weEat/views/RecipeEdit.scala

@@ -14,7 +14,7 @@ import scala.util.{Failure,Success}
 
 // TODO: prevent user from not having any of discreet/mass/volume input 
 // TODO: Save recipe node in cookie until ready to use
-object RecipeEdit extends View[Option[String]] {
+object RecipeEdit extends View[(Option[String], Option[Boolean])] {
   import com.weEat.Main.headers
 
   implicit val ec = com.weEat.shared.ctx
@@ -22,15 +22,21 @@ object RecipeEdit extends View[Option[String]] {
   val navName = Some("New Recipe")
   val tag = "editRecipe"
 
-  case class ViewPage(val id: Option[String] = None) extends P {
+  case class ViewPage(id: Option[String] = None, fork: Boolean = false) extends P {
     val title = "Edit Recipe"
     def jsonValue = Json.toJson(id)
   }
   def parseJson(jsVal: JsValue) = ViewPage(jsVal.asOpt[String])
   def route = Route.onlyQuery(
-    encode = (page: ViewPage) => page.id,
-    decode = (id: Option[String]) => ViewPage(id = id),
-    pattern = (root / tag / endOfSegments) ? param[String]("id").?
+    encode = (page: ViewPage) => (page.id, Option.when(page.fork)(true)),
+    decode = {
+      case (id: Option[String], fork: Option[Boolean]) => ViewPage(
+        id,
+        fork.getOrElse(false)
+      )
+    },
+    pattern = (root / tag / endOfSegments) ?
+      param[String]("id").? & param[Boolean]("fork").?
   )
   def defaultPage = ViewPage()
 
@@ -44,6 +50,7 @@ object RecipeEdit extends View[Option[String]] {
         FoodController.get(id)().map(_.asInstanceOf[RecipeNode])
       ))
     ).shiftOption.map(_.flatten)
+    val fork: Signal[Boolean] = page.map(_.fork)
     val recipeVar = refRecipe.map(RecipeVar(_))
     val recipe = recipeVar.flatMap(_.newInstance)
 
@@ -60,24 +67,46 @@ object RecipeEdit extends View[Option[String]] {
             )
           )
         ),
-        child <-- refRecipe.map({
-          case Some(RecipeNodeId(id, uid, _, _, _, _, _, _, _, _, _, _, _)) =>
-            button(cls := "btn",
-              onMountBind { (ctx) =>
-                onClick --> { (e: Event) =>
-                  implicit val owner = ctx.owner
-                  FoodController.update(id, Some(uid))(recipe.observe.now().get)
-                    .onComplete {
-                      case Success(recipe) =>
-                        View.router.pushState(RecipeView.ViewPage(recipe._id))
-                      case Failure(ex) =>
-                        println("Could not update recipe")
-                        throw ex
-                    }
+        child <-- refRecipe.combineWithFn(fork) {
+          case (
+            Some(RecipeNodeId(id, uid, _, _, _, _, _, _, _, _, _, _, _)),
+            false
+          ) => button(cls := "btn",
+            onMountBind { (ctx) =>
+              onClick --> { (e: Event) =>
+                implicit val owner = ctx.owner
+                FoodController.update(id, Some(uid))(recipe.observe.now().get)
+                  .onComplete {
+                    case Success(recipe) =>
+                      View.router.pushState(RecipeView.ViewPage(recipe._id))
+                    case Failure(ex) =>
+                      println("Could not update recipe")
+                      throw ex
+                  }
+              }
+            },
+            "Update"
+          )
+          case (
+            Some(RecipeNodeId(_, _, vid, _, _, _, _, _, _, _, _, _, _)),
+            true
+          ) => button(cls := "btn",
+            onMountBind { (ctx) =>
+              onClick --> { (e: Event) =>
+                implicit val owner = ctx.owner
+                FoodController.add()(recipe.observe.now().get.copy(
+                  prevVersionVid = Some(vid)
+                )).onComplete {
+                  case Success(recipe) =>
+                    View.router.pushState(RecipeView.ViewPage(recipe._id))
+                  case Failure(ex) =>
+                    println("Could not add recipe")
+                    throw ex
                 }
-              },
-              "Update"
-            )
+              }
+            },
+            "Add"
+          )
           case _ => button(cls := "btn",
             onMountBind { (ctx) =>
               onClick --> { (e: Event) =>
@@ -93,7 +122,7 @@ object RecipeEdit extends View[Option[String]] {
             },
             "Add"
           )
-        })
+        }
       )
     )
   }

+ 8 - 3
webClient/src/main/scala/com/weEat/views/View.scala

@@ -61,9 +61,12 @@ object View {
         case obj: JsObject =>
           _authedIndex(OAuthManager.currentScope)
             .find(_.tag == (obj \ "t").get.as[String])
-            .getOrElse(throw new IllegalArgumentException((obj \ "t").get.as[String]))
+            .getOrElse(throw new IllegalArgumentException(
+              (obj \ "t").get.as[String]
+            ))
             .parseJson(obj.value("v"))
-        case x => println(x); throw new IllegalStateException("x is not a JsObject")
+        case x => println(x);
+          throw new IllegalStateException("x is not a JsObject")
       }
     }
   )(
@@ -76,7 +79,9 @@ object View {
     .collectSignal[FoodSearch.ViewPage] { (page) => FoodSearch.content(page) }
     .collectSignal[RecipeEdit.ViewPage] { (page) => RecipeEdit.content(page) }
     .collectSignal[RecipeView.ViewPage] { (page) => RecipeView.content(page) }
-    .collectSignal[RecipeImporter.ViewPage] { (page) => RecipeImporter.content(page) }
+    .collectSignal[RecipeImporter.ViewPage] { (page) =>
+      RecipeImporter.content(page)
+    }
 
 }