Search Apps Documentation Source Content File Folder Download Copy Actions Download

render.gno

4.20 Kb · 144 lines
  1package namereg
  2
  3import (
  4	"gno.land/p/moul/md"
  5	"gno.land/p/moul/realmpath"
  6	"gno.land/p/moul/txlink"
  7	"gno.land/p/nt/ufmt/v0"
  8
  9	"gno.land/r/demo/profile"
 10	susers "gno.land/r/sys/users"
 11)
 12
 13func Render(path string) string {
 14	req := realmpath.Parse(path)
 15
 16	if req.Path == "" {
 17		return renderHomePage()
 18	}
 19
 20	// Otherwise, render the user page
 21	return renderUserPage(req.Path)
 22}
 23
 24func renderHomePage() string {
 25	var out string
 26
 27	out += "# Gno.land User Registry\n"
 28
 29	if paused {
 30		out += md.HorizontalRule()
 31		out += md.H2("This realm is paused.")
 32		out += md.Paragraph("Check out [`gno.land/r/sys/users`](/r/sys/users) for the current user registry.")
 33		out += md.HorizontalRule()
 34	}
 35
 36	out += renderIntroParagraph()
 37
 38	out += md.H2("Latest registrations")
 39	out += RenderLatestUsersWidget(-1)
 40
 41	return out
 42}
 43
 44func renderIntroParagraph() string {
 45	out := md.Paragraph("Welcome to the Gno.land User Registry (v1). Please register a username.")
 46	out += md.Paragraph(`Registering a username grants the registering address the right to deploy packages and realms
 47under that username’s namespace. For example, if an address registers the username ` + md.InlineCode("nym-alice123") + `, it
 48will gain permission to deploy packages and realms to package paths with the pattern ` + md.InlineCode("gno.land/{p,r}/nym-alice123/*") + `.`)
 49
 50	out += md.Paragraph("In V1, usernames must match `nym-<stem><digits>`, where:")
 51	items := []string{
 52		"`<stem>` is 5 to 13 lowercase ASCII letters",
 53		"`<digits>` is exactly 3 decimal digits",
 54		"The stem must NOT start with `gno`, `gl`, `atom`, `atone`, `photon`, or `cosmos` (reserved prefixes)",
 55		"The stem must NOT match a reserved role name (admin, root, support, ...)",
 56		"Confusable variants (e.g. `vitaiik` vs `vitalik` via `l↔i`) are blocked via canonical-form collision detection",
 57		"Total username length: 12–20 chars (distinct from `g1...` addresses, which are always 40 chars)",
 58	}
 59	out += md.BulletList(items)
 60
 61	out += "\n\n"
 62	out += md.Paragraph("Vanity names outside this format may be allocated by GovDAO governance through `ProposeNewName`.")
 63
 64	if !paused {
 65		amount := ufmt.Sprintf("%dugnot", registerPrice)
 66		link := txlink.NewLink("Register")
 67		if registerPrice > 0 {
 68			link = link.SetSend(amount)
 69		}
 70
 71		out += md.H3(ufmt.Sprintf(" [[Click here to register]](%s)", link.URL()))
 72		// XXX: Display registration price adjusting for dynamic GNOT price when it becomes possible.
 73		out += ufmt.Sprintf("Registration price: %f GNOT (%s)\n\n", float64(registerPrice)/1_000_000, amount)
 74	}
 75
 76	out += md.HorizontalRule()
 77	out += "\n\n"
 78
 79	return out
 80}
 81
 82// resolveUser resolves the user based on the path, determining if it's a name or address
 83func resolveUser(path string) (*susers.UserData, bool, bool) {
 84	if address(path).IsValid() {
 85		return susers.ResolveAddress(address(path)), false, false
 86	}
 87
 88	data, isLatest := susers.ResolveName(path)
 89	return data, isLatest, true
 90}
 91
 92// renderUserPage generates the user page based on user data and path
 93func renderUserPage(path string) string {
 94	var out string
 95
 96	// Render single user page
 97	data, isLatest, isName := resolveUser(path)
 98	if data == nil {
 99		out += md.H1("User not found.")
100		out += "This user does not exist or has been deleted.\n"
101		return out
102	}
103
104	out += md.H1("User - " + md.InlineCode(data.Name()))
105
106	if isName && !isLatest {
107		out += md.Paragraph(ufmt.Sprintf(
108			"Note: You searched for `%s`, which is a previous name of [`%s`](/u/%s).",
109			path, data.Name(), data.Name()))
110	} else {
111		out += ufmt.Sprintf("Address: %s\n\n", data.Addr().String())
112
113		out += md.H2("Bio")
114		out += profile.GetStringField(data.Addr(), "Bio", "No bio defined.")
115		out += "\n\n"
116		out += ufmt.Sprintf("[Update bio](%s)", txlink.Realm("gno.land/r/demo/profile").Call("SetStringField", "field", "Bio"))
117		out += "\n\n"
118	}
119
120	return out
121}
122
123// RenderLatestUsersWidget renders the latest num registered users.
124// For num = -1, the maximum number (100) will be displayed.
125func RenderLatestUsersWidget(num int) string {
126	size := latestUsers.Size()
127	if size == 0 {
128		return "No registered users."
129	}
130
131	if num > size || num < 0 {
132		num = size
133	}
134
135	entries := latestUsers.Entries()
136	var out string
137
138	for i := size - 1; i >= size-num; i-- {
139		user := entries[i].(string)
140		out += md.BulletItem(md.UserLink(user))
141	}
142
143	return out
144}