1package home
2
3import (
4 "sort"
5 "std"
6 "strings"
7
8 "gno.land/p/demo/avl"
9 "gno.land/p/demo/ownable"
10 "gno.land/p/demo/ufmt"
11 "gno.land/r/demo/users"
12 "gno.land/r/leon/hof"
13
14 "gno.land/r/stefann/registry"
15)
16
17type City struct {
18 Name string
19 URL string
20}
21
22type Sponsor struct {
23 Address std.Address
24 Amount std.Coins
25}
26
27type Profile struct {
28 aboutMe []string
29}
30
31type Travel struct {
32 cities []City
33 currentCityIndex int
34 jarLink string
35}
36
37type Sponsorship struct {
38 maxSponsors int
39 sponsors *avl.Tree
40 DonationsCount int
41 sponsorsCount int
42}
43
44var (
45 profile Profile
46 travel Travel
47 sponsorship Sponsorship
48 owner *ownable.Ownable
49)
50
51func init() {
52 owner = ownable.NewWithAddress(registry.MainAddr())
53 hof.Register()
54
55 profile = Profile{
56 aboutMe: []string{
57 `## About Me`,
58 `### 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!`,
59
60 `## Contributions`,
61 `### I'm just getting started, but you can follow my journey through gno.land right [here](https://github.com/gnolang/hackerspace/issues/94) 🔗`,
62 },
63 }
64
65 travel = Travel{
66 cities: []City{
67 {Name: "Venice", URL: "https://i.ibb.co/1mcZ7b1/venice.jpg"},
68 {Name: "Tokyo", URL: "https://i.ibb.co/wNDJv3H/tokyo.jpg"},
69 {Name: "São Paulo", URL: "https://i.ibb.co/yWMq2Sn/sao-paulo.jpg"},
70 {Name: "Toronto", URL: "https://i.ibb.co/pb95HJB/toronto.jpg"},
71 {Name: "Bangkok", URL: "https://i.ibb.co/pQy3w2g/bangkok.jpg"},
72 {Name: "New York", URL: "https://i.ibb.co/6JWLm0h/new-york.jpg"},
73 {Name: "Paris", URL: "https://i.ibb.co/q9vf6Hs/paris.jpg"},
74 {Name: "Kandersteg", URL: "https://i.ibb.co/60DzywD/kandersteg.jpg"},
75 {Name: "Rothenburg", URL: "https://i.ibb.co/cr8d2rQ/rothenburg.jpg"},
76 {Name: "Capetown", URL: "https://i.ibb.co/bPGn0v3/capetown.jpg"},
77 {Name: "Sydney", URL: "https://i.ibb.co/TBNzqfy/sydney.jpg"},
78 {Name: "Oeschinen Lake", URL: "https://i.ibb.co/QJQwp2y/oeschinen-lake.jpg"},
79 {Name: "Barra Grande", URL: "https://i.ibb.co/z4RXKc1/barra-grande.jpg"},
80 {Name: "London", URL: "https://i.ibb.co/CPGtvgr/london.jpg"},
81 },
82 currentCityIndex: 0,
83 jarLink: "https://TODO", // This value should be injected through UpdateJarLink after deployment.
84 }
85
86 sponsorship = Sponsorship{
87 maxSponsors: 3,
88 sponsors: avl.NewTree(),
89 DonationsCount: 0,
90 sponsorsCount: 0,
91 }
92}
93
94func UpdateCities(newCities []City) {
95 owner.AssertCallerIsOwner()
96 travel.cities = newCities
97}
98
99func AddCities(newCities ...City) {
100 owner.AssertCallerIsOwner()
101
102 travel.cities = append(travel.cities, newCities...)
103}
104
105func UpdateJarLink(newLink string) {
106 owner.AssertCallerIsOwner()
107 travel.jarLink = newLink
108}
109
110func UpdateAboutMe(aboutMeStr string) {
111 owner.AssertCallerIsOwner()
112 profile.aboutMe = strings.Split(aboutMeStr, "|")
113}
114
115func AddAboutMeRows(newRows ...string) {
116 owner.AssertCallerIsOwner()
117
118 profile.aboutMe = append(profile.aboutMe, newRows...)
119}
120
121func UpdateMaxSponsors(newMax int) {
122 owner.AssertCallerIsOwner()
123 if newMax <= 0 {
124 panic("maxSponsors must be greater than zero")
125 }
126 sponsorship.maxSponsors = newMax
127}
128
129func Donate() {
130 address := std.GetOrigCaller()
131 amount := std.GetOrigSend()
132
133 if amount.AmountOf("ugnot") == 0 {
134 panic("Donation must include GNOT")
135 }
136
137 existingAmount, exists := sponsorship.sponsors.Get(address.String())
138 if exists {
139 updatedAmount := existingAmount.(std.Coins).Add(amount)
140 sponsorship.sponsors.Set(address.String(), updatedAmount)
141 } else {
142 sponsorship.sponsors.Set(address.String(), amount)
143 sponsorship.sponsorsCount++
144 }
145
146 travel.currentCityIndex++
147 sponsorship.DonationsCount++
148
149 banker := std.GetBanker(std.BankerTypeRealmSend)
150 ownerAddr := registry.MainAddr()
151 banker.SendCoins(std.CurrentRealm().Addr(), ownerAddr, banker.GetCoins(std.CurrentRealm().Addr()))
152}
153
154type SponsorSlice []Sponsor
155
156func (s SponsorSlice) Len() int {
157 return len(s)
158}
159
160func (s SponsorSlice) Less(i, j int) bool {
161 return s[i].Amount.AmountOf("ugnot") > s[j].Amount.AmountOf("ugnot")
162}
163
164func (s SponsorSlice) Swap(i, j int) {
165 s[i], s[j] = s[j], s[i]
166}
167
168func GetTopSponsors() []Sponsor {
169 var sponsorSlice SponsorSlice
170
171 sponsorship.sponsors.Iterate("", "", func(key string, value interface{}) bool {
172 addr := std.Address(key)
173 amount := value.(std.Coins)
174 sponsorSlice = append(sponsorSlice, Sponsor{Address: addr, Amount: amount})
175 return false
176 })
177
178 sort.Sort(sponsorSlice)
179 return sponsorSlice
180}
181
182func GetTotalDonations() int {
183 total := 0
184 sponsorship.sponsors.Iterate("", "", func(key string, value interface{}) bool {
185 total += int(value.(std.Coins).AmountOf("ugnot"))
186 return false
187 })
188 return total
189}
190
191func Render(path string) string {
192 out := ufmt.Sprintf("# Exploring %s!\n\n", travel.cities[travel.currentCityIndex].Name)
193
194 out += renderAboutMe()
195 out += "\n\n"
196 out += renderTips()
197
198 return out
199}
200
201func renderAboutMe() string {
202 out := ""
203
204 out += ufmt.Sprintf("![Current Location](%s)\n\n", travel.cities[travel.currentCityIndex%len(travel.cities)].URL)
205
206 for _, rows := range profile.aboutMe {
207 out += rows + "\n\n"
208 }
209
210 return out
211}
212
213func renderTips() string {
214 out := "# Help Me Travel The World\n\n"
215
216 out += ufmt.Sprintf("## I am currently in %s, tip the jar to send me somewhere else!\n\n", travel.cities[travel.currentCityIndex].Name)
217 out += "### **Click** the jar, **tip** in GNOT coins, and **watch** my background change as I head to a new adventure!\n\n"
218
219 out += renderTipsJar() + "\n\n"
220
221 out += renderSponsors()
222
223 return out
224}
225
226func formatAddress(address string) string {
227 if len(address) <= 8 {
228 return address
229 }
230 return address[:4] + "..." + address[len(address)-4:]
231}
232
233func getDisplayName(addr std.Address) string {
234 if user := users.GetUserByAddress(addr); user != nil {
235 return user.Name
236 }
237 return formatAddress(addr.String())
238}
239
240func formatAmount(amount std.Coins) string {
241 ugnot := amount.AmountOf("ugnot")
242 if ugnot >= 1000000 {
243 gnot := float64(ugnot) / 1000000
244 return ufmt.Sprintf("`%v`*GNOT*", gnot)
245 }
246 return ufmt.Sprintf("`%d`*ugnot*", ugnot)
247}
248
249func renderSponsors() string {
250 out := "## Sponsor Leaderboard\n\n"
251
252 if sponsorship.sponsorsCount == 0 {
253 return out + "No sponsors yet. Be the first to tip the jar!\n"
254 }
255
256 topSponsors := GetTopSponsors()
257 numSponsors := len(topSponsors)
258 if numSponsors > sponsorship.maxSponsors {
259 numSponsors = sponsorship.maxSponsors
260 }
261
262 for i := 0; i < numSponsors; i++ {
263 sponsor := topSponsors[i]
264 position := ""
265 switch i {
266 case 0:
267 position = "🥇"
268 case 1:
269 position = "🥈"
270 case 2:
271 position = "🥉"
272 default:
273 position = ufmt.Sprintf("%d.", i+1)
274 }
275
276 out += ufmt.Sprintf("%s **%s** - %s\n\n",
277 position,
278 getDisplayName(sponsor.Address),
279 formatAmount(sponsor.Amount),
280 )
281 }
282
283 return out + "\n"
284}
285
286func renderTipsJar() string {
287 return ufmt.Sprintf("[![Tips Jar](https://i.ibb.co/4TH9zbw/tips-jar.png)](%s)", travel.jarLink)
288}
home.gno
6.84 Kb · 288 lines