From dbbce675f5eee3ffedfbc077b9a07e9df614a107 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 6 Jun 2024 05:43:24 -0500 Subject: [PATCH] Enable QR codes for addresses --- assets/2620_color.png | Bin 0 -> 17047 bytes src/Zenith/GUI.hs | 134 ++++++++++++++++++++++++++++++++++++++---- zenith.cabal | 3 + 3 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 assets/2620_color.png diff --git a/assets/2620_color.png b/assets/2620_color.png new file mode 100644 index 0000000000000000000000000000000000000000..ecfdc10d3a14c6be603e6db392c782867eb06015 GIT binary patch literal 17047 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 literal 0 HcmV?d00001 diff --git a/src/Zenith/GUI.hs b/src/Zenith/GUI.hs index 0913f5d..2caf5ef 100644 --- a/src/Zenith/GUI.hs +++ b/src/Zenith/GUI.hs @@ -3,10 +3,17 @@ module Zenith.GUI where +import Codec.Picture +import Codec.Picture.Types (pixelFold, promoteImage) +import Codec.QRCode +import Codec.QRCode.JuicyPixels import Control.Exception (throwIO, try) import Control.Monad.Logger (runNoLoggingT) +import qualified Data.ByteString as BS +import qualified Data.ByteString.Lazy as LBS import Data.Maybe (fromMaybe, isJust) import qualified Data.Text as T +import qualified Data.Text.Encoding as E import Data.Time.Clock.POSIX (posixSecondsToUTCTime) import Database.Persist import Lens.Micro ((&), (+~), (.~), (?~), (^.), set) @@ -14,8 +21,11 @@ import Lens.Micro.TH import Monomer import qualified Monomer.Lens as L import TextShow +import ZcashHaskell.Orchard (getSaplingFromUA, isValidUnifiedAddress) +import ZcashHaskell.Transparent (encodeTransparentReceiver) import ZcashHaskell.Types - ( ZcashNet(..) + ( UnifiedAddress(..) + , ZcashNet(..) , ZebraGetBlockChainInfo(..) , ZebraGetInfo(..) ) @@ -40,6 +50,7 @@ data AppModel = AppModel , _zebraOn :: !Bool , _balance :: !Integer , _unconfBalance :: !(Maybe Integer) + , _selPool :: !ZcashPool } deriving (Eq, Show) makeLenses ''AppModel @@ -50,6 +61,7 @@ data AppEvent | CloseMsg | WalletClicked | AccountClicked + | SetPool !ZcashPool deriving (Eq, Show) remixArrowRightWideLine :: T.Text @@ -75,6 +87,10 @@ buildUI wenv model = widgetTree if null (model ^. accounts) then Nothing else Just ((model ^. accounts) !! (model ^. selAcc)) + currentAddress = + if null (model ^. addresses) + then Nothing + else Just ((model ^. addresses) !! (model ^. selAddr)) widgetTree = zstack [mainWindow, msgOverlay `nodeVisible` isJust (model ^. msg)] mainWindow = @@ -142,15 +158,110 @@ buildUI wenv model = widgetTree ] addressBox = boxShadow $ - box_ - [alignMiddle] - (vstack - [ label "Addresses" `styleBasic` - [textFont "Bold", textColor white, bgColor btnColor] - , vscroll (vstack (zipWith addrRow [0 ..] (model ^. addresses))) `nodeKey` - "addrScroll" - ]) `styleBasic` - [padding 3, radius 2, bgColor white] + vstack + [ box_ + [alignMiddle] + (vstack + [ label "Addresses" `styleBasic` + [textFont "Bold", textColor white, bgColor btnColor] + , vscroll (vstack (zipWith addrRow [0 ..] (model ^. addresses))) `nodeKey` + "addrScroll" + ]) `styleBasic` + [padding 3, radius 2, bgColor white] + , addrQRCode currentAddress (model ^. selPool) + ] + addrQRCode :: + Maybe (Entity WalletAddress) + -> ZcashPool + -> WidgetNode AppModel AppEvent + addrQRCode wAddr zp = + case encodeText (defaultQRCodeOptions L) Utf8WithoutECI =<< dispAddr of + Just qr -> + box_ + [alignMiddle] + (hstack + [ filler + , vstack + [ box_ + [onClick (SetPool Orchard)] + (remixIcon remixShieldCheckFill `styleBasic` + [ textSize 14 + , padding 4 + , styleIf + (model ^. selPool == Orchard) + (bgColor btnColor) + ]) + , filler + , box_ + [onClick (SetPool Sapling)] + (remixIcon remixShieldLine `styleBasic` + [ textSize 14 + , padding 4 + , styleIf + (model ^. selPool == Sapling) + (bgColor btnColor) + ]) + , filler + , box_ + [onClick (SetPool Transparent)] + (remixIcon remixDislikeLine `styleBasic` + [ textSize 14 + , padding 4 + , styleIf + (model ^. selPool == Transparent) + (bgColor btnColor) + ]) + ] + , vstack + [ label + (case model ^. selPool of + Orchard -> "Unified" + Sapling -> "Legacy Shielded" + Transparent -> "Transparent" + Sprout -> "Unknown") + , imageMem_ + (T.pack $ show zp) + (qrCodeBytes qr) + (qrCodeSize qr) + [fitNone] + ] + , filler + ]) + Nothing -> + box_ [alignMiddle] (image_ "./assets/2620_color.png" [fitFill]) + where + qrCodeImg :: QRImage -> Image PixelRGBA8 + qrCodeImg qr = promoteImage (toImage 4 1 qr) + qrCodeSize :: QRImage -> Size + qrCodeSize qr = + Size + (fromIntegral $ imageWidth $ qrCodeImg qr) + (fromIntegral $ imageHeight $ qrCodeImg qr) + qrCodeBytes :: QRImage -> BS.ByteString + qrCodeBytes qr = + BS.pack $ + pixelFold + (\bs _ _ (PixelRGBA8 i j k l) -> bs <> [i, j, k, l]) + [] + (qrCodeImg qr) + dispAddr :: Maybe T.Text + dispAddr = + case zp of + Transparent -> + T.append "zcash:" . encodeTransparentReceiver (model ^. network) <$> + (t_rec =<< + (isValidUnifiedAddress . + E.encodeUtf8 . getUA . walletAddressUAddress . entityVal) =<< + wAddr) + Sapling -> + T.append "zcash:" <$> + (getSaplingFromUA . + E.encodeUtf8 . getUA . walletAddressUAddress . entityVal =<< + wAddr) + Orchard -> + T.append "zcash:" . getUA . walletAddressUAddress . entityVal <$> + wAddr + Sprout -> Nothing addrRow :: Int -> Entity WalletAddress -> WidgetNode AppModel AppEvent addrRow idx wAddr = box_ @@ -228,6 +339,7 @@ handleEvent wenv node model evt = ShowMsg t -> [Model $ model & msg ?~ t] WalletClicked -> [Model $ model & msg ?~ "You clicked Wallet!"] AccountClicked -> [Model $ model & msg ?~ "You clicked Account!"] + SetPool p -> [Model $ model & selPool .~ p] CloseMsg -> [Model $ model & msg .~ Nothing] runZenithGUI :: Config -> IO () @@ -275,6 +387,7 @@ runZenithGUI config = do True 314259000 (Just 300000) + Transparent startApp model handleEvent buildUI params Left e -> do initDb dbFilePath @@ -296,6 +409,7 @@ runZenithGUI config = do False 314259000 (Just 30000) + Orchard startApp model handleEvent buildUI params where params = diff --git a/zenith.cabal b/zenith.cabal index 5348b48..402f47d 100644 --- a/zenith.cabal +++ b/zenith.cabal @@ -60,6 +60,9 @@ library , http-client , http-conduit , http-types + , JuicyPixels + , qrcode-core + , qrcode-juicypixels , microlens , microlens-mtl , microlens-th