Search Apps Documentation Source Content File Folder Download Copy Actions Download 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}