Skip to content

Commit

Permalink
feat(gno/dao): enhance gnoweb render UI
Browse files Browse the repository at this point in the history
  • Loading branch information
MikaelVallenet committed Jan 12, 2025
1 parent fe91be8 commit d42b5d4
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 57 deletions.
25 changes: 23 additions & 2 deletions gno/p/dao_roles_based/dao_roles_based.gno
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,43 @@ import (

"gno.land/p/demo/avl"
"gno.land/p/demo/json"
"gno.land/p/demo/mux"
"gno.land/p/teritori/daocond"
)

type DaoRolesBased struct {
Name string
Description string
RealmName string

MemberModule *MemberModule
ResourcesModule *ResourcesModule
ProposalModule *ProposalModule
MessagesRegistry *MessagesRegistry

renderingRouter *mux.Router
}

func NewDaoRolesBasedJSON(rolesJSON, membersJSON, resourcesJSON string, handlers []MessageHandler) *DaoRolesBased {
// parse & set roles, members, resources
func NewDaoRolesBasedJSON(name, description, realmName, rolesJSON, membersJSON, resourcesJSON string, handlers []MessageHandler) *DaoRolesBased {
if len(name) < 5 {
panic("dao name should be at least 5 characters long")
}

if len(description) < 10 {
panic("dao description should be at least 10 characters long")
}

dao := &DaoRolesBased{
Name: name,
Description: description,
RealmName: realmName,
MemberModule: newMemberModule(),
ResourcesModule: newResourcesModule(),
ProposalModule: newProposalModule(),
MessagesRegistry: newMessagesRegistry(),
renderingRouter: mux.NewRouter(),
}
dao.initRenderingRouter()

roles := dao.parseRolesJSON(rolesJSON)
dao.MemberModule.setRoles(roles)
Expand All @@ -37,6 +56,8 @@ func NewDaoRolesBasedJSON(rolesJSON, membersJSON, resourcesJSON string, handlers
dao.MessagesRegistry.register(handler)
}

//TODO: Register name & description into the profile realm

return dao
}

Expand Down
8 changes: 8 additions & 0 deletions gno/p/dao_roles_based/members.gno
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ func (m *MemberModule) setRoles(roles []string) {
}
}

func (m *MemberModule) getRoles() []string {
return m.roleManager.GetRoles()
}

func (m *MemberModule) getUserRoles(memberId string) []string {
return m.roleManager.GetUserRoles(std.Address(memberId))
}

func (m *MemberModule) setMembers(members [][]string) {
for _, member := range members {
m.members.Set(member[0], struct{}{})
Expand Down
156 changes: 104 additions & 52 deletions gno/p/dao_roles_based/render.gno
Original file line number Diff line number Diff line change
@@ -1,85 +1,137 @@
package dao_roles_based

import (
"std"
"strings"

"gno.land/p/demo/mux"
"gno.land/p/demo/seqid"
"gno.land/p/demo/ufmt"
"gno.land/p/teritori/daocond"
)

func (d *DaoRolesBased) Render() string {
sb := strings.Builder{}
sb.WriteString((ufmt.Sprintf("# Decentralized Autonomous Organization (DAO) Roles Based\n\n")))
sb.WriteString(d.MemberModule.Render())
sb.WriteString(d.ResourcesModule.Render())
sb.WriteString(d.ProposalModule.Render())
sb.WriteString(d.MessagesRegistry.Render())
return sb.String()
const (
HOME_PATH = ""
CONFIG_PATH = "config"
PROPOSAL_HISTORY_PATH = "history"
MEMBER_DETAIL_PATH = "member/{address}"
PROPOSAL_DETAIL_PATH = "proposal/{id}"
)

func (d *DaoRolesBased) initRenderingRouter() {
d.renderingRouter.HandleFunc(HOME_PATH, d.renderHomePage)
d.renderingRouter.HandleFunc(CONFIG_PATH, d.renderConfigPage)
d.renderingRouter.HandleFunc(PROPOSAL_HISTORY_PATH, d.renderProposalHistoryPage)
d.renderingRouter.HandleFunc(MEMBER_DETAIL_PATH, d.renderMemberDetailPage)
d.renderingRouter.HandleFunc(PROPOSAL_DETAIL_PATH, d.renderProposalDetailPage)
}

func (d *DaoRolesBased) Render(path string) string {
return d.renderingRouter.Render(path)
}

func (m *MemberModule) Render() string {
sb := strings.Builder{}
sb.WriteString((ufmt.Sprintf("## Members\n\n")))
func (d *DaoRolesBased) renderHomePage(res *mux.ResponseWriter, req *mux.Request) {
res.Write(ufmt.Sprintf("# %s\n\n", d.Name))
res.Write(ufmt.Sprintf("> Description: %s\n\n", d.Description))
res.Write(ufmt.Sprintf("Discover more about this DAO on the [configuration page ⚙️](%s:%s)\n\n", d.RealmName, CONFIG_PATH))

res.Write(ufmt.Sprintf("## Members 👤 \n\n"))
i := 1
m.members.Iterate("", "", func(key string, value interface{}) bool {
sb.WriteString((ufmt.Sprintf("**Member %d: %s**\n\n", i, key)))
sb.WriteString((ufmt.Sprintf("Roles:\n\n")))
roles := m.roleManager.GetUserRoles(std.Address(key))
for _, role := range roles {
sb.WriteString((ufmt.Sprintf("- %s\n\n", role)))
}
sb.WriteString((ufmt.Sprintf("\n--------------------------------\n")))
d.MemberModule.members.Iterate("", "", func(key string, value interface{}) bool {
res.Write(ufmt.Sprintf("- **Member %d: [%s](%s:%s/%s)**\n\n", i, key, d.RealmName, "member", key))
i += 1
return false
})
return sb.String()
}

func (r *ResourcesModule) Render() string {
sb := strings.Builder{}
sb.WriteString((ufmt.Sprintf("## Resources\n\n")))
res.Write(ufmt.Sprintf("> You can find more information about a member by clicking on their address\n\n"))
res.Write(ufmt.Sprintf("\n--------------------------------\n"))

r.resources.Iterate("", "", func(key string, value interface{}) bool {
condition := value.(daocond.Condition)
sb.WriteString((ufmt.Sprintf("**Resource: %s**\n\n", key)))
sb.WriteString((ufmt.Sprintf("Condition: %s\n", condition.Render())))
sb.WriteString((ufmt.Sprintf("\n--------------------------------\n")))
res.Write(ufmt.Sprintf("## Running Proposals 🗳️\n\n"))
i = 0
d.ProposalModule.proposals.Iterate("", "", func(key string, value interface{}) bool {
proposal := value.(*Proposal)
if proposal.status != ProposalStatusOpen {
return false
}
id, err := seqid.FromString(key)
if err != nil {
panic(err)
}
res.Write(ufmt.Sprintf("- **Proposal %d: [%s](%s:%s/%s)**\n\n", uint64(id), proposal.title, d.RealmName, "proposal", key))
i += 1
return false
})
if i == 0 {
res.Write(ufmt.Sprintf("\t⚠️ There are no running proposals at the moment\n\n"))
}

return sb.String()
res.Write(ufmt.Sprintf("> See the [proposal history 📜](%s:%s) for more information\n\n", d.RealmName, PROPOSAL_HISTORY_PATH))
}

func (p *ProposalModule) Render() string {
sb := strings.Builder{}
sb.WriteString((ufmt.Sprintf("## Proposals\n\n")))
func (d *DaoRolesBased) renderConfigPage(res *mux.ResponseWriter, req *mux.Request) {
res.Write(ufmt.Sprintf("# %s - Config ⚙️\n\n", d.Name))
roles := d.MemberModule.getRoles()
res.Write(ufmt.Sprintf("## Roles 🏷️\n\n"))
for _, role := range roles {
res.Write(ufmt.Sprintf("- %s\n\n", role))
}
res.Write(ufmt.Sprintf("\n--------------------------------\n"))
res.Write(ufmt.Sprintf("## Resources 📦\n\n"))
i := 1
d.ResourcesModule.resources.Iterate("", "", func(key string, value interface{}) bool {
resource := value.(daocond.Condition)
res.Write(ufmt.Sprintf("- **Resource #%d: %s**\n\n", i, key))
res.Write(ufmt.Sprintf(" - **Condition:** %s\n\n", resource.Render()))
i += 1
return false
})
}

func (d *DaoRolesBased) renderProposalHistoryPage(res *mux.ResponseWriter, req *mux.Request) {
res.Write(ufmt.Sprintf("# %s - Proposal History\n\n", d.Name))
res.Write(ufmt.Sprintf("## Proposals 🗳️\n\n"))
i := 1
p.proposals.Iterate("", "", func(key string, value interface{}) bool {
d.ProposalModule.proposals.Iterate("", "", func(key string, value interface{}) bool {
proposal := value.(*Proposal)
sb.WriteString((ufmt.Sprintf("**Proposal %d: %s**\n\n", i, proposal.title)))
sb.WriteString((ufmt.Sprintf("Description: %s\n\n", proposal.description)))
sb.WriteString((ufmt.Sprintf("Proposer: %s\n\n", proposal.proposer)))
sb.WriteString((ufmt.Sprintf("Status: %s\n\n", proposal.status.String())))
sb.WriteString((ufmt.Sprintf("State: %s\n\n", proposal.state.RenderJSON(proposal.votes).String())))
sb.WriteString((ufmt.Sprintf("\n--------------------------------\n")))
id, err := seqid.FromString(key)
if err != nil {
panic(err)
}
res.Write(ufmt.Sprintf("- **Proposal %d: [%s](%s:%s/%s) - %s**\n\n", uint64(id), proposal.title, d.RealmName, "proposal", key, proposal.status))
i += 1
return false
})
}

return sb.String()
func (d *DaoRolesBased) renderMemberDetailPage(res *mux.ResponseWriter, req *mux.Request) {
res.Write(ufmt.Sprintf("# %s - Member Detail - %s\n\n", d.Name, req.GetVar("address")))
roles := d.MemberModule.getUserRoles(req.GetVar("address"))
res.Write(ufmt.Sprintf("## Roles 🏷️\n\n"))
for _, role := range roles {
res.Write(ufmt.Sprintf("- %s\n\n", role))
}
}

func (mr *MessagesRegistry) Render() string {
sb := strings.Builder{}
sb.WriteString((ufmt.Sprintf("## Supported Messages\n\n")))
func (d *DaoRolesBased) renderProposalDetailPage(res *mux.ResponseWriter, req *mux.Request) {
id, err := seqid.FromString(req.GetVar("id"))
if err != nil {
panic(err)
}
res.Write(ufmt.Sprintf("# %s - Proposal #%d\n\n", d.Name, uint64(id)))
proposal := d.ProposalModule.getProposal(uint64(id))
res.Write(ufmt.Sprintf("## Title - %s 📜\n\n", proposal.title))
res.Write(ufmt.Sprintf("## Description 📝\n\n%s\n\n", proposal.description))
res.Write(ufmt.Sprintf("## Resource - %s 📦\n\n", proposal.message.Type()))
if proposal.status == ProposalStatusOpen {
res.Write(ufmt.Sprintf("## Status - Open 🟡\n\n"))
} else if proposal.status == ProposalStatusPassed {
res.Write(ufmt.Sprintf("## Status - Passed 🟢\n\n"))
} else if proposal.status == ProposalStatusExecuted {
res.Write(ufmt.Sprintf("## Status - Executed ✅\n\n"))
} else {
res.Write(ufmt.Sprintf("## Status - Closed 🔴\n\n"))
}
res.Write(ufmt.Sprintf("> proposed by %s 👤\n\n", proposal.proposer))

mr.handlers.Iterate("", "", func(key string, value interface{}) bool {
sb.WriteString((ufmt.Sprintf("- %s\n", value.(MessageHandler).Type())))
return false
})
res.Write(ufmt.Sprintf("\n--------------------------------\n"))

return sb.String()
res.Write(ufmt.Sprintf("## Votes 🗳️\n\n"))
res.Write(ufmt.Sprintf("%s\n\n", proposal.state.RenderJSON(proposal.votes)))
}
11 changes: 11 additions & 0 deletions gno/p/role_manager/role_manager.gno
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,17 @@ func (rm *RoleManager) CountRoles() int {
return rm.roles.Size()
}

func (rm *RoleManager) GetRoles() []string {
i := 0
res := make([]string, rm.roles.Size())
rm.roles.Iterate("", "", func(key string, value interface{}) bool {
res[i] = key
i++
return false
})
return res
}

func (rm *RoleManager) GetUserRoles(user std.Address) []string {
userRoles, ok := rm.users.Get(user.String())
if !ok {
Expand Down
9 changes: 6 additions & 3 deletions gno/r/dao_realm2/dao_realm2.gno
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import (
var dao *dao_roles_based.DaoRolesBased

func init() {
name := "Demo DAO"
description := "This is a demo DAO"
realmName := "dao_realm2"

var rolesJSON = `["admin","public-relationships","finance-officer"]`
var membersJSON = `[{"address":"g126gx6p6d3da4ymef35ury6874j6kys044r7zlg","roles":["admin","public-relationships"]},{"address":"g1ld6uaykyugld4rnm63rcy7vju4zx23lufml3jv","roles":["public-relationships"]},{"address":"g1r69l0vhp7tqle3a0rk8m8fulr8sjvj4h7n0tth","roles":["finance-officer"]},{"address":"g16jv3rpz7mkt0gqulxas56se2js7v5vmc6n6e0r","roles":[]}]`

Expand All @@ -27,8 +31,7 @@ func init() {
}
resourcesJSON += `]`

dao = dao_roles_based.NewDaoRolesBasedJSON(rolesJSON, membersJSON, resourcesJSON, messagesHandlers)

dao = dao_roles_based.NewDaoRolesBasedJSON(name, description, realmName, rolesJSON, membersJSON, resourcesJSON, messagesHandlers)
}

func ProposeJSON(proposalJSON string) {
Expand All @@ -44,5 +47,5 @@ func Vote(proposalID uint64, vote string) {
}

func Render(path string) string {
return dao.Render()
return dao.Render(path)
}

0 comments on commit d42b5d4

Please sign in to comment.