Search Apps Documentation Source Content File Folder Download Copy

home.gno

6.84 Kb · 288 lines
  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}