forked from gnolang/gno
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(examples): Ivan's registry, home realm (gnolang#3354)
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
1 parent
3adb2ac
commit bd1d104
Showing
5 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/r/ursulovic/home |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/r/ursulovic/registry |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |