render.gno
6.75 Kb · 251 lines
1package block
2
3import (
4 "net/url"
5
6 "gno.land/p/demo/tokens/grc1155"
7 "gno.land/p/jeronimoalbi/pager"
8 "gno.land/p/nt/ufmt"
9 "gno.land/r/akkadia/admin"
10)
11
12func Render(iUrl string) string {
13 u, err := url.Parse(iUrl)
14 if err != nil {
15 return "404\n"
16 }
17
18 path := u.Path
19 query := u.Query()
20
21 switch {
22 case path == "":
23 return renderHome()
24 case path == "blocks":
25 return renderBlocksList(iUrl)
26 case path == "block":
27 return renderBlock(query.Get("id"), query.Get("page"))
28 case path == "inventory":
29 return renderInventory(query.Get("owner"))
30 case path == "grc1155":
31 return renderGRC1155(query.Get("id"), query.Get("owner"))
32 default:
33 return "404\n"
34 }
35}
36
37func renderHome() string {
38 output := `# Block
39
40Welcome to the Block management system on Akkadia.
41
42## What is Block?
43
44Block is a GRC1155-based multi-token system where each block type represents a unique item in the game:
45
46* **GRC1155 Token**: Each block type is a fungible token with supply limits
47* **Mintable**: Users can mint blocks by paying the mint price
48* **Tradeable**: Blocks can be transferred between users
49* **Usable**: Blocks can be installed/uninstalled in chunks
50
51## How Blocks Work
52
531. **System Blocks**: Admin creates system blocks (ID < 100000)
542. **User Blocks**: Users can create custom blocks (ID >= 100000)
553. **Minting**: Pay mint price to receive block tokens
564. **Usage**: Install blocks in chunks, uninstall to recover
57
58## Config
59
60`
61 output += ufmt.Sprintf("* **List Limit**: %d\n", listLimit)
62 output += ufmt.Sprintf("* **Creator BPS**: %d (%d.%02d%%)\n", creatorBPS, creatorBPS/100, creatorBPS%100)
63
64 output += `
65## Stats
66
67`
68 output += ufmt.Sprintf("* **Total Blocks**: %d\n", blocks.Size())
69 output += ufmt.Sprintf("* **Next Block ID**: %d\n", nextBlockID)
70
71 output += `
72## Quick Links
73
74* [Browse All Blocks](/r/akkadia/block:blocks)
75
76## Search Block by ID
77
78<gno-form path="block">
79 <gno-input name="id" placeholder="Enter block ID" />
80</gno-form>
81
82## Search Inventory by Owner
83
84<gno-form path="inventory">
85 <gno-input name="owner" placeholder="Enter owner address" />
86</gno-form>
87
88## Check GRC1155 Balance
89
90<gno-form path="grc1155">
91 <gno-input name="id" placeholder="Enter block ID" />
92 <gno-input name="owner" placeholder="Enter owner address" />
93</gno-form>
94`
95
96 return output
97}
98
99// renderBlocksList renders a list of all blocks with pagination
100func renderBlocksList(iUrl string) string {
101 output := `# All Blocks
102
103[← Home](/r/akkadia/block:)
104
105`
106 if blocks.Size() == 0 {
107 output += "*No blocks created yet.*\n"
108 return output
109 }
110
111 pages, err := pager.New(iUrl, blocks.Size(), pager.WithPageSize(10))
112 if err != nil {
113 return output + "Invalid page"
114 }
115
116 // Get current page number for back links
117 currentPage := (pages.Offset() / pages.PageSize()) + 1
118
119 blocks.IterateByOffset(pages.Offset(), pages.PageSize(), func(key string, value interface{}) bool {
120 block := value.(map[string]string)
121 blockID := block["id"]
122 output += ufmt.Sprintf("### [%s - %s](/r/akkadia/block:block?id=%s&page=%d)\n\n", blockID, block["name"], blockID, currentPage)
123 output += ufmt.Sprintf("* **Type**: %s\n", block["blockType"])
124 output += ufmt.Sprintf("* **Max Supply**: %s\n", block["maxSupply"])
125 output += ufmt.Sprintf("* **Mint Price**: %s ugnot\n", block["mintPrice"])
126 output += "\n---\n\n"
127 return false
128 })
129
130 if pages.HasPages() {
131 output += pager.Picker(pages)
132 }
133
134 return output
135}
136
137func renderBlock(blockID string, page string) string {
138 output := "# Block Detail\n\n"
139 if page != "" {
140 output += ufmt.Sprintf("[← Back to List](/r/akkadia/block:blocks?page=%s) | [View on Explorer](%s/m/explorer/block?id=%s)\n\n", page, admin.GetExplorerURL(), blockID)
141 } else {
142 output += ufmt.Sprintf("[← Home](/r/akkadia/block:) | [View on Explorer](%s/m/explorer/block?id=%s)\n\n", admin.GetExplorerURL(), blockID)
143 }
144
145 bid := stringToBlockID(blockID)
146 blockIDKey := blockIDToKey(bid)
147 propertiesVal, found := blocks.Get(blockIDKey)
148 if !found {
149 return output + "Block not found"
150 }
151 properties := propertiesVal.(map[string]string)
152
153 textureURL := properties["textureURL"]
154 previewURL := properties["previewURL"]
155
156 output += ufmt.Sprintf("## %s\n\n", properties["name"])
157
158 output += `## Images
159<gno-columns>
160### Texture
161
162|||
163
164### Preview
165</gno-columns>
166<gno-columns>
167`
168 output += ufmt.Sprintf("\n\n|||\n\n\n", textureURL, previewURL)
169 output += "</gno-columns>\n\n"
170
171 output += "## Properties\n\n"
172
173 for key, value := range properties {
174 if key == "mintPrice" || key == "usePrice" {
175 output += ufmt.Sprintf("* **%s**: %s ugnot\n", key, value)
176 } else if key == "installerBps" {
177 bps := stringToBlockID(value)
178 output += ufmt.Sprintf("* **%s**: %s (%d.%02d%%)\n", key, value, bps/100, bps%100)
179 } else if key == "creator" {
180 output += ufmt.Sprintf("* **%s**: %s [[View on Explorer](%s/m/explorer/player?address=%s)]\n", key, value, admin.GetExplorerURL(), value)
181 } else if key == "textureURL" || key == "previewURL" {
182 output += ufmt.Sprintf("* **%s**: [%s](%s)\n", key, value, value)
183 } else {
184 output += ufmt.Sprintf("* **%s**: %s\n", key, value)
185 }
186 }
187
188 return output
189}
190
191func renderInventory(owner string) string {
192 output := ufmt.Sprintf("# Inventory of %s\n\n", owner)
193 output += "[← Home](/r/akkadia/block:)\n\n"
194
195 if owner == "" {
196 return output + "Owner address is required"
197 }
198
199 inventory := GetInventory(address(owner))
200
201 if len(inventory) == 0 {
202 output += "*No blocks found for this owner.*\n"
203 return output
204 }
205
206 output += ufmt.Sprintf("**Total Block Types**: %d\n\n", len(inventory))
207 output += "---\n\n"
208
209 for _, item := range inventory {
210 blockID := item["id"]
211 balance := item["balance"]
212
213 // Get block info
214 bid := stringToBlockID(blockID)
215 blockIDKey := blockIDToKey(bid)
216 blockVal, found := blocks.Get(blockIDKey)
217
218 if found {
219 block := blockVal.(map[string]string)
220 output += ufmt.Sprintf("### [%s - %s](/r/akkadia/block:block?id=%s)\n\n", blockID, block["name"], blockID)
221 output += ufmt.Sprintf("* **Balance**: %s\n", balance)
222 output += ufmt.Sprintf("* **Type**: %s\n", block["blockType"])
223 } else {
224 output += ufmt.Sprintf("### %s\n\n", blockID)
225 output += ufmt.Sprintf("* **Balance**: %s\n", balance)
226 }
227 output += "\n---\n\n"
228 }
229
230 return output
231}
232
233func renderGRC1155(blockID string, owner string) string {
234 output := "# GRC1155 Balance\n\n"
235 output += "[← Home](/r/akkadia/block:)\n\n"
236
237 if blockID == "" || owner == "" {
238 return output + "Block ID and owner address are required"
239 }
240
241 balance, err := token.BalanceOf(address(owner), grc1155.TokenID(blockID))
242 if err != nil {
243 return output + "Error getting balance"
244 }
245
246 output += ufmt.Sprintf("* **Block ID**: %s\n", blockID)
247 output += ufmt.Sprintf("* **Owner**: %s [[View on Explorer](%s/m/explorer/player?address=%s)]\n", owner, admin.GetExplorerURL(), owner)
248 output += ufmt.Sprintf("* **Balance**: %d\n\n", balance)
249
250 return output
251}