From e9fd87ef583a98227276e5b3fb93fbdbb3855b1a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 14 Feb 2024 12:03:18 -0600 Subject: [PATCH] Add splash screen to TUI --- src/Zenith/CLI.hs | 118 +++++++++++++++++++++++++++++++++++++-------- src/Zenith/Core.hs | 20 ++++---- 2 files changed, 109 insertions(+), 29 deletions(-) diff --git a/src/Zenith/CLI.hs b/src/Zenith/CLI.hs index e63d581..90a2b6b 100644 --- a/src/Zenith/CLI.hs +++ b/src/Zenith/CLI.hs @@ -12,21 +12,25 @@ import Lens.Micro.Mtl import Lens.Micro.TH import qualified Brick.AttrMap as A +import qualified Brick.Focus as F +import Brick.Forms (Form(..), (@@=), editTextField, newForm, renderForm) import qualified Brick.Main as M import qualified Brick.Types as BT import Brick.Types (Widget) -import Brick.Util (fg, on) +import Brick.Util (fg, on, style) import qualified Brick.Widgets.Border as B -import Brick.Widgets.Border.Style (unicode) +import Brick.Widgets.Border.Style (unicode, unicodeBold) import qualified Brick.Widgets.Center as C import Brick.Widgets.Core ( Padding(..) , (<+>) , (<=>) , emptyWidget + , fill , hLimit , joinBorders , padAll + , padBottom , padRight , str , vBox @@ -46,8 +50,15 @@ data Name | AList | TList | HelpDialog + | WalNameField deriving (Eq, Show, Ord) +data WalletName = WalletName + { _walName :: !T.Text + } deriving (Show) + +makeLenses ''WalletName + data State = State { _network :: !String , _wallets :: !(L.List Name String) @@ -55,12 +66,16 @@ data State = State , _transactions :: !(L.List Name String) , _msg :: !String , _helpBox :: !Bool - } deriving (Show) + , _walletBox :: !Bool + , _splashBox :: !Bool + , _walletForm :: !(Form WalletName () Name) + , _focusRing :: !(F.FocusRing Name) + } makeLenses ''State drawUI :: State -> [Widget Name] -drawUI s = [helpDialog s, ui s] +drawUI s = [splashDialog s, helpDialog s, walletDialog s, ui s] where ui :: State -> Widget Name ui s = @@ -88,13 +103,43 @@ drawUI s = [helpDialog s, ui s] helpDialog s = if s ^. helpBox then D.renderDialog - (D.dialog (Just (str "Commands")) Nothing 50) + (D.dialog (Just (str "Commands")) Nothing 55) (vBox ([C.hCenter $ str "Key", B.hBorder] <> keyList) <+> vBox ([str "Actions", B.hBorder] <> actionList)) else emptyWidget where - keyList = map (C.hCenter . str) ["?", "Esc", "q"] - actionList = map (hLimit 40 . str) ["Open help", "Close dialog", "Quit"] + keyList = map (C.hCenter . str) ["?", "Esc", "c", "q"] + actionList = + map + (hLimit 40 . str) + ["Open help", "Close dialog", "Create Wallet", "Quit"] + walletDialog :: State -> Widget Name + walletDialog s = + if s ^. walletBox + then D.renderDialog + (D.dialog (Just (str "Create Wallet")) Nothing 50) + (renderForm $ s ^. walletForm) + else emptyWidget + splashDialog :: State -> Widget Name + splashDialog s = + if s ^. splashBox + then withBorderStyle unicodeBold $ + D.renderDialog + (D.dialog Nothing Nothing 30) + (withAttr + titleAttr + (str + " _____ _ _ _ \n|__ /___ _ __ (_) |_| |__\n / // _ \\ '_ \\| | __| '_ \\\n / /| __/ | | | | |_| | | |\n/____\\___|_| |_|_|\\__|_| |_|") <=> + C.hCenter (withAttr titleAttr (str "Zcash Wallet v0.4.1")) <=> + C.hCenter (withAttr blinkAttr $ str "Press any key...")) + else emptyWidget + +mkWalletForm :: WalletName -> Form WalletName e Name +mkWalletForm = + newForm [label "Name" @@= editTextField walName WalNameField (Just 1)] + where + label s w = + padBottom (Pad 1) $ vLimit 1 (hLimit 15 $ str s <+> fill ' ') <+> w listDrawElement :: (Show a) => Bool -> a -> Widget Name listDrawElement sel a = @@ -107,15 +152,42 @@ listDrawElement sel a = customAttr :: A.AttrName customAttr = L.listSelectedAttr <> A.attrName "custom" +titleAttr :: A.AttrName +titleAttr = A.attrName "title" + +blinkAttr :: A.AttrName +blinkAttr = A.attrName "blink" + appEvent :: BT.BrickEvent Name e -> BT.EventM Name State () -appEvent (BT.VtyEvent e) = - case e of - V.EvKey V.KEsc [] -> BT.modify $ set helpBox False - V.EvKey (V.KChar 'q') [] -> M.halt - V.EvKey (V.KChar '?') [] -> BT.modify $ set helpBox True - V.EvKey (V.KChar 'c') [] -> printMsg "You pressed C!" - V.EvKey (V.KChar 's') [] -> printMsg "You pressed S!" - ev -> BT.zoom addresses $ L.handleListEvent ev +appEvent (BT.VtyEvent (V.EvKey (V.KChar '\t') [])) = focusRing %= F.focusNext +appEvent (BT.VtyEvent e) = do + r <- F.focusGetCurrent <$> use focusRing + s <- BT.get + if s ^. splashBox + then BT.modify $ set splashBox False + else if s ^. helpBox + then do + case e of + V.EvKey V.KEsc [] -> do + BT.modify $ set helpBox False + ev -> return () + else do + if s ^. walletBox + then do + case e of + V.EvKey V.KEsc [] -> BT.modify $ set walletBox False + ev -> return () + else do + case e of + V.EvKey (V.KChar 'q') [] -> M.halt + V.EvKey (V.KChar '?') [] -> BT.modify $ set helpBox True + V.EvKey (V.KChar 'c') [] -> BT.modify $ set walletBox True + V.EvKey (V.KChar 's') [] -> printMsg "You pressed S!" + ev -> + case r of + Just AList -> BT.zoom addresses $ L.handleListEvent ev + Just TList -> BT.zoom transactions $ L.handleListEvent ev + Nothing -> return () where printMsg :: String -> BT.EventM Name State () printMsg s = BT.modify $ updateMsg s @@ -128,7 +200,9 @@ theMap = V.defAttr [ (L.listAttr, V.white `on` V.blue) , (L.listSelectedAttr, V.blue `on` V.white) - , (customAttr, fg V.cyan) + , (customAttr, fg V.black) + , (titleAttr, V.withStyle (fg V.brightGreen) V.bold) + , (blinkAttr, style V.blink) ] theApp :: M.App State e Name @@ -141,12 +215,12 @@ theApp = , M.appAttrMap = const theMap } -runZenithCLI :: Int -> T.Text -> IO () -runZenithCLI port dbName = do - w <- checkZebra port +runZenithCLI :: T.Text -> Int -> T.Text -> IO () +runZenithCLI host port dbName = do + w <- checkZebra host port case (w :: Maybe ZebraGetInfo) of Just zebra -> do - bc <- checkBlockChain port + bc <- checkBlockChain host port case (bc :: Maybe ZebraGetBlockChainInfo) of Nothing -> print "Unable to determine blockchain status" Just chainInfo -> do @@ -160,6 +234,10 @@ runZenithCLI port dbName = do ("Start up Ok! Connected to Zebra " ++ (T.unpack . zgi_build) zebra ++ " on port " ++ show port ++ ".") False + False + True + (mkWalletForm $ WalletName "Main") + (F.focusRing [AList, TList]) Nothing -> do print $ "No Zebra node available on port " <> diff --git a/src/Zenith/Core.hs b/src/Zenith/Core.hs index 2ba8480..097f645 100644 --- a/src/Zenith/Core.hs +++ b/src/Zenith/Core.hs @@ -25,25 +25,27 @@ checkWallets dbName znet = do -- * Zebra Node interaction -- | Checks the status of the `zebrad` node checkZebra :: - Int -- ^ Port where `zebrad` is available + T.Text -- ^ Host where `zebrad` is available + -> Int -- ^ Port where `zebrad` is available -> IO (Maybe ZebraGetInfo) -checkZebra port = do - res <- makeZebraCall port "getinfo" [] +checkZebra host port = do + res <- makeZebraCall host port "getinfo" [] let body = responseBody (res :: Response (RpcResponse ZebraGetInfo)) return $ result body -- | Checks the status of the Zcash blockchain checkBlockChain :: - Int -- ^ Port where `zebrad` is available + T.Text -- ^ Host where `zebrad` is available + -> Int -- ^ Port where `zebrad` is available -> IO (Maybe ZebraGetBlockChainInfo) -checkBlockChain port = do - let f = makeZebraCall port +checkBlockChain host port = do + let f = makeZebraCall host port result <$> (responseBody <$> f "getblockchaininfo" []) -- | Generic RPC call function connectZebra :: - FromJSON a => Int -> T.Text -> [Data.Aeson.Value] -> IO (Maybe a) -connectZebra port m params = do - res <- makeZebraCall port m params + FromJSON a => T.Text -> Int -> T.Text -> [Data.Aeson.Value] -> IO (Maybe a) +connectZebra host port m params = do + res <- makeZebraCall host port m params let body = responseBody res return $ result body