Kind Realm (renderable)
Name v0
Namespace sys / namereg
Exported functions 11
Module gno.land/r/sys/namereg/v0
gno 0.9 render.gno gno
⧉
1 package namereg 2 3 import ( 4 "gno.land/p/moul/md/v0" 5 "gno.land/p/moul/realmpath/v0" 6 "gno.land/p/moul/txlink/v0" 7 "gno.land/p/nt/ufmt/v0" Canonicalize (s string) string
IsPaused () bool
IsReserved (stem string) bool
NewSetPausedExecutor (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, newPausedValue bool) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
ProposeDeleteUser (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, reason string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
ProposeNewName (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, newName string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
ProposeNewRegisterPrice (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, newPrice int64) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}
Register (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, username string)
Render (path string) string
RenderLatestUsersWidget (num int) string
ValidateNymFormat (username string) interface {Error func() string}
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
8
9 "gno.land/r/demo/profile"
10 susers "gno.land/r/sys/users"
11 )
12
13 func 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
24 func 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
44 func renderIntroParagraph() string {
45 out := md.Paragraph( "Welcome to the Gno.land User Registry (v0). Please register a username." )
46 out += md.Paragraph(`Registering a username grants the registering address the right to deploy packages and realms
47 under that username’s namespace. For example, if an address registers the username ` + md.InlineCode("nym-alice123") + ` , it
48 will 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 V0, 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
83 func 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
93 func 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.
125 func 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 }
145
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.