Skip to content

Commit

Permalink
feat: adds QR code capability
Browse files Browse the repository at this point in the history
  • Loading branch information
HashMapsData2Value committed Oct 23, 2024
1 parent 2b99566 commit 5418742
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 10 deletions.
10 changes: 9 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.22.0
toolchain go1.23.1

require (
github.com/algonode/algourl v0.0.0-20241019213625-74154582e3d5
github.com/charmbracelet/bubbles v0.20.0
github.com/charmbracelet/bubbletea v1.1.1
github.com/charmbracelet/lipgloss v0.13.0
Expand All @@ -16,7 +17,13 @@ require (
github.com/spf13/viper v1.19.0
)

require gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect

require (
github.com/algorand/go-algorand-sdk v1.24.0
github.com/algorand/go-algorand-sdk/v2 v2.6.0
github.com/algorand/go-codec/codec v1.1.10 // indirect
github.com/algorandfoundation/go-tinyqr v0.0.0-20241018103413-2082a3d637eb // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
Expand All @@ -27,6 +34,7 @@ require (
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
Expand All @@ -50,11 +58,11 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
41 changes: 41 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/algonode/algourl v0.0.0-20241019213625-74154582e3d5 h1:OXuvUO08ZCvwLsUdiGgs/fBn51Hynoe+zsERunzCP6k=
github.com/algonode/algourl v0.0.0-20241019213625-74154582e3d5/go.mod h1:ge/QtZi9BNjnp/WFOsiyX3OeJgHzxMNr1PIz+If2aAM=
github.com/algorand/go-algorand-sdk v1.24.0 h1:mi8vqjXMC5nU87snq4vxHi+NgPR0thtZHRLA16FKZMM=
github.com/algorand/go-algorand-sdk v1.24.0/go.mod h1:WEeJcctOHMzDFTgVJ6GT8BLUo9DbFTT47S+Kzx7ffXQ=
github.com/algorand/go-algorand-sdk/v2 v2.6.0 h1:pfL8lloEi26l6PwAFicmPUguWgKpy1eZZTMlQcci5h0=
github.com/algorand/go-algorand-sdk/v2 v2.6.0/go.mod h1:4ayerzjoWChm3kuVhbgFgURTbaYTtlj0c41eP3av5lw=
github.com/algorand/go-codec/codec v1.1.10 h1:zmWYU1cp64jQVTOG8Tw8wa+k0VfwgXIPbnDfiVa+5QA=
github.com/algorand/go-codec/codec v1.1.10/go.mod h1:YkEx5nmr/zuCeaDYOIhlDg92Lxju8tj2d2NrYqP7g7k=
github.com/algorandfoundation/go-tinyqr v0.0.0-20241018103413-2082a3d637eb h1:NFKjd5BKatUpYUXiofxvWd0VqVoinsVzugtQw/W+vKs=
github.com/algorandfoundation/go-tinyqr v0.0.0-20241018103413-2082a3d637eb/go.mod h1:j4+scGl0Oz2rw+bnoPxfmvregASzMapI5ydPrGzTDBU=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
Expand Down Expand Up @@ -37,8 +47,11 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
Expand Down Expand Up @@ -109,18 +122,46 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
Expand Down
4 changes: 3 additions & 1 deletion internal/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package internal
import (
"context"
"fmt"

"github.com/algorandfoundation/hack-tui/api"
)

Expand All @@ -12,6 +13,7 @@ type StatusModel struct {
State string
Version string
Network string
GenesisHash []byte
Voting bool
NeedsUpdate bool
LastRound uint64 // Last recorded round
Expand All @@ -34,7 +36,7 @@ func (m *StatusModel) Fetch(ctx context.Context, client *api.ClientWithResponses
}
m.Network = v.JSON200.GenesisId
m.Version = fmt.Sprintf("v%d.%d.%d-%s", v.JSON200.Build.Major, v.JSON200.Build.Minor, v.JSON200.Build.BuildNumber, v.JSON200.Build.Channel)

m.GenesisHash = v.JSON200.GenesisHashB64
}

s, err := client.GetStatusWithResponse(ctx)
Expand Down
3 changes: 2 additions & 1 deletion ui/pages/keys/model.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package keys

import (
"sort"

"github.com/algorandfoundation/hack-tui/api"
"github.com/algorandfoundation/hack-tui/ui/controls"
"github.com/algorandfoundation/hack-tui/ui/pages"
"github.com/algorandfoundation/hack-tui/ui/utils"
"github.com/charmbracelet/bubbles/table"
"github.com/charmbracelet/lipgloss"
"sort"
)

type ViewModel struct {
Expand Down
104 changes: 103 additions & 1 deletion ui/pages/transaction/controller.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package transaction

import (
"encoding/base64"
"fmt"

encoder "github.com/algonode/algourl/encoder"
msgpack "github.com/algorand/go-algorand-sdk/encoding/msgpack"
types "github.com/algorand/go-algorand-sdk/v2/types"
"github.com/algorandfoundation/hack-tui/api"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
Expand All @@ -14,18 +20,114 @@ func (m ViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m.HandleMessage(msg)
}

// HandleMessage is called by the viewport to update it's Model
type NetworkParameters struct {
Network string
GenesisHash []byte
}

func (m ViewModel) UpdateTxnURLAndQRCode() error {

// TODO: Replace with a check for online status
goOnline := true
///////////////////////////////////////////////

// TODO: Replace with a fetch of the actual genesis id and hash
genesisHash, err := base64.StdEncoding.DecodeString("wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=")
if err != nil {
fmt.Printf("Failed to decode genesis hash: %v\n", err)
return err
}

extStatus := NetworkParameters{
Network: "mainnet-v1.0",
GenesisHash: genesisHash,
}
///////////////////////////////////////////////

tx := types.Transaction{}

senderAddress, err := types.DecodeAddress(m.Data.Address)
if err != nil {
return err
}

if goOnline {
var stateProofPk types.MerkleVerifier
copy(stateProofPk[:], (*m.Data.Key.StateProofKey)[:])

tx = types.Transaction{
Type: types.KeyRegistrationTx,
Header: types.Header{
Sender: senderAddress,
Fee: 0,
FirstValid: types.Round(*m.Data.EffectiveFirstValid),
LastValid: types.Round(*m.Data.EffectiveLastValid),
GenesisHash: types.Digest(extStatus.GenesisHash),
GenesisID: extStatus.Network,
},
KeyregTxnFields: types.KeyregTxnFields{
VotePK: types.VotePK(m.Data.Key.VoteParticipationKey),
SelectionPK: types.VRFPK(m.Data.Key.SelectionParticipationKey),
StateProofPK: types.MerkleVerifier(stateProofPk),
VoteFirst: types.Round(m.Data.Key.VoteFirstValid),
VoteLast: types.Round(m.Data.Key.VoteLastValid),
VoteKeyDilution: uint64(m.Data.Key.VoteKeyDilution),
},
}

} else {
tx = types.Transaction{
Type: types.KeyRegistrationTx,
Header: types.Header{
Sender: senderAddress,
Fee: 0,
FirstValid: types.Round(*m.Data.EffectiveFirstValid),
LastValid: types.Round(*m.Data.EffectiveLastValid),
GenesisHash: types.Digest(extStatus.GenesisHash),
GenesisID: extStatus.Network,
},
}
}

encodedTxn := msgpack.Encode(tx)
kr, err := encoder.MakeQRKeyRegRequest(encodedTxn)

if err != nil {
return err
}

qrCode, err := kr.ProduceQRCode()
if err != nil {
return err
}

m.urlTxn = kr.String()
m.asciiQR = qrCode

return nil
}

// HandleMessage is called by the viewport to update its Model
func (m ViewModel) HandleMessage(msg tea.Msg) (ViewModel, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
// When the participation key updates, set the models data
case api.ParticipationKey:
m.Data = msg

err := m.UpdateTxnURLAndQRCode()
if err != nil {
panic(err)
}

// Handle View Size changes
case tea.WindowSizeMsg:
if msg.Width != 0 && msg.Height != 0 {
m.Width = msg.Width
m.Height = max(0, msg.Height-lipgloss.Height(m.controls.View()))

// If the QR code is too large, set the flag
m.QRWontFit = lipgloss.Width(m.asciiQR) > m.Width || lipgloss.Height(m.asciiQR) > m.Height
}
}

Expand Down
10 changes: 7 additions & 3 deletions ui/pages/transaction/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,23 @@ type ViewModel struct {
// Height is the last known vertical lines
Height int

// QRWontFit is a flag to indicate the QR code is too large to display
QRWontFit bool

// Participation Key
Data api.ParticipationKey

// Components
controls controls.Model

// TODO: add URL
// urlTxn string
// QR Code and URL
asciiQR string
urlTxn string
}

// New creates and instance of the ViewModel with a default controls.Model
func New() ViewModel {
return ViewModel{
controls: controls.New(" (a)ccunts | (k)eys | " + green.Render("(t)xn ")),
controls: controls.New(" (a)ccounts | (k)eys | " + green.Render("(t)xn ")),
}
}
26 changes: 23 additions & 3 deletions ui/pages/transaction/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,30 @@ package transaction

import (
"github.com/charmbracelet/lipgloss"
"strings"
)

func (m ViewModel) View() string {
pad := strings.Repeat("\n", max(0, m.Height/2-1))
return lipgloss.JoinVertical(lipgloss.Center, pad, "TODO", pad, m.controls.View())

qrStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("15")).
Background(lipgloss.Color("0"))

red := lipgloss.NewStyle().Foreground(lipgloss.Color("9"))

if m.asciiQR == "" || m.urlTxn == "" {
return lipgloss.JoinVertical(
lipgloss.Left,
"No QR Code available",
m.controls.View())
}
if m.QRWontFit {
return lipgloss.JoinVertical(
lipgloss.Left,
red.Render("QR Code too large to display... Please adjust terminal dimensions or font."),
m.controls.View())
}
return lipgloss.JoinVertical(
lipgloss.Left,
qrStyle.Render(m.asciiQR),
m.controls.View())
}
1 change: 1 addition & 0 deletions ui/viewport.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ui
import (
"context"
"fmt"

"github.com/algorandfoundation/hack-tui/api"
"github.com/algorandfoundation/hack-tui/internal"
"github.com/algorandfoundation/hack-tui/ui/pages/accounts"
Expand Down

0 comments on commit 5418742

Please sign in to comment.