rvv001 - Issue 0085 - URI support implemented in GUI

- Support to generate and display a QR Code containing
		       a  ZIP-321 formatted string
This commit is contained in:
Rene V. Vergara 2025-01-15 22:05:36 -05:00
parent 3da6a57d50
commit ee71b7acbb
3 changed files with 157 additions and 68 deletions

View file

@ -22,11 +22,15 @@ import Control.Monad.Logger
)
import Data.Aeson
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import Data.HexString (toText)
import Data.Maybe (fromMaybe, isJust, isNothing)
import Data.Scientific (Scientific, fromFloatDigits)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TLE
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
import Database.Esqueleto.Experimental (ConnectionPool, fromSqlKey)
import Database.Persist
@ -170,7 +174,7 @@ data AppEvent
| ClosePaymentURIForm
| PrepareURIString
| CloseShowURIOverlay
| ShowURIOverlay !T.Text
| ShowURIOverlay !(Maybe URIQrCode) !T.Text
| CopyURIString !T.Text
| DisplayPayUsingURI
| ClosePayUsingURI
@ -247,6 +251,7 @@ data AppModel = AppModel
, _usepmtURIOverlay :: !Bool
, _uriString :: !T.Text
, _uriAddr :: !T.Text
, _uriQRImage :: !(Maybe URIQrCode)
} deriving (Eq, Show)
makeLenses ''AppModel
@ -260,6 +265,18 @@ remixHourglassFill = toGlyph 0xF338
remixIcon :: T.Text -> WidgetNode s e
remixIcon i = label i `styleBasic` [textFont "Remix", textMiddle]
getURIQRWidth :: Maybe URIQrCode -> Int
getURIQRWidth qr =
case qr of
Nothing -> 0
Just qr -> round (uriWidth qr)
getURIQRHeight :: Maybe URIQrCode -> Int
getURIQRHeight qr =
case qr of
Nothing -> 0
Just qr -> round (uriHeight qr)
buildUI ::
WidgetEnv AppModel AppEvent -> AppModel -> WidgetNode AppModel AppEvent
buildUI wenv model = widgetTree
@ -1251,66 +1268,112 @@ buildUI wenv model = widgetTree
[bgColor (white & L.a .~ 0.5)]
--
paymentURIOverlay =
alert ClosePaymentURIForm $
vstack
[ box_
[]
(label "Create a Payment URI" `styleBasic`
[textFont "Bold", textColor white, textSize 10, padding 3]) `styleBasic`
[bgColor btnColor, radius 2, padding 3]
, spacer
, hstack
[ filler
, label "Current Address:" `styleBasic` [textFont "Bold"]
, spacer
, label_ (txtWrapN (model ^. uriAddr) 64) [multiline]
, filler
]
, spacer
, hstack
[ label "Amount : " `styleBasic` [textFont "Bold"]
, numericField_ sendAmount
[ decimals 8 ]
`nodeKey` "floatInput"
`styleBasic`
[ width 150
, styleIf (model ^. sendAmount <= 0.0) (textColor red)
]
]
, spacer
, hstack
[ label "Memo: " `styleBasic` [textFont "Bold"]
, spacer
, textField_ sendMemo [] `styleBasic` [width 300]
]
, spacer
, hstack
[ filler
, mainButton "Create URI" PrepareURIString `nodeEnabled`
(model ^. sendAmount > 0.0)
, filler
]
] `styleBasic` [bgColor (white & L.a .~ 0.5)]
--
box
(vstack
[ filler
, hstack
[ filler
, box_
[]
(vstack
[ box_
[]
(label "Create a Payment URI" `styleBasic`
[textFont "Bold", textColor white, textSize 10, padding 3]) `styleBasic`
[bgColor btnColor, radius 2, padding 3]
, spacer
, hstack
[ filler
, label "Current Address:" `styleBasic` [textFont "Bold"]
, spacer
, label_ (txtWrapN (model ^. uriAddr) 64) [multiline]
, filler
]
, spacer
, hstack
[ label "Amount : " `styleBasic` [textFont "Bold"]
, numericField_ sendAmount
[ decimals 8 ]
`nodeKey` "floatInput"
`styleBasic`
[ width 150
, styleIf (model ^. sendAmount <= 0.0) (textColor red)
]
]
, spacer
, hstack
[ label "Memo: " `styleBasic` [textFont "Bold"]
, spacer
, textField_ sendMemo [] `styleBasic` [width 300]
]
, spacer
, hstack
[ filler
, mainButton "Create URI" PrepareURIString `nodeEnabled`
(model ^. sendAmount > 0.0)
, spacer
, button "Cancel" ClosePaymentURIForm
, filler
]
]) `styleBasic`
[radius 4, border 2 btnColor, bgColor white, padding 4]
, filler
]
, filler
]) `styleBasic`
[bgColor (white & L.a .~ 0.5)]
--
showURIOverlay =
alert CloseShowURIOverlay $
vstack
[ box_
[]
(label "Payment URI" `styleBasic`
[textFont "Bold", textColor white, textSize 12, padding 3]) `styleBasic`
[bgColor btnColor, radius 2, padding 3]
, spacer
, hstack
[filler, label_ (txtWrapN (model ^. uriString ) 64) [multiline], filler]
, spacer
, hstack
[ filler
, button "Copy to Clipboard" $
CopyURIString (model ^. uriString)
, filler
]
]
box
(vstack
[ filler
, hstack
[ filler
, box_
[]
(vstack
[ box_
[alignMiddle]
(label "Payment URI" `styleBasic`
[textFont "Bold", textColor white, textSize 11, padding 3]) `styleBasic`
[bgColor btnColor, radius 2, padding 3]
, spacer
, hstack
[filler, label_ (txtWrapN (model ^. uriString ) 64) [multiline], filler]
, spacer
, hstack
[ filler
, box_
[alignMiddle]
-- (image_ (T.pack ((model ^. home) </> "Zenith/assets/qr_image.png")) [fitFill])
(case model ^. uriQRImage of
Just img -> imageMem_ "URIQRCode" (uriBytes img) (Size (uriWidth img) (uriHeight img) ) [fitWidth]
Nothing -> image_
(T.pack $ (model ^. home) </> "Zenith/assets/cracked_qr.png")
[fitHeight] )
`styleBasic` [ bgColor white
, height 120
, width 120
]
, filler
]
, spacer
, hstack
[ filler
, button "Copy to Clipboard" $
CopyURIString (model ^. uriString)
, spacer
, button "Cancel" CloseShowURIOverlay
, filler
]
]) `styleBasic`
[radius 4, border 2 btnColor, bgColor white, padding 4]
, filler
]
, filler
] ) `styleBasic`
[bgColor (white & L.a .~ 0.5)]
--
pmtUsingURIOverlay =
box
(vstack
@ -1848,13 +1911,14 @@ handleEvent wenv node model evt =
-- Generate URI
--
PrepareURIString -> [ Task $ genURIString (model ^. uriAddr) (model ^. sendAmount) (model ^. sendMemo) ]
ShowURIOverlay uStr ->
ShowURIOverlay qr uStr ->
[ Model $
model & uriString .~ uStr
model & uriString .~ uStr
& uriQRImage .~ qr
& paymentURIDisplay .~ False
& showURIDisplay .~ True
]
CloseShowURIOverlay -> [ Model $ model & showURIDisplay .~ False & uriString .~ "" ]
CloseShowURIOverlay -> [ Model $ model & showURIDisplay .~ False & uriString .~ "" & uriQRImage .~ Nothing]
--
-- Display Pay using URI Form
--
@ -2108,7 +2172,24 @@ handleEvent wenv node model evt =
SproutPool -> "None"
TransparentPool -> maybe "None" (encodeTransparentReceiver (model ^. network)) $
t_rec =<< (isValidUnifiedAddress . E.encodeUtf8 . getUA . walletAddressUAddress . entityVal) =<< a
return $ DisplayPaymentURIForm ua
return $ DisplayPaymentURIForm ua
--
-- Generate a QR code for a String and save it as an PNG image
--
genURIStringQR :: Int -> T.Text -> Maybe URIQrCode
genURIStringQR scaleFactor uriStr = do
let qrOptions = defaultQRCodeOptions L
case encodeText qrOptions Utf8WithoutECI uriStr of
Nothing -> Nothing
Just qrCode -> do
let qri = promoteImage (toImage 4 scaleFactor qrCode)
let qrw = fromIntegral $ imageWidth qri
let qrh = fromIntegral $ imageHeight qri
let qrb = BS.pack $
pixelFold (\bs _ _ (PixelRGBA8 i j k l) -> bs <> [i, j, k, l])
[]
qri
Just URIQrCode { uriBytes=qrb, uriWidth=qrw, uriHeight=qrh }
--
-- Gen URI String
--
@ -2116,9 +2197,9 @@ handleEvent wenv node model evt =
genURIString addr mAmt mMemo = do
let mM = case mMemo of
"" -> Nothing
_ -> Just (T.unpack mMemo)
let uriSt = createZip321 (T.unpack addr) (Just (realToFrac mAmt)) mM
return $ ShowURIOverlay (T.pack uriSt)
_ -> Just (T.unpack mMemo)
let uriSt = createZip321 (T.unpack addr) (Just (realToFrac mAmt)) mM
return $ ShowURIOverlay (genURIStringQR 3 (T.pack uriSt)) (T.pack uriSt)
scanZebra ::
T.Text
@ -2475,6 +2556,7 @@ runZenithGUI config = do
False
""
""
Nothing
startApp model handleEvent buildUI (params hD)
Left _e -> print "Zebra not available"
where

View file

@ -517,3 +517,11 @@ data ZcashPaymentURI = ZcashPaymentURI
, uriLabel :: Maybe String
, uriMessage :: Maybe String
} deriving (Show, Eq)
-- | Define a data structure for the URI QR image
data URIQrCode = URIQrCode
{
uriBytes :: BS.ByteString -- Image as ByteString
, uriWidth :: Double -- Number of columns in QR Image
, uriHeight :: Double -- Number of rows in a QR Image
} deriving (Show, Eq)

View file

@ -17,7 +17,6 @@ import Data.Functor (void)
import Data.Maybe
import Data.Ord (clamp)
import Data.Scientific (Scientific(..), scientific, Scientific, toRealFloat)
--import Data.Scientific (Scientific, toRealFloat)
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import qualified Data.Text.Encoding as TE