Search Apps Documentation Source Content File Folder Download Copy Actions Download

render.gno

6.89 Kb · 267 lines
  1package personal_world
  2
  3import (
  4	"net/url"
  5
  6	"gno.land/p/jeronimoalbi/pager"
  7	"gno.land/p/nt/ufmt"
  8	"gno.land/r/akkadia/admin"
  9)
 10
 11// Render handles RESTful routing and returns Markdown responses
 12func Render(iUrl string) string {
 13	u, err := url.Parse(iUrl)
 14	if err != nil {
 15		return "404\n"
 16	}
 17
 18	query := u.Query()
 19
 20	switch u.Path {
 21	case "":
 22		return renderHome()
 23	case "worlds":
 24		owner := query.Get("owner")
 25		if owner != "" {
 26			return renderWorldsByOwner(iUrl, owner)
 27		}
 28		return renderWorldsList(iUrl)
 29	case "biomes":
 30		return renderBiomes()
 31	case "sizes":
 32		return renderSizes()
 33	default:
 34		return renderError("Not found")
 35	}
 36}
 37
 38// renderHome renders the home page
 39func renderHome() string {
 40	output := `# Personal World
 41
 42Welcome to the Personal World system on Akkadia.
 43
 44## What is Personal World?
 45
 46Personal World is a decentralized world creation and management system where users can:
 47
 48* Create and customize their own worlds
 49* Manage world editors and permissions
 50* Expand world sizes dynamically
 51* Set custom world types and properties
 52
 53## Getting Started
 54
 551. **Create Your First World**: Your first default-type world is **free**!
 562. **Customize**: Set name, slug and visibility
 573. **Manage**: Add editors, expand size, update properties
 584. **Explore**: Browse all worlds and discover unique creations
 59
 60## Config
 61
 62`
 63	output += ufmt.Sprintf("* **Default Biome**: %s\n", defaultBiome)
 64	output += ufmt.Sprintf("* **Fee Collector BPS**: %d (%d.%02d%%)\n", feeCollectorBPS, feeCollectorBPS/100, feeCollectorBPS%100)
 65	output += ufmt.Sprintf("* **List Limit**: %d\n", listLimit)
 66	output += ufmt.Sprintf("* **Batch Limit**: %d\n", batchLimit)
 67
 68	output += `
 69## Stats
 70
 71`
 72	output += ufmt.Sprintf("* **Total Worlds**: %d\n", worlds.Size())
 73
 74	output += `
 75## Quick Links
 76
 77* [Browse All Worlds](/r/akkadia/personal_world:worlds)
 78* [View Biomes](/r/akkadia/personal_world:biomes)
 79* [View Sizes](/r/akkadia/personal_world:sizes)
 80
 81## Search by Owner
 82
 83<gno-form path="worlds">
 84  <gno-input name="owner" placeholder="Enter owner address" />
 85</gno-form>
 86`
 87
 88	return output
 89}
 90
 91// renderBiomes renders the biomes page
 92func renderBiomes() string {
 93	output := `# Biomes
 94
 95[← Home](/r/akkadia/personal_world:)
 96
 97The system supports multiple biomes with different creation costs.
 98
 99## How Pricing Works
100
101* **cost**: The base cost to create a world with this biome
102* **priceMultiplier**: Multiplier applied to world expansion costs
103  * Example: If expansion base cost is 1000 ugnot and priceMultiplier is 1.5, actual cost = 1500 ugnot
104
105---
106
107`
108	biomeInfos.Iterate("", "", func(key string, value interface{}) bool {
109		biome := value.(map[string]string)
110
111		output += ufmt.Sprintf("### %s\n\n", biome["name"])
112
113		for k, v := range biome {
114			if k == "name" {
115				if v == defaultBiome {
116					output += ufmt.Sprintf("* **%s**: %s (default)\n", k, v)
117				} else {
118					output += ufmt.Sprintf("* **%s**: %s\n", k, v)
119				}
120			} else if k == "cost" {
121				output += ufmt.Sprintf("* **%s**: %s ugnot\n", k, v)
122			} else {
123				output += ufmt.Sprintf("* **%s**: %s\n", k, v)
124			}
125		}
126		output += "\n---\n\n"
127		return false
128	})
129
130	return output
131}
132
133// renderSizes renders the sizes page
134func renderSizes() string {
135	output := `# World Sizes
136
137[← Home](/r/akkadia/personal_world:)
138
139Worlds can be expanded to larger sizes. Each expansion has a base cost.
140
141## How Expansion Works
142
143* **size**: The world dimension (size x size blocks)
144* **cost**: Base cost to expand to this size level
145* Actual expansion cost = base cost × biome priceMultiplier
146* New worlds start at size level 0
147
148---
149
150`
151	sizeInfos.Iterate("", "", func(key string, value interface{}) bool {
152		size := value.(map[string]string)
153
154		output += ufmt.Sprintf("### Level %s\n\n", size["id"])
155		output += ufmt.Sprintf("* **Size**: %s × %s blocks\n", size["size"], size["size"])
156		output += ufmt.Sprintf("* **Base Cost**: %s ugnot\n", size["cost"])
157		output += "\n---\n\n"
158		return false
159	})
160
161	return output
162}
163
164// renderWorldsList renders a list of all worlds with pagination
165func renderWorldsList(iUrl string) string {
166	output := `# All Worlds
167
168[← Home](/r/akkadia/personal_world:)
169
170`
171
172	if worlds.Size() == 0 {
173		output += "*No worlds created yet.*\n"
174		return output
175	}
176
177	pages, err := pager.New(iUrl, worlds.Size(), pager.WithPageSize(10))
178	if err != nil {
179		return renderError("Invalid page")
180	}
181
182	// Get current page number for back links
183	currentPage := (pages.Offset() / pages.PageSize()) + 1
184
185	worlds.ReverseIterateByOffset(pages.Offset(), pages.PageSize(), func(key string, value interface{}) bool {
186		world := value.(map[string]string)
187		output += renderWorldItem(world, currentPage)
188		return false
189	})
190
191	if pages.HasPages() {
192		output += pager.Picker(pages)
193	}
194
195	return output
196}
197
198// renderWorldsByOwner renders worlds owned by a specific address with pagination
199func renderWorldsByOwner(iUrl string, owner string) string {
200	u, _ := url.Parse(iUrl)
201	page := u.Query().Get("page")
202
203	output := ufmt.Sprintf("# Personal Worlds by %s\n\n", owner)
204	if page != "" {
205		output += ufmt.Sprintf("[← Back to List](/r/akkadia/personal_world:worlds?page=%s)\n\n", page)
206	} else {
207		output += "[← Home](/r/akkadia/personal_world:)\n\n"
208	}
209
210	// Collect worlds owned by the specified owner
211	var ownerWorlds []map[string]string
212	worlds.Iterate("", "", func(key string, value interface{}) bool {
213		world := value.(map[string]string)
214		if world["owner"] == owner {
215			ownerWorlds = append(ownerWorlds, world)
216		}
217		return false
218	})
219
220	if len(ownerWorlds) == 0 {
221		output += "*No worlds found for this owner.*\n"
222		return output
223	}
224
225	pages, err := pager.New(iUrl, len(ownerWorlds), pager.WithPageSize(10))
226	if err != nil {
227		return renderError("Invalid page")
228	}
229
230	// Render paginated slice (reverse order - newest first)
231	start := len(ownerWorlds) - pages.Offset() - 1
232	end := start - pages.PageSize()
233	if end < -1 {
234		end = -1
235	}
236
237	for i := start; i > end; i-- {
238		output += renderWorldItem(ownerWorlds[i], 0)
239	}
240
241	if pages.HasPages() {
242		output += pager.Picker(pages)
243	}
244
245	return output
246}
247
248// renderWorldItem renders a single world item
249func renderWorldItem(world map[string]string, page int) string {
250	output := ""
251	output += ufmt.Sprintf("* **ID**: %s\n", world["id"])
252	output += ufmt.Sprintf("* **Name**: %s\n", world["name"])
253	output += ufmt.Sprintf("* **Owner**: %s [[View on Explorer](%s/m/explorer/player?address=%s)] [[Worlds by Owner](/r/akkadia/personal_world:worlds?owner=%s&page=%d)]\n", world["owner"], admin.GetExplorerURL(), world["owner"], world["owner"], page)
254	output += ufmt.Sprintf("* **Biome**: %s\n", world["biome"])
255	output += ufmt.Sprintf("* **Size**: %s\n", world["size"])
256	output += ufmt.Sprintf("* **Slug**: `%s` [[View on Explorer](%s/m/explorer/community-realm?slug=%s)]\n", world["slug"], admin.GetExplorerURL(), world["slug"])
257	output += "\n---\n\n"
258	return output
259}
260
261// renderError renders an error page
262func renderError(message string) string {
263	output := "# Error\n\n"
264	output += ufmt.Sprintf("> %s\n\n", message)
265	output += "[← Back to Home](/r/akkadia/personal_world:)\n"
266	return output
267}