diff --git a/.gitignore b/.gitignore index db28eaa..7c1d8d2 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,6 @@ ssl.sh *client/deployment/sapphirelocalnet/deployment.go *client/deployment/sapphiremainnet/deployment.go -*go.work.sum \ No newline at end of file +*go.work.sum + +*trace* \ No newline at end of file diff --git a/poa/lotus/.gitignore b/poa/lotus/.gitignore new file mode 100644 index 0000000..caa3ab0 --- /dev/null +++ b/poa/lotus/.gitignore @@ -0,0 +1,3 @@ +# Keep apk files local +*.apk* +*trace* \ No newline at end of file diff --git a/poa/lotus/build_android.sh b/poa/lotus/build_android.sh new file mode 100755 index 0000000..f11f300 --- /dev/null +++ b/poa/lotus/build_android.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +android_sdk_tools_zip="sdk-tools-linux-3859397.zip" +android_ndk_zip="android-ndk-r20-linux-x86_64.zip" + +cd ~/binaries/android +curl -so sdk-tools.zip https://dl.google.com/android/repository/$android_sdk_tools_zip +unzip -q sdk-tools.zip +rm sdk-tools.zip +curl -so ndk.zip https://dl.google.com/android/repository/$android_ndk_zip +unzip -q ndk.zip +rm ndk.zip +mv android-ndk-* ndk-bundle +yes|sdkmanager --licenses +sdkmanager "platforms;android-31" "build-tools;32.0.0" diff --git a/poa/lotus/go.mod b/poa/lotus/go.mod index 3a6b99a..db7d569 100644 --- a/poa/lotus/go.mod +++ b/poa/lotus/go.mod @@ -1,3 +1,20 @@ module github.com/dmigwi/dhamana-protocol/poa/lotus go 1.21.0 + +require ( + gioui.org v0.2.0 + gioui.org/x v0.2.0 + github.com/tanema/gween v0.0.0-20221212145351-621cc8a459d1 + golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63 +) + +require ( + gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 // indirect + gioui.org/shader v1.0.6 // indirect + github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 // indirect + golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 // indirect + golang.org/x/image v0.7.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect +) diff --git a/poa/lotus/go.sum b/poa/lotus/go.sum new file mode 100644 index 0000000..667d9a5 --- /dev/null +++ b/poa/lotus/go.sum @@ -0,0 +1,67 @@ +eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY= +eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA= +gioui.org v0.2.0 h1:RbzDn1h/pCVf/q44ImQSa/J3MIFpY3OWphzT/Tyei+w= +gioui.org v0.2.0/go.mod h1:1H72sKEk/fNFV+l0JNeM2Dt3co3Y4uaQcD+I+/GQ0e4= +gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= +gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc= +gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= +gioui.org/shader v1.0.6 h1:cvZmU+eODFR2545X+/8XucgZdTtEjR3QWW6W65b0q5Y= +gioui.org/shader v1.0.6/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= +gioui.org/x v0.2.0 h1:/MbdjKH19F16auv19UiQxli2n6BYPw7eyh9XBOTgmEw= +gioui.org/x v0.2.0/go.mod h1:rCGN2nZ8ZHqrtseJoQxCMZpt2xrZUrdZ2WuMRLBJmYs= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 h1:FQivqchis6bE2/9uF70M2gmmLpe82esEm2QadL0TEJo= +github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k= +github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI= +github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tanema/gween v0.0.0-20221212145351-621cc8a459d1 h1:s2Tn3G6rP4VljC5XDN6hARqXogkhr3k/jAsTqawSN5U= +github.com/tanema/gween v0.0.0-20221212145351-621cc8a459d1/go.mod h1:XXpz+9IVhUY5vTC5gXRNSjLDVwQWa5KM43NrH1GJa4M= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +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/exp v0.0.0-20221012211006-4de253d81b95 h1:sBdrWpxhGDdTAYNqbgBLAR+ULAPPhfgncLr1X0lyWtg= +golang.org/x/exp v0.0.0-20221012211006-4de253d81b95/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63 h1:3AGKexOYqL+ztdWdkB1bDwXgPBuTS/S8A4WzuTvJ8Cg= +golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= +golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw= +golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/poa/lotus/main.go b/poa/lotus/main.go index ffff79d..aaccd65 100644 --- a/poa/lotus/main.go +++ b/poa/lotus/main.go @@ -1,47 +1,40 @@ // Copyright (c) 2023 Migwi Ndung'u // See LICENSE for details. - package main import ( - "crypto/tls" - "io" "log" - "net/http" - "time" -) + "os" -const ( - clientCert = "certs/client.crt" - clientKeyFile = "certs/client.key" + "gioui.org/app" + "gioui.org/unit" + + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/pages" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils" ) func main() { - cert, err := tls.LoadX509KeyPair(clientCert, clientKeyFile) - if err != nil { - log.Fatalf("server: loadkeys: %s", err) - } - - tr := &http.Transport{ - MaxIdleConns: 10, - IdleConnTimeout: 30 * time.Second, - DisableCompression: true, - TLSClientConfig: &tls.Config{ - Certificates: []tls.Certificate{cert}, - InsecureSkipVerify: true, - }, - } - - client := &http.Client{Transport: tr} - resp, err := client.Get("https://127.0.0.1:30443") - if err != nil { - log.Fatalf("request err : %v", err) - } - - data, err := io.ReadAll(resp.Body) - if err != nil { - log.Fatalf("reading the request body failed: %v", err) - } - - log.Printf("client: %s", string(data)) + minSizeX := unit.Dp(375) + minSizeY := unit.Dp(600) + maxSizeX := unit.Dp(500) + maxSizeY := unit.Dp(1000) + + w := app.NewWindow( + app.Title(utils.AppName), + app.MinSize(minSizeX, minSizeY), + app.Size(minSizeX, minSizeY), + app.MaxSize(maxSizeX, maxSizeY), + app.PortraitOrientation.Option(), + app.NavigationColor(utils.HighlightColor), + app.StatusColor(utils.DarkPriColor), + ) + + go func() { + if err := pages.Loop(w); err != nil { + log.Fatal(err) + } + os.Exit(0) + }() + + app.Main() } diff --git a/poa/lotus/ui/assets/assets.go b/poa/lotus/ui/assets/assets.go new file mode 100644 index 0000000..0b88916 --- /dev/null +++ b/poa/lotus/ui/assets/assets.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package assets + +import ( + "bytes" + "embed" + "fmt" + "image" + + "gioui.org/widget" + "golang.org/x/exp/shiny/materialdesign/icons" +) + +var ( + AccountBalanceIcon, _ = widget.NewIcon(icons.ActionAccountBalance) + AccountBoxIcon, _ = widget.NewIcon(icons.ActionAccountBox) + BackIcon, _ = widget.NewIcon(icons.NavigationArrowBack) + CartIcon, _ = widget.NewIcon(icons.ActionAddShoppingCart) + EditIcon, _ = widget.NewIcon(icons.ContentCreate) + HeartIcon, _ = widget.NewIcon(icons.ActionFavorite) + HomeIcon, _ = widget.NewIcon(icons.ActionHome) + MenuIcon, _ = widget.NewIcon(icons.NavigationMenu) + OtherIcon, _ = widget.NewIcon(icons.ActionHelp) + PlusIcon, _ = widget.NewIcon(icons.ContentAdd) + SettingsIcon, _ = widget.NewIcon(icons.ActionSettings) + VisibilityIcon, _ = widget.NewIcon(icons.ActionVisibility) + + SplashImage, _ = getImage("splash_image.png") +) + +//go:embed images/* +var images embed.FS + +// GetImage returns the image read from the path provided as parameter. +func getImage(path string) (image.Image, error) { + data, err := images.ReadFile(fmt.Sprintf("images/%s", path)) + if err != nil { + return nil, err + } + + newImg, _, err := image.Decode(bytes.NewBuffer(data)) + + return newImg, err +} + +//go:embed fonts/* +var font embed.FS + +func GetFont(path string) ([]byte, error) { + data, err := font.ReadFile(fmt.Sprintf("fonts/%s", path)) + if err != nil { + return nil, err + } + + return data, err +} diff --git a/poa/lotus/ui/assets/fonts/GoNotoKurrent-Bold.ttf b/poa/lotus/ui/assets/fonts/GoNotoKurrent-Bold.ttf new file mode 100644 index 0000000..80359ed Binary files /dev/null and b/poa/lotus/ui/assets/fonts/GoNotoKurrent-Bold.ttf differ diff --git a/poa/lotus/ui/assets/fonts/GoNotoKurrent-Regular.ttf b/poa/lotus/ui/assets/fonts/GoNotoKurrent-Regular.ttf new file mode 100644 index 0000000..9e87b64 Binary files /dev/null and b/poa/lotus/ui/assets/fonts/GoNotoKurrent-Regular.ttf differ diff --git a/poa/lotus/ui/assets/images/splash_image.png b/poa/lotus/ui/assets/images/splash_image.png new file mode 100644 index 0000000..f11e04a Binary files /dev/null and b/poa/lotus/ui/assets/images/splash_image.png differ diff --git a/poa/lotus/ui/assets/loading.go b/poa/lotus/ui/assets/loading.go new file mode 100644 index 0000000..71c2669 --- /dev/null +++ b/poa/lotus/ui/assets/loading.go @@ -0,0 +1,102 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package assets + +import ( + "math" + "sync" + + "gioui.org/f32" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/widget" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils" + "github.com/tanema/gween" + "github.com/tanema/gween/ease" +) + +// LoadingIcon stores all the fields required to animate the loading icon. +type LoadingIcon struct { + icon *widget.Icon + anime *gween.Sequence + stop bool + + mu sync.RWMutex +} + +// NewLoadingIcon returns a loading icon component. +func NewLoadingIcon() *LoadingIcon { + // loadingIcon is an IconVg generated from a SVG file. + loadingIconVG := []byte{ + 0x89, 0x49, 0x56, 0x47, 0x02, 0x0a, 0x00, 0x50, 0x50, 0xb0, 0xb0, 0xc0, 0x19, 0x87, 0x19, 0x73, + 0x00, 0x7c, 0x54, 0xe9, 0x25, 0x86, 0xa0, 0x21, 0x76, 0x21, 0x71, 0x60, 0xd9, 0x77, 0x60, 0x80, + 0x90, 0x19, 0x86, 0xe1, 0x8e, 0x9c, 0xdd, 0x8f, 0xe9, 0xf5, 0x7b, 0xb0, 0x51, 0x7a, 0x0d, 0x7f, + 0x6c, 0x21, 0x7a, 0x6c, 0x31, 0x74, 0x90, 0x51, 0x84, 0x25, 0x75, 0x94, 0x31, 0x74, 0xe8, 0x78, + 0x20, 0x19, 0x89, 0x19, 0x77, 0xe2, 0xdd, 0x8f, 0x7c, 0xb0, 0xa9, 0x7f, 0x39, 0x7d, 0x91, 0x7e, + 0x8d, 0x7a, 0xc5, 0x7c, 0x39, 0x78, 0x20, 0x29, 0x7d, 0xd9, 0x82, 0xb0, 0x15, 0x81, 0x81, 0x81, + 0xc5, 0x81, 0x35, 0x83, 0x0d, 0x82, 0xf1, 0x84, 0xe7, 0x0d, 0x84, 0xe2, 0x84, 0xcd, 0x8b, 0xe9, + 0x0d, 0x84, 0xb0, 0xc9, 0x82, 0xa9, 0x7f, 0x7d, 0x85, 0x95, 0x7e, 0xcd, 0x87, 0xc9, 0x7c, 0x20, + 0x21, 0x7d, 0x21, 0x7d, 0xb0, 0x81, 0x7e, 0x15, 0x81, 0xd1, 0x7c, 0xc9, 0x81, 0x15, 0x7b, 0x11, + 0x82, 0xe3, 0xc9, 0x87, 0x29, 0x7b, 0x20, 0xd9, 0x82, 0xd1, 0x82, 0xb0, 0xcd, 0x81, 0xb1, 0x7d, + 0xe9, 0x82, 0x76, 0x3d, 0x83, 0x39, 0x78, 0xe7, 0xf5, 0x7b, 0xb0, 0xb9, 0x7f, 0xbd, 0x81, 0x0d, + 0x7f, 0x71, 0x83, 0xf5, 0x7d, 0xf5, 0x84, 0xe1, + } + + loading, _ := widget.NewIcon(loadingIconVG) + + return &LoadingIcon{ + icon: loading, + anime: gween.NewSequence(gween.New(5, 0, .05, ease.Linear)), + } +} + +// Start starts the sequence animation. +func (l *LoadingIcon) Start() { + l.mu.Lock() + l.stop = false + if l.anime != nil { + l.anime.SetLoop(-1) + } + l.mu.Unlock() +} + +// Stops the animation sequence from running. +func (l *LoadingIcon) Stop() { + l.mu.Lock() + l.stop = true + if l.anime != nil { + l.anime.SetLoop(0) + } + l.mu.Unlock() +} + +func (l *LoadingIcon) Layout(gtx layout.Context) layout.Dimensions { + l.mu.Lock() + defer l.mu.Unlock() + + r := op.Record(gtx.Ops) + dims := l.icon.Layout(gtx, utils.SecondaryColor) + c := r.Stop() + + gtx.Constraints.Min = dims.Size + + dt := float32(gtx.Now.Second()) + val, _, _ := l.anime.Update(dt) + + if !l.stop { + op.InvalidateOp{}.Add(gtx.Ops) + defer rotate(gtx, val).Push(gtx.Ops).Pop() + } + + c.Add(gtx.Ops) + + return dims +} + +func rotate(gtx layout.Context, value float32) op.TransformOp { + pt := gtx.Constraints.Min.Div(2) + origin := f32.Pt(float32(pt.X), float32(pt.Y)) + trans := f32.Affine2D{}.Rotate(origin, -value*2*math.Pi) + return op.Affine(trans) +} diff --git a/poa/lotus/ui/pages/about/about.go b/poa/lotus/ui/pages/about/about.go new file mode 100644 index 0000000..5085558 --- /dev/null +++ b/poa/lotus/ui/pages/about/about.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package about + +import ( + "gioui.org/layout" + "gioui.org/widget" + "gioui.org/widget/material" + "gioui.org/x/component" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/assets" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/router" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils/values" +) + +type ( + C = layout.Context + D = layout.Dimensions +) + +// AboutPage holds the state for a page demonstrating the features of +// the AppBar component. +type AboutPage struct { + widget.List + *router.Router +} + +// New constructs an AboutPage with the provided router. +func New(router *router.Router) *AboutPage { + return &AboutPage{ + Router: router, + } +} + +func (p *AboutPage) ID() string { + return utils.ABOUT_PAGE_ID +} + +func (p *AboutPage) NavItem() *component.NavItem { + return &component.NavItem{ + Tag: p.ID(), + Name: values.StrAbout, + Icon: assets.OtherIcon, + } +} + +func (p *AboutPage) OnSwitchTo() {} + +func (p *AboutPage) OnSwitchFrom() {} + +func (p *AboutPage) HandleEvents() {} + +func (p *AboutPage) Layout(gtx C, th *material.Theme) D { + p.List.Axis = layout.Vertical + return material.List(th, &p.List).Layout(gtx, 1, func(gtx C, _ int) D { + return layout.Flex{ + Alignment: layout.Middle, + Axis: layout.Vertical, + }.Layout(gtx, + layout.Rigid(func(gtx C) D { + return layout.Inset{}.Layout(gtx, material.Body2(th, "THIS IS THE ABOUT PAGE").Layout) + }), + ) + }) +} diff --git a/poa/lotus/ui/pages/account/account.go b/poa/lotus/ui/pages/account/account.go new file mode 100644 index 0000000..4d940aa --- /dev/null +++ b/poa/lotus/ui/pages/account/account.go @@ -0,0 +1,67 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package account + +import ( + "gioui.org/layout" + "gioui.org/widget" + "gioui.org/widget/material" + "gioui.org/x/component" + + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/assets" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/router" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils/values" +) + +type ( + C = layout.Context + D = layout.Dimensions +) + +// AccountPage holds the state for a page demonstrating the features of +// the NavDrawer component. +type AccountPage struct { + widget.List + *router.Router +} + +// New constructs a AccountPage with the provided router. +func New(router *router.Router) *AccountPage { + return &AccountPage{ + Router: router, + } +} + +func (p *AccountPage) ID() string { + return utils.ACCOUNT_PAGE_ID +} + +func (p *AccountPage) NavItem() *component.NavItem { + return &component.NavItem{ + Tag: p.ID(), + Name: values.StrAccount, + Icon: assets.SettingsIcon, + } +} + +func (p *AccountPage) OnSwitchTo() {} + +func (p *AccountPage) OnSwitchFrom() {} + +func (p *AccountPage) HandleEvents() {} + +func (p *AccountPage) Layout(gtx C, th *material.Theme) D { + p.List.Axis = layout.Vertical + return material.List(th, &p.List).Layout(gtx, 1, func(gtx C, _ int) D { + return layout.Flex{ + Alignment: layout.Middle, + Axis: layout.Vertical, + }.Layout(gtx, + layout.Rigid(func(gtx C) D { + return layout.Inset{}.Layout(gtx, material.Body2(th, "THIS IS THE ACCOUNT PAGE").Layout) + }), + ) + }) +} diff --git a/poa/lotus/ui/pages/feedback/feedback.go b/poa/lotus/ui/pages/feedback/feedback.go new file mode 100644 index 0000000..1e10af2 --- /dev/null +++ b/poa/lotus/ui/pages/feedback/feedback.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package feedback + +import ( + "gioui.org/layout" + "gioui.org/widget" + "gioui.org/widget/material" + "gioui.org/x/component" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/assets" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/router" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils/values" +) + +type ( + C = layout.Context + D = layout.Dimensions +) + +// FeedbackPage holds the state for a page demonstrating the features of +// the TextField component. +type FeedbackPage struct { + widget.List + *router.Router +} + +// New constructs a FeedbackPage with the provided router. +func New(router *router.Router) *FeedbackPage { + return &FeedbackPage{ + Router: router, + } +} + +func (p *FeedbackPage) ID() string { + return utils.FEEDBACK_PAGE_ID +} + +func (p *FeedbackPage) NavItem() *component.NavItem { + return &component.NavItem{ + Tag: p.ID(), + Name: values.StrFeedback, + Icon: assets.EditIcon, + } +} + +func (p *FeedbackPage) OnSwitchTo() {} + +func (p *FeedbackPage) OnSwitchFrom() {} + +func (p *FeedbackPage) HandleEvents() {} + +func (p *FeedbackPage) Layout(gtx C, th *material.Theme) D { + p.List.Axis = layout.Vertical + return material.List(th, &p.List).Layout(gtx, 1, func(gtx C, _ int) D { + return layout.Flex{ + Axis: layout.Vertical, + }.Layout( + gtx, + layout.Rigid(func(gtx C) D { + return layout.Inset{}.Layout(gtx, material.Body2(th, "THIS IS THE FEEDBACK PAGE").Layout) + }), + ) + }) +} diff --git a/poa/lotus/ui/pages/home/home.go b/poa/lotus/ui/pages/home/home.go new file mode 100644 index 0000000..41b2976 --- /dev/null +++ b/poa/lotus/ui/pages/home/home.go @@ -0,0 +1,60 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package home + +import ( + "gioui.org/layout" + "gioui.org/widget" + "gioui.org/widget/material" + "gioui.org/x/component" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/assets" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/router" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils/values" +) + +type ( + C = layout.Context + D = layout.Dimensions +) + +// HomePage holds the state for a page demonstrating the features of +// the Menu component. +type HomePage struct { + *router.Router + widget.List +} + +// New constructs a Page with the provided router. +func New(router *router.Router) *HomePage { + return &HomePage{ + Router: router, + } +} + +func (p *HomePage) ID() string { + return utils.HOME_PAGE_ID +} + +func (p *HomePage) NavItem() *component.NavItem { + return &component.NavItem{ + Tag: p.ID(), + Name: values.StrHome, + Icon: assets.HomeIcon, + } +} + +func (p *HomePage) OnSwitchTo() {} + +func (p *HomePage) OnSwitchFrom() {} + +func (p *HomePage) HandleEvents() {} + +func (p *HomePage) Layout(gtx C, th *material.Theme) D { + return layout.Flex{}.Layout(gtx, + layout.Rigid(func(gtx C) D { + return layout.Inset{}.Layout(gtx, material.Body2(th, "THIS IS THE HOME PAGE").Layout) + }), + ) +} diff --git a/poa/lotus/ui/pages/pages.go b/poa/lotus/ui/pages/pages.go new file mode 100644 index 0000000..2a48e67 --- /dev/null +++ b/poa/lotus/ui/pages/pages.go @@ -0,0 +1,100 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package pages + +import ( + "gioui.org/app" + "gioui.org/font" + "gioui.org/font/opentype" + "gioui.org/io/system" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/text" + "gioui.org/widget/material" + + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/assets" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/pages/about" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/pages/account" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/pages/feedback" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/pages/home" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/pages/signup" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/pages/splash" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/router" +) + +type ( + C = layout.Context + D = layout.Dimensions +) + +func loadFontCollection() ([]font.FontFace, error) { + // universal fonts from https://github.com/satbyy/go-noto-universal + + goNotoKurrentRegularTTF, err := assets.GetFont("GoNotoKurrent-Regular.ttf") + if err != nil { + return nil, err + } + + goNotoKurrentRegular, err := opentype.Parse(goNotoKurrentRegularTTF) + if err != nil { + return nil, err + } + + goNotoKurrentBoldTTF, err := assets.GetFont("GoNotoKurrent-Bold.ttf") + if err != nil { + return nil, err + } + + goNotoKurrentBold, err := opentype.Parse(goNotoKurrentBoldTTF) + if err != nil { + return nil, err + } + + fontCollection := []font.FontFace{} + fontCollection = append(fontCollection, font.FontFace{Font: font.Font{}, Face: goNotoKurrentRegular}) + fontCollection = append(fontCollection, font.FontFace{Font: font.Font{Weight: font.Bold}, Face: goNotoKurrentBold}) + return fontCollection, nil +} + +func Loop(w *app.Window) error { + th := material.NewTheme() + fontCollection, _ := loadFontCollection() + th.Shaper = text.NewShaper(text.NoSystemFonts(), text.WithCollection(fontCollection)) + var ops op.Ops + + pg := router.NewRouter() + pg.Register(signup.New(pg), home.New(pg), account.New(pg), feedback.New(pg), about.New(pg)) + + // Load the splash page and set it as the current. + splashPg := splash.New() + // pg.SetTemporaryPage(splashPg) + + go func() { + select { + case <-splashPg.StartTimer(): + pg.DisableTemporaryPage() + splashPg.StopTimer() + + // Invalidate the current frame, triggering page redrawing. + w.Invalidate() + } + }() + + for { + select { + case e := <-w.Events(): + switch e := e.(type) { + case system.DestroyEvent: + return e.Err + + case system.FrameEvent: + pg.ProcessEvents() + gtx := layout.NewContext(&ops, e) + + pg.Layout(gtx, th) + e.Frame(gtx.Ops) + } + } + } +} diff --git a/poa/lotus/ui/pages/signup/signup.go b/poa/lotus/ui/pages/signup/signup.go new file mode 100644 index 0000000..6813f42 --- /dev/null +++ b/poa/lotus/ui/pages/signup/signup.go @@ -0,0 +1,72 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package signup + +import ( + "gioui.org/layout" + "gioui.org/unit" + "gioui.org/widget/material" + "gioui.org/x/component" + + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/router" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils" +) + +type ( + C = layout.Context + D = layout.Dimensions +) + +// signupPage holds the state for a page demonstrating the features of +// the NavDrawer component. +type signupPage struct { + *router.Router + nameInput component.TextField + addressInput component.TextField + priceInput component.TextField + tweetInput component.TextField + numberInput component.TextField +} + +// New constructs a SignupPage with the provided router. +func New(router *router.Router) *signupPage { + return &signupPage{ + Router: router, + } +} + +func (s *signupPage) ID() string { + return utils.SIGNUP_PAGE_ID +} + +func (s *signupPage) OnSwitchTo() {} +func (s *signupPage) OnSwitchFrom() {} +func (s *signupPage) HandleEvents() {} + +// NavItem returns nil because the signup page doesn't support navigation bar +// fields. +func (s *signupPage) NavItem() *component.NavItem { + return nil +} + +func (p *signupPage) Layout(gtx C, th *material.Theme) D { + return layout.Inset{ + Right: unit.Dp(30), + Left: unit.Dp(30), + }.Layout(gtx, func(gtx C) D { + return layout.Flex{ + Alignment: layout.Middle, + Axis: layout.Vertical, + }.Layout(gtx, + layout.Rigid(material.Body2(th, "THIS IS THE SIGNUP PAGE").Layout), + layout.Rigid(func(gtx C) D { + mTh := *th + mTh.Palette.Fg = utils.PrimaryColor + mTh.Palette.ContrastBg = utils.PrimaryColor + // p.nameInput. + return p.nameInput.Layout(gtx, &mTh, "Paste Wallet Private Key") + }), + ) + }) +} diff --git a/poa/lotus/ui/pages/splash/splash.go b/poa/lotus/ui/pages/splash/splash.go new file mode 100644 index 0000000..8c3fda5 --- /dev/null +++ b/poa/lotus/ui/pages/splash/splash.go @@ -0,0 +1,118 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package splash + +import ( + "time" + + "gioui.org/font" + "gioui.org/layout" + "gioui.org/op/paint" + "gioui.org/text" + "gioui.org/unit" + "gioui.org/widget" + "gioui.org/widget/material" + "gioui.org/x/component" + + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/assets" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils/values" + "github.com/tanema/gween" + "github.com/tanema/gween/ease" +) + +type ( + C = layout.Context + D = layout.Dimensions +) + +// timerLength indicates how long the splash screen should be visible for. +const timerLength = 15 * time.Second + +type splashPage struct { + status string + image *widget.Image + loading *assets.LoadingIcon + + visibility *time.Timer +} + +// New constructs a splashPage with the provided router. +func New() *splashPage { + sequence := gween.NewSequence( + gween.New(0, 2, 0.05, ease.Linear), + ) + sequence.SetLoop(-1) + + return &splashPage{ + status: values.StrLoading, + image: &widget.Image{ + Src: paint.NewImageOp(assets.SplashImage), + Fit: widget.Contain, + }, + loading: assets.NewLoadingIcon(), + } +} + +func (s *splashPage) ID() string { + return utils.SPLASH_PAGE_ID +} + +func (s *splashPage) OnSwitchTo() {} +func (s *splashPage) OnSwitchFrom() {} +func (s *splashPage) HandleEvents() {} + +// NavItem returns nil because the splash screen doesn't support navigation bar +// fields. +func (s *splashPage) NavItem() *component.NavItem { + return nil +} + +// StartTimer initiates the timer. +func (s *splashPage) StartTimer() <-chan time.Time { + s.loading.Start() + s.visibility = time.NewTimer(timerLength) + return s.visibility.C +} + +// StopTimer stops the timer if its set and running. +func (s *splashPage) StopTimer() { + s.loading.Stop() + if s.visibility != nil { + s.visibility.Stop() + s.visibility = nil + } +} + +func (s *splashPage) Layout(gtx C, th *material.Theme) D { + // paint the background with the primary color. + paint.Fill(gtx.Ops, utils.PrimaryColor) + + return layout.Flex{ + Axis: layout.Vertical, + Alignment: layout.Middle, + }.Layout(gtx, + layout.Rigid(func(gtx C) D { + return layout.Center.Layout(gtx, func(gtx C) D { + return s.image.Layout(gtx) + }) + }), + layout.Rigid(layout.Spacer{Height: unit.Dp(20)}.Layout), + layout.Rigid(func(gtx C) D { + lbl := material.Label(th, unit.Sp(14), utils.AppVersion.String()) + lbl.Font.Weight = font.SemiBold + lbl.Alignment = text.Middle + lbl.Color = utils.SecondaryColor + return lbl.Layout(gtx) + }), + layout.Rigid(layout.Spacer{Height: unit.Dp(15)}.Layout), + layout.Rigid(func(gtx C) D { + return layout.Center.Layout(gtx, func(gtx C) D { + gtx.Constraints.Max.X = 30 + gtx.Constraints.Max.Y = 30 + return s.loading.Layout(gtx) + }) + }), + ) +} diff --git a/poa/lotus/ui/router/router.go b/poa/lotus/ui/router/router.go new file mode 100644 index 0000000..158662b --- /dev/null +++ b/poa/lotus/ui/router/router.go @@ -0,0 +1,278 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package router + +import ( + "sync" + "sync/atomic" + "time" + + "gioui.org/layout" + "gioui.org/widget" + "gioui.org/widget/material" + "gioui.org/x/component" + + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/assets" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils" + "github.com/dmigwi/dhamana-protocol/poa/lotus/ui/utils/values" +) + +// layoutRunner prevents execution of the Router.Layout() method if it is currently +// executing. The previous instance need to complete before starting on new onces. +var layoutRunner uint32 + +// Page defines the interface all pages should implement to navigation from on +// page to another one. +type Page interface { + // ID returns the unique page identity string + ID() string + // OnSwitchTo implements the functionality to be invoked once the page loads + // and before its actual functionality is invoked. + OnSwitchTo() + // OnSwitchFrom implements the functionality be invoked before the page + // exists. + OnSwitchFrom() + // HandleEvents handles all the events triggered by the components in the + // current page. + HandleEvents() + // Layout arranges the ui components in the current page. + Layout(gtx layout.Context, th *material.Theme) layout.Dimensions + // NavItem returns the navigation fields linked to each page. + NavItem() *component.NavItem +} + +type Router struct { + // current defines the page ID of the current page in view. + currentPage Page + // tempPage defines a page that is used until the window is invalidated + // moving to the regular pages. They include but not limited to: splash and + // sign up pages. + tempPage Page + // pageStack track of the order in which pages are visited for the purpose + // of backtracking to a previous page in the order in which they were + // accessed. It holds the page ID strings. + pageStack []string + // registeredPages maintains references to already initialised pages mapped + // to their respective page IDs + registeredPages map[string]Page + + backbutton *widget.Clickable + + // navDrawer defines the component holding the navigation bar title and fields. + navDrawer *component.ModalNavDrawer + // menuBar implements the top bar as defined in material UI guidelines. + // https://m2.material.io/components/app-bars-top + // It enables attaching the navigation bar to the current page. + menuBar *component.AppBar + + // modalLayer is drawn on top of the normal UI allowing it to display extra + // components like the navigation bar UI or Modals. + modalLayer *component.ModalLayer + + // mu controls the read and write access to the router fields preventing + // multipe write that could eventually lead to app crash. + mu sync.RWMutex +} + +type ( + C = layout.Context + D = layout.Dimensions +) + +// NewRouter returns a new instance of the router pointer. +func NewRouter() *Router { + modal := component.NewModal() + bar := component.NewAppBar(modal) + bar.NavigationIcon = assets.MenuIcon + nav := component.NewNav(utils.AppName, values.StrAppDescription) + + return &Router{ + modalLayer: modal, + navDrawer: component.ModalNavFrom(&nav, modal), + menuBar: bar, + } +} + +// SetTemporaryPage sets a one-time-use then throw-away page after the current +// window is invalidated. Temp pages should not be registered or pushed to the +// page stack. +func (r *Router) SetTemporaryPage(p Page) { + if p != nil { + r.mu.Lock() + r.tempPage = p + r.mu.Unlock() + } +} + +// DisableTemporaryPage disables the currently set temporary page enabling the +// regular pages already registered to be displayed. +func (r *Router) DisableTemporaryPage() { + r.mu.Lock() + r.tempPage = nil + r.mu.Unlock() +} + +// Register receives all the pages that will be regulary accessed. +func (r *Router) Register(pages ...Page) { + r.mu.Lock() + defer r.mu.Unlock() + + if r.pageStack != nil { + // Page stack has already been populated + return + } + + r.registeredPages = make(map[string]Page, len(pages)) + for _, p := range pages { + if r.currentPage == nil { + r.currentPage = p + } + + r.registeredPages[p.ID()] = p + navItem := p.NavItem() + if navItem != nil { + r.navDrawer.AddNavItem(*navItem) + } + } +} + +func (r *Router) SetBackNavButton() { + r.mu.Lock() + r.menuBar.NavigationIcon = assets.BackIcon + r.mu.Unlock() +} + +// OnDisplay appends the passed page onto of the current page stack. It helps +// maintain page access history for future backtracking if need be. +func (r *Router) OnDisplay(pageID interface{}) { + r.mu.RLock() + str, _ := pageID.(string) + p, ok := r.registeredPages[str] + if !ok { + r.mu.RUnlock() + return + } + + if r.currentPage != nil && p.ID() == r.currentPage.ID() { + r.mu.RUnlock() + return + } + + previousPage := r.currentPage + currentPage := p + r.mu.RUnlock() + + r.mu.Lock() + r.currentPage = p + r.pageStack = append(r.pageStack, p.ID()) + r.mu.Unlock() + + // execute the previous page OnSwitchFrom() method without mutex protection. + previousPage.OnSwitchFrom() + + // execute the current page OnSwitchTo() method without mutex protection. + currentPage.OnSwitchTo() +} + +// OnDisplayNew clears the current page stuck before pushing the passed page +// as the only one in the stack. +func (r *Router) OnDisplayNew(pageID interface{}) { + r.mu.Lock() + // empty out the previous pages. + r.pageStack = r.pageStack[:0] + r.mu.Unlock() + + r.OnDisplay(pageID) +} + +func (r *Router) ProcessEvents() { + r.mu.Lock() + // if current page isn't sent ignore processing the events. + if r.currentPage == nil { + return + } + + if r.menuBar.NavigationButton.Clicked() { + if r.menuBar.NavigationIcon == assets.BackIcon { + count := len(r.pageStack) + if count > 0 { + strPageID := r.pageStack[count-1] + r.currentPage = r.registeredPages[strPageID] + } + } else { + r.navDrawer.Appear(time.Now()) + } + } + + if r.navDrawer.NavDestinationChanged() { + strPageID, _ := r.navDrawer.CurrentNavDestination().(string) + p, ok := r.registeredPages[strPageID] + if ok { + r.currentPage = p + } + } + currentPage := r.currentPage + r.mu.Unlock() + + // execute the HandleEvents() method without mutex protection. + currentPage.HandleEvents() +} + +// AddBackButton set the bar navigation button to back arrow enabling pages +// backtracking. +func (r *Router) AddBackButton() { + r.mu.Lock() + r.menuBar.NavigationIcon = assets.BackIcon + r.mu.Unlock() +} + +// Layout handles ploting the componnets of the current page by calling the +// actual page Layout method. +func (r *Router) Layout(gtx C, th *material.Theme) D { + // Before we enter into a mutex lock state, check that all the previous + // Layout() instance are not running. If affirmative return an empty page. + if !atomic.CompareAndSwapUint32(&layoutRunner, 0, 1) { + return D{} + } + + r.mu.Lock() + defer func() { + r.mu.Unlock() + + // Reset the layout runner allowing other layout updates to proceed. + atomic.StoreUint32(&layoutRunner, 0) + }() + + // If a temporary page is set display it instead. + if r.tempPage != nil { + return r.tempPage.Layout(gtx, th) + } + + return layout.Flex{ + Axis: layout.Vertical, + Spacing: layout.SpaceSides, + }.Layout(gtx, + // Top bar + layout.Rigid(func(gtx C) D { + navItem := r.currentPage.NavItem() + if navItem != nil { + r.menuBar.Title = navItem.Name + } + return r.menuBar.Layout(gtx, th, "Menu", "Actions") + }), + + // Navigation bar + layout.Rigid(func(gtx C) D { + return r.modalLayer.Layout(gtx, th) + }), + + // Page content. + layout.Flexed(1, func(gtx C) D { + if r.currentPage == nil { + return D{} + } + return r.currentPage.Layout(gtx, th) + }), + ) +} diff --git a/poa/lotus/ui/utils/component.go b/poa/lotus/ui/utils/component.go new file mode 100644 index 0000000..4c7a01b --- /dev/null +++ b/poa/lotus/ui/utils/component.go @@ -0,0 +1,50 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package utils + +import ( + "fmt" + "image/color" +) + +const ( + AppName = "LOTUS" + + FEEDBACK_PAGE_ID = "FEEDBACK_PAGE" + HOME_PAGE_ID = "HOME_PAGE" + ABOUT_PAGE_ID = "ABOUT_PAGE" + ACCOUNT_PAGE_ID = "ACCOUNT_PAGE" + SIGNUP_PAGE_ID = "SIGNUP_PAGE" + SPLASH_PAGE_ID = "SPLASH_PAGE" +) + +// AppVersion defines the current semantic version of the lotus application. +var AppVersion = &version{ + Major: 0, + Minor: 0, + Patch: 1, +} + +var ( + // Application colour scheme. + PrimaryColor = color.NRGBA{R: 98, G: 0, B: 238, A: 255} // #6200EE + DarkPriColor = color.NRGBA{R: 55, G: 0, B: 179, A: 255} // #3700B3 + SecondaryColor = color.NRGBA{R: 3, G: 218, B: 198, A: 255} // #03DAC6 + DarkSecColor = color.NRGBA{R: 1, G: 135, B: 134, A: 255} // #018786 + ErrorColor = color.NRGBA{R: 176, G: 0, B: 32, A: 255} // #B00020 + SurfaceColor = color.NRGBA{R: 255, G: 255, B: 255, A: 255} // #FFFFFF + BlackColor = color.NRGBA{R: 0, G: 0, B: 0, A: 255} // #000000 + HighlightColor = color.NRGBA{R: 187, G: 134, B: 252} // #BB86FC +) + +type version struct { + Major uint8 + Minor uint8 + Patch uint8 +} + +// String implements the stringer interface for struct version. +func (v *version) String() string { + return fmt.Sprintf("%s v%d.%d.%d", AppName, v.Major, v.Minor, v.Patch) +} diff --git a/poa/lotus/ui/utils/values/en_localize.go b/poa/lotus/ui/utils/values/en_localize.go new file mode 100644 index 0000000..bb66376 --- /dev/null +++ b/poa/lotus/ui/utils/values/en_localize.go @@ -0,0 +1,13 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package values + +const ( + strAppDescription = "PoA to Dhamana Protocol." + strAccount = "Account" + strHome = "Home" + strAbout = "About this library" + strFeedback = "Feedback" + strLoading = "Loading the Application" +) diff --git a/poa/lotus/ui/utils/values/strings.go b/poa/lotus/ui/utils/values/strings.go new file mode 100644 index 0000000..876406c --- /dev/null +++ b/poa/lotus/ui/utils/values/strings.go @@ -0,0 +1,13 @@ +// Copyright (c) 2023 Migwi Ndung'u +// See LICENSE for details. + +package values + +const ( + StrAppDescription = strAppDescription + StrHome = strHome + StrAccount = strAccount + StrAbout = strAbout + StrFeedback = strFeedback + StrLoading = strLoading +)