Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

views.gno

7.00 Kb · 217 lines
  1package curation
  2
  3import (
  4	"chain/runtime"
  5	"strconv"
  6	"strings"
  7
  8	"gno.land/p/nt/avl/v0"
  9	"gno.land/p/samcrew/launchpad/meta/v1"
 10	"gno.land/r/samcrew/launchpad/nft/v1"
 11)
 12
 13// int64 values are decimal strings so a JSON client cannot round them.
 14func jsonInt(n int64) string { return meta.JSONString(strconv.FormatInt(n, 10)) }
 15
 16func StateJSON() string {
 17	return "{" +
 18		`"admin":` + meta.JSONString(admin.String()) +
 19		`,"pendingAdmin":` + meta.JSONString(pendingAdmin.String()) +
 20		`,"activeManagers":` + strconv.Itoa(ActiveManagerCount()) +
 21		`,"maxSeats":` + strconv.Itoa(maxSeats) + "}"
 22}
 23
 24// ManagersJSON lists the active seats, ordered by account.
 25func ManagersJSON() string {
 26	var b strings.Builder
 27	b.WriteByte('[')
 28	at := now()
 29	seats.Iterate("", "", func(account string, v any) bool {
 30		s := v.(seat)
 31		if at < s.until {
 32			if b.Len() > 1 {
 33				b.WriteByte(',')
 34			}
 35			b.WriteString(`{"account":` + meta.JSONString(account) +
 36				`,"lead":` + strconv.FormatBool(s.lead) +
 37				`,"until":` + jsonInt(s.until) + "}")
 38		}
 39		return false
 40	})
 41	b.WriteByte(']')
 42	return b.String()
 43}
 44
 45// ApplicationJSON returns null for a collection nobody applied for.
 46func ApplicationJSON(collection string) string {
 47	a := applicationOf(collection)
 48	if a == nil {
 49		return "null"
 50	}
 51	return "{" +
 52		`"collection":` + meta.JSONString(collection) +
 53		`,"founder":` + meta.JSONString(a.Founder.String()) +
 54		`,"statementHash":` + meta.JSONString(a.StatementHash) +
 55		`,"statementCID":` + meta.JSONString(a.StatementCID) +
 56		`,"revision":` + jsonInt(a.Revision) +
 57		`,"status":` + meta.JSONString(a.Status) +
 58		`,"reviewer":` + meta.JSONString(a.Reviewer.String()) +
 59		`,"reasonHash":` + meta.JSONString(a.ReasonHash) +
 60		`,"reasonCID":` + meta.JSONString(a.ReasonCID) +
 61		`,"updatedAt":` + jsonInt(a.UpdatedAt) + "}"
 62}
 63
 64// pageJSON renders a zero-based page of a tree in key order; the page size is
 65// 1..50.
 66func pageJSON(tree *avl.Tree, page, size int64, item func(v any) string) string {
 67	if page < 0 || size < 1 || size > 50 {
 68		panic("launchpad/curation: invalid page")
 69	}
 70	var b strings.Builder
 71	b.WriteByte('[')
 72	// page < total comes first so that page*size cannot overflow.
 73	if total := int64(tree.Size()); page < total && page*size < total {
 74		tree.IterateByOffset(int(page*size), int(size), func(_ string, v any) bool {
 75			if b.Len() > 1 {
 76				b.WriteByte(',')
 77			}
 78			b.WriteString(item(v))
 79			return false
 80		})
 81	}
 82	b.WriteByte(']')
 83	return b.String()
 84}
 85
 86// ApplicationsJSON pages through the applications in order of first filing.
 87func ApplicationsJSON(page, size int64) string {
 88	return pageJSON(&filings, page, size, func(v any) string {
 89		return ApplicationJSON(v.(string))
 90	})
 91}
 92
 93// AccessJSON tells a private inbox what an account is to a collection right
 94// now: whether it holds the creator role ("founder"), an active manager, and
 95// whether Conflicted bars it from acting as one. It is public data to be read
 96// on every request, not a token.
 97//
 98// The answer also says where and when it was evaluated: the chain ID, the
 99// block height and the block time. The answer is not signed: checking them
100// exposes an honest node on the wrong chain or behind it, and proves nothing
101// against a node that lies. Its eight fields are a contract with that reader.
102func AccessJSON(collection, who string) string {
103	account := address(who)
104	checkAccount(account)
105	info := nft.GetInfo(collection)
106	return "{" +
107		`"chainId":` + meta.JSONString(runtime.ChainID()) +
108		`,"height":` + jsonInt(runtime.ChainHeight()) +
109		`,"time":` + jsonInt(now()) +
110		`,"collection":` + meta.JSONString(collection) +
111		`,"account":` + meta.JSONString(who) +
112		`,"founder":` + strconv.FormatBool(info.Creator == account) +
113		`,"manager":` + strconv.FormatBool(active(account)) +
114		`,"conflicted":` + strconv.FormatBool(conflicted(info, account)) + "}"
115}
116
117func featureJSON(collection string) string {
118	f := featureOf(collection)
119	if f == nil {
120		return "null"
121	}
122	return "{" +
123		`"proposer":` + meta.JSONString(f.proposer.String()) +
124		`,"approver":` + meta.JSONString(f.approver.String()) +
125		`,"reasonHash":` + meta.JSONString(f.reasonHash) +
126		`,"reasonCID":` + meta.JSONString(f.reasonCID) +
127		`,"until":` + jsonInt(f.until) + "}"
128}
129
130func holdJSON(collection string) string {
131	h := holdOf(collection)
132	if h == nil {
133		return "null"
134	}
135	return "{" +
136		`"actor":` + meta.JSONString(h.actor.String()) +
137		`,"confirmer":` + meta.JSONString(h.confirmer.String()) +
138		`,"extended":` + strconv.FormatBool(h.extended) +
139		`,"reasonHash":` + meta.JSONString(h.reasonHash) +
140		`,"reasonCID":` + meta.JSONString(h.reasonCID) +
141		`,"until":` + jsonInt(h.until) + "}"
142}
143
144func verificationJSON(collection string) string {
145	v := verifications.Get(collection)
146	if v == nil {
147		return "null"
148	}
149	r := v.(verification)
150	return "{" +
151		`"verified":` + strconv.FormatBool(r.verified) +
152		`,"reasonHash":` + meta.JSONString(r.reasonHash) +
153		`,"reasonCID":` + meta.JSONString(r.reasonCID) +
154		`,"updatedAt":` + jsonInt(r.updatedAt) + "}"
155}
156
157func appealJSON(key string) string {
158	a := appealOf(key)
159	if a == nil {
160		return "null"
161	}
162	return "{" +
163		`"collection":` + meta.JSONString(a.collection) +
164		`,"subject":` + meta.JSONString(a.subject) +
165		`,"statementHash":` + meta.JSONString(a.statementHash) +
166		`,"statementCID":` + meta.JSONString(a.statementCID) +
167		`,"filedAt":` + jsonInt(a.filedAt) +
168		`,"open":` + strconv.FormatBool(a.openKey != "") +
169		`,"upheld":` + strconv.FormatBool(a.upheld) +
170		`,"reasonHash":` + meta.JSONString(a.reasonHash) +
171		`,"reasonCID":` + meta.JSONString(a.reasonCID) +
172		`,"resolvedAt":` + jsonInt(a.resolvedAt) + "}"
173}
174
175// CollectionJSON is a collection's editorial record: what a client may show
176// now, then the records behind it and the latest appeal on each subject. A
177// part with no record is null.
178func CollectionJSON(collection string) string {
179	nft.GetInfo(collection)
180	var b strings.Builder
181	b.WriteString("{" +
182		`"collection":` + meta.JSONString(collection) +
183		`,"verified":` + strconv.FormatBool(IsVerified(collection)) +
184		`,"featured":` + strconv.FormatBool(IsFeatured(collection)) +
185		`,"hidden":` + strconv.FormatBool(IsHidden(collection)) +
186		`,"verification":` + verificationJSON(collection) +
187		`,"feature":` + featureJSON(collection) +
188		`,"hold":` + holdJSON(collection) +
189		`,"appeals":{`)
190	for i, subject := range subjects {
191		if i > 0 {
192			b.WriteByte(',')
193		}
194		b.WriteString(meta.JSONString(subject) + ":" + appealJSON(collection+"/"+subject))
195	}
196	b.WriteString("}}")
197	return b.String()
198}
199
200// AppealsJSON pages through the open appeals in filing order.
201func AppealsJSON(page, size int64) string {
202	return pageJSON(&openAppeals, page, size, func(v any) string {
203		return appealJSON(v.(string))
204	})
205}
206
207// FeaturesJSON pages through every collection that has a feature slot on
208// record, in ID text order, saying whether it is featured right now.
209func FeaturesJSON(page, size int64) string {
210	return pageJSON(&features, page, size, func(v any) string {
211		f := v.(*feature)
212		return "{" +
213			`"collection":` + meta.JSONString(f.collection) +
214			`,"featured":` + strconv.FormatBool(IsFeatured(f.collection)) +
215			`,"until":` + jsonInt(f.until) + "}"
216	})
217}