Skip to content

Commit

Permalink
feat(examples): Ivan's registry, home realm (gnolang#3354)
Browse files Browse the repository at this point in the history
Created registry for further development of realms, finalising home
realm.

---------

Co-authored-by: Ivan Ursulovic <[email protected]>
Co-authored-by: Ivan Ursulovic <[email protected]>
Co-authored-by: Morgan <[email protected]>
  • Loading branch information
4 people authored Jan 14, 2025
1 parent 3adb2ac commit bd1d104
Show file tree
Hide file tree
Showing 5 changed files with 317 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/gno.land/r/ursulovic/home/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/ursulovic/home
159 changes: 159 additions & 0 deletions examples/gno.land/r/ursulovic/home/home.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package home

import (
"std"
"strconv"
"strings"

"gno.land/p/demo/ownable"
"gno.land/p/moul/md"
"gno.land/r/leon/hof"

"gno.land/r/ursulovic/registry"
)

var (
aboutMe string
selectedImage string
Ownable *ownable.Ownable

githubUrl string
linkedinUrl string
connectUrl string
imageUpdatePrice int64

isValidUrl func(string) bool
)

func init() {
Ownable = ownable.NewWithAddress(registry.MainAddress())

aboutMe = "Hi, I'm Ivan Ursulovic, a computer engineering graduate, blockchain enthusiast, and backend developer specializing in ASP.NET. I love learning new things and taking on challenges."
selectedImage = "https://i.ibb.co/W28NPkw/beograd.webp"

githubUrl = "https://github.com/ursulovic"
linkedinUrl = "https://www.linkedin.com/in/ivan-ursulovic-953310190/"
imageUpdatePrice = 5000000
isValidUrl = defaultURLValidation
hof.Register()
}

func Render(s string) string {
var sb strings.Builder
sb.WriteString(renderAboutMe())
sb.WriteString(renderSelectedImage())
sb.WriteString(renderContactsUrl())
return sb.String()
}

func defaultURLValidation(url string) bool {
const urlPrefix string = "https://i.ibb.co/"

if !strings.HasPrefix(url, urlPrefix) {
return false
}

if !(strings.HasSuffix(url, ".jpg") ||
strings.HasSuffix(url, ".png") ||
strings.HasSuffix(url, ".gif") ||
strings.HasSuffix(url, ".webp")) {
return false
}

urlPath := strings.TrimPrefix(url, "https://i.ibb.co/")
parts := strings.Split(urlPath, "/")

if len(parts) != 2 || len(parts[0]) == 0 || len(parts[1]) == 0 {
return false
}

return true
}

func UpdateSelectedImage(url string) {
if !isValidUrl(url) {
panic("Url is not valid!")
}

sentCoins := std.GetOrigSend()

if len(sentCoins) != 1 && sentCoins.AmountOf("ugnot") == imageUpdatePrice {
panic("Please send exactly " + strconv.Itoa(int(imageUpdatePrice)) + " ugnot")
}

selectedImage = url
}

func renderSelectedImage() string {
var sb strings.Builder

sb.WriteString(md.HorizontalRule())
sb.WriteString("\n")

sb.WriteString(md.H2("📸 Featured Image"))
sb.WriteString("\n")

sb.WriteString(md.Image("", selectedImage))
sb.WriteString("\n")

sb.WriteString(md.H4("✨ " + md.Link("Change this image for "+strconv.Itoa(int(imageUpdatePrice/1000000))+" GNOT. To update, set a direct image URL from ImgBB.", "https://gno.studio/connect/view/gno.land/r/ursulovic/home?network=portal-loop") + " ✨"))

return sb.String()
}

func renderAboutMe() string {
var sb strings.Builder

sb.WriteString(md.H1("👋 Welcome to Ivan's Homepage!"))
sb.WriteString("\n")

sb.WriteString(md.H2("👨‍💻 About Me"))
sb.WriteString("\n")

sb.WriteString(md.Blockquote(aboutMe))

return sb.String()
}

func renderContactsUrl() string {
var sb strings.Builder

sb.WriteString(md.HorizontalRule())
sb.WriteString("\n")

sb.WriteString(md.H2("🔗 Let's Connect"))
sb.WriteString("\n")

items := []string{
"🐙 " + md.Link("GitHub", githubUrl),
"💼 " + md.Link("LinkedIn", linkedinUrl),
}
sb.WriteString(md.BulletList(items))

return sb.String()
}

func UpdateGithubUrl(url string) {
Ownable.AssertCallerIsOwner()
githubUrl = url
}

func UpdateLinkedinUrl(url string) {
Ownable.AssertCallerIsOwner()
linkedinUrl = url
}

func UpdateAboutMe(text string) {
Ownable.AssertCallerIsOwner()
aboutMe = text
}

func UpdateImagePrice(newPrice int64) {
Ownable.AssertCallerIsOwner()
imageUpdatePrice = newPrice
}

func UpdateIsValidUrlFunction(f func(string) bool) {
Ownable.AssertCallerIsOwner()
isValidUrl = f
}
97 changes: 97 additions & 0 deletions examples/gno.land/r/ursulovic/home/home_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package home

import (
"std"
"testing"

"gno.land/p/demo/testutils"
)

func TestUpdateGithubUrl(t *testing.T) {
caller := std.Address("g1d24j8fwnc0w5q427fauyey4gdd30qgu69k6n0x")
std.TestSetOrigCaller(caller)

newUrl := "https://github.com/example"

UpdateGithubUrl(newUrl)

if githubUrl != newUrl {
t.Fatalf("GitHub url not updated properly!")
}
}

func TestUpdateLinkedinUrl(t *testing.T) {
caller := std.Address("g1d24j8fwnc0w5q427fauyey4gdd30qgu69k6n0x")
std.TestSetOrigCaller(caller)

newUrl := "https://www.linkedin.com/in/example"

UpdateGithubUrl(newUrl)

if githubUrl != newUrl {
t.Fatalf("LinkedIn url not updated properly!")
}
}

func TestUpdateAboutMe(t *testing.T) {
caller := std.Address("g1d24j8fwnc0w5q427fauyey4gdd30qgu69k6n0x")
std.TestSetOrigCaller(caller)

newAboutMe := "This is new description!"

UpdateAboutMe(newAboutMe)

if aboutMe != newAboutMe {
t.Fatalf("About mew not updated properly!")
}
}

func TestUpdateSelectedImage(t *testing.T) {
var user = testutils.TestAddress("user")
std.TestSetOrigCaller(user)

validImageUrl := "https://i.ibb.co/hLtmnX0/beautiful-rain-forest-ang-ka-nature-trail-doi-inthanon-national-park-thailand-36703721.webp"

coinsSent := std.NewCoins(std.NewCoin("ugnot", 5000000)) // Update to match the price expected by your function
std.TestSetOrigSend(coinsSent, std.NewCoins())

UpdateSelectedImage(validImageUrl)

if selectedImage != validImageUrl {
t.Fatalf("Valid image URL rejected!")
}

invalidImageUrl := "https://ibb.co/Kb3rQNn"

defer func() {
if r := recover(); r == nil {
t.Fatalf("Expected panic for invalid image URL, but got no panic")
}
}()

UpdateSelectedImage(invalidImageUrl)

invalidCoins := std.NewCoins(std.NewCoin("ugnot", 1000000))
std.TestSetOrigSend(invalidCoins, std.NewCoins())

defer func() {
if r := recover(); r == nil {
t.Fatalf("Expected panic for incorrect coin denomination or amount, but got no panic")
}
}()

UpdateSelectedImage(validImageUrl)
}

func TestUpdateImagePrice(t *testing.T) {
caller := std.Address("g1d24j8fwnc0w5q427fauyey4gdd30qgu69k6n0x")
std.TestSetOrigCaller(caller)

var newImageUpdatePrice int64 = 3000000

UpdateImagePrice(newImageUpdatePrice)

if imageUpdatePrice != newImageUpdatePrice {
t.Fatalf("Image update price not updated properly!")
}
}
1 change: 1 addition & 0 deletions examples/gno.land/r/ursulovic/registry/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/ursulovic/registry
59 changes: 59 additions & 0 deletions examples/gno.land/r/ursulovic/registry/registry.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package registry

import (
"errors"
"std"
)

var (
mainAddress std.Address
backupAddress std.Address

ErrInvalidAddr = errors.New("Ivan's registry: Invalid address")
ErrUnauthorized = errors.New("Ivan's registry: Unauthorized")
)

func init() {
mainAddress = "g1d24j8fwnc0w5q427fauyey4gdd30qgu69k6n0x"
backupAddress = "g1mw2xft3eava9kfhqw3fjj3kkf3pkammty0mtv7"
}

func MainAddress() std.Address {
return mainAddress
}

func BackupAddress() std.Address {
return backupAddress
}

func SetMainAddress(addr std.Address) error {
assertAuthorized()

if !addr.IsValid() {
return ErrInvalidAddr
}

mainAddress = addr
return nil
}

func SetBackupAddress(addr std.Address) error {
assertAuthorized()

if !addr.IsValid() {
return ErrInvalidAddr
}

backupAddress = addr
return nil
}

// It will stay here for now, might be useful later
func assertAuthorized() {
caller := std.PrevRealm().Addr()
isAuthorized := caller == mainAddress || caller == backupAddress

if !isAuthorized {
panic(ErrUnauthorized)
}
}

0 comments on commit bd1d104

Please sign in to comment.