Pure package detail
basedao
gno.land/p/samcrew/basedao
Indexed deployment identity with independently loaded latest RPC source. Functions and Render are realm-only RPC capabilities.
Indexed deployment
Identity
- Package path
- gno.land/p/samcrew/basedao
- Block
- 98962
- Deployed (UTC)
- Transaction
- vFRzo/FVwQsb8Cx+tKBmj8a88A034YhkhP+ftPai79c=
Latest RPC state
Source
package basedao
import (
"strings"
"time"
"gno.land/p/demo/svg"
"gno.land/p/moul/md"
ntavl "gno.land/p/nt/avl/v0" // svg.Canvas.Style is a *nt/avl/v0.Tree — keep that ABI at the svg boundary
"gno.land/p/nt/seqid/v0"
"gno.land/p/nt/ufmt/v0"
"gno.land/p/samcrew/avl"
"gno.land/p/samcrew/avl/pager"
"gno.land/p/samcrew/daokit"
)
// Truncates a string to a maximum length, adding "..." in the middle if it exceeds the limit.
func TruncateMiddle(s string, max ...int) string {
defMax := 12
if len(max) > 0 {
defMax = max[0]
}
runes := []rune(s)
if len(runes) <= defMax {
return s
}
if defMax <= 3 {
return string(runes[:defMax])
}
half := (defMax - 3) / 2
return ufmt.Sprintf("%s...%s", string(runes[:half]), string(runes[len(runes)-half:]))
}
// Formats an address as a clickable link in Markdown.
func RenderAddressLink(addr string) string {
return md.Link(TruncateMiddle(addr), ufmt.Sprintf("/u/%s", addr))
}
// Replaces all occurrences of roles in the given string with clickable links in Markdown.
func (d *DAOPrivate) RenderWithRolesLinks(s string) string {
roles := d.Members.GetRoles()
for _, role := range roles {
// AddRole refuses an empty name, but Roles is an exported avl.Tree that
// a same-realm write or a MigrateFn can seed directly. An empty needle
// makes ReplaceAll match between every character, so skipping here keeps
// the render path safe however the store was filled.
if role == "" {
continue
}
s = strings.ReplaceAll(s, role, d.RenderRoleLink(role))
}
return s
}
// Formats a role as a clickable link in Markdown.
func (d *DAOPrivate) RenderRoleLink(role string) string {
pkgPath := d.Realm.PkgPath()
linkPath := getLinkPath(pkgPath)
roleName := d.Members.RoleInfo(role).Name
if role == "" {
return "*No role assigned*"
}
return ufmt.Sprintf("[%s](%s:%s/%s)", roleName, linkPath, "role", role)
}
// Formats a role as a clickable link in Markdown, with colored chip at left.
func (d *DAOPrivate) RenderRoleLinkWithChip(role string) string {
pkgPath := d.Realm.PkgPath()
linkPath := getLinkPath(pkgPath)
if role == "" {
return ufmt.Sprintf("%s %s", d.RoleColoredChip(""), "*No role assigned*")
}
return ufmt.Sprintf("[%s %s](%s:%s/%s)", d.RoleColoredChip(role), role, linkPath, "role", role)
}
// Formats a time.Time object as a string with UTC offset.
func FormatDateTimeWithUTCOffset(t time.Time) string {
_, offsetSeconds := t.Zone()
hours := offsetSeconds / 3600
minutes := (offsetSeconds % 3600) / 60
sign := "+"
if hours < 0 || minutes < 0 {
sign = "-"
hours = -hours
minutes = -minutes
}
return ufmt.Sprintf("%s UTC%s%02d:%02d",
t.Format("2006-01-02 15:04:05"),
sign, hours, minutes)
}
// Returns a colored chip svg for the given role. If no role is given, a gray chip is returned.
// It can optionally take a custom svg.Canvas to define the size and style of the chip.
func (d *DAOPrivate) RoleColoredChip(role string, canvas ...*svg.Canvas) string {
roleName := ""
chipColor := ""
canvasWidth := 16
canvasHeight := 16
canvasElem := []svg.Elem{}
canvasStyle := ntavl.NewTree()
if canvas != nil && len(canvas) > 0 {
canvasWidth = canvas[0].Width
canvasHeight = canvas[0].Height
canvasElem = canvas[0].Elems
canvasStyle = canvas[0].Style
}
if role == "" {
chipColor = "#CDCDCD"
} else {
info := d.Members.RoleInfo(role)
chipColor = info.Color
roleName = info.Name
}
chipCanvas := &svg.Canvas{
Width: canvasWidth,
Height: canvasHeight,
Elems: canvasElem,
Style: canvasStyle,
}
chipCanvas.Append(svg.NewRectangle(0, 0, canvasWidth, canvasHeight, chipColor))
return chipCanvas.Render(roleName + " colored chip")
}
// Renders a table of members with their display name, address, roles, and profile link.
// It supports pagination.
func (d *DAOPrivate) RenderMembersTable(path string, members *avl.Tree) string {
pkgPath := d.Realm.PkgPath()
linkPath := getLinkPath(pkgPath)
const pageSize = 10
pager := pager.NewPager(members, pageSize, false)
page := pager.MustGetPageByPath(path)
s := ""
s += ufmt.Sprintf("| **Name** | **Address 🔗** | **Roles 🎭** | **Profile** |\n")
s += ufmt.Sprintf("|----------|----------------|--------------|-------------|\n")
for _, item := range page.Items {
displayName := d.GetProfileString(address(item.Key), "DisplayName", FALLBACK_DISPLAY_NAME)
roles := d.Members.GetMemberRoles(item.Key)
rolesStr := strings.Join(roles, ", ")
if len(roles) == 0 {
rolesStr = d.RenderRoleLinkWithChip("")
}
for _, role := range roles {
// Same empty-needle hazard as RenderWithRolesLinks: an empty role
// name matches between every character. AddRole refuses one, but
// Roles is an exported avl.Tree that can be seeded around it — and
// once it holds "", AddRoleToMember will happily assign it.
if role == "" {
continue
}
rolesStr = strings.Replace(rolesStr, role, d.RenderRoleLinkWithChip(role), -1)
}
s += ufmt.Sprintf("| %s | %s | %s | [View](%s:%s/%s) |\n",
displayName,
RenderAddressLink(item.Key),
rolesStr,
linkPath, "member", item.Key)
}
s += "\n" + page.Picker(path) + "\n"
return s
}
func (d *DAOPrivate) RenderProposalsTable(path string, proposals *daokit.ProposalsStore) string {
pkgPath := d.Realm.PkgPath()
linkPath := getLinkPath(pkgPath)
proposalsCount := proposals.Tree.Size()
const pageSize = 10
pager := pager.NewPager(proposals.Tree, pageSize, true)
page := pager.MustGetPageByPath(path)
s := ""
if proposalsCount == 0 {
s += ufmt.Sprintf("\t⚠️ There are no proposals to show\n\n")
} else {
s += ufmt.Sprintf("| **ID** | **Resource** | **Proposer** | **CreatedAt** | **Status** |\n")
s += ufmt.Sprintf("|--------|--------------|--------------|---------------|------------|\n")
}
for _, item := range page.Items {
proposal := item.Value.(*daokit.Proposal)
key := item.Key
id, err := seqid.FromString(key)
if err != nil {
panic(err)
}
resource := d.Core.Resources.Get(proposal.Action.Type())
s += ufmt.Sprintf("| [%d](%s:%s/%d) | %s | %s | %s | %s |\n",
uint64(id), linkPath, "proposal", uint64(id),
resource.DisplayName,
RenderAddressLink(proposal.ProposerID),
FormatDateTimeWithUTCOffset(proposal.CreatedAt),
proposal.DisplayStatus().String())
}
s += "\n" + page.Picker(path) + "\n"
return s
}
The verified vm/qfuncs operation accepts realm paths only.
Pure packages expose source files but do not have Realm Render.