Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(examples): add interactive realm r/stefann/home #2918

Merged
merged 25 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
54fd216
Add stefann namespace containing config and home realms
stefann-01 Oct 6, 2024
70d8ac3
Finalize home realm logic for donations and dynamic city rendering
stefann-01 Oct 7, 2024
47c0b81
Run make fmt -C ./examples
stefann-01 Oct 7, 2024
f05370c
Merge branch 'master' into master
stefann-01 Oct 8, 2024
dbda3cf
Merge branch 'master' into master
stefann-01 Oct 10, 2024
aeb8340
Refactor ownership management and clean up home package
stefann-01 Oct 11, 2024
b79ba55
Run gno mod tidy, fmt, and clean up bugs
stefann-01 Oct 11, 2024
b0ae489
Simplify totalDonated addition logic
stefann-01 Oct 11, 2024
d5c2fe4
Rename Registar to Registry
stefann-01 Oct 11, 2024
f37732a
Refactor Sponsorship Logic, Optimize Sorting, and Group Data Structures
stefann-01 Oct 14, 2024
139c8d3
Re-add UpdateMaxSponsors and refactor donations logic
stefann-01 Oct 14, 2024
1514a95
Merge remote-tracking branch 'upstream/master'
stefann-01 Oct 15, 2024
2fa6e14
Do gno mod tidy in registry
stefann-01 Oct 15, 2024
724f0e2
Merge branch 'master' into master
stefann-01 Oct 16, 2024
2ed45e3
Merge branch 'master' into master
stefann-01 Oct 16, 2024
22531c7
Simplify sponsor rendering logic for empty sponsor list
stefann-01 Oct 16, 2024
6cb50b7
Replace sponsors map with AVL tree for efficiency
stefann-01 Oct 17, 2024
3b1a905
Add functions to append cities and aboutMe rows, and add unit tests
stefann-01 Oct 17, 2024
32da9d1
Merge branch 'master' into master
stefann-01 Oct 17, 2024
9beaa83
Merge branch 'master' into master
stefann-01 Oct 18, 2024
22f9526
Merge branch 'master' into master
stefann-01 Oct 18, 2024
17c5e11
Remove redundant else in renderSponsors
stefann-01 Oct 22, 2024
73c9b7b
Update tests to use TestAddress where possible
stefann-01 Oct 22, 2024
7ff21f6
Merge remote-tracking branch 'upstream/master'
stefann-01 Oct 22, 2024
4f51b93
Merge branch 'master' into master
stefann-01 Oct 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions examples/gno.land/r/stefann/home/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module gno.land/r/stefann/home

require (
gno.land/p/demo/avl v0.0.0-latest
gno.land/p/demo/ownable v0.0.0-latest
gno.land/p/demo/ufmt v0.0.0-latest
gno.land/r/stefann/registry v0.0.0-latest
)
304 changes: 304 additions & 0 deletions examples/gno.land/r/stefann/home/home.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
package home
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved

import (
"sort"
"std"
"strings"

"gno.land/p/demo/avl"
"gno.land/p/demo/ownable"
"gno.land/p/demo/ufmt"

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

type City struct {
Name string
URL string
}

type Sponsor struct {
Address std.Address
Amount std.Coins
}

type Profile struct {
pfp string
aboutMe []string
}

type Travel struct {
cities []City
currentCityIndex int
jarLink string
}

type Sponsorship struct {
maxSponsors int
sponsors *avl.Tree
DonationsCount int
sponsorsCount int
}

var (
profile Profile
travel Travel
sponsorship Sponsorship
owner *ownable.Ownable
)

func init() {
owner = ownable.NewWithAddress(registry.MainAddr())

profile = Profile{
pfp: "https://i.ibb.co/Bc5YNCx/DSC-0095a.jpg",
aboutMe: []string{
`### About Me`,
`Hey there! I’m Stefan, a student of Computer Science. I’m all about exploring and adventure — whether it’s diving into the latest tech or discovering a new city, I’m always up for the challenge!`,

`### Contributions`,
`I'm just getting started, but you can follow my journey through Gno.land right [here](https://github.com/gnolang/hackerspace/issues/94) 🔗`,
},
}

travel = Travel{
cities: []City{
{Name: "Venice", URL: "https://i.ibb.co/1mcZ7b1/venice.jpg"},
{Name: "Tokyo", URL: "https://i.ibb.co/wNDJv3H/tokyo.jpg"},
{Name: "São Paulo", URL: "https://i.ibb.co/yWMq2Sn/sao-paulo.jpg"},
{Name: "Toronto", URL: "https://i.ibb.co/pb95HJB/toronto.jpg"},
{Name: "Bangkok", URL: "https://i.ibb.co/pQy3w2g/bangkok.jpg"},
{Name: "New York", URL: "https://i.ibb.co/6JWLm0h/new-york.jpg"},
{Name: "Paris", URL: "https://i.ibb.co/q9vf6Hs/paris.jpg"},
{Name: "Kandersteg", URL: "https://i.ibb.co/60DzywD/kandersteg.jpg"},
{Name: "Rothenburg", URL: "https://i.ibb.co/cr8d2rQ/rothenburg.jpg"},
{Name: "Capetown", URL: "https://i.ibb.co/bPGn0v3/capetown.jpg"},
{Name: "Sydney", URL: "https://i.ibb.co/TBNzqfy/sydney.jpg"},
{Name: "Oeschinen Lake", URL: "https://i.ibb.co/QJQwp2y/oeschinen-lake.jpg"},
{Name: "Barra Grande", URL: "https://i.ibb.co/z4RXKc1/barra-grande.jpg"},
{Name: "London", URL: "https://i.ibb.co/CPGtvgr/london.jpg"},
},
currentCityIndex: 0,
jarLink: "https://TODO", // This value should be injected through UpdateJarLink after deployment.
}

sponsorship = Sponsorship{
maxSponsors: 5,
sponsors: avl.NewTree(),
DonationsCount: 0,
sponsorsCount: 0,
}
}

func UpdateCities(newCities []City) {
owner.AssertCallerIsOwner()
travel.cities = newCities
}

func AddCities(newCities ...City) {
owner.AssertCallerIsOwner()

travel.cities = append(travel.cities, newCities...)
}

func UpdateJarLink(newLink string) {
owner.AssertCallerIsOwner()
travel.jarLink = newLink
}

func UpdatePFP(url string) {
owner.AssertCallerIsOwner()
profile.pfp = url
}

func UpdateAboutMe(aboutMeStr string) {
owner.AssertCallerIsOwner()
profile.aboutMe = strings.Split(aboutMeStr, "|")
}

func AddAboutMeRows(newRows ...string) {
owner.AssertCallerIsOwner()

profile.aboutMe = append(profile.aboutMe, newRows...)
}

func UpdateMaxSponsors(newMax int) {
owner.AssertCallerIsOwner()
if newMax <= 0 {
panic("maxSponsors must be greater than zero")
}
sponsorship.maxSponsors = newMax
}

func Donate() {
address := std.GetOrigCaller()
amount := std.GetOrigSend()

if amount.AmountOf("ugnot") == 0 {
panic("Donation must include GNOT")
}

existingAmount, exists := sponsorship.sponsors.Get(address.String())
if exists {
updatedAmount := existingAmount.(std.Coins).Add(amount)
sponsorship.sponsors.Set(address.String(), updatedAmount)
} else {
sponsorship.sponsors.Set(address.String(), amount)
sponsorship.sponsorsCount++
}

travel.currentCityIndex++
sponsorship.DonationsCount++

banker := std.GetBanker(std.BankerTypeRealmSend)
ownerAddr := registry.MainAddr()
banker.SendCoins(std.CurrentRealm().Addr(), ownerAddr, banker.GetCoins(std.CurrentRealm().Addr()))
}

type SponsorSlice []Sponsor

func (s SponsorSlice) Len() int {
return len(s)
}

func (s SponsorSlice) Less(i, j int) bool {
return s[i].Amount.AmountOf("ugnot") > s[j].Amount.AmountOf("ugnot")
}

func (s SponsorSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

func GetTopSponsors() []Sponsor {
var sponsorSlice SponsorSlice

sponsorship.sponsors.Iterate("", "", func(key string, value interface{}) bool {
addr := std.Address(key)
amount := value.(std.Coins)
sponsorSlice = append(sponsorSlice, Sponsor{Address: addr, Amount: amount})
return false
})

sort.Sort(sponsorSlice)
return sponsorSlice
}

func GetTotalDonations() int {
total := 0
sponsorship.sponsors.Iterate("", "", func(key string, value interface{}) bool {
total += int(value.(std.Coins).AmountOf("ugnot"))
return false
})
return total
}

func Render(path string) string {
out := ufmt.Sprintf("# Exploring %s!\n\n", travel.cities[travel.currentCityIndex].Name)

out += renderAboutMe()
out += "\n\n"
out += renderTips()

return out
}

func renderAboutMe() string {
out := "<div class='rows-3'>"

out += "<div style='position: relative; text-align: center;'>\n\n"

out += ufmt.Sprintf("<div style='background-image: url(%s); background-size: cover; background-position: center; width: 100%%; height: 600px; position: relative; border-radius: 15px; overflow: hidden;'>\n\n", travel.cities[travel.currentCityIndex%len(travel.cities)].URL)

out += ufmt.Sprintf("<img src='%s' alt='my profile pic' style='width: 250px; height: auto; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 50%%; border: 3px solid #1e1e1e; position: absolute; top: 75%%; left: 50%%; transform: translate(-50%%, -50%%);'>\n\n", profile.pfp)

out += "</div>\n\n"

for _, rows := range profile.aboutMe {
out += "<div>\n\n"
out += rows + "\n\n"
out += "</div>\n\n"
}

out += "</div><!-- /rows-3 -->\n\n"

return out
}

func renderTips() string {
out := `<div class="jumbotron" style="display: flex; flex-direction: column; justify-content: flex-start; align-items: center; padding-top: 40px; padding-bottom: 50px; text-align: center;">` + "\n\n"

out += `<div class="rows-2" style="max-width: 500px; width: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center;">` + "\n"

out += `<h1 style="margin-bottom: 50px;">Help Me Travel The World</h1>` + "\n\n"

out += renderTipsJar() + "\n"

out += ufmt.Sprintf(`<strong style="font-size: 1.2em;">I am currently in %s, <br> tip the jar to send me somewhere else!</strong>`, travel.cities[travel.currentCityIndex].Name)

out += `<br><span style="font-size: 1.2em; font-style: italic; margin-top: 10px; display: inline-block;">Click the jar, tip in GNOT coins, and watch my background change as I head to a new adventure!</span></p>` + "\n\n"

out += renderSponsors()

out += `</div><!-- /rows-2 -->` + "\n\n"

out += `</div><!-- /jumbotron -->` + "\n"

return out
}

func formatAddress(address string) string {
if len(address) <= 8 {
return address
}
return address[:4] + "..." + address[len(address)-4:]
}

func renderSponsors() string {
out := `<h3 style="margin-top: 5px; margin-bottom: 20px">Sponsor Leaderboard</h3>` + "\n"

if sponsorship.sponsorsCount == 0 {
return out + `<p style="text-align: center;">No sponsors yet. Be the first to tip the jar!</p>` + "\n"
} else {
topSponsors := GetTopSponsors()
numSponsors := len(topSponsors)
if numSponsors > sponsorship.maxSponsors {
numSponsors = sponsorship.maxSponsors
}

out += `<ul style="list-style-type: none; padding: 0; border: 1px solid #ddd; border-radius: 8px; width: 100%; max-width: 300px; margin: 0 auto;">` + "\n"

for i := 0; i < numSponsors; i++ {
sponsor := topSponsors[i]
isLastItem := (i == numSponsors-1)

padding := "10px 5px"
border := "border-bottom: 1px solid #ddd;"

if isLastItem {
padding = "8px 5px"
border = ""
}

out += ufmt.Sprintf(
`<li style="padding: %s; %s text-align: left;">
<strong style="padding-left: 5px;">%d. %s</strong>
<span style="float: right; padding-right: 5px;">%s</span>
</li>`,
padding, border, i+1, formatAddress(sponsor.Address.String()), sponsor.Amount.String(),
)
}

}

return out
}

func renderTipsJar() string {
out := ufmt.Sprintf(`<a href="%s" target="_blank" style="display: block; text-decoration: none;">`, travel.jarLink) + "\n"

out += `<img src="https://i.ibb.co/4TH9zbw/tips-jar.png" alt="Tips Jar" style="width: 300px; height: auto; display: block; margin: 0 auto;">` + "\n"

out += `</a>` + "\n"

return out
}
Loading
Loading