Search Apps Documentation Source Content File Folder Download Copy Actions Download

render.gno

10.36 Kb ยท 328 lines
  1package basedao
  2
  3import (
  4	"strconv"
  5	"strings"
  6
  7	"gno.land/p/nt/mux"
  8	"gno.land/p/nt/seqid"
  9	"gno.land/p/nt/ufmt"
 10	"gno.land/p/samcrew/daokit"
 11)
 12
 13// WELL KNOWN PATHS
 14const (
 15	HOME_PATH             = ""
 16	HOME_NO_PROFILE_PATH  = "noprofile"
 17	CONFIG_PATH           = "config"
 18	PROPOSAL_HISTORY_PATH = "history"
 19	MEMBER_DETAIL_PATH    = "member/{address}"
 20	PROPOSAL_DETAIL_PATH  = "proposal/{id}"
 21	FALLBACK_DISPLAY_NAME = "Anon"
 22)
 23
 24func (d *DAOPrivate) initRenderingRouter() {
 25	if d.RenderRouter == nil {
 26		d.RenderRouter = mux.NewRouter()
 27	}
 28}
 29
 30func (d *DAOPrivate) InitDefaultRendering() {
 31	d.RenderRouter.HandleFunc(HOME_PATH, d.MuxHomePage)
 32	d.RenderRouter.HandleFunc(HOME_NO_PROFILE_PATH, d.MuxHomePage)
 33	d.RenderRouter.HandleFunc(CONFIG_PATH, d.MuxConfigPage)
 34	d.RenderRouter.HandleFunc(PROPOSAL_HISTORY_PATH, d.MuxProposalHistoryPage)
 35	d.RenderRouter.HandleFunc(MEMBER_DETAIL_PATH, d.MuxMemberDetailPage)
 36	d.RenderRouter.HandleFunc(PROPOSAL_DETAIL_PATH, d.MuxProposalDetailPage)
 37}
 38
 39func (d *DAOPrivate) Render(path string) string {
 40	return d.RenderRouter.Render(path)
 41}
 42
 43// DEFAULT_HANDLERS
 44
 45func (d *DAOPrivate) MuxHomePage(res *mux.ResponseWriter, req *mux.Request) {
 46	res.Write(d.HomePageView())
 47}
 48
 49func (d *DAOPrivate) MuxConfigPage(res *mux.ResponseWriter, req *mux.Request) {
 50	res.Write(d.ConfigPageView())
 51}
 52
 53func (d *DAOPrivate) MuxProposalHistoryPage(res *mux.ResponseWriter, req *mux.Request) {
 54	res.Write(d.ProposalHistoryPageView())
 55}
 56
 57func (d *DAOPrivate) MuxMemberDetailPage(res *mux.ResponseWriter, req *mux.Request) {
 58	res.Write(d.MemberDetailPageView(req.GetVar("address")))
 59}
 60
 61func (d *DAOPrivate) MuxProposalDetailPage(res *mux.ResponseWriter, req *mux.Request) {
 62	id, err := strconv.ParseUint(req.GetVar("id"), 10, 64)
 63	if err != nil {
 64		panic(err)
 65	}
 66	res.Write(d.ProposalDetailPageView(id))
 67}
 68
 69// PUBLIC PAGES
 70
 71func (d *DAOPrivate) HomePageView() string {
 72	return d.HomeProfileHeaderView(false) +
 73		d.HomeMembersView() +
 74		d.HomeProposalsView()
 75}
 76
 77func (d *DAOPrivate) ConfigPageView() string {
 78	return d.ConfigHeaderView() +
 79		d.ConfigRolesView() +
 80		d.ConfigResourcesView()
 81}
 82
 83func (d *DAOPrivate) ProposalHistoryPageView() string {
 84	return d.ProposalHistoryHeaderView() +
 85		d.ProposalHistoryView()
 86}
 87
 88func (d *DAOPrivate) MemberDetailPageView(address string) string {
 89	return d.MemberDetailHeaderView() +
 90		d.MemberDetailView(address)
 91}
 92
 93func (d *DAOPrivate) ProposalDetailPageView(idu uint64) string {
 94	return d.ProposalDetailHeaderView() +
 95		d.ProposalDetailView(idu)
 96}
 97
 98// --- PUBLIC COMPONENT VIEWS ---
 99
100// HOME PAGE COMPONENTS VIEWS
101
102func (d *DAOPrivate) HomeProfileHeaderView(noprofile bool) string {
103	s := ""
104	name := d.GetProfileString(d.Realm.Address(), "DisplayName", "DAO")
105	description := d.GetProfileString(d.Realm.Address(), "Bio", "Created with daokit")
106	pkgPath := d.Realm.PkgPath()
107	linkPath := getLinkPath(pkgPath)
108
109	if !noprofile {
110		s += ufmt.Sprintf("# %s\n\n", name)
111		imageURI := d.GetProfileString(d.Realm.Address(), "Avatar", "")
112		if imageURI != "" {
113			s += ufmt.Sprintf("![Image](%s)\n\n", imageURI)
114		}
115		s += ufmt.Sprintf("%s\n\n", description)
116	}
117
118	s += ufmt.Sprintf("> Realm address: %s\n\n", d.Realm.Address())
119	s += ufmt.Sprintf("Discover more about this DAO on the [configuration page โš™๏ธ](%s:%s)\n\n", linkPath, CONFIG_PATH)
120	s += ufmt.Sprintf("\n--------------------------------\n")
121
122	return s
123}
124
125func (d *DAOPrivate) HomeMembersView() string {
126	pkgPath := d.Realm.PkgPath()
127	linkPath := getLinkPath(pkgPath)
128	s := ""
129	s += ufmt.Sprintf("## Members ๐Ÿ‘ค \n\n")
130	i := 1
131	d.Members.Members.Iterate("", "", func(key string, value interface{}) bool {
132		s += ufmt.Sprintf("- **Member %d: [%s](%s:%s/%s)**\n\n", i, key, linkPath, "member", key)
133		s += ufmt.Sprintf("	- **Profile:** %s\n", d.GetProfileString(address(key), "DisplayName", FALLBACK_DISPLAY_NAME))
134		s += ufmt.Sprintf(" 	- **Roles:** %s\n\n", strings.Join(d.Members.GetMemberRoles(key), ", "))
135		i += 1
136		return false
137	})
138	s += ufmt.Sprintf("> You can find more information about a member by clicking on their address\n\n")
139	s += ufmt.Sprintf("\n--------------------------------\n")
140	return s
141}
142
143func (d *DAOPrivate) HomeProposalsView() string {
144	pkgPath := d.Realm.PkgPath()
145	linkPath := getLinkPath(pkgPath)
146	s := ""
147	s += ufmt.Sprintf("## Proposals ๐Ÿ—ณ๏ธ \n\n")
148	i := 0
149	d.Core.Proposals.Tree.Iterate("", "", func(key string, value interface{}) bool {
150		proposal := value.(*daokit.Proposal)
151		if proposal.Status != daokit.ProposalStatusOpen {
152			return false
153		}
154		id, err := seqid.FromString(key)
155		if err != nil {
156			panic(err)
157		}
158		s += ufmt.Sprintf("- **Proposal %d: [%s](%s:%s/%d)**\n\n", uint64(id), proposal.Title, linkPath, "proposal", uint64(id))
159		i += 1
160		return false
161	})
162	if i == 0 {
163		s += ufmt.Sprintf("\tโš ๏ธ There are no running proposals at the moment\n\n")
164	}
165	s += ufmt.Sprintf("> See the [proposal history ๐Ÿ“œ](%s:%s) for more information\n\n", linkPath, PROPOSAL_HISTORY_PATH)
166	s += ufmt.Sprintf("\n--------------------------------\n")
167	return s
168}
169
170// CONFIG PAGE COMPONENTS VIEWS
171
172func (d *DAOPrivate) ConfigHeaderView() string {
173	name := d.GetProfileString(d.Realm.Address(), "DisplayName", "DAO")
174	s := ""
175	s += ufmt.Sprintf("# %s - Config โš™๏ธ\n\n", name)
176	s += ufmt.Sprintf("\n--------------------------------\n")
177	return s
178}
179
180func (d *DAOPrivate) ConfigRolesView() string {
181	roles := d.Members.GetRoles()
182	s := ""
183	s += ufmt.Sprintf("## Roles ๐Ÿท๏ธ\n\n")
184	for _, role := range roles {
185		s += ufmt.Sprintf("- %s\n\n", role)
186		info := d.Members.RoleInfo(role)
187		s += ufmt.Sprintf("  %s\n\n", info.Description)
188	}
189	s += ufmt.Sprintf("\n--------------------------------\n")
190	return s
191}
192
193func (d *DAOPrivate) ConfigResourcesView() string {
194	s := ""
195	s += ufmt.Sprintf("## Resources ๐Ÿ“ฆ\n\n")
196	i := 1
197	d.Core.Resources.Tree.Iterate("", "", func(key string, value interface{}) bool {
198		resource := value.(*daokit.Resource)
199		s += ufmt.Sprintf("- **Resource #%d: %s**\n\n", i, key)
200		// TODO: add doc to handler and print here
201		s += ufmt.Sprintf("  - **Name:** %s\n", resource.DisplayName)
202		s += ufmt.Sprintf("  - **Description:** %s\n", resource.Description)
203		s += ufmt.Sprintf("  - **Condition:** %s\n\n", resource.Condition.Render())
204		i += 1
205		return false
206	})
207	s += ufmt.Sprintf("\n--------------------------------\n")
208	return s
209}
210
211// HISTORY PAGE COMPONENTS VIEWS
212
213func (d *DAOPrivate) ProposalHistoryHeaderView() string {
214	name := d.GetProfileString(d.Realm.Address(), "DisplayName", "DAO")
215	s := ""
216	s += ufmt.Sprintf("# %s - Proposal History\n\n", name)
217	s += ufmt.Sprintf("\n--------------------------------\n")
218	return s
219}
220
221func (d *DAOPrivate) ProposalHistoryView() string {
222	pkgPath := d.Realm.PkgPath()
223	linkPath := getLinkPath(pkgPath)
224	s := ""
225	s += ufmt.Sprintf("## Proposals ๐Ÿ—ณ๏ธ\n\n")
226	i := 1
227	d.Core.Proposals.Tree.Iterate("", "", func(key string, value interface{}) bool {
228		proposal, ok := value.(*daokit.Proposal)
229		if !ok {
230			panic("unexpected invalid proposal type")
231		}
232		id, err := seqid.FromString(key)
233		if err != nil {
234			panic(err)
235		}
236		s += ufmt.Sprintf("- **Proposal %d: [%s](%s:%s/%d) - %s**\n\n", uint64(id), proposal.Title, linkPath, "proposal", uint64(id), proposal.Status)
237		i += 1
238		return false
239	})
240	s += ufmt.Sprintf("[Add a new proposal ๐Ÿ—ณ๏ธ](%s$help)\n\n", linkPath)
241	s += ufmt.Sprintf("\n--------------------------------\n")
242	return s
243}
244
245// MEMBER DETAIL PAGE COMPONENTS VIEWS
246
247func (d *DAOPrivate) MemberDetailHeaderView() string {
248	name := d.GetProfileString(d.Realm.Address(), "DisplayName", "DAO")
249	s := ""
250	s += ufmt.Sprintf("# %s - Member Detail\n\n", name)
251	s += ufmt.Sprintf("\n--------------------------------\n")
252	return s
253}
254
255func (d *DAOPrivate) MemberDetailView(addr string) string {
256	pkgPath := d.Realm.PkgPath()
257	linkPath := getLinkPath(pkgPath)
258	roles := d.Members.GetMemberRoles(addr)
259	displayName := d.GetProfileString(address(addr), "DisplayName", FALLBACK_DISPLAY_NAME)
260	bio := d.GetProfileString(address(addr), "Bio", "No bio")
261	pp := d.GetProfileString(address(addr), "Avatar", "")
262	s := ""
263	s += ufmt.Sprintf("## Profile ๐Ÿ‘ค\n\n")
264	s += ufmt.Sprintf("- **Display Name:** %s\n\n", displayName)
265	s += ufmt.Sprintf("- **Bio:** %s\n\n", bio)
266	if pp != "" {
267		s += ufmt.Sprintf("![Avatar](%s)\n\n", pp)
268	}
269	s += ufmt.Sprintf("## Roles ๐Ÿท๏ธ\n\n")
270	for _, role := range roles {
271		s += ufmt.Sprintf("- %s\n\n", role)
272	}
273	s += ufmt.Sprintf("> Learn more about the roles on the [configuration page โš™๏ธ](%s:%s)\n\n", linkPath, CONFIG_PATH)
274	s += ufmt.Sprintf("\n--------------------------------\n")
275	return s
276}
277
278// PROPOSAL DETAIL PAGE COMPONENTS VIEWS
279
280func (d *DAOPrivate) ProposalDetailHeaderView() string {
281	name := d.GetProfileString(d.Realm.Address(), "DisplayName", "DAO")
282	s := ""
283	s += ufmt.Sprintf("# %s - Proposal Detail\n\n", name)
284	s += ufmt.Sprintf("\n--------------------------------\n")
285	return s
286}
287
288func (d *DAOPrivate) ProposalDetailView(idu uint64) string {
289	pkgPath := d.Realm.PkgPath()
290	linkPath := getLinkPath(pkgPath)
291	id := seqid.ID(idu)
292	proposal := d.Core.Proposals.GetProposal(uint64(id))
293	s := ""
294	s += ufmt.Sprintf("## Title - %s ๐Ÿ“œ\n\n", proposal.Title)
295	s += ufmt.Sprintf("## Description ๐Ÿ“\n\n%s\n\n", proposal.Description)
296	s += ufmt.Sprintf("## Resource - %s ๐Ÿ“ฆ\n\n", proposal.Action.Type())
297	resource := d.Core.Resources.Get(proposal.Action.Type())
298	s += ufmt.Sprintf("  - **Name:** %s\n", resource.DisplayName)
299	s += ufmt.Sprintf("  - **Description:** %s\n", resource.Description)
300	s += ufmt.Sprintf("  - **Condition:** %s\n\n", resource.Condition.Render())
301	s += ("---\n\n")
302	s += proposal.Action.String() + "\n\n"
303	s += ("---\n\n")
304	if proposal.Status == daokit.ProposalStatusOpen {
305		s += ufmt.Sprintf("## Status - Open ๐ŸŸก\n\n")
306		s += ufmt.Sprintf("[Vote on this proposal ๐Ÿ—ณ๏ธ](%s$help)\n\n", linkPath)
307	} else if proposal.Status == daokit.ProposalStatusPassed {
308		s += ufmt.Sprintf("## Status - Passed ๐ŸŸข\n\n")
309		s += ufmt.Sprintf("[Execute this proposal ๐Ÿ—ณ๏ธ](%s$help)\n\n", linkPath)
310	} else if proposal.Status == daokit.ProposalStatusExecuted {
311		s += ufmt.Sprintf("## Status - Executed โœ…\n\n")
312	} else {
313		s += ufmt.Sprintf("## Status - Closed ๐Ÿ”ด\n\n")
314	}
315	s += ufmt.Sprintf("> proposed by %s ๐Ÿ‘ค\n\n", proposal.ProposerID)
316	s += ufmt.Sprintf("\n--------------------------------\n")
317	s += ufmt.Sprintf("## Votes ๐Ÿ—ณ๏ธ\n\n%s\n\n", proposal.Condition.RenderWithVotes(proposal.Ballot))
318	s += ufmt.Sprintf("\n--------------------------------\n")
319	return s
320}
321
322func getLinkPath(pkgPath string) string {
323	slashIdx := strings.IndexRune(pkgPath, '/')
324	if slashIdx != 1 {
325		return pkgPath[slashIdx:]
326	}
327	return ""
328}