From ee527655b87ee53e300e282627740640ed8525e9 Mon Sep 17 00:00:00 2001 From: Derek Gonyeo Date: Wed, 28 Feb 2018 17:41:26 -0800 Subject: [PATCH] more things --- cyanide.cabal | 1 + src/Cyanide/Data/Recipes.hs | 2 +- src/Cyanide/UI/App.hs | 6 ++ src/Cyanide/UI/ErrorScreen.hs | 40 ++++++++++++++ .../UI/IngredientClassDeletionScreen.hs | 2 +- src/Cyanide/UI/IngredientClassInputScreen.hs | 1 + src/Cyanide/UI/IngredientDetailScreen.hs | 55 ++++++++++++++++--- src/Cyanide/UI/IngredientInputScreen.hs | 11 ++-- src/Cyanide/UI/IngredientSelectionScreen.hs | 23 +------- src/Cyanide/UI/MainSelectionScreen.hs | 2 +- src/Cyanide/UI/PurchaseCreationScreen.hs | 5 +- src/Cyanide/UI/RecipeDeletionScreen.hs | 13 +++-- src/Cyanide/UI/RecipeDetailScreen.hs | 6 +- src/Cyanide/UI/RecipeInputIngredientScreen.hs | 4 +- src/Cyanide/UI/RecipeSelectionScreen.hs | 25 ++++++++- src/Cyanide/UI/State.hs | 10 +++- src/Cyanide/UI/Util.hs | 1 + 17 files changed, 155 insertions(+), 52 deletions(-) create mode 100644 src/Cyanide/UI/ErrorScreen.hs diff --git a/cyanide.cabal b/cyanide.cabal index 4a11c45..a1ff676 100644 --- a/cyanide.cabal +++ b/cyanide.cabal @@ -44,6 +44,7 @@ executable cyanide , Cyanide.UI.PurchaseCreationScreen , Cyanide.UI.Util , Cyanide.UI.MainSelectionScreen + , Cyanide.UI.ErrorScreen , Cyanide.UI.IngredientClassSelectionScreen , Cyanide.UI.IngredientClassDeletionScreen , Cyanide.UI.IngredientClassInputScreen diff --git a/src/Cyanide/Data/Recipes.hs b/src/Cyanide/Data/Recipes.hs index c56d726..1e41d6d 100644 --- a/src/Cyanide/Data/Recipes.hs +++ b/src/Cyanide/Data/Recipes.hs @@ -140,7 +140,7 @@ getRecipesToGlasses conn = do getRecipesIngredientAvailability :: DBConn -> IO [(Int,Bool)] getRecipesIngredientAvailability conn = do P.query_ conn - "SELECT query1.id, query1.avail AND query2.avail FROM (SELECT query1_1.id, coalesce(bool_and(avail),FALSE) AS avail FROM (SELECT recipes.id, ingredient_classes.id as icid, coalesce(bool_or(ingredients.available), FALSE) AS avail FROM recipes INNER JOIN ingredients_to_recipes ON recipe_id = recipes.id INNER JOIN ingredient_classes ON ingredient_class_id = ingredient_classes.id LEFT OUTER JOIN ingredients ON ingredients.class = ingredient_classes.id GROUP BY (ingredient_classes.id,recipes.id)) AS query1_1 GROUP BY query1_1.id) AS query1 INNER JOIN (SELECT recipes.id, bool_and(ingredients.available) as avail FROM recipes INNER JOIN ingredients_to_recipes ON ingredients_to_recipes.recipe_id = recipes.id INNER JOIN ingredients ON ingredient_id = ingredients.id GROUP BY recipes.id) AS query2 ON query1.id = query2.id order by query1.id" + "SELECT query1.id, query1.avail AND query2.avail FROM (SELECT query1_1.id, coalesce(bool_and(avail),FALSE) AS avail FROM (SELECT recipes.id, ingredient_classes.id as icid, coalesce(bool_or(ingredients.available), FALSE) AS avail FROM recipes INNER JOIN ingredients_to_recipes ON recipe_id = recipes.id INNER JOIN ingredient_classes ON ingredient_class_id = ingredient_classes.id LEFT OUTER JOIN ingredients ON ingredients.class = ingredient_classes.id WHERE (ingredients.notForRecipes = FALSE OR ingredients.notForRecipes IS NULL) GROUP BY (ingredient_classes.id,recipes.id)) AS query1_1 GROUP BY query1_1.id) AS query1 INNER JOIN (SELECT recipes.id, bool_and(ingredients.available) as avail FROM recipes INNER JOIN ingredients_to_recipes ON ingredients_to_recipes.recipe_id = recipes.id INNER JOIN ingredients ON ingredient_id = ingredients.id WHERE ingredients.notForRecipes = FALSE GROUP BY recipes.id) AS query2 ON query1.id = query2.id order by query1.id" newRecipe :: DBConn -> (Maybe T.Text,T.Text,T.Text,Maybe Glass,Maybe Ingredient,[IngredientListItem]) -> IO Recipe newRecipe conn (mName,garnish,instr,mGlass,mIngr,ingredients) = do diff --git a/src/Cyanide/UI/App.hs b/src/Cyanide/UI/App.hs index d5658ce..8be93e0 100644 --- a/src/Cyanide/UI/App.hs +++ b/src/Cyanide/UI/App.hs @@ -21,6 +21,7 @@ import qualified Cyanide.UI.RecipeInputScreen as RecipeInputScreen import qualified Cyanide.UI.RecipeInputIngredientScreen as RecipeInputIngredientScreen import qualified Cyanide.UI.RecipeDeletionScreen as RecipeDeletionScreen import qualified Cyanide.UI.MainSelectionScreen as MainSelectionScreen +import qualified Cyanide.UI.ErrorScreen as ErrorScreen import qualified Cyanide.UI.GlassSelectionScreen as GlassSelectionScreen import qualified Cyanide.UI.GlassDeletionScreen as GlassDeletionScreen import qualified Cyanide.UI.GlassInputScreen as GlassInputScreen @@ -54,6 +55,7 @@ attrMap = B.attrMap Vty.defAttr , (BL.listSelectedFocusedAttr, Vty.black `B.on` Vty.white) ] ++ MainSelectionScreen.attrMap + ++ ErrorScreen.attrMap ++ RecipeDetailScreen.attrMap ++ RecipeInputScreen.attrMap ++ RecipeInputIngredientScreen.attrMap @@ -79,6 +81,8 @@ attrMap = B.attrMap Vty.defAttr handleEvent :: CyanideState -> B.BrickEvent Name () -> B.EventM Name (B.Next CyanideState) handleEvent s@(CyanideState _ _ MainSelectionScreen) e = MainSelectionScreen.handleEvent s e +handleEvent s@(CyanideState _ _ (ErrorScreen _ _)) e = + ErrorScreen.handleEvent s e handleEvent s@(CyanideState _ _ (GlassSelectionScreen _)) e = GlassSelectionScreen.handleEvent s e handleEvent s@(CyanideState _ _ (GlassDeletionScreen _)) e = @@ -121,6 +125,8 @@ handleEvent s@(CyanideState _ _ (IngredientClassInputScreen _ _ _)) e = drawUI :: CyanideState -> [B.Widget Name] drawUI s@(CyanideState _ _ MainSelectionScreen) = MainSelectionScreen.drawUI s +drawUI s@(CyanideState _ _ (ErrorScreen _ _)) = + ErrorScreen.drawUI s drawUI s@(CyanideState _ _ (GlassSelectionScreen _)) = GlassSelectionScreen.drawUI s drawUI s@(CyanideState _ _ (GlassDeletionScreen _)) = diff --git a/src/Cyanide/UI/ErrorScreen.hs b/src/Cyanide/UI/ErrorScreen.hs new file mode 100644 index 0000000..e729843 --- /dev/null +++ b/src/Cyanide/UI/ErrorScreen.hs @@ -0,0 +1,40 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Cyanide.UI.ErrorScreen where + +import Lens.Micro ((^.)) +import qualified Brick as B +import qualified Brick.Widgets.List as BL +import qualified Graphics.Vty as Vty +import qualified Data.Text as T +import qualified Data.Vector as V +import qualified Brick.Widgets.Center as BC +import qualified Brick.Widgets.Border as BB +import Data.Monoid +import Control.Monad.IO.Class + +import Cyanide.UI.State +import Cyanide.UI.Util + +attrMap :: [(B.AttrName, Vty.Attr)] +attrMap = [] + +handleEvent :: CyanideState -> B.BrickEvent Name () -> B.EventM Name (B.Next CyanideState) +handleEvent s@(CyanideState conn _ (ErrorScreen _ prev)) (B.VtyEvent e) = + case e of + Vty.EvKey (Vty.KEsc) [] -> + B.continue $ s { stateScreen = prev } + + _ -> B.continue s +handleEvent s _ = B.continue s + +drawUI :: CyanideState -> [B.Widget Name] +drawUI (CyanideState conn _ (ErrorScreen msg _)) = [ui] + where ui = BC.center + $ B.hLimit 80 + $ B.vLimit 25 $ B.vBox + [ BC.hCenter $ B.txtWrap msg + , B.txt " " + , renderInstructions [ ("Esc","Previous screen") + ] + ] diff --git a/src/Cyanide/UI/IngredientClassDeletionScreen.hs b/src/Cyanide/UI/IngredientClassDeletionScreen.hs index 619ea62..495ecee 100644 --- a/src/Cyanide/UI/IngredientClassDeletionScreen.hs +++ b/src/Cyanide/UI/IngredientClassDeletionScreen.hs @@ -46,7 +46,7 @@ drawUI (CyanideState conn _ (IngredientClassDeletionScreen l)) = [ui] ui = BC.center $ B.hLimit 80 $ B.vLimit 25 $ B.vBox - [ BC.hCenter $ B.txt $ "Are you sure you want to delete the following glass?" + [ BC.hCenter $ B.txt $ "Are you sure you want to delete the following ingredient class?" , BC.hCenter $ B.padAll 1 $ B.txt n , renderInstructions [ ("y","Yes") , ("n","No") diff --git a/src/Cyanide/UI/IngredientClassInputScreen.hs b/src/Cyanide/UI/IngredientClassInputScreen.hs index 614d814..534ef96 100644 --- a/src/Cyanide/UI/IngredientClassInputScreen.hs +++ b/src/Cyanide/UI/IngredientClassInputScreen.hs @@ -45,6 +45,7 @@ handleEvent s@(CyanideState conn _ (IngredientClassInputScreen ed mic l)) (B.Vty newIngredientClass <- liftIO $ IngredientClasses.newIngredientClass conn newIngredientClassName let newList = BL.listInsert (length l) newIngredientClass l B.continue $ s { stateScreen = (IngredientClassSelectionScreen newList) } + _ -> B.continue s ev -> do newEdit <- BE.handleEditorEvent e ed diff --git a/src/Cyanide/UI/IngredientDetailScreen.hs b/src/Cyanide/UI/IngredientDetailScreen.hs index 1c2dba9..b1614a1 100644 --- a/src/Cyanide/UI/IngredientDetailScreen.hs +++ b/src/Cyanide/UI/IngredientDetailScreen.hs @@ -15,6 +15,7 @@ import qualified Brick.Focus as BF import Data.Monoid import Control.Monad.IO.Class import Data.Maybe +import qualified Data.List as L import Cyanide.UI.State import qualified Cyanide.UI.RecipeInputScreen as RecipeInput @@ -22,6 +23,7 @@ import qualified Cyanide.UI.IngredientInputScreen as IngredientInput import qualified Cyanide.Data.IngredientClasses as IngredientClasses import qualified Cyanide.Data.Types as Types import qualified Cyanide.Data.Ingredients as Ingredients +import qualified Cyanide.Data.Purchases as Purchases import qualified Cyanide.Data.Recipes as Recipes import qualified Cyanide.Data.Postgres as Postgres import qualified Cyanide.Data.Units as Units @@ -37,6 +39,34 @@ purchasesListName = "IngredientDetailPurchases" recipesListName :: Name recipesListName = "IngredientDetailRecipes" +newIngredientDetailScreen :: Postgres.DBConn -> Types.Ingredient -> (Maybe Types.Ingredient -> CyanideScreen) -> IO CyanideScreen +newIngredientDetailScreen conn ingr prev = do + purchases <- Purchases.getPurchasesForIngredient conn ingr + recipes <- getRecipesUsedIn conn ingr + recipeForIngr <- Recipes.getRecipeForIngredient conn ingr + let f = BF.focusRing $ [purchasesListName] + ++ if Types.notForRecipes ingr + then [] + else [recipesListName] + purchasesList = BL.list purchasesListName (V.fromList purchases) 1 + usedInList = BL.list recipesListName (V.fromList recipes) 1 + return $ IngredientDetailScreen ingr purchasesList usedInList recipeForIngr f prev + +getRecipesUsedIn :: Postgres.DBConn -> Types.Ingredient -> IO [Types.Recipe] +getRecipesUsedIn conn ingr = do + recipes1 <- liftIO $ Recipes.getRecipesUsingIngredient conn ingr + recipes2 <- case Types.ingredientClass ingr of + Just ic -> do + rlst <- liftIO $ Recipes.getRecipesUsingIngredientClass conn ic + return rlst + Nothing -> return [] + return $ L.sortBy (\r1 r2 -> compare (getRecipeName r1) (getRecipeName r2)) + $ recipes1 ++ recipes2 + where getRecipeName :: Types.Recipe -> T.Text + getRecipeName r = case Types.recipeName r of + Left t -> t + Right i -> "recipe for " `T.append` Types.ingredientName i + handleEvent :: CyanideState -> B.BrickEvent Name () -> B.EventM Name (B.Next CyanideState) handleEvent s@(CyanideState conn _ scr@(IngredientDetailScreen i ps rs mr f prev)) (B.VtyEvent e) = case e of @@ -76,14 +106,20 @@ handleEvent s@(CyanideState conn _ scr@(IngredientDetailScreen i ps rs mr f prev in case matches of [(i,_)] -> i _ -> 0 - goBack Nothing = scr - goBack (Just (ingr,_)) = scr { ingredient = ingr } + goBack Nothing = return $ scr + goBack (Just (ingr,_)) = do + recipes <- liftIO $ getRecipesUsedIn conn ingr + let newIndex = BL.listSelectedElement rs >>= (Just . fst) + newList = BL.listReplace (V.fromList recipes) newIndex rs + return $ scr { ingredient = ingr + , ingredientUsedIn = newList + } Vty.EvKey (Vty.KChar 'r') [] -> case mr of Just r -> do ingrs <- liftIO $ Recipes.getIngredientsForRecipe conn r - B.continue $ s { stateScreen = RecipeDetailScreen r Nothing ingrs goBack } + B.continue $ s { stateScreen = RecipeDetailScreen r Nothing ingrs (return . goBack) } Nothing -> do newScr <- liftIO $ RecipeInput.newRecipeInputScreen conn Nothing [] (Just i) Nothing (return . goBack) B.continue $ s { stateScreen = newScr } @@ -98,10 +134,11 @@ handleEvent s@(CyanideState conn _ scr@(IngredientDetailScreen i ps rs mr f prev B.continue $ s { stateScreen = RecipeDetailScreen r glass ingrs (goBack j) } (_,_) -> B.continue s where goBack j Nothing = let newList = BL.listRemove j rs - in scr { ingredientUsedIn = newList } - goBack _ (Just (r,_,_)) = let newList = BL.listModify (\_ -> r) rs - in scr { ingredientUsedIn = newList } - + in return $ scr { ingredientUsedIn = newList } + goBack j (Just _) = do + recipes <- liftIO $ getRecipesUsedIn conn i + let newList = BL.listReplace (V.fromList recipes) (Just j) rs + return $ scr { ingredientUsedIn = newList } Vty.EvKey (Vty.KChar 'd') [] -> if BF.focusGetCurrent f == Just purchasesListName then case BL.listSelectedElement ps of @@ -156,8 +193,8 @@ drawUI (CyanideState conn _ (IngredientDetailScreen ing pl rl mr f prev)) = [ui] ] , B.padLeft (B.Pad 2) $ B.vBox [ if isJust mr - then B.txt " r - View recipe" - else B.txt " r - Create recipe" + then B.txt "r - View recipe" + else B.txt "r - Create recipe" , B.txt "a - Toggle availability" , B.txt "e - Edit ingredient" ] diff --git a/src/Cyanide/UI/IngredientInputScreen.hs b/src/Cyanide/UI/IngredientInputScreen.hs index 46a2515..0138124 100644 --- a/src/Cyanide/UI/IngredientInputScreen.hs +++ b/src/Cyanide/UI/IngredientInputScreen.hs @@ -34,8 +34,9 @@ attrMap = [] handleEvent :: CyanideState -> B.BrickEvent Name () -> B.EventM Name (B.Next CyanideState) handleEvent s@(CyanideState conn _ scr@(IngredientInputScreen ed cl f si mi prev)) (B.VtyEvent e) = case e of - Vty.EvKey (Vty.KEsc) [] -> - B.continue $ s { stateScreen = prev Nothing } + Vty.EvKey (Vty.KEsc) [] -> do + newScr <- liftIO $ prev Nothing + B.continue $ s { stateScreen = newScr } Vty.EvKey (Vty.KChar '\t') [] -> let newFocus = BF.focusNext f @@ -53,13 +54,15 @@ handleEvent s@(CyanideState conn _ scr@(IngredientInputScreen ed cl f si mi prev let Just (_,iclass) = BL.listSelectedElement cl newIngredient <- liftIO $ Ingredients.updateIngredient conn (Types.ingredientId oldIng) (n,iclass,si) - B.continue $ s { stateScreen = prev (Just (newIngredient,iclass)) } + newScr <- liftIO $ prev (Just (newIngredient,iclass)) + B.continue $ s { stateScreen = newScr } -- We're creating a new ingredient (Nothing,Just n) -> do let Just (_,iclass) = BL.listSelectedElement cl newIngredient <- liftIO $ Ingredients.newIngredient conn (n,iclass,si) - B.continue $ s { stateScreen = prev (Just (newIngredient,iclass)) } + newScr <- liftIO $ prev (Just (newIngredient,iclass)) + B.continue $ s { stateScreen = newScr } ev -> if BF.focusGetCurrent (f) == Just editorName then do newEdit <- BE.handleEditorEvent ev ed diff --git a/src/Cyanide/UI/IngredientSelectionScreen.hs b/src/Cyanide/UI/IngredientSelectionScreen.hs index 8ff1ac9..1184ba7 100644 --- a/src/Cyanide/UI/IngredientSelectionScreen.hs +++ b/src/Cyanide/UI/IngredientSelectionScreen.hs @@ -64,25 +64,8 @@ handleEvent s@(CyanideState conn _ scr@(IngredientSelectionScreen l orig se f)) Vty.EvKey Vty.KEnter [] -> if BF.focusGetCurrent (f) == Just ingredientsName then do let Just (j,ingr) = BL.listSelectedElement l - purchases <- liftIO $ Purchases.getPurchasesForIngredient conn ingr - recipes1 <- liftIO $ Recipes.getRecipesUsingIngredient conn ingr - recipes2 <- case Types.ingredientClass ingr of - Just ic -> do - rlst <- liftIO $ Recipes.getRecipesUsingIngredientClass conn ic - return rlst - Nothing -> return [] - recipeForIngr <- liftIO $ Recipes.getRecipeForIngredient conn ingr - let f = BF.focusRing $ [IngredientDetail.purchasesListName] - ++ if Types.notForRecipes ingr - then [] - else [IngredientDetail.recipesListName] - B.continue $ s { stateScreen = IngredientDetailScreen - ingr - (BL.list IngredientDetail.purchasesListName (V.fromList purchases) 1) - (BL.list IngredientDetail.recipesListName (V.fromList (recipes1++recipes2)) 1) - recipeForIngr - f - (goBack j) } + newScr <- liftIO $ IngredientDetail.newIngredientDetailScreen conn ingr (goBack j) + B.continue $ s { stateScreen = newScr } else if BF.focusGetCurrent (f) == Just searchName then let newFocus = BF.focusNext f in B.continue $ s { stateScreen = scr { ingredientListFocusRing = newFocus } } @@ -121,7 +104,7 @@ handleEvent s@(CyanideState conn _ scr@(IngredientSelectionScreen l orig se f)) f = BF.focusRing [ IngredientInput.editorName , IngredientInput.classesName ] - B.continue $ s { stateScreen = (IngredientInputScreen ed iclist f False Nothing goBack) } + B.continue $ s { stateScreen = IngredientInputScreen ed iclist f False Nothing (return . goBack) } where goBack Nothing = scr goBack (Just (i,_)) = let newList = BL.listInsert (length l) i l diff --git a/src/Cyanide/UI/MainSelectionScreen.hs b/src/Cyanide/UI/MainSelectionScreen.hs index 69d4063..fd6cb6c 100644 --- a/src/Cyanide/UI/MainSelectionScreen.hs +++ b/src/Cyanide/UI/MainSelectionScreen.hs @@ -76,7 +76,7 @@ drawUI (CyanideState conn _ MainSelectionScreen) = $ B.vLimit 25 $ B.hBox [ bottle1 , B.txt " " - , B.hLimit 34 + , B.hLimit 35 $ B.vBox [ BC.hCenter $ B.txt "Cyanide: home bar management system" , BC.hCenter $ BB.hBorder , renderInstructions [ ("r","Recipes") diff --git a/src/Cyanide/UI/PurchaseCreationScreen.hs b/src/Cyanide/UI/PurchaseCreationScreen.hs index 3da6604..bc70c98 100644 --- a/src/Cyanide/UI/PurchaseCreationScreen.hs +++ b/src/Cyanide/UI/PurchaseCreationScreen.hs @@ -56,7 +56,7 @@ handleEvent s@(CyanideState conn _ scr@(PurchaseCreationScreen ing le ce ae ue f case (readMaybe (T.unpack $ c),readMaybe (T.unpack a)) of (Just cn,Just an) -> if cn < 0 || an < 0 || cn > 999999 || an > 999999 - then B.continue s + then B.continue $ s { stateScreen = ErrorScreen "The cost and amount must be between 0 and 999999." scr } else do -- mark the ingredient as available liftIO $ Ingredients.updateIngredientAvailability conn (ing,True) @@ -67,7 +67,8 @@ handleEvent s@(CyanideState conn _ scr@(PurchaseCreationScreen ing le ce ae ue f let today = utctDay now liftIO $ Purchases.newPurchase conn (ing,today,l,cn,an,u) B.continue $ s { stateScreen = prev (Just (newIngredient,Types.Purchase today l cn an u)) } - _ -> B.continue s + (Nothing,_) -> B.continue $ s { stateScreen = ErrorScreen "Couldn't parse the cost. Please enter the cost in cents, so $12.95 would become 1295." scr } + (_,Nothing) -> B.continue $ s { stateScreen = ErrorScreen "Couldn't parse the amount. Please enter an integer." scr } _ -> B.continue s ev -> if BF.focusGetCurrent (fe) == Just locationEditorName then do diff --git a/src/Cyanide/UI/RecipeDeletionScreen.hs b/src/Cyanide/UI/RecipeDeletionScreen.hs index 3e72014..24b881e 100644 --- a/src/Cyanide/UI/RecipeDeletionScreen.hs +++ b/src/Cyanide/UI/RecipeDeletionScreen.hs @@ -26,15 +26,18 @@ attrMap = [] handleEvent :: CyanideState -> B.BrickEvent Name () -> B.EventM Name (B.Next CyanideState) handleEvent s@(CyanideState conn _ (RecipeDeletionScreen r prev)) (B.VtyEvent e) = case e of - Vty.EvKey (Vty.KEsc) [] -> - B.continue $ s { stateScreen = prev False } + Vty.EvKey (Vty.KEsc) [] -> do + newScr <- liftIO $ prev False + B.continue $ s { stateScreen = newScr } - Vty.EvKey (Vty.KChar 'n') [] -> - B.continue $ s { stateScreen = prev False } + Vty.EvKey (Vty.KChar 'n') [] -> do + newScr <- liftIO $ prev False + B.continue $ s { stateScreen = newScr } Vty.EvKey (Vty.KChar 'y') [] -> do + newScr <- liftIO $ prev True liftIO $ Recipes.deleteRecipe conn r - B.continue $ s { stateScreen = prev True } + B.continue $ s { stateScreen = newScr } _ -> B.continue s handleEvent s _ = B.continue s diff --git a/src/Cyanide/UI/RecipeDetailScreen.hs b/src/Cyanide/UI/RecipeDetailScreen.hs index e8b0f7d..b95b202 100644 --- a/src/Cyanide/UI/RecipeDetailScreen.hs +++ b/src/Cyanide/UI/RecipeDetailScreen.hs @@ -40,9 +40,11 @@ handleEvent s@(CyanideState conn _ scr@(RecipeDetailScreen r g is prev)) (B.VtyE Vty.EvKey (Vty.KChar 'd') [Vty.MMeta] -> do B.continue $ s { stateScreen = RecipeDeletionScreen r goBack } where goBack True = prev Nothing - goBack False = scr + goBack False = return scr - Vty.EvKey (Vty.KEsc) [] -> B.continue $ s { stateScreen = (prev $ Just (r,g,is)) } + Vty.EvKey (Vty.KEsc) [] -> do + newScr <- liftIO $ prev $ Just (r,g,is) + B.continue $ s { stateScreen = newScr } ev -> B.continue s handleEvent s _ = B.continue s diff --git a/src/Cyanide/UI/RecipeInputIngredientScreen.hs b/src/Cyanide/UI/RecipeInputIngredientScreen.hs index 06fa052..37b72c6 100644 --- a/src/Cyanide/UI/RecipeInputIngredientScreen.hs +++ b/src/Cyanide/UI/RecipeInputIngredientScreen.hs @@ -53,12 +53,12 @@ handleEvent s@(CyanideState conn _ scr@(RecipeInputIngredientScreen rname amount Vty.EvKey Vty.KEnter [] -> do case parseAmount $ fromJust $ getEditorLine amountEd of - Nothing -> B.continue s + Nothing -> B.continue $ s { stateScreen = ErrorScreen "Couldn't parse amount, here's some valid examples: \"1\", \"1/2\", \"2 1/4\"." scr } Just (amtNum,amtDen) -> do let unitInput = unitAliases $ fromJust $ getEditorLine unitEd case BL.listSelectedElement ingrList of - Nothing -> B.continue s + Nothing -> B.continue $ s { stateScreen = ErrorScreen "Ingredient filter doesn't match anything." scr } Just (_,i) -> do ingrItem <- liftIO $ makeIngrItem conn amtNum amtDen unitInput i let newScr = goBack (Just ingrItem) diff --git a/src/Cyanide/UI/RecipeSelectionScreen.hs b/src/Cyanide/UI/RecipeSelectionScreen.hs index 4d421d9..2358c1b 100644 --- a/src/Cyanide/UI/RecipeSelectionScreen.hs +++ b/src/Cyanide/UI/RecipeSelectionScreen.hs @@ -109,9 +109,30 @@ handleEvent s@(CyanideState conn _ scr@(RecipeSelectionScreen l orig toICs toGs in B.continue $ s { stateScreen = scr { recipeListFocusRing = newFocus } } else B.continue s where goBack j Nothing = let newList = BL.listRemove j l - in scr { recipeList = newList } + Just (_,rec) = BL.listSelectedElement l + newOrig = removeRec orig rec + in return $ scr { recipeList = newList + , recipeListOrig = newOrig + } goBack _ (Just (r,_,_)) = let newList = BL.listModify (\_ -> r) l - in scr { recipeList = newList } + newOrig = replaceRec orig r + in return $ scr { recipeList = newList + , recipeListOrig = newOrig + } + + replaceRec :: [Types.Recipe] -> Types.Recipe -> [Types.Recipe] + replaceRec [] _ = [] + replaceRec (rec1:t) rec2 = + if Types.recipeId rec1 == Types.recipeId rec2 + then rec2 : t + else rec1 : replaceRec t rec2 + + removeRec :: [Types.Recipe] -> Types.Recipe -> [Types.Recipe] + removeRec [] _ = [] + removeRec (rec1:t) rec2 = + if Types.recipeId rec1 == Types.recipeId rec2 + then t + else rec1 : replaceRec t rec2 ev -> if BF.focusGetCurrent (f) == Just recipesName then do newList <- BL.handleListEventVi BL.handleListEvent ev l diff --git a/src/Cyanide/UI/State.hs b/src/Cyanide/UI/State.hs index 13c8c20..97e09c2 100644 --- a/src/Cyanide/UI/State.hs +++ b/src/Cyanide/UI/State.hs @@ -22,6 +22,10 @@ data CyanideState = CyanideState data CyanideScreen = MainSelectionScreen + | ErrorScreen + { errorMessage :: T.Text + , errorPreviousScreen :: CyanideScreen + } | GlassSelectionScreen { glassUIList :: BL.List Name Types.Glass } @@ -45,7 +49,7 @@ data CyanideScreen , ingredientInputFocusRing :: BF.FocusRing Name , ingredientInputNotForRecipes :: Bool , ingredientInputBeingModified :: Maybe Types.Ingredient - , ingredientInputPreviousScreen :: Maybe (Types.Ingredient,Maybe Types.IngredientClass) -> CyanideScreen + , ingredientInputPreviousScreen :: Maybe (Types.Ingredient,Maybe Types.IngredientClass) -> IO CyanideScreen } | IngredientDetailScreen { ingredient :: Types.Ingredient @@ -94,11 +98,11 @@ data CyanideScreen { recipeInstructions :: Types.Recipe , recipeGlass :: Maybe Types.Glass , recipeIngredientList :: [Types.IngredientListItem] - , recipePreviousScreen :: Maybe (Types.Recipe,Maybe Types.Glass,[Types.IngredientListItem]) -> CyanideScreen + , recipePreviousScreen :: Maybe (Types.Recipe,Maybe Types.Glass,[Types.IngredientListItem]) -> IO CyanideScreen } | RecipeDeletionScreen { recipeDeletionRecipe :: Types.Recipe - , recipeDeletionPrevioudScreen :: Bool -> CyanideScreen + , recipeDeletionPreviousScreen :: Bool -> IO CyanideScreen } | RecipeInputScreen { recipeInputName :: BE.Editor T.Text Name diff --git a/src/Cyanide/UI/Util.hs b/src/Cyanide/UI/Util.hs index 49ac9d6..5a0b35c 100644 --- a/src/Cyanide/UI/Util.hs +++ b/src/Cyanide/UI/Util.hs @@ -97,3 +97,4 @@ readFraction str collapseWhitespace (h1:h2:t) | h1 == ' ' && h2 == ' ' = h2 : collapseWhitespace t | otherwise = h1 : collapseWhitespace (h2:t) +